<commit_before>package h5

import (
	"testing"
	"testing/util"
	"fmt"
	"os"
)

func TestPushNode(t *testing.T) {
	p := new(Parser)
	util.AssertTrue(t, p.Top == nil, "Top is not nil")
	util.AssertTrue(t, p.curr == nil, "curr is not nil")
	top := pushNode(p)
	top.data = append(top.data, []int("foo")...)
	util.AssertTrue(t, p.Top != nil, "Top is still nil")
	util.AssertTrue(t, p.curr != nil, "curr is stil nil")
	util.AssertEqual(t, p.Top, top)
	util.AssertEqual(t, p.curr, top)
	next := pushNode(p)
	next.data = append(next.data, []int("bar")...)
	util.AssertEqual(t, len(top.Children), 1)
	util.AssertEqual(t, p.Top, top)
	util.AssertEqual(t, p.curr, next)
	util.AssertEqual(t, p.curr.Parent, p.Top)
}

func TestPopNode(t *testing.T) {
	p := new(Parser)
	top := pushNode(p)
	top.data = append(top.data, []int("foo")...)
	next := pushNode(p)
	next.data = append(next.data, []int("bar")...)
	popped := popNode(p)
	util.AssertEqual(t, popped, top)
	util.AssertEqual(t, p.Top, p.curr)
}

func TestAddSibling(t *testing.T) {
	p := new(Parser)
	top := pushNode(p)
	top.data = append(top.data, []int("foo")...)
	next := pushNode(p)
	next.data = append(next.data, []int("bar")...)
	sib := addSibling(p)
	sib.data = append(sib.data, []int("baz")...)
	util.AssertEqual(t, len(top.Children), 2)
	util.AssertEqual(t, top.Children[0], next)
	util.AssertEqual(t, top.Children[1], sib)
}

func TestBogusCommentHandlerNoEOF(t *testing.T) {
	p := NewParserFromString("foo comment >")
	top := pushNode(p)
	pushNode(p)
	st, err := bogusCommentHandler(p)
	util.AssertEqual(t, len(top.Children), 2)
	util.AssertEqual(t, string(top.Children[1].data), "foo comment ")
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
}

// TODO error cases
func TestBogusCommentHandlerEOF(t *testing.T) {
	p := NewParserFromString("foo comment")
	top := pushNode(p)
	pushNode(p)
	st, err := bogusCommentHandler(p)
	util.AssertEqual(t, len(top.Children), 2)
	util.AssertEqual(t, string(top.Children[1].data), "foo comment")
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
}

func TestEndTagOpenHandlerOk(t *testing.T) {
	p := NewParserFromString("FoO>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertEqual(t, err, nil)
	util.AssertTrue(t, err == nil, "err is not nil")
	//util.AssertTrue(t, p.curr == nil, "did not pop node")
}

func TestEndTagOpenHandlerTrunc(t *testing.T) {
	p := NewParserFromString("fo>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerLong(t *testing.T) {
	p := NewParserFromString("fooo>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerWrong(t *testing.T) {
	p := NewParserFromString("bar>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerBogusComment(t *testing.T) {
	p := NewParserFromString("f\no>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st != nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerEOF(t *testing.T) {
	p := NewParserFromString("foo")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestTagNameHandler(t *testing.T) {
	p := NewParserFromString("f> ")
	curr := pushNode(p)
	st, err := handleChar(tagNameHandler)(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
	util.AssertEqual(t, curr.data[0], 'f')
	st, err = handleChar(tagNameHandler)(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
	util.AssertEqual(t, curr.data[0], 'f')
	p = NewParserFromString("F")
	curr = pushNode(p)
	st, err = handleChar(tagNameHandler)(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
	util.AssertEqual(t, curr.data[0], 'f')
}

func TestTagOpenHandler(t *testing.T) {
	p := NewParserFromString("")
	st := tagOpenHandler(p, 'f')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertEqual(t, st, handleChar(tagNameHandler))
	util.AssertEqual(t, p.curr.data[0], 'f')
	util.AssertEqual(t, p.curr.Type, ElementNode)
}

func TestTagOpenHandlerEndTag(t *testing.T) {
	p := NewParserFromString("")
	st := tagOpenHandler(p, '/')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	//util.AssertEqual(t, st, endTagOpenHandler)
}

func TestDataStateHandler(t *testing.T) {
	p := NewParserFromString("")
	st := dataStateHandler(p, '<')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertEqual(t, st, handleChar(tagOpenHandler))
	util.AssertTrue(t, p.curr == nil, "curr is currently nil")
	util.AssertTrue(t, p.Top == nil, "Top is currently nil")
	p = NewParserFromString("oo<")
	st = dataStateHandler(p, 'f')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, p.curr != nil, "curr is currently nil")
	util.AssertTrue(t, p.Top != nil, "Top is currently nil")
	util.AssertEqual(t, p.curr.data, []int("foo"))
}

func TestSimpledoc(t *testing.T) {
	p := NewParserFromString("foo")
	err := p.Parse()
	util.AssertTrue(t, err == nil, "err is not nil: %v", err)
	fmt.Printf("XXX doc: %s\n", p.Top)
	util.AssertEqual(t, p.Top.Data(), "html")
	util.AssertEqual(t, len(p.Top.Children), 1)
	util.AssertEqual(t, len(p.Top.Children[0].Children), 1)
	util.AssertEqual(t, p.Top.Children[0].Data(), "body")
	util.AssertEqual(t, p.Top.Children[0].Children[0].Data(), "foo")
}

func TestScriptDoc(t *testing.T) {
	p := NewParserFromString(
		"")
	err := p.Parse()
	util.AssertTrue(t, err == nil, "err is not nil: %v", err)
	//fmt.Printf("XXX doc: %s\n", p.Top)
	util.AssertEqual(t, p.Top.Data(), "html")
	util.AssertEqual(t, len(p.Top.Children), 1)
	util.AssertEqual(t, p.Top.Children[0].Data(), "body")
	util.AssertEqual(t, len(p.Top.Children[0].Children), 1)
	util.AssertEqual(t, p.Top.Children[0].Children[0].Data(), "script")
	util.AssertEqual(t, p.Top.Children[0].Children[0].Children[0].Data(),
		" if (foo < 10) { }")
}

func TestSimpledocSiblings(t *testing.T) {
	p := NewParserFromString(
		"foo
bar
")
	err := p.Parse()
	util.AssertTrue(t, err == nil, "err is not nil: %v", err)
	//fmt.Printf("XXX doc: %s\n", p.Top)
	util.AssertEqual(t, p.Top.Data(), "html")
	util.AssertEqual(t, len(p.Top.Children), 1)
	util.AssertEqual(t, len(p.Top.Children[0].Children), 2)
	util.AssertEqual(t, p.Top.Children[0].Data(), "body")
	util.AssertEqual(t, p.Top.Children[0].Children[0].Data(), "a")
}

func TestParseFromReader(t *testing.T) {
	rdr, err := os.Open("test_data/page.html")
	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}
	p := NewParser(rdr)
	err = p.Parse()
	if err != nil {
		util.AssertTrue(t, false, "Failed to parse")
	}
	util.AssertTrue(t, p.Top != nil, "We got a parse tree back")
	fmt.Println("Doc: ", p.Top.String())
}
<commit_msg>comment out a debugging comment.<commit_after>package h5

import (
	"testing"
	"testing/util"
	"fmt"
	"os"
)

func TestPushNode(t *testing.T) {
	p := new(Parser)
	util.AssertTrue(t, p.Top == nil, "Top is not nil")
	util.AssertTrue(t, p.curr == nil, "curr is not nil")
	top := pushNode(p)
	top.data = append(top.data, []int("foo")...)
	util.AssertTrue(t, p.Top != nil, "Top is still nil")
	util.AssertTrue(t, p.curr != nil, "curr is stil nil")
	util.AssertEqual(t, p.Top, top)
	util.AssertEqual(t, p.curr, top)
	next := pushNode(p)
	next.data = append(next.data, []int("bar")...)
	util.AssertEqual(t, len(top.Children), 1)
	util.AssertEqual(t, p.Top, top)
	util.AssertEqual(t, p.curr, next)
	util.AssertEqual(t, p.curr.Parent, p.Top)
}

func TestPopNode(t *testing.T) {
	p := new(Parser)
	top := pushNode(p)
	top.data = append(top.data, []int("foo")...)
	next := pushNode(p)
	next.data = append(next.data, []int("bar")...)
	popped := popNode(p)
	util.AssertEqual(t, popped, top)
	util.AssertEqual(t, p.Top, p.curr)
}

func TestAddSibling(t *testing.T) {
	p := new(Parser)
	top := pushNode(p)
	top.data = append(top.data, []int("foo")...)
	next := pushNode(p)
	next.data = append(next.data, []int("bar")...)
	sib := addSibling(p)
	sib.data = append(sib.data, []int("baz")...)
	util.AssertEqual(t, len(top.Children), 2)
	util.AssertEqual(t, top.Children[0], next)
	util.AssertEqual(t, top.Children[1], sib)
}

func TestBogusCommentHandlerNoEOF(t *testing.T) {
	p := NewParserFromString("foo comment >")
	top := pushNode(p)
	pushNode(p)
	st, err := bogusCommentHandler(p)
	util.AssertEqual(t, len(top.Children), 2)
	util.AssertEqual(t, string(top.Children[1].data), "foo comment ")
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
}

// TODO error cases
func TestBogusCommentHandlerEOF(t *testing.T) {
	p := NewParserFromString("foo comment")
	top := pushNode(p)
	pushNode(p)
	st, err := bogusCommentHandler(p)
	util.AssertEqual(t, len(top.Children), 2)
	util.AssertEqual(t, string(top.Children[1].data), "foo comment")
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
}

func TestEndTagOpenHandlerOk(t *testing.T) {
	p := NewParserFromString("FoO>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertEqual(t, err, nil)
	util.AssertTrue(t, err == nil, "err is not nil")
	//util.AssertTrue(t, p.curr == nil, "did not pop node")
}

func TestEndTagOpenHandlerTrunc(t *testing.T) {
	p := NewParserFromString("fo>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerLong(t *testing.T) {
	p := NewParserFromString("fooo>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerWrong(t *testing.T) {
	p := NewParserFromString("bar>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerBogusComment(t *testing.T) {
	p := NewParserFromString("f\no>")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st != nil, "next state handler is not nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestEndTagOpenHandlerEOF(t *testing.T) {
	p := NewParserFromString("foo")
	curr := pushNode(p)
	curr.data = []int("foo")
	util.AssertTrue(t, p.curr != nil, "curr is not nil")
	st, err := endTagOpenHandler(p)
	util.AssertTrue(t, st == nil, "next state handler is nil")
	util.AssertTrue(t, err != nil, "err is nil")
	util.AssertEqual(t, p.curr, curr)
}

func TestTagNameHandler(t *testing.T) {
	p := NewParserFromString("f> ")
	curr := pushNode(p)
	st, err := handleChar(tagNameHandler)(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
	util.AssertEqual(t, curr.data[0], 'f')
	st, err = handleChar(tagNameHandler)(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
	util.AssertEqual(t, curr.data[0], 'f')
	p = NewParserFromString("F")
	curr = pushNode(p)
	st, err = handleChar(tagNameHandler)(p)
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, err == nil, "err is not nil")
	util.AssertEqual(t, curr.data[0], 'f')
}

func TestTagOpenHandler(t *testing.T) {
	p := NewParserFromString("")
	st := tagOpenHandler(p, 'f')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertEqual(t, st, handleChar(tagNameHandler))
	util.AssertEqual(t, p.curr.data[0], 'f')
	util.AssertEqual(t, p.curr.Type, ElementNode)
}

func TestTagOpenHandlerEndTag(t *testing.T) {
	p := NewParserFromString("")
	st := tagOpenHandler(p, '/')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	//util.AssertEqual(t, st, endTagOpenHandler)
}

func TestDataStateHandler(t *testing.T) {
	p := NewParserFromString("")
	st := dataStateHandler(p, '<')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertEqual(t, st, handleChar(tagOpenHandler))
	util.AssertTrue(t, p.curr == nil, "curr is currently nil")
	util.AssertTrue(t, p.Top == nil, "Top is currently nil")
	p = NewParserFromString("oo<")
	st = dataStateHandler(p, 'f')
	util.AssertTrue(t, st != nil, "next state handler is nil")
	util.AssertTrue(t, p.curr != nil, "curr is currently nil")
	util.AssertTrue(t, p.Top != nil, "Top is currently nil")
	util.AssertEqual(t, p.curr.data, []int("foo"))
}

func TestSimpledoc(t *testing.T) {
	p := NewParserFromString("foo")
	err := p.Parse()
	util.AssertTrue(t, err == nil, "err is not nil: %v", err)
	//fmt.Printf("XXX doc: %s\n", p.Top)
	util.AssertEqual(t, p.Top.Data(), "html")
	util.AssertEqual(t, len(p.Top.Children), 1)
	util.AssertEqual(t, len(p.Top.Children[0].Children), 1)
	util.AssertEqual(t, p.Top.Children[0].Data(), "body")
	util.AssertEqual(t, p.Top.Children[0].Children[0].Data(), "foo")
}

func TestScriptDoc(t *testing.T) {
	p := NewParserFromString(
		"")
	err := p.Parse()
	util.AssertTrue(t, err == nil, "err is not nil: %v", err)
	//fmt.Printf("XXX doc: %s\n", p.Top)
	util.AssertEqual(t, p.Top.Data(), "html")
	util.AssertEqual(t, len(p.Top.Children), 1)
	util.AssertEqual(t, p.Top.Children[0].Data(), "body")
	util.AssertEqual(t, len(p.Top.Children[0].Children), 1)
	util.AssertEqual(t, p.Top.Children[0].Children[0].Data(), "script")
	util.AssertEqual(t, p.Top.Children[0].Children[0].Children[0].Data(),
		" if (foo < 10) { }")
}

func TestSimpledocSiblings(t *testing.T) {
	p := NewParserFromString(
		"foo
bar
")
	err := p.Parse()
	util.AssertTrue(t, err == nil, "err is not nil: %v", err)
	//fmt.Printf("XXX doc: %s\n", p.Top)
	util.AssertEqual(t, p.Top.Data(), "html")
	util.AssertEqual(t, len(p.Top.Children), 1)
	util.AssertEqual(t, len(p.Top.Children[0].Children), 2)
	util.AssertEqual(t, p.Top.Children[0].Data(), "body")
	util.AssertEqual(t, p.Top.Children[0].Children[0].Data(), "a")
}

func TestParseFromReader(t *testing.T) {
	rdr, err := os.Open("test_data/page.html")
	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}
	p := NewParser(rdr)
	err = p.Parse()
	if err != nil {
		util.AssertTrue(t, false, "Failed to parse")
	}
	util.AssertTrue(t, p.Top != nil, "We got a parse tree back")
	//fmt.Println("Doc: ", p.Top.String())
}
<|endoftext|>


<commit_before>/*
Package compress provides a clever gzip compressing handler.

It takes care to not handle small contents, or contents that are already compressed (like JPEG, MPEG or PDF).
Trying to gzip them not only wastes CPU but can potentially increase the response size.

Make sure to include this handler above any other handler that alter the response body.
*/
package compress

import (
	"compress/gzip"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

var (
	gzipPool = sync.Pool{New: func() interface{} {
		return gzip.NewWriter(nil)
	}}

	gzippableMinSize = 1400

	gzippableTypes = map[string]struct{}{
		"text/xml":               {},
		"application/postscript": {},
		"text/plain":             {},
		"image/bmp":              {},
		"audio/wave":             {},
		"text/html":              {},
	}
)

// An Handler provides a clever gzip compressing handler.
type Handler struct {
	Next http.Handler
}

// Handle returns a Handler wrapping another http.Handler.
func Handle(h http.Handler) *Handler {
	return &Handler{h}
}

// HandleFunc returns a Handler wrapping an http.HandlerFunc.
func HandleFunc(f http.HandlerFunc) *Handler {
	return Handle(f)
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") || r.Header.Get("Sec-WebSocket-Key") != "" {
		h.Next.ServeHTTP(w, r)
		return
	}

	cw := &compressWriter{
		ResponseWriter: w,
		gziWriter:      gzipPool.Get().(*gzip.Writer),
	}
	defer gzipPool.Put(cw.gziWriter)
	defer cw.close()

	h.Next.ServeHTTP(cw, r)
}

// compressWriter binds the downstream repsonse writing into gziWriter if the first content is detected as gzip compressible.
// gzipUse keeps this detection result:
//	-1	detected but not used
// 	0	not detected yet
// 	1	detected and used
type compressWriter struct {
	http.ResponseWriter
	gziWriter *gzip.Writer
	gzipUse   int
	status    int
}

// WriteHeader catches a downstream WriteHeader call and caches the status code.
// The header will be written later, on the first Write call and after all the headers has been correctly set.
func (cw *compressWriter) WriteHeader(status int) {
	cw.status = status
}

// writePostponedHeader writes the response header with the cached status code.
func (cw *compressWriter) writePostponedHeader() {
	if cw.status == 0 {
		cw.status = http.StatusOK
	}
	cw.ResponseWriter.WriteHeader(cw.status)
}

// Write sets the compressing headers and calls the gzip writer, but only if the Content-Type header defines a compressible content.
// Otherwise, it calls the original Write method.
func (cw *compressWriter) Write(b []byte) (int, error) {
	if cw.gzipUse == 0 {
		ct := cw.ResponseWriter.Header().Get("Content-Type")
		if ct == "" {
			ct = http.DetectContentType(b)
			cw.ResponseWriter.Header().Set("Content-Type", ct)
		}

		cl, _ := strconv.Atoi(cw.ResponseWriter.Header().Get("Content-Length"))
		if cl < 1 {
			cl = len(b) // If no Content-Length, take the length of this first chunk.
		}

		if isGzippable(ct, cl) {
			cw.gzipUse = 1
			cw.setGzipHeaders()
			cw.gziWriter.Reset(cw.ResponseWriter)
		} else {
			cw.gzipUse = -1
		}

		cw.writePostponedHeader()
	}

	if cw.gzipUse == 1 {
		return cw.gziWriter.Write(b)
	}
	return cw.ResponseWriter.Write(b)
}

// close closes the gzip writer if it has been used.
func (cw *compressWriter) close() {
	if cw.gzipUse == 1 {
		cw.gziWriter.Close()
	}
}

// setGzipHeaders sets the Content-Encoding header for a gzip response.
// Because the compressed content will have a new size, it also removes the Content-Length header as it could have been set downstream by another handler.
func (cw *compressWriter) setGzipHeaders() {
	cw.ResponseWriter.Header().Del("Content-Length")
	cw.ResponseWriter.Header().Set("Content-Encoding", "gzip")
}

// isGzippable checks if a content must be compressed following its content length (cl) and content type (ct).
func isGzippable(ct string, cl int) bool {
	if cl < gzippableMinSize || ct == "" {
		return false
	}

	_, ok := gzippableTypes[strings.SplitN(ct, ";", 2)[0]]
	return ok
}
<commit_msg>Update gzippable criteria<commit_after>/*
Package compress provides a clever gzip compressing handler.

It takes care to not handle small contents, or contents that are already compressed (like JPEG, MPEG or PDF).
Trying to gzip them not only wastes CPU but can potentially increase the response size.

Make sure to include this handler above any other handler that alter the response body.
*/
package compress

import (
	"compress/gzip"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

var (
	gzipPool = sync.Pool{New: func() interface{} {
		return gzip.NewWriter(nil)
	}}

	gzippableMinSize = 150

	notGzippableTypes = map[string]struct{}{
		"application/font-woff": {},
		"application/gzip":      {},
		"application/pdf":       {},
		"application/zip":       {},
		"audio/mp4":             {},
		"audio/mpeg":            {},
		"audio/webm":            {},
		"image/gif":             {},
		"image/jpeg":            {},
		"image/png":             {},
		"image/webp":            {},
		"video/h264":            {},
		"video/mp4":             {},
		"video/mpeg":            {},
		"video/ogg":             {},
		"video/vp8":             {},
		"video/webm":            {},
	}
)

// An Handler provides a clever gzip compressing handler.
type Handler struct {
	Next http.Handler
}

// Handle returns a Handler wrapping another http.Handler.
func Handle(h http.Handler) *Handler {
	return &Handler{h}
}

// HandleFunc returns a Handler wrapping an http.HandlerFunc.
func HandleFunc(f http.HandlerFunc) *Handler {
	return Handle(f)
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") || r.Header.Get("Sec-WebSocket-Key") != "" {
		h.Next.ServeHTTP(w, r)
		return
	}

	cw := &compressWriter{
		ResponseWriter: w,
		gziWriter:      gzipPool.Get().(*gzip.Writer),
	}
	defer gzipPool.Put(cw.gziWriter)
	defer cw.close()

	h.Next.ServeHTTP(cw, r)
}

// compressWriter binds the downstream repsonse writing into gziWriter if the first content is detected as gzip compressible.
// gzipUse keeps this detection result:
//	-1	detected but not used
// 	0	not detected yet
// 	1	detected and used
type compressWriter struct {
	http.ResponseWriter
	gziWriter *gzip.Writer
	gzipUse   int
	status    int
}

// WriteHeader catches a downstream WriteHeader call and caches the status code.
// The header will be written later, on the first Write call and after all the headers has been correctly set.
func (cw *compressWriter) WriteHeader(status int) {
	cw.status = status
}

// writePostponedHeader writes the response header with the cached status code.
func (cw *compressWriter) writePostponedHeader() {
	if cw.status == 0 {
		cw.status = http.StatusOK
	}
	cw.ResponseWriter.WriteHeader(cw.status)
}

// Write sets the compressing headers and calls the gzip writer, but only if the Content-Type header defines a compressible content.
// Otherwise, it calls the original Write method.
func (cw *compressWriter) Write(b []byte) (int, error) {
	if cw.gzipUse == 0 {
		ct := cw.ResponseWriter.Header().Get("Content-Type")
		if ct == "" {
			ct = http.DetectContentType(b)
			cw.ResponseWriter.Header().Set("Content-Type", ct)
		}

		cl, _ := strconv.Atoi(cw.ResponseWriter.Header().Get("Content-Length"))
		if cl < 1 {
			cl = len(b) // If no Content-Length, take the length of this first chunk.
		}

		if isGzippable(ct, cl) {
			cw.gzipUse = 1
			cw.setGzipHeaders()
			cw.gziWriter.Reset(cw.ResponseWriter)
		} else {
			cw.gzipUse = -1
		}

		cw.writePostponedHeader()
	}

	if cw.gzipUse == 1 {
		return cw.gziWriter.Write(b)
	}
	return cw.ResponseWriter.Write(b)
}

// close closes the gzip writer if it has been used.
func (cw *compressWriter) close() {
	if cw.gzipUse == 1 {
		cw.gziWriter.Close()
	}
}

// setGzipHeaders sets the Content-Encoding header for a gzip response.
// Because the compressed content will have a new size, it also removes the Content-Length header as it could have been set downstream by another handler.
func (cw *compressWriter) setGzipHeaders() {
	cw.ResponseWriter.Header().Del("Content-Length")
	cw.ResponseWriter.Header().Set("Content-Encoding", "gzip")
}

// isGzippable checks if a content must be compressed following its content length (cl) and content type (ct).
func isGzippable(ct string, cl int) bool {
	if cl < gzippableMinSize || ct == "" {
		return false
	}

	_, ok := notGzippableTypes[strings.ToLower(strings.SplitN(ct, ";", 2)[0])]
	return !ok
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/mhausenblas/reshifter/pkg/backup"
	"github.com/mhausenblas/reshifter/pkg/discovery"
	"github.com/mhausenblas/reshifter/pkg/restore"
	"github.com/mhausenblas/reshifter/pkg/types"
	"github.com/mhausenblas/reshifter/pkg/util"
)

// versionHandler responds to HTTP GET requests of the form:
//		http GET localhost:8080/v1/version
func versionHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "ReShifter in version %s", releaseVersion)
}

// epstatsHandler responds to HTTP GET requests such as:
//		http GET localhost:8080/v1/epstats?endpoint=http%3A%2F%2Flocalhost%3A2379
func epstatsHandler(w http.ResponseWriter, r *http.Request) {
	endpoint := r.URL.Query().Get("endpoint")
	if endpoint == "" || strings.Index(endpoint, "http") != 0 {
		merr := "The endpoint is malformed"
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(merr)
		return
	}
	vk, vs, err := discovery.CountKeysFor(endpoint, types.Vanilla)
	if err != nil {
		merr := fmt.Sprintf("Having problems calculating stats: %s", err)
		http.Error(w, merr, http.StatusInternalServerError)
		log.Error(merr)
		return
	}
	log.Debugf("vanilla [keys:%d, size:%d]", vk, vs)
	// note: ignoring error here since we're adding up the stats
	// and if this happens to be a non-OpenShift distro we simply
	// add 0 to the overall count, and it's still fine:
	osk, oss, _ := discovery.CountKeysFor(endpoint, types.OpenShift)
	log.Debugf("openshift [keys:%d, size:%d]", osk, oss)
	_ = json.NewEncoder(w).Encode(struct {
		NumKeys         int `json:"numkeys"`
		TotalSizeValues int `json:"totalsizevalbytes"`
	}{
		vk + osk,
		vs + oss,
	})
}

// explorerHandler responds to HTTP GET requests such as:
//		http GET localhost:8080/v1/explorer?endpoint=http%3A%2F%2Flocalhost%3A2379
func explorerHandler(w http.ResponseWriter, r *http.Request) {
	endpoint := r.URL.Query().Get("endpoint")
	if endpoint == "" || strings.Index(endpoint, "http") != 0 {
		merr := "The endpoint is malformed"
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(merr)
		return
	}
	version, issecure, err := discovery.ProbeEtcd(endpoint)
	if err != nil {
		merr := fmt.Sprintf("%s", err)
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(err)
		return
	}
	distrotype, err := discovery.ProbeKubernetesDistro(endpoint)
	if err != nil {
		merr := fmt.Sprintf("Can't determine Kubernetes distro: %s", err)
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(err)
		return
	}
	secure := "insecure etcd, no SSL/TLS configured"
	if issecure {
		secure = "secure etcd, SSL/TLS configure"
	}
	_ = json.NewEncoder(w).Encode(struct {
		EtcdVersion  string `json:"etcdversion"`
		EtcdSecurity string `json:"etcdsec"`
		K8SDistro    string `json:"k8sdistro"`
	}{
		version,
		secure,
		util.LookupDistro(distrotype),
	})
}

// backupCreateHandler responds to HTTP POST requests such as:
//		http GET localhost:8080/v1/backup endpoint=http://localhost:2379
func backupCreateHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var breq types.BackupRequest
	err := decoder.Decode(&breq)
	if err != nil {
		mreq := "The backup request is malformed"
		http.Error(w, mreq, http.StatusBadRequest)
		log.Error(mreq)
		return
	}
	log.Infof("Starting backup from %s", breq.Endpoint)
	w.Header().Set("Content-Type", "application/json")
	bres := types.BackupResult{
		Outcome:  operationSuccess,
		BackupID: "0",
	}
	target := types.DefaultWorkDir
	bid, err := backup.Backup(breq.Endpoint, target, breq.Remote, breq.Bucket)
	if err != nil {
		bres.Outcome = operationFail
		log.Error(err)
	}
	bres.BackupID = bid
	backupTotal.WithLabelValues(bres.Outcome).Inc()
	log.Infof("Completed backup from %s: %v", breq.Endpoint, bres)
	if bres.Outcome == operationFail {
		http.Error(w, err.Error(), http.StatusConflict)
		return
	}
	_ = json.NewEncoder(w).Encode(bres)
}

// backupRetrieveHandler responds to HTTP GET requests such as:
//		http GET localhost:8080/v1/backup/1498230556
func backupRetrieveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	backupid := vars["backupid"]
	if !util.IsBackupID(backupid) {
		abortreason := fmt.Sprintf("Aborting backup retrieve: %s is not a valid backup ID", backupid)
		http.Error(w, abortreason, http.StatusConflict)
		log.Error(abortreason)
		return
	}
	target := types.DefaultWorkDir
	c, err := ioutil.ReadFile(filepath.Join(target, backupid) + ".zip")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error(err)
		return
	}
	fmt.Fprint(w, string(c))
}

// backupListHandler responds to HTTP GET requests at:
//		http GET localhost:8080/v1/backup/all?remote=play.minio.io:9000&bucket=test123
func backupListHandler(w http.ResponseWriter, r *http.Request) {
	remote := r.URL.Query().Get("remote")
	bucket := r.URL.Query().Get("bucket")
	backupIDs, err := backup.List(remote, bucket)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error(err)
		return
	}
	_ = json.NewEncoder(w).Encode(struct {
		NumBackups   int      `json:"numbackups"`
		EtcdSecurity []string `json:"backupids"`
	}{
		len(backupIDs),
		backupIDs,
	})
}

// / restoreUploadHandler responds to HTTP POST requests at:
//		http POST localhost:8080/v1/restore/upload
func restoreUploadHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseMultipartForm(types.UploadInMemoryBufferSize)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error(err)
		return
	}
	m := r.MultipartForm
	backupfiles := m.File["backupfile"]
	if len(backupfiles) == 0 {
		nbfe := fmt.Sprintf("No backup file uploaded. Aborting …")
		http.Error(w, nbfe, http.StatusInternalServerError)
		log.Error(nbfe)
		return
	}
	log.Infof("Got %v as backup file", backupfiles)
	var overallwritten int64
	for _, bf := range backupfiles {
		fn := bf.Filename
		bid := fn[0 : len(fn)-len(filepath.Ext(fn))]
		log.Infof("Verifying backup ID %s and then trying to upload content …", bid)
		if !util.IsBackupID(bid) {
			abortreason := fmt.Sprintf("Aborting upload: %s is not a valid backup ID. Must be a Unix timestamp formatted one such as 1499588813.zip …", bid)
			http.Error(w, abortreason, http.StatusConflict)
			log.Error(abortreason)
			return
		}
		src, err := bf.Open()
		defer func() { _ = src.Close() }()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Error(err)
			return
		}
		dst, err := os.Create(filepath.Join(types.DefaultWorkDir, bf.Filename))
		defer func() { _ = dst.Close() }()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Error(err)
			return
		}
		written, err := io.Copy(dst, src)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Error(err)
			return
		}
		overallwritten += written
	}
	log.Infof("Uploading backup file done, written %d bytes", overallwritten)
	_ = json.NewEncoder(w).Encode(struct {
		Received int64 `json:"received"`
	}{
		overallwritten,
	})
}

// restoreHandler responds to HTTP POST requests such as:
//		http POST localhost:8080/v1/restore endpoint=http://localhost:2379 backupid=1498230556
func restoreHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Only HTTP POST is supported", http.StatusMethodNotAllowed)
		return
	}
	decoder := json.NewDecoder(r.Body)
	var rreq types.RestoreRequest
	err := decoder.Decode(&rreq)
	if err != nil {
		http.Error(w, "The restore request is malformed", http.StatusBadRequest)
		return
	}
	target := types.DefaultWorkDir
	if !util.IsBackupID(rreq.BackupID) {
		abortreason := fmt.Sprintf("Aborting restore: %s is not a valid backup ID", rreq.BackupID)
		http.Error(w, abortreason, http.StatusConflict)
		log.Error(abortreason)
		return
	}
	log.Infof("Starting restore to %s from backup %s", rreq.Endpoint, rreq.BackupID)
	w.Header().Set("Content-Type", "application/json")
	rr := types.RestoreResult{
		Outcome:      operationSuccess,
		KeysRestored: 0,
		ElapsedTime:  0,
	}
	krestored, etime, err := restore.Restore(rreq.Endpoint, rreq.BackupID, target, rreq.Remote, rreq.Bucket)
	if err != nil {
		rr.Outcome = operationFail
		log.Error(err)
	}
	rr.KeysRestored = krestored
	rr.ElapsedTime = etime.Seconds()
	keysRestored.Add(float64(krestored))
	log.Infof("Completed restore from %s to %s: %v", rreq.BackupID, rreq.Endpoint, rr)
	if rr.Outcome == "fail" {
		http.Error(w, err.Error(), http.StatusConflict)
		return
	}
	_ = json.NewEncoder(w).Encode(rr)
}
<commit_msg>fixes handler re v2/v3 keyspace<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/mhausenblas/reshifter/pkg/backup"
	"github.com/mhausenblas/reshifter/pkg/discovery"
	"github.com/mhausenblas/reshifter/pkg/restore"
	"github.com/mhausenblas/reshifter/pkg/types"
	"github.com/mhausenblas/reshifter/pkg/util"
)

// versionHandler responds to HTTP GET requests of the form:
//		http GET localhost:8080/v1/version
func versionHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "ReShifter in version %s", releaseVersion)
}

// epstatsHandler responds to HTTP GET requests such as:
//		http GET localhost:8080/v1/epstats?endpoint=http%3A%2F%2Flocalhost%3A2379
func epstatsHandler(w http.ResponseWriter, r *http.Request) {
	endpoint := r.URL.Query().Get("endpoint")
	if endpoint == "" || strings.Index(endpoint, "http") != 0 {
		merr := "The endpoint is malformed"
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(merr)
		return
	}
	vk, vs, err := discovery.CountKeysFor(endpoint, types.LegacyKubernetesPrefix, types.LegacyKubernetesPrefixLast)
	if err != nil {
		log.Info("Didn't find legacy keys, trying moderns now")
	}
	vk, vs, err = discovery.CountKeysFor(endpoint, types.KubernetesPrefix, types.KubernetesPrefixLast)
	if err != nil {
		merr := fmt.Sprintf("Having problems calculating stats, no vanialla keys found: %s", err)
		http.Error(w, merr, http.StatusInternalServerError)
		log.Error(merr)
		return
	}
	log.Debugf("vanilla [keys:%d, size:%d]", vk, vs)
	// note: ignoring error here since we're adding up the stats
	// and if this happens to be a non-OpenShift distro we simply
	// add 0 to the overall count, and it's still fine:
	osk, oss, _ := discovery.CountKeysFor(endpoint, types.OpenShiftPrefix, types.OpenShiftPrefixLast)
	log.Debugf("openshift [keys:%d, size:%d]", osk, oss)
	_ = json.NewEncoder(w).Encode(struct {
		NumKeys         int `json:"numkeys"`
		TotalSizeValues int `json:"totalsizevalbytes"`
	}{
		vk + osk,
		vs + oss,
	})
}

// explorerHandler responds to HTTP GET requests such as:
//		http GET localhost:8080/v1/explorer?endpoint=http%3A%2F%2Flocalhost%3A2379
func explorerHandler(w http.ResponseWriter, r *http.Request) {
	endpoint := r.URL.Query().Get("endpoint")
	if endpoint == "" || strings.Index(endpoint, "http") != 0 {
		merr := "The endpoint is malformed"
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(merr)
		return
	}
	version, issecure, err := discovery.ProbeEtcd(endpoint)
	if err != nil {
		merr := fmt.Sprintf("%s", err)
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(err)
		return
	}
	distrotype, err := discovery.ProbeKubernetesDistro(endpoint)
	if err != nil {
		merr := fmt.Sprintf("Can't determine Kubernetes distro: %s", err)
		http.Error(w, merr, http.StatusBadRequest)
		log.Error(err)
		return
	}
	secure := "insecure etcd, no SSL/TLS configured"
	if issecure {
		secure = "secure etcd, SSL/TLS configure"
	}
	_ = json.NewEncoder(w).Encode(struct {
		EtcdVersion  string `json:"etcdversion"`
		EtcdSecurity string `json:"etcdsec"`
		K8SDistro    string `json:"k8sdistro"`
	}{
		version,
		secure,
		util.LookupDistro(distrotype),
	})
}

// backupCreateHandler responds to HTTP POST requests such as:
//		http GET localhost:8080/v1/backup endpoint=http://localhost:2379
func backupCreateHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var breq types.BackupRequest
	err := decoder.Decode(&breq)
	if err != nil {
		mreq := "The backup request is malformed"
		http.Error(w, mreq, http.StatusBadRequest)
		log.Error(mreq)
		return
	}
	log.Infof("Starting backup from %s", breq.Endpoint)
	w.Header().Set("Content-Type", "application/json")
	bres := types.BackupResult{
		Outcome:  operationSuccess,
		BackupID: "0",
	}
	target := types.DefaultWorkDir
	bid, err := backup.Backup(breq.Endpoint, target, breq.Remote, breq.Bucket)
	if err != nil {
		bres.Outcome = operationFail
		log.Error(err)
	}
	bres.BackupID = bid
	backupTotal.WithLabelValues(bres.Outcome).Inc()
	log.Infof("Completed backup from %s: %v", breq.Endpoint, bres)
	if bres.Outcome == operationFail {
		http.Error(w, err.Error(), http.StatusConflict)
		return
	}
	_ = json.NewEncoder(w).Encode(bres)
}

// backupRetrieveHandler responds to HTTP GET requests such as:
//		http GET localhost:8080/v1/backup/1498230556
func backupRetrieveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	backupid := vars["backupid"]
	if !util.IsBackupID(backupid) {
		abortreason := fmt.Sprintf("Aborting backup retrieve: %s is not a valid backup ID", backupid)
		http.Error(w, abortreason, http.StatusConflict)
		log.Error(abortreason)
		return
	}
	target := types.DefaultWorkDir
	c, err := ioutil.ReadFile(filepath.Join(target, backupid) + ".zip")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error(err)
		return
	}
	fmt.Fprint(w, string(c))
}

// backupListHandler responds to HTTP GET requests at:
//		http GET localhost:8080/v1/backup/all?remote=play.minio.io:9000&bucket=test123
func backupListHandler(w http.ResponseWriter, r *http.Request) {
	remote := r.URL.Query().Get("remote")
	bucket := r.URL.Query().Get("bucket")
	backupIDs, err := backup.List(remote, bucket)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error(err)
		return
	}
	_ = json.NewEncoder(w).Encode(struct {
		NumBackups   int      `json:"numbackups"`
		EtcdSecurity []string `json:"backupids"`
	}{
		len(backupIDs),
		backupIDs,
	})
}

// / restoreUploadHandler responds to HTTP POST requests at:
//		http POST localhost:8080/v1/restore/upload
func restoreUploadHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseMultipartForm(types.UploadInMemoryBufferSize)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error(err)
		return
	}
	m := r.MultipartForm
	backupfiles := m.File["backupfile"]
	if len(backupfiles) == 0 {
		nbfe := fmt.Sprintf("No backup file uploaded. Aborting …")
		http.Error(w, nbfe, http.StatusInternalServerError)
		log.Error(nbfe)
		return
	}
	log.Infof("Got %v as backup file", backupfiles)
	var overallwritten int64
	for _, bf := range backupfiles {
		fn := bf.Filename
		bid := fn[0 : len(fn)-len(filepath.Ext(fn))]
		log.Infof("Verifying backup ID %s and then trying to upload content …", bid)
		if !util.IsBackupID(bid) {
			abortreason := fmt.Sprintf("Aborting upload: %s is not a valid backup ID. Must be a Unix timestamp formatted one such as 1499588813.zip …", bid)
			http.Error(w, abortreason, http.StatusConflict)
			log.Error(abortreason)
			return
		}
		src, err := bf.Open()
		defer func() { _ = src.Close() }()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Error(err)
			return
		}
		dst, err := os.Create(filepath.Join(types.DefaultWorkDir, bf.Filename))
		defer func() { _ = dst.Close() }()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Error(err)
			return
		}
		written, err := io.Copy(dst, src)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Error(err)
			return
		}
		overallwritten += written
	}
	log.Infof("Uploading backup file done, written %d bytes", overallwritten)
	_ = json.NewEncoder(w).Encode(struct {
		Received int64 `json:"received"`
	}{
		overallwritten,
	})
}

// restoreHandler responds to HTTP POST requests such as:
//		http POST localhost:8080/v1/restore endpoint=http://localhost:2379 backupid=1498230556
func restoreHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Only HTTP POST is supported", http.StatusMethodNotAllowed)
		return
	}
	decoder := json.NewDecoder(r.Body)
	var rreq types.RestoreRequest
	err := decoder.Decode(&rreq)
	if err != nil {
		http.Error(w, "The restore request is malformed", http.StatusBadRequest)
		return
	}
	target := types.DefaultWorkDir
	if !util.IsBackupID(rreq.BackupID) {
		abortreason := fmt.Sprintf("Aborting restore: %s is not a valid backup ID", rreq.BackupID)
		http.Error(w, abortreason, http.StatusConflict)
		log.Error(abortreason)
		return
	}
	log.Infof("Starting restore to %s from backup %s", rreq.Endpoint, rreq.BackupID)
	w.Header().Set("Content-Type", "application/json")
	rr := types.RestoreResult{
		Outcome:      operationSuccess,
		KeysRestored: 0,
		ElapsedTime:  0,
	}
	krestored, etime, err := restore.Restore(rreq.Endpoint, rreq.BackupID, target, rreq.Remote, rreq.Bucket)
	if err != nil {
		rr.Outcome = operationFail
		log.Error(err)
	}
	rr.KeysRestored = krestored
	rr.ElapsedTime = etime.Seconds()
	keysRestored.Add(float64(krestored))
	log.Infof("Completed restore from %s to %s: %v", rreq.BackupID, rreq.Endpoint, rr)
	if rr.Outcome == "fail" {
		http.Error(w, err.Error(), http.StatusConflict)
		return
	}
	_ = json.NewEncoder(w).Encode(rr)
}
<|endoftext|>


<commit_before>package main

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

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

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

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	auth := r.Header.Get("X-Vip-Token")
	if auth != authToken {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	h(w, r)
}

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

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

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

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

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

	gc := fetch.RequestContext(r)

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

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

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

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

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

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

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

	uri := r.URL

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

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

	w.WriteHeader(http.StatusCreated)
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<commit_msg>Set content-type header at the proper point<commit_after>package main

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

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

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

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	auth := r.Header.Get("X-Vip-Token")
	if auth != authToken {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	h(w, r)
}

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

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

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

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

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

	gc := fetch.RequestContext(r)

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

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

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

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

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

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

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

	uri := r.URL

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

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

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

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

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


<commit_before>// Copyright (C) 2012 Numerotron Inc.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package bingo

import (
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"runtime/debug"
	"strings"
	"time"
)

type ContextBuilder func(*http.Request, http.ResponseWriter, *Session) Context
type ContextHandlerFunc func(Context) *AppError

var NotifyRequestTime func(elapsed time.Duration, path string)

func newHandler(fn func(http.ResponseWriter, *http.Request, *Session)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)

		fn(w, r, session)
		elapsed := time.Since(start)
		fmt.Printf("%s - request time: %.3f ms", r.URL.Path, float64(elapsed)/float64(time.Millisecond))
		if NotifyRequestTime != nil {
			NotifyRequestTime(elapsed, r.URL.Path)
		}
	}
}

func newContext(fn ContextHandlerFunc, builder ContextBuilder) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		proceed, err := context.Before()
		if err != nil {
			handleError(context, ServerError(err, "before error occurred"))
			return
		}
		if !proceed {
			return
		}

		if e := fn(context); e != nil {
			switch e.Code {
			case 404:
				renderNotFound(context)
			default:
				handleError(context, e)
			}
		}

		context.After()

		elapsed := time.Since(start)

		LogAccess(context.Request(), elapsed)
		if NotifyRequestTime != nil {
			NotifyRequestTime(elapsed, r.URL.Path)
		}
	}
}

func newReflect(pattern string, handler interface{}, builder ContextBuilder) http.HandlerFunc {
	methods := make(map[string]reflect.Method)
	t := reflect.TypeOf(handler)
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		methods[strings.ToLower(m.Name)] = m
	}

	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		context.Before()

		pieces := Path(r, len(pattern))
		var result []reflect.Value
		switch len(pieces) {
		case 0:
			m, ok := methods["index"]
			if !ok {
				renderNotFound(context)
				return
			}
			result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
		default:
			m, ok := methods[pieces[0]]
			if !ok {
				renderNotFound(context)
				return
			}

			switch m.Type.NumIn() {
			case 3:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context), reflect.ValueOf(pieces[1:])})
			case 2:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
			default:
				renderNotFound(context)
				return
			}
		}

		if len(result) != 1 {
			panic("result should be len(1)")
		}
		e := result[0].Interface().(*AppError)
		if e != nil {
			switch e.Code {
			case 404:
				renderNotFound(context)
			default:
				handleError(context, e)
			}
			return
		}

		context.After()

		elapsed := time.Since(start)
		fmt.Printf("%s - request time: %.3f ms\n", r.URL.Path, float64(elapsed)/float64(time.Millisecond))
		if NotifyRequestTime != nil {
			NotifyRequestTime(elapsed, r.URL.Path)
		}
	}
}

func handle(pattern string, fn http.HandlerFunc) {
	// handle everything below this pattern
	http.HandleFunc(pattern+"/", fn)

	// this has to come second to override http pkg default, which will add a
	// permanent redirect for the non-slash pattern to the slash pattern
	http.HandleFunc(pattern, fn)
}

func HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request, *Session)) {
	handle(pattern, newHandler(handler))
}

func HandleContext(pattern string, handler ContextHandlerFunc, builder ContextBuilder) {
	handle(pattern, newContext(handler, builder))
}

func HandleReflect(pattern string, handler interface{}, builder ContextBuilder) {
	handle(pattern, newReflect(pattern, handler, builder))
}

// pass in something like "images" to serve /images
func HandleFiles(path string) {
	pattern := fmt.Sprintf("/%s/", path)
	prefix := fmt.Sprintf("/%s", path)
	local := fmt.Sprintf("%s/%s", ContentDir, path)
	http.Handle(pattern, http.StripPrefix(prefix, http.FileServer(http.Dir(local))))
}

func handleError(c Context, aerr *AppError) {
	if Environment == EnvDevel {
		handleErrorInDev(c, aerr)
		return
	}
	renderError(c, aerr.Message)
	if AfterErrorFunc != nil {
		AfterErrorFunc(c, aerr)
	}
}

func handleErrorInDev(c Context, aerr *AppError) {
	fmt.Fprintln(c.Writer(), "An error occurred handling:", c.Request().URL.Path)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), aerr.Message)
	fmt.Fprintln(c.Writer(), aerr.Err)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), "Stack trace:")
	fmt.Fprintln(c.Writer(), string(debug.Stack()))
}
<commit_msg>working on ignoring HEAD body errors<commit_after>// Copyright (C) 2012 Numerotron Inc.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package bingo

import (
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"runtime/debug"
	"strings"
	"time"
)

type ContextBuilder func(*http.Request, http.ResponseWriter, *Session) Context
type ContextHandlerFunc func(Context) *AppError

var NotifyRequestTime func(elapsed time.Duration, path string)

func newHandler(fn func(http.ResponseWriter, *http.Request, *Session)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)

		fn(w, r, session)
		elapsed := time.Since(start)
		fmt.Printf("%s - request time: %.3f ms", r.URL.Path, float64(elapsed)/float64(time.Millisecond))
		if NotifyRequestTime != nil {
			NotifyRequestTime(elapsed, r.URL.Path)
		}
	}
}

func newContext(fn ContextHandlerFunc, builder ContextBuilder) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		proceed, err := context.Before()
		if err != nil {
			handleError(context, ServerError(err, "before error occurred"))
			return
		}
		if !proceed {
			return
		}

		if e := fn(context); e != nil {
                        if e.Err != http.ErrBodyNotAllowed {
                                switch e.Code {
                                case 404:
                                        renderNotFound(context)
                                default:
                                        handleError(context, e)
                                }
                        }
		}

		context.After()

		elapsed := time.Since(start)

		LogAccess(context.Request(), elapsed)
		if NotifyRequestTime != nil {
			NotifyRequestTime(elapsed, r.URL.Path)
		}
	}
}

func newReflect(pattern string, handler interface{}, builder ContextBuilder) http.HandlerFunc {
	methods := make(map[string]reflect.Method)
	t := reflect.TypeOf(handler)
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		methods[strings.ToLower(m.Name)] = m
	}

	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		session := loadSession(r)
		context := builder(r, w, session)

		defer func(c Context) {
			if err := recover(); err != nil {
				serr := fmt.Sprintf("runtime error: %s", err)
				apperr := new(AppError)
				apperr.Message = serr
				apperr.Code = 500
				apperr.Err = errors.New(serr)
				handleError(c, apperr)
			}
		}(context)

		context.Before()

		pieces := Path(r, len(pattern))
		var result []reflect.Value
		switch len(pieces) {
		case 0:
			m, ok := methods["index"]
			if !ok {
				renderNotFound(context)
				return
			}
			result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
		default:
			m, ok := methods[pieces[0]]
			if !ok {
				renderNotFound(context)
				return
			}

			switch m.Type.NumIn() {
			case 3:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context), reflect.ValueOf(pieces[1:])})
			case 2:
				result = m.Func.Call([]reflect.Value{reflect.ValueOf(handler), reflect.ValueOf(context)})
			default:
				renderNotFound(context)
				return
			}
		}

		if len(result) != 1 {
			panic("result should be len(1)")
		}
		e := result[0].Interface().(*AppError)
		if e != nil {
			switch e.Code {
			case 404:
				renderNotFound(context)
			default:
				handleError(context, e)
			}
			return
		}

		context.After()

		elapsed := time.Since(start)
		fmt.Printf("%s - request time: %.3f ms\n", r.URL.Path, float64(elapsed)/float64(time.Millisecond))
		if NotifyRequestTime != nil {
			NotifyRequestTime(elapsed, r.URL.Path)
		}
	}
}

func handle(pattern string, fn http.HandlerFunc) {
	// handle everything below this pattern
	http.HandleFunc(pattern+"/", fn)

	// this has to come second to override http pkg default, which will add a
	// permanent redirect for the non-slash pattern to the slash pattern
	http.HandleFunc(pattern, fn)
}

func HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request, *Session)) {
	handle(pattern, newHandler(handler))
}

func HandleContext(pattern string, handler ContextHandlerFunc, builder ContextBuilder) {
	handle(pattern, newContext(handler, builder))
}

func HandleReflect(pattern string, handler interface{}, builder ContextBuilder) {
	handle(pattern, newReflect(pattern, handler, builder))
}

// pass in something like "images" to serve /images
func HandleFiles(path string) {
	pattern := fmt.Sprintf("/%s/", path)
	prefix := fmt.Sprintf("/%s", path)
	local := fmt.Sprintf("%s/%s", ContentDir, path)
	http.Handle(pattern, http.StripPrefix(prefix, http.FileServer(http.Dir(local))))
}

func handleError(c Context, aerr *AppError) {
	if Environment == EnvDevel {
		handleErrorInDev(c, aerr)
		return
	}
	renderError(c, aerr.Message)
	if AfterErrorFunc != nil {
		AfterErrorFunc(c, aerr)
	}
}

func handleErrorInDev(c Context, aerr *AppError) {
	fmt.Fprintln(c.Writer(), "An error occurred handling:", c.Request().URL.Path)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), aerr.Message)
	fmt.Fprintln(c.Writer(), aerr.Err)
	fmt.Fprintln(c.Writer(), "")
	fmt.Fprintln(c.Writer(), "Stack trace:")
	fmt.Fprintln(c.Writer(), string(debug.Stack()))
	fmt.Println("An error occurred handling:", c.Request().URL.Path)
	fmt.Println(aerr.Message)
	fmt.Println(aerr.Err)
	fmt.Println("Stack trace:")
	fmt.Println(string(debug.Stack()))
}
<|endoftext|>


<commit_before>package main

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

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

type ErrorResponse struct {
	Msg string `json:"error"`
}

type Uploadable struct {
	Data   io.Reader
	Key    string
	length int64
}

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

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

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

	h(w, r)
}

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

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

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

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

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

	gc := fetch.RequestContext(r)

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

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

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

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]
	// Set a hard limit in MB on files
	var limit int64 = 5
	if r.ContentLength > limit<<20 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("The file size limit is %dMB", limit),
		})
		return
	} else if r.ContentLength == 0 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("File must have size greater than 0"),
		})
		return
	}

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

	data, err := processFile(r.Body, mime, bucket)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	r.Body.Close()

	err = storage.PutReader(bucket, data.Key, data.Data,
		data.length, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

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

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

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

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

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

func processFile(src io.Reader, mime string, bucket string) (*Uploadable, error) {
	if mime == "image/jpeg" || mime == "image/jpg" {
		image, format, err := fetch.GetRotatedImage(src)
		if err != nil {
			return nil, err
		}
		if format != "jpeg" {
			return nil, errors.New("You sent a bad JPEG file.")
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data := new(bytes.Buffer)
		length := int64(data.Len())
		err = jpeg.Encode(data, image, nil)
		if err != nil {
			return nil, err
		}

		upload := Uploadable{data, key, length}
		return &upload, nil

	} else {
		raw, err := ioutil.ReadAll(src)
		if err != nil {
			return nil, err
		}

		data := bytes.NewReader(raw)
		length := int64(data.Len())
		image, _, err := image.Decode(data)
		if err != nil {
			return nil, err
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data.Seek(0, 0)

		upload := Uploadable{data, key, length}
		return &upload, nil
	}
}
<commit_msg>Gotta make Length exported<commit_after>package main

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

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

type ErrorResponse struct {
	Msg string `json:"error"`
}

type Uploadable struct {
	Data   io.Reader
	Key    string
	Length int64
}

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

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

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

	h(w, r)
}

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

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

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

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

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

	gc := fetch.RequestContext(r)

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

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

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

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]
	// Set a hard limit in MB on files
	var limit int64 = 5
	if r.ContentLength > limit<<20 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("The file size limit is %dMB", limit),
		})
		return
	} else if r.ContentLength == 0 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("File must have size greater than 0"),
		})
		return
	}

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

	data, err := processFile(r.Body, mime, bucket)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	r.Body.Close()

	err = storage.PutReader(bucket, data.Key, data.Data,
		data.Length, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

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

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

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

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

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

func processFile(src io.Reader, mime string, bucket string) (*Uploadable, error) {
	if mime == "image/jpeg" || mime == "image/jpg" {
		image, format, err := fetch.GetRotatedImage(src)
		if err != nil {
			return nil, err
		}
		if format != "jpeg" {
			return nil, errors.New("You sent a bad JPEG file.")
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data := new(bytes.Buffer)
		length := int64(data.Len())
		err = jpeg.Encode(data, image, nil)
		if err != nil {
			return nil, err
		}

		upload := Uploadable{data, key, length}
		return &upload, nil

	} else {
		raw, err := ioutil.ReadAll(src)
		if err != nil {
			return nil, err
		}

		data := bytes.NewReader(raw)
		length := int64(data.Len())
		image, _, err := image.Decode(data)
		if err != nil {
			return nil, err
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data.Seek(0, 0)

		upload := Uploadable{data, key, length}
		return &upload, nil
	}
}
<|endoftext|>


<commit_before>//Package pastis implements a simple library for building RESTful APIs.
//This package provides you with everything you will need for most of your applications.
//Pastis has 3 building blocks :
// 1) An API associated to a set of Resource and callback functions. It is paired with an arbitrary port.
// 2) Resource paired with an URL-pattern. Its represents a REST resource.
// 3) A callback paired with a URL-pattern and a request method.
// Note that a pastis server can support more than one API.
// Pastis rich features are :
// Nice URL-pattern matching,
// Path parameter parsing,
// Configurable loggers,
// CORS support,
// Speaks JSON and
// Type-safe request
package pastis

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"reflect"
	"strings"
)

// An API manages a group of resources by routing to requests
// to the correct method and URL.
//
// You can instantiate multiple APIs on separate ports. Each API
// will manage its own set of resources.
type API struct {
	//An HTTP mutex to add handlers
	mux    *http.ServeMux
	//A filter chain
	chain  *FilterChain
	//A router
	router *Router
	//A configurable logger
	logger *Logger
}

// NewAPI allocates and returns a new API.
func NewAPI() *API {
	return &API{chain: &FilterChain{[]Filter{}, 0, nil}, mux: http.NewServeMux(), router: NewRouter(), logger: GetLogger("DEBUG")}
}

func (api *API)  SetLevel(level string) {
	api.logger.SetLevel(level)
}

//SetOutput sets the output destination for the standard logger of the given level.
//Example: api.SetOuput("ERROR", os.StdErr, log.Lmicroseconds)
func (api *API) SetOuput(level string, w io.Writer, flag int) *Logger {
	api.logger.SetOuput(level, w, flag)
	return api.logger
}

//A Pretty Error response
func ErrorResponse(err error) interface{} {
	return map[string]string{"error": err.Error()}
}

//Return an instance of http.HandlerFunc built from  a pair of request method and a callback value.
//The first callback input parameter is the set of URL query and path parameters.
//The second callback input parameter is the unmarshalled JSON body recieved from the request (if it exists).
func (api *API) handleMethodCall(urlValues url.Values, request *http.Request, methodRef reflect.Value) (int, interface{}) {
	api.logger.Debugf("handleMethodCall %s", request.Method)

	if methodRef.Kind() == reflect.Invalid {
		return http.StatusNotImplemented, nil
	}

	methodType := methodRef.Type()
	methodArgSize := methodRef.Type().NumIn()

	api.logger.Debugf("method has %d argument.", methodArgSize)

	if methodArgSize >= 3 {
		api.logger.Errorf("method %v cannot have more than 2 arguments", methodRef)
		return http.StatusNotImplemented, nil
	}

	if methodArgSize == 0 {
		api.logger.Errorf("method %v has no argument. Skip marshalling...", methodRef)
		return api.handleReturn(methodRef, []reflect.Value{})
	}

	valueOfUrlValues := reflect.ValueOf(urlValues)
	methodParameterValues := []reflect.Value{valueOfUrlValues}

	expectedJSONType := methodType.In(0)

	if methodArgSize == 1 {
		if expectedJSONType == valueOfUrlValues.Type() {
			api.logger.Debugf(" method has one argument of type url.Values. Skip marshalling...")
			return api.handleReturn(methodRef, methodParameterValues)
		} else {
			api.logger.Debugf(" method %v has one argument of request body type. ", methodRef)
			methodParameterValues = []reflect.Value{} // will add later the json body as parameter
		}
	} else if methodArgSize == 2 {
		api.logger.Debug(" method first argument is not the request body type.\n")
		expectedJSONType = methodType.In(1)
		api.logger.Debugf(" method second argument is the request body type %v.\n", expectedJSONType)
	}

	expectedJSONValue := reflect.New(expectedJSONType)

	jsonInterface := expectedJSONValue.Interface()

	dec := json.NewDecoder(request.Body)
	for {
		if err := dec.Decode(jsonInterface); err == io.EOF {
			break
		} else if err != nil {
			api.logger.Error(" unable to decode json blob. Check whether parameter type matches json type. \n")
			return http.StatusNotImplemented, nil
		}
	}

	jsonValue := reflect.ValueOf(jsonInterface)
	jsonValueType := jsonValue.Elem().Type()

	if expectedJSONType.Kind() != jsonValue.Elem().Type().Kind() {
		api.logger.Errorf(" Unexpected JSON format. Should be of type '", expectedJSONType.Kind(), "' instead of ", jsonValueType.Kind())
		return http.StatusNotImplemented, nil
	} else if expectedJSONType != jsonValue.Type() {
		methodParameterValues = append(methodParameterValues, jsonValue.Elem())
	} else {
		api.logger.Errorf(" Parameter type mismatches json type. Expected JSON format. ", expectedJSONType)
		return http.StatusNotImplemented, nil
	}
	return api.handleReturn(methodRef, methodParameterValues)
}

//Handles the return values. It converts the array of Value into a tuple (int, interface {} )
func (api *API) handleReturn(methodRef reflect.Value, methodParameterValues []reflect.Value) (int, interface{}) {
	responseValues := methodRef.Call(methodParameterValues)
	if len(responseValues) != 2 {
		api.logger.Errorf(" method %v does not return expected response (int, interface{}).", methodRef)
		return http.StatusNotImplemented, nil
	}
	return int(responseValues[0].Int()), responseValues[1].Interface()
}

//Return an instance of http.HandlerFunc built from  a request method, a URL-pattern matching and a callback function fn.
//The first callback input parameter is the set of URL query and path parameters.
//The second callback input parameter is the unmarshalled JSON body recieved from the request (if it exists).
func (api *API) methodHandler(pattern string, requestMethod string, fn reflect.Value) http.HandlerFunc {
	return func(rw http.ResponseWriter, request *http.Request) {

		code, data := api.handleMethodCall(request.Form, request, fn)

		api.handlerFuncReturn(code, data, rw)
	}
}

//Utility method writing status code and data to the given response
func (api *API) handlerFuncReturn(code int, data interface{}, rw http.ResponseWriter) {
	api.logger.Debugf(" handlerFuncReturn %v", code)

	content, err := json.Marshal(data)
	if err != nil {
		api.logger.Errorf(" handlerFuncReturn could not marshall content [%v]", data)
		rw.WriteHeader(http.StatusInternalServerError)
		return
	}

	//FIXME should be configurable
	rw.Header().Set("Content-Type", "application/json")

	rw.WriteHeader(code)

	if content != nil {
		rw.Write(content)
	}
}

// AddFilter adds a new filter to an API. The API will execute the filter
// before calling the target function.
func (api *API) AddFilter(filter Filter) {
	api.chain.Filters = append(api.chain.Filters, filter)
}

// AddResource adds a new resource to an API. The API will route
// requests that match the given path to its HTTP
// method on the resource.
func (api *API) AddResource(pattern string, resource interface{}) {
	methods := []string{"GET", "Get", "Put", "PUT", "Post", "POST", "Patch", "PATCH", "DELETE", "Delete", "Options", "OPTIONS"}
	for _, requestMethod := range methods {
		methodRef := reflect.ValueOf(resource).MethodByName(requestMethod)
		if methodRef.Kind() != reflect.Invalid {
			requestMethod = strings.ToUpper(requestMethod)
			handler := api.methodHandler(pattern, requestMethod, methodRef)
			api.addHandler(requestMethod, handler, pattern)
			api.logger.Debugf(" Added Resource [method={%v},pattern={%v}]", requestMethod, pattern)
		}
	}
}

// Function callback paired with a request Method and URL-matching pattern.
func (api *API) Do(requestMethod string, pattern string, fn interface{}) {
	handler := api.methodHandler(pattern, requestMethod, reflect.ValueOf(fn))
	api.addHandler(requestMethod, handler, pattern)
	api.logger.Debugf(" Added Do [method={%v},pattern={%v}]", requestMethod, pattern)
}

// Function callback paired with GET Method and URL-matching pattern.
func (api *API) Get(pattern string, fn interface{}) {
	api.Do("GET", pattern, fn)
}

// Function callback paired with PATH Method and URL-matching pattern.
func (api *API) Patch(pattern string, fn interface{}) {
	api.Do("PATCH", pattern, fn)
}

// Function callback paired with OPTIONS Method and URL-matching pattern.
func (api *API) Options(pattern string, fn interface{}) {
	api.Do("OPTIONS", pattern, fn)
}

// Function callback paired with HEAD Method and URL-matching pattern.
func (api *API) Head(pattern string, fn interface{}) {
	api.Do("HEAD", pattern, fn)
}

// Function callback paired with POST Method and URL-matching pattern.
func (api *API) Post(pattern string, fn interface{}) {
	api.Do("POST", pattern, fn)
}

// Function callback paired with LINK Method and URL-matching pattern.
func (api *API) Link(pattern string, fn interface{}) {
	api.Do("LINK", pattern, fn)
}

// Function callback paired with UNLINK Method and URL-matching pattern.
func (api *API) Unlink(pattern string, fn interface{}) {
	api.Do("UNLINK", pattern, fn)
}

// Function callback paired with PUT Method and URL-matching pattern.
func (api *API) Put(pattern string, fn interface{}) {
	api.Do("PUT", pattern, fn)
}

// Function callback paired with DELETE Method and URL-matching pattern.
func (api *API) Delete(fn interface{}, pattern string) {
	api.Do("DELETE", pattern, fn)
}

// Function callback paired with a set of URL-matching pattern.
func (api *API) addHandler(method string, handler http.HandlerFunc, pattern string) {
	api.logger.Debugf(" Add Handle Func [pattern={%v}]", pattern)
	pathChain := api.chain.Copy()
	pathChain.Target = handler
	handlerFunc := pathChain.dispatchRequestHandler()
	api.router.Add(pattern, method, handlerFunc)
}

//Implements HandlerFunc
func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	handler, _ := api.mux.Handler(r)
	handler.ServeHTTP(w, r)
}

func (api *API) HandleFunc() {
	api.mux.HandleFunc("/", api.router.Handler(api.logger))
	api.router.OpsFriendlyLog(api.logger)
}

// Start causes the API to begin serving requests on the given port.
func (api *API) Start(port int) error {
	api.HandleFunc()
	portString := fmt.Sprintf(":%d", port)

	err := http.ListenAndServe(portString, api.mux)
	if err != nil {
		api.logger.Errorf(" API could not start at port %d \n", port)
		return err
	}
	return nil
}
<commit_msg>Changed SetOuput to SetOutput<commit_after>//Package pastis implements a simple library for building RESTful APIs.
//This package provides you with everything you will need for most of your applications.
//Pastis has 3 building blocks :
// 1) An API associated to a set of Resource and callback functions. It is paired with an arbitrary port.
// 2) Resource paired with an URL-pattern. Its represents a REST resource.
// 3) A callback paired with a URL-pattern and a request method.
// Note that a pastis server can support more than one API.
// Pastis rich features are :
// Nice URL-pattern matching,
// Path parameter parsing,
// Configurable loggers,
// CORS support,
// Speaks JSON and
// Type-safe request
package pastis

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"reflect"
	"strings"
)

// An API manages a group of resources by routing to requests
// to the correct method and URL.
//
// You can instantiate multiple APIs on separate ports. Each API
// will manage its own set of resources.
type API struct {
	//An HTTP mutex to add handlers
	mux    *http.ServeMux
	//A filter chain
	chain  *FilterChain
	//A router
	router *Router
	//A configurable logger
	logger *Logger
}

// NewAPI allocates and returns a new API.
func NewAPI() *API {
	return &API{chain: &FilterChain{[]Filter{}, 0, nil}, mux: http.NewServeMux(), router: NewRouter(), logger: GetLogger("DEBUG")}
}

func (api *API)  SetLevel(level string) {
	api.logger.SetLevel(level)
}

//SetOutput sets the output destination for the standard logger of the given level.
//Example: api.SetOutput("ERROR", os.StdErr, log.Lmicroseconds)
func (api *API) SetOutput(level string, w io.Writer, flag int) *Logger {
	api.logger.SetOutput(level, w, flag)
	return api.logger
}

//A Pretty Error response
func ErrorResponse(err error) interface{} {
	return map[string]string{"error": err.Error()}
}

//Return an instance of http.HandlerFunc built from  a pair of request method and a callback value.
//The first callback input parameter is the set of URL query and path parameters.
//The second callback input parameter is the unmarshalled JSON body recieved from the request (if it exists).
func (api *API) handleMethodCall(urlValues url.Values, request *http.Request, methodRef reflect.Value) (int, interface{}) {
	api.logger.Debugf("handleMethodCall %s", request.Method)

	if methodRef.Kind() == reflect.Invalid {
		return http.StatusNotImplemented, nil
	}

	methodType := methodRef.Type()
	methodArgSize := methodRef.Type().NumIn()

	api.logger.Debugf("method has %d argument.", methodArgSize)

	if methodArgSize >= 3 {
		api.logger.Errorf("method %v cannot have more than 2 arguments", methodRef)
		return http.StatusNotImplemented, nil
	}

	if methodArgSize == 0 {
		api.logger.Errorf("method %v has no argument. Skip marshalling...", methodRef)
		return api.handleReturn(methodRef, []reflect.Value{})
	}

	valueOfUrlValues := reflect.ValueOf(urlValues)
	methodParameterValues := []reflect.Value{valueOfUrlValues}

	expectedJSONType := methodType.In(0)

	if methodArgSize == 1 {
		if expectedJSONType == valueOfUrlValues.Type() {
			api.logger.Debugf(" method has one argument of type url.Values. Skip marshalling...")
			return api.handleReturn(methodRef, methodParameterValues)
		} else {
			api.logger.Debugf(" method %v has one argument of request body type. ", methodRef)
			methodParameterValues = []reflect.Value{} // will add later the json body as parameter
		}
	} else if methodArgSize == 2 {
		api.logger.Debug(" method first argument is not the request body type.\n")
		expectedJSONType = methodType.In(1)
		api.logger.Debugf(" method second argument is the request body type %v.\n", expectedJSONType)
	}

	expectedJSONValue := reflect.New(expectedJSONType)

	jsonInterface := expectedJSONValue.Interface()

	dec := json.NewDecoder(request.Body)
	for {
		if err := dec.Decode(jsonInterface); err == io.EOF {
			break
		} else if err != nil {
			api.logger.Error(" unable to decode json blob. Check whether parameter type matches json type. \n")
			return http.StatusNotImplemented, nil
		}
	}

	jsonValue := reflect.ValueOf(jsonInterface)
	jsonValueType := jsonValue.Elem().Type()

	if expectedJSONType.Kind() != jsonValue.Elem().Type().Kind() {
		api.logger.Errorf(" Unexpected JSON format. Should be of type '", expectedJSONType.Kind(), "' instead of ", jsonValueType.Kind())
		return http.StatusNotImplemented, nil
	} else if expectedJSONType != jsonValue.Type() {
		methodParameterValues = append(methodParameterValues, jsonValue.Elem())
	} else {
		api.logger.Errorf(" Parameter type mismatches json type. Expected JSON format. ", expectedJSONType)
		return http.StatusNotImplemented, nil
	}
	return api.handleReturn(methodRef, methodParameterValues)
}

//Handles the return values. It converts the array of Value into a tuple (int, interface {} )
func (api *API) handleReturn(methodRef reflect.Value, methodParameterValues []reflect.Value) (int, interface{}) {
	responseValues := methodRef.Call(methodParameterValues)
	if len(responseValues) != 2 {
		api.logger.Errorf(" method %v does not return expected response (int, interface{}).", methodRef)
		return http.StatusNotImplemented, nil
	}
	return int(responseValues[0].Int()), responseValues[1].Interface()
}

//Return an instance of http.HandlerFunc built from  a request method, a URL-pattern matching and a callback function fn.
//The first callback input parameter is the set of URL query and path parameters.
//The second callback input parameter is the unmarshalled JSON body recieved from the request (if it exists).
func (api *API) methodHandler(pattern string, requestMethod string, fn reflect.Value) http.HandlerFunc {
	return func(rw http.ResponseWriter, request *http.Request) {

		code, data := api.handleMethodCall(request.Form, request, fn)

		api.handlerFuncReturn(code, data, rw)
	}
}

//Utility method writing status code and data to the given response
func (api *API) handlerFuncReturn(code int, data interface{}, rw http.ResponseWriter) {
	api.logger.Debugf(" handlerFuncReturn %v", code)

	content, err := json.Marshal(data)
	if err != nil {
		api.logger.Errorf(" handlerFuncReturn could not marshall content [%v]", data)
		rw.WriteHeader(http.StatusInternalServerError)
		return
	}

	//FIXME should be configurable
	rw.Header().Set("Content-Type", "application/json")

	rw.WriteHeader(code)

	if content != nil {
		rw.Write(content)
	}
}

// AddFilter adds a new filter to an API. The API will execute the filter
// before calling the target function.
func (api *API) AddFilter(filter Filter) {
	api.chain.Filters = append(api.chain.Filters, filter)
}

// AddResource adds a new resource to an API. The API will route
// requests that match the given path to its HTTP
// method on the resource.
func (api *API) AddResource(pattern string, resource interface{}) {
	methods := []string{"GET", "Get", "Put", "PUT", "Post", "POST", "Patch", "PATCH", "DELETE", "Delete", "Options", "OPTIONS"}
	for _, requestMethod := range methods {
		methodRef := reflect.ValueOf(resource).MethodByName(requestMethod)
		if methodRef.Kind() != reflect.Invalid {
			requestMethod = strings.ToUpper(requestMethod)
			handler := api.methodHandler(pattern, requestMethod, methodRef)
			api.addHandler(requestMethod, handler, pattern)
			api.logger.Debugf(" Added Resource [method={%v},pattern={%v}]", requestMethod, pattern)
		}
	}
}

// Function callback paired with a request Method and URL-matching pattern.
func (api *API) Do(requestMethod string, pattern string, fn interface{}) {
	handler := api.methodHandler(pattern, requestMethod, reflect.ValueOf(fn))
	api.addHandler(requestMethod, handler, pattern)
	api.logger.Debugf(" Added Do [method={%v},pattern={%v}]", requestMethod, pattern)
}

// Function callback paired with GET Method and URL-matching pattern.
func (api *API) Get(pattern string, fn interface{}) {
	api.Do("GET", pattern, fn)
}

// Function callback paired with PATH Method and URL-matching pattern.
func (api *API) Patch(pattern string, fn interface{}) {
	api.Do("PATCH", pattern, fn)
}

// Function callback paired with OPTIONS Method and URL-matching pattern.
func (api *API) Options(pattern string, fn interface{}) {
	api.Do("OPTIONS", pattern, fn)
}

// Function callback paired with HEAD Method and URL-matching pattern.
func (api *API) Head(pattern string, fn interface{}) {
	api.Do("HEAD", pattern, fn)
}

// Function callback paired with POST Method and URL-matching pattern.
func (api *API) Post(pattern string, fn interface{}) {
	api.Do("POST", pattern, fn)
}

// Function callback paired with LINK Method and URL-matching pattern.
func (api *API) Link(pattern string, fn interface{}) {
	api.Do("LINK", pattern, fn)
}

// Function callback paired with UNLINK Method and URL-matching pattern.
func (api *API) Unlink(pattern string, fn interface{}) {
	api.Do("UNLINK", pattern, fn)
}

// Function callback paired with PUT Method and URL-matching pattern.
func (api *API) Put(pattern string, fn interface{}) {
	api.Do("PUT", pattern, fn)
}

// Function callback paired with DELETE Method and URL-matching pattern.
func (api *API) Delete(fn interface{}, pattern string) {
	api.Do("DELETE", pattern, fn)
}

// Function callback paired with a set of URL-matching pattern.
func (api *API) addHandler(method string, handler http.HandlerFunc, pattern string) {
	api.logger.Debugf(" Add Handle Func [pattern={%v}]", pattern)
	pathChain := api.chain.Copy()
	pathChain.Target = handler
	handlerFunc := pathChain.dispatchRequestHandler()
	api.router.Add(pattern, method, handlerFunc)
}

//Implements HandlerFunc
func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	handler, _ := api.mux.Handler(r)
	handler.ServeHTTP(w, r)
}

func (api *API) HandleFunc() {
	api.mux.HandleFunc("/", api.router.Handler(api.logger))
	api.router.OpsFriendlyLog(api.logger)
}

// Start causes the API to begin serving requests on the given port.
func (api *API) Start(port int) error {
	api.HandleFunc()
	portString := fmt.Sprintf(":%d", port)

	err := http.ListenAndServe(portString, api.mux)
	if err != nil {
		api.logger.Errorf(" API could not start at port %d \n", port)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

func (cmd *hashinCommand) Name() string      { return "hash-inputs" }
func (cmd *hashinCommand) Args() string      { return "" }
func (cmd *hashinCommand) ShortHelp() string { return "" }
func (cmd *hashinCommand) LongHelp() string  { return "" }
func (cmd *hashinCommand) Hidden() bool      { return false }

func (cmd *hashinCommand) Register(fs *flag.FlagSet) {
}

type hashinCommand struct{}

func (_ hashinCommand) Run(args []string) error {
	p, err := nestContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := nestContext.sourceManager()
	if err != nil {
		return err
	}
	sm.UseDefaultSignalHandling()
	defer sm.Release()

	params := p.makeParams()
	cpr, err := nestContext.splitAbsoluteProjectRoot(p.absroot)
	if err != nil {
		return errors.Wrap(err, "determineProjectRoot")
	}

	params.RootPackageTree, err = gps.ListPackages(p.absroot, cpr)
	if err != nil {
		return errors.Wrap(err, "gps.ListPackages")
	}

	s, err := gps.Prepare(params, sm)
	if err != nil {
		return errors.Wrap(err, "prepare solver")
	}

	fmt.Println(gps.HashingInputsAsString(s))
	return nil
}
<commit_msg>add license<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"

	"github.com/pkg/errors"
	"github.com/sdboyer/gps"
)

func (cmd *hashinCommand) Name() string      { return "hash-inputs" }
func (cmd *hashinCommand) Args() string      { return "" }
func (cmd *hashinCommand) ShortHelp() string { return "" }
func (cmd *hashinCommand) LongHelp() string  { return "" }
func (cmd *hashinCommand) Hidden() bool      { return false }

func (cmd *hashinCommand) Register(fs *flag.FlagSet) {
}

type hashinCommand struct{}

func (_ hashinCommand) Run(args []string) error {
	p, err := nestContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := nestContext.sourceManager()
	if err != nil {
		return err
	}
	sm.UseDefaultSignalHandling()
	defer sm.Release()

	params := p.makeParams()
	cpr, err := nestContext.splitAbsoluteProjectRoot(p.absroot)
	if err != nil {
		return errors.Wrap(err, "determineProjectRoot")
	}

	params.RootPackageTree, err = gps.ListPackages(p.absroot, cpr)
	if err != nil {
		return errors.Wrap(err, "gps.ListPackages")
	}

	s, err := gps.Prepare(params, sm)
	if err != nil {
		return errors.Wrap(err, "prepare solver")
	}

	fmt.Println(gps.HashingInputsAsString(s))
	return nil
}
<|endoftext|>