<commit_before>package renders

import (
	"fmt"
	"html/template"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"bytes"
	"io"
	"net/http"
	"errors"
	"io/ioutil"
	"reflect"

	"github.com/lunny/tango"
	"github.com/oxtoacart/bpool"
)

const (
	ContentType    = "Content-Type"
	ContentLength  = "Content-Length"
	ContentHTML    = "text/html"
	ContentXHTML   = "application/xhtml+xml"
	defaultCharset = "UTF-8"
)

// Provides a temporary buffer to execute templates into and catch errors.

type T map[string]interface{}

// Options is a struct for specifying configuration options for the render.Renderer middleware
type Options struct {
	// if reload templates
	Reload bool
	// Directory to load templates. Default is "templates"
	Directory string
	// Extensions to parse template files from. Defaults to [".tmpl"]
	Extensions []string
	// Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Defaults to [].
	Funcs template.FuncMap
	// Vars is a data map for global
	Vars T
	// Appends the given charset to the Content-Type header. Default is "UTF-8".
	Charset string
	// Allows changing of output to XHTML instead of HTML. Default is "text/html"
	HTMLContentType string
}

type Renders struct {
	Options
	cs string
	pool *bpool.BufferPool
	templates map[string]*template.Template
}

func New(options ...Options) *Renders {
	opt := prepareOptions(options)
	t, err := compile(opt)
	if err != nil {
		panic(err)
	}
	return &Renders{
		Options: opt,
		cs: prepareCharset(opt.Charset),
		pool: bpool.NewBufferPool(64),
		templates: t,
	}
}

type IRenderer interface {
	SetRenderer(render *renderer)
}

type Renderer struct {
	*renderer
}
func (r *Renderer) SetRenderer(render *renderer) {
	r.renderer = render
}

type Before interface {
	BeforeRender(string)
}

type After interface {
	AfterRender(string)
}

func (r *Renders) Handle(ctx *tango.Context) {
	if action := ctx.Action(); action != nil {
		if rd, ok := action.(IRenderer); ok {
			var before, after func(string)
			if b, ok := action.(Before); ok {
				before = b.BeforeRender
			}
			if a, ok := action.(After); ok {
				after = a.AfterRender
			}

			var templates = r.templates

			if r.Reload {
				var err error
				// recompile for easy development
				templates, err = compile(r.Options)
				if err != nil {
					panic(err)
				}
			}

			rd.SetRenderer(&renderer{
				renders: r,
				Context: ctx,
				action: action,
				before: before,
				after: after,
				t: templates,
				opt: r.Options,
				compiledCharset: r.cs,
			})
		}
	}

	ctx.Next()
}

func compile(options Options) (map[string]*template.Template, error) {
	if len(options.Funcs) > 0 {
		return LoadWithFuncMap(options)
	}
	return Load(options)
}

func prepareCharset(charset string) string {
	if len(charset) != 0 {
		return "; charset=" + charset
	}

	return "; charset=" + defaultCharset
}

func prepareOptions(options []Options) Options {
	var opt Options
	if len(options) > 0 {
		opt = options[0]
	}

	// Defaults
	if len(opt.Directory) == 0 {
		opt.Directory = "templates"
	}
	if len(opt.Extensions) == 0 {
		opt.Extensions = []string{".html"}
	}
	if len(opt.HTMLContentType) == 0 {
		opt.HTMLContentType = ContentHTML
	}

	return opt
}

type renderer struct {
	*tango.Context
	renders *Renders
	action interface{}
	before, after func(string)
	t               map[string]*template.Template
	opt             Options
	compiledCharset string
}

func (r *Renderer) Render(name string, binding interface{}) error {
	return r.StatusRender(http.StatusOK, name, binding)
}

func (r *Renderer) StatusRender(status int, name string, binding interface{}) error {
	buf, err := r.execute(name, binding)
	if err != nil {
		return err
	}

	// template rendered fine, write out the result
	r.Header().Set(ContentType, r.opt.HTMLContentType+r.compiledCharset)
	r.WriteHeader(status)
	_, err = io.Copy(r, buf)
	r.renders.pool.Put(buf)
	return err
}

func (r *Renderer) Template(name string) *template.Template {
	return r.t[name]
}

func (r *Renderer) execute(name string, binding interface{}) (*bytes.Buffer, error) {
	buf := r.renders.pool.Get()
	if r.before != nil {
		r.before(name)
	}
	if r.after != nil {
		defer r.after(name)
	}
	if rt, ok := r.t[name]; ok {
		return buf, rt.ExecuteTemplate(buf, name, binding)
	}
	return nil, errors.New("template is not exist")
}

var (
	cache               []*namedTemplate
	regularTemplateDefs []string
	lock                sync.Mutex
	re_defineTag        = regexp.MustCompile("{{ ?define \"([^\"]*)\" ?\"?([a-zA-Z0-9]*)?\"? ?}}")
	re_templateTag      = regexp.MustCompile("{{ ?template \"([^\"]*)\" ?([^ ]*)? ?}}")
)

type namedTemplate struct {
	Name string
	Src  string
}

// Load prepares and parses all templates from the passed basePath
func Load(opt Options) (map[string]*template.Template, error) {
	return loadTemplates(opt.Directory, opt.Extensions, nil)
}

// LoadWithFuncMap prepares and parses all templates from the passed basePath and injects
// a custom template.FuncMap into each template
func LoadWithFuncMap(opt Options) (map[string]*template.Template, error) {
	return loadTemplates(opt.Directory, opt.Extensions, opt.Funcs)
}

func loadTemplates(basePath string, exts []string, funcMap template.FuncMap) (map[string]*template.Template, error) {
	lock.Lock()
	defer lock.Unlock()

	templates := make(map[string]*template.Template)

	err := filepath.Walk(basePath, func(path string, fi os.FileInfo, err error) error {
		if fi == nil || fi.IsDir() {
			return nil
		}

		r, err := filepath.Rel(basePath, path)
		if err != nil {
			return err
		}

		ext := filepath.Ext(r)
		var extRight bool
		for _, extension := range exts {
			if ext != extension {
				continue
			}
			extRight = true
			break
		}
		if !extRight {
			return nil
		}

		if err := add(basePath, path); err != nil {
			panic(err)
		}

		// Now we find all regular template definitions and check for the most recent definiton
		for _, t := range regularTemplateDefs {
			found := false
			defineIdx := 0
			// From the beginning (which should) most specifc we look for definitions
			for _, nt := range cache {
				nt.Src = re_defineTag.ReplaceAllStringFunc(nt.Src, func(raw string) string {
					parsed := re_defineTag.FindStringSubmatch(raw)
					name := parsed[1]
					if name != t {
						return raw
					}
					// Don't touch the first definition
					if !found {
						found = true
						return raw
					}

					defineIdx += 1

					return fmt.Sprintf("{{ define \"%s_invalidated_#%d\" }}", name, defineIdx)
				})
			}
		}

		var (
			baseTmpl *template.Template
			i        int
		)

		for _, nt := range cache {
			var currentTmpl *template.Template
			if i == 0 {
				baseTmpl = template.New(nt.Name)
				currentTmpl = baseTmpl
			} else {
				currentTmpl = baseTmpl.New(nt.Name)
			}

			template.Must(currentTmpl.Funcs(funcMap).Parse(nt.Src))
			i++
		}
		tname := generateTemplateName(basePath, path)
		templates[tname] = baseTmpl

		// Make sure we empty the cache between runs
		cache = cache[0:0]
		return nil
	})

	return templates, err
}

func add(basePath, path string) error {
	// Get file content
	tplSrc, err := file_content(path)
	if err != nil {
		return err
	}

	tplName := generateTemplateName(basePath, path)

	// Make sure template is not already included
	alreadyIncluded := false
	for _, nt := range cache {
		if nt.Name == tplName {
			alreadyIncluded = true
			break
		}
	}
	if alreadyIncluded {
		return nil
	}

	// Add to the cache
	nt := &namedTemplate{
		Name: tplName,
		Src:  tplSrc,
	}
	cache = append(cache, nt)

	// Check for any template block
	for _, raw := range re_templateTag.FindAllString(nt.Src, -1) {
		parsed := re_templateTag.FindStringSubmatch(raw)
		templatePath := parsed[1]
		ext := filepath.Ext(templatePath)
		if !strings.Contains(templatePath, ext) {
			regularTemplateDefs = append(regularTemplateDefs, templatePath)
			continue
		}

		// Add this template and continue looking for more template blocks
		add(basePath, filepath.Join(basePath, templatePath))
	}

	return nil
}

func isNil(a interface{}) bool {
	if a == nil {
		return true
	}
	aa := reflect.ValueOf(a)
	return !aa.IsValid() || (aa.Type().Kind() == reflect.Ptr && aa.IsNil())
}

func generateTemplateName(base, path string) string {
	return filepath.ToSlash(path[len(base)+1:])
}

func file_content(path string) (string, error) {
	// Read the file content of the template
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

	b, err := ioutil.ReadAll(file)
	if err != nil {
		return "", err
	}
	s := string(b)

	if len(s) < 1 {
		return "", errors.New("render: template file is empty")
	}

	return s, nil
}
<commit_msg>bug fixed<commit_after>package renders

import (
	"fmt"
	"html/template"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"bytes"
	"io"
	"net/http"
	"errors"
	"io/ioutil"
	"reflect"

	"github.com/lunny/tango"
	"github.com/oxtoacart/bpool"
)

const (
	ContentType    = "Content-Type"
	ContentLength  = "Content-Length"
	ContentHTML    = "text/html"
	ContentXHTML   = "application/xhtml+xml"
	defaultCharset = "UTF-8"
)

// Provides a temporary buffer to execute templates into and catch errors.

type T map[string]interface{}

// Options is a struct for specifying configuration options for the render.Renderer middleware
type Options struct {
	// if reload templates
	Reload bool
	// Directory to load templates. Default is "templates"
	Directory string
	// Extensions to parse template files from. Defaults to [".tmpl"]
	Extensions []string
	// Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Defaults to [].
	Funcs template.FuncMap
	// Vars is a data map for global
	Vars T
	// Appends the given charset to the Content-Type header. Default is "UTF-8".
	Charset string
	// Allows changing of output to XHTML instead of HTML. Default is "text/html"
	HTMLContentType string
}

type Renders struct {
	Options
	cs string
	pool *bpool.BufferPool
	templates map[string]*template.Template
}

func New(options ...Options) *Renders {
	opt := prepareOptions(options)
	t, err := compile(opt)
	if err != nil {
		panic(err)
	}
	return &Renders{
		Options: opt,
		cs: prepareCharset(opt.Charset),
		pool: bpool.NewBufferPool(64),
		templates: t,
	}
}

type IRenderer interface {
	SetRenderer(render *renderer)
}

type Renderer struct {
	*renderer
}
func (r *Renderer) SetRenderer(render *renderer) {
	r.renderer = render
}

type Before interface {
	BeforeRender(string)
}

type After interface {
	AfterRender(string)
}

func (r *Renders) Handle(ctx *tango.Context) {
	if action := ctx.Action(); action != nil {
		if rd, ok := action.(IRenderer); ok {
			var before, after func(string)
			if b, ok := action.(Before); ok {
				before = b.BeforeRender
			}
			if a, ok := action.(After); ok {
				after = a.AfterRender
			}

			var templates = r.templates

			if r.Reload {
				var err error
				// recompile for easy development
				templates, err = compile(r.Options)
				if err != nil {
					panic(err)
				}
			}

			rd.SetRenderer(&renderer{
				renders: r,
				Context: ctx,
				action: action,
				before: before,
				after: after,
				t: templates,
				opt: r.Options,
				compiledCharset: r.cs,
			})
		}
	}

	ctx.Next()
}

func compile(options Options) (map[string]*template.Template, error) {
	if len(options.Funcs) > 0 {
		return LoadWithFuncMap(options)
	}
	return Load(options)
}

func prepareCharset(charset string) string {
	if len(charset) != 0 {
		return "; charset=" + charset
	}

	return "; charset=" + defaultCharset
}

func prepareOptions(options []Options) Options {
	var opt Options
	if len(options) > 0 {
		opt = options[0]
	}

	// Defaults
	if len(opt.Directory) == 0 {
		opt.Directory = "templates"
	}
	if len(opt.Extensions) == 0 {
		opt.Extensions = []string{".html"}
	}
	if len(opt.HTMLContentType) == 0 {
		opt.HTMLContentType = ContentHTML
	}

	return opt
}

type renderer struct {
	*tango.Context
	renders *Renders
	action interface{}
	before, after func(string)
	t               map[string]*template.Template
	opt             Options
	compiledCharset string
}

func (r *Renderer) Render(name string, binding interface{}) error {
	return r.StatusRender(http.StatusOK, name, binding)
}

func (r *Renderer) StatusRender(status int, name string, binding interface{}) error {
	buf, err := r.execute(name, binding)
	if err != nil {
		return err
	}

	// template rendered fine, write out the result
	r.Header().Set(ContentType, r.opt.HTMLContentType+r.compiledCharset)
	r.WriteHeader(status)
	_, err = io.Copy(r, buf)
	r.renders.pool.Put(buf)
	return err
}

func (r *Renderer) Template(name string) *template.Template {
	return r.t[name]
}

func (r *Renderer) execute(name string, binding interface{}) (*bytes.Buffer, error) {
	buf := r.renders.pool.Get()
	if r.before != nil {
		r.before(name)
	}
	if r.after != nil {
		defer r.after(name)
	}
	if rt, ok := r.t[name]; ok {
		return buf, rt.ExecuteTemplate(buf, name, binding)
	}
	return nil, errors.New("template is not exist")
}

var (
	cache               []*namedTemplate
	regularTemplateDefs []string
	lock                sync.Mutex
	re_defineTag        = regexp.MustCompile("{{ ?define \"([^\"]*)\" ?\"?([a-zA-Z0-9]*)?\"? ?}}")
	//re_templateTag      = regexp.MustCompile("{{ ?template \"([^\"]*)\" ?([^ ]*)? ?}}")
	re_templateTag      = regexp.MustCompile("{{[ ]*template[ ]+\"([^\"]+)\"")
)

type namedTemplate struct {
	Name string
	Src  string
}

// Load prepares and parses all templates from the passed basePath
func Load(opt Options) (map[string]*template.Template, error) {
	return loadTemplates(opt.Directory, opt.Extensions, nil)
}

// LoadWithFuncMap prepares and parses all templates from the passed basePath and injects
// a custom template.FuncMap into each template
func LoadWithFuncMap(opt Options) (map[string]*template.Template, error) {
	return loadTemplates(opt.Directory, opt.Extensions, opt.Funcs)
}

func loadTemplates(basePath string, exts []string, funcMap template.FuncMap) (map[string]*template.Template, error) {
	lock.Lock()
	defer lock.Unlock()

	templates := make(map[string]*template.Template)

	err := filepath.Walk(basePath, func(path string, fi os.FileInfo, err error) error {
		if fi == nil || fi.IsDir() {
			return nil
		}

		r, err := filepath.Rel(basePath, path)
		if err != nil {
			return err
		}

		ext := filepath.Ext(r)
		var extRight bool
		for _, extension := range exts {
			if ext != extension {
				continue
			}
			extRight = true
			break
		}
		if !extRight {
			return nil
		}

		if err := add(basePath, path); err != nil {
			panic(err)
		}

		// Now we find all regular template definitions and check for the most recent definiton
		for _, t := range regularTemplateDefs {
			found := false
			defineIdx := 0
			// From the beginning (which should) most specifc we look for definitions
			for _, nt := range cache {
				nt.Src = re_defineTag.ReplaceAllStringFunc(nt.Src, func(raw string) string {
					parsed := re_defineTag.FindStringSubmatch(raw)
					name := parsed[1]
					if name != t {
						return raw
					}
					// Don't touch the first definition
					if !found {
						found = true
						return raw
					}

					defineIdx += 1

					return fmt.Sprintf("{{ define \"%s_invalidated_#%d\" }}", name, defineIdx)
				})
			}
		}

		var (
			baseTmpl *template.Template
			i        int
		)

		for _, nt := range cache {
			var currentTmpl *template.Template
			if i == 0 {
				baseTmpl = template.New(nt.Name)
				currentTmpl = baseTmpl
			} else {
				currentTmpl = baseTmpl.New(nt.Name)
			}

			template.Must(currentTmpl.Funcs(funcMap).Parse(nt.Src))
			i++
		}
		tname := generateTemplateName(basePath, path)
		templates[tname] = baseTmpl

		// Make sure we empty the cache between runs
		cache = cache[0:0]
		return nil
	})

	return templates, err
}

func add(basePath, path string) error {
	// Get file content
	tplSrc, err := file_content(path)
	if err != nil {
		return err
	}

	tplName := generateTemplateName(basePath, path)

	// Make sure template is not already included
	alreadyIncluded := false
	for _, nt := range cache {
		if nt.Name == tplName {
			alreadyIncluded = true
			break
		}
	}
	if alreadyIncluded {
		return nil
	}

	// Add to the cache
	nt := &namedTemplate{
		Name: tplName,
		Src:  tplSrc,
	}
	cache = append(cache, nt)

	// Check for any template block
	for _, raw := range re_templateTag.FindAllString(nt.Src, -1) {
		parsed := re_templateTag.FindStringSubmatch(raw)
		templatePath := parsed[1]
		ext := filepath.Ext(templatePath)
		if !strings.Contains(templatePath, ext) {
			regularTemplateDefs = append(regularTemplateDefs, templatePath)
			continue
		}

		// Add this template and continue looking for more template blocks
		add(basePath, filepath.Join(basePath, templatePath))
	}

	return nil
}

func isNil(a interface{}) bool {
	if a == nil {
		return true
	}
	aa := reflect.ValueOf(a)
	return !aa.IsValid() || (aa.Type().Kind() == reflect.Ptr && aa.IsNil())
}

func generateTemplateName(base, path string) string {
	return filepath.ToSlash(path[len(base)+1:])
}

func file_content(path string) (string, error) {
	// Read the file content of the template
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

	b, err := ioutil.ReadAll(file)
	if err != nil {
		return "", err
	}
	s := string(b)

	if len(s) < 1 {
		return "", errors.New("render: template file is empty")
	}

	return s, nil
}
<|endoftext|>


<commit_before>package dog_pool

import "os/exec"
import "time"
import "testing"
import "github.com/orfjackal/gospec/src/gospec"
import "github.com/alecthomas/log4go"

//
// NOTE: Use differient ports for each test!
//       gospec runs the specs in parallel!
//
func TestMemcachedConnectionSpecs(t *testing.T) {
	r := gospec.NewRunner()
	r.AddSpec(MemcachedConnectionSpecs)
	gospec.MainGoTest(r, t)
}

// Helpers
func MemcachedConnectionSpecs(c gospec.Context) {
	var memcached_connection_logger = log4go.NewDefaultLogger(log4go.FINEST)

	c.Specify("[MemcachedConnection] New connection is not open", func() {
		connection := MemcachedConnection{Url: "127.0.0.1:11290", Logger: &memcached_connection_logger}
		defer connection.Close()

		open := connection.IsOpen()
		closed := connection.IsClosed()

		// Should be opposite of each other:
		c.Expect(open, gospec.Equals, false)
		c.Expect(closed, gospec.Equals, true)
		c.Expect(closed, gospec.Satisfies, open != closed)
	})

	c.Specify("[MemcachedConnection] Opening connection to Invalid Host/Port has errors", func() {
		connection := MemcachedConnection{Url: "127.0.0.1:11291", Logger: &memcached_connection_logger}
		defer connection.Close()

		// The server is not running ...
		// This should return an error
		err := connection.Open()
		c.Expect(err, gospec.Satisfies, err != nil)

		closed := connection.IsClosed()
		c.Expect(closed, gospec.Equals, true)
	})

	c.Specify("[MemcachedConnection] Opening connection to Valid Host/Port has no errors", func() {
		connection := MemcachedConnection{Url: "127.0.0.1:11292", Logger: &memcached_connection_logger}
		defer connection.Close()

		// Start the server ...
		cmd := exec.Command("memcached", "-p", "11292")
		err := cmd.Start()
		c.Expect(err, gospec.Equals, nil)
		if err != nil {
			// Abort on errors
			return
		}
		time.Sleep(time.Duration(1) * time.Second)
		defer cmd.Wait()
		defer cmd.Process.Kill()

		err = connection.Open()
		c.Expect(err, gospec.Equals, nil)

		open := connection.IsOpen()
		closed := connection.IsClosed()
		c.Expect(open, gospec.Equals, true)
		c.Expect(closed, gospec.Equals, false)
		c.Expect(closed, gospec.Satisfies, open != closed)
	})

	// c.Specify("[MemcachedConnection] Ping (-->Cmd-->Append+GetReply) (re-)opens the connection automatically", func() {
	// 	connection := MemcachedConnection{Url: "127.0.0.1:11293", Logger: &memcached_connection_logger}
	// 	defer connection.Close()
	//
	// 	// Start the server ...
	// 	cmd := exec.Command("memcached", "-p", "11293")
	// 	err := cmd.Start()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	if err != nil {
	// 		// Abort on errors
	// 		return
	// 	}
	// 	time.Sleep(time.Duration(1) * time.Second)
	// 	defer cmd.Wait()
	// 	defer cmd.Process.Kill()
	//
	// 	// Starts off closed ...
	// 	c.Expect(connection.IsClosed(), gospec.Equals, true)
	//
	// 	// Ping the server
	// 	// Should now be open
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	c.Expect(connection.IsOpen(), gospec.Equals, true)
	//
	// 	// Close the connection
	// 	err = connection.Close()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	c.Expect(connection.IsClosed(), gospec.Equals, true)
	//
	// 	// Ping the server again
	// 	// Should now be open again
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	c.Expect(connection.IsOpen(), gospec.Equals, true)
	// })
	//
	// c.Specify("[MemcachedConnection] Ping to invalid Host/Port has errors", func() {
	// 	connection := MemcachedConnection{Url: "127.0.0.1:11294", Logger: &memcached_connection_logger}
	// 	defer connection.Close()
	//
	// 	// Start the server ...
	// 	cmd := exec.Command("memcached", "-p", "11294")
	// 	err := cmd.Start()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	if err != nil {
	// 		// Abort on errors
	// 		return
	// 	}
	// 	time.Sleep(time.Duration(1) * time.Second)
	// 	// Defer the evaluation of cmd
	// 	defer func() { cmd.Wait() }()
	// 	defer func() { cmd.Process.Kill() }()
	//
	// 	// Starts off closed ...
	// 	c.Expect(connection.IsClosed(), gospec.Equals, true)
	//
	// 	// Ping the server
	// 	// Should now be open
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	c.Expect(connection.IsOpen(), gospec.Equals, true)
	//
	// 	// Kill the server
	// 	cmd.Process.Kill()
	// 	cmd.Wait()
	//
	// 	// Ping the server again
	// 	// Should return an error and now be closed
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Satisfies, err != nil)
	// 	c.Expect(connection.IsClosed(), gospec.Equals, true)
	//
	// 	// Re-Start the server ...
	// 	cmd = exec.Command("memcached-server", "--port", "11294")
	// 	err = cmd.Start()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	if err != nil {
	// 		// Abort on errors
	// 		return
	// 	}
	// 	time.Sleep(time.Duration(1) * time.Second)
	//
	// 	// Ping the server
	// 	// Should now be open
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	c.Expect(connection.IsOpen(), gospec.Equals, true)
	// })
}
<commit_msg>Working on memcached + tests<commit_after>package dog_pool

import "os/exec"
import "time"
import "testing"
import "github.com/orfjackal/gospec/src/gospec"
import "github.com/alecthomas/log4go"

//
// NOTE: Use differient ports for each test!
//       gospec runs the specs in parallel!
//
func TestMemcachedConnectionSpecs(t *testing.T) {
	r := gospec.NewRunner()
	r.AddSpec(MemcachedConnectionSpecs)
	gospec.MainGoTest(r, t)
}

// Helpers
func MemcachedConnectionSpecs(c gospec.Context) {
	var memcached_connection_logger = log4go.NewDefaultLogger(log4go.FINEST)

	c.Specify("[MemcachedConnection] New connection is not open", func() {
		connection := MemcachedConnection{Url: "127.0.0.1:11290", Logger: &memcached_connection_logger}
		defer connection.Close()

		open := connection.IsOpen()
		closed := connection.IsClosed()

		// Should be opposite of each other:
		c.Expect(open, gospec.Equals, false)
		c.Expect(closed, gospec.Equals, true)
		c.Expect(closed, gospec.Satisfies, open != closed)
	})

	c.Specify("[MemcachedConnection] Opening connection to Invalid Host/Port has errors", func() {
		connection := MemcachedConnection{Url: "127.0.0.1:11291", Logger: &memcached_connection_logger}
		defer connection.Close()

		// The server is not running ...
		// This should return an error
		err := connection.Open()
		c.Expect(err, gospec.Satisfies, err != nil)

		closed := connection.IsClosed()
		c.Expect(closed, gospec.Equals, true)
	})

	c.Specify("[MemcachedConnection] Opening connection to Valid Host/Port has no errors", func() {
		connection := MemcachedConnection{Url: "127.0.0.1:11292", Logger: &memcached_connection_logger}
		defer connection.Close()

		// Start the server ...
		cmd := exec.Command("memcached", "-p", "11292")
		err := cmd.Start()
		c.Expect(err, gospec.Equals, nil)
		if err != nil {
			// Abort on errors
			return
		}
		time.Sleep(time.Duration(1) * time.Second)
		defer cmd.Wait()
		defer cmd.Process.Kill()

		err = connection.Open()
		c.Expect(err, gospec.Equals, nil)

		open := connection.IsOpen()
		closed := connection.IsClosed()
		c.Expect(open, gospec.Equals, true)
		c.Expect(closed, gospec.Equals, false)
		c.Expect(closed, gospec.Satisfies, open != closed)
	})

	c.Specify("[MemcachedConnection] Ping (-->Cmd-->Append+GetReply) (re-)opens the connection automatically", func() {
		connection := MemcachedConnection{Url: "127.0.0.1:11293", Logger: &memcached_connection_logger}
		defer connection.Close()

		// Start the server ...
		cmd := exec.Command("memcached", "-p", "11293")
		err := cmd.Start()
		c.Expect(err, gospec.Equals, nil)
		if err != nil {
			// Abort on errors
			return
		}
		time.Sleep(time.Duration(1) * time.Second)
		defer cmd.Wait()
		defer cmd.Process.Kill()

		// Starts off closed ...
		c.Expect(connection.IsClosed(), gospec.Equals, true)

		// Ping the server
		// Should now be open
		err = connection.Ping()
		c.Expect(err, gospec.Equals, nil)
		c.Expect(connection.IsOpen(), gospec.Equals, true)

		// Close the connection
		err = connection.Close()
		c.Expect(err, gospec.Equals, nil)
		c.Expect(connection.IsClosed(), gospec.Equals, true)

		// Ping the server again
		// Should now be open again
		err = connection.Ping()
		c.Expect(err, gospec.Equals, nil)
		c.Expect(connection.IsOpen(), gospec.Equals, true)
	})

	// c.Specify("[MemcachedConnection] Ping to invalid Host/Port has errors", func() {
	// 	connection := MemcachedConnection{Url: "127.0.0.1:11294", Logger: &memcached_connection_logger}
	// 	defer connection.Close()
	//
	// 	// Start the server ...
	// 	cmd := exec.Command("memcached", "-p", "11294")
	// 	err := cmd.Start()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	if err != nil {
	// 		// Abort on errors
	// 		return
	// 	}
	// 	time.Sleep(time.Duration(1) * time.Second)
	// 	// Defer the evaluation of cmd
	// 	defer func() { cmd.Wait() }()
	// 	defer func() { cmd.Process.Kill() }()
	//
	// 	// Starts off closed ...
	// 	c.Expect(connection.IsClosed(), gospec.Equals, true)
	//
	// 	// Ping the server
	// 	// Should now be open
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	c.Expect(connection.IsOpen(), gospec.Equals, true)
	//
	// 	// Kill the server
	// 	cmd.Process.Kill()
	// 	cmd.Wait()
	//
	// 	// Ping the server again
	// 	// Should return an error and now be closed
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Satisfies, err != nil)
	// 	c.Expect(connection.IsClosed(), gospec.Equals, true)
	//
	// 	// Re-Start the server ...
	// 	cmd = exec.Command("memcached-server", "--port", "11294")
	// 	err = cmd.Start()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	if err != nil {
	// 		// Abort on errors
	// 		return
	// 	}
	// 	time.Sleep(time.Duration(1) * time.Second)
	//
	// 	// Ping the server
	// 	// Should now be open
	// 	err = connection.Ping()
	// 	c.Expect(err, gospec.Equals, nil)
	// 	c.Expect(connection.IsOpen(), gospec.Equals, true)
	// })
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"citadelapp.io/citadel"
	"citadelapp.io/citadel/repository"
	"citadelapp.io/citadel/utils"
	"github.com/codegangsta/cli"
	"github.com/dancannon/gorethink"
	"github.com/samalba/dockerclient"
)

type (
	HostEngine struct {
		client     *dockerclient.DockerClient
		repository *repository.Repository
		id         string
		listenAddr string
	}
)

var runHostCommand = cli.Command{
	Name:   "run-host",
	Usage:  "run the host and connect it to the cluster",
	Action: runHostAction,
	Flags: []cli.Flag{
		cli.StringFlag{"host-id", "", "specify host id (default: detected)"},
		cli.StringFlag{"region", "", "region where the host is running"},
		cli.StringFlag{"addr", "", "external ip address for the host"},
		cli.StringFlag{"docker", "unix:///var/run/docker.sock", "docker remote ip address"},
		cli.IntFlag{"cpus", -1, "number of cpus available to the host"},
		cli.IntFlag{"memory", -1, "number of mb of memory available to the host"},
		cli.StringFlag{"listen, l", ":8787", "listen address"},
	},
}

func runHostAction(context *cli.Context) {
	var (
		cpus       = context.Int("cpus")
		memory     = context.Int("memory")
		addr       = context.String("addr")
		region     = context.String("region")
		hostId     = context.String("host-id")
		listenAddr = context.String("listen")
	)
	if hostId == "" {
		id, err := utils.GetMachineID()
		if err != nil {
			logger.WithField("error", err).Fatal("unable to read machine id")
		}
		hostId = id
	}

	switch {
	case cpus < 1:
		logger.Fatal("cpus must have a value")
	case memory < 1:
		logger.Fatal("memory must have a value")
	case addr == "":
		logger.Fatal("addr must have a value")
	case region == "":
		logger.Fatal("region must have a value")
	}

	r, err := repository.New(context.GlobalString("repository"))
	if err != nil {
		logger.WithField("error", err).Fatal("unable to connect to repository")
	}
	defer r.Close()

	host := &citadel.Host{
		ID:     hostId,
		Memory: memory,
		Cpus:   cpus,
		Addr:   addr,
		Region: region,
	}

	if err := r.SaveHost(host); err != nil {
		logger.WithField("error", err).Fatal("unable to save host")
	}
	defer r.DeleteHost(hostId)

	client, err := dockerclient.NewDockerClient(context.String("docker"))
	if err != nil {
		logger.WithField("error", err).Fatal("unable to connect to docker")
	}

	hostEngine := &HostEngine{
		client:     client,
		repository: r,
		id:         hostId,
		listenAddr: listenAddr,
	}
	// start
	go hostEngine.run()
	// watch for operations
	go hostEngine.watch()
	// handle stop signal
	hostEngine.waitForInterrupt()
}

func (eng *HostEngine) waitForInterrupt() {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	for _ = range sigChan {
		// stop engine
		eng.stop()
		os.Exit(0)
	}
}

func (eng *HostEngine) run() {
	logger.Info("Starting up")
	if err := eng.loadContainers(); err != nil {
		logger.WithField("error", err).Fatal("unable to load containers")
	}

	// listen for events
	eng.client.StartMonitorEvents(eng.dockerEventHandler)

	if err := http.ListenAndServe(eng.listenAddr, nil); err != nil {
		logger.WithField("error", err).Fatal("unable to listen on http")
	}
}

func (eng *HostEngine) stop() {
	logger.Info("Shutting down")
	// remove host from repository
	eng.repository.DeleteHost(eng.id)
}

func (eng *HostEngine) loadContainers() error {
	sesson := eng.repository.Session()

	// delete all containers for this host and recreate them
	if _, err := gorethink.Table("containers").Filter(func(row gorethink.RqlTerm) interface{} {
		return row.Field("host_id").Eq(eng.id)
	}).Delete().Run(sesson); err != nil {
		return err
	}

	containers, err := eng.client.ListContainers(true)
	if err != nil {
		return err
	}

	for _, c := range containers {
		cc, err := eng.generateContainerInfo(c)
		if err != nil {
			return err
		}
		if err := eng.repository.SaveContainer(cc); err != nil {
			return err
		}
	}

	return nil
}

func (eng *HostEngine) generateContainerInfo(cnt interface{}) (*citadel.Container, error) {
	c := cnt.(dockerclient.Container)
	info, err := eng.client.InspectContainer(c.Id)
	if err != nil {
		return nil, err
	}
	cc := &citadel.Container{
		ID:     info.Id,
		Image:  utils.CleanImageName(c.Image),
		HostID: eng.id,
		Cpus:   info.Config.CpuShares, // FIXME: not the right place, this is cpuset
	}

	if info.Config.Memory > 0 {
		cc.Memory = info.Config.Memory / 1024 / 1024
	}

	if info.State.Running {
		cc.State.Status = citadel.Running
	} else {
		cc.State.Status = citadel.Stopped
	}
	cc.State.ExitCode = info.State.ExitCode
	return cc, nil
}

func (eng *HostEngine) dockerEventHandler(event *dockerclient.Event, args ...interface{}) {
	switch event.Status {
	case "start":
		// reload containers into repository
		// when adding a single container, the Container struct is not
		// returned but instead ContainerInfo.  to keep the same
		// generateContainerInfo for a citadel container, i simply
		// re-run the loadContainers.  this can probably be improved.
		eng.loadContainers()
	case "destroy":
		// remove container from repository
		if err := eng.repository.DeleteContainer(event.Id); err != nil {
			logger.Warnf("Unable to remove container from repository: %s", err)
			return
		}
	}
}

func (eng *HostEngine) watch() {
	tickerChan := time.NewTicker(time.Millisecond * 2000).C // check for new instances every 2 seconds
	for {
		select {
		case <-tickerChan:
			tasks, err := eng.repository.FetchTasks()
			if err != nil {
				logger.Fatal("unable to fetch queue: %s", err)
			}
			for _, task := range tasks {
				// filter this hosts tasks
				if task.Host == eng.id {
					logger.Infof("Task: %s", task.Id)
				}
			}
		}
	}
}
<commit_msg>added run handler to process run tasks ; containers now start<commit_after>package main

import (
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"citadelapp.io/citadel"
	"citadelapp.io/citadel/repository"
	"citadelapp.io/citadel/utils"
	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/dancannon/gorethink"
	"github.com/samalba/dockerclient"
)

type (
	HostEngine struct {
		client     *dockerclient.DockerClient
		repository *repository.Repository
		id         string
		listenAddr string
	}
)

var runHostCommand = cli.Command{
	Name:   "run-host",
	Usage:  "run the host and connect it to the cluster",
	Action: runHostAction,
	Flags: []cli.Flag{
		cli.StringFlag{"host-id", "", "specify host id (default: detected)"},
		cli.StringFlag{"region", "", "region where the host is running"},
		cli.StringFlag{"addr", "", "external ip address for the host"},
		cli.StringFlag{"docker", "unix:///var/run/docker.sock", "docker remote ip address"},
		cli.IntFlag{"cpus", -1, "number of cpus available to the host"},
		cli.IntFlag{"memory", -1, "number of mb of memory available to the host"},
		cli.StringFlag{"listen, l", ":8787", "listen address"},
	},
}

func runHostAction(context *cli.Context) {
	var (
		cpus       = context.Int("cpus")
		memory     = context.Int("memory")
		addr       = context.String("addr")
		region     = context.String("region")
		hostId     = context.String("host-id")
		listenAddr = context.String("listen")
	)
	if hostId == "" {
		id, err := utils.GetMachineID()
		if err != nil {
			logger.WithField("error", err).Fatal("unable to read machine id")
		}
		hostId = id
	}

	switch {
	case cpus < 1:
		logger.Fatal("cpus must have a value")
	case memory < 1:
		logger.Fatal("memory must have a value")
	case addr == "":
		logger.Fatal("addr must have a value")
	case region == "":
		logger.Fatal("region must have a value")
	}

	r, err := repository.New(context.GlobalString("repository"))
	if err != nil {
		logger.WithField("error", err).Fatal("unable to connect to repository")
	}
	defer r.Close()

	host := &citadel.Host{
		ID:     hostId,
		Memory: memory,
		Cpus:   cpus,
		Addr:   addr,
		Region: region,
	}

	if err := r.SaveHost(host); err != nil {
		logger.WithField("error", err).Fatal("unable to save host")
	}
	defer r.DeleteHost(hostId)

	client, err := dockerclient.NewDockerClient(context.String("docker"))
	if err != nil {
		logger.WithField("error", err).Fatal("unable to connect to docker")
	}

	hostEngine := &HostEngine{
		client:     client,
		repository: r,
		id:         hostId,
		listenAddr: listenAddr,
	}
	// start
	go hostEngine.run()
	// watch for operations
	go hostEngine.watch()
	// handle stop signal
	hostEngine.waitForInterrupt()
}

func (eng *HostEngine) waitForInterrupt() {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
	for _ = range sigChan {
		// stop engine
		eng.stop()
		os.Exit(0)
	}
}

func (eng *HostEngine) run() {
	logger.Info("Starting Citadel")
	if err := eng.loadContainers(); err != nil {
		logger.WithField("error", err).Fatal("unable to load containers")
	}

	// listen for events
	eng.client.StartMonitorEvents(eng.dockerEventHandler)

	if err := http.ListenAndServe(eng.listenAddr, nil); err != nil {
		logger.WithField("error", err).Fatal("unable to listen on http")
	}
}

func (eng *HostEngine) stop() {
	logger.Info("Stopping")
	// remove host from repository
	eng.repository.DeleteHost(eng.id)
}

func (eng *HostEngine) loadContainers() error {
	sesson := eng.repository.Session()

	// delete all containers for this host and recreate them
	if _, err := gorethink.Table("containers").Filter(func(row gorethink.RqlTerm) interface{} {
		return row.Field("host_id").Eq(eng.id)
	}).Delete().Run(sesson); err != nil {
		return err
	}

	containers, err := eng.client.ListContainers(true)
	if err != nil {
		return err
	}

	for _, c := range containers {
		cc, err := eng.generateContainerInfo(c)
		if err != nil {
			return err
		}
		if err := eng.repository.SaveContainer(cc); err != nil {
			return err
		}
	}

	return nil
}

func (eng *HostEngine) generateContainerInfo(cnt interface{}) (*citadel.Container, error) {
	c := cnt.(dockerclient.Container)
	info, err := eng.client.InspectContainer(c.Id)
	if err != nil {
		return nil, err
	}
	cc := &citadel.Container{
		ID:     info.Id,
		Image:  utils.CleanImageName(c.Image),
		HostID: eng.id,
		Cpus:   info.Config.CpuShares, // FIXME: not the right place, this is cpuset
	}

	if info.Config.Memory > 0 {
		cc.Memory = info.Config.Memory / 1024 / 1024
	}

	if info.State.Running {
		cc.State.Status = citadel.Running
	} else {
		cc.State.Status = citadel.Stopped
	}
	cc.State.ExitCode = info.State.ExitCode
	return cc, nil
}

func (eng *HostEngine) dockerEventHandler(event *dockerclient.Event, args ...interface{}) {
	switch event.Status {
	case "start":
		// reload containers into repository
		// when adding a single container, the Container struct is not
		// returned but instead ContainerInfo.  to keep the same
		// generateContainerInfo for a citadel container, i simply
		// re-run the loadContainers.  this can probably be improved.
		eng.loadContainers()
	case "destroy":
		// remove container from repository
		if err := eng.repository.DeleteContainer(event.Id); err != nil {
			logger.Warnf("Unable to remove container from repository: %s", err)
			return
		}
	}
}

func (eng *HostEngine) watch() {
	tickerChan := time.NewTicker(time.Millisecond * 2000).C // check for new instances every 2 seconds
	for {
		select {
		case <-tickerChan:
			tasks, err := eng.repository.FetchTasks()
			if err != nil {
				logger.Fatal("unable to fetch queue: %s", err)
			}
			for _, task := range tasks {
				// filter this hosts tasks
				if task.Host == eng.id {
					go eng.taskHandler(task)
				}
			}
		}
	}
}

func (eng *HostEngine) taskHandler(task *citadel.Task) {
	switch task.Command {
	case "run":
		logger.WithFields(logrus.Fields{
			"host": task.Host,
			"args": task.Args,
		}).Info("processing run task")
		eng.runHandler(task)
		return
	default:
		logger.WithFields(logrus.Fields{
			"command": task.Command,
			"args":    task.Args,
		}).Error("unknown task command")
		return
	}
}

func (eng *HostEngine) runHandler(task *citadel.Task) {
	logger.WithFields(logrus.Fields{
		"host":      task.Host,
		"image":     task.Args["image"],
		"cpus":      task.Args["cpus"],
		"memory":    task.Args["memory"],
		"instances": task.Args["instances"],
	}).Info("running container")
	// remove task
	eng.repository.DeleteTask(task.Id)
	instances := int(task.Args["instances"].(float64))
	// run containers
	for i := 0; i < instances; i++ {
		image := task.Args["image"].(string)
		cpus := int(task.Args["cpus"].(float64))
		memory := int(task.Args["memory"].(float64))
		containerConfig := &dockerclient.ContainerConfig{
			Image:     image,
			Memory:    memory * 1048576, // convert to bytes
			CpuShares: cpus,
			Tty:       true,
			OpenStdin: true,
		}
		hostConfig := &dockerclient.HostConfig{
			PublishAllPorts: true,
		}
		// create container
		containerId, err := eng.client.CreateContainer(containerConfig, "")
		if err != nil {
			switch err.Error() {
			case "Not found":
				// missing image; pull
				eng.client.PullImage(image, "latest")
				// containerId is blank if image is missing; create new config
				cId, err := eng.client.CreateContainer(containerConfig, "")
				if err != nil {
					logger.WithFields(logrus.Fields{
						"image": image,
						"err":   err,
					}).Error("error creating container")
					return
				}
				containerId = cId
			default:
				logger.WithFields(logrus.Fields{
					"image": image,
					"err":   err,
				}).Error("error creating container")
				return
			}
		}
		// start container
		if err := eng.client.StartContainer(containerId, hostConfig); err != nil {
			logger.WithFields(logrus.Fields{
				"image": image,
				"err":   err,
			}).Error("error starting container")
			return
		}

		logger.WithFields(logrus.Fields{
			"host":        task.Host,
			"containerId": containerId,
			"image":       image,
		}).Info("started container")
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package hipache provides a router implementation that store routes in Redis,
// as specified by Hipache (https://github.com/dotcloud/hipache).
//
// It does not provided any exported type, in order to use the router, you must
// import this package and get the router intance using the function
// router.Get.
//
// In order to use this router, you need to define the "hipache:domain"
// setting.
package hipache

import (
	"errors"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/router"
	"strings"
)

var pool *redis.Pool

var errRouteNotFound = errors.New("Route not found")

func init() {
	router.Register("hipache", hipacheRouter{})
}

func connect() redis.Conn {
	if pool == nil {
		srv, err := config.GetString("hipache:redis-server")
		if err != nil {
			srv = "localhost:6379"
		}
		pool = redis.NewPool(func() (redis.Conn, error) {
			return redis.Dial("tcp", srv)
		}, 10)
	}
	return pool.Get()
}

type hipacheRouter struct{}

func (hipacheRouter) AddBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	_, err = conn.Do("RPUSH", frontend, name)
	if err != nil {
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	_, err = conn.Do("DEL", frontend)
	if err != nil {
		return &routeError{"remove", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		return err
	}
	if cname == "" {
		return nil
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"remove", err}
	}
	_, err = conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

func (r hipacheRouter) AddRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		log.Prinft("error on getting hipache domin in add route for %s - %s", name, address)
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.addRoute(frontend, address); err != nil {
		log.Prinft("error on add route for %s - %s", name, address)
		return &routeError{"add", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		log.Prinft("error on get cname in add route for %s - %s", name, address)
		return err
	}
	if cname == "" {
		return nil
	}
	return r.addRoute("frontend:"+cname, address)
}

func (hipacheRouter) addRoute(name, address string) error {
	conn := connect()
	defer conn.Close()
	_, err := conn.Do("RPUSH", name, address)
	if err != nil {
		log.Prinft("error on store in redis in add route for %s - %s", name, address)
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.removeElement(frontend, address); err != nil {
		return err
	}
	cname, err := r.getCName(name)
	if err != nil {
		return &routeError{"remove", err}
	}
	if cname == "" {
		return nil
	}
	return r.removeElement("frontend:"+cname, address)
}

func (hipacheRouter) getCName(name string) (string, error) {
	conn := connect()
	defer conn.Close()
	cname, err := redis.String(conn.Do("GET", "cname:"+name))
	if err != nil && err != redis.ErrNil {
		return "", &routeError{"getCName", err}
	}
	return cname, nil
}

// validCName returns true if the cname is not a subdomain of
// hipache:domain conf, false otherwise
func (hipacheRouter) validCName(cname string) bool {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return false
	}
	return !strings.Contains(cname, domain)
}

func (r hipacheRouter) SetCName(cname, name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"setCName", err}
	}
	if !r.validCName(cname) {
		err := errors.New(fmt.Sprintf("Invalid CNAME %s. You can't use Tsuru's application domain.", cname))
		return &routeError{"setCName", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	routes, err := redis.Strings(conn.Do("LRANGE", frontend, 0, -1))
	if err != nil {
		return &routeError{"get", err}
	}
	if oldCName, err := redis.String(conn.Do("GET", "cname:"+name)); err == nil && oldCName != "" {
		err = r.UnsetCName(oldCName, name)
		if err != nil {
			return &routeError{"setCName", err}
		}
	}
	_, err = conn.Do("SET", "cname:"+name, cname)
	if err != nil {
		return &routeError{"set", err}
	}
	frontend = "frontend:" + cname
	for _, r := range routes {
		_, err := conn.Do("RPUSH", frontend, r)
		if err != nil {
			return &routeError{"setCName", err}
		}
	}
	return nil
}

func (r hipacheRouter) UnsetCName(cname, name string) error {
	conn := connect()
	defer conn.Close()
	_, err := conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"unsetCName", err}
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"unsetCName", err}
	}
	return nil
}

func (hipacheRouter) Addr(name string) (string, error) {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return "", &routeError{"get", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	reply, err := conn.Do("LRANGE", frontend, 0, 0)
	if err != nil {
		return "", &routeError{"get", err}
	}
	backends := reply.([]interface{})
	if len(backends) < 1 {
		return "", errRouteNotFound
	}
	return fmt.Sprintf("%s.%s", name, domain), nil
}

func (hipacheRouter) removeElement(name, address string) error {
	conn := connect()
	defer conn.Close()
	_, err := conn.Do("LREM", name, 0, address)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

type routeError struct {
	op  string
	err error
}

func (e *routeError) Error() string {
	return fmt.Sprintf("Could not %s route: %s", e.op, e.err)
}
<commit_msg>router: fixed a typo.<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package hipache provides a router implementation that store routes in Redis,
// as specified by Hipache (https://github.com/dotcloud/hipache).
//
// It does not provided any exported type, in order to use the router, you must
// import this package and get the router intance using the function
// router.Get.
//
// In order to use this router, you need to define the "hipache:domain"
// setting.
package hipache

import (
	"errors"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/router"
	"strings"
)

var pool *redis.Pool

var errRouteNotFound = errors.New("Route not found")

func init() {
	router.Register("hipache", hipacheRouter{})
}

func connect() redis.Conn {
	if pool == nil {
		srv, err := config.GetString("hipache:redis-server")
		if err != nil {
			srv = "localhost:6379"
		}
		pool = redis.NewPool(func() (redis.Conn, error) {
			return redis.Dial("tcp", srv)
		}, 10)
	}
	return pool.Get()
}

type hipacheRouter struct{}

func (hipacheRouter) AddBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	_, err = conn.Do("RPUSH", frontend, name)
	if err != nil {
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveBackend(name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	_, err = conn.Do("DEL", frontend)
	if err != nil {
		return &routeError{"remove", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		return err
	}
	if cname == "" {
		return nil
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"remove", err}
	}
	_, err = conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

func (r hipacheRouter) AddRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		log.Printf("error on getting hipache domin in add route for %s - %s", name, address)
		return &routeError{"add", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.addRoute(frontend, address); err != nil {
		log.Printf("error on add route for %s - %s", name, address)
		return &routeError{"add", err}
	}
	cname, err := r.getCName(name)
	if err != nil {
		log.Printf("error on get cname in add route for %s - %s", name, address)
		return err
	}
	if cname == "" {
		return nil
	}
	return r.addRoute("frontend:"+cname, address)
}

func (hipacheRouter) addRoute(name, address string) error {
	conn := connect()
	defer conn.Close()
	_, err := conn.Do("RPUSH", name, address)
	if err != nil {
		log.Printf("error on store in redis in add route for %s - %s", name, address)
		return &routeError{"add", err}
	}
	return nil
}

func (r hipacheRouter) RemoveRoute(name, address string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"remove", err}
	}
	frontend := "frontend:" + name + "." + domain
	if err := r.removeElement(frontend, address); err != nil {
		return err
	}
	cname, err := r.getCName(name)
	if err != nil {
		return &routeError{"remove", err}
	}
	if cname == "" {
		return nil
	}
	return r.removeElement("frontend:"+cname, address)
}

func (hipacheRouter) getCName(name string) (string, error) {
	conn := connect()
	defer conn.Close()
	cname, err := redis.String(conn.Do("GET", "cname:"+name))
	if err != nil && err != redis.ErrNil {
		return "", &routeError{"getCName", err}
	}
	return cname, nil
}

// validCName returns true if the cname is not a subdomain of
// hipache:domain conf, false otherwise
func (hipacheRouter) validCName(cname string) bool {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return false
	}
	return !strings.Contains(cname, domain)
}

func (r hipacheRouter) SetCName(cname, name string) error {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return &routeError{"setCName", err}
	}
	if !r.validCName(cname) {
		err := errors.New(fmt.Sprintf("Invalid CNAME %s. You can't use Tsuru's application domain.", cname))
		return &routeError{"setCName", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	routes, err := redis.Strings(conn.Do("LRANGE", frontend, 0, -1))
	if err != nil {
		return &routeError{"get", err}
	}
	if oldCName, err := redis.String(conn.Do("GET", "cname:"+name)); err == nil && oldCName != "" {
		err = r.UnsetCName(oldCName, name)
		if err != nil {
			return &routeError{"setCName", err}
		}
	}
	_, err = conn.Do("SET", "cname:"+name, cname)
	if err != nil {
		return &routeError{"set", err}
	}
	frontend = "frontend:" + cname
	for _, r := range routes {
		_, err := conn.Do("RPUSH", frontend, r)
		if err != nil {
			return &routeError{"setCName", err}
		}
	}
	return nil
}

func (r hipacheRouter) UnsetCName(cname, name string) error {
	conn := connect()
	defer conn.Close()
	_, err := conn.Do("DEL", "cname:"+name)
	if err != nil {
		return &routeError{"unsetCName", err}
	}
	_, err = conn.Do("DEL", "frontend:"+cname)
	if err != nil {
		return &routeError{"unsetCName", err}
	}
	return nil
}

func (hipacheRouter) Addr(name string) (string, error) {
	domain, err := config.GetString("hipache:domain")
	if err != nil {
		return "", &routeError{"get", err}
	}
	frontend := "frontend:" + name + "." + domain
	conn := connect()
	defer conn.Close()
	reply, err := conn.Do("LRANGE", frontend, 0, 0)
	if err != nil {
		return "", &routeError{"get", err}
	}
	backends := reply.([]interface{})
	if len(backends) < 1 {
		return "", errRouteNotFound
	}
	return fmt.Sprintf("%s.%s", name, domain), nil
}

func (hipacheRouter) removeElement(name, address string) error {
	conn := connect()
	defer conn.Close()
	_, err := conn.Do("LREM", name, 0, address)
	if err != nil {
		return &routeError{"remove", err}
	}
	return nil
}

type routeError struct {
	op  string
	err error
}

func (e *routeError) Error() string {
	return fmt.Sprintf("Could not %s route: %s", e.op, e.err)
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"fmt"
	"github.com/github/git-media/gitmedia"
	"io/ioutil"
	"os"
	"path/filepath"
)

type LogsCommand struct {
	ClearLogs bool
	Boomtown  bool
	*Command
}

func (c *LogsCommand) Setup() {
	c.FlagSet.BoolVar(&c.ClearLogs, "clear", false, "Clear existing error logs")
	c.FlagSet.BoolVar(&c.Boomtown, "boomtown", false, "Trigger a panic")
}

func (c *LogsCommand) Run() {
	if c.ClearLogs {
		c.clear()
	}

	if c.Boomtown {
		c.boomtown()
		return
	}

	var sub string
	if len(c.SubCommands) > 0 {
		sub = c.SubCommands[0]
	}

	switch sub {
	case "last":
		c.lastLog()
	case "":
		c.listLogs()
	default:
		c.showLog(sub)
	}
}

func (c *LogsCommand) listLogs() {
	for _, path := range sortedLogs() {
		gitmedia.Print(path)
	}
}

func (c *LogsCommand) lastLog() {
	logs := sortedLogs()
	c.showLog(logs[len(logs)-1])
}

func (c *LogsCommand) showLog(name string) {
	by, err := ioutil.ReadFile(filepath.Join(gitmedia.LocalLogDir, name))
	if err != nil {
		gitmedia.Exit("Error reading log: %s", name)
	}

	gitmedia.Debug("Reading log: %s", name)
	os.Stdout.Write(by)
}

func (c *LogsCommand) clear() {
	err := os.RemoveAll(gitmedia.LocalLogDir)
	if err != nil {
		gitmedia.Panic(err, "Error clearing %s", gitmedia.LocalLogDir)
	}

	fmt.Println("Cleared", gitmedia.LocalLogDir)
}

func (c *LogsCommand) boomtown() {
	gitmedia.Debug("Debug message")
	err := errors.New("Error!")
	gitmedia.Panic(err, "Welcome to Boomtown")
	gitmedia.Debug("Never seen")
}

func sortedLogs() []string {
	fileinfos, err := ioutil.ReadDir(gitmedia.LocalLogDir)
	if err != nil {
		return []string{}
	}

	names := make([]string, len(fileinfos))
	for index, info := range fileinfos {
		names[index] = info.Name()
	}

	return names
}

func init() {
	registerCommand("logs", func(c *Command) RunnableCommand {
		return &LogsCommand{Command: c}
	})
}
<commit_msg>If there are no logs to show this code would panic. Show a message to the user instead.<commit_after>package commands

import (
	"errors"
	"fmt"
	"github.com/github/git-media/gitmedia"
	"io/ioutil"
	"os"
	"path/filepath"
)

type LogsCommand struct {
	ClearLogs bool
	Boomtown  bool
	*Command
}

func (c *LogsCommand) Setup() {
	c.FlagSet.BoolVar(&c.ClearLogs, "clear", false, "Clear existing error logs")
	c.FlagSet.BoolVar(&c.Boomtown, "boomtown", false, "Trigger a panic")
}

func (c *LogsCommand) Run() {
	if c.ClearLogs {
		c.clear()
	}

	if c.Boomtown {
		c.boomtown()
		return
	}

	var sub string
	if len(c.SubCommands) > 0 {
		sub = c.SubCommands[0]
	}

	switch sub {
	case "last":
		c.lastLog()
	case "":
		c.listLogs()
	default:
		c.showLog(sub)
	}
}

func (c *LogsCommand) listLogs() {
	for _, path := range sortedLogs() {
		gitmedia.Print(path)
	}
}

func (c *LogsCommand) lastLog() {
	logs := sortedLogs()
	if len(logs) < 1 {
		gitmedia.Print("No logs to show")
		return
	}
	c.showLog(logs[len(logs)-1])
}

func (c *LogsCommand) showLog(name string) {
	by, err := ioutil.ReadFile(filepath.Join(gitmedia.LocalLogDir, name))
	if err != nil {
		gitmedia.Exit("Error reading log: %s", name)
	}

	gitmedia.Debug("Reading log: %s", name)
	os.Stdout.Write(by)
}

func (c *LogsCommand) clear() {
	err := os.RemoveAll(gitmedia.LocalLogDir)
	if err != nil {
		gitmedia.Panic(err, "Error clearing %s", gitmedia.LocalLogDir)
	}

	fmt.Println("Cleared", gitmedia.LocalLogDir)
}

func (c *LogsCommand) boomtown() {
	gitmedia.Debug("Debug message")
	err := errors.New("Error!")
	gitmedia.Panic(err, "Welcome to Boomtown")
	gitmedia.Debug("Never seen")
}

func sortedLogs() []string {
	fileinfos, err := ioutil.ReadDir(gitmedia.LocalLogDir)
	if err != nil {
		return []string{}
	}

	names := make([]string, len(fileinfos))
	for index, info := range fileinfos {
		names[index] = info.Name()
	}

	return names
}

func init() {
	registerCommand("logs", func(c *Command) RunnableCommand {
		return &LogsCommand{Command: c}
	})
}
<|endoftext|>


<commit_before>package http

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"

	cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"

	cmds "github.com/ipfs/go-ipfs/commands"
	logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log"
)

var log = logging.Logger("commands/http")

// the internal handler for the API
type internalHandler struct {
	ctx  cmds.Context
	root *cmds.Command
	cfg  *ServerConfig
}

// The Handler struct is funny because we want to wrap our internal handler
// with CORS while keeping our fields.
type Handler struct {
	internalHandler
	corsHandler http.Handler
}

var ErrNotFound = errors.New("404 page not found")

const (
	StreamErrHeader        = "X-Stream-Error"
	streamHeader           = "X-Stream-Output"
	channelHeader          = "X-Chunked-Output"
	uaHeader               = "User-Agent"
	contentTypeHeader      = "Content-Type"
	contentLengthHeader    = "Content-Length"
	contentDispHeader      = "Content-Disposition"
	transferEncodingHeader = "Transfer-Encoding"
	applicationJson        = "application/json"
	applicationOctetStream = "application/octet-stream"
	plainText              = "text/plain"
	originHeader           = "origin"
)

const (
	ACAOrigin      = "Access-Control-Allow-Origin"
	ACAMethods     = "Access-Control-Allow-Methods"
	ACACredentials = "Access-Control-Allow-Credentials"
)

var mimeTypes = map[string]string{
	cmds.JSON: "application/json",
	cmds.XML:  "application/xml",
	cmds.Text: "text/plain",
}

type ServerConfig struct {
	// Headers is an optional map of headers that is written out.
	Headers map[string][]string

	// cORSOpts is a set of options for CORS headers.
	cORSOpts *cors.Options

	// cORSOptsRWMutex is a RWMutex for read/write CORSOpts
	cORSOptsRWMutex sync.RWMutex
}

func skipAPIHeader(h string) bool {
	switch h {
	case "Access-Control-Allow-Origin":
		return true
	case "Access-Control-Allow-Methods":
		return true
	case "Access-Control-Allow-Credentials":
		return true
	default:
		return false
	}
}

func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handler {
	if cfg == nil {
		panic("must provide a valid ServerConfig")
	}

	// Wrap the internal handler with CORS handling-middleware.
	// Create a handler for the API.
	internal := internalHandler{ctx, root, cfg}
	c := cors.New(*cfg.cORSOpts)
	return &Handler{internal, c.Handler(internal)}
}

func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Call the CORS handler which wraps the internal handler.
	i.corsHandler.ServeHTTP(w, r)
}

func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Debug("Incoming API request: ", r.URL)

	defer func() {
		if r := recover(); r != nil {
			log.Error(r)

			buf := make([]byte, 4096)
			n := runtime.Stack(buf, false)
			fmt.Fprintln(os.Stderr, string(buf[:n]))
		}
	}()

	if !allowOrigin(r, i.cfg) || !allowReferer(r, i.cfg) {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 - Forbidden"))
		log.Warningf("API blocked request to %s. (possible CSRF)", r.URL)
		return
	}

	req, err := Parse(r, i.root)
	if err != nil {
		if err == ErrNotFound {
			w.WriteHeader(http.StatusNotFound)
		} else {
			w.WriteHeader(http.StatusBadRequest)
		}
		w.Write([]byte(err.Error()))
		return
	}

	// get the node's context to pass into the commands.
	node, err := i.ctx.GetNode()
	if err != nil {
		s := fmt.Sprintf("cmds/http: couldn't GetNode(): %s", err)
		http.Error(w, s, http.StatusInternalServerError)
		return
	}

	//ps: take note of the name clash - commands.Context != context.Context
	req.SetInvocContext(i.ctx)

	ctx, cancel := context.WithCancel(node.Context())
	defer cancel()

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

	// call the command
	res := i.root.Call(req)

	// set user's headers first.
	for k, v := range i.cfg.Headers {
		if !skipAPIHeader(k) {
			w.Header()[k] = v
		}
	}

	// now handle responding to the client properly
	sendResponse(w, r, res, req)
}

func guessMimeType(res cmds.Response) (string, error) {
	// Try to guess mimeType from the encoding option
	enc, found, err := res.Request().Option(cmds.EncShort).String()
	if err != nil {
		return "", err
	}
	if !found {
		return "", errors.New("no encoding option set")
	}

	return mimeTypes[enc], nil
}

func sendResponse(w http.ResponseWriter, r *http.Request, res cmds.Response, req cmds.Request) {
	mime, err := guessMimeType(res)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	status := http.StatusOK
	// if response contains an error, write an HTTP error status code
	if e := res.Error(); e != nil {
		if e.Code == cmds.ErrClient {
			status = http.StatusBadRequest
		} else {
			status = http.StatusInternalServerError
		}
		// NOTE: The error will actually be written out by the reader below
	}

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

	h := w.Header()

	// Set up our potential trailer
	h.Set("Trailer", StreamErrHeader)

	if res.Length() > 0 {
		h.Set(contentLengthHeader, strconv.FormatUint(res.Length(), 10))
	}

	if _, ok := res.Output().(io.Reader); ok {
		// we don't set the Content-Type for streams, so that browsers can MIME-sniff the type themselves
		// we set this header so clients have a way to know this is an output stream
		// (not marshalled command output)
		mime = ""
		h.Set(streamHeader, "1")
	}

	// if output is a channel and user requested streaming channels,
	// use chunk copier for the output
	_, isChan := res.Output().(chan interface{})
	if !isChan {
		_, isChan = res.Output().(<-chan interface{})
	}

	if isChan {
		h.Set(channelHeader, "1")
	}

	if mime != "" {
		h.Set(contentTypeHeader, mime)
	}
	h.Set(transferEncodingHeader, "chunked")

	if r.Method == "HEAD" { // after all the headers.
		return
	}

	w.WriteHeader(status)
	err = flushCopy(w, out)
	if err != nil {
		log.Error("err: ", err)
		w.Header().Set(StreamErrHeader, sanitizedErrStr(err))
	}
}

func flushCopy(w io.Writer, r io.Reader) error {
	buf := make([]byte, 4096)
	f, ok := w.(http.Flusher)
	if !ok {
		_, err := io.Copy(w, r)
		return err
	}
	for {
		n, err := r.Read(buf)
		switch err {
		case io.EOF:
			return nil
		case nil:
			// continue
		default:
			return err
		}

		nw, err := w.Write(buf[:n])
		if err != nil {
			return err
		}

		if nw != n {
			return fmt.Errorf("http write failed to write full amount: %d != %d", nw, n)
		}

		f.Flush()
	}
	return nil
}

func sanitizedErrStr(err error) string {
	s := err.Error()
	s = strings.Split(s, "\n")[0]
	s = strings.Split(s, "\r")[0]
	return s
}

func NewServerConfig() *ServerConfig {
	cfg := new(ServerConfig)
	cfg.cORSOpts = new(cors.Options)
	return cfg
}

func (cfg ServerConfig) AllowedOrigins() []string {
	cfg.cORSOptsRWMutex.RLock()
	defer cfg.cORSOptsRWMutex.RUnlock()
	return cfg.cORSOpts.AllowedOrigins
}

func (cfg *ServerConfig) SetAllowedOrigins(origins ...string) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	cfg.cORSOpts.AllowedOrigins = origins
}

func (cfg *ServerConfig) AppendAllowedOrigins(origins ...string) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	cfg.cORSOpts.AllowedOrigins = append(cfg.cORSOpts.AllowedOrigins, origins...)
}

func (cfg ServerConfig) AllowedMethods() []string {
	cfg.cORSOptsRWMutex.RLock()
	defer cfg.cORSOptsRWMutex.RUnlock()
	return []string(cfg.cORSOpts.AllowedMethods)
}

func (cfg *ServerConfig) SetAllowedMethods(methods ...string) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	if cfg.cORSOpts == nil {
		cfg.cORSOpts = new(cors.Options)
	}
	cfg.cORSOpts.AllowedMethods = methods
}

func (cfg *ServerConfig) SetAllowCredentials(flag bool) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	cfg.cORSOpts.AllowCredentials = flag
}

// allowOrigin just stops the request if the origin is not allowed.
// the CORS middleware apparently does not do this for us...
func allowOrigin(r *http.Request, cfg *ServerConfig) bool {
	origin := r.Header.Get("Origin")

	// curl, or ipfs shell, typing it in manually, or clicking link
	// NOT in a browser. this opens up a hole. we should close it,
	// but right now it would break things. TODO
	if origin == "" {
		return true
	}
	origins := cfg.AllowedOrigins()
	for _, o := range origins {
		if o == "*" { // ok! you asked for it!
			return true
		}

		if o == origin { // allowed explicitly
			return true
		}
	}

	return false
}

// allowReferer this is here to prevent some CSRF attacks that
// the API would be vulnerable to. We check that the Referer
// is allowed by CORS Origin (origins and referrers here will
// work similarly in the normla uses of the API).
// See discussion at https://github.com/ipfs/go-ipfs/issues/1532
func allowReferer(r *http.Request, cfg *ServerConfig) bool {
	referer := r.Referer()

	// curl, or ipfs shell, typing it in manually, or clicking link
	// NOT in a browser. this opens up a hole. we should close it,
	// but right now it would break things. TODO
	if referer == "" {
		return true
	}

	u, err := url.Parse(referer)
	if err != nil {
		// bad referer. but there _is_ something, so bail.
		log.Debug("failed to parse referer: ", referer)
		// debug because referer comes straight from the client. dont want to
		// let people DOS by putting a huge referer that gets stored in log files.
		return false
	}
	origin := u.Scheme + "://" + u.Host

	// check CORS ACAOs and pretend Referer works like an origin.
	// this is valid for many (most?) sane uses of the API in
	// other applications, and will have the desired effect.
	origins := cfg.AllowedOrigins()
	for _, o := range origins {
		if o == "*" { // ok! you asked for it!
			return true
		}

		// referer is allowed explicitly
		if o == origin {
			return true
		}
	}

	return false
}
<commit_msg>content type on command responses default to text<commit_after>package http

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"

	cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"

	cmds "github.com/ipfs/go-ipfs/commands"
	logging "github.com/ipfs/go-ipfs/vendor/QmTBXYb6y2ZcJmoXVKk3pf9rzSEjbCg7tQaJW7RSuH14nv/go-log"
)

var log = logging.Logger("commands/http")

// the internal handler for the API
type internalHandler struct {
	ctx  cmds.Context
	root *cmds.Command
	cfg  *ServerConfig
}

// The Handler struct is funny because we want to wrap our internal handler
// with CORS while keeping our fields.
type Handler struct {
	internalHandler
	corsHandler http.Handler
}

var ErrNotFound = errors.New("404 page not found")

const (
	StreamErrHeader        = "X-Stream-Error"
	streamHeader           = "X-Stream-Output"
	channelHeader          = "X-Chunked-Output"
	uaHeader               = "User-Agent"
	contentTypeHeader      = "Content-Type"
	contentLengthHeader    = "Content-Length"
	contentDispHeader      = "Content-Disposition"
	transferEncodingHeader = "Transfer-Encoding"
	applicationJson        = "application/json"
	applicationOctetStream = "application/octet-stream"
	plainText              = "text/plain"
	originHeader           = "origin"
)

const (
	ACAOrigin      = "Access-Control-Allow-Origin"
	ACAMethods     = "Access-Control-Allow-Methods"
	ACACredentials = "Access-Control-Allow-Credentials"
)

var mimeTypes = map[string]string{
	cmds.JSON: "application/json",
	cmds.XML:  "application/xml",
	cmds.Text: "text/plain",
}

type ServerConfig struct {
	// Headers is an optional map of headers that is written out.
	Headers map[string][]string

	// cORSOpts is a set of options for CORS headers.
	cORSOpts *cors.Options

	// cORSOptsRWMutex is a RWMutex for read/write CORSOpts
	cORSOptsRWMutex sync.RWMutex
}

func skipAPIHeader(h string) bool {
	switch h {
	case "Access-Control-Allow-Origin":
		return true
	case "Access-Control-Allow-Methods":
		return true
	case "Access-Control-Allow-Credentials":
		return true
	default:
		return false
	}
}

func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handler {
	if cfg == nil {
		panic("must provide a valid ServerConfig")
	}

	// Wrap the internal handler with CORS handling-middleware.
	// Create a handler for the API.
	internal := internalHandler{ctx, root, cfg}
	c := cors.New(*cfg.cORSOpts)
	return &Handler{internal, c.Handler(internal)}
}

func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Call the CORS handler which wraps the internal handler.
	i.corsHandler.ServeHTTP(w, r)
}

func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Debug("Incoming API request: ", r.URL)

	defer func() {
		if r := recover(); r != nil {
			log.Error(r)

			buf := make([]byte, 4096)
			n := runtime.Stack(buf, false)
			fmt.Fprintln(os.Stderr, string(buf[:n]))
		}
	}()

	if !allowOrigin(r, i.cfg) || !allowReferer(r, i.cfg) {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 - Forbidden"))
		log.Warningf("API blocked request to %s. (possible CSRF)", r.URL)
		return
	}

	req, err := Parse(r, i.root)
	if err != nil {
		if err == ErrNotFound {
			w.WriteHeader(http.StatusNotFound)
		} else {
			w.WriteHeader(http.StatusBadRequest)
		}
		w.Write([]byte(err.Error()))
		return
	}

	// get the node's context to pass into the commands.
	node, err := i.ctx.GetNode()
	if err != nil {
		s := fmt.Sprintf("cmds/http: couldn't GetNode(): %s", err)
		http.Error(w, s, http.StatusInternalServerError)
		return
	}

	//ps: take note of the name clash - commands.Context != context.Context
	req.SetInvocContext(i.ctx)

	ctx, cancel := context.WithCancel(node.Context())
	defer cancel()

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

	// call the command
	res := i.root.Call(req)

	// set user's headers first.
	for k, v := range i.cfg.Headers {
		if !skipAPIHeader(k) {
			w.Header()[k] = v
		}
	}

	// now handle responding to the client properly
	sendResponse(w, r, res, req)
}

func guessMimeType(res cmds.Response) (string, error) {
	// Try to guess mimeType from the encoding option
	enc, found, err := res.Request().Option(cmds.EncShort).String()
	if err != nil {
		return "", err
	}
	if !found {
		return "", errors.New("no encoding option set")
	}

	if m, ok := mimeTypes[enc]; ok {
		return m, nil
	}

	return mimeTypes[cmds.JSON], nil
}

func sendResponse(w http.ResponseWriter, r *http.Request, res cmds.Response, req cmds.Request) {
	mime, err := guessMimeType(res)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	status := http.StatusOK
	// if response contains an error, write an HTTP error status code
	if e := res.Error(); e != nil {
		if e.Code == cmds.ErrClient {
			status = http.StatusBadRequest
		} else {
			status = http.StatusInternalServerError
		}
		// NOTE: The error will actually be written out by the reader below
	}

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

	h := w.Header()

	// Set up our potential trailer
	h.Set("Trailer", StreamErrHeader)

	if res.Length() > 0 {
		h.Set(contentLengthHeader, strconv.FormatUint(res.Length(), 10))
	}

	if _, ok := res.Output().(io.Reader); ok {
		// set streams output type to text to avoid issues with browsers rendering
		// html pages on priveleged api ports
		mime = "text/plain"
		h.Set(streamHeader, "1")
	}

	// if output is a channel and user requested streaming channels,
	// use chunk copier for the output
	_, isChan := res.Output().(chan interface{})
	if !isChan {
		_, isChan = res.Output().(<-chan interface{})
	}

	if isChan {
		h.Set(channelHeader, "1")
	}

	// catch-all, set to text as default
	if mime == "" {
		mime = "text/plain"
	}

	h.Set(contentTypeHeader, mime)
	h.Set(transferEncodingHeader, "chunked")

	if r.Method == "HEAD" { // after all the headers.
		return
	}

	w.WriteHeader(status)
	err = flushCopy(w, out)
	if err != nil {
		log.Error("err: ", err)
		w.Header().Set(StreamErrHeader, sanitizedErrStr(err))
	}
}

func flushCopy(w io.Writer, r io.Reader) error {
	buf := make([]byte, 4096)
	f, ok := w.(http.Flusher)
	if !ok {
		_, err := io.Copy(w, r)
		return err
	}
	for {
		n, err := r.Read(buf)
		switch err {
		case io.EOF:
			return nil
		case nil:
			// continue
		default:
			return err
		}

		nw, err := w.Write(buf[:n])
		if err != nil {
			return err
		}

		if nw != n {
			return fmt.Errorf("http write failed to write full amount: %d != %d", nw, n)
		}

		f.Flush()
	}
	return nil
}

func sanitizedErrStr(err error) string {
	s := err.Error()
	s = strings.Split(s, "\n")[0]
	s = strings.Split(s, "\r")[0]
	return s
}

func NewServerConfig() *ServerConfig {
	cfg := new(ServerConfig)
	cfg.cORSOpts = new(cors.Options)
	return cfg
}

func (cfg ServerConfig) AllowedOrigins() []string {
	cfg.cORSOptsRWMutex.RLock()
	defer cfg.cORSOptsRWMutex.RUnlock()
	return cfg.cORSOpts.AllowedOrigins
}

func (cfg *ServerConfig) SetAllowedOrigins(origins ...string) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	cfg.cORSOpts.AllowedOrigins = origins
}

func (cfg *ServerConfig) AppendAllowedOrigins(origins ...string) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	cfg.cORSOpts.AllowedOrigins = append(cfg.cORSOpts.AllowedOrigins, origins...)
}

func (cfg ServerConfig) AllowedMethods() []string {
	cfg.cORSOptsRWMutex.RLock()
	defer cfg.cORSOptsRWMutex.RUnlock()
	return []string(cfg.cORSOpts.AllowedMethods)
}

func (cfg *ServerConfig) SetAllowedMethods(methods ...string) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	if cfg.cORSOpts == nil {
		cfg.cORSOpts = new(cors.Options)
	}
	cfg.cORSOpts.AllowedMethods = methods
}

func (cfg *ServerConfig) SetAllowCredentials(flag bool) {
	cfg.cORSOptsRWMutex.Lock()
	defer cfg.cORSOptsRWMutex.Unlock()
	cfg.cORSOpts.AllowCredentials = flag
}

// allowOrigin just stops the request if the origin is not allowed.
// the CORS middleware apparently does not do this for us...
func allowOrigin(r *http.Request, cfg *ServerConfig) bool {
	origin := r.Header.Get("Origin")

	// curl, or ipfs shell, typing it in manually, or clicking link
	// NOT in a browser. this opens up a hole. we should close it,
	// but right now it would break things. TODO
	if origin == "" {
		return true
	}
	origins := cfg.AllowedOrigins()
	for _, o := range origins {
		if o == "*" { // ok! you asked for it!
			return true
		}

		if o == origin { // allowed explicitly
			return true
		}
	}

	return false
}

// allowReferer this is here to prevent some CSRF attacks that
// the API would be vulnerable to. We check that the Referer
// is allowed by CORS Origin (origins and referrers here will
// work similarly in the normla uses of the API).
// See discussion at https://github.com/ipfs/go-ipfs/issues/1532
func allowReferer(r *http.Request, cfg *ServerConfig) bool {
	referer := r.Referer()

	// curl, or ipfs shell, typing it in manually, or clicking link
	// NOT in a browser. this opens up a hole. we should close it,
	// but right now it would break things. TODO
	if referer == "" {
		return true
	}

	u, err := url.Parse(referer)
	if err != nil {
		// bad referer. but there _is_ something, so bail.
		log.Debug("failed to parse referer: ", referer)
		// debug because referer comes straight from the client. dont want to
		// let people DOS by putting a huge referer that gets stored in log files.
		return false
	}
	origin := u.Scheme + "://" + u.Host

	// check CORS ACAOs and pretend Referer works like an origin.
	// this is valid for many (most?) sane uses of the API in
	// other applications, and will have the desired effect.
	origins := cfg.AllowedOrigins()
	for _, o := range origins {
		if o == "*" { // ok! you asked for it!
			return true
		}

		// referer is allowed explicitly
		if o == origin {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/mislav/everyenv/cli"
)

var versionNameHelp = `
Usage: $ProgramName version-name

Show the current version
`

func versionNameCmd(args cli.Args) {
	currentVersion := detectVersion()
	cli.Println(currentVersion.Name)
}

func init() {
	cli.Register("version-name", versionNameCmd, versionNameHelp)
}
<commit_msg>Have `version-name` command abort on invalid versions<commit_after>package commands

import (
	"github.com/mislav/everyenv/cli"
	"github.com/mislav/everyenv/config"
)

var versionNameHelp = `
Usage: $ProgramName version-name

Show the current version
`

func versionNameCmd(args cli.Args) {
	currentVersion := detectVersion()

	if !currentVersion.IsSystem() {
		versionDir := config.VersionDir(currentVersion.Name)
		if !versionDir.Exists() {
			err := VersionNotFound{currentVersion.Name}
			cli.Errorf("%s: %s\n", args.ProgramName(), err)
			cli.Exit(1)
		}
	}

	cli.Println(currentVersion.Name)
}

func init() {
	cli.Register("version-name", versionNameCmd, versionNameHelp)
}
<|endoftext|>


<commit_before>package request

import (
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"time"

	. "github.com/tj/go-debug"
)

var debug = Debug("request")

type Client struct {
	httpClient *http.Client
}

func New() *Client {
	var cookie, _ = cookiejar.New(nil)

	client := &http.Client{
		Timeout: time.Second * 30,
		Jar:     cookie,
	}

	debug("#New")

	return &Client{client}
}

type Data map[string][]string
type Header map[string]string

type Option struct {
	Url     string
	Method  string
	BodyStr string
	Body    *Data
	Form    *Data
	Query   *Data
	Header  *Header
}

func (c *Client) Request(opt *Option) (body string, res *http.Response, err error) {
	debug("#Request")

	//set GET as default method
	if opt.Method == "" {
		opt.Method = "GET"
	}

	opt.Method = strings.ToUpper(opt.Method)

	//url
	reqUrl, err := makeUrl(opt.Url, opt.Query)

	if err != nil {
		return
	}

	//body
	reqBody := makeBody(opt)

	req, err := http.NewRequest(opt.Method, reqUrl.String(), strings.NewReader(reqBody))

	if err != nil {
		debug("#Request ERR(req) %v", err)
		return
	}

	//header
	makeHeader(req, opt.Header)

	res, err = c.httpClient.Do(req)

	if err != nil {
		debug("#Request ERR(http) %v", err)
		return
	}

	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)

	if err != nil {
		debug("#Request ERR(ioutil) %v", err)
		return
	}

	body = string(resBody)

	debug("#Request %v", res.Status)
	return
}

func makeUrl(urlStr string, query *Data) (u *url.URL, err error) {
	// debug("#makeUrl")

	u, err = url.Parse(urlStr)

	if err != nil {
		debug("#makeUrl ERR: %v", err)
		return
	}

	if query == nil {
		return
	}

	qs := u.Query()

	for key, slice := range *query {
		for _, value := range slice {
			qs.Add(key, value)
		}
	}

	u.RawQuery = qs.Encode()
	return
}

func makeBody(opt *Option) (body string) {
	var data *Data

	switch {
	case opt.BodyStr != "":
		body = opt.BodyStr
		return

	case opt.Form != nil:
		data = opt.Form

	case opt.Body != nil:
		data = opt.Body

	default:
		return
	}

	values := url.Values{}

	for key, slice := range *data {
		for _, value := range slice {
			values.Add(key, value)
		}
	}

	body = values.Encode()
	return
}

func makeHeader(req *http.Request, header *Header) {
	// debug("#makeHeader")

	//default User-Agent
	req.Header.Set("User-Agent", "github.com/ddo/request")

	if header == nil {
		return
	}

	for key, value := range *header {
		req.Header.Set(key, value)
	}
}
<commit_msg>update #makeHeader<commit_after>package request

import (
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"time"

	. "github.com/tj/go-debug"
)

var debug = Debug("request")

type Client struct {
	httpClient *http.Client
}

func New() *Client {
	var cookie, _ = cookiejar.New(nil)

	client := &http.Client{
		Timeout: time.Second * 30,
		Jar:     cookie,
	}

	debug("#New")

	return &Client{client}
}

type Data map[string][]string
type Header map[string]string

type Option struct {
	Url     string
	Method  string
	BodyStr string
	Body    *Data
	Form    *Data
	Query   *Data
	Header  *Header
}

func (c *Client) Request(opt *Option) (body string, res *http.Response, err error) {
	debug("#Request")

	//set GET as default method
	if opt.Method == "" {
		opt.Method = "GET"
	}

	opt.Method = strings.ToUpper(opt.Method)

	//url
	reqUrl, err := makeUrl(opt.Url, opt.Query)

	if err != nil {
		return
	}

	//body
	reqBody := makeBody(opt)

	req, err := http.NewRequest(opt.Method, reqUrl.String(), strings.NewReader(reqBody))

	if err != nil {
		debug("#Request ERR(req) %v", err)
		return
	}

	//header
	makeHeader(req, opt)

	res, err = c.httpClient.Do(req)

	if err != nil {
		debug("#Request ERR(http) %v", err)
		return
	}

	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)

	if err != nil {
		debug("#Request ERR(ioutil) %v", err)
		return
	}

	body = string(resBody)

	debug("#Request %v", res.Status)
	return
}

func makeUrl(urlStr string, query *Data) (u *url.URL, err error) {
	// debug("#makeUrl")

	u, err = url.Parse(urlStr)

	if err != nil {
		debug("#makeUrl ERR: %v", err)
		return
	}

	if query == nil {
		return
	}

	qs := u.Query()

	for key, slice := range *query {
		for _, value := range slice {
			qs.Add(key, value)
		}
	}

	u.RawQuery = qs.Encode()
	return
}

func makeBody(opt *Option) (body string) {
	var data *Data

	switch {
	case opt.BodyStr != "":
		body = opt.BodyStr
		return

	case opt.Form != nil:
		data = opt.Form

	case opt.Body != nil:
		data = opt.Body

	default:
		return
	}

	values := url.Values{}

	for key, slice := range *data {
		for _, value := range slice {
			values.Add(key, value)
		}
	}

	body = values.Encode()
	return
}

func makeHeader(req *http.Request, opt *Option) {
	//default User-Agent
	req.Header.Set("User-Agent", "github.com/ddo/request")

	switch {
	//set Content-Type header if form
	case opt.Form != nil:
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	}

	if opt.Header == nil {
		return
	}

	for key, value := range *opt.Header {
		req.Header.Set(key, value)
	}
}
<|endoftext|>


<commit_before>package web

import (
    "bytes"
    "container/vector"
    "fmt"
    "http"
    "io"
    "io/ioutil"
    "os"
    "strings"
)

type Request struct {
    Method     string    // GET, POST, PUT, etc.
    RawURL     string    // The raw URL given in the request.
    URL        *http.URL // Parsed URL.
    Proto      string    // "HTTP/1.0"
    ProtoMajor int       // 1
    ProtoMinor int       // 0
    Headers    map[string]string
    Body       io.Reader
    Close      bool
    Host       string
    Referer    string
    UserAgent  string
    Params     map[string][]string
    Cookies    map[string]string
    Files      map[string][]byte
}

type badStringError struct {
    what string
    str  string
}

func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e.str) }

func newRequest(hr *http.Request) *Request {
    req := Request{
        Method: hr.Method,
        RawURL: hr.RawURL,
        URL: hr.URL,
        Proto: hr.Proto,
        ProtoMajor: hr.ProtoMajor,
        ProtoMinor: hr.ProtoMinor,
        Headers: hr.Header,
        Body: hr.Body,
        Close: hr.Close,
        Host: hr.Host,
        Referer: hr.Referer,
        UserAgent: hr.UserAgent,
        Params: hr.Form,
    }
    return &req
}

func newRequestCgi(headers map[string]string, body io.Reader) *Request {

    var httpheader = make(map[string]string)

    method, _ := headers["REQUEST_METHOD"]
    host, _ := headers["HTTP_HOST"]
    path, _ := headers["REQUEST_URI"]
    port, _ := headers["SERVER_PORT"]
    proto, _ := headers["SERVER_PROTOCOL"]
    rawurl := "http://" + host + ":" + port + path
    url, _ := http.ParseURL(rawurl)
    useragent, _ := headers["USER_AGENT"]

    if method == "POST" {
        if ctype, ok := headers["CONTENT_TYPE"]; ok {
            httpheader["Content-Type"] = ctype
        }

        if clength, ok := headers["CONTENT_LENGTH"]; ok {
            httpheader["Content-Length"] = clength
        }
    }

    req := Request{
        Method: method,
        RawURL: rawurl,
        URL: url,
        Proto: proto,
        Host: host,
        UserAgent: useragent,
        Body: body,
        Headers: httpheader,
    }

    return &req
}

func parseForm(m map[string][]string, query string) (err os.Error) {
    data := make(map[string]*vector.StringVector)
    for _, kv := range strings.Split(query, "&", 0) {
        kvPair := strings.Split(kv, "=", 2)

        var key, value string
        var e os.Error
        key, e = http.URLUnescape(kvPair[0])
        if e == nil && len(kvPair) > 1 {
            value, e = http.URLUnescape(kvPair[1])
        }
        if e != nil {
            err = e
        }

        vec, ok := data[key]
        if !ok {
            vec = new(vector.StringVector)
            data[key] = vec
        }
        vec.Push(value)
    }

    for k, vec := range data {
        m[k] = vec.Data()
    }

    return
}

// ParseForm parses the request body as a form for POST requests, or the raw query for GET requests.
// It is idempotent.
func (r *Request) ParseParams() (err os.Error) {
    if r.Params != nil {
        return
    }
    r.Params = make(map[string][]string)

    var query string
    switch r.Method {
    case "GET":
        query = r.URL.RawQuery
    case "POST":
        if r.Body == nil {
            return os.ErrorString("missing form body")
        }
        ct, _ := r.Headers["Content-Type"]
        switch strings.Split(ct, ";", 2)[0] {
        case "text/plain", "application/x-www-form-urlencoded", "":
            var b []byte
            if b, err = ioutil.ReadAll(r.Body); err != nil {
                return err
            }
            query = string(b)
        case "multipart/form-data":
            r.Files = make(map[string][]byte)
            boundary := strings.Split(ct, "boundary=", 2)[1]
            var b []byte
            if b, err = ioutil.ReadAll(r.Body); err != nil {
                return err
            }
            parts := bytes.Split(b, strings.Bytes("--"+boundary+"\r\n"), 0)
            for _, data := range (parts) {
                if len(data) == 0 {
                    continue
                }
                var line []byte
                var rest = data
                headers := map[string]string{}
                isfile := false
                var name string
                for {
                    res := bytes.Split(rest, []byte{'\r', '\n'}, 2)
                    if len(res) != 2 {
                        break
                    }
                    line = res[0]
                    rest = res[1]
                    if len(line) == 0 {
                        break
                    }

                    header := strings.Split(string(line), ":", 2)
                    n := strings.TrimSpace(header[0])
                    v := strings.TrimSpace(header[1])
                    if n == "Content-Disposition" {
                        parts := strings.Split(v, ";", 0)
                        for _, parm := range (parts[1:]) {
                            pp := strings.Split(parm, "=", 2)
                            pn := strings.TrimSpace(pp[0])
                            pv := strings.TrimSpace(pp[1])
                            if pn == "name" {
                                name = pv[1 : len(pv)-1]
                            } else if pn == "filename" {
                                isfile = true
                            }
                        }
                    }

                    headers[n] = v
                }
                if isfile {
                    parts = bytes.Split(rest, strings.Bytes("\r\n--"+boundary+"--\r\n"), 0)
                    r.Files[name] = parts[0]
                }
            }
        default:
            return &badStringError{"unknown Content-Type", ct}
        }
    }
    return parseForm(r.Params, query)
}

func (r *Request) ParseCookies() (err os.Error) {
    if r.Cookies != nil {
        return
    }

    r.Cookies = make(map[string]string)

    for k, v := range (r.Headers) {
        if k == "Cookie" {
            cookies := strings.Split(v, ";", 0)
            for _, cookie := range (cookies) {
                cookie = strings.TrimSpace(cookie)
                parts := strings.Split(cookie, "=", 0)
                r.Cookies[parts[0]] = parts[1]
            }
        }
    }

    return nil
}

func (r *Request) HasParam(name string) bool {
    if r.Params == nil || len(r.Params) == 0 {
        return false
    }
    _, ok := r.Params[name]
    return ok
}
func (r *Request) HasFile(name string) bool {
    if r.Files == nil || len(r.Files) == 0 {
        return false
    }
    _, ok := r.Files[name]
    return ok
}
<commit_msg>Handle non-file fields in multipart form data<commit_after>package web

import (
    "bytes"
    "container/vector"
    "fmt"
    "http"
    "io"
    "io/ioutil"
    "os"
    "strings"
)

type Request struct {
    Method     string    // GET, POST, PUT, etc.
    RawURL     string    // The raw URL given in the request.
    URL        *http.URL // Parsed URL.
    Proto      string    // "HTTP/1.0"
    ProtoMajor int       // 1
    ProtoMinor int       // 0
    Headers    map[string]string
    Body       io.Reader
    Close      bool
    Host       string
    Referer    string
    UserAgent  string
    Params     map[string][]string
    Cookies    map[string]string
    Files      map[string][]byte
}

type badStringError struct {
    what string
    str  string
}

func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e.str) }

func newRequest(hr *http.Request) *Request {
    req := Request{
        Method: hr.Method,
        RawURL: hr.RawURL,
        URL: hr.URL,
        Proto: hr.Proto,
        ProtoMajor: hr.ProtoMajor,
        ProtoMinor: hr.ProtoMinor,
        Headers: hr.Header,
        Body: hr.Body,
        Close: hr.Close,
        Host: hr.Host,
        Referer: hr.Referer,
        UserAgent: hr.UserAgent,
        Params: hr.Form,
    }
    return &req
}

func newRequestCgi(headers map[string]string, body io.Reader) *Request {

    var httpheader = make(map[string]string)

    method, _ := headers["REQUEST_METHOD"]
    host, _ := headers["HTTP_HOST"]
    path, _ := headers["REQUEST_URI"]
    port, _ := headers["SERVER_PORT"]
    proto, _ := headers["SERVER_PROTOCOL"]
    rawurl := "http://" + host + ":" + port + path
    url, _ := http.ParseURL(rawurl)
    useragent, _ := headers["USER_AGENT"]

    if method == "POST" {
        if ctype, ok := headers["CONTENT_TYPE"]; ok {
            httpheader["Content-Type"] = ctype
        }

        if clength, ok := headers["CONTENT_LENGTH"]; ok {
            httpheader["Content-Length"] = clength
        }
    }

    req := Request{
        Method: method,
        RawURL: rawurl,
        URL: url,
        Proto: proto,
        Host: host,
        UserAgent: useragent,
        Body: body,
        Headers: httpheader,
    }

    return &req
}

func parseForm(m map[string][]string, query string) (err os.Error) {
    data := make(map[string]*vector.StringVector)
    for _, kv := range strings.Split(query, "&", 0) {
        kvPair := strings.Split(kv, "=", 2)

        var key, value string
        var e os.Error
        key, e = http.URLUnescape(kvPair[0])
        if e == nil && len(kvPair) > 1 {
            value, e = http.URLUnescape(kvPair[1])
        }
        if e != nil {
            err = e
        }

        vec, ok := data[key]
        if !ok {
            vec = new(vector.StringVector)
            data[key] = vec
        }
        vec.Push(value)
    }

    for k, vec := range data {
        m[k] = vec.Data()
    }

    return
}

// ParseForm parses the request body as a form for POST requests, or the raw query for GET requests.
// It is idempotent.
func (r *Request) ParseParams() (err os.Error) {
    if r.Params != nil {
        return
    }
    r.Params = make(map[string][]string)

    var query string
    switch r.Method {
    case "GET":
        query = r.URL.RawQuery
    case "POST":
        if r.Body == nil {
            return os.ErrorString("missing form body")
        }
        ct, _ := r.Headers["Content-Type"]
        switch strings.Split(ct, ";", 2)[0] {
        case "text/plain", "application/x-www-form-urlencoded", "":
            var b []byte
            if b, err = ioutil.ReadAll(r.Body); err != nil {
                return err
            }
            query = string(b)
        case "multipart/form-data":
            r.Files = make(map[string][]byte)
            boundary := strings.Split(ct, "boundary=", 2)[1]
            var b []byte
            if b, err = ioutil.ReadAll(r.Body); err != nil {
                return err
            }
            parts := bytes.Split(b, strings.Bytes("--"+boundary+"\r\n"), 0)
            for _, data := range (parts) {
                if len(data) == 0 {
                    continue
                }
                var line []byte
                var rest = data
                headers := map[string]string{}
                isfile := false
                var name string
                for {
                    res := bytes.Split(rest, []byte{'\r', '\n'}, 2)
                    if len(res) != 2 {
                        break
                    }
                    line = res[0]
                    rest = res[1]
                    if len(line) == 0 {
                        break
                    }

                    header := strings.Split(string(line), ":", 2)
                    n := strings.TrimSpace(header[0])
                    v := strings.TrimSpace(header[1])
                    if n == "Content-Disposition" {
                        parts := strings.Split(v, ";", 0)
                        for _, parm := range (parts[1:]) {
                            pp := strings.Split(parm, "=", 2)
                            pn := strings.TrimSpace(pp[0])
                            pv := strings.TrimSpace(pp[1])
                            if pn == "name" {
                                name = pv[1 : len(pv)-1]
                            } else if pn == "filename" {
                                isfile = true
                            }
                        }
                    }

                    headers[n] = v
                }
                if isfile {
                    parts = bytes.Split(rest, strings.Bytes("\r\n--"+boundary+"--\r\n"), 0)
                    r.Files[name] = parts[0]
                } else {
                    _, ok := r.Params[name]
                    if !ok {
                        r.Params[name] = []string{}
                    }
                    curlen := len(r.Params[name])
                    newlst := make([]string, curlen+1)
                    copy(newlst, r.Params[name])
                    newlst[curlen] = string(rest)
                    r.Params[name] = newlst
                }
            }
        default:
            return &badStringError{"unknown Content-Type", ct}
        }
    }
    return parseForm(r.Params, query)
}

func (r *Request) ParseCookies() (err os.Error) {
    if r.Cookies != nil {
        return
    }

    r.Cookies = make(map[string]string)

    for k, v := range (r.Headers) {
        if k == "Cookie" {
            cookies := strings.Split(v, ";", 0)
            for _, cookie := range (cookies) {
                cookie = strings.TrimSpace(cookie)
                parts := strings.Split(cookie, "=", 0)
                r.Cookies[parts[0]] = parts[1]
            }
        }
    }

    return nil
}

func (r *Request) HasParam(name string) bool {
    if r.Params == nil || len(r.Params) == 0 {
        return false
    }
    _, ok := r.Params[name]
    return ok
}
func (r *Request) HasFile(name string) bool {
    if r.Files == nil || len(r.Files) == 0 {
        return false
    }
    _, ok := r.Files[name]
    return ok
}
<|endoftext|>