<commit_before>//  Copyright (c) 2014 Couchbase, Inc.
//  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 cbft

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

	bleveHttp "github.com/blevesearch/bleve/http"

	"github.com/gorilla/mux"
)

type DiagGetHandler struct {
	versionMain string
	mgr         *Manager
	mr          *MsgRing
}

func NewDiagGetHandler(versionMain string,
	mgr *Manager, mr *MsgRing) *DiagGetHandler {
	return &DiagGetHandler{versionMain: versionMain, mgr: mgr, mr: mr}
}

func (h *DiagGetHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	handlers := []struct {
		Name        string
		Handler     http.Handler
		HandlerFunc http.HandlerFunc
	}{
		{"/api/cfg", NewCfgGetHandler(h.mgr), nil},
		{"/api/index", NewListIndexHandler(h.mgr), nil},
		{"/api/log", NewLogGetHandler(h.mgr, h.mr), nil},
		{"/api/managerMeta", NewManagerMetaHandler(h.mgr, nil), nil},
		{"/api/pindex", NewListPIndexHandler(h.mgr), nil},
		{"/api/pindex-bleve", bleveHttp.NewListIndexesHandler(), nil},
		{"/api/runtime", NewRuntimeGetHandler(h.versionMain, h.mgr), nil},
		{"/api/runtime/args", nil, restGetRuntimeArgs},
		{"/api/runtime/stats", nil, restGetRuntimeStats},
		{"/api/runtime/statsMem", nil, restGetRuntimeStatsMem},
		{"/api/stats", NewStatsHandler(h.mgr), nil},
	}

	w.Write(jsonOpenBrace)
	for i, handler := range handlers {
		if i > 0 {
			w.Write(jsonComma)
		}
		w.Write([]byte(fmt.Sprintf(`"%s":`, handler.Name)))
		if handler.Handler != nil {
			handler.Handler.ServeHTTP(w, req)
		}
		if handler.HandlerFunc != nil {
			handler.HandlerFunc.ServeHTTP(w, req)
		}
	}

	var first = true
	var visit func(path string, f os.FileInfo, err error) error
	visit = func(path string, f os.FileInfo, err error) error {
		m := map[string]interface{}{
			"Path":    path,
			"Name":    f.Name(),
			"Size":    f.Size(),
			"Mode":    f.Mode(),
			"ModTime": f.ModTime().Format(time.RFC3339Nano),
			"IsDir":   f.IsDir(),
		}
		if strings.HasPrefix(f.Name(), "PINDEX_") || // Matches PINDEX_xxx_META.
			strings.HasSuffix(f.Name(), "_META") || // Matches PINDEX_META.
			strings.HasSuffix(f.Name(), ".json") { // Matches index_meta.json.
			b, err := ioutil.ReadFile(path)
			if err == nil {
				m["Contents"] = string(b)
			}
		}
		buf, err := json.Marshal(m)
		if err == nil {
			if !first {
				w.Write(jsonComma)
			}
			w.Write(buf)
			first = false
		}
		return nil
	}

	w.Write([]byte(`,"dataDir":[`))
	filepath.Walk(h.mgr.dataDir, visit)
	w.Write([]byte(`]`))

	w.Write(jsonCloseBrace)
}

// ---------------------------------------------------

type StatsHandler struct {
	mgr *Manager
}

func NewStatsHandler(mgr *Manager) *StatsHandler {
	return &StatsHandler{mgr: mgr}
}

var statsFeedsPrefix = []byte("\"feeds\":{")
var statsPIndexesPrefix = []byte("\"pindexes\":{")
var statsManagerPrefix = []byte(",\"manager\":")
var statsNamePrefix = []byte("\"")
var statsNameSuffix = []byte("\":")

func (h *StatsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	indexName := mux.Vars(req)["indexName"]

	feeds, pindexes := h.mgr.CurrentMaps()
	feedNames := make([]string, 0, len(feeds))
	for feedName := range feeds {
		feedNames = append(feedNames, feedName)
	}
	sort.Strings(feedNames)

	pindexNames := make([]string, 0, len(pindexes))
	for pindexName := range pindexes {
		pindexNames = append(pindexNames, pindexName)
	}
	sort.Strings(pindexNames)

	w.Write(jsonOpenBrace)

	first := true
	w.Write(statsFeedsPrefix)
	for _, feedName := range feedNames {
		if indexName == "" || indexName == feeds[feedName].IndexName() {
			if !first {
				w.Write(jsonComma)
			}
			first = false
			w.Write(statsNamePrefix)
			w.Write([]byte(feedName))
			w.Write(statsNameSuffix)
			feeds[feedName].Stats(w)
		}
	}
	w.Write(jsonCloseBraceComma)

	first = true
	w.Write(statsPIndexesPrefix)
	for _, pindexName := range pindexNames {
		if indexName == "" || indexName == pindexes[pindexName].IndexName {
			if !first {
				w.Write(jsonComma)
			}
			first = false
			w.Write(statsNamePrefix)
			w.Write([]byte(pindexName))
			w.Write(statsNameSuffix)
			pindexes[pindexName].Dest.Stats(w)
		}
	}
	w.Write(jsonCloseBrace)

	if indexName == "" {
		w.Write(statsManagerPrefix)
		var mgrStats ManagerStats
		h.mgr.stats.AtomicCopyTo(&mgrStats)
		mgrStatsJSON, err := json.Marshal(&mgrStats)
		if err == nil && len(mgrStatsJSON) > 0 {
			w.Write(mgrStatsJSON)
		} else {
			w.Write(jsonNULL)
		}
	}

	w.Write(jsonCloseBrace)
}

// ---------------------------------------------------

type ManagerKickHandler struct {
	mgr *Manager
}

func NewManagerKickHandler(mgr *Manager) *ManagerKickHandler {
	return &ManagerKickHandler{mgr: mgr}
}

func (h *ManagerKickHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	h.mgr.Kick(req.FormValue("msg"))
	mustEncode(w, struct {
		Status string `json:"status"`
	}{Status: "ok"})
}

// ---------------------------------------------------

type CfgGetHandler struct {
	mgr *Manager
}

func NewCfgGetHandler(mgr *Manager) *CfgGetHandler {
	return &CfgGetHandler{mgr: mgr}
}

func (h *CfgGetHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// TODO: Might need to scrub auth passwords from this output.
	cfg := h.mgr.Cfg()
	indexDefs, indexDefsCAS, indexDefsErr :=
		CfgGetIndexDefs(cfg)
	nodeDefsWanted, nodeDefsWantedCAS, nodeDefsWantedErr :=
		CfgGetNodeDefs(cfg, NODE_DEFS_WANTED)
	nodeDefsKnown, nodeDefsKnownCAS, nodeDefsKnownErr :=
		CfgGetNodeDefs(cfg, NODE_DEFS_KNOWN)
	planPIndexes, planPIndexesCAS, planPIndexesErr :=
		CfgGetPlanPIndexes(cfg)
	mustEncode(w, struct {
		Status            string        `json:"status"`
		IndexDefs         *IndexDefs    `json:"indexDefs"`
		IndexDefsCAS      uint64        `json:"indexDefsCAS"`
		IndexDefsErr      error         `json:"indexDefsErr"`
		NodeDefsWanted    *NodeDefs     `json:"nodeDefsWanted"`
		NodeDefsWantedCAS uint64        `json:"nodeDefsWantedCAS"`
		NodeDefsWantedErr error         `json:"nodeDefsWantedErr"`
		NodeDefsKnown     *NodeDefs     `json:"nodeDefsKnown"`
		NodeDefsKnownCAS  uint64        `json:"nodeDefsKnownCAS"`
		NodeDefsKnownErr  error         `json:"nodeDefsKnownErr"`
		PlanPIndexes      *PlanPIndexes `json:"planPIndexes"`
		PlanPIndexesCAS   uint64        `json:"planPIndexesCAS"`
		PlanPIndexesErr   error         `json:"planPIndexesErr"`
	}{
		Status:            "ok",
		IndexDefs:         indexDefs,
		IndexDefsCAS:      indexDefsCAS,
		IndexDefsErr:      indexDefsErr,
		NodeDefsWanted:    nodeDefsWanted,
		NodeDefsWantedCAS: nodeDefsWantedCAS,
		NodeDefsWantedErr: nodeDefsWantedErr,
		NodeDefsKnown:     nodeDefsKnown,
		NodeDefsKnownCAS:  nodeDefsKnownCAS,
		NodeDefsKnownErr:  nodeDefsKnownErr,
		PlanPIndexes:      planPIndexes,
		PlanPIndexesCAS:   planPIndexesCAS,
		PlanPIndexesErr:   planPIndexesErr,
	})
}

// ---------------------------------------------------

type CfgRefreshHandler struct {
	mgr *Manager
}

func NewCfgRefreshHandler(mgr *Manager) *CfgRefreshHandler {
	return &CfgRefreshHandler{mgr: mgr}
}

func (h *CfgRefreshHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	h.mgr.Cfg().Refresh()
	h.mgr.GetIndexDefs(true)
	h.mgr.GetPlanPIndexes(true)
	mustEncode(w, struct {
		Status string `json:"status"`
	}{Status: "ok"})
}
<commit_msg>added /static/dist/manifest.txt|version.txt to REST diag<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  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 cbft

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

	bleveHttp "github.com/blevesearch/bleve/http"

	"github.com/gorilla/mux"
)

type DiagGetHandler struct {
	versionMain string
	mgr         *Manager
	mr          *MsgRing
}

func NewDiagGetHandler(versionMain string,
	mgr *Manager, mr *MsgRing) *DiagGetHandler {
	return &DiagGetHandler{versionMain: versionMain, mgr: mgr, mr: mr}
}

func (h *DiagGetHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	handlers := []struct {
		Name        string
		Handler     http.Handler
		HandlerFunc http.HandlerFunc
	}{
		{"/api/cfg", NewCfgGetHandler(h.mgr), nil},
		{"/api/index", NewListIndexHandler(h.mgr), nil},
		{"/api/log", NewLogGetHandler(h.mgr, h.mr), nil},
		{"/api/managerMeta", NewManagerMetaHandler(h.mgr, nil), nil},
		{"/api/pindex", NewListPIndexHandler(h.mgr), nil},
		{"/api/pindex-bleve", bleveHttp.NewListIndexesHandler(), nil},
		{"/api/runtime", NewRuntimeGetHandler(h.versionMain, h.mgr), nil},
		{"/api/runtime/args", nil, restGetRuntimeArgs},
		{"/api/runtime/stats", nil, restGetRuntimeStats},
		{"/api/runtime/statsMem", nil, restGetRuntimeStatsMem},
		{"/api/stats", NewStatsHandler(h.mgr), nil},
	}

	w.Write(jsonOpenBrace)
	for i, handler := range handlers {
		if i > 0 {
			w.Write(jsonComma)
		}
		w.Write([]byte(fmt.Sprintf(`"%s":`, handler.Name)))
		if handler.Handler != nil {
			handler.Handler.ServeHTTP(w, req)
		}
		if handler.HandlerFunc != nil {
			handler.HandlerFunc.ServeHTTP(w, req)
		}
	}

	var first = true
	var visit func(path string, f os.FileInfo, err error) error
	visit = func(path string, f os.FileInfo, err error) error {
		m := map[string]interface{}{
			"Path":    path,
			"Name":    f.Name(),
			"Size":    f.Size(),
			"Mode":    f.Mode(),
			"ModTime": f.ModTime().Format(time.RFC3339Nano),
			"IsDir":   f.IsDir(),
		}
		if strings.HasPrefix(f.Name(), "PINDEX_") || // Matches PINDEX_xxx_META.
			strings.HasSuffix(f.Name(), "_META") || // Matches PINDEX_META.
			strings.HasSuffix(f.Name(), ".json") { // Matches index_meta.json.
			b, err := ioutil.ReadFile(path)
			if err == nil {
				m["Contents"] = string(b)
			}
		}
		buf, err := json.Marshal(m)
		if err == nil {
			if !first {
				w.Write(jsonComma)
			}
			w.Write(buf)
			first = false
		}
		return nil
	}

	w.Write([]byte(`,"dataDir":[`))
	filepath.Walk(h.mgr.dataDir, visit)
	w.Write([]byte(`]`))

	entries, err := AssetDir("static/dist")
	if err == nil {
		for _, name := range entries {
			// Ex: "static/dist/manifest.txt".
			a, err := Asset("static/dist/" + name)
			if err == nil {
				j, err := json.Marshal(string(a))
				if err == nil {
					w.Write([]byte(`,"`))
					w.Write([]byte("/static/dist/" + name))
					w.Write([]byte(`":`))
					w.Write(j)
				}
			}
		}
	}

	w.Write(jsonCloseBrace)
}

// ---------------------------------------------------

type StatsHandler struct {
	mgr *Manager
}

func NewStatsHandler(mgr *Manager) *StatsHandler {
	return &StatsHandler{mgr: mgr}
}

var statsFeedsPrefix = []byte("\"feeds\":{")
var statsPIndexesPrefix = []byte("\"pindexes\":{")
var statsManagerPrefix = []byte(",\"manager\":")
var statsNamePrefix = []byte("\"")
var statsNameSuffix = []byte("\":")

func (h *StatsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	indexName := mux.Vars(req)["indexName"]

	feeds, pindexes := h.mgr.CurrentMaps()
	feedNames := make([]string, 0, len(feeds))
	for feedName := range feeds {
		feedNames = append(feedNames, feedName)
	}
	sort.Strings(feedNames)

	pindexNames := make([]string, 0, len(pindexes))
	for pindexName := range pindexes {
		pindexNames = append(pindexNames, pindexName)
	}
	sort.Strings(pindexNames)

	w.Write(jsonOpenBrace)

	first := true
	w.Write(statsFeedsPrefix)
	for _, feedName := range feedNames {
		if indexName == "" || indexName == feeds[feedName].IndexName() {
			if !first {
				w.Write(jsonComma)
			}
			first = false
			w.Write(statsNamePrefix)
			w.Write([]byte(feedName))
			w.Write(statsNameSuffix)
			feeds[feedName].Stats(w)
		}
	}
	w.Write(jsonCloseBraceComma)

	first = true
	w.Write(statsPIndexesPrefix)
	for _, pindexName := range pindexNames {
		if indexName == "" || indexName == pindexes[pindexName].IndexName {
			if !first {
				w.Write(jsonComma)
			}
			first = false
			w.Write(statsNamePrefix)
			w.Write([]byte(pindexName))
			w.Write(statsNameSuffix)
			pindexes[pindexName].Dest.Stats(w)
		}
	}
	w.Write(jsonCloseBrace)

	if indexName == "" {
		w.Write(statsManagerPrefix)
		var mgrStats ManagerStats
		h.mgr.stats.AtomicCopyTo(&mgrStats)
		mgrStatsJSON, err := json.Marshal(&mgrStats)
		if err == nil && len(mgrStatsJSON) > 0 {
			w.Write(mgrStatsJSON)
		} else {
			w.Write(jsonNULL)
		}
	}

	w.Write(jsonCloseBrace)
}

// ---------------------------------------------------

type ManagerKickHandler struct {
	mgr *Manager
}

func NewManagerKickHandler(mgr *Manager) *ManagerKickHandler {
	return &ManagerKickHandler{mgr: mgr}
}

func (h *ManagerKickHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	h.mgr.Kick(req.FormValue("msg"))
	mustEncode(w, struct {
		Status string `json:"status"`
	}{Status: "ok"})
}

// ---------------------------------------------------

type CfgGetHandler struct {
	mgr *Manager
}

func NewCfgGetHandler(mgr *Manager) *CfgGetHandler {
	return &CfgGetHandler{mgr: mgr}
}

func (h *CfgGetHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// TODO: Might need to scrub auth passwords from this output.
	cfg := h.mgr.Cfg()
	indexDefs, indexDefsCAS, indexDefsErr :=
		CfgGetIndexDefs(cfg)
	nodeDefsWanted, nodeDefsWantedCAS, nodeDefsWantedErr :=
		CfgGetNodeDefs(cfg, NODE_DEFS_WANTED)
	nodeDefsKnown, nodeDefsKnownCAS, nodeDefsKnownErr :=
		CfgGetNodeDefs(cfg, NODE_DEFS_KNOWN)
	planPIndexes, planPIndexesCAS, planPIndexesErr :=
		CfgGetPlanPIndexes(cfg)
	mustEncode(w, struct {
		Status            string        `json:"status"`
		IndexDefs         *IndexDefs    `json:"indexDefs"`
		IndexDefsCAS      uint64        `json:"indexDefsCAS"`
		IndexDefsErr      error         `json:"indexDefsErr"`
		NodeDefsWanted    *NodeDefs     `json:"nodeDefsWanted"`
		NodeDefsWantedCAS uint64        `json:"nodeDefsWantedCAS"`
		NodeDefsWantedErr error         `json:"nodeDefsWantedErr"`
		NodeDefsKnown     *NodeDefs     `json:"nodeDefsKnown"`
		NodeDefsKnownCAS  uint64        `json:"nodeDefsKnownCAS"`
		NodeDefsKnownErr  error         `json:"nodeDefsKnownErr"`
		PlanPIndexes      *PlanPIndexes `json:"planPIndexes"`
		PlanPIndexesCAS   uint64        `json:"planPIndexesCAS"`
		PlanPIndexesErr   error         `json:"planPIndexesErr"`
	}{
		Status:            "ok",
		IndexDefs:         indexDefs,
		IndexDefsCAS:      indexDefsCAS,
		IndexDefsErr:      indexDefsErr,
		NodeDefsWanted:    nodeDefsWanted,
		NodeDefsWantedCAS: nodeDefsWantedCAS,
		NodeDefsWantedErr: nodeDefsWantedErr,
		NodeDefsKnown:     nodeDefsKnown,
		NodeDefsKnownCAS:  nodeDefsKnownCAS,
		NodeDefsKnownErr:  nodeDefsKnownErr,
		PlanPIndexes:      planPIndexes,
		PlanPIndexesCAS:   planPIndexesCAS,
		PlanPIndexesErr:   planPIndexesErr,
	})
}

// ---------------------------------------------------

type CfgRefreshHandler struct {
	mgr *Manager
}

func NewCfgRefreshHandler(mgr *Manager) *CfgRefreshHandler {
	return &CfgRefreshHandler{mgr: mgr}
}

func (h *CfgRefreshHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	h.mgr.Cfg().Refresh()
	h.mgr.GetIndexDefs(true)
	h.mgr.GetPlanPIndexes(true)
	mustEncode(w, struct {
		Status string `json:"status"`
	}{Status: "ok"})
}
<|endoftext|>


<commit_before>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)
}
<commit_msg>Fixed problem on CORS<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)
	wr.addCORSHeaders(self.domain)
	if strings.ToLower(r.Method) == "options" {
		http.Redirect(wr, r, r.RequestURI, 200)
		return
	}
	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>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package router 0.5.0 provides fast HTTP request router.

The router matches incoming requests by the request method and the path.
If a handle is registered for this path and method, the router delegates the
request to defined handler.
The router package is useful to prepare a RESTful API for Go services.
It has JSON output, which bind automatically for relevant type of data.
The router has timer feature to display duration of request handling in the header

Simplest example (serve static route):

	package main

	import (
		"github.com/takama/router"
	)

	func Hello(c *router.Control) {
		c.Body("Hello")
	}

	func main() {
		r := router.New()
		r.GET("/hello", Hello)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Check it:

	curl -i http://localhost:8888/hello

Serve dynamic route with parameter:

	func main() {
		r := router.New()
		r.GET("/hello/:name", func(c *router.Control) {
			c.Code(200).Body("Hello " + c.Get(":name"))
		})

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Checks JSON Content-Type automatically:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func main() {
		r := router.New()
		r.GET("/settings/database/:db", func(c *router.Control) {
			data := map[string]map[string]string{
				"Database settings": {
					"database": c.Get(":db"),
					"host":     "localhost",
					"port":     "3306",
				},
			}
			c.Code(200).Body(data)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Custom handler with "Access-Control-Allow" options and compact JSON:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func baseHandler(handle router.Handle) router.Handle {
		return func(c *router.Control) {
			c.CompactJSON(true)
			if origin := c.Request.Header.Get("Origin"); origin != "" {
				c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
				c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
			}
			handle(c)
		}
	}

	func Info(c *router.Control) {
		data := Data{
			"debug": true,
			"error": false,
		}
		c.Body(data)
	}

	func main() {
		r := router.New()
		r.CustomHandler = baseHandler
		r.GET("/info", Info)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Use google json style `https://google.github.io/styleguide/jsoncstyleguide.xml`

	func main() {
		r := router.New()
		r.GET("/api/v1/people/:action/:id", func(c *router.Control) {

			// Do something

			c.Method("people." + c.Get(":action"))
			c.SetParams(map[string]string{"userId": c.Get(":id")})
			c.SetError(http.StatusNotFound, "UserId not found")
			c.AddError(router.Error{Message: "Group or User not found"})
			c.Code(http.StatusNotFound).Body(nil)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Go Router
*/
package router

import (
	"log"
	"net/http"
	"strings"
)

// Router represents a multiplexer for HTTP requests.
type Router struct {
	// List of handlers which accociated with known http methods (GET, POST ...)
	handlers map[string]*parser

	// NotFound is called when unknown HTTP method or a handler not found.
	// If it is not set, http.NotFound is used.
	// Please overwrite this if need your own NotFound handler.
	NotFound Handle

	// PanicHandler is called when panic happen.
	// The handler prevents your server from crashing and should be used to return
	// http status code http.StatusInternalServerError (500)
	PanicHandler Handle

	// CustomHandler is called allways if defined
	CustomHandler func(Handle) Handle

	// Logger activates logging user function for each requests
	Logger Handle
}

// Handle type is aliased to type of handler function.
type Handle func(*Control)

// Handler type contains information about method and path
type Handler struct {
	Method string
	Path   string
}

// New it returns a new multiplexer (Router).
func New() *Router {
	return &Router{handlers: make(map[string]*parser)}
}

// GET is a shortcut for Router Handle("GET", path, handle)
func (r *Router) GET(path string, h Handle) {
	r.Handle("GET", path, h)
}

// POST is a shortcut for Router Handle("POST", path, handle)
func (r *Router) POST(path string, h Handle) {
	r.Handle("POST", path, h)
}

// PUT is a shortcut for Router Handle("PUT", path, handle)
func (r *Router) PUT(path string, h Handle) {
	r.Handle("PUT", path, h)
}

// DELETE is a shortcut for Router Handle("DELETE", path, handle)
func (r *Router) DELETE(path string, h Handle) {
	r.Handle("DELETE", path, h)
}

// HEAD is a shortcut for Router Handle("HEAD", path, handle)
func (r *Router) HEAD(path string, h Handle) {
	r.Handle("HEAD", path, h)
}

// OPTIONS is a shortcut for Router Handle("OPTIONS", path, handle)
func (r *Router) OPTIONS(path string, h Handle) {
	r.Handle("OPTIONS", path, h)
}

// Handle registers a new request handle with the given path and method.
func (r *Router) Handle(method, path string, h Handle) {
	if r.handlers[method] == nil {
		r.handlers[method] = newParser()
	}
	r.handlers[method].register(path, h)
}

// Handler allows the usage of an http.Handler as a request handle.
func (r *Router) Handler(method, path string, handler http.Handler) {
	r.Handle(method, path,
		func(c *Control) {
			handler.ServeHTTP(c.Writer, c.Request)
		},
	)
}

// HandlerFunc allows the usage of an http.HandlerFunc as a request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(c *Control) {
			handler(c.Writer, c.Request)
		},
	)
}

// Lookup returns handler and URL parameters that associated with path.
func (r *Router) Lookup(method, path string) (Handle, []Param, bool) {
	if parser := r.handlers[method]; parser != nil {
		return parser.get(path)
	}
	return nil, nil, false
}

// AllowedMethods returns list of allowed methods
func (r *Router) AllowedMethods(path string) []string {
	var allowed []string
	for method, parser := range r.handlers {
		if _, _, ok := parser.get(path); ok {
			allowed = append(allowed, method)
		}
	}

	return allowed
}

// Listen and serve on requested host and port.
func (r *Router) Listen(hostPort string) {
	if err := http.ListenAndServe(hostPort, r); err != nil {
		log.Fatal(err)
	}
}

// ServeHTTP implements http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if recovery := recover(); recovery != nil {
			if r.PanicHandler != nil {
				c := &Control{Request: req, Writer: w}
				r.PanicHandler(c)
			} else {
				log.Println("Recovered in handler:", req.Method, req.URL.Path)
			}
		}
	}()
	if r.Logger != nil {
		c := &Control{Request: req, Writer: w}
		r.Logger(c)
	}
	if _, ok := r.handlers[req.Method]; ok {
		if handle, params, ok := r.handlers[req.Method].get(req.URL.Path); ok {
			c := &Control{Request: req, Writer: w}
			if len(params) > 0 {
				c.params = append(c.params, params...)
			}
			if r.CustomHandler != nil {
				r.CustomHandler(handle)(c)
			} else {
				handle(c)
			}
			return
		}
	}
	allowed := r.AllowedMethods(req.URL.Path)

	if len(allowed) == 0 {
		if r.NotFound != nil {
			c := &Control{Request: req, Writer: w}
			r.NotFound(c)
		} else {
			http.NotFound(w, req)
		}
		return
	}

	w.Header().Add("Allow", strings.Join(allowed, ", "))
	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}

// HandlersList returns list of handlers
func (r *Router) HandlersList() []Handler {
	var handlers []Handler
	for method, parser := range r.handlers {
		for _, path := range parser.paths() {
			handlers = append(handlers, Handler{Method: method, Path: path})
		}
	}

	return handlers
}
<commit_msg>Bumped version number to 0.6.0<commit_after>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package router 0.6.0 provides fast HTTP request router.

The router matches incoming requests by the request method and the path.
If a handle is registered for this path and method, the router delegates the
request to defined handler.
The router package is useful to prepare a RESTful API for Go services.
It has JSON output, which bind automatically for relevant type of data.
The router has timer feature to display duration of request handling in the header

Simplest example (serve static route):

	package main

	import (
		"github.com/takama/router"
	)

	func Hello(c *router.Control) {
		c.Body("Hello")
	}

	func main() {
		r := router.New()
		r.GET("/hello", Hello)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Check it:

	curl -i http://localhost:8888/hello

Serve dynamic route with parameter:

	func main() {
		r := router.New()
		r.GET("/hello/:name", func(c *router.Control) {
			c.Code(200).Body("Hello " + c.Get(":name"))
		})

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Checks JSON Content-Type automatically:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func main() {
		r := router.New()
		r.GET("/settings/database/:db", func(c *router.Control) {
			data := map[string]map[string]string{
				"Database settings": {
					"database": c.Get(":db"),
					"host":     "localhost",
					"port":     "3306",
				},
			}
			c.Code(200).Body(data)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Custom handler with "Access-Control-Allow" options and compact JSON:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func baseHandler(handle router.Handle) router.Handle {
		return func(c *router.Control) {
			c.CompactJSON(true)
			if origin := c.Request.Header.Get("Origin"); origin != "" {
				c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
				c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
			}
			handle(c)
		}
	}

	func Info(c *router.Control) {
		data := Data{
			"debug": true,
			"error": false,
		}
		c.Body(data)
	}

	func main() {
		r := router.New()
		r.CustomHandler = baseHandler
		r.GET("/info", Info)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Use google json style `https://google.github.io/styleguide/jsoncstyleguide.xml`

	func main() {
		r := router.New()
		r.GET("/api/v1/people/:action/:id", func(c *router.Control) {

			// Do something

			c.Method("people." + c.Get(":action"))
			c.SetParams(map[string]string{"userId": c.Get(":id")})
			c.SetError(http.StatusNotFound, "UserId not found")
			c.AddError(router.Error{Message: "Group or User not found"})
			c.Code(http.StatusNotFound).Body(nil)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Go Router
*/
package router

import (
	"log"
	"net/http"
	"strings"
)

// Router represents a multiplexer for HTTP requests.
type Router struct {
	// List of handlers which accociated with known http methods (GET, POST ...)
	handlers map[string]*parser

	// NotFound is called when unknown HTTP method or a handler not found.
	// If it is not set, http.NotFound is used.
	// Please overwrite this if need your own NotFound handler.
	NotFound Handle

	// PanicHandler is called when panic happen.
	// The handler prevents your server from crashing and should be used to return
	// http status code http.StatusInternalServerError (500)
	PanicHandler Handle

	// CustomHandler is called allways if defined
	CustomHandler func(Handle) Handle

	// Logger activates logging user function for each requests
	Logger Handle
}

// Handle type is aliased to type of handler function.
type Handle func(*Control)

// Handler type contains information about method and path
type Handler struct {
	Method string
	Path   string
}

// New it returns a new multiplexer (Router).
func New() *Router {
	return &Router{handlers: make(map[string]*parser)}
}

// GET is a shortcut for Router Handle("GET", path, handle)
func (r *Router) GET(path string, h Handle) {
	r.Handle("GET", path, h)
}

// POST is a shortcut for Router Handle("POST", path, handle)
func (r *Router) POST(path string, h Handle) {
	r.Handle("POST", path, h)
}

// PUT is a shortcut for Router Handle("PUT", path, handle)
func (r *Router) PUT(path string, h Handle) {
	r.Handle("PUT", path, h)
}

// DELETE is a shortcut for Router Handle("DELETE", path, handle)
func (r *Router) DELETE(path string, h Handle) {
	r.Handle("DELETE", path, h)
}

// HEAD is a shortcut for Router Handle("HEAD", path, handle)
func (r *Router) HEAD(path string, h Handle) {
	r.Handle("HEAD", path, h)
}

// OPTIONS is a shortcut for Router Handle("OPTIONS", path, handle)
func (r *Router) OPTIONS(path string, h Handle) {
	r.Handle("OPTIONS", path, h)
}

// Handle registers a new request handle with the given path and method.
func (r *Router) Handle(method, path string, h Handle) {
	if r.handlers[method] == nil {
		r.handlers[method] = newParser()
	}
	r.handlers[method].register(path, h)
}

// Handler allows the usage of an http.Handler as a request handle.
func (r *Router) Handler(method, path string, handler http.Handler) {
	r.Handle(method, path,
		func(c *Control) {
			handler.ServeHTTP(c.Writer, c.Request)
		},
	)
}

// HandlerFunc allows the usage of an http.HandlerFunc as a request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(c *Control) {
			handler(c.Writer, c.Request)
		},
	)
}

// Lookup returns handler and URL parameters that associated with path.
func (r *Router) Lookup(method, path string) (Handle, []Param, bool) {
	if parser := r.handlers[method]; parser != nil {
		return parser.get(path)
	}
	return nil, nil, false
}

// AllowedMethods returns list of allowed methods
func (r *Router) AllowedMethods(path string) []string {
	var allowed []string
	for method, parser := range r.handlers {
		if _, _, ok := parser.get(path); ok {
			allowed = append(allowed, method)
		}
	}

	return allowed
}

// Listen and serve on requested host and port.
func (r *Router) Listen(hostPort string) {
	if err := http.ListenAndServe(hostPort, r); err != nil {
		log.Fatal(err)
	}
}

// ServeHTTP implements http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if recovery := recover(); recovery != nil {
			if r.PanicHandler != nil {
				c := &Control{Request: req, Writer: w}
				r.PanicHandler(c)
			} else {
				log.Println("Recovered in handler:", req.Method, req.URL.Path)
			}
		}
	}()
	if r.Logger != nil {
		c := &Control{Request: req, Writer: w}
		r.Logger(c)
	}
	if _, ok := r.handlers[req.Method]; ok {
		if handle, params, ok := r.handlers[req.Method].get(req.URL.Path); ok {
			c := &Control{Request: req, Writer: w}
			if len(params) > 0 {
				c.params = append(c.params, params...)
			}
			if r.CustomHandler != nil {
				r.CustomHandler(handle)(c)
			} else {
				handle(c)
			}
			return
		}
	}
	allowed := r.AllowedMethods(req.URL.Path)

	if len(allowed) == 0 {
		if r.NotFound != nil {
			c := &Control{Request: req, Writer: w}
			r.NotFound(c)
		} else {
			http.NotFound(w, req)
		}
		return
	}

	w.Header().Add("Allow", strings.Join(allowed, ", "))
	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}

// HandlersList returns list of handlers
func (r *Router) HandlersList() []Handler {
	var handlers []Handler
	for method, parser := range r.handlers {
		for _, path := range parser.paths() {
			handlers = append(handlers, Handler{Method: method, Path: path})
		}
	}

	return handlers
}
<|endoftext|>


<commit_before>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicGoXp, then you can add Routes as a
// service with:
//
//     m := goxp.New()
//     r := goxp.NewRouter()
//     m.MapTo(r, (*goxp.Routes)(nil))
//
// If you are using ClassicGoXp, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Router {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Router {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}

func (r *router) Options(pattern string, h ...Handler) Route {
	return r.addRoute("OPTIONS", pattern, h)
}

func (r *router) Head(pattern string, h ...Handler) Route {
	return r.addRoute("HEAD", pattern, h)
}

func (r *router) Any(pattern string, h ...Handler) Route {
	return r.addRoute("*", pattern, h)
}

func (r *router) AddRoute(method, pattern string, h ...Handler) Route {
	return r.addRoute(method, pattern, h)
}

func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
	bestMatch := NoMatch
	var bestVals map[string]string
	var vestRoute *route
	for _, route := range r.getRoutes() {
		match, vals := route.Match(req.Method, req.URL.Path)
		if match.BetterThan(bestMatch) {
			bestMatch = match
			bestVals = vals
			bestRoute = route
			if match == ExactMatch {
				break
			}
		}
	}
	if bestMatch != NoMatch {
		params := Params(bestVals)
		context.Map(params)
		bestRoute.Handle(context, res)
		return
	}

	// no routes exist, 404
	c := &routeContext{context, 0, r.notFounds}
	context.MapTo(c, (*Context)(nil))
	c.run()
}

func (r *router) NotFound(handler ...Handler) {
	r.notFounds = handler
}

func (r *router) addRoute(method string, pattern, pattern string, handlers []Handler) *route {
	if len(r.groups) > 0 {
		groupPattern := ""
		h := make([]Handler, 0)
		for _, g := range r.groups {
			groupPattern += g.pattern
			h = append(h, g.handlers...)
		}

		pattern = groupPattern + pattern
		h = append(h, handlers...)
		handlers = h
	}

	route := newRoute(method, pattern, handlers)
	route.Validate()
	r.appendRoute(route)
	return route
}

func (r *router) appendRoute(rt *route) {
	r.routeLock.Lock()
	defer r.routesLock.RUnlock()
	r.routes = append(r.routes, rt)
}

func (r *router) getRoutes() []*route {
	r.routesLock.RLock()
	defer r.routesLock.RUnlock()
	return r.routes[:]
}

func (r *router) findRoute(name string) *route {
	for _, route := range r.getRoutes() {
		if route.name == name {
			return route
		}
	}

	return nil
}

// Route is an interface representing a Route in GoXp's routing layer.
type Route interface {
	// URLWith returns a rendering of the Routes's url with the given string params.
	Name(string)
	// GetName returns the name of the route.
	GetName() string
	// Pattern returns the pattern of the route.
	Pattern() string
	// Method returns the method of the route.
	Method() string
}

<commit_msg>router.go +type route struct<commit_after>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicGoXp, then you can add Routes as a
// service with:
//
//     m := goxp.New()
//     r := goxp.NewRouter()
//     m.MapTo(r, (*goxp.Routes)(nil))
//
// If you are using ClassicGoXp, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Router {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Router {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}

func (r *router) Options(pattern string, h ...Handler) Route {
	return r.addRoute("OPTIONS", pattern, h)
}

func (r *router) Head(pattern string, h ...Handler) Route {
	return r.addRoute("HEAD", pattern, h)
}

func (r *router) Any(pattern string, h ...Handler) Route {
	return r.addRoute("*", pattern, h)
}

func (r *router) AddRoute(method, pattern string, h ...Handler) Route {
	return r.addRoute(method, pattern, h)
}

func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
	bestMatch := NoMatch
	var bestVals map[string]string
	var vestRoute *route
	for _, route := range r.getRoutes() {
		match, vals := route.Match(req.Method, req.URL.Path)
		if match.BetterThan(bestMatch) {
			bestMatch = match
			bestVals = vals
			bestRoute = route
			if match == ExactMatch {
				break
			}
		}
	}
	if bestMatch != NoMatch {
		params := Params(bestVals)
		context.Map(params)
		bestRoute.Handle(context, res)
		return
	}

	// no routes exist, 404
	c := &routeContext{context, 0, r.notFounds}
	context.MapTo(c, (*Context)(nil))
	c.run()
}

func (r *router) NotFound(handler ...Handler) {
	r.notFounds = handler
}

func (r *router) addRoute(method string, pattern, pattern string, handlers []Handler) *route {
	if len(r.groups) > 0 {
		groupPattern := ""
		h := make([]Handler, 0)
		for _, g := range r.groups {
			groupPattern += g.pattern
			h = append(h, g.handlers...)
		}

		pattern = groupPattern + pattern
		h = append(h, handlers...)
		handlers = h
	}

	route := newRoute(method, pattern, handlers)
	route.Validate()
	r.appendRoute(route)
	return route
}

func (r *router) appendRoute(rt *route) {
	r.routeLock.Lock()
	defer r.routesLock.RUnlock()
	r.routes = append(r.routes, rt)
}

func (r *router) getRoutes() []*route {
	r.routesLock.RLock()
	defer r.routesLock.RUnlock()
	return r.routes[:]
}

func (r *router) findRoute(name string) *route {
	for _, route := range r.getRoutes() {
		if route.name == name {
			return route
		}
	}

	return nil
}

// Route is an interface representing a Route in GoXp's routing layer.
type Route interface {
	// URLWith returns a rendering of the Routes's url with the given string params.
	Name(string)
	// GetName returns the name of the route.
	GetName() string
	// Pattern returns the pattern of the route.
	Pattern() string
	// Method returns the method of the route.
	Method() string
}

type route struct {
	method   string
	regex    *regex.Regexp
	handlers []Handler
	pattern  string
	name     string
}

<|endoftext|>


<commit_before>package main

import (
	"log"
	"time"

	"github.com/johnmccabe/mote"
	"github.com/lucasb-eyer/go-colorful"
)

func main() {
	mote := mote.NewMote("")
	mote.ConfigureChannel(1, 16, false)
	mote.ConfigureChannel(2, 16, false)
	mote.ConfigureChannel(3, 16, false)
	mote.ConfigureChannel(4, 16, false)

	i := 0

	for {
		// t := int(time.Now().Unix())
		t := int(float64(time.Now().UnixNano()) * 0.00000005)
		// h := t * 50
		for channel := 1; channel < 5; channel++ {
			// log.Printf("Channel: %d\n", channel)
			for pixel := 0; pixel < 16; pixel++ {
				hue := (t + (channel * 64) + (pixel * 4)) % 360
				hsv := colorful.Hsv(float64(hue), 1.0, 1.0)
				// log.Printf("Hsv: %v\n", hsv)
				r := int(hsv.R * 255)
				g := int(hsv.G * 255)
				b := int(hsv.B * 255)
				if pixel == 0 {
					log.Printf("Pixel: 0, Hue: %v, R: %d, G: %d, B: %d\n", hue, r, g, b)
				}
				mote.SetPixel(channel, pixel, r, g, b)

				// log.Printf("i: %d, t: %d\n", i, t)
			}
		}

		time.Sleep(2 * time.Millisecond)
		log.Printf("i: %d, t: %d\n", i, t)
		i++
		mote.Show()
	}
	// for channel in range(4):
	// for pixel in range(16):
	//     mote.set_pixel(channel + 1, pixel, r, g, b)
	// time.Sleep(100 * time.Millisecond)
	// mote.Close()
}
<commit_msg>Remove debug logging from rainbow<commit_after>package main

import (
	"time"

	"github.com/johnmccabe/mote"
	"github.com/lucasb-eyer/go-colorful"
)

func main() {
	mote := mote.NewMote("")
	defer mote.Close()

	mote.ConfigureChannel(1, 16, false)
	mote.ConfigureChannel(2, 16, false)
	mote.ConfigureChannel(3, 16, false)
	mote.ConfigureChannel(4, 16, false)

	for {
		t := int(float64(time.Now().UnixNano()) * 0.00000005)
		for channel := 1; channel < 5; channel++ {
			for pixel := 0; pixel < 16; pixel++ {
				hue := (t + (channel * 64) + (pixel * 4)) % 360
				hsv := colorful.Hsv(float64(hue), 1.0, 1.0)
				r := int(hsv.R * 255)
				g := int(hsv.G * 255)
				b := int(hsv.B * 255)
				mote.SetPixel(channel, pixel, r, g, b)
			}
		}

		time.Sleep(10 * time.Millisecond)
		mote.Show()
	}
}
<|endoftext|>


<commit_before>package concourse_test

import (
	"fmt"
	"net/http"

	"github.com/concourse/atc"
	"github.com/concourse/go-concourse/concourse"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("ATC Handler Builds", func() {
	Describe("CreateBuild", func() {
		var (
			plan          atc.Plan
			expectedBuild atc.Build
		)

		BeforeEach(func() {
			plan = atc.Plan{
				OnSuccess: &atc.OnSuccessPlan{
					Step: atc.Plan{
						Aggregate: &atc.AggregatePlan{},
					},
					Next: atc.Plan{
						ID: "some-guid",
						Task: &atc.TaskPlan{
							Name:       "one-off",
							Privileged: true,
							Config:     &atc.TaskConfig{},
						},
					},
				},
			}

			expectedBuild = atc.Build{
				ID:      123,
				Name:    "mybuild",
				Status:  "succeeded",
				JobName: "myjob",
				URL:     "/builds/123",
				APIURL:  "/api/v1/builds/123",
			}

			expectedURL := "/api/v1/builds"

			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("POST", expectedURL),
					ghttp.VerifyJSONRepresenting(plan),
					ghttp.RespondWithJSONEncoded(http.StatusCreated, expectedBuild),
				),
			)
		})

		It("takes a plan and creates the build", func() {
			build, err := client.CreateBuild(plan)
			Expect(err).NotTo(HaveOccurred())
			Expect(build).To(Equal(expectedBuild))
		})
	})

	Describe("CreateJobBuild", func() {
		var (
			pipelineName  string
			jobName       string
			expectedBuild atc.Build
		)
		BeforeEach(func() {
			pipelineName = "mypipeline"
			jobName = "myjob"

			expectedBuild = atc.Build{
				ID:      123,
				Name:    "mybuild",
				Status:  "succeeded",
				JobName: "myjob",
				URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild",
				APIURL:  "api/v1/builds/123",
			}
			expectedURL := "/api/v1/teams/some-team/pipelines/mypipeline/jobs/myjob/builds"

			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("POST", expectedURL),
					ghttp.RespondWithJSONEncoded(http.StatusCreated, expectedBuild),
				),
			)
		})

		It("takes a pipeline and a job and creates the build", func() {
			build, err := team.CreateJobBuild(pipelineName, jobName)
			Expect(err).NotTo(HaveOccurred())
			Expect(build).To(Equal(expectedBuild))
		})
	})

	Describe("JobBuild", func() {
		var (
			expectedBuild atc.Build
			expectedURL   string
		)

		Context("when build exists", func() {
			BeforeEach(func() {
				expectedBuild = atc.Build{
					ID:      123,
					Name:    "mybuild",
					Status:  "succeeded",
					JobName: "myjob",
					URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild",
					APIURL:  "api/v1/builds/123",
				}

				expectedURL = "/api/v1/teams/some-team/pipelines/mypipeline/jobs/myjob/builds/mybuild"

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuild),
					),
				)
			})

			It("returns the given build", func() {
				build, found, err := team.JobBuild("mypipeline", "myjob", "mybuild")
				Expect(err).NotTo(HaveOccurred())
				Expect(found).To(BeTrue())
				Expect(build).To(Equal(expectedBuild))
			})
		})

		Context("when build does not exist", func() {
			BeforeEach(func() {
				expectedURL = "/api/v1/teams/some-team/pipelines/mypipeline/jobs/myjob/builds/mybuild"

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusNotFound, nil),
					),
				)
			})

			It("return false and no error", func() {
				_, found, err := team.JobBuild("mypipeline", "myjob", "mybuild")
				Expect(err).NotTo(HaveOccurred())
				Expect(found).To(BeFalse())
			})
		})
	})

	Describe("Build", func() {
		Context("when build exists", func() {
			expectedBuild := atc.Build{
				ID:      123,
				Name:    "mybuild",
				Status:  "succeeded",
				JobName: "myjob",
				URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild",
				APIURL:  "api/v1/builds/123",
			}
			expectedURL := "/api/v1/builds/123"

			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuild),
					),
				)
			})

			It("returns the given build", func() {
				build, found, err := client.Build("123")
				Expect(err).NotTo(HaveOccurred())
				Expect(found).To(BeTrue())
				Expect(build).To(Equal(expectedBuild))
			})
		})

		Context("when build does not exists", func() {
			BeforeEach(func() {
				expectedURL := "/api/v1/builds/123"

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusNotFound, nil),
					),
				)
			})

			It("returns false and no error", func() {
				_, found, err := client.Build("123")
				Expect(err).ToNot(HaveOccurred())
				Expect(found).To(BeFalse())
			})
		})
	})

	Describe("Builds", func() {
		expectedURL := "/api/v1/builds"

		var expectedBuilds []atc.Build

		var page concourse.Page

		var builds []atc.Build
		var pagination concourse.Pagination
		var clientErr error

		BeforeEach(func() {
			page = concourse.Page{}

			expectedBuilds = []atc.Build{
				{
					ID:      123,
					Name:    "mybuild1",
					Status:  "succeeded",
					JobName: "myjob",
					URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild1",
					APIURL:  "api/v1/builds/123",
				},
				{
					ID:      124,
					Name:    "mybuild2",
					Status:  "succeeded",
					JobName: "myjob",
					URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild2",
					APIURL:  "api/v1/builds/124",
				},
			}
		})

		JustBeforeEach(func() {
			builds, pagination, clientErr = client.Builds(page)
		})

		Context("when since, until, and limit are 0", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("calls to get all builds", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when since is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Since: 24}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "since=24"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("calls to get all builds since that id", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when since and limit is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Since: 24, Limit: 5}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "since=24&limit=5"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("appends limit to the url", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when until is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Until: 26}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "until=26"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("calls to get all builds until that id", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when until and limit is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Until: 26, Limit: 15}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "until=26&limit=15"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("appends limit to the url", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when since and until are both specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Since: 24, Until: 26}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "until=26"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("only sends the until", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when the server returns an error", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWith(http.StatusInternalServerError, ""),
					),
				)
			})

			It("returns false and an error", func() {
				Expect(clientErr).To(HaveOccurred())
			})
		})

		Context("pagination data", func() {
			Context("with a link header", func() {
				BeforeEach(func() {
					expectedURL = fmt.Sprint("/api/v1/builds")

					atcServer.AppendHandlers(
						ghttp.CombineHandlers(
							ghttp.VerifyRequest("GET", expectedURL),
							ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds, http.Header{
								"Link": []string{
									`<http://some-url.com/api/v1/builds?since=452&limit=123>; rel="previous"`,
									`<http://some-url.com/api/v1/builds?until=254&limit=456>; rel="next"`,
								},
							}),
						),
					)
				})

				It("returns the pagination data from the header", func() {
					Expect(clientErr).ToNot(HaveOccurred())
					Expect(pagination.Previous).To(Equal(&concourse.Page{Since: 452, Limit: 123}))
					Expect(pagination.Next).To(Equal(&concourse.Page{Until: 254, Limit: 456}))
				})
			})
		})

		Context("without a link header", func() {
			BeforeEach(func() {
				expectedURL = fmt.Sprint("/api/v1/builds")

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds, http.Header{}),
					),
				)
			})

			It("returns pagination data with nil pages", func() {
				Expect(clientErr).ToNot(HaveOccurred())
				Expect(pagination.Previous).To(BeNil())
				Expect(pagination.Next).To(BeNil())
			})
		})
	})

	Describe("AbortBuild", func() {
		BeforeEach(func() {
			expectedURL := "/api/v1/builds/123/abort"

			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("POST", expectedURL),
					ghttp.RespondWith(http.StatusNoContent, ""),
				),
			)
		})

		It("sends an abort request to ATC", func() {
			Expect(func() {
				err := client.AbortBuild("123")
				Expect(err).NotTo(HaveOccurred())
			}).To(Change(func() int {
				return len(atcServer.ReceivedRequests())
			}).By(1))
		})
	})
})
<commit_msg>changed test to expect a PUT<commit_after>package concourse_test

import (
	"fmt"
	"net/http"

	"github.com/concourse/atc"
	"github.com/concourse/go-concourse/concourse"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("ATC Handler Builds", func() {
	Describe("CreateBuild", func() {
		var (
			plan          atc.Plan
			expectedBuild atc.Build
		)

		BeforeEach(func() {
			plan = atc.Plan{
				OnSuccess: &atc.OnSuccessPlan{
					Step: atc.Plan{
						Aggregate: &atc.AggregatePlan{},
					},
					Next: atc.Plan{
						ID: "some-guid",
						Task: &atc.TaskPlan{
							Name:       "one-off",
							Privileged: true,
							Config:     &atc.TaskConfig{},
						},
					},
				},
			}

			expectedBuild = atc.Build{
				ID:      123,
				Name:    "mybuild",
				Status:  "succeeded",
				JobName: "myjob",
				URL:     "/builds/123",
				APIURL:  "/api/v1/builds/123",
			}

			expectedURL := "/api/v1/builds"

			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("POST", expectedURL),
					ghttp.VerifyJSONRepresenting(plan),
					ghttp.RespondWithJSONEncoded(http.StatusCreated, expectedBuild),
				),
			)
		})

		It("takes a plan and creates the build", func() {
			build, err := client.CreateBuild(plan)
			Expect(err).NotTo(HaveOccurred())
			Expect(build).To(Equal(expectedBuild))
		})
	})

	Describe("CreateJobBuild", func() {
		var (
			pipelineName  string
			jobName       string
			expectedBuild atc.Build
		)
		BeforeEach(func() {
			pipelineName = "mypipeline"
			jobName = "myjob"

			expectedBuild = atc.Build{
				ID:      123,
				Name:    "mybuild",
				Status:  "succeeded",
				JobName: "myjob",
				URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild",
				APIURL:  "api/v1/builds/123",
			}
			expectedURL := "/api/v1/teams/some-team/pipelines/mypipeline/jobs/myjob/builds"

			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("POST", expectedURL),
					ghttp.RespondWithJSONEncoded(http.StatusCreated, expectedBuild),
				),
			)
		})

		It("takes a pipeline and a job and creates the build", func() {
			build, err := team.CreateJobBuild(pipelineName, jobName)
			Expect(err).NotTo(HaveOccurred())
			Expect(build).To(Equal(expectedBuild))
		})
	})

	Describe("JobBuild", func() {
		var (
			expectedBuild atc.Build
			expectedURL   string
		)

		Context("when build exists", func() {
			BeforeEach(func() {
				expectedBuild = atc.Build{
					ID:      123,
					Name:    "mybuild",
					Status:  "succeeded",
					JobName: "myjob",
					URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild",
					APIURL:  "api/v1/builds/123",
				}

				expectedURL = "/api/v1/teams/some-team/pipelines/mypipeline/jobs/myjob/builds/mybuild"

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuild),
					),
				)
			})

			It("returns the given build", func() {
				build, found, err := team.JobBuild("mypipeline", "myjob", "mybuild")
				Expect(err).NotTo(HaveOccurred())
				Expect(found).To(BeTrue())
				Expect(build).To(Equal(expectedBuild))
			})
		})

		Context("when build does not exist", func() {
			BeforeEach(func() {
				expectedURL = "/api/v1/teams/some-team/pipelines/mypipeline/jobs/myjob/builds/mybuild"

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusNotFound, nil),
					),
				)
			})

			It("return false and no error", func() {
				_, found, err := team.JobBuild("mypipeline", "myjob", "mybuild")
				Expect(err).NotTo(HaveOccurred())
				Expect(found).To(BeFalse())
			})
		})
	})

	Describe("Build", func() {
		Context("when build exists", func() {
			expectedBuild := atc.Build{
				ID:      123,
				Name:    "mybuild",
				Status:  "succeeded",
				JobName: "myjob",
				URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild",
				APIURL:  "api/v1/builds/123",
			}
			expectedURL := "/api/v1/builds/123"

			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuild),
					),
				)
			})

			It("returns the given build", func() {
				build, found, err := client.Build("123")
				Expect(err).NotTo(HaveOccurred())
				Expect(found).To(BeTrue())
				Expect(build).To(Equal(expectedBuild))
			})
		})

		Context("when build does not exists", func() {
			BeforeEach(func() {
				expectedURL := "/api/v1/builds/123"

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusNotFound, nil),
					),
				)
			})

			It("returns false and no error", func() {
				_, found, err := client.Build("123")
				Expect(err).ToNot(HaveOccurred())
				Expect(found).To(BeFalse())
			})
		})
	})

	Describe("Builds", func() {
		expectedURL := "/api/v1/builds"

		var expectedBuilds []atc.Build

		var page concourse.Page

		var builds []atc.Build
		var pagination concourse.Pagination
		var clientErr error

		BeforeEach(func() {
			page = concourse.Page{}

			expectedBuilds = []atc.Build{
				{
					ID:      123,
					Name:    "mybuild1",
					Status:  "succeeded",
					JobName: "myjob",
					URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild1",
					APIURL:  "api/v1/builds/123",
				},
				{
					ID:      124,
					Name:    "mybuild2",
					Status:  "succeeded",
					JobName: "myjob",
					URL:     "/pipelines/mypipeline/jobs/myjob/builds/mybuild2",
					APIURL:  "api/v1/builds/124",
				},
			}
		})

		JustBeforeEach(func() {
			builds, pagination, clientErr = client.Builds(page)
		})

		Context("when since, until, and limit are 0", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("calls to get all builds", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when since is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Since: 24}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "since=24"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("calls to get all builds since that id", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when since and limit is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Since: 24, Limit: 5}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "since=24&limit=5"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("appends limit to the url", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when until is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Until: 26}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "until=26"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("calls to get all builds until that id", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when until and limit is specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Until: 26, Limit: 15}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "until=26&limit=15"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("appends limit to the url", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when since and until are both specified", func() {
			BeforeEach(func() {
				page = concourse.Page{Since: 24, Until: 26}

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL, "until=26"),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds),
					),
				)
			})

			It("only sends the until", func() {
				Expect(clientErr).NotTo(HaveOccurred())
				Expect(builds).To(Equal(expectedBuilds))
			})
		})

		Context("when the server returns an error", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWith(http.StatusInternalServerError, ""),
					),
				)
			})

			It("returns false and an error", func() {
				Expect(clientErr).To(HaveOccurred())
			})
		})

		Context("pagination data", func() {
			Context("with a link header", func() {
				BeforeEach(func() {
					expectedURL = fmt.Sprint("/api/v1/builds")

					atcServer.AppendHandlers(
						ghttp.CombineHandlers(
							ghttp.VerifyRequest("GET", expectedURL),
							ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds, http.Header{
								"Link": []string{
									`<http://some-url.com/api/v1/builds?since=452&limit=123>; rel="previous"`,
									`<http://some-url.com/api/v1/builds?until=254&limit=456>; rel="next"`,
								},
							}),
						),
					)
				})

				It("returns the pagination data from the header", func() {
					Expect(clientErr).ToNot(HaveOccurred())
					Expect(pagination.Previous).To(Equal(&concourse.Page{Since: 452, Limit: 123}))
					Expect(pagination.Next).To(Equal(&concourse.Page{Until: 254, Limit: 456}))
				})
			})
		})

		Context("without a link header", func() {
			BeforeEach(func() {
				expectedURL = fmt.Sprint("/api/v1/builds")

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", expectedURL),
						ghttp.RespondWithJSONEncoded(http.StatusOK, expectedBuilds, http.Header{}),
					),
				)
			})

			It("returns pagination data with nil pages", func() {
				Expect(clientErr).ToNot(HaveOccurred())
				Expect(pagination.Previous).To(BeNil())
				Expect(pagination.Next).To(BeNil())
			})
		})
	})

	Describe("AbortBuild", func() {
		BeforeEach(func() {
			expectedURL := "/api/v1/builds/123/abort"

			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("PUT", expectedURL),
					ghttp.RespondWith(http.StatusNoContent, ""),
				),
			)
		})

		It("sends an abort request to ATC", func() {
			Expect(func() {
				err := client.AbortBuild("123")
				Expect(err).NotTo(HaveOccurred())
			}).To(Change(func() int {
				return len(atcServer.ReceivedRequests())
			}).By(1))
		})
	})
})
<|endoftext|>


<commit_before>// boolmap creates a map of bools using bytes for efficiency (needs benchmarking for memory)
package boolmap

type Map struct {
	data map[uint64]byte
}

func NewMap() Map {
	return Map{make(map[uint64]byte)}
}

func (m *Map) Get(p uint64) bool {
	return m.data[p>>3]&(1<<(p&7)) != 0
}

func (m *Map) Set(p uint64, d bool) {
	if d {
		m.data[p>>3] |= 1 << (p & 7)
	} else {
		m.data[p>>3] &^= 1 << (p & 7)
	}
	if m.data[p>>3] == 0 {
		delete(m.data, p>>3)
	}
}

type Slice struct {
	data []byte
}

func NewSlice() Slice {
	return Slice{make([]byte, 1)}
}

func (s *Slice) Get(p uint) bool {
	pos := p >> 3
	if pos > uint(len(s.data)) {
		return false
	}
	return s.data[pos]&(1<<(p&7)) != 0
}

func (s *Slice) Set(p uint, d bool) {
	pos := p >> 3
	if pos >= uint(len(s.data)) {
		if pos < uint(cap(s.data)) {
			s.data = s.data[:pos]
		} else {
			var newData []byte
			if pos < 512 {
				newData = make([]byte, pos<<1)
			} else {
				newData = make([]byte, pos+(pos>>2))
			}
			copy(newData, s.data)
			s.data = newData
		}
	}
	if d {
		s.data[pos] |= 1 << (p & 7)
	} else {
		s.data[pos] &^= 1 << (p & 7)
	}
}
<commit_msg>Readability changes<commit_after>// boolmap creates a map of bools using bytes for efficiency (needs benchmarking for memory)
package boolmap

type Map struct {
	data map[uint64]byte
}

func NewMap() Map {
	return Map{make(map[uint64]byte)}
}

func (m *Map) Get(p uint64) bool {
	return m.data[p>>3]&(1<<(p&7)) != 0
}

func (m *Map) Set(p uint64, d bool) {
	shift := byte(1 << (p & 7))
	pos := p >> 3
	if d {
		m.data[pos] |= shift
	} else {
		m.data[pos] &^= shift
	}
	if m.data[pos] == 0 {
		delete(m.data, pos)
	}
}

type Slice struct {
	data []byte
}

func NewSlice() Slice {
	return Slice{make([]byte, 1)}
}

func (s *Slice) Get(p uint) bool {
	pos := p >> 3
	if pos > uint(len(s.data)) {
		return false
	}
	return s.data[pos]&(1<<(p&7)) != 0
}

func (s *Slice) Set(p uint, d bool) {
	pos := p >> 3
	if pos >= uint(len(s.data)) {
		if pos < uint(cap(s.data)) {
			s.data = s.data[:pos]
		} else {
			var newData []byte
			if pos < 512 {
				newData = make([]byte, pos<<1)
			} else {
				newData = make([]byte, pos+(pos>>2))
			}
			copy(newData, s.data)
			s.data = newData
		}
	}
	shift := byte(1 << (p & 7))
	if d {
		s.data[pos] |= shift
	} else {
		s.data[pos] &^= shift
	}
}
<|endoftext|>


<commit_before>package extdirect
import (
	"net/http"
	"fmt"
	"strings"
	"io"
	"io/ioutil"
	"encoding/json"
	"reflect"
	"github.com/mitchellh/mapstructure"
	"time"
	"golang.org/x/net/context"
	"net/url"
	"strconv"
	"github.com/nbgo/fail"
)

type ErrDecodeFromPostRequest struct {
	Details string
	Reason  error
}
func (this *ErrDecodeFromPostRequest) Error() string {
	return fmt.Sprintf("Failed to decode form post: %v. Reason: %v", this.Details, this.Reason)
}
func (this *ErrDecodeFromPostRequest) InnerError() error {
	return this.Reason
}

type ErrInvalidContentType string
func (this ErrInvalidContentType) Error() string {
	return fmt.Sprintf("invalid content type: %s", string(this))
}

type ErrTypeConversion struct {
	SourceType reflect.Type
	TargetType reflect.Type
}
func (this *ErrTypeConversion) Error() string {
	return fmt.Sprintf("cannot convert type %v to type %v", this.SourceType, this.TargetType)
}

type ErrDirectActionMethod struct {
	Action string
	Method string
	Err    interface{}
}
func (this *ErrDirectActionMethod) Error() string {
	return fmt.Sprintf("error serving %v.%v(): %v", this.Action, this.Method, this.Err)
}

type request struct {
	Type   string `json:"type"`
	Tid    int `json:"tid"`
	Action string `json:"action"`
	Method string `json:"method"`
	Upload bool `json:"upload"`
	Data   interface{} `json:"data"`
}

type response struct {
	Type    string `json:"type"`
	Tid     int `json:"tid"`
	Action  string `json:"action"`
	Method  string `json:"method"`
	Message *string `json:"message,omitempty"`
	Result  interface{} `json:"result,omitempty"`
}

func Api(provider *DirectServiceProvider) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
		if js, err := provider.JavaScript(); err != nil {
			panic(err)
		} else {
			if _, err := w.Write([]byte(js)); err != nil {
				panic(err)
			}
		}
	}
}

func ActionsHandler(provider *DirectServiceProvider) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		actionHandler(provider, nil, w, r)
	}
}

func ActionsHandlerCtx(provider *DirectServiceProvider) func(c context.Context, w http.ResponseWriter, r *http.Request) {
	return func(c context.Context, w http.ResponseWriter, r *http.Request) {
		actionHandler(provider, c, w, r)
	}
}

func actionHandler(provider *DirectServiceProvider, c context.Context, w http.ResponseWriter, r *http.Request) {
	var reqs []*request
	var err error
	contentType := r.Header.Get("Content-Type")
	isFormHandler := false

	switch {
	case strings.HasPrefix(contentType, "application/json"):
		reqs = mustDecodeTransaction(r.Body)
	case strings.HasPrefix(contentType, "application/x-www-form-urlencoded"):
		r.ParseForm()
		reqs = mustDecodeFormPost(r.Form)
		isFormHandler = true
	default:
		panic(ErrInvalidContentType(contentType))
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if !isFormHandler {
		err = json.NewEncoder(w).Encode(provider.processRequests(c, r, reqs))
	} else {
		resps := provider.processRequests(c, r, reqs)
		err = json.NewEncoder(w).Encode(resps[0])

	}
	if err != nil {
		panic(err)
	}
}

func (this *DirectServiceProvider) processRequests(c context.Context, r *http.Request, reqs []*request) []*response {
	resps := make([]*response, len(reqs))
	respsChannel := make(chan *response, len(reqs))
	for _, req := range reqs {
		go func(req *request) {
			resp := &response{
				Tid: req.Tid,
				Action: req.Action,
				Method: req.Method,
				Type: req.Type,
			}
			var tStart time.Time
			profilingStarted := false
			defer func() {
				if profilingStarted {
					duration := time.Now().Sub(tStart)
					log.Print(logLevelInfo, fmt.Sprintf("%s.%s() %v ", req.Action, req.Method, duration), map[string]interface{}{"duration":duration, "action": req.Action, "method": req.Method})
					profilingStarted = false
				}
				if err := recover(); err != nil {
					log.Print(&ErrDirectActionMethod{req.Action, req.Method, err})
					resp.Type = "exception"
					respMessage := fmt.Sprintf("%v", err)
					resp.Message = &respMessage
				}
				respsChannel <- resp
			}()

			// Create instance of action type
			actionInfo := this.actionsInfo[req.Action]
			if this.debug {
				log.Print(fmt.Sprintf("Create instance of action %s (type %v)", req.Action, actionInfo.Type))
			}
			actionVal := reflect.New(actionInfo.Type).Elem()

			// Set context and request
			if c != nil || r != nil {
				if this.debug {
					log.Print("Set action context/request.")
				}
				contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
				requestType := reflect.TypeOf(&http.Request{})
				fieldsLen := actionInfo.Type.NumField()
				for i := 0; i < fieldsLen; i++ {
					t := actionInfo.Type.Field(i).Type

					if t.Implements(contextType) {
						if c != nil {
							if this.debug {
								log.Print("Set action context.")
							}
							actionVal.Field(i).Set(reflect.ValueOf(c))
						} else {
							if this.debug {
								log.Print(logLevelWarn, "Context cannot be set to action instance because context is nil.")
							}
						}
					}

					if t == requestType {
						if r != nil {
							if this.debug {
								log.Print("Set action request.")
							}
							actionVal.Field(i).Set(reflect.ValueOf(r))
						}
					}
				}
			}

			if this.debug {
				log.Print(fmt.Sprintf("Prepare arguments for method %s.%s", req.Action, req.Method))
			}
			methodInfo := actionInfo.Methods[req.Method]
			directMethod := actionInfo.DirectMethods[req.Method]
			if this.debug {
				log.Print(fmt.Sprintf("Direct method to use: %s, formhandler=%v", directMethod.Name, directMethod.FormHandler))
			}
			methodArgsLen := methodInfo.Type.NumIn() - 1
			var args []reflect.Value
			if req.Data != nil {
				if this.debug {
					log.Print(fmt.Sprintf("Type of request data is %T", req.Data))
				}
				if directMethod.FormHandler != nil {
					if this.debug {
						log.Print("Prepare arguments for form handler call.")
					}
					args = make([]reflect.Value, 1)
					args[0] = reflect.ValueOf(req.Data.(map[string]string))
					// TODO: Support structure type argument for form handler.
				} else {
					args = make([]reflect.Value, methodArgsLen)
					for i, arg := range req.Data.([]interface{}) {
						if this.debug {
							log.Print(fmt.Sprintf("Initial arg #%v type is %T", i, arg))
						}
						convertedArg := convertArg(methodInfo.Type.In(i + 1), arg)
						if this.debug {
							log.Print(fmt.Sprintf("Converted arg #%v type is %T", i, convertedArg))
						}
						args[i] = reflect.ValueOf(convertedArg)
					}
				}
			}

			if this.profile {
				profilingStarted = true
				tStart = time.Now()
			}

			if this.debug {
				log.Print(fmt.Sprintf("Call method %s.%s", req.Action, req.Method))
			}
			// Call action method.
			resultsValues := actionVal.MethodByName(methodInfo.Name).Call(args)

			if profilingStarted {
				duration := time.Now().Sub(tStart)
				log.Print(logLevelInfo, fmt.Sprintf("%s.%s() %v ", req.Action, req.Method, duration), map[string]interface{}{"duration":duration, "action": req.Action, "method": req.Method})
				profilingStarted = false
			}
			for i, resultValue := range resultsValues {
				if methodInfo.Type.Out(i).Name() == "error" {
					if err, isErr := resultValue.Interface().(error); isErr {
						log.Print(&ErrDirectActionMethod{req.Action, req.Method, err})
						resp.Type = "exception"
						respMessage := fmt.Sprintf("%v", err)
						resp.Message = &respMessage
						resp.Result = nil
						break;
					}
				} else {
					result := resultValue.Interface()
					resp.Result = result
				}
			}
		}(req)
	}

	for i := 0; i < len(reqs); i++ {
		var resp = <-respsChannel
		resps[i] = resp
	}

	return resps
}

func mustDecodeFormPost(f url.Values) []*request {
	req := &request{
		Type:f["extType"][0],
		Action: f["extAction"][0],
		Method: f["extMethod"][0],
	}
	tid, tidErr := strconv.Atoi(f["extTID"][0]);
	if tidErr != nil {
		panic(fail.New(&ErrDecodeFromPostRequest{"could not parse TID", tidErr}))
	}
	req.Tid = tid
	upload, hasUpload := f["extUpload"]
	req.Upload = hasUpload && strings.ToLower(upload[0]) == "true"

	data := make(map[string]string, 0)
	for k, v := range f {
		if k == "extType" || k == "extTID" || k == "extAction" || k == "extMethod" || k == "extUpload" {
			continue
		}
		data[k] = v[0]
	}
	req.Data = data

	return []*request{req}
}

func mustDecodeTransaction(r io.Reader) []*request {
	if jsonData, err := ioutil.ReadAll(r); err != nil {
		panic(err)
	} else {
		var reqs []*request
		if err := json.Unmarshal(jsonData, &reqs); err != nil {
			// Attempt to unmarshal as a single request.
			var req request
			if err := json.Unmarshal(jsonData, &req); err != nil {
				panic(err)
			} else {
				reqs = make([]*request, 1)
				reqs[0] = &req
			}
		}
		return reqs
	}
}

func convertArg(argType reflect.Type, argValue interface{}) interface{} {
	sourceType := reflect.TypeOf(argValue)
	if sourceType != argType {
		switch v := argValue.(type) {
		case float64:
			switch argType.Kind() {
			case reflect.Int: return int(v)
			case reflect.Int8: return int8(v)
			case reflect.Int16: return int16(v)
			case reflect.Int32: return int32(v)
			case reflect.Float32: return float32(v)
			default: panic(&ErrTypeConversion{sourceType, argType})
			}
		case nil:
			switch argType.Kind() {
			case reflect.Int: return int(0)
			case reflect.Int8: return int8(0)
			case reflect.Int16: return int16(0)
			case reflect.Int32: return int32(0)
			case reflect.String: return ""
			default: panic(&ErrTypeConversion{sourceType, argType})
			}
		case map[string]interface{}:
			switch argType.Kind() {
			case reflect.Ptr: fallthrough
			case reflect.Struct:
				structInstanceValue := reflect.New(argType).Elem()
				structInstanceRef := structInstanceValue.Addr().Interface()
				mapstructure.Decode(v, structInstanceRef)
				return structInstanceValue.Interface()
			default: panic(&ErrTypeConversion{sourceType, argType})
			}
		}
	}

	return argValue
}<commit_msg>Error messages capitalized.<commit_after>package extdirect
import (
	"net/http"
	"fmt"
	"strings"
	"io"
	"io/ioutil"
	"encoding/json"
	"reflect"
	"github.com/mitchellh/mapstructure"
	"time"
	"golang.org/x/net/context"
	"net/url"
	"strconv"
	"github.com/nbgo/fail"
)

type ErrDecodeFromPostRequest struct {
	Details string
	Reason  error
}
func (this *ErrDecodeFromPostRequest) Error() string {
	return fmt.Sprintf("Failed to decode form post: %v. Reason: %v", this.Details, this.Reason)
}
func (this *ErrDecodeFromPostRequest) InnerError() error {
	return this.Reason
}

type ErrInvalidContentType string
func (this ErrInvalidContentType) Error() string {
	return fmt.Sprintf("Invalid content type: %s.", string(this))
}

type ErrTypeConversion struct {
	SourceType reflect.Type
	TargetType reflect.Type
}
func (this *ErrTypeConversion) Error() string {
	return fmt.Sprintf("Cannot convert type %v to type %v.", this.SourceType, this.TargetType)
}

type ErrDirectActionMethod struct {
	Action string
	Method string
	Err    interface{}
}
func (this *ErrDirectActionMethod) Error() string {
	return fmt.Sprintf("Error serving %v.%v(): %v", this.Action, this.Method, this.Err)
}

type request struct {
	Type   string `json:"type"`
	Tid    int `json:"tid"`
	Action string `json:"action"`
	Method string `json:"method"`
	Upload bool `json:"upload"`
	Data   interface{} `json:"data"`
}

type response struct {
	Type    string `json:"type"`
	Tid     int `json:"tid"`
	Action  string `json:"action"`
	Method  string `json:"method"`
	Message *string `json:"message,omitempty"`
	Result  interface{} `json:"result,omitempty"`
}

func Api(provider *DirectServiceProvider) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
		if js, err := provider.JavaScript(); err != nil {
			panic(err)
		} else {
			if _, err := w.Write([]byte(js)); err != nil {
				panic(err)
			}
		}
	}
}

func ActionsHandler(provider *DirectServiceProvider) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		actionHandler(provider, nil, w, r)
	}
}

func ActionsHandlerCtx(provider *DirectServiceProvider) func(c context.Context, w http.ResponseWriter, r *http.Request) {
	return func(c context.Context, w http.ResponseWriter, r *http.Request) {
		actionHandler(provider, c, w, r)
	}
}

func actionHandler(provider *DirectServiceProvider, c context.Context, w http.ResponseWriter, r *http.Request) {
	var reqs []*request
	var err error
	contentType := r.Header.Get("Content-Type")
	isFormHandler := false

	switch {
	case strings.HasPrefix(contentType, "application/json"):
		reqs = mustDecodeTransaction(r.Body)
	case strings.HasPrefix(contentType, "application/x-www-form-urlencoded"):
		r.ParseForm()
		reqs = mustDecodeFormPost(r.Form)
		isFormHandler = true
	default:
		panic(ErrInvalidContentType(contentType))
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if !isFormHandler {
		err = json.NewEncoder(w).Encode(provider.processRequests(c, r, reqs))
	} else {
		resps := provider.processRequests(c, r, reqs)
		err = json.NewEncoder(w).Encode(resps[0])

	}
	if err != nil {
		panic(err)
	}
}

func (this *DirectServiceProvider) processRequests(c context.Context, r *http.Request, reqs []*request) []*response {
	resps := make([]*response, len(reqs))
	respsChannel := make(chan *response, len(reqs))
	for _, req := range reqs {
		go func(req *request) {
			resp := &response{
				Tid: req.Tid,
				Action: req.Action,
				Method: req.Method,
				Type: req.Type,
			}
			var tStart time.Time
			profilingStarted := false
			defer func() {
				if profilingStarted {
					duration := time.Now().Sub(tStart)
					log.Print(logLevelInfo, fmt.Sprintf("%s.%s() %v ", req.Action, req.Method, duration), map[string]interface{}{"duration":duration, "action": req.Action, "method": req.Method})
					profilingStarted = false
				}
				if err := recover(); err != nil {
					log.Print(fail.New(&ErrDirectActionMethod{req.Action, req.Method, err}))
					resp.Type = "exception"
					respMessage := fmt.Sprintf("%v", err)
					resp.Message = &respMessage
				}
				respsChannel <- resp
			}()

			// Create instance of action type
			actionInfo := this.actionsInfo[req.Action]
			if this.debug {
				log.Print(fmt.Sprintf("Create instance of action %s (type %v)", req.Action, actionInfo.Type))
			}
			actionVal := reflect.New(actionInfo.Type).Elem()

			// Set context and request
			if c != nil || r != nil {
				if this.debug {
					log.Print("Set action context/request.")
				}
				contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
				requestType := reflect.TypeOf(&http.Request{})
				fieldsLen := actionInfo.Type.NumField()
				for i := 0; i < fieldsLen; i++ {
					t := actionInfo.Type.Field(i).Type

					if t.Implements(contextType) {
						if c != nil {
							if this.debug {
								log.Print("Set action context.")
							}
							actionVal.Field(i).Set(reflect.ValueOf(c))
						} else {
							if this.debug {
								log.Print(logLevelWarn, "Context cannot be set to action instance because context is nil.")
							}
						}
					}

					if t == requestType {
						if r != nil {
							if this.debug {
								log.Print("Set action request.")
							}
							actionVal.Field(i).Set(reflect.ValueOf(r))
						}
					}
				}
			}

			if this.debug {
				log.Print(fmt.Sprintf("Prepare arguments for method %s.%s", req.Action, req.Method))
			}
			methodInfo := actionInfo.Methods[req.Method]
			directMethod := actionInfo.DirectMethods[req.Method]
			if this.debug {
				log.Print(fmt.Sprintf("Direct method to use: %s, formhandler=%v", directMethod.Name, directMethod.FormHandler))
			}
			methodArgsLen := methodInfo.Type.NumIn() - 1
			var args []reflect.Value
			if req.Data != nil {
				if this.debug {
					log.Print(fmt.Sprintf("Type of request data is %T", req.Data))
				}
				if directMethod.FormHandler != nil {
					if this.debug {
						log.Print("Prepare arguments for form handler call.")
					}
					args = make([]reflect.Value, 1)
					args[0] = reflect.ValueOf(req.Data.(map[string]string))
					// TODO: Support structure type argument for form handler.
				} else {
					args = make([]reflect.Value, methodArgsLen)
					for i, arg := range req.Data.([]interface{}) {
						if this.debug {
							log.Print(fmt.Sprintf("Initial arg #%v type is %T", i, arg))
						}
						convertedArg := convertArg(methodInfo.Type.In(i + 1), arg)
						if this.debug {
							log.Print(fmt.Sprintf("Converted arg #%v type is %T", i, convertedArg))
						}
						args[i] = reflect.ValueOf(convertedArg)
					}
				}
			}

			if this.profile {
				profilingStarted = true
				tStart = time.Now()
			}

			if this.debug {
				log.Print(fmt.Sprintf("Call method %s.%s", req.Action, req.Method))
			}
			// Call action method.
			resultsValues := actionVal.MethodByName(methodInfo.Name).Call(args)

			if profilingStarted {
				duration := time.Now().Sub(tStart)
				log.Print(logLevelInfo, fmt.Sprintf("%s.%s() %v ", req.Action, req.Method, duration), map[string]interface{}{"duration":duration, "action": req.Action, "method": req.Method})
				profilingStarted = false
			}
			for i, resultValue := range resultsValues {
				if methodInfo.Type.Out(i).Name() == "error" {
					if err, isErr := resultValue.Interface().(error); isErr {
						log.Print(&ErrDirectActionMethod{req.Action, req.Method, err})
						resp.Type = "exception"
						respMessage := fmt.Sprintf("%v", err)
						resp.Message = &respMessage
						resp.Result = nil
						break;
					}
				} else {
					result := resultValue.Interface()
					resp.Result = result
				}
			}
		}(req)
	}

	for i := 0; i < len(reqs); i++ {
		var resp = <-respsChannel
		resps[i] = resp
	}

	return resps
}

func mustDecodeFormPost(f url.Values) []*request {
	req := &request{
		Type:f["extType"][0],
		Action: f["extAction"][0],
		Method: f["extMethod"][0],
	}
	tid, tidErr := strconv.Atoi(f["extTID"][0]);
	if tidErr != nil {
		panic(fail.New(&ErrDecodeFromPostRequest{"could not parse TID", tidErr}))
	}
	req.Tid = tid
	upload, hasUpload := f["extUpload"]
	req.Upload = hasUpload && strings.ToLower(upload[0]) == "true"

	data := make(map[string]string, 0)
	for k, v := range f {
		if k == "extType" || k == "extTID" || k == "extAction" || k == "extMethod" || k == "extUpload" {
			continue
		}
		data[k] = v[0]
	}
	req.Data = data

	return []*request{req}
}

func mustDecodeTransaction(r io.Reader) []*request {
	if jsonData, err := ioutil.ReadAll(r); err != nil {
		panic(err)
	} else {
		var reqs []*request
		if err := json.Unmarshal(jsonData, &reqs); err != nil {
			// Attempt to unmarshal as a single request.
			var req request
			if err := json.Unmarshal(jsonData, &req); err != nil {
				panic(err)
			} else {
				reqs = make([]*request, 1)
				reqs[0] = &req
			}
		}
		return reqs
	}
}

func convertArg(argType reflect.Type, argValue interface{}) interface{} {
	sourceType := reflect.TypeOf(argValue)
	if sourceType != argType {
		switch v := argValue.(type) {
		case float64:
			switch argType.Kind() {
			case reflect.Int: return int(v)
			case reflect.Int8: return int8(v)
			case reflect.Int16: return int16(v)
			case reflect.Int32: return int32(v)
			case reflect.Float32: return float32(v)
			default: panic(&ErrTypeConversion{sourceType, argType})
			}
		case nil:
			switch argType.Kind() {
			case reflect.Int: return int(0)
			case reflect.Int8: return int8(0)
			case reflect.Int16: return int16(0)
			case reflect.Int32: return int32(0)
			case reflect.String: return ""
			default: panic(&ErrTypeConversion{sourceType, argType})
			}
		case map[string]interface{}:
			switch argType.Kind() {
			case reflect.Ptr: fallthrough
			case reflect.Struct:
				structInstanceValue := reflect.New(argType).Elem()
				structInstanceRef := structInstanceValue.Addr().Interface()
				mapstructure.Decode(v, structInstanceRef)
				return structInstanceValue.Interface()
			default: panic(&ErrTypeConversion{sourceType, argType})
			}
		}
	}

	return argValue
}<|endoftext|>