<commit_before>package main

const dockerProfileTemplate = `@{DOCKER_GRAPH_PATH}=/var/lib/docker

profile /usr/bin/docker (attach_disconnected, complain) {
  # Prevent following links to these files during container setup.
  deny /etc/** mkl,
  deny /dev/** kl,
  deny /sys/** mkl,
  deny /proc/** mkl,

  mount -> @{DOCKER_GRAPH_PATH}/**,
  mount -> /,
  mount -> /proc/**,
  mount -> /sys/**,
  mount -> /run/docker/netns/**,
  mount -> /.pivot_root[0-9]*/,

  / r,

  umount,
  pivot_root,
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
  signal (receive) peer=@{profile_name},
  signal (receive) peer=unconfined,
  signal (send),
{{end}}{{end}}
  network,
  capability,
  owner /** rw,
  @{DOCKER_GRAPH_PATH}/** rwl,
  @{DOCKER_GRAPH_PATH}/linkgraph.db k,
  @{DOCKER_GRAPH_PATH}/network/files/boltdb.db k,
  @{DOCKER_GRAPH_PATH}/network/files/local-kv.db k,
  @{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/linkgraph.db k,

  # For non-root client use:
  /dev/urandom r,
  /dev/null rw,
  /dev/pts/[0-9]* rw,
  /run/docker.sock rw,
  /proc/** r,
  /proc/[0-9]*/attr/exec w,
  /sys/kernel/mm/hugepages/ r,
  /etc/localtime r,
  /etc/ld.so.cache r,
  /etc/passwd r,

{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
  ptrace peer=@{profile_name},
  ptrace (read) peer=docker-default,
  deny ptrace (trace) peer=docker-default,
  deny ptrace peer=/usr/bin/docker///bin/ps,
{{end}}{{end}}

  /usr/lib/** rm,
  /lib/** rm,

  /usr/bin/docker pix,
  /sbin/xtables-multi rCx,
  /sbin/iptables rCx,
  /sbin/modprobe rCx,
  /sbin/auplink rCx,
  /sbin/mke2fs rCx,
  /sbin/tune2fs rCx,
  /sbin/blkid rCx,
  /bin/kmod rCx,
  /usr/bin/xz rCx,
  /bin/ps rCx,
  /bin/cat rCx,
  /sbin/zfs rCx,
  /sbin/apparmor_parser rCx,

{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
  # Transitions
  change_profile -> docker-*,
  change_profile -> unconfined,
{{end}}{{end}}

  profile /bin/cat (complain) {
    /etc/ld.so.cache r,
    /lib/** rm,
    /dev/null rw,
    /proc r,
    /bin/cat mr,

    # For reading in 'docker stats':
    /proc/[0-9]*/net/dev r,
  }
  profile /bin/ps (complain) {
    /etc/ld.so.cache r,
    /etc/localtime r,
    /etc/passwd r,
    /etc/nsswitch.conf r,
    /lib/** rm,
    /proc/[0-9]*/** r,
    /dev/null rw,
    /bin/ps mr,

{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    # We don't need ptrace so we'll deny and ignore the error.
    deny ptrace (read, trace),
{{end}}{{end}}

    # Quiet dac_override denials
    deny capability dac_override,
    deny capability dac_read_search,
    deny capability sys_ptrace,

    /dev/tty r,
    /proc/stat r,
    /proc/cpuinfo r,
    /proc/meminfo r,
    /proc/uptime r,
    /sys/devices/system/cpu/online r,
    /proc/sys/kernel/pid_max r,
    /proc/ r,
    /proc/tty/drivers r,
  }
  profile /sbin/iptables (complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    capability net_admin,
  }
  profile /sbin/auplink flags=(attach_disconnected, complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    capability sys_admin,
    capability dac_override,

    @{DOCKER_GRAPH_PATH}/aufs/** rw,
    @{DOCKER_GRAPH_PATH}/tmp/** rw,
    # For user namespaces:
    @{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/** rw,

    /sys/fs/aufs/** r,
    /lib/** rm,
    /apparmor/.null r,
    /dev/null rw,
    /etc/ld.so.cache r,
    /sbin/auplink rm,
    /proc/fs/aufs/** rw,
    /proc/[0-9]*/mounts rw,
  }
  profile /sbin/modprobe /bin/kmod (complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    capability sys_module,
    /etc/ld.so.cache r,
    /lib/** rm,
    /dev/null rw,
    /apparmor/.null rw,
    /sbin/modprobe rm,
    /bin/kmod rm,
    /proc/cmdline r,
    /sys/module/** r,
    /etc/modprobe.d{/,/**} r,
  }
  # xz works via pipes, so we do not need access to the filesystem.
  profile /usr/bin/xz (complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    /etc/ld.so.cache r,
    /lib/** rm,
    /usr/bin/xz rm,
    deny /proc/** rw,
    deny /sys/** rw,
  }
  profile /sbin/xtables-multi (attach_disconnected, complain) {
    /etc/ld.so.cache r,
    /lib/** rm,
    /sbin/xtables-multi rm,
    /apparmor/.null w,
    /dev/null rw,

    /proc r,

    capability net_raw,
    capability net_admin,
    network raw,
  }
  profile /sbin/zfs (attach_disconnected, complain) {
    file,
    capability,
  }
  profile /sbin/mke2fs (complain) {
    /sbin/mke2fs rm,

    /lib/** rm,

    /apparmor/.null w,

    /etc/ld.so.cache r,
    /etc/mke2fs.conf r,
    /etc/mtab r,

    /dev/dm-* rw,
    /dev/urandom r,
    /dev/null rw,

    /proc/swaps r,
    /proc/[0-9]*/mounts r,
  }
  profile /sbin/tune2fs (complain) {
    /sbin/tune2fs rm,

    /lib/** rm,

    /apparmor/.null w,

    /etc/blkid.conf r,
    /etc/mtab r,
    /etc/ld.so.cache r,

    /dev/null rw,
    /dev/.blkid.tab r,
    /dev/dm-* rw,

    /proc/swaps r,
    /proc/[0-9]*/mounts r,
  }
  profile /sbin/blkid (complain) {
    /sbin/blkid rm,

    /lib/** rm,
    /apparmor/.null w,

    /etc/ld.so.cache r,
    /etc/blkid.conf r,

    /dev/null rw,
    /dev/.blkid.tab rl,
    /dev/.blkid.tab* rwl,
    /dev/dm-* r,

    /sys/devices/virtual/block/** r,

    capability mknod,

    mount -> @{DOCKER_GRAPH_PATH}/**,
  }
  profile /sbin/apparmor_parser (complain) {
    /sbin/apparmor_parser rm,

    /lib/** rm,

    /etc/ld.so.cache r,
    /etc/apparmor/** r,
    /etc/apparmor.d/** r,
    /etc/apparmor.d/cache/** w,

    /dev/null rw,

    /sys/kernel/security/apparmor/** r,
    /sys/kernel/security/apparmor/.replace w,

    /proc/[0-9]*/mounts r,
    /proc/sys/kernel/osrelease r,
    /proc r,

    capability mac_admin,
  }
}`
<commit_msg>add /bin/tar to apparmor profile<commit_after>package main

const dockerProfileTemplate = `@{DOCKER_GRAPH_PATH}=/var/lib/docker

profile /usr/bin/docker (attach_disconnected, complain) {
  # Prevent following links to these files during container setup.
  deny /etc/** mkl,
  deny /dev/** kl,
  deny /sys/** mkl,
  deny /proc/** mkl,

  mount -> @{DOCKER_GRAPH_PATH}/**,
  mount -> /,
  mount -> /proc/**,
  mount -> /sys/**,
  mount -> /run/docker/netns/**,
  mount -> /.pivot_root[0-9]*/,

  / r,

  umount,
  pivot_root,
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
  signal (receive) peer=@{profile_name},
  signal (receive) peer=unconfined,
  signal (send),
{{end}}{{end}}
  network,
  capability,
  owner /** rw,
  @{DOCKER_GRAPH_PATH}/** rwl,
  @{DOCKER_GRAPH_PATH}/linkgraph.db k,
  @{DOCKER_GRAPH_PATH}/network/files/boltdb.db k,
  @{DOCKER_GRAPH_PATH}/network/files/local-kv.db k,
  @{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/linkgraph.db k,

  # For non-root client use:
  /dev/urandom r,
  /dev/null rw,
  /dev/pts/[0-9]* rw,
  /run/docker.sock rw,
  /proc/** r,
  /proc/[0-9]*/attr/exec w,
  /sys/kernel/mm/hugepages/ r,
  /etc/localtime r,
  /etc/ld.so.cache r,
  /etc/passwd r,

{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
  ptrace peer=@{profile_name},
  ptrace (read) peer=docker-default,
  deny ptrace (trace) peer=docker-default,
  deny ptrace peer=/usr/bin/docker///bin/ps,
{{end}}{{end}}

  /usr/lib/** rm,
  /lib/** rm,

  /usr/bin/docker pix,
  /sbin/xtables-multi rCx,
  /sbin/iptables rCx,
  /sbin/modprobe rCx,
  /sbin/auplink rCx,
  /sbin/mke2fs rCx,
  /sbin/tune2fs rCx,
  /sbin/blkid rCx,
  /bin/kmod rCx,
  /usr/bin/xz rCx,
  /bin/ps rCx,
  /bin/tar rCx,
  /bin/cat rCx,
  /sbin/zfs rCx,
  /sbin/apparmor_parser rCx,

{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
  # Transitions
  change_profile -> docker-*,
  change_profile -> unconfined,
{{end}}{{end}}

  profile /bin/cat (complain) {
    /etc/ld.so.cache r,
    /lib/** rm,
    /dev/null rw,
    /proc r,
    /bin/cat mr,

    # For reading in 'docker stats':
    /proc/[0-9]*/net/dev r,
  }
  profile /bin/ps (complain) {
    /etc/ld.so.cache r,
    /etc/localtime r,
    /etc/passwd r,
    /etc/nsswitch.conf r,
    /lib/** rm,
    /proc/[0-9]*/** r,
    /dev/null rw,
    /bin/ps mr,

{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    # We don't need ptrace so we'll deny and ignore the error.
    deny ptrace (read, trace),
{{end}}{{end}}

    # Quiet dac_override denials
    deny capability dac_override,
    deny capability dac_read_search,
    deny capability sys_ptrace,

    /dev/tty r,
    /proc/stat r,
    /proc/cpuinfo r,
    /proc/meminfo r,
    /proc/uptime r,
    /sys/devices/system/cpu/online r,
    /proc/sys/kernel/pid_max r,
    /proc/ r,
    /proc/tty/drivers r,
  }
  profile /sbin/iptables (complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    capability net_admin,
  }
  profile /sbin/auplink flags=(attach_disconnected, complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    capability sys_admin,
    capability dac_override,

    @{DOCKER_GRAPH_PATH}/aufs/** rw,
    @{DOCKER_GRAPH_PATH}/tmp/** rw,
    # For user namespaces:
    @{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/** rw,

    /sys/fs/aufs/** r,
    /lib/** rm,
    /apparmor/.null r,
    /dev/null rw,
    /etc/ld.so.cache r,
    /sbin/auplink rm,
    /proc/fs/aufs/** rw,
    /proc/[0-9]*/mounts rw,
  }
  profile /sbin/modprobe /bin/kmod (complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    capability sys_module,
    /etc/ld.so.cache r,
    /lib/** rm,
    /dev/null rw,
    /apparmor/.null rw,
    /sbin/modprobe rm,
    /bin/kmod rm,
    /proc/cmdline r,
    /sys/module/** r,
    /etc/modprobe.d{/,/**} r,
  }
  # xz works via pipes, so we do not need access to the filesystem.
  profile /usr/bin/xz (complain) {
{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
    signal (receive) peer=/usr/bin/docker,
{{end}}{{end}}
    /etc/ld.so.cache r,
    /lib/** rm,
    /usr/bin/xz rm,
    deny /proc/** rw,
    deny /sys/** rw,
  }
  profile /sbin/xtables-multi (attach_disconnected, complain) {
    /etc/ld.so.cache r,
    /lib/** rm,
    /sbin/xtables-multi rm,
    /apparmor/.null w,
    /dev/null rw,

    /proc r,

    capability net_raw,
    capability net_admin,
    network raw,
  }
  profile /sbin/zfs (attach_disconnected, complain) {
    file,
    capability,
  }
  profile /sbin/mke2fs (complain) {
    /sbin/mke2fs rm,

    /lib/** rm,

    /apparmor/.null w,

    /etc/ld.so.cache r,
    /etc/mke2fs.conf r,
    /etc/mtab r,

    /dev/dm-* rw,
    /dev/urandom r,
    /dev/null rw,

    /proc/swaps r,
    /proc/[0-9]*/mounts r,
  }
  profile /sbin/tune2fs (complain) {
    /sbin/tune2fs rm,

    /lib/** rm,

    /apparmor/.null w,

    /etc/blkid.conf r,
    /etc/mtab r,
    /etc/ld.so.cache r,

    /dev/null rw,
    /dev/.blkid.tab r,
    /dev/dm-* rw,

    /proc/swaps r,
    /proc/[0-9]*/mounts r,
  }
  profile /sbin/blkid (complain) {
    /sbin/blkid rm,

    /lib/** rm,
    /apparmor/.null w,

    /etc/ld.so.cache r,
    /etc/blkid.conf r,

    /dev/null rw,
    /dev/.blkid.tab rl,
    /dev/.blkid.tab* rwl,
    /dev/dm-* r,

    /sys/devices/virtual/block/** r,

    capability mknod,

    mount -> @{DOCKER_GRAPH_PATH}/**,
  }
  profile /sbin/apparmor_parser (complain) {
    /sbin/apparmor_parser rm,

    /lib/** rm,

    /etc/ld.so.cache r,
    /etc/apparmor/** r,
    /etc/apparmor.d/** r,
    /etc/apparmor.d/cache/** w,

    /dev/null rw,

    /sys/kernel/security/apparmor/** r,
    /sys/kernel/security/apparmor/.replace w,

    /proc/[0-9]*/mounts r,
    /proc/sys/kernel/osrelease r,
    /proc r,

    capability mac_admin,
  }
}`
<|endoftext|>


<commit_before>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package store

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

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

// RWFileSystem is an interface which includes http.FileSystem and a Create method
// for creating files.
type RWFileSystem interface {
	FileSystem

	// Create a file with associated path, returns an io.WriteCloser.  Only when Close()
	// returns can it be assumed that the file has been written.
	Create(ctx context.Context, path string) (io.WriteCloser, error)

	// Wait blocks until any pending write calls have been completed.
	Wait() error
}

// dir implements RWFileSystem by extending the behaviour of http.Dir to include a Create
// method which creates files under the root.
type dir struct {
	FileSystem

	root string
}

// Dir creates a new RWFileSystem with the specified root (similar to http.Dir)
func Dir(root string) RWFileSystem {
	return &dir{
		NewFileSystem(http.Dir(root), fmt.Sprintf("local (%v)", root)),
		root,
	}
}

func (d *dir) absPath(path string) (string, error) {
	cleanPath := filepath.Clean(d.root + "/" + path)
	path, err := filepath.Abs(cleanPath)
	if err != nil {
		return "", fmt.Errorf("error finding absolute path for: '%v' ('%v'): %v", path, cleanPath, err)
	}

	absRoot, err := filepath.Abs(d.root)
	if err != nil {
		return "", fmt.Errorf("error finding absolute path for root '%v': %v", d.root, err)
	}

	if !strings.HasPrefix(filepath.Dir(path), absRoot) {
		return "", fmt.Errorf("invalid path ('%v' is outside '%v'): %v", filepath.Dir(path), absRoot, path)
	}
	return path, nil
}

// Create a file rooted in the Dir file system.
func (d *dir) Create(ctx context.Context, path string) (io.WriteCloser, error) {
	absPath, err := d.absPath(path)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(filepath.Dir(absPath), os.ModePerm)
	if err != nil {
		return nil, err
	}
	return os.Create(absPath)
}

// Wait implements RWFileSystem.
func (d *dir) Wait() error { return nil }

// CachedError is an error returned by CachedErrorFileSystems when Open errors are cached
// rather than live.
type CachedError struct {
	Err error
}

// Error implements error.
func (c *CachedError) Error() string {
	return fmt.Sprintf("cached error: %v", c.Err)
}

// CachedErrorFileSystem provides an error cache to prevent erroring FileSystem requests
// from being repeated. See open for more details.
type CachedErrorFileSystem struct {
	FileSystem

	sync.RWMutex
	m map[string]error
}

func (c *CachedErrorFileSystem) setError(path string, err error) {
	c.Lock()
	defer c.Unlock()

	c.m[path] = err
}

func (c *CachedErrorFileSystem) getError(path string) (error, bool) {
	c.RLock()
	defer c.RUnlock()

	err, ok := c.m[path]
	return err, ok
}

// Open implements FileSystem, and caches errors from the underlying FileSystem.  The first time
// an error is encountered it is returned unchanged. Subsequent calls with an erroring path
// return a CachedError-wrapped version of the original error.
func (c CachedErrorFileSystem) Open(ctx context.Context, path string) (http.File, error) {
	err, ok := c.getError(path)
	if ok {
		return nil, &CachedError{
			Err:err,
		}
	}

	f, err := c.FileSystem.Open(ctx, path)
	if err != nil {
		c.setError(path, err)
		return nil, err
	}
	return f, nil
}

// CachedFileSystem is an implemetation of http.FileServer which caches the results of
// calls to src in a RWFileSystem.
type CachedFileSystem struct {
	src   FileSystem
	cache RWFileSystem

	errCh chan<- error
	wg    sync.WaitGroup
}

// Open implements FileSystem.  If the required file isn't in the cache
// then the file is opened from the src, and then concurrently copied into the
// cache (with errors passed back on the filesystem error channel).
func (c *CachedFileSystem) Open(ctx context.Context, path string) (http.File, error) {
	f, err := c.cache.Open(ctx, path)
	if err == nil {
		return f, nil
	}

	f, err = c.src.Open(ctx, path)
	if err != nil {
		return nil, err
	}

	go func() { // TODO: improve this so that we don't have to fetch the file again!
		c.wg.Add(1)
		defer c.wg.Done()

		src, err := c.src.Open(ctx, path)
		if err != nil {
			c.errCh <- fmt.Errorf("error opening file for second time: %v", err)
			return
		}
		defer func() {
			err := src.Close()
			if err != nil {
				c.errCh <- err
			}
		}()

		cache, err := c.cache.Create(ctx, path)
		if err != nil {
			c.errCh <- fmt.Errorf("error creating file in cache: %v", err)
			return
		}
		defer func() {
			err := cache.Close()
			if err != nil {
				c.errCh <- err
			}
		}()

		_, err = io.Copy(cache, src)
		if err != nil {
			c.errCh <- fmt.Errorf("error copying src file data into cache: %v", err)
		}
	}()

	return f, nil
}

// Wait implements RWFileSystem.
func (c *CachedFileSystem) Wait() error {
	c.wg.Wait()
	return nil
}

// NewCachedFileSystem implements http.FileSystem and caches every request made to
// src in cache.  The returned error channel passes back any errors which occur when
// files are being concurrently copied into the cache.
func NewCachedFileSystem(src FileSystem, cache RWFileSystem) (*CachedFileSystem, <-chan error) {
	errCh := make(chan error)
	return &CachedFileSystem{
		src:   src,
		cache: cache,
		errCh: errCh,
	}, errCh
}
<commit_msg>Fix formatting.<commit_after>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package store

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

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

// RWFileSystem is an interface which includes http.FileSystem and a Create method
// for creating files.
type RWFileSystem interface {
	FileSystem

	// Create a file with associated path, returns an io.WriteCloser.  Only when Close()
	// returns can it be assumed that the file has been written.
	Create(ctx context.Context, path string) (io.WriteCloser, error)

	// Wait blocks until any pending write calls have been completed.
	Wait() error
}

// dir implements RWFileSystem by extending the behaviour of http.Dir to include a Create
// method which creates files under the root.
type dir struct {
	FileSystem

	root string
}

// Dir creates a new RWFileSystem with the specified root (similar to http.Dir)
func Dir(root string) RWFileSystem {
	return &dir{
		NewFileSystem(http.Dir(root), fmt.Sprintf("local (%v)", root)),
		root,
	}
}

func (d *dir) absPath(path string) (string, error) {
	cleanPath := filepath.Clean(d.root + "/" + path)
	path, err := filepath.Abs(cleanPath)
	if err != nil {
		return "", fmt.Errorf("error finding absolute path for: '%v' ('%v'): %v", path, cleanPath, err)
	}

	absRoot, err := filepath.Abs(d.root)
	if err != nil {
		return "", fmt.Errorf("error finding absolute path for root '%v': %v", d.root, err)
	}

	if !strings.HasPrefix(filepath.Dir(path), absRoot) {
		return "", fmt.Errorf("invalid path ('%v' is outside '%v'): %v", filepath.Dir(path), absRoot, path)
	}
	return path, nil
}

// Create a file rooted in the Dir file system.
func (d *dir) Create(ctx context.Context, path string) (io.WriteCloser, error) {
	absPath, err := d.absPath(path)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(filepath.Dir(absPath), os.ModePerm)
	if err != nil {
		return nil, err
	}
	return os.Create(absPath)
}

// Wait implements RWFileSystem.
func (d *dir) Wait() error { return nil }

// CachedError is an error returned by CachedErrorFileSystems when Open errors are cached
// rather than live.
type CachedError struct {
	Err error
}

// Error implements error.
func (c *CachedError) Error() string {
	return fmt.Sprintf("cached error: %v", c.Err)
}

// CachedErrorFileSystem provides an error cache to prevent erroring FileSystem requests
// from being repeated. See open for more details.
type CachedErrorFileSystem struct {
	FileSystem

	sync.RWMutex
	m map[string]error
}

func (c *CachedErrorFileSystem) setError(path string, err error) {
	c.Lock()
	defer c.Unlock()

	c.m[path] = err
}

func (c *CachedErrorFileSystem) getError(path string) (error, bool) {
	c.RLock()
	defer c.RUnlock()

	err, ok := c.m[path]
	return err, ok
}

// Open implements FileSystem, and caches errors from the underlying FileSystem.  The first time
// an error is encountered it is returned unchanged. Subsequent calls with an erroring path
// return a CachedError-wrapped version of the original error.
func (c CachedErrorFileSystem) Open(ctx context.Context, path string) (http.File, error) {
	err, ok := c.getError(path)
	if ok {
		return nil, &CachedError{
			Err: err,
		}
	}

	f, err := c.FileSystem.Open(ctx, path)
	if err != nil {
		c.setError(path, err)
		return nil, err
	}
	return f, nil
}

// CachedFileSystem is an implemetation of http.FileServer which caches the results of
// calls to src in a RWFileSystem.
type CachedFileSystem struct {
	src   FileSystem
	cache RWFileSystem

	errCh chan<- error
	wg    sync.WaitGroup
}

// Open implements FileSystem.  If the required file isn't in the cache
// then the file is opened from the src, and then concurrently copied into the
// cache (with errors passed back on the filesystem error channel).
func (c *CachedFileSystem) Open(ctx context.Context, path string) (http.File, error) {
	f, err := c.cache.Open(ctx, path)
	if err == nil {
		return f, nil
	}

	f, err = c.src.Open(ctx, path)
	if err != nil {
		return nil, err
	}

	go func() { // TODO: improve this so that we don't have to fetch the file again!
		c.wg.Add(1)
		defer c.wg.Done()

		src, err := c.src.Open(ctx, path)
		if err != nil {
			c.errCh <- fmt.Errorf("error opening file for second time: %v", err)
			return
		}
		defer func() {
			err := src.Close()
			if err != nil {
				c.errCh <- err
			}
		}()

		cache, err := c.cache.Create(ctx, path)
		if err != nil {
			c.errCh <- fmt.Errorf("error creating file in cache: %v", err)
			return
		}
		defer func() {
			err := cache.Close()
			if err != nil {
				c.errCh <- err
			}
		}()

		_, err = io.Copy(cache, src)
		if err != nil {
			c.errCh <- fmt.Errorf("error copying src file data into cache: %v", err)
		}
	}()

	return f, nil
}

// Wait implements RWFileSystem.
func (c *CachedFileSystem) Wait() error {
	c.wg.Wait()
	return nil
}

// NewCachedFileSystem implements http.FileSystem and caches every request made to
// src in cache.  The returned error channel passes back any errors which occur when
// files are being concurrently copied into the cache.
func NewCachedFileSystem(src FileSystem, cache RWFileSystem) (*CachedFileSystem, <-chan error) {
	errCh := make(chan error)
	return &CachedFileSystem{
		src:   src,
		cache: cache,
		errCh: errCh,
	}, errCh
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"sync"

	"github.com/liam-lai/ptt-alertor/mail"
	"github.com/liam-lai/ptt-alertor/ptt/article"
	"github.com/liam-lai/ptt-alertor/ptt/board"
	"github.com/liam-lai/ptt-alertor/user"
)

type articles []article.Article

var wg sync.WaitGroup

func main() {
	bs := new(board.Boards).All().WithNewArticles(true)
	users := new(user.Users).All()
	for _, bd := range bs {
		wg.Add(1)
		go checkUserSubscribeBoard(users, bd)
	}
	wg.Wait()
}

func checkUserSubscribeBoard(users user.Users, bd *board.Board) {
	defer wg.Done()
	for _, u := range users {
		wg.Add(1)
		go func(u *user.User) {
			defer wg.Done()
			for _, subscribe := range u.Subscribes {
				if bd.Name == subscribe.Board {
					for _, keyword := range subscribe.Keywords {
						keywordArticles := bd.NewArticles.ContainKeyword(keyword)
						if len(keywordArticles) != 0 {
							fmt.Println(u.Profile.Email + ":" + keyword + " in " + subscribe.Board)
							sendMail(u, subscribe.Board, keyword, keywordArticles)
						}

					}
				}
			}
		}(u)
	}
}

func sendMail(user *user.User, board string, keyword string, articles []article.Article) {
	m := new(mail.Mail)
	m.Title.BoardName = board
	m.Title.Keyword = keyword
	m.Body.Articles = articles
	m.Receiver = user.Profile.Email

	m.Send()
}
<commit_msg>refactor by goroutine channel using worker pattern, close #32<commit_after>package main

import (
	"fmt"

	"time"

	"github.com/liam-lai/ptt-alertor/mail"
	"github.com/liam-lai/ptt-alertor/ptt/article"
	"github.com/liam-lai/ptt-alertor/ptt/board"
	"github.com/liam-lai/ptt-alertor/user"
)

type Message struct {
	user     *user.User
	board    string
	keyword  string
	articles []article.Article
}

func main() {
	bs := new(board.Boards).All().WithNewArticles(true)
	users := new(user.Users).All()
	msgCh := make(chan Message)
	for _, user := range users {
		go userChecker(user, bs, msgCh)
	}

	for {
		select {
		case m := <-msgCh:
			sendMail(m)
		case <-time.After(time.Second * 3):
			fmt.Println("time out")
			return
		}
	}
}

func userChecker(user *user.User, bds board.Boards, msgCh chan Message) {
	msg := Message{
		user: user,
	}
	for _, bd := range bds {
		go subscribeChecker(user, bd, msg, msgCh)
	}
}

func subscribeChecker(user *user.User, bd *board.Board, msg Message, msgCh chan Message) {
	for _, sub := range user.Subscribes {
		if bd.Name == sub.Board {
			msg.board = sub.Board
			for _, keyword := range sub.Keywords {
				go keywordChecker(keyword, bd, msg, msgCh)
			}
		}
	}
}

func keywordChecker(keyword string, bd *board.Board, msg Message, msgCh chan Message) {
	keywordArticles := bd.NewArticles.ContainKeyword(keyword)
	if len(keywordArticles) != 0 {
		msg.keyword = keyword
		msg.articles = keywordArticles
		fmt.Printf("%+v", msg)
		msgCh <- msg
	}
}

func sendMail(msg Message) {
	m := new(mail.Mail)
	m.Title.BoardName = msg.board
	m.Title.Keyword = msg.keyword
	m.Body.Articles = msg.articles
	m.Receiver = msg.user.Profile.Email

	m.Send()
}
<|endoftext|>


<commit_before>package stream_test

import (
	"github.com/ghemawat/pipe"

	"bytes"
	"fmt"
	"os"
	"testing"
)

func ExampleSequence() {
	stream.ForEach(stream.Sequence(
		stream.Numbers(1, 25),
		stream.Grep("3"),
	), func(s string) { fmt.Println(s) })
	// Output:
	// 3
	// 13
	// 23
}

func ExampleForEach() {
	err := stream.ForEach(stream.Numbers(1, 5), func(s string) {
		fmt.Print(s)
	})
	if err != nil {
		panic(err)
	}
	// Output:
	// 12345
}

func ExampleContents() {
	out, err := stream.Contents(stream.Numbers(1, 3))
	fmt.Println(out, err)
	// Output:
	// [1 2 3] <nil>
}

func ExampleRun() {
	stream.Run(
		stream.Items("line 1", "line 2"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// line 1
	// line 2
}

func ExampleItems() {
	stream.Run(
		stream.Items("hello", "world"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// hello
	// world
}

func ExampleNumbers() {
	stream.Run(
		stream.Numbers(2, 5),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 2
	// 3
	// 4
	// 5
}

func ExampleMap() {
	stream.Run(
		stream.Items("hello", "there", "how", "are", "you?"),
		stream.Map(func(s string) string {
			return fmt.Sprintf("%d %s", len(s), s)
		}),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 5 hello
	// 5 there
	// 3 how
	// 3 are
	// 4 you?
}

func ExampleIf() {
	stream.Run(
		stream.Numbers(1, 12),
		stream.If(func(s string) bool { return len(s) > 1 }),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrep() {
	stream.Run(
		stream.Numbers(1, 12),
		stream.Grep(".."),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrepNot() {
	stream.Run(
		stream.Numbers(1, 12),
		stream.GrepNot("^.$"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleUniq() {
	stream.Run(
		stream.Items("a", "b", "b", "c", "b"),
		stream.Uniq(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// a
	// b
	// c
	// b
}

func ExampleUniqWithCount() {
	stream.Run(
		stream.Items("a", "b", "b", "c"),
		stream.UniqWithCount(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1 a
	// 2 b
	// 1 c
}

func ExampleParallel() {
	stream.Run(
		stream.Items("hello", "there", "how", "are", "you?"),
		stream.Parallel(4,
			stream.Map(func(s string) string {
				return fmt.Sprintf("%d %s", len(s), s)
			}),
		),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 3 are
	// 3 how
	// 4 you?
	// 5 hello
	// 5 there
}

func ExampleSubstitute() {
	stream.Run(
		stream.Numbers(1, 5),
		stream.Substitute("(3)", "$1$1"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 33
	// 4
	// 5
}

func ExampleSort() {
	stream.Run(
		stream.Items("banana", "apple", "cheese", "apple"),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// apple
	// apple
	// banana
	// cheese
}

func ExampleSort_multipleColumns() {
	// Sort numerically by column 1. Break ties by sorting
	// lexicographically by column 2.
	stream.Run(
		stream.Items(
			"1970 march",
			"1970 feb",
			"1950 june",
			"1980 sep",
		),
		stream.Sort().Num(1).Text(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1950 june
	// 1970 feb
	// 1970 march
	// 1980 sep
}

func ExampleSorter_Num() {
	stream.Run(
		stream.Items(
			"a 100",
			"b 20",
			"c notanumber", // Will sort last since column 2 is not a number
			"d",            // Will sort earliest since column 2 is missing
		),
		stream.Sort().Num(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// d
	// b 20
	// a 100
	// c notanumber
}

func ExampleSorter_NumDecreasing() {
	stream.Run(
		stream.Items(
			"a 100",
			"b 20",
			"c notanumber",
			"d",
		),
		stream.Sort().NumDecreasing(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// c notanumber
	// a 100
	// b 20
	// d
}

func ExampleSorter_Text() {
	stream.Run(
		stream.Items(
			"10 bananas",
			"20 apples",
			"30", // Will sort first since column 2 is missing
		),
		stream.Sort().Text(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 30
	// 20 apples
	// 10 bananas
}

func ExampleSorter_TextDecreasing() {
	stream.Run(
		stream.Items(
			"10 bananas",
			"20 apples",
			"30", // Will sort first since column 2 is missing
		),
		stream.Sort().TextDecreasing(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10 bananas
	// 20 apples
	// 30
}

func ExampleSorter_By() {
	stream.Run(
		stream.Items("bananas", "apples", "pears"),
		stream.Sort().By(func(a, b string) bool { return len(a) < len(b) }),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// pears
	// apples
	// bananas
}

func ExampleReverse() {
	stream.Run(
		stream.Items("a", "b"),
		stream.Reverse(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// b
	// a
}

func ExampleSample() {
	stream.Run(
		stream.Numbers(100, 200),
		stream.Sample(4),
		stream.WriteLines(os.Stdout),
	)
	// Output not checked since it is non-deterministic.
}

func ExampleFirst() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.First(3),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleLast() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.Last(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropFirst() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.DropFirst(8),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropLast() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.DropLast(3),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
	// 4
	// 5
	// 6
	// 7
}

func ExampleNumberLines() {
	stream.Run(
		stream.Items("a", "b"),
		stream.NumberLines(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	//     1 a
	//     2 b
}

func ExampleSlice() {
	stream.Run(
		stream.Items("hello", "world."),
		stream.Slice(2, 5),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// llo
	// rld
}

func ExampleColumns() {
	stream.Run(
		stream.Items("hello world"),
		stream.Columns(2, 3, 1),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// world hello
}

func ExampleFind() {
	stream.Run(
		stream.Find(".").Files(),
		stream.Grep("stream"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// stream.go
	// stream_test.go
}

func ExampleFindFilter_SkipDir() {
	stream.Run(
		stream.Find(".").SkipDir(".git"),
		stream.Grep("x"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// regexp.go
	// xargs.go
}

func ExampleFind_error() {
	err := stream.Run(stream.Find("/no_such_dir"))
	if err == nil {
		fmt.Println("stream.Find did not return expected error")
	}
	// Output:
}

func ExampleCat() {
	stream.Run(
		stream.Cat("stream_test.go"),
		stream.Grep("^func ExampleCat"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// func ExampleCat() {
}

func ExampleWriteLines() {
	stream.Run(
		stream.Numbers(1, 3),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleReadLines() {
	stream.Run(
		stream.ReadLines(bytes.NewBufferString("the\nquick\nbrown\nfox\n")),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// brown
	// fox
	// quick
	// the
}

func ExampleCommand() {
	stream.Run(
		stream.Numbers(1, 100),
		stream.Command("wc", "-l"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 100
}

func ExampleCommand_outputOnly() {
	stream.Run(
		stream.Command("find", ".", "-type", "f", "-print"),
		stream.Grep(`^\./stream.*\.go$`),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)

	// Output:
	// ./stream.go
	// ./stream_test.go
}

func ExampleCommand_withError() {
	err := stream.Run(stream.Command("no_such_command"))
	if err == nil {
		fmt.Println("execution of missing command succeeded unexpectedly")
	}
	// Output:
}

func ExampleXargs() {
	stream.Run(
		stream.Numbers(1, 5),
		stream.Xargs("echo"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1 2 3 4 5
}

func ExampleXargsFilter_LimitArgs() {
	stream.Run(
		stream.Numbers(1, 5),
		stream.Xargs("echo").LimitArgs(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1 2
	// 3 4
	// 5
}

func ExampleXargs_splitArguments() {
	// Xargs should split the long list of arguments into
	// three executions to keep command length below 4096.
	stream.Run(
		stream.Numbers(1, 2000),
		stream.Xargs("echo"),
		stream.Command("wc", "-l"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 3
}

func BenchmarkSingle(b *testing.B) {
	stream.Run(stream.Numbers(1, b.N))
}

func BenchmarkFive(b *testing.B) {
	f := stream.FilterFunc(func(arg stream.Arg) error {
		for s := range arg.In {
			arg.Out <- s
		}
		return nil
	})
	stream.Run(
		stream.Numbers(1, b.N),
		f,
		f,
		f,
		f,
	)
}
<commit_msg>renamed package<commit_after>package stream_test

import (
	"github.com/ghemawat/stream"

	"bytes"
	"fmt"
	"os"
	"testing"
)

func ExampleSequence() {
	stream.ForEach(stream.Sequence(
		stream.Numbers(1, 25),
		stream.Grep("3"),
	), func(s string) { fmt.Println(s) })
	// Output:
	// 3
	// 13
	// 23
}

func ExampleForEach() {
	err := stream.ForEach(stream.Numbers(1, 5), func(s string) {
		fmt.Print(s)
	})
	if err != nil {
		panic(err)
	}
	// Output:
	// 12345
}

func ExampleContents() {
	out, err := stream.Contents(stream.Numbers(1, 3))
	fmt.Println(out, err)
	// Output:
	// [1 2 3] <nil>
}

func ExampleRun() {
	stream.Run(
		stream.Items("line 1", "line 2"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// line 1
	// line 2
}

func ExampleItems() {
	stream.Run(
		stream.Items("hello", "world"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// hello
	// world
}

func ExampleNumbers() {
	stream.Run(
		stream.Numbers(2, 5),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 2
	// 3
	// 4
	// 5
}

func ExampleMap() {
	stream.Run(
		stream.Items("hello", "there", "how", "are", "you?"),
		stream.Map(func(s string) string {
			return fmt.Sprintf("%d %s", len(s), s)
		}),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 5 hello
	// 5 there
	// 3 how
	// 3 are
	// 4 you?
}

func ExampleIf() {
	stream.Run(
		stream.Numbers(1, 12),
		stream.If(func(s string) bool { return len(s) > 1 }),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrep() {
	stream.Run(
		stream.Numbers(1, 12),
		stream.Grep(".."),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleGrepNot() {
	stream.Run(
		stream.Numbers(1, 12),
		stream.GrepNot("^.$"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10
	// 11
	// 12
}

func ExampleUniq() {
	stream.Run(
		stream.Items("a", "b", "b", "c", "b"),
		stream.Uniq(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// a
	// b
	// c
	// b
}

func ExampleUniqWithCount() {
	stream.Run(
		stream.Items("a", "b", "b", "c"),
		stream.UniqWithCount(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1 a
	// 2 b
	// 1 c
}

func ExampleParallel() {
	stream.Run(
		stream.Items("hello", "there", "how", "are", "you?"),
		stream.Parallel(4,
			stream.Map(func(s string) string {
				return fmt.Sprintf("%d %s", len(s), s)
			}),
		),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 3 are
	// 3 how
	// 4 you?
	// 5 hello
	// 5 there
}

func ExampleSubstitute() {
	stream.Run(
		stream.Numbers(1, 5),
		stream.Substitute("(3)", "$1$1"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 33
	// 4
	// 5
}

func ExampleSort() {
	stream.Run(
		stream.Items("banana", "apple", "cheese", "apple"),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// apple
	// apple
	// banana
	// cheese
}

func ExampleSort_multipleColumns() {
	// Sort numerically by column 1. Break ties by sorting
	// lexicographically by column 2.
	stream.Run(
		stream.Items(
			"1970 march",
			"1970 feb",
			"1950 june",
			"1980 sep",
		),
		stream.Sort().Num(1).Text(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1950 june
	// 1970 feb
	// 1970 march
	// 1980 sep
}

func ExampleSorter_Num() {
	stream.Run(
		stream.Items(
			"a 100",
			"b 20",
			"c notanumber", // Will sort last since column 2 is not a number
			"d",            // Will sort earliest since column 2 is missing
		),
		stream.Sort().Num(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// d
	// b 20
	// a 100
	// c notanumber
}

func ExampleSorter_NumDecreasing() {
	stream.Run(
		stream.Items(
			"a 100",
			"b 20",
			"c notanumber",
			"d",
		),
		stream.Sort().NumDecreasing(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// c notanumber
	// a 100
	// b 20
	// d
}

func ExampleSorter_Text() {
	stream.Run(
		stream.Items(
			"10 bananas",
			"20 apples",
			"30", // Will sort first since column 2 is missing
		),
		stream.Sort().Text(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 30
	// 20 apples
	// 10 bananas
}

func ExampleSorter_TextDecreasing() {
	stream.Run(
		stream.Items(
			"10 bananas",
			"20 apples",
			"30", // Will sort first since column 2 is missing
		),
		stream.Sort().TextDecreasing(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 10 bananas
	// 20 apples
	// 30
}

func ExampleSorter_By() {
	stream.Run(
		stream.Items("bananas", "apples", "pears"),
		stream.Sort().By(func(a, b string) bool { return len(a) < len(b) }),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// pears
	// apples
	// bananas
}

func ExampleReverse() {
	stream.Run(
		stream.Items("a", "b"),
		stream.Reverse(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// b
	// a
}

func ExampleSample() {
	stream.Run(
		stream.Numbers(100, 200),
		stream.Sample(4),
		stream.WriteLines(os.Stdout),
	)
	// Output not checked since it is non-deterministic.
}

func ExampleFirst() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.First(3),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleLast() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.Last(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropFirst() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.DropFirst(8),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 9
	// 10
}

func ExampleDropLast() {
	stream.Run(
		stream.Numbers(1, 10),
		stream.DropLast(3),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
	// 4
	// 5
	// 6
	// 7
}

func ExampleNumberLines() {
	stream.Run(
		stream.Items("a", "b"),
		stream.NumberLines(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	//     1 a
	//     2 b
}

func ExampleSlice() {
	stream.Run(
		stream.Items("hello", "world."),
		stream.Slice(2, 5),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// llo
	// rld
}

func ExampleColumns() {
	stream.Run(
		stream.Items("hello world"),
		stream.Columns(2, 3, 1),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// world hello
}

func ExampleFind() {
	stream.Run(
		stream.Find(".").Files(),
		stream.Grep("stream"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// stream.go
	// stream_test.go
}

func ExampleFindFilter_SkipDir() {
	stream.Run(
		stream.Find(".").SkipDir(".git"),
		stream.Grep("x"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// regexp.go
	// xargs.go
}

func ExampleFind_error() {
	err := stream.Run(stream.Find("/no_such_dir"))
	if err == nil {
		fmt.Println("stream.Find did not return expected error")
	}
	// Output:
}

func ExampleCat() {
	stream.Run(
		stream.Cat("stream_test.go"),
		stream.Grep("^func ExampleCat"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// func ExampleCat() {
}

func ExampleWriteLines() {
	stream.Run(
		stream.Numbers(1, 3),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1
	// 2
	// 3
}

func ExampleReadLines() {
	stream.Run(
		stream.ReadLines(bytes.NewBufferString("the\nquick\nbrown\nfox\n")),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// brown
	// fox
	// quick
	// the
}

func ExampleCommand() {
	stream.Run(
		stream.Numbers(1, 100),
		stream.Command("wc", "-l"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 100
}

func ExampleCommand_outputOnly() {
	stream.Run(
		stream.Command("find", ".", "-type", "f", "-print"),
		stream.Grep(`^\./stream.*\.go$`),
		stream.Sort(),
		stream.WriteLines(os.Stdout),
	)

	// Output:
	// ./stream.go
	// ./stream_test.go
}

func ExampleCommand_withError() {
	err := stream.Run(stream.Command("no_such_command"))
	if err == nil {
		fmt.Println("execution of missing command succeeded unexpectedly")
	}
	// Output:
}

func ExampleXargs() {
	stream.Run(
		stream.Numbers(1, 5),
		stream.Xargs("echo"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1 2 3 4 5
}

func ExampleXargsFilter_LimitArgs() {
	stream.Run(
		stream.Numbers(1, 5),
		stream.Xargs("echo").LimitArgs(2),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 1 2
	// 3 4
	// 5
}

func ExampleXargs_splitArguments() {
	// Xargs should split the long list of arguments into
	// three executions to keep command length below 4096.
	stream.Run(
		stream.Numbers(1, 2000),
		stream.Xargs("echo"),
		stream.Command("wc", "-l"),
		stream.WriteLines(os.Stdout),
	)
	// Output:
	// 3
}

func BenchmarkSingle(b *testing.B) {
	stream.Run(stream.Numbers(1, b.N))
}

func BenchmarkFive(b *testing.B) {
	f := stream.FilterFunc(func(arg stream.Arg) error {
		for s := range arg.In {
			arg.Out <- s
		}
		return nil
	})
	stream.Run(
		stream.Numbers(1, b.N),
		f,
		f,
		f,
		f,
	)
}
<|endoftext|>


<commit_before>package gofakeit

import (
	"fmt"
	"testing"
)

type Basic struct {
	s string
	S string
}

type Nested struct {
	A   string
	B   *Basic
	bar *Basic
}

type BuiltIn struct {
	Uint8   *uint8
	Uint16  *uint16
	Uint32  *uint32
	Uint64  *uint64
	Int     *int
	Int8    *int8
	Int16   *int16
	Int32   *int32
	Int64   *int64
	Float32 *float32
	Float64 *float64
	Bool    *bool
}

type Template struct {
	Number *string `fake:"#"`
	Name   *string `fake:"{person.first}"`
}

func TestStructBasic(t *testing.T) {
	var basic Basic
	Struct(&basic)
	if basic.s != "" {
		t.Error("unexported field is not populated")
	}
	if basic.S == "" {
		t.Error("exported field is populated")
	}
}

func TestStructNested(t *testing.T) {
	var nested Nested
	Struct(&nested)
	if nested.A == "" {
		t.Error("exported string field is populated")
	}
	if nested.B == nil {
		t.Error("exported struct field is populated")
	}
	if nested.B.S == "" {
		t.Error("nested struct string field is not populated")
	}
	if nested.bar != nil {
		t.Error("nested struct bar should be be populated due to unexported variable")
	}
}

func TestStructBuiltInTypes(t *testing.T) {
	var builtIn BuiltIn
	Struct(&builtIn)
	if builtIn.Uint8 == nil {
		t.Error("builtIn Uint8 was not set")
	}
	if builtIn.Uint16 == nil {
		t.Error("builtIn Uint16 was not set")
	}
	if builtIn.Uint32 == nil {
		t.Error("builtIn Uint32 was not set")
	}
	if builtIn.Uint64 == nil {
		t.Error("builtIn Uint64 was not set")
	}
	if builtIn.Int == nil {
		t.Error("builtIn int was not set")
	}
	if builtIn.Int8 == nil {
		t.Error("builtIn int8 was not set")
	}
	if builtIn.Int16 == nil {
		t.Error("builtIn int16 was not set")
	}
	if builtIn.Int32 == nil {
		t.Error("builtIn int32 was not set")
	}
	if builtIn.Int64 == nil {
		t.Error("builtIn int64 was not set")
	}
	if builtIn.Float32 == nil {
		t.Error("builtIn float32 was not set")
	}
	if builtIn.Float64 == nil {
		t.Error("builtIn float64 was not set")
	}
	if builtIn.Bool == nil {
		t.Error("builtIn bool was not set")
	}
}

func TestStructWithTemplate(t *testing.T) {
	var template Template
	Struct(&template)
	if *template.Number == "" {
		t.Error("template number should set to number value")
	}
	if *template.Name == "" {
		t.Error("template number should set to number value")
	}
}

func ExampleStruct() {
	Seed(11)
	type Foo struct {
		Bar     string
		Baz     string
		Int     int
		Pointer *int
		Skip    *string `fake:"skip"`
	}
	var f Foo
	Struct(&f)
	fmt.Printf("%s\n", f.Bar)
	fmt.Printf("%s\n", f.Baz)
	fmt.Printf("%d\n", f.Int)
	fmt.Printf("%d\n", *f.Pointer)
	fmt.Printf("%v\n", f.Skip)
	// Output: gbrmarxhkijbptapwyj
	// dnsmkgtlxwnqhqclayk
	// -5858358572185296359
	// -8038678955577270446
	// <nil>
}
<commit_msg>struct - minor update to fix seeing it in docs and minor cleanup<commit_after>package gofakeit

import (
	"fmt"
	"testing"
)

type Basic struct {
	s string
	S string
}

type Nested struct {
	A   string
	B   *Basic
	bar *Basic
}

type BuiltIn struct {
	Uint8   *uint8
	Uint16  *uint16
	Uint32  *uint32
	Uint64  *uint64
	Int     *int
	Int8    *int8
	Int16   *int16
	Int32   *int32
	Int64   *int64
	Float32 *float32
	Float64 *float64
	Bool    *bool
}

type Template struct {
	Number *string `fake:"#"`
	Name   *string `fake:"{person.first}"`
}

func TestStructBasic(t *testing.T) {
	var basic Basic
	Struct(&basic)
	if basic.s != "" {
		t.Error("unexported field is not populated")
	}
	if basic.S == "" {
		t.Error("exported field is populated")
	}
}

func TestStructNested(t *testing.T) {
	var nested Nested
	Struct(&nested)
	if nested.A == "" {
		t.Error("exported string field is populated")
	}
	if nested.B == nil {
		t.Error("exported struct field is populated")
	}
	if nested.B.S == "" {
		t.Error("nested struct string field is not populated")
	}
	if nested.bar != nil {
		t.Error("nested struct bar should be be populated due to unexported variable")
	}
}

func TestStructBuiltInTypes(t *testing.T) {
	var builtIn BuiltIn
	Struct(&builtIn)
	if builtIn.Uint8 == nil {
		t.Error("builtIn Uint8 was not set")
	}
	if builtIn.Uint16 == nil {
		t.Error("builtIn Uint16 was not set")
	}
	if builtIn.Uint32 == nil {
		t.Error("builtIn Uint32 was not set")
	}
	if builtIn.Uint64 == nil {
		t.Error("builtIn Uint64 was not set")
	}
	if builtIn.Int == nil {
		t.Error("builtIn int was not set")
	}
	if builtIn.Int8 == nil {
		t.Error("builtIn int8 was not set")
	}
	if builtIn.Int16 == nil {
		t.Error("builtIn int16 was not set")
	}
	if builtIn.Int32 == nil {
		t.Error("builtIn int32 was not set")
	}
	if builtIn.Int64 == nil {
		t.Error("builtIn int64 was not set")
	}
	if builtIn.Float32 == nil {
		t.Error("builtIn float32 was not set")
	}
	if builtIn.Float64 == nil {
		t.Error("builtIn float64 was not set")
	}
	if builtIn.Bool == nil {
		t.Error("builtIn bool was not set")
	}
}

func TestStructWithTemplate(t *testing.T) {
	var template Template
	Struct(&template)
	if *template.Number == "" {
		t.Error("template number should set to number value")
	}
	if *template.Name == "" {
		t.Error("template number should set to number value")
	}
}

func Example_struct() {
	Seed(11)

	type Foo struct {
		Bar     string
		Int     int
		Pointer *int
		Name    string  `fake:"{person.first}"`
		Skip    *string `fake:"skip"`
	}

	var f Foo
	Struct(&f)

	fmt.Printf("%s\n", f.Bar)
	fmt.Printf("%d\n", f.Int)
	fmt.Printf("%d\n", *f.Pointer)
	fmt.Printf("%v\n", f.Name)
	fmt.Printf("%v\n", f.Skip)

	// Output: gbrmarxhkijbptapwyj
	// -3430133205295092491
	// -2330884613995904932
	// Fred
	// <nil>
}
<|endoftext|>


<commit_before>package main

import (
	"delay"

	"nrf5/hal/clock"
	"nrf5/hal/gpio"
	"nrf5/hal/irq"
	"nrf5/hal/rtc"
	"nrf5/hal/system"
	"nrf5/hal/system/timer/rtcst"
)

// Two BW428G-E4 four digits, LED displays (common cathode).
type Display struct {
	dig [8]gpio.Pin // 0-3 top display, 4-7 bottom display.
	seg [8]gpio.Pin // A B C D E F G :
}

var disp Display

func init() {
	// Initialize system and runtime.
	system.Setup(clock.XTAL, clock.XTAL, true)
	rtcst.Setup(rtc.RTC0, 1)

	// Allocate pins (always do it in one place to avoid conflicts).

	p0 := gpio.P0
	disp.seg[5] = p0.Pin(0)  // F
	disp.dig[5] = p0.Pin(1)  // Bottom 1
	disp.dig[6] = p0.Pin(2)  // Bottom 2
	disp.seg[3] = p0.Pin(3)  // D
	disp.seg[4] = p0.Pin(9)  // E
	disp.dig[7] = p0.Pin(11) // Bottom 3
	disp.dig[2] = p0.Pin(15) // Top 2
	disp.seg[6] = p0.Pin(17) // 6
	disp.seg[1] = p0.Pin(22) // B
	disp.seg[2] = p0.Pin(23) // C
	disp.seg[7] = p0.Pin(21) // :
	disp.dig[0] = p0.Pin(24) // Top 0
	disp.dig[3] = p0.Pin(25) // Top 3
	disp.seg[0] = p0.Pin(28) // A
	disp.dig[1] = p0.Pin(29) // Top 1
	disp.dig[4] = p0.Pin(30) // Bottom 0

	// Configure pins.

	for _, pin := range disp.dig {
		if !pin.IsValid() {
			continue
		}
		// Drive digits with higd drive, open collector.
		pin.Set()
		pin.Setup(gpio.ModeOut | gpio.DriveH0D1)
	}
	for _, pin := range disp.seg {
		if !pin.IsValid() {
			continue
		}
		pin.Setup(gpio.ModeOut | gpio.DriveD0H1)
	}
}

func wait() {
	delay.Millisec(1000)
}

func main() {
	disp.seg[0].Set()
	disp.seg[1].Set()
	disp.seg[2].Set()
	disp.seg[3].Set()
	disp.seg[4].Set()
	disp.seg[5].Set()
	disp.seg[6].Set()
	disp.seg[7].Set()
	for {
		disp.dig[7].Set()
		disp.dig[0].Clear()
		wait()
		disp.dig[0].Set()
		disp.dig[1].Clear()
		wait()
		disp.dig[1].Set()
		disp.dig[2].Clear()
		wait()
		disp.dig[2].Set()
		disp.dig[3].Clear()
		wait()
		disp.dig[3].Set()
		disp.dig[4].Clear()
		wait()
		disp.dig[4].Set()
		disp.dig[5].Clear()
		wait()
		disp.dig[5].Set()
		disp.dig[6].Clear()
		wait()
		disp.dig[6].Set()
		disp.dig[7].Clear()
		wait()
	}
}

//emgo:const
//c:__attribute__((section(".ISRs")))
var ISRs = [...]func(){
	irq.RTC0: rtcst.ISR,
}
<commit_msg>examples/core51822/ventilation: Display test.<commit_after>// This code drives ventilation unit controller. Ventilation unit consist of:
// a counter-flow recuperator, two DC fans (EBM R1G225-AF33-12) and two air
// filters.
//
// Controller produces two PWM signals to contoll both fans. The current fan
// speed is mesured by counting pulses from speed output. The desired speed can
// be set using rotary encoder. Controller has two 4-digit 7-segment displays,
// that shows the current speed of both fans.
package main

import (
	"delay"

	"nrf5/hal/clock"
	"nrf5/hal/gpio"
	"nrf5/hal/irq"
	"nrf5/hal/rtc"
	"nrf5/hal/system"
	"nrf5/hal/system/timer/rtcst"
)

const (
	A = iota
	B
	C
	D
	E
	F
	G
	Q
)

// Two 4-digit 7-segment displays (BW428G-E4, common cathode).
type Display struct {
	dig    [8]gpio.Pins // 0-3 top display, 4-7 bottom display.
	seg    [8]gpio.Pins // A B C D E F G :
	digAll gpio.Pins
	segAll gpio.Pins
}

var disp Display

func init() {
	// Initialize system and runtime.
	system.Setup(clock.XTAL, clock.XTAL, true)
	rtcst.Setup(rtc.RTC0, 1)

	// Allocate pins (always do it in one place to avoid conflicts).

	p0 := gpio.P0
	disp.seg[F] = gpio.Pin0  // F
	disp.dig[5] = gpio.Pin1  // Bottom 1
	disp.dig[6] = gpio.Pin2  // Bottom 2
	disp.seg[D] = gpio.Pin3  // D
	disp.seg[E] = gpio.Pin9  // E
	disp.dig[7] = gpio.Pin11 // Bottom 3
	disp.dig[2] = gpio.Pin15 // Top 2
	disp.seg[G] = gpio.Pin17 // G
	disp.seg[B] = gpio.Pin22 // B
	disp.seg[C] = gpio.Pin23 // C
	disp.seg[Q] = gpio.Pin21 // :
	disp.dig[0] = gpio.Pin24 // Top 0
	disp.dig[3] = gpio.Pin25 // Top 3
	disp.seg[A] = gpio.Pin28 // A
	disp.dig[1] = gpio.Pin29 // Top 1
	disp.dig[4] = gpio.Pin30 // Bottom 0

	// Configure pins.

	for _, pin := range disp.dig {
		disp.digAll |= pin
	}
	for _, pin := range disp.seg {
		disp.segAll |= pin
	}
	// Drive digits with higd drive, open drain (n-channel).
	p0.SetPins(disp.digAll)
	p0.Setup(disp.digAll, gpio.ModeOut|gpio.DriveH0D1)
	// Drive segments with higd drive, open drain (p-channel).
	p0.Setup(disp.segAll, gpio.ModeOut|gpio.DriveD0H1)
}

func wait() {
	delay.Millisec(500)
}

func main() {
	p0 := gpio.P0
	for {
		for _, dig := range disp.dig {
			p0.SetPins(disp.digAll)
			p0.ClearPins(dig)
			for _, seg := range disp.seg {
				p0.ClearPins(disp.segAll)
				p0.SetPins(seg)
				delay.Millisec(200)
			}
		}
	}
}

//emgo:const
//c:__attribute__((section(".ISRs")))
var ISRs = [...]func(){
	irq.RTC0: rtcst.ISR,
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/AnimateX/iSCSI-Server/src/serv/mylib"
)

func main() {

	fmt.Printf("Hej! %v \n", mylib.Annat())

}

<commit_msg>updated gopath<commit_after>package main

import (
	"fmt"
	"iSCSI-Server/src/serv/mylib"
)

func main() {

	fmt.Printf("Hej! %v \n", mylib.Annat())

}

<|endoftext|>


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

package persist

import (
	"encoding/csv"
	"strings"

	"github.com/casbin/casbin/v2/model"
)

// LoadPolicyLine loads a text line as a policy rule to model.
func LoadPolicyLine(line string, m model.Model) {
	if line == "" || strings.HasPrefix(line, "#") {
		return
	}

	r := csv.NewReader(strings.NewReader(line))
	r.Comma = ','
	r.Comment = '#'
	r.TrimLeadingSpace = true

	tokens, err := r.Read()
	if err != nil {
		return
	}

	key := tokens[0]
	sec := key[:1]
	m[sec][key].Policy = append(m[sec][key].Policy, tokens[1:])
	m[sec][key].PolicyMap[strings.Join(tokens[1:], model.DefaultSep)] = len(m[sec][key].Policy) - 1
}

// Adapter is the interface for Casbin adapters.
type Adapter interface {
	// LoadPolicy loads all policy rules from the storage.
	LoadPolicy(model model.Model) error
	// SavePolicy saves all policy rules to the storage.
	SavePolicy(model model.Model) error

	// AddPolicy adds a policy rule to the storage.
	// This is part of the Auto-Save feature.
	AddPolicy(sec string, ptype string, rule []string) error
	// RemovePolicy removes a policy rule from the storage.
	// This is part of the Auto-Save feature.
	RemovePolicy(sec string, ptype string, rule []string) error
	// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
	// This is part of the Auto-Save feature.
	RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error
}
<commit_msg>feat: add LoadPolicyArray() to load policy from array<commit_after>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package persist

import (
	"encoding/csv"
	"strings"

	"github.com/casbin/casbin/v2/model"
)

// LoadPolicyLine loads a text line as a policy rule to model.
func LoadPolicyLine(line string, m model.Model) {
	if line == "" || strings.HasPrefix(line, "#") {
		return
	}

	r := csv.NewReader(strings.NewReader(line))
	r.Comma = ','
	r.Comment = '#'
	r.TrimLeadingSpace = true

	tokens, err := r.Read()
	if err != nil {
		return
	}

	LoadPolicyArray(tokens, m)
}

// LoadPolicyArray loads a policy rule to model.
func LoadPolicyArray(rule []string, m model.Model) {
	key := rule[0]
	sec := key[:1]
	m[sec][key].Policy = append(m[sec][key].Policy, rule[1:])
	m[sec][key].PolicyMap[strings.Join(rule[1:], model.DefaultSep)] = len(m[sec][key].Policy) - 1
}

// Adapter is the interface for Casbin adapters.
type Adapter interface {
	// LoadPolicy loads all policy rules from the storage.
	LoadPolicy(model model.Model) error
	// SavePolicy saves all policy rules to the storage.
	SavePolicy(model model.Model) error

	// AddPolicy adds a policy rule to the storage.
	// This is part of the Auto-Save feature.
	AddPolicy(sec string, ptype string, rule []string) error
	// RemovePolicy removes a policy rule from the storage.
	// This is part of the Auto-Save feature.
	RemovePolicy(sec string, ptype string, rule []string) error
	// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
	// This is part of the Auto-Save feature.
	RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error
}
<|endoftext|>


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

package command

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path"
	"strconv"

	"github.com/spf13/cobra"
)

var (
	storesPrefix = "pd/api/v1/stores"
	storePrefix  = "pd/api/v1/store/%v"
)

// NewStoreCommand return a stores subcommand of rootCmd
func NewStoreCommand() *cobra.Command {
	s := &cobra.Command{
		Use:   `store [delete|label|weight|limit] <store_id> [--jq="<query string>"]`,
		Short: "show the store status",
		Run:   showStoreCommandFunc,
	}
	s.AddCommand(NewDeleteStoreCommand())
	s.AddCommand(NewLabelStoreCommand())
	s.AddCommand(NewSetStoreWeightCommand())
	s.AddCommand(NewSetStoreLimitCommand())
	s.Flags().String("jq", "", "jq query")
	return s
}

// NewDeleteStoreByAddrCommand returns a subcommand of delete
func NewDeleteStoreByAddrCommand() *cobra.Command {
	d := &cobra.Command{
		Use:   "addr <address>",
		Short: "delete store by its address",
		Run:   deleteStoreCommandByAddrFunc,
	}
	return d
}

// NewDeleteStoreCommand return a  delete subcommand of storeCmd
func NewDeleteStoreCommand() *cobra.Command {
	d := &cobra.Command{
		Use:   "delete <store_id>",
		Short: "delete the store",
		Run:   deleteStoreCommandFunc,
	}
	d.AddCommand(NewDeleteStoreByAddrCommand())
	return d
}

// NewLabelStoreCommand returns a label subcommand of storeCmd.
func NewLabelStoreCommand() *cobra.Command {
	l := &cobra.Command{
		Use:   "label <store_id> <key> <value>",
		Short: "set a store's label value",
		Run:   labelStoreCommandFunc,
	}
	return l
}

// NewSetStoreWeightCommand returns a weight subcommand of storeCmd.
func NewSetStoreWeightCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "weight <store_id> <leader_weight> <region_weight>",
		Short: "set a store's leader and region balance weight",
		Run:   setStoreWeightCommandFunc,
	}
}

// NewSetStoreLimitCommand returns a limit subcommand of storeCmd.
func NewSetStoreLimitCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "limit <store_id> <rate>",
		Short: "set a store's rate limit",
		Run:   setStoreLimitCommandFunc,
	}
}

// NewStoresCommand returns a store subcommand of rootCmd
func NewStoresCommand() *cobra.Command {
	s := &cobra.Command{
		Use:   `stores [subcommand] [--jq="<query string>"]`,
		Short: "store status",
	}
	s.AddCommand(NewRemoveTombStoneCommand())
	s.AddCommand(NewSetStoresCommand())
	s.AddCommand(NewShowStoresCommand())
	s.Flags().String("jq", "", "jq query")
	return s
}

// NewRemoveTombStoneCommand returns a tombstone subcommand of storesCmd.
func NewRemoveTombStoneCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "remove-tombstone",
		Short: "remove tombstone record if only safe",
		Run:   removeTombStoneCommandFunc,
	}
}

// NewShowStoresCommand returns a show subcommand of storesCmd.
func NewShowStoresCommand() *cobra.Command {
	sc := &cobra.Command{
		Use:   "show [limit]",
		Short: "show the stores",
		Run:   showStoresCommandFunc,
	}
	sc.AddCommand(NewShowAllLimitCommand())
	return sc
}

// NewShowAllLimitCommand return a show limit subcommand of show command
func NewShowAllLimitCommand() *cobra.Command {
	sc := &cobra.Command{
		Use:   "limit",
		Short: "show all stores' limit",
		Run:   showAllLimitCommandFunc,
	}
	return sc
}

// NewSetStoresCommand returns a set subcommand of storesCmd.
func NewSetStoresCommand() *cobra.Command {
	sc := &cobra.Command{
		Use:   "set [limit]",
		Short: "set stores",
	}
	sc.AddCommand(NewSetAllLimitCommand())
	return sc
}

// NewSetAllLimitCommand returns a set limit subcommand of set command.
func NewSetAllLimitCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "limit <rate>",
		Short: "set all store's rate limit",
		Run:   setAllLimitCommandFunc,
	}
}

func showStoreCommandFunc(cmd *cobra.Command, args []string) {
	prefix := storesPrefix
	if len(args) == 1 {
		if _, err := strconv.Atoi(args[0]); err != nil {
			cmd.Println("store_id should be a number")
			return
		}
		prefix = fmt.Sprintf(storePrefix, args[0])
	}
	r, err := doRequest(cmd, prefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get store: %s\n", err)
		return
	}
	if flag := cmd.Flag("jq"); flag != nil && flag.Value.String() != "" {
		printWithJQFilter(r, flag.Value.String())
		return
	}
	cmd.Println(r)
}

func deleteStoreCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		cmd.Usage()
		return
	}
	if _, err := strconv.Atoi(args[0]); err != nil {
		cmd.Println("store_id should be a number")
		return
	}
	prefix := fmt.Sprintf(storePrefix, args[0])
	_, err := doRequest(cmd, prefix, http.MethodDelete)
	if err != nil {
		cmd.Printf("Failed to delete store %s: %s\n", args[0], err)
		return
	}
	cmd.Println("Success!")
}

func deleteStoreCommandByAddrFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		cmd.Usage()
		return
	}
	addr := args[0]

	// fetch all the stores
	r, err := doRequest(cmd, storesPrefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get store: %s\n", err)
		return
	}

	storeInfo := struct {
		Stores []struct {
			Store struct {
				ID      int    `json:"id"`
				Address string `json:"address"`
			} `json:"store"`
		} `json:"stores"`
	}{}
	if err = json.Unmarshal([]byte(r), &storeInfo); err != nil {
		cmd.Printf("Failed to parse store info: %s\n", err)
		return
	}

	// filter by the addr
	id := -1
	for _, store := range storeInfo.Stores {
		if store.Store.Address == addr {
			id = store.Store.ID
			break
		}
	}

	if id == -1 {
		cmd.Printf("address not found: %s\n", addr)
		return
	}

	// delete store by its ID
	prefix := fmt.Sprintf(storePrefix, id)
	_, err = doRequest(cmd, prefix, http.MethodDelete)
	if err != nil {
		cmd.Printf("Failed to delete store %s: %s\n", args[0], err)
		return
	}
	cmd.Println("Success!")
}

func labelStoreCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 3 {
		cmd.Println("Usage: store label <store_id> <key> <value>")
		return
	}
	if _, err := strconv.Atoi(args[0]); err != nil {
		cmd.Println("store_id should be a number")
		return
	}
	prefix := fmt.Sprintf(path.Join(storePrefix, "label"), args[0])
	postJSON(cmd, prefix, map[string]interface{}{args[1]: args[2]})
}

func setStoreWeightCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 3 {
		cmd.Println("Usage: store weight <store_id> <leader_weight> <region_weight>")
		return
	}
	leader, err := strconv.ParseFloat(args[1], 64)
	if err != nil || leader < 0 {
		cmd.Println("leader_weight should be a number that >= 0.")
		return
	}
	region, err := strconv.ParseFloat(args[2], 64)
	if err != nil || region < 0 {
		cmd.Println("region_weight should be a number that >= 0")
		return
	}
	prefix := fmt.Sprintf(path.Join(storePrefix, "weight"), args[0])
	postJSON(cmd, prefix, map[string]interface{}{
		"leader": leader,
		"region": region,
	})
}

func setStoreLimitCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		cmd.Println("Usage: store limit <store_id> <rate>")
		return
	}
	rate, err := strconv.ParseFloat(args[1], 64)
	if err != nil || rate < 0 {
		cmd.Println("rate should be a number that >= 0.")
		return
	}
	prefix := fmt.Sprintf(path.Join(storePrefix, "limit"), args[0])
	postJSON(cmd, prefix, map[string]interface{}{
		"rate": rate,
	})
}

func showStoresCommandFunc(cmd *cobra.Command, args []string) {
	prefix := storesPrefix
	r, err := doRequest(cmd, prefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get store: %s\n", err)
		return
	}
	if flag := cmd.Flag("jq"); flag != nil && flag.Value.String() != "" {
		printWithJQFilter(r, flag.Value.String())
		return
	}
	cmd.Println(r)
}

func showAllLimitCommandFunc(cmd *cobra.Command, args []string) {
	prefix := path.Join(storesPrefix, "limit")
	r, err := doRequest(cmd, prefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get all limit: %s\n", err)
		return
	}
	cmd.Println(r)
}

func removeTombStoneCommandFunc(cmd *cobra.Command, args []string) {
	prefix := fmt.Sprintf(path.Join(storesPrefix, "remove-tombstone"), "")
	_, err := doRequest(cmd, prefix, http.MethodDelete)
	if err != nil {
		cmd.Printf("Failed to remove tombstone store %s \n", err)
		return
	}
	cmd.Println("Success!")
}

func setAllLimitCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		cmd.Println("Usage: stores set limit <rate>")
		return
	}
	rate, err := strconv.ParseFloat(args[0], 64)
	if err != nil || rate < 0 {
		cmd.Println("rate should be a number that >= 0.")
		return
	}
	prefix := path.Join(storesPrefix, "limit")
	postJSON(cmd, prefix, map[string]interface{}{
		"rate": rate,
	})
}
<commit_msg>pdctl: should not use a format string (#1651)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package command

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path"
	"strconv"

	"github.com/spf13/cobra"
)

var (
	storesPrefix = "pd/api/v1/stores"
	storePrefix  = "pd/api/v1/store/%v"
)

// NewStoreCommand return a stores subcommand of rootCmd
func NewStoreCommand() *cobra.Command {
	s := &cobra.Command{
		Use:   `store [delete|label|weight|limit] <store_id> [--jq="<query string>"]`,
		Short: "show the store status",
		Run:   showStoreCommandFunc,
	}
	s.AddCommand(NewDeleteStoreCommand())
	s.AddCommand(NewLabelStoreCommand())
	s.AddCommand(NewSetStoreWeightCommand())
	s.AddCommand(NewSetStoreLimitCommand())
	s.Flags().String("jq", "", "jq query")
	return s
}

// NewDeleteStoreByAddrCommand returns a subcommand of delete
func NewDeleteStoreByAddrCommand() *cobra.Command {
	d := &cobra.Command{
		Use:   "addr <address>",
		Short: "delete store by its address",
		Run:   deleteStoreCommandByAddrFunc,
	}
	return d
}

// NewDeleteStoreCommand return a  delete subcommand of storeCmd
func NewDeleteStoreCommand() *cobra.Command {
	d := &cobra.Command{
		Use:   "delete <store_id>",
		Short: "delete the store",
		Run:   deleteStoreCommandFunc,
	}
	d.AddCommand(NewDeleteStoreByAddrCommand())
	return d
}

// NewLabelStoreCommand returns a label subcommand of storeCmd.
func NewLabelStoreCommand() *cobra.Command {
	l := &cobra.Command{
		Use:   "label <store_id> <key> <value>",
		Short: "set a store's label value",
		Run:   labelStoreCommandFunc,
	}
	return l
}

// NewSetStoreWeightCommand returns a weight subcommand of storeCmd.
func NewSetStoreWeightCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "weight <store_id> <leader_weight> <region_weight>",
		Short: "set a store's leader and region balance weight",
		Run:   setStoreWeightCommandFunc,
	}
}

// NewSetStoreLimitCommand returns a limit subcommand of storeCmd.
func NewSetStoreLimitCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "limit <store_id> <rate>",
		Short: "set a store's rate limit",
		Run:   setStoreLimitCommandFunc,
	}
}

// NewStoresCommand returns a store subcommand of rootCmd
func NewStoresCommand() *cobra.Command {
	s := &cobra.Command{
		Use:   `stores [subcommand] [--jq="<query string>"]`,
		Short: "store status",
	}
	s.AddCommand(NewRemoveTombStoneCommand())
	s.AddCommand(NewSetStoresCommand())
	s.AddCommand(NewShowStoresCommand())
	s.Flags().String("jq", "", "jq query")
	return s
}

// NewRemoveTombStoneCommand returns a tombstone subcommand of storesCmd.
func NewRemoveTombStoneCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "remove-tombstone",
		Short: "remove tombstone record if only safe",
		Run:   removeTombStoneCommandFunc,
	}
}

// NewShowStoresCommand returns a show subcommand of storesCmd.
func NewShowStoresCommand() *cobra.Command {
	sc := &cobra.Command{
		Use:   "show [limit]",
		Short: "show the stores",
		Run:   showStoresCommandFunc,
	}
	sc.AddCommand(NewShowAllLimitCommand())
	return sc
}

// NewShowAllLimitCommand return a show limit subcommand of show command
func NewShowAllLimitCommand() *cobra.Command {
	sc := &cobra.Command{
		Use:   "limit",
		Short: "show all stores' limit",
		Run:   showAllLimitCommandFunc,
	}
	return sc
}

// NewSetStoresCommand returns a set subcommand of storesCmd.
func NewSetStoresCommand() *cobra.Command {
	sc := &cobra.Command{
		Use:   "set [limit]",
		Short: "set stores",
	}
	sc.AddCommand(NewSetAllLimitCommand())
	return sc
}

// NewSetAllLimitCommand returns a set limit subcommand of set command.
func NewSetAllLimitCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "limit <rate>",
		Short: "set all store's rate limit",
		Run:   setAllLimitCommandFunc,
	}
}

func showStoreCommandFunc(cmd *cobra.Command, args []string) {
	prefix := storesPrefix
	if len(args) == 1 {
		if _, err := strconv.Atoi(args[0]); err != nil {
			cmd.Println("store_id should be a number")
			return
		}
		prefix = fmt.Sprintf(storePrefix, args[0])
	}
	r, err := doRequest(cmd, prefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get store: %s\n", err)
		return
	}
	if flag := cmd.Flag("jq"); flag != nil && flag.Value.String() != "" {
		printWithJQFilter(r, flag.Value.String())
		return
	}
	cmd.Println(r)
}

func deleteStoreCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		cmd.Usage()
		return
	}
	if _, err := strconv.Atoi(args[0]); err != nil {
		cmd.Println("store_id should be a number")
		return
	}
	prefix := fmt.Sprintf(storePrefix, args[0])
	_, err := doRequest(cmd, prefix, http.MethodDelete)
	if err != nil {
		cmd.Printf("Failed to delete store %s: %s\n", args[0], err)
		return
	}
	cmd.Println("Success!")
}

func deleteStoreCommandByAddrFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		cmd.Usage()
		return
	}
	addr := args[0]

	// fetch all the stores
	r, err := doRequest(cmd, storesPrefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get store: %s\n", err)
		return
	}

	storeInfo := struct {
		Stores []struct {
			Store struct {
				ID      int    `json:"id"`
				Address string `json:"address"`
			} `json:"store"`
		} `json:"stores"`
	}{}
	if err = json.Unmarshal([]byte(r), &storeInfo); err != nil {
		cmd.Printf("Failed to parse store info: %s\n", err)
		return
	}

	// filter by the addr
	id := -1
	for _, store := range storeInfo.Stores {
		if store.Store.Address == addr {
			id = store.Store.ID
			break
		}
	}

	if id == -1 {
		cmd.Printf("address not found: %s\n", addr)
		return
	}

	// delete store by its ID
	prefix := fmt.Sprintf(storePrefix, id)
	_, err = doRequest(cmd, prefix, http.MethodDelete)
	if err != nil {
		cmd.Printf("Failed to delete store %s: %s\n", args[0], err)
		return
	}
	cmd.Println("Success!")
}

func labelStoreCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 3 {
		cmd.Println("Usage: store label <store_id> <key> <value>")
		return
	}
	if _, err := strconv.Atoi(args[0]); err != nil {
		cmd.Println("store_id should be a number")
		return
	}
	prefix := fmt.Sprintf(path.Join(storePrefix, "label"), args[0])
	postJSON(cmd, prefix, map[string]interface{}{args[1]: args[2]})
}

func setStoreWeightCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 3 {
		cmd.Println("Usage: store weight <store_id> <leader_weight> <region_weight>")
		return
	}
	leader, err := strconv.ParseFloat(args[1], 64)
	if err != nil || leader < 0 {
		cmd.Println("leader_weight should be a number that >= 0.")
		return
	}
	region, err := strconv.ParseFloat(args[2], 64)
	if err != nil || region < 0 {
		cmd.Println("region_weight should be a number that >= 0")
		return
	}
	prefix := fmt.Sprintf(path.Join(storePrefix, "weight"), args[0])
	postJSON(cmd, prefix, map[string]interface{}{
		"leader": leader,
		"region": region,
	})
}

func setStoreLimitCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		cmd.Println("Usage: store limit <store_id> <rate>")
		return
	}
	rate, err := strconv.ParseFloat(args[1], 64)
	if err != nil || rate < 0 {
		cmd.Println("rate should be a number that >= 0.")
		return
	}
	prefix := fmt.Sprintf(path.Join(storePrefix, "limit"), args[0])
	postJSON(cmd, prefix, map[string]interface{}{
		"rate": rate,
	})
}

func showStoresCommandFunc(cmd *cobra.Command, args []string) {
	prefix := storesPrefix
	r, err := doRequest(cmd, prefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get store: %s\n", err)
		return
	}
	if flag := cmd.Flag("jq"); flag != nil && flag.Value.String() != "" {
		printWithJQFilter(r, flag.Value.String())
		return
	}
	cmd.Println(r)
}

func showAllLimitCommandFunc(cmd *cobra.Command, args []string) {
	prefix := path.Join(storesPrefix, "limit")
	r, err := doRequest(cmd, prefix, http.MethodGet)
	if err != nil {
		cmd.Printf("Failed to get all limit: %s\n", err)
		return
	}
	cmd.Println(r)
}

func removeTombStoneCommandFunc(cmd *cobra.Command, args []string) {
	prefix := path.Join(storesPrefix, "remove-tombstone")
	_, err := doRequest(cmd, prefix, http.MethodDelete)
	if err != nil {
		cmd.Printf("Failed to remove tombstone store %s \n", err)
		return
	}
	cmd.Println("Success!")
}

func setAllLimitCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		cmd.Println("Usage: stores set limit <rate>")
		return
	}
	rate, err := strconv.ParseFloat(args[0], 64)
	if err != nil || rate < 0 {
		cmd.Println("rate should be a number that >= 0.")
		return
	}
	prefix := path.Join(storesPrefix, "limit")
	postJSON(cmd, prefix, map[string]interface{}{
		"rate": rate,
	})
}
<|endoftext|>


<commit_before>package docker

import (
	"errors"
	dockerClient "github.com/fsouza/go-dockerclient"
	"sync"
)

const (
	concurrentEventHandlers = 4
	dockerEventsChannelSize = 1000
)

// NewContainerStoreEventHandler a new event handler that updates the container
// store based on Docker event updates.
func NewContainerStoreEventHandler(store ContainerStore) EventHandler {
	return &containerStoreEventHandler{
		store:               store,
		dockerEventsChannel: nil,
	}
}

func (handler *containerStoreEventHandler) DockerEventsChannel() chan *dockerClient.APIEvents {
	if handler.dockerEventsChannel == nil {
		channel := make(chan *dockerClient.APIEvents, dockerEventsChannelSize)
		handler.dockerEventsChannel = &channel
	}
	return *handler.dockerEventsChannel
}

func (handler *containerStoreEventHandler) Listen() error {
	var workers sync.WaitGroup
	eventChan := make(chan *dockerClient.APIEvents, dockerEventsChannelSize)

	handler.listenMutex.Lock()
	defer handler.listenMutex.Unlock()

	workers.Add(concurrentEventHandlers)
	defer workers.Wait()

	for i := 0; i < concurrentEventHandlers; i++ {
		go func() {
			for event := range eventChan {
				id := event.ID
				switch event.Status {
				case "start":
					_ = handler.store.AddContainerByID(id)
				case "die":
					handler.store.RemoveContainer(id)
				}
			}
			workers.Done()
		}()
	}

	for event := range handler.DockerEventsChannel() {
		if event != nil {
			eventChan <- event
		}
	}

	close(eventChan)
	handler.dockerEventsChannel = nil

	return errors.New("Docker events connection closed")
}

type containerStoreEventHandler struct {
	store               ContainerStore
	dockerEventsChannel *(chan *dockerClient.APIEvents)
	listenMutex         sync.Mutex
}
<commit_msg>Stop over using defer<commit_after>package docker

import (
	"errors"
	dockerClient "github.com/fsouza/go-dockerclient"
	"sync"
)

const (
	concurrentEventHandlers = 4
	dockerEventsChannelSize = 1000
)

// NewContainerStoreEventHandler a new event handler that updates the container
// store based on Docker event updates.
func NewContainerStoreEventHandler(store ContainerStore) EventHandler {
	return &containerStoreEventHandler{
		store:               store,
		dockerEventsChannel: nil,
	}
}

func (handler *containerStoreEventHandler) DockerEventsChannel() chan *dockerClient.APIEvents {
	if handler.dockerEventsChannel == nil {
		channel := make(chan *dockerClient.APIEvents, dockerEventsChannelSize)
		handler.dockerEventsChannel = &channel
	}
	return *handler.dockerEventsChannel
}

func (handler *containerStoreEventHandler) Listen() error {
	var workers sync.WaitGroup
	eventChan := make(chan *dockerClient.APIEvents, dockerEventsChannelSize)

	handler.listenMutex.Lock()
	defer handler.listenMutex.Unlock()

	workers.Add(concurrentEventHandlers)

	for i := 0; i < concurrentEventHandlers; i++ {
		go func() {
			for event := range eventChan {
				id := event.ID
				switch event.Status {
				case "start":
					_ = handler.store.AddContainerByID(id)
				case "die":
					handler.store.RemoveContainer(id)
				}
			}
			workers.Done()
		}()
	}

	for event := range handler.DockerEventsChannel() {
		if event != nil {
			eventChan <- event
		}
	}

	close(eventChan)
	workers.Wait()
	handler.dockerEventsChannel = nil

	return errors.New("Docker events connection closed")
}

type containerStoreEventHandler struct {
	store               ContainerStore
	dockerEventsChannel *(chan *dockerClient.APIEvents)
	listenMutex         sync.Mutex
}
<|endoftext|>


<commit_before>package quiclatest

import (
	"net"
	"sync"

	"github.com/ami-GS/gQUIC/latest/qtype"
)

type Server struct {
	conn net.PacketConn
	// TODO: replace to SessionStore
	sessions          map[string]*Session //ConnectionID.String():Session
	addrSessions      map[string]*Session //remoteAddr.String():Session, for identifing single connection if zero-len dest ID
	sessionsMutex     *sync.Mutex
	SupportedVersions []qtype.Version
	// TODO: consider here? or in utility? or decide dynamically?
	SessionLimitNum int
	NumHandshake    int
}

func ListenAddr(addr string) (*Server, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	conn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
		return nil, err
	}

	s := &Server{
		conn:              conn,
		sessions:          make(map[string]*Session),
		addrSessions:      make(map[string]*Session),
		sessionsMutex:     new(sync.Mutex),
		SupportedVersions: qtype.SupportedVersions,
		// TODO: experimental
		SessionLimitNum: 1000,
	}
	go s.Serve()
	return s, nil
}

func (s *Server) Serve() error {
	buffer := make([]byte, qtype.MTUIPv4)
	for {
		length, remoteAddr, err := s.conn.ReadFrom(buffer)
		if err != nil {
			s.conn.Close()
			return err
		}
		packets, _, err := ParsePackets(buffer[:length])
		if err != nil {
			// TODO: this type assertion is dangerous
			_ = s.Close(err.(qtype.TransportError))
			return err
		}

		for _, p := range packets {
			err = s.handlePacket(remoteAddr, p)
			if err != nil {
				s.conn.Close()
				return err
			}
		}
	}
}

func (s *Server) Close(err qtype.TransportError) error {
	wg := &sync.WaitGroup{}
	frame := NewConnectionCloseFrame(err, "error: experimental")
	for _, session := range s.sessions {
		wg.Add(1)
		go func(sess *Session) {
			// this sends connection close frame to peer
			sess.Close(frame)
			wg.Done()
		}(session)
	}
	wg.Wait()
	s.conn.Close()
	// close conn
	return nil
}

func (s *Server) handlePacket(remoteAddr net.Addr, packet Packet) error {
	ph := packet.GetHeader()
	srcID, destID := ph.GetConnectionIDPair()
	lh, ok := ph.(*LongHeader)
	// need to check session existence?
	if ok && !s.IsVersionSupported(lh.Version) {
		err := s.SendVersionNegotiationPacket(srcID, destID, remoteAddr)
		if err != nil {
		}
		return nil
	}

	var sess *Session
	if len(destID) != 0 {
		s.sessionsMutex.Lock()
		sess, ok = s.sessions[destID.String()]
		s.sessionsMutex.Unlock()
		if !ok {
			if !s.IsAcceptableSession(lh.Version, srcID, destID, remoteAddr) {
				return nil
			}

			// TODO: have to reset Session when Retry Packet sent to client. then thsi can use DestID for packet maching
			sess = NewSession(&Connection{conn: s.conn, remoteAddr: remoteAddr}, destID, srcID, false)
			sess.server = s
			// packet handler for each session on server is now defined in session.go
			sess.packetHandler = sess
			// TODO: be careful to use lh
			sess.versionDecided = lh.Version
			go sess.Run()

			// might be deleted after handling packet
			s.sessionsMutex.Lock()
			s.sessions[destID.String()] = sess
			s.sessionsMutex.Unlock()
			s.addrSessions[remoteAddr.String()] = sess
		}
	} else {
		sess, ok = s.addrSessions[remoteAddr.String()]
		if !ok {
			// drop packet if no corresponding connection
			return nil
		}
	}
	sess.HandlePacket(packet)
	return nil
}

func (s *Server) IsVersionSupported(version qtype.Version) bool {
	for _, v := range s.SupportedVersions {
		if v == version {
			return true
		}
	}
	return false
}

func (s *Server) IsAcceptableSession(version qtype.Version, srcID, destID qtype.ConnectionID, remoteAddr net.Addr) bool {
	s.sessionsMutex.Lock()
	sessionNum := len(s.sessions)
	s.sessionsMutex.Unlock()
	if sessionNum >= s.SessionLimitNum {
		p := NewHandshakePacket(version, srcID, destID, qtype.InitialPacketNumber,
			[]Frame{NewConnectionCloseFrame(qtype.ServerBusy, "The number of session reached server limit")})
		wire, err := p.GetWire()
		if err != nil {
			//
		}
		_, err = s.conn.WriteTo(wire, remoteAddr)
		return false
	}
	return true
}

func (s *Server) DeleteSessionFromMap(ID qtype.ConnectionID) {
	s.sessionsMutex.Lock()
	delete(s.sessions, ID.String())
	s.sessionsMutex.Unlock()
}

func (s *Server) ChangeConnectionID(fromID, toID qtype.ConnectionID) {
	s.sessionsMutex.Lock()
	session := s.sessions[fromID.String()]
	delete(s.sessions, fromID.String())
	s.sessions[toID.String()] = session
	s.sessionsMutex.Unlock()
}

func (s *Server) SendVersionNegotiationPacket(srcID, destID qtype.ConnectionID, remoteAddr net.Addr) error {
	p := NewVersionNegotiationPacket(srcID, destID, s.SupportedVersions)
	wire, err := p.GetWire()
	if err != nil {
		//
	}
	_, err = s.conn.WriteTo(wire, remoteAddr)
	return err
}
<commit_msg>change lock position<commit_after>package quiclatest

import (
	"net"
	"sync"

	"github.com/ami-GS/gQUIC/latest/qtype"
)

type Server struct {
	conn net.PacketConn
	// TODO: replace to SessionStore
	sessions          map[string]*Session //ConnectionID.String():Session
	addrSessions      map[string]*Session //remoteAddr.String():Session, for identifing single connection if zero-len dest ID
	sessionsMutex     *sync.Mutex
	SupportedVersions []qtype.Version
	// TODO: consider here? or in utility? or decide dynamically?
	SessionLimitNum int
	NumHandshake    int
}

func ListenAddr(addr string) (*Server, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	conn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
		return nil, err
	}

	s := &Server{
		conn:              conn,
		sessions:          make(map[string]*Session),
		addrSessions:      make(map[string]*Session),
		sessionsMutex:     new(sync.Mutex),
		SupportedVersions: qtype.SupportedVersions,
		// TODO: experimental
		SessionLimitNum: 1000,
	}
	go s.Serve()
	return s, nil
}

func (s *Server) Serve() error {
	buffer := make([]byte, qtype.MTUIPv4)
	for {
		length, remoteAddr, err := s.conn.ReadFrom(buffer)
		if err != nil {
			s.conn.Close()
			return err
		}
		packets, _, err := ParsePackets(buffer[:length])
		if err != nil {
			// TODO: this type assertion is dangerous
			_ = s.Close(err.(qtype.TransportError))
			return err
		}

		for _, p := range packets {
			err = s.handlePacket(remoteAddr, p)
			if err != nil {
				s.conn.Close()
				return err
			}
		}
	}
}

func (s *Server) Close(err qtype.TransportError) error {
	wg := &sync.WaitGroup{}
	frame := NewConnectionCloseFrame(err, "error: experimental")
	for _, session := range s.sessions {
		wg.Add(1)
		go func(sess *Session) {
			// this sends connection close frame to peer
			sess.Close(frame)
			wg.Done()
		}(session)
	}
	wg.Wait()
	s.conn.Close()
	// close conn
	return nil
}

func (s *Server) handlePacket(remoteAddr net.Addr, packet Packet) error {
	ph := packet.GetHeader()
	srcID, destID := ph.GetConnectionIDPair()
	lh, ok := ph.(*LongHeader)
	// need to check session existence?
	if ok && !s.IsVersionSupported(lh.Version) {
		err := s.SendVersionNegotiationPacket(srcID, destID, remoteAddr)
		if err != nil {
		}
		return nil
	}

	var sess *Session
	if len(destID) != 0 {
		s.sessionsMutex.Lock()
		sess, ok = s.sessions[destID.String()]
		s.sessionsMutex.Unlock()
		if !ok {
			if !s.IsAcceptableSession(lh.Version, srcID, destID, remoteAddr) {
				return nil
			}

			// TODO: have to reset Session when Retry Packet sent to client. then thsi can use DestID for packet maching
			sess = NewSession(&Connection{conn: s.conn, remoteAddr: remoteAddr}, destID, srcID, false)
			sess.server = s
			// packet handler for each session on server is now defined in session.go
			sess.packetHandler = sess
			// TODO: be careful to use lh
			sess.versionDecided = lh.Version
			go sess.Run()

			// might be deleted after handling packet
			s.sessionsMutex.Lock()
			s.sessions[destID.String()] = sess
			s.addrSessions[remoteAddr.String()] = sess
			s.sessionsMutex.Unlock()
		}
	} else {
		sess, ok = s.addrSessions[remoteAddr.String()]
		if !ok {
			// drop packet if no corresponding connection
			return nil
		}
	}
	sess.HandlePacket(packet)
	return nil
}

func (s *Server) IsVersionSupported(version qtype.Version) bool {
	for _, v := range s.SupportedVersions {
		if v == version {
			return true
		}
	}
	return false
}

func (s *Server) IsAcceptableSession(version qtype.Version, srcID, destID qtype.ConnectionID, remoteAddr net.Addr) bool {
	s.sessionsMutex.Lock()
	sessionNum := len(s.sessions)
	s.sessionsMutex.Unlock()
	if sessionNum >= s.SessionLimitNum {
		p := NewHandshakePacket(version, srcID, destID, qtype.InitialPacketNumber,
			[]Frame{NewConnectionCloseFrame(qtype.ServerBusy, "The number of session reached server limit")})
		wire, err := p.GetWire()
		if err != nil {
			//
		}
		_, err = s.conn.WriteTo(wire, remoteAddr)
		return false
	}
	return true
}

func (s *Server) DeleteSessionFromMap(ID qtype.ConnectionID) {
	s.sessionsMutex.Lock()
	defer s.sessionsMutex.Unlock()
	delete(s.sessions, ID.String())
}

func (s *Server) ChangeConnectionID(fromID, toID qtype.ConnectionID) {
	s.sessionsMutex.Lock()
	defer s.sessionsMutex.Unlock()
	session := s.sessions[fromID.String()]
	delete(s.sessions, fromID.String())
	s.sessions[toID.String()] = session
}

func (s *Server) SendVersionNegotiationPacket(srcID, destID qtype.ConnectionID, remoteAddr net.Addr) error {
	p := NewVersionNegotiationPacket(srcID, destID, s.SupportedVersions)
	wire, err := p.GetWire()
	if err != nil {
		//
	}
	_, err = s.conn.WriteTo(wire, remoteAddr)
	return err
}
<|endoftext|>