<commit_before>package kuja

import (
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"reflect"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/golang/snappy"
	"github.com/plimble/errors"
	"github.com/plimble/kuja/broker"
	"github.com/plimble/kuja/encoder"
	"github.com/plimble/kuja/encoder/gogoproto"
	"github.com/plimble/kuja/encoder/json"
	"github.com/plimble/kuja/encoder/msgp"
	"github.com/plimble/kuja/encoder/proto"
	"github.com/plimble/kuja/registry"
	"github.com/satori/go.uuid"
	"golang.org/x/net/netutil"
	"gopkg.in/tylerb/graceful.v1"
)

const (
	DEVELOPMENT = "development"
	STAGING     = "staging"
	PRODUCTION  = "production"
)

type Handler func(ctx *Context, w http.ResponseWriter, r *http.Request) error

type Server struct {
	stage            string
	id               string
	pool             sync.Pool
	middleware       []Handler
	mu               sync.Mutex // protects the serviceMap
	serviceMap       map[string]*service
	subscriberMap    map[string]*subscriber
	broker           broker.Broker
	encoder          encoder.Encoder
	snappy           bool
	serviceError     ServiceErrorFunc
	subscriberError  SubscriberErrorFunc
	registry         registry.Registry
	srv              *graceful.Server
	jsonEncoder      encoder.Encoder
	protoEncoder     encoder.Encoder
	gogoProtoEncoder encoder.Encoder
	msgpEncoder      encoder.Encoder
}

func NewServer() *Server {
	server := &Server{
		stage:            DEVELOPMENT,
		id:               uuid.NewV1().String(),
		serviceMap:       make(map[string]*service),
		encoder:          json.NewEncoder(),
		serviceError:     defaulServiceErr,
		subscriberError:  defaulSubscriberErr,
		subscriberMap:    make(map[string]*subscriber),
		jsonEncoder:      json.NewEncoder(),
		protoEncoder:     proto.NewEncoder(),
		gogoProtoEncoder: gogoproto.NewEncoder(),
		msgpEncoder:      msgp.NewEncoder(),
	}

	server.pool.New = func() interface{} {
		return &Context{
			ReqMetadata:      make(Metadata),
			RespMetadata:     make(Metadata),
			returnValues:     make([]reflect.Value, 1),
			serviceError:     server.serviceError,
			isResp:           false,
			jsonEncoder:      server.jsonEncoder,
			protoEncoder:     server.protoEncoder,
			gogoProtoEncoder: server.gogoProtoEncoder,
			msgpEncoder:      server.msgpEncoder,
		}
	}

	return server
}

func (server *Server) Id() string {
	return server.id
}

func (server *Server) Use(h ...Handler) {
	server.middleware = append(server.middleware, h...)
}

func (server *Server) Stage(stage string) {
	server.stage = stage
}

func (server *Server) Snappy(enable bool) {
	server.snappy = enable
}

func (server *Server) Registry(r registry.Registry) {
	server.registry = r
}

func (server *Server) Encoder(enc encoder.Encoder) {
	server.encoder = enc
}

func (server *Server) Broker(b broker.Broker) {
	server.broker = b
}

func (server *Server) Run(addr string, timeout time.Duration) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

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

	if server.srv.ListenLimit != 0 {
		l = netutil.LimitListener(l, server.srv.ListenLimit)
	}

	if err := server.startRegistry("http://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(l)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) Close() {
	server.srv.Stop(0)
}

func (server *Server) RunTLS(addr string, timeout time.Duration, certFile, keyFile string) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

	config := &tls.Config{}
	if server.srv.TLSConfig != nil {
		*config = *server.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 {
		log.Error(err)
		return
	}

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

	tlsListener := tls.NewListener(conn, config)

	if err := server.startRegistry("https://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(tlsListener)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) startRegistry(scheme, addr string) error {
	if server.registry == nil {
		return nil
	}

	var host string
	var port string
	parts := strings.Split(addr, ":")
	if len(parts) > 1 {
		host = strings.Join(parts[:len(parts)-1], ":")
		port = parts[len(parts)-1]
	} else {
		host = parts[0]
	}

	for _, service := range server.serviceMap {
		service.node = &registry.Node{
			Id:      service.id,
			Name:    service.name,
			Host:    host,
			Port:    port,
			Address: addr,
			URL:     scheme + addr,
		}
		err := server.registry.Register(service.node)
		log.Infof("Registerd %s %s %s", service.name, service.id, addr)
		if err != nil {
			return err
		}
	}

	return nil
}

func (server *Server) stopRegistry() {
	if server.registry != nil {
		for _, service := range server.serviceMap {
			if service.node == nil {
				continue
			}
			err := server.registry.Deregister(service.node)
			if err != nil {
				log.WithFields(log.Fields{
					"error":      err,
					"service":    service.name,
					"service_id": service.id,
				}).Error("Unable to deregister")
				continue
			}
			log.Infof("Deregisterd %s %s", service.name, service.id)
		}
		server.registry.Close()
	}
}

func (server *Server) startBroker() error {
	if server.broker == nil {
		return nil
	}

	if err := server.broker.Connect(); err != nil {
		return err
	}

	log.Infof("Connected to broker")

	return nil
}

func (server *Server) stopBroker() {
	if server.broker == nil {
		return
	}

	server.broker.Close()
	log.Infof("Close broker connection")
}

func (server *Server) startSubscribe() error {
	if server.broker == nil {
		return nil
	}

	for _, s := range server.subscriberMap {
		server.broker.Subscribe(s.topic, s.queue, server.id, s.size, s.handler)
		log.Infof("Subscribe topic: %s queue: %s size: %d", s.topic, s.queue, s.size)
	}

	return nil
}

func getServiceMethod(s string) (string, string) {
	if strings.HasPrefix(s, "/") {
		s = s[1:]
	}

	if strings.HasSuffix(s, "/") {
		s = s[:len(s)-1]
	}

	for i := 0; i < len(s); i++ {
		if s[i] == '/' {
			return s[:i], s[i+1:]
		}
	}

	return "", ""
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" && req.URL.Path == "/health" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusOK)
		io.WriteString(w, "ok\n")
		return
	}

	if req.Method != "POST" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)
		io.WriteString(w, "405 must POST\n")
		return
	}

	serviceName, methodName := getServiceMethod(req.URL.Path)

	if serviceName == "" || methodName == "" {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service or method"))
		return
	}

	ctx := server.pool.Get().(*Context)

	for name, vals := range req.Header {
		ctx.ReqMetadata[name] = vals[0]
	}

	// server.mu.Lock()
	s := server.serviceMap[serviceName]
	// server.mu.Unlock()
	if s == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service " + serviceName))
		return
	}
	mt := s.method[methodName]
	if mt == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find method " + methodName))
		return
	}

	ctx.handlers = s.handlers
	ctx.mt = mt
	ctx.req = req
	ctx.w = w
	ctx.rcvr = s.rcvr
	ctx.encoder = server.encoder
	ctx.snappy = server.snappy
	ctx.ServiceID = s.id
	ctx.ServiceName = serviceName
	ctx.MethodName = methodName

	if len(s.handlers) > 0 {
		if err := s.handlers[0](ctx, w, req); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	} else {
		if err := serve(ctx); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	}
	server.pool.Put(ctx)
}

func respError(err error, ctx *Context) {
	if errs, ok := err.(errors.Error); ok {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, errs.Status(), err)
		ctx.w.Header().Set("Content-Type", "application/json; charset=utf-8")
		ctx.w.WriteHeader(errs.Status())
		ctx.jsonEncoder.Encode(ctx.w, errs)
	} else {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, 500, err)
		ctx.w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		ctx.w.WriteHeader(500)
		ctx.w.Write([]byte(err.Error()))
	}
}

func serve(ctx *Context) error {
	var err error
	var encoderType encoder.Encoder
	ctx.isResp = false

	argv := reflect.New(ctx.mt.ArgType.Elem())
	var replyv interface{}

	argvInter := argv.Interface()

	contentType := ctx.req.Header.Get("Content-Type")
	switch contentType {
	case "application/json":
		encoderType = ctx.jsonEncoder
	case "application/proto":
		encoderType = ctx.protoEncoder
	case "application/gogoproto":
		encoderType = ctx.gogoProtoEncoder
	case "application/msgp":
		encoderType = ctx.msgpEncoder
	default:
		encoderType = ctx.jsonEncoder
	}
	err = encoderType.Decode(ctx.req.Body, argvInter)
	ctx.req.Body.Close()
	if err != nil {
		return errors.InternalError("unable to encode response " + contentType)
	}

	function := ctx.mt.method.Func
	if ctx.mt.ContextType == nil {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, argv})
	} else {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, ctx.mt.prepareContext(ctx), argv})
	}

	if len(ctx.returnValues) == 1 {
		if ctx.returnValues[0].Interface() != nil {
			return ctx.returnValues[0].Interface().(error)
		}
	} else if len(ctx.returnValues) == 2 {
		if ctx.returnValues[1].Interface() != nil {
			return ctx.returnValues[1].Interface().(error)
		}
		replyv = ctx.returnValues[0].Interface()
	}

	for name, val := range ctx.RespMetadata {
		ctx.w.Header().Set(name, val)
	}

	if replyv != nil {
		if ctx.snappy {
			data, err := encoderType.Marshal(replyv)
			if err != nil {
				return err
			}
			data = snappy.Encode(nil, data)
			ctx.isResp = true
			ctx.w.Header().Set("Content-Type", contentType)
			ctx.w.Header().Set("Snappy", "true")
			ctx.w.WriteHeader(200)
			ctx.w.Write(data)
		} else {
			ctx.isResp = true
			ctx.w.Header().Set("Content-Type", contentType)
			ctx.w.WriteHeader(200)
			encoderType.Encode(ctx.w, replyv)
		}
	} else {
		ctx.isResp = true
		ctx.w.Header().Set("Content-Type", contentType)
		ctx.w.WriteHeader(200)
		ctx.w.Write(nil)
	}

	return nil
}
<commit_msg>fix bug<commit_after>package kuja

import (
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"reflect"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/golang/snappy"
	"github.com/plimble/errors"
	"github.com/plimble/kuja/broker"
	"github.com/plimble/kuja/encoder"
	"github.com/plimble/kuja/encoder/gogoproto"
	"github.com/plimble/kuja/encoder/json"
	"github.com/plimble/kuja/encoder/msgp"
	"github.com/plimble/kuja/encoder/proto"
	"github.com/plimble/kuja/registry"
	"github.com/satori/go.uuid"
	"golang.org/x/net/netutil"
	"gopkg.in/tylerb/graceful.v1"
)

const (
	DEVELOPMENT = "development"
	STAGING     = "staging"
	PRODUCTION  = "production"
)

type Handler func(ctx *Context, w http.ResponseWriter, r *http.Request) error

type Server struct {
	stage            string
	id               string
	pool             sync.Pool
	middleware       []Handler
	mu               sync.Mutex // protects the serviceMap
	serviceMap       map[string]*service
	subscriberMap    map[string]*subscriber
	broker           broker.Broker
	encoder          encoder.Encoder
	snappy           bool
	serviceError     ServiceErrorFunc
	subscriberError  SubscriberErrorFunc
	registry         registry.Registry
	srv              *graceful.Server
	jsonEncoder      encoder.Encoder
	protoEncoder     encoder.Encoder
	gogoProtoEncoder encoder.Encoder
	msgpEncoder      encoder.Encoder
}

func NewServer() *Server {
	server := &Server{
		stage:            DEVELOPMENT,
		id:               uuid.NewV1().String(),
		serviceMap:       make(map[string]*service),
		encoder:          json.NewEncoder(),
		serviceError:     defaulServiceErr,
		subscriberError:  defaulSubscriberErr,
		subscriberMap:    make(map[string]*subscriber),
		jsonEncoder:      json.NewEncoder(),
		protoEncoder:     proto.NewEncoder(),
		gogoProtoEncoder: gogoproto.NewEncoder(),
		msgpEncoder:      msgp.NewEncoder(),
	}

	server.pool.New = func() interface{} {
		return &Context{
			ReqMetadata:      make(Metadata),
			RespMetadata:     make(Metadata),
			returnValues:     make([]reflect.Value, 1),
			serviceError:     server.serviceError,
			isResp:           false,
			jsonEncoder:      server.jsonEncoder,
			protoEncoder:     server.protoEncoder,
			gogoProtoEncoder: server.gogoProtoEncoder,
			msgpEncoder:      server.msgpEncoder,
		}
	}

	return server
}

func (server *Server) Id() string {
	return server.id
}

func (server *Server) Use(h ...Handler) {
	server.middleware = append(server.middleware, h...)
}

func (server *Server) Stage(stage string) {
	server.stage = stage
}

func (server *Server) Snappy(enable bool) {
	server.snappy = enable
}

func (server *Server) Registry(r registry.Registry) {
	server.registry = r
}

func (server *Server) Encoder(enc encoder.Encoder) {
	server.encoder = enc
}

func (server *Server) Broker(b broker.Broker) {
	server.broker = b
}

func (server *Server) Run(addr string, timeout time.Duration) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

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

	if server.srv.ListenLimit != 0 {
		l = netutil.LimitListener(l, server.srv.ListenLimit)
	}

	if err := server.startRegistry("http://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(l)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) Close() {
	server.srv.Stop(0)
}

func (server *Server) RunTLS(addr string, timeout time.Duration, certFile, keyFile string) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

	config := &tls.Config{}
	if server.srv.TLSConfig != nil {
		*config = *server.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 {
		log.Error(err)
		return
	}

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

	tlsListener := tls.NewListener(conn, config)

	if err := server.startRegistry("https://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(tlsListener)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) startRegistry(scheme, addr string) error {
	if server.registry == nil {
		return nil
	}

	var host string
	var port string
	parts := strings.Split(addr, ":")
	if len(parts) > 1 {
		host = strings.Join(parts[:len(parts)-1], ":")
		port = parts[len(parts)-1]
	} else {
		host = parts[0]
	}

	for _, service := range server.serviceMap {
		service.node = &registry.Node{
			Id:      service.id,
			Name:    service.name,
			Host:    host,
			Port:    port,
			Address: addr,
			URL:     scheme + addr,
		}
		err := server.registry.Register(service.node)
		log.Infof("Registerd %s %s %s", service.name, service.id, addr)
		if err != nil {
			return err
		}
	}

	return nil
}

func (server *Server) stopRegistry() {
	if server.registry != nil {
		for _, service := range server.serviceMap {
			if service.node == nil {
				continue
			}
			err := server.registry.Deregister(service.node)
			if err != nil {
				log.WithFields(log.Fields{
					"error":      err,
					"service":    service.name,
					"service_id": service.id,
				}).Error("Unable to deregister")
				continue
			}
			log.Infof("Deregisterd %s %s", service.name, service.id)
		}
		server.registry.Close()
	}
}

func (server *Server) startBroker() error {
	if server.broker == nil {
		return nil
	}

	if err := server.broker.Connect(); err != nil {
		return err
	}

	log.Infof("Connected to broker")

	return nil
}

func (server *Server) stopBroker() {
	if server.broker == nil {
		return
	}

	server.broker.Close()
	log.Infof("Close broker connection")
}

func (server *Server) startSubscribe() error {
	if server.broker == nil {
		return nil
	}

	for _, s := range server.subscriberMap {
		server.broker.Subscribe(s.topic, s.queue, server.id, s.size, s.handler)
		log.Infof("Subscribe topic: %s queue: %s size: %d", s.topic, s.queue, s.size)
	}

	return nil
}

func getServiceMethod(s string) (string, string) {
	if strings.HasPrefix(s, "/") {
		s = s[1:]
	}

	if strings.HasSuffix(s, "/") {
		s = s[:len(s)-1]
	}

	for i := 0; i < len(s); i++ {
		if s[i] == '/' {
			return s[:i], s[i+1:]
		}
	}

	return "", ""
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" && req.URL.Path == "/health" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusOK)
		io.WriteString(w, "ok\n")
		return
	}

	if req.Method != "POST" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)
		io.WriteString(w, "405 must POST\n")
		return
	}

	serviceName, methodName := getServiceMethod(req.URL.Path)

	if serviceName == "" || methodName == "" {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service or method"))
		return
	}

	ctx := server.pool.Get().(*Context)

	for name, vals := range req.Header {
		ctx.ReqMetadata[name] = vals[0]
	}

	// server.mu.Lock()
	s := server.serviceMap[serviceName]
	// server.mu.Unlock()
	if s == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service " + serviceName))
		return
	}
	mt := s.method[methodName]
	if mt == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find method " + methodName))
		return
	}

	ctx.handlers = s.handlers
	ctx.mt = mt
	ctx.req = req
	ctx.w = w
	ctx.rcvr = s.rcvr
	ctx.encoder = server.encoder
	ctx.snappy = server.snappy
	ctx.ServiceID = s.id
	ctx.ServiceName = serviceName
	ctx.MethodName = methodName
	ctx.isResp = false

	if len(s.handlers) > 0 {
		if err := s.handlers[0](ctx, w, req); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	} else {
		if err := serve(ctx); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	}
	server.pool.Put(ctx)
}

func respError(err error, ctx *Context) {
	if errs, ok := err.(errors.Error); ok {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, errs.Status(), err)
		ctx.w.Header().Set("Content-Type", "application/json; charset=utf-8")
		ctx.w.WriteHeader(errs.Status())
		ctx.jsonEncoder.Encode(ctx.w, errs)
	} else {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, 500, err)
		ctx.w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		ctx.w.WriteHeader(500)
		ctx.w.Write([]byte(err.Error()))
	}
}

func serve(ctx *Context) error {
	var err error
	var encoderType encoder.Encoder
	ctx.isResp = false

	argv := reflect.New(ctx.mt.ArgType.Elem())
	var replyv interface{}

	argvInter := argv.Interface()

	contentType := ctx.req.Header.Get("Content-Type")
	switch contentType {
	case "application/json":
		encoderType = ctx.jsonEncoder
	case "application/proto":
		encoderType = ctx.protoEncoder
	case "application/gogoproto":
		encoderType = ctx.gogoProtoEncoder
	case "application/msgp":
		encoderType = ctx.msgpEncoder
	default:
		encoderType = ctx.jsonEncoder
	}
	err = encoderType.Decode(ctx.req.Body, argvInter)
	ctx.req.Body.Close()
	if err != nil {
		return errors.InternalError("unable to encode response " + contentType)
	}

	function := ctx.mt.method.Func
	if ctx.mt.ContextType == nil {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, argv})
	} else {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, ctx.mt.prepareContext(ctx), argv})
	}

	if len(ctx.returnValues) == 1 {
		if ctx.returnValues[0].Interface() != nil {
			return ctx.returnValues[0].Interface().(error)
		}
	} else if len(ctx.returnValues) == 2 {
		if ctx.returnValues[1].Interface() != nil {
			return ctx.returnValues[1].Interface().(error)
		}
		replyv = ctx.returnValues[0].Interface()
	}

	for name, val := range ctx.RespMetadata {
		ctx.w.Header().Set(name, val)
	}

	if replyv != nil {
		if ctx.snappy {
			data, err := encoderType.Marshal(replyv)
			if err != nil {
				return err
			}
			data = snappy.Encode(nil, data)
			ctx.isResp = true
			ctx.w.Header().Set("Content-Type", contentType)
			ctx.w.Header().Set("Snappy", "true")
			ctx.w.WriteHeader(200)
			ctx.w.Write(data)
		} else {
			ctx.isResp = true
			ctx.w.Header().Set("Content-Type", contentType)
			ctx.w.WriteHeader(200)
			encoderType.Encode(ctx.w, replyv)
		}
	} else {
		ctx.isResp = true
		ctx.w.Header().Set("Content-Type", contentType)
		ctx.w.WriteHeader(200)
		ctx.w.Write(nil)
	}

	return nil
}
<|endoftext|>


<commit_before>package kraken

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/vincent-petithory/kraken/fileserver"
)

type MountMap struct {
	m   map[string]fileserver.Server
	mu  sync.RWMutex
	fsf fileserver.Factory
}

func (mm *MountMap) Targets() []string {
	mm.mu.RLock()
	defer mm.mu.RUnlock()
	mountTargets := make([]string, 0, len(mm.m))
	for mountTarget := range mm.m {
		mountTargets = append(mountTargets, mountTarget)
	}
	return mountTargets
}

// Get retrieves the source for the given mount target.
// It returns "" if the mount target doesn't exist.
func (mm *MountMap) GetSource(mountTarget string) string {
	mm.mu.RLock()
	defer mm.mu.RUnlock()
	fs, ok := mm.m[mountTarget]
	if !ok {
		return ""
	}
	return fs.Root()
}

var (
	// ErrInvalidMountTarget describes an invalid value for a mount target.
	ErrInvalidMountTarget = errors.New("invalid mount target value")
	// ErrInvalidMountSource describes an invalid value for a mount source.
	ErrInvalidMountSource = errors.New("invalid mount source value")
)

type MountSourcePermError struct {
	err error
}

func (e *MountSourcePermError) Error() string {
	return e.err.Error()
}

// Put registers a mount target for the given mount source.
// It returns true if the mount target already exists.
func (mm *MountMap) Put(mountTarget string, mountSource string, fsType string, fsParams fileserver.Params) (bool, error) {
	mm.mu.Lock()
	defer mm.mu.Unlock()

	// mountTarget must start with /
	if !strings.HasPrefix(mountTarget, "/") {
		return false, ErrInvalidMountTarget
	}
	// if mountTarget is not "/" and has a trailing /, reject it
	if mountTarget != "/" && strings.HasSuffix(mountTarget, "/") {
		return false, ErrInvalidMountTarget
	}

	if !path.IsAbs(mountSource) {
		return false, ErrInvalidMountSource
	}

	fi, err := os.Stat(mountSource)
	if err != nil {
		return false, &MountSourcePermError{err}
	}
	if !fi.IsDir() {
		return false, &MountSourcePermError{fmt.Errorf("%s: not a directory", mountSource)}
	}

	_, ok := mm.m[mountTarget]

	fs := mm.fsf.New(mountSource, fsType, fsParams)
	mm.m[mountTarget] = fs
	return ok, nil
}

// Delete removes an existing mount target.
// It returns true if the mount target existed.
func (mm *MountMap) DeleteTarget(mountTarget string) bool {
	mm.mu.RLock()
	defer mm.mu.RUnlock()
	_, ok := mm.m[mountTarget]
	delete(mm.m, mountTarget)
	return ok
}

func (mm *MountMap) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var (
		maxMountTargetLen int
		mountTarget       string
	)
	mm.mu.RLock()
	for t := range mm.m {
		if strings.HasPrefix(r.URL.Path, t) && len(t) >= maxMountTargetLen {
			maxMountTargetLen = len(t)
			mountTarget = t
		}
	}
	if maxMountTargetLen == 0 {
		http.Error(w, fmt.Sprintf("%s: mount target or file not found", r.URL.Path), http.StatusNotFound)
		mm.mu.RUnlock()
		return
	}

	if mountTarget != "/" {
		r.URL.Path = r.URL.Path[maxMountTargetLen:]
		if r.URL.Path == "" {
			http.Redirect(w, r, mountTarget+"/", http.StatusMovedPermanently)
			return
		}
	}
	fs, ok := mm.m[mountTarget]
	mm.mu.RUnlock()
	if !ok {
		http.Error(w, fmt.Sprintf("mount target %q not found", mountTarget), http.StatusNotFound)
		return
	}
	fs.ServeHTTP(w, r)
}

func NewMountMap(fsf fileserver.Factory) *MountMap {
	return &MountMap{
		m:   make(map[string]fileserver.Server),
		fsf: fsf,
	}
}

type Server struct {
	MountMap       *MountMap
	HandlerWrapper func(http.Handler) http.Handler
	Addr           string
	Port           uint16
	Started        chan struct{}
	Running        bool
	srv            *http.Server
	ln             net.Listener
}

func NewServer(addr string, fsf fileserver.Factory) *Server {
	return &Server{
		MountMap: NewMountMap(fsf),
		Addr:     addr,
		Started:  make(chan struct{}),
	}
}

func (s *Server) ListenAndServe() error {
	ln, err := net.Listen("tcp", s.Addr)
	if err != nil {
		return err
	}
	s.Addr = ln.Addr().String()
	_, sport, err := net.SplitHostPort(s.Addr)
	if err != nil {
		return err
	}
	port, err := strconv.Atoi(sport)
	if err != nil {
		return err
	}
	s.Port = uint16(port)

	var h http.Handler
	if s.HandlerWrapper != nil {
		h = s.HandlerWrapper(s.MountMap)
	} else {
		h = s.MountMap
	}
	s.srv = &http.Server{
		Handler: h,
	}
	s.ln = &connsCloserListener{
		Listener: tcpKeepAliveListener{ln.(*net.TCPListener)},
	}

	close(s.Started)
	s.Running = true
	if err := s.srv.Serve(s.ln); err != nil {
		return err
	}
	return nil
}

func (s *Server) Close() error {
	return s.ln.Close()
}

type connsCloserListener struct {
	net.Listener
	conns []net.Conn
}

func (ln *connsCloserListener) Accept() (c net.Conn, err error) {
	c, err = ln.Listener.Accept()
	if err != nil {
		return
	}
	ln.conns = append(ln.conns, c)
	return c, nil
}

func (ln *connsCloserListener) Close() error {
	for _, c := range ln.conns {
		if err := c.Close(); err != nil {
			log.Println(err)
		}
	}
	ln.conns = nil
	return ln.Listener.Close()
}

// borrowed from net/http
type tcpKeepAliveListener struct {
	*net.TCPListener
}

// borrowed from net/http
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

type ServerPool struct {
	Srvs  []*Server
	Fsf   fileserver.Factory
	srvCh chan *Server
}

func NewServerPool(fsf fileserver.Factory) *ServerPool {
	return &ServerPool{
		Srvs:  make([]*Server, 0),
		Fsf:   fsf,
		srvCh: make(chan *Server),
	}
}

func (sp *ServerPool) Add(addr string) (*Server, error) {
	if err := checkAddr(addr); err != nil {
		return nil, err
	}
	s := NewServer(addr, sp.Fsf)
	sp.Srvs = append(sp.Srvs, s)
	return s, nil
}

func (sp *ServerPool) Get(port uint16) *Server {
	for _, srv := range sp.Srvs {
		if srv.Port == port {
			return srv
		}
	}
	return nil
}

func (sp *ServerPool) Remove(port uint16) (bool, error) {
	for i, srv := range sp.Srvs {
		if srv.Port != port {
			continue
		}
		if err := srv.Close(); err != nil {
			return false, err
		}
		copy(sp.Srvs[i:], sp.Srvs[i+1:])
		sp.Srvs[len(sp.Srvs)-1] = nil
		sp.Srvs = sp.Srvs[:len(sp.Srvs)-1]
		return true, nil
	}
	return false, nil
}

func (sp *ServerPool) StartSrv(s *Server) bool {
	// check the server is registered
	if s.Running {
		return false
	}
	for _, srv := range sp.Srvs {
		if srv == s {
			sp.srvCh <- s
			return true
		}
	}
	return false
}

func (sp *ServerPool) ListenAndRun() {
	for _, srv := range sp.Srvs {
		go func(s *Server) {
			if err := s.ListenAndServe(); err != nil {
				log.Printf("server %s stopped with error: %v", s.Addr, err)
			}
		}(srv)
	}
	for srv := range sp.srvCh {
		go func(s *Server) {
			if err := s.ListenAndServe(); err != nil {
				log.Printf("server %s stopped with error: %v", s.Addr, err)
			}
		}(srv)
	}
}

func checkAddr(addr string) error {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return ln.Close()
}
<commit_msg>ServerPool: ignore server listener closing errors (https://code.google.com/p/go/issues/detail?id=4373)<commit_after>package kraken

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/vincent-petithory/kraken/fileserver"
)

type MountMap struct {
	m   map[string]fileserver.Server
	mu  sync.RWMutex
	fsf fileserver.Factory
}

func (mm *MountMap) Targets() []string {
	mm.mu.RLock()
	defer mm.mu.RUnlock()
	mountTargets := make([]string, 0, len(mm.m))
	for mountTarget := range mm.m {
		mountTargets = append(mountTargets, mountTarget)
	}
	return mountTargets
}

// Get retrieves the source for the given mount target.
// It returns "" if the mount target doesn't exist.
func (mm *MountMap) GetSource(mountTarget string) string {
	mm.mu.RLock()
	defer mm.mu.RUnlock()
	fs, ok := mm.m[mountTarget]
	if !ok {
		return ""
	}
	return fs.Root()
}

var (
	// ErrInvalidMountTarget describes an invalid value for a mount target.
	ErrInvalidMountTarget = errors.New("invalid mount target value")
	// ErrInvalidMountSource describes an invalid value for a mount source.
	ErrInvalidMountSource = errors.New("invalid mount source value")
)

type MountSourcePermError struct {
	err error
}

func (e *MountSourcePermError) Error() string {
	return e.err.Error()
}

// Put registers a mount target for the given mount source.
// It returns true if the mount target already exists.
func (mm *MountMap) Put(mountTarget string, mountSource string, fsType string, fsParams fileserver.Params) (bool, error) {
	mm.mu.Lock()
	defer mm.mu.Unlock()

	// mountTarget must start with /
	if !strings.HasPrefix(mountTarget, "/") {
		return false, ErrInvalidMountTarget
	}
	// if mountTarget is not "/" and has a trailing /, reject it
	if mountTarget != "/" && strings.HasSuffix(mountTarget, "/") {
		return false, ErrInvalidMountTarget
	}

	if !path.IsAbs(mountSource) {
		return false, ErrInvalidMountSource
	}

	fi, err := os.Stat(mountSource)
	if err != nil {
		return false, &MountSourcePermError{err}
	}
	if !fi.IsDir() {
		return false, &MountSourcePermError{fmt.Errorf("%s: not a directory", mountSource)}
	}

	_, ok := mm.m[mountTarget]

	fs := mm.fsf.New(mountSource, fsType, fsParams)
	mm.m[mountTarget] = fs
	return ok, nil
}

// Delete removes an existing mount target.
// It returns true if the mount target existed.
func (mm *MountMap) DeleteTarget(mountTarget string) bool {
	mm.mu.RLock()
	defer mm.mu.RUnlock()
	_, ok := mm.m[mountTarget]
	delete(mm.m, mountTarget)
	return ok
}

func (mm *MountMap) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var (
		maxMountTargetLen int
		mountTarget       string
	)
	mm.mu.RLock()
	for t := range mm.m {
		if strings.HasPrefix(r.URL.Path, t) && len(t) >= maxMountTargetLen {
			maxMountTargetLen = len(t)
			mountTarget = t
		}
	}
	if maxMountTargetLen == 0 {
		http.Error(w, fmt.Sprintf("%s: mount target or file not found", r.URL.Path), http.StatusNotFound)
		mm.mu.RUnlock()
		return
	}

	if mountTarget != "/" {
		r.URL.Path = r.URL.Path[maxMountTargetLen:]
		if r.URL.Path == "" {
			http.Redirect(w, r, mountTarget+"/", http.StatusMovedPermanently)
			return
		}
	}
	fs, ok := mm.m[mountTarget]
	mm.mu.RUnlock()
	if !ok {
		http.Error(w, fmt.Sprintf("mount target %q not found", mountTarget), http.StatusNotFound)
		return
	}
	fs.ServeHTTP(w, r)
}

func NewMountMap(fsf fileserver.Factory) *MountMap {
	return &MountMap{
		m:   make(map[string]fileserver.Server),
		fsf: fsf,
	}
}

type Server struct {
	MountMap       *MountMap
	HandlerWrapper func(http.Handler) http.Handler
	Addr           string
	Port           uint16
	Started        chan struct{}
	Running        bool
	srv            *http.Server
	ln             net.Listener
}

func NewServer(addr string, fsf fileserver.Factory) *Server {
	return &Server{
		MountMap: NewMountMap(fsf),
		Addr:     addr,
		Started:  make(chan struct{}),
	}
}

func (s *Server) ListenAndServe() error {
	ln, err := net.Listen("tcp", s.Addr)
	if err != nil {
		return err
	}
	s.Addr = ln.Addr().String()
	_, sport, err := net.SplitHostPort(s.Addr)
	if err != nil {
		return err
	}
	port, err := strconv.Atoi(sport)
	if err != nil {
		return err
	}
	s.Port = uint16(port)

	var h http.Handler
	if s.HandlerWrapper != nil {
		h = s.HandlerWrapper(s.MountMap)
	} else {
		h = s.MountMap
	}
	s.srv = &http.Server{
		Handler: h,
	}
	s.ln = &connsCloserListener{
		Listener: tcpKeepAliveListener{ln.(*net.TCPListener)},
	}

	close(s.Started)
	s.Running = true
	if err := s.srv.Serve(s.ln); err != nil {
		return err
	}
	return nil
}

func (s *Server) Close() error {
	return s.ln.Close()
}

type connsCloserListener struct {
	net.Listener
	conns []net.Conn
}

func (ln *connsCloserListener) Accept() (c net.Conn, err error) {
	c, err = ln.Listener.Accept()
	if err != nil {
		return
	}
	ln.conns = append(ln.conns, c)
	return c, nil
}

func (ln *connsCloserListener) Close() error {
	for _, c := range ln.conns {
		if err := c.Close(); err != nil {
			log.Println(err)
		}
	}
	ln.conns = nil
	return ln.Listener.Close()
}

// borrowed from net/http
type tcpKeepAliveListener struct {
	*net.TCPListener
}

// borrowed from net/http
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

type ServerPool struct {
	Srvs  []*Server
	Fsf   fileserver.Factory
	srvCh chan *Server
}

func NewServerPool(fsf fileserver.Factory) *ServerPool {
	return &ServerPool{
		Srvs:  make([]*Server, 0),
		Fsf:   fsf,
		srvCh: make(chan *Server),
	}
}

func (sp *ServerPool) Add(addr string) (*Server, error) {
	if err := checkAddr(addr); err != nil {
		return nil, err
	}
	s := NewServer(addr, sp.Fsf)
	sp.Srvs = append(sp.Srvs, s)
	return s, nil
}

func (sp *ServerPool) Get(port uint16) *Server {
	for _, srv := range sp.Srvs {
		if srv.Port == port {
			return srv
		}
	}
	return nil
}

func (sp *ServerPool) Remove(port uint16) (bool, error) {
	for i, srv := range sp.Srvs {
		if srv.Port != port {
			continue
		}
		if err := srv.Close(); err != nil {
			return false, err
		}
		copy(sp.Srvs[i:], sp.Srvs[i+1:])
		sp.Srvs[len(sp.Srvs)-1] = nil
		sp.Srvs = sp.Srvs[:len(sp.Srvs)-1]
		return true, nil
	}
	return false, nil
}

func (sp *ServerPool) StartSrv(s *Server) bool {
	// check the server is registered
	if s.Running {
		return false
	}
	for _, srv := range sp.Srvs {
		if srv == s {
			sp.srvCh <- s
			return true
		}
	}
	return false
}

func (sp *ServerPool) ListenAndRun() {
	for _, srv := range sp.Srvs {
		go func(s *Server) {
			// Ignore errClosing errors. See https://code.google.com/p/go/issues/detail?id=4373
			s.ListenAndServe()
		}(srv)
	}
	for srv := range sp.srvCh {
		go func(s *Server) {
			// Ignore errClosing errors. See https://code.google.com/p/go/issues/detail?id=4373
			s.ListenAndServe()
		}(srv)
	}
}

func checkAddr(addr string) error {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return ln.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strconv"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/joho/godotenv"
	"github.com/urfave/cli"
)

var env *string
var (
	version             string
	compileDate         string
	name                string
	listenChannelPrefix string
	cmdSlave            = cli.Command{
		Name:    "slave",
		Usage:   "start gusher.slave server",
		Aliases: []string{"sl"},
		Action:  slave,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "env-file,e",
				Usage: "import env file",
			},
			cli.BoolFlag{
				Name:  "debug,d",
				Usage: "open debug mode",
			},
		},
	}
	cmdMaster = cli.Command{
		Name:    "master",
		Usage:   "start gusher.master server",
		Action:  master,
		Aliases: []string{"ma"},
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "env-file,e",
				Usage: "import env file",
			},
			cli.BoolFlag{
				Name:  "debug,d",
				Usage: "open debug mode",
			},
		},
	}
	logger          *Logger
	masterMsgFormat = "\nmaster mode start at \"{{.GetStartTime}}\"\tserver ip:\"{{.ExternalIp}}\"\tversion:\"{{.Version}}\"\tcomplie at \"{{.CompileDate}}\"\n" +
		"api_listen:\"{{.ApiListen}}\"\tapi_preifx:\"{{.ApiPrefix}}\"\n" +
		"redis_server_addr:\"{{.RedisAddr}}\"\n" +
		"redis_server_max_idle:\"{{.RedisMaxIdle}}\"\n" +
		"redis_server_max_conn:\"{{.RedisMaxConn}}\"\n" +
		"public_key_location:\"{{.PublicKeyLocation}}\"\n\n"
	slaveMsgFormat = "\nslave mode start at \"{{.GetStartTime}}\"\tserver ip:\"{{.ExternalIp}}\"\tversion:\"{{.Version}}\"\tcomplie at \"{{.CompileDate}}\"\n" +
		"api_listen:\"{{.ApiListen}}\"\tapi_preifx:\"{{.ApiPrefix}}\"\n" +
		"read_buffer:\"{{.ReadBuffer}}\"\twrite_buffer:\"{{.WriteBuffer}}\"\tmax_message_size:\"{{.MaxMessage}}\"\tscan_interval:\"{{.ScanInterval}}\"\n" +
		"redis_server_addr:\"{{.RedisAddr}}\"\n" +
		"redis_server_max_idle:\"{{.RedisMaxIdle}}\"\n" +
		"redis_server_max_conn:\"{{.RedisMaxConn}}\"\n" +
		"decode_service_addr:\"{{.DecodeServiceAddr}}\"\n\n"
)

func init() {
	listenChannelPrefix = name + "." + version + "."
	/*logger init*/
	logger = GetLogger()
}
func getSlaveConfig(c *cli.Context) (sc SlaveConfig) {
	sc = SlaveConfig{}
	envInit(c)
	sc.RedisAddr = os.Getenv("GUSHER_REDIS_ADDR")
	if sc.RedisAddr == "" {
		logger.Fatal("empty env GUSHER_REDIS_ADDR")
	}
	var err error
	sc.RedisMaxIdle, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_IDLE"))
	if err != nil {
		sc.RedisMaxIdle = 80
	}
	sc.RedisMaxConn, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_CONN"))
	if err != nil {
		sc.RedisMaxConn = 800
	}
	sc.ApiListen = os.Getenv("GUSHER_API_LISTEN")
	if sc.ApiListen == "" {
		logger.Fatal("empty env GUSHER_API_LISTEN")
	}
	sc.ApiPrefix = os.Getenv("GUSHER_API_URI_PREFIX")
	if sc.ApiPrefix == "" {
		logger.Fatal("empty env GUSHER_API_URI_PREIFX")
	}
	scanInterval, err := strconv.Atoi(os.Getenv("GUSHER_SCAN_INTERVAL"))
	if err != nil {
		scanInterval = 30
	}
	sc.ScanInterval = time.Duration(scanInterval) * time.Second
	sc.MaxMessage, err = strconv.Atoi(os.Getenv("GUSHER_RECEIVE_MAX_MESSAGE_SIZE"))
	if err != nil {
		sc.MaxMessage = 512
	}
	sc.ReadBuffer, err = strconv.Atoi(os.Getenv("GUSHER_READ_BUFFER"))
	if err != nil {
		sc.ReadBuffer = 1024
	}
	sc.WriteBuffer, err = strconv.Atoi(os.Getenv("GUSHER_WRITE_BUFFER"))
	if err != nil {
		sc.WriteBuffer = 8192
	}
	sc.DecodeServiceAddr = os.Getenv("GUSHER_DECODE_SERVICE")
	if sc.DecodeServiceAddr == "" {
		logger.Fatal("empty env GUSHER_DECODE_SERVICE")
	}
	sc.StartTime = time.Now()
	sc.CompileDate = compileDate
	sc.Version = version
	sc.ExternalIp, err = GetExternalIP()
	if err != nil {
		logger.Fatal("cant get ip")
	}
	return
}
func getMasterConfig(c *cli.Context) (mc MasterConfig) {
	envInit(c)
	mc = MasterConfig{}
	mc.PublicKeyLocation = os.Getenv("GUSHER_PUBLIC_PEM_FILE")
	if mc.PublicKeyLocation == "" {
		logger.Fatal("empty env GUSHER_PUBLIC_PEM_FILE")
	}
	mc.RedisAddr = os.Getenv("GUSHER_REDIS_ADDR")
	if mc.RedisAddr == "" {
		logger.Fatal("empty env GUSHER_REDIS_ADDR")
	}
	var err error
	mc.RedisMaxIdle, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_IDLE"))
	if err != nil {
		mc.RedisMaxIdle = 10
	}
	mc.RedisMaxConn, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_CONN"))
	if err != nil {
		mc.RedisMaxConn = 100
	}
	mc.ApiListen = os.Getenv("GUSHER_MASTER_API_LISTEN")
	if mc.ApiListen == "" {
		logger.Fatal("empty env GUSHER_MASTER_API_LISTEN")
	}
	mc.ApiPrefix = os.Getenv("GUSHER_MASTER_URI_PREFIX")
	if mc.ApiPrefix == "" {
		logger.Fatal("empty env GUSHER_MASTER_URI_PREFIX")
	}
	mc.StartTime = time.Now()
	mc.CompileDate = compileDate
	mc.Version = version
	mc.ExternalIp, err = GetExternalIP()
	if err != nil {
		logger.Fatal("cant get ip")
	}
	return
}

func envInit(c *cli.Context) {
	/*env init*/
	if c.String("env-file") != "" {
		envfile := c.String("env-file")
		//flag.Parse()
		err := godotenv.Load(envfile)
		if err != nil {
			logger.Fatal(err)
		}
	}

	if c.Bool("debug") {
		logger.Logger.Level = logrus.DebugLevel
	} else {
		logger.Logger.Level = logrus.InfoLevel
	}

}

func main() {
	cli.AppHelpTemplate += "\nWEBSITE:\n\t\thttps://github.com/syhlion/gusher.cluster\n\n"
	gusher := cli.NewApp()
	gusher.Name = name
	gusher.Author = "Scott (syhlion)"
	gusher.Usage = "websocket push server"
	gusher.UsageText = "very simple to use http request push message to websocket and very easy to scale"
	gusher.Version = version
	gusher.Compiled = time.Now()
	gusher.Commands = []cli.Command{
		cmdSlave,
		cmdMaster,
	}
	gusher.Run(os.Args)

}
<commit_msg>fix server usage msg<commit_after>package main

import (
	"os"
	"strconv"
	"time"

	"github.com/Sirupsen/logrus"

	"github.com/joho/godotenv"
	"github.com/urfave/cli"
)

var env *string
var (
	version             string
	compileDate         string
	name                string
	listenChannelPrefix string
	cmdSlave            = cli.Command{
		Name:    "slave",
		Usage:   "start gusher.slave server",
		Aliases: []string{"sl"},
		Action:  slave,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "env-file,e",
				Usage: "import env file",
			},
			cli.BoolFlag{
				Name:  "debug,d",
				Usage: "open debug mode",
			},
		},
	}
	cmdMaster = cli.Command{
		Name:    "master",
		Usage:   "start gusher.master server",
		Action:  master,
		Aliases: []string{"ma"},
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "env-file,e",
				Usage: "import env file",
			},
			cli.BoolFlag{
				Name:  "debug,d",
				Usage: "open debug mode",
			},
		},
	}
	logger          *Logger
	masterMsgFormat = "\nmaster mode start at \"{{.GetStartTime}}\"\tserver ip:\"{{.ExternalIp}}\"\tversion:\"{{.Version}}\"\tcomplie at \"{{.CompileDate}}\"\n" +
		"api_listen:\"{{.ApiListen}}\"\tapi_preifx:\"{{.ApiPrefix}}\"\n" +
		"redis_server_addr:\"{{.RedisAddr}}\"\n" +
		"redis_server_max_idle:\"{{.RedisMaxIdle}}\"\n" +
		"redis_server_max_conn:\"{{.RedisMaxConn}}\"\n" +
		"public_key_location:\"{{.PublicKeyLocation}}\"\n\n"
	slaveMsgFormat = "\nslave mode start at \"{{.GetStartTime}}\"\tserver ip:\"{{.ExternalIp}}\"\tversion:\"{{.Version}}\"\tcomplie at \"{{.CompileDate}}\"\n" +
		"api_listen:\"{{.ApiListen}}\"\tapi_preifx:\"{{.ApiPrefix}}\"\n" +
		"read_buffer:\"{{.ReadBuffer}}\"\twrite_buffer:\"{{.WriteBuffer}}\"\tmax_message_size:\"{{.MaxMessage}}\"\tscan_interval:\"{{.ScanInterval}}\"\n" +
		"redis_server_addr:\"{{.RedisAddr}}\"\n" +
		"redis_server_max_idle:\"{{.RedisMaxIdle}}\"\n" +
		"redis_server_max_conn:\"{{.RedisMaxConn}}\"\n" +
		"decode_service_addr:\"{{.DecodeServiceAddr}}\"\n\n"
)

func init() {
	listenChannelPrefix = name + "." + version + "."
	/*logger init*/
	logger = GetLogger()
}
func getSlaveConfig(c *cli.Context) (sc SlaveConfig) {
	sc = SlaveConfig{}
	envInit(c)
	sc.RedisAddr = os.Getenv("GUSHER_REDIS_ADDR")
	if sc.RedisAddr == "" {
		logger.Fatal("empty env GUSHER_REDIS_ADDR")
	}
	var err error
	sc.RedisMaxIdle, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_IDLE"))
	if err != nil {
		sc.RedisMaxIdle = 80
	}
	sc.RedisMaxConn, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_CONN"))
	if err != nil {
		sc.RedisMaxConn = 800
	}
	sc.ApiListen = os.Getenv("GUSHER_API_LISTEN")
	if sc.ApiListen == "" {
		logger.Fatal("empty env GUSHER_API_LISTEN")
	}
	sc.ApiPrefix = os.Getenv("GUSHER_API_URI_PREFIX")
	if sc.ApiPrefix == "" {
		logger.Fatal("empty env GUSHER_API_URI_PREIFX")
	}
	scanInterval, err := strconv.Atoi(os.Getenv("GUSHER_SCAN_INTERVAL"))
	if err != nil {
		scanInterval = 30
	}
	sc.ScanInterval = time.Duration(scanInterval) * time.Second
	sc.MaxMessage, err = strconv.Atoi(os.Getenv("GUSHER_RECEIVE_MAX_MESSAGE_SIZE"))
	if err != nil {
		sc.MaxMessage = 512
	}
	sc.ReadBuffer, err = strconv.Atoi(os.Getenv("GUSHER_READ_BUFFER"))
	if err != nil {
		sc.ReadBuffer = 1024
	}
	sc.WriteBuffer, err = strconv.Atoi(os.Getenv("GUSHER_WRITE_BUFFER"))
	if err != nil {
		sc.WriteBuffer = 8192
	}
	sc.DecodeServiceAddr = os.Getenv("GUSHER_DECODE_SERVICE")
	if sc.DecodeServiceAddr == "" {
		logger.Fatal("empty env GUSHER_DECODE_SERVICE")
	}
	sc.StartTime = time.Now()
	sc.CompileDate = compileDate
	sc.Version = version
	sc.ExternalIp, err = GetExternalIP()
	if err != nil {
		logger.Fatal("cant get ip")
	}
	return
}
func getMasterConfig(c *cli.Context) (mc MasterConfig) {
	envInit(c)
	mc = MasterConfig{}
	mc.PublicKeyLocation = os.Getenv("GUSHER_PUBLIC_PEM_FILE")
	if mc.PublicKeyLocation == "" {
		logger.Fatal("empty env GUSHER_PUBLIC_PEM_FILE")
	}
	mc.RedisAddr = os.Getenv("GUSHER_REDIS_ADDR")
	if mc.RedisAddr == "" {
		logger.Fatal("empty env GUSHER_REDIS_ADDR")
	}
	var err error
	mc.RedisMaxIdle, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_IDLE"))
	if err != nil {
		mc.RedisMaxIdle = 10
	}
	mc.RedisMaxConn, err = strconv.Atoi(os.Getenv("GUSHER_REDIS_MAX_CONN"))
	if err != nil {
		mc.RedisMaxConn = 100
	}
	mc.ApiListen = os.Getenv("GUSHER_MASTER_API_LISTEN")
	if mc.ApiListen == "" {
		logger.Fatal("empty env GUSHER_MASTER_API_LISTEN")
	}
	mc.ApiPrefix = os.Getenv("GUSHER_MASTER_URI_PREFIX")
	if mc.ApiPrefix == "" {
		logger.Fatal("empty env GUSHER_MASTER_URI_PREFIX")
	}
	mc.StartTime = time.Now()
	mc.CompileDate = compileDate
	mc.Version = version
	mc.ExternalIp, err = GetExternalIP()
	if err != nil {
		logger.Fatal("cant get ip")
	}
	return
}

func envInit(c *cli.Context) {
	/*env init*/
	if c.String("env-file") != "" {
		envfile := c.String("env-file")
		//flag.Parse()
		err := godotenv.Load(envfile)
		if err != nil {
			logger.Fatal(err)
		}
	}

	if c.Bool("debug") {
		logger.Logger.Level = logrus.DebugLevel
	} else {
		logger.Logger.Level = logrus.InfoLevel
	}

}

func main() {
	cli.AppHelpTemplate += "\nWEBSITE:\n\t\thttps://github.com/syhlion/gusher.cluster\n\n"
	gusher := cli.NewApp()
	gusher.Name = name
	gusher.Author = "Scott (syhlion)"
	gusher.Usage = "very simple to use http request push message to websocket and very easy to scale"
	gusher.UsageText = "gusher.cluster [slave|master] [-e envfile] [-d]"
	gusher.Version = version
	gusher.Compiled = time.Now()
	gusher.Commands = []cli.Command{
		cmdSlave,
		cmdMaster,
	}
	gusher.Run(os.Args)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"time"
)

func nextPort() int {
	l, err := net.Listen("tcp4", ":0")
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	x, _ := l.Addr().(*net.TCPAddr)

	return x.Port
}

func clean(complete chan bool, c *exec.Cmd) {
	for {
		select {
		case <-complete:
			{
				go func() {
					time.Sleep(100)
					/*
					 *cmd := exec.Command("kill", fmt.Sprintf("%d", c.Process.Pid))
					 *if err := cmd.Run(); err != nil {
					 *  log.Fatal(err)
					 *  return
					 *}
					 */
					log.Printf("cleaning %d.\n", c.Process.Pid)
				}()
			}
		}
	}
}

func phpHandler(script string, w http.ResponseWriter, r *http.Request) {
	port := nextPort()
	log.Println(port)
	args := []string{"-S", fmt.Sprintf("0.0.0.0:%d", port), script}

	complete := make(chan bool)

	log.Printf("child request url http://localhost:%d%s\n", port, r.RequestURI)
	log.Println("starting server")
	go func(complete chan bool) {
		cmd := exec.Command("php", args...)
		go clean(complete, cmd)
		if err := cmd.Run(); err != nil {
			log.Fatal(err)
			return
		}

	}(complete)

	time.Sleep(300 * time.Millisecond)

	req, err := http.NewRequest(r.Method, fmt.Sprintf("http://localhost:%d%s", port, r.RequestURI), r.Body)
	if err != nil {
		log.Fatal(err)
		return
	}

	client := &http.Client{}
	req.Header = r.Header

	log.Println("Making request")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
		return
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
		return
	}

	for key := range resp.Header {
		w.Header().Set(key, resp.Header.Get(key))
	}

	w.Write(data)
	complete <- true
}

func main() {
	if len(os.Args) < 1 {
		fmt.Println("Usage server filename")
		return
	}
	file := os.Args[1]
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		phpHandler(file, w, r)
	})
	http.ListenAndServe(":10000", nil)
}
<commit_msg>port is a param<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"time"
)

func nextPort() int {
	l, err := net.Listen("tcp4", ":0")
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	x, _ := l.Addr().(*net.TCPAddr)

	return x.Port
}

func clean(complete chan bool, c *exec.Cmd) {
	for {
		select {
		case <-complete:
			{
				go func() {
					time.Sleep(100)
					/*
					 *cmd := exec.Command("kill", fmt.Sprintf("%d", c.Process.Pid))
					 *if err := cmd.Run(); err != nil {
					 *  log.Fatal(err)
					 *  return
					 *}
					 */
					log.Printf("cleaning %d.\n", c.Process.Pid)
				}()
			}
		}
	}
}

func phpHandler(script string, w http.ResponseWriter, r *http.Request) {
	port := nextPort()
	log.Println(port)
	args := []string{"-S", fmt.Sprintf("0.0.0.0:%d", port), script}

	complete := make(chan bool)

	log.Printf("child request url http://localhost:%d%s\n", port, r.RequestURI)
	log.Println("starting server")
	go func(complete chan bool) {
		cmd := exec.Command("php", args...)
		go clean(complete, cmd)
		if err := cmd.Run(); err != nil {
			log.Fatal(err)
			return
		}

	}(complete)

	time.Sleep(300 * time.Millisecond)

	req, err := http.NewRequest(r.Method, fmt.Sprintf("http://localhost:%d%s", port, r.RequestURI), r.Body)
	if err != nil {
		log.Fatal(err)
		return
	}

	client := &http.Client{}
	req.Header = r.Header

	log.Println("Making request")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
		return
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
		return
	}

	for key := range resp.Header {
		w.Header().Set(key, resp.Header.Get(key))
	}

	w.Write(data)
	complete <- true
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage server port filename ")
		return
	}
	file := os.Args[2]
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		phpHandler(file, w, r)
	})
	port := os.Args[1]
	http.ListenAndServe(":"+port, nil)
}
<|endoftext|>


<commit_before>package main

import (
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
  "github.com/codegangsta/martini"
  "encoding/json"
  "strconv"
)

func main() {
  m := martini.Classic()

  db, err := sql.Open("mysql", "root@127.0.0.1/ops")
  if err != nil { panic(err) }
  defer db.Close()

  m.Map(db)

  schoolOne := School{Id: 1, Name: "Millard North High School", CountyId: 1, DistrictId: 1}
  schoolTwo := School{Id: 2, Name: "Millard South High School", CountyId: 1, DistrictId: 1}
  schools := []School{schoolOne, schoolTwo}

  m.Get("/schools", func() string {
    llSchoolsJ, err := json.Marshal(schools)
    if err != nil { panic(err) }
    return string(llSchoolsJ[:])
  })

  m.Get("/schools/:id", func(params martini.Params) string {
    school := schoolFind(schools, params["id"])
    llSchoolJ, err := json.Marshal(school)
    if err != nil { panic(err) }
    return string(llSchoolJ[:])
  })

  m.Run()
}

func schoolFind(schools []School, id string) School {
  schoolId, err := strconv.ParseInt(id, 0, 64)
  if err != nil { panic(err) }

  for _, value := range schools {
    if value.Id == schoolId {
      return value
    }
  }

  return School{}
}

type School struct {
  Id          int64
  Name        string `sql:"size:255"`
  CountyId    int64
  DistrictId  int64
}
<commit_msg>Fix that crappy Go code<commit_after>package main

import (
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
  "github.com/codegangsta/martini"
  "encoding/json"
  "strconv"
)

func main() {
  m := martini.Classic()

  db, err := sql.Open("mysql", "root@127.0.0.1/ops")
  if err != nil { panic(err) }
  defer db.Close()

  m.Map(db)

  schoolOne := School{
    Id: 1,
    Name: "Millard North High School",
    CountyId: 1,
    DistrictId: 1,
    ClassStats: []ClassStat{
      ClassStat{
        SchoolId: 1,
        Years: "2012-2013",
        Grade: "6",
        MaleStudents: "10",
        FemaleStudents: "15",
        TotalStudents: "25",
      },
    },
  }
  schoolTwo := School{Id: 2, Name: "Millard South High School", CountyId: 1, DistrictId: 1}
  schools := []School{schoolOne, schoolTwo}

  entry2012 := DistrictYear{
    EnrollmentSize: 15,
    District: District{
      Id: 15,
      Name: "OPS",
      Latitude: 72.12345,
      Longitude: 45.215,
    },
  }

  allDistricts := []DistrictsByYear{
    DistrictsByYear{
      Year: "2012-2013",
      Districts: []DistrictYear{entry2012},
    },
    DistrictsByYear{
      Year: "2011-2012",
      Districts: []DistrictYear{entry2012},
    },
  }

  m.Get("/schools", func() string {
    llSchoolsJ, err := json.Marshal(schools)
    if err != nil { panic(err) }
    return string(llSchoolsJ[:])
  })

  m.Get("/districts", func() string {
    district, err := json.Marshal(allDistricts)
    if err != nil { panic(err) }
    return string(district[:])
  })

  m.Get("/schools/:id", func(params martini.Params) string {
    school := schoolFind(schools, params["id"])
    llSchoolJ, err := json.Marshal(school)
    if err != nil { panic(err) }
    return string(llSchoolJ[:])
  })

  m.Get("/schools/:id/:year", func(params martini.Params) string {
    return "WOOOOO"
  })
  m.Run()
}

func schoolFind(schools []School, id string) School {
  schoolId, err := strconv.ParseInt(id, 0, 64)
  if err != nil { panic(err) }

  for _, value := range schools {
    if value.Id == schoolId {
      return value
    }
  }

  return School{}
}

type School struct {
  Id          int64
  Name        string `sql:"size:255"`
  CountyId    int64
  DistrictId  int64
  ClassStats  []ClassStat
}

type District struct {
  Id              int64
  Name            string
  Latitude        float64
  Longitude       float64
}

type DistrictsByYear struct {
  Year      string
  Districts []DistrictYear
}

type DistrictYear struct {
  EnrollmentSize  int64
  District        District
}

type ClassStat struct {
  SchoolId        int64
  Years           string
  Grade           string
  MaleStudents    string
  FemaleStudents  string
  TotalStudents   string
}
<|endoftext|>


<commit_before>package air

import (
	"context"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"golang.org/x/crypto/acme/autocert"
)

// server is an HTTP server.
type server struct {
	server     *http.Server
	h2hsServer *http.Server
}

// theServer is the singleton of the `server`.
var theServer = &server{
	server: &http.Server{},
}

// serve starts the s.
func (s *server) serve() error {
	s.server.Addr = Address
	s.server.Handler = s
	s.server.ReadTimeout = ReadTimeout
	s.server.ReadHeaderTimeout = ReadHeaderTimeout
	s.server.WriteTimeout = WriteTimeout
	s.server.IdleTimeout = IdleTimeout
	s.server.MaxHeaderBytes = MaxHeaderBytes
	s.server.ErrorLog = log.New(&serverErrorLogWriter{}, "air: ", 0)

	if DebugMode {
		LoggerLowestLevel = LoggerLevelDebug
		DEBUG("air: serving in debug mode")
	}

	if TLSCertFile == "" || TLSKeyFile == "" {
		return s.server.ListenAndServe()
	}

	host := s.server.Addr
	if strings.Contains(host, ":") {
		var err error
		if host, _, err = net.SplitHostPort(host); err != nil {
			return err
		}
	}

	h2hss := &http.Server{
		Addr: host + ":http",
		Handler: http.HandlerFunc(func(
			rw http.ResponseWriter,
			r *http.Request,
		) {
			host, _, err := net.SplitHostPort(r.Host)
			if err != nil {
				host = r.Host
			}

			http.Redirect(rw, r, "https://"+host+r.RequestURI, 301)
		}),
	}

	tlsCertFile, tlsKeyFile := TLSCertFile, TLSKeyFile
	if tlsCertFile == "Let's Encrypt" && tlsKeyFile == tlsCertFile {
		acm := autocert.Manager{
			Prompt: autocert.AcceptTOS,
			Cache:  autocert.DirCache(ACMECertRoot),
		}

		if len(HostWhitelist) > 0 {
			acm.HostPolicy = autocert.HostWhitelist(
				HostWhitelist...,
			)
		}

		if MaintainerEmail != "" {
			acm.Email = MaintainerEmail
		}

		s.server.Addr = host + ":https"
		s.server.TLSConfig = acm.TLSConfig()

		h2hss.Handler = acm.HTTPHandler(h2hss.Handler)
		s.h2hsServer = h2hss

		tlsCertFile, tlsKeyFile = "", ""
	} else if HTTPSEnforced {
		s.h2hsServer = h2hss
	}

	if s.h2hsServer != nil {
		go s.h2hsServer.ListenAndServe()
	}

	return s.server.ListenAndServeTLS(tlsCertFile, tlsKeyFile)
}

// close closes the s immediately.
func (s *server) close() error {
	if s.h2hsServer != nil {
		s.h2hsServer.Close()
	}

	return s.server.Close()
}

// shutdown gracefully shuts down the s without interrupting any active
// connections until timeout. It waits indefinitely for connections to return to
// idle and then shut down when the timeout is less than or equal to zero.
func (s *server) shutdown(timeout time.Duration) error {
	if s.h2hsServer != nil {
		s.h2hsServer.Close()
	}

	if timeout <= 0 {
		return s.server.Shutdown(context.Background())
	}

	c, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	return s.server.Shutdown(c)
}

// ServeHTTP implements the `http.Handler`.
func (s *server) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	// Check host

	if len(HostWhitelist) > 0 {
		host, _, err := net.SplitHostPort(r.Host)
		if err != nil {
			host = r.Host
		}

		allowed := false
		for _, h := range HostWhitelist {
			if h == host {
				allowed = true
				break
			}
		}

		if !allowed {
			scheme := "http"
			if r.TLS != nil {
				scheme = "https"
			}

			http.Redirect(
				rw,
				r,
				scheme+"://"+HostWhitelist[0]+r.RequestURI,
				301,
			)

			return
		}
	}

	// Request

	req := &Request{
		Method:        r.Method,
		Scheme:        "http",
		Authority:     r.Host,
		Path:          r.RequestURI,
		Headers:       make(map[string]*Header, len(r.Header)),
		Body:          r.Body,
		ContentLength: r.ContentLength,
		Cookies:       map[string]*Cookie{},
		Params: make(
			map[string]*RequestParam,
			theRouter.maxParams,
		),
		RemoteAddress: r.RemoteAddr,
		ClientAddress: r.RemoteAddr,
		Values:        map[string]interface{}{},

		request:          r,
		parseCookiesOnce: &sync.Once{},
		parseParamsOnce:  &sync.Once{},
	}

	if r.TLS != nil {
		req.Scheme = "https"
	}

	for n, vs := range r.Header {
		h := &Header{
			Name:   strings.ToLower(n),
			Values: vs,
		}

		req.Headers[h.Name] = h
	}

	if f := req.Headers["forwarded"].Value(); f != "" { // See RFC 7239
		for _, p := range strings.Split(strings.Split(f, ",")[0], ";") {
			p := strings.TrimSpace(p)
			if strings.HasPrefix(p, "for=") {
				req.ClientAddress = strings.TrimSuffix(
					strings.TrimPrefix(p[4:], "\"["),
					"]\"",
				)
				break
			}
		}
	} else if xff := req.Headers["x-forwarded-for"].Value(); xff != "" {
		req.ClientAddress = strings.TrimSpace(
			strings.Split(xff, ",")[0],
		)
	}

	theI18n.localize(req)

	// Response

	res := &Response{
		Status:  200,
		Headers: map[string]*Header{},
		Cookies: map[string]*Cookie{},

		request: req,
		writer:  rw,
	}
	res.Body = &responseBody{
		response: res,
	}

	// Chain gases

	h := func(req *Request, res *Response) error {
		rh := theRouter.route(req)
		h := func(req *Request, res *Response) error {
			if err := rh(req, res); err != nil {
				return err
			} else if !res.Written {
				return res.Write(nil)
			}

			return nil
		}

		req.ParseCookies()
		req.ParseParams()

		for i := len(Gases) - 1; i >= 0; i-- {
			h = Gases[i](h)
		}

		return h(req, res)
	}

	// Chain pregases

	for i := len(Pregases) - 1; i >= 0; i-- {
		h = Pregases[i](h)
	}

	// Execute chain

	if err := h(req, res); err != nil {
		ErrorHandler(err, req, res)
	}

	// Close opened request param file values

	for _, p := range req.Params {
		for _, pv := range p.Values {
			if pv.f != nil && pv.f.f != nil {
				pv.f.f.Close()
			}
		}
	}
}

// serverErrorLogWriter is an HTTP server error log writer.
type serverErrorLogWriter struct{}

// Write implements the `io.Writer`.
func (selw *serverErrorLogWriter) Write(b []byte) (int, error) {
	ERROR(string(b))
	return len(b), nil
}
<commit_msg>fix: check host case-insensitively<commit_after>package air

import (
	"context"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"golang.org/x/crypto/acme/autocert"
)

// server is an HTTP server.
type server struct {
	server     *http.Server
	h2hsServer *http.Server
}

// theServer is the singleton of the `server`.
var theServer = &server{
	server: &http.Server{},
}

// serve starts the s.
func (s *server) serve() error {
	s.server.Addr = Address
	s.server.Handler = s
	s.server.ReadTimeout = ReadTimeout
	s.server.ReadHeaderTimeout = ReadHeaderTimeout
	s.server.WriteTimeout = WriteTimeout
	s.server.IdleTimeout = IdleTimeout
	s.server.MaxHeaderBytes = MaxHeaderBytes
	s.server.ErrorLog = log.New(&serverErrorLogWriter{}, "air: ", 0)

	if DebugMode {
		LoggerLowestLevel = LoggerLevelDebug
		DEBUG("air: serving in debug mode")
	}

	if TLSCertFile == "" || TLSKeyFile == "" {
		return s.server.ListenAndServe()
	}

	host := s.server.Addr
	if strings.Contains(host, ":") {
		var err error
		if host, _, err = net.SplitHostPort(host); err != nil {
			return err
		}
	}

	h2hss := &http.Server{
		Addr: host + ":http",
		Handler: http.HandlerFunc(func(
			rw http.ResponseWriter,
			r *http.Request,
		) {
			host, _, err := net.SplitHostPort(r.Host)
			if err != nil {
				host = r.Host
			}

			http.Redirect(rw, r, "https://"+host+r.RequestURI, 301)
		}),
	}

	tlsCertFile, tlsKeyFile := TLSCertFile, TLSKeyFile
	if tlsCertFile == "Let's Encrypt" && tlsKeyFile == tlsCertFile {
		acm := autocert.Manager{
			Prompt: autocert.AcceptTOS,
			Cache:  autocert.DirCache(ACMECertRoot),
		}

		if len(HostWhitelist) > 0 {
			acm.HostPolicy = autocert.HostWhitelist(
				HostWhitelist...,
			)
		}

		if MaintainerEmail != "" {
			acm.Email = MaintainerEmail
		}

		s.server.Addr = host + ":https"
		s.server.TLSConfig = acm.TLSConfig()

		h2hss.Handler = acm.HTTPHandler(h2hss.Handler)
		s.h2hsServer = h2hss

		tlsCertFile, tlsKeyFile = "", ""
	} else if HTTPSEnforced {
		s.h2hsServer = h2hss
	}

	if s.h2hsServer != nil {
		go s.h2hsServer.ListenAndServe()
	}

	return s.server.ListenAndServeTLS(tlsCertFile, tlsKeyFile)
}

// close closes the s immediately.
func (s *server) close() error {
	if s.h2hsServer != nil {
		s.h2hsServer.Close()
	}

	return s.server.Close()
}

// shutdown gracefully shuts down the s without interrupting any active
// connections until timeout. It waits indefinitely for connections to return to
// idle and then shut down when the timeout is less than or equal to zero.
func (s *server) shutdown(timeout time.Duration) error {
	if s.h2hsServer != nil {
		s.h2hsServer.Close()
	}

	if timeout <= 0 {
		return s.server.Shutdown(context.Background())
	}

	c, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	return s.server.Shutdown(c)
}

// ServeHTTP implements the `http.Handler`.
func (s *server) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	// Check host

	if len(HostWhitelist) > 0 {
		host, _, err := net.SplitHostPort(r.Host)
		if err != nil {
			host = r.Host
		}

		// See RFC 3986, section 3.2.2.
		if !stringsContainsCIly(HostWhitelist, host) {
			scheme := "http"
			if r.TLS != nil {
				scheme = "https"
			}

			http.Redirect(
				rw,
				r,
				scheme+"://"+HostWhitelist[0]+r.RequestURI,
				301,
			)

			return
		}
	}

	// Request

	req := &Request{
		Method:        r.Method,
		Scheme:        "http",
		Authority:     r.Host,
		Path:          r.RequestURI,
		Headers:       make(map[string]*Header, len(r.Header)),
		Body:          r.Body,
		ContentLength: r.ContentLength,
		Cookies:       map[string]*Cookie{},
		Params: make(
			map[string]*RequestParam,
			theRouter.maxParams,
		),
		RemoteAddress: r.RemoteAddr,
		ClientAddress: r.RemoteAddr,
		Values:        map[string]interface{}{},

		request:          r,
		parseCookiesOnce: &sync.Once{},
		parseParamsOnce:  &sync.Once{},
	}

	if r.TLS != nil {
		req.Scheme = "https"
	}

	for n, vs := range r.Header {
		h := &Header{
			Name:   strings.ToLower(n),
			Values: vs,
		}

		req.Headers[h.Name] = h
	}

	if f := req.Headers["forwarded"].Value(); f != "" { // See RFC 7239
		for _, p := range strings.Split(strings.Split(f, ",")[0], ";") {
			p := strings.TrimSpace(p)
			if strings.HasPrefix(p, "for=") {
				req.ClientAddress = strings.TrimSuffix(
					strings.TrimPrefix(p[4:], "\"["),
					"]\"",
				)
				break
			}
		}
	} else if xff := req.Headers["x-forwarded-for"].Value(); xff != "" {
		req.ClientAddress = strings.TrimSpace(
			strings.Split(xff, ",")[0],
		)
	}

	theI18n.localize(req)

	// Response

	res := &Response{
		Status:  200,
		Headers: map[string]*Header{},
		Cookies: map[string]*Cookie{},

		request: req,
		writer:  rw,
	}
	res.Body = &responseBody{
		response: res,
	}

	// Chain gases

	h := func(req *Request, res *Response) error {
		rh := theRouter.route(req)
		h := func(req *Request, res *Response) error {
			if err := rh(req, res); err != nil {
				return err
			} else if !res.Written {
				return res.Write(nil)
			}

			return nil
		}

		req.ParseCookies()
		req.ParseParams()

		for i := len(Gases) - 1; i >= 0; i-- {
			h = Gases[i](h)
		}

		return h(req, res)
	}

	// Chain pregases

	for i := len(Pregases) - 1; i >= 0; i-- {
		h = Pregases[i](h)
	}

	// Execute chain

	if err := h(req, res); err != nil {
		ErrorHandler(err, req, res)
	}

	// Close opened request param file values

	for _, p := range req.Params {
		for _, pv := range p.Values {
			if pv.f != nil && pv.f.f != nil {
				pv.f.f.Close()
			}
		}
	}
}

// serverErrorLogWriter is an HTTP server error log writer.
type serverErrorLogWriter struct{}

// Write implements the `io.Writer`.
func (selw *serverErrorLogWriter) Write(b []byte) (int, error) {
	ERROR(string(b))
	return len(b), nil
}
<|endoftext|>


<commit_before>package tarantool

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net"
	"sync"
)

const greetingSize = 128
const saltSize = 32
const tarantoolVersion = "Tarantool 1.6.8 (Binary)"
const connBufSize = 128 * 1024

type QueryHandler func(query Query) *Result
type OnCloseCallback func()

type IprotoServer struct {
	conn      net.Conn
	reader    *bufio.Reader
	writer    *bufio.Writer
	uuid      string
	salt      []byte // base64-encoded salt
	quit      chan bool
	handler   QueryHandler
	onClose   OnCloseCallback
	output    chan []byte
	closeOnce sync.Once
}

func NewIprotoServer(uuid string, handler QueryHandler, onClose OnCloseCallback) *IprotoServer {
	return &IprotoServer{
		conn:    nil,
		reader:  nil,
		writer:  nil,
		handler: handler,
		onClose: onClose,
		uuid:    uuid,
	}
}

func (s *IprotoServer) Accept(conn net.Conn) {
	s.conn = conn
	s.reader = bufio.NewReaderSize(conn, connBufSize)
	s.writer = bufio.NewWriterSize(conn, connBufSize)
	s.quit = make(chan bool)
	s.output = make(chan []byte, 1024)

	err := s.greet()
	if err != nil {
		s.Close()
		return
	}

	go s.loop()
}

func (s *IprotoServer) CheckAuth(hash []byte, password string) bool {
	scr, err := scramble(s.salt, password)
	if err != nil {
		return false
	}

	if len(scr) != len(hash) {
		return false
	}

	for i, v := range hash {
		if v != scr[i] {
			return false
		}
	}
	return true
}

func (s *IprotoServer) Close() {
	s.closeOnce.Do(func() {
		if s.onClose != nil {
			s.onClose()
		}
		close(s.quit)
		s.conn.Close()
	})
}

func (s *IprotoServer) greet() (err error) {
	var line1, line2 string
	var format, greeting string
	var n int

	salt := make([]byte, saltSize)
	_, err = rand.Read(salt)
	if err != nil {
		return
	}

	s.salt = []byte(base64.StdEncoding.EncodeToString(salt))

	line1 = fmt.Sprintf("%s %s", tarantoolVersion, s.uuid)
	line2 = fmt.Sprintf("%s", s.salt)

	format = fmt.Sprintf("%%-%ds\n%%-%ds\n", greetingSize/2-1, greetingSize/2-1)
	greeting = fmt.Sprintf(format, line1, line2)

	// send greeting
	n, err = fmt.Fprintf(s.writer, "%s", greeting)
	if err != nil || n != greetingSize {
		return
	}

	return s.writer.Flush()
}

func (s *IprotoServer) loop() {
	go s.read()
	go s.write()
}

func (s *IprotoServer) read() {
	var packet *Packet
	var err error
	var body []byte

	r := s.reader

READER_LOOP:
	for {
		select {
		case <-s.quit:
			break READER_LOOP
		default:
			// read raw bytes
			body, err = readMessage(r)
			if err != nil {
				break READER_LOOP
			}

			packet, err = decodePacket(bytes.NewBuffer(body))
			if err != nil {
				break READER_LOOP
			}

			if packet.request != nil {
				go func(packet *Packet) {
					var res *Result
					var code = byte(packet.code)

					if code == PingRequest {
						s.output <- packIprotoOk(packet.requestID)
					} else {
						res = s.handler(packet.request.(Query))
						body, _ = res.pack(packet.requestID)
						s.output <- body
					}
				}(packet)
			}
		}
	}

	s.Close()
}

func (s *IprotoServer) write() {
	var err error
	var n int

	w := s.writer

WRITER_LOOP:
	for {
		select {
		case messageBody, ok := <-s.output:
			if !ok {
				break WRITER_LOOP
			}
			n, err = w.Write(messageBody)
			if err != nil || n != len(messageBody) {
				break WRITER_LOOP
			}
		case <-s.quit:
			w.Flush()
			break WRITER_LOOP
		default:
			if err = w.Flush(); err != nil {
				break WRITER_LOOP
			}

			// same without flush
			select {
			case messageBody, ok := <-s.output:
				if !ok {
					break WRITER_LOOP
				}
				n, err = w.Write(messageBody)
				if err != nil || n != len(messageBody) {
					break WRITER_LOOP
				}
			case <-s.quit:
				w.Flush()
				break WRITER_LOOP
			}

		}
	}

	s.Close()
}
<commit_msg>Expose last known error in iproto server class<commit_after>package tarantool

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net"
	"sync"
)

const greetingSize = 128
const saltSize = 32
const tarantoolVersion = "Tarantool 1.6.8 (Binary)"
const connBufSize = 128 * 1024

type QueryHandler func(query Query) *Result
type OnCloseCallback func()

type IprotoServer struct {
	sync.Mutex
	conn      net.Conn
	reader    *bufio.Reader
	writer    *bufio.Writer
	uuid      string
	salt      []byte // base64-encoded salt
	quit      chan bool
	handler   QueryHandler
	onClose   OnCloseCallback
	output    chan []byte
	closeOnce sync.Once
	lastError error
}

func NewIprotoServer(uuid string, handler QueryHandler, onClose OnCloseCallback) *IprotoServer {
	return &IprotoServer{
		conn:    nil,
		reader:  nil,
		writer:  nil,
		handler: handler,
		onClose: onClose,
		uuid:    uuid,
	}
}

func (s *IprotoServer) Accept(conn net.Conn) {
	s.conn = conn
	s.reader = bufio.NewReaderSize(conn, connBufSize)
	s.writer = bufio.NewWriterSize(conn, connBufSize)
	s.quit = make(chan bool)
	s.output = make(chan []byte, 1024)

	err := s.greet()
	if err != nil {
		s.Close()
		return
	}

	go s.loop()
}

func (s *IprotoServer) CheckAuth(hash []byte, password string) bool {
	scr, err := scramble(s.salt, password)
	if err != nil {
		return false
	}

	if len(scr) != len(hash) {
		return false
	}

	for i, v := range hash {
		if v != scr[i] {
			return false
		}
	}
	return true
}

func (s *IprotoServer) GetLastError() error {
	s.Lock()
	defer s.Unlock()
	return s.lastError
}

func (s *IprotoServer) setError(err error) {
	s.Lock()
	defer s.Unlock()
	s.lastError = err
}

func (s *IprotoServer) Close() {
	s.closeOnce.Do(func() {
		if s.onClose != nil {
			s.onClose()
		}
		close(s.quit)
		s.conn.Close()
	})
}

func (s *IprotoServer) greet() (err error) {
	var line1, line2 string
	var format, greeting string
	var n int

	salt := make([]byte, saltSize)
	_, err = rand.Read(salt)
	if err != nil {
		return
	}

	s.salt = []byte(base64.StdEncoding.EncodeToString(salt))

	line1 = fmt.Sprintf("%s %s", tarantoolVersion, s.uuid)
	line2 = fmt.Sprintf("%s", s.salt)

	format = fmt.Sprintf("%%-%ds\n%%-%ds\n", greetingSize/2-1, greetingSize/2-1)
	greeting = fmt.Sprintf(format, line1, line2)

	// send greeting
	n, err = fmt.Fprintf(s.writer, "%s", greeting)
	if err != nil || n != greetingSize {
		return
	}

	return s.writer.Flush()
}

func (s *IprotoServer) loop() {
	go s.read()
	go s.write()
}

func (s *IprotoServer) read() {
	var packet *Packet
	var err error
	var body []byte

	r := s.reader

READER_LOOP:
	for {
		select {
		case <-s.quit:
			break READER_LOOP
		default:
			// read raw bytes
			body, err = readMessage(r)
			if err != nil {
				break READER_LOOP
			}

			packet, err = decodePacket(bytes.NewBuffer(body))
			if err != nil {
				break READER_LOOP
			}

			if packet.request != nil {
				go func(packet *Packet) {
					var res *Result
					var code = byte(packet.code)

					if code == PingRequest {
						s.output <- packIprotoOk(packet.requestID)
					} else {
						res = s.handler(packet.request.(Query))
						body, _ = res.pack(packet.requestID)
						s.output <- body
					}
				}(packet)
			}
		}
	}

	if err != nil {
		s.setError(err)
	}
	s.Close()
}

func (s *IprotoServer) write() {
	var err error
	var n int

	w := s.writer

WRITER_LOOP:
	for {
		select {
		case messageBody, ok := <-s.output:
			if !ok {
				break WRITER_LOOP
			}
			n, err = w.Write(messageBody)
			if err != nil || n != len(messageBody) {
				break WRITER_LOOP
			}
		case <-s.quit:
			w.Flush()
			break WRITER_LOOP
		default:
			if err = w.Flush(); err != nil {
				break WRITER_LOOP
			}

			// same without flush
			select {
			case messageBody, ok := <-s.output:
				if !ok {
					break WRITER_LOOP
				}
				n, err = w.Write(messageBody)
				if err != nil || n != len(messageBody) {
					break WRITER_LOOP
				}
			case <-s.quit:
				w.Flush()
				break WRITER_LOOP
			}

		}
	}

	if err != nil {
		s.setError(err)
	}

	s.Close()
}
<|endoftext|>


<commit_before>package webshell

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"
)

const WEBSHELL_VERSION = "1.0.0"

// Server configuration options.
var (
	SSL_KEY     string
	SSL_CERT    string
	SERVER_ADDR string
	SERVER_PORT string
)

// LoadEnv pulls in configuration data from the environment.
func LoadEnv() {
	SSL_KEY = os.Getenv("SSL_KEY")
	SSL_CERT = os.Getenv("SSL_CERT")
	SERVER_ADDR = os.Getenv("SERVER_ADDR")
	SERVER_PORT = os.Getenv("SERVER_PORT")
}

func main() {
	log.Println("starting server")
	Serve(false, nil)
}

/*
   Server handles the HTTP server set up (with option TLS setup) and
   and serving. It is a blocking function, so any additional functions that
   need to be concurrently run should be fired off via goroutines prior to
   calling Server. If tlsCfg is not nil, the server will use it as the
   TLS configuration for the server. If it is nil, the server will create
   a default configuration. This argument has no effect if the server
   will not be serving TLS requests.
*/
func Serve(doTLS bool, tlsCfg *tls.Config) {
	var serverAddress string
	if SERVER_PORT != "" {
		serverAddress = fmt.Sprintf("%s:%s", SERVER_ADDR, SERVER_PORT)
	} else {
		serverAddress = SERVER_ADDR
	}
	log.Println("server address:", serverAddress)
	srv := &http.Server{
		Addr:           serverAddress,
		Handler:        RouterMux,
		ReadTimeout:    3 * time.Second,
		WriteTimeout:   3 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	if doTLS {
		if tlsCfg == nil {
			tlsCfg = new(tls.Config)
		}
		srv.TLSConfig = tlsCfg
		log.Println("listening for incoming TLS connections")
		log.Printf("using credentials:\n\tkey: %s\n\tcert: %s\n",
			SSL_KEY, SSL_CERT)
		log.Fatalf("error in ListenAndServeTLS:\n\t%+v",
			srv.ListenAndServeTLS(SSL_CERT, SSL_KEY))
	} else {
		log.Println("listening for incoming HTTP connections")
		log.Fatalf("error in ListenAndServe:\n\t%+v\n",
			srv.ListenAndServe())
	}
}
<commit_msg>add error initialisation to Serve<commit_after>package webshell

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"
)

const WEBSHELL_VERSION = "1.0.0"

// Server configuration options.
var (
	SSL_KEY     string
	SSL_CERT    string
	SERVER_ADDR string
	SERVER_PORT string
)

// LoadEnv pulls in configuration data from the environment.
func LoadEnv() {
	SSL_KEY = os.Getenv("SSL_KEY")
	SSL_CERT = os.Getenv("SSL_CERT")
	SERVER_ADDR = os.Getenv("SERVER_ADDR")
	SERVER_PORT = os.Getenv("SERVER_PORT")
}

func main() {
	log.Println("starting server")
	Serve(false, nil)
}

/*
   Server handles the HTTP server set up (with option TLS setup) and
   and serving. It is a blocking function, so any additional functions that
   need to be concurrently run should be fired off via goroutines prior to
   calling Server. If tlsCfg is not nil, the server will use it as the
   TLS configuration for the server. If it is nil, the server will create
   a default configuration. This argument has no effect if the server
   will not be serving TLS requests.
*/
func Serve(doTLS bool, tlsCfg *tls.Config) {
        initDefaultErrors()
	var serverAddress string
	if SERVER_PORT != "" {
		serverAddress = fmt.Sprintf("%s:%s", SERVER_ADDR, SERVER_PORT)
	} else {
		serverAddress = SERVER_ADDR
	}
	log.Println("server address:", serverAddress)
	srv := &http.Server{
		Addr:           serverAddress,
		Handler:        RouterMux,
		ReadTimeout:    3 * time.Second,
		WriteTimeout:   3 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	if doTLS {
		if tlsCfg == nil {
			tlsCfg = new(tls.Config)
		}
		srv.TLSConfig = tlsCfg
		log.Println("listening for incoming TLS connections")
		log.Printf("using credentials:\n\tkey: %s\n\tcert: %s\n",
			SSL_KEY, SSL_CERT)
		log.Fatalf("error in ListenAndServeTLS:\n\t%+v",
			srv.ListenAndServeTLS(SSL_CERT, SSL_KEY))
	} else {
		log.Println("listening for incoming HTTP connections")
		log.Fatalf("error in ListenAndServe:\n\t%+v\n",
			srv.ListenAndServe())
	}
}
<|endoftext|>


<commit_before>package rest

import (
	"github.com/bmizerany/assert"
	"testing"
)

var hooks = Webhooks{
	"service1": EventKeyUrlMap{
		"event1": Webhook{
			Url: "http://foo.com/bar/callback1",
		},
		"event2": Webhook{
			Url: "http://foo.com/bar/callback2",
		},
	},
	"service2": EventKeyUrlMap{
		"event1": Webhook{
			Url: "http://bar.com/bar/callback1",
		},
		"event2": Webhook{
			Url: "http://bar.com/bar/callback2",
		},
	},
}

type impl int

func (this *impl) Load() *Webhooks {
	return &hooks
}

func TestWebhookSerialization(t *testing.T) {

	bytes := hooks.ToJSON()
	assert.NotEqual(t, nil, bytes)
	assert.NotEqual(t, 0, len(bytes))

	hooks2 := Webhooks{}

	hooks2.FromJSON(bytes)

	assert.Equal(t, hooks, hooks2)

	t.Log("json", string(hooks2.ToJSON()))
}

func TestWebhooksManager(t *testing.T) {

}
<commit_msg>Fix broken build<commit_after>package rest

import (
	"github.com/bmizerany/assert"
	"testing"
)

var hooks = WebhookMap{
	"service1": EventKeyUrlMap{
		"event1": Webhook{
			Url: "http://foo.com/bar/callback1",
		},
		"event2": Webhook{
			Url: "http://foo.com/bar/callback2",
		},
	},
	"service2": EventKeyUrlMap{
		"event1": Webhook{
			Url: "http://bar.com/bar/callback1",
		},
		"event2": Webhook{
			Url: "http://bar.com/bar/callback2",
		},
	},
}

type impl int

func (this *impl) Load() *WebhookMap {
	return &hooks
}

func TestWebhookSerialization(t *testing.T) {

	bytes := hooks.ToJSON()
	assert.NotEqual(t, nil, bytes)
	assert.NotEqual(t, 0, len(bytes))

	hooks2 := WebhookMap{}

	hooks2.FromJSON(bytes)

	assert.Equal(t, hooks, hooks2)

	t.Log("json", string(hooks2.ToJSON()))
}
<|endoftext|>


<commit_before>package engine

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
)

func Test_Direction_CalculateDirection(t *testing.T) {
	tests := []struct {
		from        Dot
		to          Dot
		expectedDir Direction
	}{
		{Dot{0, 0}, Dot{0, 1}, DirectionSouth},
		{Dot{0, 0}, Dot{0, 2}, DirectionSouth},
		{Dot{0, 10}, Dot{0, 20}, DirectionSouth},
		{Dot{0, 10}, Dot{0, 8}, DirectionNorth},
		{Dot{0, 50}, Dot{0, 8}, DirectionNorth},
		{Dot{0, 50}, Dot{10, 50}, DirectionEast},
		{Dot{20, 50}, Dot{22, 50}, DirectionEast},
		{Dot{20, 50}, Dot{10, 50}, DirectionWest},
		{Dot{20, 50}, Dot{19, 50}, DirectionWest},
	}

	for i, test := range tests {
		actualDir := CalculateDirection(test.from, test.to)
		require.Equal(t, test.expectedDir, actualDir, fmt.Sprintf("number %d", i))
	}
}

func Test_Direction_CalculateDirection_ReturnsValidDirectionForEqualDots(t *testing.T) {
	require.True(t, ValidDirection(CalculateDirection(Dot{}, Dot{})))
	require.True(t, ValidDirection(CalculateDirection(Dot{10, 10}, Dot{10, 10})))
}

func Test_ValidDirection_ValidatesDirectionsCorrectly(t *testing.T) {
	tests := []struct {
		direction   Direction
		expectedRes bool
	}{
		{DirectionNorth, true},
		{DirectionEast, true},
		{DirectionSouth, true},
		{DirectionWest, true},
		{22, false},
		{44, false},
	}

	for i, test := range tests {
		require.Equal(t, test.expectedRes, ValidDirection(test.direction), fmt.Sprintf("number %d", i))
	}
}

func Test_Direction_Reverse_ReversesDirection(t *testing.T) {
	tests := []struct {
		directionInput    Direction
		directionExpected Direction
		expectError       bool
	}{
		{DirectionNorth, DirectionSouth, false},
		{DirectionEast, DirectionWest, false},
		{DirectionSouth, DirectionNorth, false},
		{DirectionWest, DirectionEast, false},
		{22, 0, true},
		{44, 0, true},
	}

	for i, test := range tests {
		actualDirection, err := test.directionInput.Reverse()
		if test.expectError {
			require.NotNil(t, err, fmt.Sprintf("number %d not error", i))
		} else {
			require.Nil(t, err, fmt.Sprintf("number %d error", i))
		}
		require.Equal(t, test.directionExpected, actualDirection, fmt.Sprintf("number %d", i))
	}
}
<commit_msg>Create engine.Direction MarshalJSON test<commit_after>package engine

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
)

func Test_Direction_CalculateDirection(t *testing.T) {
	tests := []struct {
		from        Dot
		to          Dot
		expectedDir Direction
	}{
		{Dot{0, 0}, Dot{0, 1}, DirectionSouth},
		{Dot{0, 0}, Dot{0, 2}, DirectionSouth},
		{Dot{0, 10}, Dot{0, 20}, DirectionSouth},
		{Dot{0, 10}, Dot{0, 8}, DirectionNorth},
		{Dot{0, 50}, Dot{0, 8}, DirectionNorth},
		{Dot{0, 50}, Dot{10, 50}, DirectionEast},
		{Dot{20, 50}, Dot{22, 50}, DirectionEast},
		{Dot{20, 50}, Dot{10, 50}, DirectionWest},
		{Dot{20, 50}, Dot{19, 50}, DirectionWest},
	}

	for i, test := range tests {
		actualDir := CalculateDirection(test.from, test.to)
		require.Equal(t, test.expectedDir, actualDir, fmt.Sprintf("number %d", i))
	}
}

func Test_Direction_CalculateDirection_ReturnsValidDirectionForEqualDots(t *testing.T) {
	require.True(t, ValidDirection(CalculateDirection(Dot{}, Dot{})))
	require.True(t, ValidDirection(CalculateDirection(Dot{10, 10}, Dot{10, 10})))
}

func Test_ValidDirection_ValidatesDirectionsCorrectly(t *testing.T) {
	tests := []struct {
		direction   Direction
		expectedRes bool
	}{
		{DirectionNorth, true},
		{DirectionEast, true},
		{DirectionSouth, true},
		{DirectionWest, true},
		{22, false},
		{44, false},
	}

	for i, test := range tests {
		require.Equal(t, test.expectedRes, ValidDirection(test.direction), fmt.Sprintf("number %d", i))
	}
}

func Test_Direction_Reverse_ReversesDirection(t *testing.T) {
	tests := []struct {
		directionInput    Direction
		directionExpected Direction
		expectError       bool
	}{
		{DirectionNorth, DirectionSouth, false},
		{DirectionEast, DirectionWest, false},
		{DirectionSouth, DirectionNorth, false},
		{DirectionWest, DirectionEast, false},
		{22, 0, true},
		{44, 0, true},
	}

	for i, test := range tests {
		actualDirection, err := test.directionInput.Reverse()
		if test.expectError {
			require.NotNil(t, err, fmt.Sprintf("number %d not error", i))
		} else {
			require.Nil(t, err, fmt.Sprintf("number %d error", i))
		}
		require.Equal(t, test.directionExpected, actualDirection, fmt.Sprintf("number %d", i))
	}
}

func Test_Direction_MarshalJSON(t *testing.T) {
	tests := []struct {
		direction    Direction
		expectedJSON []byte
		expectedErr  error
	}{
		{DirectionNorth, directionsJSON[DirectionNorth], nil},
		{DirectionEast, directionsJSON[DirectionEast], nil},
		{DirectionSouth, directionsJSON[DirectionSouth], nil},
		{DirectionWest, directionsJSON[DirectionWest], nil},
		{22, unknownDirectionJSON, &ErrDirectionMarshal{
			Err: &ErrInvalidDirection{
				Direction: 22,
			},
		}},
		{12, unknownDirectionJSON, &ErrDirectionMarshal{
			Err: &ErrInvalidDirection{
				Direction: 12,
			},
		}},
	}

	for i, test := range tests {
		json, err := test.direction.MarshalJSON()
		require.Equal(t, test.expectedJSON, json, fmt.Sprintf("number %d", i))
		require.Equal(t, test.expectedErr, err, fmt.Sprintf("number %d", i))
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// Pipe adapter to connect code expecting an io.Reader
// with code expecting an io.Writer.

package io

import (
	"errors"
	"sync"
)

// onceError is an object that will only store an error once.
type onceError struct {
	sync.Mutex // guards following
	err        error
}

func (a *onceError) Store(err error) {
	a.Lock()
	defer a.Unlock()
	if a.err != nil {
		return
	}
	a.err = err
}
func (a *onceError) Load() error {
	a.Lock()
	defer a.Unlock()
	return a.err
}

// ErrClosedPipe is the error used for read or write operations on a closed pipe.
var ErrClosedPipe = errors.New("io: read/write on closed pipe")

// A pipe is the shared pipe structure underlying PipeReader and PipeWriter.
type pipe struct {
	wrMu sync.Mutex // Serializes Write operations
	wrCh chan []byte
	rdCh chan int

	once sync.Once // Protects closing done
	done chan struct{}
	rerr onceError
	werr onceError
}

func (p *pipe) Read(b []byte) (n int, err error) {
	select {
	case <-p.done:
		return 0, p.readCloseError()
	default:
	}

	select {
	case bw := <-p.wrCh:
		nr := copy(b, bw)
		p.rdCh <- nr
		return nr, nil
	case <-p.done:
		return 0, p.readCloseError()
	}
}

func (p *pipe) readCloseError() error {
	rerr := p.rerr.Load()
	if werr := p.werr.Load(); rerr == nil && werr != nil {
		return werr
	}
	return ErrClosedPipe
}

func (p *pipe) CloseRead(err error) error {
	if err == nil {
		err = ErrClosedPipe
	}
	p.rerr.Store(err)
	p.once.Do(func() { close(p.done) })
	return nil
}

func (p *pipe) Write(b []byte) (n int, err error) {
	select {
	case <-p.done:
		return 0, p.writeCloseError()
	default:
		p.wrMu.Lock()
		defer p.wrMu.Unlock()
	}

	for once := true; once || len(b) > 0; once = false {
		select {
		case p.wrCh <- b:
			nw := <-p.rdCh
			b = b[nw:]
			n += nw
		case <-p.done:
			return n, p.writeCloseError()
		}
	}
	return n, nil
}

func (p *pipe) writeCloseError() error {
	werr := p.werr.Load()
	if rerr := p.rerr.Load(); werr == nil && rerr != nil {
		return rerr
	}
	return ErrClosedPipe
}

func (p *pipe) CloseWrite(err error) error {
	if err == nil {
		err = EOF
	}
	p.werr.Store(err)
	p.once.Do(func() { close(p.done) })
	return nil
}

// A PipeReader is the read half of a pipe.
type PipeReader struct {
	p *pipe
}

// Read implements the standard Read interface:
// it reads data from the pipe, blocking until a writer
// arrives or the write end is closed.
// If the write end is closed with an error, that error is
// returned as err; otherwise err is EOF.
func (r *PipeReader) Read(data []byte) (n int, err error) {
	return r.p.Read(data)
}

// Close closes the reader; subsequent writes to the
// write half of the pipe will return the error ErrClosedPipe.
func (r *PipeReader) Close() error {
	return r.CloseWithError(nil)
}

// CloseWithError closes the reader; subsequent writes
// to the write half of the pipe will return the error err.
//
// CloseWithError never overwrites the previous error if it exists
// and always returns nil.
func (r *PipeReader) CloseWithError(err error) error {
	return r.p.CloseRead(err)
}

// A PipeWriter is the write half of a pipe.
type PipeWriter struct {
	p *pipe
}

// Write implements the standard Write interface:
// it writes data to the pipe, blocking until one or more readers
// have consumed all the data or the read end is closed.
// If the read end is closed with an error, that err is
// returned as err; otherwise err is ErrClosedPipe.
func (w *PipeWriter) Write(data []byte) (n int, err error) {
	return w.p.Write(data)
}

// Close closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and EOF.
func (w *PipeWriter) Close() error {
	return w.CloseWithError(nil)
}

// CloseWithError closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and the error err,
// or EOF if err is nil.
//
// CloseWithError never overwrites the previous error if it exists
// and always returns nil.
func (w *PipeWriter) CloseWithError(err error) error {
	return w.p.CloseWrite(err)
}

// Pipe creates a synchronous in-memory pipe.
// It can be used to connect code expecting an io.Reader
// with code expecting an io.Writer.
//
// Reads and Writes on the pipe are matched one to one
// except when multiple Reads are needed to consume a single Write.
// That is, each Write to the PipeWriter blocks until it has satisfied
// one or more Reads from the PipeReader that fully consume
// the written data.
// The data is copied directly from the Write to the corresponding
// Read (or Reads); there is no internal buffering.
//
// It is safe to call Read and Write in parallel with each other or with Close.
// Parallel calls to Read and parallel calls to Write are also safe:
// the individual calls will be gated sequentially.
func Pipe() (*PipeReader, *PipeWriter) {
	p := &pipe{
		wrCh: make(chan []byte),
		rdCh: make(chan int),
		done: make(chan struct{}),
	}
	return &PipeReader{p}, &PipeWriter{p}
}
<commit_msg>io: unexport internal methods<commit_after>// Copyright 2009 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.

// Pipe adapter to connect code expecting an io.Reader
// with code expecting an io.Writer.

package io

import (
	"errors"
	"sync"
)

// onceError is an object that will only store an error once.
type onceError struct {
	sync.Mutex // guards following
	err        error
}

func (a *onceError) Store(err error) {
	a.Lock()
	defer a.Unlock()
	if a.err != nil {
		return
	}
	a.err = err
}
func (a *onceError) Load() error {
	a.Lock()
	defer a.Unlock()
	return a.err
}

// ErrClosedPipe is the error used for read or write operations on a closed pipe.
var ErrClosedPipe = errors.New("io: read/write on closed pipe")

// A pipe is the shared pipe structure underlying PipeReader and PipeWriter.
type pipe struct {
	wrMu sync.Mutex // Serializes Write operations
	wrCh chan []byte
	rdCh chan int

	once sync.Once // Protects closing done
	done chan struct{}
	rerr onceError
	werr onceError
}

func (p *pipe) read(b []byte) (n int, err error) {
	select {
	case <-p.done:
		return 0, p.readCloseError()
	default:
	}

	select {
	case bw := <-p.wrCh:
		nr := copy(b, bw)
		p.rdCh <- nr
		return nr, nil
	case <-p.done:
		return 0, p.readCloseError()
	}
}

func (p *pipe) closeRead(err error) error {
	if err == nil {
		err = ErrClosedPipe
	}
	p.rerr.Store(err)
	p.once.Do(func() { close(p.done) })
	return nil
}

func (p *pipe) write(b []byte) (n int, err error) {
	select {
	case <-p.done:
		return 0, p.writeCloseError()
	default:
		p.wrMu.Lock()
		defer p.wrMu.Unlock()
	}

	for once := true; once || len(b) > 0; once = false {
		select {
		case p.wrCh <- b:
			nw := <-p.rdCh
			b = b[nw:]
			n += nw
		case <-p.done:
			return n, p.writeCloseError()
		}
	}
	return n, nil
}

func (p *pipe) closeWrite(err error) error {
	if err == nil {
		err = EOF
	}
	p.werr.Store(err)
	p.once.Do(func() { close(p.done) })
	return nil
}

// readCloseError is considered internal to the pipe type.
func (p *pipe) readCloseError() error {
	rerr := p.rerr.Load()
	if werr := p.werr.Load(); rerr == nil && werr != nil {
		return werr
	}
	return ErrClosedPipe
}

// writeCloseError is considered internal to the pipe type.
func (p *pipe) writeCloseError() error {
	werr := p.werr.Load()
	if rerr := p.rerr.Load(); werr == nil && rerr != nil {
		return rerr
	}
	return ErrClosedPipe
}

// A PipeReader is the read half of a pipe.
type PipeReader struct {
	p *pipe
}

// Read implements the standard Read interface:
// it reads data from the pipe, blocking until a writer
// arrives or the write end is closed.
// If the write end is closed with an error, that error is
// returned as err; otherwise err is EOF.
func (r *PipeReader) Read(data []byte) (n int, err error) {
	return r.p.read(data)
}

// Close closes the reader; subsequent writes to the
// write half of the pipe will return the error ErrClosedPipe.
func (r *PipeReader) Close() error {
	return r.CloseWithError(nil)
}

// CloseWithError closes the reader; subsequent writes
// to the write half of the pipe will return the error err.
//
// CloseWithError never overwrites the previous error if it exists
// and always returns nil.
func (r *PipeReader) CloseWithError(err error) error {
	return r.p.closeRead(err)
}

// A PipeWriter is the write half of a pipe.
type PipeWriter struct {
	p *pipe
}

// Write implements the standard Write interface:
// it writes data to the pipe, blocking until one or more readers
// have consumed all the data or the read end is closed.
// If the read end is closed with an error, that err is
// returned as err; otherwise err is ErrClosedPipe.
func (w *PipeWriter) Write(data []byte) (n int, err error) {
	return w.p.write(data)
}

// Close closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and EOF.
func (w *PipeWriter) Close() error {
	return w.CloseWithError(nil)
}

// CloseWithError closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and the error err,
// or EOF if err is nil.
//
// CloseWithError never overwrites the previous error if it exists
// and always returns nil.
func (w *PipeWriter) CloseWithError(err error) error {
	return w.p.closeWrite(err)
}

// Pipe creates a synchronous in-memory pipe.
// It can be used to connect code expecting an io.Reader
// with code expecting an io.Writer.
//
// Reads and Writes on the pipe are matched one to one
// except when multiple Reads are needed to consume a single Write.
// That is, each Write to the PipeWriter blocks until it has satisfied
// one or more Reads from the PipeReader that fully consume
// the written data.
// The data is copied directly from the Write to the corresponding
// Read (or Reads); there is no internal buffering.
//
// It is safe to call Read and Write in parallel with each other or with Close.
// Parallel calls to Read and parallel calls to Write are also safe:
// the individual calls will be gated sequentially.
func Pipe() (*PipeReader, *PipeWriter) {
	p := &pipe{
		wrCh: make(chan []byte),
		rdCh: make(chan int),
		done: make(chan struct{}),
	}
	return &PipeReader{p}, &PipeWriter{p}
}
<|endoftext|>