<commit_before>// +build functional

// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"text/template"

	"golang.org/x/tools/imports"
)

// TODO: add more awsvpc functional tests using simple test template
var simpleTestPattern = `
// +build functional,%s

// Copyright 2014-2016 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 simpletest is an auto-generated set of tests defined by the json
// descriptions in testdata/simpletests.
//
// This file should not be edited; rather you should edit the generator instead
package simpletest

import (
	"strings"
	"testing"
	"time"
	"os"

	. "github.com/aws/amazon-ecs-agent/agent/functional_tests/util"
)

{{ range $i,$el := $ }}

// Test{{ $el.Name }} {{ $el.Description }}
func Test{{ $el.Name }}(t *testing.T) {
	{{if $el.DockerVersion}}
	// Test only available for docker version {{ $el.DockerVersion }}
	RequireDockerVersion(t, "{{ $el.DockerVersion }}")
	{{end}}
	// Parallel is opt in because resource constraints could cause test failures
	// on smaller instances
	if os.Getenv("ECS_FUNCTIONAL_PARALLEL") != "" { t.Parallel() }
	var options *AgentOptions
	if "{{ $el.AwsvpcMode }}" == "true" {
			options = &AgentOptions{EnableTaskENI: true}
	}
	agent := RunAgent(t, options)
	defer agent.Cleanup()
	agent.RequireVersion("{{ $el.Version }}")

	td, err := GetTaskDefinition("{{ $el.TaskDefinition }}")
	if err != nil {
		t.Fatalf("Could not register task definition: %%v", err)
	}
	var testTasks []*TestTask
	if "{{ $el.AwsvpcMode }}" == "true"{
		for i := 0; i < {{ $el.Count }}; i ++{
			tmpTask, err := agent.StartAWSVPCTask( "{{ $el.TaskDefinition }}", nil)
			if err != nil{
				t.Fatalf("Could not start task in awsvpc mode: %%v",err)
			}
			testTasks = append(testTasks, tmpTask)
		}
	}else{
		testTasks, err = agent.StartMultipleTasks(t, td, {{ $el.Count }})
		if err != nil {
			t.Fatalf("Could not start task: %%v", err)
		}
	}

	timeout, err := time.ParseDuration("{{ $el.Timeout }}")
	if err != nil {
		t.Fatalf("Could not parse timeout: %%#v", err)
	}

	{{if $el.Daemon}}
	// Make sure the task is running
	for _, testTask := range testTasks {
		err = testTask.WaitRunning(timeout)
		if err != nil {
			t.Errorf("Timed out waiting for task to reach running. Error %%v, task %%v", err, testTask)
		}
	}

	// Cleanup, stop all the tasks and wait for the containers to be stopped
	for _, testTask := range testTasks {
		err = testTask.Stop()
		if err != nil {
			t.Errorf("Failed to stop task, Error %%v, task %%v", err, testTask)
		}
	}
	{{end}}

	for _, testTask := range testTasks {
		err = testTask.WaitStopped(timeout)
		if err != nil {
			t.Fatalf("Timed out waiting for task to reach stopped. Error %%#v, task %%#v", err, testTask)
		}

		{{ range $name, $code := $el.ExitCodes }}
		if exit, ok := testTask.ContainerExitcode("{{$name}}"); !ok || exit != {{ $code }} {
			t.Errorf("Expected {{$name}} to exit with {{$code}}; actually exited (%%v) with %%v", ok, exit)
		}
		{{ end }}
		defer agent.SweepTask(testTask)
	}

}
{{ end }}
`

func main() {
	type simpleTestMetadata struct {
		Name           string
		Description    string
		TaskDefinition string
		Timeout        string
		ExitCodes      map[string]int
		Tags           []string
		Version        string
		Count          int
		DockerVersion  string
		Daemon         bool
		AwsvpcMode     string
	}

	types := []struct {
		buildTag       string
		testDir        string
		templateName   string
		outputFileName string
	}{{
		buildTag:       "windows",
		testDir:        "simpletests_windows",
		templateName:   "simpleTestWindows",
		outputFileName: "simpletests_generated_windows_test",
	}, {
		buildTag:       "!windows",
		testDir:        "simpletests_unix",
		templateName:   "simpleTestUnix",
		outputFileName: "simpletests_generated_unix_test",
	}}

	for _, ostype := range types {
		_, filename, _, _ := runtime.Caller(0)
		metadataFiles, err := filepath.Glob(filepath.Join(path.Dir(filename), "..", "testdata", ostype.testDir, "*.json"))
		if err != nil || len(metadataFiles) == 0 {
			panic("No tests found" + err.Error())
		}

		testMetadatas := make([]simpleTestMetadata, len(metadataFiles))
		for i, f := range metadataFiles {
			data, err := ioutil.ReadFile(f)
			if err != nil {
				panic("Cannot read file " + f)
			}

			// By default the number of task to run is 1
			testMetadatas[i].Count = 1
			err = json.Unmarshal(data, &testMetadatas[i])
			if err != nil {
				panic("Cannot parse " + f + ": " + err.Error())
			}
		}

		simpleTests := template.Must(template.New(ostype.templateName).Parse(
			fmt.Sprintf(simpleTestPattern, ostype.buildTag),
		))
		output := bytes.NewBuffer([]byte{})
		err = simpleTests.Execute(output, testMetadatas)
		if err != nil {
			panic(err)
		}
		formattedOutput, err := imports.Process("", output.Bytes(), nil)
		if err != nil {
			fmt.Println(string(output.Bytes()))
			panic(err)
		}

		// Add '.go' so the arg can be used with 'go run' as well, without being interpreted as a file to run
		fmt.Println(ostype.testDir + "/" + ostype.outputFileName + ".go")
		outputFile, err := os.Create(filepath.Join(ostype.testDir, ostype.outputFileName+".go"))
		if err != nil {
			panic(err)
		}
		outputFile.Write(formattedOutput)
	}
}
<commit_msg>ftest: update generator to avoid go misinterpretation<commit_after>// +build functional

// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"text/template"

	"golang.org/x/tools/imports"
)

// TODO: add more awsvpc functional tests using simple test template
var simpleTestPattern = `// +build functional,%s

// Copyright 2014-2016 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 simpletest is an auto-generated set of tests defined by the json
// descriptions in testdata/simpletests.
//
// This file should not be edited; rather you should edit the generator instead
package simpletest

import (
	"strings"
	"testing"
	"time"
	"os"

	. "github.com/aws/amazon-ecs-agent/agent/functional_tests/util"
)

{{ range $i,$el := $ }}

// Test{{ $el.Name }} {{ $el.Description }}
func Test{{ $el.Name }}(t *testing.T) {
	{{if $el.DockerVersion}}
	// Test only available for docker version {{ $el.DockerVersion }}
	RequireDockerVersion(t, "{{ $el.DockerVersion }}")
	{{end}}
	// Parallel is opt in because resource constraints could cause test failures
	// on smaller instances
	if os.Getenv("ECS_FUNCTIONAL_PARALLEL") != "" { t.Parallel() }
	var options *AgentOptions
	if "{{ $el.AwsvpcMode }}" == "true" {
			options = &AgentOptions{EnableTaskENI: true}
	}
	agent := RunAgent(t, options)
	defer agent.Cleanup()
	agent.RequireVersion("{{ $el.Version }}")

	td, err := GetTaskDefinition("{{ $el.TaskDefinition }}")
	if err != nil {
		t.Fatalf("Could not register task definition: %%v", err)
	}
	var testTasks []*TestTask
	if "{{ $el.AwsvpcMode }}" == "true"{
		for i := 0; i < {{ $el.Count }}; i ++{
			tmpTask, err := agent.StartAWSVPCTask( "{{ $el.TaskDefinition }}", nil)
			if err != nil{
				t.Fatalf("Could not start task in awsvpc mode: %%v",err)
			}
			testTasks = append(testTasks, tmpTask)
		}
	}else{
		testTasks, err = agent.StartMultipleTasks(t, td, {{ $el.Count }})
		if err != nil {
			t.Fatalf("Could not start task: %%v", err)
		}
	}

	timeout, err := time.ParseDuration("{{ $el.Timeout }}")
	if err != nil {
		t.Fatalf("Could not parse timeout: %%#v", err)
	}

	{{if $el.Daemon}}
	// Make sure the task is running
	for _, testTask := range testTasks {
		err = testTask.WaitRunning(timeout)
		if err != nil {
			t.Errorf("Timed out waiting for task to reach running. Error %%v, task %%v", err, testTask)
		}
	}

	// Cleanup, stop all the tasks and wait for the containers to be stopped
	for _, testTask := range testTasks {
		err = testTask.Stop()
		if err != nil {
			t.Errorf("Failed to stop task, Error %%v, task %%v", err, testTask)
		}
	}
	{{end}}

	for _, testTask := range testTasks {
		err = testTask.WaitStopped(timeout)
		if err != nil {
			t.Fatalf("Timed out waiting for task to reach stopped. Error %%#v, task %%#v", err, testTask)
		}

		{{ range $name, $code := $el.ExitCodes }}
		if exit, ok := testTask.ContainerExitcode("{{$name}}"); !ok || exit != {{ $code }} {
			t.Errorf("Expected {{$name}} to exit with {{$code}}; actually exited (%%v) with %%v", ok, exit)
		}
		{{ end }}
		defer agent.SweepTask(testTask)
	}

}
{{ end }}
`

func main() {
	type simpleTestMetadata struct {
		Name           string
		Description    string
		TaskDefinition string
		Timeout        string
		ExitCodes      map[string]int
		Tags           []string
		Version        string
		Count          int
		DockerVersion  string
		Daemon         bool
		AwsvpcMode     string
	}

	types := []struct {
		buildTag       string
		testDir        string
		templateName   string
		outputFileName string
	}{{
		buildTag:       "windows",
		testDir:        "simpletests_windows",
		templateName:   "simpleTestWindows",
		outputFileName: "simpletests_generated_windows_test",
	}, {
		buildTag:       "!windows",
		testDir:        "simpletests_unix",
		templateName:   "simpleTestUnix",
		outputFileName: "simpletests_generated_unix_test",
	}}

	for _, ostype := range types {
		_, filename, _, _ := runtime.Caller(0)
		metadataFiles, err := filepath.Glob(filepath.Join(path.Dir(filename), "..", "testdata", ostype.testDir, "*.json"))
		if err != nil || len(metadataFiles) == 0 {
			panic("No tests found" + err.Error())
		}

		testMetadatas := make([]simpleTestMetadata, len(metadataFiles))
		for i, f := range metadataFiles {
			data, err := ioutil.ReadFile(f)
			if err != nil {
				panic("Cannot read file " + f)
			}

			// By default the number of task to run is 1
			testMetadatas[i].Count = 1
			err = json.Unmarshal(data, &testMetadatas[i])
			if err != nil {
				panic("Cannot parse " + f + ": " + err.Error())
			}
		}

		simpleTests := template.Must(template.New(ostype.templateName).Parse(
			fmt.Sprintf(simpleTestPattern, ostype.buildTag),
		))
		output := bytes.NewBuffer([]byte{})
		err = simpleTests.Execute(output, testMetadatas)
		if err != nil {
			panic(err)
		}
		formattedOutput, err := imports.Process("", output.Bytes(), nil)
		if err != nil {
			fmt.Println(string(output.Bytes()))
			panic(err)
		}

		// Add '.go' so the arg can be used with 'go run' as well, without being interpreted as a file to run
		fmt.Println(ostype.testDir + "/" + ostype.outputFileName + ".go")
		outputFile, err := os.Create(filepath.Join(ostype.testDir, ostype.outputFileName+".go"))
		if err != nil {
			panic(err)
		}
		outputFile.Write(formattedOutput)
	}
}
<|endoftext|>


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

// +build darwin freebsd linux netbsd openbsd windows

// TCP sockets

package net

import (
	"io"
	"os"
	"syscall"
	"time"
)

// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
// will not be routed to an IPv6 socket - two separate sockets are required
// if both AFs are to be supported. See inet6(4) on OpenBSD for details.

func sockaddrToTCP(sa syscall.Sockaddr) Addr {
	switch sa := sa.(type) {
	case *syscall.SockaddrInet4:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	case *syscall.SockaddrInet6:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	default:
		if sa != nil {
			// Diagnose when we will turn a non-nil sockaddr into a nil.
			panic("unexpected type in sockaddrToTCP")
		}
	}
	return nil
}

func (a *TCPAddr) family() int {
	if a == nil || len(a.IP) <= IPv4len {
		return syscall.AF_INET
	}
	if a.IP.To4() != nil {
		return syscall.AF_INET
	}
	return syscall.AF_INET6
}

func (a *TCPAddr) isWildcard() bool {
	if a == nil || a.IP == nil {
		return true
	}
	return a.IP.IsUnspecified()
}

func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
	return ipToSockaddr(family, a.IP, a.Port)
}

func (a *TCPAddr) toAddr() sockaddr {
	if a == nil { // nil *TCPAddr
		return nil // nil interface
	}
	return a
}

// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
	conn
}

func newTCPConn(fd *netFD) *TCPConn {
	c := &TCPConn{conn{fd}}
	c.SetNoDelay(true)
	return c
}

// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
	if n, err, handled := sendFile(c.fd, r); handled {
		return n, err
	}
	return genericReadFrom(c, r)
}

// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseRead()
}

// CloseWrite shuts down the writing side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseWrite() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseWrite()
}

// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
// If sec < 0 (the default), Close returns immediately and
// the operating system finishes sending the data in the background.
//
// If sec == 0, Close returns immediately and the operating system
// discards any unsent or unacknowledged data.
//
// If sec > 0, Close blocks for at most sec seconds waiting for
// data to be sent and acknowledged.
func (c *TCPConn) SetLinger(sec int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setLinger(c.fd, sec)
}

// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
func (c *TCPConn) SetKeepAlive(keepalive bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setKeepAlive(c.fd, keepalive)
}

// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets
// (Nagle's algorithm).  The default is true (no delay), meaning
// that data is sent as soon as possible after a Write.
func (c *TCPConn) SetNoDelay(noDelay bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setNoDelay(c.fd, noDelay)
}

// DialTCP connects to the remote address raddr on the network net,
// which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
	switch net {
	case "tcp", "tcp4", "tcp6":
	default:
		return nil, UnknownNetworkError(net)
	}
	if raddr == nil {
		return nil, &OpError{"dial", net, nil, errMissingAddress}
	}
	return dialTCP(net, laddr, raddr, noDeadline)
}

func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
	fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)

	// TCP has a rarely used mechanism called a 'simultaneous connection' in
	// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
	// connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
	// at addr2, without either machine executing Listen.  If laddr == nil,
	// it means we want the kernel to pick an appropriate originating local
	// address.  Some Linux kernels cycle blindly through a fixed range of
	// local ports, regardless of destination port.  If a kernel happens to
	// pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"),
	// then the Dial will succeed, having simultaneously connected to itself.
	// This can only happen when we are letting the kernel pick a port (laddr == nil)
	// and when there is no listener for the destination address.
	// It's hard to argue this is anything other than a kernel bug.  If we
	// see this happen, rather than expose the buggy effect to users, we
	// close the fd and try again.  If it happens twice more, we relent and
	// use the result.  See also:
	//	http://golang.org/issue/2690
	//	http://stackoverflow.com/questions/4949858/
	//
	// The opposite can also happen: if we ask the kernel to pick an appropriate
	// originating local address, sometimes it picks one that is already in use.
	// So if the error is EADDRNOTAVAIL, we have to try again too, just for
	// a different reason.
	//
	// The kernel socket code is no doubt enjoying watching us squirm.
	for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
		if err == nil {
			fd.Close()
		}
		fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
	}

	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

func selfConnect(fd *netFD, err error) bool {
	// If the connect failed, we clearly didn't connect to ourselves.
	if err != nil {
		return false
	}

	// The socket constructor can return an fd with raddr nil under certain
	// unknown conditions. The errors in the calls there to Getpeername
	// are discarded, but we can't catch the problem there because those
	// calls are sometimes legally erroneous with a "socket not connected".
	// Since this code (selfConnect) is already trying to work around
	// a problem, we make sure if this happens we recognize trouble and
	// ask the DialTCP routine to try again.
	// TODO: try to understand what's really going on.
	if fd.laddr == nil || fd.raddr == nil {
		return true
	}
	l := fd.laddr.(*TCPAddr)
	r := fd.raddr.(*TCPAddr)
	return l.Port == r.Port && l.IP.Equal(r.IP)
}

func spuriousENOTAVAIL(err error) bool {
	e, ok := err.(*OpError)
	return ok && e.Err == syscall.EADDRNOTAVAIL
}

// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
type TCPListener struct {
	fd *netFD
}

// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
	if l == nil || l.fd == nil || l.fd.sysfd < 0 {
		return nil, syscall.EINVAL
	}
	fd, err := l.fd.accept(sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *TCPListener) Accept() (c Conn, err error) {
	c1, err := l.AcceptTCP()
	if err != nil {
		return nil, err
	}
	return c1, nil
}

// Close stops listening on the TCP address.
// Already Accepted connections are not closed.
func (l *TCPListener) Close() error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return l.fd.Close()
}

// Addr returns the listener's network address, a *TCPAddr.
func (l *TCPListener) Addr() Addr { return l.fd.laddr }

// SetDeadline sets the deadline associated with the listener.
// A zero time value disables the deadline.
func (l *TCPListener) SetDeadline(t time.Time) error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return setDeadline(l.fd, t)
}

// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }

// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
	switch net {
	case "tcp", "tcp4", "tcp6":
	default:
		return nil, UnknownNetworkError(net)
	}
	if laddr == nil {
		laddr = &TCPAddr{}
	}
	fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	err = syscall.Listen(fd.sysfd, listenerBacklog)
	if err != nil {
		closesocket(fd.sysfd)
		return nil, &OpError{"listen", net, laddr, err}
	}
	l := new(TCPListener)
	l.fd = fd
	return l, nil
}
<commit_msg>net: fix data race on fd.sysfd<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin freebsd linux netbsd openbsd windows

// TCP sockets

package net

import (
	"io"
	"os"
	"syscall"
	"time"
)

// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
// will not be routed to an IPv6 socket - two separate sockets are required
// if both AFs are to be supported. See inet6(4) on OpenBSD for details.

func sockaddrToTCP(sa syscall.Sockaddr) Addr {
	switch sa := sa.(type) {
	case *syscall.SockaddrInet4:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	case *syscall.SockaddrInet6:
		return &TCPAddr{sa.Addr[0:], sa.Port}
	default:
		if sa != nil {
			// Diagnose when we will turn a non-nil sockaddr into a nil.
			panic("unexpected type in sockaddrToTCP")
		}
	}
	return nil
}

func (a *TCPAddr) family() int {
	if a == nil || len(a.IP) <= IPv4len {
		return syscall.AF_INET
	}
	if a.IP.To4() != nil {
		return syscall.AF_INET
	}
	return syscall.AF_INET6
}

func (a *TCPAddr) isWildcard() bool {
	if a == nil || a.IP == nil {
		return true
	}
	return a.IP.IsUnspecified()
}

func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
	return ipToSockaddr(family, a.IP, a.Port)
}

func (a *TCPAddr) toAddr() sockaddr {
	if a == nil { // nil *TCPAddr
		return nil // nil interface
	}
	return a
}

// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
	conn
}

func newTCPConn(fd *netFD) *TCPConn {
	c := &TCPConn{conn{fd}}
	c.SetNoDelay(true)
	return c
}

// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
	if n, err, handled := sendFile(c.fd, r); handled {
		return n, err
	}
	return genericReadFrom(c, r)
}

// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseRead()
}

// CloseWrite shuts down the writing side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseWrite() error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return c.fd.CloseWrite()
}

// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
// If sec < 0 (the default), Close returns immediately and
// the operating system finishes sending the data in the background.
//
// If sec == 0, Close returns immediately and the operating system
// discards any unsent or unacknowledged data.
//
// If sec > 0, Close blocks for at most sec seconds waiting for
// data to be sent and acknowledged.
func (c *TCPConn) SetLinger(sec int) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setLinger(c.fd, sec)
}

// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
func (c *TCPConn) SetKeepAlive(keepalive bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setKeepAlive(c.fd, keepalive)
}

// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets
// (Nagle's algorithm).  The default is true (no delay), meaning
// that data is sent as soon as possible after a Write.
func (c *TCPConn) SetNoDelay(noDelay bool) error {
	if !c.ok() {
		return syscall.EINVAL
	}
	return setNoDelay(c.fd, noDelay)
}

// DialTCP connects to the remote address raddr on the network net,
// which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
	switch net {
	case "tcp", "tcp4", "tcp6":
	default:
		return nil, UnknownNetworkError(net)
	}
	if raddr == nil {
		return nil, &OpError{"dial", net, nil, errMissingAddress}
	}
	return dialTCP(net, laddr, raddr, noDeadline)
}

func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
	fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)

	// TCP has a rarely used mechanism called a 'simultaneous connection' in
	// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
	// connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
	// at addr2, without either machine executing Listen.  If laddr == nil,
	// it means we want the kernel to pick an appropriate originating local
	// address.  Some Linux kernels cycle blindly through a fixed range of
	// local ports, regardless of destination port.  If a kernel happens to
	// pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"),
	// then the Dial will succeed, having simultaneously connected to itself.
	// This can only happen when we are letting the kernel pick a port (laddr == nil)
	// and when there is no listener for the destination address.
	// It's hard to argue this is anything other than a kernel bug.  If we
	// see this happen, rather than expose the buggy effect to users, we
	// close the fd and try again.  If it happens twice more, we relent and
	// use the result.  See also:
	//	http://golang.org/issue/2690
	//	http://stackoverflow.com/questions/4949858/
	//
	// The opposite can also happen: if we ask the kernel to pick an appropriate
	// originating local address, sometimes it picks one that is already in use.
	// So if the error is EADDRNOTAVAIL, we have to try again too, just for
	// a different reason.
	//
	// The kernel socket code is no doubt enjoying watching us squirm.
	for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
		if err == nil {
			fd.Close()
		}
		fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
	}

	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

func selfConnect(fd *netFD, err error) bool {
	// If the connect failed, we clearly didn't connect to ourselves.
	if err != nil {
		return false
	}

	// The socket constructor can return an fd with raddr nil under certain
	// unknown conditions. The errors in the calls there to Getpeername
	// are discarded, but we can't catch the problem there because those
	// calls are sometimes legally erroneous with a "socket not connected".
	// Since this code (selfConnect) is already trying to work around
	// a problem, we make sure if this happens we recognize trouble and
	// ask the DialTCP routine to try again.
	// TODO: try to understand what's really going on.
	if fd.laddr == nil || fd.raddr == nil {
		return true
	}
	l := fd.laddr.(*TCPAddr)
	r := fd.raddr.(*TCPAddr)
	return l.Port == r.Port && l.IP.Equal(r.IP)
}

func spuriousENOTAVAIL(err error) bool {
	e, ok := err.(*OpError)
	return ok && e.Err == syscall.EADDRNOTAVAIL
}

// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
type TCPListener struct {
	fd *netFD
}

// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
	if l == nil || l.fd == nil {
		return nil, syscall.EINVAL
	}
	fd, err := l.fd.accept(sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	return newTCPConn(fd), nil
}

// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *TCPListener) Accept() (c Conn, err error) {
	c1, err := l.AcceptTCP()
	if err != nil {
		return nil, err
	}
	return c1, nil
}

// Close stops listening on the TCP address.
// Already Accepted connections are not closed.
func (l *TCPListener) Close() error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return l.fd.Close()
}

// Addr returns the listener's network address, a *TCPAddr.
func (l *TCPListener) Addr() Addr { return l.fd.laddr }

// SetDeadline sets the deadline associated with the listener.
// A zero time value disables the deadline.
func (l *TCPListener) SetDeadline(t time.Time) error {
	if l == nil || l.fd == nil {
		return syscall.EINVAL
	}
	return setDeadline(l.fd, t)
}

// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }

// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
	switch net {
	case "tcp", "tcp4", "tcp6":
	default:
		return nil, UnknownNetworkError(net)
	}
	if laddr == nil {
		laddr = &TCPAddr{}
	}
	fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
	if err != nil {
		return nil, err
	}
	err = syscall.Listen(fd.sysfd, listenerBacklog)
	if err != nil {
		closesocket(fd.sysfd)
		return nil, &OpError{"listen", net, laddr, err}
	}
	l := new(TCPListener)
	l.fd = fd
	return l, nil
}
<|endoftext|>


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

package user

import (
	"runtime"
	"testing"
)

func check(t *testing.T) {
	if !implemented {
		t.Skip("user: not implemented; skipping tests")
	}
	switch runtime.GOOS {
	case "linux", "freebsd", "darwin", "windows", "plan9":
		// test supported
	default:
		t.Skipf("user: Lookup not implemented on %q; skipping test", runtime.GOOS)
	}
}

func TestCurrent(t *testing.T) {
	check(t)

	u, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	if u.HomeDir == "" {
		t.Errorf("didn't get a HomeDir")
	}
	if u.Username == "" {
		t.Errorf("didn't get a username")
	}
}

func compare(t *testing.T, want, got *User) {
	if want.Uid != got.Uid {
		t.Errorf("got Uid=%q; want %q", got.Uid, want.Uid)
	}
	if want.Username != got.Username {
		t.Errorf("got Username=%q; want %q", got.Username, want.Username)
	}
	if want.Name != got.Name {
		t.Errorf("got Name=%q; want %q", got.Name, want.Name)
	}
	// TODO(brainman): fix it once we know how.
	if runtime.GOOS == "windows" {
		t.Skip("skipping Gid and HomeDir comparisons")
	}
	if want.Gid != got.Gid {
		t.Errorf("got Gid=%q; want %q", got.Gid, want.Gid)
	}
	if want.HomeDir != got.HomeDir {
		t.Errorf("got HomeDir=%q; want %q", got.HomeDir, want.HomeDir)
	}
}

func TestLookup(t *testing.T) {
	check(t)

	if runtime.GOOS == "plan9" {
		t.Skipf("Lookup not implemented on %q", runtime.GOOS)
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := Lookup(want.Username)
	if err != nil {
		t.Fatalf("Lookup: %v", err)
	}
	compare(t, want, got)
}

func TestLookupId(t *testing.T) {
	check(t)

	if runtime.GOOS == "plan9" {
		t.Skipf("LookupId not implemented on %q", runtime.GOOS)
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := LookupId(want.Uid)
	if err != nil {
		t.Fatalf("LookupId: %v", err)
	}
	compare(t, want, got)
}
<commit_msg>os/user: enable tests on all supported platforms<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package user

import (
	"runtime"
	"testing"
)

func check(t *testing.T) {
	if !implemented {
		t.Skip("user: not implemented; skipping tests")
	}
}

func TestCurrent(t *testing.T) {
	check(t)

	u, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	if u.HomeDir == "" {
		t.Errorf("didn't get a HomeDir")
	}
	if u.Username == "" {
		t.Errorf("didn't get a username")
	}
}

func compare(t *testing.T, want, got *User) {
	if want.Uid != got.Uid {
		t.Errorf("got Uid=%q; want %q", got.Uid, want.Uid)
	}
	if want.Username != got.Username {
		t.Errorf("got Username=%q; want %q", got.Username, want.Username)
	}
	if want.Name != got.Name {
		t.Errorf("got Name=%q; want %q", got.Name, want.Name)
	}
	// TODO(brainman): fix it once we know how.
	if runtime.GOOS == "windows" {
		t.Skip("skipping Gid and HomeDir comparisons")
	}
	if want.Gid != got.Gid {
		t.Errorf("got Gid=%q; want %q", got.Gid, want.Gid)
	}
	if want.HomeDir != got.HomeDir {
		t.Errorf("got HomeDir=%q; want %q", got.HomeDir, want.HomeDir)
	}
}

func TestLookup(t *testing.T) {
	check(t)

	if runtime.GOOS == "plan9" {
		t.Skipf("Lookup not implemented on %q", runtime.GOOS)
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := Lookup(want.Username)
	if err != nil {
		t.Fatalf("Lookup: %v", err)
	}
	compare(t, want, got)
}

func TestLookupId(t *testing.T) {
	check(t)

	if runtime.GOOS == "plan9" {
		t.Skipf("LookupId not implemented on %q", runtime.GOOS)
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := LookupId(want.Uid)
	if err != nil {
		t.Fatalf("LookupId: %v", err)
	}
	compare(t, want, got)
}
<|endoftext|>


<commit_before>// Package backblaze B2 API for Golang
package backblaze // import "gopkg.in/kothar/go-backblaze.v0"

import (
	"bytes"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"sync"

	"github.com/pquerna/ffjson/ffjson"
)

const (
	b2Host = "https://api.backblazeb2.com"
	v1     = "/b2api/v1/"
)

// Credentials are the identification required by the Backblaze B2 API
//
// The account ID is a 12-digit hex number that you can get from
// your account page on backblaze.com.
//
// The application key is a 40-digit hex number that you can get from
// your account page on backblaze.com.
//
// The key id is the application key id that you get when generating an
// application key. If using the master application key, leave this set
// to an empty string as your account id will be used instead.
type Credentials struct {
	AccountID      string
	ApplicationKey string
	KeyID          string
}

// B2 implements a B2 API client. Do not modify state concurrently.
type B2 struct {
	Credentials

	// If true, don't retry requests if authorization has expired
	NoRetry bool

	// If true, display debugging information about API calls
	Debug bool

	// Number of MaxIdleUploads to keep for reuse.
	// This must be set prior to creating a bucket struct
	MaxIdleUploads int

	// State
	mutex      sync.Mutex
	host       string
	auth       *authorizationState
	httpClient http.Client
}

// The current auth state of the client. Can be individually invalidated by
// requests which fail, tringgering a reauth the next time its validity is
// checked
type authorizationState struct {
	sync.Mutex
	*authorizeAccountResponse

	valid bool
}

func (a *authorizationState) isValid() bool {
	if a == nil {
		return false
	}

	a.Lock()
	defer a.Unlock()

	return a.valid
}

// Marks the authorization as invalid. This will result in a new Authorization
// on the next API call.
func (a *authorizationState) invalidate() {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	a.valid = false
	a.authorizeAccountResponse = nil
}

// NewB2 creates a new Client for accessing the B2 API.
// The AuthorizeAccount method will be called immediately.
func NewB2(creds Credentials) (*B2, error) {
	c := &B2{
		Credentials:    creds,
		MaxIdleUploads: 1,
	}

	// Authorize account
	if err := c.AuthorizeAccount(); err != nil {
		return nil, err
	}

	return c, nil
}

// AuthorizeAccount is used to log in to the B2 API.
func (c *B2) AuthorizeAccount() error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	return c.internalAuthorizeAccount()
}

// The body of AuthorizeAccount without a mutex
func (c *B2) internalAuthorizeAccount() error {
	if c.host == "" {
		c.host = b2Host
	}

	req, err := http.NewRequest("GET", c.host+v1+"b2_authorize_account", nil)
	if err != nil {
		return err
	}

	// Support the use of application keys. If a KeyID is not explicitly set,
	// use the account ID as the key ID
	keyID := c.KeyID
	if keyID == "" {
		keyID = c.AccountID
	}
	req.SetBasicAuth(keyID, c.ApplicationKey)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}

	authResponse := &authorizeAccountResponse{}
	if err = c.parseResponse(resp, authResponse, nil); err != nil {
		return err
	}

	// Store token
	c.auth = &authorizationState{
		authorizeAccountResponse: authResponse,
		valid:                    true,
	}

	return nil
}

// DownloadURL returns the URL prefix needed to construct download links.
// Bucket.FileURL will costruct a full URL for given file names.
func (c *B2) DownloadURL() (string, error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if !c.auth.isValid() {
		if err := c.internalAuthorizeAccount(); err != nil {
			return "", err
		}
	}
	return c.auth.DownloadURL, nil
}

// Create an authorized request using the client's credentials
func (c *B2) authRequest(method, apiPath string, body io.Reader) (*http.Request, *authorizationState, error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if !c.auth.isValid() {
		if c.Debug {
			log.Println("No valid authorization token, re-authorizing client")
		}
		if err := c.internalAuthorizeAccount(); err != nil {
			return nil, nil, err
		}
	}

	path := c.auth.APIEndpoint + v1 + apiPath

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Add("Authorization", c.auth.AuthorizationToken)

	if c.Debug {
		log.Printf("authRequest: %s %s\n", method, req.URL)
	}

	return req, c.auth, nil
}

// Dispatch an authorized API GET request
func (c *B2) authGet(apiPath string) (*http.Response, *authorizationState, error) {
	req, auth, err := c.authRequest("GET", apiPath, nil)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.httpClient.Do(req)
	return resp, auth, err
}

// Dispatch an authorized POST request
func (c *B2) authPost(apiPath string, body io.Reader) (*http.Response, *authorizationState, error) {
	req, auth, err := c.authRequest("POST", apiPath, body)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.httpClient.Do(req)
	return resp, auth, err
}

// Looks for an error message in the response body and parses it into a
// B2Error object
func (c *B2) parseError(body []byte) error {
	b2err := &B2Error{}
	if ffjson.Unmarshal(body, b2err) != nil {
		return nil
	}
	return b2err
}

// Attempts to parse a response body into the provided result struct
func (c *B2) parseResponse(resp *http.Response, result interface{}, auth *authorizationState) error {
	defer resp.Body.Close()

	// Read response body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if c.Debug {
		log.Printf("Response: %s", body)
	}

	// Check response code
	switch resp.StatusCode {
	case 200: // Response is OK
	case 401:
		auth.invalidate()
		if err := c.parseError(body); err != nil {
			return err
		}
		return &B2Error{
			Code:    "UNAUTHORIZED",
			Message: "The account ID is wrong, the account does not have B2 enabled, or the application key is not valid",
			Status:  resp.StatusCode,
		}
	default:
		if err := c.parseError(body); err != nil {
			return err
		}
		return &B2Error{
			Code:    "UNKNOWN",
			Message: "Unrecognised status code",
			Status:  resp.StatusCode,
		}
	}

	return ffjson.Unmarshal(body, result)
}

// Perform a B2 API request with the provided request and response objects
func (c *B2) apiRequest(apiPath string, request interface{}, response interface{}) error {
	body, err := ffjson.Marshal(request)
	if err != nil {
		return err
	}
	defer ffjson.Pool(body)

	if c.Debug {
		log.Println("----")
		log.Printf("apiRequest: %s %s", apiPath, body)
	}

	err = c.tryAPIRequest(apiPath, body, response)

	// Retry after non-fatal errors
	if b2err, ok := err.(*B2Error); ok {
		if !b2err.IsFatal() && !c.NoRetry {
			if c.Debug {
				log.Printf("Retrying request %q due to error: %v", apiPath, err)
			}

			return c.tryAPIRequest(apiPath, body, response)
		}
	}
	return err
}

func (c *B2) tryAPIRequest(apiPath string, body []byte, response interface{}) error {
	resp, auth, err := c.authPost(apiPath, bytes.NewReader(body))
	if err != nil {
		if c.Debug {
			log.Println("B2.post returned an error: ", err)
		}
		return err
	}

	return c.parseResponse(resp, response, auth)
}
<commit_msg>pull in @ConnorJarvis's change so that we don't have to explicitly set the account ID when using an app key<commit_after>// Package backblaze B2 API for Golang
package backblaze // import "gopkg.in/kothar/go-backblaze.v0"

import (
	"bytes"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"sync"

	"github.com/pquerna/ffjson/ffjson"
)

const (
	b2Host = "https://api.backblazeb2.com"
	v1     = "/b2api/v1/"
)

// Credentials are the identification required by the Backblaze B2 API
//
// The account ID is a 12-digit hex number that you can get from
// your account page on backblaze.com.
//
// The application key is a 40-digit hex number that you can get from
// your account page on backblaze.com.
//
// The key id is the application key id that you get when generating an
// application key. If using the master application key, leave this set
// to an empty string as your account id will be used instead.
type Credentials struct {
	AccountID      string
	ApplicationKey string
	KeyID          string
}

// B2 implements a B2 API client. Do not modify state concurrently.
type B2 struct {
	Credentials

	// If true, don't retry requests if authorization has expired
	NoRetry bool

	// If true, display debugging information about API calls
	Debug bool

	// Number of MaxIdleUploads to keep for reuse.
	// This must be set prior to creating a bucket struct
	MaxIdleUploads int

	// State
	mutex      sync.Mutex
	host       string
	auth       *authorizationState
	httpClient http.Client
}

// The current auth state of the client. Can be individually invalidated by
// requests which fail, tringgering a reauth the next time its validity is
// checked
type authorizationState struct {
	sync.Mutex
	*authorizeAccountResponse

	valid bool
}

func (a *authorizationState) isValid() bool {
	if a == nil {
		return false
	}

	a.Lock()
	defer a.Unlock()

	return a.valid
}

// Marks the authorization as invalid. This will result in a new Authorization
// on the next API call.
func (a *authorizationState) invalidate() {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	a.valid = false
	a.authorizeAccountResponse = nil
}

// NewB2 creates a new Client for accessing the B2 API.
// The AuthorizeAccount method will be called immediately.
func NewB2(creds Credentials) (*B2, error) {
	c := &B2{
		Credentials:    creds,
		MaxIdleUploads: 1,
	}

	// Authorize account
	if err := c.AuthorizeAccount(); err != nil {
		return nil, err
	}

	return c, nil
}

// AuthorizeAccount is used to log in to the B2 API.
func (c *B2) AuthorizeAccount() error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	return c.internalAuthorizeAccount()
}

// The body of AuthorizeAccount without a mutex
func (c *B2) internalAuthorizeAccount() error {
	if c.host == "" {
		c.host = b2Host
	}

	req, err := http.NewRequest("GET", c.host+v1+"b2_authorize_account", nil)
	if err != nil {
		return err
	}

	// Support the use of application keys. If a KeyID is not explicitly set,
	// use the account ID as the key ID
	keyID := c.KeyID
	if keyID == "" {
		keyID = c.AccountID
	}
	req.SetBasicAuth(keyID, c.ApplicationKey)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}

	authResponse := &authorizeAccountResponse{}
	if err = c.parseResponse(resp, authResponse, nil); err != nil {
		return err
	}

	// Store token
	c.auth = &authorizationState{
		authorizeAccountResponse: authResponse,
		valid:                    true,
	}

	// Set AccountID to the returned value from authorizeAccountResponse
	// This is for when an Application Key is used instead of Master Application Key
	if c.AccountID == "" {
		c.AccountID = authResponse.AccountID
	}

	return nil
}

// DownloadURL returns the URL prefix needed to construct download links.
// Bucket.FileURL will costruct a full URL for given file names.
func (c *B2) DownloadURL() (string, error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if !c.auth.isValid() {
		if err := c.internalAuthorizeAccount(); err != nil {
			return "", err
		}
	}
	return c.auth.DownloadURL, nil
}

// Create an authorized request using the client's credentials
func (c *B2) authRequest(method, apiPath string, body io.Reader) (*http.Request, *authorizationState, error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if !c.auth.isValid() {
		if c.Debug {
			log.Println("No valid authorization token, re-authorizing client")
		}
		if err := c.internalAuthorizeAccount(); err != nil {
			return nil, nil, err
		}
	}

	path := c.auth.APIEndpoint + v1 + apiPath

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Add("Authorization", c.auth.AuthorizationToken)

	if c.Debug {
		log.Printf("authRequest: %s %s\n", method, req.URL)
	}

	return req, c.auth, nil
}

// Dispatch an authorized API GET request
func (c *B2) authGet(apiPath string) (*http.Response, *authorizationState, error) {
	req, auth, err := c.authRequest("GET", apiPath, nil)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.httpClient.Do(req)
	return resp, auth, err
}

// Dispatch an authorized POST request
func (c *B2) authPost(apiPath string, body io.Reader) (*http.Response, *authorizationState, error) {
	req, auth, err := c.authRequest("POST", apiPath, body)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.httpClient.Do(req)
	return resp, auth, err
}

// Looks for an error message in the response body and parses it into a
// B2Error object
func (c *B2) parseError(body []byte) error {
	b2err := &B2Error{}
	if ffjson.Unmarshal(body, b2err) != nil {
		return nil
	}
	return b2err
}

// Attempts to parse a response body into the provided result struct
func (c *B2) parseResponse(resp *http.Response, result interface{}, auth *authorizationState) error {
	defer resp.Body.Close()

	// Read response body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if c.Debug {
		log.Printf("Response: %s", body)
	}

	// Check response code
	switch resp.StatusCode {
	case 200: // Response is OK
	case 401:
		auth.invalidate()
		if err := c.parseError(body); err != nil {
			return err
		}
		return &B2Error{
			Code:    "UNAUTHORIZED",
			Message: "The account ID is wrong, the account does not have B2 enabled, or the application key is not valid",
			Status:  resp.StatusCode,
		}
	default:
		if err := c.parseError(body); err != nil {
			return err
		}
		return &B2Error{
			Code:    "UNKNOWN",
			Message: "Unrecognised status code",
			Status:  resp.StatusCode,
		}
	}

	return ffjson.Unmarshal(body, result)
}

// Perform a B2 API request with the provided request and response objects
func (c *B2) apiRequest(apiPath string, request interface{}, response interface{}) error {
	body, err := ffjson.Marshal(request)
	if err != nil {
		return err
	}
	defer ffjson.Pool(body)

	if c.Debug {
		log.Println("----")
		log.Printf("apiRequest: %s %s", apiPath, body)
	}

	err = c.tryAPIRequest(apiPath, body, response)

	// Retry after non-fatal errors
	if b2err, ok := err.(*B2Error); ok {
		if !b2err.IsFatal() && !c.NoRetry {
			if c.Debug {
				log.Printf("Retrying request %q due to error: %v", apiPath, err)
			}

			return c.tryAPIRequest(apiPath, body, response)
		}
	}
	return err
}

func (c *B2) tryAPIRequest(apiPath string, body []byte, response interface{}) error {
	resp, auth, err := c.authPost(apiPath, bytes.NewReader(body))
	if err != nil {
		if c.Debug {
			log.Println("B2.post returned an error: ", err)
		}
		return err
	}

	return c.parseResponse(resp, response, auth)
}
<|endoftext|>


<commit_before>// +build darwin ios

package bind

import (
	"fmt"
	"reflect"

	"github.com/keybase/go-keychain"
)

type Test interface {
	Fail(s string)
}

func AddGenericPassword(service string, account string, label string, password string, accessGroup string) error {
	item := keychain.NewGenericPassword(service, account, label, []byte(password), accessGroup)
	return keychain.AddItem(item)
}

func DeleteGenericPassword(service string, account string, accessGroup string) error {
	item := keychain.NewItem()
	item.SetSecClass(keychain.SecClassGenericPassword)
	item.SetService(service)
	item.SetAccount(account)
	item.SetAccessGroup(accessGroup)
	return keychain.DeleteItem(item)
}

// TestGenericPassword runs test code for generic password keychain item.
// This is here so we can export using gomobile bind and run this method on iOS simulator and device.
// Access groups aren't supported in iOS simulator.
func GenericPasswordTest(t Test, service string, accessGroup string) {
	var err error

	account := "Testing account with unicode テスト"
	item := keychain.NewGenericPassword(service, account, "", []byte("toomanysecrets"), accessGroup)
	item.SetSynchronizable(keychain.SynchronizableNo)
	item.SetAccessible(keychain.AccessibleWhenUnlocked)

	account2 := "Testing account #2"
	item2 := keychain.NewGenericPassword(service, account2, "", []byte("toomanysecrets2"), accessGroup)

	// Cleanup
	defer keychain.DeleteItem(item)
	defer keychain.DeleteItem(item2)

	// Test account names empty
	accounts, err := keychain.GetGenericPasswordAccounts(service)
	if len(accounts) != 0 {
		t.Fail("Should have no accounts")
	}

	// Test add
	err = keychain.AddItem(item)
	if err != nil {
		t.Fail(err.Error())
	}

	// Test dupe
	err = keychain.AddItem(item)
	if err != keychain.ErrorDuplicateItem {
		t.Fail("Should error with duplicate item")
	}

	// Test add another
	err = keychain.AddItem(item2)
	if err != nil {
		t.Fail(err.Error())
	}

	// Test querying attributes
	query := keychain.NewItem()
	query.SetSecClass(keychain.SecClassGenericPassword)
	query.SetService(service)
	query.SetAccount(account)
	query.SetAccessGroup(accessGroup)
	query.SetMatchLimit(keychain.MatchLimitAll)
	query.SetReturnAttributes(true)
	results, err := keychain.QueryItem(query)
	if err != nil {
		t.Fail(err.Error())
	}

	if len(results) != 1 {
		t.Fail(fmt.Sprintf("Invalid results count: %d", len(results)))
	}

	if results[0].Service != service {
		t.Fail(fmt.Sprintf("Invalid service, %v != %v, %v", results[0].Service, service, results))
	}

	if results[0].Account != account {
		t.Fail(fmt.Sprintf("Invalid account, %v != %v, %v", results[0].Account, account, results))
	}

	if len(results[0].Data) != 0 {
		t.Fail("Password shouldn't come back when returning attributes")
	}

	// Test querying data
	queryData := keychain.NewItem()
	queryData.SetSecClass(keychain.SecClassGenericPassword)
	queryData.SetService(service)
	queryData.SetAccount(account)
	queryData.SetAccessGroup(accessGroup)
	queryData.SetMatchLimit(keychain.MatchLimitOne)
	queryData.SetReturnData(true)
	resultsData, err := keychain.QueryItem(queryData)
	if err != nil {
		t.Fail(err.Error())
	}

	if len(resultsData) != 1 {
		t.Fail("Too many results")
	}

	if string(resultsData[0].Data) != "toomanysecrets" {
		t.Fail("Invalid password")
	}

	// Test account names
	accounts2, err := keychain.GetGenericPasswordAccounts(service)
	if err != nil {
		t.Fail(err.Error())
	}
	if len(accounts2) != 2 {
		t.Fail(fmt.Sprintf("Should have 2 accounts: %v", accounts2))
	}

	if !reflect.DeepEqual(accounts2, []string{account, account2}) {
		t.Fail(fmt.Sprintf("Invalid accounts: %v", accounts2))
	}

	// Remove
	queryDel := keychain.NewItem()
	queryDel.SetSecClass(keychain.SecClassGenericPassword)
	queryDel.SetService(service)
	queryDel.SetAccount(account)
	queryDel.SetAccessGroup(accessGroup)
	err = keychain.DeleteItem(queryDel)
	if err != nil {
		t.Fail(err.Error())
	}

	// Test removed
	query3 := keychain.NewItem()
	query3.SetService(service)
	query3.SetAccount(account)
	query3.SetAccessGroup(accessGroup)
	query3.SetMatchLimit(keychain.MatchLimitAll)
	query3.SetReturnAttributes(true)
	results3, err := keychain.QueryItem(query3)

	if len(results3) != 0 {
		t.Fail("Results should have been empty")
	}

	accounts3, err := keychain.GetGenericPasswordAccounts(service)
	if err != nil {
		t.Fail(err.Error())
	}
	if len(accounts3) != 1 {
		t.Fail("Should have an account")
	}

	// Test remove not found
	err = keychain.DeleteItem(item)
	if err != keychain.ErrorItemNotFound {
		t.Fail("Error should be not found")
	}
}
<commit_msg>Fix test comment<commit_after>// +build darwin ios

package bind

import (
	"fmt"
	"reflect"

	"github.com/keybase/go-keychain"
)

type Test interface {
	Fail(s string)
}

func AddGenericPassword(service string, account string, label string, password string, accessGroup string) error {
	item := keychain.NewGenericPassword(service, account, label, []byte(password), accessGroup)
	return keychain.AddItem(item)
}

func DeleteGenericPassword(service string, account string, accessGroup string) error {
	item := keychain.NewItem()
	item.SetSecClass(keychain.SecClassGenericPassword)
	item.SetService(service)
	item.SetAccount(account)
	item.SetAccessGroup(accessGroup)
	return keychain.DeleteItem(item)
}

// GenericPasswordTest runs test code for generic password keychain item.
// This is here so we can export using gomobile bind and run this method on iOS simulator and device.
// Access groups aren't supported in iOS simulator.
func GenericPasswordTest(t Test, service string, accessGroup string) {
	var err error

	account := "Testing account with unicode テスト"
	item := keychain.NewGenericPassword(service, account, "", []byte("toomanysecrets"), accessGroup)
	item.SetSynchronizable(keychain.SynchronizableNo)
	item.SetAccessible(keychain.AccessibleWhenUnlocked)

	account2 := "Testing account #2"
	item2 := keychain.NewGenericPassword(service, account2, "", []byte("toomanysecrets2"), accessGroup)

	// Cleanup
	defer keychain.DeleteItem(item)
	defer keychain.DeleteItem(item2)

	// Test account names empty
	accounts, err := keychain.GetGenericPasswordAccounts(service)
	if len(accounts) != 0 {
		t.Fail("Should have no accounts")
	}

	// Test add
	err = keychain.AddItem(item)
	if err != nil {
		t.Fail(err.Error())
	}

	// Test dupe
	err = keychain.AddItem(item)
	if err != keychain.ErrorDuplicateItem {
		t.Fail("Should error with duplicate item")
	}

	// Test add another
	err = keychain.AddItem(item2)
	if err != nil {
		t.Fail(err.Error())
	}

	// Test querying attributes
	query := keychain.NewItem()
	query.SetSecClass(keychain.SecClassGenericPassword)
	query.SetService(service)
	query.SetAccount(account)
	query.SetAccessGroup(accessGroup)
	query.SetMatchLimit(keychain.MatchLimitAll)
	query.SetReturnAttributes(true)
	results, err := keychain.QueryItem(query)
	if err != nil {
		t.Fail(err.Error())
	}

	if len(results) != 1 {
		t.Fail(fmt.Sprintf("Invalid results count: %d", len(results)))
	}

	if results[0].Service != service {
		t.Fail(fmt.Sprintf("Invalid service, %v != %v, %v", results[0].Service, service, results))
	}

	if results[0].Account != account {
		t.Fail(fmt.Sprintf("Invalid account, %v != %v, %v", results[0].Account, account, results))
	}

	if len(results[0].Data) != 0 {
		t.Fail("Password shouldn't come back when returning attributes")
	}

	// Test querying data
	queryData := keychain.NewItem()
	queryData.SetSecClass(keychain.SecClassGenericPassword)
	queryData.SetService(service)
	queryData.SetAccount(account)
	queryData.SetAccessGroup(accessGroup)
	queryData.SetMatchLimit(keychain.MatchLimitOne)
	queryData.SetReturnData(true)
	resultsData, err := keychain.QueryItem(queryData)
	if err != nil {
		t.Fail(err.Error())
	}

	if len(resultsData) != 1 {
		t.Fail("Too many results")
	}

	if string(resultsData[0].Data) != "toomanysecrets" {
		t.Fail("Invalid password")
	}

	// Test account names
	accounts2, err := keychain.GetGenericPasswordAccounts(service)
	if err != nil {
		t.Fail(err.Error())
	}
	if len(accounts2) != 2 {
		t.Fail(fmt.Sprintf("Should have 2 accounts: %v", accounts2))
	}

	if !reflect.DeepEqual(accounts2, []string{account, account2}) {
		t.Fail(fmt.Sprintf("Invalid accounts: %v", accounts2))
	}

	// Remove
	queryDel := keychain.NewItem()
	queryDel.SetSecClass(keychain.SecClassGenericPassword)
	queryDel.SetService(service)
	queryDel.SetAccount(account)
	queryDel.SetAccessGroup(accessGroup)
	err = keychain.DeleteItem(queryDel)
	if err != nil {
		t.Fail(err.Error())
	}

	// Test removed
	query3 := keychain.NewItem()
	query3.SetService(service)
	query3.SetAccount(account)
	query3.SetAccessGroup(accessGroup)
	query3.SetMatchLimit(keychain.MatchLimitAll)
	query3.SetReturnAttributes(true)
	results3, err := keychain.QueryItem(query3)

	if len(results3) != 0 {
		t.Fail("Results should have been empty")
	}

	accounts3, err := keychain.GetGenericPasswordAccounts(service)
	if err != nil {
		t.Fail(err.Error())
	}
	if len(accounts3) != 1 {
		t.Fail("Should have an account")
	}

	// Test remove not found
	err = keychain.DeleteItem(item)
	if err != keychain.ErrorItemNotFound {
		t.Fail("Error should be not found")
	}
}
<|endoftext|>


<commit_before>// © 2014 Steve McCoy under the MIT license. See LICENSE for details.

package pngplus

import (
	"bytes"
	"encoding/binary"
	"hash/crc32"
	"testing"
)

func TestEncodeBinary(t *testing.T) {
	var w bytes.Buffer
	s := []byte("hello")

	err := EncodeBinary(&w, s)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	bs := w.Bytes()

	var n uint32
	err = binary.Read(bytes.NewBuffer(bs), binary.BigEndian, &n)
	if err != nil {
		t.Fatalf("unexpected length read error: %v", err)
	}

	if n != uint32(len(s)) {
		t.Fatalf("wrong chunk length: %d vs. %d", n, len(s))
	}

	content := string(bs[4:len(bs)-4])
	expected := "biNyhello"
	if content != expected {
		t.Fatalf("wrong content: %q vs. %q", content, expected)
	}

	var crc uint32
	err = binary.Read(bytes.NewBuffer(bs[8+len(s):]), binary.BigEndian, &crc)
	if err != nil {
		t.Fatalf("unexpected crc read error: %v", err)
	}

	excrc := crc32.ChecksumIEEE([]byte(expected))
	if crc != excrc {
		t.Fatalf("wrong crc: %d vs. %d", crc, excrc)
	}
}

func TestDecodeBinary(t *testing.T) {
	var w bytes.Buffer
	s := []byte("hello")

	err := EncodeBinary(&w, s)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result, err := DecodeBinary(&w)
	if err != nil {
		t.Fatalf("failed to decode: %v", err)
	}
	if string(result) != "hello" {
		t.Fatalf("unexpected result: %q", string(result))
	}
}
<commit_msg>Test for short read in Decode.<commit_after>// © 2014 Steve McCoy under the MIT license. See LICENSE for details.

package pngplus

import (
	"bytes"
	"encoding/binary"
	"io"
	"hash/crc32"
	"testing"
)

func TestEncodeBinary(t *testing.T) {
	var w bytes.Buffer
	s := []byte("hello")

	err := EncodeBinary(&w, s)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	bs := w.Bytes()

	var n uint32
	err = binary.Read(bytes.NewBuffer(bs), binary.BigEndian, &n)
	if err != nil {
		t.Fatalf("unexpected length read error: %v", err)
	}

	if n != uint32(len(s)) {
		t.Fatalf("wrong chunk length: %d vs. %d", n, len(s))
	}

	content := string(bs[4:len(bs)-4])
	expected := "biNyhello"
	if content != expected {
		t.Fatalf("wrong content: %q vs. %q", content, expected)
	}

	var crc uint32
	err = binary.Read(bytes.NewBuffer(bs[8+len(s):]), binary.BigEndian, &crc)
	if err != nil {
		t.Fatalf("unexpected crc read error: %v", err)
	}

	excrc := crc32.ChecksumIEEE([]byte(expected))
	if crc != excrc {
		t.Fatalf("wrong crc: %d vs. %d", crc, excrc)
	}
}

func TestDecodeBinary(t *testing.T) {
	var w bytes.Buffer

	_, err := DecodeBinary(&w)
	if err != io.EOF {
		t.Errorf("expected EOF, got %v", err)
	}

	s := []byte("hello")

	err = EncodeBinary(&w, s)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	result, err := DecodeBinary(&w)
	if err != nil {
		t.Fatalf("failed to decode: %v", err)
	}
	if string(result) != "hello" {
		t.Fatalf("unexpected result: %q", string(result))
	}
}
<|endoftext|>


<commit_before>package bone

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

// Test if the route is valid
func TestRouting(t *testing.T) {
	mux := New()
	call := false
	mux.Get("/a/:id", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = true
	}))

	r, _ := http.NewRequest("GET", "/b/123", nil)
	w := httptest.NewRecorder()
	mux.ServeHTTP(w, r)

	if call {
		t.Error("handler should not be called")
	}
}

// Test if the http method is valid
func TestRoutingMethod(t *testing.T) {
	mux := New()
	call := false
	mux.Get("/t", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = true
	}))

	r, _ := http.NewRequest("POST", "/t", nil)
	w := httptest.NewRecorder()
	mux.ServeHTTP(w, r)

	if call {
		t.Error("response to a wrong method")
	}
}

// Test if the mux don't handle by prefix
func TestRoutingPath(t *testing.T) {
	mux := New()
	call := false
	mux.Get("/t", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = true
	}))
	mux.Get("/t/x", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = false
	}))

	r, _ := http.NewRequest("GET", "/t/x", nil)
	w := httptest.NewRecorder()
	mux.ServeHTTP(w, r)

	if call {
		t.Error("response with the wrong path")
	}
}
<commit_msg>add routing variable test.<commit_after>package bone

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
)

// Test if the route is valid
func TestRouting(t *testing.T) {
	mux := New()
	call := false
	mux.Get("/a/:id", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = true
	}))

	r, _ := http.NewRequest("GET", "/b/123", nil)
	w := httptest.NewRecorder()
	mux.ServeHTTP(w, r)

	if call {
		t.Error("handler should not be called")
	}
}

// Test if the http method is valid
func TestRoutingMethod(t *testing.T) {
	mux := New()
	call := false
	mux.Get("/t", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = true
	}))

	r, _ := http.NewRequest("POST", "/t", nil)
	w := httptest.NewRecorder()
	mux.ServeHTTP(w, r)

	if call {
		t.Error("response to a wrong method")
	}
}

// Test if the mux don't handle by prefix
func TestRoutingPath(t *testing.T) {
	mux := New()
	call := false
	mux.Get("/t", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = true
	}))
	mux.Get("/t/x", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		call = false
	}))

	r, _ := http.NewRequest("GET", "/t/x", nil)
	w := httptest.NewRecorder()
	mux.ServeHTTP(w, r)

	if call {
		t.Error("response with the wrong path")
	}
}

func TestRoutingVariable(t *testing.T) {
	var (
		expected = "variable"
		got      string
		mux      = New()
		w        = httptest.NewRecorder()
	)
	mux.Get("/:var", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		got = r.URL.Query().Get("var")
	}))

	r, err := http.NewRequest("GET", fmt.Sprintf("/%s", expected), nil)
	if err != nil {
		t.Fatal(err)
	}
	mux.ServeHTTP(w, r)

	if got != expected {
		t.Fatalf("expected %s, got %s", expected, got)
	}
}
<|endoftext|>


<commit_before>package bot

// RetVal is a integer type for returning error conditions from bot methods, or 0 for Ok
type RetVal int

// PlugRetVal is an integer type for return values from plugins, mainly for elevation & authorization
type PlugRetVal int

const (
	// Success indicates successful authorization or elevation
	Success PlugRetVal = iota
	// Fail indicates requested authorization or elevation failed
	Fail
	// MechanismFailed indicates authorization or elevation couldn't be determined due to a technical issue that should be logged
	MechanismFail
	// ConfigurationFail indicates authorization or elevation failed due to misconfiguration
	ConfigurationFail
)

const (
	// Ok indicates a successful result
	Ok RetVal = iota // success

	/* Connector issues */

	// UserNotFound - failed lookup
	UserNotFound
	// ChannelNotFound - failed lookup
	ChannelNotFound
	// AttributeNotFound - failed looking up user/robot attributes like email, name, etc.
	AttributeNotFound
	// FailedUserDM - the bot was not able to send an direct message (DM) to a given user
	FailedUserDM
	// FailedChannelJoin - the robot couldn't join a channel; e.g. slack doesn't allow bots to join
	FailedChannelJoin

	/* Brain Maladies */

	// DatumNotFound - key not found in the global hash when update called
	DatumNotFound
	// DatumLockExpired - A datum was checked out for too long, and the lock expired
	DatumLockExpired
	// DataFormatError - Problem unmarshalling JSON
	DataFormatError
	// BrainFailed - An error condition prevented the brain from storing/retrieving; redis down, file write failed, etc.
	BrainFailed
	// InvalidDatumKey - Key name didn't match the regex for valid key names
	InvalidDatumKey

	/* GetPluginConfig */

	// InvalidDblPtr - GetPluginConfig wasn't called with a double-pointer to a config struct
	InvalidDblPtr
	// InvalidCfgStruct - The struct type in GetPluginConfig doesn't match the struct registered for the plugin
	InvalidCfgStruct
	// NoConfigFound - The plugin doesn't have any config data
	NoConfigFound

	/* Elevator */
	// TechnicalProblem - There was a problem with the elevator (e.g. service unreachable)
	TechnicalProblem

	/* Prompt(User)ForReply */

	// RetryPrompt - There was already a prompt in progress for the user/channel
	RetryPrompt
	// ReplyNotMatched - The user reply didn't match the pattern waited for
	ReplyNotMatched
	// UseDefaultValue - The user replied with a single '=', meaning use a default value
	UseDefaultValue
	// TimeoutExpired - The user didn't reply within the given timeout
	TimeoutExpired
	// Interrupted - The user issued another command instead of replying, or replied with '-' (cancel)
	Interrupted
	// MatcherNotFound - There was no matcher configured with the given string, or the regex didn't compile
	MatcherNotFound

	/* Email */

	// NoUserEmail - Couldn't look up the user's email address
	NoUserEmail
	// NoBotEmail - Couldn't look up the robot's email address
	NoBotEmail
	// MailError - There was an error sending email
	MailError

	// InvalidPluginID - The external plugin made a function call with an invalid plugin ID
	InvalidPluginID
)

func (ret RetVal) String() string {
	errMsg := []string{
		"Ok",
		"User not found",
		"Channel not found",
		"Attribute not found",
		"Failed sending direct message to user",
		"Failed to join the channel",
		"Datum not found in robot's brain",
		"Datum lock expired before update",
		"Problem unmarshalling JSON/Yaml",
		"Brain storage failed",
		"Invalid string given for datum key",
		"Argument to GetPluginConfig wasn't a double pointer to a struct",
		"Mismatch between struct registered in RegisterPlugin and struct passed to GetPluginConfig",
		"Plugin configuration didn't have a Config section",
		"User OTP configuration not found",
		"Unspecified error in checking OTP",
		"The user's reply didn't match the requested regex",
		"The user didn't respond within the given timeout",
		"The user is already engaged in an interactive command with the robot in the same channel",
		"The matcher key supplied in WaitForReply doesn't correspond to a configured regex, or a provided regex didn't compile",
		"User email attribute not available",
		"Robot email attribute not available",
		"Unspecified error sending email",
	}
	return errMsg[int(ret)]
}
<commit_msg>Ensure normal exit doesn't indicate success<commit_after>package bot

// RetVal is a integer type for returning error conditions from bot methods, or 0 for Ok
type RetVal int

// PlugRetVal is an integer type for return values from plugins, mainly for elevation & authorization
type PlugRetVal int

const (
	// Normal exit is for non-auth/non-elevating plugins; since this is the
	// default exit value, we don't use it to indicate successful authentication
	// or elevation.
	Normal PlugRetVal = iota
	// Success indicates successful authorization or elevation
	Success
	// Fail indicates requested authorization or elevation failed
	Fail
	// MechanismFail indicates authorization or elevation couldn't be determined due to a technical issue that should be logged
	MechanismFail
	// ConfigurationFail indicates authorization or elevation failed due to misconfiguration
	ConfigurationFail
)

const (
	// Ok indicates a successful result
	Ok RetVal = iota // success

	/* Connector issues */

	// UserNotFound - failed lookup
	UserNotFound
	// ChannelNotFound - failed lookup
	ChannelNotFound
	// AttributeNotFound - failed looking up user/robot attributes like email, name, etc.
	AttributeNotFound
	// FailedUserDM - the bot was not able to send an direct message (DM) to a given user
	FailedUserDM
	// FailedChannelJoin - the robot couldn't join a channel; e.g. slack doesn't allow bots to join
	FailedChannelJoin

	/* Brain Maladies */

	// DatumNotFound - key not found in the global hash when update called
	DatumNotFound
	// DatumLockExpired - A datum was checked out for too long, and the lock expired
	DatumLockExpired
	// DataFormatError - Problem unmarshalling JSON
	DataFormatError
	// BrainFailed - An error condition prevented the brain from storing/retrieving; redis down, file write failed, etc.
	BrainFailed
	// InvalidDatumKey - Key name didn't match the regex for valid key names
	InvalidDatumKey

	/* GetPluginConfig */

	// InvalidDblPtr - GetPluginConfig wasn't called with a double-pointer to a config struct
	InvalidDblPtr
	// InvalidCfgStruct - The struct type in GetPluginConfig doesn't match the struct registered for the plugin
	InvalidCfgStruct
	// NoConfigFound - The plugin doesn't have any config data
	NoConfigFound

	/* Prompt(User)ForReply */

	// RetryPrompt - There was already a prompt in progress for the user/channel
	RetryPrompt
	// ReplyNotMatched - The user reply didn't match the pattern waited for
	ReplyNotMatched
	// UseDefaultValue - The user replied with a single '=', meaning use a default value
	UseDefaultValue
	// TimeoutExpired - The user didn't reply within the given timeout
	TimeoutExpired
	// Interrupted - The user issued another command instead of replying, or replied with '-' (cancel)
	Interrupted
	// MatcherNotFound - There was no matcher configured with the given string, or the regex didn't compile
	MatcherNotFound

	/* Email */

	// NoUserEmail - Couldn't look up the user's email address
	NoUserEmail
	// NoBotEmail - Couldn't look up the robot's email address
	NoBotEmail
	// MailError - There was an error sending email
	MailError

	// InvalidPluginID - The external plugin made a function call with an invalid plugin ID
	InvalidPluginID
)

func (ret RetVal) String() string {
	errMsg := []string{
		"Ok",
		"User not found",
		"Channel not found",
		"Attribute not found",
		"Failed sending direct message to user",
		"Failed to join the channel",
		"Datum not found in robot's brain",
		"Datum lock expired before update",
		"Problem unmarshalling JSON/Yaml",
		"Brain storage failed",
		"Invalid string given for datum key",
		"Argument to GetPluginConfig wasn't a double pointer to a struct",
		"Mismatch between struct registered in RegisterPlugin and struct passed to GetPluginConfig",
		"Plugin configuration didn't have a Config section",
		"User OTP configuration not found",
		"Unspecified error in checking OTP",
		"The user's reply didn't match the requested regex",
		"The user didn't respond within the given timeout",
		"The user is already engaged in an interactive command with the robot in the same channel",
		"The matcher key supplied in WaitForReply doesn't correspond to a configured regex, or a provided regex didn't compile",
		"User email attribute not available",
		"Robot email attribute not available",
		"Unspecified error sending email",
	}
	return errMsg[int(ret)]
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"strings"
	"time"

	"strconv"

	"github.com/nlopes/slack"
)

var (
	prefix        string = "party_"
	partyIndexKey string = prefix + "keys"
)

func rescheduleParty(bot *Meu) {
	items, err := bot.rc.Keys(prefix + "*")
	if err != nil {
		return
	}
	now := time.Now()
	for _, key := range items {
		parts := strings.Split(key, "_")
		sec, _ := strconv.Atoi(parts[1])
		saved_time := time.Unix(int64(sec), 0)
		if now.After(saved_time) {
			bot.rc.Erase(key)
		} else {
			scheduleParty(bot, &saved_time, parts[2])
			registerToIndex(bot, &saved_time, key)
		}
	}

	bot.rc.SortedSetRemoveRange(partyIndexKey, 0, now.Unix())
}

func registerToIndex(bot *Meu, date *time.Time, key string) {
	if _, err := bot.rc.SortedSetRank(partyIndexKey, key).Result(); err != nil {
		bot.rc.SortedSetAdd(partyIndexKey, int(date.Unix()), key)
	}
}

func scheduleParty(bot *Meu, date *time.Time, keyword string) {
	bot.cr.AddFunc(fmt.Sprintf("0 %d %d %d %d *", date.Minute(), date.Hour(), date.Day(), date.Month()), alarmFuncGenerator(bot, keyword, partyKey(date, keyword)))
}

func partyKey(date *time.Time, keyword string) string {
	return prefix + strconv.FormatInt(date.Unix(), 10) + "_" + keyword
}

func alarmFuncGenerator(bot *Meu, keyword string, key string) func() {
	return func() {
		list, err := bot.rc.SetList(key)
		bot.rc.Erase(key)
		if err == nil {
			members := make([]string, len(list))
			for i, item := range list {
				members[i] = fmt.Sprintf("<@%s>", item)
			}
			bot.PostMessage("#random", fmt.Sprintf("'%s' 파티 10분 전이다 메우. %s", keyword, strings.Join(members, " ")), slack.PostMessageParameters{
				AsUser:    false,
				Username:  "파티 안내원 메우",
				IconEmoji: ":meu:",
			})
		}
	}
}

func register_party(bot *Meu, e *slack.MessageEvent, matched []string) {
	now := time.Now()
	month, err := strconv.Atoi(matched[1])
	var not_set struct {
		month bool
		day   bool
	}
	if err != nil {
		month = int(now.Month())
		not_set.month = true
	}
	day, err := strconv.Atoi(matched[2])
	if err != nil {
		day = now.Day()
		not_set.day = true
	}
	hour, err := strconv.Atoi(matched[3])
	min, err := strconv.Atoi(matched[4])
	if err != nil {
		min = 0
	}
	keyword := matched[5]

	date := time.Date(now.Year(), time.Month(month), day, hour, min, 0, 0, now.Location())
	if date.Before(now) {
		corrected := false
		// first try after 12 hour
		if not_set.day {
			if date.Hour() < 12 {
				date = date.Add(time.Hour * 12)
				if corrected = !date.Before(now); !corrected {
					// reset
					date = date.Add(time.Hour * -12)
				}
			}
			if !corrected {
				date = date.AddDate(0, 0, 1)
			}
		} else if not_set.month {
			date = date.AddDate(0, 1, 0)
		} else {
			date = date.AddDate(1, 0, 0)
		}
		if date.Before(now) {
			bot.replySimple(e, "과거에 대해서 파티를 모집할 수 없다 메우")
			return
		}
	}
	key := partyKey(&date, keyword)
	inserted := bot.rc.SetAdd(key, e.User)
	registerToIndex(bot, &date, key)
	if inserted.Val() == 1 {
		bot.replySimple(e, fmt.Sprintf("파티 대기에 들어갔다 메우. - %s %s", date.String(), keyword))
		cardinal := bot.rc.SetCard(key)
		if cardinal.Val() == 1 {
			scheduleParty(bot, &date, keyword)
		}
	} else {
		bot.replySimple(e, fmt.Sprintf("이미 들어가있는 파티다 메우. - %s %s", date.String(), keyword))
	}
}
<commit_msg>Seperate date correction function<commit_after>package bot

import (
	"fmt"
	"strings"
	"time"

	"strconv"

	"github.com/nlopes/slack"
)

var (
	prefix        string = "party_"
	partyIndexKey string = prefix + "keys"
)

func rescheduleParty(bot *Meu) {
	items, err := bot.rc.Keys(prefix + "*")
	if err != nil {
		return
	}
	now := time.Now()
	for _, key := range items {
		parts := strings.Split(key, "_")
		sec, _ := strconv.Atoi(parts[1])
		saved_time := time.Unix(int64(sec), 0)
		if now.After(saved_time) {
			bot.rc.Erase(key)
		} else {
			scheduleParty(bot, &saved_time, parts[2])
			registerToIndex(bot, &saved_time, key)
		}
	}

	bot.rc.SortedSetRemoveRange(partyIndexKey, 0, now.Unix())
}

func registerToIndex(bot *Meu, date *time.Time, key string) {
	if _, err := bot.rc.SortedSetRank(partyIndexKey, key).Result(); err != nil {
		bot.rc.SortedSetAdd(partyIndexKey, int(date.Unix()), key)
	}
}

func scheduleParty(bot *Meu, date *time.Time, keyword string) {
	bot.cr.AddFunc(fmt.Sprintf("0 %d %d %d %d *", date.Minute(), date.Hour(), date.Day(), date.Month()), alarmFuncGenerator(bot, keyword, partyKey(date, keyword)))
}

func partyKey(date *time.Time, keyword string) string {
	return prefix + strconv.FormatInt(date.Unix(), 10) + "_" + keyword
}

func alarmFuncGenerator(bot *Meu, keyword string, key string) func() {
	return func() {
		list, err := bot.rc.SetList(key)
		bot.rc.Erase(key)
		if err == nil {
			members := make([]string, len(list))
			for i, item := range list {
				members[i] = fmt.Sprintf("<@%s>", item)
			}
			bot.PostMessage("#random", fmt.Sprintf("'%s' 파티 10분 전이다 메우. %s", keyword, strings.Join(members, " ")), slack.PostMessageParameters{
				AsUser:    false,
				Username:  "파티 안내원 메우",
				IconEmoji: ":meu:",
			})
		}
	}
}

func correctDate(matched []string) *time.Time {
	now := time.Now()
	month, err := strconv.Atoi(matched[0])
	var not_set struct {
		month bool
		day   bool
	}
	if err != nil {
		month = int(now.Month())
		not_set.month = true
	}
	day, err := strconv.Atoi(matched[1])
	if err != nil {
		day = now.Day()
		not_set.day = true
	}
	hour, err := strconv.Atoi(matched[2])
	min, err := strconv.Atoi(matched[3])
	if err != nil {
		min = 0
	}

	date := time.Date(now.Year(), time.Month(month), day, hour, min, 0, 0, now.Location())
	if date.Before(now) {
		corrected := false
		// first try after 12 hour
		if not_set.day {
			if date.Hour() < 12 {
				date = date.Add(time.Hour * 12)
				if corrected = !date.Before(now); !corrected {
					// reset
					date = date.Add(time.Hour * -12)
				}
			}
			if !corrected {
				date = date.AddDate(0, 0, 1)
			}
		} else if not_set.month {
			date = date.AddDate(0, 1, 0)
		} else {
			date = date.AddDate(1, 0, 0)
		}
		if date.Before(now) {
			return nil
		}
	}

	return &date
}

func register_party(bot *Meu, e *slack.MessageEvent, matched []string) {
	keyword := matched[5]

	date := correctDate(matched[1:])
	if date == nil {
		bot.replySimple(e, "과거에 대해서 파티를 모집할 수 없다 메우")
		return
	}

	key := partyKey(date, keyword)
	inserted := bot.rc.SetAdd(key, e.User)
	registerToIndex(bot, date, key)
	if inserted.Val() == 1 {
		bot.replySimple(e, fmt.Sprintf("파티 대기에 들어갔다 메우. - %s %s", date.String(), keyword))
		cardinal := bot.rc.SetCard(key)
		if cardinal.Val() == 1 {
			scheduleParty(bot, date, keyword)
		}
	} else {
		bot.replySimple(e, fmt.Sprintf("이미 들어가있는 파티다 메우. - %s %s", date.String(), keyword))
	}
}
<|endoftext|>


<commit_before>package box

import (
	"errors"
	"github.com/as/frame/font"
	"io"
	"unicode/utf8"
	//	"log"
)

var (
	ErrZeroRead     = errors.New("zero length read")
	ErrDoubleUnread = errors.New("double unread")
)

type Ruler interface {
	Advance() error
	Bytes() []byte
	Next() (size, width int, err error)
	Last() []byte
	Len() int
	Unread() (size, width int, err error)
	Width() int
	Reset(p []byte)
}

type byteRuler struct {
	*font.Font
	b                   []byte
	i                   int
	w                   int
	lastSize, lastWidth int
}

type runeRuler struct {
	*font.Font
	b                   []byte
	i                   int
	w                   int
	lastSize, lastWidth int
}

func NewByteRuler(b []byte, ft *font.Font) Ruler {
	return &byteRuler{Font: ft, b: b}
}

func NewRuneRuler(b []byte, ft *font.Font) Ruler {
	return &runeRuler{Font: ft, b: b}
}

func (bs *byteRuler) Bytes() []byte { return bs.b[:bs.i] }
func (bs *runeRuler) Bytes() []byte { return bs.b[:bs.i] }

func (bs *byteRuler) Advance() (err error) {
	if bs.i == len(bs.b) {
		return io.EOF
	}
	bs.b = bs.b[bs.i:]
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0

	return nil
}

func (bs *runeRuler) Advance() (err error) {
	if bs.i == len(bs.b) {
		return io.EOF
	}
	bs.b = bs.b[bs.i:]
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0

	return nil
}

func (bs *byteRuler) Next() (size, widthPx int, err error) {
	if bs.i == len(bs.b) {
		return 0, 0, io.EOF
	}
	size = 1
	widthPx = bs.Font.MeasureByte(bs.b[bs.i])
	bs.i += size
	bs.w += widthPx
	bs.lastSize = size
	bs.lastWidth = widthPx
	return
}

func (bs *runeRuler) Next() (size, widthPx int, err error) {
	if bs.i == len(bs.b) {
		return 0, 0, io.EOF
	}
	r, size := utf8.DecodeRune(bs.b[bs.i:])
	if r == utf8.RuneError {
	}
	if size == 0 {
		panic("size = 0")
	}
	widthPx = bs.MeasureRune(r)
	bs.i += size
	bs.w += widthPx
	bs.lastSize = size
	bs.lastWidth = widthPx
	//	log.Printf("size:%d widthpx: %d err=%s\n", size, widthPx, err)
	return
}

func (bs *byteRuler) Unread() (size, widthPx int, err error) {
	if bs.lastSize == -1 {
		return 0, 0, ErrDoubleUnread
	}
	if bs.i-bs.lastSize < 0 {
		return 0, 0, ErrZeroRead
	}
	lsz := bs.lastSize
	bs.i -= lsz
	bs.w -= bs.lastWidth
	bs.lastSize = -1
	return lsz, bs.lastWidth, nil
}

func (bs *runeRuler) Unread() (size, widthPx int, err error) {
	if bs.lastSize == -1 {
		return 0, 0, ErrDoubleUnread
	}
	if bs.i-bs.lastSize < 0 {
		return 0, 0, ErrZeroRead
	}
	lsz := bs.lastSize
	bs.i -= lsz
	bs.w -= bs.lastWidth
	bs.lastSize = -bs.lastSize
	return lsz, bs.lastWidth, nil
}

func (bs *byteRuler) Last() []byte {
	return bs.b[bs.i-bs.lastSize : bs.i]
}

func (bs *runeRuler) Last() []byte {
	//	log.Printf("called Last: bytes=%s\n\tbs.i=%d\n\tlastsize=%d\n\n",bs.b, bs.i, bs.lastSize)
	return bs.b[bs.i-bs.lastSize : bs.i]
}

func (bs *byteRuler) Reset(p []byte) {
	bs.b = p
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0
}
func (bs *runeRuler) Reset(p []byte) {
	bs.b = p
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0
}
func (bs *byteRuler) Len() int   { return bs.i }
func (bs *byteRuler) Width() int { return bs.w }
func (bs *runeRuler) Len() int   { return bs.i }
func (bs *runeRuler) Width() int { return bs.w }
<commit_msg>cache measurebyte<commit_after>package box

import (
	"errors"
	"github.com/as/frame/font"
	"io"
	"unicode/utf8"
	//	"log"
)

var (
	ErrZeroRead     = errors.New("zero length read")
	ErrDoubleUnread = errors.New("double unread")
)

type Ruler interface {
	Advance() error
	Bytes() []byte
	Next() (size, width int, err error)
	Last() []byte
	Len() int
	Unread() (size, width int, err error)
	Width() int
	Reset(p []byte)
}

type byteRuler struct {
	*font.Font
	b                   []byte
	i                   int
	w                   int
	lastSize, lastWidth int
	sizetab [256]int
}

type runeRuler struct {
	*font.Font
	b                   []byte
	i                   int
	w                   int
	lastSize, lastWidth int
}

func NewByteRuler(b []byte, ft *font.Font) Ruler {
	return &byteRuler{Font: ft, b: b}
}

func (bs *byteRuler) MeasureByte(b byte) int{
	if px:= bs.sizetab[b]; px != 0{
		return px
	}
	bs.sizetab[b] = bs.Font.MeasureByte(b)
	return bs.sizetab[b]
}

func NewRuneRuler(b []byte, ft *font.Font) Ruler {
	return &runeRuler{Font: ft, b: b}
}

func (bs *byteRuler) Bytes() []byte { return bs.b[:bs.i] }
func (bs *runeRuler) Bytes() []byte { return bs.b[:bs.i] }

func (bs *byteRuler) Advance() (err error) {
	if bs.i == len(bs.b) {
		return io.EOF
	}
	bs.b = bs.b[bs.i:]
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0

	return nil
}

func (bs *runeRuler) Advance() (err error) {
	if bs.i == len(bs.b) {
		return io.EOF
	}
	bs.b = bs.b[bs.i:]
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0

	return nil
}

func (bs *byteRuler) Next() (size, widthPx int, err error) {
	if bs.i == len(bs.b) {
		return 0, 0, io.EOF
	}
	size = 1
	widthPx = bs.MeasureByte(bs.b[bs.i])
	bs.i += size
	bs.w += widthPx
	bs.lastSize = size
	bs.lastWidth = widthPx
	return
}

func (bs *runeRuler) Next() (size, widthPx int, err error) {
	if bs.i == len(bs.b) {
		return 0, 0, io.EOF
	}
	r, size := utf8.DecodeRune(bs.b[bs.i:])
	if r == utf8.RuneError {
	}
	if size == 0 {
		panic("size = 0")
	}
	widthPx = bs.MeasureRune(r)
	bs.i += size
	bs.w += widthPx
	bs.lastSize = size
	bs.lastWidth = widthPx
	//	log.Printf("size:%d widthpx: %d err=%s\n", size, widthPx, err)
	return
}

func (bs *byteRuler) Unread() (size, widthPx int, err error) {
	if bs.lastSize == -1 {
		return 0, 0, ErrDoubleUnread
	}
	if bs.i-bs.lastSize < 0 {
		return 0, 0, ErrZeroRead
	}
	lsz := bs.lastSize
	bs.i -= lsz
	bs.w -= bs.lastWidth
	bs.lastSize = -1
	return lsz, bs.lastWidth, nil
}

func (bs *runeRuler) Unread() (size, widthPx int, err error) {
	if bs.lastSize == -1 {
		return 0, 0, ErrDoubleUnread
	}
	if bs.i-bs.lastSize < 0 {
		return 0, 0, ErrZeroRead
	}
	lsz := bs.lastSize
	bs.i -= lsz
	bs.w -= bs.lastWidth
	bs.lastSize = -bs.lastSize
	return lsz, bs.lastWidth, nil
}

func (bs *byteRuler) Last() []byte {
	return bs.b[bs.i-bs.lastSize : bs.i]
}

func (bs *runeRuler) Last() []byte {
	//	log.Printf("called Last: bytes=%s\n\tbs.i=%d\n\tlastsize=%d\n\n",bs.b, bs.i, bs.lastSize)
	return bs.b[bs.i-bs.lastSize : bs.i]
}

func (bs *byteRuler) Reset(p []byte) {
	bs.b = p
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0
}
func (bs *runeRuler) Reset(p []byte) {
	bs.b = p
	bs.i = 0
	bs.w = 0
	bs.lastSize = 0
	bs.lastWidth = 0
}
func (bs *byteRuler) Len() int   { return bs.i }
func (bs *byteRuler) Width() int { return bs.w }
func (bs *runeRuler) Len() int   { return bs.i }
func (bs *runeRuler) Width() int { return bs.w }
<|endoftext|>


<commit_before>//package broadcast provides a broadcast channel for event loops.
package broadcast

type broadcast struct {
	subscribers []*bchan
	sub         chan *bchan
	unsub       chan int
	bufsize     int
}

//run is the broadcast event loop. It dies when all subscribers Close.
func (b *broadcast) run() {
	for {
		select {
		case s := <-sub:
			subscribers = append(subscribers, s)
		case i := <-unsub:
			subscribers = append(subscribers[:i], subscribers[i+1:]...)
		case m, ok := <-msg:
			if !ok {
				panic("Send channel is closed")
			}
			for _, v := range subscribers {
				v <- m
			}
		}
	}
}

type bchan struct {
	send chan interface{}
	recv chan interface{}
}

func (b *bchan) close() {
	close(b.send)
	close(b.recv)
}

type Chan struct {
	Send chan<- interface{}
	Recv <-chan interface{}
	id   int
	bc   *broadcast
}

func New(n int) *Chan {
	//TODO init broadcast
	go func() {
		defer close(bc.sub)
		defer close(bc.unsub)
		bc.run()
	}()
}

func (c *Chan) Close() {
	//TODO should I call close here, or in run() ?
	c.bc.subscribers[c.id].close()
	c.bc.unsub <- c.id
}

//Is having a Chan beget more Chans make sense? Is this better than
//having a public Broadcast type whose only purpose is to beget Chans?
func (c *Chan) Subscribe() *Chan {
	s := make(chan<- interface{}, c.bf.bufsize)
	r := make(chan<- interface{}, c.bf.bufsize)
	c.bc.sub <- bchan{s, r}
	return &Chan{s, r, len(c.bc.subscribers) - 1, bc}
}
<commit_msg>a better design<commit_after>//package broadcast provides a broadcast channel for event loops.
package broadcast

//Broadcast represents a broadcast channel. Broadcast channels have a goroutine that takes messages from Send and transmits them to all subscriber channels.
type Broadcast struct {
	subscribers []chan interface{}
	sub         chan chan interface
	unsub       chan int
	//Send is the broadcast channel. All subscribers receive
	//interfaces sent on this channel. Do not close this channel.
	//Instead, call the Close() method to close down all both the
	//Send channel and all subscriber channels.
	Send        chan<-interface{}
}

//run is the broadcast event loop. It dies when all subscribers Close.
func (b *broadcast) run() {
	for {
		select {
		case s := <-sub:
			subscribers = append(subscribers, s)
		case i := <-unsub:
			subscribers = append(subscribers[:i], subscribers[i+1:]...)
		case m, ok := <-msg:
			if !ok {
				panic("Send channel is closed")
			}
			for _, v := range subscribers {
				v <- m
			}
		}
	}
}

//New creates a Broadcast with the given buffer size on the Send channel.
func New(n int) *Broadcast {

}

//Subscribe returns a new subscriber channel with the given buffer size.
func (b *Broadcast) Subscribe(n int) <-chan interface{} {
	c := make(chan interface{}, n)
	b.sub <- c
	return c
}

//Close closes the broadcast channel and all subscriber channels.
func (*Broadcast) Close() {

}

//Chan
type Chan struct {
	b *Broadcast
	id int
	Rcv <-chan interface{}
}

//Close unsubscribes from a Broadcast channel. You should always Close an unused Chan, because eventually (depending on the size of the Chan's buffer) the Broadcast will block trying to send a message to it, and no other subscribed channels will receive messages. Alternatively, closing the Broadcast this Chan is subscribed to will close the Chan.
func (*Chan) Close() {

}
<|endoftext|>