<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 dragonfly freebsd linux nacl netbsd openbsd solaris windows

package net

import (
	"os"
	"runtime"
	"syscall"
)

// Boolean to int.
func boolint(b bool) int {
	if b {
		return 1
	}
	return 0
}

func ipv4AddrToInterface(ip IP) (*Interface, error) {
	ift, err := Interfaces()
	if err != nil {
		return nil, err
	}
	for _, ifi := range ift {
		ifat, err := ifi.Addrs()
		if err != nil {
			return nil, err
		}
		for _, ifa := range ifat {
			switch v := ifa.(type) {
			case *IPAddr:
				if ip.Equal(v.IP) {
					return &ifi, nil
				}
			case *IPNet:
				if ip.Equal(v.IP) {
					return &ifi, nil
				}
			}
		}
	}
	if ip.Equal(IPv4zero) {
		return nil, nil
	}
	return nil, errNoSuchInterface
}

func interfaceToIPv4Addr(ifi *Interface) (IP, error) {
	if ifi == nil {
		return IPv4zero, nil
	}
	ifat, err := ifi.Addrs()
	if err != nil {
		return nil, err
	}
	for _, ifa := range ifat {
		switch v := ifa.(type) {
		case *IPAddr:
			if v.IP.To4() != nil {
				return v.IP, nil
			}
		case *IPNet:
			if v.IP.To4() != nil {
				return v.IP, nil
			}
		}
	}
	return nil, errNoSuchInterface
}

func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error {
	if ifi == nil {
		return nil
	}
	ifat, err := ifi.Addrs()
	if err != nil {
		return err
	}
	for _, ifa := range ifat {
		switch v := ifa.(type) {
		case *IPAddr:
			if a := v.IP.To4(); a != nil {
				copy(mreq.Interface[:], a)
				goto done
			}
		case *IPNet:
			if a := v.IP.To4(); a != nil {
				copy(mreq.Interface[:], a)
				goto done
			}
		}
	}
done:
	if bytesEqual(mreq.Multiaddr[:], IPv4zero.To4()) {
		return errNoSuchMulticastInterface
	}
	return nil
}

func setReadBuffer(fd *netFD, bytes int) error {
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes))
}

func setWriteBuffer(fd *netFD, bytes int) error {
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes))
}

func setKeepAlive(fd *netFD, keepalive bool) error {
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)))
}

func setLinger(fd *netFD, sec int) error {
	var l syscall.Linger
	if sec >= 0 {
		l.Onoff = 1
		l.Linger = int32(sec)
	} else {
		l.Onoff = 0
		l.Linger = 0
	}
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	opt := syscall.SO_LINGER
	if runtime.GOOS == "darwin" {
		opt = syscall.SO_LINGER_SEC
	}
	return os.NewSyscallError("setsockopt", syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, opt, &l))
}
<commit_msg>undo CL 92210044 / 5cb21eee2d35<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 dragonfly freebsd linux nacl netbsd openbsd solaris windows

package net

import (
	"os"
	"syscall"
)

// Boolean to int.
func boolint(b bool) int {
	if b {
		return 1
	}
	return 0
}

func ipv4AddrToInterface(ip IP) (*Interface, error) {
	ift, err := Interfaces()
	if err != nil {
		return nil, err
	}
	for _, ifi := range ift {
		ifat, err := ifi.Addrs()
		if err != nil {
			return nil, err
		}
		for _, ifa := range ifat {
			switch v := ifa.(type) {
			case *IPAddr:
				if ip.Equal(v.IP) {
					return &ifi, nil
				}
			case *IPNet:
				if ip.Equal(v.IP) {
					return &ifi, nil
				}
			}
		}
	}
	if ip.Equal(IPv4zero) {
		return nil, nil
	}
	return nil, errNoSuchInterface
}

func interfaceToIPv4Addr(ifi *Interface) (IP, error) {
	if ifi == nil {
		return IPv4zero, nil
	}
	ifat, err := ifi.Addrs()
	if err != nil {
		return nil, err
	}
	for _, ifa := range ifat {
		switch v := ifa.(type) {
		case *IPAddr:
			if v.IP.To4() != nil {
				return v.IP, nil
			}
		case *IPNet:
			if v.IP.To4() != nil {
				return v.IP, nil
			}
		}
	}
	return nil, errNoSuchInterface
}

func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error {
	if ifi == nil {
		return nil
	}
	ifat, err := ifi.Addrs()
	if err != nil {
		return err
	}
	for _, ifa := range ifat {
		switch v := ifa.(type) {
		case *IPAddr:
			if a := v.IP.To4(); a != nil {
				copy(mreq.Interface[:], a)
				goto done
			}
		case *IPNet:
			if a := v.IP.To4(); a != nil {
				copy(mreq.Interface[:], a)
				goto done
			}
		}
	}
done:
	if bytesEqual(mreq.Multiaddr[:], IPv4zero.To4()) {
		return errNoSuchMulticastInterface
	}
	return nil
}

func setReadBuffer(fd *netFD, bytes int) error {
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes))
}

func setWriteBuffer(fd *netFD, bytes int) error {
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes))
}

func setKeepAlive(fd *netFD, keepalive bool) error {
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)))
}

func setLinger(fd *netFD, sec int) error {
	var l syscall.Linger
	if sec >= 0 {
		l.Onoff = 1
		l.Linger = int32(sec)
	} else {
		l.Onoff = 0
		l.Linger = 0
	}
	if err := fd.incref(); err != nil {
		return err
	}
	defer fd.decref()
	return os.NewSyscallError("setsockopt", syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l))
}
<|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.

package exec

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

func helperCommand(s ...string) *Cmd {
	cs := []string{"-test.run=TestHelperProcess", "--"}
	cs = append(cs, s...)
	cmd := Command(os.Args[0], cs...)
	cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
	return cmd
}

func TestEcho(t *testing.T) {
	bs, err := helperCommand("echo", "foo bar", "baz").Output()
	if err != nil {
		t.Errorf("echo: %v", err)
	}
	if g, e := string(bs), "foo bar baz\n"; g != e {
		t.Errorf("echo: want %q, got %q", e, g)
	}
}

func TestCatStdin(t *testing.T) {
	// Cat, testing stdin and stdout.
	input := "Input string\nLine 2"
	p := helperCommand("cat")
	p.Stdin = strings.NewReader(input)
	bs, err := p.Output()
	if err != nil {
		t.Errorf("cat: %v", err)
	}
	s := string(bs)
	if s != input {
		t.Errorf("cat: want %q, got %q", input, s)
	}
}

func TestCatGoodAndBadFile(t *testing.T) {
	// Testing combined output and error values.
	bs, err := helperCommand("cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
	if _, ok := err.(*ExitError); !ok {
		t.Errorf("expected *ExitError from cat combined; got %T: %v", err, err)
	}
	s := string(bs)
	sp := strings.SplitN(s, "\n", 2)
	if len(sp) != 2 {
		t.Fatalf("expected two lines from cat; got %q", s)
	}
	errLine, body := sp[0], sp[1]
	if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") {
		t.Errorf("expected stderr to complain about file; got %q", errLine)
	}
	if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") {
		t.Errorf("expected test code; got %q (len %d)", body, len(body))
	}
}

func TestNoExistBinary(t *testing.T) {
	// Can't run a non-existent binary
	err := Command("/no-exist-binary").Run()
	if err == nil {
		t.Error("expected error from /no-exist-binary")
	}
}

func TestExitStatus(t *testing.T) {
	// Test that exit values are returned correctly
	err := helperCommand("exit", "42").Run()
	if werr, ok := err.(*ExitError); ok {
		if s, e := werr.Error(), "exit status 42"; s != e {
			t.Errorf("from exit 42 got exit %q, want %q", s, e)
		}
	} else {
		t.Fatalf("expected *ExitError from exit 42; got %T: %v", err, err)
	}
}

func TestPipes(t *testing.T) {
	check := func(what string, err error) {
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
	}
	// Cat, testing stdin and stdout.
	c := helperCommand("pipetest")
	stdin, err := c.StdinPipe()
	check("StdinPipe", err)
	stdout, err := c.StdoutPipe()
	check("StdoutPipe", err)
	stderr, err := c.StderrPipe()
	check("StderrPipe", err)

	outbr := bufio.NewReader(stdout)
	errbr := bufio.NewReader(stderr)
	line := func(what string, br *bufio.Reader) string {
		line, _, err := br.ReadLine()
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
		return string(line)
	}

	err = c.Start()
	check("Start", err)

	_, err = stdin.Write([]byte("O:I am output\n"))
	check("first stdin Write", err)
	if g, e := line("first output line", outbr), "O:I am output"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("E:I am error\n"))
	check("second stdin Write", err)
	if g, e := line("first error line", errbr), "E:I am error"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("O:I am output2\n"))
	check("third stdin Write 3", err)
	if g, e := line("second output line", outbr), "O:I am output2"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	stdin.Close()
	err = c.Wait()
	check("Wait", err)
}

func TestExtraFiles(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Logf("no operating system support; skipping")
		return
	}

	// Force network usage, to verify the epoll (or whatever) fd
	// doesn't leak to the child,
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	tf, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("TempFile: %v", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	const text = "Hello, fd 3!"
	_, err = tf.Write([]byte(text))
	if err != nil {
		t.Fatalf("Write: %v", err)
	}
	_, err = tf.Seek(0, os.SEEK_SET)
	if err != nil {
		t.Fatalf("Seek: %v", err)
	}

	c := helperCommand("read3")
	c.ExtraFiles = []*os.File{tf}
	bs, err := c.CombinedOutput()
	if err != nil {
		t.Fatalf("CombinedOutput: %v; output %q", err, bs)
	}
	if string(bs) != text {
		t.Errorf("got %q; want %q", string(bs), text)
	}
}

// TestHelperProcess isn't a real test. It's used as a helper process
// for TestParameterRun.
func TestHelperProcess(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	defer os.Exit(0)

	args := os.Args
	for len(args) > 0 {
		if args[0] == "--" {
			args = args[1:]
			break
		}
		args = args[1:]
	}
	if len(args) == 0 {
		fmt.Fprintf(os.Stderr, "No command\n")
		os.Exit(2)
	}

	cmd, args := args[0], args[1:]
	switch cmd {
	case "echo":
		iargs := []interface{}{}
		for _, s := range args {
			iargs = append(iargs, s)
		}
		fmt.Println(iargs...)
	case "cat":
		if len(args) == 0 {
			io.Copy(os.Stdout, os.Stdin)
			return
		}
		exit := 0
		for _, fn := range args {
			f, err := os.Open(fn)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				exit = 2
			} else {
				defer f.Close()
				io.Copy(os.Stdout, f)
			}
		}
		os.Exit(exit)
	case "pipetest":
		bufr := bufio.NewReader(os.Stdin)
		for {
			line, _, err := bufr.ReadLine()
			if err == io.EOF {
				break
			} else if err != nil {
				os.Exit(1)
			}
			if bytes.HasPrefix(line, []byte("O:")) {
				os.Stdout.Write(line)
				os.Stdout.Write([]byte{'\n'})
			} else if bytes.HasPrefix(line, []byte("E:")) {
				os.Stderr.Write(line)
				os.Stderr.Write([]byte{'\n'})
			} else {
				os.Exit(1)
			}
		}
	case "read3": // read fd 3
		fd3 := os.NewFile(3, "fd3")
		bs, err := ioutil.ReadAll(fd3)
		if err != nil {
			fmt.Printf("ReadAll from fd 3: %v", err)
			os.Exit(1)
		}
		// Now verify that there are no other open fds.
		var files []*os.File
		for wantfd := os.Stderr.Fd() + 2; wantfd <= 100; wantfd++ {
			f, err := os.Open(os.Args[0])
			if err != nil {
				fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
				fmt.Println(Command("lsof", "-p", fmt.Sprint(os.Getpid())).CombinedOutput())
				os.Exit(1)
			}
			if got := f.Fd(); got != wantfd {
				fmt.Printf("leaked parent file. fd = %d; want %d", got, wantfd)
				os.Exit(1)
			}
			files = append(files, f)
		}
		for _, f := range files {
			f.Close()
		}
		os.Stderr.Write(bs)
	case "exit":
		n, _ := strconv.Atoi(args[0])
		os.Exit(n)
	default:
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
		os.Exit(2)
	}
}
<commit_msg>os/exec: put the print where it will help<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.

package exec

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

func helperCommand(s ...string) *Cmd {
	cs := []string{"-test.run=TestHelperProcess", "--"}
	cs = append(cs, s...)
	cmd := Command(os.Args[0], cs...)
	cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
	return cmd
}

func TestEcho(t *testing.T) {
	bs, err := helperCommand("echo", "foo bar", "baz").Output()
	if err != nil {
		t.Errorf("echo: %v", err)
	}
	if g, e := string(bs), "foo bar baz\n"; g != e {
		t.Errorf("echo: want %q, got %q", e, g)
	}
}

func TestCatStdin(t *testing.T) {
	// Cat, testing stdin and stdout.
	input := "Input string\nLine 2"
	p := helperCommand("cat")
	p.Stdin = strings.NewReader(input)
	bs, err := p.Output()
	if err != nil {
		t.Errorf("cat: %v", err)
	}
	s := string(bs)
	if s != input {
		t.Errorf("cat: want %q, got %q", input, s)
	}
}

func TestCatGoodAndBadFile(t *testing.T) {
	// Testing combined output and error values.
	bs, err := helperCommand("cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
	if _, ok := err.(*ExitError); !ok {
		t.Errorf("expected *ExitError from cat combined; got %T: %v", err, err)
	}
	s := string(bs)
	sp := strings.SplitN(s, "\n", 2)
	if len(sp) != 2 {
		t.Fatalf("expected two lines from cat; got %q", s)
	}
	errLine, body := sp[0], sp[1]
	if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") {
		t.Errorf("expected stderr to complain about file; got %q", errLine)
	}
	if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") {
		t.Errorf("expected test code; got %q (len %d)", body, len(body))
	}
}

func TestNoExistBinary(t *testing.T) {
	// Can't run a non-existent binary
	err := Command("/no-exist-binary").Run()
	if err == nil {
		t.Error("expected error from /no-exist-binary")
	}
}

func TestExitStatus(t *testing.T) {
	// Test that exit values are returned correctly
	err := helperCommand("exit", "42").Run()
	if werr, ok := err.(*ExitError); ok {
		if s, e := werr.Error(), "exit status 42"; s != e {
			t.Errorf("from exit 42 got exit %q, want %q", s, e)
		}
	} else {
		t.Fatalf("expected *ExitError from exit 42; got %T: %v", err, err)
	}
}

func TestPipes(t *testing.T) {
	check := func(what string, err error) {
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
	}
	// Cat, testing stdin and stdout.
	c := helperCommand("pipetest")
	stdin, err := c.StdinPipe()
	check("StdinPipe", err)
	stdout, err := c.StdoutPipe()
	check("StdoutPipe", err)
	stderr, err := c.StderrPipe()
	check("StderrPipe", err)

	outbr := bufio.NewReader(stdout)
	errbr := bufio.NewReader(stderr)
	line := func(what string, br *bufio.Reader) string {
		line, _, err := br.ReadLine()
		if err != nil {
			t.Fatalf("%s: %v", what, err)
		}
		return string(line)
	}

	err = c.Start()
	check("Start", err)

	_, err = stdin.Write([]byte("O:I am output\n"))
	check("first stdin Write", err)
	if g, e := line("first output line", outbr), "O:I am output"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("E:I am error\n"))
	check("second stdin Write", err)
	if g, e := line("first error line", errbr), "E:I am error"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	_, err = stdin.Write([]byte("O:I am output2\n"))
	check("third stdin Write 3", err)
	if g, e := line("second output line", outbr), "O:I am output2"; g != e {
		t.Errorf("got %q, want %q", g, e)
	}

	stdin.Close()
	err = c.Wait()
	check("Wait", err)
}

func TestExtraFiles(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Logf("no operating system support; skipping")
		return
	}

	// Force network usage, to verify the epoll (or whatever) fd
	// doesn't leak to the child,
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	tf, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("TempFile: %v", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	const text = "Hello, fd 3!"
	_, err = tf.Write([]byte(text))
	if err != nil {
		t.Fatalf("Write: %v", err)
	}
	_, err = tf.Seek(0, os.SEEK_SET)
	if err != nil {
		t.Fatalf("Seek: %v", err)
	}

	c := helperCommand("read3")
	c.ExtraFiles = []*os.File{tf}
	bs, err := c.CombinedOutput()
	if err != nil {
		t.Fatalf("CombinedOutput: %v; output %q", err, bs)
	}
	if string(bs) != text {
		t.Errorf("got %q; want %q", string(bs), text)
	}
}

// TestHelperProcess isn't a real test. It's used as a helper process
// for TestParameterRun.
func TestHelperProcess(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	defer os.Exit(0)

	args := os.Args
	for len(args) > 0 {
		if args[0] == "--" {
			args = args[1:]
			break
		}
		args = args[1:]
	}
	if len(args) == 0 {
		fmt.Fprintf(os.Stderr, "No command\n")
		os.Exit(2)
	}

	cmd, args := args[0], args[1:]
	switch cmd {
	case "echo":
		iargs := []interface{}{}
		for _, s := range args {
			iargs = append(iargs, s)
		}
		fmt.Println(iargs...)
	case "cat":
		if len(args) == 0 {
			io.Copy(os.Stdout, os.Stdin)
			return
		}
		exit := 0
		for _, fn := range args {
			f, err := os.Open(fn)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				exit = 2
			} else {
				defer f.Close()
				io.Copy(os.Stdout, f)
			}
		}
		os.Exit(exit)
	case "pipetest":
		bufr := bufio.NewReader(os.Stdin)
		for {
			line, _, err := bufr.ReadLine()
			if err == io.EOF {
				break
			} else if err != nil {
				os.Exit(1)
			}
			if bytes.HasPrefix(line, []byte("O:")) {
				os.Stdout.Write(line)
				os.Stdout.Write([]byte{'\n'})
			} else if bytes.HasPrefix(line, []byte("E:")) {
				os.Stderr.Write(line)
				os.Stderr.Write([]byte{'\n'})
			} else {
				os.Exit(1)
			}
		}
	case "read3": // read fd 3
		fd3 := os.NewFile(3, "fd3")
		bs, err := ioutil.ReadAll(fd3)
		if err != nil {
			fmt.Printf("ReadAll from fd 3: %v", err)
			os.Exit(1)
		}
		// Now verify that there are no other open fds.
		var files []*os.File
		for wantfd := os.Stderr.Fd() + 2; wantfd <= 100; wantfd++ {
			f, err := os.Open(os.Args[0])
			if err != nil {
				fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
				os.Exit(1)
			}
			if got := f.Fd(); got != wantfd {
				fmt.Printf("leaked parent file. fd = %d; want %d", got, wantfd)
				fmt.Println(Command("lsof", "-p", fmt.Sprint(os.Getpid())).CombinedOutput())
				os.Exit(1)
			}
			files = append(files, f)
		}
		for _, f := range files {
			f.Close()
		}
		os.Stderr.Write(bs)
	case "exit":
		n, _ := strconv.Atoi(args[0])
		os.Exit(n)
	default:
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package stepman

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/bitrise-io/go-utils/command"
	"github.com/bitrise-io/go-utils/command/git"
	"github.com/bitrise-io/go-utils/fileutil"
	"github.com/bitrise-io/go-utils/log"
	"github.com/bitrise-io/go-utils/pathutil"
	"github.com/bitrise-io/go-utils/retry"
	"github.com/bitrise-io/go-utils/urlutil"
	"github.com/bitrise-io/go-utils/versions"
	"github.com/bitrise-io/stepman/models"
	"gopkg.in/yaml.v2"
)

// ParseStepGroupInfoModel ...
func ParseStepGroupInfoModel(pth string) (models.StepGroupInfoModel, bool, error) {
	if exist, err := pathutil.IsPathExists(pth); err != nil {
		return models.StepGroupInfoModel{}, false, err
	} else if !exist {
		return models.StepGroupInfoModel{}, false, nil
	}

	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepGroupInfoModel{}, true, err
	}

	var globalStepInfo models.StepGroupInfoModel
	if err := yaml.Unmarshal(bytes, &globalStepInfo); err != nil {
		return models.StepGroupInfoModel{}, true, err
	}

	return globalStepInfo, true, nil
}

// ParseStepDefinition ...
func ParseStepDefinition(pth string, validate bool) (models.StepModel, error) {
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepModel{}, err
	}

	var stepModel models.StepModel
	if err := yaml.Unmarshal(bytes, &stepModel); err != nil {
		return models.StepModel{}, err
	}

	if err := stepModel.Normalize(); err != nil {
		return models.StepModel{}, err
	}

	if validate {
		if err := stepModel.Audit(); err != nil {
			return models.StepModel{}, err
		}
	}

	if err := stepModel.FillMissingDefaults(); err != nil {
		return models.StepModel{}, err
	}

	return stepModel, nil
}

// ParseStepGroupInfo ...
func ParseStepGroupInfo(pth string) (models.StepGroupInfoModel, error) {
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepGroupInfoModel{}, err
	}

	var stepGroupInfo models.StepGroupInfoModel
	if err := yaml.Unmarshal(bytes, &stepGroupInfo); err != nil {
		return models.StepGroupInfoModel{}, err
	}

	return stepGroupInfo, nil
}

// ParseStepCollection ...
func ParseStepCollection(pth string) (models.StepCollectionModel, error) {
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepCollectionModel{}, err
	}

	var stepCollection models.StepCollectionModel
	if err := yaml.Unmarshal(bytes, &stepCollection); err != nil {
		return models.StepCollectionModel{}, err
	}
	return stepCollection, nil
}

// DownloadStep ...
func DownloadStep(collectionURI string, collection models.StepCollectionModel, id, version, commithash string) error {
	downloadLocations, err := collection.GetDownloadLocations(id, version)
	if err != nil {
		return err
	}

	route, found := ReadRoute(collectionURI)
	if !found {
		return fmt.Errorf("No routing found for lib: %s", collectionURI)
	}

	stepPth := GetStepCacheDirPath(route, id, version)
	if exist, err := pathutil.IsPathExists(stepPth); err != nil {
		return err
	} else if exist {
		return nil
	}

	success := false
	for _, downloadLocation := range downloadLocations {
		switch downloadLocation.Type {
		case "zip":
			err := retry.Times(2).Wait(3 * time.Second).Try(func(attempt uint) error {
				return command.DownloadAndUnZIP(downloadLocation.Src, stepPth)
			})

			if err != nil {
				log.Warnf("Failed to download step.zip: ", err)
			} else {
				success = true
				return nil
			}
		case "git":
			err := retry.Times(2).Wait(3 * time.Second).Try(func(attempt uint) error {
				return git.CloneTagOrBranchAndValidateCommitHash(downloadLocation.Src, stepPth, version, commithash)
			})

			if err != nil {
				log.Warnf("Failed to clone step (%s): %v", downloadLocation.Src, err)
			} else {
				success = true
				return nil
			}
		default:
			return fmt.Errorf("Failed to download: Invalid download location (%#v) for step %#v (%#v)", downloadLocation, id, version)
		}
	}

	if !success {
		return errors.New("Failed to download step")
	}
	return nil
}

func addStepVersionToStepGroup(step models.StepModel, version string, stepGroup models.StepGroupModel) (models.StepGroupModel, error) {
	if stepGroup.LatestVersionNumber != "" {
		r, err := versions.CompareVersions(stepGroup.LatestVersionNumber, version)
		if err != nil {
			return models.StepGroupModel{}, err
		}
		if r == 1 {
			stepGroup.LatestVersionNumber = version
		}
	} else {
		stepGroup.LatestVersionNumber = version
	}
	stepGroup.Versions[version] = step
	return stepGroup, nil
}

func generateStepLib(route SteplibRoute, templateCollection models.StepCollectionModel) (models.StepCollectionModel, error) {
	collection := models.StepCollectionModel{
		FormatVersion:         templateCollection.FormatVersion,
		GeneratedAtTimeStamp:  time.Now().Unix(),
		SteplibSource:         templateCollection.SteplibSource,
		DownloadLocations:     templateCollection.DownloadLocations,
		AssetsDownloadBaseURI: templateCollection.AssetsDownloadBaseURI,
	}

	stepHash := models.StepHash{}

	stepsSpecDirPth := GetLibraryBaseDirPath(route)
	if err := filepath.Walk(stepsSpecDirPth, func(pth string, f os.FileInfo, err error) error {
		truncatedPath := strings.Replace(pth, stepsSpecDirPth+"/", "", -1)
		match, matchErr := regexp.MatchString("([a-z]+).yml", truncatedPath)
		if matchErr != nil {
			return matchErr
		}

		if match {
			components := strings.Split(truncatedPath, "/")
			if len(components) == 4 {
				stepsDirName := components[0]
				stepID := components[1]
				stepVersion := components[2]

				step, parseErr := ParseStepDefinition(pth, true)
				if parseErr != nil {
					return parseErr
				}

				stepGroupInfo := models.StepGroupInfoModel{}

				// Check for step-info.yml - STEP_SPEC_DIR/steps/step-id/step-info.yml
				stepGroupInfoPth := filepath.Join(stepsSpecDirPth, stepsDirName, stepID, "step-info.yml")
				if exist, err := pathutil.IsPathExists(stepGroupInfoPth); err != nil {
					return err
				} else if exist {
					deprecationInfo, err := ParseStepGroupInfo(stepGroupInfoPth)
					if err != nil {
						return err
					}

					stepGroupInfo.RemovalDate = deprecationInfo.RemovalDate
					stepGroupInfo.DeprecateNotes = deprecationInfo.DeprecateNotes
				}

				// Check for assets - STEP_SPEC_DIR/steps/step-id/assets
				if collection.AssetsDownloadBaseURI != "" {
					assetsFolderPth := path.Join(stepsSpecDirPth, stepsDirName, stepID, "assets")
					exist, err := pathutil.IsPathExists(assetsFolderPth)
					if err != nil {
						return err
					}
					if exist {
						assetsMap := map[string]string{}
						err := filepath.Walk(assetsFolderPth, func(pth string, f os.FileInfo, err error) error {
							_, file := filepath.Split(pth)
							if pth != assetsFolderPth && file != "" {
								assetURI, err := urlutil.Join(collection.AssetsDownloadBaseURI, stepID, "assets", file)
								if err != nil {
									return err
								}
								assetsMap[file] = assetURI
							}
							return nil
						})

						if err != nil {
							return err
						}

						step.AssetURLs = assetsMap
						stepGroupInfo.AssetURLs = assetsMap
					}
				}

				// Add to stepgroup
				stepGroup, found := stepHash[stepID]
				if !found {
					stepGroup = models.StepGroupModel{
						Versions: map[string]models.StepModel{},
					}
				}
				stepGroup, err = addStepVersionToStepGroup(step, stepVersion, stepGroup)
				if err != nil {
					return err
				}

				stepGroup.Info = stepGroupInfo

				stepHash[stepID] = stepGroup
			} else {
			}
		}

		return err
	}); err != nil {
		return models.StepCollectionModel{}, fmt.Errorf("Failed to walk through path, error: %s", err)
	}

	collection.Steps = stepHash

	return collection, nil
}

// WriteStepSpecToFile ...
func WriteStepSpecToFile(templateCollection models.StepCollectionModel, route SteplibRoute) error {
	pth := GetStepSpecPath(route)

	if exist, err := pathutil.IsPathExists(pth); err != nil {
		return err
	} else if !exist {
		dir, _ := path.Split(pth)
		err := os.MkdirAll(dir, 0777)
		if err != nil {
			return err
		}
	} else {
		err := os.Remove(pth)
		if err != nil {
			return err
		}
	}

	collection, err := generateStepLib(route, templateCollection)
	if err != nil {
		return err
	}

	bytes, err := json.MarshalIndent(collection, "", "\t")
	if err != nil {
		return err
	}
	return fileutil.WriteBytesToFile(pth, bytes)
}

// ReadStepSpec ...
func ReadStepSpec(uri string) (models.StepCollectionModel, error) {
	route, found := ReadRoute(uri)
	if !found {
		return models.StepCollectionModel{}, errors.New("No route found for lib: " + uri)
	}
	pth := GetStepSpecPath(route)
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepCollectionModel{}, err
	}
	var stepLib models.StepCollectionModel
	if err := json.Unmarshal(bytes, &stepLib); err != nil {
		return models.StepCollectionModel{}, err
	}

	return stepLib, nil
}

// ReadStepVersionInfo ...
func ReadStepVersionInfo(collectionURI, stepID, stepVersionID string) (models.StepVersionModel, error) {
	// Input validation
	if stepID == "" {
		return models.StepVersionModel{}, errors.New("Missing required input: step id")
	}

	// Check if step exist in collection
	collection, err := ReadStepSpec(collectionURI)
	if err != nil {
		return models.StepVersionModel{}, fmt.Errorf("Failed to read steps spec (spec.json), err: %s", err)
	}

	stepWithVersion, stepFound := collection.GetStepVersion(stepID, stepVersionID)
	if !stepFound {
		if stepVersionID == "" {
			return models.StepVersionModel{}, fmt.Errorf("Collection doesn't contain any version of step (id:%s)", stepID)
		}
		return models.StepVersionModel{}, fmt.Errorf("Collection doesn't contain step (id:%s) (version:%s)", stepID, stepVersionID)
	}

	return stepWithVersion, nil
}

// ReGenerateLibrarySpec ...
func ReGenerateLibrarySpec(route SteplibRoute) error {
	pth := GetLibraryBaseDirPath(route)
	if exists, err := pathutil.IsPathExists(pth); err != nil {
		return err
	} else if !exists {
		return errors.New("Not initialized")
	}

	specPth := pth + "/steplib.yml"
	collection, err := ParseStepCollection(specPth)
	if err != nil {
		return err
	}

	if err := WriteStepSpecToFile(collection, route); err != nil {
		return err
	}
	return nil
}
<commit_msg>warn fix (#227)<commit_after>package stepman

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/bitrise-io/go-utils/command"
	"github.com/bitrise-io/go-utils/command/git"
	"github.com/bitrise-io/go-utils/fileutil"
	"github.com/bitrise-io/go-utils/log"
	"github.com/bitrise-io/go-utils/pathutil"
	"github.com/bitrise-io/go-utils/retry"
	"github.com/bitrise-io/go-utils/urlutil"
	"github.com/bitrise-io/go-utils/versions"
	"github.com/bitrise-io/stepman/models"
	"gopkg.in/yaml.v2"
)

// ParseStepGroupInfoModel ...
func ParseStepGroupInfoModel(pth string) (models.StepGroupInfoModel, bool, error) {
	if exist, err := pathutil.IsPathExists(pth); err != nil {
		return models.StepGroupInfoModel{}, false, err
	} else if !exist {
		return models.StepGroupInfoModel{}, false, nil
	}

	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepGroupInfoModel{}, true, err
	}

	var globalStepInfo models.StepGroupInfoModel
	if err := yaml.Unmarshal(bytes, &globalStepInfo); err != nil {
		return models.StepGroupInfoModel{}, true, err
	}

	return globalStepInfo, true, nil
}

// ParseStepDefinition ...
func ParseStepDefinition(pth string, validate bool) (models.StepModel, error) {
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepModel{}, err
	}

	var stepModel models.StepModel
	if err := yaml.Unmarshal(bytes, &stepModel); err != nil {
		return models.StepModel{}, err
	}

	if err := stepModel.Normalize(); err != nil {
		return models.StepModel{}, err
	}

	if validate {
		if err := stepModel.Audit(); err != nil {
			return models.StepModel{}, err
		}
	}

	if err := stepModel.FillMissingDefaults(); err != nil {
		return models.StepModel{}, err
	}

	return stepModel, nil
}

// ParseStepGroupInfo ...
func ParseStepGroupInfo(pth string) (models.StepGroupInfoModel, error) {
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepGroupInfoModel{}, err
	}

	var stepGroupInfo models.StepGroupInfoModel
	if err := yaml.Unmarshal(bytes, &stepGroupInfo); err != nil {
		return models.StepGroupInfoModel{}, err
	}

	return stepGroupInfo, nil
}

// ParseStepCollection ...
func ParseStepCollection(pth string) (models.StepCollectionModel, error) {
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepCollectionModel{}, err
	}

	var stepCollection models.StepCollectionModel
	if err := yaml.Unmarshal(bytes, &stepCollection); err != nil {
		return models.StepCollectionModel{}, err
	}
	return stepCollection, nil
}

// DownloadStep ...
func DownloadStep(collectionURI string, collection models.StepCollectionModel, id, version, commithash string) error {
	downloadLocations, err := collection.GetDownloadLocations(id, version)
	if err != nil {
		return err
	}

	route, found := ReadRoute(collectionURI)
	if !found {
		return fmt.Errorf("No routing found for lib: %s", collectionURI)
	}

	stepPth := GetStepCacheDirPath(route, id, version)
	if exist, err := pathutil.IsPathExists(stepPth); err != nil {
		return err
	} else if exist {
		return nil
	}

	success := false
	for _, downloadLocation := range downloadLocations {
		switch downloadLocation.Type {
		case "zip":
			err := retry.Times(2).Wait(3 * time.Second).Try(func(attempt uint) error {
				return command.DownloadAndUnZIP(downloadLocation.Src, stepPth)
			})

			if err != nil {
				log.Warnf("Failed to download step.zip: %s", err)
			} else {
				success = true
				return nil
			}
		case "git":
			err := retry.Times(2).Wait(3 * time.Second).Try(func(attempt uint) error {
				return git.CloneTagOrBranchAndValidateCommitHash(downloadLocation.Src, stepPth, version, commithash)
			})

			if err != nil {
				log.Warnf("Failed to clone step (%s): %v", downloadLocation.Src, err)
			} else {
				success = true
				return nil
			}
		default:
			return fmt.Errorf("Failed to download: Invalid download location (%#v) for step %#v (%#v)", downloadLocation, id, version)
		}
	}

	if !success {
		return errors.New("Failed to download step")
	}
	return nil
}

func addStepVersionToStepGroup(step models.StepModel, version string, stepGroup models.StepGroupModel) (models.StepGroupModel, error) {
	if stepGroup.LatestVersionNumber != "" {
		r, err := versions.CompareVersions(stepGroup.LatestVersionNumber, version)
		if err != nil {
			return models.StepGroupModel{}, err
		}
		if r == 1 {
			stepGroup.LatestVersionNumber = version
		}
	} else {
		stepGroup.LatestVersionNumber = version
	}
	stepGroup.Versions[version] = step
	return stepGroup, nil
}

func generateStepLib(route SteplibRoute, templateCollection models.StepCollectionModel) (models.StepCollectionModel, error) {
	collection := models.StepCollectionModel{
		FormatVersion:         templateCollection.FormatVersion,
		GeneratedAtTimeStamp:  time.Now().Unix(),
		SteplibSource:         templateCollection.SteplibSource,
		DownloadLocations:     templateCollection.DownloadLocations,
		AssetsDownloadBaseURI: templateCollection.AssetsDownloadBaseURI,
	}

	stepHash := models.StepHash{}

	stepsSpecDirPth := GetLibraryBaseDirPath(route)
	if err := filepath.Walk(stepsSpecDirPth, func(pth string, f os.FileInfo, err error) error {
		truncatedPath := strings.Replace(pth, stepsSpecDirPth+"/", "", -1)
		match, matchErr := regexp.MatchString("([a-z]+).yml", truncatedPath)
		if matchErr != nil {
			return matchErr
		}

		if match {
			components := strings.Split(truncatedPath, "/")
			if len(components) == 4 {
				stepsDirName := components[0]
				stepID := components[1]
				stepVersion := components[2]

				step, parseErr := ParseStepDefinition(pth, true)
				if parseErr != nil {
					return parseErr
				}

				stepGroupInfo := models.StepGroupInfoModel{}

				// Check for step-info.yml - STEP_SPEC_DIR/steps/step-id/step-info.yml
				stepGroupInfoPth := filepath.Join(stepsSpecDirPth, stepsDirName, stepID, "step-info.yml")
				if exist, err := pathutil.IsPathExists(stepGroupInfoPth); err != nil {
					return err
				} else if exist {
					deprecationInfo, err := ParseStepGroupInfo(stepGroupInfoPth)
					if err != nil {
						return err
					}

					stepGroupInfo.RemovalDate = deprecationInfo.RemovalDate
					stepGroupInfo.DeprecateNotes = deprecationInfo.DeprecateNotes
				}

				// Check for assets - STEP_SPEC_DIR/steps/step-id/assets
				if collection.AssetsDownloadBaseURI != "" {
					assetsFolderPth := path.Join(stepsSpecDirPth, stepsDirName, stepID, "assets")
					exist, err := pathutil.IsPathExists(assetsFolderPth)
					if err != nil {
						return err
					}
					if exist {
						assetsMap := map[string]string{}
						err := filepath.Walk(assetsFolderPth, func(pth string, f os.FileInfo, err error) error {
							_, file := filepath.Split(pth)
							if pth != assetsFolderPth && file != "" {
								assetURI, err := urlutil.Join(collection.AssetsDownloadBaseURI, stepID, "assets", file)
								if err != nil {
									return err
								}
								assetsMap[file] = assetURI
							}
							return nil
						})

						if err != nil {
							return err
						}

						step.AssetURLs = assetsMap
						stepGroupInfo.AssetURLs = assetsMap
					}
				}

				// Add to stepgroup
				stepGroup, found := stepHash[stepID]
				if !found {
					stepGroup = models.StepGroupModel{
						Versions: map[string]models.StepModel{},
					}
				}
				stepGroup, err = addStepVersionToStepGroup(step, stepVersion, stepGroup)
				if err != nil {
					return err
				}

				stepGroup.Info = stepGroupInfo

				stepHash[stepID] = stepGroup
			} else {
			}
		}

		return err
	}); err != nil {
		return models.StepCollectionModel{}, fmt.Errorf("Failed to walk through path, error: %s", err)
	}

	collection.Steps = stepHash

	return collection, nil
}

// WriteStepSpecToFile ...
func WriteStepSpecToFile(templateCollection models.StepCollectionModel, route SteplibRoute) error {
	pth := GetStepSpecPath(route)

	if exist, err := pathutil.IsPathExists(pth); err != nil {
		return err
	} else if !exist {
		dir, _ := path.Split(pth)
		err := os.MkdirAll(dir, 0777)
		if err != nil {
			return err
		}
	} else {
		err := os.Remove(pth)
		if err != nil {
			return err
		}
	}

	collection, err := generateStepLib(route, templateCollection)
	if err != nil {
		return err
	}

	bytes, err := json.MarshalIndent(collection, "", "\t")
	if err != nil {
		return err
	}
	return fileutil.WriteBytesToFile(pth, bytes)
}

// ReadStepSpec ...
func ReadStepSpec(uri string) (models.StepCollectionModel, error) {
	route, found := ReadRoute(uri)
	if !found {
		return models.StepCollectionModel{}, errors.New("No route found for lib: " + uri)
	}
	pth := GetStepSpecPath(route)
	bytes, err := fileutil.ReadBytesFromFile(pth)
	if err != nil {
		return models.StepCollectionModel{}, err
	}
	var stepLib models.StepCollectionModel
	if err := json.Unmarshal(bytes, &stepLib); err != nil {
		return models.StepCollectionModel{}, err
	}

	return stepLib, nil
}

// ReadStepVersionInfo ...
func ReadStepVersionInfo(collectionURI, stepID, stepVersionID string) (models.StepVersionModel, error) {
	// Input validation
	if stepID == "" {
		return models.StepVersionModel{}, errors.New("Missing required input: step id")
	}

	// Check if step exist in collection
	collection, err := ReadStepSpec(collectionURI)
	if err != nil {
		return models.StepVersionModel{}, fmt.Errorf("Failed to read steps spec (spec.json), err: %s", err)
	}

	stepWithVersion, stepFound := collection.GetStepVersion(stepID, stepVersionID)
	if !stepFound {
		if stepVersionID == "" {
			return models.StepVersionModel{}, fmt.Errorf("Collection doesn't contain any version of step (id:%s)", stepID)
		}
		return models.StepVersionModel{}, fmt.Errorf("Collection doesn't contain step (id:%s) (version:%s)", stepID, stepVersionID)
	}

	return stepWithVersion, nil
}

// ReGenerateLibrarySpec ...
func ReGenerateLibrarySpec(route SteplibRoute) error {
	pth := GetLibraryBaseDirPath(route)
	if exists, err := pathutil.IsPathExists(pth); err != nil {
		return err
	} else if !exists {
		return errors.New("Not initialized")
	}

	specPth := pth + "/steplib.yml"
	collection, err := ParseStepCollection(specPth)
	if err != nil {
		return err
	}

	if err := WriteStepSpecToFile(collection, route); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package stream

import (
	"reflect"
	"sync"

	"github.com/cevian/go-stream/util/slog"
)

type Chain interface {
	Operators() []Operator
	Run() error
	Stop() error
	Add(o Operator) Chain
	SetName(string) Chain
	//NewSubChain creates a new empty
	//chain inheriting the properties of the parent chain
	//Usefull for distribute/fanout building functions
	NewSubChain() Chain
	//async functions
	Start() error
	Wait() error
}

/* A SimpleChain implements the operator interface too! */
type SimpleChain struct {
	runner Runner
	//	Ops         []Operator
	//	wg          *sync.WaitGroup
	//	closenotify chan bool
	//	closeerror  chan error
	sentstop bool
	Name     string
}

func NewChain() *SimpleChain {
	return NewSimpleChain()
}

func NewSimpleChain() *SimpleChain {
	c := &SimpleChain{runner: NewFailSilentRunner(), Name: "SimpleChain"}

	var stopOnce sync.Once
	opCloseHandler := func(op Operator, err error) {
		stopOnce.Do(func() {
			if err != nil {
				slog.Warnf("Hard close of chain %s was triggered by op  (%v, %v). Error: %v", c.Name, op, reflect.TypeOf(op), err)
				c.Stop()
			} else {
				slog.Infof("Soft close of chain %s was triggered by op  (%v, %v).", c.Name, op, reflect.TypeOf(op))
				c.SoftStop()
			}
		})
	}

	c.runner.SetOpCloseHandler(opCloseHandler)

	return c
}

func (c *SimpleChain) Operators() []Operator {
	return c.runner.Operators()
}

func (c *SimpleChain) SetName(name string) Chain {
	c.Name = name
	c.runner.SetName(name)
	return c
}

func (c *SimpleChain) NewSubChain() Chain {
	return NewSimpleChain()
}

func (c *SimpleChain) Add(o Operator) Chain {
	ops := c.runner.Operators()
	opIn, isIn := o.(In)
	if isIn && len(ops) > 0 {
		slog.Infof("Setting input channel of %s", Name(o))
		last := ops[len(ops)-1]

		lastOutCh := last.(Out).Out()

		opIn.SetIn(lastOutCh)
	}

	out, ok := o.(Out)
	if ok && nil == out.Out() {
		slog.Infof("Setting output channel of %s", Name(o))
		ch := make(chan Object, CHAN_SLACK)
		out.SetOut(ch)
	}

	c.runner.Add(o)
	return c
}

func (c *SimpleChain) Start() error {
	c.runner.AsyncRunAll()
	return nil
}

func (c *SimpleChain) SoftStop() error {
	if !c.sentstop {
		c.sentstop = true
		slog.Warnf("In soft close of chain %s", c.Name)
		ops := c.runner.Operators()
		ops[0].Stop()
	}
	return nil
}

/* A stop is a hard stop as per the Operator interface */
func (c *SimpleChain) Stop() error {
	if !c.sentstop {
		c.sentstop = true
		slog.Warnf("In hard close of chain %s", c.Name)
		c.runner.HardStop()
	}
	return nil
}

func (c *SimpleChain) Wait() error {
	slog.Infof("Waiting for runner to finish in chain %s", c.Name)
	err := c.runner.Wait()
	slog.Infof("Exiting chain %s with error: %v", c.Name, err)

	return err
}

/* Operator compatibility */
func (c *SimpleChain) Run() error {
	if err := c.Start(); err != nil {
		return err
	}
	return c.Wait()
}

type OrderedChain struct {
	*SimpleChain
}

func NewOrderedChain() *OrderedChain {
	return &OrderedChain{NewChain()}
}

func (c *OrderedChain) Add(o Operator) Chain {
	parallel, ok := o.(ParallelizableOperator)
	if ok && parallel.IsParallel() {
		if !parallel.IsOrdered() {
			parallel = parallel.MakeOrdered()
			if !parallel.IsOrdered() {
				slog.Fatalf("%s", "Couldn't make parallel operator ordered")
			}
		}
		c.SimpleChain.Add(parallel)
	} else {
		c.SimpleChain.Add(o)
	}
	return c
}

func (c *OrderedChain) NewSubChain() Chain {
	return NewOrderedChain()
}

type InChain interface {
	Chain
	In
}

type inChain struct {
	Chain
}

func NewInChainWrapper(c Chain) InChain {
	return &inChain{c}
}

func (c *inChain) In() chan Object {
	ops := c.Operators()
	return ops[0].(In).In()
}

func (c *inChain) GetInDepth() int {
	ops := c.Operators()
	return ops[0].(In).GetInDepth()
}

func (c *inChain) SetIn(ch chan Object) {
	ops := c.Operators()
	ops[0].(In).SetIn(ch)
}
<commit_msg>solve race<commit_after>package stream

import (
	"reflect"
	"sync"

	"github.com/cevian/go-stream/util/slog"
)

type Chain interface {
	Operators() []Operator
	Run() error
	Stop() error
	Add(o Operator) Chain
	SetName(string) Chain
	//NewSubChain creates a new empty
	//chain inheriting the properties of the parent chain
	//Usefull for distribute/fanout building functions
	NewSubChain() Chain
	//async functions
	Start() error
	Wait() error
}

/* A SimpleChain implements the operator interface too! */
type SimpleChain struct {
	runner Runner
	//	Ops         []Operator
	//	wg          *sync.WaitGroup
	//	closenotify chan bool
	//	closeerror  chan error
	stopOnce sync.Once
	Name     string
}

func NewChain() *SimpleChain {
	return NewSimpleChain()
}

func NewSimpleChain() *SimpleChain {
	c := &SimpleChain{runner: NewFailSilentRunner(), Name: "SimpleChain", stopOnce: sync.Once{}}

	var stopOnce sync.Once
	opCloseHandler := func(op Operator, err error) {
		stopOnce.Do(func() {
			if err != nil {
				slog.Warnf("Hard close of chain %s was triggered by op  (%v, %v). Error: %v", c.Name, op, reflect.TypeOf(op), err)
				c.Stop()
			} else {
				slog.Infof("Soft close of chain %s was triggered by op  (%v, %v).", c.Name, op, reflect.TypeOf(op))
				c.SoftStop()
			}
		})
	}

	c.runner.SetOpCloseHandler(opCloseHandler)

	return c
}

func (c *SimpleChain) Operators() []Operator {
	return c.runner.Operators()
}

func (c *SimpleChain) SetName(name string) Chain {
	c.Name = name
	c.runner.SetName(name)
	return c
}

func (c *SimpleChain) NewSubChain() Chain {
	return NewSimpleChain()
}

func (c *SimpleChain) Add(o Operator) Chain {
	ops := c.runner.Operators()
	opIn, isIn := o.(In)
	if isIn && len(ops) > 0 {
		slog.Infof("Setting input channel of %s", Name(o))
		last := ops[len(ops)-1]

		lastOutCh := last.(Out).Out()

		opIn.SetIn(lastOutCh)
	}

	out, ok := o.(Out)
	if ok && nil == out.Out() {
		slog.Infof("Setting output channel of %s", Name(o))
		ch := make(chan Object, CHAN_SLACK)
		out.SetOut(ch)
	}

	c.runner.Add(o)
	return c
}

func (c *SimpleChain) Start() error {
	c.runner.AsyncRunAll()
	return nil
}

func (c *SimpleChain) SoftStop() error {
	c.stopOnce.Do(func() {
		slog.Warnf("In soft close of chain %s", c.Name)
		ops := c.runner.Operators()
		ops[0].Stop()
	})
	return nil
}

/* A stop is a hard stop as per the Operator interface */
func (c *SimpleChain) Stop() error {
	c.stopOnce.Do(func() {
		slog.Warnf("In hard close of chain %s", c.Name)
		c.runner.HardStop()
	})
	return nil
}

func (c *SimpleChain) Wait() error {
	slog.Infof("Waiting for runner to finish in chain %s", c.Name)
	err := c.runner.Wait()
	slog.Infof("Exiting chain %s with error: %v", c.Name, err)

	return err
}

/* Operator compatibility */
func (c *SimpleChain) Run() error {
	if err := c.Start(); err != nil {
		return err
	}
	return c.Wait()
}

type OrderedChain struct {
	*SimpleChain
}

func NewOrderedChain() *OrderedChain {
	return &OrderedChain{NewChain()}
}

func (c *OrderedChain) Add(o Operator) Chain {
	parallel, ok := o.(ParallelizableOperator)
	if ok && parallel.IsParallel() {
		if !parallel.IsOrdered() {
			parallel = parallel.MakeOrdered()
			if !parallel.IsOrdered() {
				slog.Fatalf("%s", "Couldn't make parallel operator ordered")
			}
		}
		c.SimpleChain.Add(parallel)
	} else {
		c.SimpleChain.Add(o)
	}
	return c
}

func (c *OrderedChain) NewSubChain() Chain {
	return NewOrderedChain()
}

type InChain interface {
	Chain
	In
}

type inChain struct {
	Chain
}

func NewInChainWrapper(c Chain) InChain {
	return &inChain{c}
}

func (c *inChain) In() chan Object {
	ops := c.Operators()
	return ops[0].(In).In()
}

func (c *inChain) GetInDepth() int {
	ops := c.Operators()
	return ops[0].(In).GetInDepth()
}

func (c *inChain) SetIn(ch chan Object) {
	ops := c.Operators()
	ops[0].(In).SetIn(ch)
}
<|endoftext|>


<commit_before>// Copyright ©2011-2013 The bíogo 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 errors

import (
	check "launchpad.net/gocheck"
	"testing"
)

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

type S struct{}

var _ = check.Suite(&S{})

// Helpers
func f(i int) error {
	if i == 0 {
		return Make("message", 0, 10)
	}

	i--
	return f(i)
}

var traceRE = `Trace: message:

 code.google.com/p/biogo/errors.f:
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=21
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25

 code.google.com/p/biogo/errors.\(\*S\).TestCaller:
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=60

 reflect.Value.call:
	(?:[A-Z]:)?/.*/go/src/pkg/reflect/value.go#L=[0-9]+

 reflect.Value.Call:
	(?:[A-Z]:)?/.*/go/src/pkg/reflect/value.go#L=[0-9]+

 launchpad.net/gocheck._?func[_·][0-9]+:
	(?:[A-Z]:)?/.*/launchpad.net/gocheck/gocheck.go#L=[0-9]+
`

// Tests
func (s *S) TestCaller(c *check.C) {
	err := Make("message", 0, 10, "item")
	c.Check(err.Error(), check.Equals, "message")
	fn, ln := err.FileLine()
	c.Check(fn, check.Matches, "(?:[A-Z]:)?/.*/biogo/errors/errors_test.go")
	c.Check(ln, check.Equals, 53)
	c.Check(err.Package(), check.Equals, "code.google.com/p/biogo/errors.(*S)")
	c.Check(err.Function(), check.Equals, "TestCaller")
	err = f(5).(Error)
	c.Check(err.Tracef(10), check.Matches, traceRE)
}

func (s *S) TestMakeFail(c *check.C) {
	c.Check(func() { Make("message", 0, 0) }, check.Panics, "errors: zero trace depth")
}
<commit_msg>Make test more independent of standard library<commit_after>// Copyright ©2011-2013 The bíogo 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 errors

import (
	check "launchpad.net/gocheck"
	"testing"
)

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

type S struct{}

var _ = check.Suite(&S{})

// Helpers
func f(i int) error {
	if i == 0 {
		return Make("message", 0, 10)
	}

	i--
	return f(i)
}

var traceRE = `Trace: message:

 code.google.com/p/biogo/errors.f:
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=21
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25

 code.google.com/p/biogo/errors.\(\*S\).TestCaller:
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=51
`

// Tests
func (s *S) TestCaller(c *check.C) {
	err := Make("message", 0, 10, "item")
	c.Check(err.Error(), check.Equals, "message")
	fn, ln := err.FileLine()
	c.Check(fn, check.Matches, "(?:[A-Z]:)?/.*/biogo/errors/errors_test.go")
	c.Check(ln, check.Equals, 44)
	c.Check(err.Package(), check.Equals, "code.google.com/p/biogo/errors.(*S)")
	c.Check(err.Function(), check.Equals, "TestCaller")
	err = f(5).(Error)
	c.Check(err.Tracef(7), check.Matches, traceRE)
}

func (s *S) TestMakeFail(c *check.C) {
	c.Check(func() { Make("message", 0, 0) }, check.Panics, "errors: zero trace depth")
}
<|endoftext|>


<commit_before>package handler

import (
	"fmt"
	"io"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/faststackco/machinestack/driver"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"
	"github.com/labstack/echo"
)

var (
	execs map[string]exec
)

type exec struct {
	stdin   io.WriteCloser
	stdout  io.ReadCloser
	control chan driver.ControlMessage
	created time.Time
}

func (h *Handler) CreateExec(c echo.Context) error {

	name := c.Param("name")
	claims := c.Get("user").(*jwt.Token).Claims.(*JwtClaims)

	var machine Machine
	if err := h.db.Model(&machine).Where("machine.name = ?", name).Select(); err != nil {
		return err
	}

	if machine.Owner != claims.Name {
		return c.String(http.StatusBadRequest, fmt.Sprintf("machine '%s' is not owned by '%s'", name, claims.Name))
	}

	inr, inw := io.Pipe()
	outr, outw := io.Pipe()
	control := make(chan driver.ControlMessage)

	h.sched.Exec(machine.Name, machine.Driver, machine.Node, inr, outw, control)

	id := uuid.New().String()

	execs[id] = exec{
		stdin:   inw,
		stdout:  outr,
		control: control,
		created: time.Now(),
	}

	return c.String(http.StatusCreated, id)
}

func (h *Handler) ExecIO(c echo.Context) error {

	id := c.Param("id")
	exec, ok := execs[id]
	if !ok {
		return c.String(http.StatusNotFound, fmt.Sprintf("exec '%s' not found", id))
	}

	upgrader := websocket.Upgrader{}

	conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
	if err != nil {
		return err
	}
	defer conn.Close()

	for {
		messageType, r, err := conn.NextReader()
		if err != nil {
			return nil
		}
		if _, err := io.Copy(exec.stdin, r); err != nil {
			return err
		}

		w, err := conn.NextWriter(messageType)
		if err != nil {
			return err
		}
		if _, err := io.Copy(w, exec.stdout); err != nil {
			return err
		}
		if err := w.Close(); err != nil {
			return err
		}
	}
}

func (h *Handler) ExecControl(c echo.Context) error {
	id := c.Param("id")
	exec, ok := execs[id]
	if !ok {
		return c.String(http.StatusNotFound, fmt.Sprintf("exec '%s' not found", id))
	}

	upgrader := websocket.Upgrader{}

	conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
	if err != nil {
		return nil
	}
	defer conn.Close()

	for {
		var msg driver.ControlMessage
		if err := conn.ReadJSON(msg); err != nil {
			return err
		}

		exec.control <- msg
	}
}
<commit_msg>ignore exec parse error<commit_after>package handler

import (
	"fmt"
	"io"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/faststackco/machinestack/driver"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"
	"github.com/labstack/echo"
)

var (
	execs map[string]exec
)

type exec struct {
	stdin   io.WriteCloser
	stdout  io.ReadCloser
	control chan driver.ControlMessage
	created time.Time
}

func (h *Handler) CreateExec(c echo.Context) error {

	name := c.Param("name")
	claims := c.Get("user").(*jwt.Token).Claims.(*JwtClaims)

	var machine Machine
	if err := h.db.Model(&machine).Where("machine.name = ?", name).Select(); err != nil {
		return err
	}

	if machine.Owner != claims.Name {
		return c.String(http.StatusBadRequest, fmt.Sprintf("machine '%s' is not owned by '%s'", name, claims.Name))
	}

	inr, inw := io.Pipe()
	outr, outw := io.Pipe()
	control := make(chan driver.ControlMessage)

	h.sched.Exec(machine.Name, machine.Driver, machine.Node, inr, outw, control)

	id := uuid.New().String()

	execs[id] = exec{
		stdin:   inw,
		stdout:  outr,
		control: control,
		created: time.Now(),
	}

	return c.String(http.StatusCreated, id)
}

func (h *Handler) ExecIO(c echo.Context) error {

	id := c.Param("id")
	exec, ok := execs[id]
	if !ok {
		return c.String(http.StatusNotFound, fmt.Sprintf("exec '%s' not found", id))
	}

	upgrader := websocket.Upgrader{}

	conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
	if err != nil {
		return err
	}
	defer conn.Close()

	for {
		messageType, r, err := conn.NextReader()
		if err != nil {
			return nil
		}
		if _, err := io.Copy(exec.stdin, r); err != nil {
			return err
		}

		w, err := conn.NextWriter(messageType)
		if err != nil {
			return err
		}
		if _, err := io.Copy(w, exec.stdout); err != nil {
			return err
		}
		if err := w.Close(); err != nil {
			return err
		}
	}
}

func (h *Handler) ExecControl(c echo.Context) error {
	id := c.Param("id")
	exec, ok := execs[id]
	if !ok {
		return c.String(http.StatusNotFound, fmt.Sprintf("exec '%s' not found", id))
	}

	upgrader := websocket.Upgrader{}

	conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
	if err != nil {
		return nil
	}
	defer conn.Close()

	for {
		var msg driver.ControlMessage
		if err := conn.ReadJSON(msg); err != nil {
			return nil
		}

		exec.control <- msg
	}
}
<|endoftext|>


<commit_before>package handling

import (
	"bytes"
	"errors"
	"fmt"

	"net/http"
	"path"
	"reflect"
	"runtime"
	"strconv"
	"strings"

	"github.com/gotgo/fw/logging"
	"github.com/gotgo/fw/me"
	"github.com/gotgo/fw/tracing"
	"github.com/gotgo/gokn/rest"
)

// RootHandler binds api endpoint to a router
//
//	Example:
//
//		func newSetupHandlers(router *mux.Router, graph inject.Graph) {
//			//Set Custom Binder
//			kandle := new(KraveHandler)
//			root := &RootHandler{
//				Binder: kandle.RequiresDeviceAuthentication,
//			}
//
//			//Bind
//			pingEndpoint := new(PingEndpoint)
//			pingHandler := new(PingHandler)
//			root.Bind(router, pingEndpoint, pingHandler)
//		}
//
type RootHandler struct {
	Log          logging.Logger `inject:""`
	Binder       BindingFunc
	TraceHeader  string
	SpanHeader   string
	Encoders     *ContentTypeEncoders
	Decoders     *ContentTypeDecoders
	TraceHandler func(*tracing.TraceMessage)
}

func NewRootHandler() *RootHandler {
	root := &RootHandler{
		Log:          new(logging.NoOpLogger),
		Binder:       AnonymousHandler,
		TraceHeader:  traceHeader,
		SpanHeader:   spanHeader,
		Encoders:     NewContentTypeEncoders(),
		Decoders:     NewContentTypeDecoders(),
		TraceHandler: func(*tracing.TraceMessage) {},
	}

	return root
}

const (
	traceHeader = "tr-trace"
	spanHeader  = "tr-span"
)

func (rh *RootHandler) convertRequestResponse(w http.ResponseWriter, r *http.Request, endpoint rest.ServerResource) (*rest.Request, *rest.Response) {

	request := rest.NewRequest(r, rest.NewRequestContext(), endpoint)

	response := &rest.Response{
		Status:  200,
		Message: "ok",
		Headers: make(map[string]string),
	}
	return request, response
}

func requestName(r *rest.Request) string {
	rs := r.Definition.ResourceT()
	return fmt.Sprintf("%s - %s", r.Raw.Method, rs)
}

func setResponseContentType(response *rest.Response, req *http.Request, resp http.ResponseWriter, endpoint rest.ServerResource) {
	// TODO: ideally we'd match the preferred accept type, with a type we can respond with,
	// for now, until we support this, just ignore the accept string and return the first
	// content type we know we can return
	if response.ContentType == "" {
		cts := endpoint.ResponseContentTypes()
		if cts != nil && len(cts) > 0 {
			response.ContentType = cts[0]
		} else if cts = req.Header["Content-Type"]; cts != nil && len(cts) > 0 {
			response.ContentType = cts[0] //try returning the same as the requested type
		} else if cts = endpoint.RequestContentTypes(); cts != nil && len(cts) > 0 {
			response.ContentType = cts[0]
		}
	}
	resp.Header()["Content-Type"] = []string{response.ContentType}
}

type responseData struct {
	Data          []byte
	StatusCode    int
	StatusMessage string
	PanicMessage  string
}

func getErrorMessage(e interface{}) string {
	var msg string
	if err, ok := e.(error); ok {
		msg = err.Error()
	} else if str, ok := e.(string); ok {
		msg = str
	} else if _, ok := e.(runtime.Error); ok {
		msg = err.Error()
	} else {
		msg = ""
	}
	return msg
}

func (root *RootHandler) guaranteedReply(writer http.ResponseWriter, response *responseData, trace *tracing.TraceMessage) {
	defer root.TraceHandler(trace)

	var panicMessage string
	if r := recover(); r != nil {
		stack := make([]byte, 2048)
		runtime.Stack(stack, true)
		panicMessage = getErrorMessage(r)
		response.StatusMessage = "Internal Server Error"
		response.StatusCode = 500
		trace.Annotate(tracing.FromPanic, "request fail", panicMessage)
		trace.Annotate(tracing.FromPanic, "stack", stack)
		root.Log.Error("Panic Occured", me.NewErr(panicMessage+" "+string(stack)))
	}

	if response.StatusCode != 200 {
		if response.StatusCode == 0 {
			response.StatusCode = 500
			if response.StatusMessage == "" {
				response.StatusMessage = "Internal Server Error: Failed to complete"
			}
			root.Log.Error("Unhandled Panic", errors.New("Request Not completed"))
		}

		trace.Annotate(tracing.FromError, fmt.Sprintf("httpResponse: %v", response.StatusCode), response.StatusMessage)
		trace.RequestFail()
		http.Error(writer, response.StatusMessage, response.StatusCode)
		writer.Write([]byte{})
	} else {
		data := response.Data
		if data == nil {
			data = []byte{}
		}

		if bytesSent, err := writer.Write(data); err != nil {
			root.Log.Warn("failed to write response",
				&logging.KeyValue{"message", "partial reply, failed to send entire reply"},
				&logging.KeyValue{"bytesSent", bytesSent},
				&logging.KeyValue{"totalBytes", len(data)},
			)
		}
		trace.RequestCompleted()
	}
}

func flattenForm(form map[string][]string) map[string]string {
	m := make(map[string]string)
	for k, v := range form {
		m[k] = strings.Join(v, ",")
	}
	return m
}

func (root *RootHandler) createHttpHandler(handler rest.HandlerFunc, endpoint rest.ServerResource) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		traceUid := rest.GetHeaderValue(root.TraceHeader, r.Header)
		spanUid := rest.GetHeaderValue(root.SpanHeader, r.Header)
		traceMessage := tracing.NewReceiveTrace(traceUid, spanUid)
		tracer := tracing.NewMessageTracer(traceMessage)
		responseData := &responseData{}
		defer root.guaranteedReply(w, responseData, traceMessage)

		r.ParseForm()
		args := flattenForm(r.Form)

		request, response := root.convertRequestResponse(w, r, endpoint)
		request.Context.Trace = tracer

		traceMessage.ReceivedRequest(requestName(request), args, r.Header)

		if err := request.DecodeArgs(args); err != nil {
			responseData.StatusCode = http.StatusBadRequest
			responseData.StatusMessage = "Bad Request: failed parse expected URL parameters"
			return
		}

		if err := root.Decoders.DecodeBody(request, traceMessage); err != nil {
			responseData.StatusCode = http.StatusBadRequest
			responseData.StatusMessage = "Bad Request: Failed to decode request body for the provided Content-Type"
			return
		}

		boundHandler := root.Binder(handler)
		boundHandler(request, response)

		if response.Error != nil {
			request.Context.Trace.Annotate(tracing.FromError, "failed to forward order request", response.Error.Error())
		}

		responseData.StatusCode = response.Status

		if response.Status != http.StatusOK {
			responseData.StatusMessage = response.Message
			return
		}

		setResponseContentType(response, r, w, endpoint)

		var bts []byte
		var err error

		if bts, err = root.Encoders.Encode(response.Body, response.ContentType); err != nil {
			responseData.StatusCode = http.StatusInternalServerError
			responseData.StatusMessage = "Internal Server Error - Failed to encode response body"
			return
		}
		w.Header()["ContentLength"] = []string{strconv.Itoa(len(bts))}
		responseData.Data = bts

		if response.IsBinary() {
			traceMessage.AnnotateBinary(tracing.FromResponseData, "body", bytes.NewReader(bts), response.ContentType)
		} else {
			traceMessage.Annotate(tracing.FromResponseData, "body", response.Body)
		}
	}
}

func (root *RootHandler) Bind(router SimpleRouter, endpoint rest.ServerResource, handler rest.Handler, resourceRoot string) {
	httpMethod := endpoint.Verb()
	errMessage := "can't bind. method named %s is missing from type %s"
	handlerName := reflect.TypeOf(handler).Name()
	var fn rest.HandlerFunc

	if httpMethod == "GET" {
		if h, ok := handler.(rest.GetHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Get
		}
	} else if httpMethod == "POST" {
		if h, ok := handler.(rest.PostHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Post
		}
	} else if httpMethod == "PUT" {
		if h, ok := handler.(rest.PutHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Put
		}
	} else if httpMethod == "DELETE" {
		if h, ok := handler.(rest.DeleteHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Delete
		}
	} else if httpMethod == "HEAD" {
		if h, ok := handler.(rest.HeadHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Head
		}
	} else if httpMethod == "PATCH" {
		if h, ok := handler.(rest.PatchHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Patch
		}
	}

	wrappedHandler := root.createHttpHandler(fn, endpoint)
	resourcePathT := path.Join(resourceRoot, endpoint.ResourceT())
	router.RegisterRoute(httpMethod, resourcePathT, wrappedHandler)
	root.Log.Inform(fmt.Sprintf("Bound endpoint %s %s", httpMethod, resourcePathT))
}

// BindAll is a helper for calling Bind on a list of endpoints
func (root *RootHandler) BindAll(router SimpleRouter, endpoints map[rest.ServerResource]rest.Handler, resourceRoot string) {
	for definition, handler := range endpoints {
		root.Bind(router, definition, handler, resourceRoot)
	}
}
<commit_msg>readable stack trace<commit_after>package handling

import (
	"bytes"
	"errors"
	"fmt"

	"net/http"
	"path"
	"reflect"
	"runtime"
	"strconv"
	"strings"

	"github.com/gotgo/fw/logging"
	"github.com/gotgo/fw/me"
	"github.com/gotgo/fw/tracing"
	"github.com/gotgo/gokn/rest"
)

// RootHandler binds api endpoint to a router
//
//	Example:
//
//		func newSetupHandlers(router *mux.Router, graph inject.Graph) {
//			//Set Custom Binder
//			kandle := new(KraveHandler)
//			root := &RootHandler{
//				Binder: kandle.RequiresDeviceAuthentication,
//			}
//
//			//Bind
//			pingEndpoint := new(PingEndpoint)
//			pingHandler := new(PingHandler)
//			root.Bind(router, pingEndpoint, pingHandler)
//		}
//
type RootHandler struct {
	Log          logging.Logger `inject:""`
	Binder       BindingFunc
	TraceHeader  string
	SpanHeader   string
	Encoders     *ContentTypeEncoders
	Decoders     *ContentTypeDecoders
	TraceHandler func(*tracing.TraceMessage)
}

func NewRootHandler() *RootHandler {
	root := &RootHandler{
		Log:          new(logging.NoOpLogger),
		Binder:       AnonymousHandler,
		TraceHeader:  traceHeader,
		SpanHeader:   spanHeader,
		Encoders:     NewContentTypeEncoders(),
		Decoders:     NewContentTypeDecoders(),
		TraceHandler: func(*tracing.TraceMessage) {},
	}

	return root
}

const (
	traceHeader = "tr-trace"
	spanHeader  = "tr-span"
)

func (rh *RootHandler) convertRequestResponse(w http.ResponseWriter, r *http.Request, endpoint rest.ServerResource) (*rest.Request, *rest.Response) {

	request := rest.NewRequest(r, rest.NewRequestContext(), endpoint)

	response := &rest.Response{
		Status:  200,
		Message: "ok",
		Headers: make(map[string]string),
	}
	return request, response
}

func requestName(r *rest.Request) string {
	rs := r.Definition.ResourceT()
	return fmt.Sprintf("%s - %s", r.Raw.Method, rs)
}

func setResponseContentType(response *rest.Response, req *http.Request, resp http.ResponseWriter, endpoint rest.ServerResource) {
	// TODO: ideally we'd match the preferred accept type, with a type we can respond with,
	// for now, until we support this, just ignore the accept string and return the first
	// content type we know we can return
	if response.ContentType == "" {
		cts := endpoint.ResponseContentTypes()
		if cts != nil && len(cts) > 0 {
			response.ContentType = cts[0]
		} else if cts = req.Header["Content-Type"]; cts != nil && len(cts) > 0 {
			response.ContentType = cts[0] //try returning the same as the requested type
		} else if cts = endpoint.RequestContentTypes(); cts != nil && len(cts) > 0 {
			response.ContentType = cts[0]
		}
	}
	resp.Header()["Content-Type"] = []string{response.ContentType}
}

type responseData struct {
	Data          []byte
	StatusCode    int
	StatusMessage string
	PanicMessage  string
}

func getErrorMessage(e interface{}) string {
	var msg string
	if err, ok := e.(error); ok {
		msg = err.Error()
	} else if str, ok := e.(string); ok {
		msg = str
	} else if _, ok := e.(runtime.Error); ok {
		msg = err.Error()
	} else {
		msg = ""
	}
	return msg
}

func (root *RootHandler) guaranteedReply(writer http.ResponseWriter, response *responseData, trace *tracing.TraceMessage) {
	defer root.TraceHandler(trace)

	var panicMessage string
	if r := recover(); r != nil {
		stack := make([]byte, 2048)
		runtime.Stack(stack, true)
		panicMessage = getErrorMessage(r)
		response.StatusMessage = "Internal Server Error"
		response.StatusCode = 500
		trace.Annotate(tracing.FromPanic, "request fail", panicMessage)
		trace.Annotate(tracing.FromPanic, "stack", stack)
		root.Log.Error("Panic Occured", me.NewErr(fmt.Sprintf("%s callstack: %s", panicMessage, stack)))
	}

	if response.StatusCode != 200 {
		if response.StatusCode == 0 {
			response.StatusCode = 500
			if response.StatusMessage == "" {
				response.StatusMessage = "Internal Server Error: Failed to complete"
			}
			root.Log.Error("Unhandled Panic", errors.New("Request Not completed"))
		}

		trace.Annotate(tracing.FromError, fmt.Sprintf("httpResponse: %v", response.StatusCode), response.StatusMessage)
		trace.RequestFail()
		http.Error(writer, response.StatusMessage, response.StatusCode)
		writer.Write([]byte{})
	} else {
		data := response.Data
		if data == nil {
			data = []byte{}
		}

		if bytesSent, err := writer.Write(data); err != nil {
			root.Log.Warn("failed to write response",
				&logging.KeyValue{"message", "partial reply, failed to send entire reply"},
				&logging.KeyValue{"bytesSent", bytesSent},
				&logging.KeyValue{"totalBytes", len(data)},
			)
		}
		trace.RequestCompleted()
	}
}

func flattenForm(form map[string][]string) map[string]string {
	m := make(map[string]string)
	for k, v := range form {
		m[k] = strings.Join(v, ",")
	}
	return m
}

func (root *RootHandler) createHttpHandler(handler rest.HandlerFunc, endpoint rest.ServerResource) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		traceUid := rest.GetHeaderValue(root.TraceHeader, r.Header)
		spanUid := rest.GetHeaderValue(root.SpanHeader, r.Header)
		traceMessage := tracing.NewReceiveTrace(traceUid, spanUid)
		tracer := tracing.NewMessageTracer(traceMessage)
		responseData := &responseData{}
		defer root.guaranteedReply(w, responseData, traceMessage)

		r.ParseForm()
		args := flattenForm(r.Form)

		request, response := root.convertRequestResponse(w, r, endpoint)
		request.Context.Trace = tracer

		traceMessage.ReceivedRequest(requestName(request), args, r.Header)

		if err := request.DecodeArgs(args); err != nil {
			responseData.StatusCode = http.StatusBadRequest
			responseData.StatusMessage = "Bad Request: failed parse expected URL parameters"
			return
		}

		if err := root.Decoders.DecodeBody(request, traceMessage); err != nil {
			responseData.StatusCode = http.StatusBadRequest
			responseData.StatusMessage = "Bad Request: Failed to decode request body for the provided Content-Type"
			return
		}

		boundHandler := root.Binder(handler)
		boundHandler(request, response)

		if response.Error != nil {
			request.Context.Trace.Annotate(tracing.FromError, "failed to forward order request", response.Error.Error())
		}

		responseData.StatusCode = response.Status

		if response.Status != http.StatusOK {
			responseData.StatusMessage = response.Message
			return
		}

		setResponseContentType(response, r, w, endpoint)

		var bts []byte
		var err error

		if bts, err = root.Encoders.Encode(response.Body, response.ContentType); err != nil {
			responseData.StatusCode = http.StatusInternalServerError
			responseData.StatusMessage = "Internal Server Error - Failed to encode response body"
			return
		}
		w.Header()["ContentLength"] = []string{strconv.Itoa(len(bts))}
		responseData.Data = bts

		if response.IsBinary() {
			traceMessage.AnnotateBinary(tracing.FromResponseData, "body", bytes.NewReader(bts), response.ContentType)
		} else {
			traceMessage.Annotate(tracing.FromResponseData, "body", response.Body)
		}
	}
}

func (root *RootHandler) Bind(router SimpleRouter, endpoint rest.ServerResource, handler rest.Handler, resourceRoot string) {
	httpMethod := endpoint.Verb()
	errMessage := "can't bind. method named %s is missing from type %s"
	handlerName := reflect.TypeOf(handler).Name()
	var fn rest.HandlerFunc

	if httpMethod == "GET" {
		if h, ok := handler.(rest.GetHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Get
		}
	} else if httpMethod == "POST" {
		if h, ok := handler.(rest.PostHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Post
		}
	} else if httpMethod == "PUT" {
		if h, ok := handler.(rest.PutHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Put
		}
	} else if httpMethod == "DELETE" {
		if h, ok := handler.(rest.DeleteHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Delete
		}
	} else if httpMethod == "HEAD" {
		if h, ok := handler.(rest.HeadHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Head
		}
	} else if httpMethod == "PATCH" {
		if h, ok := handler.(rest.PatchHandler); !ok {
			panic(fmt.Sprintf(errMessage, httpMethod, handlerName))
		} else {
			fn = h.Patch
		}
	}

	wrappedHandler := root.createHttpHandler(fn, endpoint)
	resourcePathT := path.Join(resourceRoot, endpoint.ResourceT())
	router.RegisterRoute(httpMethod, resourcePathT, wrappedHandler)
	root.Log.Inform(fmt.Sprintf("Bound endpoint %s %s", httpMethod, resourcePathT))
}

// BindAll is a helper for calling Bind on a list of endpoints
func (root *RootHandler) BindAll(router SimpleRouter, endpoints map[rest.ServerResource]rest.Handler, resourceRoot string) {
	for definition, handler := range endpoints {
		root.Bind(router, definition, handler, resourceRoot)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/miekg/dns"
	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/app/log"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/dice"
	"v2ray.com/core/common/net"
	"v2ray.com/core/transport/internet/udp"
)

const (
	CleanupInterval  = time.Second * 120
	CleanupThreshold = 512
)

var (
	pseudoDestination = net.UDPDestination(net.LocalHostIP, net.Port(53))
)

type ARecord struct {
	IPs    []net.IP
	Expire time.Time
}

type NameServer interface {
	QueryA(domain string) <-chan *ARecord
}

type PendingRequest struct {
	expire   time.Time
	response chan<- *ARecord
}

type UDPNameServer struct {
	sync.Mutex
	address     net.Destination
	requests    map[uint16]*PendingRequest
	udpServer   *udp.Dispatcher
	nextCleanup time.Time
}

func NewUDPNameServer(address net.Destination, dispatcher dispatcher.Interface) *UDPNameServer {
	s := &UDPNameServer{
		address:   address,
		requests:  make(map[uint16]*PendingRequest),
		udpServer: udp.NewDispatcher(dispatcher),
	}
	return s
}

func (v *UDPNameServer) Cleanup() {
	expiredRequests := make([]uint16, 0, 16)
	now := time.Now()
	v.Lock()
	for id, r := range v.requests {
		if r.expire.Before(now) {
			expiredRequests = append(expiredRequests, id)
			close(r.response)
		}
	}
	for _, id := range expiredRequests {
		delete(v.requests, id)
	}
	v.Unlock()
}

func (v *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
	var id uint16
	v.Lock()
	if len(v.requests) > CleanupThreshold && v.nextCleanup.Before(time.Now()) {
		v.nextCleanup = time.Now().Add(CleanupInterval)
		go v.Cleanup()
	}

	for {
		id = dice.RollUint16()
		if _, found := v.requests[id]; found {
			continue
		}
		log.Trace(newError("add pending request id ", id).AtDebug())
		v.requests[id] = &PendingRequest{
			expire:   time.Now().Add(time.Second * 8),
			response: response,
		}
		break
	}
	v.Unlock()
	return id
}

func (v *UDPNameServer) HandleResponse(payload *buf.Buffer) {
	msg := new(dns.Msg)
	err := msg.Unpack(payload.Bytes())
	if err != nil {
		log.Trace(newError("failed to parse DNS response").Base(err).AtWarning())
		return
	}
	record := &ARecord{
		IPs: make([]net.IP, 0, 16),
	}
	id := msg.Id
	ttl := uint32(3600) // an hour
	log.Trace(newError("handling response for id ", id, " content: ", msg).AtDebug())

	v.Lock()
	request, found := v.requests[id]
	if !found {
		v.Unlock()
		return
	}
	delete(v.requests, id)
	v.Unlock()

	for _, rr := range msg.Answer {
		switch rr := rr.(type) {
		case *dns.A:
			record.IPs = append(record.IPs, rr.A)
			fmt.Println("Adding ans:", rr.A)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		case *dns.AAAA:
			record.IPs = append(record.IPs, rr.AAAA)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		}
	}
	record.Expire = time.Now().Add(time.Second * time.Duration(ttl))

	request.response <- record
	close(request.response)
}

func (v *UDPNameServer) BuildQueryA(domain string, id uint16) *buf.Buffer {

	msg := new(dns.Msg)
	msg.Id = id
	msg.RecursionDesired = true
	msg.Question = []dns.Question{
		{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeA,
			Qclass: dns.ClassINET,
		},
		{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeAAAA,
			Qclass: dns.ClassINET,
		}}

	buffer := buf.New()
	common.Must(buffer.Reset(func(b []byte) (int, error) {
		writtenBuffer, err := msg.PackBuffer(b)
		return len(writtenBuffer), err
	}))

	return buffer
}

func (v *UDPNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)
	id := v.AssignUnusedID(response)

	ctx, cancel := context.WithCancel(context.Background())
	v.udpServer.Dispatch(ctx, v.address, v.BuildQueryA(domain, id), v.HandleResponse)

	go func() {
		for i := 0; i < 2; i++ {
			time.Sleep(time.Second)
			v.Lock()
			_, found := v.requests[id]
			v.Unlock()
			if !found {
				break
			}
			v.udpServer.Dispatch(ctx, v.address, v.BuildQueryA(domain, id), v.HandleResponse)
		}
		cancel()
	}()

	return response
}

type LocalNameServer struct {
}

func (v *LocalNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)

	go func() {
		defer close(response)

		ips, err := net.LookupIP(domain)
		if err != nil {
			log.Trace(newError("failed to lookup IPs for domain ", domain).Base(err))
			return
		}

		response <- &ARecord{
			IPs:    ips,
			Expire: time.Now().Add(time.Hour),
		}
	}()

	return response
}
<commit_msg>remove debug info<commit_after>package server

import (
	"context"
	"sync"
	"time"

	"github.com/miekg/dns"
	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/app/log"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/dice"
	"v2ray.com/core/common/net"
	"v2ray.com/core/transport/internet/udp"
)

const (
	CleanupInterval  = time.Second * 120
	CleanupThreshold = 512
)

var (
	pseudoDestination = net.UDPDestination(net.LocalHostIP, net.Port(53))
)

type ARecord struct {
	IPs    []net.IP
	Expire time.Time
}

type NameServer interface {
	QueryA(domain string) <-chan *ARecord
}

type PendingRequest struct {
	expire   time.Time
	response chan<- *ARecord
}

type UDPNameServer struct {
	sync.Mutex
	address     net.Destination
	requests    map[uint16]*PendingRequest
	udpServer   *udp.Dispatcher
	nextCleanup time.Time
}

func NewUDPNameServer(address net.Destination, dispatcher dispatcher.Interface) *UDPNameServer {
	s := &UDPNameServer{
		address:   address,
		requests:  make(map[uint16]*PendingRequest),
		udpServer: udp.NewDispatcher(dispatcher),
	}
	return s
}

func (v *UDPNameServer) Cleanup() {
	expiredRequests := make([]uint16, 0, 16)
	now := time.Now()
	v.Lock()
	for id, r := range v.requests {
		if r.expire.Before(now) {
			expiredRequests = append(expiredRequests, id)
			close(r.response)
		}
	}
	for _, id := range expiredRequests {
		delete(v.requests, id)
	}
	v.Unlock()
}

func (v *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
	var id uint16
	v.Lock()
	if len(v.requests) > CleanupThreshold && v.nextCleanup.Before(time.Now()) {
		v.nextCleanup = time.Now().Add(CleanupInterval)
		go v.Cleanup()
	}

	for {
		id = dice.RollUint16()
		if _, found := v.requests[id]; found {
			continue
		}
		log.Trace(newError("add pending request id ", id).AtDebug())
		v.requests[id] = &PendingRequest{
			expire:   time.Now().Add(time.Second * 8),
			response: response,
		}
		break
	}
	v.Unlock()
	return id
}

func (v *UDPNameServer) HandleResponse(payload *buf.Buffer) {
	msg := new(dns.Msg)
	err := msg.Unpack(payload.Bytes())
	if err != nil {
		log.Trace(newError("failed to parse DNS response").Base(err).AtWarning())
		return
	}
	record := &ARecord{
		IPs: make([]net.IP, 0, 16),
	}
	id := msg.Id
	ttl := uint32(3600) // an hour
	log.Trace(newError("handling response for id ", id, " content: ", msg).AtDebug())

	v.Lock()
	request, found := v.requests[id]
	if !found {
		v.Unlock()
		return
	}
	delete(v.requests, id)
	v.Unlock()

	for _, rr := range msg.Answer {
		switch rr := rr.(type) {
		case *dns.A:
			record.IPs = append(record.IPs, rr.A)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		case *dns.AAAA:
			record.IPs = append(record.IPs, rr.AAAA)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		}
	}
	record.Expire = time.Now().Add(time.Second * time.Duration(ttl))

	request.response <- record
	close(request.response)
}

func (v *UDPNameServer) BuildQueryA(domain string, id uint16) *buf.Buffer {

	msg := new(dns.Msg)
	msg.Id = id
	msg.RecursionDesired = true
	msg.Question = []dns.Question{
		{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeA,
			Qclass: dns.ClassINET,
		},
		{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeAAAA,
			Qclass: dns.ClassINET,
		}}

	buffer := buf.New()
	common.Must(buffer.Reset(func(b []byte) (int, error) {
		writtenBuffer, err := msg.PackBuffer(b)
		return len(writtenBuffer), err
	}))

	return buffer
}

func (v *UDPNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)
	id := v.AssignUnusedID(response)

	ctx, cancel := context.WithCancel(context.Background())
	v.udpServer.Dispatch(ctx, v.address, v.BuildQueryA(domain, id), v.HandleResponse)

	go func() {
		for i := 0; i < 2; i++ {
			time.Sleep(time.Second)
			v.Lock()
			_, found := v.requests[id]
			v.Unlock()
			if !found {
				break
			}
			v.udpServer.Dispatch(ctx, v.address, v.BuildQueryA(domain, id), v.HandleResponse)
		}
		cancel()
	}()

	return response
}

type LocalNameServer struct {
}

func (v *LocalNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)

	go func() {
		defer close(response)

		ips, err := net.LookupIP(domain)
		if err != nil {
			log.Trace(newError("failed to lookup IPs for domain ", domain).Base(err))
			return
		}

		response <- &ARecord{
			IPs:    ips,
			Expire: time.Now().Add(time.Hour),
		}
	}()

	return response
}
<|endoftext|>