<commit_before>package testing_test

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"strings"
	"testing"

	"github.com/influxdata/flux/ast"
	"github.com/influxdata/flux/execute"
	"github.com/influxdata/flux/lang"
	"github.com/influxdata/flux/parser"
	"github.com/influxdata/flux/runtime"
	"github.com/influxdata/flux/stdlib"

	platform "github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/cmd/influxd/launcher"
	influxdbcontext "github.com/influxdata/influxdb/v2/context"
	"github.com/influxdata/influxdb/v2/kit/feature"
	"github.com/influxdata/influxdb/v2/kit/feature/override"
	"github.com/influxdata/influxdb/v2/mock"
	"github.com/influxdata/influxdb/v2/query"
	_ "github.com/influxdata/influxdb/v2/query/stdlib"
	itesting "github.com/influxdata/influxdb/v2/query/stdlib/testing" // Import the stdlib
)

// Flagger for end-to-end test cases. This flagger contains a pointer to a
// single struct instance that all the test cases will consult. It will return flags
// based on the contents of FluxEndToEndFeatureFlags and the currently active
// test case. This works only because tests are serialized. We can set the
// current test case in the common flagger state, then run the test. If we were
// to run tests in parallel we would need to create multiple users and assign
// them different flags combinations, then run the tests under different users.

type Flagger struct {
	flaggerState *FlaggerState
}

type FlaggerState struct {
	Path           string
	Name           string
	FeatureFlags   itesting.PerTestFeatureFlagMap
	DefaultFlagger feature.Flagger
}

func newFlagger(featureFlagMap itesting.PerTestFeatureFlagMap) Flagger {
	flaggerState := &FlaggerState{}
	flaggerState.FeatureFlags = featureFlagMap
	flaggerState.DefaultFlagger = feature.DefaultFlagger()
	return Flagger{flaggerState}
}

func (f Flagger) SetActiveTestCase(path string, name string) {
	f.flaggerState.Path = path
	f.flaggerState.Name = name
}

func (f Flagger) Flags(ctx context.Context, _f ...feature.Flag) (map[string]interface{}, error) {
	// If an override is set for the test case, construct an override flagger
	// and use it's computed flags.
	overrides := f.flaggerState.FeatureFlags[f.flaggerState.Path][f.flaggerState.Name]
	if overrides != nil {
		f, err := override.Make(overrides, nil)
		if err != nil {
			panic("failed to construct override flagger, probably an invalid flag in FluxEndToEndFeatureFlags")
		}
		return f.Flags(ctx)
	}

	// Otherwise use flags from a default flagger.
	return f.flaggerState.DefaultFlagger.Flags(ctx)
}

// Default context.
var ctx = influxdbcontext.SetAuthorizer(context.Background(), mock.NewMockAuthorizer(true, nil))

func init() {
	runtime.FinalizeBuiltIns()
}

func TestFluxEndToEnd(t *testing.T) {
	runEndToEnd(t, stdlib.FluxTestPackages)
}
func BenchmarkFluxEndToEnd(b *testing.B) {
	benchEndToEnd(b, stdlib.FluxTestPackages)
}

func runEndToEnd(t *testing.T, pkgs []*ast.Package) {
	l := launcher.NewTestLauncher()

	flagger := newFlagger(itesting.FluxEndToEndFeatureFlags)
	l.SetFlagger(flagger)

	l.RunOrFail(t, ctx)
	defer l.ShutdownOrFail(t, ctx)
	l.SetupOrFail(t)

	for _, pkg := range pkgs {
		test := func(t *testing.T, f func(t *testing.T)) {
			t.Run(pkg.Path, f)
		}
		if pkg.Path == "universe" {
			test = func(t *testing.T, f func(t *testing.T)) {
				f(t)
			}
		}

		test(t, func(t *testing.T) {
			for _, file := range pkg.Files {
				name := strings.TrimSuffix(file.Name, "_test.flux")
				t.Run(name, func(t *testing.T) {
					if reason, ok := itesting.FluxEndToEndSkipList[pkg.Path][name]; ok {
						t.Skip(reason)
					}

					flagger.SetActiveTestCase(pkg.Path, name)
					testFlux(t, l, file)
				})
			}
		})
	}
}

func benchEndToEnd(b *testing.B, pkgs []*ast.Package) {
	// TODO(jsternberg): These benchmarks don't run properly
	// and need to be fixed. Commenting out the code for now.
	b.Skip("https://github.com/influxdata/influxdb/issues/15391")
	// l := launcher.RunTestLauncherOrFail(b, ctx)
	// l.SetupOrFail(b)
	// defer l.ShutdownOrFail(b, ctx)
	// for _, pkg := range pkgs {
	// 	pkg := pkg.Copy().(*ast.Package)
	// 	name := pkg.Files[0].Name
	// 	b.Run(name, func(b *testing.B) {
	// 		if reason, ok := itesting.FluxEndToEndSkipList[strings.TrimSuffix(name, ".flux")]; ok {
	// 			b.Skip(reason)
	// 		}
	// 		b.ResetTimer()
	// 		b.ReportAllocs()
	// 		for i := 0; i < b.N; i++ {
	// 			testFlux(b, l, pkg)
	// 		}
	// 	})
	// }
}

func makeTestPackage(file *ast.File) *ast.Package {
	file = file.Copy().(*ast.File)
	file.Package.Name.Name = "main"
	pkg := &ast.Package{
		Package: "main",
		Files:   []*ast.File{file},
	}
	return pkg
}

// This options definition puts to() in the path of the CSV input. The tests
// get run in this case and they would normally pass, if we checked the
// results, but don't look at them.
var writeOptSource = `
import "testing"
import c "csv"

option testing.loadStorage = (csv) => {
	return c.from(csv: csv) |> to(bucket: bucket, org: org)
}
`

// This options definition is for the second run, the test run. It loads the
// data from previously written bucket. We check the results after running this
// second pass and report on them.
var readOptSource = `
import "testing"
import c "csv"

option testing.loadStorage = (csv) => {
	return from(bucket: bucket)
}
`

var writeOptAST *ast.File
var readOptAST *ast.File

func prepareOptions(optionsSource string) *ast.File {
	pkg := parser.ParseSource(optionsSource)
	if ast.Check(pkg) > 0 {
		panic(ast.GetError(pkg))
	}
	return pkg.Files[0]
}

func init() {
	writeOptAST = prepareOptions(writeOptSource)
	readOptAST = prepareOptions(readOptSource)
}

func testFlux(t testing.TB, l *launcher.TestLauncher, file *ast.File) {
	b := &platform.Bucket{
		OrgID:           l.Org.ID,
		Name:            t.Name(),
		RetentionPeriod: 0,
	}

	s := l.BucketService(t)
	if err := s.CreateBucket(context.Background(), b); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := s.DeleteBucket(context.Background(), b.ID); err != nil {
			t.Logf("Failed to delete bucket: %s", err)
		}
	}()

	// Define bucket and org options
	bucketOpt := &ast.OptionStatement{
		Assignment: &ast.VariableAssignment{
			ID:   &ast.Identifier{Name: "bucket"},
			Init: &ast.StringLiteral{Value: b.Name},
		},
	}
	orgOpt := &ast.OptionStatement{
		Assignment: &ast.VariableAssignment{
			ID:   &ast.Identifier{Name: "org"},
			Init: &ast.StringLiteral{Value: l.Org.Name},
		},
	}

	executeWithOptions(t, l, bucketOpt, orgOpt, writeOptAST, file)

	results := executeWithOptions(t, l, bucketOpt, orgOpt, readOptAST, file)
	if results != nil {
		logFormatted := func(name string, results map[string]*bytes.Buffer) {
			if _, ok := results[name]; ok {
				scanner := bufio.NewScanner(results[name])
				for scanner.Scan() {
					t.Log(scanner.Text())
				}
			} else {
				t.Log("table ", name, " not present in results")
			}
		}
		if _, ok := results["diff"]; ok {
			t.Error("diff table was not empty")
			logFormatted("diff", results)
			logFormatted("want", results)
			logFormatted("got", results)
		}
	}
}

func executeWithOptions(t testing.TB, l *launcher.TestLauncher, bucketOpt *ast.OptionStatement,
	orgOpt *ast.OptionStatement, optionsAST *ast.File, file *ast.File) map[string]*bytes.Buffer {
	var results map[string]*bytes.Buffer

	options := optionsAST.Copy().(*ast.File)
	options.Body = append([]ast.Statement{bucketOpt, orgOpt}, options.Body...)

	// Add options to pkg
	pkg := makeTestPackage(file)
	pkg.Files = append(pkg.Files, options)

	// Use testing.inspect call to get all of diff, want, and got
	inspectCalls := stdlib.TestingInspectCalls(pkg)
	if len(inspectCalls.Body) == 0 {
		t.Skip("no tests found")
		return nil
	}
	pkg.Files = append(pkg.Files, inspectCalls)

	bs, err := json.Marshal(pkg)
	if err != nil {
		t.Fatal(err)
	}

	req := &query.Request{
		OrganizationID: l.Org.ID,
		Compiler:       lang.ASTCompiler{AST: bs},
	}

	if r, err := l.FluxQueryService().Query(ctx, req); err != nil {
		t.Fatal(err)
	} else {
		results = make(map[string]*bytes.Buffer)

		for r.More() {
			v := r.Next()

			if _, ok := results[v.Name()]; !ok {
				results[v.Name()] = &bytes.Buffer{}
			}
			err := execute.FormatResult(results[v.Name()], v)
			if err != nil {
				t.Error(err)
			}
		}
		if err := r.Err(); err != nil {
			t.Error(err)
		}
	}
	return results
}
<commit_msg>feat: add additional log to flux e2e tests (#22366)<commit_after>package testing_test

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"testing"

	"github.com/influxdata/flux/ast"
	"github.com/influxdata/flux/execute"
	"github.com/influxdata/flux/lang"
	"github.com/influxdata/flux/parser"
	"github.com/influxdata/flux/runtime"
	"github.com/influxdata/flux/stdlib"

	platform "github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/cmd/influxd/launcher"
	influxdbcontext "github.com/influxdata/influxdb/v2/context"
	"github.com/influxdata/influxdb/v2/http"
	"github.com/influxdata/influxdb/v2/kit/feature"
	"github.com/influxdata/influxdb/v2/kit/feature/override"
	"github.com/influxdata/influxdb/v2/mock"
	"github.com/influxdata/influxdb/v2/query"
	_ "github.com/influxdata/influxdb/v2/query/stdlib"
	itesting "github.com/influxdata/influxdb/v2/query/stdlib/testing" // Import the stdlib
)

// Flagger for end-to-end test cases. This flagger contains a pointer to a
// single struct instance that all the test cases will consult. It will return flags
// based on the contents of FluxEndToEndFeatureFlags and the currently active
// test case. This works only because tests are serialized. We can set the
// current test case in the common flagger state, then run the test. If we were
// to run tests in parallel we would need to create multiple users and assign
// them different flags combinations, then run the tests under different users.

type Flagger struct {
	flaggerState *FlaggerState
}

type FlaggerState struct {
	Path           string
	Name           string
	FeatureFlags   itesting.PerTestFeatureFlagMap
	DefaultFlagger feature.Flagger
}

func newFlagger(featureFlagMap itesting.PerTestFeatureFlagMap) Flagger {
	flaggerState := &FlaggerState{}
	flaggerState.FeatureFlags = featureFlagMap
	flaggerState.DefaultFlagger = feature.DefaultFlagger()
	return Flagger{flaggerState}
}

func (f Flagger) SetActiveTestCase(path string, name string) {
	f.flaggerState.Path = path
	f.flaggerState.Name = name
}

func (f Flagger) Flags(ctx context.Context, _f ...feature.Flag) (map[string]interface{}, error) {
	// If an override is set for the test case, construct an override flagger
	// and use it's computed flags.
	overrides := f.flaggerState.FeatureFlags[f.flaggerState.Path][f.flaggerState.Name]
	if overrides != nil {
		f, err := override.Make(overrides, nil)
		if err != nil {
			panic("failed to construct override flagger, probably an invalid flag in FluxEndToEndFeatureFlags")
		}
		return f.Flags(ctx)
	}

	// Otherwise use flags from a default flagger.
	return f.flaggerState.DefaultFlagger.Flags(ctx)
}

// Default context.
var ctx = influxdbcontext.SetAuthorizer(context.Background(), mock.NewMockAuthorizer(true, nil))

func init() {
	runtime.FinalizeBuiltIns()
}

func TestFluxEndToEnd(t *testing.T) {
	runEndToEnd(t, stdlib.FluxTestPackages)
}
func BenchmarkFluxEndToEnd(b *testing.B) {
	benchEndToEnd(b, stdlib.FluxTestPackages)
}

func runEndToEnd(t *testing.T, pkgs []*ast.Package) {
	l := launcher.NewTestLauncher()

	flagger := newFlagger(itesting.FluxEndToEndFeatureFlags)
	l.SetFlagger(flagger)

	l.RunOrFail(t, ctx)
	defer l.ShutdownOrFail(t, ctx)
	l.SetupOrFail(t)

	for _, pkg := range pkgs {
		test := func(t *testing.T, f func(t *testing.T)) {
			t.Run(pkg.Path, f)
		}
		if pkg.Path == "universe" {
			test = func(t *testing.T, f func(t *testing.T)) {
				f(t)
			}
		}

		test(t, func(t *testing.T) {
			for _, file := range pkg.Files {
				name := strings.TrimSuffix(file.Name, "_test.flux")
				t.Run(name, func(t *testing.T) {
					if reason, ok := itesting.FluxEndToEndSkipList[pkg.Path][name]; ok {
						t.Skip(reason)
					}

					flagger.SetActiveTestCase(pkg.Path, name)
					testFlux(t, l, file)
				})
			}
		})
	}
}

func benchEndToEnd(b *testing.B, pkgs []*ast.Package) {
	// TODO(jsternberg): These benchmarks don't run properly
	// and need to be fixed. Commenting out the code for now.
	b.Skip("https://github.com/influxdata/influxdb/issues/15391")
	// l := launcher.RunTestLauncherOrFail(b, ctx)
	// l.SetupOrFail(b)
	// defer l.ShutdownOrFail(b, ctx)
	// for _, pkg := range pkgs {
	// 	pkg := pkg.Copy().(*ast.Package)
	// 	name := pkg.Files[0].Name
	// 	b.Run(name, func(b *testing.B) {
	// 		if reason, ok := itesting.FluxEndToEndSkipList[strings.TrimSuffix(name, ".flux")]; ok {
	// 			b.Skip(reason)
	// 		}
	// 		b.ResetTimer()
	// 		b.ReportAllocs()
	// 		for i := 0; i < b.N; i++ {
	// 			testFlux(b, l, pkg)
	// 		}
	// 	})
	// }
}

func makeTestPackage(file *ast.File) *ast.Package {
	file = file.Copy().(*ast.File)
	file.Package.Name.Name = "main"
	pkg := &ast.Package{
		Package: "main",
		Files:   []*ast.File{file},
	}
	return pkg
}

// This options definition puts to() in the path of the CSV input. The tests
// get run in this case and they would normally pass, if we checked the
// results, but don't look at them.
var writeOptSource = `
import "testing"
import c "csv"

option testing.loadStorage = (csv) => {
	return c.from(csv: csv) |> to(bucket: bucket, org: org)
}
`

// This options definition is for the second run, the test run. It loads the
// data from previously written bucket. We check the results after running this
// second pass and report on them.
var readOptSource = `
import "testing"
import c "csv"

option testing.loadStorage = (csv) => {
	return from(bucket: bucket)
}
`

var writeOptAST *ast.File
var readOptAST *ast.File

func prepareOptions(optionsSource string) *ast.File {
	pkg := parser.ParseSource(optionsSource)
	if ast.Check(pkg) > 0 {
		panic(ast.GetError(pkg))
	}
	return pkg.Files[0]
}

func init() {
	writeOptAST = prepareOptions(writeOptSource)
	readOptAST = prepareOptions(readOptSource)
}

func testFlux(t testing.TB, l *launcher.TestLauncher, file *ast.File) {
	b := &platform.Bucket{
		OrgID:           l.Org.ID,
		Name:            t.Name(),
		RetentionPeriod: 0,
	}

	s := l.BucketService(t)
	if err := s.CreateBucket(context.Background(), b); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := s.DeleteBucket(context.Background(), b.ID); err != nil {
			t.Logf("Failed to delete bucket: %s", err)
		}
	}()

	// Define bucket and org options
	bucketOpt := &ast.OptionStatement{
		Assignment: &ast.VariableAssignment{
			ID:   &ast.Identifier{Name: "bucket"},
			Init: &ast.StringLiteral{Value: b.Name},
		},
	}
	orgOpt := &ast.OptionStatement{
		Assignment: &ast.VariableAssignment{
			ID:   &ast.Identifier{Name: "org"},
			Init: &ast.StringLiteral{Value: l.Org.Name},
		},
	}

	executeWithOptions(t, l, bucketOpt, orgOpt, writeOptAST, file)

	results := executeWithOptions(t, l, bucketOpt, orgOpt, readOptAST, file)
	if results != nil {
		logFormatted := func(name string, results map[string]*bytes.Buffer) {
			if _, ok := results[name]; ok {
				scanner := bufio.NewScanner(results[name])
				for scanner.Scan() {
					t.Log(scanner.Text())
				}
			} else {
				t.Log("table ", name, " not present in results")
			}
		}
		if _, ok := results["diff"]; ok {
			t.Error("diff table was not empty")
			logFormatted("diff", results)
			logFormatted("want", results)
			logFormatted("got", results)

			t.Logf("all data in %s:", t.Name())
			logFormatted(t.Name(), allDataFromBucket(t, l, t.Name()))
		}
	}
}

func allDataFromBucket(t testing.TB, l *launcher.TestLauncher, bucket string) map[string]*bytes.Buffer {
	q := fmt.Sprintf(`from(bucket: "%s") |> range(start: 0)`, bucket)
	bs, err := http.SimpleQuery(l.URL(), q, l.Org.Name, l.Auth.Token)
	if err != nil {
		t.Fatal(err)
	}

	return map[string]*bytes.Buffer{bucket: bytes.NewBuffer(bs)}
}

func executeWithOptions(t testing.TB, l *launcher.TestLauncher, bucketOpt *ast.OptionStatement,
	orgOpt *ast.OptionStatement, optionsAST *ast.File, file *ast.File) map[string]*bytes.Buffer {
	var results map[string]*bytes.Buffer

	options := optionsAST.Copy().(*ast.File)
	options.Body = append([]ast.Statement{bucketOpt, orgOpt}, options.Body...)

	// Add options to pkg
	pkg := makeTestPackage(file)
	pkg.Files = append(pkg.Files, options)

	// Use testing.inspect call to get all of diff, want, and got
	inspectCalls := stdlib.TestingInspectCalls(pkg)
	if len(inspectCalls.Body) == 0 {
		t.Skip("no tests found")
		return nil
	}
	pkg.Files = append(pkg.Files, inspectCalls)

	bs, err := json.Marshal(pkg)
	if err != nil {
		t.Fatal(err)
	}

	req := &query.Request{
		OrganizationID: l.Org.ID,
		Compiler:       lang.ASTCompiler{AST: bs},
	}

	if r, err := l.FluxQueryService().Query(ctx, req); err != nil {
		t.Fatal(err)
	} else {
		results = make(map[string]*bytes.Buffer)

		for r.More() {
			v := r.Next()

			if _, ok := results[v.Name()]; !ok {
				results[v.Name()] = &bytes.Buffer{}
			}
			err := execute.FormatResult(results[v.Name()], v)
			if err != nil {
				t.Error(err)
			}
		}
		if err := r.Err(); err != nil {
			t.Error(err)
		}
	}
	return results
}
<|endoftext|>


<commit_before>/*
Copyright 2014 go-trello 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 trello

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Card struct {
	client                *Client
	Id                    string   `json:"id"`
	Name                  string   `json:"name"`
	Email                 string   `json:"email"`
	IdShort               int      `json:"idShort"`
	IdAttachmentCover     string   `json:"idAttachmentCover"`
	IdCheckLists          []string `json:"idCheckLists"`
	IdBoard               string   `json:"idBoard"`
	IdList                string   `json:"idList"`
	IdMembers             []string `json:"idMembers"`
	IdMembersVoted        []string `json:"idMembersVoted"`
	ManualCoverAttachment bool     `json:"manualCoverAttachment"`
	Closed                bool     `json:"closed"`
	Pos                   float32  `json:"pos"`
	ShortLink             string   `json:"shortLink"`
	DateLastActivity      string   `json:"dateLastActivity"`
	ShortUrl              string   `json:"shortUrl"`
	Subscribed            bool     `json:"subscribed"`
	Url                   string   `json:"url"`
	Due                   string   `json:"due"`
	Desc                  string   `json:"desc"`
	DescData              struct {
		Emoji struct{} `json:"emoji"`
	} `json:"descData"`
	CheckItemStates []struct {
		IdCheckItem string `json:"idCheckItem"`
		State       string `json:"state"`
	} `json:"checkItemStates"`
	Badges struct {
		Votes              int    `json:"votes"`
		ViewingMemberVoted bool   `json:"viewingMemberVoted"`
		Subscribed         bool   `json:"subscribed"`
		Fogbugz            string `json:"fogbugz"`
		CheckItems         int    `json:"checkItems"`
		CheckItemsChecked  int    `json:"checkItemsChecked"`
		Comments           int    `json:"comments"`
		Attachments        int    `json:"attachments"`
		Description        bool   `json:"description"`
		Due                string `json:"due"`
	} `json:"badges"`
	Labels []struct {
		Color string `json:"color"`
		Name  string `json:"name"`
	} `json:"labels"`
}

func (c *Client) Card(CardId string) (card *Card, err error) {
	req, err := http.NewRequest("GET", c.endpoint+"/card/"+CardId, nil)
	if err != nil {
		return
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	} else if resp.StatusCode != 200 {
		err = fmt.Errorf("Received unexpected status %d while trying to retrieve the server data", resp.StatusCode)
		return
	}

	err = json.Unmarshal(body, &card)
	card.client = c
	return
}

func (c *Card) Checklists() (checklists []Checklist, err error) {
	req, err := http.NewRequest("GET", c.client.endpoint+"/card/"+c.Id+"/checklists", nil)
	if err != nil {
		return
	}

	resp, err := c.client.client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	} else if resp.StatusCode != 200 {
		err = fmt.Errorf("Received unexpected status %d while trying to retrieve the server data", resp.StatusCode)
		return
	}

	err = json.Unmarshal(body, &checklists)
	for i, _ := range checklists {
		checklists[i].client = c.client
	}
	return
}

func (c *Card) Members() (members []Member, err error) {
	req, err := http.NewRequest("GET", c.client.endpoint+"/cards/"+c.Id+"/members", nil)
	if err != nil {
		return
	}

	resp, err := c.client.client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	} else if resp.StatusCode != 200 {
		err = fmt.Errorf("Received unexpected status %d while trying to retrieve the server data", resp.StatusCode)
		return
	}

	err = json.Unmarshal(body, &members)
	for i, _ := range members {
		members[i].client = c.client
	}
	return
}
<commit_msg>Add Card.Attachments() method<commit_after>/*
Copyright 2014 go-trello 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 trello

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Card struct {
	client                *Client
	Id                    string   `json:"id"`
	Name                  string   `json:"name"`
	Email                 string   `json:"email"`
	IdShort               int      `json:"idShort"`
	IdAttachmentCover     string   `json:"idAttachmentCover"`
	IdCheckLists          []string `json:"idCheckLists"`
	IdBoard               string   `json:"idBoard"`
	IdList                string   `json:"idList"`
	IdMembers             []string `json:"idMembers"`
	IdMembersVoted        []string `json:"idMembersVoted"`
	ManualCoverAttachment bool     `json:"manualCoverAttachment"`
	Closed                bool     `json:"closed"`
	Pos                   float32  `json:"pos"`
	ShortLink             string   `json:"shortLink"`
	DateLastActivity      string   `json:"dateLastActivity"`
	ShortUrl              string   `json:"shortUrl"`
	Subscribed            bool     `json:"subscribed"`
	Url                   string   `json:"url"`
	Due                   string   `json:"due"`
	Desc                  string   `json:"desc"`
	DescData              struct {
		Emoji struct{} `json:"emoji"`
	} `json:"descData"`
	CheckItemStates []struct {
		IdCheckItem string `json:"idCheckItem"`
		State       string `json:"state"`
	} `json:"checkItemStates"`
	Badges struct {
		Votes              int    `json:"votes"`
		ViewingMemberVoted bool   `json:"viewingMemberVoted"`
		Subscribed         bool   `json:"subscribed"`
		Fogbugz            string `json:"fogbugz"`
		CheckItems         int    `json:"checkItems"`
		CheckItemsChecked  int    `json:"checkItemsChecked"`
		Comments           int    `json:"comments"`
		Attachments        int    `json:"attachments"`
		Description        bool   `json:"description"`
		Due                string `json:"due"`
	} `json:"badges"`
	Labels []struct {
		Color string `json:"color"`
		Name  string `json:"name"`
	} `json:"labels"`
}

func (c *Client) Card(CardId string) (card *Card, err error) {
	req, err := http.NewRequest("GET", c.endpoint+"/card/"+CardId, nil)
	if err != nil {
		return
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	} else if resp.StatusCode != 200 {
		err = fmt.Errorf("Received unexpected status %d while trying to retrieve the server data", resp.StatusCode)
		return
	}

	err = json.Unmarshal(body, &card)
	card.client = c
	return
}

func (c *Card) Checklists() (checklists []Checklist, err error) {
	req, err := http.NewRequest("GET", c.client.endpoint+"/card/"+c.Id+"/checklists", nil)
	if err != nil {
		return
	}

	resp, err := c.client.client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	} else if resp.StatusCode != 200 {
		err = fmt.Errorf("Received unexpected status %d while trying to retrieve the server data", resp.StatusCode)
		return
	}

	err = json.Unmarshal(body, &checklists)
	for i, _ := range checklists {
		checklists[i].client = c.client
	}
	return
}

func (c *Card) Members() (members []Member, err error) {
	req, err := http.NewRequest("GET", c.client.endpoint+"/cards/"+c.Id+"/members", nil)
	if err != nil {
		return
	}

	resp, err := c.client.client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	} else if resp.StatusCode != 200 {
		err = fmt.Errorf("Received unexpected status %d while trying to retrieve the server data", resp.StatusCode)
		return
	}

	err = json.Unmarshal(body, &members)
	for i, _ := range members {
		members[i].client = c.client
	}
	return
}

func (c *Card) Attachments() (attachments []Attachment, err error) {
	req, err := http.NewRequest("GET", c.client.endpoint+"/cards/"+c.Id+"/attachments", nil)
	if err != nil {
		return
	}

	resp, err := c.client.client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	} else if resp.StatusCode != 200 {
		err = fmt.Errorf("Received unexpected status %d while trying to retrieve the server data", resp.StatusCode)
		return
	}

	err = json.Unmarshal(body, &attachments)
	for i, _ := range attachments {
		attachments[i].client = c.client
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
Loadtest does some load testing through the Go client library for Cloud Bigtable.
*/
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"sync"
	"sync/atomic"
	"time"

	"cloud.google.com/go/bigtable"
	"cloud.google.com/go/bigtable/internal/cbtconfig"
	"cloud.google.com/go/bigtable/internal/stat"
	"golang.org/x/net/context"
	"google.golang.org/api/option"
)

var (
	runFor       = flag.Duration("run_for", 5*time.Second, "how long to run the load test for")
	scratchTable = flag.String("scratch_table", "loadtest-scratch", "name of table to use; should not already exist")
	csvOutput    = flag.String("csv_output", "",
		"output path for statistics in .csv format. If this file already exists it will be overwritten.")
	poolSize = flag.Int("pool_size", 1, "size of the gRPC connection pool to use for the data client")
	reqCount = flag.Int("req_count", 100, "number of concurrent requests")

	config      *cbtconfig.Config
	client      *bigtable.Client
	adminClient *bigtable.AdminClient
)

func main() {
	var err error
	config, err = cbtconfig.Load()
	if err != nil {
		log.Fatal(err)
	}
	config.RegisterFlags()

	flag.Parse()
	if err := config.CheckFlags(cbtconfig.ProjectAndInstanceRequired); err != nil {
		log.Fatal(err)
	}
	if config.Creds != "" {
		os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", config.Creds)
	}
	if flag.NArg() != 0 {
		flag.Usage()
		os.Exit(1)
	}

	var options []option.ClientOption
	if *poolSize > 1 {
		options = append(options, option.WithGRPCConnectionPool(*poolSize))
	}

	var csvFile *os.File
	if *csvOutput != "" {
		csvFile, err = os.Create(*csvOutput)
		if err != nil {
			log.Fatalf("creating csv output file: %v", err)
		}
		defer csvFile.Close()
		log.Printf("Writing statistics to %q ...", *csvOutput)
	}

	log.Printf("Dialing connections...")
	client, err = bigtable.NewClient(context.Background(), config.Project, config.Instance, options...)
	if err != nil {
		log.Fatalf("Making bigtable.Client: %v", err)
	}
	defer client.Close()
	adminClient, err = bigtable.NewAdminClient(context.Background(), config.Project, config.Instance)
	if err != nil {
		log.Fatalf("Making bigtable.AdminClient: %v", err)
	}
	defer adminClient.Close()

	// Create a scratch table.
	log.Printf("Setting up scratch table...")
	if err := adminClient.CreateTable(context.Background(), *scratchTable); err != nil {
		log.Fatalf("Making scratch table %q: %v", *scratchTable, err)
	}
	if err := adminClient.CreateColumnFamily(context.Background(), *scratchTable, "f"); err != nil {
		log.Fatalf("Making scratch table column family: %v", err)
	}
	// Upon a successful run, delete the table. Don't bother checking for errors.
	defer adminClient.DeleteTable(context.Background(), *scratchTable)

	// Also delete the table on SIGTERM.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		s := <-c
		log.Printf("Caught %v, cleaning scratch table.", s)
		adminClient.DeleteTable(context.Background(), *scratchTable)
		os.Exit(1)
	}()

	log.Printf("Starting load test... (run for %v)", *runFor)
	tbl := client.Open(*scratchTable)
	sem := make(chan int, *reqCount) // limit the number of requests happening at once
	var reads, writes stats
	stopTime := time.Now().Add(*runFor)
	var wg sync.WaitGroup
	for time.Now().Before(stopTime) {
		sem <- 1
		wg.Add(1)
		go func() {
			defer wg.Done()
			defer func() { <-sem }()

			ok := true
			opStart := time.Now()
			var stats *stats
			defer func() {
				stats.Record(ok, time.Since(opStart))
			}()

			row := fmt.Sprintf("row%d", rand.Intn(100)) // operate on 1 of 100 rows

			switch rand.Intn(10) {
			default:
				// read
				stats = &reads
				_, err := tbl.ReadRow(context.Background(), row, bigtable.RowFilter(bigtable.LatestNFilter(1)))
				if err != nil {
					log.Printf("Error doing read: %v", err)
					ok = false
				}
			case 0, 1, 2, 3, 4:
				// write
				stats = &writes
				mut := bigtable.NewMutation()
				mut.Set("f", "col", bigtable.Now(), bytes.Repeat([]byte("0"), 1<<10)) // 1 KB write
				if err := tbl.Apply(context.Background(), row, mut); err != nil {
					log.Printf("Error doing mutation: %v", err)
					ok = false
				}
			}
		}()
	}
	wg.Wait()

	readsAgg := stat.NewAggregate("reads", reads.ds, reads.tries-reads.ok)
	writesAgg := stat.NewAggregate("writes", writes.ds, writes.tries-writes.ok)
	log.Printf("Reads (%d ok / %d tries):\n%v", reads.ok, reads.tries, readsAgg)
	log.Printf("Writes (%d ok / %d tries):\n%v", writes.ok, writes.tries, writesAgg)

	if csvFile != nil {
		stat.WriteCSV([]*stat.Aggregate{readsAgg, writesAgg}, csvFile)
	}
}

var allStats int64 // atomic

type stats struct {
	mu        sync.Mutex
	tries, ok int
	ds        []time.Duration
}

func (s *stats) Record(ok bool, d time.Duration) {
	s.mu.Lock()
	s.tries++
	if ok {
		s.ok++
	}
	s.ds = append(s.ds, d)
	s.mu.Unlock()

	if n := atomic.AddInt64(&allStats, 1); n%1000 == 0 {
		log.Printf("Progress: done %d ops", n)
	}
}
<commit_msg>Allow loadgen to run forever<commit_after>/*
Copyright 2015 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
Loadtest does some load testing through the Go client library for Cloud Bigtable.
*/
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"sync"
	"sync/atomic"
	"time"

	"cloud.google.com/go/bigtable"
	"cloud.google.com/go/bigtable/internal/cbtconfig"
	"cloud.google.com/go/bigtable/internal/stat"
	"golang.org/x/net/context"
	"google.golang.org/api/option"
)

var (
	runFor = flag.Duration("run_for", 5*time.Second,
		"how long to run the load test for; 0 to run forever until SIGTERM")
	scratchTable = flag.String("scratch_table", "loadtest-scratch", "name of table to use; should not already exist")
	csvOutput    = flag.String("csv_output", "",
		"output path for statistics in .csv format. If this file already exists it will be overwritten.")
	poolSize = flag.Int("pool_size", 1, "size of the gRPC connection pool to use for the data client")
	reqCount = flag.Int("req_count", 100, "number of concurrent requests")

	config      *cbtconfig.Config
	client      *bigtable.Client
	adminClient *bigtable.AdminClient
)

func main() {
	var err error
	config, err = cbtconfig.Load()
	if err != nil {
		log.Fatal(err)
	}
	config.RegisterFlags()

	flag.Parse()
	if err := config.CheckFlags(cbtconfig.ProjectAndInstanceRequired); err != nil {
		log.Fatal(err)
	}
	if config.Creds != "" {
		os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", config.Creds)
	}
	if flag.NArg() != 0 {
		flag.Usage()
		os.Exit(1)
	}

	var options []option.ClientOption
	if *poolSize > 1 {
		options = append(options, option.WithGRPCConnectionPool(*poolSize))
	}

	var csvFile *os.File
	if *csvOutput != "" {
		csvFile, err = os.Create(*csvOutput)
		if err != nil {
			log.Fatalf("creating csv output file: %v", err)
		}
		defer csvFile.Close()
		log.Printf("Writing statistics to %q ...", *csvOutput)
	}

	log.Printf("Dialing connections...")
	client, err = bigtable.NewClient(context.Background(), config.Project, config.Instance, options...)
	if err != nil {
		log.Fatalf("Making bigtable.Client: %v", err)
	}
	defer client.Close()
	adminClient, err = bigtable.NewAdminClient(context.Background(), config.Project, config.Instance)
	if err != nil {
		log.Fatalf("Making bigtable.AdminClient: %v", err)
	}
	defer adminClient.Close()

	// Create a scratch table.
	log.Printf("Setting up scratch table...")
	if err := adminClient.CreateTable(context.Background(), *scratchTable); err != nil {
		log.Fatalf("Making scratch table %q: %v", *scratchTable, err)
	}
	if err := adminClient.CreateColumnFamily(context.Background(), *scratchTable, "f"); err != nil {
		log.Fatalf("Making scratch table column family: %v", err)
	}
	// Upon a successful run, delete the table. Don't bother checking for errors.
	defer adminClient.DeleteTable(context.Background(), *scratchTable)

	// Also delete the table on SIGTERM.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		s := <-c
		log.Printf("Caught %v, cleaning scratch table.", s)
		adminClient.DeleteTable(context.Background(), *scratchTable)
		os.Exit(1)
	}()

	log.Printf("Starting load test... (run for %v)", *runFor)
	tbl := client.Open(*scratchTable)
	sem := make(chan int, *reqCount) // limit the number of requests happening at once
	var reads, writes stats
	stopTime := time.Now().Add(*runFor)
	var wg sync.WaitGroup
	for time.Now().Before(stopTime) || *runFor == 0 {
		sem <- 1
		wg.Add(1)
		go func() {
			defer wg.Done()
			defer func() { <-sem }()

			ok := true
			opStart := time.Now()
			var stats *stats
			defer func() {
				stats.Record(ok, time.Since(opStart))
			}()

			row := fmt.Sprintf("row%d", rand.Intn(100)) // operate on 1 of 100 rows

			switch rand.Intn(10) {
			default:
				// read
				stats = &reads
				_, err := tbl.ReadRow(context.Background(), row, bigtable.RowFilter(bigtable.LatestNFilter(1)))
				if err != nil {
					log.Printf("Error doing read: %v", err)
					ok = false
				}
			case 0, 1, 2, 3, 4:
				// write
				stats = &writes
				mut := bigtable.NewMutation()
				mut.Set("f", "col", bigtable.Now(), bytes.Repeat([]byte("0"), 1<<10)) // 1 KB write
				if err := tbl.Apply(context.Background(), row, mut); err != nil {
					log.Printf("Error doing mutation: %v", err)
					ok = false
				}
			}
		}()
	}
	wg.Wait()

	readsAgg := stat.NewAggregate("reads", reads.ds, reads.tries-reads.ok)
	writesAgg := stat.NewAggregate("writes", writes.ds, writes.tries-writes.ok)
	log.Printf("Reads (%d ok / %d tries):\n%v", reads.ok, reads.tries, readsAgg)
	log.Printf("Writes (%d ok / %d tries):\n%v", writes.ok, writes.tries, writesAgg)

	if csvFile != nil {
		stat.WriteCSV([]*stat.Aggregate{readsAgg, writesAgg}, csvFile)
	}
}

var allStats int64 // atomic

type stats struct {
	mu        sync.Mutex
	tries, ok int
	ds        []time.Duration
}

func (s *stats) Record(ok bool, d time.Duration) {
	s.mu.Lock()
	s.tries++
	if ok {
		s.ok++
	}
	s.ds = append(s.ds, d)
	s.mu.Unlock()

	if n := atomic.AddInt64(&allStats, 1); n%1000 == 0 {
		log.Printf("Progress: done %d ops", n)
	}
}
<|endoftext|>


<commit_before>// Streams methods of the twitch api.
// https://github.com/justintv/Twitch-API/blob/master/v3_resources/chat.md

package twitch

type ChatLinks struct {
	Emoticons string `json:"emoticons,omitempty"`
	Badges    string `json:"badges,omitempty"`
}

type EmoticonsS struct {
	Emoticons []*EmoticonS `json:"emoticons,omitempty"`
}

type EmoticonS struct {
	Regex string `json:"regex,omitempty"`
	Images []*ImageS `json:"images,omitempty"`
}

type ImageS struct {
	EmoticonSet int    `json:"emoticon_set,omitempty"`
	Height      int    `json:"height,omitempty"`
	Width       int    `json:"width,omitempty"`
	Url         string `json:"url,omitempty"`
}

type ChatMethod struct {
	client *Client
}

func (c *ChatMethod) Channel(name string) (*ChatLinks, error) {
	rel := "chat/" + name

	chatLinks := new(ChatLinks)
	_, err := c.client.Get(rel, chatLinks)
	return chatLinks, err
}

func (c *ChatMethod) Emoticons() (*EmoticonsS, error) {
	rel := "chat/emoticons"

	emoticons := new(EmoticonsS)
	_, err := c.client.Get(rel, emoticons)
	return emoticons, err
}
<commit_msg>Formatting.<commit_after>// Streams methods of the twitch api.
// https://github.com/justintv/Twitch-API/blob/master/v3_resources/chat.md

package twitch

type ChatLinks struct {
	Emoticons string `json:"emoticons,omitempty"`
	Badges    string `json:"badges,omitempty"`
}

type EmoticonsS struct {
	Emoticons []*EmoticonS `json:"emoticons,omitempty"`
}

type EmoticonS struct {
	Regex string     `json:"regex,omitempty"`
	Images []*ImageS `json:"images,omitempty"`
}

type ImageS struct {
	EmoticonSet int    `json:"emoticon_set,omitempty"`
	Height      int    `json:"height,omitempty"`
	Width       int    `json:"width,omitempty"`
	Url         string `json:"url,omitempty"`
}

type ChatMethod struct {
	client *Client
}

func (c *ChatMethod) Channel(name string) (*ChatLinks, error) {
	rel := "chat/" + name

	chatLinks := new(ChatLinks)
	_, err := c.client.Get(rel, chatLinks)
	return chatLinks, err
}

func (c *ChatMethod) Emoticons() (*EmoticonsS, error) {
	rel := "chat/emoticons"

	emoticons := new(EmoticonsS)
	_, err := c.client.Get(rel, emoticons)
	return emoticons, err
}
<|endoftext|>


<commit_before>// Package clog implements an alternative logger to the one found in the standard
// library with support for more logging levels and a different output format.
// This is not exhaustive or feature-rich.
//
// Author: Clint Caywood (www.clintcaywood.com)
// License: Public domain
package clog

import (
    "time"
    "sync"
    "io"
    "fmt"
)

type Level uint8
const (
    LevelFatal Level = iota + 1
    LevelError
    LevelWarning
    LevelInfo
    LevelTrace
)

var LevelStrings = map[Level]string {
    LevelFatal: "Fatal",
    LevelError: "Error",
    LevelWarning: "Warning",
    LevelInfo: "Info",
    LevelTrace: "Trace",
}

type Output struct {
    writer io.Writer
    level Level
}

type Clog struct {
    mtx sync.Mutex
    outputs []Output
}

func NewClog() *Clog {
    return &Clog{sync.Mutex{}, make([]Output, 0)}
}

func (this *Clog) AddOutput(writer io.Writer, level Level) {
    this.outputs = append(this.outputs, Output{writer, level})
}

func (this *Clog) Trace(format string, v ...interface{}) {
    this.Log(LevelTrace, format, v...)
}

func (this *Clog) Info(format string, v ...interface{}) {
    this.Log(LevelInfo, format, v...)
}

func (this *Clog) Warning(format string, v ...interface{}) {
    this.Log(LevelWarning, format, v...)
}

func (this *Clog) Error(format string, v ...interface{}) {
    this.Log(LevelError, format, v...)
}

// Will not terminate the program
func (this *Clog) Fatal(format string, v ...interface{}) {
    this.Log(LevelFatal, format, v...)
}

// Logs a message
func (this *Clog) Log(level Level, format string, v ...interface{}) {
    message := fmt.Sprintf(format + "\n", v...)
    strTimestamp := getTimestamp()
    strFinal := fmt.Sprintf("%s [%7s] %s", strTimestamp, LevelStrings[level], message)
    bytes := []byte(strFinal)
    this.mtx.Lock()
    defer this.mtx.Unlock()
    for _, output := range(this.outputs) {
        if output.level >= level {
            output.writer.Write(bytes)
        }
    }
}

// Gets the timestamp string
func getTimestamp() string {
    now := time.Now()
    return fmt.Sprintf("%v-%02v-%02d %02d:%02d:%02d.%03d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), now.Nanosecond() / 1000000)
}

<commit_msg>Remove "Public Domain" from license in source code<commit_after>// Package clog implements an alternative logger to the one found in the standard
// library with support for more logging levels and a different output format.
// This is not exhaustive or feature-rich.
//
// Author: Clint Caywood (www.clintcaywood.com)
package clog

import (
    "time"
    "sync"
    "io"
    "fmt"
)

type Level uint8
const (
    LevelFatal Level = iota + 1
    LevelError
    LevelWarning
    LevelInfo
    LevelTrace
)

var LevelStrings = map[Level]string {
    LevelFatal: "Fatal",
    LevelError: "Error",
    LevelWarning: "Warning",
    LevelInfo: "Info",
    LevelTrace: "Trace",
}

type Output struct {
    writer io.Writer
    level Level
}

type Clog struct {
    mtx sync.Mutex
    outputs []Output
}

func NewClog() *Clog {
    return &Clog{sync.Mutex{}, make([]Output, 0)}
}

func (this *Clog) AddOutput(writer io.Writer, level Level) {
    this.outputs = append(this.outputs, Output{writer, level})
}

func (this *Clog) Trace(format string, v ...interface{}) {
    this.Log(LevelTrace, format, v...)
}

func (this *Clog) Info(format string, v ...interface{}) {
    this.Log(LevelInfo, format, v...)
}

func (this *Clog) Warning(format string, v ...interface{}) {
    this.Log(LevelWarning, format, v...)
}

func (this *Clog) Error(format string, v ...interface{}) {
    this.Log(LevelError, format, v...)
}

// Will not terminate the program
func (this *Clog) Fatal(format string, v ...interface{}) {
    this.Log(LevelFatal, format, v...)
}

// Logs a message
func (this *Clog) Log(level Level, format string, v ...interface{}) {
    message := fmt.Sprintf(format + "\n", v...)
    strTimestamp := getTimestamp()
    strFinal := fmt.Sprintf("%s [%7s] %s", strTimestamp, LevelStrings[level], message)
    bytes := []byte(strFinal)
    this.mtx.Lock()
    defer this.mtx.Unlock()
    for _, output := range(this.outputs) {
        if output.level >= level {
            output.writer.Write(bytes)
        }
    }
}

// Gets the timestamp string
func getTimestamp() string {
    now := time.Now()
    return fmt.Sprintf("%v-%02v-%02d %02d:%02d:%02d.%03d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), now.Nanosecond() / 1000000)
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"runtime"
	"time"
)

// State of a virtual machine.
type vmState string

const (
	vmRunning      vmState = "running"
	vmPoweroff             = "poweroff"
	vmPaused               = "paused"
	vmSaved                = "saved"
	vmAborted              = "aborted"
	vmUnregistered         = "(unregistered)" // not actually reported by VirtualBox
	vmUnknown              = "(unknown)"      // not actually reported by VirtualBox
)

// Call the external SSH command to login into boot2docker VM.
func cmdSSH() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		// TODO What SSH client is used on Windows?
		if err := cmd(B2D.SSH,
			"-o", "StrictHostKeyChecking=no",
			"-o", "UserKnownHostsFile=/dev/null",
			"-p", fmt.Sprintf("%d", B2D.SSHPort),
			"docker@localhost"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
		return 1
	}
	return 0
}

// Start the VM from all possible states.
func cmdStart() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		logf("%s is already running.", B2D.VM)
	case vmPaused:
		logf("Resuming %s", B2D.VM)
		if err := vbm("controlvm", B2D.VM, "resume"); err != nil {
			logf("Failed to resume vm: %s", err)
			return 1
		}
		addr := fmt.Sprintf("localhost:%d", B2D.SSHPort)
		if err := read(addr); err != nil {
			logf("Failed to connect to SSH port at %s: %s", addr, err)
			return 1
		}
		logf("Resumed.")
	case vmSaved, vmPoweroff, vmAborted:
		logf("Starting %s...", B2D.VM)
		if err := vbm("startvm", B2D.VM, "--type", "headless"); err != nil {
			logf("Failed to start vm: %s", err)
			return 1
		}
		logf("Waiting for SSH server to start...")
		addr := fmt.Sprintf("localhost:%d", B2D.SSHPort)
		if err := read(addr); err != nil {
			logf("Failed to connect to SSH port at %s: %s", addr, err)
			return 1
		}
		logf("Started.")
	default:
		logf("Cannot start %s from state %.", B2D.VM, state)
		return 1
	}

	// Check if $DOCKER_HOST ENV var is properly configured.
	DockerHost := getenv("DOCKER_HOST", "")
	if DockerHost != fmt.Sprintf("tcp://localhost:%d", B2D.DockerPort) {
		fmt.Printf("\nTo connect the docker client to the Docker daemon, please set:\n")
		fmt.Printf("export DOCKER_HOST=tcp://localhost:%d\n\n", B2D.DockerPort)
	}
	return 0
}

// Save the current state of VM on disk.
func cmdSave() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		logf("Suspending %s", B2D.VM)
		if err := vbm("controlvm", B2D.VM, "savestate"); err != nil {
			logf("Failed to suspend vm: %s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Pause the VM.
func cmdPause() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		if err := vbm("controlvm", B2D.VM, "pause"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Gracefully stop the VM by sending ACPI shutdown signal.
func cmdStop() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		logf("Shutting down %s...", B2D.VM)
		if err := vbm("controlvm", B2D.VM, "acpipowerbutton"); err != nil {
			logf("Failed to shutdown vm: %s", err)
			return 1
		}
		for status(B2D.VM) == vmRunning {
			time.Sleep(1 * time.Second)
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Forcefully power off the VM (equivalent to unplug power). Could potentially
// result in corrupted disk. Use with care.
func cmdPoweroff() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		if err := vbm("controlvm", B2D.VM, "poweroff"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Gracefully stop and then start the VM.
func cmdRestart() int {
	if state := status(B2D.VM); state == vmRunning {
		if exitcode := cmdStop(); exitcode != 0 {
			return exitcode
		}
	}
	return cmdStart()
}

// Forcefully reset the VM. Could potentially result in corrupted disk. Use
// with care.
func cmdReset() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		if err := vbm("controlvm", B2D.VM, "reset"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Delete the VM and remove associated files.
func cmdDelete() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)

	case vmRunning, vmPaused:
		logf("%s needs to be stopped to delete it.", B2D.VM)
		return 1

	default:
		if err := vbm("unregistervm", "--delete", B2D.VM); err != nil {
			logf("Failed to delete vm: %s", err)
			return 1
		}

	}
	return 0
}

// Show detailed info of the VM.
func cmdInfo() int {
	if err := vbm("showvminfo", B2D.VM); err != nil {
		logf("%s", err)
		return 1
	}
	return 0
}

// Show the current state of the VM.
func cmdStatus() int {
	fmt.Printf("%s\n", status(B2D.VM))
	return 0
}

// Initialize the boot2docker VM from scratch.
func cmdInit() int {
	if state := status(B2D.VM); state != vmUnregistered {
		logf("%q already exists.", B2D.VM)
		return 1
	}

	if ping(fmt.Sprintf("localhost:%d", B2D.DockerPort)) {
		logf("DOCKER_PORT=%d on localhost is occupied. Please choose another none.", B2D.DockerPort)
		return 1
	}

	if ping(fmt.Sprintf("localhost:%d", B2D.SSHPort)) {
		logf("SSH_PORT=%d on localhost is occupied. Please choose another one.", B2D.SSHPort)
		return 1
	}

	if _, err := os.Stat(B2D.ISO); err != nil {
		if os.IsNotExist(err) {
			if exitcode := cmdDownload(); exitcode != 0 {
				return exitcode
			}
		} else {
			logf("Failed to open ISO image: %s", err)
			return 1
		}
	}

	if _, err := os.Stat(B2D.Disk); err != nil {
		if os.IsNotExist(err) {
			if err := makeDiskImage(B2D.Disk, B2D.DiskSize); err != nil {
				logf("Failed to create disk image: %s", err)
				return 1
			}
		} else {
			logf("Failed to open disk image: %s", err)
			return 1
		}
	}

	logf("Creating VM %s...", B2D.VM)
	if err := vbm("createvm", "--name", B2D.VM, "--register"); err != nil {
		logf("Failed to create vm: %s", err)
		return 1
	}

	if err := vbm("modifyvm", B2D.VM,
		"--ostype", "Linux26_64",
		"--cpus", fmt.Sprintf("%d", runtime.NumCPU()),
		"--memory", fmt.Sprintf("%d", B2D.Memory),
		"--rtcuseutc", "on",
		"--acpi", "on",
		"--ioapic", "on",
		"--hpet", "on",
		"--hwvirtex", "on",
		"--vtxvpid", "on",
		"--largepages", "on",
		"--nestedpaging", "on",
		"--firmware", "bios",
		"--bioslogofadein", "off",
		"--bioslogofadeout", "off",
		"--bioslogodisplaytime", "0",
		"--biosbootmenu", "disabled",
		"--boot1", "dvd",
	); err != nil {
		logf("Failed to modify %s: %s", B2D.VM, err)
		return 1
	}

	logf("Setting VM networking...")
	if err := vbm("modifyvm", B2D.VM,
		"--nic1", "nat",
		"--nictype1", "virtio",
		"--cableconnected1", "on",
	); err != nil {
		logf("Failed to modify %s: %s", B2D.VM, err)
		return 1
	}

	if err := vbm("modifyvm", B2D.VM,
		"--natpf1", fmt.Sprintf("ssh,tcp,127.0.0.1,%d,,22", B2D.SSHPort),
		"--natpf1", fmt.Sprintf("docker,tcp,127.0.0.1,%d,,4243", B2D.DockerPort),
	); err != nil {
		logf("Failed to modify %s: %s", B2D.VM, err)
		return 1
	}
	logf("Port forwarding [ssh]: host tcp://127.0.0.1:%d --> guest tcp://0.0.0.0:22", B2D.SSHPort)
	logf("Port forwarding [docker]: host tcp://127.0.0.1:%d --> guest tcp://0.0.0.0:4243", B2D.DockerPort)

	logf("Setting VM storage...")
	if err := vbm("storagectl", B2D.VM,
		"--name", "SATA",
		"--add", "sata",
		"--hostiocache", "on",
	); err != nil {
		logf("Failed to add storage controller: %s", err)
		return 1
	}

	if err := vbm("storageattach", B2D.VM,
		"--storagectl", "SATA",
		"--port", "0",
		"--device", "0",
		"--type", "dvddrive",
		"--medium", B2D.ISO,
	); err != nil {
		logf("Failed to attach storage device %s: %s", B2D.ISO, err)
		return 1
	}

	if err := vbm("storageattach", B2D.VM,
		"--storagectl", "SATA",
		"--port", "1",
		"--device", "0",
		"--type", "hdd",
		"--medium", B2D.Disk,
	); err != nil {
		logf("Failed to attach storage device %s: %s", B2D.Disk, err)
		return 1
	}

	logf("Done. Type `%s up` to start the VM.", os.Args[0])
	return 0
}

// Download the boot2docker ISO image.
func cmdDownload() int {
	logf("Downloading boot2docker ISO image...")
	url := "https://api.github.com/repos/boot2docker/boot2docker/releases"
	tag, err := getLatestReleaseName(url)
	if err != nil {
		logf("Failed to get latest release: %s", err)
		return 1
	}
	logf("Latest release is %s", tag)

	url = fmt.Sprintf("https://github.com/boot2docker/boot2docker/releases/download/%s/boot2docker.iso", tag)
	if err := download(B2D.ISO, url); err != nil {
		logf("Failed to download ISO image: %s", err)
		return 1
	}
	return 0
}
<commit_msg>Different message on Windows after starting boot2docker VM<commit_after>package main

import (
	"fmt"
	"os"
	"runtime"
	"time"
)

// State of a virtual machine.
type vmState string

const (
	vmRunning      vmState = "running"
	vmPoweroff             = "poweroff"
	vmPaused               = "paused"
	vmSaved                = "saved"
	vmAborted              = "aborted"
	vmUnregistered         = "(unregistered)" // not actually reported by VirtualBox
	vmUnknown              = "(unknown)"      // not actually reported by VirtualBox
)

// Call the external SSH command to login into boot2docker VM.
func cmdSSH() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		// TODO What SSH client is used on Windows?
		if err := cmd(B2D.SSH,
			"-o", "StrictHostKeyChecking=no",
			"-o", "UserKnownHostsFile=/dev/null",
			"-p", fmt.Sprintf("%d", B2D.SSHPort),
			"docker@localhost"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
		return 1
	}
	return 0
}

// Start the VM from all possible states.
func cmdStart() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		logf("%s is already running.", B2D.VM)
	case vmPaused:
		logf("Resuming %s", B2D.VM)
		if err := vbm("controlvm", B2D.VM, "resume"); err != nil {
			logf("Failed to resume vm: %s", err)
			return 1
		}
	case vmSaved, vmPoweroff, vmAborted:
		logf("Starting %s...", B2D.VM)
		if err := vbm("startvm", B2D.VM, "--type", "headless"); err != nil {
			logf("Failed to start vm: %s", err)
			return 1
		}
	default:
		logf("Cannot start %s from state %.", B2D.VM, state)
		return 1
	}

	logf("Waiting for SSH server to start...")
	addr := fmt.Sprintf("localhost:%d", B2D.SSHPort)
	if err := read(addr); err != nil {
		logf("Failed to connect to SSH port at %s: %s", addr, err)
		return 1
	}
	logf("Started.")

	switch runtime.GOOS {
	case "windows":
		fmt.Println("Docker client does not run on Windows for now. Please SSH into the VM instead.")
	default:
		// Check if $DOCKER_HOST ENV var is properly configured.
		if os.Getenv("DOCKER_HOST") != fmt.Sprintf("tcp://localhost:%d", B2D.DockerPort) {
			logf("To connect the Docker client to the Docker daemon, please set:")
			logf("    export DOCKER_HOST=tcp://localhost:%d", B2D.DockerPort)
		}
	}
	return 0
}

// Save the current state of VM on disk.
func cmdSave() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		logf("Suspending %s", B2D.VM)
		if err := vbm("controlvm", B2D.VM, "savestate"); err != nil {
			logf("Failed to suspend vm: %s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Pause the VM.
func cmdPause() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		if err := vbm("controlvm", B2D.VM, "pause"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Gracefully stop the VM by sending ACPI shutdown signal.
func cmdStop() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		logf("Shutting down %s...", B2D.VM)
		if err := vbm("controlvm", B2D.VM, "acpipowerbutton"); err != nil {
			logf("Failed to shutdown vm: %s", err)
			return 1
		}
		for status(B2D.VM) == vmRunning {
			time.Sleep(1 * time.Second)
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Forcefully power off the VM (equivalent to unplug power). Could potentially
// result in corrupted disk. Use with care.
func cmdPoweroff() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		if err := vbm("controlvm", B2D.VM, "poweroff"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Gracefully stop and then start the VM.
func cmdRestart() int {
	if state := status(B2D.VM); state == vmRunning {
		if exitcode := cmdStop(); exitcode != 0 {
			return exitcode
		}
	}
	return cmdStart()
}

// Forcefully reset the VM. Could potentially result in corrupted disk. Use
// with care.
func cmdReset() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)
		return 1
	case vmRunning:
		if err := vbm("controlvm", B2D.VM, "reset"); err != nil {
			logf("%s", err)
			return 1
		}
	default:
		logf("%s is not running.", B2D.VM)
	}
	return 0
}

// Delete the VM and remove associated files.
func cmdDelete() int {
	switch state := status(B2D.VM); state {
	case vmUnregistered:
		logf("%s is not registered.", B2D.VM)

	case vmRunning, vmPaused:
		logf("%s needs to be stopped to delete it.", B2D.VM)
		return 1

	default:
		if err := vbm("unregistervm", "--delete", B2D.VM); err != nil {
			logf("Failed to delete vm: %s", err)
			return 1
		}

	}
	return 0
}

// Show detailed info of the VM.
func cmdInfo() int {
	if err := vbm("showvminfo", B2D.VM); err != nil {
		logf("%s", err)
		return 1
	}
	return 0
}

// Show the current state of the VM.
func cmdStatus() int {
	fmt.Printf("%s\n", status(B2D.VM))
	return 0
}

// Initialize the boot2docker VM from scratch.
func cmdInit() int {
	if state := status(B2D.VM); state != vmUnregistered {
		logf("%q already exists.", B2D.VM)
		return 1
	}

	if ping(fmt.Sprintf("localhost:%d", B2D.DockerPort)) {
		logf("DOCKER_PORT=%d on localhost is occupied. Please choose another none.", B2D.DockerPort)
		return 1
	}

	if ping(fmt.Sprintf("localhost:%d", B2D.SSHPort)) {
		logf("SSH_PORT=%d on localhost is occupied. Please choose another one.", B2D.SSHPort)
		return 1
	}

	if _, err := os.Stat(B2D.ISO); err != nil {
		if os.IsNotExist(err) {
			if exitcode := cmdDownload(); exitcode != 0 {
				return exitcode
			}
		} else {
			logf("Failed to open ISO image: %s", err)
			return 1
		}
	}

	if _, err := os.Stat(B2D.Disk); err != nil {
		if os.IsNotExist(err) {
			if err := makeDiskImage(B2D.Disk, B2D.DiskSize); err != nil {
				logf("Failed to create disk image: %s", err)
				return 1
			}
		} else {
			logf("Failed to open disk image: %s", err)
			return 1
		}
	}

	logf("Creating VM %s...", B2D.VM)
	if err := vbm("createvm", "--name", B2D.VM, "--register"); err != nil {
		logf("Failed to create vm: %s", err)
		return 1
	}

	if err := vbm("modifyvm", B2D.VM,
		"--ostype", "Linux26_64",
		"--cpus", fmt.Sprintf("%d", runtime.NumCPU()),
		"--memory", fmt.Sprintf("%d", B2D.Memory),
		"--rtcuseutc", "on",
		"--acpi", "on",
		"--ioapic", "on",
		"--hpet", "on",
		"--hwvirtex", "on",
		"--vtxvpid", "on",
		"--largepages", "on",
		"--nestedpaging", "on",
		"--firmware", "bios",
		"--bioslogofadein", "off",
		"--bioslogofadeout", "off",
		"--bioslogodisplaytime", "0",
		"--biosbootmenu", "disabled",
		"--boot1", "dvd",
	); err != nil {
		logf("Failed to modify %s: %s", B2D.VM, err)
		return 1
	}

	logf("Setting VM networking...")
	if err := vbm("modifyvm", B2D.VM,
		"--nic1", "nat",
		"--nictype1", "virtio",
		"--cableconnected1", "on",
	); err != nil {
		logf("Failed to modify %s: %s", B2D.VM, err)
		return 1
	}

	if err := vbm("modifyvm", B2D.VM,
		"--natpf1", fmt.Sprintf("ssh,tcp,127.0.0.1,%d,,22", B2D.SSHPort),
		"--natpf1", fmt.Sprintf("docker,tcp,127.0.0.1,%d,,4243", B2D.DockerPort),
	); err != nil {
		logf("Failed to modify %s: %s", B2D.VM, err)
		return 1
	}
	logf("Port forwarding [ssh]: host tcp://127.0.0.1:%d --> guest tcp://0.0.0.0:22", B2D.SSHPort)
	logf("Port forwarding [docker]: host tcp://127.0.0.1:%d --> guest tcp://0.0.0.0:4243", B2D.DockerPort)

	logf("Setting VM storage...")
	if err := vbm("storagectl", B2D.VM,
		"--name", "SATA",
		"--add", "sata",
		"--hostiocache", "on",
	); err != nil {
		logf("Failed to add storage controller: %s", err)
		return 1
	}

	if err := vbm("storageattach", B2D.VM,
		"--storagectl", "SATA",
		"--port", "0",
		"--device", "0",
		"--type", "dvddrive",
		"--medium", B2D.ISO,
	); err != nil {
		logf("Failed to attach storage device %s: %s", B2D.ISO, err)
		return 1
	}

	if err := vbm("storageattach", B2D.VM,
		"--storagectl", "SATA",
		"--port", "1",
		"--device", "0",
		"--type", "hdd",
		"--medium", B2D.Disk,
	); err != nil {
		logf("Failed to attach storage device %s: %s", B2D.Disk, err)
		return 1
	}

	logf("Done. Type `%s up` to start the VM.", os.Args[0])
	return 0
}

// Download the boot2docker ISO image.
func cmdDownload() int {
	logf("Downloading boot2docker ISO image...")
	url := "https://api.github.com/repos/boot2docker/boot2docker/releases"
	tag, err := getLatestReleaseName(url)
	if err != nil {
		logf("Failed to get latest release: %s", err)
		return 1
	}
	logf("Latest release is %s", tag)

	url = fmt.Sprintf("https://github.com/boot2docker/boot2docker/releases/download/%s/boot2docker.iso", tag)
	if err := download(B2D.ISO, url); err != nil {
		logf("Failed to download ISO image: %s", err)
		return 1
	}
	return 0
}
<|endoftext|>


<commit_before>// Copyright 2016 The CMux 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 cmux

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

// Matcher matches a connection based on its content.
type Matcher func(io.Reader) bool

// MatchWriter is a match that can also write response (say to do handshake).
type MatchWriter func(io.Writer, io.Reader) bool

// ErrorHandler handles an error and returns whether
// the mux should continue serving the listener.
type ErrorHandler func(error) bool

var _ net.Error = ErrNotMatched{}

// ErrNotMatched is returned whenever a connection is not matched by any of
// the matchers registered in the multiplexer.
type ErrNotMatched struct {
	c net.Conn
}

func (e ErrNotMatched) Error() string {
	return fmt.Sprintf("mux: connection %v not matched by an matcher",
		e.c.RemoteAddr())
}

// Temporary implements the net.Error interface.
func (e ErrNotMatched) Temporary() bool { return true }

// Timeout implements the net.Error interface.
func (e ErrNotMatched) Timeout() bool { return false }

type errListenerClosed string

func (e errListenerClosed) Error() string   { return string(e) }
func (e errListenerClosed) Temporary() bool { return false }
func (e errListenerClosed) Timeout() bool   { return false }

// ErrListenerClosed is returned from muxListener.Accept when the underlying
// listener is closed.
var ErrListenerClosed = errListenerClosed("mux: listener closed")

// for readability of readTimeout
var noTimeout time.Duration

// New instantiates a new connection multiplexer.
func New(l net.Listener) CMux {
	return &cMux{
		root:        l,
		bufLen:      1024,
		errh:        func(_ error) bool { return true },
		donec:       make(chan struct{}),
		readTimeout: noTimeout,
	}
}

// CMux is a multiplexer for network connections.
type CMux interface {
	// Match returns a net.Listener that sees (i.e., accepts) only
	// the connections matched by at least one of the matcher.
	//
	// The order used to call Match determines the priority of matchers.
	Match(...Matcher) net.Listener
	// MatchWithWriters returns a net.Listener that accepts only the
	// connections that matched by at least of the matcher writers.
	//
	// Prefer Matchers over MatchWriters, since the latter can write on the
	// connection before the actual handler.
	//
	// The order used to call Match determines the priority of matchers.
	MatchWithWriters(...MatchWriter) net.Listener
	// Serve starts multiplexing the listener. Serve blocks and perhaps
	// should be invoked concurrently within a go routine.
	Serve() error
	// HandleError registers an error handler that handles listener errors.
	HandleError(ErrorHandler)
	// sets a timeout for the read of matchers
	SetReadTimeout(time.Duration)
}

type matchersListener struct {
	ss []MatchWriter
	l  muxListener
}

type cMux struct {
	root        net.Listener
	bufLen      int
	errh        ErrorHandler
	donec       chan struct{}
	sls         []matchersListener
	readTimeout time.Duration
}

func matchersToMatchWriters(matchers []Matcher) []MatchWriter {
	mws := make([]MatchWriter, 0, len(matchers))
	for _, m := range matchers {
		cm := m
		mws = append(mws, func(w io.Writer, r io.Reader) bool {
			return cm(r)
		})
	}
	return mws
}

func (m *cMux) Match(matchers ...Matcher) net.Listener {
	mws := matchersToMatchWriters(matchers)
	return m.MatchWithWriters(mws...)
}

func (m *cMux) MatchWithWriters(matchers ...MatchWriter) net.Listener {
	ml := muxListener{
		Listener: m.root,
		connc:    make(chan net.Conn, m.bufLen),
	}
	m.sls = append(m.sls, matchersListener{ss: matchers, l: ml})
	return ml
}

func (m *cMux) SetReadTimeout(t time.Duration) {
	m.readTimeout = t
}

func (m *cMux) Serve() error {
	var wg sync.WaitGroup

	defer func() {
		close(m.donec)
		wg.Wait()

		for _, sl := range m.sls {
			close(sl.l.connc)
			// Drain the connections enqueued for the listener.
			for c := range sl.l.connc {
				_ = c.Close()
			}
		}
	}()

	for {
		c, err := m.root.Accept()
		if err != nil {
			if !m.handleErr(err) {
				return err
			}
			continue
		}

		wg.Add(1)
		go m.serve(c, m.donec, &wg)
	}
}

func (m *cMux) serve(c net.Conn, donec <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	muc := newMuxConn(c)
	if m.readTimeout > noTimeout {
		_ = c.SetReadDeadline(time.Now().Add(m.readTimeout))
	}
	for _, sl := range m.sls {
		for _, s := range sl.ss {
			matched := s(muc.Conn, muc.startSniffing())
			if matched {
				muc.doneSniffing()
				if m.readTimeout > noTimeout {
					_ = c.SetReadDeadline(time.Time{})
				}
				select {
				case sl.l.connc <- muc:
				case <-donec:
					_ = c.Close()
				}
				return
			}
		}
	}

	_ = c.Close()
	err := ErrNotMatched{c: c}
	if !m.handleErr(err) {
		_ = m.root.Close()
	}
}

func (m *cMux) HandleError(h ErrorHandler) {
	m.errh = h
}

func (m *cMux) handleErr(err error) bool {
	if !m.errh(err) {
		return false
	}

	if ne, ok := err.(net.Error); ok {
		return ne.Temporary()
	}

	return false
}

type muxListener struct {
	net.Listener
	connc chan net.Conn
}

func (l muxListener) Accept() (net.Conn, error) {
	c, ok := <-l.connc
	if !ok {
		return nil, ErrListenerClosed
	}
	return c, nil
}

// MuxConn wraps a net.Conn and provides transparent sniffing of connection data.
type MuxConn struct {
	net.Conn
	buf bufferedReader
}

func newMuxConn(c net.Conn) *MuxConn {
	return &MuxConn{
		Conn: c,
		buf:  bufferedReader{source: c},
	}
}

// From the io.Reader documentation:
//
// When Read encounters an error or end-of-file condition after
// successfully reading n > 0 bytes, it returns the number of
// bytes read.  It may return the (non-nil) error from the same call
// or return the error (and n == 0) from a subsequent call.
// An instance of this general case is that a Reader returning
// a non-zero number of bytes at the end of the input stream may
// return either err == EOF or err == nil.  The next Read should
// return 0, EOF.
func (m *MuxConn) Read(p []byte) (int, error) {
	return m.buf.Read(p)
}

func (m *MuxConn) startSniffing() io.Reader {
	m.buf.reset(true)
	return &m.buf
}

func (m *MuxConn) doneSniffing() {
	m.buf.reset(false)
}
<commit_msg>add cmux.Close() function<commit_after>// Copyright 2016 The CMux 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 cmux

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

// Matcher matches a connection based on its content.
type Matcher func(io.Reader) bool

// MatchWriter is a match that can also write response (say to do handshake).
type MatchWriter func(io.Writer, io.Reader) bool

// ErrorHandler handles an error and returns whether
// the mux should continue serving the listener.
type ErrorHandler func(error) bool

var _ net.Error = ErrNotMatched{}

// ErrNotMatched is returned whenever a connection is not matched by any of
// the matchers registered in the multiplexer.
type ErrNotMatched struct {
	c net.Conn
}

func (e ErrNotMatched) Error() string {
	return fmt.Sprintf("mux: connection %v not matched by an matcher",
		e.c.RemoteAddr())
}

// Temporary implements the net.Error interface.
func (e ErrNotMatched) Temporary() bool { return true }

// Timeout implements the net.Error interface.
func (e ErrNotMatched) Timeout() bool { return false }

type errListenerClosed string

func (e errListenerClosed) Error() string   { return string(e) }
func (e errListenerClosed) Temporary() bool { return false }
func (e errListenerClosed) Timeout() bool   { return false }

// ErrListenerClosed is returned from muxListener.Accept when the underlying
// listener is closed.
var ErrListenerClosed = errListenerClosed("mux: listener closed")

// for readability of readTimeout
var noTimeout time.Duration

// New instantiates a new connection multiplexer.
func New(l net.Listener) CMux {
	return &cMux{
		root:        l,
		bufLen:      1024,
		errh:        func(_ error) bool { return true },
		donec:       make(chan struct{}),
		readTimeout: noTimeout,
	}
}

// CMux is a multiplexer for network connections.
type CMux interface {
	// Match returns a net.Listener that sees (i.e., accepts) only
	// the connections matched by at least one of the matcher.
	//
	// The order used to call Match determines the priority of matchers.
	Match(...Matcher) net.Listener
	// MatchWithWriters returns a net.Listener that accepts only the
	// connections that matched by at least of the matcher writers.
	//
	// Prefer Matchers over MatchWriters, since the latter can write on the
	// connection before the actual handler.
	//
	// The order used to call Match determines the priority of matchers.
	MatchWithWriters(...MatchWriter) net.Listener
	// Serve starts multiplexing the listener. Serve blocks and perhaps
	// should be invoked concurrently within a go routine.
	Serve() error
	// Closes cmux server and stops accepting any connections on listener
	Close()
	// HandleError registers an error handler that handles listener errors.
	HandleError(ErrorHandler)
	// sets a timeout for the read of matchers
	SetReadTimeout(time.Duration)
}

type matchersListener struct {
	ss []MatchWriter
	l  muxListener
}

type cMux struct {
	root        net.Listener
	bufLen      int
	errh        ErrorHandler
	donec       chan struct{}
	sls         []matchersListener
	readTimeout time.Duration
	mu          sync.Mutex
}

func matchersToMatchWriters(matchers []Matcher) []MatchWriter {
	mws := make([]MatchWriter, 0, len(matchers))
	for _, m := range matchers {
		cm := m
		mws = append(mws, func(w io.Writer, r io.Reader) bool {
			return cm(r)
		})
	}
	return mws
}

func (m *cMux) Match(matchers ...Matcher) net.Listener {
	mws := matchersToMatchWriters(matchers)
	return m.MatchWithWriters(mws...)
}

func (m *cMux) MatchWithWriters(matchers ...MatchWriter) net.Listener {
	ml := muxListener{
		Listener: m.root,
		connc:    make(chan net.Conn, m.bufLen),
	}
	m.sls = append(m.sls, matchersListener{ss: matchers, l: ml})
	return ml
}

func (m *cMux) SetReadTimeout(t time.Duration) {
	m.readTimeout = t
}

func (m *cMux) Serve() error {
	var wg sync.WaitGroup

	defer func() {
		m.closeDoneChanLocked()
		wg.Wait()

		for _, sl := range m.sls {
			close(sl.l.connc)
			// Drain the connections enqueued for the listener.
			for c := range sl.l.connc {
				_ = c.Close()
			}
		}
	}()

	for {
		c, err := m.root.Accept()
		if err != nil {
			select {
			case <-m.getDoneChan():
				// cmux was closed with cmux.Close()
				return nil
			}
			if !m.handleErr(err) {
				return err
			}
			continue
		}

		wg.Add(1)
		go m.serve(c, m.donec, &wg)
	}
}

func (m *cMux) serve(c net.Conn, donec <-chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	muc := newMuxConn(c)
	if m.readTimeout > noTimeout {
		_ = c.SetReadDeadline(time.Now().Add(m.readTimeout))
	}
	for _, sl := range m.sls {
		for _, s := range sl.ss {
			matched := s(muc.Conn, muc.startSniffing())
			if matched {
				muc.doneSniffing()
				if m.readTimeout > noTimeout {
					_ = c.SetReadDeadline(time.Time{})
				}
				select {
				case sl.l.connc <- muc:
				case <-m.getDoneChan():
					_ = c.Close()
				}
				return
			}
		}
	}

	_ = c.Close()
	err := ErrNotMatched{c: c}
	if !m.handleErr(err) {
		_ = m.root.Close()
	}
}

func (m *cMux) Close() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.closeDoneChanLocked()
}

func (m *cMux) getDoneChan() chan struct{} {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.getDoneChanLocked()
}

func (m *cMux) getDoneChanLocked() chan struct{} {
	if m.donec == nil {
		m.donec = make(chan struct{})
	}
	return m.donec
}

func (m *cMux) closeDoneChanLocked() {
	ch := m.getDoneChanLocked()
	select {
	case <-ch:
		// Already closed. Don't close again
	default:
		close(ch)
	}
}

func (m *cMux) HandleError(h ErrorHandler) {
	m.errh = h
}

func (m *cMux) handleErr(err error) bool {
	if !m.errh(err) {
		return false
	}

	if ne, ok := err.(net.Error); ok {
		return ne.Temporary()
	}

	return false
}

type muxListener struct {
	net.Listener
	connc chan net.Conn
}

func (l muxListener) Accept() (net.Conn, error) {
	c, ok := <-l.connc
	if !ok {
		return nil, ErrListenerClosed
	}
	return c, nil
}

// MuxConn wraps a net.Conn and provides transparent sniffing of connection data.
type MuxConn struct {
	net.Conn
	buf bufferedReader
}

func newMuxConn(c net.Conn) *MuxConn {
	return &MuxConn{
		Conn: c,
		buf:  bufferedReader{source: c},
	}
}

// From the io.Reader documentation:
//
// When Read encounters an error or end-of-file condition after
// successfully reading n > 0 bytes, it returns the number of
// bytes read.  It may return the (non-nil) error from the same call
// or return the error (and n == 0) from a subsequent call.
// An instance of this general case is that a Reader returning
// a non-zero number of bytes at the end of the input stream may
// return either err == EOF or err == nil.  The next Read should
// return 0, EOF.
func (m *MuxConn) Read(p []byte) (int, error) {
	return m.buf.Read(p)
}

func (m *MuxConn) startSniffing() io.Reader {
	m.buf.reset(true)
	return &m.buf
}

func (m *MuxConn) doneSniffing() {
	m.buf.reset(false)
}
<|endoftext|>


<commit_before>package neptulon

import (
	"errors"
	"io"
	"log"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/neptulon/cmap"
	"github.com/neptulon/shortid"

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

// Conn is a client connection.
type Conn struct {
	ID           string
	Session      *cmap.CMap
	middleware   []func(ctx *ReqCtx) error
	resRoutes    *cmap.CMap // message ID (string) -> handler func(ctx *ResCtx) error : expected responses for requests that we've sent
	ws           *websocket.Conn
	wg           sync.WaitGroup
	deadline     time.Duration
	isClientConn bool
	connected    bool
}

// NewConn creates a new Conn object.
func NewConn() (*Conn, error) {
	id, err := shortid.UUID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:        id,
		Session:   cmap.New(),
		resRoutes: cmap.New(),
		deadline:  time.Second * time.Duration(300),
	}, nil
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
// Default value for read/write deadline is 300 seconds.
func (c *Conn) SetDeadline(seconds int) {
	c.deadline = time.Second * time.Duration(seconds)
}

// Middleware registers middleware to handle incoming request messages.
func (c *Conn) Middleware(middleware ...func(ctx *ReqCtx) error) {
	c.middleware = append(c.middleware, middleware...)
}

// Connect connects to the given WebSocket server.
func (c *Conn) Connect(addr string) error {
	ws, err := websocket.Dial(addr, "", "http://localhost")
	if err != nil {
		return err
	}

	c.ws = ws
	c.connected = true
	c.wg.Add(1)
	go func() {
		defer recoverAndLog(c, &c.wg)
		c.startReceive()
	}()
	time.Sleep(time.Millisecond) // give receive goroutine a few cycles to start
	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	if c.ws == nil {
		return nil
	}

	return c.ws.RemoteAddr()
}

// SendRequest sends a JSON-RPC request through the connection with an auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequest(method string, params interface{}, resHandler func(res *ResCtx) error) (reqID string, err error) {
	id, err := shortid.UUID()
	if err != nil {
		return "", err
	}

	req := request{ID: id, Method: method, Params: params}
	if err = c.send(req); err != nil {
		return "", err
	}

	c.resRoutes.Set(req.ID, resHandler)
	return id, nil
}

// SendRequestArr sends a JSON-RPC request through the connection, with array params and auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequestArr(method string, resHandler func(res *ResCtx) error, params ...interface{}) (reqID string, err error) {
	return c.SendRequest(method, params, resHandler)
}

// Close closes the connection.
func (c *Conn) Close() error {
	c.connected = false
	return c.ws.Close()
}

// SendResponse sends a JSON-RPC response message through the connection.
func (c *Conn) sendResponse(id string, result interface{}, err *ResError) error {
	return c.send(response{ID: id, Result: result, Error: err})
}

// Send sends the given message through the connection.
func (c *Conn) send(msg interface{}) error {
	if !c.connected {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetWriteDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Send(c.ws, msg)
}

// Receive receives message from the connection.
func (c *Conn) receive(msg *message) error {
	if !c.connected {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetReadDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Receive(c.ws, &msg)
}

// UseConn reuses an established websocket.Conn.
// This function blocks and does not return until the connection is closed by another goroutine.
func (c *Conn) useConn(ws *websocket.Conn) {
	c.ws = ws
	c.connected = true
	c.startReceive()
}

// startReceive starts receiving messages. This method blocks and does not return until the connection is closed.
func (c *Conn) startReceive() {
	// append the last middleware to request stack, which will write the response to connection, if any
	c.middleware = append(c.middleware, func(ctx *ReqCtx) error {
		if ctx.Res != nil || ctx.Err != nil {
			return ctx.Conn.sendResponse(ctx.ID, ctx.Res, ctx.Err)
		}

		return nil
	})

	for {
		var m message
		err := c.receive(&m)
		if err != nil {
			// if we closed the connection
			if !c.connected {
				log.Printf("conn: closed %v: %v", c.ID, c.RemoteAddr())
				break
			}

			// if peer closed the connection
			if err == io.EOF {
				log.Printf("conn: peer disconnected %v: %v", c.ID, c.RemoteAddr())
				break
			}

			log.Printf("conn: error while receiving message: %v", err)
			break
		}

		// if the message is a request
		if m.Method != "" {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				if err := newReqCtx(c, m.ID, m.Method, m.Params, c.middleware).Next(); err != nil {
					log.Printf("conn: error while handling request: %v", err)
					c.Close()
				}
			}()

			continue
		}

		// if the message is not a JSON-RPC message
		if m.ID == "" || (m.Result == nil && m.Error == nil) {
			log.Printf("conn: received an unknown message %v: %v\n%v", c.ID, c.RemoteAddr(), m)
			break
		}

		// if the message is a response
		if resHandler, ok := c.resRoutes.GetOk(m.ID); ok {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				err := resHandler.(func(ctx *ResCtx) error)(newResCtx(c, m.ID, m.Result, m.Error))
				c.resRoutes.Delete(m.ID)
				if err != nil {
					log.Printf("conn: error while handling response: %v", err)
					c.Close()
				}
			}()
		} else {
			log.Printf("conn: error while handling response: got response to a request with unknown ID: %v", m.ID)
			break
		}
	}

	c.Close()
}

func recoverAndLog(c *Conn, wg *sync.WaitGroup) {
	if err := recover(); err != nil {
		const size = 64 << 10
		buf := make([]byte, size)
		buf = buf[:runtime.Stack(buf, false)]
		log.Printf("conn: panic handling response %v: %v\n%s", c.RemoteAddr(), err, buf)
	}
	wg.Done()
}
<commit_msg>defer wg operations to guard against panics<commit_after>package neptulon

import (
	"errors"
	"io"
	"log"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/neptulon/cmap"
	"github.com/neptulon/shortid"

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

// Conn is a client connection.
type Conn struct {
	ID           string
	Session      *cmap.CMap
	middleware   []func(ctx *ReqCtx) error
	resRoutes    *cmap.CMap // message ID (string) -> handler func(ctx *ResCtx) error : expected responses for requests that we've sent
	ws           *websocket.Conn
	wg           sync.WaitGroup
	deadline     time.Duration
	isClientConn bool
	connected    bool
}

// NewConn creates a new Conn object.
func NewConn() (*Conn, error) {
	id, err := shortid.UUID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:        id,
		Session:   cmap.New(),
		resRoutes: cmap.New(),
		deadline:  time.Second * time.Duration(300),
	}, nil
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
// Default value for read/write deadline is 300 seconds.
func (c *Conn) SetDeadline(seconds int) {
	c.deadline = time.Second * time.Duration(seconds)
}

// Middleware registers middleware to handle incoming request messages.
func (c *Conn) Middleware(middleware ...func(ctx *ReqCtx) error) {
	c.middleware = append(c.middleware, middleware...)
}

// Connect connects to the given WebSocket server.
func (c *Conn) Connect(addr string) error {
	ws, err := websocket.Dial(addr, "", "http://localhost")
	if err != nil {
		return err
	}

	c.ws = ws
	c.connected = true
	c.wg.Add(1)
	go func() {
		defer recoverAndLog(c, &c.wg)
		c.startReceive()
	}()
	time.Sleep(time.Millisecond) // give receive goroutine a few cycles to start
	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	if c.ws == nil {
		return nil
	}

	return c.ws.RemoteAddr()
}

// SendRequest sends a JSON-RPC request through the connection with an auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequest(method string, params interface{}, resHandler func(res *ResCtx) error) (reqID string, err error) {
	id, err := shortid.UUID()
	if err != nil {
		return "", err
	}

	req := request{ID: id, Method: method, Params: params}
	if err = c.send(req); err != nil {
		return "", err
	}

	c.resRoutes.Set(req.ID, resHandler)
	return id, nil
}

// SendRequestArr sends a JSON-RPC request through the connection, with array params and auto generated request ID.
// resHandler is called when a response is returned.
func (c *Conn) SendRequestArr(method string, resHandler func(res *ResCtx) error, params ...interface{}) (reqID string, err error) {
	return c.SendRequest(method, params, resHandler)
}

// Close closes the connection.
func (c *Conn) Close() error {
	c.connected = false
	return c.ws.Close()
}

// SendResponse sends a JSON-RPC response message through the connection.
func (c *Conn) sendResponse(id string, result interface{}, err *ResError) error {
	return c.send(response{ID: id, Result: result, Error: err})
}

// Send sends the given message through the connection.
func (c *Conn) send(msg interface{}) error {
	if !c.connected {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetWriteDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Send(c.ws, msg)
}

// Receive receives message from the connection.
func (c *Conn) receive(msg *message) error {
	if !c.connected {
		return errors.New("use of closed connection")
	}

	if err := c.ws.SetReadDeadline(time.Now().Add(c.deadline)); err != nil {
		return err
	}

	return websocket.JSON.Receive(c.ws, &msg)
}

// UseConn reuses an established websocket.Conn.
// This function blocks and does not return until the connection is closed by another goroutine.
func (c *Conn) useConn(ws *websocket.Conn) {
	c.ws = ws
	c.connected = true
	c.startReceive()
}

// startReceive starts receiving messages. This method blocks and does not return until the connection is closed.
func (c *Conn) startReceive() {
	defer c.Close()

	// append the last middleware to request stack, which will write the response to connection, if any
	c.middleware = append(c.middleware, func(ctx *ReqCtx) error {
		if ctx.Res != nil || ctx.Err != nil {
			return ctx.Conn.sendResponse(ctx.ID, ctx.Res, ctx.Err)
		}

		return nil
	})

	for {
		var m message
		err := c.receive(&m)
		if err != nil {
			// if we closed the connection
			if !c.connected {
				log.Printf("conn: closed %v: %v", c.ID, c.RemoteAddr())
				break
			}

			// if peer closed the connection
			if err == io.EOF {
				log.Printf("conn: peer disconnected %v: %v", c.ID, c.RemoteAddr())
				break
			}

			log.Printf("conn: error while receiving message: %v", err)
			break
		}

		// if the message is a request
		if m.Method != "" {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				if err := newReqCtx(c, m.ID, m.Method, m.Params, c.middleware).Next(); err != nil {
					log.Printf("conn: error while handling request: %v", err)
					c.Close()
				}
			}()

			continue
		}

		// if the message is not a JSON-RPC message
		if m.ID == "" || (m.Result == nil && m.Error == nil) {
			log.Printf("conn: received an unknown message %v: %v\n%v", c.ID, c.RemoteAddr(), m)
			break
		}

		// if the message is a response
		if resHandler, ok := c.resRoutes.GetOk(m.ID); ok {
			c.wg.Add(1)
			go func() {
				defer recoverAndLog(c, &c.wg)
				err := resHandler.(func(ctx *ResCtx) error)(newResCtx(c, m.ID, m.Result, m.Error))
				c.resRoutes.Delete(m.ID)
				if err != nil {
					log.Printf("conn: error while handling response: %v", err)
					c.Close()
				}
			}()
		} else {
			log.Printf("conn: error while handling response: got response to a request with unknown ID: %v", m.ID)
			break
		}
	}
}

func recoverAndLog(c *Conn, wg *sync.WaitGroup) {
	defer wg.Done()
	if err := recover(); err != nil {
		const size = 64 << 10
		buf := make([]byte, size)
		buf = buf[:runtime.Stack(buf, false)]
		log.Printf("conn: panic handling response %v: %v\n%s", c.RemoteAddr(), err, buf)
	}
}
<|endoftext|>