<commit_before>package main

import (
	"io"
	"net/http"
	"path"
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
)

type DirEntry struct {
	pth      string
	fi       os.FileInfo
	checksum string
}

type DirEntries []DirEntry

func (a DirEntries) Len() int           { return len(a) }
func (a DirEntries) Less(i, j int) bool { return a[i].pth < a[j].pth }
func (a DirEntries) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type AppConfig struct {
	Name        string
	InputRoot   string
	OutputDir   string
	BuildCmd    string
	ArchiveLocal string
	ArchiveRemote string
	Include		[]string
	Exclude		[]string
}

func countFullChecksum(ents *DirEntries) {
	for i, v := range *ents {
		st, err := os.Stat(v.pth)
		if err != nil {
			fmt.Printf("SKIP bad file [%s]\n", v.pth)
			continue
		}
		v.fi = st

		if v.fi.IsDir() {
			continue
		}

		if v.fi.Size() == 0 {
			v.checksum = "0000"
			continue
		}

		dat, err := ioutil.ReadFile(v.pth)
		if err != nil {
			panic(err)
		}
		var csum = md5.Sum(dat)
		v.checksum = hex.EncodeToString(csum[:])
		//fmt.Printf("%s\n %x", v.checksum, csum)
		(*ents)[i] = v
	}
}

func shouldIgnore(config *AppConfig, pth string) bool {
	for _,v := range config.Exclude {
		if strings.HasPrefix(pth, v) {
			return true
		}
	}

	if len(config.Include) == 0 {
		return false
	}

	for _, v := range config.Include {
		if strings.HasPrefix(pth, v) {
			return false
		}
	}
	return true
}

func collectWithGit(config *AppConfig) DirEntries{
	cmd := exec.Command("git", "ls-files")

	cmd.Dir = config.InputRoot
	out, err := cmd.Output()
	if (err != nil) {
		panic(err)
	}
	asStr := string(out)
	lines := strings.Split(asStr, "\n")
	var all DirEntries

	for _, v := range lines {
		if shouldIgnore(config, v) {
			continue
		}
		all = append(all, DirEntry { pth: path.Join(config.InputRoot ,v)})
	}
	return all
}

func normalizePaths(ents *DirEntries, rootPath string) {
	for i, v := range *ents {
		oldpath := v.pth
		newpath := strings.Replace(strings.TrimPrefix(oldpath, rootPath+"/"), "\\", "/", -1)
		v.pth = newpath
		(*ents)[i] = v
	}
}

func collectByConfig(config *AppConfig) DirEntries {
	return collectWithGit(config)
}

func getCheckSumForFiles(config *AppConfig) (DirEntries, string) {
	all := collectByConfig(config)
	sort.Sort(all)
	countFullChecksum(&all)
	normalizePaths(&all, config.InputRoot)
	var manifest bytes.Buffer
	for _, v := range all {
		manifest.WriteString(v.pth)
		manifest.WriteString(v.checksum)
		//fmt.Printf("%s %s\n", v.pth, v.checksum)
	}
	manifestSum := md5.Sum(manifest.Bytes())
	return all, hex.EncodeToString(manifestSum[:])
}

func run(cwd string, bin string, arg ...string) {
	fmt.Printf("> %s %s", bin, arg)
	cmd := exec.Command(bin, arg...)
	cmd.Dir = cwd
	out, err := cmd.CombinedOutput()
	fmt.Printf("%s", string(out))
	if err != nil {
		panic(err)
	}
}

func zipOutput(path string, zipfile string) {
	run(path, "zip", "-r", zipfile, "*")
}

func unzipOutput(pth string, zipfile string) {
	// we will replace the old path completely
	ensureDir(pth)
	err := os.RemoveAll(pth)
	if err != nil {
		panic(err)
	}
	ensureDir(pth)
	run(".", "unzip", zipfile, "-d"+pth)
}

func runBuildCommand(config *AppConfig) {
	fmt.Printf("Running build command '%s' in %s\n", config.BuildCmd, config.InputRoot)
	parts := strings.Fields(config.BuildCmd)
	cmd := exec.Command(parts[0], parts[1:]...)
	cmd.Dir = config.InputRoot
	out, err := cmd.CombinedOutput()
	fmt.Println(string(out))
	if err != nil {
		fmt.Printf("Build failed with error!")
		panic(err)
	}
}

func fetchTo(url string, to string) bool {
	fmt.Printf("GET %s\n", url)
	resp, err := http.Get(url)
	if err != nil || resp.StatusCode != 200 {
		fmt.Printf("Not available: %s\n", url)
		return false
	}
	defer resp.Body.Close()
	out, err := os.Create(to)
	if err != nil {
		panic(err)
	}
	defer out.Close()
	_, err  = io.Copy(out, resp.Body)
	if err != nil {
		panic(err)
	}
	return true
}

func discoverArchive(config *AppConfig, checksum string) (string,bool) {
	archiveRoot := config.ArchiveLocal
	zipName := config.Name + "_" + checksum + ".zip"

	// 1. just try local
	localZipName := filepath.Join(archiveRoot, zipName)
	_, err := os.Stat(localZipName)
	if err == nil {
		return localZipName, true
	}

	// 2. try remote if applicable

	remoteArchive := config.ArchiveRemote

	if remoteArchive == "" {
		return localZipName, false
	}
	if strings.Index(remoteArchive, "[ZIP]") == -1 {
		fmt.Printf("Error: remote archive template %s does not contain [ZIP]\n", remoteArchive)
		return "", false
	}
	remoteUrl := strings.Replace(remoteArchive, "[ZIP]", zipName, -1)
	fetched := fetchTo(remoteUrl, localZipName)
	if !fetched {
		return localZipName, false
	}
	return localZipName, true
}

func buildWithConfig(config *AppConfig) {
	// check input checksum
	fmt.Printf("Config %s\n", config)
	archiveRoot := config.ArchiveLocal

	if archiveRoot == "" {
		fmt.Println("HASHIBUILD_ARCHIVE not set, building without artifact caching")
		runBuildCommand(config)
		return
	}

	_, inputChecksum := getCheckSumForFiles(config)
	// if finding archive found, unzip it and we are ready
	ensureDir(archiveRoot)

	zipName, found := discoverArchive(config, inputChecksum)

	if found {
		fmt.Printf("Unzip %s to %s\n", zipName, config.OutputDir)
		unzipOutput(config.OutputDir, zipName)
		return
	}

	// run build if mismatch

	runBuildCommand(config)

	// zip the results
	fmt.Printf("Zipping %s to %s\n", config.OutputDir, zipName)
	zipOutput(config.OutputDir, zipName)
}

func checkDir(pth string) {
	if _, err := os.Stat(pth); os.IsNotExist(err) {
		fmt.Printf("Path does not exist: %s", pth)
		panic(err)
	}
}

func ensureDir(pth string) {
	if _, err := os.Stat(pth); os.IsNotExist(err) {
		fmt.Printf("Creating dir: %s\n", pth)
		os.MkdirAll(pth, 0777)
	} else {
		fmt.Printf("Path exists: %s\n", pth)
	}

}

func parseConfig(configPath string) AppConfig {
	cont, err := ioutil.ReadFile(configPath)
	if err != nil {
		panic(err)
	}
	config := AppConfig{}
	err = json.Unmarshal(cont, &config)
	if (err != nil) {
		panic(err)
	}
	// fixup paths to be relative to config file
	configDir, _ := filepath.Abs(filepath.Dir(configPath))
	config.InputRoot = filepath.Join(configDir, config.InputRoot)
	config.OutputDir = filepath.Join(configDir, config.OutputDir)

	checkDir(config.InputRoot)

	return config
}

func dumpManifest(config *AppConfig) {

	all, csum := getCheckSumForFiles(config)
	for _, v := range all {
		fmt.Printf("%s %s\n", v.pth, v.checksum)
	}
	fmt.Printf("Total: %s\n", csum)
}

func main() {
	manifest := flag.Bool("manifest", false, "Show manifest (requires --config)")
	treeHash := flag.String("treehash", "", "Show manifest for specified path (no config needed)")
	toParse := flag.String("config", "", "Json config file")
	startBuild := flag.Bool("build", false, "Run build")
	archiveDir := flag.String("archive", "", "Archive root dir (needed if HASHIBUILD_ARCHIVE env var is not set)")
	fetch := flag.String("fetch", "", "Fetch remote archive file to local archive")
	if len(os.Args) < 2 {
		flag.Usage()
		return
	}

	flag.Parse()

	var config AppConfig
	if (*toParse) != "" {
		config = parseConfig(*toParse)
		if *archiveDir != "" {
			config.ArchiveLocal = *archiveDir
		}
		if config.ArchiveLocal == "" {
			config.ArchiveLocal = os.Getenv("HASHIBUILD_ARCHIVE")
		}
		if config.ArchiveRemote == "" {
			config.ArchiveRemote = os.Getenv("HASHIBUILD_ARCHIVE_REMOTE")
		}
	}

	if *fetch != "" {
		_, inputChecksum := getCheckSumForFiles(&config)
		// if finding archive found, unzip it and we are ready
		zipName, _ := discoverArchive(&config, inputChecksum)
		fmt.Printf("%s", zipName)
	}

	if *manifest {
		dumpManifest(&config)
	}

	if *startBuild {
		buildWithConfig(&config)
	}

	if len(*treeHash) > 0 {
		pth, _ := filepath.Abs(*treeHash)

		config := AppConfig{InputRoot: pth }
		dumpManifest(&config)
	}

}
<commit_msg>support salt<commit_after>package main

import (
	"io"
	"net/http"
	"path"
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
)

type DirEntry struct {
	pth      string
	fi       os.FileInfo
	checksum string
}

type DirEntries []DirEntry

func (a DirEntries) Len() int           { return len(a) }
func (a DirEntries) Less(i, j int) bool { return a[i].pth < a[j].pth }
func (a DirEntries) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type AppConfig struct {
	Name        string
	InputRoot   string
	OutputDir   string
	BuildCmd    string
	ArchiveLocal string
	ArchiveRemote string
	Include		[]string
	Exclude		[]string
	Salt		string
}

func countFullChecksum(ents *DirEntries) {
	for i, v := range *ents {
		st, err := os.Stat(v.pth)
		if err != nil {
			fmt.Printf("SKIP bad file [%s]\n", v.pth)
			continue
		}
		v.fi = st

		if v.fi.IsDir() {
			continue
		}

		if v.fi.Size() == 0 {
			v.checksum = "0000"
			continue
		}

		dat, err := ioutil.ReadFile(v.pth)
		if err != nil {
			panic(err)
		}
		var csum = md5.Sum(dat)
		v.checksum = hex.EncodeToString(csum[:])
		//fmt.Printf("%s\n %x", v.checksum, csum)
		(*ents)[i] = v
	}
}

func shouldIgnore(config *AppConfig, pth string) bool {
	for _,v := range config.Exclude {
		if strings.HasPrefix(pth, v) {
			return true
		}
	}

	if len(config.Include) == 0 {
		return false
	}

	for _, v := range config.Include {
		if strings.HasPrefix(pth, v) {
			return false
		}
	}
	return true
}

func collectWithGit(config *AppConfig) DirEntries{
	cmd := exec.Command("git", "ls-files")

	cmd.Dir = config.InputRoot
	out, err := cmd.Output()
	if (err != nil) {
		panic(err)
	}
	asStr := string(out)
	lines := strings.Split(asStr, "\n")
	var all DirEntries

	for _, v := range lines {
		if shouldIgnore(config, v) {
			continue
		}
		all = append(all, DirEntry { pth: path.Join(config.InputRoot ,v)})
	}
	return all
}

func normalizePaths(ents *DirEntries, rootPath string) {
	for i, v := range *ents {
		oldpath := v.pth
		newpath := strings.Replace(strings.TrimPrefix(oldpath, rootPath+"/"), "\\", "/", -1)
		v.pth = newpath
		(*ents)[i] = v
	}
}

func collectByConfig(config *AppConfig) DirEntries {
	return collectWithGit(config)
}

func getCheckSumForFiles(config *AppConfig) (DirEntries, string) {
	all := collectByConfig(config)
	sort.Sort(all)
	countFullChecksum(&all)
	normalizePaths(&all, config.InputRoot)
	var manifest bytes.Buffer
	for _, v := range all {
		manifest.WriteString(v.pth)
		manifest.WriteString(v.checksum)
		//fmt.Printf("%s %s\n", v.pth, v.checksum)
	}
	if config.Salt != "" {
		manifest.WriteString(config.Salt)
	}

	manifestSum := md5.Sum(manifest.Bytes())
	return all, hex.EncodeToString(manifestSum[:])
}

func run(cwd string, bin string, arg ...string) {
	fmt.Printf("> %s %s", bin, arg)
	cmd := exec.Command(bin, arg...)
	cmd.Dir = cwd
	out, err := cmd.CombinedOutput()
	fmt.Printf("%s", string(out))
	if err != nil {
		panic(err)
	}
}

func zipOutput(path string, zipfile string) {
	run(path, "zip", "-r", zipfile, "*")
}

func unzipOutput(pth string, zipfile string) {
	// we will replace the old path completely
	ensureDir(pth)
	err := os.RemoveAll(pth)
	if err != nil {
		panic(err)
	}
	ensureDir(pth)
	run(".", "unzip", zipfile, "-d"+pth)
}

func runBuildCommand(config *AppConfig) {
	fmt.Printf("Running build command '%s' in %s\n", config.BuildCmd, config.InputRoot)
	parts := strings.Fields(config.BuildCmd)
	cmd := exec.Command(parts[0], parts[1:]...)
	cmd.Dir = config.InputRoot
	out, err := cmd.CombinedOutput()
	fmt.Println(string(out))
	if err != nil {
		fmt.Printf("Build failed with error!")
		panic(err)
	}
}

func fetchTo(url string, to string) bool {
	fmt.Printf("GET %s\n", url)
	resp, err := http.Get(url)
	if err != nil || resp.StatusCode != 200 {
		fmt.Printf("Not available: %s\n", url)
		return false
	}
	defer resp.Body.Close()
	out, err := os.Create(to)
	if err != nil {
		panic(err)
	}
	defer out.Close()
	_, err  = io.Copy(out, resp.Body)
	if err != nil {
		panic(err)
	}
	return true
}

func discoverArchive(config *AppConfig, checksum string) (string,bool) {
	archiveRoot := config.ArchiveLocal
	zipName := config.Name + "_" + checksum + ".zip"

	// 1. just try local
	localZipName := filepath.Join(archiveRoot, zipName)
	_, err := os.Stat(localZipName)
	if err == nil {
		return localZipName, true
	}

	// 2. try remote if applicable

	remoteArchive := config.ArchiveRemote

	if remoteArchive == "" {
		return localZipName, false
	}
	if strings.Index(remoteArchive, "[ZIP]") == -1 {
		fmt.Printf("Error: remote archive template %s does not contain [ZIP]\n", remoteArchive)
		return "", false
	}
	remoteUrl := strings.Replace(remoteArchive, "[ZIP]", zipName, -1)
	fetched := fetchTo(remoteUrl, localZipName)
	if !fetched {
		return localZipName, false
	}
	return localZipName, true
}

func buildWithConfig(config *AppConfig) {
	// check input checksum
	fmt.Printf("Config %s\n", config)
	archiveRoot := config.ArchiveLocal

	if archiveRoot == "" {
		fmt.Println("HASHIBUILD_ARCHIVE not set, building without artifact caching")
		runBuildCommand(config)
		return
	}

	_, inputChecksum := getCheckSumForFiles(config)
	// if finding archive found, unzip it and we are ready
	ensureDir(archiveRoot)

	zipName, found := discoverArchive(config, inputChecksum)

	if found {
		fmt.Printf("Unzip %s to %s\n", zipName, config.OutputDir)
		unzipOutput(config.OutputDir, zipName)
		return
	}

	// run build if mismatch

	runBuildCommand(config)

	// zip the results
	fmt.Printf("Zipping %s to %s\n", config.OutputDir, zipName)
	zipOutput(config.OutputDir, zipName)
}

func checkDir(pth string) {
	if _, err := os.Stat(pth); os.IsNotExist(err) {
		fmt.Printf("Path does not exist: %s", pth)
		panic(err)
	}
}

func ensureDir(pth string) {
	if _, err := os.Stat(pth); os.IsNotExist(err) {
		fmt.Printf("Creating dir: %s\n", pth)
		os.MkdirAll(pth, 0777)
	} else {
		fmt.Printf("Path exists: %s\n", pth)
	}

}

func parseConfig(configPath string) AppConfig {
	cont, err := ioutil.ReadFile(configPath)
	if err != nil {
		panic(err)
	}
	config := AppConfig{}
	err = json.Unmarshal(cont, &config)
	if (err != nil) {
		panic(err)
	}
	// fixup paths to be relative to config file
	configDir, _ := filepath.Abs(filepath.Dir(configPath))
	config.InputRoot = filepath.Join(configDir, config.InputRoot)
	config.OutputDir = filepath.Join(configDir, config.OutputDir)
	checkDir(config.InputRoot)
	return config
}

func dumpManifest(config *AppConfig) {

	all, csum := getCheckSumForFiles(config)
	for _, v := range all {
		fmt.Printf("%s %s\n", v.pth, v.checksum)
	}
	fmt.Printf("Total: %s\n", csum)
}

func main() {
	manifest := flag.Bool("manifest", false, "Show manifest (requires --config)")
	treeHash := flag.String("treehash", "", "Show manifest for specified path (no config needed)")
	toParse := flag.String("config", "", "Json config file")
	startBuild := flag.Bool("build", false, "Run build")
	archiveDir := flag.String("archive", "", "Archive root dir (needed if HASHIBUILD_ARCHIVE env var is not set)")
	fetch := flag.String("fetch", "", "Fetch remote archive file to local archive")
	salt := flag.String("salt", "", "Provide salt string to invalidate hashes that would otherwise be same")
	if len(os.Args) < 2 {
		flag.Usage()
		return
	}

	flag.Parse()

	var config AppConfig
	if (*toParse) != "" {
		config = parseConfig(*toParse)
		if *archiveDir != "" {
			config.ArchiveLocal = *archiveDir
		}
		if config.ArchiveLocal == "" {
			config.ArchiveLocal = os.Getenv("HASHIBUILD_ARCHIVE")
		}
		if config.ArchiveRemote == "" {
			config.ArchiveRemote = os.Getenv("HASHIBUILD_ARCHIVE_REMOTE")
		}
		config.Salt = *salt
	}

	if *fetch != "" {
		_, inputChecksum := getCheckSumForFiles(&config)
		// if finding archive found, unzip it and we are ready
		zipName, _ := discoverArchive(&config, inputChecksum)
		fmt.Printf("%s", zipName)
	}

	if *manifest {
		dumpManifest(&config)
	}

	if *startBuild {
		buildWithConfig(&config)
	}

	if len(*treeHash) > 0 {
		pth, _ := filepath.Abs(*treeHash)

		config := AppConfig{InputRoot: pth, Salt: *salt }
		dumpManifest(&config)
	}

}
<|endoftext|>


<commit_before>package clusterresourceoverride

import (
	"fmt"
	"io"
	"strings"

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apiserver/pkg/admission"
	kapi "k8s.io/kubernetes/pkg/apis/core"
	kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
	kadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
	"k8s.io/kubernetes/plugin/pkg/admission/limitranger"

	oadmission "github.com/openshift/origin/pkg/cmd/server/admission"
	configlatest "github.com/openshift/origin/pkg/cmd/server/apis/config/latest"
	"github.com/openshift/origin/pkg/project/cache"
	"github.com/openshift/origin/pkg/project/registry/projectrequest/delegated"
	api "github.com/openshift/origin/pkg/quota/admission/apis/clusterresourceoverride"
	"github.com/openshift/origin/pkg/quota/admission/apis/clusterresourceoverride/validation"
)

const (
	clusterResourceOverrideAnnotation = "quota.openshift.io/cluster-resource-override-enabled"
	cpuBaseScaleFactor                = 1000.0 / (1024.0 * 1024.0 * 1024.0) // 1000 milliCores per 1GiB
)

var (
	cpuFloor = resource.MustParse("1m")
	memFloor = resource.MustParse("1Mi")
)

func Register(plugins *admission.Plugins) {
	plugins.Register(api.PluginName,
		func(config io.Reader) (admission.Interface, error) {
			pluginConfig, err := ReadConfig(config)
			if err != nil {
				return nil, err
			}
			if pluginConfig == nil {
				glog.Infof("Admission plugin %q is not configured so it will be disabled.", api.PluginName)
				return nil, nil
			}
			return newClusterResourceOverride(pluginConfig)
		})
}

type internalConfig struct {
	limitCPUToMemoryRatio     float64
	cpuRequestToLimitRatio    float64
	memoryRequestToLimitRatio float64
}
type clusterResourceOverridePlugin struct {
	*admission.Handler
	config       *internalConfig
	ProjectCache *cache.ProjectCache
	LimitRanger  admission.Interface
}

var _ = oadmission.WantsProjectCache(&clusterResourceOverridePlugin{})
var _ = kadmission.WantsInternalKubeInformerFactory(&clusterResourceOverridePlugin{})
var _ = kadmission.WantsInternalKubeClientSet(&clusterResourceOverridePlugin{})

// newClusterResourceOverride returns an admission controller for containers that
// configurably overrides container resource request/limits
func newClusterResourceOverride(config *api.ClusterResourceOverrideConfig) (admission.Interface, error) {
	glog.V(2).Infof("%s admission controller loaded with config: %v", api.PluginName, config)
	var internal *internalConfig
	if config != nil {
		internal = &internalConfig{
			limitCPUToMemoryRatio:     float64(config.LimitCPUToMemoryPercent) / 100,
			cpuRequestToLimitRatio:    float64(config.CPURequestToLimitPercent) / 100,
			memoryRequestToLimitRatio: float64(config.MemoryRequestToLimitPercent) / 100,
		}
	}

	limitRanger, err := limitranger.NewLimitRanger(nil)
	if err != nil {
		return nil, err
	}

	return &clusterResourceOverridePlugin{
		Handler:     admission.NewHandler(admission.Create),
		config:      internal,
		LimitRanger: limitRanger,
	}, nil
}

func (d *clusterResourceOverridePlugin) SetInternalKubeInformerFactory(i informers.SharedInformerFactory) {
	d.LimitRanger.(kadmission.WantsInternalKubeInformerFactory).SetInternalKubeInformerFactory(i)
}

func (d *clusterResourceOverridePlugin) SetInternalKubeClientSet(c kclientset.Interface) {
	d.LimitRanger.(kadmission.WantsInternalKubeClientSet).SetInternalKubeClientSet(c)
}

func (a *clusterResourceOverridePlugin) SetProjectCache(projectCache *cache.ProjectCache) {
	a.ProjectCache = projectCache
}

func ReadConfig(configFile io.Reader) (*api.ClusterResourceOverrideConfig, error) {
	obj, err := configlatest.ReadYAML(configFile)
	if err != nil {
		glog.V(5).Infof("%s error reading config: %v", api.PluginName, err)
		return nil, err
	}
	if obj == nil {
		return nil, nil
	}
	config, ok := obj.(*api.ClusterResourceOverrideConfig)
	if !ok {
		return nil, fmt.Errorf("unexpected config object: %#v", obj)
	}
	glog.V(5).Infof("%s config is: %v", api.PluginName, config)
	if errs := validation.Validate(config); len(errs) > 0 {
		return nil, errs.ToAggregate()
	}

	return config, nil
}

func (a *clusterResourceOverridePlugin) ValidateInitialization() error {
	if a.ProjectCache == nil {
		return fmt.Errorf("%s did not get a project cache", api.PluginName)
	}
	v, ok := a.LimitRanger.(admission.InitializationValidator)
	if !ok {
		return fmt.Errorf("LimitRanger does not implement kadmission.Validator")
	}
	return v.ValidateInitialization()
}

func isExemptedNamespace(name string) bool {
	for _, s := range delegated.ForbiddenNames {
		if name == s {
			return true
		}
	}
	for _, s := range delegated.ForbiddenPrefixes {
		if strings.HasPrefix(name, s) {
			return true
		}
	}
	return false
}

// TODO this will need to update when we have pod requests/limits
func (a *clusterResourceOverridePlugin) Admit(attr admission.Attributes) error {
	glog.V(6).Infof("%s admission controller is invoked", api.PluginName)
	if a.config == nil || attr.GetResource().GroupResource() != kapi.Resource("pods") || attr.GetSubresource() != "" {
		return nil // not applicable
	}
	pod, ok := attr.GetObject().(*kapi.Pod)
	if !ok {
		return admission.NewForbidden(attr, fmt.Errorf("unexpected object: %#v", attr.GetObject()))
	}
	glog.V(5).Infof("%s is looking at creating pod %s in project %s", api.PluginName, pod.Name, attr.GetNamespace())

	// allow annotations on project to override
	ns, err := a.ProjectCache.GetNamespace(attr.GetNamespace())
	if err != nil {
		glog.Warningf("%s got an error retrieving namespace: %v", api.PluginName, err)
		return admission.NewForbidden(attr, err) // this should not happen though
	}

	projectEnabledPlugin, exists := ns.Annotations[clusterResourceOverrideAnnotation]
	if exists && projectEnabledPlugin != "true" {
		glog.V(5).Infof("%s is disabled for project %s", api.PluginName, attr.GetNamespace())
		return nil // disabled for this project, do nothing
	}

	if isExemptedNamespace(ns.Name) {
		glog.V(5).Infof("%s is skipping exempted project %s", api.PluginName, attr.GetNamespace())
		return nil // project is exempted, do nothing
	}

	// Reuse LimitRanger logic to apply limit/req defaults from the project. Ignore validation
	// errors, assume that LimitRanger will run after this plugin to validate.
	glog.V(5).Infof("%s: initial pod limits are: %#v", api.PluginName, pod.Spec)
	if err := a.LimitRanger.(admission.MutationInterface).Admit(attr); err != nil {
		glog.V(5).Infof("%s: error from LimitRanger: %#v", api.PluginName, err)
	}
	glog.V(5).Infof("%s: pod limits after LimitRanger: %#v", api.PluginName, pod.Spec)
	for i := range pod.Spec.InitContainers {
		updateContainerResources(a.config, &pod.Spec.InitContainers[i])
	}
	for i := range pod.Spec.Containers {
		updateContainerResources(a.config, &pod.Spec.Containers[i])
	}
	glog.V(5).Infof("%s: pod limits after overrides are: %#v", api.PluginName, pod.Spec)
	return nil
}

func updateContainerResources(config *internalConfig, container *kapi.Container) {
	resources := container.Resources
	memLimit, memFound := resources.Limits[kapi.ResourceMemory]
	if memFound && config.memoryRequestToLimitRatio != 0 {
		// memory is measured in whole bytes.
		// the plugin rounds down to the nearest MiB rather than bytes to improve ease of use for end-users.
		amount := memLimit.Value() * int64(config.memoryRequestToLimitRatio*100) / 100
		// TODO: move into resource.Quantity
		var mod int64
		switch memLimit.Format {
		case resource.BinarySI:
			mod = 1024 * 1024
		default:
			mod = 1000 * 1000
		}
		if rem := amount % mod; rem != 0 {
			amount = amount - rem
		}
		q := resource.NewQuantity(int64(amount), memLimit.Format)
		if memFloor.Cmp(*q) > 0 {
			q = memFloor.Copy()
		}
		resources.Requests[kapi.ResourceMemory] = *q
	}
	if memFound && config.limitCPUToMemoryRatio != 0 {
		amount := float64(memLimit.Value()) * config.limitCPUToMemoryRatio * cpuBaseScaleFactor
		q := resource.NewMilliQuantity(int64(amount), resource.DecimalSI)
		if cpuFloor.Cmp(*q) > 0 {
			q = cpuFloor.Copy()
		}
		resources.Limits[kapi.ResourceCPU] = *q
	}

	cpuLimit, cpuFound := resources.Limits[kapi.ResourceCPU]
	if cpuFound && config.cpuRequestToLimitRatio != 0 {
		amount := float64(cpuLimit.MilliValue()) * config.cpuRequestToLimitRatio
		q := resource.NewMilliQuantity(int64(amount), cpuLimit.Format)
		if cpuFloor.Cmp(*q) > 0 {
			q = cpuFloor.Copy()
		}
		resources.Requests[kapi.ResourceCPU] = *q
	}

}
<commit_msg>Do not mutate resource limits below namespace minimums<commit_after>package clusterresourceoverride

import (
	"fmt"
	"io"
	"strings"

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apiserver/pkg/admission"
	kapi "k8s.io/kubernetes/pkg/apis/core"
	kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
	kadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
	"k8s.io/kubernetes/plugin/pkg/admission/limitranger"

	oadmission "github.com/openshift/origin/pkg/cmd/server/admission"
	configlatest "github.com/openshift/origin/pkg/cmd/server/apis/config/latest"
	"github.com/openshift/origin/pkg/project/cache"
	"github.com/openshift/origin/pkg/project/registry/projectrequest/delegated"
	api "github.com/openshift/origin/pkg/quota/admission/apis/clusterresourceoverride"
	"github.com/openshift/origin/pkg/quota/admission/apis/clusterresourceoverride/validation"
)

const (
	clusterResourceOverrideAnnotation = "quota.openshift.io/cluster-resource-override-enabled"
	cpuBaseScaleFactor                = 1000.0 / (1024.0 * 1024.0 * 1024.0) // 1000 milliCores per 1GiB
)

var (
	cpuFloor = resource.MustParse("1m")
	memFloor = resource.MustParse("1Mi")
)

func Register(plugins *admission.Plugins) {
	plugins.Register(api.PluginName,
		func(config io.Reader) (admission.Interface, error) {
			pluginConfig, err := ReadConfig(config)
			if err != nil {
				return nil, err
			}
			if pluginConfig == nil {
				glog.Infof("Admission plugin %q is not configured so it will be disabled.", api.PluginName)
				return nil, nil
			}
			return newClusterResourceOverride(pluginConfig, defaultGetNamespaceLimitRanges)
		})
}

type namespaceLimitsFunc func(a *clusterResourceOverridePlugin, attr admission.Attributes) ([]*kapi.LimitRange, error)

type internalConfig struct {
	limitCPUToMemoryRatio     float64
	cpuRequestToLimitRatio    float64
	memoryRequestToLimitRatio float64
}
type clusterResourceOverridePlugin struct {
	*admission.Handler
	config       *internalConfig
	ProjectCache *cache.ProjectCache
	LimitRanger  admission.Interface
	namespaceLimitsFunc
}

var _ = oadmission.WantsProjectCache(&clusterResourceOverridePlugin{})
var _ = kadmission.WantsInternalKubeInformerFactory(&clusterResourceOverridePlugin{})
var _ = kadmission.WantsInternalKubeClientSet(&clusterResourceOverridePlugin{})

// newClusterResourceOverride returns an admission controller for containers that
// configurably overrides container resource request/limits
func newClusterResourceOverride(config *api.ClusterResourceOverrideConfig, f namespaceLimitsFunc) (admission.Interface, error) {
	glog.V(2).Infof("%s admission controller loaded with config: %v", api.PluginName, config)
	var internal *internalConfig
	if config != nil {
		internal = &internalConfig{
			limitCPUToMemoryRatio:     float64(config.LimitCPUToMemoryPercent) / 100,
			cpuRequestToLimitRatio:    float64(config.CPURequestToLimitPercent) / 100,
			memoryRequestToLimitRatio: float64(config.MemoryRequestToLimitPercent) / 100,
		}
	}

	limitRanger, err := limitranger.NewLimitRanger(nil)
	if err != nil {
		return nil, err
	}

	return &clusterResourceOverridePlugin{
		Handler:             admission.NewHandler(admission.Create),
		config:              internal,
		LimitRanger:         limitRanger,
		namespaceLimitsFunc: f,
	}, nil
}

func (d *clusterResourceOverridePlugin) SetInternalKubeInformerFactory(i informers.SharedInformerFactory) {
	d.LimitRanger.(kadmission.WantsInternalKubeInformerFactory).SetInternalKubeInformerFactory(i)
}

func (d *clusterResourceOverridePlugin) SetInternalKubeClientSet(c kclientset.Interface) {
	d.LimitRanger.(kadmission.WantsInternalKubeClientSet).SetInternalKubeClientSet(c)
}

func (a *clusterResourceOverridePlugin) SetProjectCache(projectCache *cache.ProjectCache) {
	a.ProjectCache = projectCache
}

func ReadConfig(configFile io.Reader) (*api.ClusterResourceOverrideConfig, error) {
	obj, err := configlatest.ReadYAML(configFile)
	if err != nil {
		glog.V(5).Infof("%s error reading config: %v", api.PluginName, err)
		return nil, err
	}
	if obj == nil {
		return nil, nil
	}
	config, ok := obj.(*api.ClusterResourceOverrideConfig)
	if !ok {
		return nil, fmt.Errorf("unexpected config object: %#v", obj)
	}
	glog.V(5).Infof("%s config is: %v", api.PluginName, config)
	if errs := validation.Validate(config); len(errs) > 0 {
		return nil, errs.ToAggregate()
	}

	return config, nil
}

func (a *clusterResourceOverridePlugin) ValidateInitialization() error {
	if a.ProjectCache == nil {
		return fmt.Errorf("%s did not get a project cache", api.PluginName)
	}
	v, ok := a.LimitRanger.(admission.InitializationValidator)
	if !ok {
		return fmt.Errorf("LimitRanger does not implement kadmission.Validator")
	}
	return v.ValidateInitialization()
}

func isExemptedNamespace(name string) bool {
	for _, s := range delegated.ForbiddenNames {
		if name == s {
			return true
		}
	}
	for _, s := range delegated.ForbiddenPrefixes {
		if strings.HasPrefix(name, s) {
			return true
		}
	}
	return false
}

// TODO this will need to update when we have pod requests/limits
func (a *clusterResourceOverridePlugin) Admit(attr admission.Attributes) error {
	glog.V(6).Infof("%s admission controller is invoked", api.PluginName)
	if a.config == nil || attr.GetResource().GroupResource() != kapi.Resource("pods") || attr.GetSubresource() != "" {
		return nil // not applicable
	}
	pod, ok := attr.GetObject().(*kapi.Pod)
	if !ok {
		return admission.NewForbidden(attr, fmt.Errorf("unexpected object: %#v", attr.GetObject()))
	}
	glog.V(5).Infof("%s is looking at creating pod %s in project %s", api.PluginName, pod.Name, attr.GetNamespace())

	// allow annotations on project to override
	ns, err := a.ProjectCache.GetNamespace(attr.GetNamespace())
	if err != nil {
		glog.Warningf("%s got an error retrieving namespace: %v", api.PluginName, err)
		return admission.NewForbidden(attr, err) // this should not happen though
	}

	projectEnabledPlugin, exists := ns.Annotations[clusterResourceOverrideAnnotation]
	if exists && projectEnabledPlugin != "true" {
		glog.V(5).Infof("%s is disabled for project %s", api.PluginName, attr.GetNamespace())
		return nil // disabled for this project, do nothing
	}

	if isExemptedNamespace(ns.Name) {
		glog.V(5).Infof("%s is skipping exempted project %s", api.PluginName, attr.GetNamespace())
		return nil // project is exempted, do nothing
	}

	namespaceLimits := []*kapi.LimitRange{}

	if a.namespaceLimitsFunc != nil {
		limits, err := a.namespaceLimitsFunc(a, attr)
		if err != nil {
			return err
		}
		namespaceLimits = limits
	}

	// Don't mutate resource requirements below the namespace
	// limit minimums.
	nsCPUFloor, nsMemFloor := minResourceLimits(namespaceLimits)

	// Reuse LimitRanger logic to apply limit/req defaults from the project. Ignore validation
	// errors, assume that LimitRanger will run after this plugin to validate.
	glog.V(5).Infof("%s: initial pod limits are: %#v", api.PluginName, pod.Spec)
	if err := a.LimitRanger.(admission.MutationInterface).Admit(attr); err != nil {
		glog.V(5).Infof("%s: error from LimitRanger: %#v", api.PluginName, err)
	}
	glog.V(5).Infof("%s: pod limits after LimitRanger: %#v", api.PluginName, pod.Spec)
	for i := range pod.Spec.InitContainers {
		updateContainerResources(a.config, &pod.Spec.InitContainers[i], nsCPUFloor, nsMemFloor)
	}
	for i := range pod.Spec.Containers {
		updateContainerResources(a.config, &pod.Spec.Containers[i], nsCPUFloor, nsMemFloor)
	}
	glog.V(5).Infof("%s: pod limits after overrides are: %#v", api.PluginName, pod.Spec)
	return nil
}

func updateContainerResources(config *internalConfig, container *kapi.Container, nsCPUFloor, nsMemFloor *resource.Quantity) {
	resources := container.Resources
	memLimit, memFound := resources.Limits[kapi.ResourceMemory]
	if memFound && config.memoryRequestToLimitRatio != 0 {
		// memory is measured in whole bytes.
		// the plugin rounds down to the nearest MiB rather than bytes to improve ease of use for end-users.
		amount := memLimit.Value() * int64(config.memoryRequestToLimitRatio*100) / 100
		// TODO: move into resource.Quantity
		var mod int64
		switch memLimit.Format {
		case resource.BinarySI:
			mod = 1024 * 1024
		default:
			mod = 1000 * 1000
		}
		if rem := amount % mod; rem != 0 {
			amount = amount - rem
		}
		q := resource.NewQuantity(int64(amount), memLimit.Format)
		if memFloor.Cmp(*q) > 0 {
			q = memFloor.Copy()
		}
		if nsMemFloor != nil && q.Cmp(*nsMemFloor) < 0 {
			glog.V(5).Infof("%s: %s pod limit %q below namespace limit; setting limit to %q", api.PluginName, kapi.ResourceMemory, q.String(), nsMemFloor.String())
			q = nsMemFloor.Copy()
		}
		resources.Requests[kapi.ResourceMemory] = *q
	}
	if memFound && config.limitCPUToMemoryRatio != 0 {
		amount := float64(memLimit.Value()) * config.limitCPUToMemoryRatio * cpuBaseScaleFactor
		q := resource.NewMilliQuantity(int64(amount), resource.DecimalSI)
		if cpuFloor.Cmp(*q) > 0 {
			q = cpuFloor.Copy()
		}
		if nsCPUFloor != nil && q.Cmp(*nsCPUFloor) < 0 {
			glog.V(5).Infof("%s: %s pod limit %q below namespace limit; setting limit to %q", api.PluginName, kapi.ResourceCPU, q.String(), nsCPUFloor.String())
			q = nsCPUFloor.Copy()
		}
		resources.Limits[kapi.ResourceCPU] = *q
	}

	cpuLimit, cpuFound := resources.Limits[kapi.ResourceCPU]
	if cpuFound && config.cpuRequestToLimitRatio != 0 {
		amount := float64(cpuLimit.MilliValue()) * config.cpuRequestToLimitRatio
		q := resource.NewMilliQuantity(int64(amount), cpuLimit.Format)
		if cpuFloor.Cmp(*q) > 0 {
			q = cpuFloor.Copy()
		}
		if nsCPUFloor != nil && q.Cmp(*nsCPUFloor) < 0 {
			glog.V(5).Infof("%s: %s pod limit %q below namespace limit; setting limit to %q", api.PluginName, kapi.ResourceCPU, q.String(), nsCPUFloor.String())
			q = nsCPUFloor.Copy()
		}
		resources.Requests[kapi.ResourceCPU] = *q
	}
}

// minResourceLimits finds the minimum CPU and minimum Memory resource
// values across all the limits in limitRanges. Nil is returned if
// there is CPU or Memory resource respectively.
func minResourceLimits(limitRanges []*kapi.LimitRange) (*resource.Quantity, *resource.Quantity) {
	cpuLimits := []*resource.Quantity{}
	memLimits := []*resource.Quantity{}

	for _, limitRange := range limitRanges {
		for _, limit := range limitRange.Spec.Limits {
			if limit.Type != kapi.LimitTypeContainer {
				continue
			}
			if cpuLimit, cpuFound := limit.Min[kapi.ResourceCPU]; cpuFound {
				cpuLimits = append(cpuLimits, cpuLimit.Copy())
			}
			if memLimit, memFound := limit.Min[kapi.ResourceMemory]; memFound {
				memLimits = append(memLimits, memLimit.Copy())
			}
		}
	}

	var cpuMin *resource.Quantity
	var memMin *resource.Quantity

	if len(cpuLimits) > 0 {
		cpuMin = minQuantity(cpuLimits)
	}

	if len(memLimits) > 0 {
		memMin = minQuantity(memLimits)
	}

	return cpuMin, memMin
}

func minQuantity(quantities []*resource.Quantity) *resource.Quantity {
	min := quantities[0].Copy()

	for i := range quantities {
		if quantities[i].Cmp(*min) < 0 {
			min = quantities[i].Copy()
		}
	}

	return min
}

func defaultGetNamespaceLimitRanges(a *clusterResourceOverridePlugin, attr admission.Attributes) ([]*kapi.LimitRange, error) {
	return a.LimitRanger.(*limitranger.LimitRanger).GetLimitRanges(attr)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"math/big"
	"mime"
	"mime/multipart"
	"net/http"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/crowdmob/goamz/aws"
	"github.com/crowdmob/goamz/s3"
	"github.com/goji/httpauth"
	"github.com/tv42/base58"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
)

var (
	bucketName string
	baseURL    string
)

func init() {
	flag.StringVar(&bucketName, "b", "", "Bucket Name")
	flag.StringVar(&baseURL, "u", "", "Base URL")
}

func openBucket() (*s3.Bucket, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return nil, err
	}
	s := s3.New(auth, aws.EUWest)
	bucket := s.Bucket(bucketName)
	return bucket, nil
}

func readerToS3(ioReader io.Reader, basePath string, originalFilename string, prependOriginalFilename bool, contentType string, contentLength int64) (string, error) {
	bucket, err := openBucket()
	if err != nil {
		return "", err
	}

	fileExt := filepath.Ext(originalFilename)
	unixTime := time.Now().UTC().Unix()
	b58buf := base58.EncodeBig(nil, big.NewInt(unixTime))

	var filename string

	if prependOriginalFilename {
		name := strings.TrimSuffix(originalFilename, fileExt)
		filename = fmt.Sprintf("%s-%s%s", name, b58buf, fileExt)
	} else {
		filename = fmt.Sprintf("%s%s", b58buf, fileExt)
	}

	path := basePath + filename

	err = bucket.PutReader(path, ioReader, contentLength, contentType, s3.PublicRead, s3.Options{CacheControl: "public, max-age=315360000"})
	if err != nil {
		return "", err
	}

	url := fmt.Sprintf("%s/%s", baseURL, path)

	return url, nil
}

func uploadPartToS3(part *multipart.Part, basePath string) (string, error) {
	originalFilename := part.FileName()

	contentType := part.Header.Get("Content-Type")
	if contentType == "" {
		fileExt := filepath.Ext(originalFilename)
		contentType = mime.TypeByExtension(fileExt)

		if contentType == "" || contentType == "application/octet-stream" {
			contentType = "application/octet-stream"
		}
	}

	contentLength, err := strconv.ParseInt(part.Header.Get("Content-Length"), 10, 64)
	if err != nil {
		return "", err
	}

	url, err := readerToS3(part, basePath, originalFilename, false, contentType, contentLength)

	return url, err
}

func rootHandler(c web.C, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Nothing to see here!")
}

func tweetbot(c web.C, w http.ResponseWriter, r *http.Request) {
	multiReader, err := r.MultipartReader()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// We only want the first part, the media
	part, err := multiReader.NextPart()

	// Ensure that the Content-Length is set
	_, err = strconv.ParseInt(part.Header.Get("Content-Length"), 10, 64)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	url, err := uploadPartToS3(part, "tweetbot/")
	if err != nil {
		panic(err.Error())
	}

	responseMap := map[string]string{"url": url}
	jsonResponse, _ := json.Marshal(responseMap)

	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(jsonResponse))
}

func webDavUpload(c web.C, w http.ResponseWriter, r *http.Request) {
	// Ensure that the Content-Length is set
	if r.ContentLength < 1 {
		http.Error(w, "Content-Length must be set", http.StatusBadRequest)
		return
	}

	originalFilename := c.URLParams["name"]

	contentType := r.Header.Get("Content-Type")
	if contentType == "" || contentType == "application/octet-stream" {
		fileExt := filepath.Ext(originalFilename)
		contentType = mime.TypeByExtension(fileExt)

		if contentType == "" {
			contentType = "application/octet-stream"
		}
	}

	basePath := ""

	url, err := readerToS3(r.Body, basePath, originalFilename, true, contentType, r.ContentLength)

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

	http.Redirect(w, r, url, http.StatusCreated)
}

func PropfindInterceptHeader(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "PROPFIND" {
			xml := "<?xml version=\"1.0\" ?>\n" +
				"<D:multistatus xmlns:D=\"DAV:\">\n" +
				"<D:response>\n" +
				"<D:href>http://www.contoso.com/public/container/</D:href>\n" +
				"<D:propstat>\n" +
				"<D:status>HTTP/1.1 200 OK</D:status>\n" +
				"</D:propstat>\n" +
				"</D:response>\n" +
				"</D:multistatus>\n"

			w.WriteHeader(207)
			w.Header().Set("Content-Type", "text/xml")
			w.Write([]byte(xml))
			return
		}

		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func main() {
	goji.Use(PropfindInterceptHeader)
	goji.Use(httpauth.SimpleBasicAuth("x", "password"))

	goji.Get("/", rootHandler)

	goji.Put("/:name", webDavUpload)

	re := regexp.MustCompile(`\A/tweetbot/?\z`)
	goji.Post(re, tweetbot)

	goji.Serve()
}
<commit_msg>WebDAV works properly now!<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"math/big"
	"mime"
	"mime/multipart"
	"net/http"
	"path/filepath"
	"regexp"
	"strconv"
	"time"

	"github.com/crowdmob/goamz/aws"
	"github.com/crowdmob/goamz/s3"
	"github.com/goji/httpauth"
	"github.com/tv42/base58"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
)

var (
	bucketName string
	baseURL    string
)

func init() {
	flag.StringVar(&bucketName, "b", "", "Bucket Name")
	flag.StringVar(&baseURL, "u", "", "Base URL")
}

func openBucket() (*s3.Bucket, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return nil, err
	}
	s := s3.New(auth, aws.EUWest)
	bucket := s.Bucket(bucketName)
	return bucket, nil
}

func readerToS3(ioReader io.Reader, basePath string, originalFilename string, generateNewFileName bool, contentType string, contentLength int64) (string, error) {
	bucket, err := openBucket()
	if err != nil {
		return "", err
	}

	fileExt := filepath.Ext(originalFilename)

	var filename string

	if generateNewFileName {
		unixTime := time.Now().UTC().Unix()
		b58buf := base58.EncodeBig(nil, big.NewInt(unixTime))
		filename = fmt.Sprintf("%s%s", b58buf, fileExt)
	} else {
		filename = originalFilename
	}

	path := basePath + filename

	if contentType == "" || contentType == "application/octet-stream" {
		contentType = mime.TypeByExtension(fileExt)

		if contentType == "" {
			contentType = "application/octet-stream"
		}
	}

	err = bucket.PutReader(path, ioReader, contentLength, contentType, s3.PublicRead, s3.Options{CacheControl: "public, max-age=315360000"})
	if err != nil {
		return "", err
	}

	url := fmt.Sprintf("%s/%s", baseURL, path)

	return url, nil
}

func uploadPartToS3(part *multipart.Part, basePath string) (string, error) {
	originalFilename := part.FileName()

	contentType := part.Header.Get("Content-Type")

	contentLength, err := strconv.ParseInt(part.Header.Get("Content-Length"), 10, 64)
	if err != nil {
		return "", err
	}

	url, err := readerToS3(part, basePath, originalFilename, true, contentType, contentLength)

	return url, err
}

func rootHandler(c web.C, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Nothing to see here!")
}

func tweetbot(c web.C, w http.ResponseWriter, r *http.Request) {
	multiReader, err := r.MultipartReader()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// We only want the first part, the media
	part, err := multiReader.NextPart()

	// Ensure that the Content-Length is set
	_, err = strconv.ParseInt(part.Header.Get("Content-Length"), 10, 64)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	url, err := uploadPartToS3(part, "tweetbot/")
	if err != nil {
		panic(err.Error())
	}

	responseMap := map[string]string{"url": url}
	jsonResponse, _ := json.Marshal(responseMap)

	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(jsonResponse))
}

func webDavUpload(c web.C, w http.ResponseWriter, r *http.Request) {
	// Ensure that the Content-Length is set
	if r.ContentLength < 1 {
		http.Error(w, "Content-Length must be set", http.StatusBadRequest)
		return
	}

	originalFilename := c.URLParams["name"]

	contentType := r.Header.Get("Content-Type")

	basePath := ""

	url, err := readerToS3(r.Body, basePath, originalFilename, false, contentType, r.ContentLength)

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

	http.Redirect(w, r, url, http.StatusCreated)
}

func webDavDelete(c web.C, w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Deleting files is not supported", http.StatusNotImplemented)
}

func PropfindInterceptHeader(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "PROPFIND" {
			xml := "<?xml version=\"1.0\" ?>\n" +
				"<D:multistatus xmlns:D=\"DAV:\">\n" +
				"<D:response>\n" +
				"<D:href>http://www.contoso.com/public/container/</D:href>\n" +
				"<D:propstat>\n" +
				"<D:status>HTTP/1.1 200 OK</D:status>\n" +
				"</D:propstat>\n" +
				"</D:response>\n" +
				"</D:multistatus>\n"

			w.WriteHeader(207)
			w.Header().Set("Content-Type", "text/xml")
			w.Write([]byte(xml))
			return
		}

		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func main() {
	goji.Use(PropfindInterceptHeader)
	goji.Use(httpauth.SimpleBasicAuth("x", "password"))

	goji.Get("/", rootHandler)

	goji.Put("/:name", webDavUpload)
	goji.Delete("/:name", webDavDelete)

	re := regexp.MustCompile(`\A/tweetbot/?\z`)
	goji.Post(re, tweetbot)

	goji.Serve()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"

	"golang.org/x/tools/imports"
)

func run(fileName string) error {
	cmd := exec.Command("go", "run", fileName)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func tempFile() (*os.File, error) {
	curDir, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	file, err := ioutil.TempFile(curDir, "temp")
	if err != nil {
		return nil, err
	}
	file.Close()

	// Add the .go suffix to the temp file.
	if err = os.Rename(file.Name(), file.Name()+".go"); err != nil {
		return nil, err
	}
	return os.Open(file.Name() + ".go")
}

func editImports(fileName string) error {
	// Read the code from the temp go file.
	data, err := ioutil.ReadFile(fileName)
	if err != nil {
		return err
	}

	// res holds the go file re-written with imports added.
	opt := &imports.Options{}
	res, err := imports.Process(fileName, data, opt)
	if err != nil {
		return err
	}

	// Write the edited file into the original temp file.
	if err = ioutil.WriteFile(fileName, res, 0644); err != nil {
		return err
	}
	return nil
}

func usage() {
	fmt.Println("Usage: gommand [code]")
	fmt.Println("Example: gommand 'name := \"Sno6\"; fmt.Println(name)'")
	os.Exit(1)
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}
	code := os.Args[1]
	if code == "" {
		usage()
	}

	file, err := tempFile()
	if err != nil {
		log.Fatalf("main: error creating temp file: %v\n", err)
	}
	file.Close()

	defer func() {
		if err = os.Remove(file.Name()); err != nil {
			log.Printf("main: error removing temp file: %v\n", err)
		}
	}()

	// bp holds the go boiler plate code with user inputted code added.
	bp := fmt.Sprintf("package main\nfunc main() {\n\t%v\n}", code)

	// Write go code to temp file and add missing imports.
	// Use Printf over Fatalf so removing of temp file will run through defer.
	if err = ioutil.WriteFile(file.Name(), []byte(bp), 0644); err != nil {
		log.Printf("main: error writing code to temp file: %v\n", err)
	}
	if err = editImports(file.Name()); err != nil {
		log.Printf("main: error editing imports: %v\n", err)
	}
	if err = run(file.Name()); err != nil {
		log.Printf("main: error running go code query: %v\n", err)
	}
}
<commit_msg>Revert "Change file handling and spacing"<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"

	"golang.org/x/tools/imports"
)

func run(fileName string) error {
	cmd := exec.Command("go", "run", fileName)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func tempFile() (*os.File, error) {
	curDir, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	file, err := ioutil.TempFile(curDir, "temp")
	if err != nil {
		return nil, err
	}
	file.Close()

	// Add the .go suffix to the temp file.
	if err = os.Rename(file.Name(), file.Name()+".go"); err != nil {
		return nil, err
	}
	return os.Open(file.Name() + ".go")
}

func editImports(file *os.File) error {
	// Read the code from the temp go file.
	data, err := ioutil.ReadFile(file.Name())
	if err != nil {
		return err
	}

	// res holds the go file re-written with imports added.
	opt := &imports.Options{}
	res, err := imports.Process(file.Name(), data, opt)
	if err != nil {
		return err
	}

	// Write the edited file into the original temp file.
	if err = ioutil.WriteFile(file.Name(), res, 0644); err != nil {
		return err
	}
	return nil
}

func usage() {
	fmt.Println("Usage: gommand [code]")
	fmt.Println("Example: gommand 'name := \"Sno6\"; fmt.Println(name)'")
	os.Exit(1)
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}
	code := os.Args[1]
	if code == "" {
		usage()
	}

	file, err := tempFile()
	if err != nil {
		log.Fatalf("main: error creating temp file: %v\n", err)
	}
	file.Close()

	defer func() {
		if err = os.Remove(file.Name()); err != nil {
			log.Printf("main: error removing temp file: %v\n", err)
		}
	}()

	// bp holds the go boiler plate code with user inputted code added.
	bp := fmt.Sprintf("package main\nfunc main() {\n\t%v\n}", code)

	// Write go code to temp file and add missing imports.
	// Use Printf over Fatalf so removing of temp file will run through defer.
	if err = ioutil.WriteFile(file.Name(), []byte(bp), 0644); err != nil {
		log.Printf("main: error writing code to temp file: %v\n", err)
	}
	if err = editImports(file); err != nil {
		log.Printf("main: error editing imports: %v\n", err)
	}

	if err = run(file.Name()); err != nil {
		log.Printf("main: error running go code query: %v\n", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "image"
    "image/color"
    "image/png"
    "os"
    "sort"

    "github.com/zagrodzki/goscope/dummy"
    "github.com/zagrodzki/goscope/scope"
)

type Plot struct {
    img *image.RGBA
}

func (plot Plot) Fill(col color.RGBA) {
    for i := plot.img.Bounds().Min.X; i < plot.img.Bounds().Max.X; i++ {
        for j := plot.img.Bounds().Min.Y; j < plot.img.Bounds().Max.Y; j++ {
            plot.img.Set(i, j, col)
        }
    }
}

func Min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func Max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func Abs(a int) int {
    if a < 0 {
        return -a
    }
    return a
}

func LinePoints(p1, p2 image.Point) []image.Point {
    points := make([]image.Point, 0)
    if p1.X == p2.X {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            points = append(points, image.Point{p1.X, i})
        }
        return points
    }
    a := (float64(p1.Y) - float64(p2.Y)) / (float64(p1.X) - float64(p2.X))
    b := float64(p1.Y) - float64(p1.X) * a

    if Abs(p1.X - p2.X) >= Abs(p1.Y - p2.Y) {
        for i := Min(p1.X, p2.X); i <= Max(p1.X, p2.X); i++ {
            points = append(points, image.Point{i, int(a * float64(i) + b)})
        }
    } else {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            points = append(points, image.Point{int((float64(i) - b) / a), i})
        }
    }
    return points
}

func SamplesToPoints(s []scope.Sample, start, end image.Point) []image.Point {
    min_y := s[0]
    max_y := s[0]
    for _, y := range s {
        if min_y > y {
            min_y = y
        }
        if max_y < y {
            max_y = y
        }
    }
    range_x := float64(len(s)-1)
    range_y := float64(max_y - min_y)
    points := make([]image.Point, 0)
    sums := make(map[int]int)
    sizes := make(map[int]int)
    for i, y := range s {
        mapx := int(float64(start.X) + (float64(i) / range_x) * float64(end.X - start.X))
        mapy := int(float64(end.Y) - ((float64(y) - float64(min_y)) / range_y) * float64(end.Y - start.Y))
        sums[mapx] = sums[mapx]+mapy
        sizes[mapx] = sizes[mapx]+1
    }
    var keys []int
    for k := range sums {
        keys = append(keys, k)
    }
    sort.Ints(keys)
    for _, i := range keys {
        points = append(points, image.Point{i, int(float64(sums[i])/float64(sizes[i]))})
    }

    return points
}

func (plot Plot) DrawPoints(points []image.Point, col color.RGBA) {
    for _, p := range points {
        plot.img.Set(p.X, p.Y, col)
    }
}

func (plot Plot) DrawSamples(start, end image.Point, s []scope.Sample, col color.RGBA) {
    points := SamplesToPoints(s, start, end)
    plot.DrawPoints(points, col)
    for i := 1; i < len(points); i++ {
        plot.DrawPoints(LinePoints(points[i-1], points[i]), col)
    }
}

func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, col color.RGBA) {
    b := plot.img.Bounds()
    x1 := b.Min.X + 10
    x2 := b.Max.X - 10
    y1 := b.Min.Y + 10
    y2 := b.Min.Y + 10 + int((b.Max.Y - b.Min.Y - 10 * (len(samples) + 1)) / len(samples))
    step := y2 - b.Min.Y
    for _, v := range samples {
        plot.DrawSamples(image.Point{x1, y1}, image.Point{x2, y2}, v, col)
        y1 = y1 + step
        y2 = y2 + step
    }
}

func main() {
    dum, _ := dummy.Open("")
    data, stop, _ := dum.StartSampling()

    samples := (<-data).Samples
    fmt.Println(samples)
    stop()

    plot := Plot{img: image.NewRGBA(image.Rect(0, 0, 800, 600))}
    colWhite := color.RGBA{255, 255, 255, 255}
    colRed := color.RGBA{255, 0, 0, 255}
    plot.Fill(colWhite)
    plot.DrawAll(samples, colRed)

    f, err := os.Create("draw.png")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    png.Encode(f, plot.img)
}
<commit_msg>small changes<commit_after>package main

import (
    "fmt"
    "image"
    "image/color"
    "image/png"
    "os"
    "sort"

    "github.com/zagrodzki/goscope/dummy"
    "github.com/zagrodzki/goscope/scope"
)

type Plot struct {
    img *image.RGBA
}

func (plot Plot) Fill(col color.RGBA) {
    bounds := plot.img.Bounds()
    for i := bounds.Min.X; i < bounds.Max.X; i++ {
        for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
            plot.img.Set(i, j, col)
        }
    }
}

func Min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func Max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func Abs(a int) int {
    if a < 0 {
        return -a
    }
    return a
}

func LinePoints(p1, p2 image.Point) []image.Point {
    var points []image.Point
    if p1.X == p2.X {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            points = append(points, image.Point{p1.X, i})
        }
        return points
    }
    a := float64(p1.Y - p2.Y) / float64(p1.X - p2.X)
    b := float64(p1.Y) - float64(p1.X) * a

    if Abs(p1.X - p2.X) >= Abs(p1.Y - p2.Y) {
        for i := Min(p1.X, p2.X); i <= Max(p1.X, p2.X); i++ {
            points = append(points, image.Point{i, int(a * float64(i) + b)})
        }
    } else {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            points = append(points, image.Point{int((float64(i) - b) / a), i})
        }
    }
    return points
}

func SamplesToPoints(s []scope.Sample, start, end image.Point) []image.Point {
    if len(s) == 0 {
        return nil
    }
    minY := s[0]
    maxY := s[0]
    for _, y := range s {
        if minY > y {
            minY = y
        }
        if maxY < y {
            maxY = y
        }
    }
    rangeX := float64(len(s)-1)
    rangeY := float64(maxY - minY)
    var points []image.Point
    sums := make(map[int]int)
    sizes := make(map[int]int)
    for i, y := range s {
        mapX := int(float64(start.X) + (float64(i) / rangeX) * float64(end.X - start.X))
        mapY := int(float64(end.Y) - ((float64(y) - float64(minY)) / rangeY) * float64(end.Y - start.Y))
        sums[mapX] += mapY
        sizes[mapX] += 1
    }
    var keys []int
    for k := range sums {
        keys = append(keys, k)
    }
    sort.Ints(keys)
    for _, i := range keys {
        points = append(points, image.Point{i, int(float64(sums[i])/float64(sizes[i]))})
    }

    return points
}

func (plot Plot) DrawPoints(points []image.Point, col color.RGBA) {
    for _, p := range points {
        plot.img.Set(p.X, p.Y, col)
    }
}

func (plot Plot) DrawSamples(start, end image.Point, s []scope.Sample, col color.RGBA) {
    points := SamplesToPoints(s, start, end)
    plot.DrawPoints(points, col)
    for i := 1; i < len(points); i++ {
        plot.DrawPoints(LinePoints(points[i-1], points[i]), col)
    }
}

func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, col color.RGBA) {
    b := plot.img.Bounds()
    x1 := b.Min.X + 10
    x2 := b.Max.X - 10
    y1 := b.Min.Y + 10
    y2 := b.Min.Y + 10 + int((b.Max.Y - b.Min.Y - 10 * (len(samples) + 1)) / len(samples))
    step := y2 - b.Min.Y
    for _, v := range samples {
        plot.DrawSamples(image.Point{x1, y1}, image.Point{x2, y2}, v, col)
        y1 = y1 + step
        y2 = y2 + step
    }
}

func main() {
    dum, _ := dummy.Open("")
    data, stop, _ := dum.StartSampling()

    samples := (<-data).Samples
    fmt.Println(samples)
    stop()

    plot := Plot{img: image.NewRGBA(image.Rect(0, 0, 800, 600))}
    colWhite := color.RGBA{255, 255, 255, 255}
    colRed := color.RGBA{255, 0, 0, 255}
    plot.Fill(colWhite)
    plot.DrawAll(samples, colRed)

    f, err := os.Create("draw.png")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    png.Encode(f, plot.img)
}
<|endoftext|>


<commit_before>package synapse

// Handler is the interface used
// to register server response callbacks.
type Handler interface {

	// ServeCall handles a synapse request and
	// writes a response. It should be safe to
	// call ServeCall from multiple goroutines.
	// Both the request and response interfaces
	// become invalid after the call is returned;
	// no implementation of ServeCall should
	// maintain a reference to either object
	// after the function returns.
	ServeCall(req Request, res ResponseWriter)
}

// Status represents
// a response status code
type Status int

// These are the status
// codes that can be passed
// to a ResponseWriter as
// an error to send to the
// client.
const (
	// these are for
	// internal use;
	// 'ok' is not
	// an error
	invalidStatus Status = iota
	okStatus

	// NotFound means the
	// call requested could
	// not be located on the
	// server
	NotFound

	// BadRequest means the
	// request was not formatted
	// as expected
	BadRequest

	// NotAuthed means the client
	// was not authorized to make
	// the call
	NotAuthed

	// ServerError means the server
	// encountered an error when
	// making the call
	ServerError
)

// Status implements the error interface
func (s Status) Error() string {
	switch s {
	case okStatus:
		return "<ok>"
	case NotFound:
		return "not found"
	case BadRequest:
		return "bad request"
	case NotAuthed:
		return "not authorized"
	case ServerError:
		return "server error"
	default:
		return "<invalid status>"
	}
}
<commit_msg>show status const block<commit_after>package synapse

// Handler is the interface used
// to register server response callbacks.
type Handler interface {

	// ServeCall handles a synapse request and
	// writes a response. It should be safe to
	// call ServeCall from multiple goroutines.
	// Both the request and response interfaces
	// become invalid after the call is returned;
	// no implementation of ServeCall should
	// maintain a reference to either object
	// after the function returns.
	ServeCall(req Request, res ResponseWriter)
}

// Status represents
// a response status code
type Status int

// These are the status
// codes that can be passed
// to a ResponseWriter as
// an error to send to the
// client.
const (
	// The zero value for
	// Status is InvalidStatus
	InvalidStatus Status = iota

	okStatus

	// NotFound means the
	// call requested could
	// not be located on the
	// server
	NotFound

	// BadRequest means the
	// request was not formatted
	// as expected
	BadRequest

	// NotAuthed means the client
	// was not authorized to make
	// the call
	NotAuthed

	// ServerError means the server
	// encountered an error when
	// making the call
	ServerError
)

// Status implements the error interface
func (s Status) Error() string {
	switch s {
	case okStatus:
		return "<ok>"
	case NotFound:
		return "not found"
	case BadRequest:
		return "bad request"
	case NotAuthed:
		return "not authorized"
	case ServerError:
		return "server error"
	default:
		return "<invalid status>"
	}
}
<|endoftext|>


<commit_before>package cors

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

	"github.com/volatile/core"
)

const (
	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"

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

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

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

// Use tells the core to use this handler with the provided options.
func Use(options *Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, func() {
			c.Next()
		})
	})
}

// LocalUse allows to set CORS locally, for a single handler.
// Remember you can't set headers after Write or WriteHeader has been called.
func LocalUse(c *core.Context, options *Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt *Options) *formattedOptions {
	if opt == nil {
		opt = new(Options)
	}

	fmtOpt := &formattedOptions{
		AllowedHeaders:     strings.Join(opt.AllowedHeaders, ", "),
		AllowedMethods:     strings.Join(opt.AllowedMethods, ", "),
		CredentialsAllowed: strconv.FormatBool(opt.CredentialsAllowed),
		ExposedHeaders:     strings.Join(opt.ExposedHeaders, ", "),
		MaxAge:             fmt.Sprintf("%.f", opt.MaxAge.Seconds()),
	}

	// If no allowed origins set, all origins are allowed.
	if opt.AllowedOrigins == nil {
		fmtOpt.AllowedOrigins = "*"
	} else {
		fmtOpt.AllowedOrigins = strings.Join(opt.AllowedOrigins, ", ")
	}

	return fmtOpt
}

// setCORS set the response headers and continue if it's not a preflight request.
func setCORS(c *core.Context, fmtOpt *formattedOptions, handler func()) {
	c.ResponseWriter.Header().Set(headerAllowOrigin, fmtOpt.AllowedOrigins)
	if fmtOpt.AllowedOrigins != "*" {
		c.ResponseWriter.Header().Set("Vary", "Origin")
	}

	if fmtOpt.CredentialsAllowed == "true" {
		c.ResponseWriter.Header().Set(headerAllowCredentials, fmtOpt.CredentialsAllowed)
	}

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

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

	// OPTIONS is used for preflight requests.
	// For that, only the CORS handler must respond, so the handlers chain is broken.
	if c.Request.Method == "OPTIONS" {
		// If no allowed headers are set, accept all from the real request.
		if fmtOpt.AllowedHeaders == "" {
			c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
		} else {
			c.ResponseWriter.Header().Set(headerAllowHeaders, fmtOpt.AllowedHeaders)
		}

		// If no allowed methods are set, accept the method of the real request.
		if fmtOpt.AllowedMethods == "" {
			c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
		} else {
			c.ResponseWriter.Header().Set(headerAllowMethods, fmtOpt.AllowedMethods)
		}

		c.ResponseWriter.WriteHeader(http.StatusOK)
	} else {
		handler()
	}
}
<commit_msg>Move Options comments<commit_after>package cors

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

	"github.com/volatile/core"
)

const (
	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"

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

// Options represents access control options.
type Options struct {
	AllowedHeaders     []string      // AllowedHeaders indicates, in the case of a preflight request, which headers can be used during the actual request.
	AllowedMethods     []string      // AllowedMethods indicates, in the case of a preflight request, which methods can be used during the actual request.
	AllowedOrigins     []string      // AllowedOrigins indicates which origins can make a request.
	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
	AllowedOrigins     string
	CredentialsAllowed string
	ExposedHeaders     string
	MaxAge             string
}

// Use tells the core to use this handler with the provided options.
func Use(options *Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, func() {
			c.Next()
		})
	})
}

// LocalUse allows to set CORS locally, for a single handler.
// Remember you can't set headers after Write or WriteHeader has been called.
func LocalUse(c *core.Context, options *Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt *Options) *formattedOptions {
	if opt == nil {
		opt = new(Options)
	}

	fmtOpt := &formattedOptions{
		AllowedHeaders:     strings.Join(opt.AllowedHeaders, ", "),
		AllowedMethods:     strings.Join(opt.AllowedMethods, ", "),
		CredentialsAllowed: strconv.FormatBool(opt.CredentialsAllowed),
		ExposedHeaders:     strings.Join(opt.ExposedHeaders, ", "),
		MaxAge:             fmt.Sprintf("%.f", opt.MaxAge.Seconds()),
	}

	// If no allowed origins set, all origins are allowed.
	if opt.AllowedOrigins == nil {
		fmtOpt.AllowedOrigins = "*"
	} else {
		fmtOpt.AllowedOrigins = strings.Join(opt.AllowedOrigins, ", ")
	}

	return fmtOpt
}

// setCORS set the response headers and continue if it's not a preflight request.
func setCORS(c *core.Context, fmtOpt *formattedOptions, handler func()) {
	c.ResponseWriter.Header().Set(headerAllowOrigin, fmtOpt.AllowedOrigins)
	if fmtOpt.AllowedOrigins != "*" {
		c.ResponseWriter.Header().Set("Vary", "Origin")
	}

	if fmtOpt.CredentialsAllowed == "true" {
		c.ResponseWriter.Header().Set(headerAllowCredentials, fmtOpt.CredentialsAllowed)
	}

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

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

	// OPTIONS is used for preflight requests.
	// For that, only the CORS handler must respond, so the handlers chain is broken.
	if c.Request.Method == "OPTIONS" {
		// If no allowed headers are set, accept all from the real request.
		if fmtOpt.AllowedHeaders == "" {
			c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
		} else {
			c.ResponseWriter.Header().Set(headerAllowHeaders, fmtOpt.AllowedHeaders)
		}

		// If no allowed methods are set, accept the method of the real request.
		if fmtOpt.AllowedMethods == "" {
			c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
		} else {
			c.ResponseWriter.Header().Set(headerAllowMethods, fmtOpt.AllowedMethods)
		}

		c.ResponseWriter.WriteHeader(http.StatusOK)
	} else {
		handler()
	}
}
<|endoftext|>


<commit_before>package podca_api

import(
	"net/http"
	"encoding/json"
	"github.com/gorilla/mux"
	"strconv"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/delay"
	"golang.org/x/net/context"
)

func rootHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		w.Write([]byte("API Podca "))
	}

}

func podcastSearchHandler(searchEngine *ITunesSearchEngine, task FeedTask) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {

		ctx := appengine.NewContext(r)
		var term = r.FormValue("term")
		podcasts, err := searchEngine.Search(ctx, term) ; if err != nil {
			panic(err)
		}

		for _, podcast := range podcasts {
			var delayedTask = delay.Func("feedWorker", func(ctx context.Context, podcast Podcast){
				task.FetchAndStore(ctx, podcast)
			})

			delayedTask.Call(ctx, podcast)
		}

		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		if jsonerr := json.NewEncoder(w).Encode(podcasts); jsonerr != nil {
			panic(jsonerr)
		}
	}

}

func feedHandler(feedRepo *FeedRepo) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		switch r.Method {
		case "GET":
			ctx := appengine.NewContext(r)
			vars := mux.Vars(r)

			feedId, _ := strconv.Atoi(vars["feedId"])
			if feedId > 0 {
				log.Infof(ctx, "FEEDID> %d", int64(feedId))
				result, _ := feedRepo.get(ctx, int64(feedId))
				w.WriteHeader(http.StatusOK)
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				if jsonerr := json.NewEncoder(w).Encode(result); jsonerr != nil {
					panic(jsonerr)
				}
			} else {
				result, _ :=feedRepo.getAll(ctx)
				w.WriteHeader(http.StatusOK)
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				if jsonerr := json.NewEncoder(w).Encode(result); jsonerr != nil {
					panic(jsonerr)
				}
			}

		case "PUT":
			// update
			w.Write([]byte("NOT YET IMPLEMENTED "))
			w.WriteHeader(http.StatusNoContent)
		case "POST":
			// create
			w.Write([]byte("NOT YET IMPLEMENTED "))
			w.WriteHeader(http.StatusNoContent)
		case "DELETE":
			// remove
			w.Write([]byte("NOT YET IMPLEMENTED "))
			w.WriteHeader(http.StatusNoContent)

		}

	}
}

func feedImageHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("API Podca "))
	}
}

func feedEpisodeHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("API Podca "))
	}
}

<commit_msg>Use PodcastSearch Interface<commit_after>package podca_api

import(
	"net/http"
	"encoding/json"
	"github.com/gorilla/mux"
	"strconv"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/delay"
	"golang.org/x/net/context"
)

func rootHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		w.Write([]byte("API Podca "))
	}

}

func podcastSearchHandler(searchEngine PodcastSearchEngine, task FeedTask) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {

		ctx := appengine.NewContext(r)
		var term = r.FormValue("term")
		podcasts, err := searchEngine.Search(ctx, term) ; if err != nil {
			panic(err)
		}

		for _, podcast := range podcasts {
			var delayedTask = delay.Func("feedWorker", func(ctx context.Context, podcast Podcast){
				task.FetchAndStore(ctx, podcast)
			})

			delayedTask.Call(ctx, podcast)
		}

		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		if jsonerr := json.NewEncoder(w).Encode(podcasts); jsonerr != nil {
			panic(jsonerr)
		}
	}

}

func feedHandler(feedRepo *FeedRepo) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		switch r.Method {
		case "GET":
			ctx := appengine.NewContext(r)
			vars := mux.Vars(r)

			feedId, _ := strconv.Atoi(vars["feedId"])
			if feedId > 0 {
				log.Infof(ctx, "FEEDID> %d", int64(feedId))
				result, _ := feedRepo.get(ctx, int64(feedId))
				w.WriteHeader(http.StatusOK)
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				if jsonerr := json.NewEncoder(w).Encode(result); jsonerr != nil {
					panic(jsonerr)
				}
			} else {
				result, _ :=feedRepo.getAll(ctx)
				w.WriteHeader(http.StatusOK)
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				if jsonerr := json.NewEncoder(w).Encode(result); jsonerr != nil {
					panic(jsonerr)
				}
			}

		case "PUT":
			// update
			w.Write([]byte("NOT YET IMPLEMENTED "))
			w.WriteHeader(http.StatusNoContent)
		case "POST":
			// create
			w.Write([]byte("NOT YET IMPLEMENTED "))
			w.WriteHeader(http.StatusNoContent)
		case "DELETE":
			// remove
			w.Write([]byte("NOT YET IMPLEMENTED "))
			w.WriteHeader(http.StatusNoContent)

		}

	}
}

func feedImageHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("API Podca "))
	}
}

func feedEpisodeHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("API Podca "))
	}
}

<|endoftext|>


<commit_before>package har

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

var startingEntrySize int = 1000

type Har struct {
	HarLog HarLog `json:"harLog"`
}

type HarLog struct {
	Version string     `json:"version"`
	Creator string     `json:"creator"`
	Browser string     `json:"browser"`
	Pages   []HarPage  `json:"pages"`
	Entries []HarEntry `json:"entries"`
}

func NewHarLog() *HarLog {
	harLog := HarLog{
		Version: "1.2",
		Creator: "GoProxy",
		Browser: "",
		Pages:   make([]HarPage, 0, 10),
		Entries: makeNewEntries(),
	}
	return &harLog
}

func (harLog *HarLog) AddEntry(entry ...HarEntry) {
	entries := harLog.Entries
	m := len(entries)
	n := m + len(entry)
	if n > cap(entries) { // if necessary, reallocate
		// allocate double what's needed, for future growth.
		newEntries := make([]HarEntry, (n+1)*2)
		copy(newEntries, entries)
		entries = newEntries
	}
	entries = entries[0:n]
	copy(entries[m:n], entry)
	harLog.Entries = entries
	log.Println("Added entry ", entry[0].Request.Url)
}

func makeNewEntries() []HarEntry {
	return make([]HarEntry, 0, startingEntrySize)
}

type HarPage struct {
	Id              string         `json:"id"`
	StartedDateTime time.Time      `json:"startedDateTime"`
	Title           string         `json:"title"`
	PageTimings     HarPageTimings `json:"pageTimings"`
}

type HarEntry struct {
	PageRef         string       `json:"pageRef"`
	StartedDateTime time.Time    `json:"startedDateTime"`
	Time            int64        `json:"time"`
	Request         *HarRequest  `json:"request"`
	Response        *HarResponse `json:"response"`
	Timings         HarTimings   `json:"timings"`
	ServerIpAddress string       `json:"serverIpAddress"`
	Connection      string       `json:"connection"`
}

type HarRequest struct {
	Method      string             `json:"method"`
	Url         string             `json:"url"`
	HttpVersion string             `json:"httpVersion"`
	Cookies     []HarCookie        `json:"cookies"`
	Headers     []HarNameValuePair `json:"headers"`
	QueryString []HarNameValuePair `json:"queryString"`
	PostData    *HarPostData       `json:"postData"`
	BodySize    int64              `json:"bodySize"`
	HeadersSize int64              `json:"headersSize"`
}

func ParseRequest(req *http.Request, captureContent bool) *HarRequest {
	if req == nil {
		return nil
	}
	harRequest := HarRequest{
		Method:      req.Method,
		Url:         req.URL.String(),
		HttpVersion: req.Proto,
		Cookies:     parseCookies(req.Cookies()),
		Headers:     parseStringArrMap(req.Header),
		QueryString: parseStringArrMap((req.URL.Query())),
		BodySize:    req.ContentLength,
		HeadersSize: calcHeaderSize(req.Header),
	}

	if captureContent && (req.Method == "POST" || req.Method == "PUT") {
		harRequest.PostData = parsePostData(req)
	}

	return &harRequest
}

func (harEntry *HarEntry) FillIPAddress(req *http.Request) {
	host, _, err := net.SplitHostPort(req.URL.Host)
	if err != nil {
		host = req.URL.Host
	}
	if ip := net.ParseIP(host); ip != nil {
		harEntry.ServerIpAddress = string(ip)
	}

	if ipaddr, err := net.LookupIP(host); err == nil {
		for _, ip := range ipaddr {
			if ip.To4() != nil {
				harEntry.ServerIpAddress = ip.String()
				return
			}
		}
	}
}

func calcHeaderSize(header http.Header) int64 {
	headerSize := 0
	for headerName, headerValues := range header {
		headerSize += len(headerName) + 2
		for _, v := range headerValues {
			headerSize += len(v)
		}
	}
	return int64(headerSize)
}

func parsePostData(req *http.Request) *HarPostData {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing request to %v: %v\n", req.URL, e)
		}
	}()

	harPostData := new(HarPostData)
	contentType := req.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in request")
	}
	harPostData.MimeType = contentType[0]

	if len(req.PostForm) > 0 {
		index := 0
		params := make([]HarPostDataParam, len(req.PostForm))
		for k, v := range req.PostForm {
			param := HarPostDataParam{
				Name:  k,
				Value: strings.Join(v, ","),
			}
			params[index] = param
			index++
		}
		harPostData.Params = params
	} else {
		str, _ := ioutil.ReadAll(req.Body)
		harPostData.Text = string(str)
	}
	return harPostData
}

func parseStringArrMap(stringArrMap map[string][]string) []HarNameValuePair {
	index := 0
	harQueryString := make([]HarNameValuePair, len(stringArrMap))
	for k, v := range stringArrMap {
		escapedKey, _ := url.QueryUnescape(k)
		escapedValues, _ := url.QueryUnescape(strings.Join(v, ","))
		harNameValuePair := HarNameValuePair{
			Name:  escapedKey,
			Value: escapedValues,
		}
		harQueryString[index] = harNameValuePair
		index++
	}
	return harQueryString
}

func parseCookies(cookies []*http.Cookie) []HarCookie {
	harCookies := make([]HarCookie, len(cookies))
	for i, cookie := range cookies {
		harCookie := HarCookie{
			Name:     cookie.Name,
			Domain:   cookie.Domain,
			Expires:  cookie.Expires,
			HttpOnly: cookie.HttpOnly,
			Path:     cookie.Path,
			Secure:   cookie.Secure,
			Value:    cookie.Value,
		}
		harCookies[i] = harCookie
	}
	return harCookies
}

type HarResponse struct {
	Status      int                `json:"status"`
	StatusText  string             `json:"statusText"`
	HttpVersion string             `json:"httpVersion`
	Cookies     []HarCookie        `json:"cookies"`
	Headers     []HarNameValuePair `json:"headers"`
	Content     *HarContent        `json:"content"`
	RedirectUrl string             `json:"redirectUrl"`
	BodySize    int64              `json:"bodySize"`
	HeadersSize int64              `json:"headersSize"`
}

func ParseResponse(resp *http.Response, captureContent bool) *HarResponse {
	if resp == nil {
		return nil
	}

	harResponse := HarResponse{
		Status:      resp.StatusCode,
		StatusText:  resp.Status,
		HttpVersion: resp.Proto,
		Cookies:     parseCookies(resp.Cookies()),
		Headers:     parseStringArrMap(resp.Header),
		RedirectUrl: "",
		BodySize:    resp.ContentLength,
		HeadersSize: calcHeaderSize(resp.Header),
	}

	if captureContent {
		harResponse.Content = harParseContent(resp)
	}

	return &harResponse
}

func harParseContent(resp *http.Response) *HarContent {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing response to %v: %v\n", resp.Request.URL, e)
		}
	}()

	harContent := new(HarContent)
	contentType := resp.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in response")
	}
	harContent.MimeType = contentType[0]
	if resp.ContentLength <= 0 {
		log.Println("Empty content")
		return nil
	}

	body, _ := ioutil.ReadAll(resp.Body)
	harContent.Text = string(body)
	return harContent
}

type HarCookie struct {
	Name     string    `json:"name"`
	Value    string    `json:"value"`
	Path     string    `json:"path"`
	Domain   string    `json:"domain"`
	Expires  time.Time `json:"expires"`
	HttpOnly bool      `json:"httpOnly"`
	Secure   bool      `json:"secure"`
}

type HarNameValuePair struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type HarPostData struct {
	MimeType string             `json:"mimeType"`
	Params   []HarPostDataParam `json:"params"`
	Text     string             `json:"text"`
}

type HarPostDataParam struct {
	Name        string `json:"name"`
	Value       string `json:"value"`
	FileName    string `json:"fileName"`
	ContentType string `json:"contentType`
}

type HarContent struct {
	Size        int64  `json:"size"`
	Compression int64  `json:"compression"`
	MimeType    string `json:"mimeType"`
	Text        string `json:"text"`
	Encoding    string `json:"encoding"`
}

type HarPageTimings struct {
	OnContentLoad int64 `json:"onContentLoad"`
	OnLoad        int64 `json:"onLoad"`
}

type HarTimings struct {
	Blocked int64
	Dns     int64
	Connect int64
	Send    int64
	Wait    int64
	Receive int64
	Ssl     int64
}
<commit_msg>Fixup har file format.<commit_after>package har

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

var startingEntrySize int = 1000

type Har struct {
	HarLog HarLog `json:"log"`
}

type HarLog struct {
	Version string     `json:"version"`
	Creator HarCreator `json:"creator"`
	Browser string     `json:"browser"`
	Pages   []HarPage  `json:"pages"`
	Entries []HarEntry `json:"entries"`
}

func NewHarLog() *HarLog {
	harLog := HarLog{
		Version: "1.2",
		Creator: HarCreator{
			"GoProxy",
			"12345",
		},
		Pages:   make([]HarPage, 0, 10),
		Entries: makeNewEntries(),
	}
	return &harLog
}

func (harLog *HarLog) AddEntry(entry ...HarEntry) {
	entries := harLog.Entries
	m := len(entries)
	n := m + len(entry)
	if n > cap(entries) { // if necessary, reallocate
		// allocate double what's needed, for future growth.
		newEntries := make([]HarEntry, (n+1)*2)
		copy(newEntries, entries)
		entries = newEntries
	}
	entries = entries[0:n]
	copy(entries[m:n], entry)
	harLog.Entries = entries
	log.Println("Added entry ", entry[0].Request.Url)
}

func makeNewEntries() []HarEntry {
	return make([]HarEntry, 0, startingEntrySize)
}

type HarCreator struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

type HarPage struct {
	Id              string         `json:"id"`
	StartedDateTime time.Time      `json:"startedDateTime"`
	Title           string         `json:"title"`
	PageTimings     HarPageTimings `json:"pageTimings"`
}

type HarEntry struct {
	PageRef         string       `json:"pageRef,omitempty"`
	StartedDateTime time.Time    `json:"startedDateTime"`
	Time            int64        `json:"time"`
	Request         *HarRequest  `json:"request"`
	Response        *HarResponse `json:"response"`
	Timings         HarTimings   `json:"timings"`
	ServerIpAddress string       `json:"serverIpAddress"`
	Connection      string       `json:"connection"`
}

type HarRequest struct {
	Method      string             `json:"method"`
	Url         string             `json:"url"`
	HttpVersion string             `json:"httpVersion"`
	Cookies     []HarCookie        `json:"cookies"`
	Headers     []HarNameValuePair `json:"headers"`
	QueryString []HarNameValuePair `json:"queryString"`
	PostData    *HarPostData       `json:"postData"`
	BodySize    int64              `json:"bodySize"`
	HeadersSize int64              `json:"headersSize"`
}

func ParseRequest(req *http.Request, captureContent bool) *HarRequest {
	if req == nil {
		return nil
	}
	harRequest := HarRequest{
		Method:      req.Method,
		Url:         req.URL.String(),
		HttpVersion: req.Proto,
		Cookies:     parseCookies(req.Cookies()),
		Headers:     parseStringArrMap(req.Header),
		QueryString: parseStringArrMap((req.URL.Query())),
		BodySize:    req.ContentLength,
		HeadersSize: calcHeaderSize(req.Header),
	}

	if captureContent && (req.Method == "POST" || req.Method == "PUT") {
		harRequest.PostData = parsePostData(req)
	}

	return &harRequest
}

func (harEntry *HarEntry) FillIPAddress(req *http.Request) {
	host, _, err := net.SplitHostPort(req.URL.Host)
	if err != nil {
		host = req.URL.Host
	}
	if ip := net.ParseIP(host); ip != nil {
		harEntry.ServerIpAddress = string(ip)
	}

	if ipaddr, err := net.LookupIP(host); err == nil {
		for _, ip := range ipaddr {
			if ip.To4() != nil {
				harEntry.ServerIpAddress = ip.String()
				return
			}
		}
	}
}

func calcHeaderSize(header http.Header) int64 {
	headerSize := 0
	for headerName, headerValues := range header {
		headerSize += len(headerName) + 2
		for _, v := range headerValues {
			headerSize += len(v)
		}
	}
	return int64(headerSize)
}

func parsePostData(req *http.Request) *HarPostData {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing request to %v: %v\n", req.URL, e)
		}
	}()

	harPostData := new(HarPostData)
	contentType := req.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in request")
	}
	harPostData.MimeType = contentType[0]

	if len(req.PostForm) > 0 {
		index := 0
		params := make([]HarPostDataParam, len(req.PostForm))
		for k, v := range req.PostForm {
			param := HarPostDataParam{
				Name:  k,
				Value: strings.Join(v, ","),
			}
			params[index] = param
			index++
		}
		harPostData.Params = params
	} else {
		str, _ := ioutil.ReadAll(req.Body)
		harPostData.Text = string(str)
	}
	return harPostData
}

func parseStringArrMap(stringArrMap map[string][]string) []HarNameValuePair {
	index := 0
	harQueryString := make([]HarNameValuePair, len(stringArrMap))
	for k, v := range stringArrMap {
		escapedKey, _ := url.QueryUnescape(k)
		escapedValues, _ := url.QueryUnescape(strings.Join(v, ","))
		harNameValuePair := HarNameValuePair{
			Name:  escapedKey,
			Value: escapedValues,
		}
		harQueryString[index] = harNameValuePair
		index++
	}
	return harQueryString
}

func parseCookies(cookies []*http.Cookie) []HarCookie {
	harCookies := make([]HarCookie, len(cookies))
	for i, cookie := range cookies {
		harCookie := HarCookie{
			Name:     cookie.Name,
			Domain:   cookie.Domain,
			Expires:  cookie.Expires,
			HttpOnly: cookie.HttpOnly,
			Path:     cookie.Path,
			Secure:   cookie.Secure,
			Value:    cookie.Value,
		}
		harCookies[i] = harCookie
	}
	return harCookies
}

type HarResponse struct {
	Status      int                `json:"status"`
	StatusText  string             `json:"statusText"`
	HttpVersion string             `json:"httpVersion`
	Cookies     []HarCookie        `json:"cookies"`
	Headers     []HarNameValuePair `json:"headers"`
	Content     *HarContent        `json:"content"`
	RedirectUrl string             `json:"redirectUrl"`
	BodySize    int64              `json:"bodySize"`
	HeadersSize int64              `json:"headersSize"`
}

func ParseResponse(resp *http.Response, captureContent bool) *HarResponse {
	if resp == nil {
		return nil
	}

	harResponse := HarResponse{
		Status:      resp.StatusCode,
		StatusText:  resp.Status,
		HttpVersion: resp.Proto,
		Cookies:     parseCookies(resp.Cookies()),
		Headers:     parseStringArrMap(resp.Header),
		RedirectUrl: "",
		BodySize:    resp.ContentLength,
		HeadersSize: calcHeaderSize(resp.Header),
	}

	if captureContent {
		harResponse.Content = harParseContent(resp)
	}

	return &harResponse
}

func harParseContent(resp *http.Response) *HarContent {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing response to %v: %v\n", resp.Request.URL, e)
		}
	}()

	harContent := new(HarContent)
	contentType := resp.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in response")
	}
	harContent.MimeType = contentType[0]
	if resp.ContentLength <= 0 {
		log.Println("Empty content")
		return nil
	}

	body, _ := ioutil.ReadAll(resp.Body)
	harContent.Text = string(body)
	return harContent
}

type HarCookie struct {
	Name     string    `json:"name"`
	Value    string    `json:"value"`
	Path     string    `json:"path"`
	Domain   string    `json:"domain"`
	Expires  time.Time `json:"expires"`
	HttpOnly bool      `json:"httpOnly"`
	Secure   bool      `json:"secure"`
}

type HarNameValuePair struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type HarPostData struct {
	MimeType string             `json:"mimeType"`
	Params   []HarPostDataParam `json:"params"`
	Text     string             `json:"text"`
}

type HarPostDataParam struct {
	Name        string `json:"name"`
	Value       string `json:"value"`
	FileName    string `json:"fileName"`
	ContentType string `json:"contentType`
}

type HarContent struct {
	Size        int64  `json:"size"`
	Compression int64  `json:"compression"`
	MimeType    string `json:"mimeType"`
	Text        string `json:"text"`
	Encoding    string `json:"encoding"`
}

type HarPageTimings struct {
	OnContentLoad int64 `json:"onContentLoad"`
	OnLoad        int64 `json:"onLoad"`
}

type HarTimings struct {
	Blocked int64
	Dns     int64
	Connect int64
	Send    int64
	Wait    int64
	Receive int64
	Ssl     int64
}
<|endoftext|>