<commit_before>package openapi

import (
	"testing"
)

func TestHasDuplicatedParameter(t *testing.T) {
	t.Run("no duplicated param", testHasDuplicatedParameterFalse)
	t.Run("there's duplicated param", testHasDuplicatedParameterTrue)
}

func testHasDuplicatedParameterFalse(t *testing.T) {
	params := []*Parameter{
		&Parameter{Name: "foo", In: "header"},
		&Parameter{Name: "foo", In: "path", Required: true},
		&Parameter{Name: "bar", In: "path", Required: true},
	}
	if hasDuplicatedParameter(params) {
		t.Error("should return false")
	}
}

func testHasDuplicatedParameterTrue(t *testing.T) {
	params := []*Parameter{
		&Parameter{Name: "foo", In: "header"},
		&Parameter{Name: "foo", In: "header"},
	}
	if !hasDuplicatedParameter(params) {
		t.Error("should return true")
	}
}

func TestMustURL(t *testing.T) {
	candidates := []struct {
		label  string
		in     string
		hasErr bool
	}{
		{"empty", "", true},
		{"valid HTTP url", "http://example.com", false},
		{"allowed relative path", "foo/bar/baz", true},
		{"absolute path", "/foo/bar/baz", false},
		{"plain string", "foobarbaz", true},
	}
	for _, c := range candidates {
		if err := mustURL(c.label, c.in); (err != nil) != c.hasErr {
			t.Logf("error occured at %s", c.label)
			if c.hasErr {
				t.Error("error should occured, but not")
				return
			}
			t.Error("error should not occurred, but occurred")
			return
		}
	}
}

const exampleCom = "https://example.com"

var mockScopes = map[string]string{"foo": "bar"}

func TestOAuthFlowValidate(t *testing.T) {
	empty := OAuthFlow{}
	aURL := OAuthFlow{AuthorizationURL: exampleCom}
	tURL := OAuthFlow{TokenURL: exampleCom}
	rURL := OAuthFlow{RefreshURL: exampleCom}
	scopes := OAuthFlow{Scopes: mockScopes}
	atURL := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom}
	arURL := OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom}
	aURLscopes := OAuthFlow{AuthorizationURL: exampleCom, Scopes: mockScopes}
	trURL := OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom}
	tURLscopes := OAuthFlow{TokenURL: exampleCom, Scopes: mockScopes}
	rURLscopes := OAuthFlow{RefreshURL: exampleCom, Scopes: mockScopes}
	atrURL := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom}
	atURLscopes := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, Scopes: mockScopes}
	arURLscopes := OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	trURLscopes := OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	atrURLscopes := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}

	candidates := []struct {
		label   string
		in      OAuthFlow
		haveErr [4]bool
	}{
		{"empty", empty, [4]bool{true, true, true, true}},
		{"aURL", aURL, [4]bool{true, true, true, true}},
		{"tURL", tURL, [4]bool{true, true, true, true}},
		{"rURL", rURL, [4]bool{true, true, true, true}},
		{"scopes", scopes, [4]bool{true, true, true, true}},
		{"aURL/tURL", atURL, [4]bool{true, true, true, true}},
		{"aURL/rURL", arURL, [4]bool{true, true, true, true}},
		{"aURL/scopes", aURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL", trURL, [4]bool{true, true, true, true}},
		{"tURL/scopes", tURLscopes, [4]bool{true, false, false, true}},
		{"rURL/scopes", rURLscopes, [4]bool{true, true, true, true}},
		{"aURL/tURL/rURL", atrURL, [4]bool{true, true, true, true}},
		{"aURL/tURL/scopes", atURLscopes, [4]bool{false, false, false, false}},
		{"aURL/rURL/scopes", arURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL/scopes", trURLscopes, [4]bool{true, false, false, true}},
		{"aURL/tURL/rURL/scopes", atrURLscopes, [4]bool{false, false, false, false}},
	}
	for _, c := range candidates {
		testOAuthFlowValidate(t, c.label, c.in, c.haveErr)
	}
}

var flowTypes = []string{"implicit", "password", "clientCredentials", "authorizationCode"}

func testOAuthFlowValidate(t *testing.T, label string, oauthFlow OAuthFlow, haveErr [4]bool) {
	t.Logf("%s-empty", label)
	if err := oauthFlow.Validate(""); err == nil {
		t.Error("error should be occurred, but not")
	}
	t.Logf("%s-wrongtype", label)
	if err := oauthFlow.Validate("foobar"); err == nil {
		t.Error("error should be occurred, but not")
	}
	for i, flowType := range flowTypes {
		t.Logf("%s-%s", label, flowType)
		if err := oauthFlow.Validate(flowType); (err != nil) != haveErr[i] {
			if haveErr[i] {
				t.Error("error should be occurred, but not")
				continue
			}
			t.Error("error should not be occurred, but occurred")
			t.Log(err)
		}
	}
}
<commit_msg>add test pattern for OAuthFlow.Validate<commit_after>package openapi

import (
	"testing"
)

func TestHasDuplicatedParameter(t *testing.T) {
	t.Run("no duplicated param", testHasDuplicatedParameterFalse)
	t.Run("there's duplicated param", testHasDuplicatedParameterTrue)
}

func testHasDuplicatedParameterFalse(t *testing.T) {
	params := []*Parameter{
		&Parameter{Name: "foo", In: "header"},
		&Parameter{Name: "foo", In: "path", Required: true},
		&Parameter{Name: "bar", In: "path", Required: true},
	}
	if hasDuplicatedParameter(params) {
		t.Error("should return false")
	}
}

func testHasDuplicatedParameterTrue(t *testing.T) {
	params := []*Parameter{
		&Parameter{Name: "foo", In: "header"},
		&Parameter{Name: "foo", In: "header"},
	}
	if !hasDuplicatedParameter(params) {
		t.Error("should return true")
	}
}

func TestMustURL(t *testing.T) {
	candidates := []struct {
		label  string
		in     string
		hasErr bool
	}{
		{"empty", "", true},
		{"valid HTTP url", "http://example.com", false},
		{"allowed relative path", "foo/bar/baz", true},
		{"absolute path", "/foo/bar/baz", false},
		{"plain string", "foobarbaz", true},
	}
	for _, c := range candidates {
		if err := mustURL(c.label, c.in); (err != nil) != c.hasErr {
			t.Logf("error occured at %s", c.label)
			if c.hasErr {
				t.Error("error should occured, but not")
				return
			}
			t.Error("error should not occurred, but occurred")
			return
		}
	}
}

const exampleCom = "https://example.com"

var mockScopes = map[string]string{"foo": "bar"}

func TestOAuthFlowValidate(t *testing.T) {
	empty := OAuthFlow{}
	aURL := OAuthFlow{AuthorizationURL: exampleCom}
	tURL := OAuthFlow{TokenURL: exampleCom}
	rURL := OAuthFlow{RefreshURL: exampleCom}
	scopes := OAuthFlow{Scopes: mockScopes}
	atURL := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom}
	arURL := OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom}
	aURLscopes := OAuthFlow{AuthorizationURL: exampleCom, Scopes: mockScopes}
	trURL := OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom}
	tURLscopes := OAuthFlow{TokenURL: exampleCom, Scopes: mockScopes}
	rURLscopes := OAuthFlow{RefreshURL: exampleCom, Scopes: mockScopes}
	atrURL := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom}
	atURLscopes := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, Scopes: mockScopes}
	arURLscopes := OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	trURLscopes := OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	atrURLscopes := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	invalidURL := OAuthFlow{AuthorizationURL: "foobar", TokenURL: "foobar", RefreshURL: "foobar", Scopes: mockScopes}
	zeroMap := OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: map[string]string{}}

	candidates := []struct {
		label   string
		in      OAuthFlow
		haveErr [4]bool
	}{
		{"empty", empty, [4]bool{true, true, true, true}},
		{"aURL", aURL, [4]bool{true, true, true, true}},
		{"tURL", tURL, [4]bool{true, true, true, true}},
		{"rURL", rURL, [4]bool{true, true, true, true}},
		{"scopes", scopes, [4]bool{true, true, true, true}},
		{"aURL/tURL", atURL, [4]bool{true, true, true, true}},
		{"aURL/rURL", arURL, [4]bool{true, true, true, true}},
		{"aURL/scopes", aURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL", trURL, [4]bool{true, true, true, true}},
		{"tURL/scopes", tURLscopes, [4]bool{true, false, false, true}},
		{"rURL/scopes", rURLscopes, [4]bool{true, true, true, true}},
		{"aURL/tURL/rURL", atrURL, [4]bool{true, true, true, true}},
		{"aURL/tURL/scopes", atURLscopes, [4]bool{false, false, false, false}},
		{"aURL/rURL/scopes", arURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL/scopes", trURLscopes, [4]bool{true, false, false, true}},
		{"aURL/tURL/rURL/scopes", atrURLscopes, [4]bool{false, false, false, false}},

		{"invalidURL", invalidURL, [4]bool{true, true, true, true}},
		{"zero length map", zeroMap, [4]bool{true, true, true, true}},
	}
	for _, c := range candidates {
		testOAuthFlowValidate(t, c.label, c.in, c.haveErr)
	}
}

var flowTypes = []string{"implicit", "password", "clientCredentials", "authorizationCode"}

func testOAuthFlowValidate(t *testing.T, label string, oauthFlow OAuthFlow, haveErr [4]bool) {
	t.Logf("%s-empty", label)
	if err := oauthFlow.Validate(""); err == nil {
		t.Error("error should be occurred, but not")
	}
	t.Logf("%s-wrongtype", label)
	if err := oauthFlow.Validate("foobar"); err == nil {
		t.Error("error should be occurred, but not")
	}
	for i, flowType := range flowTypes {
		t.Logf("%s-%s", label, flowType)
		if err := oauthFlow.Validate(flowType); (err != nil) != haveErr[i] {
			if haveErr[i] {
				t.Error("error should be occurred, but not")
				continue
			}
			t.Error("error should not be occurred, but occurred")
			t.Log(err)
		}
	}
}
<|endoftext|>


<commit_before>package host

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

	log "github.com/Sirupsen/logrus"
	conf "github.com/citruspi/iago/configuration"
	"github.com/citruspi/iago/notification"
	"github.com/citruspi/iago/travis"
)

type Host struct {
	Hostname     string    `json:"hostname"`
	Expiration   time.Time `json:"expiration"`
	Protocol     string    `json:"protocol"`
	Port         int       `json:"port"`
	Path         string    `json:"path"`
	Repositories []string  `json:"repositories"`
}

var (
	List []Host
)

func init() {
	log.SetLevel(log.DebugLevel)
}

func (h Host) URL() string {
	var buffer bytes.Buffer

	buffer.WriteString(h.Protocol)
	buffer.WriteString("://")
	buffer.WriteString(h.Hostname)
	buffer.WriteString(":")
	buffer.WriteString(strconv.Itoa(h.Port))
	buffer.WriteString(h.Path)

	log.WithFields(log.Fields{
		"url": string(buffer.Bytes()),
	}).Debug("Prepared host URL")

	return string(buffer.Bytes())
}

func (h Host) CheckIn() {
	log.WithFields(log.Fields{
		"host": h.Hostname,
	}).Info("Checking in host")

	log.WithFields(log.Fields{
		"host": h.Hostname,
	}).Debug("Checking if host is already checked in")

	for i, e := range List {
		if e.Hostname == h.Hostname {

			log.WithFields(log.Fields{
				"host": h.Hostname,
			}).Debug("Host is already checked in")

			diff := List
			diff = append(diff[:i], diff[i+1:]...)
			List = diff

			log.WithFields(log.Fields{
				"host": h.Hostname,
			}).Debug("Removed existing host")
		}
	}

	if h.Protocol == "" {
		log.WithFields(log.Fields{
			"host": h.Hostname,
		}).Debug("Host protocol is undefined")

		h.Protocol = "http"
	}

	if h.Port == 0 {

		log.WithFields(log.Fields{
			"host": h.Hostname,
		}).Debug("Host port is undefined")

		if h.Protocol == "http" {
			h.Port = 80
		} else if h.Protocol == "https" {
			h.Port = 443
		}
	}

	if h.Path == "" {
		log.WithFields(log.Fields{
			"host": h.Hostname,
		}).Debug("Host path is undefined")

		h.Path = "/"
	}

	expiration := time.Now().UTC()
	expiration = expiration.Add(time.Duration(conf.Host.TTL) * time.Second)

	h.Expiration = expiration

	List = append(List, h)

	log.WithFields(log.Fields{
		"host":          h.Hostname,
		"protocol":      h.Protocol,
		"port":          h.Port,
		"path":          h.Path,
		"expiration":    h.Expiration,
		"repositorites": h.Repositories,
	}).Info("Finished checking in host")
}

func Cleanup() {
	for {
		log.Debug("Removing expired hosts")

		for i, h := range List {
			if time.Now().UTC().After(h.Expiration) {
				diff := List
				diff = append(diff[:i], diff[i+1:]...)
				List = diff

				log.WithFields(log.Fields{
					"host": h.Hostname,
				}).Debug("Removed expired host")
			}
		}

		log.Debug("Finished removing expired hosts")

		time.Sleep(time.Duration(conf.Host.TTL) * time.Second)
	}
}

func Notify(announcement travis.Announcement) {
	log.WithFields(log.Fields{
		"repository": announcement.Payload.Repository.Name,
		"owner":      announcement.Payload.Repository.Owner,
		"commit":     announcement.Payload.Commit,
		"branch":     announcement.Payload.Branch,
		"message":    announcement.Payload.Message,
	}).Debug("Preparing to notify hosts about deployment")

	n := notification.Build(announcement)

	log.WithFields(log.Fields{
		"repository": n.Repository,
		"owner":      n.Owner,
		"commit":     n.Commit,
		"branch":     n.Branch,
	}).Debug("Built notification")

	if conf.Notification.Sign {
		n = n.Sign(conf.Notification.PrivateKey)
	}

	content, _ := json.Marshal(n)
	body := bytes.NewBuffer(content)

	var buffer bytes.Buffer

	buffer.WriteString(n.Owner)
	buffer.WriteString("/")
	buffer.WriteString(n.Repository)

	identifier := string(buffer.Bytes())

	for _, host := range List {
		for _, repository := range host.Repositories {
			if repository == identifier {
				log.WithFields(log.Fields{
					"repository": n.Repository,
					"owner":      n.Owner,
					"commit":     n.Commit,
					"branch":     n.Branch,
					"host":       host.Hostname,
				}).Debug("Preparing to notify host")

				urlStr := host.URL()

				req, err := http.NewRequest("POST", urlStr, body)
				req.Header.Set("Content-Type", "application/json")

				client := &http.Client{}
				resp, err := client.Do(req)

				if err != nil {
					log.WithFields(log.Fields{
						"repository": n.Repository,
						"owner":      n.Owner,
						"commit":     n.Commit,
						"branch":     n.Branch,
						"host":       host.Hostname,
						"error":      err,
					}).Error("Failed to notify host about deployment")
				} else {
					defer resp.Body.Close()

					log.WithFields(log.Fields{
						"repository": n.Repository,
						"owner":      n.Owner,
						"commit":     n.Commit,
						"branch":     n.Branch,
						"host":       host.Hostname,
					}).Info("Notified host about deployment")
				}
			}
		}
	}
}
<commit_msg>Fixed a typo<commit_after>package host

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

	log "github.com/Sirupsen/logrus"
	conf "github.com/citruspi/iago/configuration"
	"github.com/citruspi/iago/notification"
	"github.com/citruspi/iago/travis"
)

type Host struct {
	Hostname     string    `json:"hostname"`
	Expiration   time.Time `json:"expiration"`
	Protocol     string    `json:"protocol"`
	Port         int       `json:"port"`
	Path         string    `json:"path"`
	Repositories []string  `json:"repositories"`
}

var (
	List []Host
)

func init() {
	log.SetLevel(log.DebugLevel)
}

func (h Host) URL() string {
	var buffer bytes.Buffer

	buffer.WriteString(h.Protocol)
	buffer.WriteString("://")
	buffer.WriteString(h.Hostname)
	buffer.WriteString(":")
	buffer.WriteString(strconv.Itoa(h.Port))
	buffer.WriteString(h.Path)

	log.WithFields(log.Fields{
		"url": string(buffer.Bytes()),
	}).Debug("Prepared host URL")

	return string(buffer.Bytes())
}

func (h Host) CheckIn() {
	log.WithFields(log.Fields{
		"host": h.Hostname,
	}).Info("Checking in host")

	log.WithFields(log.Fields{
		"host": h.Hostname,
	}).Debug("Checking if host is already checked in")

	for i, e := range List {
		if e.Hostname == h.Hostname {

			log.WithFields(log.Fields{
				"host": h.Hostname,
			}).Debug("Host is already checked in")

			diff := List
			diff = append(diff[:i], diff[i+1:]...)
			List = diff

			log.WithFields(log.Fields{
				"host": h.Hostname,
			}).Debug("Removed existing host")
		}
	}

	if h.Protocol == "" {
		log.WithFields(log.Fields{
			"host": h.Hostname,
		}).Debug("Host protocol is undefined")

		h.Protocol = "http"
	}

	if h.Port == 0 {

		log.WithFields(log.Fields{
			"host": h.Hostname,
		}).Debug("Host port is undefined")

		if h.Protocol == "http" {
			h.Port = 80
		} else if h.Protocol == "https" {
			h.Port = 443
		}
	}

	if h.Path == "" {
		log.WithFields(log.Fields{
			"host": h.Hostname,
		}).Debug("Host path is undefined")

		h.Path = "/"
	}

	expiration := time.Now().UTC()
	expiration = expiration.Add(time.Duration(conf.Host.TTL) * time.Second)

	h.Expiration = expiration

	List = append(List, h)

	log.WithFields(log.Fields{
		"host":         h.Hostname,
		"protocol":     h.Protocol,
		"port":         h.Port,
		"path":         h.Path,
		"expiration":   h.Expiration,
		"repositories": h.Repositories,
	}).Info("Finished checking in host")
}

func Cleanup() {
	for {
		log.Debug("Removing expired hosts")

		for i, h := range List {
			if time.Now().UTC().After(h.Expiration) {
				diff := List
				diff = append(diff[:i], diff[i+1:]...)
				List = diff

				log.WithFields(log.Fields{
					"host": h.Hostname,
				}).Debug("Removed expired host")
			}
		}

		log.Debug("Finished removing expired hosts")

		time.Sleep(time.Duration(conf.Host.TTL) * time.Second)
	}
}

func Notify(announcement travis.Announcement) {
	log.WithFields(log.Fields{
		"repository": announcement.Payload.Repository.Name,
		"owner":      announcement.Payload.Repository.Owner,
		"commit":     announcement.Payload.Commit,
		"branch":     announcement.Payload.Branch,
		"message":    announcement.Payload.Message,
	}).Debug("Preparing to notify hosts about deployment")

	n := notification.Build(announcement)

	log.WithFields(log.Fields{
		"repository": n.Repository,
		"owner":      n.Owner,
		"commit":     n.Commit,
		"branch":     n.Branch,
	}).Debug("Built notification")

	if conf.Notification.Sign {
		n = n.Sign(conf.Notification.PrivateKey)
	}

	content, _ := json.Marshal(n)
	body := bytes.NewBuffer(content)

	var buffer bytes.Buffer

	buffer.WriteString(n.Owner)
	buffer.WriteString("/")
	buffer.WriteString(n.Repository)

	identifier := string(buffer.Bytes())

	for _, host := range List {
		for _, repository := range host.Repositories {
			if repository == identifier {
				log.WithFields(log.Fields{
					"repository": n.Repository,
					"owner":      n.Owner,
					"commit":     n.Commit,
					"branch":     n.Branch,
					"host":       host.Hostname,
				}).Debug("Preparing to notify host")

				urlStr := host.URL()

				req, err := http.NewRequest("POST", urlStr, body)
				req.Header.Set("Content-Type", "application/json")

				client := &http.Client{}
				resp, err := client.Do(req)

				if err != nil {
					log.WithFields(log.Fields{
						"repository": n.Repository,
						"owner":      n.Owner,
						"commit":     n.Commit,
						"branch":     n.Branch,
						"host":       host.Hostname,
						"error":      err,
					}).Error("Failed to notify host about deployment")
				} else {
					defer resp.Body.Close()

					log.WithFields(log.Fields{
						"repository": n.Repository,
						"owner":      n.Owner,
						"commit":     n.Commit,
						"branch":     n.Branch,
						"host":       host.Hostname,
					}).Info("Notified host about deployment")
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package http
<commit_msg>Add RegexPath HandlerFunc<commit_after>package http

import (
	"net/http"
	"regexp"
)

// RegexPath returns a handler that serves HTTP requests
// with the first match of the the given regex from the request URL's Path
// and invoking the handler h. RegexPath handles a
// request for a path that doesn't match by
// replying with an HTTP 404 not found error.
func RegexPath(reg *regexp.Regexp, h http.Handler) http.Handler {
	if reg == nil {
		return h
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		matches := reg.FindStringSubmatch(r.URL.Path)
		if len(matches) > 1 {
			r.URL.Path = matches[1]
			h.ServeHTTP(w, r)
		} else {
			http.NotFound(w, r)
		}
	})
}
<|endoftext|>


<commit_before>package link

import (
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	UserAgent        = "Opendoor"
	DeliciousPostUrl = "https://api.del.icio.us/v1/posts/add"
	HttpTimeout      = 5 // Wait at most 5 seconds for Delicious.
)

var (
	LinkRx = regexp.MustCompile(`(https?://[^ )]+)`)
	TagsRx = regexp.MustCompile(`#[\w-]+`)
	CodeRx = regexp.MustCompile(`code="(.*?)"`)
)

func Find(txt string) string {
	return LinkRx.FindString(txt)
}

// Extract tags from a string, w/o the leading '#'
func Tags(txt string) []string {
	tags := []string{}
	for _, tag := range TagsRx.FindAllString(txt, -1) {
		tags = append(tags, strings.TrimLeft(tag, "#"))
	}
	return tags
}

func deliciousPostParams(u string, tags []string) io.Reader {
	form := url.Values{}
	form.Set("url", u)
	form.Set("description", u) // FIXME actually a title
	form.Set("tags", strings.Join(tags, ","))
	if IncludesPrivate(tags) {
		form.Set("shared", "no")
	}
	return strings.NewReader(form.Encode())
}

// Custom Delicious API client, without keep-alive.
func deliciousClient() *http.Client {
	return &http.Client{
		Timeout:   HttpTimeout * time.Second,
		Transport: &http.Transport{DisableKeepAlives: true},
	}
}

func Save(u string, tags []string) (err error) {
	params := deliciousPostParams(u, tags)
	req, err := http.NewRequest("POST", DeliciousPostUrl, params)
	if err != nil {
		return
	}
	token, err := oauthToken()
	if err != nil {
		return
	}
	req.Header.Add("Authorization", token)
	req.Header.Add("User-Agent", UserAgent)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	client := deliciousClient()
	if resp, err := client.Do(req); err == nil {
		return parseResponse(resp)
	}
	return
}

func IncludesPrivate(tags []string) bool {
	for _, t := range tags {
		if t == "#private" || t == "private" {
			return true
		}
	}
	return false
}

// Delicious has a shitty API. Forget about HTTP status codes, embrace XML!
func parseResponse(resp *http.Response) error {
	defer resp.Body.Close()

	// We need to read code's value at <result code="value" />, to acknowledge
	// the API's response: HTTP codes are probably too hard.
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return errors.New("Delicious hung up...")
	}
	m := CodeRx.FindSubmatch(body)
	if len(m) != 2 {
		return errors.New(string(body))
	}
	code := string(m[1])

	// "done" is good, we like "done".
	if code == "done" {
		return nil
	}
	log.Println("Delicious error: ", string(body))
	return errors.New(code)
}

func oauthToken() (string, error) {
	token := os.Getenv("DELICIOUS_OAUTH_TOKEN")
	if token == "" {
		return "", errors.New("Missing Delicious Token")
	}
	return "Bearer " + token, nil
}
<commit_msg>Better tag matching<commit_after>package link

import (
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	UserAgent        = "Opendoor"
	DeliciousPostUrl = "https://api.del.icio.us/v1/posts/add"
	HttpTimeout      = 5 // Wait at most 5 seconds for Delicious.
)

var (
	LinkRx = regexp.MustCompile(`(https?://[^ )]+)`)
	TagsRx = regexp.MustCompile(`\s(#[\w\pL-]+)`)
	CodeRx = regexp.MustCompile(`code="(.*?)"`)
)

func Find(txt string) string {
	return LinkRx.FindString(txt)
}

// Extract tags from a string, w/o the leading '#'
func Tags(txt string) []string {
	tags := []string{}
	for _, match := range TagsRx.FindAllStringSubmatch(txt, -1) {
		tags = append(tags, strings.TrimLeft(match[1], "#"))
	}
	return tags
}

func deliciousPostParams(u string, tags []string) io.Reader {
	form := url.Values{}
	form.Set("url", u)
	form.Set("description", u) // FIXME actually a title
	form.Set("tags", strings.Join(tags, ","))
	if IncludesPrivate(tags) {
		form.Set("shared", "no")
	}
	return strings.NewReader(form.Encode())
}

// Custom Delicious API client, without keep-alive.
func deliciousClient() *http.Client {
	return &http.Client{
		Timeout:   HttpTimeout * time.Second,
		Transport: &http.Transport{DisableKeepAlives: true},
	}
}

func Save(u string, tags []string) (err error) {
	params := deliciousPostParams(u, tags)
	req, err := http.NewRequest("POST", DeliciousPostUrl, params)
	if err != nil {
		return
	}
	token, err := oauthToken()
	if err != nil {
		return
	}
	req.Header.Add("Authorization", token)
	req.Header.Add("User-Agent", UserAgent)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	client := deliciousClient()
	if resp, err := client.Do(req); err == nil {
		return parseResponse(resp)
	}
	return
}

func IncludesPrivate(tags []string) bool {
	for _, t := range tags {
		if t == "#private" || t == "private" {
			return true
		}
	}
	return false
}

// Delicious has a shitty API. Forget about HTTP status codes, embrace XML!
func parseResponse(resp *http.Response) error {
	defer resp.Body.Close()

	// We need to read code's value at <result code="value" />, to acknowledge
	// the API's response: HTTP codes are probably too hard.
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return errors.New("Delicious hung up...")
	}
	m := CodeRx.FindSubmatch(body)
	if len(m) != 2 {
		return errors.New(string(body))
	}
	code := string(m[1])

	// "done" is good, we like "done".
	if code == "done" {
		return nil
	}
	log.Println("Delicious error: ", string(body))
	return errors.New(code)
}

func oauthToken() (string, error) {
	token := os.Getenv("DELICIOUS_OAUTH_TOKEN")
	if token == "" {
		return "", errors.New("Missing Delicious Token")
	}
	return "Bearer " + token, nil
}
<|endoftext|>


<commit_before>package batchgcd

// NOTE: This code was written with fastgcd available at https://factorable.net/
// as a reference, which was written by Nadia Heninger and J. Alex Halderman.
// I have put a substantial amount of my own design into this, and they do not
// claim it as a derivative work.
// I thank them for their original code and paper.

import (
	"fmt"
	"github.com/ncw/gmp"
	"io"
	"log"
	"os"
	"runtime"
	"time"
)

func encodeLength(buf []byte, length int) {
	buf[0] = byte(length >> 56)
	buf[1] = byte(length >> 48)
	buf[2] = byte(length >> 40)
	buf[3] = byte(length >> 32)
	buf[4] = byte(length >> 24)
	buf[5] = byte(length >> 16)
	buf[6] = byte(length >> 8)
	buf[7] = byte(length)
}

func decodeLength(buf []byte) int {
	var ret int

	ret |= int(buf[0]) << 56
	ret |= int(buf[1]) << 48
	ret |= int(buf[2]) << 40
	ret |= int(buf[3]) << 32
	ret |= int(buf[4]) << 24
	ret |= int(buf[5]) << 16
	ret |= int(buf[6]) << 8
	ret |= int(buf[7])

	return ret
}

func tmpfileReadWriter(inChan chan *gmp.Int, outChan chan *gmp.Int, prefix string, typ string, level int) {
	filename := fmt.Sprintf("%s-%s-%d", typ, prefix, level)
	tmpFile, err := os.Create(filename)
	if err != nil {
		log.Panic(err)
	}

	length := make([]byte, 8)

	var writeCount uint64
	for inData := range inChan {
		buf := inData.Bytes()
		encodeLength(length, len(buf))
		if _, err := tmpFile.Write(length); err != nil {
			log.Panic(err)
		}
		if _, err := tmpFile.Write(buf); err != nil {
			log.Panic(err)
		}
		writeCount += 1
	}

	if newOffset, e := tmpFile.Seek(0, 0); e != nil || newOffset != 0 {
		log.Panic(e)
	}

	var readCount uint64
	m := new(gmp.Int)
	for {
		if _, e := io.ReadFull(tmpFile, length); e != nil {
			if e == io.EOF {
				break
			}
			log.Panic(e)
		}
		buf := make([]byte, decodeLength(length))
		if _, e := io.ReadFull(tmpFile, buf); e != nil {
			log.Panic(e)
		}
		readCount += 1
		outChan <- m.SetBytes(buf)
		m = new(gmp.Int)
	}

	if writeCount != readCount {
		log.Panicf("Didn't write as many as we read: write=%v read=%v", writeCount, readCount)
	}
	close(outChan)
	// tmpFile.Truncate(0);
}

// Multiply sets of two adjacent inputs, placing into a single output
func lowmemProductTreeLevel(prefix string, level int, input chan *gmp.Int, channels []chan *gmp.Int, finalOutput chan<- Collision) {
	resultChan := make(chan *gmp.Int, 0)
	defer close(resultChan)

	hold := <-input
	m, ok := <-input
	if !ok {
		go lowmemRemainderTreeLevel(resultChan, channels, finalOutput)
		resultChan <- hold
		return
	}

	fileWriteChan := make(chan *gmp.Int, 1)
	fileReadChan := make(chan *gmp.Int, 1)
	go tmpfileReadWriter(fileWriteChan, fileReadChan, prefix, "product", level)
	fileWriteChan <- hold
	fileWriteChan <- m

	channels = append(channels, fileReadChan)
	go lowmemProductTreeLevel(prefix, level+1, resultChan, channels, finalOutput)
	resultChan <- new(gmp.Int).Mul(hold, m)
	hold = nil

	for m = range input {
		fileWriteChan <- m
		if hold != nil {
			resultChan <- new(gmp.Int).Mul(hold, m)
			hold = nil
		} else {
			hold = m
		}
	}

	close(fileWriteChan)

	if hold != nil {
		resultChan <- hold
	}
}

// For each productTree node 'x', and remainderTree parent 'y', compute y%(x*x)
func lowmemRemainderTreeLevel(input chan *gmp.Int, productTree []chan *gmp.Int, finalOutput chan<- Collision) {
	tmp := new(gmp.Int)
	runtime.GC()
	defer runtime.GC()

	products := productTree[len(productTree)-1]
	productTree = productTree[:len(productTree)-1]
	output := make(chan *gmp.Int, 0)
	defer close(output)

	if len(productTree) == 0 {
		lowmemRemainderTreeFinal(input, products, finalOutput)
		return
	} else {
		go lowmemRemainderTreeLevel(output, productTree, finalOutput)
	}

	for y := range input {
		x, ok := <-products
		if !ok {
			log.Panicf("Expecting more products")
		}
		tmp.Mul(x, x)
		x.Rem(y, tmp)
		output <- x

		x, ok = <-products
		if ok {
			tmp.Mul(x, x)
			x.Rem(y, tmp)
			output <- x
		}
	}
}

// For each input modulus 'x' and remainderTree parent 'y', compute z = (y%(x*x))/x; gcd(z, x)
func lowmemRemainderTreeFinal(input, moduli chan *gmp.Int, output chan<- Collision) {
	defer close(output)
	tmp := new(gmp.Int)

	for y := range input {
		for i := 0; i < 2; i++ {
			modulus, ok := <-moduli
			if !ok {
				log.Print("Odd number of moduli? (should only see this once)")
				continue
			}
			tmp.Mul(modulus, modulus)
			tmp.Rem(y, tmp)
			tmp.Quo(tmp, modulus)
			if tmp.GCD(nil, nil, tmp, modulus).BitLen() != 1 {
				q := new(gmp.Int).Quo(modulus, tmp)
				output <- Collision{
					Modulus: modulus,
					P:       tmp,
					Q:       q,
				}
				tmp = new(gmp.Int)
			}
		}
	}
}

// Implementation of D.J. Bernstein's "How to find smooth parts of integers"
// http://cr.yp.to/papers.html#smoothparts
func LowMemSmoothPartsGCD(moduli chan *gmp.Int, output chan<- Collision) {
	prefix := time.Now().Format(time.RFC3339Nano)
	go lowmemProductTreeLevel(prefix, 1, moduli, nil, output)
}
<commit_msg>Try not to kick off read until ready<commit_after>package batchgcd

// NOTE: This code was written with fastgcd available at https://factorable.net/
// as a reference, which was written by Nadia Heninger and J. Alex Halderman.
// I have put a substantial amount of my own design into this, and they do not
// claim it as a derivative work.
// I thank them for their original code and paper.

import (
	"github.com/ncw/gmp"
	"io"
	"io/ioutil"
	"log"
	"runtime"
	"syscall"
)

type fileChannels struct {
	writeChan chan *gmp.Int
	readChan  chan *gmp.Int
	producing bool
}

func (fch *fileChannels) StartProducing() {
	if fch.producing {
		return
	}
	close(fch.writeChan)
	fch.producing = true
}

func encodeLength(buf []byte, length int) {
	buf[0] = byte(length >> 56)
	buf[1] = byte(length >> 48)
	buf[2] = byte(length >> 40)
	buf[3] = byte(length >> 32)
	buf[4] = byte(length >> 24)
	buf[5] = byte(length >> 16)
	buf[6] = byte(length >> 8)
	buf[7] = byte(length)
}

func decodeLength(buf []byte) int {
	var ret int

	ret |= int(buf[0]) << 56
	ret |= int(buf[1]) << 48
	ret |= int(buf[2]) << 40
	ret |= int(buf[3]) << 32
	ret |= int(buf[4]) << 24
	ret |= int(buf[5]) << 16
	ret |= int(buf[6]) << 8
	ret |= int(buf[7])

	return ret
}

func tmpfileReadWriter(ch fileChannels) {
	tmpFile, err := ioutil.TempFile(".", "product")
	if err != nil {
		log.Panic(err)
	}

	length := make([]byte, 8)

	var writeCount uint64
	for inData := range ch.writeChan {
		buf := inData.Bytes()
		encodeLength(length, len(buf))
		if _, err := tmpFile.Write(length); err != nil {
			log.Panic(err)
		}
		if _, err := tmpFile.Write(buf); err != nil {
			log.Panic(err)
		}
		writeCount += 1
	}

	if newOffset, e := tmpFile.Seek(0, 0); e != nil || newOffset != 0 {
		log.Panic(e)
	}

	var readCount uint64
	m := new(gmp.Int)
	for {
		if _, e := io.ReadFull(tmpFile, length); e != nil {
			if e == io.EOF {
				break
			}
			log.Panic(e)
		}
		buf := make([]byte, decodeLength(length))
		if _, e := io.ReadFull(tmpFile, buf); e != nil {
			log.Panic(e)
		}
		readCount += 1
		ch.readChan <- m.SetBytes(buf)
		m = new(gmp.Int)
	}

	if writeCount != readCount {
		log.Panicf("Didn't write as many as we read: write=%v read=%v", writeCount, readCount)
	}
	close(ch.readChan)
	syscall.Unlink(tmpFile.Name())
	// tmpFile.Truncate(0);
}

// Multiply sets of two adjacent inputs, placing into a single output
func lowmemProductTreeLevel(input chan *gmp.Int, channels []fileChannels, finalOutput chan<- Collision) {
	resultChan := make(chan *gmp.Int, 2)
	defer close(resultChan)

	hold := <-input
	m, ok := <-input
	if !ok {
		go lowmemRemainderTreeLevel(resultChan, channels, finalOutput)
		resultChan <- hold
		return
	}

	fileChans := fileChannels{
		writeChan: make(chan *gmp.Int, 2),
		readChan:  make(chan *gmp.Int, 1),
	}
	go tmpfileReadWriter(fileChans)
	fileChans.writeChan <- hold
	fileChans.writeChan <- m

	channels = append(channels, fileChans)
	go lowmemProductTreeLevel(resultChan, channels, finalOutput)
	resultChan <- new(gmp.Int).Mul(hold, m)
	hold = nil

	for m = range input {
		fileChans.writeChan <- m
		if hold != nil {
			resultChan <- new(gmp.Int).Mul(hold, m)
			hold = nil
		} else {
			hold = m
		}
	}

	if hold != nil {
		resultChan <- hold
	}
}

// For each productTree node 'x', and remainderTree parent 'y', compute y%(x*x)
func lowmemRemainderTreeLevel(input chan *gmp.Int, productTree []fileChannels, finalOutput chan<- Collision) {
	tmp := new(gmp.Int)
	runtime.GC()
	defer runtime.GC()

	ch := productTree[len(productTree)-1]
	productTree = productTree[:len(productTree)-1]

	// We close the fileWriteChannel here so it kicks off reading now, instead of starting too early
	products := ch.readChan

	output := make(chan *gmp.Int, 2)
	defer close(output)

	if len(productTree) == 0 {
		ch.StartProducing()
		lowmemRemainderTreeFinal(input, products, finalOutput)
		return
	} else {
		go lowmemRemainderTreeLevel(output, productTree, finalOutput)
	}

	for y := range input {
		ch.StartProducing()
		x, ok := <-products
		if !ok {
			log.Panicf("Expecting more products")
		}
		tmp.Mul(x, x)
		x.Rem(y, tmp)
		output <- x

		x, ok = <-products
		if ok {
			tmp.Mul(x, x)
			x.Rem(y, tmp)
			output <- x
		}
	}
}

// For each input modulus 'x' and remainderTree parent 'y', compute z = (y%(x*x))/x; gcd(z, x)
func lowmemRemainderTreeFinal(input, moduli chan *gmp.Int, output chan<- Collision) {
	defer close(output)
	tmp := new(gmp.Int)

	for y := range input {
		for i := 0; i < 2; i++ {
			modulus, ok := <-moduli
			if !ok {
				log.Print("Odd number of moduli? (should only see this once)")
				continue
			}
			tmp.Mul(modulus, modulus)
			tmp.Rem(y, tmp)
			tmp.Quo(tmp, modulus)
			if tmp.GCD(nil, nil, tmp, modulus).BitLen() != 1 {
				q := new(gmp.Int).Quo(modulus, tmp)
				output <- Collision{
					Modulus: modulus,
					P:       tmp,
					Q:       q,
				}
				tmp = new(gmp.Int)
			}
		}
	}
}

// Implementation of D.J. Bernstein's "How to find smooth parts of integers"
// http://cr.yp.to/papers.html#smoothparts
func LowMemSmoothPartsGCD(moduli chan *gmp.Int, output chan<- Collision) {
	go lowmemProductTreeLevel(moduli, nil, output)
}
<|endoftext|>


<commit_before>/*
 * Spreed WebRTC.
 * Copyright (C) 2013-2014 struktur AG
 *
 * This file is part of Spreed WebRTC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main

import (
	"errors"
	"fmt"
	"github.com/gorilla/securecookie"
	"strings"
	"sync"
	"time"
)

var sessionNonces *securecookie.SecureCookie

type Session struct {
	SessionManager
	Unicaster
	Broadcaster
	RoomStatusManager
	buddyImages   ImageCache
	Id            string
	Sid           string
	Ua            string
	UpdateRev     uint64
	Status        interface{}
	Nonce         string
	Prio          int
	Hello         bool
	Roomid        string
	mutex         sync.RWMutex
	userid        string
	fake          bool
	stamp         int64
	attestation   *SessionAttestation
	attestations  *securecookie.SecureCookie
	subscriptions map[string]*Session
	subscribers   map[string]*Session
	disconnected  bool
	replaced      bool
}

func NewSession(manager SessionManager, unicaster Unicaster, broadcaster Broadcaster, rooms RoomStatusManager, buddyImages ImageCache, attestations *securecookie.SecureCookie, id, sid string) *Session {

	session := &Session{
		SessionManager:    manager,
		Unicaster:         unicaster,
		Broadcaster:       broadcaster,
		RoomStatusManager: rooms,
		buddyImages:       buddyImages,
		Id:                id,
		Sid:               sid,
		Prio:              100,
		stamp:             time.Now().Unix(),
		attestations:      attestations,
		subscriptions:     make(map[string]*Session),
		subscribers:       make(map[string]*Session),
	}
	session.NewAttestation()
	return session

}

func (s *Session) authenticated() (authenticated bool) {
	authenticated = s.userid != ""
	return
}

func (s *Session) Subscribe(session *Session) {

	s.mutex.Lock()
	s.subscriptions[session.Id] = session
	s.mutex.Unlock()
	session.AddSubscriber(s)

}

func (s *Session) Unsubscribe(id string) {

	s.mutex.Lock()
	if session, ok := s.subscriptions[id]; ok {
		delete(s.subscriptions, id)
		s.mutex.Unlock()
		session.RemoveSubscriber(id)
	} else {
		s.mutex.Unlock()
	}

}

func (s *Session) AddSubscriber(session *Session) {
	s.mutex.Lock()
	s.subscribers[session.Id] = session
	s.mutex.Unlock()
}

func (s *Session) RemoveSubscriber(id string) {
	s.mutex.Lock()
	if _, ok := s.subscribers[id]; ok {
		delete(s.subscribers, id)
	}
	s.mutex.Unlock()
}

func (s *Session) JoinRoom(roomID string, credentials *DataRoomCredentials, sender Sender) (*DataRoom, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.Hello && s.Roomid != roomID {
		s.RoomStatusManager.LeaveRoom(s.Roomid, s.Id)
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Left",
				Id:     s.Id,
				Status: "soft",
			},
		})
	}

	room, err := s.RoomStatusManager.JoinRoom(roomID, credentials, s, s.authenticated(), sender)
	if err == nil {
		s.Hello = true
		s.Roomid = roomID
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Joined",
				Id:     s.Id,
				Userid: s.userid,
				Ua:     s.Ua,
				Prio:   s.Prio,
			},
		})
	} else {
		s.Hello = false
	}
	return room, err
}

func (s *Session) Broadcast(m interface{}) {
	s.mutex.RLock()
	if s.Hello {
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: m,
		})
	}
	s.mutex.RUnlock()
}

func (s *Session) BroadcastStatus() {
	s.mutex.RLock()
	if s.Hello {
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Status",
				Id:     s.Id,
				Userid: s.userid,
				Status: s.Status,
				Rev:    s.UpdateRev,
				Prio:   s.Prio,
			},
		})
	}
	s.mutex.RUnlock()
}

func (s *Session) Unicast(to string, m interface{}) {
	s.mutex.RLock()
	outgoing := &DataOutgoing{
		From: s.Id,
		To:   to,
		A:    s.attestation.Token(),
		Data: m,
	}
	s.mutex.RUnlock()

	s.Unicaster.Unicast(to, outgoing)
}

func (s *Session) Close() {
	s.mutex.Lock()
	if s.disconnected {
		s.mutex.Unlock()
		return
	}

	// TODO(longsleep): Verify that it is ok to not do all this when replaced is true.
	if !s.replaced {

		outgoing := &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Left",
				Id:     s.Id,
				Status: "hard",
			},
		}

		if s.Hello {
			// NOTE(lcooper): If we don't check for Hello here, we could deadlock
			// when implicitly creating a room while a user is reconnecting.
			s.Broadcaster.Broadcast(s.Id, s.Roomid, outgoing)
			s.RoomStatusManager.LeaveRoom(s.Roomid, s.Id)
		}

		for _, session := range s.subscribers {
			s.Unicaster.Unicast(session.Id, outgoing)
		}

		for _, session := range s.subscriptions {
			session.RemoveSubscriber(s.Id)
		}

		s.SessionManager.DestroySession(s.Id, s.userid)
		s.buddyImages.Delete(s.Id)

	}

	s.subscriptions = make(map[string]*Session)
	s.subscribers = make(map[string]*Session)
	s.disconnected = true

	s.mutex.Unlock()
}

func (s *Session) Replace(oldSession *Session) {

	oldSession.mutex.Lock()
	if oldSession.disconnected {
		oldSession.mutex.Unlock()
		return
	}

	s.mutex.Lock()

	s.subscriptions = oldSession.subscriptions
	s.subscribers = oldSession.subscribers

	s.mutex.Unlock()

	// Mark old session as replaced.
	oldSession.replaced = true
	oldSession.mutex.Unlock()

}

func (s *Session) Update(update *SessionUpdate) uint64 {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if update.Status != nil {
		status, ok := update.Status.(map[string]interface{})
		if ok && status["buddyPicture"] != nil {
			pic := status["buddyPicture"].(string)
			if strings.HasPrefix(pic, "data:") {
				imageId := s.buddyImages.Update(s.Id, pic[5:])
				if imageId != "" {
					status["buddyPicture"] = "img:" + imageId
				}
			}
		}
	}

	for _, key := range update.Types {

		//fmt.Println("type update", key)
		switch key {
		case "Ua":
			s.Ua = update.Ua
		case "Status":
			s.Status = update.Status
		case "Prio":
			s.Prio = update.Prio
		}

	}

	s.UpdateRev++
	return s.UpdateRev

}

func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.Id != st.Id || s.Sid != st.Sid {
		return "", errors.New("session id mismatch")
	}
	if s.userid != "" {
		return "", errors.New("session already authenticated")
	}

	// Create authentication nonce.
	var err error
	s.Nonce, err = sessionNonces.Encode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Userid)

	return s.Nonce, err

}

func (s *Session) Authenticate(realm string, st *SessionToken, userid string) error {

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.userid != "" {
		return errors.New("session already authenticated")
	}
	if userid == "" {
		if s.Nonce == "" || s.Nonce != st.Nonce {
			return errors.New("nonce validation failed")
		}
		err := sessionNonces.Decode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Nonce, &userid)
		if err != nil {
			return err
		}
		if st.Userid != userid {
			return errors.New("user id mismatch")
		}
		s.Nonce = ""
	}

	s.userid = userid
	s.stamp = time.Now().Unix()
	s.UpdateRev++
	return nil

}

func (s *Session) Token() *SessionToken {

	s.mutex.RLock()
	defer s.mutex.RUnlock()

	return &SessionToken{Id: s.Id, Sid: s.Sid, Userid: s.userid}
}

func (s *Session) Data() *DataSession {

	s.mutex.RLock()
	defer s.mutex.RUnlock()

	return &DataSession{
		Id:     s.Id,
		Userid: s.userid,
		Ua:     s.Ua,
		Status: s.Status,
		Rev:    s.UpdateRev,
		Prio:   s.Prio,
		stamp:  s.stamp,
	}

}

func (s *Session) Userid() (userid string) {

	s.mutex.RLock()
	userid = s.userid
	s.mutex.RUnlock()
	return

}

func (s *Session) SetUseridFake(userid string) {

	s.mutex.Lock()
	s.userid = userid
	s.fake = true
	s.mutex.Unlock()

}

func (s *Session) NewAttestation() {
	s.attestation = &SessionAttestation{
		s: s,
	}
	s.attestation.Update()
}

func (s *Session) UpdateAttestation() {
	s.mutex.Lock()
	s.attestation.Update()
	s.mutex.Unlock()
}

type SessionUpdate struct {
	Types  []string
	Ua     string
	Prio   int
	Status interface{}
}

type SessionToken struct {
	Id     string // Public session id.
	Sid    string // Secret session id.
	Userid string // Public user id.
	Nonce  string `json:"Nonce,omitempty"` // User autentication nonce.
}

type SessionAttestation struct {
	refresh int64
	token   string
	s       *Session
}

func (sa *SessionAttestation) Update() (string, error) {
	token, err := sa.Encode()
	if err == nil {
		sa.token = token
		sa.refresh = time.Now().Unix() + 180 // expires after 3 minutes
	}
	return token, err
}

func (sa *SessionAttestation) Token() (token string) {
	if sa.refresh < time.Now().Unix() {
		token, _ = sa.Update()
	} else {
		token = sa.token
	}
	return
}

func (sa *SessionAttestation) Encode() (string, error) {
	return sa.s.attestations.Encode("attestation", sa.s.Id)
}

func (sa *SessionAttestation) Decode(token string) (string, error) {
	var id string
	err := sa.s.attestations.Decode("attestation", token, &id)
	return id, err
}

func init() {
	// Create nonce generator.
	sessionNonces = securecookie.New(securecookie.GenerateRandomKey(64), nil)
	sessionNonces.MaxAge(60)
}
<commit_msg>Make sure to send session close messages to subscriptions too to notify both ways.<commit_after>/*
 * Spreed WebRTC.
 * Copyright (C) 2013-2014 struktur AG
 *
 * This file is part of Spreed WebRTC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main

import (
	"errors"
	"fmt"
	"github.com/gorilla/securecookie"
	"strings"
	"sync"
	"time"
)

var sessionNonces *securecookie.SecureCookie

type Session struct {
	SessionManager
	Unicaster
	Broadcaster
	RoomStatusManager
	buddyImages   ImageCache
	Id            string
	Sid           string
	Ua            string
	UpdateRev     uint64
	Status        interface{}
	Nonce         string
	Prio          int
	Hello         bool
	Roomid        string
	mutex         sync.RWMutex
	userid        string
	fake          bool
	stamp         int64
	attestation   *SessionAttestation
	attestations  *securecookie.SecureCookie
	subscriptions map[string]*Session
	subscribers   map[string]*Session
	disconnected  bool
	replaced      bool
}

func NewSession(manager SessionManager, unicaster Unicaster, broadcaster Broadcaster, rooms RoomStatusManager, buddyImages ImageCache, attestations *securecookie.SecureCookie, id, sid string) *Session {

	session := &Session{
		SessionManager:    manager,
		Unicaster:         unicaster,
		Broadcaster:       broadcaster,
		RoomStatusManager: rooms,
		buddyImages:       buddyImages,
		Id:                id,
		Sid:               sid,
		Prio:              100,
		stamp:             time.Now().Unix(),
		attestations:      attestations,
		subscriptions:     make(map[string]*Session),
		subscribers:       make(map[string]*Session),
	}
	session.NewAttestation()
	return session

}

func (s *Session) authenticated() (authenticated bool) {
	authenticated = s.userid != ""
	return
}

func (s *Session) Subscribe(session *Session) {
	s.mutex.Lock()
	s.subscriptions[session.Id] = session
	s.mutex.Unlock()
	session.AddSubscriber(s)
}

func (s *Session) Unsubscribe(id string) {
	s.mutex.Lock()
	if session, ok := s.subscriptions[id]; ok {
		delete(s.subscriptions, id)
		s.mutex.Unlock()
		session.RemoveSubscriber(id)
	} else {
		s.mutex.Unlock()
	}
}

func (s *Session) AddSubscriber(session *Session) {
	s.mutex.Lock()
	s.subscribers[session.Id] = session
	s.mutex.Unlock()
}

func (s *Session) RemoveSubscriber(id string) {
	s.mutex.Lock()
	if _, ok := s.subscribers[id]; ok {
		delete(s.subscribers, id)
	}
	s.mutex.Unlock()
}

func (s *Session) JoinRoom(roomID string, credentials *DataRoomCredentials, sender Sender) (*DataRoom, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.Hello && s.Roomid != roomID {
		s.RoomStatusManager.LeaveRoom(s.Roomid, s.Id)
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Left",
				Id:     s.Id,
				Status: "soft",
			},
		})
	}

	room, err := s.RoomStatusManager.JoinRoom(roomID, credentials, s, s.authenticated(), sender)
	if err == nil {
		s.Hello = true
		s.Roomid = roomID
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Joined",
				Id:     s.Id,
				Userid: s.userid,
				Ua:     s.Ua,
				Prio:   s.Prio,
			},
		})
	} else {
		s.Hello = false
	}
	return room, err
}

func (s *Session) Broadcast(m interface{}) {
	s.mutex.RLock()
	if s.Hello {
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: m,
		})
	}
	s.mutex.RUnlock()
}

func (s *Session) BroadcastStatus() {
	s.mutex.RLock()
	if s.Hello {
		s.Broadcaster.Broadcast(s.Id, s.Roomid, &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Status",
				Id:     s.Id,
				Userid: s.userid,
				Status: s.Status,
				Rev:    s.UpdateRev,
				Prio:   s.Prio,
			},
		})
	}
	s.mutex.RUnlock()
}

func (s *Session) Unicast(to string, m interface{}) {
	s.mutex.RLock()
	outgoing := &DataOutgoing{
		From: s.Id,
		To:   to,
		A:    s.attestation.Token(),
		Data: m,
	}
	s.mutex.RUnlock()

	s.Unicaster.Unicast(to, outgoing)
}

func (s *Session) Close() {
	s.mutex.Lock()
	if s.disconnected {
		s.mutex.Unlock()
		return
	}

	// TODO(longsleep): Verify that it is ok to not do all this when replaced is true.
	if !s.replaced {

		outgoing := &DataOutgoing{
			From: s.Id,
			A:    s.attestation.Token(),
			Data: &DataSession{
				Type:   "Left",
				Id:     s.Id,
				Status: "hard",
			},
		}

		if s.Hello {
			// NOTE(lcooper): If we don't check for Hello here, we could deadlock
			// when implicitly creating a room while a user is reconnecting.
			s.Broadcaster.Broadcast(s.Id, s.Roomid, outgoing)
			s.RoomStatusManager.LeaveRoom(s.Roomid, s.Id)
		}

		for _, session := range s.subscribers {
			s.Unicaster.Unicast(session.Id, outgoing)
		}

		for _, session := range s.subscriptions {
			session.RemoveSubscriber(s.Id)
			s.Unicaster.Unicast(session.Id, outgoing)
		}

		s.SessionManager.DestroySession(s.Id, s.userid)
		s.buddyImages.Delete(s.Id)

	}

	s.subscriptions = make(map[string]*Session)
	s.subscribers = make(map[string]*Session)
	s.disconnected = true

	s.mutex.Unlock()
}

func (s *Session) Replace(oldSession *Session) {

	oldSession.mutex.Lock()
	if oldSession.disconnected {
		oldSession.mutex.Unlock()
		return
	}

	s.mutex.Lock()

	s.subscriptions = oldSession.subscriptions
	s.subscribers = oldSession.subscribers

	s.mutex.Unlock()

	// Mark old session as replaced.
	oldSession.replaced = true
	oldSession.mutex.Unlock()

}

func (s *Session) Update(update *SessionUpdate) uint64 {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if update.Status != nil {
		status, ok := update.Status.(map[string]interface{})
		if ok && status["buddyPicture"] != nil {
			pic := status["buddyPicture"].(string)
			if strings.HasPrefix(pic, "data:") {
				imageId := s.buddyImages.Update(s.Id, pic[5:])
				if imageId != "" {
					status["buddyPicture"] = "img:" + imageId
				}
			}
		}
	}

	for _, key := range update.Types {

		//fmt.Println("type update", key)
		switch key {
		case "Ua":
			s.Ua = update.Ua
		case "Status":
			s.Status = update.Status
		case "Prio":
			s.Prio = update.Prio
		}

	}

	s.UpdateRev++
	return s.UpdateRev

}

func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.Id != st.Id || s.Sid != st.Sid {
		return "", errors.New("session id mismatch")
	}
	if s.userid != "" {
		return "", errors.New("session already authenticated")
	}

	// Create authentication nonce.
	var err error
	s.Nonce, err = sessionNonces.Encode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Userid)

	return s.Nonce, err

}

func (s *Session) Authenticate(realm string, st *SessionToken, userid string) error {

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.userid != "" {
		return errors.New("session already authenticated")
	}
	if userid == "" {
		if s.Nonce == "" || s.Nonce != st.Nonce {
			return errors.New("nonce validation failed")
		}
		err := sessionNonces.Decode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Nonce, &userid)
		if err != nil {
			return err
		}
		if st.Userid != userid {
			return errors.New("user id mismatch")
		}
		s.Nonce = ""
	}

	s.userid = userid
	s.stamp = time.Now().Unix()
	s.UpdateRev++
	return nil

}

func (s *Session) Token() *SessionToken {

	s.mutex.RLock()
	defer s.mutex.RUnlock()

	return &SessionToken{Id: s.Id, Sid: s.Sid, Userid: s.userid}
}

func (s *Session) Data() *DataSession {

	s.mutex.RLock()
	defer s.mutex.RUnlock()

	return &DataSession{
		Id:     s.Id,
		Userid: s.userid,
		Ua:     s.Ua,
		Status: s.Status,
		Rev:    s.UpdateRev,
		Prio:   s.Prio,
		stamp:  s.stamp,
	}

}

func (s *Session) Userid() (userid string) {

	s.mutex.RLock()
	userid = s.userid
	s.mutex.RUnlock()
	return

}

func (s *Session) SetUseridFake(userid string) {

	s.mutex.Lock()
	s.userid = userid
	s.fake = true
	s.mutex.Unlock()

}

func (s *Session) NewAttestation() {
	s.attestation = &SessionAttestation{
		s: s,
	}
	s.attestation.Update()
}

func (s *Session) UpdateAttestation() {
	s.mutex.Lock()
	s.attestation.Update()
	s.mutex.Unlock()
}

type SessionUpdate struct {
	Types  []string
	Ua     string
	Prio   int
	Status interface{}
}

type SessionToken struct {
	Id     string // Public session id.
	Sid    string // Secret session id.
	Userid string // Public user id.
	Nonce  string `json:"Nonce,omitempty"` // User autentication nonce.
}

type SessionAttestation struct {
	refresh int64
	token   string
	s       *Session
}

func (sa *SessionAttestation) Update() (string, error) {
	token, err := sa.Encode()
	if err == nil {
		sa.token = token
		sa.refresh = time.Now().Unix() + 180 // expires after 3 minutes
	}
	return token, err
}

func (sa *SessionAttestation) Token() (token string) {
	if sa.refresh < time.Now().Unix() {
		token, _ = sa.Update()
	} else {
		token = sa.token
	}
	return
}

func (sa *SessionAttestation) Encode() (string, error) {
	return sa.s.attestations.Encode("attestation", sa.s.Id)
}

func (sa *SessionAttestation) Decode(token string) (string, error) {
	var id string
	err := sa.s.attestations.Decode("attestation", token, &id)
	return id, err
}

func init() {
	// Create nonce generator.
	sessionNonces = securecookie.New(securecookie.GenerateRandomKey(64), nil)
	sessionNonces.MaxAge(60)
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package expression

import (
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	mysql "github.com/pingcap/tidb/mysqldef"
	"github.com/pingcap/tidb/util/types"
)

// Extract is for time extract function.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract
type Extract struct {
	Unit string
	Date Expression
}

// Clone implements the Expression Clone interface.
func (e *Extract) Clone() Expression {
	n := *e
	return &n
}

// Eval implements the Expression Eval interface.
func (e *Extract) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) {
	v, err := e.Date.Eval(ctx, args)
	if v == nil || err != nil {
		return nil, errors.Trace(err)
	}

	f := types.NewFieldType(mysql.TypeDatetime)
	f.Decimal = mysql.MaxFsp

	v, err = types.Convert(v, f)
	if v == nil || err != nil {
		return nil, errors.Trace(err)
	}

	t, ok := v.(mysql.Time)
	if !ok {
		return nil, errors.Errorf("need time type, but got %T", v)
	}

	n, err1 := extractTime(e.Unit, t)
	if err1 != nil {
		return nil, errors.Trace(err1)
	}

	return int64(n), nil
}

// IsStatic implements the Expression IsStatic interface.
func (e *Extract) IsStatic() bool {
	return e.Date.IsStatic()
}

// String implements the Expression String interface.
func (e *Extract) String() string {
	return fmt.Sprintf("EXTRACT(%s FROM %s)", strings.ToUpper(e.Unit), e.Date)
}

// Accept implements the Visitor Accept interface.
func (e *Extract) Accept(v Visitor) (Expression, error) {
	return v.VisitExtract(e)
}

func extractTime(unit string, t mysql.Time) (int, error) {
	switch strings.ToUpper(unit) {
	case "MICROSECOND":
		return t.Nanosecond() / 1000, nil
	case "SECOND":
		return t.Second(), nil
	case "MINUTE":
		return t.Minute(), nil
	case "HOUR":
		return t.Hour(), nil
	case "DAY":
		return t.Day(), nil
	case "WEEK":
		_, week := t.ISOWeek()
		return week, nil
	case "MONTH":
		return int(t.Month()), nil
	case "QUARTER":
		m := int(t.Month())
		// 1 - 3 -> 1
		// 4 - 6 -> 2
		// 7 - 9 -> 3
		// 10 - 12 -> 4
		return (m + 2) / 3, nil
	case "YEAR":
		return t.Year(), nil
	case "SECOND_MICROSECOND":
		return t.Second()*1000000 + t.Nanosecond()/1000, nil
	case "MINUTE_MICROSECOND":
		_, m, s := t.Clock()
		return m*100000000 + s*1000000 + t.Nanosecond()/1000, nil
	case "MINUTE_SECOND":
		_, m, s := t.Clock()
		return m*100 + s, nil
	case "HOUR_MICROSECOND":
		h, m, s := t.Clock()
		return h*10000000000 + m*100000000 + s*1000000 + t.Nanosecond()/1000, nil
	case "HOUR_SECOND":
		h, m, s := t.Clock()
		return h*10000 + m*100 + s, nil
	case "HOUR_MINUTE":
		h, m, _ := t.Clock()
		return h*100 + m, nil
	case "DAY_MICROSECOND":
		h, m, s := t.Clock()
		d := t.Day()
		return (d*1000000+h*10000+m*100+s)*1000000 + t.Nanosecond()/1000, nil
	case "DAY_SECOND":
		h, m, s := t.Clock()
		d := t.Day()
		return d*1000000 + h*10000 + m*100 + s, nil
	case "DAY_MINUTE":
		h, m, _ := t.Clock()
		d := t.Day()
		return d*10000 + h*100 + m, nil
	case "DAY_HOUR":
		h, _, _ := t.Clock()
		d := t.Day()
		return d*100 + h, nil
	case "YEAR_MONTH":
		y, m, _ := t.Date()
		return y*100 + int(m), nil
	default:
		return 0, errors.Errorf("invalid unit %s", unit)
	}
}
<commit_msg>use int64 instead int type,for x32 compilation<commit_after>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package expression

import (
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	mysql "github.com/pingcap/tidb/mysqldef"
	"github.com/pingcap/tidb/util/types"
)

// Extract is for time extract function.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_extract
type Extract struct {
	Unit string
	Date Expression
}

// Clone implements the Expression Clone interface.
func (e *Extract) Clone() Expression {
	n := *e
	return &n
}

// Eval implements the Expression Eval interface.
func (e *Extract) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) {
	v, err := e.Date.Eval(ctx, args)
	if v == nil || err != nil {
		return nil, errors.Trace(err)
	}

	f := types.NewFieldType(mysql.TypeDatetime)
	f.Decimal = mysql.MaxFsp

	v, err = types.Convert(v, f)
	if v == nil || err != nil {
		return nil, errors.Trace(err)
	}

	t, ok := v.(mysql.Time)
	if !ok {
		return nil, errors.Errorf("need time type, but got %T", v)
	}

	n, err1 := extractTime(e.Unit, t)
	if err1 != nil {
		return nil, errors.Trace(err1)
	}

	return n, nil
}

// IsStatic implements the Expression IsStatic interface.
func (e *Extract) IsStatic() bool {
	return e.Date.IsStatic()
}

// String implements the Expression String interface.
func (e *Extract) String() string {
	return fmt.Sprintf("EXTRACT(%s FROM %s)", strings.ToUpper(e.Unit), e.Date)
}

// Accept implements the Visitor Accept interface.
func (e *Extract) Accept(v Visitor) (Expression, error) {
	return v.VisitExtract(e)
}

func extractTime(unit string, t mysql.Time) (int64, error) {
	switch strings.ToUpper(unit) {
	case "MICROSECOND":
		return int64(t.Nanosecond() / 1000), nil
	case "SECOND":
		return int64(t.Second()), nil
	case "MINUTE":
		return int64(t.Minute()), nil
	case "HOUR":
		return int64(t.Hour()), nil
	case "DAY":
		return int64(t.Day()), nil
	case "WEEK":
		_, week := t.ISOWeek()
		return int64(week), nil
	case "MONTH":
		return int64(t.Month()), nil
	case "QUARTER":
		m := int64(t.Month())
		// 1 - 3 -> 1
		// 4 - 6 -> 2
		// 7 - 9 -> 3
		// 10 - 12 -> 4
		return (m + 2) / 3, nil
	case "YEAR":
		return int64(t.Year()), nil
	case "SECOND_MICROSECOND":
		return int64(t.Second())*1000000 + int64(t.Nanosecond())/1000, nil
	case "MINUTE_MICROSECOND":
		_, m, s := t.Clock()
		return int64(m)*100000000 + int64(s)*1000000 + int64(t.Nanosecond())/1000, nil
	case "MINUTE_SECOND":
		_, m, s := t.Clock()
		return int64(m*100 + s), nil
	case "HOUR_MICROSECOND":
		h, m, s := t.Clock()
		return int64(h)*10000000000 + int64(m)*100000000 + int64(s)*1000000 + int64(t.Nanosecond())/1000, nil
	case "HOUR_SECOND":
		h, m, s := t.Clock()
		return int64(h)*10000 + int64(m)*100 + int64(s), nil
	case "HOUR_MINUTE":
		h, m, _ := t.Clock()
		return int64(h)*100 + int64(m), nil
	case "DAY_MICROSECOND":
		h, m, s := t.Clock()
		d := t.Day()
		return int64(d*1000000+h*10000+m*100+s)*1000000 + int64(t.Nanosecond())/1000, nil
	case "DAY_SECOND":
		h, m, s := t.Clock()
		d := t.Day()
		return int64(d)*1000000 + int64(h)*10000 + int64(m)*100 + int64(s), nil
	case "DAY_MINUTE":
		h, m, _ := t.Clock()
		d := t.Day()
		return int64(d)*10000 + int64(h)*100 + int64(m), nil
	case "DAY_HOUR":
		h, _, _ := t.Clock()
		d := t.Day()
		return int64(d)*100 + int64(h), nil
	case "YEAR_MONTH":
		y, m, _ := t.Date()
		return int64(y)*100 + int64(m), nil
	default:
		return 0, errors.Errorf("invalid unit %s", unit)
	}
}
<|endoftext|>


<commit_before>package TF2RconWrapper

import (
	"bytes"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
)

type EventListener struct {
	PlayerConnected      func(PlayerData)
	PlayerDisconnected   func(PlayerData)
	PlayerGlobalMessage  func(PlayerData, string) // strings are chat message
	PlayerTeamMessage    func(PlayerData, string)
	PlayerSpawned        func(PlayerData, string) // string is class
	PlayerClassChanged   func(PlayerData, string) // string is new classes
	PlayerTeamChange     func(PlayerData, string) // string is new team
	PlayerKilled         func(PlayerKill)
	PlayerDamaged        func(PlayerDamage)
	PlayerHealed         func(PlayerHeal)
	PlayerKilledMedic    func(PlayerTrigger)
	PlayerUberFinished   func(PlayerData)
	PlayerBlockedCapture func(CPData, PlayerData) // cp blocked by player
	PlayerItemPickup     func(ItemPickup)
	TeamPointCapture     func(TeamData)
	TeamScoreUpdate      func(TeamData)
	GameOver             func()
	WorldRoundWin        func(string) // string is team which won
	CVarChange           func(variable string, value string)
	LogFileClosed        func()
	TournamentStarted    func()
}

type Listener struct {
	mapMu    *sync.RWMutex
	sources  map[string]*Source
	channels map[string](chan string)

	listenAddr   *net.UDPAddr
	redirectAddr string
}

type Source struct {
	Secret string
	logsMu *sync.RWMutex //protects logs
	logs   *bytes.Buffer

	handlerMu *sync.Mutex //protects handler and closed
	handler   *EventListener
	closed    bool
}

func (s *Source) Logs() *bytes.Buffer {
	return s.logs
}

// NewListener returns a new Listener
func NewListener(addr string) (*Listener, error) {
	return NewListenerAddr(addr, addr)
}

func NewListenerAddr(port, redirectAddr string) (*Listener, error) {
	addr, err := net.ResolveUDPAddr("udp", ":"+port)
	if err != nil {
		return nil, err
	}

	l := &Listener{
		mapMu:    new(sync.RWMutex),
		sources:  make(map[string]*Source),
		channels: make(map[string](chan string)),

		listenAddr:   addr,
		redirectAddr: redirectAddr,
	}

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

	go l.start(conn)
	return l, nil
}

func (l *Listener) RemoveSource(s *Source, m *TF2RconConnection) {
	s.handlerMu.Lock()
	s.closed = true
	s.handlerMu.Unlock()

	l.mapMu.Lock()
	delete(l.sources, s.Secret)
	l.mapMu.Unlock()

	m.StopLogRedirection(l.redirectAddr)
}

func (l *Listener) start(conn *net.UDPConn) {
	for {
		buff := make([]byte, 512)
		n, err := conn.Read(buff)
		if err != nil {
			log.Println(err)
		}

		secret, Lpos, err := getSecret(buff[0:n])
		if err != nil {
			continue
		}

		l.mapMu.RLock()
		source, ok := l.sources[secret]
		l.mapMu.RUnlock()

		if !ok {
			continue
		}

		go func() {
			source.handlerMu.Lock()
			defer source.handlerMu.Unlock()

			if source.closed {
				return
			}

			source.logsMu.Lock()
			source.logs.Write(buff[Lpos : n-1])
			source.logsMu.Unlock()

			handler := source.handler

			m := ParseLogEntry(string(buff[Lpos : n-2]))

			m.Parsed.CallHandler(handler)
		}()
	}
}

func (l *Listener) AddSource(handler *EventListener, m *TF2RconConnection) *Source {
	secret := strconv.FormatUint(uint64(rand.Int63()+1), 10)
	rand.Seed(time.Now().Unix())

	l.mapMu.RLock()
	_, ok := l.sources[secret]
	for ok {
		secret = strconv.Itoa(100000 + rand.Intn(800000))
		_, ok = l.sources[secret]
	}
	l.mapMu.RUnlock()

	return l.AddSourceSecret(secret, handler, m)
}

func (l *Listener) AddSourceSecret(secret string, handler *EventListener, m *TF2RconConnection) *Source {
	s := newSource(secret, handler)

	l.mapMu.Lock()
	l.sources[secret] = s
	l.mapMu.Unlock()

	m.Query("sv_logsecret " + secret)
	m.RedirectLogs(l.redirectAddr)
	return s
}

func newSource(secret string, handler *EventListener) *Source {
	return &Source{
		Secret: secret,
		logsMu: new(sync.RWMutex),
		logs:   new(bytes.Buffer),

		handlerMu: new(sync.Mutex),
		handler:   handler,
	}
}
<commit_msg>add print argument to NewListener(Addr)<commit_after>package TF2RconWrapper

import (
	"bytes"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
)

type EventListener struct {
	PlayerConnected      func(PlayerData)
	PlayerDisconnected   func(PlayerData)
	PlayerGlobalMessage  func(PlayerData, string) // strings are chat message
	PlayerTeamMessage    func(PlayerData, string)
	PlayerSpawned        func(PlayerData, string) // string is class
	PlayerClassChanged   func(PlayerData, string) // string is new classes
	PlayerTeamChange     func(PlayerData, string) // string is new team
	PlayerKilled         func(PlayerKill)
	PlayerDamaged        func(PlayerDamage)
	PlayerHealed         func(PlayerHeal)
	PlayerKilledMedic    func(PlayerTrigger)
	PlayerUberFinished   func(PlayerData)
	PlayerBlockedCapture func(CPData, PlayerData) // cp blocked by player
	PlayerItemPickup     func(ItemPickup)
	TeamPointCapture     func(TeamData)
	TeamScoreUpdate      func(TeamData)
	GameOver             func()
	WorldRoundWin        func(string) // string is team which won
	CVarChange           func(variable string, value string)
	LogFileClosed        func()
	TournamentStarted    func()
}

type Listener struct {
	mapMu    *sync.RWMutex
	sources  map[string]*Source
	channels map[string](chan string)

	listenAddr   *net.UDPAddr
	redirectAddr string
	print        bool
}

type Source struct {
	Secret string
	logsMu *sync.RWMutex //protects logs
	logs   *bytes.Buffer

	handlerMu *sync.Mutex //protects handler and closed
	handler   *EventListener
	closed    bool
}

func (s *Source) Logs() *bytes.Buffer {
	return s.logs
}

// NewListener returns a new Listener
func NewListener(addr string, print bool) (*Listener, error) {
	return NewListenerAddr(addr, addr, print)
}

func NewListenerAddr(port, redirectAddr string, print bool) (*Listener, error) {
	addr, err := net.ResolveUDPAddr("udp", ":"+port)
	if err != nil {
		return nil, err
	}

	l := &Listener{
		mapMu:    new(sync.RWMutex),
		sources:  make(map[string]*Source),
		channels: make(map[string](chan string)),

		listenAddr:   addr,
		redirectAddr: redirectAddr,
		print:        print,
	}

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

	go l.start(conn)
	return l, nil
}

func (l *Listener) RemoveSource(s *Source, m *TF2RconConnection) {
	s.handlerMu.Lock()
	s.closed = true
	s.handlerMu.Unlock()

	l.mapMu.Lock()
	delete(l.sources, s.Secret)
	l.mapMu.Unlock()

	m.StopLogRedirection(l.redirectAddr)
}

func (l *Listener) start(conn *net.UDPConn) {
	for {
		buff := make([]byte, 512)
		n, err := conn.Read(buff)
		if err != nil {
			log.Println(err)
		}

		secret, Lpos, err := getSecret(buff[0:n])
		if err != nil {
			continue
		}

		if l.print {
			log.Println(string(buff[0 : n-1]))
		}

		l.mapMu.RLock()
		source, ok := l.sources[secret]
		l.mapMu.RUnlock()

		if !ok {
			continue
		}

		go func() {
			source.handlerMu.Lock()
			defer source.handlerMu.Unlock()

			if source.closed {
				return
			}

			source.logsMu.Lock()
			source.logs.Write(buff[Lpos : n-1])
			source.logsMu.Unlock()

			handler := source.handler

			m := ParseLogEntry(string(buff[Lpos : n-2]))

			m.Parsed.CallHandler(handler)
		}()
	}
}

func (l *Listener) AddSource(handler *EventListener, m *TF2RconConnection) *Source {
	secret := strconv.FormatUint(uint64(rand.Int63()+1), 10)
	rand.Seed(time.Now().Unix())

	l.mapMu.RLock()
	_, ok := l.sources[secret]
	for ok {
		secret = strconv.Itoa(100000 + rand.Intn(800000))
		_, ok = l.sources[secret]
	}
	l.mapMu.RUnlock()

	return l.AddSourceSecret(secret, handler, m)
}

func (l *Listener) AddSourceSecret(secret string, handler *EventListener, m *TF2RconConnection) *Source {
	s := newSource(secret, handler)

	l.mapMu.Lock()
	l.sources[secret] = s
	l.mapMu.Unlock()

	m.Query("sv_logsecret " + secret)
	m.RedirectLogs(l.redirectAddr)
	return s
}

func newSource(secret string, handler *EventListener) *Source {
	return &Source{
		Secret: secret,
		logsMu: new(sync.RWMutex),
		logs:   new(bytes.Buffer),

		handlerMu: new(sync.Mutex),
		handler:   handler,
	}
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

package logs

import (
	"bytes"
	"encoding/xml"
	"io"
	"log"
	"testing"

	"github.com/issue9/assert"

	"github.com/issue9/logs/v3/config"
	"github.com/issue9/logs/v3/internal/initfunc"
)

var (
	debugW    = new(bytes.Buffer)
	infoW     = new(bytes.Buffer)
	errorW    = new(bytes.Buffer)
	traceW    = new(bytes.Buffer)
	warnW     = new(bytes.Buffer)
	criticalW = new(bytes.Buffer)
)

func initLogs(logs *Logs, a *assert.Assertion) {
	infoW.Reset()
	debugW.Reset()
	errorW.Reset()
	traceW.Reset()
	warnW.Reset()
	criticalW.Reset()

	a.True(infoW.Len() == 0)
	a.True(debugW.Len() == 0)
	a.True(errorW.Len() == 0)
	a.True(traceW.Len() == 0)
	a.True(warnW.Len() == 0)
	a.True(criticalW.Len() == 0)

	a.NotError(logs.SetOutput(LevelInfo, infoW))
	a.NotError(logs.SetOutput(LevelDebug, debugW))
	a.NotError(logs.SetOutput(LevelError, errorW))
	a.NotError(logs.SetOutput(LevelTrace, traceW))
	a.NotError(logs.SetOutput(LevelWarn, warnW))
	a.NotError(logs.SetOutput(LevelCritical, criticalW))
}

func checkLog(a *assert.Assertion) {
	a.True(infoW.Len() > 0)
	a.True(debugW.Len() > 0)
	a.True(errorW.Len() > 0)
	a.True(traceW.Len() > 0)
	a.True(warnW.Len() > 0)
	a.True(criticalW.Len() > 0)
}

func TestLogs_All(t *testing.T) {
	a := assert.New(t)

	l, err := New(nil)
	a.NotError(err).NotNil(l)

	initLogs(l, a)
	l.All("abc")
	checkLog(a)
}

func TestLogs_Allf(t *testing.T) {
	a := assert.New(t)

	l, err := New(nil)
	a.NotError(err).NotNil(l)

	initLogs(l, a)
	l.Allf("abc")
	checkLog(a)
}

func debugWInit(*config.Config) (io.Writer, error) {
	return debugW, nil
}

func TestNew(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	// 重新注册以下用到的 writer
	clearInitializer()
	a.True(Register("debug", loggerInitializer(LevelDebug)), "注册 debug 时失败")
	a.True(Register("buffer", initfunc.Buffer), "注册 buffer 时失败")
	a.True(Register("debugW", debugWInit), "注册 debugW 时失败")

	data := `
<?xml version="1.0" encoding="utf-8" ?>
<logs>
	<debug prefix="[DEBUG]">
		<buffer size="10">
			<debugW />
		</buffer>
	</debug>
</logs>
`
	debugW.Reset()

	cfg := &config.Config{}
	a.NotError(xml.Unmarshal([]byte(data), cfg))
	l, err = New(cfg)
	a.NotError(err).NotNil(l)

	l.Debug("abc")
	a.True(debugW.Len() == 0, "assert.True 失败，实际值为%d", debugW.Len()) // 缓存未达 10，依然为空
	l.Allf("def\n")
	a.True(debugW.Len() == 0, "assert.True 失败，实际值为%d", debugW.Len()) // 缓存未达 10，依然为空

	// 测试 Flush
	a.NotError(l.Flush())
	a.True(debugW.Len() > 0)
}

func TestLogs_SetOutput(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	l.SetOutput(0, nil) // 无任何操作发生

	a.NotError(l.SetOutput(LevelError, &bytes.Buffer{}))
	a.Equal(l.logs(LevelError)[0].container.Len(), 1)
	a.NotError(l.SetOutput(LevelError, nil))
	a.Equal(l.logs(LevelError)[0].container.Len(), 0)
}

func TestLogs_SetFlags(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	l.SetFlags(LevelAll, log.Ldate)
	for _, item := range l.loggers {
		a.Equal(item.Flags(), log.Ldate)
	}

	l.SetFlags(LevelAll, log.Lmsgprefix)
	for _, item := range l.loggers {
		a.Equal(item.Flags(), log.Lmsgprefix)
	}
}

func TestLogs_SetPrefix(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	l.SetPrefix(LevelAll, "p")
	for _, item := range l.loggers {
		a.Equal(item.Prefix(), "p")
	}

	l.SetPrefix(LevelAll, "")
	for _, item := range l.loggers {
		a.Equal(item.Prefix(), "")
	}
}

func TestLogs_Panicf(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	initLogs(l, a)

	l.Error("error")
	a.True(errorW.Len() > 0)
	a.Equal(debugW.Len(), 0)

	a.Panic(func() {
		l.Panicf(LevelError, "panic")
	})

	a.True(infoW.Len() == 0)
	a.True(errorW.Len() > 0)
}
<commit_msg>test: 调整测试用例<commit_after>// SPDX-License-Identifier: MIT

package logs

import (
	"bytes"
	"encoding/xml"
	"io"
	"log"
	"testing"

	"github.com/issue9/assert"

	"github.com/issue9/logs/v3/config"
	"github.com/issue9/logs/v3/internal/initfunc"
)

type testLogs struct {
	logs                                     *Logs
	info, debug, erro, trace, warn, critical *bytes.Buffer
}

func newLogs(a *assert.Assertion) *testLogs {
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	debug := new(bytes.Buffer)
	info := new(bytes.Buffer)
	erro := new(bytes.Buffer)
	trace := new(bytes.Buffer)
	warn := new(bytes.Buffer)
	critical := new(bytes.Buffer)

	a.NotError(l.SetOutput(LevelInfo, info))
	a.NotError(l.SetOutput(LevelDebug, debug))
	a.NotError(l.SetOutput(LevelError, erro))
	a.NotError(l.SetOutput(LevelTrace, trace))
	a.NotError(l.SetOutput(LevelWarn, warn))
	a.NotError(l.SetOutput(LevelCritical, critical))

	return &testLogs{
		logs:     l,
		info:     info,
		debug:    debug,
		erro:     erro,
		trace:    trace,
		warn:     warn,
		critical: critical,
	}
}

func (l *testLogs) checkLog(a *assert.Assertion) {
	a.True(l.info.Len() > 0)
	a.True(l.debug.Len() > 0)
	a.True(l.erro.Len() > 0)
	a.True(l.trace.Len() > 0)
	a.True(l.warn.Len() > 0)
	a.True(l.critical.Len() > 0)
}

func TestLogs_All(t *testing.T) {
	a := assert.New(t)

	l := newLogs(a)
	l.logs.All("abc")
	l.checkLog(a)
}

func TestLogs_Allf(t *testing.T) {
	a := assert.New(t)

	l := newLogs(a)
	l.logs.Allf("abc")
	l.checkLog(a)
}

func TestNew(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)
	debugW := new(bytes.Buffer)

	debugWInit := func(*config.Config) (io.Writer, error) {
		return debugW, nil
	}

	// 重新注册以下用到的 writer
	clearInitializer()
	a.True(Register("debug", loggerInitializer(LevelDebug)), "注册 debug 时失败")
	a.True(Register("buffer", initfunc.Buffer), "注册 buffer 时失败")
	a.True(Register("debugW", debugWInit), "注册 debugW 时失败")

	data := `
<?xml version="1.0" encoding="utf-8" ?>
<logs>
	<debug prefix="[DEBUG]">
		<buffer size="10">
			<debugW />
		</buffer>
	</debug>
</logs>
`
	debugW.Reset()

	cfg := &config.Config{}
	a.NotError(xml.Unmarshal([]byte(data), cfg))
	l, err = New(cfg)
	a.NotError(err).NotNil(l)

	l.Debug("abc")
	a.True(debugW.Len() == 0, "assert.True 失败，实际值为%d", debugW.Len()) // 缓存未达 10，依然为空
	l.Allf("def\n")
	a.True(debugW.Len() == 0, "assert.True 失败，实际值为%d", debugW.Len()) // 缓存未达 10，依然为空

	// 测试 Flush
	a.NotError(l.Flush())
	a.True(debugW.Len() > 0)
}

func TestLogs_SetOutput(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	l.SetOutput(0, nil) // 无任何操作发生

	a.NotError(l.SetOutput(LevelError, &bytes.Buffer{}))
	a.Equal(l.logs(LevelError)[0].container.Len(), 1)
	a.NotError(l.SetOutput(LevelError, nil))
	a.Equal(l.logs(LevelError)[0].container.Len(), 0)
}

func TestLogs_SetFlags(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	l.SetFlags(LevelAll, log.Ldate)
	for _, item := range l.loggers {
		a.Equal(item.Flags(), log.Ldate)
	}

	l.SetFlags(LevelAll, log.Lmsgprefix)
	for _, item := range l.loggers {
		a.Equal(item.Flags(), log.Lmsgprefix)
	}
}

func TestLogs_SetPrefix(t *testing.T) {
	a := assert.New(t)
	l, err := New(nil)
	a.NotError(err).NotNil(l)

	l.SetPrefix(LevelAll, "p")
	for _, item := range l.loggers {
		a.Equal(item.Prefix(), "p")
	}

	l.SetPrefix(LevelAll, "")
	for _, item := range l.loggers {
		a.Equal(item.Prefix(), "")
	}
}

func TestLogs_Panicf(t *testing.T) {
	a := assert.New(t)

	l := newLogs(a)

	l.logs.Error("error")
	a.True(l.erro.Len() > 0)
	a.Equal(l.debug.Len(), 0)

	a.Panic(func() {
		l.logs.Panicf(LevelError, "panic")
	})

	a.True(l.info.Len() == 0)
	a.True(l.erro.Len() > 0)

	// panic

	l = newLogs(a)

	a.Panic(func() {
		l.logs.Panic(LevelError, "panic")
	})

	a.True(l.info.Len() == 0)
	a.Contains(l.erro.String(), "panic")
}
<|endoftext|>


<commit_before>package loop // import "fknsrs.biz/p/ottoext/loop"

import (
	"fmt"
	"sync"
	"sync/atomic"

	"github.com/robertkrimen/otto"
)

func formatTask(t Task) string {
	if t == nil {
		return "<nil>"
	}

	return fmt.Sprintf("<%T> %d", t, t.GetID())
}

type Task interface {
	SetID(id int64)
	GetID() int64
	Execute(vm *otto.Otto, l *Loop) error
	Cancel()
}

type Loop struct {
	vm     *otto.Otto
	id     int64
	lock   sync.RWMutex
	tasks  map[int64]Task
	ready  chan Task
	closed bool
}

func New(vm *otto.Otto) *Loop {
	return NewWithBacklog(vm, 0)
}

func NewWithBacklog(vm *otto.Otto, backlog int) *Loop {
	return &Loop{
		vm:    vm,
		tasks: make(map[int64]Task),
		ready: make(chan Task, backlog),
	}
}

func (l *Loop) Add(t Task) {
	l.lock.Lock()
	t.SetID(atomic.AddInt64(&l.id, 1))
	l.tasks[t.GetID()] = t
	l.lock.Unlock()
}

func (l *Loop) Remove(t Task) {
	l.remove(t)
	go l.Ready(nil)
}

func (l *Loop) remove(t Task) {
	l.removeByID(t.GetID())
}

func (l *Loop) removeByID(id int64) {
	l.lock.Lock()
	delete(l.tasks, id)
	l.lock.Unlock()
}

func (l *Loop) Ready(t Task) {
	if l.closed {
		return
	}

	l.ready <- t
}

func (l *Loop) EvalAndRun(s interface{}) error {
	if err := l.Eval(s); err != nil {
		return err
	}

	return l.Run()
}

func (l *Loop) Eval(s interface{}) error {
	if _, err := l.vm.Run(s); err != nil {
		return err
	}

	return nil
}

func (l *Loop) processTask(t Task) error {
	id := t.GetID()

	if err := t.Execute(l.vm, l); err != nil {
		l.lock.RLock()
		for _, t := range l.tasks {
			t.Cancel()
		}
		l.lock.RUnlock()

		return err
	}

	l.removeByID(id)

	return nil
}

func (l *Loop) Run() error {
	for {
		t := <-l.ready

		if t != nil {
			if err := l.processTask(t); err != nil {
				return err
			}
		}

		l.lock.Lock()
		if len(l.tasks) == 0 {
			// prevent any more tasks entering the ready channel
			l.closed = true

			l.lock.Unlock()

			break
		}
		l.lock.Unlock()
	}

	// drain ready channel of any existing tasks
outer:
	for {
		select {
		case t := <-l.ready:
			if err := l.processTask(t); err != nil {
				return err
			}
		default:
			break outer
		}
	}

	close(l.ready)

	return nil
}
<commit_msg>don't block Run() if there are no tasks at start time<commit_after>package loop // import "fknsrs.biz/p/ottoext/loop"

import (
	"fmt"
	"sync"
	"sync/atomic"

	"github.com/robertkrimen/otto"
)

func formatTask(t Task) string {
	if t == nil {
		return "<nil>"
	}

	return fmt.Sprintf("<%T> %d", t, t.GetID())
}

type Task interface {
	SetID(id int64)
	GetID() int64
	Execute(vm *otto.Otto, l *Loop) error
	Cancel()
}

type Loop struct {
	vm     *otto.Otto
	id     int64
	lock   sync.RWMutex
	tasks  map[int64]Task
	ready  chan Task
	closed bool
}

func New(vm *otto.Otto) *Loop {
	return NewWithBacklog(vm, 0)
}

func NewWithBacklog(vm *otto.Otto, backlog int) *Loop {
	return &Loop{
		vm:    vm,
		tasks: make(map[int64]Task),
		ready: make(chan Task, backlog),
	}
}

func (l *Loop) Add(t Task) {
	l.lock.Lock()
	t.SetID(atomic.AddInt64(&l.id, 1))
	l.tasks[t.GetID()] = t
	l.lock.Unlock()
}

func (l *Loop) Remove(t Task) {
	l.remove(t)
	go l.Ready(nil)
}

func (l *Loop) remove(t Task) {
	l.removeByID(t.GetID())
}

func (l *Loop) removeByID(id int64) {
	l.lock.Lock()
	delete(l.tasks, id)
	l.lock.Unlock()
}

func (l *Loop) Ready(t Task) {
	if l.closed {
		return
	}

	l.ready <- t
}

func (l *Loop) EvalAndRun(s interface{}) error {
	if err := l.Eval(s); err != nil {
		return err
	}

	return l.Run()
}

func (l *Loop) Eval(s interface{}) error {
	if _, err := l.vm.Run(s); err != nil {
		return err
	}

	return nil
}

func (l *Loop) processTask(t Task) error {
	id := t.GetID()

	if err := t.Execute(l.vm, l); err != nil {
		l.lock.RLock()
		for _, t := range l.tasks {
			t.Cancel()
		}
		l.lock.RUnlock()

		return err
	}

	l.removeByID(id)

	return nil
}

func (l *Loop) Run() error {
	for {
		l.lock.Lock()
		if len(l.tasks) == 0 {
			// prevent any more tasks entering the ready channel
			l.closed = true

			l.lock.Unlock()

			break
		}
		l.lock.Unlock()

		t := <-l.ready

		if t != nil {
			if err := l.processTask(t); err != nil {
				return err
			}
		}
	}

	// drain ready channel of any existing tasks
outer:
	for {
		select {
		case t := <-l.ready:
			if err := l.processTask(t); err != nil {
				return err
			}
		default:
			break outer
		}
	}

	close(l.ready)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"runtime"
	"strings"

	"github.com/cloudfoundry/cli/cf"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/configuration/plugin_config"
	"github.com/cloudfoundry/cli/cf/help"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/net"
	"github.com/cloudfoundry/cli/cf/panic_printer"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/commands_loader"
	"github.com/cloudfoundry/cli/flags"
	"github.com/cloudfoundry/cli/plugin/rpc"
)

var deps = command_registry.NewDependency()

var cmdRegistry = command_registry.Commands

func main() {
	defer handlePanics(deps.TeePrinter)
	defer deps.Config.Close()

	//handles `cf` | `cf -h` || `cf -help`
	if len(os.Args) == 1 || os.Args[1] == "--help" || os.Args[1] == "-help" ||
		os.Args[1] == "--h" || os.Args[1] == "-h" {
		help.ShowHelp(help.GetHelpTemplate())
		os.Exit(0)
	}

	//handle `cf -v` for cf version
	if len(os.Args) == 2 && (os.Args[1] == "-v" || os.Args[1] == "--version") {
		deps.Ui.Say(os.Args[0] + " version " + cf.Version + "-" + cf.BuiltOnDate)
		os.Exit(0)
	}

	//handle `cf --build`
	if len(os.Args) == 2 && (os.Args[1] == "--build" || os.Args[1] == "-b") {
		deps.Ui.Say(T("{{.CFName}} was built with Go version: {{.GoVersion}}",
			map[string]interface{}{
				"CFName":    os.Args[0],
				"GoVersion": runtime.Version(),
			}))
		os.Exit(0)
	}

	//handles `cf [COMMAND] -h ...`
	//rearrage args to `cf help COMMAND` and let `command help` to print out usage
	if requestHelp(os.Args[2:]) {
		os.Args[2] = os.Args[1]
		os.Args[1] = "help"
	}

	warningProducers := []net.WarningProducer{}
	for _, warningProducer := range deps.Gateways {
		warningProducers = append(warningProducers, warningProducer)
	}

	warningsCollector := net.NewWarningsCollector(deps.Ui, warningProducers...)

	commands_loader.Load()

	//run core command
	cmdName := os.Args[1]
	cmd := cmdRegistry.FindCommand(cmdName)
	if cmd != nil {
		meta := cmd.MetaData()
		flagContext := flags.NewFlagContext(meta.Flags)
		flagContext.SkipFlagParsing(meta.SkipFlagParsing)

		cmdArgs := os.Args[2:]
		err := flagContext.Parse(cmdArgs...)
		if err != nil {
			usage := cmdRegistry.CommandUsage(cmdName)
			deps.Ui.Failed("Incorrect Usage\n\n" + err.Error() + "\n\n" + usage)
		}

		cmd = cmd.SetDependency(deps, false)
		cmdRegistry.SetCommand(cmd)

		requirementsFactory := requirements.NewFactory(deps.Ui, deps.Config, deps.RepoLocator)
		reqs, err := cmd.Requirements(requirementsFactory, flagContext)
		if err != nil {
			deps.Ui.Failed(err.Error())
		}

		for _, req := range reqs {
			req.Execute()
		}

		cmd.Execute(flagContext)

		warningsCollector.PrintWarnings()

		os.Exit(0)
	}

	//non core command, try plugin command
	rpcService := newCliRpcServer(deps.TeePrinter, deps.TeePrinter)

	pluginConfig := plugin_config.NewPluginConfig(func(err error) {
		deps.Ui.Failed(fmt.Sprintf("Error read/writing plugin config: %s, ", err.Error()))
	})
	pluginList := pluginConfig.Plugins()

	ran := rpc.RunMethodIfExists(rpcService, os.Args[1:], pluginList)
	if !ran {
		deps.Ui.Say("'" + os.Args[1] + T("' is not a registered command. See 'cf help'"))
		os.Exit(1)
	}

}

func gatewaySliceFromMap(gateway_map map[string]net.Gateway) []net.WarningProducer {
	gateways := []net.WarningProducer{}
	for _, gateway := range gateway_map {
		gateways = append(gateways, gateway)
	}
	return gateways
}

func handlePanics(printer terminal.Printer) {
	panic_printer.UI = terminal.NewUI(os.Stdin, printer)

	commandArgs := strings.Join(os.Args, " ")
	stackTrace := generateBacktrace()

	err := recover()
	panic_printer.DisplayCrashDialog(err, commandArgs, stackTrace)

	if err != nil {
		os.Exit(1)
	}
}

func generateBacktrace() string {
	stackByteCount := 0
	STACK_SIZE_LIMIT := 1024 * 1024
	var bytes []byte
	for stackSize := 1024; (stackByteCount == 0 || stackByteCount == stackSize) && stackSize < STACK_SIZE_LIMIT; stackSize = 2 * stackSize {
		bytes = make([]byte, stackSize)
		stackByteCount = runtime.Stack(bytes, true)
	}
	stackTrace := "\t" + strings.Replace(string(bytes), "\n", "\n\t", -1)
	return stackTrace
}

func requestHelp(args []string) bool {
	for _, v := range args {
		if v == "-h" || v == "--help" || v == "--h" {
			return true
		}
	}

	return false
}

func newCliRpcServer(outputCapture terminal.OutputCapture, terminalOutputSwitch terminal.TerminalOutputSwitch) *rpc.CliRpcService {
	cliServer, err := rpc.NewRpcService(outputCapture, terminalOutputSwitch, deps.Config, deps.RepoLocator, rpc.NewNonCodegangstaRunner())
	if err != nil {
		deps.Ui.Say(T("Error initializing RPC service: ") + err.Error())
		os.Exit(1)
	}

	return cliServer
}
<commit_msg>Remove unused code<commit_after>package main

import (
	"fmt"
	"os"
	"runtime"
	"strings"

	"github.com/cloudfoundry/cli/cf"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/configuration/plugin_config"
	"github.com/cloudfoundry/cli/cf/help"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/net"
	"github.com/cloudfoundry/cli/cf/panic_printer"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/commands_loader"
	"github.com/cloudfoundry/cli/flags"
	"github.com/cloudfoundry/cli/plugin/rpc"
)

var deps = command_registry.NewDependency()

var cmdRegistry = command_registry.Commands

func main() {
	defer handlePanics(deps.TeePrinter)
	defer deps.Config.Close()

	//handles `cf` | `cf -h` || `cf -help`
	if len(os.Args) == 1 || os.Args[1] == "--help" || os.Args[1] == "-help" ||
		os.Args[1] == "--h" || os.Args[1] == "-h" {
		help.ShowHelp(help.GetHelpTemplate())
		os.Exit(0)
	}

	//handle `cf -v` for cf version
	if len(os.Args) == 2 && (os.Args[1] == "-v" || os.Args[1] == "--version") {
		deps.Ui.Say(os.Args[0] + " version " + cf.Version + "-" + cf.BuiltOnDate)
		os.Exit(0)
	}

	//handle `cf --build`
	if len(os.Args) == 2 && (os.Args[1] == "--build" || os.Args[1] == "-b") {
		deps.Ui.Say(T("{{.CFName}} was built with Go version: {{.GoVersion}}",
			map[string]interface{}{
				"CFName":    os.Args[0],
				"GoVersion": runtime.Version(),
			}))
		os.Exit(0)
	}

	//handles `cf [COMMAND] -h ...`
	//rearrage args to `cf help COMMAND` and let `command help` to print out usage
	if requestHelp(os.Args[2:]) {
		os.Args[2] = os.Args[1]
		os.Args[1] = "help"
	}

	warningProducers := []net.WarningProducer{}
	for _, warningProducer := range deps.Gateways {
		warningProducers = append(warningProducers, warningProducer)
	}

	warningsCollector := net.NewWarningsCollector(deps.Ui, warningProducers...)

	commands_loader.Load()

	//run core command
	cmdName := os.Args[1]
	cmd := cmdRegistry.FindCommand(cmdName)
	if cmd != nil {
		meta := cmd.MetaData()
		flagContext := flags.NewFlagContext(meta.Flags)
		flagContext.SkipFlagParsing(meta.SkipFlagParsing)

		cmdArgs := os.Args[2:]
		err := flagContext.Parse(cmdArgs...)
		if err != nil {
			usage := cmdRegistry.CommandUsage(cmdName)
			deps.Ui.Failed("Incorrect Usage\n\n" + err.Error() + "\n\n" + usage)
		}

		cmd = cmd.SetDependency(deps, false)
		cmdRegistry.SetCommand(cmd)

		requirementsFactory := requirements.NewFactory(deps.Ui, deps.Config, deps.RepoLocator)
		reqs, err := cmd.Requirements(requirementsFactory, flagContext)
		if err != nil {
			deps.Ui.Failed(err.Error())
		}

		for _, req := range reqs {
			req.Execute()
		}

		cmd.Execute(flagContext)

		warningsCollector.PrintWarnings()

		os.Exit(0)
	}

	//non core command, try plugin command
	rpcService := newCliRpcServer(deps.TeePrinter, deps.TeePrinter)

	pluginConfig := plugin_config.NewPluginConfig(func(err error) {
		deps.Ui.Failed(fmt.Sprintf("Error read/writing plugin config: %s, ", err.Error()))
	})
	pluginList := pluginConfig.Plugins()

	ran := rpc.RunMethodIfExists(rpcService, os.Args[1:], pluginList)
	if !ran {
		deps.Ui.Say("'" + os.Args[1] + T("' is not a registered command. See 'cf help'"))
		os.Exit(1)
	}

}

func handlePanics(printer terminal.Printer) {
	panic_printer.UI = terminal.NewUI(os.Stdin, printer)

	commandArgs := strings.Join(os.Args, " ")
	stackTrace := generateBacktrace()

	err := recover()
	panic_printer.DisplayCrashDialog(err, commandArgs, stackTrace)

	if err != nil {
		os.Exit(1)
	}
}

func generateBacktrace() string {
	stackByteCount := 0
	STACK_SIZE_LIMIT := 1024 * 1024
	var bytes []byte
	for stackSize := 1024; (stackByteCount == 0 || stackByteCount == stackSize) && stackSize < STACK_SIZE_LIMIT; stackSize = 2 * stackSize {
		bytes = make([]byte, stackSize)
		stackByteCount = runtime.Stack(bytes, true)
	}
	stackTrace := "\t" + strings.Replace(string(bytes), "\n", "\n\t", -1)
	return stackTrace
}

func requestHelp(args []string) bool {
	for _, v := range args {
		if v == "-h" || v == "--help" || v == "--h" {
			return true
		}
	}

	return false
}

func newCliRpcServer(outputCapture terminal.OutputCapture, terminalOutputSwitch terminal.TerminalOutputSwitch) *rpc.CliRpcService {
	cliServer, err := rpc.NewRpcService(outputCapture, terminalOutputSwitch, deps.Config, deps.RepoLocator, rpc.NewNonCodegangstaRunner())
	if err != nil {
		deps.Ui.Say(T("Error initializing RPC service: ") + err.Error())
		os.Exit(1)
	}

	return cliServer
}
<|endoftext|>


<commit_before>// Copyright 2017 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 main

import "testing"

func Test_shortenVersion(t *testing.T) {
	type args struct {
		v string
	}
	tests := []struct {
		version string
		want    string
	}{
		{
			version: "go1.8.1.typealias",
			want:    "go1.8.1.typealias",
		},
		{
			version: "go1.9",
			want:    "go1.9",
		},
		{
			version: "go1.9rc",
			want:    "go1.9rc",
		},
		{
			version: "devel +990dac2723 Fri Jun 30 18:24:58 2017 +0000",
			want:    "devel +990dac2723",
		},
	}
	for _, tt := range tests {
		t.Run(tt.version, func(t *testing.T) {
			if got := shortenVersion(tt.version); got != tt.want {
				t.Errorf("shortenVersion() = %v, want %v", got, tt.want)
			}
		})
	}
}
<commit_msg>test: remove unused type (#66)<commit_after>// Copyright 2017 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 main

import "testing"

func Test_shortenVersion(t *testing.T) {
	tests := []struct {
		version string
		want    string
	}{
		{
			version: "go1.8.1.typealias",
			want:    "go1.8.1.typealias",
		},
		{
			version: "go1.9",
			want:    "go1.9",
		},
		{
			version: "go1.9rc",
			want:    "go1.9rc",
		},
		{
			version: "devel +990dac2723 Fri Jun 30 18:24:58 2017 +0000",
			want:    "devel +990dac2723",
		},
	}
	for _, tt := range tests {
		t.Run(tt.version, func(t *testing.T) {
			if got := shortenVersion(tt.version); got != tt.want {
				t.Errorf("shortenVersion() = %v, want %v", got, tt.want)
			}
		})
	}
}
<|endoftext|>