<commit_before>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

const testScratchBufferSize = 16 * 1024

func helperFilepathWalk(tb testing.TB, osDirname string) []string {
	var entries []string
	err := filepath.Walk(osDirname, func(osPathname string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.Name() == "skip" {
			return filepath.SkipDir
		}
		// filepath.Walk invokes callback function with a slashed version of the
		// pathname, while godirwalk invokes callback function with the
		// os-specific pathname separator.
		entries = append(entries, filepath.ToSlash(osPathname))
		return nil
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func helperGodirwalkWalk(tb testing.TB, osDirname string) []string {
	var entries []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			entries = append(entries, filepath.ToSlash(osPathname))
			return nil
		},
		ScratchBuffer: make([]byte, testScratchBufferSize),
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func symlinkAbs(oldname, newname string) error {
	absDir, err := filepath.Abs(oldname)
	if err != nil {
		return err
	}
	return os.Symlink(absDir, newname)
}

func TestWalkSkipDir(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	// Ensure the results from calling filepath.Walk exactly match the results
	// for calling this library's walk function.

	test := func(t *testing.T, osDirname string) {
		osDirname = filepath.Join(root, osDirname)
		expected := helperFilepathWalk(t, osDirname)
		actual := helperGodirwalkWalk(t, osDirname)

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	}

	// Test cases for encountering the filepath.SkipDir error at different times
	// from the call.

	t.Run("SkipFileAtRoot", func(t *testing.T) {
		test(t, "dir1/dir1a")
	})

	t.Run("SkipFileUnderRoot", func(t *testing.T) {
		test(t, "dir1")
	})

	t.Run("SkipDirAtRoot", func(t *testing.T) {
		test(t, "dir2/skip")
	})

	t.Run("SkipDirUnderRoot", func(t *testing.T) {
		test(t, "dir2")
	})

	t.Run("SkipDirOnSymlink", func(t *testing.T) {
		osDirname := filepath.Join(root, "dir3")
		actual := helperGodirwalkWalk(t, osDirname)

		expected := []string{
			filepath.Join(root, "dir3"),
			filepath.Join(root, "dir3/aaa.txt"),
			filepath.Join(root, "dir3/zzz"),
			filepath.Join(root, "dir3/zzz/aaa.txt"),
		}

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	})
}

func TestWalkNoAccess(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	var actual []string

	err := Walk(root, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		ErrorCallback: func(osChildname string, err error) ErrorAction {
			actual = append(actual, osChildname)
			return SkipNode
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(root, "dir6/noaccess"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksFalse(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	osDirname := filepath.Join(root, "dir4")
	symlink := filepath.Join(root, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(root, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(root, "dir4"),
		filepath.Join(root, "dir4/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToAbsDirectory"),
		filepath.Join(root, "dir4/symlinkToDirectory"),
		filepath.Join(root, "dir4/symlinkToFile"),
		filepath.Join(root, "dir4/zzz"),
		filepath.Join(root, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksTrue(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	osDirname := filepath.Join(root, "dir4")
	symlink := filepath.Join(root, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(root, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(root, "dir4"),
		filepath.Join(root, "dir4/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToAbsDirectory"),
		filepath.Join(root, "dir4/symlinkToAbsDirectory/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToDirectory"),
		filepath.Join(root, "dir4/symlinkToDirectory/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToFile"),
		filepath.Join(root, "dir4/zzz"),
		filepath.Join(root, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkSymbolicRelativeLinkChain(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	var actual []string
	err := Walk(filepath.Join(root, "dir7"), &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(root, "dir7"),
		filepath.Join(root, "dir7", "a"),
		filepath.Join(root, "dir7", "a", "x"),
		filepath.Join(root, "dir7", "a", "x", "y"),
		filepath.Join(root, "dir7", "b"),
		filepath.Join(root, "dir7", "b", "y"),
		filepath.Join(root, "dir7", "z"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestPostChildrenCallback(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	osDirname := filepath.Join(root, "dir5")

	var actual []string

	err := Walk(osDirname, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		PostChildrenCallback: func(osPathname string, de *Dirent) error {
			// t.Logf("walk out: %s", osPathname)
			actual = append(actual, osPathname)
			return nil
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(root, "dir5/a2/a2a"),
		filepath.Join(root, "dir5/a2"),
		filepath.Join(root, "dir5"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Errorf("(GOT) %v; (WNT) %v", got, want)
	}

	for i := 0; i < len(actual); i++ {
		if i >= len(expected) {
			t.Fatalf("(GOT) %v; (WNT): %v", actual[i], nil)
		}
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

var goPrefix = filepath.Join(os.Getenv("GOPATH"), "src")

func BenchmarkFilepathWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkGoDirWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}

const flameIterations = 10

func BenchmarkFlameGraphFilepathWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkFlameGraphGoDirWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}
<commit_msg>helper functions declared as such<commit_after>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

const testScratchBufferSize = 16 * 1024

func helperFilepathWalk(tb testing.TB, osDirname string) []string {
	tb.Helper()
	var entries []string
	err := filepath.Walk(osDirname, func(osPathname string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.Name() == "skip" {
			return filepath.SkipDir
		}
		// filepath.Walk invokes callback function with a slashed version of the
		// pathname, while godirwalk invokes callback function with the
		// os-specific pathname separator.
		entries = append(entries, filepath.ToSlash(osPathname))
		return nil
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func helperGodirwalkWalk(tb testing.TB, osDirname string) []string {
	tb.Helper()
	var entries []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			entries = append(entries, filepath.ToSlash(osPathname))
			return nil
		},
		ScratchBuffer: make([]byte, testScratchBufferSize),
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func symlinkAbs(oldname, newname string) error {
	absDir, err := filepath.Abs(oldname)
	if err != nil {
		return err
	}
	return os.Symlink(absDir, newname)
}

func TestWalkSkipDir(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	// Ensure the results from calling filepath.Walk exactly match the results
	// for calling this library's walk function.

	test := func(t *testing.T, osDirname string) {
		osDirname = filepath.Join(root, osDirname)
		expected := helperFilepathWalk(t, osDirname)
		actual := helperGodirwalkWalk(t, osDirname)

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	}

	// Test cases for encountering the filepath.SkipDir error at different times
	// from the call.

	t.Run("SkipFileAtRoot", func(t *testing.T) {
		test(t, "dir1/dir1a")
	})

	t.Run("SkipFileUnderRoot", func(t *testing.T) {
		test(t, "dir1")
	})

	t.Run("SkipDirAtRoot", func(t *testing.T) {
		test(t, "dir2/skip")
	})

	t.Run("SkipDirUnderRoot", func(t *testing.T) {
		test(t, "dir2")
	})

	t.Run("SkipDirOnSymlink", func(t *testing.T) {
		osDirname := filepath.Join(root, "dir3")
		actual := helperGodirwalkWalk(t, osDirname)

		expected := []string{
			filepath.Join(root, "dir3"),
			filepath.Join(root, "dir3/aaa.txt"),
			filepath.Join(root, "dir3/zzz"),
			filepath.Join(root, "dir3/zzz/aaa.txt"),
		}

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	})
}

func TestWalkNoAccess(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	var actual []string

	err := Walk(root, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		ErrorCallback: func(osChildname string, err error) ErrorAction {
			actual = append(actual, osChildname)
			return SkipNode
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(root, "dir6/noaccess"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksFalse(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	osDirname := filepath.Join(root, "dir4")
	symlink := filepath.Join(root, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(root, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(root, "dir4"),
		filepath.Join(root, "dir4/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToAbsDirectory"),
		filepath.Join(root, "dir4/symlinkToDirectory"),
		filepath.Join(root, "dir4/symlinkToFile"),
		filepath.Join(root, "dir4/zzz"),
		filepath.Join(root, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksTrue(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	osDirname := filepath.Join(root, "dir4")
	symlink := filepath.Join(root, "dir4/symlinkToAbsDirectory")

	if err := symlinkAbs(filepath.Join(root, "dir4/zzz"), symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := Walk(osDirname, &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(root, "dir4"),
		filepath.Join(root, "dir4/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToAbsDirectory"),
		filepath.Join(root, "dir4/symlinkToAbsDirectory/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToDirectory"),
		filepath.Join(root, "dir4/symlinkToDirectory/aaa.txt"),
		filepath.Join(root, "dir4/symlinkToFile"),
		filepath.Join(root, "dir4/zzz"),
		filepath.Join(root, "dir4/zzz/aaa.txt"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkSymbolicRelativeLinkChain(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	var actual []string
	err := Walk(filepath.Join(root, "dir7"), &Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *Dirent) error {
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		filepath.Join(root, "dir7"),
		filepath.Join(root, "dir7", "a"),
		filepath.Join(root, "dir7", "a", "x"),
		filepath.Join(root, "dir7", "a", "x", "y"),
		filepath.Join(root, "dir7", "b"),
		filepath.Join(root, "dir7", "b", "y"),
		filepath.Join(root, "dir7", "z"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestPostChildrenCallback(t *testing.T) {
	root := setup(t)
	defer teardown(t, root)

	osDirname := filepath.Join(root, "dir5")

	var actual []string

	err := Walk(osDirname, &Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *Dirent) error {
			// t.Logf("walk in: %s", osPathname)
			return nil
		},
		PostChildrenCallback: func(osPathname string, de *Dirent) error {
			// t.Logf("walk out: %s", osPathname)
			actual = append(actual, osPathname)
			return nil
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		filepath.Join(root, "dir5/a2/a2a"),
		filepath.Join(root, "dir5/a2"),
		filepath.Join(root, "dir5"),
	}

	if got, want := len(actual), len(expected); got != want {
		t.Errorf("(GOT) %v; (WNT) %v", got, want)
	}

	for i := 0; i < len(actual); i++ {
		if i >= len(expected) {
			t.Fatalf("(GOT) %v; (WNT): %v", actual[i], nil)
		}
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

var goPrefix = filepath.Join(os.Getenv("GOPATH"), "src")

func BenchmarkFilepathWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkGoDirWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}

const flameIterations = 10

func BenchmarkFlameGraphFilepathWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkFlameGraphGoDirWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}
<|endoftext|>


<commit_before>package client

import (
	"compress/gzip"
	"crypto/x509"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"time"

	"github.com/getlantern/keyman"
	"github.com/getlantern/yaml"

	"github.com/getlantern/flashlight/client"
)

const (
	httpIfNoneMatch = "If-None-Match"
	httpEtag        = "Etag"
)

var httpDefaultClient = &http.Client{Timeout: time.Second * 5}

var lastCloudConfigETag string

type config struct {
	Client     *client.ClientConfig `yaml:"client"`
	TrustedCAs []*ca                `yaml:"trustedcas"`
}

var (
	// errFailedConfigRequest is returned when the server replies with a non-200
	// status code to our request for a configuration file.
	errFailedConfigRequest = errors.New(`Could not get configuration file.`)

	// errInvalidConfiguration is returned in case the configuration file is
	// downloaded but has no useful data.
	errInvalidConfiguration = errors.New(`Invalid configuration file.`)

	errConfigurationUnchanged = errors.New(`Configuration remain unchanged.`)
)

const (
	cloudConfigCA = ``
	// URL of the configuration file. Remember to use HTTPs.
	remoteConfigURL = `https://s3.amazonaws.com/lantern_config/cloud.2.0.0-nl.yaml.gz`
)

// pullConfigFile attempts to retrieve a configuration file over the network,
// then it decompresses it and returns the file's raw bytes.
func pullConfigFile(cli *http.Client) ([]byte, error) {
	var err error
	var req *http.Request
	var res *http.Response

	if cli == nil {
		return nil, errors.New("Missing HTTP client.")
	}

	if req, err = http.NewRequest("GET", remoteConfigURL, nil); err != nil {
		return nil, err
	}

	if lastCloudConfigETag != "" {
		// Don't bother fetching if unchanged.
		req.Header.Set(httpIfNoneMatch, lastCloudConfigETag)
	}

	if res, err = cli.Do(req); err != nil {
		return nil, err
	}

	// Has changed?
	if res.StatusCode == http.StatusNotModified {
		log.Printf("Configuration file has not changed since last pull.\n")
		return nil, errConfigurationUnchanged
	}

	// Expecting 200 OK
	if res.StatusCode != http.StatusOK {
		return nil, errFailedConfigRequest
	}

	// Saving ETAG
	lastCloudConfigETag = res.Header.Get(httpEtag)

	// Using a gzip reader as we're getting a compressed file.
	var body io.ReadCloser
	if body, err = gzip.NewReader(res.Body); err != nil {
		return nil, err
	}
	defer body.Close()

	// Uncompressing bytes.
	return ioutil.ReadAll(body)
}

// defaultConfig returns the embedded configuration.
func defaultConfig() *config {

	cfg := &config{
		Client: &client.ClientConfig{
			FrontedServers: defaultFrontedServerList,
			MasqueradeSets: defaultMasqueradeSets,
		},
	}
	return cfg
}

// getConfig attempts to provide a
func getConfig() (*config, error) {
	var err error
	var buf []byte

	var cfg config

	// Attempt to download configuration file.
	if buf, err = pullConfigFile(httpDefaultClient); err != nil {
		return defaultConfig(), err
	}

	if err = cfg.updateFrom(buf); err != nil {
		return defaultConfig(), err
	}

	return &cfg, nil
}

func (c *config) updateFrom(buf []byte) error {
	var err error
	var newCfg config

	// Attempt to parse configuration file.
	if err = yaml.Unmarshal(buf, &newCfg); err != nil {
		return err
	}

	// Making sure we can actually use this configuration.
	if len(newCfg.Client.FrontedServers) > 0 && len(newCfg.Client.MasqueradeSets) > 0 && len(newCfg.TrustedCAs) > 0 {
		if reflect.DeepEqual(newCfg, *c) {
			return errConfigurationUnchanged
		}
		*c = newCfg
		return nil
	}

	return errInvalidConfiguration
}

func (c *config) getTrustedCerts() []string {
	certs := make([]string, 0, len(c.TrustedCAs))

	for _, ca := range c.TrustedCAs {
		certs = append(certs, ca.Cert)
	}

	return certs
}

func (c *config) getTrustedCertPool() (certPool *x509.CertPool, err error) {
	trustedCerts := c.getTrustedCerts()

	if certPool, err = keyman.PoolContainingCerts(trustedCerts...); err != nil {
		return nil, err
	}

	return certPool, nil
}
<commit_msg>update fronted server info<commit_after>package client

import (
	"compress/gzip"
	"crypto/x509"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"time"

	"github.com/getlantern/keyman"
	"github.com/getlantern/yaml"

	"github.com/getlantern/flashlight/client"
)

const (
	httpIfNoneMatch = "If-None-Match"
	httpEtag        = "Etag"
)

var httpDefaultClient = &http.Client{Timeout: time.Second * 5}

var lastCloudConfigETag string

type config struct {
	Client     *client.ClientConfig `yaml:"client"`
	TrustedCAs []*ca                `yaml:"trustedcas"`
}

var (
	// errFailedConfigRequest is returned when the server replies with a non-200
	// status code to our request for a configuration file.
	errFailedConfigRequest = errors.New(`Could not get configuration file.`)

	// errInvalidConfiguration is returned in case the configuration file is
	// downloaded but has no useful data.
	errInvalidConfiguration = errors.New(`Invalid configuration file.`)

	errConfigurationUnchanged = errors.New(`Configuration remain unchanged.`)
)

const (
	cloudConfigCA = ``
	// URL of the configuration file. Remember to use HTTPs.
	remoteConfigURL = `https://s3.amazonaws.com/lantern_config/cloud.2.0.0-nl.yaml.gz`
)

// pullConfigFile attempts to retrieve a configuration file over the network,
// then it decompresses it and returns the file's raw bytes.
func pullConfigFile(cli *http.Client) ([]byte, error) {
	var err error
	var req *http.Request
	var res *http.Response

	if cli == nil {
		return nil, errors.New("Missing HTTP client.")
	}

	if req, err = http.NewRequest("GET", remoteConfigURL, nil); err != nil {
		return nil, err
	}

	if lastCloudConfigETag != "" {
		// Don't bother fetching if unchanged.
		req.Header.Set(httpIfNoneMatch, lastCloudConfigETag)
	}

	if res, err = cli.Do(req); err != nil {
		return nil, err
	}

	// Has changed?
	if res.StatusCode == http.StatusNotModified {
		log.Printf("Configuration file has not changed since last pull.\n")
		return nil, errConfigurationUnchanged
	}

	// Expecting 200 OK
	if res.StatusCode != http.StatusOK {
		return nil, errFailedConfigRequest
	}

	// Saving ETAG
	lastCloudConfigETag = res.Header.Get(httpEtag)

	// Using a gzip reader as we're getting a compressed file.
	var body io.ReadCloser
	if body, err = gzip.NewReader(res.Body); err != nil {
		return nil, err
	}
	defer body.Close()

	// Uncompressing bytes.
	return ioutil.ReadAll(body)
}

// defaultConfig returns the embedded configuration.
func defaultConfig() *config {

	cfg := &config{
		Client: &client.ClientConfig{
			FrontedServers: []*client.FrontedServerInfo{
				&client.FrontedServerInfo{
					Host:           "nl.fallbacks.getiantem.org",
					Port:           443,
					PoolSize:       30,
					MasqueradeSet:  cloudflare,
					MaxMasquerades: 20,
					QOS:            10,
					Weight:         4000,
				},
			},
			MasqueradeSets: defaultMasqueradeSets,
		},
	}
	return cfg
}

// getConfig attempts to provide a
func getConfig() (*config, error) {
	var err error
	var buf []byte

	var cfg config

	// Attempt to download configuration file.
	if buf, err = pullConfigFile(httpDefaultClient); err != nil {
		return defaultConfig(), err
	}

	if err = cfg.updateFrom(buf); err != nil {
		return defaultConfig(), err
	}

	return &cfg, nil
}

func (c *config) updateFrom(buf []byte) error {
	var err error
	var newCfg config

	// Attempt to parse configuration file.
	if err = yaml.Unmarshal(buf, &newCfg); err != nil {
		return err
	}

	// Making sure we can actually use this configuration.
	if len(newCfg.Client.FrontedServers) > 0 && len(newCfg.Client.MasqueradeSets) > 0 && len(newCfg.TrustedCAs) > 0 {
		if reflect.DeepEqual(newCfg, *c) {
			return errConfigurationUnchanged
		}
		*c = newCfg
		return nil
	}

	return errInvalidConfiguration
}

func (c *config) getTrustedCerts() []string {
	certs := make([]string, 0, len(c.TrustedCAs))

	for _, ca := range c.TrustedCAs {
		certs = append(certs, ca.Cert)
	}

	return certs
}

func (c *config) getTrustedCertPool() (certPool *x509.CertPool, err error) {
	trustedCerts := c.getTrustedCerts()

	if certPool, err = keyman.PoolContainingCerts(trustedCerts...); err != nil {
		return nil, err
	}

	return certPool, nil
}
<|endoftext|>


<commit_before>package network // import "collectd.org/network"

import (
	"log"
	"net"

	"collectd.org/api"
)

// ListenAndWrite listens on the provided UDP address, parses the received
// packets and writes them to the provided api.Writer.
// This is a convenience function for a minimally configured server. If you
// need more control, see the "Server" type below.
func ListenAndWrite(address string, d api.Writer) error {
	srv := &Server{
		Addr:   address,
		Writer: d,
	}

	return srv.ListenAndWrite()
}

// Server holds parameters for running a collectd server.
type Server struct {
	Addr           string         // UDP address to listen on.
	Writer         api.Writer     // Object used to send incoming ValueLists to.
	BufferSize     uint16         // Maximum packet size to accept.
	PasswordLookup PasswordLookup // User to password lookup.
	SecurityLevel  SecurityLevel  // Minimal required security level
	// Interface is the name of the interface to use when subscribing to a
	// multicast group. Has no effect when using unicast.
	Interface string
}

// ListenAndWrite listens on the provided UDP address, parses the received
// packets and writes them to the provided api.Writer.
func (srv *Server) ListenAndWrite() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":" + DefaultService
	}

	laddr, err := net.ResolveUDPAddr("udp", srv.Addr)
	if err != nil {
		return err
	}

	var sock *net.UDPConn
	if laddr.IP.IsMulticast() {
		var ifi *net.Interface
		if srv.Interface != "" {
			if ifi, err = net.InterfaceByName(srv.Interface); err != nil {
				return err
			}
		}
		sock, err = net.ListenMulticastUDP("udp", ifi, laddr)
	} else {
		sock, err = net.ListenUDP("udp", laddr)
	}
	if err != nil {
		return err
	}
	defer sock.Close()

	if srv.BufferSize <= 0 {
		srv.BufferSize = DefaultBufferSize
	}
	buf := make([]byte, srv.BufferSize)

	popts := ParseOpts{
		PasswordLookup: srv.PasswordLookup,
		SecurityLevel:  srv.SecurityLevel,
	}

	for {
		n, err := sock.Read(buf)
		if err != nil {
			return err
		}

		valueLists, err := Parse(buf[:n], popts)
		if err != nil {
			log.Printf("error while parsing: %v", err)
			continue
		}

		go dispatch(valueLists, srv.Writer)
	}
}

func dispatch(valueLists []api.ValueList, d api.Writer) {
	for _, vl := range valueLists {
		if err := d.Write(vl); err != nil {
			log.Printf("error while dispatching: %v", err)
		}
	}
}
<commit_msg>network package: Check if laddr.IP is nil.<commit_after>package network // import "collectd.org/network"

import (
	"log"
	"net"

	"collectd.org/api"
)

// ListenAndWrite listens on the provided UDP address, parses the received
// packets and writes them to the provided api.Writer.
// This is a convenience function for a minimally configured server. If you
// need more control, see the "Server" type below.
func ListenAndWrite(address string, d api.Writer) error {
	srv := &Server{
		Addr:   address,
		Writer: d,
	}

	return srv.ListenAndWrite()
}

// Server holds parameters for running a collectd server.
type Server struct {
	Addr           string         // UDP address to listen on.
	Writer         api.Writer     // Object used to send incoming ValueLists to.
	BufferSize     uint16         // Maximum packet size to accept.
	PasswordLookup PasswordLookup // User to password lookup.
	SecurityLevel  SecurityLevel  // Minimal required security level
	// Interface is the name of the interface to use when subscribing to a
	// multicast group. Has no effect when using unicast.
	Interface string
}

// ListenAndWrite listens on the provided UDP address, parses the received
// packets and writes them to the provided api.Writer.
func (srv *Server) ListenAndWrite() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":" + DefaultService
	}

	laddr, err := net.ResolveUDPAddr("udp", srv.Addr)
	if err != nil {
		return err
	}

	var sock *net.UDPConn
	if laddr.IP != nil && laddr.IP.IsMulticast() {
		var ifi *net.Interface
		if srv.Interface != "" {
			if ifi, err = net.InterfaceByName(srv.Interface); err != nil {
				return err
			}
		}
		sock, err = net.ListenMulticastUDP("udp", ifi, laddr)
	} else {
		sock, err = net.ListenUDP("udp", laddr)
	}
	if err != nil {
		return err
	}
	defer sock.Close()

	if srv.BufferSize <= 0 {
		srv.BufferSize = DefaultBufferSize
	}
	buf := make([]byte, srv.BufferSize)

	popts := ParseOpts{
		PasswordLookup: srv.PasswordLookup,
		SecurityLevel:  srv.SecurityLevel,
	}

	for {
		n, err := sock.Read(buf)
		if err != nil {
			return err
		}

		valueLists, err := Parse(buf[:n], popts)
		if err != nil {
			log.Printf("error while parsing: %v", err)
			continue
		}

		go dispatch(valueLists, srv.Writer)
	}
}

func dispatch(valueLists []api.ValueList, d api.Writer) {
	for _, vl := range valueLists {
		if err := d.Write(vl); err != nil {
			log.Printf("error while dispatching: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package pagerduty

/*
 * Copyright 2016 Albert P. Tobey <atobey@netflix.com>
 *
 * 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.
 */

import (
	"bytes"
	"fmt"
	"log"
	"sort"
	"strings"
	"time"

	"github.com/netflix/hal-9001/hal"
)

const OncallUsage = `!oncall <alias>

Find out who is oncall. If only one argument is provided, it must match
a known alias for a Pagerduty service. Otherwise, it is expected to be
a subcommand.

!oncall core
`

const DefaultTopicInterval = "10m"

// TODO: add the service key to the output such that someone trying to contact a team
// can page them from within Slack without having to set up a page alias or go out
// to a web page. !page should be able to take a service key so the output can include something
// like: "To page <team> use the command: !page <servicekey> <message>"
func oncall(msg hal.Evt) {
	parts := msg.BodyAsArgv()

	if len(parts) == 1 {
		msg.Reply(OncallUsage)
		return
	}

	// make sure the pagerduty token is setup in hal.Secrets
	token, err := getSecrets()
	if err != nil || token == "" {
		msg.Replyf("pagerduty: %s is not set up in hal.Secrets. Cannot continue.", PagerdutyTokenKey)
		return
	}

	if parts[1] == "cache-now" {
		msg.Reply("Updating Pagerduty policy cache now.")
		cacheNow(token, msg.RoomId)
		msg.Reply("Pagerduty policy cache update complete.")
		return
	} else if parts[1] == "cache-status" {
		age := int(hal.Cache().Age(CacheKey).Seconds())
		next := time.Time{}
		status := "broken"
		pf := hal.GetPeriodicFunc(cacheFuncName(msg.RoomId))
		if pf != nil {
			next = pf.Last().Add(pf.Interval)
			status = pf.Status()
		}
		msg.Replyf("The cache is %d seconds old. Auto-update is %s and its next update is at %s.",
			age, status, next.Format(time.UnixDate))
		return
	} else if len(parts) > 2 {
		// flatten split words back into position 1, parts isn't used after the ToLower
		parts[1] = strings.Join(parts[1:], " ")
	}

	// TODO: look at the aliases set up for !page and try for an exact match
	// before doing fuzzy search -- move fuzzy search to a "search" subcommand
	// so it's clear that it is not precise
	want := strings.ToLower(parts[1])

	// see if there's an exact match on an alias, e.g. "!oncall core" -> alias.core
	/*
		aliasPref := msg.AsPref().SetUser("").FindKey(aliasKey(want)).One()
		if aliasPref.Success {
			svc, err := GetServiceByKey(token, aliasPref.Value)
			if err == nil {
			}
			// all through to search ...
		}
	*/

	// search over all policies looking for matching policy name, escalation
	// rule name, or service name
	matches := make([]Oncall, 0)
	oncalls := getOncallCache(token, false)
	var exactMatchFound bool

	for _, oncall := range oncalls {
		schedSummary := strings.ToLower(oncall.Schedule.Summary)
		if schedSummary == want {
			matches = append(matches, oncall)
			exactMatchFound = true
			continue
		} else if !exactMatchFound && strings.Contains(schedSummary, want) {
			matches = append(matches, oncall)
			continue
		}

		epSummary := strings.ToLower(oncall.EscalationPolicy.Summary)
		if epSummary == want {
			matches = append(matches, oncall)
			exactMatchFound = true
			continue
		} else if !exactMatchFound && strings.Contains(epSummary, want) {
			matches = append(matches, oncall)
			continue
		}
	}

	// check team names if there were no matches
	// TODO: cache some of these results and always check team names
	if len(matches) == 0 {
		teams, err := GetTeams(token)
		if err != nil {
			log.Printf("REST call to Pagerduty /teams failed: %s", err)
		} else {
			for _, team := range teams {
				ltname := strings.ToLower(team.Name)
				ltdesc := strings.ToLower(team.Description)

				if strings.Contains(ltname, want) || strings.Contains(ltdesc, want) {
					matches = getTeamOncalls(token, team)
				}
			}
		}
	}

	reply := formatOncallReply(want, exactMatchFound, matches)
	msg.Reply(reply)
}

// getTeamOncalls fetches escalation policies for the team then the oncalls for those
// policies and returns a list.
func getTeamOncalls(token string, team Team) []Oncall {
	out := make([]Oncall, 0)
	log.Printf("Returning empty list but should have fetched oncalls for team %+v", team)

	params := map[string][]string{"team_ids[]": []string{team.Id}}
	policies, err := GetEscalationPolicies(token, params)
	if err != nil {
		log.Printf("Error while fetching escalation policies for team id %q: %s", team.Id, err)
		return out
	}

	policy_ids := make([]string, 0)
	for _, policy := range policies {
		policy_ids = append(policy_ids, policy.Id)
	}

	params = map[string][]string{
		"include[]":               []string{"users", "schedules", "escalation_policies"},
		"escalation_policy_ids[]": policy_ids,
	}

	oncalls, err := GetOncalls(token, params)
	if err != nil {
		log.Printf("Error while fetching oncalls for team id %q's policies: %s", team.Id, err)
	} else {
		oncalls2directory(oncalls)
		return oncalls
	}

	return out
}

func getOncallCache(token string, forceUpdate bool) []Oncall {
	oncalls := []Oncall{}

	// see if there's a copy cached
	if hal.Cache().Exists(CacheKey) {
		ttl, err := hal.Cache().Get(CacheKey, &oncalls)
		if err != nil {
			log.Printf("Error retreiving oncalls from the Hal TTL cache: %s", err)
			oncalls = []Oncall{}
		} else if ttl == 0 || forceUpdate {
			oncalls = []Oncall{}
		}
	}

	// the cache exists and is still valid, return it now
	if len(oncalls) > 0 {
		return oncalls
	}

	// get all of the defined policies
	var err error
	params := map[string][]string{
		"include[]": []string{"users", "schedules", "escalation_policies"},
	}
	oncalls, err = GetOncalls(token, params)
	if err != nil {
		log.Printf("Returning empty list. REST call to Pagerduty failed: %s", err)
		return []Oncall{}
	}

	// always update the cache regardless of ttl
	hal.Cache().Set(CacheKey, &oncalls, cacheExpire)

	oncalls2directory(oncalls)

	return oncalls
}

func oncallInit(i *hal.Instance) {
	cacheFreq := hal.GetPref("", "", i.RoomId, "pagerduty", "cache-update-frequency", DefaultCacheInterval)
	cd, err := time.ParseDuration(cacheFreq.Value)
	if err != nil {
		log.Panicf("BUG: could not parse cache update frequency preference: %q", cacheFreq.Value)
	}

	topicFreq := hal.GetPref("", "", i.RoomId, "pagerduty", "topic-update-frequency", DefaultTopicInterval)
	td, err := time.ParseDuration(topicFreq.Value)
	if err != nil {
		log.Panicf("BUG: could not parse topic update frequency preference: %q", topicFreq.Value)
	}

	token, err := getSecrets()
	if err != nil || token == "" {
		return // getSecrets will log the error
	}

	go func() {
		pf := hal.PeriodicFunc{
			Name:     cacheFuncName(i.RoomId),
			Interval: cd,
			Function: func() { cacheNow(token, i.RoomId) },
		}

		pf.Register()
		pf.Start()
	}()

	go func() {
		pf := hal.PeriodicFunc{
			Name:     topicFuncName(i.RoomId),
			Interval: td,
			Function: func() { topicUpdater(token, i.RoomId, i.Broker.Name()) },
		}

		pf.Register()
		pf.Start()
	}()

	// TODO: add a command to stop, etc.
}

func cacheNow(token, roomId string) {
	getOncallCache(token, true)
}

// topicUpdater runs periodically to update the topic in the room
// it's configured in.
// To fully enable it, you need the oncall schedule id from the pagerduty API.
// !prefs set --room * --broker slack --plugin pagerduty --key topic-updater-schedule-id --value <schedule id>
// !prefs set --room * --broker slack --plugin pagerduty --key topic-prefix --value <text>
// !prefs set --room * --broker slack --plugin pagerduty --key topic-suffix --value <text>
// TODO: see if there's a way to also resolve integration keys instead of using the schedule id
func topicUpdater(token, roomId, brokerName string) {
	log.Printf("ENTER topicUpdater(token, %q, %q)", roomId, brokerName)

	pref := hal.GetPref("", brokerName, roomId, "pagerduty", "topic-updater-schedule-id", "-")
	// probably not configured, nothing to see here...
	if !pref.Success || pref.Value == "-" {
		log.Printf("The pref ''/%q/%q/pagerduty/topic-updater-schedule-id does not seem to be set. Returning without taking action.",
			brokerName, roomId)
		return
	}

	params := map[string][]string{
		"include[]":      []string{"users", "schedules", "escalation_policies"},
		"schedule_ids[]": []string{pref.Value},
	}

	oncalls, err := GetOncalls(token, params)
	if err != nil {
		log.Printf("Failed to fetch oncalls for schedule id %q: %s", pref.Value, err)
		return
	}

	log.Printf("Got %d users for schedule id %q", len(oncalls), pref.Value)

	// there may be more than one entry but if they're both on the same
	// schedule it should be the same primary oncall so ignore all but the first
	if len(oncalls) == 0 {
		log.Printf("no oncall results for id %q", pref.Value)
		return
	}

	// TODO: yet another place some kind of templating support would be handy
	prefix := hal.GetPref("", brokerName, roomId, "pagerduty", "topic-prefix", "")
	suffix := hal.GetPref("", brokerName, roomId, "pagerduty", "topic-suffix", "")

	// e.g. prefix = "", summary = "Al Tobey", suffix = " [team-dl@company.com] !pageus"
	topic := prefix.Value + oncalls[0].User.Summary + suffix.Value

	broker := hal.Router().GetBroker(brokerName)

	oldTopic, err := broker.GetTopic(roomId)
	if err != nil {
		log.Printf("Could not fetch current topic for room %q: %s", roomId, err)
		return
	}

	// only do the update if the topic has changed
	if topic != oldTopic {
		broker.SetTopic(roomId, topic)
	}
}

func cacheFuncName(roomId string) string {
	return fmt.Sprintf("pagerduty-cache-updater-%s", roomId)
}

func topicFuncName(roomId string) string {
	return fmt.Sprintf("pagerduty-topic-updater-%s", roomId)
}

// OncallsByLevel provides sorting by oncall level for []Oncall.
type OncallsByLevel []Oncall

func (a OncallsByLevel) Len() int      { return len(a) }
func (a OncallsByLevel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a OncallsByLevel) Less(i, j int) bool {
	// sort "always on call" users to the end of the list
	if a[j].Schedule.Summary == "" {
		return true
	}

	return a[i].EscalationLevel < a[j].EscalationLevel
}

func formatOncallReply(wanted string, exactMatchFound bool, oncalls []Oncall) string {
	buf := bytes.NewBuffer([]byte{})

	if exactMatchFound {
		fmt.Fprintf(buf, "exact match found for %q\n", oncalls[0].EscalationPolicy.Summary)
	} else {
		fmt.Fprintf(buf, "%d records matched for query: %q\n", len(oncalls), wanted)
	}

	sort.Sort(OncallsByLevel(oncalls))

	for _, oncall := range oncalls {
		indent := strings.Repeat("    ", oncall.EscalationLevel)
		sched := oncall.Schedule.Summary
		if sched == "" {
			sched = "always on call"
		}

		if exactMatchFound {
			fmt.Fprintf(buf, "%s%s - %s\n", indent, oncall.User.Summary, sched)
		} else {
			fmt.Fprintf(buf, "%s%s - %s - %s\n", indent,
				oncall.EscalationPolicy.Summary, oncall.User.Summary, sched)
		}
	}

	return buf.String()
}

func oncalls2directory(oncalls []Oncall) {
	// insert oncalls into the hal directory
	for _, oncall := range oncalls {
		attrs := map[string]string{
			"email":          oncall.User.Email,
			"name":           oncall.User.Name,
			"pd-user-id":     oncall.User.Id,
			"pd-schedule-id": oncall.Schedule.Id,
			"pd-policy-id":   oncall.EscalationPolicy.Id,
		}

		// plug in the contact methods
		// per PD docs, types can be: email_contact_method, phone_contact_method, push_notification_contact_method, or sms_contact_method
		for _, cm := range oncall.User.ContactMethods {
			attrs["contact-method-id"] = cm.Id
			attrs[cm.Type] = cm.Address
		}

		edges := []string{"name", "email", "phone_contact_method", "sms_contact_method"}
		err := hal.Directory().Put(oncall.User.Id, "pd-oncall", attrs, edges)
		if err != nil {
			log.Println(err)
		}
	}
}
<commit_msg>back out directory code in oncall plugin<commit_after>package pagerduty

/*
 * Copyright 2016 Albert P. Tobey <atobey@netflix.com>
 *
 * 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.
 */

import (
	"bytes"
	"fmt"
	"log"
	"sort"
	"strings"
	"time"

	"github.com/netflix/hal-9001/hal"
)

const OncallUsage = `!oncall <alias>

Find out who is oncall. If only one argument is provided, it must match
a known alias for a Pagerduty service. Otherwise, it is expected to be
a subcommand.

!oncall core
`

const DefaultTopicInterval = "10m"

// TODO: add the service key to the output such that someone trying to contact a team
// can page them from within Slack without having to set up a page alias or go out
// to a web page. !page should be able to take a service key so the output can include something
// like: "To page <team> use the command: !page <servicekey> <message>"
func oncall(msg hal.Evt) {
	parts := msg.BodyAsArgv()

	if len(parts) == 1 {
		msg.Reply(OncallUsage)
		return
	}

	// make sure the pagerduty token is setup in hal.Secrets
	token, err := getSecrets()
	if err != nil || token == "" {
		msg.Replyf("pagerduty: %s is not set up in hal.Secrets. Cannot continue.", PagerdutyTokenKey)
		return
	}

	if parts[1] == "cache-now" {
		msg.Reply("Updating Pagerduty policy cache now.")
		cacheNow(token, msg.RoomId)
		msg.Reply("Pagerduty policy cache update complete.")
		return
	} else if parts[1] == "cache-status" {
		age := int(hal.Cache().Age(CacheKey).Seconds())
		next := time.Time{}
		status := "broken"
		pf := hal.GetPeriodicFunc(cacheFuncName(msg.RoomId))
		if pf != nil {
			next = pf.Last().Add(pf.Interval)
			status = pf.Status()
		}
		msg.Replyf("The cache is %d seconds old. Auto-update is %s and its next update is at %s.",
			age, status, next.Format(time.UnixDate))
		return
	} else if len(parts) > 2 {
		// flatten split words back into position 1, parts isn't used after the ToLower
		parts[1] = strings.Join(parts[1:], " ")
	}

	// TODO: look at the aliases set up for !page and try for an exact match
	// before doing fuzzy search -- move fuzzy search to a "search" subcommand
	// so it's clear that it is not precise
	want := strings.ToLower(parts[1])

	// see if there's an exact match on an alias, e.g. "!oncall core" -> alias.core
	/*
		aliasPref := msg.AsPref().SetUser("").FindKey(aliasKey(want)).One()
		if aliasPref.Success {
			svc, err := GetServiceByKey(token, aliasPref.Value)
			if err == nil {
			}
			// all through to search ...
		}
	*/

	// search over all policies looking for matching policy name, escalation
	// rule name, or service name
	matches := make([]Oncall, 0)
	oncalls := getOncallCache(token, false)
	var exactMatchFound bool

	for _, oncall := range oncalls {
		schedSummary := strings.ToLower(oncall.Schedule.Summary)
		if schedSummary == want {
			matches = append(matches, oncall)
			exactMatchFound = true
			continue
		} else if !exactMatchFound && strings.Contains(schedSummary, want) {
			matches = append(matches, oncall)
			continue
		}

		epSummary := strings.ToLower(oncall.EscalationPolicy.Summary)
		if epSummary == want {
			matches = append(matches, oncall)
			exactMatchFound = true
			continue
		} else if !exactMatchFound && strings.Contains(epSummary, want) {
			matches = append(matches, oncall)
			continue
		}
	}

	// check team names if there were no matches
	// TODO: cache some of these results and always check team names
	if len(matches) == 0 {
		teams, err := GetTeams(token, nil)
		if err != nil {
			log.Printf("REST call to Pagerduty /teams failed: %s", err)
		} else {
			for _, team := range teams {
				ltname := strings.ToLower(team.Name)
				ltdesc := strings.ToLower(team.Description)

				if strings.Contains(ltname, want) || strings.Contains(ltdesc, want) {
					matches = getTeamOncalls(token, team)
				}
			}
		}
	}

	reply := formatOncallReply(want, exactMatchFound, matches)
	msg.Reply(reply)
}

// getTeamOncalls fetches escalation policies for the team then the oncalls for those
// policies and returns a list.
func getTeamOncalls(token string, team Team) []Oncall {
	out := make([]Oncall, 0)
	log.Printf("Returning empty list but should have fetched oncalls for team %+v", team)

	params := map[string][]string{"team_ids[]": []string{team.Id}}
	policies, err := GetEscalationPolicies(token, params)
	if err != nil {
		log.Printf("Error while fetching escalation policies for team id %q: %s", team.Id, err)
		return out
	}

	policy_ids := make([]string, 0)
	for _, policy := range policies {
		policy_ids = append(policy_ids, policy.Id)
	}

	params = map[string][]string{
		"include[]":               []string{"users"},
		"escalation_policy_ids[]": policy_ids,
	}

	oncalls, err := GetOncalls(token, params)
	if err != nil {
		log.Printf("Error while fetching oncalls for team id %q's policies: %s", team.Id, err)
	} else {
		return oncalls
	}

	return out
}

func getOncallCache(token string, forceUpdate bool) []Oncall {
	oncalls := []Oncall{}

	// see if there's a copy cached
	if hal.Cache().Exists(CacheKey) {
		ttl, err := hal.Cache().Get(CacheKey, &oncalls)
		if err != nil {
			log.Printf("Error retreiving oncalls from the Hal TTL cache: %s", err)
			oncalls = []Oncall{}
		} else if ttl == 0 || forceUpdate {
			oncalls = []Oncall{}
		}
	}

	// the cache exists and is still valid, return it now
	if len(oncalls) > 0 {
		return oncalls
	}

	// get all of the defined policies
	params := map[string][]string{"include[]": []string{"users"}}
	oncalls, err := GetOncalls(token, params)
	if err != nil {
		log.Printf("Returning empty list. REST call to Pagerduty failed: %s", err)
		return []Oncall{}
	}

	// always update the cache regardless of ttl
	hal.Cache().Set(CacheKey, &oncalls, cacheExpire)

	return oncalls
}

func oncallInit(i *hal.Instance) {
	cacheFreq := hal.GetPref("", "", i.RoomId, "pagerduty", "cache-update-frequency", DefaultCacheInterval)
	cd, err := time.ParseDuration(cacheFreq.Value)
	if err != nil {
		log.Panicf("BUG: could not parse cache update frequency preference: %q", cacheFreq.Value)
	}

	topicFreq := hal.GetPref("", "", i.RoomId, "pagerduty", "topic-update-frequency", DefaultTopicInterval)
	td, err := time.ParseDuration(topicFreq.Value)
	if err != nil {
		log.Panicf("BUG: could not parse topic update frequency preference: %q", topicFreq.Value)
	}

	token, err := getSecrets()
	if err != nil || token == "" {
		return // getSecrets will log the error
	}

	go func() {
		pf := hal.PeriodicFunc{
			Name:     cacheFuncName(i.RoomId),
			Interval: cd,
			Function: func() { cacheNow(token, i.RoomId) },
		}

		pf.Register()
		pf.Start()
	}()

	go func() {
		pf := hal.PeriodicFunc{
			Name:     topicFuncName(i.RoomId),
			Interval: td,
			Function: func() { topicUpdater(token, i.RoomId, i.Broker.Name()) },
		}

		pf.Register()
		pf.Start()
	}()

	// TODO: add a command to stop, etc.
}

func cacheNow(token, roomId string) {
	getOncallCache(token, true)
}

// topicUpdater runs periodically to update the topic in the room
// it's configured in.
// To fully enable it, you need the oncall schedule id from the pagerduty API.
// !prefs set --room * --broker slack --plugin pagerduty --key topic-updater-schedule-id --value <schedule id>
// !prefs set --room * --broker slack --plugin pagerduty --key topic-prefix --value <text>
// !prefs set --room * --broker slack --plugin pagerduty --key topic-suffix --value <text>
// TODO: see if there's a way to also resolve integration keys instead of using the schedule id
func topicUpdater(token, roomId, brokerName string) {
	log.Printf("ENTER topicUpdater(token, %q, %q)", roomId, brokerName)

	pref := hal.GetPref("", brokerName, roomId, "pagerduty", "topic-updater-schedule-id", "-")
	// probably not configured, nothing to see here...
	if !pref.Success || pref.Value == "-" {
		log.Printf("The pref ''/%q/%q/pagerduty/topic-updater-schedule-id does not seem to be set. Returning without taking action.",
			brokerName, roomId)
		return
	}

	params := map[string][]string{
		"include[]":      []string{"users", "contact_methods"},
		"schedule_ids[]": []string{pref.Value},
	}

	oncalls, err := GetOncalls(token, params)
	if err != nil {
		log.Printf("Failed to fetch oncalls for schedule id %q: %s", pref.Value, err)
		return
	}

	log.Printf("Got %d users for schedule id %q", len(oncalls), pref.Value)

	// there may be more than one entry but if they're both on the same
	// schedule it should be the same primary oncall so ignore all but the first
	if len(oncalls) == 0 {
		log.Printf("no oncall results for id %q", pref.Value)
		return
	}

	// TODO: yet another place some kind of templating support would be handy
	prefix := hal.GetPref("", brokerName, roomId, "pagerduty", "topic-prefix", "")
	suffix := hal.GetPref("", brokerName, roomId, "pagerduty", "topic-suffix", "")

	// e.g. prefix = "", summary = "Al Tobey", suffix = " [team-dl@company.com] !pageus"
	topic := prefix.Value + oncalls[0].User.Summary + suffix.Value

	broker := hal.Router().GetBroker(brokerName)

	oldTopic, err := broker.GetTopic(roomId)
	if err != nil {
		log.Printf("Could not fetch current topic for room %q: %s", roomId, err)
		return
	}

	// only do the update if the topic has changed
	if topic != oldTopic {
		broker.SetTopic(roomId, topic)
	}
}

func cacheFuncName(roomId string) string {
	return fmt.Sprintf("pagerduty-cache-updater-%s", roomId)
}

func topicFuncName(roomId string) string {
	return fmt.Sprintf("pagerduty-topic-updater-%s", roomId)
}

// OncallsByLevel provides sorting by oncall level for []Oncall.
type OncallsByLevel []Oncall

func (a OncallsByLevel) Len() int      { return len(a) }
func (a OncallsByLevel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a OncallsByLevel) Less(i, j int) bool {
	// sort "always on call" users to the end of the list
	if a[j].Schedule.Summary == "" {
		return true
	}

	return a[i].EscalationLevel < a[j].EscalationLevel
}

func formatOncallReply(wanted string, exactMatchFound bool, oncalls []Oncall) string {
	buf := bytes.NewBuffer([]byte{})

	if exactMatchFound {
		fmt.Fprintf(buf, "exact match found for %q\n", oncalls[0].EscalationPolicy.Summary)
	} else {
		fmt.Fprintf(buf, "%d records matched for query: %q\n", len(oncalls), wanted)
	}

	sort.Sort(OncallsByLevel(oncalls))

	for _, oncall := range oncalls {
		indent := strings.Repeat("    ", oncall.EscalationLevel)
		sched := oncall.Schedule.Summary
		if sched == "" {
			sched = "always on call"
		}

		if exactMatchFound {
			fmt.Fprintf(buf, "%s%s - %s\n", indent, oncall.User.Summary, sched)
		} else {
			fmt.Fprintf(buf, "%s%s - %s - %s\n", indent,
				oncall.EscalationPolicy.Summary, oncall.User.Summary, sched)
		}
	}

	return buf.String()
}
<|endoftext|>


<commit_before>package gopcap

//-----------------------------------------------------------------------------
// Unknown Transport
//-----------------------------------------------------------------------------

// UnknownTransport represents the data for a Transport-Layer packet that gopcap doesn't
// understand. It simply provides uninterpreted data representing the entire transport-layer
// packet.
type UnknownTransport struct {
	data []byte
}

func (u *UnknownTransport) TransportData() []byte {
	return u.data
}

func (u *UnknownTransport) FromBytes(data []byte) error {
	u.data = data
	return nil
}

//-----------------------------------------------------------------------------
// TCPSegment
//-----------------------------------------------------------------------------

// TCPSegment represents the data for a single Transmission Control Protocol segment. This method of
// storing a TCPSegment is less efficient than storing the binary representation on the wire.
type TCPSegment struct {
	SourcePort      uint16
	DestinationPort uint16
	SequenceNumber  uint32
	AckNumber       uint32
	HeaderSize      uint8
	NS              bool // This should be viewed as a temporary solution for flags: it's hugely space-inefficient,
	CWR             bool // and so I'll probably update the TCPSegment to handle flags differently.
	ECE             bool
	URG             bool
	ACK             bool
	PSH             bool
	RST             bool
	SYN             bool
	FIN             bool
	WindowSize      uint16
	Checksum        uint16
	UrgentOffset    uint16
	OptionData      []byte // This is temporary. We should handle TCP options properly.
	data            []byte
}

func (t *TCPSegment) TransportData() []byte {
	return t.data
}

func (t *TCPSegment) FromBytes(data []byte) error {
	// Begin by confirming that we have enough data for a complete TCP header.
	if len(data) < 20 {
		return InsufficientLength
	}

	// The first four fields are really easy.
	t.SourcePort = getUint16(data[0:2], false)
	t.DestinationPort = getUint16(data[2:4], false)
	t.SequenceNumber = getUint32(data[4:8], false)
	t.AckNumber = getUint32(data[8:12], false)

	// The header size is the top four bits of the next byte.
	t.HeaderSize = uint8(data[12]) >> 4

	// Now we have all the flag fields. First, the NS flag.
	if (uint8(data[12]) & 0x01) != 0 {
		t.NS = true
	}

	// The next eight flags are all in the next byte.
	flags := uint8(data[13])
	if (flags & 0x80) != 0 {
		t.CWR = true
	}
	if (flags & 0x40) != 0 {
		t.ECE = true
	}
	if (flags & 0x20) != 0 {
		t.URG = true
	}
	if (flags & 0x10) != 0 {
		t.ACK = true
	}
	if (flags & 0x08) != 0 {
		t.PSH = true
	}
	if (flags & 0x04) != 0 {
		t.RST = true
	}
	if (flags & 0x02) != 0 {
		t.SYN = true
	}
	if (flags & 0x01) != 0 {
		t.FIN = true
	}

	// Now we're back to sane things.
	t.WindowSize = getUint16(data[14:16], false)
	t.Checksum = getUint16(data[16:18], false)
	t.UrgentOffset = getUint16(data[18:20], false)

	// If the header size is larger than 5 (it's measured in 32-bit words for reasons that escape me),
	// we have some number of extra bytes that form the TCP options.
	extraBytes := (t.HeaderSize - 5) * 4
	data = data[20:]

	if len(data) < int(extraBytes) {
		return InsufficientLength
	}

	t.OptionData = data[:extraBytes]

	// All that remains is the contained data.
	t.data = data[extraBytes:]

	return nil
}
<commit_msg>Add the UDP datagram.<commit_after>package gopcap

//-----------------------------------------------------------------------------
// Unknown Transport
//-----------------------------------------------------------------------------

// UnknownTransport represents the data for a Transport-Layer packet that gopcap doesn't
// understand. It simply provides uninterpreted data representing the entire transport-layer
// packet.
type UnknownTransport struct {
	data []byte
}

func (u *UnknownTransport) TransportData() []byte {
	return u.data
}

func (u *UnknownTransport) FromBytes(data []byte) error {
	u.data = data
	return nil
}

//-----------------------------------------------------------------------------
// TCPSegment
//-----------------------------------------------------------------------------

// TCPSegment represents the data for a single Transmission Control Protocol segment. This method of
// storing a TCPSegment is less efficient than storing the binary representation on the wire.
type TCPSegment struct {
	SourcePort      uint16
	DestinationPort uint16
	SequenceNumber  uint32
	AckNumber       uint32
	HeaderSize      uint8
	NS              bool // This should be viewed as a temporary solution for flags: it's hugely space-inefficient,
	CWR             bool // and so I'll probably update the TCPSegment to handle flags differently.
	ECE             bool
	URG             bool
	ACK             bool
	PSH             bool
	RST             bool
	SYN             bool
	FIN             bool
	WindowSize      uint16
	Checksum        uint16
	UrgentOffset    uint16
	OptionData      []byte // This is temporary. We should handle TCP options properly.
	data            []byte
}

func (t *TCPSegment) TransportData() []byte {
	return t.data
}

func (t *TCPSegment) FromBytes(data []byte) error {
	// Begin by confirming that we have enough data for a complete TCP header.
	if len(data) < 20 {
		return InsufficientLength
	}

	// The first four fields are really easy.
	t.SourcePort = getUint16(data[0:2], false)
	t.DestinationPort = getUint16(data[2:4], false)
	t.SequenceNumber = getUint32(data[4:8], false)
	t.AckNumber = getUint32(data[8:12], false)

	// The header size is the top four bits of the next byte.
	t.HeaderSize = uint8(data[12]) >> 4

	// Now we have all the flag fields. First, the NS flag.
	if (uint8(data[12]) & 0x01) != 0 {
		t.NS = true
	}

	// The next eight flags are all in the next byte.
	flags := uint8(data[13])
	if (flags & 0x80) != 0 {
		t.CWR = true
	}
	if (flags & 0x40) != 0 {
		t.ECE = true
	}
	if (flags & 0x20) != 0 {
		t.URG = true
	}
	if (flags & 0x10) != 0 {
		t.ACK = true
	}
	if (flags & 0x08) != 0 {
		t.PSH = true
	}
	if (flags & 0x04) != 0 {
		t.RST = true
	}
	if (flags & 0x02) != 0 {
		t.SYN = true
	}
	if (flags & 0x01) != 0 {
		t.FIN = true
	}

	// Now we're back to sane things.
	t.WindowSize = getUint16(data[14:16], false)
	t.Checksum = getUint16(data[16:18], false)
	t.UrgentOffset = getUint16(data[18:20], false)

	// If the header size is larger than 5 (it's measured in 32-bit words for reasons that escape me),
	// we have some number of extra bytes that form the TCP options.
	extraBytes := (t.HeaderSize - 5) * 4
	data = data[20:]

	if len(data) < int(extraBytes) {
		return InsufficientLength
	}

	t.OptionData = data[:extraBytes]

	// All that remains is the contained data.
	t.data = data[extraBytes:]

	return nil
}

//-----------------------------------------------------------------------------
// UDPDatagram
//-----------------------------------------------------------------------------

// UDPDatagram represents the data for a single User Datagram Protocol datagram. This method of
// storing a UDPDatagram is less efficient than storing the binary representation on the wire.
type UDPDatagram struct {
	SourcePort      uint16
	DestinationPort uint16
	Length          uint16
	Checksum        uint16
	data            []byte
}

func (u *UDPDatagram) TransportData() []byte {
	return u.data
}

func (u *UDPDatagram) FromBytes(data []byte) error {
	// Begin by confirming that we have enough data to actually represent a UDP datagram.
	if len(data) < 8 {
		return InsufficientLength
	}

	// Happily, UDP is super simple. This makes this code equally simple.
	u.SourcePort = getUint16(data[0:2], false)
	u.DestinationPort = getUint16(data[2:4], false)
	u.Length = getUint16(data[4:6], false)
	u.Checksum = getUint16(data[6:8], false)

	// All that remains is data.
	u.data = data[8:]

	return nil
}
<|endoftext|>


<commit_before>package trie

import (
	"container/list"
	"fmt"
	"strings"
)

type Trie struct {
	root Node
}

func NewTrie() *Trie {
	var trie = Trie{
		root: Node{letter: rune(0)},
	}

	return &trie
}

func (this *Trie) Insert(word string) {

	currentNode := &this.root
	word = strings.ToLower(word)

	for _, l := range word {

		if existing := currentNode.FindChild(l); existing == nil {
			currentNode = currentNode.AddChild(l, false) //insert new node
			//fmt.Printf(" '%c' ", currentNode.letter)
		} else {

			currentNode = existing //use existing node
			//fmt.Printf(" \\'%c' ", l)
		}
	}

	currentNode.isTerminal = true
	//fmt.Print("\n")
}

func (this *Trie) FindAllPrefixes(word string) []string {
	var prefixrunes []rune
	var prefixes []string

	currentNode := &this.root

	for _, l := range word {

		if existing := currentNode.FindChild(l); existing == nil {
			return prefixes
		} else {
			currentNode = existing //use existing node
		}

		prefixrunes = append(prefixrunes, currentNode.letter)

		if currentNode.isTerminal == true {
			prefixes = append(prefixes, string(prefixrunes))
		}
	}

	return prefixes
}

func (this *Trie) HasWord(word string) bool {

	currentNode := &this.root

	for _, l := range word {

		if existing := currentNode.FindChild(l); existing == nil {
			return false
		} else {
			currentNode = existing //use existing node
		}
	}

	return currentNode.isTerminal
}

type CompoundQueueData struct {
	word   string
	suffix string
}

func (this *Trie) LongestCompoundWord(words []string) string {
	queue := list.New()

	//Insertion-loop
	for _, word := range words {
		word = strings.ToLower(word)
		this.Insert(word)
	}

	/*
		Initial suffix extraction loop. Done here So I don't need to sort the data first, hence
		reducing the time complexity and increasing the speed of the algorithm.
	*/
	for _, word := range words {
		lword := strings.ToLower(word)
		prefixes := this.FindAllPrefixes(lword)
		for _, prefix := range prefixes {
			queue.PushBack(CompoundQueueData{word: word, suffix: strings.Replace(lword, prefix, "", 1)})
		}

	}

	//Further Processing
	longestCompoundWord := ""
	maxLength := 0

	for e := queue.Front(); e != nil; e = e.Next() {
		pair := e.Value.(CompoundQueueData)
		//fmt.Println(pair)
		//shorting make-shift queue as we run to conserve memory....and to act like a queue.
		if prev := e.Prev(); prev != nil {
			queue.Remove(prev)
		}

		if this.HasWord(pair.suffix) && len(pair.word) > maxLength {
			longestCompoundWord = pair.word
		} else {
			prefixes := this.FindAllPrefixes(pair.suffix)
			for _, prefix := range prefixes {
				queue.PushBack(CompoundQueueData{word: pair.word, suffix: strings.Replace(pair.suffix, prefix, "", -1)})
			}
		}
	}

	return longestCompoundWord
}

func (this *Trie) DisplayToConsole() {
	fmt.Println("\n Displaying Tree...\n")
	this.root.Displayf(0)
}
<commit_msg>Rectified maxLength issue<commit_after>package trie

import (
	"container/list"
	"fmt"
	"strings"
)

type Trie struct {
	root Node
}

func NewTrie() *Trie {
	var trie = Trie{
		root: Node{letter: rune(0)},
	}

	return &trie
}

func (this *Trie) Insert(word string) {

	currentNode := &this.root
	word = strings.ToLower(word)

	for _, l := range word {

		if existing := currentNode.FindChild(l); existing == nil {
			currentNode = currentNode.AddChild(l, false) //insert new node
			//fmt.Printf(" '%c' ", currentNode.letter)
		} else {

			currentNode = existing //use existing node
			//fmt.Printf(" \\'%c' ", l)
		}
	}

	currentNode.isTerminal = true
	//fmt.Print("\n")
}

func (this *Trie) FindAllPrefixes(word string) []string {
	var prefixrunes []rune
	var prefixes []string

	currentNode := &this.root

	for _, l := range word {

		if existing := currentNode.FindChild(l); existing == nil {
			return prefixes
		} else {
			currentNode = existing //use existing node
		}

		prefixrunes = append(prefixrunes, currentNode.letter)

		if currentNode.isTerminal == true {
			prefixes = append(prefixes, string(prefixrunes))
		}
	}

	return prefixes
}

func (this *Trie) HasWord(word string) bool {

	currentNode := &this.root

	for _, l := range word {

		if existing := currentNode.FindChild(l); existing == nil {
			return false
		} else {
			currentNode = existing //use existing node
		}
	}

	return currentNode.isTerminal
}

type CompoundQueueData struct {
	word   string
	suffix string
}

func (this *Trie) LongestCompoundWord(words []string) string {
	queue := list.New()

	//Insertion-loop
	for _, word := range words {
		word = strings.ToLower(word)
		this.Insert(word)
	}

	/*
		Initial suffix extraction loop. Done here So I don't need to sort the data first, hence
		reducing the time complexity and increasing the speed of the algorithm.
	*/
	for _, word := range words {
		lword := strings.ToLower(word)
		prefixes := this.FindAllPrefixes(lword)
		for _, prefix := range prefixes {
			queue.PushBack(CompoundQueueData{word: word, suffix: strings.Replace(lword, prefix, "", 1)})
		}

	}

	//Further Processing
	longestCompoundWord := ""
	maxLength := 0

	for e := queue.Front(); e != nil; e = e.Next() {
		pair := e.Value.(CompoundQueueData)
		//fmt.Println(pair)
		//shorting make-shift queue as we run to conserve memory....and to act like a queue.
		if prev := e.Prev(); prev != nil {
			queue.Remove(prev)
		}

		if this.HasWord(pair.suffix) && len(pair.word) > maxLength {
			longestCompoundWord = pair.word
			maxLength = len(longestCompoundWord)
			//fmt.Println(pair)
		} else {
			prefixes := this.FindAllPrefixes(pair.suffix)
			for _, prefix := range prefixes {
				queue.PushBack(CompoundQueueData{word: pair.word, suffix: strings.Replace(pair.suffix, prefix, "", 1)})
			}
		}
	}

	return longestCompoundWord
}

func (this *Trie) DisplayToConsole() {
	fmt.Println("\n Displaying Tree...\n")
	this.root.Displayf(0)
}
<|endoftext|>


<commit_before>//go:build !linux && !freebsd

package tuntap

const flagTruncated = 0

func createInterface(ifPattern string, kind DevKind) (*Interface, error) {
	panic("tuntap: Not implemented on this platform")
}
<commit_msg>Add stubs for non-linux, non-freebsd platforms to fix builds on MacOS.<commit_after>//go:build !linux && !freebsd

package tuntap

import (
	"net"
)

const flagTruncated = 0

func createInterface(ifPattern string, kind DevKind) (*Interface, error) {
	panic("tuntap: Not implemented on this platform")
}

// IPv6SLAAC enables/disables stateless address auto-configuration (SLAAC) for the interface.
func (t *Interface) IPv6SLAAC(ctrl bool) error {
	panic("tuntap: Not implemented on this platform")
}

// IPv6Forwarding enables/disables ipv6 forwarding for the interface.
func (t *Interface) IPv6Forwarding(ctrl bool) error {
	panic("tuntap: Not implemented on this platform")
}

// IPv6 enables/disable ipv6 for the interface.
func (t *Interface) IPv6(ctrl bool) error {
	panic("tuntap: Not implemented on this platform")
}

// AddAddress adds an IP address to the tunnel interface.
func (t *Interface) AddAddress(ip net.IP, subnet *net.IPNet) error {
	panic("tuntap: Not implemented on this platform")
}

// SetMTU sets the tunnel interface MTU size.
func (t *Interface) SetMTU(mtu int) error {
	panic("tuntap: Not implemented on this platform")
}

// Up sets the tunnel interface to the UP state.
func (t *Interface) Up() error {
	panic("tuntap: Not implemented on this platform")
}

// GetAddrList returns the IP addresses (as bytes) associated with the interface.
func (t *Interface) GetAddrList() ([][]byte, error) {
	panic("tuntap: Not implemented on this platform")
}
<|endoftext|>


<commit_before>/*
Copyright 2017 - The TXTdirect Authors
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 txtdirect

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/mholt/caddy/caddyhttp/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	basezone        = "_redirect"
	defaultSub      = "www"
	defaultProtocol = "https"
	proxyKeepalive  = 30
	logFormat       = "02/Jan/2006:15:04:05 -0700"
	proxyTimeout    = 30 * time.Second
)

var PlaceholderRegex = regexp.MustCompile("{[~>?]?\\w+}")

type record struct {
	Version string
	To      string
	Code    int
	Type    string
	Vcs     string
	From    string
	Root    string
	Re      string
}

// Config contains the middleware's configuration
type Config struct {
	Enable     []string
	Redirect   string
	Resolver   string
	Prometheus Prometheus
}

func (r *record) Parse(str string, req *http.Request) error {
	s := strings.Split(str, ";")
	for _, l := range s {
		switch {
		case strings.HasPrefix(l, "code="):
			l = strings.TrimPrefix(l, "code=")
			i, err := strconv.Atoi(l)
			if err != nil {
				return fmt.Errorf("could not parse status code: %s", err)
			}
			r.Code = i

		case strings.HasPrefix(l, "from="):
			l = strings.TrimPrefix(l, "from=")
			l, err := parsePlaceholders(l, req)
			if err != nil {
				return err
			}
			r.From = l

		case strings.HasPrefix(l, "re="):
			l = strings.TrimPrefix(l, "re=")
			r.Re = l

		case strings.HasPrefix(l, "root="):
			l = strings.TrimPrefix(l, "root=")
			r.Root = l

		case strings.HasPrefix(l, "to="):
			l = strings.TrimPrefix(l, "to=")
			l, err := parsePlaceholders(l, req)
			if err != nil {
				return err
			}
			r.To = l

		case strings.HasPrefix(l, "type="):
			l = strings.TrimPrefix(l, "type=")
			r.Type = l

		case strings.HasPrefix(l, "v="):
			l = strings.TrimPrefix(l, "v=")
			r.Version = l
			if r.Version != "txtv0" {
				return fmt.Errorf("unhandled version '%s'", r.Version)
			}
			log.Print("WARN: txtv0 is not suitable for production")

		case strings.HasPrefix(l, "vcs="):
			l = strings.TrimPrefix(l, "vcs=")
			r.Vcs = l

		default:
			tuple := strings.Split(l, "=")
			if len(tuple) != 2 {
				return fmt.Errorf("arbitrary data not allowed")
			}
			continue
		}
		if len(l) > 255 {
			return fmt.Errorf("TXT record cannot exceed the maximum of 255 characters")
		}
	}

	if r.Code == 0 {
		r.Code = http.StatusMovedPermanently
	}

	if r.Vcs == "" && r.Type == "gometa" {
		r.Vcs = "git"
	}

	if r.Type == "" {
		r.Type = "host"
	}

	return nil
}

func getBaseTarget(rec record, r *http.Request) (string, int, error) {
	if strings.ContainsAny(rec.To, "{}") {
		to, err := parsePlaceholders(rec.To, r)
		if err != nil {
			return "", 0, err
		}
		rec.To = to
	}
	return rec.To, rec.Code, nil
}

func contains(array []string, word string) bool {
	for _, w := range array {
		if w == word {
			return true
		}
	}
	return false
}

func getRecord(host, path string, ctx context.Context, c Config, r *http.Request) (record, error) {
	txts, err := query(host, ctx, c)
	if err != nil {
		return record{}, err
	}

	if len(txts) != 1 {
		return record{}, fmt.Errorf("could not parse TXT record with %d records", len(txts))
	}

	rec := record{}
	if err = rec.Parse(txts[0], r); err != nil {
		return rec, fmt.Errorf("could not parse record: %s", err)
	}

	return rec, nil
}

func fallback(w http.ResponseWriter, r *http.Request, fallback string, code int, c Config) {
	if fallback != "" {
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), fallback)
		http.Redirect(w, r, fallback, code)
		RequestsByStatus.WithLabelValues(r.URL.Host, string(code)).Add(1)
	} else if c.Redirect != "" {
		for _, enable := range c.Enable {
			if enable == "www" {
				log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), c.Redirect)
				http.Redirect(w, r, c.Redirect, http.StatusForbidden)
				RequestsByStatus.WithLabelValues(r.URL.Host, string(http.StatusForbidden)).Add(1)
			}
		}
	} else {
		http.NotFound(w, r)
	}
}

func customResolver(c Config) net.Resolver {
	return net.Resolver{
		PreferGo: true,
		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
			d := net.Dialer{}
			return d.DialContext(ctx, network, c.Resolver)
		},
	}
}

func query(zone string, ctx context.Context, c Config) ([]string, error) {
	// Removes port from zone
	if strings.Contains(zone, ":") {
		zoneSlice := strings.Split(zone, ":")
		zone = zoneSlice[0]
	}

	if !strings.HasPrefix(zone, basezone) {
		zone = strings.Join([]string{basezone, zone}, ".")
	}

	// Use absolute zone
	var absoluteZone string
	if strings.HasSuffix(zone, ".") {
		absoluteZone = zone
	} else {
		absoluteZone = strings.Join([]string{zone, "."}, "")
	}

	var txts []string
	var err error
	if c.Resolver != "" {
		net := customResolver(c)
		txts, err = net.LookupTXT(ctx, absoluteZone)
	} else {
		txts, err = net.LookupTXT(absoluteZone)
	}
	if err != nil {
		return nil, fmt.Errorf("could not get TXT record: %s", err)
	}
	return txts, nil
}

// Redirect the request depending on the redirect record found
func Redirect(w http.ResponseWriter, r *http.Request, c Config) error {
	if strings.Contains(r.URL.Path, "/metrics") {
		promhttp.Handler().ServeHTTP(w, r)
		return nil
	}
	host := r.Host
	path := r.URL.Path
	RequestsCount.WithLabelValues(host).Add(1)

	bl := make(map[string]bool)
	bl["/favicon.ico"] = true

	if bl[path] {
		redirect := strings.Join([]string{host, path}, "")
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), redirect)
		http.Redirect(w, r, redirect, http.StatusOK)
		RequestsByStatus.WithLabelValues(host, string(http.StatusOK)).Add(1)
		return nil
	}

	rec, err := getRecord(host, path, r.Context(), c, r)
	if err != nil {
		if strings.HasSuffix(err.Error(), "no such host") {
			if c.Redirect != "" {
				log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), c.Redirect)
				http.Redirect(w, r, c.Redirect, http.StatusMovedPermanently)
				RequestsByStatus.WithLabelValues(host, string(http.StatusMovedPermanently)).Add(1)
				return nil
			}
			if contains(c.Enable, "www") {
				s := strings.Join([]string{defaultProtocol, "://", defaultSub, ".", host}, "")
				log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), s)
				http.Redirect(w, r, s, http.StatusMovedPermanently)
				RequestsByStatus.WithLabelValues(host, string(http.StatusMovedPermanently)).Add(1)
				return nil
			}
			http.NotFound(w, r)
			RequestsByStatus.WithLabelValues(host, string(http.StatusNotFound)).Add(1)
			return nil
		}
		return err
	}

	if !contains(c.Enable, rec.Type) {
		return fmt.Errorf("option disabled")
	}

	fallbackURL, code, err := getBaseTarget(rec, r)
	if err != nil {
		return err
	}

	if rec.Re != "" && rec.From != "" {
		fallback(w, r, fallbackURL, code, c)
		return nil
	}

	if rec.Type == "path" && contains(c.Enable, rec.Type) {
		if path == "/" {
			if rec.Root == "" {
				fallback(w, r, fallbackURL, code, c)
				return nil
			}
			log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), rec.Root)
			http.Redirect(w, r, rec.Root, rec.Code)
			RequestsByStatus.WithLabelValues(host, string(rec.Code)).Add(1)
			return nil
		}

		if path != "" {
			zone, from, err := zoneFromPath(host, path, rec)
			rec, err = getFinalRecord(zone, from, r.Context(), c, r)
			if err != nil {
				log.Print("Fallback is triggered because an error has occurred: ", err)
				fallback(w, r, fallbackURL, code, c)
				return nil
			}
		}
	}

	if rec.Type == "proxy" && contains(c.Enable, rec.Type) {
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), rec.From, rec.To)
		to, _, err := getBaseTarget(rec, r)
		if err != nil {
			log.Print("Fallback is triggered because an error has occurred: ", err)
			fallback(w, r, fallbackURL, code, c)
			return err
		}
		u, err := url.Parse(to)
		if err != nil {
			return err
		}
		reverseProxy := proxy.NewSingleHostReverseProxy(u, "", proxyKeepalive, proxyTimeout)
		reverseProxy.ServeHTTP(w, r, nil)
		return nil
	}

	if rec.Type == "host" && contains(c.Enable, rec.Type) {
		to, code, err := getBaseTarget(rec, r)
		if err != nil {
			log.Print("Fallback is triggered because an error has occurred: ", err)
			fallback(w, r, fallbackURL, code, c)
			return err
		}
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), to)
		http.Redirect(w, r, to, code)
		RequestsByStatus.WithLabelValues(host, string(code)).Add(1)
		return nil
	}

	if rec.Type == "gometa" && contains(c.Enable, rec.Type) {
		return gometa(w, rec, host, path)
	}

	return fmt.Errorf("record type %s unsupported", rec.Type)
}
<commit_msg>Only increment counters if Prometheus is enabled<commit_after>/*
Copyright 2017 - The TXTdirect Authors
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 txtdirect

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/mholt/caddy/caddyhttp/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	basezone        = "_redirect"
	defaultSub      = "www"
	defaultProtocol = "https"
	proxyKeepalive  = 30
	logFormat       = "02/Jan/2006:15:04:05 -0700"
	proxyTimeout    = 30 * time.Second
)

var PlaceholderRegex = regexp.MustCompile("{[~>?]?\\w+}")

type record struct {
	Version string
	To      string
	Code    int
	Type    string
	Vcs     string
	From    string
	Root    string
	Re      string
}

// Config contains the middleware's configuration
type Config struct {
	Enable     []string
	Redirect   string
	Resolver   string
	Prometheus Prometheus
}

func (r *record) Parse(str string, req *http.Request) error {
	s := strings.Split(str, ";")
	for _, l := range s {
		switch {
		case strings.HasPrefix(l, "code="):
			l = strings.TrimPrefix(l, "code=")
			i, err := strconv.Atoi(l)
			if err != nil {
				return fmt.Errorf("could not parse status code: %s", err)
			}
			r.Code = i

		case strings.HasPrefix(l, "from="):
			l = strings.TrimPrefix(l, "from=")
			l, err := parsePlaceholders(l, req)
			if err != nil {
				return err
			}
			r.From = l

		case strings.HasPrefix(l, "re="):
			l = strings.TrimPrefix(l, "re=")
			r.Re = l

		case strings.HasPrefix(l, "root="):
			l = strings.TrimPrefix(l, "root=")
			r.Root = l

		case strings.HasPrefix(l, "to="):
			l = strings.TrimPrefix(l, "to=")
			l, err := parsePlaceholders(l, req)
			if err != nil {
				return err
			}
			r.To = l

		case strings.HasPrefix(l, "type="):
			l = strings.TrimPrefix(l, "type=")
			r.Type = l

		case strings.HasPrefix(l, "v="):
			l = strings.TrimPrefix(l, "v=")
			r.Version = l
			if r.Version != "txtv0" {
				return fmt.Errorf("unhandled version '%s'", r.Version)
			}
			log.Print("WARN: txtv0 is not suitable for production")

		case strings.HasPrefix(l, "vcs="):
			l = strings.TrimPrefix(l, "vcs=")
			r.Vcs = l

		default:
			tuple := strings.Split(l, "=")
			if len(tuple) != 2 {
				return fmt.Errorf("arbitrary data not allowed")
			}
			continue
		}
		if len(l) > 255 {
			return fmt.Errorf("TXT record cannot exceed the maximum of 255 characters")
		}
	}

	if r.Code == 0 {
		r.Code = http.StatusMovedPermanently
	}

	if r.Vcs == "" && r.Type == "gometa" {
		r.Vcs = "git"
	}

	if r.Type == "" {
		r.Type = "host"
	}

	return nil
}

func getBaseTarget(rec record, r *http.Request) (string, int, error) {
	if strings.ContainsAny(rec.To, "{}") {
		to, err := parsePlaceholders(rec.To, r)
		if err != nil {
			return "", 0, err
		}
		rec.To = to
	}
	return rec.To, rec.Code, nil
}

func contains(array []string, word string) bool {
	for _, w := range array {
		if w == word {
			return true
		}
	}
	return false
}

func getRecord(host, path string, ctx context.Context, c Config, r *http.Request) (record, error) {
	txts, err := query(host, ctx, c)
	if err != nil {
		return record{}, err
	}

	if len(txts) != 1 {
		return record{}, fmt.Errorf("could not parse TXT record with %d records", len(txts))
	}

	rec := record{}
	if err = rec.Parse(txts[0], r); err != nil {
		return rec, fmt.Errorf("could not parse record: %s", err)
	}

	return rec, nil
}

func fallback(w http.ResponseWriter, r *http.Request, fallback string, code int, c Config) {
	if fallback != "" {
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), fallback)
		http.Redirect(w, r, fallback, code)
		if c.Prometheus.Enable {
			RequestsByStatus.WithLabelValues(r.URL.Host, string(code)).Add(1)
		}
	} else if c.Redirect != "" {
		for _, enable := range c.Enable {
			if enable == "www" {
				log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), c.Redirect)
				http.Redirect(w, r, c.Redirect, http.StatusForbidden)
				if c.Prometheus.Enable {
					RequestsByStatus.WithLabelValues(r.URL.Host, string(http.StatusForbidden)).Add(1)
				}
			}
		}
	} else {
		http.NotFound(w, r)
	}
}

func customResolver(c Config) net.Resolver {
	return net.Resolver{
		PreferGo: true,
		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
			d := net.Dialer{}
			return d.DialContext(ctx, network, c.Resolver)
		},
	}
}

func query(zone string, ctx context.Context, c Config) ([]string, error) {
	// Removes port from zone
	if strings.Contains(zone, ":") {
		zoneSlice := strings.Split(zone, ":")
		zone = zoneSlice[0]
	}

	if !strings.HasPrefix(zone, basezone) {
		zone = strings.Join([]string{basezone, zone}, ".")
	}

	// Use absolute zone
	var absoluteZone string
	if strings.HasSuffix(zone, ".") {
		absoluteZone = zone
	} else {
		absoluteZone = strings.Join([]string{zone, "."}, "")
	}

	var txts []string
	var err error
	if c.Resolver != "" {
		net := customResolver(c)
		txts, err = net.LookupTXT(ctx, absoluteZone)
	} else {
		txts, err = net.LookupTXT(absoluteZone)
	}
	if err != nil {
		return nil, fmt.Errorf("could not get TXT record: %s", err)
	}
	return txts, nil
}

// Redirect the request depending on the redirect record found
func Redirect(w http.ResponseWriter, r *http.Request, c Config) error {
	if strings.Contains(r.URL.Path, "/metrics") {
		promhttp.Handler().ServeHTTP(w, r)
		return nil
	}
	host := r.Host
	path := r.URL.Path
	if c.Prometheus.Enable {
		RequestsCount.WithLabelValues(host).Add(1)
	}
	bl := make(map[string]bool)
	bl["/favicon.ico"] = true

	if bl[path] {
		redirect := strings.Join([]string{host, path}, "")
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), redirect)
		http.Redirect(w, r, redirect, http.StatusOK)
		if c.Prometheus.Enable {
			RequestsByStatus.WithLabelValues(host, string(http.StatusOK)).Add(1)
		}
		return nil
	}

	rec, err := getRecord(host, path, r.Context(), c, r)
	if err != nil {
		if strings.HasSuffix(err.Error(), "no such host") {
			if c.Redirect != "" {
				log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), c.Redirect)
				http.Redirect(w, r, c.Redirect, http.StatusMovedPermanently)
				if c.Prometheus.Enable {
					RequestsByStatus.WithLabelValues(host, string(http.StatusMovedPermanently)).Add(1)
				}
				return nil
			}
			if contains(c.Enable, "www") {
				s := strings.Join([]string{defaultProtocol, "://", defaultSub, ".", host}, "")
				log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), s)
				http.Redirect(w, r, s, http.StatusMovedPermanently)
				if c.Prometheus.Enable {
					RequestsByStatus.WithLabelValues(host, string(http.StatusMovedPermanently)).Add(1)
				}
				return nil
			}
			http.NotFound(w, r)
			if c.Prometheus.Enable {
				RequestsByStatus.WithLabelValues(host, string(http.StatusNotFound)).Add(1)
			}
			return nil
		}
		return err
	}

	if !contains(c.Enable, rec.Type) {
		return fmt.Errorf("option disabled")
	}

	fallbackURL, code, err := getBaseTarget(rec, r)
	if err != nil {
		return err
	}

	if rec.Re != "" && rec.From != "" {
		fallback(w, r, fallbackURL, code, c)
		return nil
	}

	if rec.Type == "path" && contains(c.Enable, rec.Type) {
		if path == "/" {
			if rec.Root == "" {
				fallback(w, r, fallbackURL, code, c)
				return nil
			}
			log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), rec.Root)
			http.Redirect(w, r, rec.Root, rec.Code)
			if c.Prometheus.Enable {
				RequestsByStatus.WithLabelValues(host, string(rec.Code)).Add(1)
			}
			return nil
		}

		if path != "" {
			zone, from, err := zoneFromPath(host, path, rec)
			rec, err = getFinalRecord(zone, from, r.Context(), c, r)
			if err != nil {
				log.Print("Fallback is triggered because an error has occurred: ", err)
				fallback(w, r, fallbackURL, code, c)
				return nil
			}
		}
	}

	if rec.Type == "proxy" && contains(c.Enable, rec.Type) {
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), rec.From, rec.To)
		to, _, err := getBaseTarget(rec, r)
		if err != nil {
			log.Print("Fallback is triggered because an error has occurred: ", err)
			fallback(w, r, fallbackURL, code, c)
			return err
		}
		u, err := url.Parse(to)
		if err != nil {
			return err
		}
		reverseProxy := proxy.NewSingleHostReverseProxy(u, "", proxyKeepalive, proxyTimeout)
		reverseProxy.ServeHTTP(w, r, nil)
		return nil
	}

	if rec.Type == "host" && contains(c.Enable, rec.Type) {
		to, code, err := getBaseTarget(rec, r)
		if err != nil {
			log.Print("Fallback is triggered because an error has occurred: ", err)
			fallback(w, r, fallbackURL, code, c)
			return err
		}
		log.Printf("<%s> [txtdirect]: %s > %s", time.Now().Format(logFormat), r.URL.String(), to)
		http.Redirect(w, r, to, code)
		if c.Prometheus.Enable {
			RequestsByStatus.WithLabelValues(host, string(code)).Add(1)
		}
		return nil
	}

	if rec.Type == "gometa" && contains(c.Enable, rec.Type) {
		return gometa(w, rec, host, path)
	}

	return fmt.Errorf("record type %s unsupported", rec.Type)
}
<|endoftext|>


<commit_before>// Copyright 2017 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 ssa

import "math"

func softfloat(f *Func) {
	if !f.Config.SoftFloat {
		return
	}
	newInt64 := false

	for _, b := range f.Blocks {
		for _, v := range b.Values {
			if v.Type.IsFloat() {
				switch v.Op {
				case OpPhi, OpLoad, OpArg:
					if v.Type.Size() == 4 {
						v.Type = f.Config.Types.UInt32
					} else {
						v.Type = f.Config.Types.UInt64
					}
				case OpConst32F:
					v.Op = OpConst32
					v.Type = f.Config.Types.UInt32
					v.AuxInt = int64(int32(math.Float32bits(auxTo32F(v.AuxInt))))
				case OpConst64F:
					v.Op = OpConst64
					v.Type = f.Config.Types.UInt64
				case OpNeg32F:
					arg0 := v.Args[0]
					v.reset(OpXor32)
					v.Type = f.Config.Types.UInt32
					v.AddArg(arg0)
					mask := v.Block.NewValue0(v.Pos, OpConst32, v.Type)
					mask.AuxInt = -0x80000000
					v.AddArg(mask)
				case OpNeg64F:
					arg0 := v.Args[0]
					v.reset(OpXor64)
					v.Type = f.Config.Types.UInt64
					v.AddArg(arg0)
					mask := v.Block.NewValue0(v.Pos, OpConst64, v.Type)
					mask.AuxInt = -0x8000000000000000
					v.AddArg(mask)
				case OpRound32F:
					v.Op = OpCopy
					v.Type = f.Config.Types.UInt32
				case OpRound64F:
					v.Op = OpCopy
					v.Type = f.Config.Types.UInt64
				}
				newInt64 = newInt64 || v.Type.Size() == 8
			}
		}
	}

	if newInt64 && f.Config.RegSize == 4 {
		// On 32bit arch, decompose Uint64 introduced in the switch above.
		decomposeBuiltIn(f)
		applyRewrite(f, rewriteBlockdec64, rewriteValuedec64)
	}

}
<commit_msg>cmd/compile: use correct store types in softfloat<commit_after>// Copyright 2017 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 ssa

import (
	"cmd/compile/internal/types"
	"math"
)

func softfloat(f *Func) {
	if !f.Config.SoftFloat {
		return
	}
	newInt64 := false

	for _, b := range f.Blocks {
		for _, v := range b.Values {
			if v.Type.IsFloat() {
				switch v.Op {
				case OpPhi, OpLoad, OpArg:
					if v.Type.Size() == 4 {
						v.Type = f.Config.Types.UInt32
					} else {
						v.Type = f.Config.Types.UInt64
					}
				case OpConst32F:
					v.Op = OpConst32
					v.Type = f.Config.Types.UInt32
					v.AuxInt = int64(int32(math.Float32bits(auxTo32F(v.AuxInt))))
				case OpConst64F:
					v.Op = OpConst64
					v.Type = f.Config.Types.UInt64
				case OpNeg32F:
					arg0 := v.Args[0]
					v.reset(OpXor32)
					v.Type = f.Config.Types.UInt32
					v.AddArg(arg0)
					mask := v.Block.NewValue0(v.Pos, OpConst32, v.Type)
					mask.AuxInt = -0x80000000
					v.AddArg(mask)
				case OpNeg64F:
					arg0 := v.Args[0]
					v.reset(OpXor64)
					v.Type = f.Config.Types.UInt64
					v.AddArg(arg0)
					mask := v.Block.NewValue0(v.Pos, OpConst64, v.Type)
					mask.AuxInt = -0x8000000000000000
					v.AddArg(mask)
				case OpRound32F:
					v.Op = OpCopy
					v.Type = f.Config.Types.UInt32
				case OpRound64F:
					v.Op = OpCopy
					v.Type = f.Config.Types.UInt64
				}
				newInt64 = newInt64 || v.Type.Size() == 8
			} else if (v.Op == OpStore || v.Op == OpZero || v.Op == OpMove) && v.Aux.(*types.Type).IsFloat() {
				switch size := v.Aux.(*types.Type).Size(); size {
				case 4:
					v.Aux = f.Config.Types.UInt32
				case 8:
					v.Aux = f.Config.Types.UInt64
				default:
					v.Fatalf("bad float type with size %d", size)
				}
			}
		}
	}

	if newInt64 && f.Config.RegSize == 4 {
		// On 32bit arch, decompose Uint64 introduced in the switch above.
		decomposeBuiltIn(f)
		applyRewrite(f, rewriteBlockdec64, rewriteValuedec64)
	}

}
<|endoftext|>