<commit_before>/*

aufs driver directory structure

.
├── layers // Metadata of layers
│   ├── 1
│   ├── 2
│   └── 3
├── diffs  // Content of the layer
│   ├── 1  // Contains layers that need to be mounted for the id
│   ├── 2
│   └── 3
└── mnt    // Mount points for the rw layers to be mounted
    ├── 1
    ├── 2
    └── 3

*/

package aufs

import (
	"bufio"
	"fmt"
	"github.com/dotcloud/docker/archive"
	"github.com/dotcloud/docker/graphdriver"
	mountpk "github.com/dotcloud/docker/pkg/mount"
	"github.com/dotcloud/docker/utils"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
)

func init() {
	graphdriver.Register("aufs", Init)
}

type Driver struct {
	root       string
	sync.Mutex // Protects concurrent modification to active
	active     map[string]int
}

// New returns a new AUFS driver.
// An error is returned if AUFS is not supported.
func Init(root string) (graphdriver.Driver, error) {
	// Try to load the aufs kernel module
	if err := supportsAufs(); err != nil {
		return nil, err
	}
	paths := []string{
		"mnt",
		"diff",
		"layers",
	}

	a := &Driver{
		root:   root,
		active: make(map[string]int),
	}

	// Create the root aufs driver dir and return
	// if it already exists
	// If not populate the dir structure
	if err := os.MkdirAll(root, 0755); err != nil {
		if os.IsExist(err) {
			return a, nil
		}
		return nil, err
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
			return nil, err
		}
	}
	return a, nil
}

// Return a nil error if the kernel supports aufs
// We cannot modprobe because inside dind modprobe fails
// to run
func supportsAufs() error {
	// We can try to modprobe aufs first before looking at
	// proc/filesystems for when aufs is supported
	exec.Command("modprobe", "aufs").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		return err
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if strings.Contains(s.Text(), "aufs") {
			return nil
		}
	}
	return fmt.Errorf("AUFS was not found in /proc/filesystems")
}

func (a Driver) rootPath() string {
	return a.root
}

func (Driver) String() string {
	return "aufs"
}

func (a Driver) Status() [][2]string {
	ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
	return [][2]string{
		{"Root Dir", a.rootPath()},
		{"Dirs", fmt.Sprintf("%d", len(ids))},
	}
}

// Exists returns true if the given id is registered with
// this driver
func (a Driver) Exists(id string) bool {
	if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
		return false
	}
	return true
}

// Three folders are created for each id
// mnt, layers, and diff
func (a *Driver) Create(id, parent string) error {
	if err := a.createDirsFor(id); err != nil {
		return err
	}
	// Write the layers metadata
	f, err := os.Create(path.Join(a.rootPath(), "layers", id))
	if err != nil {
		return err
	}
	defer f.Close()

	if parent != "" {
		ids, err := getParentIds(a.rootPath(), parent)
		if err != nil {
			return err
		}

		if _, err := fmt.Fprintln(f, parent); err != nil {
			return err
		}
		for _, i := range ids {
			if _, err := fmt.Fprintln(f, i); err != nil {
				return err
			}
		}
	}
	return nil
}

func (a *Driver) createDirsFor(id string) error {
	paths := []string{
		"mnt",
		"diff",
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
			return err
		}
	}
	return nil
}

// Unmount and remove the dir information
func (a *Driver) Remove(id string) error {
	// Protect the a.active from concurrent access
	a.Lock()
	defer a.Unlock()

	if a.active[id] != 0 {
		utils.Errorf("Warning: removing active id %s\n", id)
	}

	// Make sure the dir is umounted first
	if err := a.unmount(id); err != nil {
		return err
	}
	tmpDirs := []string{
		"mnt",
		"diff",
	}

	// Remove the dirs atomically
	for _, p := range tmpDirs {
		// We need to use a temp dir in the same dir as the driver so Rename
		// does not fall back to the slow copy if /tmp and the driver dir
		// are on different devices
		tmp := path.Join(a.rootPath(), "tmp", p, id)
		if err := os.MkdirAll(tmp, 0755); err != nil {
			return err
		}
		realPath := path.Join(a.rootPath(), p, id)
		if err := os.Rename(realPath, tmp); err != nil && !os.IsNotExist(err) {
			return err
		}
		defer os.RemoveAll(tmp)
	}

	// Remove the layers file for the id
	if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Return the rootfs path for the id
// This will mount the dir at it's given path
func (a *Driver) Get(id string) (string, error) {
	ids, err := getParentIds(a.rootPath(), id)
	if err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
		ids = []string{}
	}

	// Protect the a.active from concurrent access
	a.Lock()
	defer a.Unlock()

	count := a.active[id]

	// If a dir does not have a parent ( no layers )do not try to mount
	// just return the diff path to the data
	out := path.Join(a.rootPath(), "diff", id)
	if len(ids) > 0 {
		out = path.Join(a.rootPath(), "mnt", id)

		if count == 0 {
			if err := a.mount(id); err != nil {
				return "", err
			}
		}
	}

	a.active[id] = count + 1

	return out, nil
}

func (a *Driver) Put(id string) {
	// Protect the a.active from concurrent access
	a.Lock()
	defer a.Unlock()

	if count := a.active[id]; count > 1 {
		a.active[id] = count - 1
	} else {
		ids, _ := getParentIds(a.rootPath(), id)
		// We only mounted if there are any parents
		if ids != nil && len(ids) > 0 {
			a.unmount(id)
		}
		delete(a.active, id)
	}
}

// Returns an archive of the contents for the id
func (a *Driver) Diff(id string) (archive.Archive, error) {
	return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
		Compression: archive.Uncompressed,
	})
}

func (a *Driver) ApplyDiff(id string, diff archive.Archive) error {
	return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
}

// Returns the size of the contents for the id
func (a *Driver) DiffSize(id string) (int64, error) {
	return utils.TreeSize(path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) Changes(id string) ([]archive.Change, error) {
	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return nil, err
	}
	return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
	parentIds, err := getParentIds(a.rootPath(), id)
	if err != nil {
		return nil, err
	}
	if len(parentIds) == 0 {
		return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
	}
	layers := make([]string, len(parentIds))

	// Get the diff paths for all the parent ids
	for i, p := range parentIds {
		layers[i] = path.Join(a.rootPath(), "diff", p)
	}
	return layers, nil
}

func (a *Driver) mount(id string) error {
	// If the id is mounted or we get an error return
	if mounted, err := a.mounted(id); err != nil || mounted {
		return err
	}

	var (
		target = path.Join(a.rootPath(), "mnt", id)
		rw     = path.Join(a.rootPath(), "diff", id)
	)

	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return err
	}

	if err := a.aufsMount(layers, rw, target); err != nil {
		return err
	}
	return nil
}

func (a *Driver) unmount(id string) error {
	if mounted, err := a.mounted(id); err != nil || !mounted {
		return err
	}
	target := path.Join(a.rootPath(), "mnt", id)
	return Unmount(target)
}

func (a *Driver) mounted(id string) (bool, error) {
	target := path.Join(a.rootPath(), "mnt", id)
	return mountpk.Mounted(target)
}

// During cleanup aufs needs to unmount all mountpoints
func (a *Driver) Cleanup() error {
	ids, err := loadIds(path.Join(a.rootPath(), "layers"))
	if err != nil {
		return err
	}
	for _, id := range ids {
		if err := a.unmount(id); err != nil {
			utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
		}
	}
	return nil
}

func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
	defer func() {
		if err != nil {
			Unmount(target)
		}
	}()

	if err = a.tryMount(ro, rw, target); err != nil {
		if err = a.mountRw(rw, target); err != nil {
			return
		}

		for _, layer := range ro {
			branch := fmt.Sprintf("append:%s=ro+wh", layer)
			if err = mount("none", target, "aufs", MsRemount, branch); err != nil {
				return
			}
		}
	}
	return
}

// Try to mount using the aufs fast path, if this fails then
// append ro layers.
func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
	var (
		rwBranch   = fmt.Sprintf("%s=rw", rw)
		roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
	)
	return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
}

func (a *Driver) mountRw(rw, target string) error {
	return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
}

func rollbackMount(target string, err error) {
	if err != nil {
		Unmount(target)
	}
}
<commit_msg>Fix for #3674 Can't `rm` containers when disk full<commit_after>/*

aufs driver directory structure

.
├── layers // Metadata of layers
│   ├── 1
│   ├── 2
│   └── 3
├── diffs  // Content of the layer
│   ├── 1  // Contains layers that need to be mounted for the id
│   ├── 2
│   └── 3
└── mnt    // Mount points for the rw layers to be mounted
    ├── 1
    ├── 2
    └── 3

*/

package aufs

import (
	"bufio"
	"fmt"
	"github.com/dotcloud/docker/archive"
	"github.com/dotcloud/docker/graphdriver"
	mountpk "github.com/dotcloud/docker/pkg/mount"
	"github.com/dotcloud/docker/utils"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
)

func init() {
	graphdriver.Register("aufs", Init)
}

type Driver struct {
	root       string
	sync.Mutex // Protects concurrent modification to active
	active     map[string]int
}

// New returns a new AUFS driver.
// An error is returned if AUFS is not supported.
func Init(root string) (graphdriver.Driver, error) {
	// Try to load the aufs kernel module
	if err := supportsAufs(); err != nil {
		return nil, err
	}
	paths := []string{
		"mnt",
		"diff",
		"layers",
	}

	a := &Driver{
		root:   root,
		active: make(map[string]int),
	}

	// Create the root aufs driver dir and return
	// if it already exists
	// If not populate the dir structure
	if err := os.MkdirAll(root, 0755); err != nil {
		if os.IsExist(err) {
			return a, nil
		}
		return nil, err
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
			return nil, err
		}
	}
	return a, nil
}

// Return a nil error if the kernel supports aufs
// We cannot modprobe because inside dind modprobe fails
// to run
func supportsAufs() error {
	// We can try to modprobe aufs first before looking at
	// proc/filesystems for when aufs is supported
	exec.Command("modprobe", "aufs").Run()

	f, err := os.Open("/proc/filesystems")
	if err != nil {
		return err
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		if strings.Contains(s.Text(), "aufs") {
			return nil
		}
	}
	return fmt.Errorf("AUFS was not found in /proc/filesystems")
}

func (a Driver) rootPath() string {
	return a.root
}

func (Driver) String() string {
	return "aufs"
}

func (a Driver) Status() [][2]string {
	ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
	return [][2]string{
		{"Root Dir", a.rootPath()},
		{"Dirs", fmt.Sprintf("%d", len(ids))},
	}
}

// Exists returns true if the given id is registered with
// this driver
func (a Driver) Exists(id string) bool {
	if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
		return false
	}
	return true
}

// Three folders are created for each id
// mnt, layers, and diff
func (a *Driver) Create(id, parent string) error {
	if err := a.createDirsFor(id); err != nil {
		return err
	}
	// Write the layers metadata
	f, err := os.Create(path.Join(a.rootPath(), "layers", id))
	if err != nil {
		return err
	}
	defer f.Close()

	if parent != "" {
		ids, err := getParentIds(a.rootPath(), parent)
		if err != nil {
			return err
		}

		if _, err := fmt.Fprintln(f, parent); err != nil {
			return err
		}
		for _, i := range ids {
			if _, err := fmt.Fprintln(f, i); err != nil {
				return err
			}
		}
	}
	return nil
}

func (a *Driver) createDirsFor(id string) error {
	paths := []string{
		"mnt",
		"diff",
	}

	for _, p := range paths {
		if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
			return err
		}
	}
	return nil
}

// Unmount and remove the dir information
func (a *Driver) Remove(id string) error {
	// Protect the a.active from concurrent access
	a.Lock()
	defer a.Unlock()

	if a.active[id] != 0 {
		utils.Errorf("Warning: removing active id %s\n", id)
	}

	// Make sure the dir is umounted first
	if err := a.unmount(id); err != nil {
		return err
	}
	tmpDirs := []string{
		"mnt",
		"diff",
	}

	// Atomically remove each directory in turn by first moving it out of the
	// way (so that docker doesn't find it anymore) before doing removal of
	// the whole tree.
	for _, p := range tmpDirs {

		realPath := path.Join(a.rootPath(), p, id)
		tmpPath := path.Join(a.rootPath(), p, fmt.Sprintf("%s-removing", id))
		if err := os.Rename(realPath, tmpPath); err != nil && !os.IsNotExist(err) {
			return err
		}
		defer os.RemoveAll(tmpPath)
	}

	// Remove the layers file for the id
	if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Return the rootfs path for the id
// This will mount the dir at it's given path
func (a *Driver) Get(id string) (string, error) {
	ids, err := getParentIds(a.rootPath(), id)
	if err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
		ids = []string{}
	}

	// Protect the a.active from concurrent access
	a.Lock()
	defer a.Unlock()

	count := a.active[id]

	// If a dir does not have a parent ( no layers )do not try to mount
	// just return the diff path to the data
	out := path.Join(a.rootPath(), "diff", id)
	if len(ids) > 0 {
		out = path.Join(a.rootPath(), "mnt", id)

		if count == 0 {
			if err := a.mount(id); err != nil {
				return "", err
			}
		}
	}

	a.active[id] = count + 1

	return out, nil
}

func (a *Driver) Put(id string) {
	// Protect the a.active from concurrent access
	a.Lock()
	defer a.Unlock()

	if count := a.active[id]; count > 1 {
		a.active[id] = count - 1
	} else {
		ids, _ := getParentIds(a.rootPath(), id)
		// We only mounted if there are any parents
		if ids != nil && len(ids) > 0 {
			a.unmount(id)
		}
		delete(a.active, id)
	}
}

// Returns an archive of the contents for the id
func (a *Driver) Diff(id string) (archive.Archive, error) {
	return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
		Compression: archive.Uncompressed,
	})
}

func (a *Driver) ApplyDiff(id string, diff archive.Archive) error {
	return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
}

// Returns the size of the contents for the id
func (a *Driver) DiffSize(id string) (int64, error) {
	return utils.TreeSize(path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) Changes(id string) ([]archive.Change, error) {
	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return nil, err
	}
	return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
	parentIds, err := getParentIds(a.rootPath(), id)
	if err != nil {
		return nil, err
	}
	if len(parentIds) == 0 {
		return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
	}
	layers := make([]string, len(parentIds))

	// Get the diff paths for all the parent ids
	for i, p := range parentIds {
		layers[i] = path.Join(a.rootPath(), "diff", p)
	}
	return layers, nil
}

func (a *Driver) mount(id string) error {
	// If the id is mounted or we get an error return
	if mounted, err := a.mounted(id); err != nil || mounted {
		return err
	}

	var (
		target = path.Join(a.rootPath(), "mnt", id)
		rw     = path.Join(a.rootPath(), "diff", id)
	)

	layers, err := a.getParentLayerPaths(id)
	if err != nil {
		return err
	}

	if err := a.aufsMount(layers, rw, target); err != nil {
		return err
	}
	return nil
}

func (a *Driver) unmount(id string) error {
	if mounted, err := a.mounted(id); err != nil || !mounted {
		return err
	}
	target := path.Join(a.rootPath(), "mnt", id)
	return Unmount(target)
}

func (a *Driver) mounted(id string) (bool, error) {
	target := path.Join(a.rootPath(), "mnt", id)
	return mountpk.Mounted(target)
}

// During cleanup aufs needs to unmount all mountpoints
func (a *Driver) Cleanup() error {
	ids, err := loadIds(path.Join(a.rootPath(), "layers"))
	if err != nil {
		return err
	}
	for _, id := range ids {
		if err := a.unmount(id); err != nil {
			utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
		}
	}
	return nil
}

func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
	defer func() {
		if err != nil {
			Unmount(target)
		}
	}()

	if err = a.tryMount(ro, rw, target); err != nil {
		if err = a.mountRw(rw, target); err != nil {
			return
		}

		for _, layer := range ro {
			branch := fmt.Sprintf("append:%s=ro+wh", layer)
			if err = mount("none", target, "aufs", MsRemount, branch); err != nil {
				return
			}
		}
	}
	return
}

// Try to mount using the aufs fast path, if this fails then
// append ro layers.
func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
	var (
		rwBranch   = fmt.Sprintf("%s=rw", rw)
		roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
	)
	return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
}

func (a *Driver) mountRw(rw, target string) error {
	return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
}

func rollbackMount(target string, err error) {
	if err != nil {
		Unmount(target)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

type downloadResult struct {
	Page  []byte
	Error error
}

type releaseView struct {
	DownloadUrl string
	Version     string
	Md5         string
	Path        string
}

var rePackageUrl = regexp.MustCompile(`(?i)<a href=\"(?P<url>.+?)#md5=.+?\">(?P<filename>.+?)</a>`)
var reDownloadUrl = regexp.MustCompile(`(?i)<a href=\"(?P<url>.+?)\"\s+rel=\"download\">(?P<version>.+?) download_url</a>`)

func simpleIndexHandler(w http.ResponseWriter, r *http.Request) {
	renderTemplate(w, "simple/index", nil)
}

func getPage(url string) downloadResult {
	res, err := http.Get(url)
	if err != nil {
		return downloadResult{Page: []byte(""), Error: err}
	}
	page, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return downloadResult{Page: []byte(""), Error: err}
	}
	return downloadResult{Page: page, Error: err}
}

func updateProxyCache(w http.ResponseWriter, pkg Package) error {
	url := Config.PypiMirror + "/simple/" + pkg.Name

	result := getPage(url)

	if result.Error != nil {
		// We'll try next time
		return result.Error
	}

	finalizeCache(w, pkg, result.Page)
	return nil
}

func finalizeCache(w http.ResponseWriter, pkg Package, data []byte) {
	returnData := string(data)
	// Replace the local package links with links to a local proxy
	// so we can cache that result as well.
	packageUris := rePackageUrl.FindAllSubmatch(data, -1)
	for _, line := range packageUris {
		uri := line[1]
		filename := line[2]
		// TODO: This most certainly has edge cases that aren't addressed here.
		// Specifically: ".tar.gz" can't be the only filetype uploaded...
		versionSplit := strings.Split(string(uri), "-")
		almostVersion := versionSplit[len(versionSplit)-1]
		version := strings.Replace(almostVersion, ".tar.gz", "", -1)
		quoteduri := url.QueryEscape(Config.PypiMirror + "/a/b/" + string(uri))
		replaceuri := "/fondu/cached-file/" + string(filename) + "?package=" + pkg.Name + "&release=" + version + "&original=" + quoteduri + "&name=" + url.QueryEscape(string(filename))
		println("Going to replace: " + string(uri))
		println("            with: " + replaceuri)
		returnData = strings.Replace(returnData, string(uri), replaceuri, -1)
	}

	// Replace the download links with links to a local proxy so that
	// we can cache the downloads as well.
	downloadUrls := reDownloadUrl.FindAll(data, -1)
	for _, line := range downloadUrls {
		uri := line[1]
		version := line[2]
		filename := pkg.Name + "-" + string(version) + ".tar.gz"
		quotedUri := url.QueryEscape(string(uri))
		replaceUri := "/fondu/cached-file/" + filename + "?package=" + pkg.Name + "&release=" + string(version) + "&original=" + quotedUri + "&name=" + url.QueryEscape(filename)
		returnData = strings.Replace(returnData, string(uri), replaceUri, -1)
	}

	pkg.SetProxy([]byte(returnData))
}

func renderProxy(w http.ResponseWriter, pkg Package) {
	io.WriteString(w, string(pkg.ProxyData()))
}

func buildReleaseMap(pkg Package) []releaseView {
	releaseMap := []releaseView{}
	for _, rel := range pkg.Releases() {
		metadata, err := rel.Metadata()
		if err != nil {
			break
		}
		md5Json, err := metadata.Get("md5_digest").Array()
		if err != nil {
			continue
		}
		md5 := md5Json[0]

		releaseMap = append(releaseMap, releaseView{
			DownloadUrl: rel.DownloadUrl(),
			Version:     rel.Version,
			Md5:         md5.(string),
			Path:        rel.Path(),
		})
	}
	return releaseMap
}

func simpleHandler(w http.ResponseWriter, r *http.Request) {
	paths := strings.Split(r.URL.Path, "/")
	name := paths[len(paths)-2]
	pkg := Package{Name: name, DataDir: Config.DataDir}

	// The package is ours, so we serve it ourselves.
	if pkg.Exists() && !pkg.Proxied() {
		releaseMap := buildReleaseMap(pkg)
		renderTemplate(w, "simple/single", &releaseMap)
		return
	}

	// Public package, so just render the proxy
	if pkg.Proxied() {
		log.Print("Proxied package. Sending cached data.")
		go updateProxyCache(w, pkg)
		renderProxy(w, pkg)
		return
	}

	if err := updateProxyCache(w, pkg); err != nil {
		http.Error(w, "Unable to update proxy", http.StatusInternalServerError)
	}

	renderProxy(w, pkg)
}
<commit_msg>Removing unnecessary prints<commit_after>package main

import (
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

type downloadResult struct {
	Page  []byte
	Error error
}

type releaseView struct {
	DownloadUrl string
	Version     string
	Md5         string
	Path        string
}

var rePackageUrl = regexp.MustCompile(`(?i)<a href=\"(?P<url>.+?)#md5=.+?\">(?P<filename>.+?)</a>`)
var reDownloadUrl = regexp.MustCompile(`(?i)<a href=\"(?P<url>.+?)\"\s+rel=\"download\">(?P<version>.+?) download_url</a>`)

func simpleIndexHandler(w http.ResponseWriter, r *http.Request) {
	renderTemplate(w, "simple/index", nil)
}

func getPage(url string) downloadResult {
	res, err := http.Get(url)
	if err != nil {
		return downloadResult{Page: []byte(""), Error: err}
	}
	page, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return downloadResult{Page: []byte(""), Error: err}
	}
	return downloadResult{Page: page, Error: err}
}

func updateProxyCache(w http.ResponseWriter, pkg Package) error {
	url := Config.PypiMirror + "/simple/" + pkg.Name

	result := getPage(url)

	if result.Error != nil {
		// We'll try next time
		return result.Error
	}

	finalizeCache(w, pkg, result.Page)
	return nil
}

func finalizeCache(w http.ResponseWriter, pkg Package, data []byte) {
	returnData := string(data)
	// Replace the local package links with links to a local proxy
	// so we can cache that result as well.
	packageUris := rePackageUrl.FindAllSubmatch(data, -1)
	for _, line := range packageUris {
		uri := line[1]
		filename := line[2]
		// TODO: This most certainly has edge cases that aren't addressed here.
		// Specifically: ".tar.gz" can't be the only filetype uploaded...
		versionSplit := strings.Split(string(uri), "-")
		almostVersion := versionSplit[len(versionSplit)-1]
		version := strings.Replace(almostVersion, ".tar.gz", "", -1)
		quoteduri := url.QueryEscape(Config.PypiMirror + "/a/b/" + string(uri))
		replaceuri := "/fondu/cached-file/" + string(filename) + "?package=" + pkg.Name + "&release=" + version + "&original=" + quoteduri + "&name=" + url.QueryEscape(string(filename))
		returnData = strings.Replace(returnData, string(uri), replaceuri, -1)
	}

	// Replace the download links with links to a local proxy so that
	// we can cache the downloads as well.
	downloadUrls := reDownloadUrl.FindAll(data, -1)
	for _, line := range downloadUrls {
		uri := line[1]
		version := line[2]
		filename := pkg.Name + "-" + string(version) + ".tar.gz"
		quotedUri := url.QueryEscape(string(uri))
		replaceUri := "/fondu/cached-file/" + filename + "?package=" + pkg.Name + "&release=" + string(version) + "&original=" + quotedUri + "&name=" + url.QueryEscape(filename)
		returnData = strings.Replace(returnData, string(uri), replaceUri, -1)
	}

	pkg.SetProxy([]byte(returnData))
}

func renderProxy(w http.ResponseWriter, pkg Package) {
	io.WriteString(w, string(pkg.ProxyData()))
}

func buildReleaseMap(pkg Package) []releaseView {
	releaseMap := []releaseView{}
	for _, rel := range pkg.Releases() {
		metadata, err := rel.Metadata()
		if err != nil {
			break
		}
		md5Json, err := metadata.Get("md5_digest").Array()
		if err != nil {
			continue
		}
		md5 := md5Json[0]

		releaseMap = append(releaseMap, releaseView{
			DownloadUrl: rel.DownloadUrl(),
			Version:     rel.Version,
			Md5:         md5.(string),
			Path:        rel.Path(),
		})
	}
	return releaseMap
}

func simpleHandler(w http.ResponseWriter, r *http.Request) {
	paths := strings.Split(r.URL.Path, "/")
	name := paths[len(paths)-2]
	pkg := Package{Name: name, DataDir: Config.DataDir}

	// The package is ours, so we serve it ourselves.
	if pkg.Exists() && !pkg.Proxied() {
		releaseMap := buildReleaseMap(pkg)
		renderTemplate(w, "simple/single", &releaseMap)
		return
	}

	// Public package, so just render the proxy
	if pkg.Proxied() {
		log.Print("Proxied package. Sending cached data.")
		go updateProxyCache(w, pkg)
		renderProxy(w, pkg)
		return
	}

	if err := updateProxyCache(w, pkg); err != nil {
		http.Error(w, "Unable to update proxy", http.StatusInternalServerError)
	}

	renderProxy(w, pkg)
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 Lachlan Evenson

   Permission is hereby granted, free of charge, to any person obtaining
   a copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the Software
   is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package main

import (
	"fmt"
	"net/http"
)

var html = `

        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Croc Hunter</title>
        <link rel='stylesheet' href='/static/game.css'/>
        <link rel="icon" type="image/png" href="/static/favicon-16x16.png" sizes="16x16" />
        <link rel="icon" type="image/png" href="/static/favicon-32x32.png" sizes="32x32" />

    	<canvas id="canvasBg" width="800" height="490" ></canvas>
    	<canvas id="canvasEnemy" width="800" height="500" ></canvas>
    	<canvas id="canvasJet" width="800" height="500" ></canvas>
        <canvas id="canvasHud" width="800" height="500" ></canvas>

		Hostname: %s

		Release: %s

		Commit: %s

		Powered By: %s

`

func httpHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, html, hostname, release, commit, powered)
}
<commit_msg>bumped js version<commit_after>/*
   Copyright (c) 2016 Lachlan Evenson

   Permission is hereby granted, free of charge, to any person obtaining
   a copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the Software
   is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package main

import (
	"fmt"
	"net/http"
)

var html = `

        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Croc Hunter</title>
        <link rel='stylesheet' href='/static/game.css'/>
        <link rel="icon" type="image/png" href="/static/favicon-16x16.png" sizes="16x16" />
        <link rel="icon" type="image/png" href="/static/favicon-32x32.png" sizes="32x32" />

    	<canvas id="canvasBg" width="800" height="490" ></canvas>
    	<canvas id="canvasEnemy" width="800" height="500" ></canvas>
    	<canvas id="canvasJet" width="800" height="500" ></canvas>
        <canvas id="canvasHud" width="800" height="500" ></canvas>

		Hostname: %s

		Release: %s

		Commit: %s

		Powered By: %s

`

func httpHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, html, hostname, release, commit, powered)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	// TODO: Whitelist the domain via an environment variable
	if origin := r.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers",
			"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
	}

	if r.Method == "OPTIONS" {
		return
	}

	auth := r.Header.Get("X-Vip-Token")
	if auth != authToken {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	if r.ContentLength > 5<<20 {
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		uri.Scheme = "http"
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<commit_msg>set domain via env variable<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	// TODO: Whitelist the domain via an environment variable
	if domain := os.Getenv("DOMAIN_DATA"); domain != "": {
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	}

	if r.Method == "OPTIONS" {
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	if r.ContentLength > 5<<20 {
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		uri.Scheme = "http"
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<|endoftext|>


<commit_before>package cors

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

	"github.com/volatile/core"
)

const (
	// AllOrigins contains the wildcard symbol.
	AllOrigins = "*"

	// Response headers
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	// Request headers
	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// Options represents access control options for an origin.
type Options struct {
	AllowedHeaders     []string      // AllowedHeaders indicates, in the case of a preflight request, which headers can be used during the actual request. If none are set, all are allowed.
	AllowedMethods     []string      // AllowedMethods indicates, in the case of a preflight request, which methods can be used during the actual request. If none are set, all are allowed.
	CredentialsAllowed bool          // CredentialsAllowed indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.
	ExposedHeaders     []string      // ExposedHeaders whitelists headers that browsers are allowed to access.
	MaxAge             time.Duration // MaxAge indicates how long the results of a preflight request can be cached.
}

type formattedOptions struct {
	AllowedHeaders     *string
	AllowedMethods     *string
	CredentialsAllowed *string
	ExposedHeaders     *string
	MaxAge             *string
}

// Use adds a handler that sets CORS with the provided options for all dowstream requests.
func Use(options map[string]Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, c.Next)
	})
}

// LocalUse sets CORS with the provided options locally, for a single handler.
// The global options (if used) are overwritten in this situation.
func LocalUse(c *core.Context, options map[string]Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt map[string]Options) (fmtOpt map[string]formattedOptions) {
	// TODO: Remove this check as there is no other solution to accept credentials for all origins.
	if opt, ok := opt[AllOrigins]; ok && opt.CredentialsAllowed {
		panic("cors: sending credentials via CORS is not permitted for wildcarded origins")
	}

	fmtOpt = make(map[string]formattedOptions, len(opt))

	for origin, item := range opt {
		result := formattedOptions{}
		if len(item.AllowedHeaders) > 0 {
			*result.AllowedHeaders = strings.Join(item.AllowedHeaders, ", ")
		}
		if len(item.AllowedMethods) > 0 {
			*result.AllowedMethods = strings.Join(item.AllowedMethods, ", ")
		}
		if item.CredentialsAllowed {
			*result.CredentialsAllowed = "true"
		}
		if len(item.ExposedHeaders) > 0 {
			*result.ExposedHeaders = strings.Join(item.ExposedHeaders, ", ")
		}
		if item.MaxAge.Seconds() > 0.5 {
			*result.MaxAge = fmt.Sprintf("%.f", item.MaxAge.Seconds())
		}

		fmtOpt[origin] = result
	}

	return
}

// setCORS sets the response headers and continues downstream if it's not a preflight request.
func setCORS(c *core.Context, fmtOpts map[string]formattedOptions, handler func()) {
	origin := c.Request.Header.Get("Origin")

	// Use CORS only if an Origin header is defined for the request.
	if origin != "" {
		fmtOpt, ok := fmtOpts[origin]
		wildcard := false

		// Unknown origin: check for wildcard.
		if !ok {
			fmtOpt, wildcard = fmtOpts[AllOrigins]
		}

		// No origin matched and wildcard not accepted: reject the request.
		if !ok && !wildcard {
			http.Error(c.ResponseWriter, "Invalid CORS request", http.StatusForbidden)
			return
		}

		c.ResponseWriter.Header().Set(headerAllowOrigin, origin)
		if !wildcard {
			c.ResponseWriter.Header().Set("Vary", "Origin")
		}

		// Set credentials header only if they are allowed.
		if fmtOpt.CredentialsAllowed != nil {
			c.ResponseWriter.Header().Set(headerAllowCredentials, *fmtOpt.CredentialsAllowed)
		}

		if fmtOpt.ExposedHeaders != nil {
			c.ResponseWriter.Header().Set(headerExposeHeaders, *fmtOpt.ExposedHeaders)
		}

		if fmtOpt.MaxAge != nil {
			c.ResponseWriter.Header().Set(headerMaxAge, *fmtOpt.MaxAge)
		}

		// OPTIONS method is used for a preflight request.
		// In this case, other CORS headers still need to be set before sending all of them, without any other work.
		if c.Request.Method == "OPTIONS" {
			// If no allowed headers are set, all are allowed.
			if fmtOpt.AllowedHeaders == nil {
				c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
			} else {
				c.ResponseWriter.Header().Set(headerAllowHeaders, *fmtOpt.AllowedHeaders)
			}

			// If no allowed methods are set, all are allowed.
			if fmtOpt.AllowedMethods == nil {
				c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
			} else {
				c.ResponseWriter.Header().Set(headerAllowMethods, *fmtOpt.AllowedMethods)
			}

			c.ResponseWriter.WriteHeader(http.StatusOK)
			return
		}
	}

	handler()
}
<commit_msg>Remove useless checks<commit_after>package cors

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

	"github.com/volatile/core"
)

const (
	// AllOrigins contains the wildcard symbol.
	AllOrigins = "*"

	// Response headers
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	// Request headers
	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// Options represents access control options for an origin.
type Options struct {
	AllowedHeaders     []string      // AllowedHeaders indicates, in the case of a preflight request, which headers can be used during the actual request. If none are set, all are allowed.
	AllowedMethods     []string      // AllowedMethods indicates, in the case of a preflight request, which methods can be used during the actual request. If none are set, all are allowed.
	CredentialsAllowed bool          // CredentialsAllowed indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.
	ExposedHeaders     []string      // ExposedHeaders whitelists headers that browsers are allowed to access.
	MaxAge             time.Duration // MaxAge indicates how long the results of a preflight request can be cached.
}

type formattedOptions struct {
	AllowedHeaders     *string
	AllowedMethods     *string
	CredentialsAllowed *string
	ExposedHeaders     *string
	MaxAge             *string
}

// Use adds a handler that sets CORS with the provided options for all dowstream requests.
func Use(options map[string]Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, c.Next)
	})
}

// LocalUse sets CORS with the provided options locally, for a single handler.
// The global options (if used) are overwritten in this situation.
func LocalUse(c *core.Context, options map[string]Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt map[string]Options) map[string]formattedOptions {
	fmtOpt := make(map[string]formattedOptions, len(opt))

	for origin, item := range opt {
		result := formattedOptions{}
		if len(item.AllowedHeaders) > 0 {
			*result.AllowedHeaders = strings.Join(item.AllowedHeaders, ", ")
		}
		if len(item.AllowedMethods) > 0 {
			*result.AllowedMethods = strings.Join(item.AllowedMethods, ", ")
		}
		if item.CredentialsAllowed {
			*result.CredentialsAllowed = "true"
		}
		if len(item.ExposedHeaders) > 0 {
			*result.ExposedHeaders = strings.Join(item.ExposedHeaders, ", ")
		}
		if item.MaxAge.Seconds() > 0.5 {
			*result.MaxAge = fmt.Sprintf("%.f", item.MaxAge.Seconds())
		}

		fmtOpt[origin] = result
	}

	return fmtOpt
}

// setCORS sets the response headers and continues downstream if it's not a preflight request.
func setCORS(c *core.Context, fmtOpts map[string]formattedOptions, handler func()) {
	origin := c.Request.Header.Get("Origin")

	// Use CORS only if an Origin header is defined for the request.
	if origin != "" {
		fmtOpt, knownOrigin := fmtOpts[origin]
		allOriginsAllowed := false

		// Unknown origin: check for wildcard.
		if !knownOrigin {
			fmtOpt, allOriginsAllowed = fmtOpts[AllOrigins]
		}

		// No origin matched and wildcard not accepted: reject the request.
		if !knownOrigin && !allOriginsAllowed {
			http.Error(c.ResponseWriter, "Invalid CORS request", http.StatusForbidden)
			return
		}

		c.ResponseWriter.Header().Set(headerAllowOrigin, origin)
		c.ResponseWriter.Header().Set("Vary", "Origin")

		// Set credentials header only if they are allowed.
		if fmtOpt.CredentialsAllowed != nil {
			c.ResponseWriter.Header().Set(headerAllowCredentials, *fmtOpt.CredentialsAllowed)
		}

		if fmtOpt.ExposedHeaders != nil {
			c.ResponseWriter.Header().Set(headerExposeHeaders, *fmtOpt.ExposedHeaders)
		}

		if fmtOpt.MaxAge != nil {
			c.ResponseWriter.Header().Set(headerMaxAge, *fmtOpt.MaxAge)
		}

		// OPTIONS method is used for a preflight request.
		// In this case, other CORS headers still need to be set before sending all of them, without any other work.
		if c.Request.Method == "OPTIONS" {
			// If no allowed headers are set, all are allowed.
			if fmtOpt.AllowedHeaders == nil {
				c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
			} else {
				c.ResponseWriter.Header().Set(headerAllowHeaders, *fmtOpt.AllowedHeaders)
			}

			// If no allowed methods are set, all are allowed.
			if fmtOpt.AllowedMethods == nil {
				c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
			} else {
				c.ResponseWriter.Header().Set(headerAllowMethods, *fmtOpt.AllowedMethods)
			}

			c.ResponseWriter.WriteHeader(http.StatusOK)
			return
		}
	}

	handler()
}
<|endoftext|>


<commit_before>package glogger

import (
	"container/list"
	"io"
	"os"
	"sync"
)

type Handler interface {
	GetMutex() *sync.Mutex
	Emit(log string)
	SetFormatter(fmt Formatter)
	Format(rec *Record) string
}

type HandlerGroup struct {
	FilterGroup
	Level    LogLevel
	Handlers *list.List
}

func (hg *HandlerGroup) AddHandler(h Handler) {
	if hg.Handlers == nil {
		hg.Handlers = list.New()
	}
	hg.Handlers.PushBack(h)
}

func (hg *HandlerGroup) Handle(rec *Record) {
	if rec.Level < hg.Level || !hg.DoFilter(rec) || hg.Handlers == nil {
		return
	}
	for e := hg.Handlers.Front(); e != nil; e = e.Next() {
		var h Handler = e.Value.(Handler)
		func() {
			h.GetMutex().Lock()
			defer h.GetMutex().Unlock()
			log := h.Format(rec)
			h.Emit(log)
		}()
	}
}

type GenericHandler struct {
	Fmter Formatter
	mu    sync.Mutex
}

func (gh *GenericHandler) Format(rec *Record) string {
	return gh.Fmter.Format(rec)
}

func (gh *GenericHandler) GetMutex() *sync.Mutex {
	return &(gh.mu)
}

func (gh *GenericHandler) SetFormatter(fmt Formatter) {
	gh.Fmter = fmt
}

type StreamHandler struct {
	GenericHandler
	Writer io.Writer
}

func NewStreamHandler(w io.Writer) *StreamHandler {
	if w == nil {
		w = os.Stderr
	}
	sh := new(StreamHandler)
	sh.Writer = w
	sh.Fmter = NewDefaultFormatter("")
	return sh
}

func (sh *StreamHandler) Emit(text string) {
	sh.Writer.Write([]byte(text + "\n"))
}
<commit_msg>new FileHandler<commit_after>package glogger

import (
	"container/list"
	"io"
	"os"
	"sync"
)

type Handler interface {
	GetMutex() *sync.Mutex
	Emit(log string)
	SetFormatter(fmt Formatter)
	Format(rec *Record) string
}

type HandlerGroup struct {
	FilterGroup
	Level    LogLevel
	Handlers *list.List
}

func (hg *HandlerGroup) AddHandler(h Handler) {
	if hg.Handlers == nil {
		hg.Handlers = list.New()
	}
	hg.Handlers.PushBack(h)
}

func (hg *HandlerGroup) Handle(rec *Record) {
	if rec.Level < hg.Level || !hg.DoFilter(rec) || hg.Handlers == nil {
		return
	}
	for e := hg.Handlers.Front(); e != nil; e = e.Next() {
		var h Handler = e.Value.(Handler)
		func() {
			h.GetMutex().Lock()
			defer h.GetMutex().Unlock()
			log := h.Format(rec)
			h.Emit(log)
		}()
	}
}

type GenericHandler struct {
	Fmter Formatter
	mu    sync.Mutex
}

func (gh *GenericHandler) Format(rec *Record) string {
	return gh.Fmter.Format(rec)
}

func (gh *GenericHandler) GetMutex() *sync.Mutex {
	return &(gh.mu)
}

func (gh *GenericHandler) SetFormatter(fmt Formatter) {
	gh.Fmter = fmt
}

type StreamHandler struct {
	GenericHandler
	Writer io.Writer
}

func NewStreamHandler(w io.Writer) *StreamHandler {
	if w == nil {
		w = os.Stderr
	}
	sh := new(StreamHandler)
	sh.Writer = w
	sh.Fmter = NewDefaultFormatter("")
	return sh
}

func (sh *StreamHandler) Emit(text string) {
	sh.Writer.Write([]byte(text + "\n"))
}

type FileHandler struct {
	StreamHandler
	FileName string
	Flag     int
	Pem      os.FileMode
}

func NewFileHandler(fileName string, flag int, pem os.FileMode) *FileHandler {
	fh := &FileHandler{
		FileName: fileName,
		Flag:     flag,
		Pem:      pem,
	}
	fh.Fmter = NewDefaultFormatter("")
	return fh
}

func (fh *FileHandler) Emit(text string) {
	if fh.Writer == nil {
		file, _ := os.OpenFile(fh.FileName, fh.Flag, fh.Pem)
		fh.Writer = file
	}
	fh.StreamHandler.Emit(text)
}
<|endoftext|>


<commit_before>package handler

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/graphql-go/graphql"

	"context"
)

const (
	ContentTypeJSON           = "application/json"
	ContentTypeGraphQL        = "application/graphql"
	ContentTypeFormURLEncoded = "application/x-www-form-urlencoded"
)

type Handler struct {
	Schema *graphql.Schema

	pretty bool
}
type RequestOptions struct {
	Query         string                 `json:"query" url:"query" schema:"query"`
	Variables     map[string]interface{} `json:"variables" url:"variables" schema:"variables"`
	OperationName string                 `json:"operationName" url:"operationName" schema:"operationName"`
}

// a workaround for getting`variables` as a JSON string
type requestOptionsCompatibility struct {
	Query         string `json:"query" url:"query" schema:"query"`
	Variables     string `json:"variables" url:"variables" schema:"variables"`
	OperationName string `json:"operationName" url:"operationName" schema:"operationName"`
}

func getFromForm(values url.Values) *RequestOptions {
	query := values.Get("query")
	if query != "" {
		// get variables map
		var variables map[string]interface{}
		variablesStr := values.Get("variables")
		json.Unmarshal([]byte(variablesStr), variables)

		return &RequestOptions{
			Query:         query,
			Variables:     variables,
			OperationName: values.Get("operationName"),
		}
	}

	return nil
}

// RequestOptions Parses a http.Request into GraphQL request options struct
func NewRequestOptions(r *http.Request) *RequestOptions {
	if reqOpt := getFromForm(r.URL.Query()); reqOpt != nil {
		return reqOpt
	}

	if r.Method != "POST" {
		return &RequestOptions{}
	}

	if r.Body == nil {
		return &RequestOptions{}
	}

	// TODO: improve Content-Type handling
	contentTypeStr := r.Header.Get("Content-Type")
	contentTypeTokens := strings.Split(contentTypeStr, ";")
	contentType := contentTypeTokens[0]

	switch contentType {
	case ContentTypeGraphQL:
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &RequestOptions{}
		}
		return &RequestOptions{
			Query: string(body),
		}
	case ContentTypeFormURLEncoded:
		if err := r.ParseForm(); err != nil {
			return &RequestOptions{}
		}

		if reqOpt := getFromForm(r.PostForm); reqOpt != nil {
			return reqOpt
		}

		return &RequestOptions{}

	case ContentTypeJSON:
		fallthrough
	default:
		var opts RequestOptions
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &opts
		}
		err = json.Unmarshal(body, &opts)
		if err != nil {
			// Probably `variables` was sent as a string instead of an object.
			// So, we try to be polite and try to parse that as a JSON string
			var optsCompatible requestOptionsCompatibility
			json.Unmarshal(body, &optsCompatible)
			json.Unmarshal([]byte(optsCompatible.Variables), &opts.Variables)
		}
		return &opts
	}
}

// ContextHandler provides an entrypoint into executing graphQL queries with a
// user-provided context.
func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	// get query
	opts := NewRequestOptions(r)

	// execute graphql query
	params := graphql.Params{
		Schema:         *h.Schema,
		RequestString:  opts.Query,
		VariableValues: opts.Variables,
		OperationName:  opts.OperationName,
		Context:        ctx,
	}
	result := graphql.Do(params)

	if h.pretty {
		w.WriteHeader(http.StatusOK)
		buff, _ := json.MarshalIndent(result, "", "\t")

		w.Write(buff)
	} else {
		w.WriteHeader(http.StatusOK)
		buff, _ := json.Marshal(result)

		w.Write(buff)
	}
}

// ServeHTTP provides an entrypoint into executing graphQL queries.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.ContextHandler(r.Context(), w, r)
}

type Config struct {
	Schema *graphql.Schema
	Pretty bool
}

func NewConfig() *Config {
	return &Config{
		Schema: nil,
		Pretty: true,
	}
}

func New(p *Config) *Handler {
	if p == nil {
		p = NewConfig()
	}
	if p.Schema == nil {
		panic("undefined GraphQL schema")
	}

	return &Handler{
		Schema: p.Schema,
		pretty: p.Pretty,
	}
}
<commit_msg>add proper JSON header<commit_after>package handler

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/graphql-go/graphql"

	"context"
)

const (
	ContentTypeJSON           = "application/json"
	ContentTypeGraphQL        = "application/graphql"
	ContentTypeFormURLEncoded = "application/x-www-form-urlencoded"
)

type Handler struct {
	Schema *graphql.Schema

	pretty bool
}
type RequestOptions struct {
	Query         string                 `json:"query" url:"query" schema:"query"`
	Variables     map[string]interface{} `json:"variables" url:"variables" schema:"variables"`
	OperationName string                 `json:"operationName" url:"operationName" schema:"operationName"`
}

// a workaround for getting`variables` as a JSON string
type requestOptionsCompatibility struct {
	Query         string `json:"query" url:"query" schema:"query"`
	Variables     string `json:"variables" url:"variables" schema:"variables"`
	OperationName string `json:"operationName" url:"operationName" schema:"operationName"`
}

func getFromForm(values url.Values) *RequestOptions {
	query := values.Get("query")
	if query != "" {
		// get variables map
		var variables map[string]interface{}
		variablesStr := values.Get("variables")
		json.Unmarshal([]byte(variablesStr), variables)

		return &RequestOptions{
			Query:         query,
			Variables:     variables,
			OperationName: values.Get("operationName"),
		}
	}

	return nil
}

// RequestOptions Parses a http.Request into GraphQL request options struct
func NewRequestOptions(r *http.Request) *RequestOptions {
	if reqOpt := getFromForm(r.URL.Query()); reqOpt != nil {
		return reqOpt
	}

	if r.Method != "POST" {
		return &RequestOptions{}
	}

	if r.Body == nil {
		return &RequestOptions{}
	}

	// TODO: improve Content-Type handling
	contentTypeStr := r.Header.Get("Content-Type")
	contentTypeTokens := strings.Split(contentTypeStr, ";")
	contentType := contentTypeTokens[0]

	switch contentType {
	case ContentTypeGraphQL:
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &RequestOptions{}
		}
		return &RequestOptions{
			Query: string(body),
		}
	case ContentTypeFormURLEncoded:
		if err := r.ParseForm(); err != nil {
			return &RequestOptions{}
		}

		if reqOpt := getFromForm(r.PostForm); reqOpt != nil {
			return reqOpt
		}

		return &RequestOptions{}

	case ContentTypeJSON:
		fallthrough
	default:
		var opts RequestOptions
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &opts
		}
		err = json.Unmarshal(body, &opts)
		if err != nil {
			// Probably `variables` was sent as a string instead of an object.
			// So, we try to be polite and try to parse that as a JSON string
			var optsCompatible requestOptionsCompatibility
			json.Unmarshal(body, &optsCompatible)
			json.Unmarshal([]byte(optsCompatible.Variables), &opts.Variables)
		}
		return &opts
	}
}

// ContextHandler provides an entrypoint into executing graphQL queries with a
// user-provided context.
func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	// get query
	opts := NewRequestOptions(r)

	// execute graphql query
	params := graphql.Params{
		Schema:         *h.Schema,
		RequestString:  opts.Query,
		VariableValues: opts.Variables,
		OperationName:  opts.OperationName,
		Context:        ctx,
	}
	result := graphql.Do(params)

	// use proper JSON Header
	w.Header().Add("Content-Type", "application/json")

	if h.pretty {
		w.WriteHeader(http.StatusOK)
		buff, _ := json.MarshalIndent(result, "", "\t")

		w.Write(buff)
	} else {
		w.WriteHeader(http.StatusOK)
		buff, _ := json.Marshal(result)

		w.Write(buff)
	}
}

// ServeHTTP provides an entrypoint into executing graphQL queries.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.ContextHandler(r.Context(), w, r)
}

type Config struct {
	Schema *graphql.Schema
	Pretty bool
}

func NewConfig() *Config {
	return &Config{
		Schema: nil,
		Pretty: true,
	}
}

func New(p *Config) *Handler {
	if p == nil {
		p = NewConfig()
	}
	if p.Schema == nil {
		panic("undefined GraphQL schema")
	}

	return &Handler{
		Schema: p.Schema,
		pretty: p.Pretty,
	}
}
<|endoftext|>


<commit_before>package ae

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/chrisolsen/ae/flash"
	"golang.org/x/net/context"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
)

type handlerError struct {
	AppVersion string   `json:"appVersion"`
	URL        *url.URL `json:"url"`
	Method     string   `json:"method"`
	StatusCode int      `json:"statusCode"`
	InstanceID string   `json:"instanceId"`
	VersionID  string   `json:"versionId"`
	RequestID  string   `json:"requestId"`
	ModuleName string   `json:"moduleName"`
	Err        string   `json:"message"`
}

func (e *handlerError) Error() string {
	b, err := json.MarshalIndent(e, "", "  ")
	if err != nil {
		return err.Error()
	}
	return string(b)
}

// Handler struct designed to be extended by more specific url handlers
type Handler struct {
	Ctx context.Context
	Req *http.Request
	Res http.ResponseWriter

	config          HandlerConfig
	templates       map[string]*template.Template
	templateHelpers map[string]interface{}
}

// HandlerConfig contains the custom handler configuration settings
type HandlerConfig struct {
	DefaultLayout    string
	LayoutPath       string
	ViewPath         string
	ParentLayoutName string
}

var defaultHandlerConfig = HandlerConfig{
	DefaultLayout:    "application.html",
	LayoutPath:       "layouts",
	ViewPath:         "views",
	ParentLayoutName: "layout",
}

// NewHandler allows one to override the default configuration settings.
//  func NewRootHandler() rootHandler {
//  	return rootHandler{Handler: handler.New(&handler.Config{
//  		LayoutPath: "layouts/admin.html",
//  	})}
//  }
func NewHandler(c *HandlerConfig) Handler {
	if c.DefaultLayout == "" {
		c.DefaultLayout = defaultHandlerConfig.DefaultLayout
	}
	if c.LayoutPath == "" {
		c.LayoutPath = defaultHandlerConfig.LayoutPath
	}
	if c.ParentLayoutName == "" {
		c.ParentLayoutName = defaultHandlerConfig.ParentLayoutName
	}
	if c.ViewPath == "" {
		c.ViewPath = defaultHandlerConfig.ViewPath
	}
	b := Handler{config: *c} // copy the passed in pointer
	b.templates = make(map[string]*template.Template)
	return b
}

// DefaultHandler uses the default config settings
//  func NewRootHandler() rootHandler {
//  	return rootHandler{Handler: handler.Default()}
//  }
func DefaultHandler() Handler {
	return NewHandler(&defaultHandlerConfig)
}

// AddHelpers sets the html.template functions for the handler. This method should be
// called once to intialize the handler with a set of common template helpers used
// throughout the app.
func (h *Handler) AddHelpers(helpers map[string]interface{}) {
	dup := make(map[string]interface{})
	for k, v := range helpers {
		dup[k] = v
	}
	h.templateHelpers = dup
}

// AddHelper allows one to add additional helpers to a handler. Use this when a handler
// needs a less common helper.
func (h *Handler) AddHelper(name string, fn interface{}) {
	if h.templateHelpers == nil {
		h.templateHelpers = make(map[string]interface{})
	}
	h.templateHelpers[name] = fn
}

// Auth is a helper for the handler operation method that will only call on the operation if allowed
func (h *Handler) Auth(allowed bool, op func()) {
	if !allowed {
		url := fmt.Sprintf("/sessions/new?returnUrl=%s", h.Req.RequestURI)
		h.SetFlash("Sign in is required")
		http.Redirect(h.Res, h.Req, url, 301)
		return
	}
	op()
}

// OriginMiddleware returns a middleware function that validates the origin
// header within the request matches the allowed values
func OriginMiddleware(allowed []string) func(context.Context, http.ResponseWriter, *http.Request) context.Context {
	return func(c context.Context, w http.ResponseWriter, r *http.Request) context.Context {
		origin := r.Header.Get("Origin")
		if len(origin) == 0 {
			return c
		}
		ok := validateOrigin(origin, allowed)
		if !ok {
			c2, cancel := context.WithCancel(c)
			cancel()
			return c2
		}

		w.Header().Add("Access-Control-Allow-Headers", "Content-Type, Authorization")
		w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS")
		w.Header().Add("Access-Control-Allow-Origin", origin)

		return c
	}
}

// ValidateOrigin is a helper method called within the ServeHTTP method on
// OPTION requests to validate the allowed origins
func (h *Handler) ValidateOrigin(allowed []string) {
	origin := h.Req.Header.Get("Origin")
	ok := validateOrigin(origin, allowed)
	if !ok {
		_, cancel := context.WithCancel(h.Ctx)
		cancel()
	}
}

func validateOrigin(origin string, allowed []string) bool {
	if allowed == nil || len(allowed) == 0 {
		return true
	}
	if len(origin) == 0 {
		return false
	}
	for _, allowedOrigin := range allowed {
		if origin == allowedOrigin {
			return true
		}
	}
	return false
}

// ToJSON encodes an interface into the response writer with a default http
// status code of 200
func (h *Handler) ToJSON(data interface{}) {
	h.Res.Header().Add("Content-Type", "application/json")
	err := json.NewEncoder(h.Res).Encode(data)
	if err != nil {
		h.Abort(http.StatusInternalServerError, fmt.Errorf("Decoding JSON: %v", err))
	}
}

// ToJSONWithStatus json encodes an interface into the response writer with a
// custom http status code
func (h *Handler) ToJSONWithStatus(data interface{}, status int) {
	h.Res.Header().Add("Content-Type", "application/json")
	h.Res.WriteHeader(status)
	h.ToJSON(data)
}

// SendStatus writes the passed in status to the response without any data
func (h *Handler) SendStatus(status int) {
	h.Res.WriteHeader(status)
}

// Bind must be called at the beginning of every request to set the required references
func (h *Handler) Bind(c context.Context, w http.ResponseWriter, r *http.Request) {
	h.Ctx, h.Res, h.Req = c, w, r
}

// Header gets the request header value
func (h *Handler) Header(name string) string {
	return h.Req.Header.Get(name)
}

// SetHeader sets a response header value
func (h *Handler) SetHeader(name, value string) {
	h.Res.Header().Set(name, value)
}

// Abort is called when pre-maturally exiting from a handler function due to an
// error. A detailed error is delivered to the client and logged to provide the
// details required to identify the issue.
func (h *Handler) Abort(statusCode int, err error) {
	c, cancel := context.WithCancel(h.Ctx)
	defer cancel()

	// testapp is the name given to all apps when being tested
	var isTest = appengine.AppID(c) == "testapp"

	hErr := &handlerError{
		URL:        h.Req.URL,
		Method:     h.Req.Method,
		StatusCode: statusCode,
		AppVersion: appengine.AppID(c),
		RequestID:  appengine.RequestID(c),
	}
	if err != nil {
		hErr.Err = err.Error()
	}

	if !isTest {
		hErr.InstanceID = appengine.InstanceID()
		hErr.VersionID = appengine.VersionID(c)
		hErr.ModuleName = appengine.ModuleName(c)
	}

	// log method to appengine log
	log.Errorf(c, hErr.Error())

	h.Res.WriteHeader(statusCode)
	if strings.Index(h.Req.Header.Get("Accept"), "application/json") >= 0 {
		json.NewEncoder(h.Res).Encode(hErr)
	}
}

// Redirect is a simple wrapper around the core http method
func (h *Handler) Redirect(str string, args ...interface{}) {
	http.Redirect(h.Res, h.Req, fmt.Sprintf(str, args...), 303)
}

// Render pre-caches and renders template.
func (h *Handler) Render(path string, data interface{}) {
	h.RenderTemplate(path, data, RenderOptions{
		Name:    h.config.ParentLayoutName,
		FuncMap: h.templateHelpers,
		Parents: []string{filepath.Join(h.config.LayoutPath, h.config.DefaultLayout)},
	})
}

// RenderOptions contain the optional data items for rendering
type RenderOptions struct {
	// http status to return in the response
	Status int

	// template functions
	FuncMap template.FuncMap

	// parent layout paths to render the defined view within
	Parents []string

	// the defined *name* to render
	// 	{{define "layout"}}...{{end}}
	Name string
}

// RenderTemplate renders the template without any layout
func (h *Handler) RenderTemplate(tmplPath string, data interface{}, opts RenderOptions) {
	name := strings.TrimPrefix(tmplPath, "/")
	tmpl := h.templates[name]
	if tmpl == nil {
		t := template.New(name)
		if opts.FuncMap != nil {
			t.Funcs(opts.FuncMap)
		}
		var views []string
		if opts.Parents != nil {
			for _, p := range opts.Parents {
				views = append(views, h.fileNameWithExt(p))
			}
		} else {
			views = make([]string, 0)
		}

		views = append(views, filepath.Join(h.config.ViewPath, h.fileNameWithExt(name)))
		tmpl = template.Must(t.ParseFiles(views...))
		h.templates[name] = tmpl
	}
	if opts.Status != 0 {
		h.Res.WriteHeader(opts.Status)
	} else {
		h.Res.WriteHeader(http.StatusOK)
	}

	var renderErr error
	if opts.Name != "" {
		renderErr = tmpl.ExecuteTemplate(h.Res, opts.Name, data)
	} else {
		renderErr = tmpl.Execute(h.Res, data)
	}
	if renderErr != nil {
		panic(renderErr)
	}
}

// SetLastModified sets the Last-Modified header in the RFC1123 time format
func (h *Handler) SetLastModified(t time.Time) {
	h.Res.Header().Set("Last-Modified", t.Format(time.RFC1123))
}

// SetETag sets the etag with the md5 value
func (h *Handler) SetETag(val interface{}) {
	var str string
	switch val.(type) {
	case string:
		str = val.(string)
	case time.Time:
		str = val.(time.Time).Format(time.RFC1123)
	case fmt.Stringer:
		str = val.(fmt.Stringer).String()
	default:
		str = fmt.Sprintf("%v", val)
	}

	hash := md5.New()
	io.WriteString(hash, str)
	etag := base64.StdEncoding.EncodeToString(hash.Sum(nil))
	h.Res.Header().Set("ETag", etag)
}

// SetExpires sets the Expires response header with a properly formatted time value
func (h *Handler) SetExpires(t time.Time) {
	h.Res.Header().Set("Expires", t.Format(time.RFC1123))
}

// SetExpiresIn is a helper to simplify the calling of SetExpires
func (h *Handler) SetExpiresIn(d time.Duration) {
	h.Res.Header().Set("Expires", time.Now().Add(d).Format(time.RFC1123))
}

func (h *Handler) fileNameWithExt(name string) string {
	var ext string
	if strings.Index(name, ".") > 0 {
		ext = ""
	} else {
		ext = ".html"
	}
	return fmt.Sprintf("%s%s", name, ext)
}

// SetFlash sets a temporary message into a response cookie, that after
// being viewed will be removed, to prevent it from being viewed again.
func (h *Handler) SetFlash(msg string, args ...interface{}) {
	if len(args) > 0 {
		msg = fmt.Sprintf(msg, args...)
	}
	flash.Set(h.Res, msg)
}

// SetCookie is a simple wrapper around the http.SetCookie methoe
func (h *Handler) SetCookie(c *http.Cookie) {
	http.SetCookie(h.Res, c)
}

// Flash gets the flash value
func (h *Handler) Flash() string {
	return flash.Get(h.Res, h.Req)
}
<commit_msg>rename some handler attributes<commit_after>package ae

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/chrisolsen/ae/flash"
	"golang.org/x/net/context"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
)

type handlerError struct {
	AppVersion string   `json:"appVersion"`
	URL        *url.URL `json:"url"`
	Method     string   `json:"method"`
	StatusCode int      `json:"statusCode"`
	InstanceID string   `json:"instanceId"`
	VersionID  string   `json:"versionId"`
	RequestID  string   `json:"requestId"`
	ModuleName string   `json:"moduleName"`
	Err        string   `json:"message"`
}

func (e *handlerError) Error() string {
	b, err := json.MarshalIndent(e, "", "  ")
	if err != nil {
		return err.Error()
	}
	return string(b)
}

// Handler struct designed to be extended by more specific url handlers
type Handler struct {
	Ctx context.Context
	Req *http.Request
	Res http.ResponseWriter

	config          HandlerConfig
	templates       map[string]*template.Template
	templateHelpers map[string]interface{}
}

// HandlerConfig contains the custom handler configuration settings
type HandlerConfig struct {
	// File name of the layout
	LayoutFileName string
	// Path, relative to the app root, of layouts
	LayoutPath string
	// Path, relative to the app root, of layouts
	ViewPath string
	// ???
	ParentLayoutName string
}

var defaultHandlerConfig = HandlerConfig{
	LayoutFileName:   "application.html",
	LayoutPath:       "layouts",
	ViewPath:         "views",
	ParentLayoutName: "layout",
}

// NewHandler allows one to override the default configuration settings.
//  func NewRootHandler() rootHandler {
//  	return rootHandler{Handler: handler.New(&handler.Config{
//  		LayoutPath: "layouts",
//  	})}
//  }
func NewHandler(c *HandlerConfig) Handler {
	if c.LayoutFileName == "" {
		c.LayoutFileName = defaultHandlerConfig.LayoutFileName
	}
	if c.LayoutPath == "" {
		c.LayoutPath = defaultHandlerConfig.LayoutPath
	}
	if c.ParentLayoutName == "" {
		c.ParentLayoutName = defaultHandlerConfig.ParentLayoutName
	}
	if c.ViewPath == "" {
		c.ViewPath = defaultHandlerConfig.ViewPath
	}
	b := Handler{config: *c} // copy the passed in pointer
	b.templates = make(map[string]*template.Template)
	return b
}

// DefaultHandler uses the default config settings
//  func NewRootHandler() rootHandler {
//  	return rootHandler{Handler: handler.Default()}
//  }
func DefaultHandler() Handler {
	return NewHandler(&defaultHandlerConfig)
}

// AddHelpers sets the html.template functions for the handler. This method should be
// called once to intialize the handler with a set of common template helpers used
// throughout the app.
func (h *Handler) AddHelpers(helpers map[string]interface{}) {
	dup := make(map[string]interface{})
	for k, v := range helpers {
		dup[k] = v
	}
	h.templateHelpers = dup
}

// AddHelper allows one to add additional helpers to a handler. Use this when a handler
// needs a less common helper.
func (h *Handler) AddHelper(name string, fn interface{}) {
	if h.templateHelpers == nil {
		h.templateHelpers = make(map[string]interface{})
	}
	h.templateHelpers[name] = fn
}

// Auth is a helper for the handler operation method that will only call on the operation if allowed
func (h *Handler) Auth(allowed bool, op func()) {
	if !allowed {
		url := fmt.Sprintf("/sessions/new?returnUrl=%s", h.Req.RequestURI)
		h.SetFlash("Sign in is required")
		http.Redirect(h.Res, h.Req, url, 301)
		return
	}
	op()
}

// OriginMiddleware returns a middleware function that validates the origin
// header within the request matches the allowed values
func OriginMiddleware(allowed []string) func(context.Context, http.ResponseWriter, *http.Request) context.Context {
	return func(c context.Context, w http.ResponseWriter, r *http.Request) context.Context {
		origin := r.Header.Get("Origin")
		if len(origin) == 0 {
			return c
		}
		ok := validateOrigin(origin, allowed)
		if !ok {
			c2, cancel := context.WithCancel(c)
			cancel()
			return c2
		}

		w.Header().Add("Access-Control-Allow-Headers", "Content-Type, Authorization")
		w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS")
		w.Header().Add("Access-Control-Allow-Origin", origin)

		return c
	}
}

// ValidateOrigin is a helper method called within the ServeHTTP method on
// OPTION requests to validate the allowed origins
func (h *Handler) ValidateOrigin(allowed []string) {
	origin := h.Req.Header.Get("Origin")
	ok := validateOrigin(origin, allowed)
	if !ok {
		_, cancel := context.WithCancel(h.Ctx)
		cancel()
	}
}

func validateOrigin(origin string, allowed []string) bool {
	if allowed == nil || len(allowed) == 0 {
		return true
	}
	if len(origin) == 0 {
		return false
	}
	for _, allowedOrigin := range allowed {
		if origin == allowedOrigin {
			return true
		}
	}
	return false
}

// ToJSON encodes an interface into the response writer with a default http
// status code of 200
func (h *Handler) ToJSON(data interface{}) {
	h.Res.Header().Add("Content-Type", "application/json")
	err := json.NewEncoder(h.Res).Encode(data)
	if err != nil {
		h.Abort(http.StatusInternalServerError, fmt.Errorf("Decoding JSON: %v", err))
	}
}

// ToJSONWithStatus json encodes an interface into the response writer with a
// custom http status code
func (h *Handler) ToJSONWithStatus(data interface{}, status int) {
	h.Res.Header().Add("Content-Type", "application/json")
	h.Res.WriteHeader(status)
	h.ToJSON(data)
}

// SendStatus writes the passed in status to the response without any data
func (h *Handler) SendStatus(status int) {
	h.Res.WriteHeader(status)
}

// Bind must be called at the beginning of every request to set the required references
func (h *Handler) Bind(c context.Context, w http.ResponseWriter, r *http.Request) {
	h.Ctx, h.Res, h.Req = c, w, r
}

// Header gets the request header value
func (h *Handler) Header(name string) string {
	return h.Req.Header.Get(name)
}

// SetHeader sets a response header value
func (h *Handler) SetHeader(name, value string) {
	h.Res.Header().Set(name, value)
}

// Abort is called when pre-maturally exiting from a handler function due to an
// error. A detailed error is delivered to the client and logged to provide the
// details required to identify the issue.
func (h *Handler) Abort(statusCode int, err error) {
	c, cancel := context.WithCancel(h.Ctx)
	defer cancel()

	// testapp is the name given to all apps when being tested
	var isTest = appengine.AppID(c) == "testapp"

	hErr := &handlerError{
		URL:        h.Req.URL,
		Method:     h.Req.Method,
		StatusCode: statusCode,
		AppVersion: appengine.AppID(c),
		RequestID:  appengine.RequestID(c),
	}
	if err != nil {
		hErr.Err = err.Error()
	}

	if !isTest {
		hErr.InstanceID = appengine.InstanceID()
		hErr.VersionID = appengine.VersionID(c)
		hErr.ModuleName = appengine.ModuleName(c)
	}

	// log method to appengine log
	log.Errorf(c, hErr.Error())

	h.Res.WriteHeader(statusCode)
	if strings.Index(h.Req.Header.Get("Accept"), "application/json") >= 0 {
		json.NewEncoder(h.Res).Encode(hErr)
	}
}

// Redirect is a simple wrapper around the core http method
func (h *Handler) Redirect(str string, args ...interface{}) {
	http.Redirect(h.Res, h.Req, fmt.Sprintf(str, args...), 303)
}

// Render pre-caches and renders template.
func (h *Handler) Render(path string, data interface{}) {
	h.RenderTemplate(path, data, RenderOptions{
		Name:    h.config.ParentLayoutName,
		FuncMap: h.templateHelpers,
		Parents: []string{filepath.Join(h.config.LayoutPath, h.config.LayoutFileName)},
	})
}

// RenderOptions contain the optional data items for rendering
type RenderOptions struct {
	// http status to return in the response
	Status int

	// template functions
	FuncMap template.FuncMap

	// parent layout paths to render the defined view within
	Parents []string

	// the defined *name* to render
	// 	{{define "layout"}}...{{end}}
	Name string
}

// RenderTemplate renders the template without any layout
func (h *Handler) RenderTemplate(tmplPath string, data interface{}, opts RenderOptions) {
	name := strings.TrimPrefix(tmplPath, "/")
	tmpl := h.templates[name]
	if tmpl == nil {
		t := template.New(name)
		if opts.FuncMap != nil {
			t.Funcs(opts.FuncMap)
		}
		var views []string
		if opts.Parents != nil {
			for _, p := range opts.Parents {
				views = append(views, h.fileNameWithExt(p))
			}
		} else {
			views = make([]string, 0)
		}

		views = append(views, filepath.Join(h.config.ViewPath, h.fileNameWithExt(name)))
		tmpl = template.Must(t.ParseFiles(views...))
		h.templates[name] = tmpl
	}
	if opts.Status != 0 {
		h.Res.WriteHeader(opts.Status)
	} else {
		h.Res.WriteHeader(http.StatusOK)
	}

	var renderErr error
	if opts.Name != "" {
		renderErr = tmpl.ExecuteTemplate(h.Res, opts.Name, data)
	} else {
		renderErr = tmpl.Execute(h.Res, data)
	}
	if renderErr != nil {
		panic(renderErr)
	}
}

// SetLastModified sets the Last-Modified header in the RFC1123 time format
func (h *Handler) SetLastModified(t time.Time) {
	h.Res.Header().Set("Last-Modified", t.Format(time.RFC1123))
}

// SetETag sets the etag with the md5 value
func (h *Handler) SetETag(val interface{}) {
	var str string
	switch val.(type) {
	case string:
		str = val.(string)
	case time.Time:
		str = val.(time.Time).Format(time.RFC1123)
	case fmt.Stringer:
		str = val.(fmt.Stringer).String()
	default:
		str = fmt.Sprintf("%v", val)
	}

	hash := md5.New()
	io.WriteString(hash, str)
	etag := base64.StdEncoding.EncodeToString(hash.Sum(nil))
	h.Res.Header().Set("ETag", etag)
}

// SetExpires sets the Expires response header with a properly formatted time value
func (h *Handler) SetExpires(t time.Time) {
	h.Res.Header().Set("Expires", t.Format(time.RFC1123))
}

// SetExpiresIn is a helper to simplify the calling of SetExpires
func (h *Handler) SetExpiresIn(d time.Duration) {
	h.Res.Header().Set("Expires", time.Now().Add(d).Format(time.RFC1123))
}

func (h *Handler) fileNameWithExt(name string) string {
	var ext string
	if strings.Index(name, ".") > 0 {
		ext = ""
	} else {
		ext = ".html"
	}
	return fmt.Sprintf("%s%s", name, ext)
}

// SetFlash sets a temporary message into a response cookie, that after
// being viewed will be removed, to prevent it from being viewed again.
func (h *Handler) SetFlash(msg string, args ...interface{}) {
	if len(args) > 0 {
		msg = fmt.Sprintf(msg, args...)
	}
	flash.Set(h.Res, msg)
}

// SetCookie is a simple wrapper around the http.SetCookie methoe
func (h *Handler) SetCookie(c *http.Cookie) {
	http.SetCookie(h.Res, c)
}

// Flash gets the flash value
func (h *Handler) Flash() string {
	return flash.Get(h.Res, h.Req)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"

	log "github.com/Sirupsen/logrus"
	"github.com/skeema/mycli"
	"golang.org/x/crypto/ssh/terminal"
)

const version = "0.1 (pre-release)"
const rootDesc = `Skeema is a MySQL schema management tool. It allows you to export a database
schema to the filesystem, and apply online schema changes by modifying files.`

// CommandSuite is the root command. It is global so that subcommands can be
// added to it via init() functions in each subcommand's source file.
var CommandSuite = mycli.NewCommandSuite("skeema", version, rootDesc)

// AddGlobalConfigFiles takes the mycli.Config generated from the CLI and adds
// global option files as sources. It also handles special processing for a few
// options. Generally, subcommand handlers should call AddGlobalConfigFiles at
// the top of the method.
func AddGlobalConfigFiles(cfg *mycli.Config) {
	globalFilePaths := []string{"/etc/skeema", "/usr/local/etc/skeema"}
	home := filepath.Clean(os.Getenv("HOME"))
	if home != "" {
		globalFilePaths = append(globalFilePaths, path.Join(home, ".my.cnf"), path.Join(home, ".skeema"))
	}
	for _, path := range globalFilePaths {
		f := mycli.NewFile(path)
		if !f.Exists() {
			continue
		}
		if err := f.Read(); err != nil {
			log.Warnf("Ignoring global option file %s due to read error: %s", f.Path(), err)
			continue
		}
		if strings.HasSuffix(path, ".my.cnf") {
			f.IgnoreUnknownOptions = true
		}
		if err := f.Parse(cfg); err != nil {
			log.Warnf("Ignoring global option file %s due to parse error: %s", f.Path(), err)
			continue
		}
		if strings.HasSuffix(path, ".my.cnf") {
			_ = f.UseSection("skeema", "client") // safe to ignore error (doesn't matter if section doesn't exist)
		} else {
			_ = f.UseSection(cfg.Get("environment")) // safe to ignore error (doesn't matter if section doesn't exist)
		}

		cfg.AddSource(f)
	}

	// The host and schema options are special -- most commands only expect
	// to find them when recursively crawling directory configs. So if these
	// options have been set globally (via CLI or a global config file), and
	// the current subcommand hasn't explicitly overridden these options (as
	// init and add-environment do), silently ignore the value.
	for _, name := range []string{"host", "schema"} {
		if cfg.Changed(name) && cfg.FindOption(name) == CommandSuite.Options()[name] {
			cfg.CLI.OptionValues[name] = ""
			cfg.MarkDirty()
		}
	}

	// Special handling for password option: supplying it with no value prompts on STDIN
	if cfg.Get("password") == "" {
		var err error
		cfg.CLI.OptionValues["password"], err = PromptPassword()
		if err != nil {
			Exit(NewExitValue(CodeNoInput, err.Error()))
		}
		cfg.MarkDirty()
		fmt.Println()
	}

	if cfg.GetBool("debug") {
		log.SetLevel(log.DebugLevel)
	}
}

// PromptPassword reads a password from STDIN without echoing the typed
// characters. Requires that STDIN is a TTY.
func PromptPassword() (string, error) {
	stdin := int(syscall.Stdin)
	if !terminal.IsTerminal(stdin) {
		return "", errors.New("STDIN must be a TTY to read password")
	}
	fmt.Printf("Enter password: ")
	bytePassword, err := terminal.ReadPassword(stdin)
	if err != nil {
		return "", err
	}
	return string(bytePassword), nil
}

func main() {
	// Add global options. Sub-commands may override these when needed.
	CommandSuite.AddOption(mycli.StringOption("help", '?', "", "Display help for the specified command").ValueOptional())
	CommandSuite.AddOption(mycli.StringOption("host", 0, "", "Database hostname or IP address").Hidden())
	CommandSuite.AddOption(mycli.StringOption("port", 0, "3306", "Port to use for database host").Hidden())
	CommandSuite.AddOption(mycli.StringOption("socket", 'S', "/tmp/mysql.sock", "Absolute path to Unix domain socket file for use when hostname==localhost").Hidden())
	CommandSuite.AddOption(mycli.StringOption("user", 'u', "root", "Username to connect to database host"))
	CommandSuite.AddOption(mycli.StringOption("password", 'p', "<no password>", "Password for database user. Supply with no value to prompt.").ValueOptional())
	CommandSuite.AddOption(mycli.StringOption("schema", 0, "", "Database schema name").Hidden())
	CommandSuite.AddOption(mycli.StringOption("temp-schema", 't', "_skeema_tmp", "Name of temporary schema to use for intermediate operations. Will be created and dropped unless --reuse-temp-schema enabled."))
	CommandSuite.AddOption(mycli.BoolOption("reuse-temp-schema", 0, false, "Do not drop temp-schema when done. Useful for running without create/drop database privileges."))
	CommandSuite.AddOption(mycli.BoolOption("debug", 0, false, "Enable debug logging"))

	cfg, err := mycli.ParseCLI(CommandSuite, os.Args)
	if err != nil {
		Exit(NewExitValue(CodeBadConfig, err.Error()))
	}

	Exit(cfg.HandleCommand())
}
<commit_msg>If a panic occurs, only display full stack trace if --debug in use<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime/debug"
	"strings"
	"syscall"

	log "github.com/Sirupsen/logrus"
	"github.com/skeema/mycli"
	"golang.org/x/crypto/ssh/terminal"
)

const version = "0.1 (pre-release)"
const rootDesc = `Skeema is a MySQL schema management tool. It allows you to export a database
schema to the filesystem, and apply online schema changes by modifying files.`

// CommandSuite is the root command. It is global so that subcommands can be
// added to it via init() functions in each subcommand's source file.
var CommandSuite = mycli.NewCommandSuite("skeema", version, rootDesc)

// AddGlobalConfigFiles takes the mycli.Config generated from the CLI and adds
// global option files as sources. It also handles special processing for a few
// options. Generally, subcommand handlers should call AddGlobalConfigFiles at
// the top of the method.
func AddGlobalConfigFiles(cfg *mycli.Config) {
	globalFilePaths := []string{"/etc/skeema", "/usr/local/etc/skeema"}
	home := filepath.Clean(os.Getenv("HOME"))
	if home != "" {
		globalFilePaths = append(globalFilePaths, path.Join(home, ".my.cnf"), path.Join(home, ".skeema"))
	}
	for _, path := range globalFilePaths {
		f := mycli.NewFile(path)
		if !f.Exists() {
			continue
		}
		if err := f.Read(); err != nil {
			log.Warnf("Ignoring global option file %s due to read error: %s", f.Path(), err)
			continue
		}
		if strings.HasSuffix(path, ".my.cnf") {
			f.IgnoreUnknownOptions = true
		}
		if err := f.Parse(cfg); err != nil {
			log.Warnf("Ignoring global option file %s due to parse error: %s", f.Path(), err)
			continue
		}
		if strings.HasSuffix(path, ".my.cnf") {
			_ = f.UseSection("skeema", "client") // safe to ignore error (doesn't matter if section doesn't exist)
		} else {
			_ = f.UseSection(cfg.Get("environment")) // safe to ignore error (doesn't matter if section doesn't exist)
		}

		cfg.AddSource(f)
	}

	// The host and schema options are special -- most commands only expect
	// to find them when recursively crawling directory configs. So if these
	// options have been set globally (via CLI or a global config file), and
	// the current subcommand hasn't explicitly overridden these options (as
	// init and add-environment do), silently ignore the value.
	for _, name := range []string{"host", "schema"} {
		if cfg.Changed(name) && cfg.FindOption(name) == CommandSuite.Options()[name] {
			cfg.CLI.OptionValues[name] = ""
			cfg.MarkDirty()
		}
	}

	// Special handling for password option: supplying it with no value prompts on STDIN
	if cfg.Get("password") == "" {
		var err error
		cfg.CLI.OptionValues["password"], err = PromptPassword()
		if err != nil {
			Exit(NewExitValue(CodeNoInput, err.Error()))
		}
		cfg.MarkDirty()
		fmt.Println()
	}

	if cfg.GetBool("debug") {
		log.SetLevel(log.DebugLevel)
	}
}

// PromptPassword reads a password from STDIN without echoing the typed
// characters. Requires that STDIN is a TTY.
func PromptPassword() (string, error) {
	stdin := int(syscall.Stdin)
	if !terminal.IsTerminal(stdin) {
		return "", errors.New("STDIN must be a TTY to read password")
	}
	fmt.Printf("Enter password: ")
	bytePassword, err := terminal.ReadPassword(stdin)
	if err != nil {
		return "", err
	}
	return string(bytePassword), nil
}

func main() {
	// Add global options. Sub-commands may override these when needed.
	CommandSuite.AddOption(mycli.StringOption("host", 0, "", "Database hostname or IP address").Hidden())
	CommandSuite.AddOption(mycli.StringOption("port", 0, "3306", "Port to use for database host").Hidden())
	CommandSuite.AddOption(mycli.StringOption("socket", 'S', "/tmp/mysql.sock", "Absolute path to Unix domain socket file for use when hostname==localhost").Hidden())
	CommandSuite.AddOption(mycli.StringOption("user", 'u', "root", "Username to connect to database host"))
	CommandSuite.AddOption(mycli.StringOption("password", 'p', "<no password>", "Password for database user. Supply with no value to prompt.").ValueOptional())
	CommandSuite.AddOption(mycli.StringOption("schema", 0, "", "Database schema name").Hidden())
	CommandSuite.AddOption(mycli.StringOption("temp-schema", 't', "_skeema_tmp", "Name of temporary schema to use for intermediate operations. Will be created and dropped unless --reuse-temp-schema enabled."))
	CommandSuite.AddOption(mycli.BoolOption("reuse-temp-schema", 0, false, "Do not drop temp-schema when done. Useful for running without create/drop database privileges."))
	CommandSuite.AddOption(mycli.BoolOption("debug", 0, false, "Enable debug logging"))

	var cfg *mycli.Config

	defer func() {
		if err := recover(); err != nil {
			if cfg == nil || !cfg.GetBool("debug") {
				Exit(NewExitValue(CodeFatalError, fmt.Sprint(err)))
			} else {
				log.Error(err)
				log.Debug(string(debug.Stack()))
				Exit(NewExitValue(CodeFatalError, ""))
			}
		}
	}()

	cfg, err := mycli.ParseCLI(CommandSuite, os.Args)
	if err != nil {
		Exit(NewExitValue(CodeBadConfig, err.Error()))
	}

	Exit(cfg.HandleCommand())
}
<|endoftext|>


<commit_before>package token

const (
	// ILLEGAL is unexpected value
	ILLEGAL = "ILLEGAL"

	// EOF is end of file
	EOF = "EOF"

	// IDENT is indent
	IDENT = ""

	// INT is int value
	INT = "INT"

	// ASSIGN is =
	ASSIGN = "="

	// PLUS is +
	PLUS = "+"

	// MINUS is -
	MINUS = "-"

	// BANG is !
	BANG = "!"

	// ASTERISK is *
	ASTERISK = "*"

	// SLASH is !
	SLASH = "/"

	// LT is <
	LT = "<"

	// GT is >
	GT = ">"

	// COMMA is ,
	COMMA = ","

	// SEMICOLON is ;
	SEMICOLON = ";"

	// LPAREN is (
	LPAREN = "("

	// RPAREN is )
	RPAREN = ")"

	// LBRACE is {
	LBRACE = "{"

	// RBRACE is {
	RBRACE = "}"

	// FUNCTION is function
	FUNCTION = "FUNCTION"

	// LET is variable declaration
	LET = "LET"

	// TRUE is true declatration
	TRUE = "TRUE"

	// FALSE is false declatration
	FALSE = "FALSE"

	// IF is if declatation
	IF = "IF"

	// ELSE is else declatation
	ELSE = "ELSE"

	// RETURN is return declatation
	RETURN = "RETURN"

	// EQ is equal declatation
	EQ = "=="

	// NOT_EQ is not equal declatation
	NOT_EQ = "!="
)

// TokenType is token's type
type TokenType string

// Token is zungo tokens
type Token struct {
	Type    TokenType
	Literal string
}

var keywords = map[string]TokenType{
	"fn":     FUNCTION,
	"let":    LET,
	"if":     IF,
	"else":   ELSE,
	"return": RETURN,
	"true":   TRUE,
	"false":  FALSE,
}

// LookupIdent is refer to identifiers
func LookupIdent(ident string) TokenType {
	if tok, ok := keywords[ident]; ok {
		return tok
	}
	return IDENT
}
<commit_msg>:bug: chg IDENT<commit_after>package token

const (
	// ILLEGAL is unexpected value
	ILLEGAL = "ILLEGAL"

	// EOF is end of file
	EOF = "EOF"

	// IDENT is indent
	IDENT = "IDENT"

	// INT is int value
	INT = "INT"

	// ASSIGN is =
	ASSIGN = "="

	// PLUS is +
	PLUS = "+"

	// MINUS is -
	MINUS = "-"

	// BANG is !
	BANG = "!"

	// ASTERISK is *
	ASTERISK = "*"

	// SLASH is !
	SLASH = "/"

	// LT is <
	LT = "<"

	// GT is >
	GT = ">"

	// COMMA is ,
	COMMA = ","

	// SEMICOLON is ;
	SEMICOLON = ";"

	// LPAREN is (
	LPAREN = "("

	// RPAREN is )
	RPAREN = ")"

	// LBRACE is {
	LBRACE = "{"

	// RBRACE is {
	RBRACE = "}"

	// FUNCTION is function
	FUNCTION = "FUNCTION"

	// LET is variable declaration
	LET = "LET"

	// TRUE is true declatration
	TRUE = "TRUE"

	// FALSE is false declatration
	FALSE = "FALSE"

	// IF is if declatation
	IF = "IF"

	// ELSE is else declatation
	ELSE = "ELSE"

	// RETURN is return declatation
	RETURN = "RETURN"

	// EQ is equal declatation
	EQ = "=="

	// NOT_EQ is not equal declatation
	NOT_EQ = "!="
)

// TokenType is token's type
type TokenType string

// Token is zungo tokens
type Token struct {
	Type    TokenType
	Literal string
}

var keywords = map[string]TokenType{
	"fn":     FUNCTION,
	"let":    LET,
	"if":     IF,
	"else":   ELSE,
	"return": RETURN,
	"true":   TRUE,
	"false":  FALSE,
}

// LookupIdent is refer to identifiers
func LookupIdent(ident string) TokenType {
	if tok, ok := keywords[ident]; ok {
		return tok
	}
	return IDENT
}
<|endoftext|>