<commit_before>// Copyright 2013 Martini Authors
// Copyright 2014 Unknown
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package macaron

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/Unknwon/macaron/bpool"
)

const (
	ContentType    = "Content-Type"
	ContentLength  = "Content-Length"
	ContentBinary  = "application/octet-stream"
	ContentJSON    = "application/json"
	ContentHTML    = "text/html"
	ContentXHTML   = "application/xhtml+xml"
	ContentXML     = "text/xml"
	defaultCharset = "UTF-8"
)

// Provides a temporary buffer to execute templates into and catch errors.
var bufpool *bpool.BufferPool

// Included helper functions for use when rendering html
var helperFuncs = template.FuncMap{
	"yield": func() (string, error) {
		return "", fmt.Errorf("yield called with no layout defined")
	},
	"current": func() (string, error) {
		return "", nil
	},
}

// Delims represents a set of Left and Right delimiters for HTML template rendering
type Delims struct {
	// Left delimiter, defaults to {{
	Left string
	// Right delimiter, defaults to }}
	Right string
}

// RenderOptions represents a struct for specifying configuration options for the Render middleware.
type RenderOptions struct {
	// Directory to load templates. Default is "templates"
	Directory string
	// Layout template name. Will not render a layout if "". Defaults to "".
	Layout string
	// Extensions to parse template files from. Defaults to [".tmpl", ".html"]
	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
	// Delims sets the action delimiters to the specified strings in the Delims struct.
	Delims Delims
	// Appends the given charset to the Content-Type header. Default is "UTF-8".
	Charset string
	// Outputs human readable JSON
	IndentJSON bool
	// Outputs human readable XML
	IndentXML bool
	// Prefixes the JSON output with the given bytes.
	PrefixJSON []byte
	// Prefixes the XML output with the given bytes.
	PrefixXML []byte
	// Allows changing of output to XHTML instead of HTML. Default is "text/html"
	HTMLContentType string
}

// HTMLOptions is a struct for overriding some rendering Options for specific HTML call
type HTMLOptions struct {
	// Layout template name. Overrides Options.Layout.
	Layout string
}

type Render interface {
	http.ResponseWriter
	RW() http.ResponseWriter

	JSON(int, interface{})
	JSONString(interface{}) (string, error)
	RawData(int, []byte)
	HTML(int, string, interface{}, ...HTMLOptions)
	HTMLString(string, interface{}, ...HTMLOptions) (string, error)
	XML(int, interface{})
	Error(int, ...string)
	Status(int)
	Redirect(string, ...int)
}

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

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

	return opt
}

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

	return "; charset=" + defaultCharset
}

func getExt(s string) string {
	if strings.Index(s, ".") == -1 {
		return ""
	}
	return "." + strings.Join(strings.Split(s, ".")[1:], ".")
}

func compile(options RenderOptions) *template.Template {
	dir := options.Directory
	t := template.New(dir)
	t.Delims(options.Delims.Left, options.Delims.Right)
	// parse an initial template in case we don't have any
	template.Must(t.Parse("Macaron"))

	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		r, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}

		ext := getExt(r)

		for _, extension := range options.Extensions {
			if ext == extension {

				buf, err := ioutil.ReadFile(path)
				if err != nil {
					panic(err)
				}

				name := (r[0 : len(r)-len(ext)])
				tmpl := t.New(filepath.ToSlash(name))

				// add our funcmaps
				for _, funcs := range options.Funcs {
					tmpl.Funcs(funcs)
				}

				// Bomb out if parse fails. We don't want any silent server starts.
				template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf)))
				break
			}
		}

		return nil
	})

	return t
}

// Renderer is a Middleware that maps a macaron.Render service into the Macaron handler chain.
// An single variadic macaron.RenderOptions struct can be optionally provided to configure
// HTML rendering. The default directory for templates is "templates" and the default
// file extension is ".tmpl" and ".html".
//
// If MACARON_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the
// MACARON_ENV environment variable to "production".
func Renderer(options ...RenderOptions) Handler {
	opt := prepareOptions(options)
	cs := prepareCharset(opt.Charset)
	t := compile(opt)
	bufpool = bpool.NewBufferPool(64)
	return func(ctx *Context, rw http.ResponseWriter, req *http.Request) {
		var tc *template.Template
		if Env == DEV {
			// recompile for easy development
			tc = compile(opt)
		} else {
			// use a clone of the initial template
			tc, _ = t.Clone()
		}
		r := &TplRender{
			ResponseWriter:  rw,
			req:             req,
			t:               tc,
			Opt:             opt,
			CompiledCharset: cs,
		}
		ctx.Data["TmplLoadTimes"] = func() string {
			if r.startTime.IsZero() {
				return ""
			}
			return fmt.Sprint(time.Since(r.startTime).Nanoseconds()/1e6) + "ms"
		}

		ctx.Render = r
		ctx.MapTo(r, (*Render)(nil))
	}
}

type TplRender struct {
	http.ResponseWriter
	req             *http.Request
	t               *template.Template
	Opt             RenderOptions
	CompiledCharset string

	startTime time.Time
}

func (r *TplRender) RW() http.ResponseWriter {
	return r.ResponseWriter
}

func (r *TplRender) JSON(status int, v interface{}) {
	var result []byte
	var err error
	if r.Opt.IndentJSON {
		result, err = json.MarshalIndent(v, "", "  ")
	} else {
		result, err = json.Marshal(v)
	}
	if err != nil {
		http.Error(r, err.Error(), 500)
		return
	}

	// json rendered fine, write out the result
	r.Header().Set(ContentType, ContentJSON+r.CompiledCharset)
	r.WriteHeader(status)
	if len(r.Opt.PrefixJSON) > 0 {
		r.Write(r.Opt.PrefixJSON)
	}
	r.Write(result)
}

func (r *TplRender) JSONString(v interface{}) (string, error) {
	var result []byte
	var err error
	if r.Opt.IndentJSON {
		result, err = json.MarshalIndent(v, "", "  ")
	} else {
		result, err = json.Marshal(v)
	}
	if err != nil {
		return "", err
	}
	return string(result), nil
}

func (r *TplRender) RawData(status int, v []byte) {
	if r.Header().Get(ContentType) == "" {
		r.Header().Set(ContentType, ContentBinary)
	}
	r.WriteHeader(status)
	r.Write(v)
}

func (r *TplRender) renderBytes(name string, binding interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) {
	opt := r.prepareHTMLOptions(htmlOpt)

	if len(opt.Layout) > 0 {
		r.addYield(name, binding)
		name = opt.Layout
	}

	out, err := r.execute(name, binding)
	if err != nil {
		return nil, err
	}

	return out, nil
}

func (r *TplRender) HTML(status int, name string, binding interface{}, htmlOpt ...HTMLOptions) {
	r.startTime = time.Now()

	out, err := r.renderBytes(name, binding, htmlOpt...)
	if err != nil {
		http.Error(r, err.Error(), http.StatusInternalServerError)
		return
	}

	r.Header().Set(ContentType, r.Opt.HTMLContentType+r.CompiledCharset)
	r.WriteHeader(status)
	io.Copy(r, out)
	bufpool.Put(out)
}

func (r *TplRender) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOptions) (string, error) {
	if out, err := r.renderBytes(name, binding, htmlOpt...); err != nil {
		return "", err
	} else {
		return out.String(), nil
	}
}

func (r *TplRender) XML(status int, v interface{}) {
	var result []byte
	var err error
	if r.Opt.IndentXML {
		result, err = xml.MarshalIndent(v, "", "  ")
	} else {
		result, err = xml.Marshal(v)
	}
	if err != nil {
		http.Error(r, err.Error(), 500)
		return
	}

	// XML rendered fine, write out the result
	r.Header().Set(ContentType, ContentXML+r.CompiledCharset)
	r.WriteHeader(status)
	if len(r.Opt.PrefixXML) > 0 {
		r.Write(r.Opt.PrefixXML)
	}
	r.Write(result)
}

// Error writes the given HTTP status to the current ResponseWriter
func (r *TplRender) Error(status int, message ...string) {
	r.WriteHeader(status)
	if len(message) > 0 {
		r.Write([]byte(message[0]))
	}
}

func (r *TplRender) Status(status int) {
	r.WriteHeader(status)
}

func (r *TplRender) Redirect(location string, status ...int) {
	code := http.StatusFound
	if len(status) == 1 {
		code = status[0]
	}

	http.Redirect(r, r.req, location, code)
}

func (r *TplRender) execute(name string, binding interface{}) (*bytes.Buffer, error) {
	buf := bufpool.Get()
	return buf, r.t.ExecuteTemplate(buf, name, binding)
}

func (r *TplRender) addYield(name string, binding interface{}) {
	funcs := template.FuncMap{
		"yield": func() (template.HTML, error) {
			buf, err := r.execute(name, binding)
			// return safe html here since we are rendering our own template
			return template.HTML(buf.String()), err
		},
		"current": func() (string, error) {
			return name, nil
		},
	}
	r.t.Funcs(funcs)
}

func (r *TplRender) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions {
	if len(htmlOpt) > 0 {
		return htmlOpt[0]
	}

	return HTMLOptions{
		Layout: r.Opt.Layout,
	}
}
<commit_msg>Fix redirect issue<commit_after>// Copyright 2013 Martini Authors
// Copyright 2014 Unknown
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package macaron

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/Unknwon/macaron/bpool"
)

const (
	ContentType    = "Content-Type"
	ContentLength  = "Content-Length"
	ContentBinary  = "application/octet-stream"
	ContentJSON    = "application/json"
	ContentHTML    = "text/html"
	ContentXHTML   = "application/xhtml+xml"
	ContentXML     = "text/xml"
	defaultCharset = "UTF-8"
)

// Provides a temporary buffer to execute templates into and catch errors.
var bufpool *bpool.BufferPool

// Included helper functions for use when rendering html
var helperFuncs = template.FuncMap{
	"yield": func() (string, error) {
		return "", fmt.Errorf("yield called with no layout defined")
	},
	"current": func() (string, error) {
		return "", nil
	},
}

// Delims represents a set of Left and Right delimiters for HTML template rendering
type Delims struct {
	// Left delimiter, defaults to {{
	Left string
	// Right delimiter, defaults to }}
	Right string
}

// RenderOptions represents a struct for specifying configuration options for the Render middleware.
type RenderOptions struct {
	// Directory to load templates. Default is "templates"
	Directory string
	// Layout template name. Will not render a layout if "". Defaults to "".
	Layout string
	// Extensions to parse template files from. Defaults to [".tmpl", ".html"]
	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
	// Delims sets the action delimiters to the specified strings in the Delims struct.
	Delims Delims
	// Appends the given charset to the Content-Type header. Default is "UTF-8".
	Charset string
	// Outputs human readable JSON
	IndentJSON bool
	// Outputs human readable XML
	IndentXML bool
	// Prefixes the JSON output with the given bytes.
	PrefixJSON []byte
	// Prefixes the XML output with the given bytes.
	PrefixXML []byte
	// Allows changing of output to XHTML instead of HTML. Default is "text/html"
	HTMLContentType string
}

// HTMLOptions is a struct for overriding some rendering Options for specific HTML call
type HTMLOptions struct {
	// Layout template name. Overrides Options.Layout.
	Layout string
}

type Render interface {
	http.ResponseWriter
	RW() http.ResponseWriter

	JSON(int, interface{})
	JSONString(interface{}) (string, error)
	RawData(int, []byte)
	HTML(int, string, interface{}, ...HTMLOptions)
	HTMLString(string, interface{}, ...HTMLOptions) (string, error)
	XML(int, interface{})
	Error(int, ...string)
	Status(int)
	Redirect(string, ...int)
}

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

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

	return opt
}

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

	return "; charset=" + defaultCharset
}

func getExt(s string) string {
	if strings.Index(s, ".") == -1 {
		return ""
	}
	return "." + strings.Join(strings.Split(s, ".")[1:], ".")
}

func compile(options RenderOptions) *template.Template {
	dir := options.Directory
	t := template.New(dir)
	t.Delims(options.Delims.Left, options.Delims.Right)
	// parse an initial template in case we don't have any
	template.Must(t.Parse("Macaron"))

	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		r, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}

		ext := getExt(r)

		for _, extension := range options.Extensions {
			if ext == extension {

				buf, err := ioutil.ReadFile(path)
				if err != nil {
					panic(err)
				}

				name := (r[0 : len(r)-len(ext)])
				tmpl := t.New(filepath.ToSlash(name))

				// add our funcmaps
				for _, funcs := range options.Funcs {
					tmpl.Funcs(funcs)
				}

				// Bomb out if parse fails. We don't want any silent server starts.
				template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf)))
				break
			}
		}

		return nil
	})

	return t
}

// Renderer is a Middleware that maps a macaron.Render service into the Macaron handler chain.
// An single variadic macaron.RenderOptions struct can be optionally provided to configure
// HTML rendering. The default directory for templates is "templates" and the default
// file extension is ".tmpl" and ".html".
//
// If MACARON_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the
// MACARON_ENV environment variable to "production".
func Renderer(options ...RenderOptions) Handler {
	opt := prepareOptions(options)
	cs := prepareCharset(opt.Charset)
	t := compile(opt)
	bufpool = bpool.NewBufferPool(64)
	return func(ctx *Context, rw http.ResponseWriter, req *http.Request) {
		var tc *template.Template
		if Env == DEV {
			// recompile for easy development
			tc = compile(opt)
		} else {
			// use a clone of the initial template
			tc, _ = t.Clone()
		}
		r := &TplRender{
			ResponseWriter:  rw,
			Req:             req,
			t:               tc,
			Opt:             opt,
			CompiledCharset: cs,
		}
		ctx.Data["TmplLoadTimes"] = func() string {
			if r.startTime.IsZero() {
				return ""
			}
			return fmt.Sprint(time.Since(r.startTime).Nanoseconds()/1e6) + "ms"
		}

		ctx.Render = r
		ctx.MapTo(r, (*Render)(nil))
	}
}

type TplRender struct {
	http.ResponseWriter
	Req             *http.Request
	t               *template.Template
	Opt             RenderOptions
	CompiledCharset string

	startTime time.Time
}

func (r *TplRender) RW() http.ResponseWriter {
	return r.ResponseWriter
}

func (r *TplRender) JSON(status int, v interface{}) {
	var result []byte
	var err error
	if r.Opt.IndentJSON {
		result, err = json.MarshalIndent(v, "", "  ")
	} else {
		result, err = json.Marshal(v)
	}
	if err != nil {
		http.Error(r, err.Error(), 500)
		return
	}

	// json rendered fine, write out the result
	r.Header().Set(ContentType, ContentJSON+r.CompiledCharset)
	r.WriteHeader(status)
	if len(r.Opt.PrefixJSON) > 0 {
		r.Write(r.Opt.PrefixJSON)
	}
	r.Write(result)
}

func (r *TplRender) JSONString(v interface{}) (string, error) {
	var result []byte
	var err error
	if r.Opt.IndentJSON {
		result, err = json.MarshalIndent(v, "", "  ")
	} else {
		result, err = json.Marshal(v)
	}
	if err != nil {
		return "", err
	}
	return string(result), nil
}

func (r *TplRender) RawData(status int, v []byte) {
	if r.Header().Get(ContentType) == "" {
		r.Header().Set(ContentType, ContentBinary)
	}
	r.WriteHeader(status)
	r.Write(v)
}

func (r *TplRender) renderBytes(name string, binding interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) {
	opt := r.prepareHTMLOptions(htmlOpt)

	if len(opt.Layout) > 0 {
		r.addYield(name, binding)
		name = opt.Layout
	}

	out, err := r.execute(name, binding)
	if err != nil {
		return nil, err
	}

	return out, nil
}

func (r *TplRender) HTML(status int, name string, binding interface{}, htmlOpt ...HTMLOptions) {
	r.startTime = time.Now()

	out, err := r.renderBytes(name, binding, htmlOpt...)
	if err != nil {
		http.Error(r, err.Error(), http.StatusInternalServerError)
		return
	}

	r.Header().Set(ContentType, r.Opt.HTMLContentType+r.CompiledCharset)
	r.WriteHeader(status)
	io.Copy(r, out)
	bufpool.Put(out)
}

func (r *TplRender) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOptions) (string, error) {
	if out, err := r.renderBytes(name, binding, htmlOpt...); err != nil {
		return "", err
	} else {
		return out.String(), nil
	}
}

func (r *TplRender) XML(status int, v interface{}) {
	var result []byte
	var err error
	if r.Opt.IndentXML {
		result, err = xml.MarshalIndent(v, "", "  ")
	} else {
		result, err = xml.Marshal(v)
	}
	if err != nil {
		http.Error(r, err.Error(), 500)
		return
	}

	// XML rendered fine, write out the result
	r.Header().Set(ContentType, ContentXML+r.CompiledCharset)
	r.WriteHeader(status)
	if len(r.Opt.PrefixXML) > 0 {
		r.Write(r.Opt.PrefixXML)
	}
	r.Write(result)
}

// Error writes the given HTTP status to the current ResponseWriter
func (r *TplRender) Error(status int, message ...string) {
	r.WriteHeader(status)
	if len(message) > 0 {
		r.Write([]byte(message[0]))
	}
}

func (r *TplRender) Status(status int) {
	r.WriteHeader(status)
}

func (r *TplRender) Redirect(location string, status ...int) {
	code := http.StatusFound
	if len(status) == 1 {
		code = status[0]
	}

	http.Redirect(r, r.Req, location, code)
}

func (r *TplRender) execute(name string, binding interface{}) (*bytes.Buffer, error) {
	buf := bufpool.Get()
	return buf, r.t.ExecuteTemplate(buf, name, binding)
}

func (r *TplRender) addYield(name string, binding interface{}) {
	funcs := template.FuncMap{
		"yield": func() (template.HTML, error) {
			buf, err := r.execute(name, binding)
			// return safe html here since we are rendering our own template
			return template.HTML(buf.String()), err
		},
		"current": func() (string, error) {
			return name, nil
		},
	}
	r.t.Funcs(funcs)
}

func (r *TplRender) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions {
	if len(htmlOpt) > 0 {
		return htmlOpt[0]
	}

	return HTMLOptions{
		Layout: r.Opt.Layout,
	}
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"io"
	"reflect"
	"testing"
	"time"

	"github.com/hashicorp/vault/physical"
	"github.com/hashicorp/vault/vault"
)

func TestLogical(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	// WRITE
	resp := testHttpPut(t, token, addr+"/v1/secret/foo", map[string]interface{}{
		"data": "bar",
	})
	testResponseStatus(t, resp, 204)

	// READ
	resp = testHttpGet(t, token, addr+"/v1/secret/foo")

	var actual map[string]interface{}
	var nilWarnings interface{}
	expected := map[string]interface{}{
		"renewable":      false,
		"lease_duration": float64((30 * 24 * time.Hour) / time.Second),
		"data": map[string]interface{}{
			"data": "bar",
		},
		"auth":     nil,
		"warnings": nilWarnings,
	}
	testResponseStatus(t, resp, 200)
	testResponseBody(t, resp, &actual)
	delete(actual, "lease_id")
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad:\nactual:\n%#v\nexpected:\n%#v", actual, expected)
	}

	// DELETE
	resp = testHttpDelete(t, token, addr+"/v1/secret/foo")
	testResponseStatus(t, resp, 204)

	resp = testHttpGet(t, token, addr+"/v1/secret/foo")
	testResponseStatus(t, resp, 404)
}

func TestLogical_noExist(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	resp := testHttpGet(t, token, addr+"/v1/secret/foo")
	testResponseStatus(t, resp, 404)
}

func TestLogical_StandbyRedirect(t *testing.T) {
	ln1, addr1 := TestListener(t)
	defer ln1.Close()
	ln2, addr2 := TestListener(t)
	defer ln2.Close()

	// Create an HA Vault
	inm := physical.NewInmem()
	conf := &vault.CoreConfig{
		Physical:      inm,
		AdvertiseAddr: addr1,
		DisableMlock:  true,
	}
	core1, err := vault.NewCore(conf)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	key, root := vault.TestCoreInit(t, core1)
	if _, err := core1.Unseal(vault.TestKeyCopy(key)); err != nil {
		t.Fatalf("unseal err: %s", err)
	}

	// Create a second HA Vault
	conf2 := &vault.CoreConfig{
		Physical:      inm,
		AdvertiseAddr: addr2,
		DisableMlock:  true,
	}
	core2, err := vault.NewCore(conf2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if _, err := core2.Unseal(vault.TestKeyCopy(key)); err != nil {
		t.Fatalf("unseal err: %s", err)
	}

	TestServerWithListener(t, ln1, addr1, core1)
	TestServerWithListener(t, ln2, addr2, core2)
	TestServerAuth(t, addr1, root)

	// WRITE to STANDBY
	resp := testHttpPut(t, root, addr2+"/v1/secret/foo", map[string]interface{}{
		"data": "bar",
	})
	testResponseStatus(t, resp, 307)

	//// READ to standby
	resp = testHttpGet(t, root, addr2+"/v1/auth/token/lookup-self")
	var actual map[string]interface{}
	var nilWarnings interface{}
	expected := map[string]interface{}{
		"renewable":      false,
		"lease_duration": float64(0),
		"data": map[string]interface{}{
			"meta":         nil,
			"num_uses":     float64(0),
			"path":         "auth/token/root",
			"policies":     []interface{}{"root"},
			"display_name": "root",
			"orphan":       true,
			"id":           root,
			"ttl":          float64(0),
		},
		"warnings": nilWarnings,
		"auth":     nil,
	}

	testResponseStatus(t, resp, 200)
	testResponseBody(t, resp, &actual)
	actualDataMap := actual["data"].(map[string]interface{})
	delete(actualDataMap, "creation_time")
	actual["data"] = actualDataMap
	delete(actual, "lease_id")
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: got %#v; expected %#v", actual, expected)
	}

	//// DELETE to standby
	resp = testHttpDelete(t, root, addr2+"/v1/secret/foo")
	testResponseStatus(t, resp, 307)
}

func TestLogical_CreateToken(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	// WRITE
	resp := testHttpPut(t, token, addr+"/v1/auth/token/create", map[string]interface{}{
		"data": "bar",
	})

	var actual map[string]interface{}
	var nilWarnings interface{}
	expected := map[string]interface{}{
		"lease_id":       "",
		"renewable":      false,
		"lease_duration": float64(0),
		"data":           nil,
		"auth": map[string]interface{}{
			"policies":       []interface{}{"root"},
			"metadata":       nil,
			"lease_duration": float64(0),
			"renewable":      false,
		},
		"warnings": nilWarnings,
	}
	testResponseStatus(t, resp, 200)
	testResponseBody(t, resp, &actual)
	delete(actual["auth"].(map[string]interface{}), "client_token")
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad:\nexpected:\n%#v\nactual:\n%#v", expected, actual)
	}
}

func TestLogical_RawHTTP(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	resp := testHttpPost(t, token, addr+"/v1/sys/mounts/foo", map[string]interface{}{
		"type": "http",
	})
	testResponseStatus(t, resp, 204)

	// Get the raw response
	resp = testHttpGet(t, token, addr+"/v1/foo/raw")
	testResponseStatus(t, resp, 200)

	// Test the headers
	if resp.Header.Get("Content-Type") != "plain/text" {
		t.Fatalf("Bad: %#v", resp.Header)
	}

	// Get the body
	body := new(bytes.Buffer)
	io.Copy(body, resp.Body)
	if string(body.Bytes()) != "hello world" {
		t.Fatalf("Bad: %s", body.Bytes())
	}
}
<commit_msg>Fix StandbyRedirect test<commit_after>package http

import (
	"bytes"
	"io"
	"reflect"
	"testing"
	"time"

	"github.com/hashicorp/vault/physical"
	"github.com/hashicorp/vault/vault"
)

func TestLogical(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	// WRITE
	resp := testHttpPut(t, token, addr+"/v1/secret/foo", map[string]interface{}{
		"data": "bar",
	})
	testResponseStatus(t, resp, 204)

	// READ
	resp = testHttpGet(t, token, addr+"/v1/secret/foo")

	var actual map[string]interface{}
	var nilWarnings interface{}
	expected := map[string]interface{}{
		"renewable":      false,
		"lease_duration": float64((30 * 24 * time.Hour) / time.Second),
		"data": map[string]interface{}{
			"data": "bar",
		},
		"auth":     nil,
		"warnings": nilWarnings,
	}
	testResponseStatus(t, resp, 200)
	testResponseBody(t, resp, &actual)
	delete(actual, "lease_id")
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad:\nactual:\n%#v\nexpected:\n%#v", actual, expected)
	}

	// DELETE
	resp = testHttpDelete(t, token, addr+"/v1/secret/foo")
	testResponseStatus(t, resp, 204)

	resp = testHttpGet(t, token, addr+"/v1/secret/foo")
	testResponseStatus(t, resp, 404)
}

func TestLogical_noExist(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	resp := testHttpGet(t, token, addr+"/v1/secret/foo")
	testResponseStatus(t, resp, 404)
}

func TestLogical_StandbyRedirect(t *testing.T) {
	ln1, addr1 := TestListener(t)
	defer ln1.Close()
	ln2, addr2 := TestListener(t)
	defer ln2.Close()

	// Create an HA Vault
	inmha := physical.NewInmemHA()
	conf := &vault.CoreConfig{
		Physical:      inmha,
		HAPhysical:    inmha,
		AdvertiseAddr: addr1,
		DisableMlock:  true,
	}
	core1, err := vault.NewCore(conf)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	key, root := vault.TestCoreInit(t, core1)
	if _, err := core1.Unseal(vault.TestKeyCopy(key)); err != nil {
		t.Fatalf("unseal err: %s", err)
	}

	// Create a second HA Vault
	conf2 := &vault.CoreConfig{
		Physical:      inmha,
		HAPhysical:    inmha,
		AdvertiseAddr: addr2,
		DisableMlock:  true,
	}
	core2, err := vault.NewCore(conf2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if _, err := core2.Unseal(vault.TestKeyCopy(key)); err != nil {
		t.Fatalf("unseal err: %s", err)
	}

	TestServerWithListener(t, ln1, addr1, core1)
	TestServerWithListener(t, ln2, addr2, core2)
	TestServerAuth(t, addr1, root)

	// WRITE to STANDBY
	resp := testHttpPut(t, root, addr2+"/v1/secret/foo", map[string]interface{}{
		"data": "bar",
	})
	testResponseStatus(t, resp, 307)

	//// READ to standby
	resp = testHttpGet(t, root, addr2+"/v1/auth/token/lookup-self")
	var actual map[string]interface{}
	var nilWarnings interface{}
	expected := map[string]interface{}{
		"renewable":      false,
		"lease_duration": float64(0),
		"data": map[string]interface{}{
			"meta":         nil,
			"num_uses":     float64(0),
			"path":         "auth/token/root",
			"policies":     []interface{}{"root"},
			"display_name": "root",
			"orphan":       true,
			"id":           root,
			"ttl":          float64(0),
		},
		"warnings": nilWarnings,
		"auth":     nil,
	}

	testResponseStatus(t, resp, 200)
	testResponseBody(t, resp, &actual)
	actualDataMap := actual["data"].(map[string]interface{})
	delete(actualDataMap, "creation_time")
	actual["data"] = actualDataMap
	delete(actual, "lease_id")
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: got %#v; expected %#v", actual, expected)
	}

	//// DELETE to standby
	resp = testHttpDelete(t, root, addr2+"/v1/secret/foo")
	testResponseStatus(t, resp, 307)
}

func TestLogical_CreateToken(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	// WRITE
	resp := testHttpPut(t, token, addr+"/v1/auth/token/create", map[string]interface{}{
		"data": "bar",
	})

	var actual map[string]interface{}
	var nilWarnings interface{}
	expected := map[string]interface{}{
		"lease_id":       "",
		"renewable":      false,
		"lease_duration": float64(0),
		"data":           nil,
		"auth": map[string]interface{}{
			"policies":       []interface{}{"root"},
			"metadata":       nil,
			"lease_duration": float64(0),
			"renewable":      false,
		},
		"warnings": nilWarnings,
	}
	testResponseStatus(t, resp, 200)
	testResponseBody(t, resp, &actual)
	delete(actual["auth"].(map[string]interface{}), "client_token")
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad:\nexpected:\n%#v\nactual:\n%#v", expected, actual)
	}
}

func TestLogical_RawHTTP(t *testing.T) {
	core, _, token := vault.TestCoreUnsealed(t)
	ln, addr := TestServer(t, core)
	defer ln.Close()
	TestServerAuth(t, addr, token)

	resp := testHttpPost(t, token, addr+"/v1/sys/mounts/foo", map[string]interface{}{
		"type": "http",
	})
	testResponseStatus(t, resp, 204)

	// Get the raw response
	resp = testHttpGet(t, token, addr+"/v1/foo/raw")
	testResponseStatus(t, resp, 200)

	// Test the headers
	if resp.Header.Get("Content-Type") != "plain/text" {
		t.Fatalf("Bad: %#v", resp.Header)
	}

	// Get the body
	body := new(bytes.Buffer)
	io.Copy(body, resp.Body)
	if string(body.Bytes()) != "hello world" {
		t.Fatalf("Bad: %s", body.Bytes())
	}
}
<|endoftext|>


<commit_before>package tcp

import (
	"strings"
	"sync"
	"time"

	"github.com/marten-seemann/tcp"
	"github.com/mikioh/tcpinfo"
	manet "github.com/multiformats/go-multiaddr/net"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	newConns      *prometheus.CounterVec
	closedConns   *prometheus.CounterVec
	segsSentDesc  *prometheus.Desc
	segsRcvdDesc  *prometheus.Desc
	bytesSentDesc *prometheus.Desc
	bytesRcvdDesc *prometheus.Desc
)

const collectFrequency = 10 * time.Second

var collector *aggregatingCollector

func init() {
	segsSentDesc = prometheus.NewDesc("tcp_sent_segments_total", "TCP segments sent", nil, nil)
	segsRcvdDesc = prometheus.NewDesc("tcp_rcvd_segments_total", "TCP segments received", nil, nil)
	bytesSentDesc = prometheus.NewDesc("tcp_sent_bytes", "TCP bytes sent", nil, nil)
	bytesRcvdDesc = prometheus.NewDesc("tcp_rcvd_bytes", "TCP bytes received", nil, nil)

	collector = newAggregatingCollector()
	prometheus.MustRegister(collector)

	const direction = "direction"

	newConns = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "tcp_connections_new_total",
			Help: "TCP new connections",
		},
		[]string{direction},
	)
	prometheus.MustRegister(newConns)
	closedConns = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "tcp_connections_closed_total",
			Help: "TCP connections closed",
		},
		[]string{direction},
	)
	prometheus.MustRegister(closedConns)
}

type aggregatingCollector struct {
	mutex sync.Mutex

	highestID            uint64
	conns                map[uint64] /* id */ *tracingConn
	rtts                 prometheus.Histogram
	connDurations        prometheus.Histogram
	segsSent, segsRcvd   uint64
	bytesSent, bytesRcvd uint64
}

var _ prometheus.Collector = &aggregatingCollector{}

func newAggregatingCollector() *aggregatingCollector {
	c := &aggregatingCollector{
		conns: make(map[uint64]*tracingConn),
		rtts: prometheus.NewHistogram(prometheus.HistogramOpts{
			Name:    "tcp_rtt",
			Help:    "TCP round trip time",
			Buckets: prometheus.ExponentialBuckets(0.001, 1.25, 40), // 1ms to ~6000ms
		}),
		connDurations: prometheus.NewHistogram(prometheus.HistogramOpts{
			Name:    "tcp_connection_duration",
			Help:    "TCP Connection Duration",
			Buckets: prometheus.ExponentialBuckets(1, 1.5, 40), // 1s to ~12 weeks
		}),
	}
	go c.cron()
	return c
}

func (c *aggregatingCollector) AddConn(t *tracingConn) uint64 {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.highestID++
	c.conns[c.highestID] = t
	return c.highestID
}

func (c *aggregatingCollector) removeConn(id uint64) {
	delete(c.conns, id)
}

func (c *aggregatingCollector) Describe(descs chan<- *prometheus.Desc) {
	descs <- c.rtts.Desc()
	descs <- c.connDurations.Desc()
	if hasSegmentCounter {
		descs <- segsSentDesc
		descs <- segsRcvdDesc
	}
	if hasByteCounter {
		descs <- bytesSentDesc
		descs <- bytesRcvdDesc
	}
}

func (c *aggregatingCollector) cron() {
	ticker := time.NewTicker(collectFrequency)
	defer ticker.Stop()

	for now := range ticker.C {
		c.mutex.Lock()
		c.segsSent = 0
		c.segsRcvd = 0
		c.bytesSent = 0
		c.bytesRcvd = 0
		for _, conn := range c.conns {
			info, err := conn.getTCPInfo()
			if err != nil {
				if strings.Contains(err.Error(), "use of closed network connection") {
					c.closedConn(conn)
					continue
				}
				log.Errorf("Failed to get TCP info: %s", err)
				continue
			}
			if hasSegmentCounter {
				c.segsSent += getSegmentsSent(info)
				c.segsRcvd += getSegmentsRcvd(info)
			}
			if hasByteCounter {
				c.bytesSent += getBytesSent(info)
				c.bytesRcvd += getBytesRcvd(info)
			}
			c.rtts.Observe(info.RTT.Seconds())
			c.connDurations.Observe(now.Sub(conn.startTime).Seconds())
			if info.State == tcpinfo.Closed {
				c.closedConn(conn)
			}
		}
		c.mutex.Unlock()
	}
}

func (c *aggregatingCollector) Collect(metrics chan<- prometheus.Metric) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	metrics <- c.rtts
	metrics <- c.connDurations
	if hasSegmentCounter {
		segsSentMetric, err := prometheus.NewConstMetric(segsSentDesc, prometheus.CounterValue, float64(c.segsSent))
		if err != nil {
			log.Errorf("creating tcp_sent_segments_total metric failed: %v", err)
			return
		}
		segsRcvdMetric, err := prometheus.NewConstMetric(segsRcvdDesc, prometheus.CounterValue, float64(c.segsRcvd))
		if err != nil {
			log.Errorf("creating tcp_rcvd_segments_total metric failed: %v", err)
			return
		}
		metrics <- segsSentMetric
		metrics <- segsRcvdMetric
	}
	if hasByteCounter {
		bytesSentMetric, err := prometheus.NewConstMetric(bytesSentDesc, prometheus.CounterValue, float64(c.bytesSent))
		if err != nil {
			log.Errorf("creating tcp_sent_bytes metric failed: %v", err)
			return
		}
		bytesRcvdMetric, err := prometheus.NewConstMetric(bytesRcvdDesc, prometheus.CounterValue, float64(c.bytesRcvd))
		if err != nil {
			log.Errorf("creating tcp_rcvd_bytes metric failed: %v", err)
			return
		}
		metrics <- bytesSentMetric
		metrics <- bytesRcvdMetric
	}
}

func (c *aggregatingCollector) closedConn(conn *tracingConn) {
	collector.removeConn(conn.id)
	closedConns.WithLabelValues(conn.getDirection()).Inc()
}

type tracingConn struct {
	id uint64

	startTime time.Time
	isClient  bool

	manet.Conn
	tcpConn *tcp.Conn
}

func newTracingConn(c manet.Conn, isClient bool) (*tracingConn, error) {
	conn, err := tcp.NewConn(c)
	if err != nil {
		return nil, err
	}
	tc := &tracingConn{
		startTime: time.Now(),
		isClient:  isClient,
		Conn:      c,
		tcpConn:   conn,
	}
	tc.id = collector.AddConn(tc)
	newConns.WithLabelValues(tc.getDirection()).Inc()
	return tc, nil
}

func (c *tracingConn) getDirection() string {
	if c.isClient {
		return "outgoing"
	}
	return "incoming"
}

func (c *tracingConn) Close() error {
	return c.Conn.Close()
}

func (c *tracingConn) getTCPInfo() (*tcpinfo.Info, error) {
	var o tcpinfo.Info
	var b [256]byte
	i, err := c.tcpConn.Option(o.Level(), o.Name(), b[:])
	if err != nil {
		return nil, err
	}
	info := i.(*tcpinfo.Info)
	return info, nil
}

type tracingListener struct {
	manet.Listener
}

func (l *tracingListener) Accept() (manet.Conn, error) {
	conn, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	return newTracingConn(conn, false)
}
<commit_msg>use conn.Close() to remove closed connections from tracer<commit_after>package tcp

import (
	"strings"
	"sync"
	"time"

	"github.com/marten-seemann/tcp"
	"github.com/mikioh/tcpinfo"
	manet "github.com/multiformats/go-multiaddr/net"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	newConns      *prometheus.CounterVec
	closedConns   *prometheus.CounterVec
	segsSentDesc  *prometheus.Desc
	segsRcvdDesc  *prometheus.Desc
	bytesSentDesc *prometheus.Desc
	bytesRcvdDesc *prometheus.Desc
)

const collectFrequency = 10 * time.Second

var collector *aggregatingCollector

func init() {
	segsSentDesc = prometheus.NewDesc("tcp_sent_segments_total", "TCP segments sent", nil, nil)
	segsRcvdDesc = prometheus.NewDesc("tcp_rcvd_segments_total", "TCP segments received", nil, nil)
	bytesSentDesc = prometheus.NewDesc("tcp_sent_bytes", "TCP bytes sent", nil, nil)
	bytesRcvdDesc = prometheus.NewDesc("tcp_rcvd_bytes", "TCP bytes received", nil, nil)

	collector = newAggregatingCollector()
	prometheus.MustRegister(collector)

	const direction = "direction"

	newConns = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "tcp_connections_new_total",
			Help: "TCP new connections",
		},
		[]string{direction},
	)
	prometheus.MustRegister(newConns)
	closedConns = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "tcp_connections_closed_total",
			Help: "TCP connections closed",
		},
		[]string{direction},
	)
	prometheus.MustRegister(closedConns)
}

type aggregatingCollector struct {
	mutex sync.Mutex

	highestID            uint64
	conns                map[uint64] /* id */ *tracingConn
	rtts                 prometheus.Histogram
	connDurations        prometheus.Histogram
	segsSent, segsRcvd   uint64
	bytesSent, bytesRcvd uint64
}

var _ prometheus.Collector = &aggregatingCollector{}

func newAggregatingCollector() *aggregatingCollector {
	c := &aggregatingCollector{
		conns: make(map[uint64]*tracingConn),
		rtts: prometheus.NewHistogram(prometheus.HistogramOpts{
			Name:    "tcp_rtt",
			Help:    "TCP round trip time",
			Buckets: prometheus.ExponentialBuckets(0.001, 1.25, 40), // 1ms to ~6000ms
		}),
		connDurations: prometheus.NewHistogram(prometheus.HistogramOpts{
			Name:    "tcp_connection_duration",
			Help:    "TCP Connection Duration",
			Buckets: prometheus.ExponentialBuckets(1, 1.5, 40), // 1s to ~12 weeks
		}),
	}
	go c.cron()
	return c
}

func (c *aggregatingCollector) AddConn(t *tracingConn) uint64 {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.highestID++
	c.conns[c.highestID] = t
	return c.highestID
}

func (c *aggregatingCollector) removeConn(id uint64) {
	delete(c.conns, id)
}

func (c *aggregatingCollector) Describe(descs chan<- *prometheus.Desc) {
	descs <- c.rtts.Desc()
	descs <- c.connDurations.Desc()
	if hasSegmentCounter {
		descs <- segsSentDesc
		descs <- segsRcvdDesc
	}
	if hasByteCounter {
		descs <- bytesSentDesc
		descs <- bytesRcvdDesc
	}
}

func (c *aggregatingCollector) cron() {
	ticker := time.NewTicker(collectFrequency)
	defer ticker.Stop()

	for now := range ticker.C {
		c.mutex.Lock()
		c.segsSent = 0
		c.segsRcvd = 0
		c.bytesSent = 0
		c.bytesRcvd = 0
		for _, conn := range c.conns {
			info, err := conn.getTCPInfo()
			if err != nil {
				if strings.Contains(err.Error(), "use of closed network connection") {
					continue
				}
				log.Errorf("Failed to get TCP info: %s", err)
				continue
			}
			if hasSegmentCounter {
				c.segsSent += getSegmentsSent(info)
				c.segsRcvd += getSegmentsRcvd(info)
			}
			if hasByteCounter {
				c.bytesSent += getBytesSent(info)
				c.bytesRcvd += getBytesRcvd(info)
			}
			c.rtts.Observe(info.RTT.Seconds())
			c.connDurations.Observe(now.Sub(conn.startTime).Seconds())
		}
		c.mutex.Unlock()
	}
}

func (c *aggregatingCollector) Collect(metrics chan<- prometheus.Metric) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	metrics <- c.rtts
	metrics <- c.connDurations
	if hasSegmentCounter {
		segsSentMetric, err := prometheus.NewConstMetric(segsSentDesc, prometheus.CounterValue, float64(c.segsSent))
		if err != nil {
			log.Errorf("creating tcp_sent_segments_total metric failed: %v", err)
			return
		}
		segsRcvdMetric, err := prometheus.NewConstMetric(segsRcvdDesc, prometheus.CounterValue, float64(c.segsRcvd))
		if err != nil {
			log.Errorf("creating tcp_rcvd_segments_total metric failed: %v", err)
			return
		}
		metrics <- segsSentMetric
		metrics <- segsRcvdMetric
	}
	if hasByteCounter {
		bytesSentMetric, err := prometheus.NewConstMetric(bytesSentDesc, prometheus.CounterValue, float64(c.bytesSent))
		if err != nil {
			log.Errorf("creating tcp_sent_bytes metric failed: %v", err)
			return
		}
		bytesRcvdMetric, err := prometheus.NewConstMetric(bytesRcvdDesc, prometheus.CounterValue, float64(c.bytesRcvd))
		if err != nil {
			log.Errorf("creating tcp_rcvd_bytes metric failed: %v", err)
			return
		}
		metrics <- bytesSentMetric
		metrics <- bytesRcvdMetric
	}
}

func (c *aggregatingCollector) ClosedConn(conn *tracingConn, direction string) {
	c.mutex.Lock()
	collector.removeConn(conn.id)
	c.mutex.Unlock()
	closedConns.WithLabelValues(direction).Inc()
}

type tracingConn struct {
	id uint64

	startTime time.Time
	isClient  bool

	manet.Conn
	tcpConn *tcp.Conn
}

func newTracingConn(c manet.Conn, isClient bool) (*tracingConn, error) {
	conn, err := tcp.NewConn(c)
	if err != nil {
		return nil, err
	}
	tc := &tracingConn{
		startTime: time.Now(),
		isClient:  isClient,
		Conn:      c,
		tcpConn:   conn,
	}
	tc.id = collector.AddConn(tc)
	newConns.WithLabelValues(tc.getDirection()).Inc()
	return tc, nil
}

func (c *tracingConn) getDirection() string {
	if c.isClient {
		return "outgoing"
	}
	return "incoming"
}

func (c *tracingConn) Close() error {
	collector.ClosedConn(c, c.getDirection())
	return c.Conn.Close()
}

func (c *tracingConn) getTCPInfo() (*tcpinfo.Info, error) {
	var o tcpinfo.Info
	var b [256]byte
	i, err := c.tcpConn.Option(o.Level(), o.Name(), b[:])
	if err != nil {
		return nil, err
	}
	info := i.(*tcpinfo.Info)
	return info, nil
}

type tracingListener struct {
	manet.Listener
}

func (l *tracingListener) Accept() (manet.Conn, error) {
	conn, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	return newTracingConn(conn, false)
}
<|endoftext|>


<commit_before>package goros

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"regexp"
	"strings"
)

const (
	MessageData      = 0x02
	BagHeader        = 0x03
	IndexData        = 0x04
	ChunkRecord      = 0x05
	ChunkInfo        = 0x06
	ConnectionRecord = 0x07
)

var (
	typeMap = map[string]reflect.Type{
		"bool":    reflect.TypeOf((*bool)(nil)).Elem(),
		"float32": reflect.TypeOf((*float32)(nil)).Elem(),
		"float64": reflect.TypeOf((*float64)(nil)).Elem(),
		"uint32":  reflect.TypeOf((*uint32)(nil)).Elem(),
		"uint64":  reflect.TypeOf((*uint32)(nil)).Elem(),
		"int32":   reflect.TypeOf((*int32)(nil)).Elem(),
		"int64":   reflect.TypeOf((*int64)(nil)).Elem(),
	}
)

func parseSubMessageDefinition(subMessage string, superMessageTypeString string) {
	commentRegexp, _ := regexp.Compile("\\s*#[^\n]*")
	subMessageWithoutComments := commentRegexp.ReplaceAllString(subMessage, "")
	subMessageMap := make(map[string]reflect.Type)
	var subMessageTypeString string
	for _, line := range strings.Split(subMessageWithoutComments, "\n") {
		if len(line) != 0 {
			if strings.HasPrefix(line, "MSG") {
				subMessageTypeString = strings.TrimPrefix(line, "MSG: ")
				continue
			}

			split := strings.Split(line, " ")
			typeString := split[0]
			fieldName := split[1]

			var fieldType reflect.Type
			if strings.HasSuffix(typeString, "[]") {
				elementType := typeMap[typeString[:len(typeString)-2]]
				fieldType = reflect.SliceOf(elementType)
			} else {
				fieldType = typeMap[typeString]
			}

			subMessageMap[fieldName] = fieldType
			//fmt.Println(line)
		}
	}

	//return subMessageTypeString, subMessageMap
	if len(subMessageTypeString) == 0 {
		subMessageTypeString = superMessageTypeString
	}

	typeString := strings.Split(subMessageTypeString)[1]
	typeMap[typeString] = subMessageMap
}

func parseMessageDefinition(dataMap map[string]interface{}) {
	//fmt.Println(dataMap["message_definition"])

	messageSplitterRegex, _ := regexp.Compile("[=]+\n")

	subMessages := messageSplitterRegex.Split(dataMap["message_definition"].(string), -1)

	//fmt.Println(subMessages)
	/*
		for _, line := range strings.Split(dataMap["message_definition"].(string), "\n") {
			fmt.Println(line)
		}
	*/
	for _, subMessage := range subMessages {
		defer parseSubMessageDefinition(subMessage, dataMap["type"].(string)) //log.Println(subMessage)
	}
}

func parseRecordHeader(buffer []byte) map[string]interface{} {
	//log.Println(string(buffer))
	reader := bufio.NewReader(bytes.NewReader(buffer))
	//log.Println(reader.Buffered())

	valueMap := make(map[string]interface{})

	var fieldLength uint32
	for binary.Read(reader, binary.LittleEndian, &fieldLength) == nil {
		fieldName, _ := reader.ReadString('=')
		fieldName = fieldName[:len(fieldName)-1]
		//log.Println(fieldName)
		//reader.Discard(int(fieldLength) - len(fieldName))
		valueLength := int(fieldLength) - len(fieldName) - 1

		switch fieldName {
		case "op":
			value, _ := reader.ReadByte()
			valueMap[fieldName] = value
		case "conn", "size", "conn_count", "chunk_count", "count", "ver":
			var value uint32
			binary.Read(reader, binary.LittleEndian, &value)
			valueMap[fieldName] = value
		case "index_pos", "time", "start_time", "end_time", "chunk_pos":
			var value uint64
			binary.Read(reader, binary.LittleEndian, &value)
			valueMap[fieldName] = value
		case "compression", "topic":
			value := make([]byte, valueLength)
			reader.Read(value)
			valueMap[fieldName] = string(value)
		default:
			value := make([]byte, valueLength)
			reader.Read(value)
			valueMap[fieldName] = string(value)
			log.Printf("Congratulations! I don't know what to do with %s", fieldName)
		}
	}

	return valueMap
}

func parseRecord(reader *bufio.Reader) error {
	var headerLength uint32
	err := binary.Read(reader, binary.LittleEndian, &headerLength)
	if err != nil {
		return err
	}
	//log.Println(headerLength)
	buffer := make([]byte, int(headerLength))
	reader.Read(buffer)
	valueMap := parseRecordHeader(buffer)
	//fmt.Println(valueMap)

	var dataLength uint32
	binary.Read(reader, binary.LittleEndian, &dataLength)
	//log.Println(dataLength)
	//if skip {
	//reader.Discard(int(dataLength))
	dataBuffer := make([]byte, int(dataLength))
	bytesRead, _ := io.ReadFull(reader, dataBuffer)
	//log.Println(bytesRead)

	if bytesRead != int(dataLength) {
		panic(fmt.Sprintf("expected %d bytes, got %d", dataLength, bytesRead))
	}
	/*} else {
		buffer := make([]byte, int(dataLength))
		reader.Read(buffer)
		log.Println(string(buffer))
	}
	*/
	switch valueMap["op"].(byte) {
	case BagHeader:
	case ChunkRecord:
		chunkReader := bufio.NewReaderSize(bytes.NewReader(dataBuffer), 8000000)
		var chunkErr error
		for chunkErr == nil {
			chunkErr = parseRecord(chunkReader)
		}
	case ConnectionRecord:
		//log.Println(string(dataBuffer))
		dataMap := parseRecordHeader(dataBuffer)
		//log.Println(dataMap)
		parseMessageDefinition(dataMap)
	case MessageData:
	case IndexData:
	case ChunkInfo:
	}

	return nil

}

func ParseRosbag(path string) {
	log.Println(path)
	file, err := os.Open(path) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	reader := bufio.NewReaderSize(file, 8000000)

	line, _ := reader.ReadString('\n')
	log.Print(line)

	//parseRecord(reader)
	//parseRecord(reader)
	var recordError error
	for recordError == nil {
		recordError = parseRecord(reader)
	}
	/*chunkHeader := make([]byte, 4096)
	_, _ = file.Read(chunkHeader)
	log.Println(string(buffer))
	*/
}
<commit_msg>fixed time format<commit_after>package goros

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"regexp"
	"strings"
	"time"
)

const (
	MessageData      = 0x02
	BagHeader        = 0x03
	IndexData        = 0x04
	ChunkRecord      = 0x05
	ChunkInfo        = 0x06
	ConnectionRecord = 0x07
)

var (
	typeMap = map[string]reflect.Type{
		"bool":    reflect.TypeOf((*bool)(nil)).Elem(),
		"float32": reflect.TypeOf((*float32)(nil)).Elem(),
		"float64": reflect.TypeOf((*float64)(nil)).Elem(),
		"uint32":  reflect.TypeOf((*uint32)(nil)).Elem(),
		"uint64":  reflect.TypeOf((*uint32)(nil)).Elem(),
		"int32":   reflect.TypeOf((*int32)(nil)).Elem(),
		"int64":   reflect.TypeOf((*int64)(nil)).Elem(),
	}
)

func parseSubMessageDefinition(subMessage string, superMessageTypeString string) {
	commentRegexp, _ := regexp.Compile("\\s*#[^\n]*")
	subMessageWithoutComments := commentRegexp.ReplaceAllString(subMessage, "")
	subMessageMap := make(map[string]reflect.Type)
	var subMessageTypeString string
	for _, line := range strings.Split(subMessageWithoutComments, "\n") {
		if len(line) != 0 {
			if strings.HasPrefix(line, "MSG") {
				subMessageTypeString = strings.TrimPrefix(line, "MSG: ")
				continue
			}

			split := strings.Split(line, " ")
			typeString := split[0]
			fieldName := split[1]

			var fieldType reflect.Type
			if strings.HasSuffix(typeString, "[]") {
				elementType := typeMap[typeString[:len(typeString)-2]]
				fieldType = reflect.SliceOf(elementType)
			} else {
				fieldType = typeMap[typeString]
			}

			subMessageMap[fieldName] = fieldType
			//fmt.Println(line)
		}
	}

	//return subMessageTypeString, subMessageMap
	if len(subMessageTypeString) == 0 {
		subMessageTypeString = superMessageTypeString
	}

	//typeString := strings.Split(subMessageTypeString, " ")[1]
	//typeMap[typeString] = subMessageMap
}

func parseMessageDefinition(dataMap map[string]interface{}) {
	//fmt.Println(dataMap["message_definition"])

	messageSplitterRegex, _ := regexp.Compile("[=]+\n")

	subMessages := messageSplitterRegex.Split(dataMap["message_definition"].(string), -1)

	//fmt.Println(subMessages)
	/*
		for _, line := range strings.Split(dataMap["message_definition"].(string), "\n") {
			fmt.Println(line)
		}
	*/
	for _, subMessage := range subMessages {
		fmt.Println(subMessage)
		//defer parseSubMessageDefinition(subMessage, dataMap["type"].(string)) //log.Println(subMessage)
	}
}

func parseRecordHeader(buffer []byte) map[string]interface{} {
	//log.Println(string(buffer))
	reader := bufio.NewReader(bytes.NewReader(buffer))
	//log.Println(reader.Buffered())

	valueMap := make(map[string]interface{})

	var fieldLength uint32
	for binary.Read(reader, binary.LittleEndian, &fieldLength) == nil {
		fieldName, _ := reader.ReadString('=')
		fieldName = fieldName[:len(fieldName)-1]
		//log.Println(fieldName)
		//reader.Discard(int(fieldLength) - len(fieldName))
		valueLength := int(fieldLength) - len(fieldName) - 1

		switch fieldName {
		case "op":
			value, _ := reader.ReadByte()
			valueMap[fieldName] = value
		case "conn", "size", "conn_count", "chunk_count", "count", "ver":
			var value uint32
			binary.Read(reader, binary.LittleEndian, &value)
			valueMap[fieldName] = value
		case "index_pos":
			var value uint64
			binary.Read(reader, binary.LittleEndian, &value)
			valueMap[fieldName] = value
		case "time", "start_time", "end_time", "chunk_pos":
			var secs int32
			var nsecs int32
			binary.Read(reader, binary.LittleEndian, &secs)
			binary.Read(reader, binary.LittleEndian, &nsecs)
			valueMap[fieldName] = time.Unix(int64(secs), int64(nsecs))
		case "compression", "topic":
			value := make([]byte, valueLength)
			reader.Read(value)
			valueMap[fieldName] = string(value)
		default:
			value := make([]byte, valueLength)
			reader.Read(value)
			valueMap[fieldName] = string(value)
			log.Printf("Congratulations! I don't know what to do with %s", fieldName)
		}
	}

	return valueMap
}

func parseRecord(reader *bufio.Reader) error {
	var headerLength uint32
	err := binary.Read(reader, binary.LittleEndian, &headerLength)
	if err != nil {
		return err
	}
	//log.Println(headerLength)
	buffer := make([]byte, int(headerLength))
	reader.Read(buffer)
	valueMap := parseRecordHeader(buffer)
	fmt.Println(valueMap)

	var dataLength uint32
	binary.Read(reader, binary.LittleEndian, &dataLength)
	//log.Println(dataLength)
	//if skip {
	//reader.Discard(int(dataLength))
	dataBuffer := make([]byte, int(dataLength))
	bytesRead, _ := io.ReadFull(reader, dataBuffer)
	//log.Println(bytesRead)

	if bytesRead != int(dataLength) {
		panic(fmt.Sprintf("expected %d bytes, got %d", dataLength, bytesRead))
	}
	/*} else {
		buffer := make([]byte, int(dataLength))
		reader.Read(buffer)
		log.Println(string(buffer))
	}
	*/
	switch valueMap["op"].(byte) {
	case BagHeader:
	case ChunkRecord:
		chunkReader := bufio.NewReaderSize(bytes.NewReader(dataBuffer), 8000000)
		var chunkErr error
		for chunkErr == nil {
			chunkErr = parseRecord(chunkReader)
		}
	case ConnectionRecord:
		//log.Println(string(dataBuffer))
		dataMap := parseRecordHeader(dataBuffer)
		//log.Println(dataMap)
		parseMessageDefinition(dataMap)
	case MessageData:
	case IndexData:
	case ChunkInfo:
	}

	return nil

}

func ParseRosbag(path string) {
	log.Println(path)
	file, err := os.Open(path) // For read access.
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	reader := bufio.NewReaderSize(file, 8000000)

	line, _ := reader.ReadString('\n')
	log.Print(line)

	//parseRecord(reader)
	//parseRecord(reader)
	var recordError error
	for recordError == nil {
		recordError = parseRecord(reader)
	}
	/*chunkHeader := make([]byte, 4096)
	_, _ = file.Read(chunkHeader)
	log.Println(string(buffer))
	*/
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
)

// NewRouter creates and returns a mux.Router with default routes
func NewRouter() *mux.Router {
	router := mux.NewRouter()
	router.HandleFunc("/hello/{name}", helloHandler)
	router.HandleFunc("/add_job", initiateTranscriptionJobHandler)

	for _, route := range routes {
		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(route.HandlerFunc)
	}

	return router
}

// ApplyMiddleware wraps the router in some middleware
func ApplyMiddleware(router http.Handler) http.Handler {
	stderrLoggingHandler := func(http.Handler) http.Handler {
		return handlers.LoggingHandler(os.Stderr, router)
	}
	middlewareRouter := alice.New(handlers.CompressHandler, stderrLoggingHandler).Then(router)
	return middlewareRouter
}
<commit_msg>Fixed function comments to include periods and be slightly more descriptive.<commit_after>package main

import (
	"net/http"
	"os"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
)

// NewRouter creates and returns a mux.Router with default routes.
func NewRouter() *mux.Router {
	router := mux.NewRouter()

	for _, route := range routes {
		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(route.HandlerFunc)
	}

	return router
}

// ApplyMiddleware wraps the router in some middleware. This middleware includes
// logging and gzip compression.
func ApplyMiddleware(router http.Handler) http.Handler {
	stderrLoggingHandler := func(http.Handler) http.Handler {
		return handlers.LoggingHandler(os.Stderr, router)
	}
	middlewareRouter := alice.New(handlers.CompressHandler, stderrLoggingHandler).Then(router)
	return middlewareRouter
}
<|endoftext|>


<commit_before>package http

import (
	"github.com/gorilla/mux"
	"github.com/gorilla/pat"
	"labix.org/v2/mgo/bson"
	"net/http"
	"strings"
	"fmt"
)

var (
	routers = []*Router{}
)

// MgoObject is a simple Object that has an Id (bson.ObjectId).
type MgoObject struct {
	ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
}

// ObjectRester
type ObjectRester interface {
	GetRootURL() string
}

// Router is a pat.Router which has a domain for handling CORS requests
// and hooks to execute some functions before executing the HandlerFunc.
type Router struct {
	*pat.Router
	domain string
	hooks  []HandlerFunc
}

// NewRouter returns a new Router.
func NewRouter(domain string) *Router {
	router := &Router{pat.New(), domain, make([]HandlerFunc, 0)}
	routers = append(routers, router)
	return router
}

// Domain returns the domain of the Router.
func (self *Router) Domain() string {
	return self.domain
}

// SetDomain set the domain of the Router.
func (self *Router) SetDomain(domain string) {
	self.domain = domain
}

// Add a function to be executed before serving HTTP.
func (self *Router) AddHooks(hooks ...HandlerFunc) { self.hooks = append(self.hooks, hooks...) }

// Register add a POST, GET, PUT and DELETE HandlerFunc to add, get, edit and delete an instance of this object.
// Example:
//
// 		type User struct {
//			http.MgoObject
//			Name string `json:"name"`
//		}
//
//		func (user *User) GetRootURL() string { return "users" }
//
//		func main() {
//			r := http.NewRouter("example.com")
//			r.Register(&User{})
//			http.ListenAndServe(":8080", r)
//		}
//
func (self *Router) Register(object ObjectRester) {
	self.registerGet(object)
	self.registerDelete(object)
	self.registerPost(object)
	self.registerPut(object)
}

func (self *Router) registerGet(object ObjectRester) {
	self.Get(fmt.Sprintf("/%v", object.GetRootURL()), func(w ResponseWriter, r *Request) *Error {

			return nil
		})
}

func (self *Router) registerDelete(object ObjectRester) {

}

func (self *Router) registerPut(object ObjectRester) {

}

func (self *Router) registerPost(object ObjectRester) {

}

// runHooks run each hooks from the Router.
func (self *Router) runHooks(w ResponseWriter, r *Request) *Error {
	for _, hook := range self.hooks {
		if err := hook(w, r); err != nil {
			return err
		}
	}
	return nil
}

// Get registers a pattern with a handler for GET requests.
func (self *Router) Get(route string, h HandlerFunc) *mux.Route {
	return self.Add("GET", route, createHandler(h))
}

// Post registers a pattern with a handler for POST requests.
func (self *Router) Post(route string, h HandlerFunc) *mux.Route {
	return self.Add("POST", route, createHandler(h))
}

// Delete registers a pattern with a handler for DELETE requests.
func (self *Router) Delete(route string, h HandlerFunc) *mux.Route {
	return self.Add("DELETE", route, createHandler(h))
}

// Put registers a pattern with a handler for PUT requests.
func (self *Router) Put(route string, h HandlerFunc) *mux.Route {
	return self.Add("PUT", route, createHandler(h))
}

// ServeHTTP dispatches the handler registered in the matched route.
// It performs any hooks and add the domain registered in the Router to be allowed for cross-domain requests.
func (self *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	wr, rr := createResponseWriter(w), createRequest(r)
	if strings.ToLower(r.Method) == "options" {
		http.Redirect(wr, r, r.RequestURI, 200)
		return
	}
	wr.addCORSHeaders(self.domain)
	if err := self.runHooks(wr, rr); err != nil {
		wr.WriteError(err)
		return
	}
	self.Router.ServeHTTP(w, r)
}

// Redirect replies to the request with a redirect to url, which may be a path relative to the request path.
func Redirect(w ResponseWriter, r *Request, urlStr string, code int) {
	http.Redirect(w, r.Request, urlStr, code)
}

// StatusText returns a text for the HTTP status code. It returns the empty string if the code is unknown.
func StatusText(code int) string {
	return http.StatusText(code)
}
<commit_msg>Working on Register of Router<commit_after>package http

import (
	"github.com/gorilla/mux"
	"github.com/gorilla/pat"
	"net/http"
	"strings"
	"fmt"
)

var (
	routers = []*Router{}
)

// ObjectRester
type ObjectRester interface {
	GetRootURL() string
	Get(ResponseWriter, *Request) *Error
	Post(ResponseWriter, *Request) *Error
	Put(ResponseWriter, *Request) *Error
	Delete(ResponseWriter, *Request) *Error
}

// Router is a pat.Router which has a domain for handling CORS requests
// and hooks to execute some functions before executing the HandlerFunc.
type Router struct {
	*pat.Router
	domain string
	hooks  []HandlerFunc
}

// NewRouter returns a new Router.
func NewRouter(domain string) *Router {
	router := &Router{pat.New(), domain, make([]HandlerFunc, 0)}
	routers = append(routers, router)
	return router
}

// Domain returns the domain of the Router.
func (self *Router) Domain() string {
	return self.domain
}

// SetDomain set the domain of the Router.
func (self *Router) SetDomain(domain string) {
	self.domain = domain
}

// Add a function to be executed before serving HTTP.
func (self *Router) AddHooks(hooks ...HandlerFunc) { self.hooks = append(self.hooks, hooks...) }

// Register add a POST, GET, PUT and DELETE HandlerFunc to add, get, edit and delete an instance of this object.
// Example:
//
// 		type User struct {
//			Name string `json:"name"`
//		}
//
// 		var (
//			Jonathan = &User{"Jonathan"}
//			Vincent = &User{"Vincent"}
//		)
//
//		type UserHandler struct {}
//
//		func (handler *UserHandler) GetRootURL() string { return "users" }
//
//		func (handler *UserHandler) Get(w http.ResponseWriter, r *http.Request) *http.Error {
//			// Do something
//		}
//
//		func (handler *UserHandler) Post(w http.ResponseWriter, r *http.Request) *http.Error {
//			// Do something
//		}
//
//		func (handler *UserHandler) Put(w http.ResponseWriter, r *http.Request) *http.Error {
//			// Do something
//		}
//
//		func (handler *UserHandler) Delete(w http.ResponseWriter, r *http.Request) *http.Error {
//			// Do something
//		}
//
//		func main() {
//			r := http.NewRouter("example.com")
//			r.Register(&UserHandler{})
//			http.ListenAndServe(":8080", r)
//		}
//
func (self *Router) Register(object ObjectRester) {
	route := fmt.Sprintf("/%v", object.GetRootURL())
	self.Get(route, object.Get)
	self.Post(route, object.Post)
	self.Put(route, object.Put)
	self.Delete(route, object.Delete)
}

// runHooks run each hooks from the Router.
func (self *Router) runHooks(w ResponseWriter, r *Request) *Error {
	for _, hook := range self.hooks {
		if err := hook(w, r); err != nil {
			return err
		}
	}
	return nil
}

// Get registers a pattern with a handler for GET requests.
func (self *Router) Get(route string, h HandlerFunc) *mux.Route {
	return self.Add("GET", route, createHandler(h))
}

// Post registers a pattern with a handler for POST requests.
func (self *Router) Post(route string, h HandlerFunc) *mux.Route {
	return self.Add("POST", route, createHandler(h))
}

// Delete registers a pattern with a handler for DELETE requests.
func (self *Router) Delete(route string, h HandlerFunc) *mux.Route {
	return self.Add("DELETE", route, createHandler(h))
}

// Put registers a pattern with a handler for PUT requests.
func (self *Router) Put(route string, h HandlerFunc) *mux.Route {
	return self.Add("PUT", route, createHandler(h))
}

// ServeHTTP dispatches the handler registered in the matched route.
// It performs any hooks and add the domain registered in the Router to be allowed for cross-domain requests.
func (self *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	wr, rr := createResponseWriter(w), createRequest(r)
	if strings.ToLower(r.Method) == "options" {
		http.Redirect(wr, r, r.RequestURI, 200)
		return
	}
	wr.addCORSHeaders(self.domain)
	if err := self.runHooks(wr, rr); err != nil {
		wr.WriteError(err)
		return
	}
	self.Router.ServeHTTP(w, r)
}

// Redirect replies to the request with a redirect to url, which may be a path relative to the request path.
func Redirect(w ResponseWriter, r *Request, urlStr string, code int) {
	http.Redirect(w, r.Request, urlStr, code)
}

// StatusText returns a text for the HTTP status code. It returns the empty string if the code is unknown.
func StatusText(code int) string {
	return http.StatusText(code)
}
<|endoftext|>


<commit_before>package router

import (
	"github.com/gorilla/mux"
	"net/http"
)

// Route type is used to specify a specific endpoint.
type Route struct {
	Name       string
	Method     []string
	Pattern    string
	Handler    http.HandlerFunc
	Middleware []Middleware
}

type Middleware func(http.HandlerFunc) http.HandlerFunc

// Routes is used to bundle Route's
type Routes []Route

// Resource is the "parent" of routes, meaning that the pattern of the Routes will always be prefixed by the Path of the preceding resources.
type Resource struct {
	Path       string
	Resources  Resources
	Routes     Routes
	Middleware []Middleware
}

func (r Resource) resources() Resources {
	if r.Middleware == nil {
		return r.Resources
	}

	for i := range r.Resources {
		r.Resources[i].Middleware = append(r.Middleware, r.Resources[i].Middleware...)
	}

	return r.Resources
}

// Resources is used to bundle Resource's
type Resources []Resource

// CreateRouter Creates a router you can use to listen and serve
func CreateRouter(resources Resources) *mux.Router {
	r := mux.NewRouter().StrictSlash(true)
	createResources(resources, r)
	return r
}

func createResources(resources Resources, r *mux.Router) {
	if resources == nil {
		return
	}

	c := make(chan bool, len(resources))
	for _, resource := range resources {
		go func() {
			createSubRouter(resource, r)
			c <- true
		}()
	}

	for _ = range resources {
		<-c
	}
}

func createSubRouter(res Resource, r *mux.Router) {
	s := r.Path(res.Path).Subrouter()
	createResources(res.resources(), s)

	if res.Routes == nil {
		return
	}
	for _, route := range res.Routes {
		handler := createHandler(route.Handler, route.Middleware, res.Middleware)
		s.
			Methods(route.Method...).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)
	}

}

func createHandler(handler http.HandlerFunc, wrappers ...[]Middleware) http.HandlerFunc {
	for _, middlewares := range wrappers {
		for _, middleware := range middlewares {
			handler = middleware(handler)
		}
	}
	return handler
}
<commit_msg>Added comment<commit_after>package router

import (
	"github.com/gorilla/mux"
	"net/http"
)

// Route type is used to specify a specific endpoint.
type Route struct {
	Name       string
	Method     []string
	Pattern    string
	Handler    http.HandlerFunc
	Middleware []Middleware
}

// Middleware is a faux class for functions that wrap the handler
type Middleware func(http.HandlerFunc) http.HandlerFunc

// Routes is used to bundle Route's
type Routes []Route

// Resource is the "parent" of routes, meaning that the pattern of the Routes will always be prefixed by the Path of the preceding resources.
type Resource struct {
	Path       string
	Resources  Resources
	Routes     Routes
	Middleware []Middleware
}

func (r Resource) resources() Resources {
	if r.Middleware == nil {
		return r.Resources
	}

	for i := range r.Resources {
		r.Resources[i].Middleware = append(r.Middleware, r.Resources[i].Middleware...)
	}

	return r.Resources
}

// Resources is used to bundle Resource's
type Resources []Resource

// CreateRouter Creates a router you can use to listen and serve
func CreateRouter(resources Resources) *mux.Router {
	r := mux.NewRouter().StrictSlash(true)
	createResources(resources, r)
	return r
}

func createResources(resources Resources, r *mux.Router) {
	if resources == nil {
		return
	}

	c := make(chan bool, len(resources))
	for _, resource := range resources {
		go func() {
			createSubRouter(resource, r)
			c <- true
		}()
	}

	for _ = range resources {
		<-c
	}
}

func createSubRouter(res Resource, r *mux.Router) {
	s := r.Path(res.Path).Subrouter()
	createResources(res.resources(), s)

	if res.Routes == nil {
		return
	}
	for _, route := range res.Routes {
		handler := createHandler(route.Handler, route.Middleware, res.Middleware)
		s.
			Methods(route.Method...).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)
	}

}

func createHandler(handler http.HandlerFunc, wrappers ...[]Middleware) http.HandlerFunc {
	for _, middlewares := range wrappers {
		for _, middleware := range middlewares {
			handler = middleware(handler)
		}
	}
	return handler
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/r3b/goku"
	"github.com/r3b/usergrid-go-sdk"
	"log"
	"net/http"
	"net/smtp"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"runtime"
	"strconv"
	// "strings"
	"syscall"
	"text/template"
	"time"
)

type QueueItem struct {
	email string
	site  url.URL
}
type EmailUser struct {
	Username    string
	Password    string
	EmailServer string
	Port        int
}
type SmtpTemplateData struct {
	From    string
	To      string
	Subject string
	Site    string
}

const EMAILTEMPLATE = `From: {{.From}}
To: {{.To}}
Subject: {{.Subject}}

Your request for {{.Site}} has been processed. Check it out the game at http://fasterness.com/game and nice, legible charts at http://fasterness.com/speedreport.

Thanks for playing!

-ryan`

const EMAILTEMPLATEEXIT = `From: {{.From}}
To: {{.To}}
Subject: {{.Subject}}

You broke something.

-ryan`

var (
	CLIENT_ID      string            = os.Getenv("UG_CLIENT_ID")
	CLIENT_SECRET  string            = os.Getenv("UG_CLIENT_SECRET")
	EMAIL_USER     string            = os.Getenv("SR_EMAIL_USER")
	EMAIL_PASSWORD string            = os.Getenv("SR_EMAIL_PASSWORD")
	HOST           string            = os.Getenv("HOST")
	PORT           string            = os.Getenv("PORT")
	PHANTOMJS      string            = os.Getenv("PHANTOMJS")
	SPEEDREPORT    string            = os.Getenv("SR_SCRIPT")
	queue          *goku.Queue       = goku.New(1000)
	m0             *runtime.MemStats = &runtime.MemStats{}
	m1             *runtime.MemStats = &runtime.MemStats{}
	client         usergrid.Client   = usergrid.Client{Organization: "rbridges", Application: "speedreport", Uri: "http://api.usergrid.com"}
	emailUser      *EmailUser        = &EmailUser{
		EMAIL_USER,
		EMAIL_PASSWORD,
		"smtp.1and1.com",
		587,
	}
	bodyTemplate *template.Template
	auth         smtp.Auth
	t0           time.Time
)

func onExit() {
	defer os.Exit(1)
	log.Printf("Sending exit notifier.")
	context := &SmtpTemplateData{
		"speedreport@fasterness.com",
		"ryan@fasterness.com",
		"The server has exited.",
		"",
	}
	var doc bytes.Buffer
	err := bodyTemplate.Execute(&doc, context)
	if err != nil {
		log.Print("error trying to execute mail template")
	}
	err = smtp.SendMail(emailUser.EmailServer+":"+strconv.Itoa(emailUser.Port), // in our case, "smtp.google.com:587"
		auth,
		"speedreport@fasterness.com",
		[]string{"ryan@fasterness.com"},
		doc.Bytes())
	if err != nil {
		log.Print("ERROR: attempting to send a mail ", err)
	}
	stat("Exiting...")
}
func trap() {
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, os.Kill)
	signal.Notify(sig, syscall.SIGTERM)
	signal.Notify(sig, syscall.SIGABRT)
	stat("Starting up")
	for {
		s := <-sig
		log.Printf("Got signal: %n", s)
		log.Printf("Current queue size: %d\n", queue.Size())
		stat("Exiting...")
		os.Exit(0)
	}
}
func stat(message string) {
	log.Printf(message)
	runtime.ReadMemStats(m1)
	num := runtime.NumGoroutine()
	log.Printf("Number of goroutines: %d\n", num)
	log.Printf("Current total memory: %.2fmb\n", float64(m1.Sys)/(1024*1024))
	log.Printf("Per goroutine:\n")
	log.Printf("\tMemory: %.2f bytes\n", float64(m1.Sys-m0.Sys)/float64(num))
	log.Printf("\tTime:   %.2f µs\n", float64(time.Since(t0).Nanoseconds())/float64(num)/1e3)
}
func statJSON() (string, error) {
	runtime.ReadMemStats(m1)
	num := runtime.NumGoroutine()
	dur := time.Since(t0)
	things := map[string]interface{}{
		"goroutines":     num,
		"total_memory":   float64(m1.Sys) / (1024 * 1024),
		"uptime":         float64(dur.Seconds()),
		"uptime_hours":   dur.Hours(),
		"uptime_minutes": dur.Minutes(),
		"uptime_seconds": dur.Seconds(),
		"queue_depth":    queue.Size(),
	}

	body, err := json.Marshal(things)
	return string(body), err
}
func init() {
	go trap()
	if CLIENT_ID == "" || CLIENT_SECRET == "" {
		log.Fatalf("Usergrid authentication information not found.")
	}
	if EMAIL_USER == "" || EMAIL_PASSWORD == "" {
		log.Fatalf("Email authentication information not found.")
	}
	err := client.OrgLogin(CLIENT_ID, CLIENT_SECRET)
	if err != nil {
		log.Fatalf(err.Error())
	}
	if PHANTOMJS == "" {
		PHANTOMJS = "phantomjs"
	}
	if SPEEDREPORT == "" {
		SPEEDREPORT = "/home/ubuntu/speedreport/speedreport.js"
	}
	auth = smtp.PlainAuth("", emailUser.Username, emailUser.Password, emailUser.EmailServer)
	t := template.New("EMAILTEMPLATE")
	bodyTemplate, err = t.Parse(EMAILTEMPLATE)
	if err != nil {
		log.Panicf("error trying to parse mail template")
	}
	t0 = time.Now().UTC()

}
func main() {
	defer onExit()
	waiting, awaiting_notification, completed := make(chan *QueueItem), make(chan *QueueItem), make(chan *QueueItem)

	go QueueReader(waiting)
	go TestRunner(waiting, awaiting_notification)
	go Notifier(awaiting_notification, completed)

	mux := pat.New()
	mux.Get("/test", http.HandlerFunc(TestHandler))
	mux.Get("/status", http.HandlerFunc(StatusHandler))
	mux.Options("/test", http.HandlerFunc(OptionsHandler))
	mux.Options("/status", http.HandlerFunc(OptionsHandler))
	http.Handle("/", mux)

	stat("Server is ready.")
	if HOST == "" {
		HOST = "127.0.0.1"
	}
	if PORT == "" {
		PORT = "80"
	}
	http.ListenAndServe(fmt.Sprintf("%s:%s", HOST, PORT), nil)
}

func AlreadyInQueue(queue goku.Queue, address url.URL) bool {
	for i := range queue.Items {
		item := queue.GetItem(i).(QueueItem)
		if address.String() == item.site.String() {
			return true
		}
	}
	return false
}
func SaveRequest(request QueueItem) {
	var objmap interface{}
	data := map[string]string{
		"url":   request.site.String(),
		"email": request.email,
	}
	log.Printf("Saving request: %s\n", data)
	err := client.Post("requests", nil, data, usergrid.JSONResponseHandler(&objmap))
	if err != nil {
		log.Printf("Error saving request: %v\n", err)
		// } else {
		// 	log.Printf("Saved request: %v\n", objmap)
	}
}
func QueueReader(out chan<- *QueueItem) {
	for {
		if queue.IsEmpty() {
			time.Sleep(30 * time.Second)
		} else {
			item := queue.Dequeue()
			qitem := item.(QueueItem)
			go SaveRequest(qitem)
			log.Printf("Request for %v", qitem)
			out <- &qitem
		}
	}
}

func TestRunner(in <-chan *QueueItem, out chan<- *QueueItem) {
	for {
		select {
		case v := <-in:
			//run program
			go func() {
				log.Printf("Testing %v", v)
				cmd := exec.Command(PHANTOMJS, SPEEDREPORT, "--url="+v.site.String())
				var output bytes.Buffer
				cmd.Stdout = &output
				if err := cmd.Run(); err != nil {
					log.Println(err)
				}
				// v.uuid = strings.TrimSpace(output.String())
				// log.Println(v.uuid)
				out <- v
			}()
		default:
			time.Sleep(5 * time.Second)
		}

	}
}
func Notifier(in <-chan *QueueItem, out chan<- *QueueItem) {
	for {
		select {
		case v := <-in:
			go func() {
				log.Printf("Notifying %v", v.email)

				context := &SmtpTemplateData{
					"speedreport@fasterness.com",
					v.email,
					"Your Web Performance adventure is waiting.",
					v.site.String(),
				}
				var doc bytes.Buffer
				err := bodyTemplate.Execute(&doc, context)
				if err != nil {
					log.Print("error trying to execute mail template")
				}
				err = smtp.SendMail(emailUser.EmailServer+":"+strconv.Itoa(emailUser.Port), // in our case, "smtp.google.com:587"
					auth,
					"speedreport@fasterness.com",
					[]string{v.email},
					doc.Bytes())
				if err != nil {
					log.Print("ERROR: attempting to send a mail ", err)
				} else {
					log.Printf("Notification to %v was sent successfully", v.email)
				}
				out <- v
			}()
		default:
			time.Sleep(5 * time.Second)
		}
	}
}
func OptionsHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
	writer.Header().Add("Access-Control-Allow-Headers", "Content-Type")
	writer.Header().Add("Access-Control-Allow-Origin", "*")
	writer.Header().Add("Content-Type", "application/json")
	writer.Write([]byte(""))
}
func StatusHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Add("Access-Control-Allow-Origin", "*")
	writer.Header().Add("Content-Type", "application/json")
	status, _ := statJSON()
	writer.Write([]byte(status))
}
func TestHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Add("Access-Control-Allow-Origin", "*")
	writer.Header().Add("Content-Type", "application/json")
	params := request.URL.Query()
	testUrl := params.Get(":url")
	if testUrl == "" {
		testUrl = params.Get("url")
	}

	email := params.Get(":email")
	if email == "" {
		email = params.Get("email")
	}

	if testUrl == "" {
		http.Error(writer, "no URL specified", http.StatusNotAcceptable)
	} else {

		testUrl, _ := url.QueryUnescape(testUrl)
		matched, err := regexp.MatchString("/^https?://", testUrl)
		if !matched {
			testUrl = "http://" + testUrl
		}
		address, err := url.Parse(testUrl)
		if err != nil || address.Host == "" {
			http.Error(writer, fmt.Sprintf("Could not parse URL: %s", testUrl), http.StatusBadRequest)
			return
		}
		if address.Scheme == "" {
			address.Scheme = "http"
		}
		if AlreadyInQueue(*queue, *address) {
			writer.Write([]byte(fmt.Sprintf("%s is already in the queue", testUrl)))
			return
		}
		email, _ := url.QueryUnescape(email)
		item := QueueItem{site: *address, email: email}
		queue.Enqueue(item)
		// waiting <- item
		response := "Testing " + testUrl
		if email != "" {
			response += "\nWe will email the results to " + email
		}
		response += fmt.Sprintf("\nNumber %d in queue", queue.Size())
		writer.Write([]byte(response))
	}
}
<commit_msg>Refactored CORS middleware into its own package<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/fasterness/cors"
	"github.com/r3b/goku"
	"github.com/r3b/usergrid-go-sdk"
	"log"
	"net/http"
	"net/smtp"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"runtime"
	"strconv"
	// "strings"
	"syscall"
	"text/template"
	"time"
)

type QueueItem struct {
	email string
	site  url.URL
}
type EmailUser struct {
	Username    string
	Password    string
	EmailServer string
	Port        int
}
type SmtpTemplateData struct {
	From    string
	To      string
	Subject string
	Site    string
}

const EMAILTEMPLATE = `From: {{.From}}
To: {{.To}}
Subject: {{.Subject}}

Your request for {{.Site}} has been processed. Check it out the game at http://fasterness.com/game and nice, legible charts at http://fasterness.com/speedreport.

Thanks for playing!

-ryan`

const EMAILTEMPLATEEXIT = `From: {{.From}}
To: {{.To}}
Subject: {{.Subject}}

You broke something.

-ryan`

var (
	CLIENT_ID      string            = os.Getenv("UG_CLIENT_ID")
	CLIENT_SECRET  string            = os.Getenv("UG_CLIENT_SECRET")
	EMAIL_USER     string            = os.Getenv("SR_EMAIL_USER")
	EMAIL_PASSWORD string            = os.Getenv("SR_EMAIL_PASSWORD")
	HOST           string            = os.Getenv("HOST")
	PORT           string            = os.Getenv("PORT")
	PHANTOMJS      string            = os.Getenv("PHANTOMJS")
	SPEEDREPORT    string            = os.Getenv("SR_SCRIPT")
	queue          *goku.Queue       = goku.New(1000)
	m0             *runtime.MemStats = &runtime.MemStats{}
	m1             *runtime.MemStats = &runtime.MemStats{}
	client         usergrid.Client   = usergrid.Client{Organization: "rbridges", Application: "speedreport", Uri: "http://api.usergrid.com"}
	emailUser      *EmailUser        = &EmailUser{
		EMAIL_USER,
		EMAIL_PASSWORD,
		"smtp.1and1.com",
		587,
	}
	bodyTemplate *template.Template
	auth         smtp.Auth
	t0           time.Time
)

func onExit() {
	defer os.Exit(1)
	log.Printf("Sending exit notifier.")
	context := &SmtpTemplateData{
		"speedreport@fasterness.com",
		"ryan@fasterness.com",
		"The server has exited.",
		"",
	}
	var doc bytes.Buffer
	err := bodyTemplate.Execute(&doc, context)
	if err != nil {
		log.Print("error trying to execute mail template")
	}
	err = smtp.SendMail(emailUser.EmailServer+":"+strconv.Itoa(emailUser.Port), // in our case, "smtp.google.com:587"
		auth,
		"speedreport@fasterness.com",
		[]string{"ryan@fasterness.com"},
		doc.Bytes())
	if err != nil {
		log.Print("ERROR: attempting to send a mail ", err)
	}
	stat("Exiting...")
}
func trap() {
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, os.Kill)
	signal.Notify(sig, syscall.SIGTERM)
	signal.Notify(sig, syscall.SIGABRT)
	stat("Starting up")
	for {
		s := <-sig
		log.Printf("Got signal: %n", s)
		log.Printf("Current queue size: %d\n", queue.Size())
		stat("Exiting...")
		os.Exit(0)
	}
}
func stat(message string) {
	log.Printf(message)
	runtime.ReadMemStats(m1)
	num := runtime.NumGoroutine()
	log.Printf("Number of goroutines: %d\n", num)
	log.Printf("Current total memory: %.2fmb\n", float64(m1.Sys)/(1024*1024))
	log.Printf("Per goroutine:\n")
	log.Printf("\tMemory: %.2f bytes\n", float64(m1.Sys-m0.Sys)/float64(num))
	log.Printf("\tTime:   %.2f µs\n", float64(time.Since(t0).Nanoseconds())/float64(num)/1e3)
}
func statJSON() (string, error) {
	runtime.ReadMemStats(m1)
	num := runtime.NumGoroutine()
	dur := time.Since(t0)
	things := map[string]interface{}{
		"goroutines":     num,
		"total_memory":   float64(m1.Sys) / (1024 * 1024),
		"uptime":         float64(dur.Seconds()),
		"uptime_hours":   dur.Hours(),
		"uptime_minutes": dur.Minutes(),
		"uptime_seconds": dur.Seconds(),
		"queue_depth":    queue.Size(),
	}

	body, err := json.Marshal(things)
	return string(body), err
}
func init() {
	go trap()
	if CLIENT_ID == "" || CLIENT_SECRET == "" {
		log.Fatalf("Usergrid authentication information not found.")
	}
	if EMAIL_USER == "" || EMAIL_PASSWORD == "" {
		log.Fatalf("Email authentication information not found.")
	}
	err := client.OrgLogin(CLIENT_ID, CLIENT_SECRET)
	if err != nil {
		log.Fatalf(err.Error())
	}
	if PHANTOMJS == "" {
		PHANTOMJS = "phantomjs"
	}
	if SPEEDREPORT == "" {
		SPEEDREPORT = "/home/ubuntu/speedreport/speedreport.js"
	}
	auth = smtp.PlainAuth("", emailUser.Username, emailUser.Password, emailUser.EmailServer)
	t := template.New("EMAILTEMPLATE")
	bodyTemplate, err = t.Parse(EMAILTEMPLATE)
	if err != nil {
		log.Panicf("error trying to parse mail template")
	}
	t0 = time.Now().UTC()

}
func main() {
	defer onExit()
	waiting, awaiting_notification, completed := make(chan *QueueItem), make(chan *QueueItem), make(chan *QueueItem)

	go QueueReader(waiting)
	go TestRunner(waiting, awaiting_notification)
	go Notifier(awaiting_notification, completed)

	mux := pat.New()
	mux.Get("/test", http.HandlerFunc(TestHandler))
	mux.Get("/status", http.HandlerFunc(StatusHandler))
	mux.Options("/test", http.HandlerFunc(OptionsHandler))
	mux.Options("/status", http.HandlerFunc(OptionsHandler))
	handler := cors.New(mux)
	handler.RemoveMethod("PUT")
	handler.RemoveMethod("DELETE")
	http.Handle("/", handler)

	stat("Server is ready.")
	if HOST == "" {
		HOST = "127.0.0.1"
	}
	if PORT == "" {
		PORT = "80"
	}
	http.ListenAndServe(fmt.Sprintf("%s:%s", HOST, PORT), nil)
}

func AlreadyInQueue(queue goku.Queue, address url.URL) bool {
	for i := range queue.Items {
		item := queue.GetItem(i).(QueueItem)
		if address.String() == item.site.String() {
			return true
		}
	}
	return false
}
func SaveRequest(request QueueItem) {
	var objmap interface{}
	data := map[string]string{
		"url":   request.site.String(),
		"email": request.email,
	}
	log.Printf("Saving request: %s\n", data)
	err := client.Post("requests", nil, data, usergrid.JSONResponseHandler(&objmap))
	if err != nil {
		log.Printf("Error saving request: %v\n", err)
	}
}
func QueueReader(out chan<- *QueueItem) {
	for {
		if queue.IsEmpty() {
			time.Sleep(30 * time.Second)
		} else {
			item := queue.Dequeue()
			qitem := item.(QueueItem)
			go SaveRequest(qitem)
			log.Printf("Request for %v", qitem)
			out <- &qitem
		}
	}
}

func TestRunner(in <-chan *QueueItem, out chan<- *QueueItem) {
	for {
		select {
		case v := <-in:
			//run program
			go func() {
				log.Printf("Testing %v", v)
				cmd := exec.Command(PHANTOMJS, SPEEDREPORT, "--url="+v.site.String())
				var output bytes.Buffer
				cmd.Stdout = &output
				if err := cmd.Run(); err != nil {
					log.Println(err)
				}
				// v.uuid = strings.TrimSpace(output.String())
				// log.Println(v.uuid)
				out <- v
			}()
		default:
			time.Sleep(5 * time.Second)
		}

	}
}
func Notifier(in <-chan *QueueItem, out chan<- *QueueItem) {
	for {
		select {
		case v := <-in:
			go func() {
				log.Printf("Notifying %v", v.email)

				context := &SmtpTemplateData{
					"speedreport@fasterness.com",
					v.email,
					"Your Web Performance adventure is waiting.",
					v.site.String(),
				}
				var doc bytes.Buffer
				err := bodyTemplate.Execute(&doc, context)
				if err != nil {
					log.Print("error trying to execute mail template")
				}
				err = smtp.SendMail(emailUser.EmailServer+":"+strconv.Itoa(emailUser.Port), // in our case, "smtp.google.com:587"
					auth,
					"speedreport@fasterness.com",
					[]string{v.email},
					doc.Bytes())
				if err != nil {
					log.Print("ERROR: attempting to send a mail ", err)
				} else {
					log.Printf("Notification to %v was sent successfully", v.email)
				}
				out <- v
			}()
		default:
			time.Sleep(5 * time.Second)
		}
	}
}
func OptionsHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Write([]byte(""))
}
func StatusHandler(writer http.ResponseWriter, request *http.Request) {
	status, _ := statJSON()
	writer.Write([]byte(status))
}
func TestHandler(writer http.ResponseWriter, request *http.Request) {
	params := request.URL.Query()
	testUrl := params.Get(":url")
	if testUrl == "" {
		testUrl = params.Get("url")
	}

	email := params.Get(":email")
	if email == "" {
		email = params.Get("email")
	}

	if testUrl == "" {
		http.Error(writer, "no URL specified", http.StatusNotAcceptable)
	} else {

		testUrl, _ := url.QueryUnescape(testUrl)
		matched, err := regexp.MatchString("/^https?://", testUrl)
		if !matched {
			testUrl = "http://" + testUrl
		}
		address, err := url.Parse(testUrl)
		if err != nil || address.Host == "" {
			http.Error(writer, fmt.Sprintf("Could not parse URL: %s", testUrl), http.StatusBadRequest)
			return
		}
		if address.Scheme == "" {
			address.Scheme = "http"
		}
		if AlreadyInQueue(*queue, *address) {
			writer.Write([]byte(fmt.Sprintf("%s is already in the queue", testUrl)))
			return
		}
		email, _ := url.QueryUnescape(email)
		item := QueueItem{site: *address, email: email}
		queue.Enqueue(item)
		// waiting <- item
		response := "Testing " + testUrl
		if email != "" {
			response += "\nWe will email the results to " + email
		}
		response += fmt.Sprintf("\nNumber %d in queue", queue.Size())
		writer.Write([]byte(response))
	}
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "html"
    "net/http"
    "strconv"
    "time"
)

type Pet struct {
    PetId       int     `json:"pet_id"`
    Breed       string  `json:"breed"`
    Name        string  `json:"name"`
    DispenserId int     `json:"dispenser_id"`
    DongleId    int     `json:"dongle_id"`
    Status      int     `json:"status"`
}

type Event struct {
    Event       string  `json:"event"`
    Id          int     `json:"id,omitempty"`
    PetId       int     `json:"pet_id"`
    Timestamp   int     `json:"timestamp,omitempty"`
    Valid       string  `json:"valid,omitempty"`
    Weight      int     `json:"weight"`
}

type Meal struct {
    EndDate     time.Time  `json:"end_date"`
    Duration    float32 `json:"duration"`
    Quantity    int      `json:"quantity"`
    StartDate   time.Time  `json:"start_date"`
}

type Statistics struct {
    PetId int
    Meals []Meal
}

func NewRouter() (*mux.Router) {
    router := mux.NewRouter().StrictSlash(false)
    for _, route := range routes {
        var handler http.Handler
        handler = route.HandlerFunc
        handler = Logger(handler, route.Name)

        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(handler)
    }

    return router
}

func hello (w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}

func RegisterEvent (w http.ResponseWriter, r *http.Request) {
    var event Event

    enc := json.NewEncoder(w)
    dec := json.NewDecoder(r.Body)
    err := dec.Decode(&event)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }

    err = RegisterEventDB(event)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }

    enc.Encode(event)
    fmt.Println(event)
}

func PetDailyStats (w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    petId, _ := strconv.Atoi(vars["petId"])

    stats, err := DailyStats(petId)
    if err != nil {
        fmt.Println("Error: ", err)
        json.NewEncoder(w).Encode(err)
        return
    }
    json.NewEncoder(w).Encode(stats)
}

func Config (w http.ResponseWriter, r *http.Request) {
    pets, err := GetConfig()
    if err != nil {
        fmt.Println("Error: ", err)
        json.NewEncoder(w).Encode(err)
        return
    }
    json.NewEncoder(w).Encode(pets)
}

func StopFood (w http.ResponseWriter, r *http.Request) {
    var status int
    vars := mux.Vars(r)
    petId, _ := strconv.Atoi(vars["petId"])
    more, _ := vars["yesno"]
    if more == "yes" {
        status = 1
    } else {
        status = 0
    }

    err := UpdatePetStatus(petId, status)
    if err != nil {
        fmt.Println("Error: ", err)
        json.NewEncoder(w).Encode(err)
        return
    }
}

func LovePet (w http.ResponseWriter, r *http.Request) {
    var pet Pet

    enc := json.NewEncoder(w)
    dec := json.NewDecoder(r.Body)
    err := dec.Decode(&pet)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }

    err = AddPet(pet)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }
    enc.Encode(pet)
    fmt.Println(pet)
}
<commit_msg>Add Luisus cross-*hit<commit_after>package main

import (
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "html"
    "net/http"
    "strconv"
    "time"
)

type Pet struct {
    PetId       int     `json:"pet_id"`
    Breed       string  `json:"breed"`
    Name        string  `json:"name"`
    DispenserId int     `json:"dispenser_id"`
    DongleId    int     `json:"dongle_id"`
    Status      int     `json:"status"`
}

type Event struct {
    Event       string  `json:"event"`
    Id          int     `json:"id,omitempty"`
    PetId       int     `json:"pet_id"`
    Timestamp   int     `json:"timestamp,omitempty"`
    Valid       string  `json:"valid,omitempty"`
    Weight      int     `json:"weight"`
}

type Meal struct {
    EndDate     time.Time  `json:"end_date"`
    Duration    float32 `json:"duration"`
    Quantity    int      `json:"quantity"`
    StartDate   time.Time  `json:"start_date"`
}

type Statistics struct {
    PetId int
    Meals []Meal
}

func NewRouter() (*mux.Router) {
    router := mux.NewRouter().StrictSlash(false)
    for _, route := range routes {
        var handler http.Handler
        handler = route.HandlerFunc
        handler = Logger(handler, route.Name)

        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(handler)
    }

    return router
}

func hello (w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "*")
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}

func RegisterEvent (w http.ResponseWriter, r *http.Request) {
    var event Event

    enc := json.NewEncoder(w)
    dec := json.NewDecoder(r.Body)
    err := dec.Decode(&event)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }

    err = RegisterEventDB(event)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }

    w.Header().Set("Access-Control-Allow-Origin", "*")
    enc.Encode(event)
    fmt.Println(event)
}

func PetDailyStats (w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    petId, _ := strconv.Atoi(vars["petId"])

    stats, err := DailyStats(petId)
    if err != nil {
        fmt.Println("Error: ", err)
        json.NewEncoder(w).Encode(err)
        return
    }
    w.Header().Set("Access-Control-Allow-Origin", "*")
    json.NewEncoder(w).Encode(stats)
}

func Config (w http.ResponseWriter, r *http.Request) {
    pets, err := GetConfig()
    if err != nil {
        fmt.Println("Error: ", err)
        json.NewEncoder(w).Encode(err)
        return
    }
    w.Header().Set("Access-Control-Allow-Origin", "*")
    json.NewEncoder(w).Encode(pets)
}

func StopFood (w http.ResponseWriter, r *http.Request) {
    var status int
    vars := mux.Vars(r)
    petId, _ := strconv.Atoi(vars["petId"])
    more, _ := vars["yesno"]
    if more == "yes" {
        status = 1
    } else {
        status = 0
    }

    err := UpdatePetStatus(petId, status)
    if err != nil {
        fmt.Println("Error: ", err)
        w.Header().Set("Access-Control-Allow-Origin", "*")
        json.NewEncoder(w).Encode(err)
        return
    }
    w.Header().Set("Access-Control-Allow-Origin", "*")
}

func LovePet (w http.ResponseWriter, r *http.Request) {
    var pet Pet

    enc := json.NewEncoder(w)
    dec := json.NewDecoder(r.Body)
    err := dec.Decode(&pet)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }

    err = AddPet(pet)
    if err != nil {
        fmt.Println("Error: ", err)
        enc.Encode(err)
        return
    }
    w.Header().Set("Access-Control-Allow-Origin", "*")
    enc.Encode(pet)
    fmt.Println(pet)
}
<|endoftext|>