<commit_before>package goboots

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/gabstv/i18ngo"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"
	"time"
)

const (
	ErrNil = 0
	// TLS
	ErrTLSNil = 100
)

var (
	sessionDbs   map[string]ISessionDBEngine
	curSessionDb ISessionDBEngine
	controllers  []IController
)

func RegisterController(controller IController) {
	//TODO: error if controller with same name exists!
	if controllers == nil {
		controllers = make([]IController, 0)
	}
	controllers = append(controllers, controller)
}

func RegisterSessionStorageDriver(name string, engine ISessionDBEngine) {
	if sessionDbs == nil {
		sessionDbs = make(map[string]ISessionDBEngine, 0)
	}
	sessionDbs[name] = engine
}

func (app *App) InitSessionStorage(driver string) error {
	if sessionDbs == nil {
		return errors.New("No session storage registered.")
	}
	if _, ok := sessionDbs[driver]; !ok {
		return errors.New("The session storage driver " + driver + " does not exist.")
	}
	curSessionDb = sessionDbs[driver]
	curSessionDb.SetApp(app)
	return nil
}

func CloseSessionStorage() {
	if curSessionDb != nil {
		curSessionDb.Close()
	}
}

func initAnySessionStorage() {
	if curSessionDb != nil {
		return
	}
	if sessionDbs == nil {
		//TODO: should panic
		return
	}
	for _, v := range sessionDbs {
		curSessionDb = v
		break
	}
}

type AppError struct {
	Id      int
	Message string
}

func (e *AppError) Error() string {
	return e.Message
}

type env struct {
	path       string
	controller string
	action     string
	params     []string
	request    *http.Request
	body       string
	form       map[string][]string
}

type ISession interface {
	GetData() (string, map[string]interface{}, time.Time)
}

type Session struct {
	SID     string
	Data    map[string]interface{}
	Flash   SessFlash `json:"-" bson:"-"` // never save flash
	Time    time.Time
	Updated time.Time
}

type SessFlash struct {
	vals map[string]interface{}
}

func (s *SessFlash) init() {
	if s.vals == nil {
		s.vals = make(map[string]interface{})
	}
}

func (s *SessFlash) Set(key string, val interface{}) {
	s.init()
	s.vals[key] = val
}

func (s *SessFlash) Get(key string) interface{} {
	s.init()
	return s.vals[key]
}

func (s *SessFlash) Get2(key string) (interface{}, bool) {
	s.init()
	v, ok := s.vals[key]
	return v, ok
}

func (s *SessFlash) Del(key string) {
	s.init()
	delete(s.vals, key)
}

func (s *SessFlash) Clear() {
	s.vals = nil
}

func (s *SessFlash) All() map[string]interface{} {
	s.init()
	return s.vals
}

func (s *Session) GetData() (string, map[string]interface{}, time.Time) {
	return s.SID, s.Data, s.Time
}

func (s *Session) Flush() {
	FlushSession(s)
}

func (s *Session) GetBool(key string) (bool, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return false, false
	}
	return bool(reflect.ValueOf(iface).Bool()), true
}

func (s *Session) GetBoolD(key string, defaultValue bool) bool {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return bool(reflect.ValueOf(iface).Bool())
}

func (s *Session) GetInt32(key string) (int, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return 0, false
	}
	return int(reflect.ValueOf(iface).Int()), true
}

func (s *Session) GetInt32D(key string, defaultValue int) int {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return int(reflect.ValueOf(iface).Int())
}

func (s *Session) GetString(key string) (string, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return "", false
	}
	return reflect.ValueOf(iface).String(), true
}

func (s *Session) GetStringD(key string, defaultValue string) string {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return reflect.ValueOf(iface).String()
}

func (s *Session) Expire(w http.ResponseWriter, t time.Time) {
	SetCookieAdv(w, "goboots_sessid", s.SID, "/", "", t, 0, false, true)
}

func GetSession(w http.ResponseWriter, r *http.Request) *Session {
	initAnySessionStorage()
	var cookie *http.Cookie
	var sid string
	var err error
	//LOAD
	cookie, err = r.Cookie("goboots_sessid")
	if err == nil {
		sid = cookie.Value
		if _validateSidString(sid) {
			var msession *Session
			// get from a saved location
			msession, err = curSessionDb.GetSession(sid)
			if err == nil {
				return msession
			}
			fmt.Printf("SESSION ERROR :( [%s] %s\n", sid, err.Error())
			// not found, generate a new sid
		} else {
			fmt.Printf("COULD NOT VALIDATE :( [%s]\n", sid)
		}
	}
	//TODO: improve session generation because it created a duplicate key!
	// gen session
	rand.Seed(time.Now().UnixNano())
	var uuid [16]byte
	for i := 0; i < 16; i++ {
		uuid[i] = byte(rand.Intn(255))
	}
	// secrets
	sid = fmt.Sprintf("%x", uuid)

	session := &Session{
		SID:     sid,
		Data:    make(map[string]interface{}),
		Time:    time.Now(),
		Updated: time.Now(),
	}
	err = curSessionDb.NewSession(session)
	__panic(err)
	SetCookieAdv(w, "goboots_sessid", sid, "/", "", time.Now().AddDate(0, 1, 0), 0, false, true)
	return session
}

func GetUserLang(w http.ResponseWriter, r *http.Request) string {
	var l string
	// get lang from cookies
	cookie, err := r.Cookie("lang")
	if err != nil && cookie != nil {
		l = cookie.Value
		return l
	}
	// get lang from http request
	alh := r.Header.Get("Accept-Language")
	// 2013-07-29 : not all clients actually send this header (googlebot/wget etc)
	if len(alh) > 1 {
		validlangs := i18ngo.GetLanguageCodes()
		// break alh
		alh0 := strings.Split(alh, ",")
		for _, v := range alh0 {
			lcd := strings.Split(v, ";")[0]
			var lc string
			if len(lcd) >= 2 {
				lc = lcd[0:2]
			}
			lc = lcd
			if StringIndexOf(validlangs, lc) != -1 {
				l = lc
				break
			}
		}
		if len(l) > 0 {
			// just update the cookies
			if w != nil {
				SetCookieSimple(w, "lang", l)
			}
		} else {
			l = i18ngo.GetDefaultLanguageCode()
		}
		return l
	}
	return i18ngo.GetDefaultLanguageCode()
}

func SetUserLang(w http.ResponseWriter, r *http.Request, langcode string) {
	SetCookieSimple(w, "lang", langcode)
}

func FlushSession(s *Session) error {
	return curSessionDb.PutSession(s)
}

func DestroySession(w http.ResponseWriter, r *http.Request, s *Session) {
	curSessionDb.RemoveSession(s)
	SetCookieAdv(w, "goboots_sessid", "", "/", "", time.Now(), 1, false, true)
}

func _validateSidString(sid string) bool {
	if len(sid) != 32 {
		return false
	}
	for _, c := range sid {
		// check if sid contains invalid chars
		if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
			return false
		}
	}
	return true
}

type templateInfo struct {
	path       string
	data       *template.Template
	lastUpdate time.Time
}

func SetCookieAdv(w http.ResponseWriter, name string, value string, path string, domain string, expires time.Time, maxage int, secure bool, httpOnly bool) {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     path,
		Domain:   domain,
		Expires:  expires,
		MaxAge:   maxage,
		Secure:   secure,
		HttpOnly: httpOnly,
	}
	http.SetCookie(w, cookie)
}

func SetCookieSimple(w http.ResponseWriter, key string, value string) {
	SetCookieAdv(w, key, value, "/", "", time.Now().AddDate(100, 0, 0), 0, false, false)
}

func StrConcat(strings ...string) string {
	var buffer bytes.Buffer
	for _, str := range strings {
		buffer.WriteString(str)
	}
	return buffer.String()
}

func DeleteItem(slice interface{}, i int) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}

func InsertItem(slice interface{}, i int, val interface{}) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i+1), v.Slice(i, v.Len())))
	v.Index(i).Set(reflect.ValueOf(val))
}

func StringIndexOf(haystack []string, needle string) int {
	for i, v := range haystack {
		if v == needle {
			return i
		}
	}
	return -1
}

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

func __panic(err error) {
	if err != nil {
		log.Panicln(err)
	}
}

func FormatPath(rawpath string) string {
	cwd, _ := os.Getwd()
	if strings.HasPrefix(rawpath, "./") {
		return filepath.Clean(cwd + rawpath[1:])
	} else if strings.HasPrefix(rawpath, "../") {
		return filepath.Clean(cwd + "/" + rawpath)
	}
	return filepath.Clean(rawpath)
}
<commit_msg>remove writer dependency for session Expire function<commit_after>package goboots

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/gabstv/i18ngo"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"
	"time"
)

const (
	ErrNil = 0
	// TLS
	ErrTLSNil = 100
)

var (
	sessionDbs   map[string]ISessionDBEngine
	curSessionDb ISessionDBEngine
	controllers  []IController
)

func RegisterController(controller IController) {
	//TODO: error if controller with same name exists!
	if controllers == nil {
		controllers = make([]IController, 0)
	}
	controllers = append(controllers, controller)
}

func RegisterSessionStorageDriver(name string, engine ISessionDBEngine) {
	if sessionDbs == nil {
		sessionDbs = make(map[string]ISessionDBEngine, 0)
	}
	sessionDbs[name] = engine
}

func (app *App) InitSessionStorage(driver string) error {
	if sessionDbs == nil {
		return errors.New("No session storage registered.")
	}
	if _, ok := sessionDbs[driver]; !ok {
		return errors.New("The session storage driver " + driver + " does not exist.")
	}
	curSessionDb = sessionDbs[driver]
	curSessionDb.SetApp(app)
	return nil
}

func CloseSessionStorage() {
	if curSessionDb != nil {
		curSessionDb.Close()
	}
}

func initAnySessionStorage() {
	if curSessionDb != nil {
		return
	}
	if sessionDbs == nil {
		//TODO: should panic
		return
	}
	for _, v := range sessionDbs {
		curSessionDb = v
		break
	}
}

type AppError struct {
	Id      int
	Message string
}

func (e *AppError) Error() string {
	return e.Message
}

type env struct {
	path       string
	controller string
	action     string
	params     []string
	request    *http.Request
	body       string
	form       map[string][]string
}

type ISession interface {
	GetData() (string, map[string]interface{}, time.Time)
}

type Session struct {
	SID     string
	Data    map[string]interface{}
	Flash   SessFlash `json:"-" bson:"-"` // never save flash
	Time    time.Time
	Updated time.Time
	r       *http.Request
	w       http.ResponseWriter
}

type SessFlash struct {
	vals map[string]interface{}
}

func (s *SessFlash) init() {
	if s.vals == nil {
		s.vals = make(map[string]interface{})
	}
}

func (s *SessFlash) Set(key string, val interface{}) {
	s.init()
	s.vals[key] = val
}

func (s *SessFlash) Get(key string) interface{} {
	s.init()
	return s.vals[key]
}

func (s *SessFlash) Get2(key string) (interface{}, bool) {
	s.init()
	v, ok := s.vals[key]
	return v, ok
}

func (s *SessFlash) Del(key string) {
	s.init()
	delete(s.vals, key)
}

func (s *SessFlash) Clear() {
	s.vals = nil
}

func (s *SessFlash) All() map[string]interface{} {
	s.init()
	return s.vals
}

func (s *Session) GetData() (string, map[string]interface{}, time.Time) {
	return s.SID, s.Data, s.Time
}

func (s *Session) Flush() {
	FlushSession(s)
}

func (s *Session) GetBool(key string) (bool, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return false, false
	}
	return bool(reflect.ValueOf(iface).Bool()), true
}

func (s *Session) GetBoolD(key string, defaultValue bool) bool {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return bool(reflect.ValueOf(iface).Bool())
}

func (s *Session) GetInt32(key string) (int, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return 0, false
	}
	return int(reflect.ValueOf(iface).Int()), true
}

func (s *Session) GetInt32D(key string, defaultValue int) int {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return int(reflect.ValueOf(iface).Int())
}

func (s *Session) GetString(key string) (string, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return "", false
	}
	return reflect.ValueOf(iface).String(), true
}

func (s *Session) GetStringD(key string, defaultValue string) string {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return reflect.ValueOf(iface).String()
}

func (s *Session) Expire(t time.Time) {
	SetCookieAdv(s.w, "goboots_sessid", s.SID, "/", "", t, 0, false, true)
}

func (s *Session) GetExpires() time.Time {
	cookie, err := s.r.Cookie("goboots_sessid")
	if err != nil {
		log.Println("Error @ (s *Session) GetExpires()", err)
		return time.Now()
	}
	return cookie.Expires
}

func GetSession(w http.ResponseWriter, r *http.Request) *Session {
	initAnySessionStorage()
	var cookie *http.Cookie
	var sid string
	var err error
	//LOAD
	cookie, err = r.Cookie("goboots_sessid")
	if err == nil {
		sid = cookie.Value
		if _validateSidString(sid) {
			var msession *Session
			// get from a saved location
			msession, err = curSessionDb.GetSession(sid)
			if err == nil {
				msession.r = r
				msession.w = w
				return msession
			}
			fmt.Printf("SESSION ERROR :( [%s] %s\n", sid, err.Error())
			// not found, generate a new sid
		} else {
			fmt.Printf("COULD NOT VALIDATE :( [%s]\n", sid)
		}
	}
	//TODO: improve session generation because it created a duplicate key!
	// gen session
	rand.Seed(time.Now().UnixNano())
	var uuid [16]byte
	for i := 0; i < 16; i++ {
		uuid[i] = byte(rand.Intn(255))
	}
	// secrets
	sid = fmt.Sprintf("%x", uuid)

	session := &Session{
		SID:     sid,
		Data:    make(map[string]interface{}),
		Time:    time.Now(),
		Updated: time.Now(),
	}
	err = curSessionDb.NewSession(session)
	__panic(err)
	SetCookieAdv(w, "goboots_sessid", sid, "/", "", time.Now().AddDate(0, 1, 0), 0, false, true)
	session.w = w
	session.r = r
	return session
}

func GetUserLang(w http.ResponseWriter, r *http.Request) string {
	var l string
	// get lang from cookies
	cookie, err := r.Cookie("lang")
	if err != nil && cookie != nil {
		l = cookie.Value
		return l
	}
	// get lang from http request
	alh := r.Header.Get("Accept-Language")
	// 2013-07-29 : not all clients actually send this header (googlebot/wget etc)
	if len(alh) > 1 {
		validlangs := i18ngo.GetLanguageCodes()
		// break alh
		alh0 := strings.Split(alh, ",")
		for _, v := range alh0 {
			lcd := strings.Split(v, ";")[0]
			var lc string
			if len(lcd) >= 2 {
				lc = lcd[0:2]
			}
			lc = lcd
			if StringIndexOf(validlangs, lc) != -1 {
				l = lc
				break
			}
		}
		if len(l) > 0 {
			// just update the cookies
			if w != nil {
				SetCookieSimple(w, "lang", l)
			}
		} else {
			l = i18ngo.GetDefaultLanguageCode()
		}
		return l
	}
	return i18ngo.GetDefaultLanguageCode()
}

func SetUserLang(w http.ResponseWriter, r *http.Request, langcode string) {
	SetCookieSimple(w, "lang", langcode)
}

func FlushSession(s *Session) error {
	return curSessionDb.PutSession(s)
}

func DestroySession(w http.ResponseWriter, r *http.Request, s *Session) {
	curSessionDb.RemoveSession(s)
	SetCookieAdv(w, "goboots_sessid", "", "/", "", time.Now(), 1, false, true)
}

func _validateSidString(sid string) bool {
	if len(sid) != 32 {
		return false
	}
	for _, c := range sid {
		// check if sid contains invalid chars
		if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
			return false
		}
	}
	return true
}

type templateInfo struct {
	path       string
	data       *template.Template
	lastUpdate time.Time
}

func SetCookieAdv(w http.ResponseWriter, name string, value string, path string, domain string, expires time.Time, maxage int, secure bool, httpOnly bool) {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     path,
		Domain:   domain,
		Expires:  expires,
		MaxAge:   maxage,
		Secure:   secure,
		HttpOnly: httpOnly,
	}
	http.SetCookie(w, cookie)
}

func SetCookieSimple(w http.ResponseWriter, key string, value string) {
	SetCookieAdv(w, key, value, "/", "", time.Now().AddDate(100, 0, 0), 0, false, false)
}

func StrConcat(strings ...string) string {
	var buffer bytes.Buffer
	for _, str := range strings {
		buffer.WriteString(str)
	}
	return buffer.String()
}

func DeleteItem(slice interface{}, i int) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}

func InsertItem(slice interface{}, i int, val interface{}) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i+1), v.Slice(i, v.Len())))
	v.Index(i).Set(reflect.ValueOf(val))
}

func StringIndexOf(haystack []string, needle string) int {
	for i, v := range haystack {
		if v == needle {
			return i
		}
	}
	return -1
}

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

func __panic(err error) {
	if err != nil {
		log.Panicln(err)
	}
}

func FormatPath(rawpath string) string {
	cwd, _ := os.Getwd()
	if strings.HasPrefix(rawpath, "./") {
		return filepath.Clean(cwd + rawpath[1:])
	} else if strings.HasPrefix(rawpath, "../") {
		return filepath.Clean(cwd + "/" + rawpath)
	}
	return filepath.Clean(rawpath)
}
<|endoftext|>


<commit_before>package cors

import (
	"errors"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// Config represents all available options for the middleware.
type Config struct {
	AllowAllOrigins bool

	// AllowOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// Default value is []
	AllowOrigins []string

	// AllowOriginFunc is a custom function to validate the origin. It take the origin
	// as argument and returns true if allowed or false otherwise. If this option is
	// set, the content of AllowOrigins is ignored.
	AllowOriginFunc func(origin string) bool

	// AllowMethods is a list of methods the client is allowed to use with
	// cross-domain requests. Default value is simple methods (GET and POST)
	AllowMethods []string

	// AllowHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests.
	AllowHeaders []string

	// AllowCredentials indicates whether the request can include user credentials like
	// cookies, HTTP authentication or client side SSL certificates.
	AllowCredentials bool

	// ExposeHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposeHeaders []string

	// MaxAge indicates how long (in seconds) the results of a preflight request
	// can be cached
	MaxAge time.Duration

	// Allows to add origins like http://some-domain/*, https://api.* or http://some.*.subdomain.com
	AllowWildcard bool

	// Allows usage of popular browser extensions schemas
	AllowBrowserExtensions bool

	// Allows usage of WebSocket protocol
	AllowWebSockets bool

	// Allows usage of file:// schema (dangerous!) use it only when you 100% sure it's needed
	AllowFiles bool
}

// AddAllowMethods is allowed to add custom methods
func (c *Config) AddAllowMethods(methods ...string) {
	c.AllowMethods = append(c.AllowMethods, methods...)
}

// AddAllowHeaders is allowed to add custom headers
func (c *Config) AddAllowHeaders(headers ...string) {
	c.AllowHeaders = append(c.AllowHeaders, headers...)
}

// AddExposeHeaders is allowed to add custom expose headers
func (c *Config) AddExposeHeaders(headers ...string) {
	c.ExposeHeaders = append(c.ExposeHeaders, headers...)
}

func (c Config) getAllowedSchemas() []string {
	allowedSchemas := DefaultSchemas
	if c.AllowBrowserExtensions {
		allowedSchemas = append(allowedSchemas, ExtensionSchemas...)
	}
	if c.AllowWebSockets {
		allowedSchemas = append(allowedSchemas, WebSocketSchemas...)
	}
	if c.AllowFiles {
		allowedSchemas = append(allowedSchemas, FileSchemas...)
	}
	return allowedSchemas
}

func (c Config) validateAllowedSchemas(origin string) bool {
	allowedSchemas := c.getAllowedSchemas()
	for _, schema := range allowedSchemas {
		if strings.HasPrefix(origin, schema) {
			return true
		}
	}
	return false
}

// Validate is check configuration of user defined.
func (c Config) Validate() error {
	if c.AllowAllOrigins && (c.AllowOriginFunc != nil || len(c.AllowOrigins) > 0) {
		return errors.New("conflict settings: all origins are allowed. AllowOriginFunc or AllowOrigins is not needed")
	}
	if !c.AllowAllOrigins && c.AllowOriginFunc == nil && len(c.AllowOrigins) == 0 {
		return errors.New("conflict settings: all origins disabled")
	}
	for _, origin := range c.AllowOrigins {
		if !strings.Contains(origin, "*") && !c.validateAllowedSchemas(origin) {
			return errors.New("bad origin: origins must contain '*' or include " + strings.Join(c.getAllowedSchemas(), ","))
		}
	}
	return nil
}

func (c Config) parseWildcardRules() [][]string {
	var wRules [][]string

	if !c.AllowWildcard {
		return wRules
	}

	for _, o := range c.AllowOrigins {
		if !strings.Contains(o, "*") {
			continue
		}

		if c := strings.Count(o, "*"); c > 1 {
			panic(errors.New("only one * is allowed").Error())
		}

		i := strings.Index(o, "*")
		if i == 0 {
			wRules = append(wRules, []string{"*", o[1:]})
			continue
		}
		if i == (len(o) - 1) {
			wRules = append(wRules, []string{o[:i-1], "*"})
			continue
		}

		wRules = append(wRules, []string{o[:i], o[i+1:]})
	}

	return wRules
}

// DefaultConfig returns a generic default configuration mapped to localhost.
func DefaultConfig() Config {
	return Config{
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
		AllowHeaders:     []string{"Origin", "Content-Length", "Content-Type"},
		AllowCredentials: false,
		MaxAge:           12 * time.Hour,
	}
}

// Default returns the location middleware with default configuration.
func Default() gin.HandlerFunc {
	config := DefaultConfig()
	config.AllowAllOrigins = true
	return New(config)
}

// New returns the location middleware with user-defined custom configuration.
func New(config Config) gin.HandlerFunc {
	cors := newCors(config)
	return func(c *gin.Context) {
		cors.applyCors(c)
	}
}
<commit_msg>Document update (#79)<commit_after>package cors

import (
	"errors"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// Config represents all available options for the middleware.
type Config struct {
	AllowAllOrigins bool

	// AllowOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// Default value is []
	AllowOrigins []string

	// AllowOriginFunc is a custom function to validate the origin. It take the origin
	// as argument and returns true if allowed or false otherwise. If this option is
	// set, the content of AllowOrigins is ignored.
	AllowOriginFunc func(origin string) bool

	// AllowMethods is a list of methods the client is allowed to use with
	// cross-domain requests. Default value is simple methods (GET and POST)
	AllowMethods []string

	// AllowHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests.
	AllowHeaders []string

	// AllowCredentials indicates whether the request can include user credentials like
	// cookies, HTTP authentication or client side SSL certificates.
	AllowCredentials bool

	// ExposeHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposeHeaders []string

	// MaxAge indicates how long (with second-precision) the results of a preflight request
	// can be cached
	MaxAge time.Duration

	// Allows to add origins like http://some-domain/*, https://api.* or http://some.*.subdomain.com
	AllowWildcard bool

	// Allows usage of popular browser extensions schemas
	AllowBrowserExtensions bool

	// Allows usage of WebSocket protocol
	AllowWebSockets bool

	// Allows usage of file:// schema (dangerous!) use it only when you 100% sure it's needed
	AllowFiles bool
}

// AddAllowMethods is allowed to add custom methods
func (c *Config) AddAllowMethods(methods ...string) {
	c.AllowMethods = append(c.AllowMethods, methods...)
}

// AddAllowHeaders is allowed to add custom headers
func (c *Config) AddAllowHeaders(headers ...string) {
	c.AllowHeaders = append(c.AllowHeaders, headers...)
}

// AddExposeHeaders is allowed to add custom expose headers
func (c *Config) AddExposeHeaders(headers ...string) {
	c.ExposeHeaders = append(c.ExposeHeaders, headers...)
}

func (c Config) getAllowedSchemas() []string {
	allowedSchemas := DefaultSchemas
	if c.AllowBrowserExtensions {
		allowedSchemas = append(allowedSchemas, ExtensionSchemas...)
	}
	if c.AllowWebSockets {
		allowedSchemas = append(allowedSchemas, WebSocketSchemas...)
	}
	if c.AllowFiles {
		allowedSchemas = append(allowedSchemas, FileSchemas...)
	}
	return allowedSchemas
}

func (c Config) validateAllowedSchemas(origin string) bool {
	allowedSchemas := c.getAllowedSchemas()
	for _, schema := range allowedSchemas {
		if strings.HasPrefix(origin, schema) {
			return true
		}
	}
	return false
}

// Validate is check configuration of user defined.
func (c Config) Validate() error {
	if c.AllowAllOrigins && (c.AllowOriginFunc != nil || len(c.AllowOrigins) > 0) {
		return errors.New("conflict settings: all origins are allowed. AllowOriginFunc or AllowOrigins is not needed")
	}
	if !c.AllowAllOrigins && c.AllowOriginFunc == nil && len(c.AllowOrigins) == 0 {
		return errors.New("conflict settings: all origins disabled")
	}
	for _, origin := range c.AllowOrigins {
		if !strings.Contains(origin, "*") && !c.validateAllowedSchemas(origin) {
			return errors.New("bad origin: origins must contain '*' or include " + strings.Join(c.getAllowedSchemas(), ","))
		}
	}
	return nil
}

func (c Config) parseWildcardRules() [][]string {
	var wRules [][]string

	if !c.AllowWildcard {
		return wRules
	}

	for _, o := range c.AllowOrigins {
		if !strings.Contains(o, "*") {
			continue
		}

		if c := strings.Count(o, "*"); c > 1 {
			panic(errors.New("only one * is allowed").Error())
		}

		i := strings.Index(o, "*")
		if i == 0 {
			wRules = append(wRules, []string{"*", o[1:]})
			continue
		}
		if i == (len(o) - 1) {
			wRules = append(wRules, []string{o[:i-1], "*"})
			continue
		}

		wRules = append(wRules, []string{o[:i], o[i+1:]})
	}

	return wRules
}

// DefaultConfig returns a generic default configuration mapped to localhost.
func DefaultConfig() Config {
	return Config{
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
		AllowHeaders:     []string{"Origin", "Content-Length", "Content-Type"},
		AllowCredentials: false,
		MaxAge:           12 * time.Hour,
	}
}

// Default returns the location middleware with default configuration.
func Default() gin.HandlerFunc {
	config := DefaultConfig()
	config.AllowAllOrigins = true
	return New(config)
}

// New returns the location middleware with user-defined custom configuration.
func New(config Config) gin.HandlerFunc {
	cors := newCors(config)
	return func(c *gin.Context) {
		cors.applyCors(c)
	}
}
<|endoftext|>


<commit_before>package zip

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func Unzip(archive, target string, excludedFiles []string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	defer reader.Close()

	if err := os.MkdirAll(target, 0777); err != nil {
		return err
	}

	for _, file := range reader.File {

		excludeFile := false
		for _, excludedFile := range excludedFiles {
			if excludedFile == file.Name {
				excludeFile = true
				break
			}
		}

		if excludeFile == true {
			fmt.Println("Excluding File:  " + file.Name)
			continue
		}

		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			fmt.Println("Creating Directory:  " + file.Name)
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		fmt.Println("Extracting File:  " + file.Name)
		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func Zipit(source, target string) error {
	zipfile, err := os.Create(target)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	info, err := os.Stat(source)
	if err != nil {
		return nil
	}

	var baseDir string
	if info.IsDir() {
		baseDir = filepath.Base(source)
	}

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

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		if baseDir != "" {
			header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
		}

		if info.IsDir() {
			header.Name += "/"
		} else {
			header.Method = zip.Deflate
		}

		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()
		_, err = io.Copy(writer, file)
		return err
	})

	return err
}
<commit_msg>Too many open files on defers<commit_after>package zip

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func Unzip(archive, target string, excludedFiles []string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	defer reader.Close()

	if err := os.MkdirAll(target, 0777); err != nil {
		return err
	}

	for _, file := range reader.File {

		excludeFile := false
		for _, excludedFile := range excludedFiles {
			if excludedFile == file.Name {
				excludeFile = true
				break
			}
		}

		if excludeFile == true {
			fmt.Println("Excluding File:  " + file.Name)
			continue
		}

		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			fmt.Println("Creating Directory:  " + file.Name)
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}

		fmt.Println("Extracting File:  " + file.Name)
		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
		fileReader.Close()
		targetFile.Close()
	}

	return nil
}

func Zipit(source, target string) error {
	zipfile, err := os.Create(target)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	info, err := os.Stat(source)
	if err != nil {
		return nil
	}

	var baseDir string
	if info.IsDir() {
		baseDir = filepath.Base(source)
	}

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

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		if baseDir != "" {
			header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
		}

		if info.IsDir() {
			header.Name += "/"
		} else {
			header.Method = zip.Deflate
		}

		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()
		_, err = io.Copy(writer, file)
		return err
	})

	return err
}
<|endoftext|>


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

package testing_test

import (
	"bytes"
	"runtime"
	"sync/atomic"
	"testing"
	"text/template"
)

var roundDownTests = []struct {
	v, expected int
}{
	{1, 1},
	{9, 1},
	{10, 10},
	{11, 10},
	{100, 100},
	{101, 100},
	{999, 100},
	{1000, 1000},
	{1001, 1000},
}

func TestRoundDown10(t *testing.T) {
	for _, tt := range roundDownTests {
		actual := testing.RoundDown10(tt.v)
		if tt.expected != actual {
			t.Errorf("roundDown10(%d): expected %d, actual %d", tt.v, tt.expected, actual)
		}
	}
}

var roundUpTests = []struct {
	v, expected int
}{
	{0, 1},
	{1, 1},
	{2, 2},
	{5, 5},
	{9, 10},
	{999, 1000},
	{1000, 1000},
	{1400, 2000},
	{1700, 2000},
	{4999, 5000},
	{5000, 5000},
	{5001, 10000},
}

func TestRoundUp(t *testing.T) {
	for _, tt := range roundUpTests {
		actual := testing.RoundUp(tt.v)
		if tt.expected != actual {
			t.Errorf("roundUp(%d): expected %d, actual %d", tt.v, tt.expected, actual)
		}
	}
}

func TestRunParallel(t *testing.T) {
	testing.Benchmark(func(b *testing.B) {
		procs := uint32(0)
		iters := uint64(0)
		b.SetParallelism(3)
		b.RunParallel(func(pb *testing.PB) {
			atomic.AddUint32(&procs, 1)
			for pb.Next() {
				atomic.AddUint64(&iters, 1)
			}
		})
		if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
			t.Errorf("got %v procs, want %v", procs, want)
		}
		if iters != uint64(b.N) {
			t.Errorf("got %v iters, want %v", iters, b.N)
		}
	})
}

func TestRunParallelFail(t *testing.T) {
	testing.Benchmark(func(b *testing.B) {
		b.RunParallel(func(pb *testing.PB) {
			// The function must be able to log/abort
			// w/o crashing/deadlocking the whole benchmark.
			b.Log("log")
			b.Error("error")
			b.Fatal("fatal")
		})
	})
}

func ExampleB_RunParallel() {
	// Parallel benchmark for text/template.Template.Execute on a single object.
	testing.Benchmark(func(b *testing.B) {
		templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
		// RunParallel will create GOMAXPROCS goroutines
		// and distribute work among them.
		b.RunParallel(func(pb *testing.PB) {
			// Each goroutine has its own bytes.Buffer.
			var buf bytes.Buffer
			for pb.Next() {
				// The loop body is executed b.N times total across all goroutines.
				buf.Reset()
				templ.Execute(&buf, "World")
			}
		})
	})
}
<commit_msg>testing: fix bogus benchmark Fatal must not be called from secondary goroutines. Fixes issue 7401.<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing_test

import (
	"bytes"
	"runtime"
	"sync/atomic"
	"testing"
	"text/template"
)

var roundDownTests = []struct {
	v, expected int
}{
	{1, 1},
	{9, 1},
	{10, 10},
	{11, 10},
	{100, 100},
	{101, 100},
	{999, 100},
	{1000, 1000},
	{1001, 1000},
}

func TestRoundDown10(t *testing.T) {
	for _, tt := range roundDownTests {
		actual := testing.RoundDown10(tt.v)
		if tt.expected != actual {
			t.Errorf("roundDown10(%d): expected %d, actual %d", tt.v, tt.expected, actual)
		}
	}
}

var roundUpTests = []struct {
	v, expected int
}{
	{0, 1},
	{1, 1},
	{2, 2},
	{5, 5},
	{9, 10},
	{999, 1000},
	{1000, 1000},
	{1400, 2000},
	{1700, 2000},
	{4999, 5000},
	{5000, 5000},
	{5001, 10000},
}

func TestRoundUp(t *testing.T) {
	for _, tt := range roundUpTests {
		actual := testing.RoundUp(tt.v)
		if tt.expected != actual {
			t.Errorf("roundUp(%d): expected %d, actual %d", tt.v, tt.expected, actual)
		}
	}
}

func TestRunParallel(t *testing.T) {
	testing.Benchmark(func(b *testing.B) {
		procs := uint32(0)
		iters := uint64(0)
		b.SetParallelism(3)
		b.RunParallel(func(pb *testing.PB) {
			atomic.AddUint32(&procs, 1)
			for pb.Next() {
				atomic.AddUint64(&iters, 1)
			}
		})
		if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
			t.Errorf("got %v procs, want %v", procs, want)
		}
		if iters != uint64(b.N) {
			t.Errorf("got %v iters, want %v", iters, b.N)
		}
	})
}

func TestRunParallelFail(t *testing.T) {
	testing.Benchmark(func(b *testing.B) {
		b.RunParallel(func(pb *testing.PB) {
			// The function must be able to log/abort
			// w/o crashing/deadlocking the whole benchmark.
			b.Log("log")
			b.Error("error")
		})
	})
}

func ExampleB_RunParallel() {
	// Parallel benchmark for text/template.Template.Execute on a single object.
	testing.Benchmark(func(b *testing.B) {
		templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
		// RunParallel will create GOMAXPROCS goroutines
		// and distribute work among them.
		b.RunParallel(func(pb *testing.PB) {
			// Each goroutine has its own bytes.Buffer.
			var buf bytes.Buffer
			for pb.Next() {
				// The loop body is executed b.N times total across all goroutines.
				buf.Reset()
				templ.Execute(&buf, "World")
			}
		})
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Gocov Authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

package cov

import (
	"fmt"
	"github.com/axw/gocov"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"strings"
)

const (
	hitPrefix  = "    "
	missPrefix = "MISS"
)

type annotator struct {
	fset  *token.FileSet
	files map[string]*token.File
}

func annotateFunctionSource(w io.Writer, fn *gocov.Function) {
	if fn == nil {
		panic("nil function to annotate")
	}
	a := &annotator{}
	a.fset = token.NewFileSet()
	a.files = make(map[string]*token.File)
	a.printFunctionSource(w, fn)
}

func (a *annotator) printFunctionSource(w io.Writer, fn *gocov.Function) error {
	// Load the file for line information. Probably overkill, maybe
	// just compute the lines from offsets in here.
	setContent := false
	file := a.files[fn.File]
	if file == nil {
		info, err := os.Stat(fn.File)
		if err != nil {
			return err
		}
		file = a.fset.AddFile(fn.File, a.fset.Base(), int(info.Size()))
		setContent = true
	}

	data, err := ioutil.ReadFile(fn.File)
	if err != nil {
		return err
	}

	if setContent {
		// This processes the content and records line number info.
		file.SetLinesForContent(data)
	}

	statements := fn.Statements[:]
	lineno := file.Line(file.Pos(fn.Start))
	lines := strings.Split(string(data)[fn.Start:fn.End], "\n")
	fmt.Fprintln(w)
	fmt.Fprintf(w, "
func %s
", fn.Name, fn.Name)
	fmt.Fprintf(w, "
Back
In %s:

",
		fn.Name, fn.File)
	fmt.Fprintln(w, "
")
	for i, line := range lines {
		lineno := lineno + i
		statementFound := false
		hit := false
		for j := 0; j < len(statements); j++ {
			start := file.Line(file.Pos(statements[j].Start))
			if start == lineno {
				statementFound = true
				if !hit && statements[j].Reached > 0 {
					hit = true
				}
				statements = append(statements[:j], statements[j+1:]...)
			}
		}
		hitmiss := hitPrefix
		if statementFound && !hit {
			hitmiss = missPrefix
		}
		tr := "<tr"
		if hitmiss == missPrefix {
			tr += ` class="miss">`
		} else {
			tr += ">"
		}
		fmt.Fprintf(w, "%s%d
%s

", tr, lineno,
			strings.Replace(line, "\t", "        ", 1))
	}
	fmt.Fprintln(w, "
")

	return nil
}
<commit_msg>Fix code indentation issue<commit_after>// Copyright (c) 2012 The Gocov Authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

package cov

import (
	"fmt"
	"github.com/axw/gocov"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"strings"
)

const (
	hitPrefix  = "    "
	missPrefix = "MISS"
)

type annotator struct {
	fset  *token.FileSet
	files map[string]*token.File
}

func annotateFunctionSource(w io.Writer, fn *gocov.Function) {
	if fn == nil {
		panic("nil function to annotate")
	}
	a := &annotator{}
	a.fset = token.NewFileSet()
	a.files = make(map[string]*token.File)
	a.printFunctionSource(w, fn)
}

func (a *annotator) printFunctionSource(w io.Writer, fn *gocov.Function) error {
	// Load the file for line information. Probably overkill, maybe
	// just compute the lines from offsets in here.
	setContent := false
	file := a.files[fn.File]
	if file == nil {
		info, err := os.Stat(fn.File)
		if err != nil {
			return err
		}
		file = a.fset.AddFile(fn.File, a.fset.Base(), int(info.Size()))
		setContent = true
	}

	data, err := ioutil.ReadFile(fn.File)
	if err != nil {
		return err
	}

	if setContent {
		// This processes the content and records line number info.
		file.SetLinesForContent(data)
	}

	statements := fn.Statements[:]
	lineno := file.Line(file.Pos(fn.Start))
	lines := strings.Split(string(data)[fn.Start:fn.End], "\n")
	fmt.Fprintln(w)
	fmt.Fprintf(w, "
func %s
", fn.Name, fn.Name)
	fmt.Fprintf(w, "
Back
In %s:

",
		fn.Name, fn.File)
	fmt.Fprintln(w, "
")
	for i, line := range lines {
		lineno := lineno + i
		statementFound := false
		hit := false
		for j := 0; j < len(statements); j++ {
			start := file.Line(file.Pos(statements[j].Start))
			if start == lineno {
				statementFound = true
				if !hit && statements[j].Reached > 0 {
					hit = true
				}
				statements = append(statements[:j], statements[j+1:]...)
			}
		}
		hitmiss := hitPrefix
		if statementFound && !hit {
			hitmiss = missPrefix
		}
		tr := "<tr"
		if hitmiss == missPrefix {
			tr += ` class="miss">`
		} else {
			tr += ">"
		}
		fmt.Fprintf(w, "%s%d
%s

", tr, lineno,
			strings.Replace(line, "\t", "        ", -1))
	}
	fmt.Fprintln(w, "
")

	return nil
}
<|endoftext|>


<commit_before>package integration_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/cloudfoundry/libbuildpack/cutlass"

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

	"testing"
)

var bpDir string
var buildpackVersion string
var isSerialTest bool
var packagedBuildpack cutlass.VersionedBuildpackPackage
var token string
var platform string
var stack string

var _ = func() bool {
	testing.Init()
	return true
}()

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.BoolVar(&isSerialTest, "serial", false, "run just serial tests")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "128M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.StringVar(&token, "github-token", "", "use the token to make GitHub API requests")
	flag.StringVar(&platform, "platform", "cf", "platform to run tests against")
	flag.StringVar(&stack, "stack", "cflinuxfs3", "stack to use when pushing apps")
	flag.Parse()
}

var _ = SynchronizedBeforeSuite(func() []byte {
	// Run once
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack(os.Getenv("CF_STACK"), ApiHasStackAssociation())
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	// Run on all nodes
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	Expect(cutlass.CopyCfHome()).To(Succeed())
	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter

	SetDefaultEventuallyTimeout(10 * time.Second)
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
}, func() {
	// Run once
	Expect(cutlass.RemovePackagedBuildpack(packagedBuildpack)).To(Succeed())
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func ApiHasTask() bool {
	supported, err := cutlass.ApiGreaterThan("2.75.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasMultiBuildpack() bool {
	supported, err := cutlass.ApiGreaterThan("2.90.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasStackAssociation() bool {
	supported, err := cutlass.ApiGreaterThan("2.113.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func Fixtures(names ...string) string {
	root, err := cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	names = append([]string{root, "fixtures"}, names...)
	return filepath.Join(names...)
}

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) {
	Context("with an uncached buildpack", func() {
		BeforeEach(func() {
			if cutlass.Cached {
				Skip("Running cached tests")
			}
		})

		It("uses a proxy during staging if present", func() {
			proxy, err := cutlass.NewProxy()
			Expect(err).To(BeNil())
			defer proxy.Close()

			bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
			cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
			err = cmd.Run()
			Expect(err).To(BeNil())
			defer os.Remove(bpFile)

			traffic, _, _, err := cutlass.InternetTraffic(
				Fixtures(fixtureName),
				bpFile,
				[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
			)
			Expect(err).To(BeNil())
			// Expect(built).To(BeTrue())

			destUrl, err := url.Parse(proxy.URL)
			Expect(err).To(BeNil())

			Expect(cutlass.UniqueDestination(
				traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
			)).To(BeNil())
		})
	})
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		if !cutlass.Cached {
			Skip("Running uncached tests")
		}

		bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
		cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
		err := cmd.Run()
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, built, logs, err := cutlass.InternetTraffic(
			Fixtures(fixtureName),
			bpFile,
			[]string{"LC_ALL C.UTF-8", "LANG C.UTF-8"},
		)
		Expect(err).To(BeNil())
		Expect(built).To(BeTrue(), strings.Join(logs, "\n"))
		Expect(traffic).To(BeEmpty(), strings.Join(logs, "\n"))
	})
}

func RunCf(args ...string) error {
	command := exec.Command("cf", args...)
	command.Stdout = GinkgoWriter
	command.Stderr = GinkgoWriter
	return command.Run()
}

func SkipOnCflinuxfs4() {
	if os.Getenv("CF_STACK") == "cflinuxfs4" {
		Skip("Skipping test not relevant for stack cflinuxfs4")
	}
}
<commit_msg>Add format.MaxLength = 0 to tests to increase log print<commit_after>package integration_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"testing"

	"github.com/cloudfoundry/libbuildpack/cutlass"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/format"
)

var bpDir string
var buildpackVersion string
var isSerialTest bool
var packagedBuildpack cutlass.VersionedBuildpackPackage
var token string
var platform string
var stack string

var _ = func() bool {
	testing.Init()
	return true
}()

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.BoolVar(&isSerialTest, "serial", false, "run just serial tests")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "128M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.StringVar(&token, "github-token", "", "use the token to make GitHub API requests")
	flag.StringVar(&platform, "platform", "cf", "platform to run tests against")
	flag.StringVar(&stack, "stack", "cflinuxfs3", "stack to use when pushing apps")
	flag.Parse()
	format.MaxLength = 0
}

var _ = SynchronizedBeforeSuite(func() []byte {
	// Run once
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack(os.Getenv("CF_STACK"), ApiHasStackAssociation())
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	// Run on all nodes
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	Expect(cutlass.CopyCfHome()).To(Succeed())
	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter

	SetDefaultEventuallyTimeout(10 * time.Second)
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
}, func() {
	// Run once
	Expect(cutlass.RemovePackagedBuildpack(packagedBuildpack)).To(Succeed())
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func ApiHasTask() bool {
	supported, err := cutlass.ApiGreaterThan("2.75.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasMultiBuildpack() bool {
	supported, err := cutlass.ApiGreaterThan("2.90.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func ApiHasStackAssociation() bool {
	supported, err := cutlass.ApiGreaterThan("2.113.0")
	Expect(err).NotTo(HaveOccurred())
	return supported
}

func Fixtures(names ...string) string {
	root, err := cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	names = append([]string{root, "fixtures"}, names...)
	return filepath.Join(names...)
}

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) {
	Context("with an uncached buildpack", func() {
		BeforeEach(func() {
			if cutlass.Cached {
				Skip("Running cached tests")
			}
		})

		It("uses a proxy during staging if present", func() {
			proxy, err := cutlass.NewProxy()
			Expect(err).To(BeNil())
			defer proxy.Close()

			bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
			cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
			err = cmd.Run()
			Expect(err).To(BeNil())
			defer os.Remove(bpFile)

			traffic, _, _, err := cutlass.InternetTraffic(
				Fixtures(fixtureName),
				bpFile,
				[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
			)
			Expect(err).To(BeNil())
			// Expect(built).To(BeTrue())

			destUrl, err := url.Parse(proxy.URL)
			Expect(err).To(BeNil())

			Expect(cutlass.UniqueDestination(
				traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
			)).To(BeNil())
		})
	})
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		if !cutlass.Cached {
			Skip("Running uncached tests")
		}

		bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
		cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
		err := cmd.Run()
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, built, logs, err := cutlass.InternetTraffic(
			Fixtures(fixtureName),
			bpFile,
			[]string{"LC_ALL C.UTF-8", "LANG C.UTF-8"},
		)
		Expect(err).To(BeNil())
		Expect(built).To(BeTrue(), strings.Join(logs, "\n"))
		Expect(traffic).To(BeEmpty(), strings.Join(logs, "\n"))
	})
}

func RunCf(args ...string) error {
	command := exec.Command("cf", args...)
	command.Stdout = GinkgoWriter
	command.Stderr = GinkgoWriter
	return command.Run()
}

func SkipOnCflinuxfs4() {
	if os.Getenv("CF_STACK") == "cflinuxfs4" {
		Skip("Skipping test not relevant for stack cflinuxfs4")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"hash"
	"hash/crc64"
	"testing"
)

var (
	text = []byte("something to write")
)

func BenchmarkWithPool(b *testing.B) {
	for i := 0; i < b.N; i++ {
		h := crcPool.Get().(hash.Hash64)
		h.Write(text)
		h.Reset()
		crcPool.Put(h)
	}
}

func BenchmarkWithoutPool(b *testing.B) {
	for i := 0; i < b.N; i++ {
		h := crc64.New(crc64.MakeTable(crc64.ECMA))
		h.Write(text)
	}
}
<commit_msg>added sum for better results<commit_after>package main

import (
	"hash"
	"hash/crc64"
	"testing"
)

var (
	text = []byte("something to write")
)

func BenchmarkWithPool(b *testing.B) {
	for i := 0; i < b.N; i++ {
		h := crcPool.Get().(hash.Hash64)
		h.Write(text)
		h.Sum64()
		h.Reset()
		crcPool.Put(h)
	}
}

func BenchmarkWithoutPool(b *testing.B) {
	for i := 0; i < b.N; i++ {
		h := crc64.New(crc64.MakeTable(crc64.ECMA))
		h.Write(text)
		h.Sum64()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/gob"
	"fmt"
	"github.com/rdwilliamson/aws"
	"github.com/rdwilliamson/aws/glacier"
	"io"
	"os"
	"strconv"
)

type multipartData struct {
	Region      string
	Vault       string
	Description string
	PartSize    uint
	FileName    string
	UploadId    string
	Parts       []multipartPart
	TreeHash    string
	Size        uint64
}

type multipartPart struct {
	Hash     string
	TreeHash string
	Uploaded bool
}

type limitedReadSeeker struct {
	R         io.ReadSeeker
	N         int64
	OriginalN int64
}

func (l *limitedReadSeeker) Read(p []byte) (n int, err error) {
	if l.N <= 0 {
		return 0, io.EOF
	}
	if int64(len(p)) > l.N {
		p = p[0:l.N]
	}
	n, err = l.R.Read(p)
	l.N -= int64(n)
	return
}

func (l *limitedReadSeeker) Seek(offset int64, whence int) (ret int64, err error) {
	switch whence {
	case 0:
		_, err := l.R.Seek(l.N-l.OriginalN+offset, 1)
		l.N = l.OriginalN - offset
		return offset, err
	case 1:
		// untested
		_, err := l.R.Seek(offset, 1)
		l.N -= offset
		return l.OriginalN - l.N, err
	case 2:
		// untested
		_, err := l.R.Seek(l.N-offset, 1)
		l.N = offset
		return l.OriginalN - l.N, err
	}
	panic("invalid whence")
}

func parseRegion(region string) {
	for _, v := range aws.Regions {
		if region == v.Region {
			secret, access := aws.KeysFromEnviroment()
			if secret == "" || access == "" {
				fmt.Println("could not get keys")
				os.Exit(1)
			}

			connection = glacier.NewConnection(secret, access, v)

			break
		}
	}
	if connection == nil {
		fmt.Println("error getting region")
		os.Exit(1)
	}
}

func multipart(args []string) {
	if len(args) < 1 {
		fmt.Println("no multipart command")
		os.Exit(1)
	}
	command := args[0]
	args = args[1:]

	switch command {
	case "init":
		var data multipartData

		args = getConnection(args)
		data.Region = connection.Signature.Region.Region

		if len(args) < 3 {
			fmt.Println("no vault, file name and/or part size")
			os.Exit(1)
		}
		data.Vault = args[0]
		data.FileName = args[1]
		partSize, err := strconv.ParseUint(args[2], 10, 32)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		data.PartSize = uint(partSize) * 1024 * 1024
		args = args[3:]

		if len(args) > 0 {
			data.Description = args[0]
		}

		f, err := os.Open(data.FileName)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer f.Close()

		s, _ := f.Stat()
		parts := s.Size() / int64(data.PartSize)
		if s.Size()%int64(data.PartSize) > 0 {
			parts++
		}
		data.Parts = make([]multipartPart, parts)

		partHasher := glacier.NewTreeHash()
		wholeHasher := glacier.NewTreeHash()
		hasher := io.MultiWriter(partHasher, wholeHasher)
		for i := range data.Parts {
			n, err := io.CopyN(hasher, f, int64(data.PartSize))
			if err != nil && err != io.EOF {
				fmt.Println(err)
				os.Exit(1)
			}
			data.Size += uint64(n)
			partHasher.Close()
			data.Parts[i].Hash = partHasher.Hash()
			data.Parts[i].TreeHash = partHasher.TreeHash()
			partHasher.Reset()
		}
		wholeHasher.Close()
		data.TreeHash = wholeHasher.TreeHash()

		data.UploadId, err = connection.InitiateMultipart(data.Vault, data.PartSize, data.Description)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		out, err := os.Create(data.FileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer out.Close()

		enc := gob.NewEncoder(out)
		err = enc.Encode(data)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "print":
		if len(args) < 1 {
			fmt.Println("no file name")
			os.Exit(1)
		}
		fileName := args[0]

		f, err := os.Open(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer f.Close()

		dec := gob.NewDecoder(f)
		var data multipartData
		err = dec.Decode(&data)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		fmt.Println("Region:", data.Region)
		fmt.Println("Vault:", data.Vault)
		fmt.Println("Description:", data.Description)
		fmt.Println("Part Size:", prettySize(uint64(data.PartSize)))
		fmt.Println("Upload ID:", data.UploadId)
		uploaded := 0
		for i := range data.Parts {
			if data.Parts[i].Uploaded {
				uploaded++
			}
		}
		fmt.Println("Parts Uploaded", uploaded, "/", len(data.Parts))
		fmt.Println("Tree Hash:", data.TreeHash)
		fmt.Println("Size:", data.Size, prettySize(data.Size))

	case "run":
		if len(args) < 1 {
			fmt.Println("no file")
			os.Exit(1)
		}
		fileName := args[0]
		args = args[1:]

		var parts int
		if len(args) > 0 {
			parts64, err := strconv.ParseInt(args[1], 10, 64)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			parts = int(parts64)
		}

		gobFile, err := os.Open(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		dec := gob.NewDecoder(gobFile)
		var data multipartData
		err = dec.Decode(&data)
		gobFile.Close()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		parseRegion(data.Region)

		file, err := os.Open(fileName)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer file.Close()

		start := uint64(0)
		index := 0
		for _, v := range data.Parts {
			if v.Uploaded {
				start += uint64(data.PartSize)
				index++
			} else {
				break
			}
		}

		if len(data.Parts) < parts {
			parts = len(data.Parts)
		}
		if parts == 0 {
			parts = len(data.Parts)
		}

		i, try := 0, 0
		for i < parts {
			if index >= len(data.Parts) {
				break
			}

			_, err = file.Seek(int64(start), 0)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			body := &limitedReadSeeker{file, int64(data.PartSize), int64(data.PartSize)}

			err = connection.UploadMultipart(data.Vault, data.UploadId, start, body)

			if err != nil {
				fmt.Println(err)
				if try++; try > retrys {
					fmt.Println("too many retrys")
					os.Exit(1)
				}
				continue
			}

			i++
			try = 0

			data.Parts[index].Uploaded = true
			gobFile, err = os.Create(fileName + ".gob.new")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			enc := gob.NewEncoder(gobFile)
			err = enc.Encode(data)
			gobFile.Close()
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			err = os.Remove(fileName + ".gob")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			err = os.Rename(fileName+".gob.new", fileName+".gob")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			start += uint64(data.PartSize)
			index++
		}

		done := true
		for _, v := range data.Parts {
			if !v.Uploaded {
				done = false
				break
			}
		}

		if done {
			archiveId, err := connection.CompleteMultipart(data.Vault, data.UploadId, data.TreeHash, data.Size)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Println(archiveId)

			err = os.Remove(fileName + ".gob")
			if err != nil {
				fmt.Println(err)
			}
		}

	case "abort":
		if len(args) < 1 {
			fmt.Println("no file name")
			os.Exit(1)
		}
		fileName := args[0]

		f, err := os.Open(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer f.Close()

		dec := gob.NewDecoder(f)
		var data multipartData
		err = dec.Decode(&data)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		parseRegion(data.Region)

		err = connection.AbortMultipart(data.Vault, data.UploadId)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		err = os.Remove(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "list":
		if len(args) < 1 {
			fmt.Println("no multipart sub command")
		}
		subCommand := args[0]
		args = args[1:]

		switch subCommand {
		case "parts":
			if len(args) < 1 {
				fmt.Println("no file")
				os.Exit(1)
			}
			fileName := args[0]

			f, err := os.Open(fileName + ".gob")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			defer f.Close()

			dec := gob.NewDecoder(f)
			var data multipartData
			err = dec.Decode(&data)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			parseRegion(data.Region)

			parts, err := connection.ListMultipartParts(data.Vault, data.UploadId, "", 0)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			fmt.Printf("%+v\n", *parts)

		case "uploads":
			args = getConnection(args)

			if len(args) < 1 {
				fmt.Println("no vault")
				os.Exit(1)
			}
			vault := args[0]

			parts, _, err := connection.ListMultipartUploads(vault, "", 0)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			for _, v := range parts {
				fmt.Println("Archive Description:", v.ArchiveDescription)
				fmt.Println("Creation Data:", v.CreationDate)
				fmt.Println("Multipart Upload ID:", v.MultipartUploadId)
				fmt.Println("Part Size:", prettySize(uint64(v.PartSizeInBytes)))
				fmt.Println("Vault ARN:", v.VaultARN)
				fmt.Println()
			}

		default:
			fmt.Println("unknown multipart sub command:", subCommand)
			os.Exit(1)
		}

	default:
		fmt.Println("unknown multipart command:", command)
		os.Exit(1)
	}
}
<commit_msg>Added missing colon.<commit_after>package main

import (
	"encoding/gob"
	"fmt"
	"github.com/rdwilliamson/aws"
	"github.com/rdwilliamson/aws/glacier"
	"io"
	"os"
	"strconv"
)

type multipartData struct {
	Region      string
	Vault       string
	Description string
	PartSize    uint
	FileName    string
	UploadId    string
	Parts       []multipartPart
	TreeHash    string
	Size        uint64
}

type multipartPart struct {
	Hash     string
	TreeHash string
	Uploaded bool
}

type limitedReadSeeker struct {
	R         io.ReadSeeker
	N         int64
	OriginalN int64
}

func (l *limitedReadSeeker) Read(p []byte) (n int, err error) {
	if l.N <= 0 {
		return 0, io.EOF
	}
	if int64(len(p)) > l.N {
		p = p[0:l.N]
	}
	n, err = l.R.Read(p)
	l.N -= int64(n)
	return
}

func (l *limitedReadSeeker) Seek(offset int64, whence int) (ret int64, err error) {
	switch whence {
	case 0:
		_, err := l.R.Seek(l.N-l.OriginalN+offset, 1)
		l.N = l.OriginalN - offset
		return offset, err
	case 1:
		// untested
		_, err := l.R.Seek(offset, 1)
		l.N -= offset
		return l.OriginalN - l.N, err
	case 2:
		// untested
		_, err := l.R.Seek(l.N-offset, 1)
		l.N = offset
		return l.OriginalN - l.N, err
	}
	panic("invalid whence")
}

func parseRegion(region string) {
	for _, v := range aws.Regions {
		if region == v.Region {
			secret, access := aws.KeysFromEnviroment()
			if secret == "" || access == "" {
				fmt.Println("could not get keys")
				os.Exit(1)
			}

			connection = glacier.NewConnection(secret, access, v)

			break
		}
	}
	if connection == nil {
		fmt.Println("error getting region")
		os.Exit(1)
	}
}

func multipart(args []string) {
	if len(args) < 1 {
		fmt.Println("no multipart command")
		os.Exit(1)
	}
	command := args[0]
	args = args[1:]

	switch command {
	case "init":
		var data multipartData

		args = getConnection(args)
		data.Region = connection.Signature.Region.Region

		if len(args) < 3 {
			fmt.Println("no vault, file name and/or part size")
			os.Exit(1)
		}
		data.Vault = args[0]
		data.FileName = args[1]
		partSize, err := strconv.ParseUint(args[2], 10, 32)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		data.PartSize = uint(partSize) * 1024 * 1024
		args = args[3:]

		if len(args) > 0 {
			data.Description = args[0]
		}

		f, err := os.Open(data.FileName)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer f.Close()

		s, _ := f.Stat()
		parts := s.Size() / int64(data.PartSize)
		if s.Size()%int64(data.PartSize) > 0 {
			parts++
		}
		data.Parts = make([]multipartPart, parts)

		partHasher := glacier.NewTreeHash()
		wholeHasher := glacier.NewTreeHash()
		hasher := io.MultiWriter(partHasher, wholeHasher)
		for i := range data.Parts {
			n, err := io.CopyN(hasher, f, int64(data.PartSize))
			if err != nil && err != io.EOF {
				fmt.Println(err)
				os.Exit(1)
			}
			data.Size += uint64(n)
			partHasher.Close()
			data.Parts[i].Hash = partHasher.Hash()
			data.Parts[i].TreeHash = partHasher.TreeHash()
			partHasher.Reset()
		}
		wholeHasher.Close()
		data.TreeHash = wholeHasher.TreeHash()

		data.UploadId, err = connection.InitiateMultipart(data.Vault, data.PartSize, data.Description)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		out, err := os.Create(data.FileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer out.Close()

		enc := gob.NewEncoder(out)
		err = enc.Encode(data)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "print":
		if len(args) < 1 {
			fmt.Println("no file name")
			os.Exit(1)
		}
		fileName := args[0]

		f, err := os.Open(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer f.Close()

		dec := gob.NewDecoder(f)
		var data multipartData
		err = dec.Decode(&data)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		fmt.Println("Region:", data.Region)
		fmt.Println("Vault:", data.Vault)
		fmt.Println("Description:", data.Description)
		fmt.Println("Part Size:", prettySize(uint64(data.PartSize)))
		fmt.Println("Upload ID:", data.UploadId)
		uploaded := 0
		for i := range data.Parts {
			if data.Parts[i].Uploaded {
				uploaded++
			}
		}
		fmt.Println("Parts Uploaded:", uploaded, "/", len(data.Parts))
		fmt.Println("Tree Hash:", data.TreeHash)
		fmt.Println("Size:", data.Size, prettySize(data.Size))

	case "run":
		if len(args) < 1 {
			fmt.Println("no file")
			os.Exit(1)
		}
		fileName := args[0]
		args = args[1:]

		var parts int
		if len(args) > 0 {
			parts64, err := strconv.ParseInt(args[1], 10, 64)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			parts = int(parts64)
		}

		gobFile, err := os.Open(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		dec := gob.NewDecoder(gobFile)
		var data multipartData
		err = dec.Decode(&data)
		gobFile.Close()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		parseRegion(data.Region)

		file, err := os.Open(fileName)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer file.Close()

		start := uint64(0)
		index := 0
		for _, v := range data.Parts {
			if v.Uploaded {
				start += uint64(data.PartSize)
				index++
			} else {
				break
			}
		}

		if len(data.Parts) < parts {
			parts = len(data.Parts)
		}
		if parts == 0 {
			parts = len(data.Parts)
		}

		i, try := 0, 0
		for i < parts {
			if index >= len(data.Parts) {
				break
			}

			_, err = file.Seek(int64(start), 0)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			body := &limitedReadSeeker{file, int64(data.PartSize), int64(data.PartSize)}

			err = connection.UploadMultipart(data.Vault, data.UploadId, start, body)

			if err != nil {
				fmt.Println(err)
				if try++; try > retrys {
					fmt.Println("too many retrys")
					os.Exit(1)
				}
				continue
			}

			i++
			try = 0

			data.Parts[index].Uploaded = true
			gobFile, err = os.Create(fileName + ".gob.new")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			enc := gob.NewEncoder(gobFile)
			err = enc.Encode(data)
			gobFile.Close()
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			err = os.Remove(fileName + ".gob")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			err = os.Rename(fileName+".gob.new", fileName+".gob")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			start += uint64(data.PartSize)
			index++
		}

		done := true
		for _, v := range data.Parts {
			if !v.Uploaded {
				done = false
				break
			}
		}

		if done {
			archiveId, err := connection.CompleteMultipart(data.Vault, data.UploadId, data.TreeHash, data.Size)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Println(archiveId)

			err = os.Remove(fileName + ".gob")
			if err != nil {
				fmt.Println(err)
			}
		}

	case "abort":
		if len(args) < 1 {
			fmt.Println("no file name")
			os.Exit(1)
		}
		fileName := args[0]

		f, err := os.Open(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer f.Close()

		dec := gob.NewDecoder(f)
		var data multipartData
		err = dec.Decode(&data)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		parseRegion(data.Region)

		err = connection.AbortMultipart(data.Vault, data.UploadId)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		err = os.Remove(fileName + ".gob")
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "list":
		if len(args) < 1 {
			fmt.Println("no multipart sub command")
		}
		subCommand := args[0]
		args = args[1:]

		switch subCommand {
		case "parts":
			if len(args) < 1 {
				fmt.Println("no file")
				os.Exit(1)
			}
			fileName := args[0]

			f, err := os.Open(fileName + ".gob")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			defer f.Close()

			dec := gob.NewDecoder(f)
			var data multipartData
			err = dec.Decode(&data)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			parseRegion(data.Region)

			parts, err := connection.ListMultipartParts(data.Vault, data.UploadId, "", 0)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			fmt.Printf("%+v\n", *parts)

		case "uploads":
			args = getConnection(args)

			if len(args) < 1 {
				fmt.Println("no vault")
				os.Exit(1)
			}
			vault := args[0]

			parts, _, err := connection.ListMultipartUploads(vault, "", 0)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			for _, v := range parts {
				fmt.Println("Archive Description:", v.ArchiveDescription)
				fmt.Println("Creation Data:", v.CreationDate)
				fmt.Println("Multipart Upload ID:", v.MultipartUploadId)
				fmt.Println("Part Size:", prettySize(uint64(v.PartSizeInBytes)))
				fmt.Println("Vault ARN:", v.VaultARN)
				fmt.Println()
			}

		default:
			fmt.Println("unknown multipart sub command:", subCommand)
			os.Exit(1)
		}

	default:
		fmt.Println("unknown multipart command:", command)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"testing"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/open-lambda/open-lambda/worker/config"
	"github.com/open-lambda/open-lambda/worker/dockerutil"
)

var server *Server
var docker_client *docker.Client

func init() {
	server = RunServer()
	var err error
	docker_client, err = docker.NewClientFromEnv()
	if err != nil {
		log.Fatal("failed to get docker client: ", err)
	}
}

func RunServer() *Server {
	conf, err := config.ParseConfig(os.Getenv("WORKER_CONFIG"))
	if err != nil {
		log.Fatal(err)
	}

	log.Printf(conf.Registry)
	log.Printf("Set skip_pull_existing = true\n")
	conf.Skip_pull_existing = true

	server, err := NewServer(conf)
	if err != nil {
		log.Fatal(err)
	}

	listener, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/runLambda/", server.RunLambda)
	s := &http.Server{Addr: ":8080", Handler: mux}
	go func() {
		log.Fatal(s.Serve(listener))
	}()

	return server
}

func testReq(lambda_name string, post string) (string, error) {
	url := "http://localhost:8080/runLambda/" + lambda_name
	req, err := http.NewRequest("POST", url, strings.NewReader(post))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func kill() {
	containers, err := docker_client.ListContainers(docker.ListContainersOptions{})
	if err != nil {
		log.Fatal("failed to get docker container list: ", err)
	}

	for _, container := range containers {
		if container.Labels[dockerutil.DOCKER_LABEL_CLUSTER] == server.config.Cluster_name {
			cid := container.ID
			typ := server.config.Cluster_name

			container_insp, err := docker_client.InspectContainer(cid)
			if err != nil {
				log.Fatalf("failed to get inspect docker container ID %v: ", cid, err)
			}

			if container_insp.State.Paused {
				fmt.Printf("Unpause container %v (%s)\n", cid, typ)
				if err := docker_client.UnpauseContainer(cid); err != nil {
					fmt.Printf("%s\n", err.Error())
					fmt.Printf("Failed to unpause container %v (%s).  May require manual cleanup.\n", cid, typ)
				}
			}

			fmt.Printf("Kill container %v (%s)\n", cid, typ)
			opts := docker.KillContainerOptions{ID: cid}
			if err := docker_client.KillContainer(opts); err != nil {
				fmt.Printf("%s\n", err.Error())
				fmt.Printf("Failed to kill container %v (%s).  May require manual cleanup.\n", cid, typ)
			}
		}
	}

}

func TestMain(m *testing.M) {
	ret_val := m.Run()
	fmt.Printf("\n========Cleaning========\n")
	kill()
	fmt.Printf("========================\n\n")
	os.Exit(ret_val)
}

func TestHello(t *testing.T) {
	recv, err := testReq("hello", "{}")
	if err != nil {
		t.Fatal(err)
	}
	expected := "\"hello\""
	if recv != expected {
		t.Fatalf("Expected '%v' from hello but got back '%v'\n", expected, recv)
	}
}

func TestEcho(t *testing.T) {
	values := []string{
		"{}",
		"{\"one\": 1}",
		"1",
		"\"test\"",
	}
	for _, send := range values {
		recv, err := testReq("echo", send)
		if err != nil {
			t.Fatal(err)
		}
		if recv != send {
			t.Fatalf("Sent '%v' to echo but got back '%v'\n", send, recv)
		}
	}
}

func last_count(img string) int {
	logs, err := server.handlers.Get(img).Sandbox().Logs()
	if err != nil {
		log.Fatal(err.Error())
	}
	lines := strings.Split(logs, "\n")
	for i := range lines {
		line := lines[len(lines)-i-1]
		parts := strings.Split(line, "=")
		if parts[0] == "counter" {
			if n, err := strconv.Atoi(parts[1]); err == nil {
				return n
			} else {
				panic("not an int: " + parts[1])
			}
		}
	}

	return 0
}

// thread_counter starts a backup thread that runs forever,
// incrementing a counter between 10ms sleeps.  If pausing works, the
// counter won't tick many times between requests, even if wait
// between them.
func TestThreadPausing(t *testing.T) {
	img := "thread_counter"
	testReq(img, "null")
	count1 := last_count(img)
	time.Sleep(100 * time.Millisecond)
	count2 := last_count(img)
	if count1 <= 0 {
		log.Fatal(fmt.Sprintf("count1 isn't positive (%d) (logs working?)", count1))
	}
	if count2 != count1 {
		log.Fatal(fmt.Sprintf("count1 (%d) != count2 (%d)", count1, count2))
	}
}

func BenchmarkEcho(b *testing.B) {
	values := []string{
		"{\"one\": 1}",
	}
	for i := 0; i < b.N; i++ {
		recv, err := testReq("echo", values[0])
		if err != nil {
			b.Fatal(err)
		}
		if recv != values[0] {
			b.Fatalf("Sent '%v' to echo but got back '%v'\n", values[0], recv)
		}
	}
}

func BenchmarkEchoParallel(b *testing.B) {
	values := []string{
		"{\"one\": 1}",
	}
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			recv, err := testReq("echo", values[0])
			if err != nil {
				b.Fatal(err)
			}
			if recv != values[0] {
				b.Fatalf("Sent '%v' to echo but got back '%v'\n", values[0], recv)
			}
		}
	})
}
<commit_msg>using testing Fatal instead of log Fatal<commit_after>package server

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"testing"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/open-lambda/open-lambda/worker/config"
	"github.com/open-lambda/open-lambda/worker/dockerutil"
)

var server *Server
var docker_client *docker.Client

func init() {
	server = RunServer()
	var err error
	docker_client, err = docker.NewClientFromEnv()
	if err != nil {
		log.Fatal("failed to get docker client: ", err)
	}
}

func RunServer() *Server {
	conf, err := config.ParseConfig(os.Getenv("WORKER_CONFIG"))
	if err != nil {
		log.Fatal(err)
	}

	log.Printf(conf.Registry)
	log.Printf("Set skip_pull_existing = true\n")
	conf.Skip_pull_existing = true

	server, err := NewServer(conf)
	if err != nil {
		log.Fatal(err)
	}

	listener, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/runLambda/", server.RunLambda)
	s := &http.Server{Addr: ":8080", Handler: mux}
	go func() {
		log.Fatal(s.Serve(listener))
	}()

	return server
}

func testReq(lambda_name string, post string) (string, error) {
	url := "http://localhost:8080/runLambda/" + lambda_name
	req, err := http.NewRequest("POST", url, strings.NewReader(post))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func kill() {
	containers, err := docker_client.ListContainers(docker.ListContainersOptions{})
	if err != nil {
		log.Fatal("failed to get docker container list: ", err)
	}

	for _, container := range containers {
		if container.Labels[dockerutil.DOCKER_LABEL_CLUSTER] == server.config.Cluster_name {
			cid := container.ID
			typ := server.config.Cluster_name

			container_insp, err := docker_client.InspectContainer(cid)
			if err != nil {
				log.Fatalf("failed to get inspect docker container ID %v: ", cid, err)
			}

			if container_insp.State.Paused {
				fmt.Printf("Unpause container %v (%s)\n", cid, typ)
				if err := docker_client.UnpauseContainer(cid); err != nil {
					fmt.Printf("%s\n", err.Error())
					fmt.Printf("Failed to unpause container %v (%s).  May require manual cleanup.\n", cid, typ)
				}
			}

			fmt.Printf("Kill container %v (%s)\n", cid, typ)
			opts := docker.KillContainerOptions{ID: cid}
			if err := docker_client.KillContainer(opts); err != nil {
				fmt.Printf("%s\n", err.Error())
				fmt.Printf("Failed to kill container %v (%s).  May require manual cleanup.\n", cid, typ)
			}
		}
	}

}

func TestMain(m *testing.M) {
	ret_val := m.Run()
	fmt.Printf("\n========Cleaning========\n")
	kill()
	fmt.Printf("========================\n\n")
	os.Exit(ret_val)
}

func TestHello(t *testing.T) {
	recv, err := testReq("hello", "{}")
	if err != nil {
		t.Fatal(err)
	}
	expected := "\"hello\""
	if recv != expected {
		t.Fatalf("Expected '%v' from hello but got back '%v'\n", expected, recv)
	}
}

func TestEcho(t *testing.T) {
	values := []string{
		"{}",
		"{\"one\": 1}",
		"1",
		"\"test\"",
	}
	for _, send := range values {
		recv, err := testReq("echo", send)
		if err != nil {
			t.Fatal(err)
		}
		if recv != send {
			t.Fatalf("Sent '%v' to echo but got back '%v'\n", send, recv)
		}
	}
}

func last_count(img string) (int, error) {
	logs, err := server.handlers.Get(img).Sandbox().Logs()
	if err != nil {
		return 0, err
	}

	lines := strings.Split(logs, "\n")
	for i := range lines {
		line := lines[len(lines)-i-1]
		parts := strings.Split(line, "=")
		if parts[0] == "counter" {
			if n, err := strconv.Atoi(parts[1]); err == nil {
				return n, nil
			} else {
                return 0, err
			}
		}
	}

	return 0, nil
}

// thread_counter starts a backup thread that runs forever,
// incrementing a counter between 10ms sleeps.  If pausing works, the
// counter won't tick many times between requests, even if wait
// between them.
func TestThreadPausing(t *testing.T) {
	img := "thread_counter"
	testReq(img, "null")

	count1, err := last_count(img);
    if err != nil {
        t.Fatalf("Failed to get first count with: %v", err)
    }

	time.Sleep(100 * time.Millisecond)

	count2, err := last_count(img)
    if err != nil {
        t.Fatalf("Failed to get second count with: %v", err)
    }

	if count1 <= 0 {
		t.Fatalf("count1 isn't positive (%d) - logs working?", count1)
	}
	if count2 != count1 {
		t.Fatalf("count1 (%d) != count2 (%d)", count1, count2)
	}
}

func BenchmarkEcho(b *testing.B) {
	values := []string{
		"{\"one\": 1}",
	}
	for i := 0; i < b.N; i++ {
		recv, err := testReq("echo", values[0])
		if err != nil {
			b.Fatal(err)
		}
		if recv != values[0] {
			b.Fatalf("Sent '%v' to echo but got back '%v'\n", values[0], recv)
		}
	}
}

func BenchmarkEchoParallel(b *testing.B) {
	values := []string{
		"{\"one\": 1}",
	}
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			recv, err := testReq("echo", values[0])
			if err != nil {
				b.Fatal(err)
			}
			if recv != values[0] {
				b.Fatalf("Sent '%v' to echo but got back '%v'\n", values[0], recv)
			}
		}
	})
}
<|endoftext|>


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

package multipass

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"errors"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	jose "gopkg.in/square/go-jose.v1"
)

// Portable errors
var (
	ErrInvalidToken = errors.New("invalid token")
)

// Multipass implements the http.Handler interface which can handle
// authentication and authorization of users and resources using signed JWT.
type Multipass struct {
	Resources []string
	Basepath  string
	SiteAddr  string
	Expires   time.Duration

	HandleService

	signer jose.Signer
	key    *rsa.PrivateKey
	tmpl   *template.Template
	mux    *http.ServeMux
}

// NewMultipass returns a new instance of Multipass with reasonalble defaults:
// 2048 bit RSA key pair, `/multipass` basepath a token expiration time of
// 24 hours.
func NewMultipass(basepath string, service HandleService) (*Multipass, error) {
	// Absolute the given basepath or set a default
	if len(basepath) > 0 {
		basepath = path.Join("/", basepath)
	} else {
		basepath = "/multipass"
	}

	// Get or generate and set the RSA key pairs
	var pk *rsa.PrivateKey
	buf := bytes.NewBufferString(os.Getenv(PKENV))
	if key := pemDecodePrivateKey(buf.Bytes()); key != nil {
		pk = key
	} else {
		key, err := rsa.GenerateKey(rand.Reader, 2048)
		if err != nil {
			return nil, err
		}
		pk = key
		if err := pemEncodePrivateKey(buf, pk); err != nil {
			log.Println(err)
		} else {
			os.Setenv(PKENV, buf.String())
		}
	}
	signer, err := jose.NewSigner(jose.PS512, pk)
	if err != nil {
		return nil, err
	}

	// Load HTML templates
	tmpl, err := loadTemplates()
	if err != nil {
		return nil, err
	}

	m := &Multipass{
		Resources:     []string{"/"},
		Basepath:      basepath,
		Expires:       time.Hour * 24,
		HandleService: service,
		signer:        signer,
		key:           pk,
		tmpl:          tmpl,
	}

	// Create the router
	mux := http.NewServeMux()
	mux.HandleFunc(path.Join(basepath, "/"), m.rootHandler)
	mux.HandleFunc(path.Join(basepath, "/login"), m.loginHandler)
	mux.HandleFunc(path.Join(basepath, "/confirm"), m.confirmHandler)
	mux.HandleFunc(path.Join(basepath, "/signout"), m.signoutHandler)
	mux.HandleFunc(path.Join(basepath, "/pub.cer"), m.publickeyHandler)
	m.mux = mux

	return m, nil
}

// ServeHTTP satisfies the ServeHTTP interface
func (m *Multipass) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h, p := m.mux.Handler(r); len(p) > 0 {
		h.ServeHTTP(w, r)
		return
	}
	http.NotFound(w, r)
}

// rootHandler handles the "/" path of the Multipass handler.
// Shows login page when no JWT present
// Show continue or signout page when JWT is valid
// Show token invalid page when JWT is invalid
func (m *Multipass) rootHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {

		// Regular login page
		p := &page{
			Page:        loginPage,
			LoginPath:   path.Join(m.Basepath, "login"),
			SignoutPath: path.Join(m.Basepath, "signout"),
		}

		// Show login page when there is no token
		tokenStr, err := extractToken(r)
		if err != nil {
			if s := r.URL.Query().Get("url"); !strings.HasPrefix(s, m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		var claims *Claims
		if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
			p.Page = tokenInvalidPage
			if s := r.URL.Query().Get("url"); !strings.HasPrefix(s, m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		// Authorize handle claim
		if ok := m.HandleService.Listed(claims.Handle); !ok {
			p.Page = tokenInvalidPage
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		if cookie, err := r.Cookie("next_url"); err == nil {
			p.NextURL = cookie.Value
		}
		p.Page = continueOrSignoutPage
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		if tokenStr := r.URL.Query().Get("token"); len(tokenStr) > 0 {
			cookie := &http.Cookie{
				Name:  "jwt_token",
				Value: tokenStr,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		if nexturl := r.URL.Query().Get("url"); len(nexturl) > 0 {
			cookie := &http.Cookie{
				Name:  "next_url",
				Value: nexturl,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	if r.Method == "POST" {
		r.ParseForm()
		handle := r.PostForm.Get("handle")
		if len(handle) > 0 {
			if m.HandleService.Listed(handle) {
				token, err := m.AccessToken(handle)
				if err != nil {
					log.Print(err)
				}
				values := url.Values{}
				if s := r.PostForm.Get("url"); len(s) > 0 {
					values.Set("url", s)
				}
				loginURL, err := NewLoginURL(m.SiteAddr, m.Basepath, token, values)
				if err != nil {
					log.Print(err)
				}
				if err := m.HandleService.Notify(handle, loginURL.String()); err != nil {
					log.Print(err)
				}
			}
			// Redirect even when the handle is not listed in order to prevent guessing
			location := path.Join(m.Basepath, "confirm")
			http.Redirect(w, r, location, http.StatusSeeOther)
			return
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) confirmHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		w.Header().Add("Content-Type", "text/html; charset=utf-8")
		p := &page{
			Page: tokenSentPage,
		}
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// signoutHandler deletes the jwt_token cookie and redirect to the login
// location.
func (m *Multipass) signoutHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		if cookie, err := r.Cookie("jwt_token"); err == nil {
			cookie.Expires = time.Now().AddDate(-1, 0, 0)
			cookie.MaxAge = -1
			cookie.Path = "/"
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// publickeyHandler writes the public key to the given ResponseWriter to allow
// other to validate Multipass signed tokens.
func (m *Multipass) publickeyHandler(w http.ResponseWriter, r *http.Request) {
	err := pemEncodePublicKey(w, &m.key.PublicKey)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
	w.Header().Set("Content-Type", "application/pkix-cert")
}

// AccessToken returns a new signed and serialized token with the given handle
// as a claim.
func (m *Multipass) AccessToken(handle string) (tokenStr string, err error) {
	claims := &Claims{
		Handle:    handle,
		Resources: m.Resources,
		Expires:   time.Now().Add(m.Expires).Unix(),
	}
	return accessToken(m.signer, claims)
}

// NewLoginURL returns a login url which can be used as a time limited login.
// Optional values will be encoded in the login URL.
func NewLoginURL(siteaddr, basepath, token string, v url.Values) (*url.URL, error) {
	u, err := url.Parse(siteaddr)
	if err != nil {
		return u, err
	}
	u.Path = path.Join(basepath, "login")
	v.Set("token", token)
	u.RawQuery = v.Encode()
	return u, nil
}

func tokenHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	// Extract token from HTTP header, query parameter or cookie
	tokenStr, err := extractToken(r)
	if err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	var claims *Claims
	if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Authorize handle claim
	if ok := m.HandleService.Listed(claims.Handle); !ok {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Verify path claim
	var match bool
	for _, path := range claims.Resources {
		if strings.HasPrefix(r.URL.Path, path) {
			match = true
			continue
		}
	}
	if !match {
		return http.StatusForbidden, errors.New("forbidden")
	}

	// Pass on authorized handle to downstream handlers
	r.Header.Set("Multipass-Handle", claims.Handle)
	return http.StatusOK, nil
}
<commit_msg>Remove private key from Multipass instance, load from environment<commit_after>// Copyright 2016 Lars Wiegman. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

package multipass

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"errors"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	jose "gopkg.in/square/go-jose.v1"
)

// Portable errors
var (
	ErrInvalidToken = errors.New("invalid token")
)

// Multipass implements the http.Handler interface which can handle
// authentication and authorization of users and resources using signed JWT.
type Multipass struct {
	Resources []string
	Basepath  string
	SiteAddr  string
	Expires   time.Duration

	HandleService

	tmpl *template.Template
	mux  *http.ServeMux
}

// NewMultipass returns a new instance of Multipass with reasonalble defaults:
// 2048 bit RSA key pair, `/multipass` basepath a token expiration time of
// 24 hours.
func NewMultipass(basepath string, service HandleService) (*Multipass, error) {
	// Absolute the given basepath or set a default
	if len(basepath) > 0 {
		basepath = path.Join("/", basepath)
	} else {
		basepath = "/multipass"
	}

	// Generate and set a private key if none is set
	if k := pemDecodePrivateKey([]byte(os.Getenv(PKENV))); k == nil {
		pk, err := rsa.GenerateKey(rand.Reader, 2048)
		if err != nil {
			return nil, err
		}
		buf := new(bytes.Buffer)
		if err := pemEncodePrivateKey(buf, pk); err != nil {
			return nil, err
		}
		os.Setenv(PKENV, buf.String())
	}

	// Load HTML templates
	tmpl, err := loadTemplates()
	if err != nil {
		return nil, err
	}

	m := &Multipass{
		Resources:     []string{"/"},
		Basepath:      basepath,
		Expires:       time.Hour * 24,
		HandleService: service,
		tmpl:          tmpl,
	}

	// Create the router
	mux := http.NewServeMux()
	mux.HandleFunc(path.Join(basepath, "/"), m.rootHandler)
	mux.HandleFunc(path.Join(basepath, "/login"), m.loginHandler)
	mux.HandleFunc(path.Join(basepath, "/confirm"), m.confirmHandler)
	mux.HandleFunc(path.Join(basepath, "/signout"), m.signoutHandler)
	mux.HandleFunc(path.Join(basepath, "/pub.cer"), m.publickeyHandler)
	m.mux = mux

	return m, nil
}

// ServeHTTP satisfies the ServeHTTP interface
func (m *Multipass) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h, p := m.mux.Handler(r); len(p) > 0 {
		h.ServeHTTP(w, r)
		return
	}
	http.NotFound(w, r)
}

// rootHandler handles the "/" path of the Multipass handler.
// Shows login page when no JWT present
// Show continue or signout page when JWT is valid
// Show token invalid page when JWT is invalid
func (m *Multipass) rootHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {

		// Regular login page
		p := &page{
			Page:        loginPage,
			LoginPath:   path.Join(m.Basepath, "login"),
			SignoutPath: path.Join(m.Basepath, "signout"),
		}

		// Show login page when there is no token
		tokenStr, err := extractToken(r)
		if err != nil {
			if s := r.URL.Query().Get("url"); !strings.HasPrefix(s, m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		pk := pemDecodePrivateKey([]byte(os.Getenv(PKENV)))
		if pk == nil {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}
		var claims *Claims
		if claims, err = validateToken(tokenStr, pk.PublicKey); err != nil {
			p.Page = tokenInvalidPage
			if s := r.URL.Query().Get("url"); !strings.HasPrefix(s, m.Basepath) {
				p.NextURL = s
			}
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		// Authorize handle claim
		if ok := m.HandleService.Listed(claims.Handle); !ok {
			p.Page = tokenInvalidPage
			m.tmpl.ExecuteTemplate(w, "page", p)
			return
		}
		if cookie, err := r.Cookie("next_url"); err == nil {
			p.NextURL = cookie.Value
		}
		p.Page = continueOrSignoutPage
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		if tokenStr := r.URL.Query().Get("token"); len(tokenStr) > 0 {
			cookie := &http.Cookie{
				Name:  "jwt_token",
				Value: tokenStr,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		if nexturl := r.URL.Query().Get("url"); len(nexturl) > 0 {
			cookie := &http.Cookie{
				Name:  "next_url",
				Value: nexturl,
				Path:  "/",
			}
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	if r.Method == "POST" {
		r.ParseForm()
		handle := r.PostForm.Get("handle")
		if len(handle) > 0 {
			if m.HandleService.Listed(handle) {
				token, err := m.AccessToken(handle)
				if err != nil {
					log.Print(err)
				}
				values := url.Values{}
				if s := r.PostForm.Get("url"); len(s) > 0 {
					values.Set("url", s)
				}
				loginURL, err := NewLoginURL(m.SiteAddr, m.Basepath, token, values)
				if err != nil {
					log.Print(err)
				}
				if err := m.HandleService.Notify(handle, loginURL.String()); err != nil {
					log.Print(err)
				}
			}
			// Redirect even when the handle is not listed in order to prevent guessing
			location := path.Join(m.Basepath, "confirm")
			http.Redirect(w, r, location, http.StatusSeeOther)
			return
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

func (m *Multipass) confirmHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		w.Header().Add("Content-Type", "text/html; charset=utf-8")
		p := &page{
			Page: tokenSentPage,
		}
		m.tmpl.ExecuteTemplate(w, "page", p)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// signoutHandler deletes the jwt_token cookie and redirect to the login
// location.
func (m *Multipass) signoutHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		if cookie, err := r.Cookie("jwt_token"); err == nil {
			cookie.Expires = time.Now().AddDate(-1, 0, 0)
			cookie.MaxAge = -1
			cookie.Path = "/"
			http.SetCookie(w, cookie)
		}
		http.Redirect(w, r, m.Basepath, http.StatusSeeOther)
		return
	}
	w.WriteHeader(http.StatusMethodNotAllowed)
}

// publickeyHandler writes the public key to the given ResponseWriter to allow
// other to validate Multipass signed tokens.
func (m *Multipass) publickeyHandler(w http.ResponseWriter, r *http.Request) {
	pk := pemDecodePrivateKey([]byte(os.Getenv(PKENV)))
	if pk == nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
	err := pemEncodePublicKey(w, &pk.PublicKey)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
	w.Header().Set("Content-Type", "application/pkix-cert")
}

// AccessToken returns a new signed and serialized token with the given handle
// as a claim.
func (m *Multipass) AccessToken(handle string) (tokenStr string, err error) {
	claims := &Claims{
		Handle:    handle,
		Resources: m.Resources,
		Expires:   time.Now().Add(m.Expires).Unix(),
	}
	pk := pemDecodePrivateKey([]byte(os.Getenv(PKENV)))
	if pk == nil {
		return "", err
	}
	signer, err := jose.NewSigner(jose.PS512, pk)
	if err != nil {
		return "", err
	}
	return accessToken(signer, claims)
}

// NewLoginURL returns a login url which can be used as a time limited login.
// Optional values will be encoded in the login URL.
func NewLoginURL(siteaddr, basepath, token string, v url.Values) (*url.URL, error) {
	u, err := url.Parse(siteaddr)
	if err != nil {
		return u, err
	}
	u.Path = path.Join(basepath, "login")
	v.Set("token", token)
	u.RawQuery = v.Encode()
	return u, nil
}

func tokenHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	// Extract token from HTTP header, query parameter or cookie
	tokenStr, err := extractToken(r)
	if err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	var claims *Claims
	pk := pemDecodePrivateKey([]byte(os.Getenv(PKENV)))
	if pk == nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	if claims, err = validateToken(tokenStr, pk.PublicKey); err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Authorize handle claim
	if ok := m.HandleService.Listed(claims.Handle); !ok {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Verify path claim
	var match bool
	for _, path := range claims.Resources {
		if strings.HasPrefix(r.URL.Path, path) {
			match = true
			continue
		}
	}
	if !match {
		return http.StatusForbidden, errors.New("forbidden")
	}

	// Pass on authorized handle to downstream handlers
	r.Header.Set("Multipass-Handle", claims.Handle)
	return http.StatusOK, nil
}
<|endoftext|>