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

package upstart_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	gc "launchpad.net/gocheck"

	jc "launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/upstart"
)

func Test(t *testing.T) { gc.TestingT(t) }

type UpstartSuite struct {
	origPath string
	testPath string
	service  *upstart.Service
}

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

func (s *UpstartSuite) SetUpTest(c *gc.C) {
	s.origPath = os.Getenv("PATH")
	s.testPath = c.MkDir()
	os.Setenv("PATH", s.testPath+":"+s.origPath)
	s.service = &upstart.Service{Name: "some-service", InitDir: c.MkDir()}
	_, err := os.Create(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
}

func (s *UpstartSuite) TearDownTest(c *gc.C) {
	os.Setenv("PATH", s.origPath)
}

var checkargs = `
#!/bin/bash
if [ "$1" == "--system" ]; then
  shift
fi
if [ "$1" != "some-service" ]; then
  exit 255
fi
if [ "$2" != "" ]; then
  exit 255
fi
`[1:]

func (s *UpstartSuite) MakeTool(c *gc.C, name, script string) {
	path := filepath.Join(s.testPath, name)
	err := ioutil.WriteFile(path, []byte(checkargs+script), 0755)
	c.Assert(err, gc.IsNil)
}

func (s *UpstartSuite) StoppedStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service stop/waiting"`)
}

func (s *UpstartSuite) RunningStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service start/running, process 123"`)
}

func (s *UpstartSuite) TestInitDir(c *gc.C) {
	svc := upstart.NewService("blah")
	c.Assert(svc.InitDir, gc.Equals, "/etc/init")
}

func (s *UpstartSuite) TestInstalled(c *gc.C) {
	c.Assert(s.service.Installed(), gc.Equals, true)
	err := os.Remove(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
	c.Assert(s.service.Installed(), gc.Equals, false)
}

func (s *UpstartSuite) TestRunning(c *gc.C) {
	s.MakeTool(c, "status", "exit 1")
	c.Assert(s.service.Running(), gc.Equals, false)
	s.MakeTool(c, "status", `echo "GIBBERISH NONSENSE"`)
	c.Assert(s.service.Running(), gc.Equals, false)
	s.RunningStatus(c)
	c.Assert(s.service.Running(), gc.Equals, true)
}

func (s *UpstartSuite) TestStart(c *gc.C) {
	s.RunningStatus(c)
	s.MakeTool(c, "start", "exit 99")
	c.Assert(s.service.Start(), gc.IsNil)
	s.StoppedStatus(c)
	c.Assert(s.service.Start(), gc.ErrorMatches, ".*exit status 99.*")
	s.MakeTool(c, "start", "exit 0")
	c.Assert(s.service.Start(), gc.IsNil)
}

func (s *UpstartSuite) TestStop(c *gc.C) {
	s.StoppedStatus(c)
	s.MakeTool(c, "stop", "exit 99")
	c.Assert(s.service.Stop(), gc.IsNil)
	s.RunningStatus(c)
	c.Assert(s.service.Stop(), gc.ErrorMatches, ".*exit status 99.*")
	s.MakeTool(c, "stop", "exit 0")
	c.Assert(s.service.Stop(), gc.IsNil)
}

func (s *UpstartSuite) TestRemoveMissing(c *gc.C) {
	err := os.Remove(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
	c.Assert(s.service.StopAndRemove(), gc.IsNil)
}

func (s *UpstartSuite) TestRemoveStopped(c *gc.C) {
	s.StoppedStatus(c)
	c.Assert(s.service.StopAndRemove(), gc.IsNil)
	_, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (s *UpstartSuite) TestRemoveRunning(c *gc.C) {
	s.RunningStatus(c)
	s.MakeTool(c, "stop", "exit 99")
	c.Assert(s.service.StopAndRemove(), gc.ErrorMatches, ".*exit status 99.*")
	_, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
	s.MakeTool(c, "stop", "exit 0")
	c.Assert(s.service.StopAndRemove(), gc.IsNil)
	_, err = os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (s *UpstartSuite) TestStopAndRemove(c *gc.C) {
	s.RunningStatus(c)
	s.MakeTool(c, "stop", "exit 99")

	// StopAndRemove will fail, as it calls stop.
	c.Assert(s.service.StopAndRemove(), gc.ErrorMatches, ".*exit status 99.*")
	_, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)

	// Plain old Remove will succeed.
	c.Assert(s.service.Remove(), gc.IsNil)
	_, err = os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (s *UpstartSuite) TestInstallErrors(c *gc.C) {
	conf := &upstart.Conf{}
	check := func(msg string) {
		c.Assert(conf.Install(), gc.ErrorMatches, msg)
		_, err := conf.InstallCommands()
		c.Assert(err, gc.ErrorMatches, msg)
	}
	check("missing Name")
	conf.Name = "some-service"
	check("missing InitDir")
	conf.InitDir = c.MkDir()
	check("missing Desc")
	conf.Desc = "this is an upstart service"
	check("missing Cmd")
}

const expectStart = `description "this is an upstart service"
author "Juju Team <juju@lists.ubuntu.com>"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
normal exit 0
`

func (s *UpstartSuite) dummyConf(c *gc.C) *upstart.Conf {
	return &upstart.Conf{
		Service: *s.service,
		Desc:    "this is an upstart service",
		Cmd:     "do something",
	}
}

func (s *UpstartSuite) assertInstall(c *gc.C, conf *upstart.Conf, expectEnd string) {
	expectContent := expectStart + expectEnd
	expectPath := filepath.Join(conf.InitDir, "some-service.conf")

	cmds, err := conf.InstallCommands()
	c.Assert(err, gc.IsNil)
	c.Assert(cmds, gc.DeepEquals, []string{
		"cat >> " + expectPath + " << 'EOF'\n" + expectContent + "EOF\n",
		"start some-service",
	})

	s.MakeTool(c, "start", "exit 99")
	err = conf.Install()
	c.Assert(err, gc.ErrorMatches, ".*exit status 99.*")
	s.MakeTool(c, "start", "exit 0")
	err = conf.Install()
	c.Assert(err, gc.IsNil)
	content, err := ioutil.ReadFile(expectPath)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Equals, expectContent)
}

func (s *UpstartSuite) TestInstallSimple(c *gc.C) {
	conf := s.dummyConf(c)
	s.assertInstall(c, conf, "\n\nexec do something\n")
}

func (s *UpstartSuite) TestInstallOutput(c *gc.C) {
	conf := s.dummyConf(c)
	conf.Out = "/some/output/path"
	s.assertInstall(c, conf, "\n\nexec do something >> /some/output/path 2>&1\n")
}

func (s *UpstartSuite) TestInstallEnv(c *gc.C) {
	conf := s.dummyConf(c)
	conf.Env = map[string]string{"FOO": "bar baz", "QUX": "ping pong"}
	s.assertInstall(c, conf, `env FOO="bar baz"
env QUX="ping pong"

exec do something
`)
}

func (s *UpstartSuite) TestInstallLimit(c *gc.C) {
	conf := s.dummyConf(c)
	conf.Limit = map[string]string{"nofile": "65000 65000", "nproc": "20000 20000"}
	s.assertInstall(c, conf, `
limit nofile 65000 65000
limit nproc 20000 20000

exec do something
`)
}

func (s *UpstartSuite) TestInstallAlreadyRunning(c *gc.C) {
	pathTo := func(name string) string {
		return filepath.Join(s.testPath, name)
	}
	s.MakeTool(c, "status-stopped", `echo "some-service stop/waiting"`)
	s.MakeTool(c, "status-started", `echo "some-service start/running, process 123"`)
	s.MakeTool(c, "stop", fmt.Sprintf(
		"rm %s; ln -s %s %s",
		pathTo("status"), pathTo("status-stopped"), pathTo("status"),
	))
	s.MakeTool(c, "start", fmt.Sprintf(
		"rm %s; ln -s %s %s",
		pathTo("status"), pathTo("status-started"), pathTo("status"),
	))
	err := os.Symlink(pathTo("status-started"), pathTo("status"))
	c.Assert(err, gc.IsNil)

	conf := s.dummyConf(c)
	err = conf.Install()
	c.Assert(err, gc.IsNil)
	c.Assert(&conf.Service, jc.Satisfies, (*upstart.Service).Running)
}
<commit_msg>Make sure that all the commands pass --system<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package upstart_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	gc "launchpad.net/gocheck"

	jc "launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/upstart"
)

func Test(t *testing.T) { gc.TestingT(t) }

type UpstartSuite struct {
	origPath string
	testPath string
	service  *upstart.Service
}

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

func (s *UpstartSuite) SetUpTest(c *gc.C) {
	s.origPath = os.Getenv("PATH")
	s.testPath = c.MkDir()
	os.Setenv("PATH", s.testPath+":"+s.origPath)
	s.service = &upstart.Service{Name: "some-service", InitDir: c.MkDir()}
	_, err := os.Create(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
}

func (s *UpstartSuite) TearDownTest(c *gc.C) {
	os.Setenv("PATH", s.origPath)
}

var checkargs = `
#!/bin/bash
if [ "$1" != "--system" ]; then
  exit 255
fi
if [ "$2" != "some-service" ]; then
  exit 255
fi
if [ "$3" != "" ]; then
  exit 255
fi
`[1:]

func (s *UpstartSuite) MakeTool(c *gc.C, name, script string) {
	path := filepath.Join(s.testPath, name)
	err := ioutil.WriteFile(path, []byte(checkargs+script), 0755)
	c.Assert(err, gc.IsNil)
}

func (s *UpstartSuite) StoppedStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service stop/waiting"`)
}

func (s *UpstartSuite) RunningStatus(c *gc.C) {
	s.MakeTool(c, "status", `echo "some-service start/running, process 123"`)
}

func (s *UpstartSuite) TestInitDir(c *gc.C) {
	svc := upstart.NewService("blah")
	c.Assert(svc.InitDir, gc.Equals, "/etc/init")
}

func (s *UpstartSuite) TestInstalled(c *gc.C) {
	c.Assert(s.service.Installed(), gc.Equals, true)
	err := os.Remove(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
	c.Assert(s.service.Installed(), gc.Equals, false)
}

func (s *UpstartSuite) TestRunning(c *gc.C) {
	s.MakeTool(c, "status", "exit 1")
	c.Assert(s.service.Running(), gc.Equals, false)
	s.MakeTool(c, "status", `echo "GIBBERISH NONSENSE"`)
	c.Assert(s.service.Running(), gc.Equals, false)
	s.RunningStatus(c)
	c.Assert(s.service.Running(), gc.Equals, true)
}

func (s *UpstartSuite) TestStart(c *gc.C) {
	s.RunningStatus(c)
	s.MakeTool(c, "start", "exit 99")
	c.Assert(s.service.Start(), gc.IsNil)
	s.StoppedStatus(c)
	c.Assert(s.service.Start(), gc.ErrorMatches, ".*exit status 99.*")
	s.MakeTool(c, "start", "exit 0")
	c.Assert(s.service.Start(), gc.IsNil)
}

func (s *UpstartSuite) TestStop(c *gc.C) {
	s.StoppedStatus(c)
	s.MakeTool(c, "stop", "exit 99")
	c.Assert(s.service.Stop(), gc.IsNil)
	s.RunningStatus(c)
	c.Assert(s.service.Stop(), gc.ErrorMatches, ".*exit status 99.*")
	s.MakeTool(c, "stop", "exit 0")
	c.Assert(s.service.Stop(), gc.IsNil)
}

func (s *UpstartSuite) TestRemoveMissing(c *gc.C) {
	err := os.Remove(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
	c.Assert(s.service.StopAndRemove(), gc.IsNil)
}

func (s *UpstartSuite) TestRemoveStopped(c *gc.C) {
	s.StoppedStatus(c)
	c.Assert(s.service.StopAndRemove(), gc.IsNil)
	_, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (s *UpstartSuite) TestRemoveRunning(c *gc.C) {
	s.RunningStatus(c)
	s.MakeTool(c, "stop", "exit 99")
	c.Assert(s.service.StopAndRemove(), gc.ErrorMatches, ".*exit status 99.*")
	_, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)
	s.MakeTool(c, "stop", "exit 0")
	c.Assert(s.service.StopAndRemove(), gc.IsNil)
	_, err = os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (s *UpstartSuite) TestStopAndRemove(c *gc.C) {
	s.RunningStatus(c)
	s.MakeTool(c, "stop", "exit 99")

	// StopAndRemove will fail, as it calls stop.
	c.Assert(s.service.StopAndRemove(), gc.ErrorMatches, ".*exit status 99.*")
	_, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, gc.IsNil)

	// Plain old Remove will succeed.
	c.Assert(s.service.Remove(), gc.IsNil)
	_, err = os.Stat(filepath.Join(s.service.InitDir, "some-service.conf"))
	c.Assert(err, jc.Satisfies, os.IsNotExist)
}

func (s *UpstartSuite) TestInstallErrors(c *gc.C) {
	conf := &upstart.Conf{}
	check := func(msg string) {
		c.Assert(conf.Install(), gc.ErrorMatches, msg)
		_, err := conf.InstallCommands()
		c.Assert(err, gc.ErrorMatches, msg)
	}
	check("missing Name")
	conf.Name = "some-service"
	check("missing InitDir")
	conf.InitDir = c.MkDir()
	check("missing Desc")
	conf.Desc = "this is an upstart service"
	check("missing Cmd")
}

const expectStart = `description "this is an upstart service"
author "Juju Team <juju@lists.ubuntu.com>"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
normal exit 0
`

func (s *UpstartSuite) dummyConf(c *gc.C) *upstart.Conf {
	return &upstart.Conf{
		Service: *s.service,
		Desc:    "this is an upstart service",
		Cmd:     "do something",
	}
}

func (s *UpstartSuite) assertInstall(c *gc.C, conf *upstart.Conf, expectEnd string) {
	expectContent := expectStart + expectEnd
	expectPath := filepath.Join(conf.InitDir, "some-service.conf")

	cmds, err := conf.InstallCommands()
	c.Assert(err, gc.IsNil)
	c.Assert(cmds, gc.DeepEquals, []string{
		"cat >> " + expectPath + " << 'EOF'\n" + expectContent + "EOF\n",
		"start some-service",
	})

	s.MakeTool(c, "start", "exit 99")
	err = conf.Install()
	c.Assert(err, gc.ErrorMatches, ".*exit status 99.*")
	s.MakeTool(c, "start", "exit 0")
	err = conf.Install()
	c.Assert(err, gc.IsNil)
	content, err := ioutil.ReadFile(expectPath)
	c.Assert(err, gc.IsNil)
	c.Assert(string(content), gc.Equals, expectContent)
}

func (s *UpstartSuite) TestInstallSimple(c *gc.C) {
	conf := s.dummyConf(c)
	s.assertInstall(c, conf, "\n\nexec do something\n")
}

func (s *UpstartSuite) TestInstallOutput(c *gc.C) {
	conf := s.dummyConf(c)
	conf.Out = "/some/output/path"
	s.assertInstall(c, conf, "\n\nexec do something >> /some/output/path 2>&1\n")
}

func (s *UpstartSuite) TestInstallEnv(c *gc.C) {
	conf := s.dummyConf(c)
	conf.Env = map[string]string{"FOO": "bar baz", "QUX": "ping pong"}
	s.assertInstall(c, conf, `env FOO="bar baz"
env QUX="ping pong"

exec do something
`)
}

func (s *UpstartSuite) TestInstallLimit(c *gc.C) {
	conf := s.dummyConf(c)
	conf.Limit = map[string]string{"nofile": "65000 65000", "nproc": "20000 20000"}
	s.assertInstall(c, conf, `
limit nofile 65000 65000
limit nproc 20000 20000

exec do something
`)
}

func (s *UpstartSuite) TestInstallAlreadyRunning(c *gc.C) {
	pathTo := func(name string) string {
		return filepath.Join(s.testPath, name)
	}
	s.MakeTool(c, "status-stopped", `echo "some-service stop/waiting"`)
	s.MakeTool(c, "status-started", `echo "some-service start/running, process 123"`)
	s.MakeTool(c, "stop", fmt.Sprintf(
		"rm %s; ln -s %s %s",
		pathTo("status"), pathTo("status-stopped"), pathTo("status"),
	))
	s.MakeTool(c, "start", fmt.Sprintf(
		"rm %s; ln -s %s %s",
		pathTo("status"), pathTo("status-started"), pathTo("status"),
	))
	err := os.Symlink(pathTo("status-started"), pathTo("status"))
	c.Assert(err, gc.IsNil)

	conf := s.dummyConf(c)
	err = conf.Install()
	c.Assert(err, gc.IsNil)
	c.Assert(&conf.Service, jc.Satisfies, (*upstart.Service).Running)
}
<|endoftext|>


<commit_before>package filepathfilter

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
)

type Pattern interface {
	Match(filename string) bool
	// String returns a string representation (see: regular expressions) of
	// the underlying pattern used to match filenames against this Pattern.
	String() string
}

type Filter struct {
	include []Pattern
	exclude []Pattern
}

func NewFromPatterns(include, exclude []Pattern) *Filter {
	return &Filter{include: include, exclude: exclude}
}

func New(include, exclude []string) *Filter {
	return NewFromPatterns(convertToPatterns(include), convertToPatterns(exclude))
}

// Include returns the result of calling String() on each Pattern in the
// include set of this *Filter.
func (f *Filter) Include() []string { return patternsToStrings(f.include...) }

// Exclude returns the result of calling String() on each Pattern in the
// exclude set of this *Filter.
func (f *Filter) Exclude() []string { return patternsToStrings(f.exclude...) }

// patternsToStrings maps the given set of Pattern's to a string slice by
// calling String() on each pattern.
func patternsToStrings(ps ...Pattern) []string {
	s := make([]string, 0, len(ps))
	for _, p := range ps {
		s = append(s, p.String())
	}

	return s
}

func (f *Filter) Allows(filename string) bool {
	_, allowed := f.AllowsPattern(filename)
	return allowed
}

// AllowsPattern returns whether the given filename is permitted by the
// inclusion/exclusion rules of this filter, as well as the pattern that either
// allowed or disallowed that filename.
//
// In special cases, such as a nil `*Filter` receiver, the absence of any
// patterns, or the given filename not being matched by any pattern, the empty
// string "" will be returned in place of the pattern.
func (f *Filter) AllowsPattern(filename string) (pattern string, allowed bool) {
	if f == nil {
		return "", true
	}

	if len(f.include)+len(f.exclude) == 0 {
		return "", true
	}

	cleanedName := filepath.Clean(filename)

	if len(f.include) > 0 {
		matched := false
		for _, inc := range f.include {
			matched = inc.Match(cleanedName)
			if matched {
				pattern = inc.String()
				break
			}
		}
		if !matched {
			return "", false
		}
	}

	if len(f.exclude) > 0 {
		for _, ex := range f.exclude {
			if ex.Match(cleanedName) {
				return ex.String(), false
			}
		}
	}

	return pattern, true
}

func NewPattern(rawpattern string) Pattern {
	cleanpattern := filepath.Clean(rawpattern)

	// Special case local dir, matches all (inc subpaths)
	if _, local := localDirSet[cleanpattern]; local {
		return noOpMatcher{}
	}

	sep := string(filepath.Separator)
	hasPathSep := strings.Contains(cleanpattern, sep)
	ext := filepath.Ext(cleanpattern)
	plen := len(cleanpattern)
	if plen > 1 && !hasPathSep && strings.HasPrefix(cleanpattern, "*") && cleanpattern[1:plen] == ext {
		return &simpleExtPattern{ext: ext}
	}

	// special case * when there are no path separators
	// filepath.Match never allows * to match a path separator, which is correct
	// for gitignore IF the pattern includes a path separator, but not otherwise
	// So *.txt should match in any subdir, as should test*, but sub/*.txt would
	// only match directly in the sub dir
	// Don't need to test cross-platform separators as both cleaned above
	if !hasPathSep && strings.Contains(cleanpattern, "*") {
		pattern := regexp.QuoteMeta(cleanpattern)
		regpattern := fmt.Sprintf("^%s$", strings.Replace(pattern, "\\*", ".*", -1))
		return &pathlessWildcardPattern{
			rawPattern: cleanpattern,
			wildcardRE: regexp.MustCompile(regpattern),
		}
	}

	// Also support ** with path separators
	if hasPathSep && strings.Contains(cleanpattern, "**") {
		pattern := regexp.QuoteMeta(cleanpattern)
		regpattern := fmt.Sprintf("^%s$", strings.Replace(pattern, "\\*\\*", ".*", -1))
		return &doubleWildcardPattern{
			rawPattern: cleanpattern,
			wildcardRE: regexp.MustCompile(regpattern),
		}
	}

	if hasPathSep && strings.HasPrefix(cleanpattern, sep) {
		rel := cleanpattern[1:len(cleanpattern)]
		prefix := rel
		if strings.HasSuffix(rel, sep) {
			rel = rel[0 : len(rel)-1]
		} else {
			prefix += sep
		}

		return &pathPrefixPattern{
			rawPattern: cleanpattern,
			relative:   rel,
			prefix:     prefix,
		}
	}

	return &pathPattern{
		rawPattern: cleanpattern,
		prefix:     cleanpattern + sep,
		suffix:     sep + cleanpattern,
		inner:      sep + cleanpattern + sep,
	}
}

func convertToPatterns(rawpatterns []string) []Pattern {
	patterns := make([]Pattern, len(rawpatterns))
	for i, raw := range rawpatterns {
		patterns[i] = NewPattern(raw)
	}
	return patterns
}

type pathPrefixPattern struct {
	rawPattern string
	relative   string
	prefix     string
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *pathPrefixPattern) Match(name string) bool {
	if name == p.relative || strings.HasPrefix(name, p.prefix) {
		return true
	}
	matched, _ := filepath.Match(p.rawPattern, name)
	return matched
}

// String returns a string representation of the underlying pattern for which
// this *pathPrefixPattern is matching.
func (p *pathPrefixPattern) String() string {
	return p.rawPattern
}

type pathPattern struct {
	rawPattern string
	prefix     string
	suffix     string
	inner      string
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *pathPattern) Match(name string) bool {
	if strings.HasPrefix(name, p.prefix) || strings.HasSuffix(name, p.suffix) || strings.Contains(name, p.inner) {
		return true
	}
	matched, _ := filepath.Match(p.rawPattern, name)
	return matched
}

// String returns a string representation of the underlying pattern for which
// this *pathPattern is matching.
func (p *pathPattern) String() string {
	return p.rawPattern
}

type simpleExtPattern struct {
	ext string
}

func (p *simpleExtPattern) Match(name string) bool {
	return strings.HasSuffix(name, p.ext)
}

// String returns a string representation of the underlying pattern for which
// this *simpleExtPattern is matching.
func (p *simpleExtPattern) String() string {
	return fmt.Sprintf("*%s", p.ext)
}

type pathlessWildcardPattern struct {
	rawPattern string
	wildcardRE *regexp.Regexp
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *pathlessWildcardPattern) Match(name string) bool {
	matched, _ := filepath.Match(p.rawPattern, name)
	// Match the whole of the base name but allow matching in folders if no path
	return matched || p.wildcardRE.MatchString(filepath.Base(name))
}

// String returns a string representation of the underlying pattern for which
// this *pathlessWildcardPattern is matching.
func (p *pathlessWildcardPattern) String() string {
	return p.rawPattern
}

type doubleWildcardPattern struct {
	rawPattern string
	wildcardRE *regexp.Regexp
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *doubleWildcardPattern) Match(name string) bool {
	matched, _ := filepath.Match(p.rawPattern, name)
	// Match the whole of the base name but allow matching in folders if no path
	return matched || p.wildcardRE.MatchString(name)
}

// String returns a string representation of the underlying pattern for which
// this *doubleWildcardPattern is matching.
func (p *doubleWildcardPattern) String() string {
	return p.rawPattern
}

type noOpMatcher struct {
}

func (n noOpMatcher) Match(name string) bool {
	return true
}

func (n noOpMatcher) String() string {
	return ""
}

var localDirSet = map[string]struct{}{
	".":   struct{}{},
	"./":  struct{}{},
	".\\": struct{}{},
}
<commit_msg>filepathfilter: teach 'HasPrefix' to Pattern interface<commit_after>package filepathfilter

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
)

type Pattern interface {
	// HasPrefix returns whether the receiving Pattern will match a fullpath
	// that contains the prefix "prefix".
	//
	// For instance, if the receiving pattern were to match 'a/b/c.txt',
	// HasPrefix() will return true for:
	//
	//   - 'a', and 'a/'
	//   - 'a/b', and 'a/b/'
	HasPrefix(prefix string) bool

	Match(filename string) bool
	// String returns a string representation (see: regular expressions) of
	// the underlying pattern used to match filenames against this Pattern.
	String() string
}

type Filter struct {
	include []Pattern
	exclude []Pattern
}

func NewFromPatterns(include, exclude []Pattern) *Filter {
	return &Filter{include: include, exclude: exclude}
}

func New(include, exclude []string) *Filter {
	return NewFromPatterns(convertToPatterns(include), convertToPatterns(exclude))
}

// Include returns the result of calling String() on each Pattern in the
// include set of this *Filter.
func (f *Filter) Include() []string { return patternsToStrings(f.include...) }

// Exclude returns the result of calling String() on each Pattern in the
// exclude set of this *Filter.
func (f *Filter) Exclude() []string { return patternsToStrings(f.exclude...) }

// patternsToStrings maps the given set of Pattern's to a string slice by
// calling String() on each pattern.
func patternsToStrings(ps ...Pattern) []string {
	s := make([]string, 0, len(ps))
	for _, p := range ps {
		s = append(s, p.String())
	}

	return s
}

func (f *Filter) Allows(filename string) bool {
	_, allowed := f.AllowsPattern(filename)
	return allowed
}

// AllowsPattern returns whether the given filename is permitted by the
// inclusion/exclusion rules of this filter, as well as the pattern that either
// allowed or disallowed that filename.
//
// In special cases, such as a nil `*Filter` receiver, the absence of any
// patterns, or the given filename not being matched by any pattern, the empty
// string "" will be returned in place of the pattern.
func (f *Filter) AllowsPattern(filename string) (pattern string, allowed bool) {
	if f == nil {
		return "", true
	}

	if len(f.include)+len(f.exclude) == 0 {
		return "", true
	}

	cleanedName := filepath.Clean(filename)

	if len(f.include) > 0 {
		matched := false
		for _, inc := range f.include {
			matched = inc.Match(cleanedName)
			if matched {
				pattern = inc.String()
				break
			}
		}
		if !matched {
			return "", false
		}
	}

	if len(f.exclude) > 0 {
		for _, ex := range f.exclude {
			if ex.Match(cleanedName) {
				return ex.String(), false
			}
		}
	}

	return pattern, true
}

const (
	sep = string(filepath.Separator)
)

func NewPattern(rawpattern string) Pattern {
	cleanpattern := filepath.Clean(rawpattern)

	// Special case local dir, matches all (inc subpaths)
	if _, local := localDirSet[cleanpattern]; local {
		return noOpMatcher{}
	}

	hasPathSep := strings.Contains(cleanpattern, sep)
	ext := filepath.Ext(cleanpattern)
	plen := len(cleanpattern)
	if plen > 1 && !hasPathSep && strings.HasPrefix(cleanpattern, "*") && cleanpattern[1:plen] == ext {
		return &simpleExtPattern{ext: ext}
	}

	// special case * when there are no path separators
	// filepath.Match never allows * to match a path separator, which is correct
	// for gitignore IF the pattern includes a path separator, but not otherwise
	// So *.txt should match in any subdir, as should test*, but sub/*.txt would
	// only match directly in the sub dir
	// Don't need to test cross-platform separators as both cleaned above
	if !hasPathSep && strings.Contains(cleanpattern, "*") {
		pattern := regexp.QuoteMeta(cleanpattern)
		regpattern := fmt.Sprintf("^%s$", strings.Replace(pattern, "\\*", ".*", -1))
		return &pathlessWildcardPattern{
			rawPattern: cleanpattern,
			wildcardRE: regexp.MustCompile(regpattern),
		}
	}

	// Also support ** with path separators
	if hasPathSep && strings.Contains(cleanpattern, "**") {
		pattern := regexp.QuoteMeta(cleanpattern)
		regpattern := fmt.Sprintf("^%s$", strings.Replace(pattern, "\\*\\*", ".*", -1))
		return &doubleWildcardPattern{
			rawPattern: cleanpattern,
			wildcardRE: regexp.MustCompile(regpattern),
		}
	}

	if hasPathSep && strings.HasPrefix(cleanpattern, sep) {
		rel := cleanpattern[1:len(cleanpattern)]
		prefix := rel
		if strings.HasSuffix(rel, sep) {
			rel = rel[0 : len(rel)-1]
		} else {
			prefix += sep
		}

		return &pathPrefixPattern{
			rawPattern: cleanpattern,
			relative:   rel,
			prefix:     prefix,
		}
	}

	return &pathPattern{
		rawPattern: cleanpattern,
		prefix:     cleanpattern + sep,
		suffix:     sep + cleanpattern,
		inner:      sep + cleanpattern + sep,
	}
}

func convertToPatterns(rawpatterns []string) []Pattern {
	patterns := make([]Pattern, len(rawpatterns))
	for i, raw := range rawpatterns {
		patterns[i] = NewPattern(raw)
	}
	return patterns
}

type pathPrefixPattern struct {
	rawPattern string
	relative   string
	prefix     string
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *pathPrefixPattern) Match(name string) bool {
	if name == p.relative || strings.HasPrefix(name, p.prefix) {
		return true
	}
	matched, _ := filepath.Match(p.rawPattern, name)
	return matched
}

func (p *pathPrefixPattern) HasPrefix(name string) bool {
	return strings.HasPrefix(p.relative, name)
}

// String returns a string representation of the underlying pattern for which
// this *pathPrefixPattern is matching.
func (p *pathPrefixPattern) String() string {
	return p.rawPattern
}

type pathPattern struct {
	rawPattern string
	prefix     string
	suffix     string
	inner      string
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *pathPattern) Match(name string) bool {
	if strings.HasPrefix(name, p.prefix) || strings.HasSuffix(name, p.suffix) || strings.Contains(name, p.inner) {
		return true
	}
	matched, _ := filepath.Match(p.rawPattern, name)
	return matched
}

func (p *pathPattern) HasPrefix(name string) bool {
	return strings.HasPrefix(p.prefix, name)
}

// String returns a string representation of the underlying pattern for which
// this *pathPattern is matching.
func (p *pathPattern) String() string {
	return p.rawPattern
}

type simpleExtPattern struct {
	ext string
}

func (p *simpleExtPattern) Match(name string) bool {
	return strings.HasSuffix(name, p.ext)
}

func (p *simpleExtPattern) HasPrefix(name string) bool {
	return true
}

// String returns a string representation of the underlying pattern for which
// this *simpleExtPattern is matching.
func (p *simpleExtPattern) String() string {
	return fmt.Sprintf("*%s", p.ext)
}

type pathlessWildcardPattern struct {
	rawPattern string
	wildcardRE *regexp.Regexp
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *pathlessWildcardPattern) Match(name string) bool {
	matched, _ := filepath.Match(p.rawPattern, name)
	// Match the whole of the base name but allow matching in folders if no path
	return matched || p.wildcardRE.MatchString(filepath.Base(name))
}

func (p *pathlessWildcardPattern) HasPrefix(name string) bool {
	lit, ok := p.wildcardRE.LiteralPrefix()
	if !ok {
		return true
	}

	return strings.HasPrefix(name, lit)
}

// String returns a string representation of the underlying pattern for which
// this *pathlessWildcardPattern is matching.
func (p *pathlessWildcardPattern) String() string {
	return p.rawPattern
}

type doubleWildcardPattern struct {
	rawPattern string
	wildcardRE *regexp.Regexp
}

// Match is a revised version of filepath.Match which makes it behave more
// like gitignore
func (p *doubleWildcardPattern) Match(name string) bool {
	matched, _ := filepath.Match(p.rawPattern, name)
	// Match the whole of the base name but allow matching in folders if no path
	return matched || p.wildcardRE.MatchString(name)
}

func (p *doubleWildcardPattern) HasPrefix(name string) bool {
	lit, ok := p.wildcardRE.LiteralPrefix()
	if !ok {
		return true
	}

	return strings.HasPrefix(name, lit)
}

// String returns a string representation of the underlying pattern for which
// this *doubleWildcardPattern is matching.
func (p *doubleWildcardPattern) String() string {
	return p.rawPattern
}

type noOpMatcher struct {
}

func (n noOpMatcher) Match(name string) bool {
	return true
}

func (n noOpMatcher) HasPrefix(name string) bool {
	return true
}

func (n noOpMatcher) String() string {
	return ""
}

var localDirSet = map[string]struct{}{
	".":   struct{}{},
	"./":  struct{}{},
	".\\": struct{}{},
}
<|endoftext|>


<commit_before>package tris

import (
	"bytes"
	"errors"
	"fmt"
	zmq "github.com/alecthomas/gozmq"
	"github.com/fvbock/trie"
	"io/ioutil"
	"log"
	"os"
	// "os/signal"
	"strings"
	"sync"
	// "syscall"
	"time"
)

const (
	VERSION    = "0.0.2"
	DEFAULT_DB = "0"

	STATE_STOP    = 1
	STATE_STOPPED = 2
	STATE_RUNNING = 3
)

type Server struct {
	sync.RWMutex
	Log              *log.Logger
	Config           *ServerConfig
	Commands         map[string]Command
	Databases        map[string]*Database
	DatabaseOpCount  map[string]int
	State            int
	Stateswitch      chan int
	CycleLength      int64
	cycleTicker      <-chan time.Time
	CheckStateChange time.Duration

	// zeromq
	Context   *zmq.Context
	Socket    *zmq.Socket
	pollItems zmq.PollItems

	// CommandQueue  chan *ClientConnection
	ActiveClients     map[string]*ClientConnection
	InactiveClientIds chan string
	RequestsRunning   int
	CommandsProcessed int
}

func NewServer(config *ServerConfig) (s *Server, err error) {
	s = &Server{
		Config: config,
		// server
		Commands:          make(map[string]Command),
		Databases:         make(map[string]*Database),
		Stateswitch:       make(chan int, 1),
		CycleLength:       int64(time.Microsecond) * 500,
		CheckStateChange:  time.Second * 1,
		ActiveClients:     make(map[string]*ClientConnection),
		InactiveClientIds: make(chan string),
		Log:               log.New(os.Stderr, "", log.LstdFlags),
		// stats
		RequestsRunning:   0,
		CommandsProcessed: 0,
	}
	s.Initialize()
	return
}

func (s *Server) Initialize() {
	// register commands
	TrisCommands = append(TrisCommands, &CommandInfo{})
	TrisCommands = append(TrisCommands, &CommandDbInfo{})
	TrisCommands = append(TrisCommands, &CommandExit{})
	TrisCommands = append(TrisCommands, &CommandPing{})
	TrisCommands = append(TrisCommands, &CommandSave{})
	TrisCommands = append(TrisCommands, &CommandImportDb{})
	TrisCommands = append(TrisCommands, &CommandMergeDb{})
	TrisCommands = append(TrisCommands, &CommandSelect{})
	TrisCommands = append(TrisCommands, &CommandCreateTrie{})
	// TrisCommands = append(TrisCommands, &CommandFlushTrie{})
	// TrisCommands = append(TrisCommands, &CommandDropTrie{})
	TrisCommands = append(TrisCommands, &CommandAdd{})
	TrisCommands = append(TrisCommands, &CommandDel{})
	TrisCommands = append(TrisCommands, &CommandHas{})
	TrisCommands = append(TrisCommands, &CommandHasCount{})
	TrisCommands = append(TrisCommands, &CommandHasPrefix{})
	TrisCommands = append(TrisCommands, &CommandMembers{})
	TrisCommands = append(TrisCommands, &CommandPrefixMembers{})
	TrisCommands = append(TrisCommands, &CommandTree{})
	TrisCommands = append(TrisCommands, &CommandTiming{})
	TrisCommands = append(TrisCommands, &CommandShutdown{})
	s.registerCommands(TrisCommands...)

	//

	dataFiles, err := ioutil.ReadDir(s.Config.DataDir)
	if err != nil {

	}
	waitLoadDataFiles := sync.WaitGroup{}
	for _, f := range dataFiles {
		if !f.IsDir() {
			waitLoadDataFiles.Add(1)
			go func(datafile os.FileInfo) {
				err := s.loadDataFile(datafile.Name())
				if err != nil {
					s.Log.Printf("Error loading trie file %s: %v\n", f.Name(), err)
				}
				waitLoadDataFiles.Done()
			}(f)
		}
	}
	waitLoadDataFiles.Wait()
	s.NewDatabase(DEFAULT_DB)
}

func (s *Server) NewDatabase(name string) {
	s.Databases[name] = &Database{
		Name:                name,
		Db:                  trie.NewTrie(),
		OpsCount:            0,
		LastPersistOpsCount: 0,
		PersistOpsLimit:     s.Config.PersistOpsLimit,
		PersistInterval:     s.Config.PersistInterval,
	}
}

func (s *Server) loadDataFile(fname string) (err error) {
	if len(fname) > len(s.Config.StorageFilePrefix) && fname[0:len(s.Config.StorageFilePrefix)] == s.Config.StorageFilePrefix {
		id := strings.Split(fname, s.Config.StorageFilePrefix)[1]
		s.Log.Printf("Loading Trie %s\n", id)
		s.NewDatabase(id)
		s.Databases[id].Db, err = trie.LoadFromFile(fmt.Sprintf("%s/%s%s", s.Config.DataDir, s.Config.StorageFilePrefix, id))
		if err != nil {
			return
		}
	} else {
		err = errors.New("")
	}
	return
}

func (s *Server) Start() (err error) {
	s.Stateswitch <- STATE_RUNNING
	go func(s *Server) {
		s.Log.Println("Starting server...")

		// // setup signal handling
		// sigChan := make(chan os.Signal, 1)
		// signal.Notify(sigChan,
		// 	syscall.SIGTERM,
		// 	syscall.SIGINT,
		// 	syscall.SIGKILL,
		// 	syscall.SIGQUIT,
		// 	syscall.SIGHUP,
		// )

		// zmq
		s.Context, err = zmq.NewContext()
		if err != nil {

		}
		s.Socket, err = s.Context.NewSocket(zmq.ROUTER)
		if err != nil {

		}
		s.Socket.SetSockOptInt(zmq.LINGER, 0)
		s.Log.Println(fmt.Sprintf("Binding to %s://%s:%v", s.Config.Protocol, s.Config.Host, s.Config.Port))
		s.Socket.Bind(fmt.Sprintf("%s://%s:%v", s.Config.Protocol, s.Config.Host, s.Config.Port))
		s.Log.Println("Server started...")

		s.pollItems = zmq.PollItems{
			zmq.PollItem{Socket: s.Socket, Events: zmq.POLLIN},
		}

		var cycleLength int64
		var cycleStart time.Time
		s.cycleTicker = time.Tick(time.Duration(s.CycleLength) * time.Nanosecond)
		stateTicker := time.Tick(s.CheckStateChange)
	mainLoop:
		for {
			cycleStart = time.Now()

			// make the poller run in a sep goroutine and push to a channel?
			if s.RequestsRunning > 0 {
				s.Lock()
				_, _ = zmq.Poll(s.pollItems, 1)
				s.Unlock()
			} else {
				_, _ = zmq.Poll(s.pollItems, 1000000)
				s.Log.Println(">")
			}
			switch {
			case s.pollItems[0].REvents&zmq.POLLIN != 0:
				s.Lock()
				msgParts, _ := s.pollItems[0].Socket.RecvMultipart(0)
				s.RequestsRunning++
				s.Unlock()
				go s.handleRequest(msgParts)
			default:
				select {
				case <-stateTicker:
					select {
					case s.State = <-s.Stateswitch:
						s.Log.Println("state changed:", s.State)
						if s.State == STATE_STOP {
							break mainLoop
						}
						if s.State == STATE_RUNNING {
						}
					default:
						time.Sleep(1)
					}
				// case sig := <-sigChan:
				// 	s.Log.Println("got signal:", sig)
				default:
					// s.Log.Println(".")
				}
			}
			s.beforeSleep()
			cycleLength = time.Now().UnixNano() - cycleStart.UnixNano()
			if cycleLength < s.CycleLength {
				d := (s.CycleLength - cycleLength)
				time.Sleep(time.Duration(d) * time.Nanosecond)
			}
		}
		s.prepareShutdown()
		s.Log.Println("Server stopped running...")
		s.State = STATE_STOPPED
		s.shutdown()
	}(s)
	s.Log.Println("Server starting...")
	return
}

func (s *Server) beforeSleep() {
beforeSleepCycle:
	for {
		select {
		case <-s.cycleTicker:
			break beforeSleepCycle
		case cId := <-s.InactiveClientIds:
			delete(s.ActiveClients, cId)
			continue
		default:
			break
		}
	}
	return
}

func (s *Server) handleRequest(msgParts [][]byte) {
	clientKey := string(msgParts[0])
	var c *ClientConnection
	var unknown bool
	if c, unknown = s.ActiveClients[clientKey]; !unknown {
		s.ActiveClients[clientKey] = NewClientConnection(s, msgParts[0])
		c = s.ActiveClients[clientKey]
	}
	var execStart time.Time
	if c.ShowExecTime {
		execStart = time.Now()
	}

	cmds, args, err := splitMsgs(msgParts[2])
	if err != nil {
		// TODO
	}

	var reply *Reply
	var replies []*Reply

	for i, cmd := range cmds {
		var cmdName string = strings.ToUpper(cmd)
		if _, exists := s.Commands[cmdName]; !exists {
			// handle non existing command call
			reply = NewReply(
				[][]byte{[]byte(fmt.Sprintf("Unknown Command %s.", cmd))},
				COMMAND_FAIL)
		} else {
			reply = s.Commands[cmdName].Function(s, c, args[i]...)
			if reply.ReturnCode != COMMAND_OK {
				s.Log.Println(string(reply.Payload[0]))
			}
			// do this even when we fail...?
			if COMMAND_FLAG_WRITE&s.Commands[cmdName].Flags() == COMMAND_FLAG_WRITE {
				s.Log.Println("WRITE cmd +1")
				c.ActiveDb.Lock()
				c.ActiveDb.OpsCount += 1
				c.ActiveDb.Unlock()
			}
		}
		replies = append(replies, reply)
		s.Lock()
		s.CommandsProcessed += 1
		s.Unlock()
	}

	var response []byte
	for rn, rep := range replies {
		response = append(response, rep.Serialize()...)
		if rn > 0 {
			response = append(response, []byte("\n")...)
		}
	}
	s.Lock()
	s.pollItems[0].Socket.SendMultipart([][]byte{c.Id, []byte(""), response}, 0)
	// stats
	s.RequestsRunning--
	s.Unlock()
	if c.ShowExecTime {
		s.Log.Printf("%s %v took %v\n", cmds, args, time.Since(execStart))
	}
	// TODO: count db write operations
}

func (s *Server) Stop() {
	s.Log.Println("Stopping server.")
	s.Stateswitch <- STATE_STOP
}

/*
Take care of stuff...
*/
func (s *Server) prepareShutdown() {
	s.Log.Println("Preparing server shutdown...")
	for s.RequestsRunning > 0 {
		s.Log.Println("Requests running:", s.RequestsRunning)
		time.Sleep(100 * time.Millisecond)
	}
	waitPersist := sync.WaitGroup{}
	for _, db := range s.Databases {
		waitPersist.Add(1)
		go func(d *Database) {
			d.Persist(fmt.Sprintf("%s/%s%s", s.Config.DataDir, s.Config.StorageFilePrefix, d.Name))
			waitPersist.Done()
		}(db)
	}
	waitPersist.Wait()
}

func (s *Server) shutdown() {
	s.Log.Println("Server teardown.")
	s.Socket.Close()
	s.Log.Println("Socket closed.")
	s.Context.Close()
	s.Log.Println("Context closed.")
	s.Log.Println("Stopped server.")
	os.Exit(0)
}

func (s *Server) registerCommands(cmds ...Command) (err error) {
	for _, c := range cmds {
		if _, exists := s.Commands[c.Name()]; exists {
			err = errors.New(fmt.Sprintf("Command %s has already been registered.", c.Name()))
			return
		}
		// s.Log.Println("Registering command", c.Name())
		s.Commands[c.Name()] = c
	}
	return
}

func (s *Server) dbExists(name string) bool {
	if _, exists := s.Databases[name]; !exists {
		return false
	}
	return true
}

func splitMsgs(payload []byte) (cmds []string, args [][]interface{}, err error) {
	msgs := bytes.Split(bytes.Trim(payload, " "), []byte("\n"))
	for n, msg := range msgs {
		parts := bytes.Split(bytes.Trim(msg, " "), []byte(" "))
		for i, p := range parts {
			if len(p) == 0 {
				continue
			}
			if i == 0 {
				cmds = append(cmds, string(p))
				args = append(args, make([]interface{}, 0))
			} else {
				args[n] = append(args[n], string(p))
			}
		}
	}
	return
}
<commit_msg>fix timeout when wiating for a first connection<commit_after>package tris

import (
	"bytes"
	"errors"
	"fmt"
	zmq "github.com/alecthomas/gozmq"
	"github.com/fvbock/trie"
	"io/ioutil"
	"log"
	"os"
	// "os/signal"
	"strings"
	"sync"
	// "syscall"
	"time"
)

const (
	VERSION    = "0.0.2"
	DEFAULT_DB = "0"

	STATE_STOP    = 1
	STATE_STOPPED = 2
	STATE_RUNNING = 3
)

type Server struct {
	sync.RWMutex
	Log              *log.Logger
	Config           *ServerConfig
	Commands         map[string]Command
	Databases        map[string]*Database
	DatabaseOpCount  map[string]int
	State            int
	Stateswitch      chan int
	CycleLength      int64
	cycleTicker      <-chan time.Time
	CheckStateChange time.Duration

	// zeromq
	Context   *zmq.Context
	Socket    *zmq.Socket
	pollItems zmq.PollItems

	// CommandQueue  chan *ClientConnection
	ActiveClients     map[string]*ClientConnection
	InactiveClientIds chan string
	RequestsRunning   int
	CommandsProcessed int
}

func NewServer(config *ServerConfig) (s *Server, err error) {
	s = &Server{
		Config: config,
		// server
		Commands:          make(map[string]Command),
		Databases:         make(map[string]*Database),
		Stateswitch:       make(chan int, 1),
		CycleLength:       int64(time.Microsecond) * 500,
		CheckStateChange:  time.Second * 1,
		ActiveClients:     make(map[string]*ClientConnection),
		InactiveClientIds: make(chan string),
		Log:               log.New(os.Stderr, "", log.LstdFlags),
		// stats
		RequestsRunning:   0,
		CommandsProcessed: 0,
	}
	s.Initialize()
	return
}

func (s *Server) Initialize() {
	// register commands
	TrisCommands = append(TrisCommands, &CommandInfo{})
	TrisCommands = append(TrisCommands, &CommandDbInfo{})
	TrisCommands = append(TrisCommands, &CommandExit{})
	TrisCommands = append(TrisCommands, &CommandPing{})
	TrisCommands = append(TrisCommands, &CommandSave{})
	TrisCommands = append(TrisCommands, &CommandImportDb{})
	TrisCommands = append(TrisCommands, &CommandMergeDb{})
	TrisCommands = append(TrisCommands, &CommandSelect{})
	TrisCommands = append(TrisCommands, &CommandCreateTrie{})
	// TrisCommands = append(TrisCommands, &CommandFlushTrie{})
	// TrisCommands = append(TrisCommands, &CommandDropTrie{})
	TrisCommands = append(TrisCommands, &CommandAdd{})
	TrisCommands = append(TrisCommands, &CommandDel{})
	TrisCommands = append(TrisCommands, &CommandHas{})
	TrisCommands = append(TrisCommands, &CommandHasCount{})
	TrisCommands = append(TrisCommands, &CommandHasPrefix{})
	TrisCommands = append(TrisCommands, &CommandMembers{})
	TrisCommands = append(TrisCommands, &CommandPrefixMembers{})
	TrisCommands = append(TrisCommands, &CommandTree{})
	TrisCommands = append(TrisCommands, &CommandTiming{})
	TrisCommands = append(TrisCommands, &CommandShutdown{})
	s.registerCommands(TrisCommands...)

	//

	dataFiles, err := ioutil.ReadDir(s.Config.DataDir)
	if err != nil {

	}
	waitLoadDataFiles := sync.WaitGroup{}
	for _, f := range dataFiles {
		if !f.IsDir() {
			waitLoadDataFiles.Add(1)
			go func(datafile os.FileInfo) {
				err := s.loadDataFile(datafile.Name())
				if err != nil {
					s.Log.Printf("Error loading trie file %s: %v\n", f.Name(), err)
				}
				waitLoadDataFiles.Done()
			}(f)
		}
	}
	waitLoadDataFiles.Wait()
	s.NewDatabase(DEFAULT_DB)
}

func (s *Server) NewDatabase(name string) {
	s.Databases[name] = &Database{
		Name:                name,
		Db:                  trie.NewTrie(),
		OpsCount:            0,
		LastPersistOpsCount: 0,
		PersistOpsLimit:     s.Config.PersistOpsLimit,
		PersistInterval:     s.Config.PersistInterval,
	}
}

func (s *Server) loadDataFile(fname string) (err error) {
	if len(fname) > len(s.Config.StorageFilePrefix) && fname[0:len(s.Config.StorageFilePrefix)] == s.Config.StorageFilePrefix {
		id := strings.Split(fname, s.Config.StorageFilePrefix)[1]
		s.Log.Printf("Loading Trie %s\n", id)
		s.NewDatabase(id)
		s.Databases[id].Db, err = trie.LoadFromFile(fmt.Sprintf("%s/%s%s", s.Config.DataDir, s.Config.StorageFilePrefix, id))
		if err != nil {
			return
		}
	} else {
		err = errors.New("")
	}
	return
}

func (s *Server) Start() (err error) {
	s.Stateswitch <- STATE_RUNNING
	go func(s *Server) {
		s.Log.Println("Starting server...")

		// // setup signal handling
		// sigChan := make(chan os.Signal, 1)
		// signal.Notify(sigChan,
		// 	syscall.SIGTERM,
		// 	syscall.SIGINT,
		// 	syscall.SIGKILL,
		// 	syscall.SIGQUIT,
		// 	syscall.SIGHUP,
		// )

		// zmq
		s.Context, err = zmq.NewContext()
		if err != nil {

		}
		s.Socket, err = s.Context.NewSocket(zmq.ROUTER)
		if err != nil {

		}
		s.Socket.SetSockOptInt(zmq.LINGER, 0)
		s.Log.Println(fmt.Sprintf("Binding to %s://%s:%v", s.Config.Protocol, s.Config.Host, s.Config.Port))
		s.Socket.Bind(fmt.Sprintf("%s://%s:%v", s.Config.Protocol, s.Config.Host, s.Config.Port))
		s.Log.Println("Server started...")

		s.pollItems = zmq.PollItems{
			zmq.PollItem{Socket: s.Socket, Events: zmq.POLLIN},
		}

		var cycleLength int64
		var cycleStart time.Time
		s.cycleTicker = time.Tick(time.Duration(s.CycleLength) * time.Nanosecond)
		stateTicker := time.Tick(s.CheckStateChange)
	mainLoop:
		for {
			cycleStart = time.Now()

			// make the poller run in a sep goroutine and push to a channel?
			if s.RequestsRunning > 0 {
				s.Lock()
				_, _ = zmq.Poll(s.pollItems, 1)
				s.Unlock()
			} else {
				_, _ = zmq.Poll(s.pollItems, time.Second*1)
			}
			switch {
			case s.pollItems[0].REvents&zmq.POLLIN != 0:
				s.Lock()
				msgParts, _ := s.pollItems[0].Socket.RecvMultipart(0)
				s.RequestsRunning++
				s.Unlock()
				go s.handleRequest(msgParts)
			default:
				select {
				case <-stateTicker:
					select {
					case s.State = <-s.Stateswitch:
						s.Log.Println("state changed:", s.State)
						if s.State == STATE_STOP {
							break mainLoop
						}
						if s.State == STATE_RUNNING {
						}
					default:
						time.Sleep(1)
					}
				// case sig := <-sigChan:
				// 	s.Log.Println("got signal:", sig)
				default:
					// s.Log.Println(".")
				}
			}
			s.beforeSleep()
			cycleLength = time.Now().UnixNano() - cycleStart.UnixNano()
			if cycleLength < s.CycleLength {
				d := (s.CycleLength - cycleLength)
				time.Sleep(time.Duration(d) * time.Nanosecond)
			}
		}
		s.prepareShutdown()
		s.Log.Println("Server stopped running...")
		s.State = STATE_STOPPED
		s.shutdown()
	}(s)
	s.Log.Println("Server starting...")
	return
}

func (s *Server) beforeSleep() {
beforeSleepCycle:
	for {
		select {
		case <-s.cycleTicker:
			break beforeSleepCycle
		case cId := <-s.InactiveClientIds:
			delete(s.ActiveClients, cId)
			continue
		default:
			break
		}
	}
	return
}

func (s *Server) handleRequest(msgParts [][]byte) {
	clientKey := string(msgParts[0])
	var c *ClientConnection
	var unknown bool
	if c, unknown = s.ActiveClients[clientKey]; !unknown {
		s.ActiveClients[clientKey] = NewClientConnection(s, msgParts[0])
		c = s.ActiveClients[clientKey]
	}
	var execStart time.Time
	if c.ShowExecTime {
		execStart = time.Now()
	}

	cmds, args, err := splitMsgs(msgParts[2])
	if err != nil {
		// TODO
	}

	var reply *Reply
	var replies []*Reply

	for i, cmd := range cmds {
		var cmdName string = strings.ToUpper(cmd)
		if _, exists := s.Commands[cmdName]; !exists {
			// handle non existing command call
			reply = NewReply(
				[][]byte{[]byte(fmt.Sprintf("Unknown Command %s.", cmd))},
				COMMAND_FAIL)
		} else {
			reply = s.Commands[cmdName].Function(s, c, args[i]...)
			if reply.ReturnCode != COMMAND_OK {
				s.Log.Println(string(reply.Payload[0]))
			}
			// do this even when we fail...?
			if COMMAND_FLAG_WRITE&s.Commands[cmdName].Flags() == COMMAND_FLAG_WRITE {
				s.Log.Println("WRITE cmd +1")
				c.ActiveDb.Lock()
				c.ActiveDb.OpsCount += 1
				c.ActiveDb.Unlock()
			}
		}
		replies = append(replies, reply)
		s.Lock()
		s.CommandsProcessed += 1
		s.Unlock()
	}

	var response []byte
	for rn, rep := range replies {
		response = append(response, rep.Serialize()...)
		if rn > 0 {
			response = append(response, []byte("\n")...)
		}
	}
	s.Lock()
	s.pollItems[0].Socket.SendMultipart([][]byte{c.Id, []byte(""), response}, 0)
	// stats
	s.RequestsRunning--
	s.Unlock()
	if c.ShowExecTime {
		s.Log.Printf("%s %v took %v\n", cmds, args, time.Since(execStart))
	}
	// TODO: count db write operations
}

func (s *Server) Stop() {
	s.Log.Println("Stopping server.")
	s.Stateswitch <- STATE_STOP
}

/*
Take care of stuff...
*/
func (s *Server) prepareShutdown() {
	s.Log.Println("Preparing server shutdown...")
	for s.RequestsRunning > 0 {
		s.Log.Println("Requests running:", s.RequestsRunning)
		time.Sleep(100 * time.Millisecond)
	}
	waitPersist := sync.WaitGroup{}
	for _, db := range s.Databases {
		waitPersist.Add(1)
		go func(d *Database) {
			d.Persist(fmt.Sprintf("%s/%s%s", s.Config.DataDir, s.Config.StorageFilePrefix, d.Name))
			waitPersist.Done()
		}(db)
	}
	waitPersist.Wait()
}

func (s *Server) shutdown() {
	s.Log.Println("Server teardown.")
	s.Socket.Close()
	s.Log.Println("Socket closed.")
	s.Context.Close()
	s.Log.Println("Context closed.")
	s.Log.Println("Stopped server.")
	os.Exit(0)
}

func (s *Server) registerCommands(cmds ...Command) (err error) {
	for _, c := range cmds {
		if _, exists := s.Commands[c.Name()]; exists {
			err = errors.New(fmt.Sprintf("Command %s has already been registered.", c.Name()))
			return
		}
		// s.Log.Println("Registering command", c.Name())
		s.Commands[c.Name()] = c
	}
	return
}

func (s *Server) dbExists(name string) bool {
	if _, exists := s.Databases[name]; !exists {
		return false
	}
	return true
}

func splitMsgs(payload []byte) (cmds []string, args [][]interface{}, err error) {
	msgs := bytes.Split(bytes.Trim(payload, " "), []byte("\n"))
	for n, msg := range msgs {
		parts := bytes.Split(bytes.Trim(msg, " "), []byte(" "))
		for i, p := range parts {
			if len(p) == 0 {
				continue
			}
			if i == 0 {
				cmds = append(cmds, string(p))
				args = append(args, make([]interface{}, 0))
			} else {
				args[n] = append(args[n], string(p))
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/LapisBlue/Lapitar/mc"
	"github.com/LapisBlue/Lapitar/server/cache"
	"github.com/LapisBlue/Lapitar/util"
	"github.com/zenazn/goji/web"
	"image"
	"image/png"
	"log"
	"net/http"
	"strconv"
)

func printError(err error, message ...interface{}) {
	if err == nil {
		return
	}

	log.Println(message...)
	log.Println(err)
}

func parseSize(c web.C, def int) (result int) {
	size := c.URLParams["size"]
	result, err := strconv.Atoi(size)
	if err != nil {
		printError(err, "Failed to parse size", size)
		return def
	}
	return
}

func downloadSkin(player string, watch *util.StopWatch) (sk *mc.Skin, id string, err error) {
	watch.Mark()
	sk, id, err = cache.GetSkin(player)
	if err == nil {
		log.Println("Downloaded skin:", player, watch)
	} else {
		printError(err, "Failed to download skin:", player, watch)
	}

	return
}

const (
	cacheControl = "max-age=86400" // 24*60*60, one day in seconds
)

func prepareResponse(w http.ResponseWriter, r *http.Request, id string) bool {
	w.Header().Add("Cache-Control", cacheControl)
	w.Header().Add("ETag", id)

	if tag := r.Header.Get("If-None-Match"); tag == id {
		w.WriteHeader(http.StatusNotModified)
		return false
	}

	return true
}

func sendResult(w http.ResponseWriter, player string, result image.Image, watch *util.StopWatch) (err error) {
	watch.Mark()
	w.Header().Add("Content-Type", "image/png")
	err = png.Encode(w, result)
	if err == nil {
		log.Println("Sent response:", player, watch)
	} else {
		printError(err, "Failed to send response:", player, watch)
	}

	return
}
<commit_msg>Add Expires header<commit_after>package server

import (
	"github.com/LapisBlue/Lapitar/mc"
	"github.com/LapisBlue/Lapitar/server/cache"
	"github.com/LapisBlue/Lapitar/util"
	"github.com/zenazn/goji/web"
	"image"
	"image/png"
	"log"
	"net/http"
	"strconv"
	"time"
)

func printError(err error, message ...interface{}) {
	if err == nil {
		return
	}

	log.Println(message...)
	log.Println(err)
}

func parseSize(c web.C, def int) (result int) {
	size := c.URLParams["size"]
	result, err := strconv.Atoi(size)
	if err != nil {
		printError(err, "Failed to parse size", size)
		return def
	}
	return
}

func downloadSkin(player string, watch *util.StopWatch) (sk *mc.Skin, id string, err error) {
	watch.Mark()
	sk, id, err = cache.GetSkin(player)
	if err == nil {
		log.Println("Downloaded skin:", player, watch)
	} else {
		printError(err, "Failed to download skin:", player, watch)
	}

	return
}

const (
	keepCache    = 24 * time.Hour
	cacheControl = "max-age=86400" // 24*60*60, one day in seconds
)

func prepareResponse(w http.ResponseWriter, r *http.Request, id string) bool {
	w.Header().Add("Cache-Control", cacheControl)
	w.Header().Add("Expires", time.Now().Add(keepCache).UTC().Format(http.TimeFormat))
	w.Header().Add("ETag", id)

	if tag := r.Header.Get("If-None-Match"); tag == id {
		w.WriteHeader(http.StatusNotModified)
		return false
	}

	return true
}

func sendResult(w http.ResponseWriter, player string, result image.Image, watch *util.StopWatch) (err error) {
	watch.Mark()
	w.Header().Add("Content-Type", "image/png")
	err = png.Encode(w, result)
	if err == nil {
		log.Println("Sent response:", player, watch)
	} else {
		printError(err, "Failed to send response:", player, watch)
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"runtime"
	"time"

	"git.tideland.biz/goas/loop"
	"github.com/remogatto/mandala"
	gl "github.com/remogatto/opengles2"
)

const (
	FRAMES_PER_SECOND = 24
)

type viewportSize struct {
	width, height int
}

type renderLoopControl struct {
	resizeViewport chan viewportSize
	pause          chan bool
	resume         chan bool
	window         chan mandala.Window
}

var (
	window mandala.Window
)

func newRenderLoopControl() *renderLoopControl {
	return &renderLoopControl{
		make(chan viewportSize),
		make(chan bool),
		make(chan bool),
		make(chan mandala.Window, 1),
	}
}

func draw() {
	gl.ClearColor(1.0, 0.0, 0.0, 1.0)
	gl.Clear(gl.COLOR_BUFFER_BIT)
}

// Run runs renderLoop. The loop renders a frame and swaps the buffer
// at each tick received.
func renderLoopFunc(control *renderLoopControl) loop.LoopFunc {
	return func(loop loop.Loop) error {
		var window mandala.Window
		// Lock/unlock the loop to the current OS thread. This is
		// necessary because OpenGL functions should be called from
		// the same thread.
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		// Create an instance of ticker and immediately stop
		// it because we don't want to swap buffers before
		// initializing a rendering state.
		ticker := time.NewTicker(time.Duration(1e9 / int(FRAMES_PER_SECOND)))
		ticker.Stop()

		for {
			select {
			case window = <-control.window:
				ticker.Stop()

				window.MakeContextCurrent()

				width, height := window.GetSize()
				gl.Viewport(0, 0, gl.Sizei(width), gl.Sizei(height))

				mandala.Logf("Restarting rendering loop...")
				ticker = time.NewTicker(time.Duration(1e9 / int(FRAMES_PER_SECOND)))

			// At each tick render a frame and swap buffers.
			case <-ticker.C:
				draw()
				window.SwapBuffers()

			case <-control.pause:
				ticker.Stop()

			case <-control.resume:

			case <-loop.ShallStop():
				ticker.Stop()
				return nil
			}
		}
	}
}

// eventLoopFunc listen to events originating from the
// framework.
func eventLoopFunc(renderLoopControl *renderLoopControl) loop.LoopFunc {
	return func(loop loop.Loop) error {

		for {
			select {

			// Receive an EGL state from the
			// framework and notify the render
			// loop about that.
			// case eglState := <-mandala.Init:
			// 	mandala.Logf("EGL surface initialized W:%d H:%d", eglState.SurfaceWidth, eglState.SurfaceHeight)
			// 	renderLoopControl.eglState <- eglState

			// Receive events from the framework.
			//
			// When the application starts the
			// typical events chain is:
			//
			// * onCreate
			// * onResume
			// * onInputQueueCreated
			// * onNativeWindowCreated
			// * onNativeWindowResized
			// * onWindowFocusChanged
			// * onNativeRedrawNeeded
			//
			// Pausing (i.e. clicking on the back
			// button) the application produces
			// following events chain:
			//
			// * onPause
			// * onWindowDestroy
			// * onWindowFocusChanged
			// * onInputQueueDestroy
			// * onDestroy

			case untypedEvent := <-mandala.Events():
				switch event := untypedEvent.(type) {

				// Receive a native window
				// from the framework and send
				// it to the render loop in
				// order to begin the
				// rendering process.
				case mandala.NativeWindowCreatedEvent:
					renderLoopControl.window <- event.Window

				// Finger down/up on the screen.
				case mandala.ActionUpDownEvent:
					if event.Down {
						mandala.Logf("Finger is DOWN at %f %f", event.X, event.Y)
					} else {
						mandala.Logf("Finger is now UP")
					}

					// Finger is moving on the screen.
				case mandala.ActionMoveEvent:
					mandala.Logf("Finger is moving at coord %f %f", event.X, event.Y)

				case mandala.DestroyEvent:
					mandala.Logf("Stop rendering...\n")
					mandala.Logf("Quitting from application...\n")
					return nil

				case mandala.NativeWindowRedrawNeededEvent:

				case mandala.PauseEvent:
					mandala.Logf("Application was paused. Stopping rendering ticker.")
					renderLoopControl.pause <- true

				case mandala.ResumeEvent:
					mandala.Logf("Application was resumed. Reactivating rendering ticker.")
					renderLoopControl.resume <- true

				}
			}
		}
	}
}
<commit_msg>Signal when application actually paused using Paused semaphore<commit_after>package main

import (
	"runtime"
	"time"

	"git.tideland.biz/goas/loop"
	"github.com/remogatto/mandala"
	gl "github.com/remogatto/opengles2"
)

const (
	FRAMES_PER_SECOND = 24
)

type viewportSize struct {
	width, height int
}

type renderLoopControl struct {
	resizeViewport chan viewportSize
	pause          chan mandala.PauseEvent
	resume         chan bool
	window         chan mandala.Window
}

var (
	window mandala.Window
)

func newRenderLoopControl() *renderLoopControl {
	return &renderLoopControl{
		resizeViewport: make(chan viewportSize),
		pause:          make(chan mandala.PauseEvent),
		resume:         make(chan bool),
		window:         make(chan mandala.Window, 1),
	}
}

func draw() {
	gl.ClearColor(1.0, 0.0, 0.0, 1.0)
	gl.Clear(gl.COLOR_BUFFER_BIT)
}

// Run runs renderLoop. The loop renders a frame and swaps the buffer
// at each tick received.
func renderLoopFunc(control *renderLoopControl) loop.LoopFunc {
	return func(loop loop.Loop) error {
		var window mandala.Window
		// Lock/unlock the loop to the current OS thread. This is
		// necessary because OpenGL functions should be called from
		// the same thread.
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		// Create an instance of ticker and immediately stop
		// it because we don't want to swap buffers before
		// initializing a rendering state.
		ticker := time.NewTicker(time.Duration(1e9 / int(FRAMES_PER_SECOND)))
		ticker.Stop()

		for {
			select {
			case window = <-control.window:
				ticker.Stop()

				window.MakeContextCurrent()

				width, height := window.GetSize()
				gl.Viewport(0, 0, gl.Sizei(width), gl.Sizei(height))

				mandala.Logf("Restarting rendering loop...")
				ticker = time.NewTicker(time.Duration(1e9 / int(FRAMES_PER_SECOND)))

			// At each tick render a frame and swap buffers.
			case <-ticker.C:
				draw()
				window.SwapBuffers()

			case event := <-control.pause:
				ticker.Stop()
				event.Paused <- true

			case <-control.resume:

			case <-loop.ShallStop():
				ticker.Stop()
				return nil
			}
		}
	}
}

// eventLoopFunc listen to events originating from the
// framework.
func eventLoopFunc(renderLoopControl *renderLoopControl) loop.LoopFunc {
	return func(loop loop.Loop) error {

		for {
			select {

			// Receive an EGL state from the
			// framework and notify the render
			// loop about that.
			// case eglState := <-mandala.Init:
			// 	mandala.Logf("EGL surface initialized W:%d H:%d", eglState.SurfaceWidth, eglState.SurfaceHeight)
			// 	renderLoopControl.eglState <- eglState

			// Receive events from the framework.
			//
			// When the application starts the
			// typical events chain is:
			//
			// * onCreate
			// * onResume
			// * onInputQueueCreated
			// * onNativeWindowCreated
			// * onNativeWindowResized
			// * onWindowFocusChanged
			// * onNativeRedrawNeeded
			//
			// Pausing (i.e. clicking on the back
			// button) the application produces
			// following events chain:
			//
			// * onPause
			// * onWindowDestroy
			// * onWindowFocusChanged
			// * onInputQueueDestroy
			// * onDestroy

			case untypedEvent := <-mandala.Events():
				switch event := untypedEvent.(type) {

				// Receive a native window
				// from the framework and send
				// it to the render loop in
				// order to begin the
				// rendering process.
				case mandala.NativeWindowCreatedEvent:
					renderLoopControl.window <- event.Window

				// Finger down/up on the screen.
				case mandala.ActionUpDownEvent:
					if event.Down {
						mandala.Logf("Finger is DOWN at %f %f", event.X, event.Y)
					} else {
						mandala.Logf("Finger is now UP")
					}

					// Finger is moving on the screen.
				case mandala.ActionMoveEvent:
					mandala.Logf("Finger is moving at coord %f %f", event.X, event.Y)

				case mandala.DestroyEvent:
					mandala.Logf("Stop rendering...\n")
					mandala.Logf("Quitting from application...\n")
					return nil

				case mandala.NativeWindowRedrawNeededEvent:

				case mandala.PauseEvent:
					mandala.Logf("Application was paused. Stopping rendering ticker.")
					renderLoopControl.pause <- event

				case mandala.ResumeEvent:
					mandala.Logf("Application was resumed. Reactivating rendering ticker.")
					renderLoopControl.resume <- true

				}
			}
		}
	}
}
<|endoftext|>


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

// +build darwin dragonfly freebsd linux netbsd openbsd

package tcp

import (
	"runtime"
	"syscall"
)

func (c *Conn) buffered() int {
	fd, err := c.sysfd()
	if err != nil {
		return 0
	}
	opt := sockOpts[ssoBuffered]
	if opt.name < 1 || opt.typ != ssoTypeInt {
		return 0
	}
	n, err := getsockoptIntByIoctl(fd, opt.name)
	if err != nil {
		return 0
	}
	return n
}

func (c *Conn) available() int {
	fd, err := c.sysfd()
	if err != nil {
		return 0
	}
	opt := sockOpts[ssoAvailable]
	if opt.name < 1 || opt.typ != ssoTypeInt {
		return 0
	}
	n, err := getsockoptIntByIoctl(fd, opt.name)
	if err != nil {
		return 0
	}
	if runtime.GOOS == "linux" {
		l, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF)
		if err != nil {
			return 0
		}
		return l - n
	}
	return n
}
<commit_msg>tcp: refactor ioctl helpers<commit_after>// Copyright 2014 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin dragonfly freebsd linux netbsd openbsd

package tcp

import (
	"runtime"
	"syscall"
)

func (c *Conn) buffered() int {
	fd, err := c.sysfd()
	if err != nil {
		return 0
	}
	n, err := getIntByIoctl(fd, &sockOpts[ssoBuffered])
	if err != nil {
		return 0
	}
	return n
}

func (c *Conn) available() int {
	fd, err := c.sysfd()
	if err != nil {
		return 0
	}
	n, err := getIntByIoctl(fd, &sockOpts[ssoAvailable])
	if err != nil {
		return 0
	}
	if runtime.GOOS == "linux" {
		l, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF)
		if err != nil {
			return 0
		}
		return l - n
	}
	return n
}

func getIntByIoctl(fd int, opt *sockOpt) (int, error) {
	if opt.name < 1 || opt.typ != ssoTypeInt {
		return 0, errOpNoSupport
	}
	v, err := getsockoptIntByIoctl(fd, opt.name)
	if err != nil {
		return 0, err
	}
	return v, nil
}
<|endoftext|>


<commit_before>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum 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 General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @authors
 * 	Jeffrey Wilcke <i@jev.io>
 * @date 2014
 *
 */

package miner

import (
	"math/big"
	"sort"

	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/pow"
	"github.com/ethereum/go-ethereum/pow/ezp"

	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/wire"
)

type LocalTx struct {
	To       []byte `json:"to"`
	Data     []byte `json:"data"`
	Gas      string `json:"gas"`
	GasPrice string `json:"gasPrice"`
	Value    string `json:"value"`
}

func (self *LocalTx) Sign(key []byte) *types.Transaction {
	return nil
}

var minerlogger = logger.NewLogger("MINER")

type Miner struct {
	eth    *eth.Ethereum
	events event.Subscription

	uncles    types.Blocks
	localTxs  map[int]*LocalTx
	localTxId int

	pow       pow.PoW
	quitCh    chan struct{}
	powQuitCh chan struct{}

	Coinbase []byte

	mining bool

	MinAcceptedGasPrice *big.Int
}

func New(coinbase []byte, eth *eth.Ethereum) *Miner {
	return &Miner{
		eth:                 eth,
		powQuitCh:           make(chan struct{}),
		pow:                 ezp.New(),
		mining:              false,
		localTxs:            make(map[int]*LocalTx),
		MinAcceptedGasPrice: big.NewInt(10000000000000),
		Coinbase:            coinbase,
	}
}

func (self *Miner) GetPow() pow.PoW {
	return self.pow
}

func (self *Miner) AddLocalTx(tx *LocalTx) int {
	minerlogger.Infof("Added local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value)

	self.localTxId++
	self.localTxs[self.localTxId] = tx
	self.eth.EventMux().Post(tx)

	return self.localTxId
}

func (self *Miner) RemoveLocalTx(id int) {
	if tx := self.localTxs[id]; tx != nil {
		minerlogger.Infof("Removed local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value)
	}
	self.eth.EventMux().Post(&LocalTx{})

	delete(self.localTxs, id)
}

func (self *Miner) Start() {
	if self.mining {
		return
	}

	minerlogger.Infoln("Starting mining operations")
	self.mining = true
	self.quitCh = make(chan struct{})
	self.powQuitCh = make(chan struct{})

	mux := self.eth.EventMux()
	self.events = mux.Subscribe(core.NewBlockEvent{}, core.TxPreEvent{}, &LocalTx{})

	go self.update()
	go self.mine()
}

func (self *Miner) Stop() {
	if !self.mining {
		return
	}

	self.mining = false

	minerlogger.Infoln("Stopping mining operations")

	self.events.Unsubscribe()

	close(self.quitCh)
	close(self.powQuitCh)
}

func (self *Miner) Mining() bool {
	return self.mining
}

func (self *Miner) update() {
out:
	for {
		select {
		case event := <-self.events.Chan():
			switch event := event.(type) {
			case core.NewBlockEvent:
				block := event.Block
				if self.eth.ChainManager().HasBlock(block.Hash()) {
					self.reset()
					self.eth.TxPool().RemoveSet(block.Transactions())
					go self.mine()
				} else if true {
					// do uncle stuff
				}
			case core.TxPreEvent, *LocalTx:
				self.reset()
				go self.mine()
			}
		case <-self.quitCh:
			break out
		}
	}
}

func (self *Miner) reset() {
	close(self.powQuitCh)
	self.powQuitCh = make(chan struct{})
}

func (self *Miner) mine() {
	var (
		blockManager = self.eth.BlockManager()
		chainMan     = self.eth.ChainManager()
		block        = chainMan.NewBlock(self.Coinbase)
	)

	// Apply uncles
	if len(self.uncles) > 0 {
		block.SetUncles(self.uncles)
	}

	parent := chainMan.GetBlock(block.PrevHash)
	coinbase := block.State().GetOrNewStateObject(block.Coinbase)
	coinbase.SetGasPool(block.CalcGasLimit(parent))

	transactions := self.finiliseTxs()

	// Accumulate all valid transactions and apply them to the new state
	// Error may be ignored. It's not important during mining
	receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, block.State(), block, transactions, true)
	if err != nil {
		minerlogger.Debugln(err)
	}
	self.eth.TxPool().RemoveSet(erroneous)

	block.SetTransactions(txs)
	block.SetReceipts(receipts)

	// Accumulate the rewards included for this block
	blockManager.AccumelateRewards(block.State(), block, parent)

	block.State().Update(ethutil.Big0)

	minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions))

	// Find a valid nonce
	nonce := self.pow.Search(block, self.powQuitCh)
	if nonce != nil {
		block.Nonce = nonce
		err := chainMan.InsertChain(types.Blocks{block})
		if err != nil {
			minerlogger.Infoln(err)
		} else {
			self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val})

			minerlogger.Infof("🔨  Mined block %x\n", block.Hash())
			minerlogger.Infoln(block)
		}

		go self.mine()
	}
}

func (self *Miner) finiliseTxs() types.Transactions {
	// Sort the transactions by nonce in case of odd network propagation
	actualSize := len(self.localTxs) // See copy below
	txs := make(types.Transactions, actualSize+self.eth.TxPool().Size())

	state := self.eth.ChainManager().TransState()
	// XXX This has to change. Coinbase is, for new, same as key.
	key := self.eth.KeyManager()
	for i, ltx := range self.localTxs {
		tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
		tx.Nonce = state.GetNonce(self.Coinbase)
		state.SetNonce(self.Coinbase, tx.Nonce+1)

		tx.Sign(key.PrivateKey())

		txs[i] = tx
	}

	// Faster than append
	for _, tx := range self.eth.TxPool().CurrentTransactions() {
		if tx.GasPrice.Cmp(self.MinAcceptedGasPrice) >= 0 {
			txs[actualSize] = tx
			actualSize++
		}
	}

	newTransactions := make(types.Transactions, actualSize)
	copy(newTransactions, txs[:actualSize])
	sort.Sort(types.TxByNonce{newTransactions})

	return newTransactions
}
<commit_msg>adapt miner to new backend. use events to broadcast new mined blocks<commit_after>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum 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 General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @authors
 * 	Jeffrey Wilcke <i@jev.io>
 * @date 2014
 *
 */

package miner

import (
	"math/big"
	"sort"

	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/pow"
	"github.com/ethereum/go-ethereum/pow/ezp"

	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
)

type LocalTx struct {
	To       []byte `json:"to"`
	Data     []byte `json:"data"`
	Gas      string `json:"gas"`
	GasPrice string `json:"gasPrice"`
	Value    string `json:"value"`
}

func (self *LocalTx) Sign(key []byte) *types.Transaction {
	return nil
}

var minerlogger = logger.NewLogger("MINER")

type Miner struct {
	eth    *eth.Ethereum
	events event.Subscription

	uncles    types.Blocks
	localTxs  map[int]*LocalTx
	localTxId int

	pow       pow.PoW
	quitCh    chan struct{}
	powQuitCh chan struct{}

	Coinbase []byte

	mining bool

	MinAcceptedGasPrice *big.Int
}

func New(coinbase []byte, eth *eth.Ethereum) *Miner {
	return &Miner{
		eth:                 eth,
		powQuitCh:           make(chan struct{}),
		pow:                 ezp.New(),
		mining:              false,
		localTxs:            make(map[int]*LocalTx),
		MinAcceptedGasPrice: big.NewInt(10000000000000),
		Coinbase:            coinbase,
	}
}

func (self *Miner) GetPow() pow.PoW {
	return self.pow
}

func (self *Miner) AddLocalTx(tx *LocalTx) int {
	minerlogger.Infof("Added local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value)

	self.localTxId++
	self.localTxs[self.localTxId] = tx
	self.eth.EventMux().Post(tx)

	return self.localTxId
}

func (self *Miner) RemoveLocalTx(id int) {
	if tx := self.localTxs[id]; tx != nil {
		minerlogger.Infof("Removed local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value)
	}
	self.eth.EventMux().Post(&LocalTx{})

	delete(self.localTxs, id)
}

func (self *Miner) Start() {
	if self.mining {
		return
	}

	minerlogger.Infoln("Starting mining operations")
	self.mining = true
	self.quitCh = make(chan struct{})
	self.powQuitCh = make(chan struct{})

	mux := self.eth.EventMux()
	self.events = mux.Subscribe(core.NewBlockEvent{}, core.TxPreEvent{}, &LocalTx{})

	go self.update()
	go self.mine()
}

func (self *Miner) Stop() {
	if !self.mining {
		return
	}

	self.mining = false

	minerlogger.Infoln("Stopping mining operations")

	self.events.Unsubscribe()

	close(self.quitCh)
	close(self.powQuitCh)
}

func (self *Miner) Mining() bool {
	return self.mining
}

func (self *Miner) update() {
out:
	for {
		select {
		case event := <-self.events.Chan():
			switch event := event.(type) {
			case core.NewBlockEvent:
				block := event.Block
				if self.eth.ChainManager().HasBlock(block.Hash()) {
					self.reset()
					self.eth.TxPool().RemoveSet(block.Transactions())
					go self.mine()
				} else if true {
					// do uncle stuff
				}
			case core.TxPreEvent, *LocalTx:
				self.reset()
				go self.mine()
			}
		case <-self.quitCh:
			break out
		}
	}
}

func (self *Miner) reset() {
	close(self.powQuitCh)
	self.powQuitCh = make(chan struct{})
}

func (self *Miner) mine() {
	var (
		blockManager = self.eth.BlockManager()
		chainMan     = self.eth.ChainManager()
		block        = chainMan.NewBlock(self.Coinbase)
	)

	// Apply uncles
	if len(self.uncles) > 0 {
		block.SetUncles(self.uncles)
	}

	parent := chainMan.GetBlock(block.PrevHash)
	coinbase := block.State().GetOrNewStateObject(block.Coinbase)
	coinbase.SetGasPool(block.CalcGasLimit(parent))

	transactions := self.finiliseTxs()

	// Accumulate all valid transactions and apply them to the new state
	// Error may be ignored. It's not important during mining
	receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, block.State(), block, transactions, true)
	if err != nil {
		minerlogger.Debugln(err)
	}
	self.eth.TxPool().RemoveSet(erroneous)

	block.SetTransactions(txs)
	block.SetReceipts(receipts)

	// Accumulate the rewards included for this block
	blockManager.AccumelateRewards(block.State(), block, parent)

	block.State().Update(ethutil.Big0)

	minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions))

	// Find a valid nonce
	nonce := self.pow.Search(block, self.powQuitCh)
	if nonce != nil {
		block.Nonce = nonce
		err := chainMan.InsertChain(types.Blocks{block})
		if err != nil {
			minerlogger.Infoln(err)
		} else {
			self.eth.EventMux().Post(core.NewMinedBlockEvent{block})

			minerlogger.Infof("🔨  Mined block %x\n", block.Hash())
			minerlogger.Infoln(block)
		}

		go self.mine()
	}
}

func (self *Miner) finiliseTxs() types.Transactions {
	// Sort the transactions by nonce in case of odd network propagation
	actualSize := len(self.localTxs) // See copy below
	txs := make(types.Transactions, actualSize+self.eth.TxPool().Size())

	state := self.eth.ChainManager().TransState()
	// XXX This has to change. Coinbase is, for new, same as key.
	key := self.eth.KeyManager()
	for i, ltx := range self.localTxs {
		tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
		tx.Nonce = state.GetNonce(self.Coinbase)
		state.SetNonce(self.Coinbase, tx.Nonce+1)

		tx.Sign(key.PrivateKey())

		txs[i] = tx
	}

	// Faster than append
	for _, tx := range self.eth.TxPool().GetTransactions() {
		if tx.GasPrice.Cmp(self.MinAcceptedGasPrice) >= 0 {
			txs[actualSize] = tx
			actualSize++
		}
	}

	newTransactions := make(types.Transactions, actualSize)
	copy(newTransactions, txs[:actualSize])
	sort.Sort(types.TxByNonce{newTransactions})

	return newTransactions
}
<|endoftext|>


<commit_before>package tsdbutil

import (
	"math/rand"
	"sort"
	"testing"

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

func TestSampleRing(t *testing.T) {
	cases := []struct {
		input []int64
		delta int64
		size  int
	}{
		{
			input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			delta: 2,
			size:  1,
		},
		{
			input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			delta: 2,
			size:  2,
		},
		{
			input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			delta: 7,
			size:  3,
		},
		{
			input: []int64{1, 2, 3, 4, 5, 16, 17, 18, 19, 20},
			delta: 7,
			size:  1,
		},
	}
	for _, c := range cases {
		r := newSampleRing(c.delta, c.size)

		input := []sample{}
		for _, t := range c.input {
			input = append(input, sample{
				t: t,
				v: float64(rand.Intn(100)),
			})
		}

		for i, s := range input {
			r.add(s.t, s.v)
			buffered := r.samples()

			for _, sold := range input[:i] {
				found := false
				for _, bs := range buffered {
					if bs.t == sold.t && bs.v == sold.v {
						found = true
						break
					}
				}
				if sold.t >= s.t-c.delta && !found {
					t.Fatalf("%d: expected sample %d to be in buffer but was not; buffer %v", i, sold.t, buffered)
				}
				if sold.t < s.t-c.delta && found {
					t.Fatalf("%d: unexpected sample %d in buffer; buffer %v", i, sold.t, buffered)
				}
			}
		}
	}
}

func TestBufferedSeriesIterator(t *testing.T) {
	var it *BufferedSeriesIterator

	bufferEq := func(exp []sample) {
		var b []sample
		bit := it.Buffer()
		for bit.Next() {
			t, v := bit.At()
			b = append(b, sample{t: t, v: v})
		}
		require.Equal(t, exp, b, "buffer mismatch")
	}
	sampleEq := func(ets int64, ev float64) {
		ts, v := it.At()
		require.Equal(t, ets, ts, "timestamp mismatch")
		require.Equal(t, ev, v, "value mismatch")
	}

	it = NewBuffer(newListSeriesIterator([]sample{
		{t: 1, v: 2},
		{t: 2, v: 3},
		{t: 3, v: 4},
		{t: 4, v: 5},
		{t: 5, v: 6},
		{t: 99, v: 8},
		{t: 100, v: 9},
		{t: 101, v: 10},
	}), 2)

	require.True(t, it.Seek(-123), "seek failed")
	sampleEq(1, 2)
	bufferEq(nil)

	require.True(t, it.Next(), "next failed")
	sampleEq(2, 3)
	bufferEq([]sample{{t: 1, v: 2}})

	require.True(t, it.Next(), "next failed")
	require.True(t, it.Next(), "next failed")
	require.True(t, it.Next(), "next failed")
	sampleEq(5, 6)
	bufferEq([]sample{{t: 2, v: 3}, {t: 3, v: 4}, {t: 4, v: 5}})

	require.True(t, it.Seek(5), "seek failed")
	sampleEq(5, 6)
	bufferEq([]sample{{t: 2, v: 3}, {t: 3, v: 4}, {t: 4, v: 5}})

	require.True(t, it.Seek(101), "seek failed")
	sampleEq(101, 10)
	bufferEq([]sample{{t: 99, v: 8}, {t: 100, v: 9}})

	require.False(t, it.Next(), "next succeeded unexpectedly")
}

type listSeriesIterator struct {
	list []sample
	idx  int
}

func newListSeriesIterator(list []sample) *listSeriesIterator {
	return &listSeriesIterator{list: list, idx: -1}
}

func (it *listSeriesIterator) At() (int64, float64) {
	s := it.list[it.idx]
	return s.t, s.v
}

func (it *listSeriesIterator) Next() bool {
	it.idx++
	return it.idx < len(it.list)
}

func (it *listSeriesIterator) Seek(t int64) bool {
	if it.idx == -1 {
		it.idx = 0
	}
	// Do binary search between current position and end.
	it.idx = sort.Search(len(it.list)-it.idx, func(i int) bool {
		s := it.list[i+it.idx]
		return s.t >= t
	})

	return it.idx < len(it.list)
}

func (it *listSeriesIterator) Err() error {
	return nil
}
<commit_msg>use test utils in buffer_test<commit_after>package tsdbutil

import (
	"math/rand"
	"sort"
	"testing"

	"github.com/prometheus/tsdb/testutil"
)

func TestSampleRing(t *testing.T) {
	cases := []struct {
		input []int64
		delta int64
		size  int
	}{
		{
			input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			delta: 2,
			size:  1,
		},
		{
			input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			delta: 2,
			size:  2,
		},
		{
			input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			delta: 7,
			size:  3,
		},
		{
			input: []int64{1, 2, 3, 4, 5, 16, 17, 18, 19, 20},
			delta: 7,
			size:  1,
		},
	}
	for _, c := range cases {
		r := newSampleRing(c.delta, c.size)

		input := []sample{}
		for _, t := range c.input {
			input = append(input, sample{
				t: t,
				v: float64(rand.Intn(100)),
			})
		}

		for i, s := range input {
			r.add(s.t, s.v)
			buffered := r.samples()

			for _, sold := range input[:i] {
				found := false
				for _, bs := range buffered {
					if bs.t == sold.t && bs.v == sold.v {
						found = true
						break
					}
				}
				if sold.t >= s.t-c.delta && !found {
					t.Fatalf("%d: expected sample %d to be in buffer but was not; buffer %v", i, sold.t, buffered)
				}
				if sold.t < s.t-c.delta && found {
					t.Fatalf("%d: unexpected sample %d in buffer; buffer %v", i, sold.t, buffered)
				}
			}
		}
	}
}

func TestBufferedSeriesIterator(t *testing.T) {
	var it *BufferedSeriesIterator

	bufferEq := func(exp []sample) {
		var b []sample
		bit := it.Buffer()
		for bit.Next() {
			t, v := bit.At()
			b = append(b, sample{t: t, v: v})
		}
		testutil.Equals(t, exp, b)
	}
	sampleEq := func(ets int64, ev float64) {
		ts, v := it.At()
		testutil.Equals(t, ets, ts)
		testutil.Equals(t, ev, v)
	}

	it = NewBuffer(newListSeriesIterator([]sample{
		{t: 1, v: 2},
		{t: 2, v: 3},
		{t: 3, v: 4},
		{t: 4, v: 5},
		{t: 5, v: 6},
		{t: 99, v: 8},
		{t: 100, v: 9},
		{t: 101, v: 10},
	}), 2)

	testutil.Assert(t, it.Seek(-123) == true, "seek failed")
	sampleEq(1, 2)
	bufferEq(nil)

	testutil.Assert(t, it.Next() == true, "next failed")
	sampleEq(2, 3)
	bufferEq([]sample{{t: 1, v: 2}})

	testutil.Assert(t, it.Next() == true, "next failed")
	testutil.Assert(t, it.Next() == true, "next failed")
	testutil.Assert(t, it.Next() == true, "next failed")
	sampleEq(5, 6)
	bufferEq([]sample{{t: 2, v: 3}, {t: 3, v: 4}, {t: 4, v: 5}})

	testutil.Assert(t, it.Seek(5) == true, "seek failed")
	sampleEq(5, 6)
	bufferEq([]sample{{t: 2, v: 3}, {t: 3, v: 4}, {t: 4, v: 5}})

	testutil.Assert(t, it.Seek(101) == true, "seek failed")
	sampleEq(101, 10)
	bufferEq([]sample{{t: 99, v: 8}, {t: 100, v: 9}})

	testutil.Assert(t, it.Next() == false, "next succeeded unexpectedly")
}

type listSeriesIterator struct {
	list []sample
	idx  int
}

func newListSeriesIterator(list []sample) *listSeriesIterator {
	return &listSeriesIterator{list: list, idx: -1}
}

func (it *listSeriesIterator) At() (int64, float64) {
	s := it.list[it.idx]
	return s.t, s.v
}

func (it *listSeriesIterator) Next() bool {
	it.idx++
	return it.idx < len(it.list)
}

func (it *listSeriesIterator) Seek(t int64) bool {
	if it.idx == -1 {
		it.idx = 0
	}
	// Do binary search between current position and end.
	it.idx = sort.Search(len(it.list)-it.idx, func(i int) bool {
		s := it.list[i+it.idx]
		return s.t >= t
	})

	return it.idx < len(it.list)
}

func (it *listSeriesIterator) Err() error {
	return nil
}
<|endoftext|>


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

package web

import (
	"encoding/json"
	"encoding/xml"
	"net/http"
	"testing"
	"time"

	"github.com/issue9/assert"
	"github.com/issue9/assert/rest"
	"github.com/issue9/logs/v2"
	"golang.org/x/text/language"
	"golang.org/x/text/message"

	"github.com/issue9/web/content"
	"github.com/issue9/web/content/gob"
	"github.com/issue9/web/content/mimetypetest"
)

var f201 = func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusCreated)
	_, err := w.Write([]byte("1234567890"))
	if err != nil {
		println(err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

var f202 = func(ctx *Context) {
	ctx.Response.WriteHeader(http.StatusAccepted)
	_, err := ctx.Response.Write([]byte("1234567890"))
	if err != nil {
		println(err)
		ctx.Response.WriteHeader(http.StatusInternalServerError)
	}
}

// 声明一个 server 实例
func newServer(a *assert.Assertion) *Server {
	o := &Options{Root: "/root"}
	srv, err := NewServer(logs.New(), o)
	a.NotError(err).NotNil(srv)

	// srv.Catalog 默认指向 message.DefaultCatalog
	a.NotError(message.SetString(language.Und, "lang", "und"))
	a.NotError(message.SetString(language.SimplifiedChinese, "lang", "hans"))
	a.NotError(message.SetString(language.TraditionalChinese, "lang", "hant"))

	err = srv.Mimetypes().AddMarshals(map[string]content.MarshalFunc{
		"application/json":      json.Marshal,
		"application/xml":       xml.Marshal,
		content.DefaultMimetype: gob.Marshal,
		mimetypetest.Mimetype:   mimetypetest.TextMarshal,
	})
	a.NotError(err)

	err = srv.Mimetypes().AddUnmarshals(map[string]content.UnmarshalFunc{
		"application/json":      json.Unmarshal,
		"application/xml":       xml.Unmarshal,
		content.DefaultMimetype: gob.Unmarshal,
		mimetypetest.Mimetype:   mimetypetest.TextUnmarshal,
	})
	a.NotError(err)

	srv.AddResultMessage(411, 41110, "41110")

	return srv
}

func TestNewServer(t *testing.T) {
	a := assert.New(t)
	l := logs.New()
	srv, err := NewServer(l, &Options{})
	a.NotError(err).NotNil(srv)
	a.False(srv.Uptime().IsZero())
	a.Equal(l, srv.Logs())
	a.NotNil(srv.Cache())
	a.Equal(srv.catalog, message.DefaultCatalog)
	a.Equal(srv.Location(), time.Local)
}

func TestServer_Vars(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)

	type (
		t1 int
		t2 int64
		t3 = t2
	)
	var (
		v1 t1 = 1
		v2 t2 = 1
		v3 t3 = 1
	)

	srv.Set(v1, 1)
	srv.Set(v2, 2)
	srv.Set(v3, 3)

	a.Equal(srv.Get(v1), 1).Equal(srv.Get(v2), 3)
}

func TestServer_Serve(t *testing.T) {
	a := assert.New(t)
	exit := make(chan bool, 1)

	server := newServer(a)
	server.Router().Get("/mux/test", f202)

	m1, err := server.NewModule("m1", "m1 desc")
	a.NotError(err).NotNil(m1)
	m1.Get("/m1/test", f202)
	m1.NewTag("tag1")

	m2, err := server.NewModule("m2", "m2 desc", "m1")
	a.NotError(err).NotNil(m2)
	m2.Get("/m2/test", func(ctx *Context) {
		srv := ctx.Server()
		a.NotNil(srv)
		a.Equal(2, len(srv.Modules()))
		a.Equal(1, len(srv.Tags())).
			Equal(srv.Tags()["m1"], []string{"tag1"}).
			Nil(srv.Tags()["m2"])

		ctx.Response.WriteHeader(http.StatusAccepted)
		_, err := ctx.Response.Write([]byte("1234567890"))
		if err != nil {
			println(err)
			ctx.Response.WriteHeader(http.StatusInternalServerError)
		}
	})

	go func() {
		err := server.Serve()
		a.ErrorType(err, http.ErrServerClosed, "assert.ErrorType 错误，%v", err)
		exit <- true
	}()
	time.Sleep(5000 * time.Microsecond) // 等待 go func() 完成

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/m1/test").
		Do().
		Status(http.StatusAccepted)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/m2/test").
		Do().
		Status(http.StatusAccepted)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/mux/test").
		Do().
		Status(http.StatusAccepted)

	// static 中定义的静态文件
	server.Router().Static("/admin/{path}", "./testdata")
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/admin/file1.txt").
		Do().
		Status(http.StatusOK)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/admin/file1.txt").
		Do().
		Status(http.StatusOK)

	a.NotError(server.Close(0))
	<-exit
}

func TestServer_Close(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)
	exit := make(chan bool, 1)

	srv.Router().Get("/test", f202)
	srv.Router().Get("/close", func(ctx *Context) {
		_, err := ctx.Response.Write([]byte("closed"))
		if err != nil {
			ctx.Response.WriteHeader(http.StatusInternalServerError)
		}
		a.NotError(srv.Close(0))
	})

	go func() {
		err := srv.Serve()
		a.Error(err).ErrorType(err, http.ErrServerClosed, "错误信息为:%v", err)
		exit <- true
	}()

	// 等待 srv.Serve() 启动完毕，不同机器可能需要的时间会不同
	time.Sleep(5000 * time.Microsecond)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/test").
		Do().
		Status(http.StatusAccepted)

	// 连接被关闭，返回错误内容
	resp, err := http.Get("http://localhost/root/close")
	a.Error(err).Nil(resp)

	resp, err = http.Get("http://localhost/root/test")
	a.Error(err).Nil(resp)

	<-exit
}

func TestServer_CloseWithTimeout(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)
	exit := make(chan bool, 1)

	srv.Router().Get("/test", f202)
	srv.Router().Get("/close", func(ctx *Context) {
		ctx.Response.WriteHeader(http.StatusCreated)
		_, err := ctx.Response.Write([]byte("shutdown with ctx"))
		a.NotError(err)
		srv.Close(300 * time.Millisecond)
	})

	go func() {
		err := srv.Serve()
		a.Error(err).ErrorType(err, http.ErrServerClosed, "错误信息为:%v", err)
		exit <- true
	}()

	// 等待 srv.Serve() 启动完毕，不同机器可能需要的时间会不同
	time.Sleep(5000 * time.Microsecond)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/test").
		Do().
		Status(http.StatusAccepted)

	// 关闭指令可以正常执行
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost/root/close").
		Do().
		Status(http.StatusCreated)

	// 未超时，但是拒绝新的链接
	resp, err := http.Get("http://localhost/root/test")
	a.Error(err).Nil(resp)

	// 已被关闭
	time.Sleep(30 * time.Microsecond)
	resp, err = http.Get("http://localhost/root/test")
	a.Error(err).Nil(resp)

	<-exit
}
<commit_msg>ci: 修正 action 下 linux 无权使用 80 端口测试的错误<commit_after>// SPDX-License-Identifier: MIT

package web

import (
	"encoding/json"
	"encoding/xml"
	"net/http"
	"testing"
	"time"

	"github.com/issue9/assert"
	"github.com/issue9/assert/rest"
	"github.com/issue9/logs/v2"
	"golang.org/x/text/language"
	"golang.org/x/text/message"

	"github.com/issue9/web/content"
	"github.com/issue9/web/content/gob"
	"github.com/issue9/web/content/mimetypetest"
)

var f201 = func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusCreated)
	_, err := w.Write([]byte("1234567890"))
	if err != nil {
		println(err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

var f202 = func(ctx *Context) {
	ctx.Response.WriteHeader(http.StatusAccepted)
	_, err := ctx.Response.Write([]byte("1234567890"))
	if err != nil {
		println(err)
		ctx.Response.WriteHeader(http.StatusInternalServerError)
	}
}

// 声明一个 server 实例
func newServer(a *assert.Assertion) *Server {
	o := &Options{Root: "http://localhost:8080/root"}
	srv, err := NewServer(logs.New(), o)
	a.NotError(err).NotNil(srv)

	// srv.Catalog 默认指向 message.DefaultCatalog
	a.NotError(message.SetString(language.Und, "lang", "und"))
	a.NotError(message.SetString(language.SimplifiedChinese, "lang", "hans"))
	a.NotError(message.SetString(language.TraditionalChinese, "lang", "hant"))

	err = srv.Mimetypes().AddMarshals(map[string]content.MarshalFunc{
		"application/json":      json.Marshal,
		"application/xml":       xml.Marshal,
		content.DefaultMimetype: gob.Marshal,
		mimetypetest.Mimetype:   mimetypetest.TextMarshal,
	})
	a.NotError(err)

	err = srv.Mimetypes().AddUnmarshals(map[string]content.UnmarshalFunc{
		"application/json":      json.Unmarshal,
		"application/xml":       xml.Unmarshal,
		content.DefaultMimetype: gob.Unmarshal,
		mimetypetest.Mimetype:   mimetypetest.TextUnmarshal,
	})
	a.NotError(err)

	srv.AddResultMessage(411, 41110, "41110")

	return srv
}

func TestNewServer(t *testing.T) {
	a := assert.New(t)
	l := logs.New()
	srv, err := NewServer(l, &Options{})
	a.NotError(err).NotNil(srv)
	a.False(srv.Uptime().IsZero())
	a.Equal(l, srv.Logs())
	a.NotNil(srv.Cache())
	a.Equal(srv.catalog, message.DefaultCatalog)
	a.Equal(srv.Location(), time.Local)
}

func TestServer_Vars(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)

	type (
		t1 int
		t2 int64
		t3 = t2
	)
	var (
		v1 t1 = 1
		v2 t2 = 1
		v3 t3 = 1
	)

	srv.Set(v1, 1)
	srv.Set(v2, 2)
	srv.Set(v3, 3)

	a.Equal(srv.Get(v1), 1).Equal(srv.Get(v2), 3)
}

func TestServer_Serve(t *testing.T) {
	a := assert.New(t)
	exit := make(chan bool, 1)

	server := newServer(a)
	server.Router().Get("/mux/test", f202)

	m1, err := server.NewModule("m1", "m1 desc")
	a.NotError(err).NotNil(m1)
	m1.Get("/m1/test", f202)
	m1.NewTag("tag1")

	m2, err := server.NewModule("m2", "m2 desc", "m1")
	a.NotError(err).NotNil(m2)
	m2.Get("/m2/test", func(ctx *Context) {
		srv := ctx.Server()
		a.NotNil(srv)
		a.Equal(2, len(srv.Modules()))
		a.Equal(1, len(srv.Tags())).
			Equal(srv.Tags()["m1"], []string{"tag1"}).
			Nil(srv.Tags()["m2"])

		ctx.Response.WriteHeader(http.StatusAccepted)
		_, err := ctx.Response.Write([]byte("1234567890"))
		if err != nil {
			println(err)
			ctx.Response.WriteHeader(http.StatusInternalServerError)
		}
	})

	go func() {
		err := server.Serve()
		a.ErrorType(err, http.ErrServerClosed, "assert.ErrorType 错误，%v", err)
		exit <- true
	}()
	time.Sleep(5000 * time.Microsecond) // 等待 go func() 完成

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/m1/test").
		Do().
		Status(http.StatusAccepted)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/m2/test").
		Do().
		Status(http.StatusAccepted)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/mux/test").
		Do().
		Status(http.StatusAccepted)

	// static 中定义的静态文件
	server.Router().Static("/admin/{path}", "./testdata")
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/admin/file1.txt").
		Do().
		Status(http.StatusOK)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/admin/file1.txt").
		Do().
		Status(http.StatusOK)

	a.NotError(server.Close(0))
	<-exit
}

func TestServer_Close(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)
	exit := make(chan bool, 1)

	srv.Router().Get("/test", f202)
	srv.Router().Get("/close", func(ctx *Context) {
		_, err := ctx.Response.Write([]byte("closed"))
		if err != nil {
			ctx.Response.WriteHeader(http.StatusInternalServerError)
		}
		a.NotError(srv.Close(0))
	})

	go func() {
		err := srv.Serve()
		a.Error(err).ErrorType(err, http.ErrServerClosed, "错误信息为:%v", err)
		exit <- true
	}()

	// 等待 srv.Serve() 启动完毕，不同机器可能需要的时间会不同
	time.Sleep(5000 * time.Microsecond)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/test").
		Do().
		Status(http.StatusAccepted)

	// 连接被关闭，返回错误内容
	resp, err := http.Get("http://localhost:8080/root/close")
	a.Error(err).Nil(resp)

	resp, err = http.Get("http://localhost:8080/root/test")
	a.Error(err).Nil(resp)

	<-exit
}

func TestServer_CloseWithTimeout(t *testing.T) {
	a := assert.New(t)
	srv := newServer(a)
	exit := make(chan bool, 1)

	srv.Router().Get("/test", f202)
	srv.Router().Get("/close", func(ctx *Context) {
		ctx.Response.WriteHeader(http.StatusCreated)
		_, err := ctx.Response.Write([]byte("shutdown with ctx"))
		a.NotError(err)
		srv.Close(300 * time.Millisecond)
	})

	go func() {
		err := srv.Serve()
		a.Error(err).ErrorType(err, http.ErrServerClosed, "错误信息为:%v", err)
		exit <- true
	}()

	// 等待 srv.Serve() 启动完毕，不同机器可能需要的时间会不同
	time.Sleep(5000 * time.Microsecond)

	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/test").
		Do().
		Status(http.StatusAccepted)

	// 关闭指令可以正常执行
	rest.NewRequest(a, nil, http.MethodGet, "http://localhost:8080/root/close").
		Do().
		Status(http.StatusCreated)

	// 未超时，但是拒绝新的链接
	resp, err := http.Get("http://localhost:8080/root/test")
	a.Error(err).Nil(resp)

	// 已被关闭
	time.Sleep(30 * time.Microsecond)
	resp, err = http.Get("http://localhost:8080/root/test")
	a.Error(err).Nil(resp)

	<-exit
}
<|endoftext|>