<commit_before>package toolkits

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

	"github.com/bitrise-io/bitrise/models"
	"github.com/bitrise-io/go-utils/command"
	"github.com/stretchr/testify/require"
)

func Test_stepBinaryFilename(t *testing.T) {
	{
		sIDData := models.StepIDData{SteplibSource: "path", IDorURI: "./", Version: ""}
		require.Equal(t, "path-._-", stepBinaryFilename(sIDData))
	}

	{
		sIDData := models.StepIDData{SteplibSource: "git", IDorURI: "https://github.com/bitrise-steplib/steps-go-toolkit-hello-world.git", Version: "master"}
		require.Equal(t, "git-https___github.com_bitrise-steplib_steps-go-toolkit-hello-world.git-master", stepBinaryFilename(sIDData))
	}

	{
		sIDData := models.StepIDData{SteplibSource: "_", IDorURI: "https://github.com/bitrise-steplib/steps-go-toolkit-hello-world.git", Version: "master"}
		require.Equal(t, "_-https___github.com_bitrise-steplib_steps-go-toolkit-hello-world.git-master", stepBinaryFilename(sIDData))
	}

	{
		sIDData := models.StepIDData{SteplibSource: "https://github.com/bitrise-io/bitrise-steplib.git", IDorURI: "script", Version: "1.2.3"}
		require.Equal(t, "https___github.com_bitrise-io_bitrise-steplib.git-script-1.2.3", stepBinaryFilename(sIDData))
	}
}

func Test_parseGoVersionFromGoVersionOutput(t *testing.T) {
	t.Log("Example OK")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("go version go1.7 darwin/amd64")
		require.NoError(t, err)
		require.Equal(t, "1.7", verStr)
	}

	t.Log("Example OK 2")
	{
		verStr, err := parseGoVersionFromGoVersionOutput(`go version go1.7 darwin/amd64

`)
		require.NoError(t, err)
		require.Equal(t, "1.7", verStr)
	}

	t.Log("Example OK 3")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("go version go1.7.1 darwin/amd64")
		require.NoError(t, err)
		require.Equal(t, "1.7.1", verStr)
	}

	t.Log("Empty")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("")
		require.EqualError(t, err, "Failed to parse Go version, error: version call output was empty")
		require.Equal(t, "", verStr)
	}

	t.Log("Empty 2")
	{
		verStr, err := parseGoVersionFromGoVersionOutput(`

`)
		require.EqualError(t, err, "Failed to parse Go version, error: version call output was empty")
		require.Equal(t, "", verStr)
	}

	t.Log("Invalid")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("go version REMOVED darwin/amd64")
		require.EqualError(t, err, "Failed to parse Go version, error: failed to find version in input: go version REMOVED darwin/amd64")
		require.Equal(t, "", verStr)
	}
}

type mockRunner struct {
	outputs map[string]string
	cmds    []string
}

func (m *mockRunner) run(cmd *command.Model) error {
	m.cmds = append(m.cmds, cmd.PrintableCommandArgs())

	return nil
}

func (m *mockRunner) runForOutput(cmd *command.Model) (string, error) {
	m.cmds = append(m.cmds, cmd.PrintableCommandArgs())
	if val, ok := m.outputs[cmd.PrintableCommandArgs()]; ok {
		return val, nil
	}

	return "", nil
}

func Test_goBuildStep(t *testing.T) {
	type args struct {
		packageName   string
		outputBinPath string
	}
	tests := []struct {
		name        string
		isGoModStep bool
		args        args
		mockOutputs map[string]string
		wantCmds    []string
		wantGoMod   bool
	}{
		{
			name:        "Go module step -> Run in Go module mode",
			isGoModStep: true,
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			wantCmds: []string{
				`go "build" "-o" "/output"`,
			},
		},
		{
			name: "GOPATH step, GO111MODULES=on -> should migrate",
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			mockOutputs: map[string]string{
				`go "env" "-json" "GO111MODULE"`: `{"GO111MODULE": "on"}`,
			},
			wantCmds: []string{
				`go "env" "-json" "GO111MODULE"`,
				`go "build" "-mod=vendor" "-o" "/output"`,
			},
			wantGoMod: true,
		},
		{
			name: "GOPATH step, GO111MODULES='' -> should migrate",
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			mockOutputs: map[string]string{
				`go "env" "-json" "GO111MODULE"`: `{"GO111MODULE": ""}`,
			},
			wantCmds: []string{
				`go "env" "-json" "GO111MODULE"`,
				`go "build" "-mod=vendor" "-o" "/output"`,
			},
			wantGoMod: true,
		},
		{
			name: "GOPATH step, GO111MODULES=auto -> Run in GOPATH mode",
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			mockOutputs: map[string]string{
				`go "env" "-json" "GO111MODULE"`: `{"GO111MODULE": "auto"}`,
			},
			wantCmds: []string{
				`go "env" "-json" "GO111MODULE"`,
				`go "build" "-o" "/output" "github.com/bitrise-steplib/my-step"`,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			stepDir, err := ioutil.TempDir("", "")
			require.NoError(t, err, "failed to create temp dir")

			goModPath := filepath.Join(stepDir, "go.mod")
			if tt.isGoModStep {
				err := ioutil.WriteFile(goModPath, []byte{}, 0600)
				require.NoError(t, err, "failed to create file")
			}

			mockRunner := mockRunner{outputs: tt.mockOutputs}
			goConfig := GoConfigurationModel{
				GoBinaryPath: "go",
				GOROOT:       "/goroot",
			}

			err = goBuildStep(&mockRunner, goConfig, tt.args.packageName, stepDir, tt.args.outputBinPath)

			require.NoError(t, err, "goBuildStep()")
			require.Equal(t, tt.wantCmds, mockRunner.cmds, "goBuildStep() run commands do not match")

			if tt.wantGoMod {
				_, err := os.Stat(goModPath)
				require.NoError(t, err, "go.mod was not created")
			}
		})
	}
}
<commit_msg>Adding benchmark for Step build time (#759)<commit_after>package toolkits

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

	"github.com/bitrise-io/bitrise/models"
	"github.com/bitrise-io/go-utils/command"
	"github.com/stretchr/testify/require"
)

func Test_stepBinaryFilename(t *testing.T) {
	{
		sIDData := models.StepIDData{SteplibSource: "path", IDorURI: "./", Version: ""}
		require.Equal(t, "path-._-", stepBinaryFilename(sIDData))
	}

	{
		sIDData := models.StepIDData{SteplibSource: "git", IDorURI: "https://github.com/bitrise-steplib/steps-go-toolkit-hello-world.git", Version: "master"}
		require.Equal(t, "git-https___github.com_bitrise-steplib_steps-go-toolkit-hello-world.git-master", stepBinaryFilename(sIDData))
	}

	{
		sIDData := models.StepIDData{SteplibSource: "_", IDorURI: "https://github.com/bitrise-steplib/steps-go-toolkit-hello-world.git", Version: "master"}
		require.Equal(t, "_-https___github.com_bitrise-steplib_steps-go-toolkit-hello-world.git-master", stepBinaryFilename(sIDData))
	}

	{
		sIDData := models.StepIDData{SteplibSource: "https://github.com/bitrise-io/bitrise-steplib.git", IDorURI: "script", Version: "1.2.3"}
		require.Equal(t, "https___github.com_bitrise-io_bitrise-steplib.git-script-1.2.3", stepBinaryFilename(sIDData))
	}
}

func Test_parseGoVersionFromGoVersionOutput(t *testing.T) {
	t.Log("Example OK")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("go version go1.7 darwin/amd64")
		require.NoError(t, err)
		require.Equal(t, "1.7", verStr)
	}

	t.Log("Example OK 2")
	{
		verStr, err := parseGoVersionFromGoVersionOutput(`go version go1.7 darwin/amd64

`)
		require.NoError(t, err)
		require.Equal(t, "1.7", verStr)
	}

	t.Log("Example OK 3")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("go version go1.7.1 darwin/amd64")
		require.NoError(t, err)
		require.Equal(t, "1.7.1", verStr)
	}

	t.Log("Empty")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("")
		require.EqualError(t, err, "Failed to parse Go version, error: version call output was empty")
		require.Equal(t, "", verStr)
	}

	t.Log("Empty 2")
	{
		verStr, err := parseGoVersionFromGoVersionOutput(`

`)
		require.EqualError(t, err, "Failed to parse Go version, error: version call output was empty")
		require.Equal(t, "", verStr)
	}

	t.Log("Invalid")
	{
		verStr, err := parseGoVersionFromGoVersionOutput("go version REMOVED darwin/amd64")
		require.EqualError(t, err, "Failed to parse Go version, error: failed to find version in input: go version REMOVED darwin/amd64")
		require.Equal(t, "", verStr)
	}
}

type mockRunner struct {
	outputs map[string]string
	cmds    []string
}

func (m *mockRunner) run(cmd *command.Model) error {
	m.cmds = append(m.cmds, cmd.PrintableCommandArgs())

	return nil
}

func (m *mockRunner) runForOutput(cmd *command.Model) (string, error) {
	m.cmds = append(m.cmds, cmd.PrintableCommandArgs())
	if val, ok := m.outputs[cmd.PrintableCommandArgs()]; ok {
		return val, nil
	}

	return "", nil
}

func Test_goBuildStep(t *testing.T) {
	type args struct {
		packageName   string
		outputBinPath string
	}
	tests := []struct {
		name        string
		isGoModStep bool
		args        args
		mockOutputs map[string]string
		wantCmds    []string
		wantGoMod   bool
	}{
		{
			name:        "Go module step -> Run in Go module mode",
			isGoModStep: true,
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			wantCmds: []string{
				`go "build" "-o" "/output"`,
			},
		},
		{
			name: "GOPATH step, GO111MODULES=on -> should migrate",
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			mockOutputs: map[string]string{
				`go "env" "-json" "GO111MODULE"`: `{"GO111MODULE": "on"}`,
			},
			wantCmds: []string{
				`go "env" "-json" "GO111MODULE"`,
				`go "build" "-mod=vendor" "-o" "/output"`,
			},
			wantGoMod: true,
		},
		{
			name: "GOPATH step, GO111MODULES='' -> should migrate",
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			mockOutputs: map[string]string{
				`go "env" "-json" "GO111MODULE"`: `{"GO111MODULE": ""}`,
			},
			wantCmds: []string{
				`go "env" "-json" "GO111MODULE"`,
				`go "build" "-mod=vendor" "-o" "/output"`,
			},
			wantGoMod: true,
		},
		{
			name: "GOPATH step, GO111MODULES=auto -> Run in GOPATH mode",
			args: args{
				packageName:   "github.com/bitrise-steplib/my-step",
				outputBinPath: "/output",
			},
			mockOutputs: map[string]string{
				`go "env" "-json" "GO111MODULE"`: `{"GO111MODULE": "auto"}`,
			},
			wantCmds: []string{
				`go "env" "-json" "GO111MODULE"`,
				`go "build" "-o" "/output" "github.com/bitrise-steplib/my-step"`,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			stepDir, err := ioutil.TempDir("", "")
			require.NoError(t, err, "failed to create temp dir")

			goModPath := filepath.Join(stepDir, "go.mod")
			if tt.isGoModStep {
				err := ioutil.WriteFile(goModPath, []byte{}, 0600)
				require.NoError(t, err, "failed to create file")
			}

			mockRunner := mockRunner{outputs: tt.mockOutputs}
			goConfig := GoConfigurationModel{
				GoBinaryPath: "go",
				GOROOT:       "/goroot",
			}

			err = goBuildStep(&mockRunner, goConfig, tt.args.packageName, stepDir, tt.args.outputBinPath)

			require.NoError(t, err, "goBuildStep()")
			require.Equal(t, tt.wantCmds, mockRunner.cmds, "goBuildStep() run commands do not match")

			if tt.wantGoMod {
				_, err := os.Stat(goModPath)
				require.NoError(t, err, "go.mod was not created")
			}
		})
	}
}

func Benchmark_goBuildStep(b *testing.B) {
	isInstallRequired, _, goConfig, err := selectGoConfiguration()
	require.NoError(b, err, "Failed to select an appropriate Go installation for compiling the Step")

	if isInstallRequired {
		b.Fatalf("Failed to select an appropriate Go installation for compiling the Step")
	}

	stepDir, err := ioutil.TempDir("", "")
	require.NoError(b, err)

	defer func() {
		err := os.RemoveAll(stepDir)
		require.NoError(b, err)
	}()

	outputDir, err := ioutil.TempDir("", "")
	require.NoError(b, err)

	defer func() {
		err := os.RemoveAll(outputDir)
		require.NoError(b, err)
	}()

	stepmanCmd := command.New("stepman", "activate",
		"--collection", "https://github.com/bitrise-io/bitrise-steplib",
		"--id", "xcode-test",
		"--version", "2.5.0", "--update",
		"--path", stepDir)
	err = stepmanCmd.Run()
	require.NoError(b, err)

	packageName := "github.com/bitrise-steplib/steps-xcode-test"

	for _, mode := range []string{"on", "auto"} {
		b.Run(fmt.Sprintf("Benchmarking GO111MODULE=%s", mode), func(b *testing.B) {
			err := os.Setenv("GO111MODULE", mode)
			require.NoError(b, err)

			for i := 0; i < b.N; i++ {
				stepPerTestDir, err := ioutil.TempDir("", "")
				require.NoError(b, err)

				defer func() {
					err := os.RemoveAll(stepPerTestDir)
					require.NoError(b, err)
				}()

				err = command.CopyDir(stepDir, stepPerTestDir, true)
				require.NoError(b, err)

				err = goBuildStep(&defaultRunner{},
					goConfig,
					packageName,
					stepPerTestDir,
					filepath.Join(outputDir, fmt.Sprintf("%s_%d", mode, i)))
				require.NoError(b, err)
			}
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/alecthomas/kingpin"
	"github.com/ammario/mcping"
	"github.com/fatih/color"
	"time"
)

func main() {
	//Parse command line flags
	var (
		_host     = kingpin.Flag("host", "Hostname").Short('h').Required().String()
		_port     = kingpin.Flag("port", "Port number").Short('p').Default("25565").Int()
		_timeout  = kingpin.Flag("timeout", "Timeout per ping (ms)").Short('t').Default("300").Int()
		_interval = kingpin.Flag("interval", "Interval between pings (ms)").Short('i').Default("1000").Int()
		_count    = kingpin.Flag("count", "Total amount of pings to send").Short('c').Default("4294967295").Int()
	)

	kingpin.Parse()

	//Standardize input variable access

	host := *_host
	port := uint16(*_port)
	timeout := time.Duration(*_timeout)
	interval := time.Duration(*_interval)
	count := uint32(*_count)

	//Sequentiating ping responses
	var id uint32

	//Output colorer
	failOut := color.New(color.FgRed)

    //Change color based on whether player count changed
    stayOut := color.New(color.FgWhite)
    riseOut := color.New(color.FgGreen)
    dropOut := color.New(color.FgYellow)

    var lastPlayerCount int;

	//Output format strings
	failFmt := "(%x) %s; %s\n"
	successFmt := "(%x) %s; latency=%vms players=(%s)\n"

	fullAddr := fmt.Sprint(host, ":", port)

	for id = 1; id < count; id++ {
		//Have each request asynchronous
		go func(pid uint32) {
			err := errors.New("")
			timeoutChan := make(chan bool, 1)

			//Default response
			resp := mcping.PingResponse{}

			//Race timeout against ping
			go func() {
				time.Sleep(timeout * time.Millisecond)
				timeoutChan <- true
			}()
			go func() {
				resp, err = mcping.Ping(host, port)
				timeoutChan <- false
			}()
			if <-timeoutChan {
				err = mcping.TimeoutErr
			}
			if err != nil {
				failOut.Printf(failFmt, pid, fullAddr, err)
			} else {
				playerCount := fmt.Sprint(resp.Online, "/", resp.Max)
				latency := resp.Latency
                if resp.Online == lastPlayerCount {
                    stayOut.Printf(successFmt, pid, fullAddr, latency, playerCount)
                } else if resp.Online > lastPlayerCount {
                    riseOut.Printf(successFmt, pid, fullAddr, latency, playerCount)
                } else if resp.Online < lastPlayerCount {
                    dropOut.Printf(successFmt, pid, fullAddr, latency, playerCount)
                }
                lastPlayerCount = resp.Online
			}
		}(id)
		time.Sleep(interval * time.Millisecond)
	}
}
<commit_msg>go fmt<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/alecthomas/kingpin"
	"github.com/ammario/mcping"
	"github.com/fatih/color"
	"time"
)

func main() {
	//Parse command line flags
	var (
		_host     = kingpin.Arg("host", "Hostname").Required().String()
		_port     = kingpin.Flag("port", "Port number").Short('p').Default("25565").Int()
		_timeout  = kingpin.Flag("timeout", "Timeout per ping (ms)").Short('t').Default("300").Int()
		_interval = kingpin.Flag("interval", "Interval between pings (ms)").Short('i').Default("1000").Int()
		_count    = kingpin.Flag("count", "Total amount of pings to send").Short('c').Default("4294967295").Int()
	)

	kingpin.Parse()

	//Standardize input variable access

	host := *_host
	port := uint16(*_port)
	timeout := time.Duration(*_timeout)
	interval := time.Duration(*_interval)
	count := uint32(*_count)

	//Sequentiating ping responses
	var id uint32

	//Output colorer
	failOut := color.New(color.FgRed)

	//Change color based on whether player count changed
	stayOut := color.New(color.FgWhite)
	riseOut := color.New(color.FgGreen)
	dropOut := color.New(color.FgYellow)

	var lastPlayerCount int

	//Output format strings
	failFmt := "(%x) %s; %s\n"
	successFmt := "(%x) %s; latency=%vms players=(%s)\n"

	fullAddr := fmt.Sprint(host, ":", port)

	for id = 1; id < count; id++ {
		//Have each request asynchronous
		go func(pid uint32) {
			err := errors.New("")
			timeoutChan := make(chan bool, 1)

			//Default response
			resp := mcping.PingResponse{}

			//Race timeout against ping
			go func() {
				time.Sleep(timeout * time.Millisecond)
				timeoutChan <- true
			}()
			go func() {
				resp, err = mcping.Ping(host, port)
				timeoutChan <- false
			}()
			if <-timeoutChan {
				err = mcping.TimeoutErr
			}
			if err != nil {
				failOut.Printf(failFmt, pid, fullAddr, err)
			} else {
				playerCount := fmt.Sprint(resp.Online, "/", resp.Max)
				latency := resp.Latency
				if resp.Online == lastPlayerCount {
					stayOut.Printf(successFmt, pid, fullAddr, latency, playerCount)
				} else if resp.Online > lastPlayerCount {
					riseOut.Printf(successFmt, pid, fullAddr, latency, playerCount)
				} else if resp.Online < lastPlayerCount {
					dropOut.Printf(successFmt, pid, fullAddr, latency, playerCount)
				}
				lastPlayerCount = resp.Online
			}
		}(id)
		time.Sleep(interval * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>// Package spigot implements Spigot algorithm for Pi and E
package spigot

import "fmt"

/*
   1. Initialize: Let A = (2, 2, 2, 2,... ,2) be an array of length
      [10n/3]+1.

   2. Repeat n times:

      Multiply by 10: Multiply each entry of A by 10.

      Put A into regular form: Starting from the right, reduce the
      ith element of A (corresponding to b-entry (i - 1)/(2i - 1))
      modulo 2i - 1, to get a quotient q and a remainder r. Leave
      r in place and carry q(i - 1) one place left. The last integer
      carried (from the position where i - 1 = 2) may be as large
      as 19.

   3. Get the next predigit: Reduce the leftmost entry of A (which
      is at most 109 (= 9 - 10 + 191)) modulo 10. The quotient, q,
      is the new predigit of π, the remainder staying in place.

   4. Adjust the predigits: If q is neither 9 nor 10, release the
      held predigits as true digits of π and hold q. If q is 9, add
      q to the queue of held predigits. If q is 10 then:

      - set the current predigit to 0 and hold it;
      - increase all other held predigits by 1(9 becomes 0);
      - release as true digits of π all but the current held predigit.

*/

// alloc allocates initial slice of size n
func alloc(n, v int) []int {
	a := make([]int, n)
	for i := range a {
		a[i] = v
	}
	return a
}

// Pi returns n digits of Pi
func Pi(n int) <-chan int {
	c := make(chan int)
	go func(n int) {
		a := alloc(10*n/3+1, 2)
		for k := 0; k < n; k++ {
			a[len(a)-1] *= 10
			for i := len(a) - 1; i > 0; i-- {
				q := 2*i + 1
				a[i-1] *= 10
				a[i-1] += i * (a[i] / q)
				a[i] %= q
			}
			c <- a[0] / 10
			a[0] %= 10
		}
		close(c)
	}(n + 1)
	return predigit(c)
}

func predigit(in <-chan int) <-chan int {
	c := make(chan int)
	go func() {
		var pre []int
		for i := range in {
			switch i {
			case 9:
				pre = append(pre, i)
			case 10:
				for _, v := range pre {
					c <- (v + 1) % 10
				}
				pre = []int{0}
			default:
				for _, v := range pre {
					c <- v
				}
				pre = []int{i}
			}
		}
		for _, v := range pre {
			c <- v
		}
		close(c)
	}()
	return c
}

/*
   1. Initialize: Let the first digit be 2 and initialize an array
      A of length n + 1 to (1, 1, 1, . . . , 1).

   2. Repeat n − 1 times:

      Multiply by 10: Multiply each entry of A by 10.

      Take the fractional part: Starting from the right, reduce the
      ith entry of A modulo i + 1, carrying the quotient one place
      left.

      Output the next digit: The final quotient is the next digit of e.
*/

// E returns n digits of E
func E(n int) <-chan int {
	c := make(chan int)
	go func(n int) {
		a := alloc(n+1, 1)
		a[0] = 2
		for k := 0; k < n; k++ {
			a[len(a)-1] *= 10
			for i := len(a) - 1; i > 0; i-- {
				q := i + 1
				a[i-1] *= 10
				a[i-1] += a[i] / q
				a[i] %= q
			}
			c <- a[0] / 10
			a[0] %= 10
		}
		close(c)
	}(n + 1)
	return c
}

// Print digits from channel
func Print(n <-chan int) {
	for v := range n {
		fmt.Print(v)
	}
	fmt.Println("")
}

// Drain values from channel
func Drain(c <-chan int) {
	for range c {
	}
}
<commit_msg>Unify<commit_after>// Package spigot implements Spigot algorithm for Pi and E
package spigot

import "fmt"

/*
   1. Initialize: Let A = (2, 2, 2, 2,... ,2) be an array of length
      [10n/3]+1.

   2. Repeat n times:

      Multiply by 10: Multiply each entry of A by 10.

      Put A into regular form: Starting from the right, reduce the
      ith element of A (corresponding to b-entry (i - 1)/(2i - 1))
      modulo 2i - 1, to get a quotient q and a remainder r. Leave
      r in place and carry q(i - 1) one place left. The last integer
      carried (from the position where i - 1 = 2) may be as large
      as 19.

   3. Get the next predigit: Reduce the leftmost entry of A (which
      is at most 109 (= 9 - 10 + 191)) modulo 10. The quotient, q,
      is the new predigit of π, the remainder staying in place.

   4. Adjust the predigits: If q is neither 9 nor 10, release the
      held predigits as true digits of π and hold q. If q is 9, add
      q to the queue of held predigits. If q is 10 then:

      - set the current predigit to 0 and hold it;
      - increase all other held predigits by 1(9 becomes 0);
      - release as true digits of π all but the current held predigit.

*/

// alloc allocates initial slice of size n
func alloc(n, v int) []int {
	a := make([]int, n)
	for i := range a {
		a[i] = v
	}
	return a
}

// Pi returns n digits of Pi
func Pi(n int) <-chan int {
	c := make(chan int)
	go func(n int) {
		a := alloc(10*n/3+1, 2)
		for k := 0; k < n; k++ {
			a[len(a)-1] *= 10
			for i := len(a) - 1; i > 0; i-- {
				q := 2*i + 1
				a[i-1] *= 10
				a[i-1] += i * (a[i] / q)
				a[i] %= q
			}
			c <- a[0] / 10
			a[0] %= 10
		}
		close(c)
	}(n + 1)
	return predigit(c)
}

func predigit(in <-chan int) <-chan int {
	c := make(chan int)
	go func() {
		var pre []int
		for i := range in {
			switch i {
			case 9:
				pre = append(pre, i)
			case 10:
				for _, v := range pre {
					c <- (v + 1) % 10
				}
				pre = []int{i % 10}
			default:
				for _, v := range pre {
					c <- v
				}
				pre = []int{i}
			}
		}
		for _, v := range pre {
			c <- v
		}
		close(c)
	}()
	return c
}

/*
   1. Initialize: Let the first digit be 2 and initialize an array
      A of length n + 1 to (1, 1, 1, . . . , 1).

   2. Repeat n − 1 times:

      Multiply by 10: Multiply each entry of A by 10.

      Take the fractional part: Starting from the right, reduce the
      ith entry of A modulo i + 1, carrying the quotient one place
      left.

      Output the next digit: The final quotient is the next digit of e.
*/

// E returns n digits of E
func E(n int) <-chan int {
	c := make(chan int)
	go func(n int) {
		a := alloc(n+1, 1)
		a[0] = 2
		for k := 0; k < n; k++ {
			a[len(a)-1] *= 10
			for i := len(a) - 1; i > 0; i-- {
				q := i + 1
				a[i-1] *= 10
				a[i-1] += a[i] / q
				a[i] %= q
			}
			c <- a[0] / 10
			a[0] %= 10
		}
		close(c)
	}(n + 1)
	return c
}

// Print digits from channel
func Print(n <-chan int) {
	for v := range n {
		fmt.Print(v)
	}
	fmt.Println("")
}

// Drain values from channel
func Drain(c <-chan int) {
	for range c {
	}
}
<|endoftext|>


<commit_before>package main

import (
	"debug/dwarf"
	"debug/elf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
)

const attrMIPSLinkageName dwarf.Attr = 0x2007

type rangeSizeMap map[dwarf.Offset]uint64

// Go's debug/dwarf package doesn't include .debug_ranges parsing support.
func parseDebugRangesFromELF(file *elf.File) (rangeSizeMap, error) {
	log.Print("parsing .debug_ranges...")
	sectionReader := file.Section(".debug_ranges").Open()

	var byteOrder binary.ByteOrder
	switch file.Data {
	case elf.ELFDATA2LSB:
		byteOrder = binary.LittleEndian
	case elf.ELFDATA2MSB:
		byteOrder = binary.BigEndian
	default:
		return nil, fmt.Errorf("%v has an unknown byte order", file)
	}

	var bytesPerAddress uint8
	switch file.Class {
	case elf.ELFCLASS32:
		bytesPerAddress = 4
	case elf.ELFCLASS64:
		bytesPerAddress = 8
	default:
		return nil, fmt.Errorf("%v has unknown class value", file)
	}

	// The .debug_ranges format is pretty simple. A DIE may use DW_AT_ranges to refer to a
	// range in the .debug_ranges section, which represents a range of non-contiguous
	// addresses. Each entry in the range is a either a range list entry, a base address
	// selection entry, or an end of list entry.
	// - A range list entry consists of a beginning address offset and an ending address
	//   offset. The beginning address offset may be 0x0, and the length of the range may be
	//   0, if the beginning and ending address offsets are equal. Range list entries may
	//   not overlap.
	// - A base address selection entry, which consists of the largest representable
	//   address, e.g. 0xffffffff for 32-bit addresses, and an address that defines the base
	//   address of subsequent entries.
	// - An end of list entry is a range list entry that has a beginning and ending address
	//   offset of 0.
	var currentOffset, pendingOffset dwarf.Offset
	rangeSizes := make(rangeSizeMap)
	buffer := make([]byte, 2*bytesPerAddress)
	for {
		n, err := sectionReader.Read(buffer)
		if n == 0 && err == io.EOF {
			return rangeSizes, nil
		} else if n != len(buffer) {
			return nil, fmt.Errorf("read strange number of bytes: %d", n)
		} else if err != nil {
			return nil, err
		}
		pendingOffset += dwarf.Offset(n)
		var begin, end uint64
		switch file.Class {
		case elf.ELFCLASS32:
			begin = uint64(byteOrder.Uint32(buffer))
			end = uint64(byteOrder.Uint64(buffer[4:]))
		case elf.ELFCLASS64:
			begin = byteOrder.Uint64(buffer)
			end = byteOrder.Uint64(buffer[8:])
		}
		if begin == 0 && end == 0 {
			currentOffset = pendingOffset
			continue
		}
		bytes := end - begin
		if bytes < 0 {
			return nil, fmt.Errorf("got invalid range %v", buffer)
		}
		rangeSizes[currentOffset] += bytes
	}
}

type subprogramMap map[dwarf.Offset]*dwarf.Entry

// Attempts to extract a function name from the DIE at the provided offset. Unfortunately, since
// it's C++ and DWARF, it's not just a simple matter of getting name attribute and returning it.
func nameForEntry(subprograms subprogramMap, offset dwarf.Offset) (string, error) {
	subprogram, ok := subprograms[offset]
	if !ok {
		return "", errors.New("couldn't find subprogram")
	}

	// TODO(dcheng): Ideally, demangle this name.
	if name, ok := subprogram.Val(attrMIPSLinkageName).(string); ok {
		return name, nil
	}

	// Some DIEs chain to another DIE with DW_AT_specification.
	// TODO(dcheng): Document when this happens.
	if specOffset, ok := subprogram.Val(dwarf.AttrSpecification).(dwarf.Offset); ok {
		return nameForEntry(subprograms, specOffset)
	}

	// Otherwise, fall back to DW_AT_name. This is probably a plain C function.
	if name, ok := subprogram.Val(dwarf.AttrName).(string); ok {
		return name, nil
	}

	return "", fmt.Errorf("%v missing name, linkage name, and spec", subprogram)
}

func bytesForEntry(rangeSizes rangeSizeMap, entry *dwarf.Entry) (uint64, error) {
	// Per the DWARF spec, a DIE with associated machine code may have:
	// - A DW_AT_low_pc attribute for a snigle address (not handled)
	// - A DW_AT_low_pc and DW_AT_high_pc attribute for a single contiguous range of
	//   addresses, or
	// - A DW_AT_ranges attribute for a non-contiguous range of addresses.

	// The spec notes that DW_AT_high_pc may be either of class address or class constant.
	// In the latter case, DW_AT_high_pc is an offset from DW_AT_low_pc which gives the
	// first instruction past the last instruction associated with the DIE. This code
	// assumes the latter, since that's what Clang emits and it makes the code simpler.
	if bytes, ok := entry.Val(dwarf.AttrHighpc).(int64); ok {
		if bytes < 0 {
			return 0, fmt.Errorf("%v has negative size %d", entry, bytes)
		}
		return uint64(bytes), nil
	}

	rangeOffset, ok := entry.Val(dwarf.AttrRanges).(dwarf.Offset)
	if !ok {
		return 0, fmt.Errorf("%v has no valid high pc or range", entry)
	}
	bytes, ok := rangeSizes[rangeOffset]
	if !ok {
		return 0, fmt.Errorf("couldn't find range entry for %v", entry)
	}
	return bytes, nil
}

type inlineStats struct {
	count uint64 // Number of times the function was inlined.
	bytes uint64 // Total bytes inlined for the function.
}

func analyze(file *elf.File) (map[string]*inlineStats, error) {
	rangeSizes, err := parseDebugRangesFromELF(file)
	if err != nil {
		return nil, err
	}

	// Strictly speaking, dwarf.Data should have other debug sections too, but in practice,
	// only .debug_info is exposed.
	debugInfo, err := file.DWARF()
	if err != nil {
		return nil, err
	}

	// DIEs may refer to a DIE with a greater offset, so collect all interesting DIEs first.
	infoReader := debugInfo.Reader()
	subprograms := make(subprogramMap)
	inlinedSubroutines := make([]*dwarf.Entry, 0, 1024)
	for i := 0; ; i++ {
		if i%1000000 == 0 {
			log.Printf("read %d DIEs...", i)
		}
		entry, err := infoReader.Next()
		if err != nil {
			return nil, err
		}
		if entry == nil {
			break
		}
		switch entry.Tag {
		case dwarf.TagSubprogram:
			// TODO(dcheng): Consider storing a reduced set of information, since
			// there are a lot of subprogram DIEs in Chrome.
			subprograms[entry.Offset] = entry
		case dwarf.TagInlinedSubroutine:
			inlinedSubroutines = append(inlinedSubroutines, entry)
		}
	}

	log.Printf("got %d inlined subroutines", len(inlinedSubroutines))
	results := make(map[string]*inlineStats)
	for _, entry := range inlinedSubroutines {
		subprogramOffset, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
		if !ok {
			log.Printf("error: %v missing abstract origin", entry)
			continue
		}
		name, err := nameForEntry(subprograms, subprogramOffset)
		if err != nil {
			log.Printf("error: couldn't extract name for %v: %v", entry, err)
		}

		bytes, err := bytesForEntry(rangeSizes, entry)

		info, ok := results[name]
		if !ok {
			info = &inlineStats{}
			results[name] = info
		}
		info.count++
		info.bytes += uint64(bytes)
	}
	return results, nil
}

type resultSorter struct {
	// TOOO(dcheng): Bad names are bad.
	keys    []string
	results map[string]*inlineStats
}

func (s *resultSorter) Len() int {
	return len(s.keys)
}

func (s *resultSorter) Swap(i, j int) {
	s.keys[i], s.keys[j] = s.keys[j], s.keys[i]
}

func (s *resultSorter) Less(i, j int) bool {
	return s.results[s.keys[i]].bytes > s.results[s.keys[j]].bytes
}

func sortAndPrintTop100(results map[string]*inlineStats) {
	keys := make([]string, 0, len(results))
	for k := range results {
		keys = append(keys, k)
	}
	sort.Sort(&resultSorter{keys, results})
	fmt.Printf("     Count      Bytes   Name\n")
	fmt.Printf("  --------  ---------   ---------------------------------\n")
	for i, k := range keys {
		if i > 100 {
			break
		}
		fmt.Printf("%10d %10d   %s\n", results[k].count, results[k].bytes, k)
	}
}

func main() {
	for _, f := range os.Args[1:] {
		log.Printf("analyzing %s...", f)
		file, err := elf.Open(f)
		if err != nil {
			log.Printf("error: couldn't open %s: %v", f, err)
			continue
		}
		results, err := analyze(file)
		if err != nil {
			log.Printf("error: couldn't analyze debug data for %s: %v", f, err)
			continue
		}
		sortAndPrintTop100(results)
	}
}
<commit_msg>Handle base selection address entries in .debug_ranges.<commit_after>package main

import (
	"debug/dwarf"
	"debug/elf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"math"
	"os"
	"sort"
)

const attrMIPSLinkageName dwarf.Attr = 0x2007

type rangeSizeMap map[dwarf.Offset]uint64

// Go's debug/dwarf package doesn't include .debug_ranges parsing support.
func parseDebugRangesFromELF(file *elf.File) (rangeSizeMap, error) {
	log.Print("parsing .debug_ranges...")
	sectionReader := file.Section(".debug_ranges").Open()

	var byteOrder binary.ByteOrder
	switch file.Data {
	case elf.ELFDATA2LSB:
		byteOrder = binary.LittleEndian
	case elf.ELFDATA2MSB:
		byteOrder = binary.BigEndian
	default:
		return nil, fmt.Errorf("%v has an unknown byte order", file)
	}

	var bytesPerAddress uint8
	switch file.Class {
	case elf.ELFCLASS32:
		bytesPerAddress = 4
	case elf.ELFCLASS64:
		bytesPerAddress = 8
	default:
		return nil, fmt.Errorf("%v has unknown class value", file)
	}

	// The .debug_ranges format is pretty simple. A DIE may use DW_AT_ranges to refer to a
	// range in the .debug_ranges section, which represents a range of non-contiguous
	// addresses. Each entry in the range is a either a range list entry, a base address
	// selection entry, or an end of list entry.
	// - A range list entry consists of a beginning address offset and an ending address
	//   offset. The beginning address offset may be 0x0, and the length of the range may be
	//   0, if the beginning and ending address offsets are equal. Range list entries may
	//   not overlap.
	// - A base address selection entry, which consists of the largest representable
	//   address, e.g. 0xffffffff for 32-bit addresses, and an address that defines the base
	//   address of subsequent entries.
	// - An end of list entry is a range list entry that has a beginning and ending address
	//   offset of 0.
	var currentOffset, pendingOffset dwarf.Offset
	rangeSizes := make(rangeSizeMap)
	buffer := make([]byte, 2*bytesPerAddress)
	for {
		n, err := sectionReader.Read(buffer)
		if n == 0 && err == io.EOF {
			return rangeSizes, nil
		} else if n != len(buffer) {
			return nil, fmt.Errorf("read strange number of bytes: %d", n)
		} else if err != nil {
			return nil, err
		}
		pendingOffset += dwarf.Offset(n)
		var begin, end uint64
		switch file.Class {
		case elf.ELFCLASS32:
			begin = uint64(byteOrder.Uint32(buffer))
			end = uint64(byteOrder.Uint32(buffer[4:]))
			if begin == math.MaxUint32 {
				continue
			}
		case elf.ELFCLASS64:
			begin = byteOrder.Uint64(buffer)
			end = byteOrder.Uint64(buffer[8:])
			if begin == math.MaxUint64 {
				continue
			}
		}
		if begin == 0 && end == 0 {
			currentOffset = pendingOffset
			continue
		}
		bytes := end - begin
		if bytes < 0 {
			return nil, fmt.Errorf("got invalid range %v", buffer)
		}
		rangeSizes[currentOffset] += bytes
	}
}

type subprogramMap map[dwarf.Offset]*dwarf.Entry

// Attempts to extract a function name from the DIE at the provided offset. Unfortunately, since
// it's C++ and DWARF, it's not just a simple matter of getting name attribute and returning it.
func nameForEntry(subprograms subprogramMap, offset dwarf.Offset) (string, error) {
	subprogram, ok := subprograms[offset]
	if !ok {
		return "", errors.New("couldn't find subprogram")
	}

	// TODO(dcheng): Ideally, demangle this name.
	if name, ok := subprogram.Val(attrMIPSLinkageName).(string); ok {
		return name, nil
	}

	// Some DIEs chain to another DIE with DW_AT_specification.
	// TODO(dcheng): Document when this happens.
	if specOffset, ok := subprogram.Val(dwarf.AttrSpecification).(dwarf.Offset); ok {
		return nameForEntry(subprograms, specOffset)
	}

	// Otherwise, fall back to DW_AT_name. This is probably a plain C function.
	if name, ok := subprogram.Val(dwarf.AttrName).(string); ok {
		return name, nil
	}

	return "", fmt.Errorf("%v missing name, linkage name, and spec", subprogram)
}

func bytesForEntry(rangeSizes rangeSizeMap, entry *dwarf.Entry) (uint64, error) {
	// Per the DWARF spec, a DIE with associated machine code may have:
	// - A DW_AT_low_pc attribute for a snigle address (not handled)
	// - A DW_AT_low_pc and DW_AT_high_pc attribute for a single contiguous range of
	//   addresses, or
	// - A DW_AT_ranges attribute for a non-contiguous range of addresses.

	// The spec notes that DW_AT_high_pc may be either of class address or class constant.
	// In the latter case, DW_AT_high_pc is an offset from DW_AT_low_pc which gives the
	// first instruction past the last instruction associated with the DIE. This code
	// assumes the latter, since that's what Clang emits and it makes the code simpler.
	if bytes, ok := entry.Val(dwarf.AttrHighpc).(int64); ok {
		if bytes < 0 {
			return 0, fmt.Errorf("%v has negative size %d", entry, bytes)
		}
		return uint64(bytes), nil
	}

	rangeOffset, ok := entry.Val(dwarf.AttrRanges).(dwarf.Offset)
	if !ok {
		return 0, fmt.Errorf("%v has no valid high pc or range", entry)
	}
	bytes, ok := rangeSizes[rangeOffset]
	if !ok {
		return 0, fmt.Errorf("couldn't find range entry for %v", entry)
	}
	return bytes, nil
}

type inlineStats struct {
	count uint64 // Number of times the function was inlined.
	bytes uint64 // Total bytes inlined for the function.
}

func analyze(file *elf.File) (map[string]*inlineStats, error) {
	rangeSizes, err := parseDebugRangesFromELF(file)
	if err != nil {
		return nil, err
	}

	// Strictly speaking, dwarf.Data should have other debug sections too, but in practice,
	// only .debug_info is exposed.
	debugInfo, err := file.DWARF()
	if err != nil {
		return nil, err
	}

	// DIEs may refer to a DIE with a greater offset, so collect all interesting DIEs first.
	infoReader := debugInfo.Reader()
	subprograms := make(subprogramMap)
	inlinedSubroutines := make([]*dwarf.Entry, 0, 1024)
	for i := 0; ; i++ {
		if i%1000000 == 0 {
			log.Printf("read %d DIEs...", i)
		}
		entry, err := infoReader.Next()
		if err != nil {
			return nil, err
		}
		if entry == nil {
			break
		}
		switch entry.Tag {
		case dwarf.TagSubprogram:
			// TODO(dcheng): Consider storing a reduced set of information, since
			// there are a lot of subprogram DIEs in Chrome.
			subprograms[entry.Offset] = entry
		case dwarf.TagInlinedSubroutine:
			inlinedSubroutines = append(inlinedSubroutines, entry)
		}
	}

	log.Printf("got %d inlined subroutines", len(inlinedSubroutines))
	results := make(map[string]*inlineStats)
	for _, entry := range inlinedSubroutines {
		subprogramOffset, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
		if !ok {
			log.Printf("error: %v missing abstract origin", entry)
			continue
		}
		name, err := nameForEntry(subprograms, subprogramOffset)
		if err != nil {
			log.Printf("error: couldn't extract name for %v: %v", entry, err)
		}

		bytes, err := bytesForEntry(rangeSizes, entry)

		info, ok := results[name]
		if !ok {
			info = &inlineStats{}
			results[name] = info
		}
		info.count++
		info.bytes += uint64(bytes)
	}
	return results, nil
}

type resultSorter struct {
	// TOOO(dcheng): Bad names are bad.
	keys    []string
	results map[string]*inlineStats
}

func (s *resultSorter) Len() int {
	return len(s.keys)
}

func (s *resultSorter) Swap(i, j int) {
	s.keys[i], s.keys[j] = s.keys[j], s.keys[i]
}

func (s *resultSorter) Less(i, j int) bool {
	return s.results[s.keys[i]].bytes > s.results[s.keys[j]].bytes
}

func sortAndPrintTop100(results map[string]*inlineStats) {
	keys := make([]string, 0, len(results))
	for k := range results {
		keys = append(keys, k)
	}
	sort.Sort(&resultSorter{keys, results})
	fmt.Printf("     Count      Bytes   Name\n")
	fmt.Printf("  --------  ---------   ---------------------------------\n")
	for i, k := range keys {
		if i > 100 {
			break
		}
		fmt.Printf("%10d %10d   %s\n", results[k].count, results[k].bytes, k)
	}
}

func main() {
	for _, f := range os.Args[1:] {
		log.Printf("analyzing %s...", f)
		file, err := elf.Open(f)
		if err != nil {
			log.Printf("error: couldn't open %s: %v", f, err)
			continue
		}
		results, err := analyze(file)
		if err != nil {
			log.Printf("error: couldn't analyze debug data for %s: %v", f, err)
			continue
		}
		sortAndPrintTop100(results)
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

// Package imageproxy provides an image proxy server.  For typical use of
// creating and using a Proxy, see cmd/imageproxy/main.go.
package imageproxy // import "willnorris.com/go/imageproxy"

import (
	"bufio"
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/gregjones/httpcache"
)

// Proxy serves image requests.
//
// Note that a Proxy should not be run behind a http.ServeMux, since the
// ServeMux aggressively cleans URLs and removes the double slash in the
// embedded request URL.
type Proxy struct {
	Client *http.Client // client used to fetch remote URLs
	Cache  Cache        // cache used to cache responses

	// Whitelist specifies a list of remote hosts that images can be
	// proxied from.  An empty list means all hosts are allowed.
	Whitelist []string

	// Referrers, when given, requires that requests to the image
	// proxy come from a referring host. An empty list means all
	// hosts are allowed.
	Referrers []string

	// DefaultBaseURL is the URL that relative remote URLs are resolved in
	// reference to.  If nil, all remote URLs specified in requests must be
	// absolute.
	DefaultBaseURL *url.URL

	// SignatureKey is the HMAC key used to verify signed requests.
	SignatureKey []byte
}

// NewProxy constructs a new proxy.  The provided http RoundTripper will be
// used to fetch remote URLs.  If nil is provided, http.DefaultTransport will
// be used.
func NewProxy(transport http.RoundTripper, cache Cache) *Proxy {
	if transport == nil {
		transport = http.DefaultTransport
	}
	if cache == nil {
		cache = NopCache
	}

	client := new(http.Client)
	client.Transport = &httpcache.Transport{
		Transport:           &TransformingTransport{transport, client},
		Cache:               cache,
		MarkCachedResponses: true,
	}

	return &Proxy{
		Client: client,
		Cache:  cache,
	}
}

// ServeHTTP handles image requests.
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		return // ignore favicon requests
	}

	req, err := NewRequest(r, p.DefaultBaseURL)
	if err != nil {
		msg := fmt.Sprintf("invalid request URL: %v", err)
		glog.Error(msg)
		http.Error(w, msg, http.StatusBadRequest)
		return
	}

	if !p.allowed(req) {
		msg := fmt.Sprintf("request does not contain an allowed host or valid signature")
		glog.Error(msg)
		http.Error(w, msg, http.StatusForbidden)
		return
	}

	resp, err := p.Client.Get(req.String())

	// Allow only jpg, png, gif, bmp
	contentType := resp.Header.Get("Content-Type")
	if contentType != "image/jpeg" &&
	   contentType != "image/png" &&
	   contentType != "image/bmp" &&
	   contentType != "image/gif" {
	   	http.Error(w, "resource is not a valid image", http.StatusForbidden)
		return
	}

	if err != nil {
		msg := fmt.Sprintf("error fetching remote image: %v", err)
		glog.Error(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	cached := resp.Header.Get(httpcache.XFromCache)
	glog.Infof("request: %v (served from cache: %v)", *req, cached == "1")

	copyHeader(w, resp, "Last-Modified")
	copyHeader(w, resp, "Expires")
	copyHeader(w, resp, "Etag")

	if is304 := check304(r, resp); is304 {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	copyHeader(w, resp, "Content-Length")
	copyHeader(w, resp, "Content-Type")
	w.WriteHeader(resp.StatusCode)
	io.Copy(w, resp.Body)
}

func copyHeader(w http.ResponseWriter, r *http.Response, header string) {
	key := http.CanonicalHeaderKey(header)
	if value, ok := r.Header[key]; ok {
		w.Header()[key] = value
	}
}

// allowed returns whether the specified request is allowed because it matches
// a host in the proxy whitelist or it has a valid signature.
func (p *Proxy) allowed(r *Request) bool {
	if len(p.Referrers) > 0 && !validReferrer(p.Referrers, r.Original) {
		glog.Infof("request not coming from allowed referrer: %v", r)
		return false
	}

	if len(p.Whitelist) == 0 && len(p.SignatureKey) == 0 {
		return true // no whitelist or signature key, all requests accepted
	}

	if len(p.Whitelist) > 0 {
		if validHost(p.Whitelist, r.URL) {
			return true
		}
		glog.Infof("request is not for an allowed host: %v", r)
	}

	if len(p.SignatureKey) > 0 {
		if validSignature(p.SignatureKey, r) {
			return true
		}
		glog.Infof("request contains invalid signature: %v", r)
	}

	return false
}

// validHost returns whether the host in u matches one of hosts.
func validHost(hosts []string, u *url.URL) bool {
	for _, host := range hosts {
		if u.Host == host {
			return true
		}
		if strings.HasPrefix(host, "*.") && strings.HasSuffix(u.Host, host[2:]) {
			return true
		}
	}

	return false
}

// returns whether the referrer from the request is in the host list.
func validReferrer(hosts []string, r *http.Request) bool {
	parsed, err := url.Parse(r.Header.Get("Referer"))
	if err != nil { // malformed or blank header, just deny
		return false
	}

	return validHost(hosts, parsed)
}

// validSignature returns whether the request signature is valid.
func validSignature(key []byte, r *Request) bool {
	sig := r.Options.Signature
	if m := len(sig) % 4; m != 0 { // add padding if missing
		sig += strings.Repeat("=", 4-m)
	}

	got, err := base64.URLEncoding.DecodeString(sig)
	if err != nil {
		glog.Errorf("error base64 decoding signature %q", r.Options.Signature)
		return false
	}

	mac := hmac.New(sha256.New, key)
	mac.Write([]byte(r.URL.String()))
	want := mac.Sum(nil)

	return hmac.Equal(got, want)
}

// check304 checks whether we should send a 304 Not Modified in response to
// req, based on the response resp.  This is determined using the last modified
// time and the entity tag of resp.
func check304(req *http.Request, resp *http.Response) bool {
	// TODO(willnorris): if-none-match header can be a comma separated list
	// of multiple tags to be matched, or the special value "*" which
	// matches all etags
	etag := resp.Header.Get("Etag")
	if etag != "" && etag == req.Header.Get("If-None-Match") {
		return true
	}

	lastModified, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
	if err != nil {
		return false
	}
	ifModSince, err := time.Parse(time.RFC1123, req.Header.Get("If-Modified-Since"))
	if err != nil {
		return false
	}
	if lastModified.Before(ifModSince) {
		return true
	}

	return false
}

// TransformingTransport is an implementation of http.RoundTripper that
// optionally transforms images using the options specified in the request URL
// fragment.
type TransformingTransport struct {
	// Transport is the underlying http.RoundTripper used to satisfy
	// non-transform requests (those that do not include a URL fragment).
	Transport http.RoundTripper

	// CachingClient is used to fetch images to be resized.  This client is
	// used rather than Transport directly in order to ensure that
	// responses are properly cached.
	CachingClient *http.Client
}

// RoundTrip implements the http.RoundTripper interface.
func (t *TransformingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.URL.Fragment == "" {
		// normal requests pass through
		glog.Infof("fetching remote URL: %v", req.URL)
		return t.Transport.RoundTrip(req)
	}

	u := *req.URL
	u.Fragment = ""
	resp, err := t.CachingClient.Get(u.String())
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	opt := ParseOptions(req.URL.Fragment)
	img, err := Transform(b, opt)
	if err != nil {
		glog.Errorf("error transforming image: %v", err)
		img = b
	}

	// replay response with transformed image and updated content length
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%s %s\n", resp.Proto, resp.Status)
	resp.Header.WriteSubset(buf, map[string]bool{"Content-Length": true})
	fmt.Fprintf(buf, "Content-Length: %d\n\n", len(img))
	buf.Write(img)

	return http.ReadResponse(bufio.NewReader(buf), req)
}
<commit_msg>Pre-filter Content-Type differently. All tests pass<commit_after>// Copyright 2013 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.

// Package imageproxy provides an image proxy server.  For typical use of
// creating and using a Proxy, see cmd/imageproxy/main.go.
package imageproxy // import "willnorris.com/go/imageproxy"

import (
	"bufio"
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/gregjones/httpcache"
)

// Proxy serves image requests.
//
// Note that a Proxy should not be run behind a http.ServeMux, since the
// ServeMux aggressively cleans URLs and removes the double slash in the
// embedded request URL.
type Proxy struct {
	Client *http.Client // client used to fetch remote URLs
	Cache  Cache        // cache used to cache responses

	// Whitelist specifies a list of remote hosts that images can be
	// proxied from.  An empty list means all hosts are allowed.
	Whitelist []string

	// Referrers, when given, requires that requests to the image
	// proxy come from a referring host. An empty list means all
	// hosts are allowed.
	Referrers []string

	// DefaultBaseURL is the URL that relative remote URLs are resolved in
	// reference to.  If nil, all remote URLs specified in requests must be
	// absolute.
	DefaultBaseURL *url.URL

	// SignatureKey is the HMAC key used to verify signed requests.
	SignatureKey []byte
}

// NewProxy constructs a new proxy.  The provided http RoundTripper will be
// used to fetch remote URLs.  If nil is provided, http.DefaultTransport will
// be used.
func NewProxy(transport http.RoundTripper, cache Cache) *Proxy {
	if transport == nil {
		transport = http.DefaultTransport
	}
	if cache == nil {
		cache = NopCache
	}

	client := new(http.Client)
	client.Transport = &httpcache.Transport{
		Transport:           &TransformingTransport{transport, client},
		Cache:               cache,
		MarkCachedResponses: true,
	}

	return &Proxy{
		Client: client,
		Cache:  cache,
	}
}

// ServeHTTP handles image requests.
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		return // ignore favicon requests
	}

	req, err := NewRequest(r, p.DefaultBaseURL)
	if err != nil {
		msg := fmt.Sprintf("invalid request URL: %v", err)
		glog.Error(msg)
		http.Error(w, msg, http.StatusBadRequest)
		return
	}

	if !p.allowed(req) {
		msg := fmt.Sprintf("request does not contain an allowed host or valid signature")
		glog.Error(msg)
		http.Error(w, msg, http.StatusForbidden)
		return
	}

	resp, err := p.Client.Get(req.String())

	if err != nil {
		msg := fmt.Sprintf("error fetching remote image: %v", err)
		glog.Error(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	// Allow only jpg, png, gif, bmp
	contentType := resp.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "text/plain"
	} else if contentType != "image/jpeg" &&
	   contentType != "image/png" &&
	   contentType != "image/bmp" &&
	   contentType != "image/gif" &&
	   contentType != "text/plain" {
	   	http.Error(w, "resource is not a valid image", http.StatusForbidden)
		return
	}

	cached := resp.Header.Get(httpcache.XFromCache)
	glog.Infof("request: %v (served from cache: %v)", *req, cached == "1")

	copyHeader(w, resp, "Last-Modified")
	copyHeader(w, resp, "Expires")
	copyHeader(w, resp, "Etag")

	if is304 := check304(r, resp); is304 {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	copyHeader(w, resp, "Content-Length")
	w.Header()["Content-Type"] = []string { contentType }
	w.WriteHeader(resp.StatusCode)
	io.Copy(w, resp.Body)
}

func copyHeader(w http.ResponseWriter, r *http.Response, header string) {
	key := http.CanonicalHeaderKey(header)
	if value, ok := r.Header[key]; ok {
		w.Header()[key] = value
	}
}

// allowed returns whether the specified request is allowed because it matches
// a host in the proxy whitelist or it has a valid signature.
func (p *Proxy) allowed(r *Request) bool {
	if len(p.Referrers) > 0 && !validReferrer(p.Referrers, r.Original) {
		glog.Infof("request not coming from allowed referrer: %v", r)
		return false
	}

	if len(p.Whitelist) == 0 && len(p.SignatureKey) == 0 {
		return true // no whitelist or signature key, all requests accepted
	}

	if len(p.Whitelist) > 0 {
		if validHost(p.Whitelist, r.URL) {
			return true
		}
		glog.Infof("request is not for an allowed host: %v", r)
	}

	if len(p.SignatureKey) > 0 {
		if validSignature(p.SignatureKey, r) {
			return true
		}
		glog.Infof("request contains invalid signature: %v", r)
	}

	return false
}

// validHost returns whether the host in u matches one of hosts.
func validHost(hosts []string, u *url.URL) bool {
	for _, host := range hosts {
		if u.Host == host {
			return true
		}
		if strings.HasPrefix(host, "*.") && strings.HasSuffix(u.Host, host[2:]) {
			return true
		}
	}

	return false
}

// returns whether the referrer from the request is in the host list.
func validReferrer(hosts []string, r *http.Request) bool {
	parsed, err := url.Parse(r.Header.Get("Referer"))
	if err != nil { // malformed or blank header, just deny
		return false
	}

	return validHost(hosts, parsed)
}

// validSignature returns whether the request signature is valid.
func validSignature(key []byte, r *Request) bool {
	sig := r.Options.Signature
	if m := len(sig) % 4; m != 0 { // add padding if missing
		sig += strings.Repeat("=", 4-m)
	}

	got, err := base64.URLEncoding.DecodeString(sig)
	if err != nil {
		glog.Errorf("error base64 decoding signature %q", r.Options.Signature)
		return false
	}

	mac := hmac.New(sha256.New, key)
	mac.Write([]byte(r.URL.String()))
	want := mac.Sum(nil)

	return hmac.Equal(got, want)
}

// check304 checks whether we should send a 304 Not Modified in response to
// req, based on the response resp.  This is determined using the last modified
// time and the entity tag of resp.
func check304(req *http.Request, resp *http.Response) bool {
	// TODO(willnorris): if-none-match header can be a comma separated list
	// of multiple tags to be matched, or the special value "*" which
	// matches all etags
	etag := resp.Header.Get("Etag")
	if etag != "" && etag == req.Header.Get("If-None-Match") {
		return true
	}

	lastModified, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
	if err != nil {
		return false
	}
	ifModSince, err := time.Parse(time.RFC1123, req.Header.Get("If-Modified-Since"))
	if err != nil {
		return false
	}
	if lastModified.Before(ifModSince) {
		return true
	}

	return false
}

// TransformingTransport is an implementation of http.RoundTripper that
// optionally transforms images using the options specified in the request URL
// fragment.
type TransformingTransport struct {
	// Transport is the underlying http.RoundTripper used to satisfy
	// non-transform requests (those that do not include a URL fragment).
	Transport http.RoundTripper

	// CachingClient is used to fetch images to be resized.  This client is
	// used rather than Transport directly in order to ensure that
	// responses are properly cached.
	CachingClient *http.Client
}

// RoundTrip implements the http.RoundTripper interface.
func (t *TransformingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.URL.Fragment == "" {
		// normal requests pass through
		glog.Infof("fetching remote URL: %v", req.URL)
		return t.Transport.RoundTrip(req)
	}

	u := *req.URL
	u.Fragment = ""
	resp, err := t.CachingClient.Get(u.String())
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	opt := ParseOptions(req.URL.Fragment)
	img, err := Transform(b, opt)
	if err != nil {
		glog.Errorf("error transforming image: %v", err)
		img = b
	}

	// replay response with transformed image and updated content length
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%s %s\n", resp.Proto, resp.Status)
	resp.Header.WriteSubset(buf, map[string]bool{"Content-Length": true})
	fmt.Fprintf(buf, "Content-Length: %d\n\n", len(img))
	buf.Write(img)

	return http.ReadResponse(bufio.NewReader(buf), req)
}
<|endoftext|>


<commit_before>package main

const index = `<!DOCTYPE html>

		<title>kill-trigger</title>

		{{ range .commands }}
			<button id="{{.ID}}">{{.Name}}</button>
		{{ end }}

`
<commit_msg>Adds LastKeepAlive instead of showing KeepAlives.<commit_after>package main

const index = `<!DOCTYPE html>

		<title>kill-trigger</title>

		{{ range .commands }}
			<button id="{{.ID}}">{{.Name}}</button>
		{{ end }}

			Last KeepAlive:
			<input id="lastKeepAlive"></input>

`
<|endoftext|>


<commit_before>// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package gtimer implements Hierarchical Timing Wheel for interval/delayed jobs
// running and management.
//
// This package is designed for management for millions of timing jobs. The differences
// between gtimer and gcron are as follows:
// 1. package gcron is implemented based on package gtimer.
// 2. gtimer is designed for high performance and for millions of timing jobs.
// 3. gcron supports configuration pattern grammar like linux crontab, which is more manually
//    readable.
// 4. gtimer's benchmark OP is measured in nanoseconds, and gcron's benchmark OP is measured
//    in microseconds.
//
// ALSO VERY NOTE the common delay of the timer: https://github.com/golang/go/issues/14410
package gtimer

import (
	"fmt"
	"math"
	"time"

	"github.com/gogf/gf/os/gcmd"
)

const (
	StatusReady          = 0             // Job is ready for running.
	StatusRunning        = 1             // Job is already running.
	StatusStopped        = 2             // Job is stopped.
	StatusReset          = 3             // Job is reset.
	StatusClosed         = -1            // Job is closed and waiting to be deleted.
	panicExit            = "exit"        // Internal usage for custom job exit function with panic.
	defaultTimes         = math.MaxInt32 // Default limit running times, a big number.
	defaultSlotNumber    = 10            // Default slot number.
	defaultWheelInterval = 100           // Default wheel interval.
	defaultWheelLevel    = 5             // Default wheel level.
	cmdEnvKey            = "gf.gtimer"   // Configuration key for command argument or environment.
)

var (
	defaultSlots    = gcmd.GetWithEnv(fmt.Sprintf("%s.slots", cmdEnvKey), defaultSlotNumber).Int()
	defaultLevel    = gcmd.GetWithEnv(fmt.Sprintf("%s.level", cmdEnvKey), defaultWheelLevel).Int()
	defaultInterval = gcmd.GetWithEnv(fmt.Sprintf("%s.interval", cmdEnvKey), defaultWheelInterval).Duration() * time.Millisecond
	defaultTimer    = New(defaultSlots, defaultInterval, defaultLevel)
)

// SetTimeout runs the job once after duration of <delay>.
// It is like the one in javascript.
func SetTimeout(delay time.Duration, job JobFunc) {
	AddOnce(delay, job)
}

// SetInterval runs the job every duration of <delay>.
// It is like the one in javascript.
func SetInterval(interval time.Duration, job JobFunc) {
	Add(interval, job)
}

// Add adds a timing job to the default timer, which runs in interval of <interval>.
func Add(interval time.Duration, job JobFunc) *Entry {
	return defaultTimer.Add(interval, job)
}

// AddEntry adds a timing job to the default timer with detailed parameters.
//
// The parameter <interval> specifies the running interval of the job.
//
// The parameter <singleton> specifies whether the job running in singleton mode.
// There's only one of the same job is allowed running when its a singleton mode job.
//
// The parameter <times> specifies limit for the job running times, which means the job
// exits if its run times exceeds the <times>.
//
// The parameter <status> specifies the job status when it's firstly added to the timer.
func AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry {
	return defaultTimer.AddEntry(interval, job, singleton, times, status)
}

// AddSingleton is a convenience function for add singleton mode job.
func AddSingleton(interval time.Duration, job JobFunc) *Entry {
	return defaultTimer.AddSingleton(interval, job)
}

// AddOnce is a convenience function for adding a job which only runs once and then exits.
func AddOnce(interval time.Duration, job JobFunc) *Entry {
	return defaultTimer.AddOnce(interval, job)
}

// AddTimes is a convenience function for adding a job which is limited running times.
func AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
	return defaultTimer.AddTimes(interval, times, job)
}

// DelayAdd adds a timing job after delay of <interval> duration.
// Also see Add.
func DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) {
	defaultTimer.DelayAdd(delay, interval, job)
}

// DelayAddEntry adds a timing job after delay of <interval> duration.
// Also see AddEntry.
func DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) {
	defaultTimer.DelayAddEntry(delay, interval, job, singleton, times, status)
}

// DelayAddSingleton adds a timing job after delay of <interval> duration.
// Also see AddSingleton.
func DelayAddSingleton(delay time.Duration, interval time.Duration, job JobFunc) {
	defaultTimer.DelayAddSingleton(delay, interval, job)
}

// DelayAddOnce adds a timing job after delay of <interval> duration.
// Also see AddOnce.
func DelayAddOnce(delay time.Duration, interval time.Duration, job JobFunc) {
	defaultTimer.DelayAddOnce(delay, interval, job)
}

// DelayAddTimes adds a timing job after delay of <interval> duration.
// Also see AddTimes.
func DelayAddTimes(delay time.Duration, interval time.Duration, times int, job JobFunc) {
	defaultTimer.DelayAddTimes(delay, interval, times, job)
}

// Exit is used in timing job internally, which exits and marks it closed from timer.
// The timing job will be automatically removed from timer later. It uses "panic-recover"
// mechanism internally implementing this feature, which is designed for simplification
// and convenience.
func Exit() {
	panic(panicExit)
}
<commit_msg>revert default interval from 100ms to 50ms<commit_after>// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package gtimer implements Hierarchical Timing Wheel for interval/delayed jobs
// running and management.
//
// This package is designed for management for millions of timing jobs. The differences
// between gtimer and gcron are as follows:
// 1. package gcron is implemented based on package gtimer.
// 2. gtimer is designed for high performance and for millions of timing jobs.
// 3. gcron supports configuration pattern grammar like linux crontab, which is more manually
//    readable.
// 4. gtimer's benchmark OP is measured in nanoseconds, and gcron's benchmark OP is measured
//    in microseconds.
//
// ALSO VERY NOTE the common delay of the timer: https://github.com/golang/go/issues/14410
package gtimer

import (
	"fmt"
	"math"
	"time"

	"github.com/gogf/gf/os/gcmd"
)

const (
	StatusReady          = 0             // Job is ready for running.
	StatusRunning        = 1             // Job is already running.
	StatusStopped        = 2             // Job is stopped.
	StatusReset          = 3             // Job is reset.
	StatusClosed         = -1            // Job is closed and waiting to be deleted.
	panicExit            = "exit"        // Internal usage for custom job exit function with panic.
	defaultTimes         = math.MaxInt32 // Default limit running times, a big number.
	defaultSlotNumber    = 10            // Default slot number.
	defaultWheelInterval = 50            // Default wheel interval.
	defaultWheelLevel    = 5             // Default wheel level.
	cmdEnvKey            = "gf.gtimer"   // Configuration key for command argument or environment.
)

var (
	defaultSlots    = gcmd.GetWithEnv(fmt.Sprintf("%s.slots", cmdEnvKey), defaultSlotNumber).Int()
	defaultLevel    = gcmd.GetWithEnv(fmt.Sprintf("%s.level", cmdEnvKey), defaultWheelLevel).Int()
	defaultInterval = gcmd.GetWithEnv(fmt.Sprintf("%s.interval", cmdEnvKey), defaultWheelInterval).Duration() * time.Millisecond
	defaultTimer    = New(defaultSlots, defaultInterval, defaultLevel)
)

// SetTimeout runs the job once after duration of <delay>.
// It is like the one in javascript.
func SetTimeout(delay time.Duration, job JobFunc) {
	AddOnce(delay, job)
}

// SetInterval runs the job every duration of <delay>.
// It is like the one in javascript.
func SetInterval(interval time.Duration, job JobFunc) {
	Add(interval, job)
}

// Add adds a timing job to the default timer, which runs in interval of <interval>.
func Add(interval time.Duration, job JobFunc) *Entry {
	return defaultTimer.Add(interval, job)
}

// AddEntry adds a timing job to the default timer with detailed parameters.
//
// The parameter <interval> specifies the running interval of the job.
//
// The parameter <singleton> specifies whether the job running in singleton mode.
// There's only one of the same job is allowed running when its a singleton mode job.
//
// The parameter <times> specifies limit for the job running times, which means the job
// exits if its run times exceeds the <times>.
//
// The parameter <status> specifies the job status when it's firstly added to the timer.
func AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry {
	return defaultTimer.AddEntry(interval, job, singleton, times, status)
}

// AddSingleton is a convenience function for add singleton mode job.
func AddSingleton(interval time.Duration, job JobFunc) *Entry {
	return defaultTimer.AddSingleton(interval, job)
}

// AddOnce is a convenience function for adding a job which only runs once and then exits.
func AddOnce(interval time.Duration, job JobFunc) *Entry {
	return defaultTimer.AddOnce(interval, job)
}

// AddTimes is a convenience function for adding a job which is limited running times.
func AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
	return defaultTimer.AddTimes(interval, times, job)
}

// DelayAdd adds a timing job after delay of <interval> duration.
// Also see Add.
func DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) {
	defaultTimer.DelayAdd(delay, interval, job)
}

// DelayAddEntry adds a timing job after delay of <interval> duration.
// Also see AddEntry.
func DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) {
	defaultTimer.DelayAddEntry(delay, interval, job, singleton, times, status)
}

// DelayAddSingleton adds a timing job after delay of <interval> duration.
// Also see AddSingleton.
func DelayAddSingleton(delay time.Duration, interval time.Duration, job JobFunc) {
	defaultTimer.DelayAddSingleton(delay, interval, job)
}

// DelayAddOnce adds a timing job after delay of <interval> duration.
// Also see AddOnce.
func DelayAddOnce(delay time.Duration, interval time.Duration, job JobFunc) {
	defaultTimer.DelayAddOnce(delay, interval, job)
}

// DelayAddTimes adds a timing job after delay of <interval> duration.
// Also see AddTimes.
func DelayAddTimes(delay time.Duration, interval time.Duration, times int, job JobFunc) {
	defaultTimer.DelayAddTimes(delay, interval, times, job)
}

// Exit is used in timing job internally, which exits and marks it closed from timer.
// The timing job will be automatically removed from timer later. It uses "panic-recover"
// mechanism internally implementing this feature, which is designed for simplification
// and convenience.
func Exit() {
	panic(panicExit)
}
<|endoftext|>


<commit_before>// Copyright 2017 Julien Schmidt. All rights reserved.
// Use of this source code is governed by MIT license,
// a copy can be found in the LICENSE file.

// Package systemd provides functions for native systemd integration.
package systemd

import (
	"errors"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"
)

// See https://github.com/systemd/systemd/blob/master/src/libsystemd/sd-daemon/sd-daemon.c

const fdStart = 3 // first systemd socket

// Socket is the abstraction of a socket passed as a file descriptor by systemd.
type Socket struct {
	f *os.File
}

func newSocket(fd int, name string) Socket {
	// set the close-on-exec flag for the file descriptor
	syscall.CloseOnExec(fd)

	return Socket{os.NewFile(uintptr(fd), name)}
}

// Fd returns the integer Unix file descriptor referencing the open socket.
// The file descriptor is valid only until s.Close is called or s is garbage
// collected.
func (s *Socket) Fd() uintptr {
	return s.f.Fd()
}

// Name returns the name assigned to the socket.
func (s *Socket) Name() string {
	return s.f.Name()
}

// Close closes the Socket, rendering it unusable for I/O.
// It returns an error, if any.
func (s *Socket) Close() error {
	return s.f.Close()
}

// File returns the underlying os.File of the socket.
// Closing f does also close s and closing s does also close f.
func (s *Socket) File() (f *os.File) {
	return s.f
}

// Listener returns a copy of the network listener corresponding to the open
// socket s.
// It is the caller's responsibility to close ln when finished.
// Closing ln does not affect s, and closing s does not affect ln.
func (s *Socket) Listener() (ln net.Listener, err error) {
	return net.FileListener(s.f)
}

// Conn returns a copy of the network connection corresponding to the open
// socket s.
// It is the caller's responsibility to close s when finished.
// Closing c does not affect s, and closing s does not affect c.
func (s *Socket) Conn() (c net.Conn, err error) {
	return net.FileConn(s.f)
}

// PacketConn returns a copy of the packet network connection corresponding
// to the open socket s.
// It is the caller's responsibility to close s when finished.
// Closing c does not affect s, and closing s does not affect c.
func (s *Socket) PacketConn() (c net.PacketConn, err error) {
	return net.FilePacketConn(s.f)
}

func parseEnv() (n int, err error) {
	envPID := os.Getenv("LISTEN_PID")
	envFDs := os.Getenv("LISTEN_FDS")

	// In Go programs there should be no need to unset the environment variables
	// as there is no API for forking.
	// if unsetEnv {
	//  os.Unsetenv("LISTEN_PID")
	//  os.Unsetenv("LISTEN_FDS")
	//  os.Unsetenv("LISTEN_FDNAMES")
	// }

	if len(envPID) == 0 {
		err = errors.New("listen enviornment not set")
		return
	}

	pid, err := strconv.Atoi(envPID)
	if err != nil {
		err = errors.New("invalid listen PID")
		return
	}

	if pid != os.Getpid() {
		err = errors.New("listen PID does not match")
		return
	}

	n, err = strconv.Atoi(envFDs)
	if err != nil {
		err = errors.New("invalid number of file descriptors")
	}
	return
}

func parseNames(n int) (names []string, err error) {
	envNames := os.Getenv("LISTEN_FDNAMES")
	if len(envNames) < 1 {
		return nil, errors.New("socket names not set")
	}

	names = strings.SplitN(envNames, ":", n)
	if len(names) != n || strings.IndexByte(names[n-1], ':') > 0 {
		return nil, errors.New("mismatch between number of socket and socket names:" +
			" expected " + strconv.Itoa(n) +
			", got " + strconv.Itoa(strings.Count(envNames, ":")+1))
	}
	return
}

// Listen returns sockets passed by the service manager as part of the
// socket-based activation logic.
// If no sockets have been received, an empty slice is returned.
// If more than one socket is received, they will be passed in the same order as
// configured in the systemd socket unit file.
func Listen() (sockets []Socket, err error) {
	n, err := parseEnv()
	if n < 1 { // includes err != nil case
		return
	}

	sockets = make([]Socket, n)
	for i := 0; i < n; i++ {
		fd := fdStart + i
		sockets[i] = newSocket(fd, "/proc/self/fd/"+strconv.Itoa(fd))
	}
	return
}

// ListenWithNames is like Listen but also assigns passed names to the sockets.
// The name can be used to identify a socket.
// Names can be assigned in the systemd unit files.
func ListenWithNames() (files []Socket, err error) {
	n, err := parseEnv()
	if n < 1 { // includes err != nil case
		return
	}

	names, err := parseNames(n)
	if err != nil {
		return
	}

	files = make([]Socket, n)
	for i := 0; i < n; i++ {
		fd := fdStart + i
		files[i] = newSocket(fd, names[i])
	}
	return
}
<commit_msg>socket: fix typo<commit_after>// Copyright 2017 Julien Schmidt. All rights reserved.
// Use of this source code is governed by MIT license,
// a copy can be found in the LICENSE file.

// Package systemd provides functions for native systemd integration.
package systemd

import (
	"errors"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"
)

// See https://github.com/systemd/systemd/blob/master/src/libsystemd/sd-daemon/sd-daemon.c

const fdStart = 3 // first systemd socket

// Socket is the abstraction of a socket passed as a file descriptor by systemd.
type Socket struct {
	f *os.File
}

func newSocket(fd int, name string) Socket {
	// set the close-on-exec flag for the file descriptor
	syscall.CloseOnExec(fd)

	return Socket{os.NewFile(uintptr(fd), name)}
}

// Fd returns the integer Unix file descriptor referencing the open socket.
// The file descriptor is valid only until s.Close is called or s is garbage
// collected.
func (s *Socket) Fd() uintptr {
	return s.f.Fd()
}

// Name returns the name assigned to the socket.
func (s *Socket) Name() string {
	return s.f.Name()
}

// Close closes the Socket, rendering it unusable for I/O.
// It returns an error, if any.
func (s *Socket) Close() error {
	return s.f.Close()
}

// File returns the underlying os.File of the socket.
// Closing f does also close s and closing s does also close f.
func (s *Socket) File() (f *os.File) {
	return s.f
}

// Listener returns a copy of the network listener corresponding to the open
// socket s.
// It is the caller's responsibility to close ln when finished.
// Closing ln does not affect s, and closing s does not affect ln.
func (s *Socket) Listener() (ln net.Listener, err error) {
	return net.FileListener(s.f)
}

// Conn returns a copy of the network connection corresponding to the open
// socket s.
// It is the caller's responsibility to close s when finished.
// Closing c does not affect s, and closing s does not affect c.
func (s *Socket) Conn() (c net.Conn, err error) {
	return net.FileConn(s.f)
}

// PacketConn returns a copy of the packet network connection corresponding
// to the open socket s.
// It is the caller's responsibility to close s when finished.
// Closing c does not affect s, and closing s does not affect c.
func (s *Socket) PacketConn() (c net.PacketConn, err error) {
	return net.FilePacketConn(s.f)
}

func parseEnv() (n int, err error) {
	envPID := os.Getenv("LISTEN_PID")
	envFDs := os.Getenv("LISTEN_FDS")

	// In Go programs there should be no need to unset the environment variables
	// as there is no API for forking.
	// if unsetEnv {
	//  os.Unsetenv("LISTEN_PID")
	//  os.Unsetenv("LISTEN_FDS")
	//  os.Unsetenv("LISTEN_FDNAMES")
	// }

	if len(envPID) == 0 {
		err = errors.New("listen environment not set")
		return
	}

	pid, err := strconv.Atoi(envPID)
	if err != nil {
		err = errors.New("invalid listen PID")
		return
	}

	if pid != os.Getpid() {
		err = errors.New("listen PID does not match")
		return
	}

	n, err = strconv.Atoi(envFDs)
	if err != nil {
		err = errors.New("invalid number of file descriptors")
	}
	return
}

func parseNames(n int) (names []string, err error) {
	envNames := os.Getenv("LISTEN_FDNAMES")
	if len(envNames) < 1 {
		return nil, errors.New("socket names not set")
	}

	names = strings.SplitN(envNames, ":", n)
	if len(names) != n || strings.IndexByte(names[n-1], ':') > 0 {
		return nil, errors.New("mismatch between number of socket and socket names:" +
			" expected " + strconv.Itoa(n) +
			", got " + strconv.Itoa(strings.Count(envNames, ":")+1))
	}
	return
}

// Listen returns sockets passed by the service manager as part of the
// socket-based activation logic.
// If no sockets have been received, an empty slice is returned.
// If more than one socket is received, they will be passed in the same order as
// configured in the systemd socket unit file.
func Listen() (sockets []Socket, err error) {
	n, err := parseEnv()
	if n < 1 { // includes err != nil case
		return
	}

	sockets = make([]Socket, n)
	for i := 0; i < n; i++ {
		fd := fdStart + i
		sockets[i] = newSocket(fd, "/proc/self/fd/"+strconv.Itoa(fd))
	}
	return
}

// ListenWithNames is like Listen but also assigns passed names to the sockets.
// The name can be used to identify a socket.
// Names can be assigned in the systemd unit files.
func ListenWithNames() (files []Socket, err error) {
	n, err := parseEnv()
	if n < 1 { // includes err != nil case
		return
	}

	names, err := parseNames(n)
	if err != nil {
		return
	}

	files = make([]Socket, n)
	for i := 0; i < n; i++ {
		fd := fdStart + i
		files[i] = newSocket(fd, names[i])
	}
	return
}
<|endoftext|>


<commit_before>package spec

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
)

func TestCloudKey(t *testing.T) {
	g := &CloudGenerator{}

	if g.Key() != "cloud" {
		t.Error("key should be cloud")
	}
}

func TestCloudGenerate(t *testing.T) {
	handler := func(res http.ResponseWriter, req *http.Request) {
		fmt.Fprint(res, "i-4f90d537")
	}
	ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
		handler(res, req)
	}))
	defer ts.Close()

	u, err := url.Parse(ts.URL)
	if err != nil {
		t.Errorf("should not raise error: %s", err)
	}
	g := &CloudGenerator{&EC2Generator{u}}

	value, err := g.Generate()
	if err != nil {
		t.Errorf("should not raise error: %s", err)
	}

	cloud, typeOk := value.(map[string]interface{})
	if !typeOk {
		t.Errorf("value should be map. %+v", value)
	}

	value, ok := cloud["metadata"]
	if !ok {
		t.Error("results should have metadata.")
	}

	metadata, typeOk := value.(map[string]string)
	if !typeOk {
		t.Errorf("v should be map. %+v", value)
	}

	if len(metadata["instance-id"]) == 0 {
		t.Error("instance-id should be filled")
	}
}

func TestGCEGenerate(t *testing.T) {
	// curl "http://metadata.google.internal/computeMetadata/v1/?recursive=true" -H "Metadata-Flavor: Google"
	sampleJSON := []byte(`{
	  "instance": {
		"attributes": {},
		"cpuPlatform": "Intel Ivy Bridge",
		"description": "",
		"disks": [
		  {
			"deviceName": "gce-1",
			"index": 0,
			"mode": "READ_WRITE",
			"type": "PERSISTENT"
		  }
		],
		"hostname": "gce-1.c.dummyproj-987.internal",
		"id": 4567890123456789123,
		"image": "",
		"machineType": "projects/1234567890123/machineTypes/g1-small",
		"maintenanceEvent": "NONE",
		"networkInterfaces": [
		  {
			"accessConfigs": [
			  {
				"externalIp": "203.0.113.1",
				"type": "ONE_TO_ONE_NAT"
			  }
			],
			"forwardedIps": [],
			"ip": "192.0.2.1",
			"network": "projects/1234567890123/networks/default"
		  }
		],
		"scheduling": {
		  "automaticRestart": "TRUE",
		  "onHostMaintenance": "MIGRATE"
		},
		"serviceAccounts": {
		  "1234567890123-compute@developer.gserviceaccount.com": {
			"aliases": [
			  "default"
			],
			"email": "1234567890123-compute@developer.gserviceaccount.com",
			"scopes": [
			  "https://www.googleapis.com/auth/devstorage.read_only",
			  "https://www.googleapis.com/auth/logging.write"
			]
		  },
		  "default": {
			"aliases": [
			  "default"
			],
			"email": "1234567890123-compute@developer.gserviceaccount.com",
			"scopes": [
			  "https://www.googleapis.com/auth/devstorage.read_only",
			  "https://www.googleapis.com/auth/logging.write"
			]
		  }
		},
		"tags": [],
		"virtualClock": {
		  "driftToken": "12345678901234567890"
		},
		"zone": "projects/1234567890123/zones/asia-east1-a"
	  },
	  "project": {
		"attributes": {
		  "google-compute-default-region": "us-central1",
		  "google-compute-default-zone": "us-central1-f",
		  "sshKeys": "dummy_user:ssh-rsa AAAhogehoge google-ssh {\"userName\":\"dummy_user@example.com\",\"expireOn\":\"2015-07-12T11:11:43+0000\"}\ndummy_user:ecdsa-sha2-nistp256 AAAhogefuga google-ssh {\"userName\":\"dummy_user@example.com\",\"expireOn\":\"2015-07-12T11:11:39+0000\"}\n"
		},
		"numericProjectId": 1234567890123,
		"projectId": "dummyprof-987"
	  }
	}`)

	var data gceMeta
	json.Unmarshal(sampleJSON, &data)

	if !reflect.DeepEqual(data.Instance, &gceInstance{
		Zone:         "projects/1234567890123/zones/asia-east1-a",
		InstanceType: "projects/1234567890123/machineTypes/g1-small",
		Hostname:     "gce-1.c.dummyproj-987.internal",
		InstanceID:   4567890123456789123,
	}) {
		t.Errorf("data.Instance should be assigned")
	}

	if !reflect.DeepEqual(data.Project, &gceProject{
		ProjectID:        "dummyprof-987",
		NumericProjectID: 1234567890123,
	}) {
		t.Errorf("data.Project should be assigned")
	}

	if d := data.toGeneratorMeta(); !reflect.DeepEqual(d, map[string]string{
		"zone":          "asia-east1-a",
		"instance-type": "g1-small",
		"hostname":      "gce-1.c.dummyproj-987.internal",
		"instance-id":   "4567890123456789123",
		"projectId":     "dummyprof-987",
	}) {
		t.Errorf("data.Project should be assigned")
	}

}

func TestSuggestCloudGenerator(t *testing.T) {
	// both of ec2BaseURL and gceMetaURL are unreachable
	unreachableURL, _ := url.Parse("http://unreachable.localhost")
	ec2BaseURL = unreachableURL
	gceMetaURL = unreachableURL
	cGen := SuggestCloudGenerator()
	if cGen != nil {
		t.Errorf("cGen should be nil but, %s", cGen)
	}

	func() { // ec2BaseURL is reachable but returns 404
		ts := httptest.NewServer(http.NotFoundHandler())
		defer ts.Close()
		u, _ := url.Parse(ts.URL)
		ec2BaseURL = u

		cGen = SuggestCloudGenerator()
		if cGen != nil {
			t.Errorf("cGen should be nil but, %s", cGen)
		}
	}()

	func() { // suggest EC2Generator
		ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
			fmt.Fprint(res, "OK:ami-id")
		}))
		defer ts.Close()
		u, _ := url.Parse(ts.URL)
		ec2BaseURL = u

		cGen = SuggestCloudGenerator()
		if cGen == nil {
			t.Errorf("cGen should not be nil.")
		}

		ec2gen, ok := cGen.CloudMetaGenerator.(*EC2Generator)

		if !ok {
			t.Errorf("cGen should be *EC2Generator")
		}

		if ec2gen.baseURL != ec2BaseURL {
			t.Errorf("something went wrong")
		}
	}()

	func() { // suggest EC2Generator
		ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
			fmt.Fprint(res, "GCE:OK")
		}))
		defer ts.Close()
		ec2BaseURL = unreachableURL
		u, _ := url.Parse(ts.URL)
		gceMetaURL = u

		cGen = SuggestCloudGenerator()
		if cGen == nil {
			t.Errorf("cGen should not be nil.")
		}

		gceGen, ok := cGen.CloudMetaGenerator.(*GCEGenerator)
		if !ok {
			t.Errorf("cGen should be *GCEGenerator")
		}
		if gceGen.metaURL != gceMetaURL {
			t.Errorf("something went wrong")
		}
	}()
}
<commit_msg>test SuggestCustomIdentifier<commit_after>package spec

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
)

func TestCloudKey(t *testing.T) {
	g := &CloudGenerator{}

	if g.Key() != "cloud" {
		t.Error("key should be cloud")
	}
}

func TestCloudGenerate(t *testing.T) {
	handler := func(res http.ResponseWriter, req *http.Request) {
		fmt.Fprint(res, "i-4f90d537")
	}
	ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
		handler(res, req)
	}))
	defer ts.Close()

	u, err := url.Parse(ts.URL)
	if err != nil {
		t.Errorf("should not raise error: %s", err)
	}
	g := &CloudGenerator{&EC2Generator{u}}

	value, err := g.Generate()
	if err != nil {
		t.Errorf("should not raise error: %s", err)
	}

	cloud, typeOk := value.(map[string]interface{})
	if !typeOk {
		t.Errorf("value should be map. %+v", value)
	}

	value, ok := cloud["metadata"]
	if !ok {
		t.Error("results should have metadata.")
	}

	metadata, typeOk := value.(map[string]string)
	if !typeOk {
		t.Errorf("v should be map. %+v", value)
	}

	if len(metadata["instance-id"]) == 0 {
		t.Error("instance-id should be filled")
	}

	customIdentifier, err := g.SuggestCustomIdentifier()
	if err != nil {
		t.Errorf("should not raise error: %s", err)
	}

	if len(customIdentifier) == 0 {
		t.Error("customIdentifier should be retrieved")
	}
}

func TestGCEGenerate(t *testing.T) {
	// curl "http://metadata.google.internal/computeMetadata/v1/?recursive=true" -H "Metadata-Flavor: Google"
	sampleJSON := []byte(`{
	  "instance": {
		"attributes": {},
		"cpuPlatform": "Intel Ivy Bridge",
		"description": "",
		"disks": [
		  {
			"deviceName": "gce-1",
			"index": 0,
			"mode": "READ_WRITE",
			"type": "PERSISTENT"
		  }
		],
		"hostname": "gce-1.c.dummyproj-987.internal",
		"id": 4567890123456789123,
		"image": "",
		"machineType": "projects/1234567890123/machineTypes/g1-small",
		"maintenanceEvent": "NONE",
		"networkInterfaces": [
		  {
			"accessConfigs": [
			  {
				"externalIp": "203.0.113.1",
				"type": "ONE_TO_ONE_NAT"
			  }
			],
			"forwardedIps": [],
			"ip": "192.0.2.1",
			"network": "projects/1234567890123/networks/default"
		  }
		],
		"scheduling": {
		  "automaticRestart": "TRUE",
		  "onHostMaintenance": "MIGRATE"
		},
		"serviceAccounts": {
		  "1234567890123-compute@developer.gserviceaccount.com": {
			"aliases": [
			  "default"
			],
			"email": "1234567890123-compute@developer.gserviceaccount.com",
			"scopes": [
			  "https://www.googleapis.com/auth/devstorage.read_only",
			  "https://www.googleapis.com/auth/logging.write"
			]
		  },
		  "default": {
			"aliases": [
			  "default"
			],
			"email": "1234567890123-compute@developer.gserviceaccount.com",
			"scopes": [
			  "https://www.googleapis.com/auth/devstorage.read_only",
			  "https://www.googleapis.com/auth/logging.write"
			]
		  }
		},
		"tags": [],
		"virtualClock": {
		  "driftToken": "12345678901234567890"
		},
		"zone": "projects/1234567890123/zones/asia-east1-a"
	  },
	  "project": {
		"attributes": {
		  "google-compute-default-region": "us-central1",
		  "google-compute-default-zone": "us-central1-f",
		  "sshKeys": "dummy_user:ssh-rsa AAAhogehoge google-ssh {\"userName\":\"dummy_user@example.com\",\"expireOn\":\"2015-07-12T11:11:43+0000\"}\ndummy_user:ecdsa-sha2-nistp256 AAAhogefuga google-ssh {\"userName\":\"dummy_user@example.com\",\"expireOn\":\"2015-07-12T11:11:39+0000\"}\n"
		},
		"numericProjectId": 1234567890123,
		"projectId": "dummyprof-987"
	  }
	}`)

	var data gceMeta
	json.Unmarshal(sampleJSON, &data)

	if !reflect.DeepEqual(data.Instance, &gceInstance{
		Zone:         "projects/1234567890123/zones/asia-east1-a",
		InstanceType: "projects/1234567890123/machineTypes/g1-small",
		Hostname:     "gce-1.c.dummyproj-987.internal",
		InstanceID:   4567890123456789123,
	}) {
		t.Errorf("data.Instance should be assigned")
	}

	if !reflect.DeepEqual(data.Project, &gceProject{
		ProjectID:        "dummyprof-987",
		NumericProjectID: 1234567890123,
	}) {
		t.Errorf("data.Project should be assigned")
	}

	if d := data.toGeneratorMeta(); !reflect.DeepEqual(d, map[string]string{
		"zone":          "asia-east1-a",
		"instance-type": "g1-small",
		"hostname":      "gce-1.c.dummyproj-987.internal",
		"instance-id":   "4567890123456789123",
		"projectId":     "dummyprof-987",
	}) {
		t.Errorf("data.Project should be assigned")
	}

}

func TestSuggestCloudGenerator(t *testing.T) {
	// both of ec2BaseURL and gceMetaURL are unreachable
	unreachableURL, _ := url.Parse("http://unreachable.localhost")
	ec2BaseURL = unreachableURL
	gceMetaURL = unreachableURL
	cGen := SuggestCloudGenerator()
	if cGen != nil {
		t.Errorf("cGen should be nil but, %s", cGen)
	}

	func() { // ec2BaseURL is reachable but returns 404
		ts := httptest.NewServer(http.NotFoundHandler())
		defer ts.Close()
		u, _ := url.Parse(ts.URL)
		ec2BaseURL = u

		cGen = SuggestCloudGenerator()
		if cGen != nil {
			t.Errorf("cGen should be nil but, %s", cGen)
		}
	}()

	func() { // suggest EC2Generator
		ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
			fmt.Fprint(res, "OK:ami-id")
		}))
		defer ts.Close()
		u, _ := url.Parse(ts.URL)
		ec2BaseURL = u

		cGen = SuggestCloudGenerator()
		if cGen == nil {
			t.Errorf("cGen should not be nil.")
		}

		ec2gen, ok := cGen.CloudMetaGenerator.(*EC2Generator)

		if !ok {
			t.Errorf("cGen should be *EC2Generator")
		}

		if ec2gen.baseURL != ec2BaseURL {
			t.Errorf("something went wrong")
		}
	}()

	func() { // suggest EC2Generator
		ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
			fmt.Fprint(res, "GCE:OK")
		}))
		defer ts.Close()
		ec2BaseURL = unreachableURL
		u, _ := url.Parse(ts.URL)
		gceMetaURL = u

		cGen = SuggestCloudGenerator()
		if cGen == nil {
			t.Errorf("cGen should not be nil.")
		}

		gceGen, ok := cGen.CloudMetaGenerator.(*GCEGenerator)
		if !ok {
			t.Errorf("cGen should be *GCEGenerator")
		}
		if gceGen.metaURL != gceMetaURL {
			t.Errorf("something went wrong")
		}
	}()
}
<|endoftext|>