<commit_before>package net4g

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/carsonsx/log4g"
	"github.com/carsonsx/net4g/util"
	"github.com/golang/protobuf/proto"
	"reflect"
)

type Serializer interface {
	SetIdStartingValue(id int)
	RegisterById(t reflect.Type, id_at_most_one ...int) (id int, err error)
	RegisterByKey(t reflect.Type, key_at_most_one ...string) (key string, err error)
	Serialize(v interface{}) (data []byte, err error)
	Deserialize(data []byte) (v interface{}, err error)
	RangeId(f func(id int, t reflect.Type))
	RangeKey(f func(key string, t reflect.Type))
}

type emptySerializer struct {
	type_id_map  map[reflect.Type]int
	type_key_map map[reflect.Type]string
	id_type_map  map[int]reflect.Type
	key_type_map map[string]reflect.Type
	ids []int
	keys []string
	id           int
	registered   bool
	byId         bool
}

func (s *emptySerializer) SetIdStartingValue(id int) {
	s.id = id
}

func (s *emptySerializer) RegisterById(t reflect.Type, id_at_most_one ...int) (id int, err error) {

	if t == nil || t.Kind() != reflect.Ptr {
		panic("type must be a pointer")
	}

	if len(s.type_key_map) > 0 {
		panic("can not registered id and key in one serializer")
	}

	if len(id_at_most_one) > 1 {
		panic("only mapping one type with one id")
	}

	if _id, ok := s.type_id_map[t]; ok {
		text := fmt.Sprintf("%s has been registered by %d", t.String(), _id)
		log4g.Error(text)
		return 0, errors.New(text)
	}

	if len(id_at_most_one) == 1 {
		id = id_at_most_one[0]
	} else {
		id = s.id
	}

	s.type_id_map[t] = id
	s.id_type_map[id] = t
	s.ids = append(s.ids, id)

	s.byId = true
	s.registered = true

	s.id++

	return
}

func (s *emptySerializer) RegisterByKey(t reflect.Type, key_at_most_one ...string) (key string, err error) {

	if t == nil || t.Kind() != reflect.Ptr {
		panic("type must be a pointer")
	}

	if len(s.type_id_map) > 0 {
		panic("can not registered key and id in one serializer")
	}

	if len(key_at_most_one) > 1 {
		panic("only mapping one type with one key")
	}

	if _key, ok := s.type_key_map[t]; ok {
		text := fmt.Sprintf("%s has been registered by %s", t.Elem().Name(), _key)
		log4g.Error(text)
		err = errors.New(text)
		return
	}

	if len(key_at_most_one) == 1 {
		key = key_at_most_one[0]
	} else {
		key = t.String()
	}

	s.type_key_map[t] = key
	s.key_type_map[key] = t
	s.keys = append(s.keys, key)

	s.byId = false
	s.registered = true

	log4g.Info("%v register by key '%s'\n", t, key)

	return
}

func (s *emptySerializer) Serialize(v interface{}) (data []byte, err error) {
	return v.([]byte), nil
}

func (s *emptySerializer) Deserialize(data []byte) (v interface{}, err error) {
	return data, nil
}

func (s *emptySerializer) RangeId(f func(id int, t reflect.Type)) {
	for _, id := range s.ids {
		f(id, s.id_type_map[id])
	}
}

func (s *emptySerializer) RangeKey(f func(key string, t reflect.Type)) {
	for _, key := range s.keys {
		f(key, s.key_type_map[key])
	}
}

func NewEmptySerializer() Serializer {
	return newEmptySerializer()
}

func newEmptySerializer() *emptySerializer {
	s := new(emptySerializer)
	s.type_id_map = make(map[reflect.Type]int)
	s.id_type_map = make(map[int]reflect.Type)
	s.type_key_map = make(map[reflect.Type]string)
	s.key_type_map = make(map[string]reflect.Type)
	s.id = 1
	return s
}

type stringSerializer struct {
	Serializer
}

func (s *stringSerializer) Serialize(v interface{}) (data []byte, err error) {
	return []byte(v.(string)), nil
}

func (s *stringSerializer) Deserialize(data []byte) (v interface{}, err error) {
	return string(data), nil
}

func NewStringSerializer() Serializer {
	s := new(stringSerializer)
	s.Serializer = newEmptySerializer()
	return s
}

type jsonSerializer struct {
	*emptySerializer
}

func (s *jsonSerializer) Serialize(v interface{}) (data []byte, err error) {

	if !s.registered {
		panic("not registered any id or key")
	}

	t := reflect.TypeOf(v)
	if t == nil || t.Kind() != reflect.Ptr {
		panic("value type must be a pointer")
	}

	if s.byId {
		if id, ok := s.type_id_map[t]; ok {
			data, err = json.Marshal(v)
			if err != nil {
				log4g.Error(err)
				return
			}
			data = util.AddIntHeader(data, NetConfig.MessageIdSize, uint64(id), NetConfig.LittleEndian)
			if log4g.IsTraceEnabled() {
				log4g.Trace("serialized %v - %s", t, string(data))
			}
		} else {
			err = errors.New(fmt.Sprintf("%v is not registed by any id", t))
			log4g.Error(err)
		}
	} else {
		if key, ok := s.type_key_map[t]; ok {
			m := map[string]interface{}{key: v}
			data, err = json.Marshal(m)
			if err != nil {
				log4g.Error(err)
				return
			}
			if log4g.IsTraceEnabled() {
				log4g.Trace("serialized %v - %s", t, string(data))
			}
		} else {
			log4g.Panic("%v is not registered by any key", t)
		}
	}

	return
}

func (s *jsonSerializer) Deserialize(data []byte) (v interface{}, err error) {

	if !s.registered {
		panic("not registered any id or key")
	}

	if s.byId {
		id := int(util.GetIntHeader(data, NetConfig.MessageIdSize, NetConfig.LittleEndian))
		if t, ok := s.id_type_map[id]; ok {
			value := reflect.New(t.Elem()).Interface()
			err = json.Unmarshal(data[NetConfig.MessageIdSize:], value)
			if err != nil {
				log4g.Error(err)
			} else {
				v = value
				log4g.Trace("rcvd %v - %s", t, string(data))
			}
		} else {
			err = errors.New(fmt.Sprintf("id[%d] is not registered by any type", id))
			log4g.Error(err)
		}
	} else {
		var m_raw map[string]json.RawMessage
		err = json.Unmarshal(data, &m_raw)
		if err != nil {
			log4g.Error(err)
			return nil, err
		}
		if len(m_raw) == 0 {
			text := fmt.Sprintf("invalid json: %v", string(data))
			log4g.Error(text)
			err = errors.New(text)
			return
		}
		for key, raw := range m_raw {
			if t, ok := s.key_type_map[key]; ok {
				value := reflect.New(t.Elem()).Interface()
				err = json.Unmarshal(raw, value)
				if err != nil {
					log4g.Error(err)
				} else {
					v = value
					log4g.Trace("rcvd %v - %s", t, string(raw))
				}
			} else {
				err = errors.New(fmt.Sprintf("key '%s' is not registered by any type", key))
				log4g.Error(err)
			}
		}
	}
	return
}

func NewJsonSerializer() Serializer {
	s := new(jsonSerializer)
	s.emptySerializer = newEmptySerializer()
	return s
}

type protobufSerializer struct {
	*emptySerializer
}

func (s *protobufSerializer) Serialize(v interface{}) (data []byte, err error) {

	if !s.registered {
		log4g.Panic("not registered any id")
	}

	t := reflect.TypeOf(v)
	if t == nil || t.Kind() != reflect.Ptr {
		panic("value type must be a pointer")
	}

	if id, ok := s.type_id_map[t]; ok {
		data, err = proto.Marshal(v.(proto.Message))
		if err != nil {
			log4g.Error(err)
			return
		}
		data = util.AddIntHeader(data, NetConfig.MessageIdSize, uint64(id), NetConfig.LittleEndian)
		if log4g.IsDebugEnabled() {
			bytes, _ := json.Marshal(v)
			log4g.Debug("serialize %v - %v", t, string(bytes))
		}
	} else {
		err = errors.New(fmt.Sprintf("%v is not registed by any id", t))
	}
	return
}

func (s *protobufSerializer) Deserialize(data []byte) (v interface{}, err error) {
	if !s.registered {
		log4g.Panic("not registered any id")
	}
	id := int(util.GetIntHeader(data, NetConfig.MessageIdSize, NetConfig.LittleEndian))
	if t, ok := s.id_type_map[id]; ok {
		value := reflect.New(t.Elem()).Interface()
		err = proto.UnmarshalMerge(data[NetConfig.MessageIdSize:], value.(proto.Message))
		if err != nil {
			log4g.Error(err)
		} else {
			v = value
			if log4g.IsDebugEnabled() {
				bytes, _ := json.Marshal(v)
				log4g.Debug("deserialize %v - %v", t, string(bytes))
			}
		}
	} else {
		err = errors.New(fmt.Sprintf("id[%d] is not registered by any type", id))
		log4g.Error(err)
	}
	return
}

func NewProtobufSerializer() Serializer {
	s := new(protobufSerializer)
	s.emptySerializer = newEmptySerializer()
	return s
}
<commit_msg>change log level<commit_after>package net4g

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/carsonsx/log4g"
	"github.com/carsonsx/net4g/util"
	"github.com/golang/protobuf/proto"
	"reflect"
)

type Serializer interface {
	SetIdStartingValue(id int)
	RegisterById(t reflect.Type, id_at_most_one ...int) (id int, err error)
	RegisterByKey(t reflect.Type, key_at_most_one ...string) (key string, err error)
	Serialize(v interface{}) (data []byte, err error)
	Deserialize(data []byte) (v interface{}, err error)
	RangeId(f func(id int, t reflect.Type))
	RangeKey(f func(key string, t reflect.Type))
}

type emptySerializer struct {
	type_id_map  map[reflect.Type]int
	type_key_map map[reflect.Type]string
	id_type_map  map[int]reflect.Type
	key_type_map map[string]reflect.Type
	ids []int
	keys []string
	id           int
	registered   bool
	byId         bool
}

func (s *emptySerializer) SetIdStartingValue(id int) {
	s.id = id
}

func (s *emptySerializer) RegisterById(t reflect.Type, id_at_most_one ...int) (id int, err error) {

	if t == nil || t.Kind() != reflect.Ptr {
		panic("type must be a pointer")
	}

	if len(s.type_key_map) > 0 {
		panic("can not registered id and key in one serializer")
	}

	if len(id_at_most_one) > 1 {
		panic("only mapping one type with one id")
	}

	if _id, ok := s.type_id_map[t]; ok {
		text := fmt.Sprintf("%s has been registered by %d", t.String(), _id)
		log4g.Error(text)
		return 0, errors.New(text)
	}

	if len(id_at_most_one) == 1 {
		id = id_at_most_one[0]
	} else {
		id = s.id
	}

	s.type_id_map[t] = id
	s.id_type_map[id] = t
	s.ids = append(s.ids, id)

	s.byId = true
	s.registered = true

	s.id++

	return
}

func (s *emptySerializer) RegisterByKey(t reflect.Type, key_at_most_one ...string) (key string, err error) {

	if t == nil || t.Kind() != reflect.Ptr {
		panic("type must be a pointer")
	}

	if len(s.type_id_map) > 0 {
		panic("can not registered key and id in one serializer")
	}

	if len(key_at_most_one) > 1 {
		panic("only mapping one type with one key")
	}

	if _key, ok := s.type_key_map[t]; ok {
		text := fmt.Sprintf("%s has been registered by %s", t.Elem().Name(), _key)
		log4g.Error(text)
		err = errors.New(text)
		return
	}

	if len(key_at_most_one) == 1 {
		key = key_at_most_one[0]
	} else {
		key = t.String()
	}

	s.type_key_map[t] = key
	s.key_type_map[key] = t
	s.keys = append(s.keys, key)

	s.byId = false
	s.registered = true

	log4g.Info("%v register by key '%s'\n", t, key)

	return
}

func (s *emptySerializer) Serialize(v interface{}) (data []byte, err error) {
	return v.([]byte), nil
}

func (s *emptySerializer) Deserialize(data []byte) (v interface{}, err error) {
	return data, nil
}

func (s *emptySerializer) RangeId(f func(id int, t reflect.Type)) {
	for _, id := range s.ids {
		f(id, s.id_type_map[id])
	}
}

func (s *emptySerializer) RangeKey(f func(key string, t reflect.Type)) {
	for _, key := range s.keys {
		f(key, s.key_type_map[key])
	}
}

func NewEmptySerializer() Serializer {
	return newEmptySerializer()
}

func newEmptySerializer() *emptySerializer {
	s := new(emptySerializer)
	s.type_id_map = make(map[reflect.Type]int)
	s.id_type_map = make(map[int]reflect.Type)
	s.type_key_map = make(map[reflect.Type]string)
	s.key_type_map = make(map[string]reflect.Type)
	s.id = 1
	return s
}

type stringSerializer struct {
	Serializer
}

func (s *stringSerializer) Serialize(v interface{}) (data []byte, err error) {
	return []byte(v.(string)), nil
}

func (s *stringSerializer) Deserialize(data []byte) (v interface{}, err error) {
	return string(data), nil
}

func NewStringSerializer() Serializer {
	s := new(stringSerializer)
	s.Serializer = newEmptySerializer()
	return s
}

type jsonSerializer struct {
	*emptySerializer
}

func (s *jsonSerializer) Serialize(v interface{}) (data []byte, err error) {

	if !s.registered {
		panic("not registered any id or key")
	}

	t := reflect.TypeOf(v)
	if t == nil || t.Kind() != reflect.Ptr {
		panic("value type must be a pointer")
	}

	if s.byId {
		if id, ok := s.type_id_map[t]; ok {
			data, err = json.Marshal(v)
			if err != nil {
				log4g.Error(err)
				return
			}
			data = util.AddIntHeader(data, NetConfig.MessageIdSize, uint64(id), NetConfig.LittleEndian)
			if log4g.IsTraceEnabled() {
				log4g.Trace("serialized %v - %s", t, string(data))
			}
		} else {
			err = errors.New(fmt.Sprintf("%v is not registed by any id", t))
			log4g.Error(err)
		}
	} else {
		if key, ok := s.type_key_map[t]; ok {
			m := map[string]interface{}{key: v}
			data, err = json.Marshal(m)
			if err != nil {
				log4g.Error(err)
				return
			}
			if log4g.IsTraceEnabled() {
				log4g.Trace("serialized %v - %s", t, string(data))
			}
		} else {
			log4g.Panic("%v is not registered by any key", t)
		}
	}

	return
}

func (s *jsonSerializer) Deserialize(data []byte) (v interface{}, err error) {

	if !s.registered {
		panic("not registered any id or key")
	}

	if s.byId {
		id := int(util.GetIntHeader(data, NetConfig.MessageIdSize, NetConfig.LittleEndian))
		if t, ok := s.id_type_map[id]; ok {
			value := reflect.New(t.Elem()).Interface()
			err = json.Unmarshal(data[NetConfig.MessageIdSize:], value)
			if err != nil {
				log4g.Error(err)
			} else {
				v = value
				log4g.Trace("rcvd %v - %s", t, string(data))
			}
		} else {
			err = errors.New(fmt.Sprintf("id[%d] is not registered by any type", id))
			log4g.Error(err)
		}
	} else {
		var m_raw map[string]json.RawMessage
		err = json.Unmarshal(data, &m_raw)
		if err != nil {
			log4g.Error(err)
			return nil, err
		}
		if len(m_raw) == 0 {
			text := fmt.Sprintf("invalid json: %v", string(data))
			log4g.Error(text)
			err = errors.New(text)
			return
		}
		for key, raw := range m_raw {
			if t, ok := s.key_type_map[key]; ok {
				value := reflect.New(t.Elem()).Interface()
				err = json.Unmarshal(raw, value)
				if err != nil {
					log4g.Error(err)
				} else {
					v = value
					log4g.Trace("rcvd %v - %s", t, string(raw))
				}
			} else {
				err = errors.New(fmt.Sprintf("key '%s' is not registered by any type", key))
				log4g.Error(err)
			}
		}
	}
	return
}

func NewJsonSerializer() Serializer {
	s := new(jsonSerializer)
	s.emptySerializer = newEmptySerializer()
	return s
}

type protobufSerializer struct {
	*emptySerializer
}

func (s *protobufSerializer) Serialize(v interface{}) (data []byte, err error) {

	if !s.registered {
		log4g.Panic("not registered any id")
	}

	t := reflect.TypeOf(v)
	if t == nil || t.Kind() != reflect.Ptr {
		panic("value type must be a pointer")
	}

	if id, ok := s.type_id_map[t]; ok {
		data, err = proto.Marshal(v.(proto.Message))
		if err != nil {
			log4g.Error(err)
			return
		}
		data = util.AddIntHeader(data, NetConfig.MessageIdSize, uint64(id), NetConfig.LittleEndian)
		if log4g.IsDebugEnabled() {
			bytes, _ := json.Marshal(v)
			log4g.Trace("serialize %v - %v", t, string(bytes))
		}
	} else {
		err = errors.New(fmt.Sprintf("%v is not registed by any id", t))
	}
	return
}

func (s *protobufSerializer) Deserialize(data []byte) (v interface{}, err error) {
	if !s.registered {
		log4g.Panic("not registered any id")
	}
	id := int(util.GetIntHeader(data, NetConfig.MessageIdSize, NetConfig.LittleEndian))
	if t, ok := s.id_type_map[id]; ok {
		value := reflect.New(t.Elem()).Interface()
		err = proto.UnmarshalMerge(data[NetConfig.MessageIdSize:], value.(proto.Message))
		if err != nil {
			log4g.Error(err)
		} else {
			v = value
			if log4g.IsDebugEnabled() {
				bytes, _ := json.Marshal(v)
				log4g.Trace("deserialize %v - %v", t, string(bytes))
			}
		}
	} else {
		err = errors.New(fmt.Sprintf("id[%d] is not registered by any type", id))
		log4g.Error(err)
	}
	return
}

func NewProtobufSerializer() Serializer {
	s := new(protobufSerializer)
	s.emptySerializer = newEmptySerializer()
	return s
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gorilla/mux"
	"github.com/m-o-s-e-s/mgm/core"
	"github.com/m-o-s-e-s/mgm/core/database"
	"github.com/m-o-s-e-s/mgm/core/host"
	"github.com/m-o-s-e-s/mgm/core/job"
	"github.com/m-o-s-e-s/mgm/core/logger"
	"github.com/m-o-s-e-s/mgm/core/region"
	"github.com/m-o-s-e-s/mgm/core/session"
	"github.com/m-o-s-e-s/mgm/core/user"
	"github.com/m-o-s-e-s/mgm/email"
	"github.com/m-o-s-e-s/mgm/simian"
	"github.com/m-o-s-e-s/mgm/webClient"

	//"github.com/m-o-s-e-s/mgm/opensim"
	"flag"
	"net/http"

	"code.google.com/p/gcfg"
	"github.com/jcelliott/lumber"
)

type mgmConfig struct {
	MGM struct {
		SimianURL        string
		SessionSecret    string
		OpensimPort      string
		WebPort          string
		NodePort         string
		PublicHostname   string
		LocalFileStorage string
	}

	MySQL struct {
		Username string
		Password string
		Host     string
		Database string
	}

	Email email.EmailConfig
}

func main() {
	//instantiate our logger
	logger := logger.Wrap("MGM", lumber.NewConsoleLogger(lumber.DEBUG))

	cfgPtr := flag.String("config", "/opt/mgm/mgm.gcfg", "path to config file")

	flag.Parse()

	//read configuration file
	config := mgmConfig{}
	err := gcfg.ReadFileInto(&config, *cfgPtr)
	if err != nil {
		logger.Fatal("Error reading config file: ", err)
		return
	}

	//instantiate our email module
	mailer := email.NewClientMailer(config.Email, config.MGM.PublicHostname)

	//create our database connector
	db := database.NewDatabase(
		config.MySQL.Username,
		config.MySQL.Password,
		config.MySQL.Database,
		config.MySQL.Host,
	)
	err = db.TestConnection()
	if err != nil {
		logger.Error("Connecting to mysql: ", err)
		return
	}
	//create our simian connector
	sim, err := simian.NewConnector(config.MGM.SimianURL)
	if err != nil {
		logger.Error("Error instantiating Simian connection: ", err)
		return
	}

	//Hook up core processing...
	jMgr := job.NewManager(config.MGM.LocalFileStorage, db, logger)
	rMgr := region.NewManager(db, logger)
	nMgr, err := host.NewManager(config.MGM.NodePort, rMgr, db, logger)
	if err != nil {
		logger.Error("Error instantiating host manager: ", err)
		return
	}
	uMgr := user.NewManager(rMgr, nMgr, sim, db, logger)
	sessionListenerChan := make(chan core.UserSession, 64)

	_ = session.NewManager(sessionListenerChan, uMgr, jMgr, nMgr, rMgr, sim, logger)

	httpCon := webClient.NewHTTPConnector(config.MGM.SessionSecret, jMgr, sim, uMgr, mailer, logger)
	sockCon := webClient.NewWebsocketConnector(httpCon, sessionListenerChan, logger)

	r := mux.NewRouter()
	r.HandleFunc("/ws", sockCon.WebsocketHandler)
	r.HandleFunc("/auth", httpCon.ResumeHandler)
	r.HandleFunc("/auth/login", httpCon.LoginHandler)
	r.HandleFunc("/auth/logout", httpCon.LogoutHandler)
	r.HandleFunc("/auth/register", httpCon.RegisterHandler)
	r.HandleFunc("/auth/passwordToken", httpCon.PasswordTokenHandler)
	r.HandleFunc("/auth/passwordReset", httpCon.PasswordResetHandler)
	r.HandleFunc("/upload/{id}", httpCon.UploadHandler)

	//http.Handle("/", r)
	logger.Info("Listening for clients on :%v", config.MGM.WebPort)
	if err := http.ListenAndServe(":"+config.MGM.WebPort, nil); err != nil {
		logger.Fatal("ListenAndServe:", err)
	}
}
<commit_msg>changed config options from string to int, to force the parsing and validation of int values before it gets to the networking code<commit_after>package main

import (
	"strconv"

	"github.com/gorilla/mux"
	"github.com/m-o-s-e-s/mgm/core"
	"github.com/m-o-s-e-s/mgm/core/database"
	"github.com/m-o-s-e-s/mgm/core/host"
	"github.com/m-o-s-e-s/mgm/core/job"
	"github.com/m-o-s-e-s/mgm/core/logger"
	"github.com/m-o-s-e-s/mgm/core/region"
	"github.com/m-o-s-e-s/mgm/core/session"
	"github.com/m-o-s-e-s/mgm/core/user"
	"github.com/m-o-s-e-s/mgm/email"
	"github.com/m-o-s-e-s/mgm/simian"
	"github.com/m-o-s-e-s/mgm/webClient"

	//"github.com/m-o-s-e-s/mgm/opensim"
	"flag"
	"net/http"

	"code.google.com/p/gcfg"
	"github.com/jcelliott/lumber"
)

type mgmConfig struct {
	MGM struct {
		SimianURL        string
		SessionSecret    string
		OpensimPort      string
		WebPort          int
		NodePort         int
		PublicHostname   string
		LocalFileStorage string
	}

	MySQL struct {
		Username string
		Password string
		Host     string
		Database string
	}

	Email email.EmailConfig
}

func main() {
	//instantiate our logger
	logger := logger.Wrap("MGM", lumber.NewConsoleLogger(lumber.DEBUG))

	cfgPtr := flag.String("config", "/opt/mgm/mgm.gcfg", "path to config file")

	flag.Parse()

	//read configuration file
	config := mgmConfig{}
	err := gcfg.ReadFileInto(&config, *cfgPtr)
	if err != nil {
		logger.Fatal("Error reading config file: ", err)
		return
	}

	//instantiate our email module
	mailer := email.NewClientMailer(config.Email, config.MGM.PublicHostname)

	//create our database connector
	db := database.NewDatabase(
		config.MySQL.Username,
		config.MySQL.Password,
		config.MySQL.Database,
		config.MySQL.Host,
	)
	err = db.TestConnection()
	if err != nil {
		logger.Error("Connecting to mysql: ", err)
		return
	}
	//create our simian connector
	sim, err := simian.NewConnector(config.MGM.SimianURL)
	if err != nil {
		logger.Error("Error instantiating Simian connection: ", err)
		return
	}

	//Hook up core processing...
	jMgr := job.NewManager(config.MGM.LocalFileStorage, db, logger)
	rMgr := region.NewManager(db, logger)
	nMgr, err := host.NewManager(config.MGM.NodePort, rMgr, db, logger)
	if err != nil {
		logger.Error("Error instantiating host manager: ", err)
		return
	}
	uMgr := user.NewManager(rMgr, nMgr, sim, db, logger)
	sessionListenerChan := make(chan core.UserSession, 64)

	_ = session.NewManager(sessionListenerChan, uMgr, jMgr, nMgr, rMgr, sim, logger)

	httpCon := webClient.NewHTTPConnector(config.MGM.SessionSecret, jMgr, sim, uMgr, mailer, logger)
	sockCon := webClient.NewWebsocketConnector(httpCon, sessionListenerChan, logger)

	r := mux.NewRouter()
	r.HandleFunc("/ws", sockCon.WebsocketHandler)
	r.HandleFunc("/auth", httpCon.ResumeHandler)
	r.HandleFunc("/auth/login", httpCon.LoginHandler)
	r.HandleFunc("/auth/logout", httpCon.LogoutHandler)
	r.HandleFunc("/auth/register", httpCon.RegisterHandler)
	r.HandleFunc("/auth/passwordToken", httpCon.PasswordTokenHandler)
	r.HandleFunc("/auth/passwordReset", httpCon.PasswordResetHandler)
	r.HandleFunc("/upload/{id}", httpCon.UploadHandler)

	http.Handle("/", r)
	logger.Info("Listening for clients on :%d", config.MGM.WebPort)
	if err := http.ListenAndServe(":"+strconv.Itoa(config.MGM.WebPort), nil); err != nil {
		logger.Fatal("ListenAndServe:", err)
	}
}
<|endoftext|>


<commit_before>package server

import (
    "log"
    "time"
    "strconv"
    "net/http"
    "github.com/gorilla/websocket"
    "github.com/itsankoff/gotcha/util"
)

type WebSocketServer struct {
    upgrader            websocket.Upgrader
    connections         map[*util.User]*websocket.Conn
    connected           chan<- *util.User
    disconnected        chan<- *util.User
}

func NewWebSocket() WebSocketServer {
    var upgrader = websocket.Upgrader{
        ReadBufferSize: 1024,
        WriteBufferSize: 1024,
        CheckOrigin: func(r *http.Request) bool { return true },
    }

    return WebSocketServer{
        upgrader:upgrader,
        connections: make(map[*util.User]*websocket.Conn),
    }
}

func (wss *WebSocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    conn, err := wss.upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return
    }

    wss.addConnection(conn)
}

func (wss *WebSocketServer) addConnection(conn *websocket.Conn) {
    now := time.Now().Unix()
    id := strconv.FormatInt(now, 10)
    user := &util.User{
        Id: id,
        In: make(chan util.Message),
        Out: make(chan util.Message),
    }

    wss.connections[user] = conn
    go wss.inputHandler(user, conn)
    go wss.outputHandler(user, conn)
    wss.connected <- user
    log.Println("Add connections", user.Id)
}

func (wss *WebSocketServer) removeConnection(conn *websocket.Conn) {
    for user, c := range wss.connections {
        if c == conn {
            if wss.disconnected != nil {
                wss.disconnected <- user
            }

            close(user.In)
            close(user.Out)
            conn.Close()
            delete(wss.connections, user)

            log.Println("Remove connection", user.Id)
            break
        }
    }
}

func (wss *WebSocketServer) inputHandler(user *util.User, conn *websocket.Conn) {
    log.Println("Start websocket input handler for", user.Id)
    for {
        msgType, msg, err := conn.ReadMessage()
        if err != nil {
            log.Println("Connection read error", err.Error())
            wss.removeConnection(conn)
            return
        }

        message, err := wss.decodeMessage(user, msg, msgType)
        if err != nil {
            log.Println("Failed to decode message", msgType, msg)
            wss.removeConnection(conn)
            return
        }

        user.In <- message
    }
}

func (wss *WebSocketServer) outputHandler(user *util.User, conn *websocket.Conn) {
    log.Println("Start websocket output handler for", user.Id)
    select {
    case msg := <-user.Out:
        message, msgType := wss.encodeMessage(user, msg)
        if err := conn.WriteMessage(msgType, message); err != nil {
            log.Println("Connection write error", err.Error())
            wss.removeConnection(conn)
            return
        }
    }
}

func (wss *WebSocketServer) Start(host string, done <-chan interface{}) {
    subPath := "/websocket"
    http.Handle(subPath, wss)
    defer func() {
        http.Handle(subPath, nil)
    }()

    log.Println("Listen on:", host + subPath)
    log.Fatal(http.ListenAndServe(host, nil))
}

func (wss *WebSocketServer) OnUserConnected(handler chan<- *util.User) {
    wss.connected = handler
}

func (wss *WebSocketServer) OnUserDisconnected(handler chan<- *util.User) {
    wss.disconnected = handler
}

func (wss WebSocketServer) encodeMessage(u *util.User,
                                         msg util.Message) ([]byte, int) {
    return msg.Raw(), int(msg.DataType())
}

func (wss WebSocketServer) decodeMessage(u *util.User,
                                         data []byte,
                                         dataType int) (util.Message, error) {
    message := util.NewMessage(u, u, "message", util.DataType(dataType), data)
    return message, nil
}
<commit_msg>Add TODO for websocket decode method<commit_after>package server

import (
    "log"
    "time"
    "strconv"
    "net/http"
    "github.com/gorilla/websocket"
    "github.com/itsankoff/gotcha/util"
)

type WebSocketServer struct {
    upgrader            websocket.Upgrader
    connections         map[*util.User]*websocket.Conn
    connected           chan<- *util.User
    disconnected        chan<- *util.User
}

func NewWebSocket() WebSocketServer {
    var upgrader = websocket.Upgrader{
        ReadBufferSize: 1024,
        WriteBufferSize: 1024,
        CheckOrigin: func(r *http.Request) bool { return true },
    }

    return WebSocketServer{
        upgrader:upgrader,
        connections: make(map[*util.User]*websocket.Conn),
    }
}

func (wss *WebSocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    conn, err := wss.upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return
    }

    wss.addConnection(conn)
}

func (wss *WebSocketServer) addConnection(conn *websocket.Conn) {
    now := time.Now().Unix()
    id := strconv.FormatInt(now, 10)
    user := &util.User{
        Id: id,
        In: make(chan util.Message),
        Out: make(chan util.Message),
    }

    wss.connections[user] = conn
    go wss.inputHandler(user, conn)
    go wss.outputHandler(user, conn)
    wss.connected <- user
    log.Println("Add connections", user.Id)
}

func (wss *WebSocketServer) removeConnection(conn *websocket.Conn) {
    for user, c := range wss.connections {
        if c == conn {
            if wss.disconnected != nil {
                wss.disconnected <- user
            }

            close(user.In)
            close(user.Out)
            conn.Close()
            delete(wss.connections, user)

            log.Println("Remove connection", user.Id)
            break
        }
    }
}

func (wss *WebSocketServer) inputHandler(user *util.User, conn *websocket.Conn) {
    log.Println("Start websocket input handler for", user.Id)
    for {
        msgType, msg, err := conn.ReadMessage()
        if err != nil {
            log.Println("Connection read error", err.Error())
            wss.removeConnection(conn)
            return
        }

        message, err := wss.decodeMessage(user, msg, msgType)
        if err != nil {
            log.Println("Failed to decode message", msgType, msg)
            wss.removeConnection(conn)
            return
        }

        user.In <- message
    }
}

func (wss *WebSocketServer) outputHandler(user *util.User, conn *websocket.Conn) {
    log.Println("Start websocket output handler for", user.Id)
    select {
    case msg := <-user.Out:
        message, msgType := wss.encodeMessage(user, msg)
        if err := conn.WriteMessage(msgType, message); err != nil {
            log.Println("Connection write error", err.Error())
            wss.removeConnection(conn)
            return
        }
    }
}

func (wss *WebSocketServer) Start(host string, done <-chan interface{}) {
    subPath := "/websocket"
    http.Handle(subPath, wss)
    defer func() {
        http.Handle(subPath, nil)
    }()

    log.Println("Listen on:", host + subPath)
    log.Fatal(http.ListenAndServe(host, nil))
}

func (wss *WebSocketServer) OnUserConnected(handler chan<- *util.User) {
    wss.connected = handler
}

func (wss *WebSocketServer) OnUserDisconnected(handler chan<- *util.User) {
    wss.disconnected = handler
}

func (wss WebSocketServer) encodeMessage(u *util.User,
                                         msg util.Message) ([]byte, int) {
    return msg.Raw(), int(msg.DataType())
}

func (wss WebSocketServer) decodeMessage(u *util.User,
                                         data []byte,
                                         dataType int) (util.Message, error) {
    // TODO: Parse message data
    //       And find who is the destination
    message := util.NewMessage(u, u, "message", util.DataType(dataType), data)
    return message, nil
}
<|endoftext|>


<commit_before>/* plumber: a deployment pipeline template
 * Copyright (C) 2014 Recruit Technologies Co., Ltd. and contributors
 * (see CONTRIBUTORS.md)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package config

import (
	"testing"

	"github.com/recruit-tech/plumber/stages"
)

func TestParseFromFile(t *testing.T) {
	configData := ReadConfig("../tests/fixtures/pipeline.yml")
	actual := (*Parse(configData)).Stages.Front().Value.(*stages.CommandStage).Command

	expected := "echo \"hello, world\""
	if expected != actual {
		t.Errorf("got %v\nwant %v", actual, expected)
	}
}

func TestParseJustHeading(t *testing.T) {
	configData := ReadConfigBytes([]byte("pipeline:"))
	actual := Parse(configData)

	expected := 0
	if expected != actual.Size() {
		t.Errorf("got %v\nwant %v", actual, expected)
	}
}

func TestParseVoid(t *testing.T) {
	configData := ReadConfigBytes([]byte(""))
	actual := Parse(configData).Size()

	expected := 0
	if expected != actual {
		t.Errorf("got %v\nwant %v", actual, expected)
	}
}

func TestParseConfWithChildren(t *testing.T) {
	configData := ReadConfigBytes([]byte(`pipeline:
    - stage_name: command_stage_1
      stage_type: command
      command: echo "hello, world"
      run_after:
          -  stage_name: command_stage_2_group_1
             stage_type: command
             command: echo "hello, world, command_stage_2_group_1"
          -  stage_name: command_stage_3_group_1
             stage_type: command
             command: echo "hello, world, command_stage_3_group_1"
`))
	result := Parse(configData)

	expectedPipelineSize := 1
	if expectedPipelineSize != result.Size() {
		t.Errorf("got %v\nwant %v", result.Size(), expectedPipelineSize)
	}

	childStages := result.Stages.Front().Value.(stages.Stage).GetChildStages()

	expectedChildStageSize := 2
	if expectedChildStageSize != childStages.Len() {
		t.Errorf("got %v\nwant %v", childStages.Len(), expectedChildStageSize)
	}

}
<commit_msg>Apply assert function to parser tests<commit_after>/* plumber: a deployment pipeline template
 * Copyright (C) 2014 Recruit Technologies Co., Ltd. and contributors
 * (see CONTRIBUTORS.md)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package config

import (
	"testing"

	"github.com/recruit-tech/plumber/stages"
	"github.com/stretchr/testify/assert"
)

func TestParseFromFile(t *testing.T) {
	configData := ReadConfig("../tests/fixtures/pipeline.yml")
	actual := (*Parse(configData)).Stages.Front().Value.(*stages.CommandStage).Command
	assert.Equal(t, "echo \"hello, world\"", actual)
}

func TestParseJustHeading(t *testing.T) {
	configData := ReadConfigBytes([]byte("pipeline:"))
	actual := Parse(configData).Size()
	assert.Equal(t, 0, actual)
}

func TestParseVoid(t *testing.T) {
	configData := ReadConfigBytes([]byte(""))
	actual := Parse(configData).Size()
	assert.Equal(t, 0, actual)
}

func TestParseConfWithChildren(t *testing.T) {
	configData := ReadConfigBytes([]byte(`pipeline:
    - stage_name: command_stage_1
      stage_type: command
      command: echo "hello, world"
      run_after:
          -  stage_name: command_stage_2_group_1
             stage_type: command
             command: echo "hello, world, command_stage_2_group_1"
          -  stage_name: command_stage_3_group_1
             stage_type: command
             command: echo "hello, world, command_stage_3_group_1"`))
	result := Parse(configData)
	assert.Equal(t, 1, result.Size())

	childStages := result.Stages.Front().Value.(stages.Stage).GetChildStages()
	assert.Equal(t, 2, childStages.Len())
}
<|endoftext|>


<commit_before>package reseed

import (
	"bytes"
	"crypto/tls"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"

	"gopkg.in/throttled/throttled.v2"
	"gopkg.in/throttled/throttled.v2/store"
	"github.com/gorilla/handlers"
	"github.com/justinas/alice"
)

const (
	I2P_USER_AGENT = "Wget/1.11.4"
)

type Server struct {
	*http.Server
	Reseeder  Reseeder
	Blacklist *Blacklist
}

func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	return srv.Serve(newBlacklistListener(ln, srv.Blacklist))
}

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{}
	if srv.TLSConfig != nil {
		*config = *srv.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(newBlacklistListener(ln, srv.Blacklist), config)
	return srv.Serve(tlsListener)
}

func NewServer(prefix string, trustProxy bool) *Server {
	config := &tls.Config{
		MinVersion:               tls.VersionTLS10,
		PreferServerCipherSuites: true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
			tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
		},
	}
	h := &http.Server{TLSConfig: config}
	server := Server{Server: h, Reseeder: nil}

	th := throttled.RateLimit(throttled.PerDay(4), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(200000))

	middlewareChain := alice.New()
	if trustProxy {
		middlewareChain = middlewareChain.Append(proxiedMiddleware)
	}

	errorHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		if _, err := w.Write(nil); nil != err {
			log.Println(err)
		}
	})

	mux := http.NewServeMux()
	mux.Handle("/", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware).Then(errorHandler))
	mux.Handle(prefix+"/i2pseeds.su3", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware, verifyMiddleware, th.Throttle).Then(http.HandlerFunc(server.reseedHandler)))
	server.Handler = mux

	return &server
}

func (s *Server) reseedHandler(w http.ResponseWriter, r *http.Request) {
	var peer Peer
	if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
		peer = Peer(ip)
	} else {
		peer = Peer(r.RemoteAddr)
	}

	su3Bytes, err := s.Reseeder.PeerSu3Bytes(peer)
	if nil != err {
		http.Error(w, "500 Unable to serve su3", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Disposition", "attachment; filename=i2pseeds.su3")
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Length", strconv.FormatInt(int64(len(su3Bytes)), 10))

	io.Copy(w, bytes.NewReader(su3Bytes))
}

func disableKeepAliveMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Connection", "close")
		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func loggingMiddleware(next http.Handler) http.Handler {
	return handlers.CombinedLoggingHandler(os.Stdout, next)
}

func verifyMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if I2P_USER_AGENT != r.UserAgent() {
			http.Error(w, "403 Forbidden", http.StatusForbidden)
			return
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func proxiedMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if prior, ok := r.Header["X-Forwarded-For"]; ok {
			r.RemoteAddr = prior[0]
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<commit_msg>Update server.go<commit_after>package reseed

import (
	"bytes"
	"crypto/tls"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"

	"gopkg.in/throttled/throttled.v2"
	"gopkg.in/throttled/throttled.v2/store"
	"github.com/gorilla/handlers"
	"github.com/justinas/alice"
)

const (
	I2P_USER_AGENT = "Wget/1.11.4"
)

type Server struct {
	*http.Server
	Reseeder  Reseeder
	Blacklist *Blacklist
}

func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	return srv.Serve(newBlacklistListener(ln, srv.Blacklist))
}

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{}
	if srv.TLSConfig != nil {
		*config = *srv.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(newBlacklistListener(ln, srv.Blacklist), config)
	return srv.Serve(tlsListener)
}

func NewServer(prefix string, trustProxy bool) *Server {
	config := &tls.Config{
		MinVersion:               tls.VersionTLS10,
		PreferServerCipherSuites: true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
		},
	}
	h := &http.Server{TLSConfig: config}
	server := Server{Server: h, Reseeder: nil}

	th := throttled.RateLimit(throttled.PerDay(4), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(200000))

	middlewareChain := alice.New()
	if trustProxy {
		middlewareChain = middlewareChain.Append(proxiedMiddleware)
	}

	errorHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		if _, err := w.Write(nil); nil != err {
			log.Println(err)
		}
	})

	mux := http.NewServeMux()
	mux.Handle("/", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware).Then(errorHandler))
	mux.Handle(prefix+"/i2pseeds.su3", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware, verifyMiddleware, th.Throttle).Then(http.HandlerFunc(server.reseedHandler)))
	server.Handler = mux

	return &server
}

func (s *Server) reseedHandler(w http.ResponseWriter, r *http.Request) {
	var peer Peer
	if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
		peer = Peer(ip)
	} else {
		peer = Peer(r.RemoteAddr)
	}

	su3Bytes, err := s.Reseeder.PeerSu3Bytes(peer)
	if nil != err {
		http.Error(w, "500 Unable to serve su3", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Disposition", "attachment; filename=i2pseeds.su3")
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Length", strconv.FormatInt(int64(len(su3Bytes)), 10))

	io.Copy(w, bytes.NewReader(su3Bytes))
}

func disableKeepAliveMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Connection", "close")
		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func loggingMiddleware(next http.Handler) http.Handler {
	return handlers.CombinedLoggingHandler(os.Stdout, next)
}

func verifyMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if I2P_USER_AGENT != r.UserAgent() {
			http.Error(w, "403 Forbidden", http.StatusForbidden)
			return
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func proxiedMiddleware(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if prior, ok := r.Header["X-Forwarded-For"]; ok {
			r.RemoteAddr = prior[0]
		}

		next.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<|endoftext|>


<commit_before>package httpexpect

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestResponseFailed(t *testing.T) {
	checker := newMockChecker(t)

	checker.Fail("fail")

	resp := NewResponse(checker, &http.Response{})

	resp.Status(123)
	resp.Headers(nil)
	resp.Header("foo", "bar")
	resp.NoContent()
	resp.JSON()
}

func TestResponseHeaders(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"First-Header":  []string{"foo"},
		"Second-Header": []string{"bar"},
	}

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       nil,
	}

	resp := NewResponse(checker, httpResp)
	checker.AssertSuccess(t)
	checker.Reset()

	assert.Equal(t, httpResp, resp.Raw())

	resp.Status(http.StatusOK)
	checker.AssertSuccess(t)
	checker.Reset()

	resp.Status(http.StatusNotFound)
	checker.AssertFailed(t)
	checker.Reset()

	resp.Headers(headers)
	checker.AssertSuccess(t)
	checker.Reset()

	partialHeaders := make(map[string][]string)
	partialHeaders["Content-Type"] = headers["Content-Type"]

	resp.Headers(partialHeaders)
	checker.AssertFailed(t)
	checker.Reset()

	for k, v := range headers {
		resp.Header(k, v[0])
		checker.AssertSuccess(t)
		checker.Reset()
	}

	resp.Header("Bad-Header", "noValue")
	checker.AssertFailed(t)
	checker.Reset()
}

func TestResponseNoContent(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{""},
	}

	body := ""

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString(body)},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertSuccess(t)
	checker.Reset()

	resp.JSON()
	checker.AssertFailed(t)
	checker.Reset()
}

func TestResponseJson(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{"application/json; charset=utf-8"},
	}

	body := `{"key": "value"}`

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString(body)},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertFailed(t)
	checker.Reset()

	resp.JSON()
	checker.AssertSuccess(t)
	checker.Reset()

	assert.Equal(t,
		map[string]interface{}{"key": "value"}, resp.JSON().Object().Raw())

	assert.False(t, resp.checker == resp.JSON().checker)
}

func TestResponseJsonEncodingEmpty(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{"application/json"},
	}

	body := `{"key": "value"}`

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString(body)},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertFailed(t)
	checker.Reset()

	resp.JSON()
	checker.AssertSuccess(t)
	checker.Reset()

	assert.Equal(t,
		map[string]interface{}{"key": "value"}, resp.JSON().Object().Raw())
}

func TestResponseJsonEncodingBad(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{"application/json; charset=bad"},
	}

	body := `{"key": "value"}`

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString(body)},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertFailed(t)
	checker.Reset()

	resp.JSON()
	checker.AssertFailed(t)
	checker.Reset()

	assert.Equal(t, nil, resp.JSON().Raw())
}
<commit_msg>Add test for nil response body<commit_after>package httpexpect

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestResponseFailed(t *testing.T) {
	checker := newMockChecker(t)

	checker.Fail("fail")

	resp := NewResponse(checker, &http.Response{})

	resp.Status(123)
	resp.Headers(nil)
	resp.Header("foo", "bar")
	resp.NoContent()
	resp.JSON()
}

func TestResponseHeaders(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"First-Header":  []string{"foo"},
		"Second-Header": []string{"bar"},
	}

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       nil,
	}

	resp := NewResponse(checker, httpResp)
	checker.AssertSuccess(t)
	checker.Reset()

	assert.Equal(t, httpResp, resp.Raw())

	resp.Status(http.StatusOK)
	checker.AssertSuccess(t)
	checker.Reset()

	resp.Status(http.StatusNotFound)
	checker.AssertFailed(t)
	checker.Reset()

	resp.Headers(headers)
	checker.AssertSuccess(t)
	checker.Reset()

	partialHeaders := make(map[string][]string)
	partialHeaders["Content-Type"] = headers["Content-Type"]

	resp.Headers(partialHeaders)
	checker.AssertFailed(t)
	checker.Reset()

	for k, v := range headers {
		resp.Header(k, v[0])
		checker.AssertSuccess(t)
		checker.Reset()
	}

	resp.Header("Bad-Header", "noValue")
	checker.AssertFailed(t)
	checker.Reset()
}

func TestResponseNoContentEmpty(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{""},
	}

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString("")},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertSuccess(t)
	checker.Reset()

	resp.JSON()
	checker.AssertFailed(t)
	checker.Reset()
}

func TestResponseNoContentNil(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{""},
	}

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       nil,
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertSuccess(t)
	checker.Reset()

	resp.JSON()
	checker.AssertFailed(t)
	checker.Reset()
}

func TestResponseJson(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{"application/json; charset=utf-8"},
	}

	body := `{"key": "value"}`

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString(body)},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertFailed(t)
	checker.Reset()

	resp.JSON()
	checker.AssertSuccess(t)
	checker.Reset()

	assert.Equal(t,
		map[string]interface{}{"key": "value"}, resp.JSON().Object().Raw())

	assert.False(t, resp.checker == resp.JSON().checker)
}

func TestResponseJsonEncodingEmpty(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{"application/json"},
	}

	body := `{"key": "value"}`

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString(body)},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertFailed(t)
	checker.Reset()

	resp.JSON()
	checker.AssertSuccess(t)
	checker.Reset()

	assert.Equal(t,
		map[string]interface{}{"key": "value"}, resp.JSON().Object().Raw())
}

func TestResponseJsonEncodingBad(t *testing.T) {
	checker := newMockChecker(t)

	headers := map[string][]string{
		"Content-Type": []string{"application/json; charset=bad"},
	}

	body := `{"key": "value"}`

	httpResp := &http.Response{
		StatusCode: http.StatusOK,
		Header:     http.Header(headers),
		Body:       closingBuffer{bytes.NewBufferString(body)},
	}

	resp := NewResponse(checker, httpResp)

	resp.NoContent()
	checker.AssertFailed(t)
	checker.Reset()

	resp.JSON()
	checker.AssertFailed(t)
	checker.Reset()

	assert.Equal(t, nil, resp.JSON().Raw())
}
<|endoftext|>


<commit_before>// Copyright 2013 The hgo 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 revlog

import (
	"errors"
)

type RevisionSpec interface {
	Lookup(*Index) (*Rec, error)
}

type FileRevSpec int

func (n FileRevSpec) Lookup(i *Index) (r *Rec, err error) {
	if n < 0 {
		n += FileRevSpec(len(i.index))
	}
	if n < 0 || int(n) >= len(i.index) {
		err = ErrRevisionNotFound
	} else {
		r = i.Record(int(n))
	}
	return
}

// A LinkRevSpec can be used to find a file revision
// that was present at a certain changelog revision, by
// examining the filelog records' linkrev values.
// The behaviour of the Lookup method can be configured
// by setting FindPresent to a user implemented function.
type LinkRevSpec struct {
	Rev int

	// FindPresent should examine maybeAncestors' Linkrev values to
	// find a changelog record that is an ancestor of Rev. It also has to
	// make sure that the file actually existed in the revision specified
	// by Rev.
	// If FindPresent is nil (the default), Lookup will -- in case of multiple
	// matching branches -- return the last visited record, or a Null record
	// if no revision matches at all.
	FindPresent func(maybeAncestors []*Rec) (index int, err error)
}

func NewLinkRevSpec(rev int) *LinkRevSpec {
	return &LinkRevSpec{Rev: rev}
}

func (l LinkRevSpec) Lookup(i *Index) (match *Rec, err error) {
	// While doing the range loop, vr keeps track of the
	// last visited records of all branches.
	var vr []*Rec
	branch := 0

	for j := range i.index {
		if li := int(i.index[j].Linkrev); li == int(l.Rev) {
			// exact match
			match = i.Record(j)
			return
		} else if li > int(l.Rev) {
			break
		}

		r := i.Record(j)
		if vr == nil {
			vr = append(vr, r)
			continue
		}

		// If Parent2 points to one of the last visited
		// records of all visited branches, store the
		// entries index into p2branch.
		p2branch := -1
		if r.Parent2Present() {
			p := r.Parent2().FileRev()
			for k, r := range vr {
				if r == nil {
					continue
				}
				if r.FileRev() == p {
					p2branch = k
				}
			}
		}

		// If the parent of the current record is not a member of the
		// last visited branch, look if either parent or parent2 is one of
		// the other last visited records. Else, create a new branch.
		if p := r.Parent().FileRev(); vr[branch].FileRev() != p {
			for k, r := range vr {
				if r == nil {
					continue
				}
				if r.FileRev() == p {
					branch = k
					goto found
				}
			}
			if p2branch != -1 {
				branch = p2branch
			} else {
				branch = len(vr)
				vr = append(vr, r)
			}
		found:
		}
		vr[branch] = r
		if p2branch != -1 && p2branch != branch {
			vr[p2branch] = nil
		}
	}

	// Sort out nil entries.
	w := 0
	for i := range vr {
		if vr[i] != nil {
			vr[w] = vr[i]
			w++
		}
	}
	vr = vr[:w]

	switch len(vr) {
	case 0:
		if l.FindPresent != nil {
			match = nil
			err = ErrRevisionNotFound
			return
		}
		match = i.Null()
	default:
		if l.FindPresent != nil {
			// make sure the most recent updated entry comes first
			if branch != 0 {
				vr[0], vr[branch] = vr[branch], vr[0]
			}
			branch, err = l.FindPresent(vr)
			if err == nil {
				match = vr[branch]
			}
			return
		}
		fallthrough

	case 1:
		match = vr[branch]
		if match.IsLeaf() {
			if l.FindPresent != nil {
				_, err = l.FindPresent([]*Rec{match})
			}
		}
	}

	return
}

type NodeIdRevSpec string

func (hash NodeIdRevSpec) Lookup(rv *Index) (r *Rec, err error) {
	var i = -1
	var found bool

	wantid, err := NewId(string(hash))
	if err != nil {
		return
	}
	for j := range rv.index {
		nodeid := rv.NewNodeId(rv.index[j].NodeId[:])
		if len(wantid) <= len(nodeid) {
			if wantid.Eq(nodeid[:len(wantid)]) {
				if found {
					err = ErrRevisionAmbiguous
				}
				found = true
				i = j
			}
		}
	}
	if i == -1 {
		err = ErrRevNotFound
	} else {
		r = rv.Record(i)
	}
	return
}

type TipRevSpec struct{}

func (TipRevSpec) String() string {
	return "tip"
}

func (TipRevSpec) Lookup(i *Index) (r *Rec, err error) {
	if n := len(i.index); n == 0 {
		err = ErrRevisionNotFound
	} else {
		r = i.Record(n - 1)
	}
	return
}

type NullRevSpec struct{}

func (NullRevSpec) String() string {
	return "null"
}

func (NullRevSpec) Lookup(i *Index) (r *Rec, err error) {
	r = &i.null
	return
}

var ErrRevisionNotFound = errors.New("hg/revlog: revision not found")
var ErrRevisionAmbiguous = errors.New("hg/revlog: ambiguous revision spec")
<commit_msg>properly update branch variable after removing nils from vr<commit_after>// Copyright 2013 The hgo 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 revlog

import "errors"

type RevisionSpec interface {
	Lookup(*Index) (*Rec, error)
}

type FileRevSpec int

func (n FileRevSpec) Lookup(i *Index) (r *Rec, err error) {
	if n < 0 {
		n += FileRevSpec(len(i.index))
	}
	if n < 0 || int(n) >= len(i.index) {
		err = ErrRevisionNotFound
	} else {
		r = i.Record(int(n))
	}
	return
}

// A LinkRevSpec can be used to find a file revision
// that was present at a certain changelog revision, by
// examining the filelog records' linkrev values.
// The behaviour of the Lookup method can be configured
// by setting FindPresent to a user implemented function.
type LinkRevSpec struct {
	Rev int

	// FindPresent should examine maybeAncestors' Linkrev values to
	// find a changelog record that is an ancestor of Rev. It also has to
	// make sure that the file actually existed in the revision specified
	// by Rev.
	// If FindPresent is nil (the default), Lookup will -- in case of multiple
	// matching branches -- return the last visited record, or a Null record
	// if no revision matches at all.
	FindPresent func(maybeAncestors []*Rec) (index int, err error)
}

func NewLinkRevSpec(rev int) *LinkRevSpec {
	return &LinkRevSpec{Rev: rev}
}

func (l LinkRevSpec) Lookup(i *Index) (match *Rec, err error) {
	// While doing the range loop, vr keeps track of the
	// last visited records of all branches.
	var vr []*Rec
	branch := 0

	for j := range i.index {
		if li := int(i.index[j].Linkrev); li == int(l.Rev) {
			// exact match
			match = i.Record(j)
			return
		} else if li > int(l.Rev) {
			break
		}

		r := i.Record(j)
		if vr == nil {
			vr = append(vr, r)
			continue
		}

		// If Parent2 points to one of the last visited
		// records of all visited branches, store the
		// entries index into p2branch.
		p2branch := -1
		if r.Parent2Present() {
			p := r.Parent2().FileRev()
			for k, r := range vr {
				if r == nil {
					continue
				}
				if r.FileRev() == p {
					p2branch = k
				}
			}
		}

		// If the parent of the current record is not a member of the
		// last visited branch, look if either parent or parent2 is one of
		// the other last visited records. Else, create a new branch.
		if p := r.Parent().FileRev(); vr[branch].FileRev() != p {
			for k, r := range vr {
				if r == nil {
					continue
				}
				if r.FileRev() == p {
					branch = k
					goto found
				}
			}
			if p2branch != -1 {
				branch = p2branch
			} else {
				branch = len(vr)
				vr = append(vr, r)
			}
		found:
		}
		vr[branch] = r
		if p2branch != -1 && p2branch != branch {
			vr[p2branch] = nil
		}
	}

	// Sort out nil entries.
	w := 0
	numNilsBeforeBranch := 0
	for i := range vr {
		if vr[i] != nil {
			vr[w] = vr[i]
			w++
		} else if i < branch {
			numNilsBeforeBranch++
		}
	}
	vr = vr[:w]
	branch -= numNilsBeforeBranch

	switch len(vr) {
	case 0:
		if l.FindPresent != nil {
			match = nil
			err = ErrRevisionNotFound
			return
		}
		match = i.Null()
	default:
		if l.FindPresent != nil {
			// make sure the most recent updated entry comes first
			if branch != 0 {
				vr[0], vr[branch] = vr[branch], vr[0]
			}
			branch, err = l.FindPresent(vr)
			if err == nil {
				match = vr[branch]
			}
			return
		}
		fallthrough

	case 1:
		match = vr[branch]
		if match.IsLeaf() {
			if l.FindPresent != nil {
				_, err = l.FindPresent([]*Rec{match})
			}
		}
	}

	return
}

type NodeIdRevSpec string

func (hash NodeIdRevSpec) Lookup(rv *Index) (r *Rec, err error) {
	var i = -1
	var found bool

	wantid, err := NewId(string(hash))
	if err != nil {
		return
	}
	for j := range rv.index {
		nodeid := rv.NewNodeId(rv.index[j].NodeId[:])
		if len(wantid) <= len(nodeid) {
			if wantid.Eq(nodeid[:len(wantid)]) {
				if found {
					err = ErrRevisionAmbiguous
				}
				found = true
				i = j
			}
		}
	}
	if i == -1 {
		err = ErrRevNotFound
	} else {
		r = rv.Record(i)
	}
	return
}

type TipRevSpec struct{}

func (TipRevSpec) String() string {
	return "tip"
}

func (TipRevSpec) Lookup(i *Index) (r *Rec, err error) {
	if n := len(i.index); n == 0 {
		err = ErrRevisionNotFound
	} else {
		r = i.Record(n - 1)
	}
	return
}

type NullRevSpec struct{}

func (NullRevSpec) String() string {
	return "null"
}

func (NullRevSpec) Lookup(i *Index) (r *Rec, err error) {
	r = &i.null
	return
}

var ErrRevisionNotFound = errors.New("hg/revlog: revision not found")
var ErrRevisionAmbiguous = errors.New("hg/revlog: ambiguous revision spec")
<|endoftext|>


<commit_before>package router

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/oursky/skygear/skyerr"
)

// pipeline encapsulates a transformation which a request will come throught
// from preprocessors to the actual handler. (and postprocessor later)
type pipeline struct {
	Action        string
	Preprocessors []Processor
	Handler
}

// Router to dispatch HTTP request to respective handler
type Router struct {
	actions struct {
		sync.RWMutex
		m map[string]pipeline
	}
}

// PreprocessorRegistry is holding all preprocessors and their mapping with
// a string name.
type PreprocessorRegistry map[string]Processor

// GetByNames returns a list of registered preprocessors by preprocessor names.
func (r PreprocessorRegistry) GetByNames(names ...string) []Processor {
	preprocessors := make([]Processor, len(names))
	for i, name := range names {
		pp, ok := r[name]
		if !ok {
			log.Fatalf("preprocessor %s is not defined", name)
		}
		preprocessors[i] = pp
	}
	return preprocessors
}

// NewRouter is factory for Router
func NewRouter() *Router {
	return &Router{
		struct {
			sync.RWMutex
			m map[string]pipeline
		}{
			m: map[string]pipeline{},
		},
	}
}

// Map to register action to handle mapping
func (r *Router) Map(action string, handler Handler, preprocessors ...Processor) {
	r.actions.Lock()
	defer r.actions.Unlock()
	if len(preprocessors) == 0 {
		preprocessors = handler.GetPreprocessors()
	}
	r.actions.m[action] = pipeline{
		Action:        action,
		Preprocessors: preprocessors,
		Handler:       handler,
	}
}

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var (
		httpStatus    = http.StatusOK
		resp          Response
		handler       Handler
		preprocessors []Processor
		payload       *Payload
	)

	resp.writer = w
	defer func() {
		if r := recover(); r != nil {
			resp.Err = errorFromRecoveringPanic(r)
			log.WithField("recovered", r).Errorln("panic occurred while handling request")
		}

		if !resp.written {
			resp.Header().Set("Content-Type", "application/json")
			if resp.Err != nil && httpStatus >= 200 && httpStatus <= 299 {
				resp.writer.WriteHeader(defaultStatusCode(resp.Err))
			} else {
				resp.writer.WriteHeader(httpStatus)
			}
			if err := resp.WriteEntity(resp); err != nil {
				panic(err)
			}
		}
	}()

	var err error
	payload, err = newPayloadForJSONHandler(req)
	if err != nil {
		httpStatus = http.StatusBadRequest
		resp.Err = skyerr.NewRequestJSONInvalidErr(err)
		return
	}

	handler, preprocessors = r.matchRouteHandler(req)

	if handler == nil {
		httpStatus = http.StatusNotFound
		resp.Err = skyerr.NewError(skyerr.UndefinedOperation, "route unmatched")
	} else {
		for _, p := range preprocessors {
			httpStatus = p.Preprocess(payload, &resp)
			if resp.Err != nil {
				if httpStatus == http.StatusOK {
					httpStatus = defaultStatusCode(resp.Err)
				}
				return
			}
		}

		handler.Handle(payload, &resp)
	}
}

func (r *Router) matchRouteHandler(req *http.Request) (h Handler, pp []Processor) {
	r.actions.RLock()
	defer r.actions.RUnlock()

	action := req.URL.Path
	if strings.HasPrefix(action, "/") {
		action = action[1:]
	}

	action = strings.Replace(action, "/", ":", -1)

	if pipeline, ok := r.actions.m[action]; ok {
		h = pipeline.Handler
		pp = pipeline.Preprocessors
	}
	return
}

func newPayloadForJSONHandler(req *http.Request) (p *Payload, err error) {
	reqBody := req.Body
	if reqBody == nil {
		reqBody = ioutil.NopCloser(bytes.NewReader(nil))
	}

	data := map[string]interface{}{}
	if jsonErr := json.NewDecoder(reqBody).Decode(&data); jsonErr != nil && jsonErr != io.EOF {
		err = jsonErr
		return
	}

	p = &Payload{
		Data: data,
		Meta: map[string]interface{}{},
	}

	if apiKey := req.Header.Get("X-Skygear-Api-Key"); apiKey != "" {
		p.Data["api_key"] = apiKey
	}
	if accessToken := req.Header.Get("X-Skygear-Access-Token"); accessToken != "" {
		p.Data["access_token"] = accessToken
	}

	return
}
<commit_msg>Add fallback for routing using payload action field #590<commit_after>package router

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/oursky/skygear/skyerr"
)

// pipeline encapsulates a transformation which a request will come throught
// from preprocessors to the actual handler. (and postprocessor later)
type pipeline struct {
	Action        string
	Preprocessors []Processor
	Handler
}

// Router to dispatch HTTP request to respective handler
type Router struct {
	actions struct {
		sync.RWMutex
		m map[string]pipeline
	}
}

// PreprocessorRegistry is holding all preprocessors and their mapping with
// a string name.
type PreprocessorRegistry map[string]Processor

// GetByNames returns a list of registered preprocessors by preprocessor names.
func (r PreprocessorRegistry) GetByNames(names ...string) []Processor {
	preprocessors := make([]Processor, len(names))
	for i, name := range names {
		pp, ok := r[name]
		if !ok {
			log.Fatalf("preprocessor %s is not defined", name)
		}
		preprocessors[i] = pp
	}
	return preprocessors
}

// NewRouter is factory for Router
func NewRouter() *Router {
	return &Router{
		struct {
			sync.RWMutex
			m map[string]pipeline
		}{
			m: map[string]pipeline{},
		},
	}
}

// Map to register action to handle mapping
func (r *Router) Map(action string, handler Handler, preprocessors ...Processor) {
	r.actions.Lock()
	defer r.actions.Unlock()
	if len(preprocessors) == 0 {
		preprocessors = handler.GetPreprocessors()
	}
	r.actions.m[action] = pipeline{
		Action:        action,
		Preprocessors: preprocessors,
		Handler:       handler,
	}
}

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var (
		httpStatus    = http.StatusOK
		resp          Response
		handler       Handler
		preprocessors []Processor
		payload       *Payload
	)

	resp.writer = w
	defer func() {
		if r := recover(); r != nil {
			resp.Err = errorFromRecoveringPanic(r)
			log.WithField("recovered", r).Errorln("panic occurred while handling request")
		}

		if !resp.written {
			resp.Header().Set("Content-Type", "application/json")
			if resp.Err != nil && httpStatus >= 200 && httpStatus <= 299 {
				resp.writer.WriteHeader(defaultStatusCode(resp.Err))
			} else {
				resp.writer.WriteHeader(httpStatus)
			}
			if err := resp.WriteEntity(resp); err != nil {
				panic(err)
			}
		}
	}()

	var err error
	payload, err = newPayloadForJSONHandler(req)
	if err != nil {
		httpStatus = http.StatusBadRequest
		resp.Err = skyerr.NewRequestJSONInvalidErr(err)
		return
	}

	handler, preprocessors = r.matchRouteHandler(req)

	if handler == nil {
		// Fallback to match action in JSON.
		// This would match to HomeHandler if action field is omitted.
		handler, preprocessors = r.matchJSONHandler(payload)
	}

	if handler == nil {
		httpStatus = http.StatusNotFound
		resp.Err = skyerr.NewError(skyerr.UndefinedOperation, "route unmatched")
	} else {
		for _, p := range preprocessors {
			httpStatus = p.Preprocess(payload, &resp)
			if resp.Err != nil {
				if httpStatus == http.StatusOK {
					httpStatus = defaultStatusCode(resp.Err)
				}
				return
			}
		}

		handler.Handle(payload, &resp)
	}
}

func (r *Router) matchRouteHandler(req *http.Request) (h Handler, pp []Processor) {
	r.actions.RLock()
	defer r.actions.RUnlock()

	action := req.URL.Path
	if strings.HasPrefix(action, "/") {
		action = action[1:]
	}

	action = strings.Replace(action, "/", ":", -1)

	if len(action) > 0 { // prevent matching HomeHandler
		if pipeline, ok := r.actions.m[action]; ok {
			h = pipeline.Handler
			pp = pipeline.Preprocessors
		}
	}

	return
}

func (r *Router) matchJSONHandler(p *Payload) (h Handler, pp []Processor) {
	r.actions.RLock()
	defer r.actions.RUnlock()
	if pipeline, ok := r.actions.m[p.RouteAction()]; ok {
		h = pipeline.Handler
		pp = pipeline.Preprocessors
	}
	return
}

func newPayloadForJSONHandler(req *http.Request) (p *Payload, err error) {
	reqBody := req.Body
	if reqBody == nil {
		reqBody = ioutil.NopCloser(bytes.NewReader(nil))
	}

	data := map[string]interface{}{}
	if jsonErr := json.NewDecoder(reqBody).Decode(&data); jsonErr != nil && jsonErr != io.EOF {
		err = jsonErr
		return
	}

	p = &Payload{
		Data: data,
		Meta: map[string]interface{}{},
	}

	if apiKey := req.Header.Get("X-Skygear-Api-Key"); apiKey != "" {
		p.Data["api_key"] = apiKey
	}
	if accessToken := req.Header.Get("X-Skygear-Access-Token"); accessToken != "" {
		p.Data["access_token"] = accessToken
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "log"
    "os"
    "syscall"
    "github.com/Jeffail/gabs"
    "github.com/davecgh/go-spew/spew"
//    "github.com/syndtr/gocapability/capability"
)

func main() {
	// User-set, be verbose?
	verbose := (os.Getenv("RUNNER_VERBOSE") == "1")
	// Determine if KVM is available.
	var hasKvm bool
	devKvm, err := os.OpenFile("/dev/kvm", os.O_RDWR, 0)
	if err != nil {
		hasKvm = false
	} else {
		hasKvm = true
		devKvm.Close()
	}
	// Load any configuration provided with the guest, or create an empty
	// configuration if none.
	gCfg, err := loadGuestConfig("/unikernel/config.json")
	if err != nil {
		if os.IsNotExist(err) {
			gCfg = gabs.New()
		} else {
			log.Fatal(err)
		}
	}
	// Detect container-side ("host") network configuration and merge with
	// guest configuration.
	nCfg, err := getNetConfig()
	if err != nil {
		log.Fatalf("getNetConfig(): %v", err)
	}
	if err := mergeNetConfig(nCfg, gCfg); err != nil {
		log.Fatalf("mergeNetConfig(): %v", err)
	}
	// Enumerate volumes in /unikernel/fs/* (if any) and merge with guest
	// block device configuration.
	vCfg, err := getVolumeConfig("/unikernel/fs")
	if err != nil {
		log.Fatalf("getVolumeConfig(): %v", err)
	}
	if err := mergeVolumeConfig(vCfg, gCfg); err != nil {
		log.Fatalf("mergeVolumeConfig(): %v", err)
	}
	// Save merged guest configuration to /unikernel/run.json.
	if err := saveGuestConfig(gCfg, "/unikernel/run.json"); err != nil {
		log.Fatalf("saveGuestConfig(): %v", err)
	}
	// Wire up guest tap interface.
	tapInterface := wireTapInterface(nCfg)
	// Construct qemu arguments.
	guestArgs := []string{
		"qemu-system-x86_64",
		"-kernel", "/unikernel/unikernel.bin",
		"-initrd", "/unikernel/run.json",
		"-vga", "none", "-nographic",
	}
	// Enable KVM if available
	if hasKvm {
		guestArgs = append(guestArgs,
			"-enable-kvm", "-cpu", "host,migratable=no,+invtsc")
	}
	// Tap interface -> virtio-net
	guestArgs = append(guestArgs, "-net")
	guestArgs = append(guestArgs,
		fmt.Sprintf("nic,macaddr=%s,model=virtio",
			nCfg.hardwareAddress))
	guestArgs = append(guestArgs, "-net")
	guestArgs = append(guestArgs,
		fmt.Sprintf("tap,ifname=%s,script=no,downscript=no",
			tapInterface))
	// Block devices -> virtio-block
	for _, v := range vCfg {
		guestArgs = append(guestArgs, "-drive")
		volArg := fmt.Sprintf("file=%v,if=virtio,format=raw", v.path)
		guestArgs = append(guestArgs, volArg)
	}
	if verbose {
		fmt.Println("---- DEBUG: netConfig")
		spew.Dump(nCfg)
		fmt.Println("---- DEBUG: volumeConfig")
		spew.Dump(vCfg)
		fmt.Println("---- DEBUG: guestArgs")
		spew.Dump(guestArgs)
	}
	// Drop CAP_NET_ADMIN here, we no longer need it.
	// XXX This doesn't actually appear to do anything, "getpcaps" on the
	// qemu process still prints "cap_net_admin+ep" in its list?!
	// caps, err := capability.NewPid(0)
//	if err != nil {
//		log.Fatalf("Init capabilities: %v", err)
//	}
//	if err := caps.Load(); err != nil {
//		log.Fatalf("Get capabilities: %v", err)
//	}
//	caps.Unset(capability.INHERITABLE | capability.EFFECTIVE | capability.PERMITTED | capability.BOUNDING, capability.CAP_NET_ADMIN)
//	if err := caps.Apply(capability.CAPS); err != nil {
//		log.Fatalf("Cannot drop CAP_NET_ADMIN: %v", err)
//	}
	// Boom!
	err = syscall.Exec("/runtime/qemu/bin/qemu-system-x86_64", guestArgs, nil)
	if err != nil {
		log.Fatalf("Exec(%v): %v", guestArgs, err)
	}
}
<commit_msg>Run QEMU as non-root<commit_after>package main

import (
    "fmt"
    "log"
    "os"
    "syscall"
    "github.com/Jeffail/gabs"
    "github.com/davecgh/go-spew/spew"
)

func main() {
	// User-set, be verbose?
	verbose := (os.Getenv("RUNNER_VERBOSE") == "1")
	// Determine if KVM is available.
	var hasKvm bool
	devKvm, err := os.OpenFile("/dev/kvm", os.O_RDWR, 0)
	if err != nil {
		hasKvm = false
	} else {
		hasKvm = true
		devKvm.Close()
	}
	// Load any configuration provided with the guest, or create an empty
	// configuration if none.
	gCfg, err := loadGuestConfig("/unikernel/config.json")
	if err != nil {
		if os.IsNotExist(err) {
			gCfg = gabs.New()
		} else {
			log.Fatal(err)
		}
	}
	// Detect container-side ("host") network configuration and merge with
	// guest configuration.
	nCfg, err := getNetConfig()
	if err != nil {
		log.Fatalf("getNetConfig(): %v", err)
	}
	if err := mergeNetConfig(nCfg, gCfg); err != nil {
		log.Fatalf("mergeNetConfig(): %v", err)
	}
	// Enumerate volumes in /unikernel/fs/* (if any) and merge with guest
	// block device configuration.
	vCfg, err := getVolumeConfig("/unikernel/fs")
	if err != nil {
		log.Fatalf("getVolumeConfig(): %v", err)
	}
	if err := mergeVolumeConfig(vCfg, gCfg); err != nil {
		log.Fatalf("mergeVolumeConfig(): %v", err)
	}
	// Save merged guest configuration to /unikernel/run.json.
	if err := saveGuestConfig(gCfg, "/unikernel/run.json"); err != nil {
		log.Fatalf("saveGuestConfig(): %v", err)
	}
	// Wire up guest tap interface.
	tapInterface := wireTapInterface(nCfg)
	// Construct qemu arguments.
	guestArgs := []string{
		"qemu-system-x86_64",
		"-kernel", "/unikernel/unikernel.bin",
		"-initrd", "/unikernel/run.json",
		"-vga", "none", "-nographic",
	}
	// Enable KVM if available
	if hasKvm {
		guestArgs = append(guestArgs,
			"-enable-kvm", "-cpu", "host,migratable=no,+invtsc")
		// QEMU will be run as UID 1, ensure it can access /dev/kvm
		if err := syscall.Chown("/dev/kvm", 1, 1); err != nil {
			log.Fatalf("Could not chown() /dev/kvm: %v", err)
		}
	}
	// QEMU will be run as UID 1, ensure it can access /dev/net/tun.
	// Not strictly necessary as this should be mode 0666, but you never know.
	if err := syscall.Chown("/dev/net/tun", 1, 1); err != nil {
		log.Fatalf("Could not chown() /dev/net/tun: %v", err)
	}
	// Tap interface -> virtio-net
	guestArgs = append(guestArgs, "-net")
	guestArgs = append(guestArgs,
		fmt.Sprintf("nic,macaddr=%s,model=virtio",
			nCfg.hardwareAddress))
	guestArgs = append(guestArgs, "-net")
	guestArgs = append(guestArgs,
		fmt.Sprintf("tap,ifname=%s,script=no,downscript=no",
			tapInterface))
	// Block devices -> virtio-block
	for _, v := range vCfg {
		guestArgs = append(guestArgs, "-drive")
		volArg := fmt.Sprintf("file=%v,if=virtio,format=raw", v.path)
		guestArgs = append(guestArgs, volArg)
		// QEMU will be run as UID 1, ensure it can access the volume
		if err := syscall.Chown(v.path, 1, 1); err != nil {
			log.Fatalf("Could not chown() %v: %v", v.path, err)
		}
	}
	if verbose {
		fmt.Println("---- DEBUG: netConfig")
		spew.Dump(nCfg)
		fmt.Println("---- DEBUG: volumeConfig")
		spew.Dump(vCfg)
		fmt.Println("---- DEBUG: guestArgs")
		spew.Dump(guestArgs)
	}
	// So, there's no feasible way of dropping CAP_NET_ADMIN or calling
	// setuid() in a golang program (see https://github.com/golang/go/issues/1435)
	// Hence, we chown anything QEMU needs to write to to UID 1 above, and
	// make the qemu binary setuid.
	qemuPath := "/runtime/qemu/bin/qemu-system-x86_64"
	if err := syscall.Chown(qemuPath, 1, 1); err != nil {
		log.Fatalf("Could not chown() %v: %v", qemuPath, err)
	}
	if err := syscall.Chmod(qemuPath, 04755); err != nil {
		log.Fatalf("Could not chmod() %v: %v", qemuPath, err)
	}
	err = syscall.Exec(qemuPath, guestArgs, nil)
	if err != nil {
		log.Fatalf("Exec(%v, %v): %v", qemuPath, guestArgs, err)
	}
}
<|endoftext|>


<commit_before>package scanln

import (
	"fmt"
	"time"
)

/*
REVISION HISTORY
-------- -------
24 Sep 22 -- First version.
*/

const lastAltered = "Sep 24, 2022"
const maxTimeout = 10

// WithTimeout (prompt string, timeOut int) string
// If <enter> is hit before the timeout, then it will return an empty string.  If the timeout is reached before anything is entered then it returns a nil string.
func WithTimeout(prompt string, timeOut int) string {
	var ans string
	strChannel := make(chan string, 1) // Note that the buffer size of 1 is necessary to avoid deadlock of goroutines and guarantee garbage collection of the timeout channel.
	defer close(strChannel)
	ticker := time.NewTicker(1 * time.Second)
	if timeOut > maxTimeout {
		timeOut = maxTimeout
	}
	ticks := timeOut
	go func() {
		fmt.Printf(" %s \n", prompt)
		n, err := fmt.Scanln(&ans)
		if n == 0 || err != nil {
			strChannel <- ""
		}
		strChannel <- ans
	}()

	for {
		select {
		case <-ticker.C:
			if ticks < 1 {
				ticker.Stop()
				return ""
			}
			fmt.Printf("\r %d second(s)   ", ticks)
			ticks--

		case s := <-strChannel:
			return s
		}
	}
} // scanlnWithTimeout

/*
func main() {
	var err error
	fmt.Printf(" scanlineWithTimeout test last altered %s, len(os.Args) = %d.\n", lastAltered, len(os.Args))
	timeout := 0
	if len(os.Args) > 1 { // param is entered
		timeout, err = strconv.Atoi(os.Args[1])
		if err != nil {
			fmt.Printf(" Error from Atoi call is %v, timeout set to max of %d.\n", err, maxTimeout)
			timeout = maxTimeout
		}
	} else {
		var toutStr string // abbreviation for timeout string
		fmt.Printf(" Enter value for the timeout: ")
		n, er := fmt.Scanln(&toutStr)
		if n == 0 || er != nil {
			timeout = maxTimeout
		} else {
			timeout, err = strconv.Atoi(toutStr)
			if err != nil {
				fmt.Printf(" Error from Atoi call is %v, timeout set to max of %d.\n", err, maxTimeout)
				timeout = maxTimeout
			}
		}
		fmt.Printf(" Entered %s, timeout = %d\n", toutStr, timeout)
	}

	returnedString := scanlnWithTimeout("enter something before it times out", timeout)
	fmt.Printf(" returnedString is %q\n", returnedString)
}

*/
<commit_msg>09/24/2022 20:08:02 scanln/scanln.go<commit_after>package scanln

import (
	"fmt"
	"time"
)

/*
REVISION HISTORY
-------- -------
24 Sep 22 -- First version.
*/

const lastAltered = "Sep 24, 2022"
const maxTimeout = 10

// WithTimeout (prompt string, timeOut int) string
// If it times out, or <enter> is hit before the timeout, then it will return an empty string.
func WithTimeout(prompt string, timeOut int) string {
	var ans string
	strChannel := make(chan string, 1) // Note that the buffer size of 1 is necessary to avoid deadlock of goroutines and guarantee garbage collection of the timeout channel.
	defer close(strChannel)
	ticker := time.NewTicker(1 * time.Second)
	if timeOut > maxTimeout {
		timeOut = maxTimeout
	}
	ticks := timeOut
	go func() {
		fmt.Printf(" %s \n", prompt)
		n, err := fmt.Scanln(&ans)
		if n == 0 || err != nil {
			strChannel <- ""
		}
		strChannel <- ans
	}()

	for {
		select {
		case <-ticker.C:
			if ticks < 1 {
				ticker.Stop()
				return ""
			}
			fmt.Printf("\r %d second(s)   ", ticks)
			ticks--

		case s := <-strChannel:
			return s
		}
	}
} // scanlnWithTimeout

/*
func main() {
	var err error
	fmt.Printf(" scanlineWithTimeout test last altered %s, len(os.Args) = %d.\n", lastAltered, len(os.Args))
	timeout := 0
	if len(os.Args) > 1 { // param is entered
		timeout, err = strconv.Atoi(os.Args[1])
		if err != nil {
			fmt.Printf(" Error from Atoi call is %v, timeout set to max of %d.\n", err, maxTimeout)
			timeout = maxTimeout
		}
	} else {
		var toutStr string // abbreviation for timeout string
		fmt.Printf(" Enter value for the timeout: ")
		n, er := fmt.Scanln(&toutStr)
		if n == 0 || er != nil {
			timeout = maxTimeout
		} else {
			timeout, err = strconv.Atoi(toutStr)
			if err != nil {
				fmt.Printf(" Error from Atoi call is %v, timeout set to max of %d.\n", err, maxTimeout)
				timeout = maxTimeout
			}
		}
		fmt.Printf(" Entered %s, timeout = %d\n", toutStr, timeout)
	}

	returnedString := scanlnWithTimeout("enter something before it times out", timeout)
	fmt.Printf(" returnedString is %q\n", returnedString)
}

*/
<|endoftext|>