<commit_before>package debugserver

import (
	"flag"
	"io/ioutil"
	"net/http"
	"net/http/pprof"
	"runtime"
	"strconv"

	"code.cloudfoundry.org/lager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/http_server"
)

const (
	DebugFlag = "debugAddr"
)

type DebugServerConfig struct {
	DebugAddress string `json:"debug_address"`
}

func AddFlags(flags *flag.FlagSet) {
	flags.String(
		DebugFlag,
		"",
		"host:port for serving pprof debugging info",
	)
}

func DebugAddress(flags *flag.FlagSet) string {
	dbgFlag := flags.Lookup(DebugFlag)
	if dbgFlag == nil {
		return ""
	}

	return dbgFlag.Value.String()
}

func Runner(address string, sink *lager.ReconfigurableSink) ifrit.Runner {
	return http_server.New(address, Handler(sink))
}

func Run(address string, sink *lager.ReconfigurableSink) (ifrit.Process, error) {
	p := ifrit.Invoke(Runner(address, sink))
	select {
	case <-p.Ready():
	case err := <-p.Wait():
		return nil, err
	}
	return p, nil
}

func Handler(sink *lager.ReconfigurableSink) http.Handler {
	mux := http.NewServeMux()
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/log-level", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		level, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return
		}

		switch string(level) {
		case "debug", "DEBUG", "d", strconv.Itoa(int(lager.DEBUG)):
			sink.SetMinLevel(lager.DEBUG)
		case "info", "INFO", "i", strconv.Itoa(int(lager.INFO)):
			sink.SetMinLevel(lager.INFO)
		case "error", "ERROR", "e", strconv.Itoa(int(lager.ERROR)):
			sink.SetMinLevel(lager.ERROR)
		case "fatal", "FATAL", "f", strconv.Itoa(int(lager.FATAL)):
			sink.SetMinLevel(lager.FATAL)
		}
	}))
	mux.Handle("/block-profile-rate", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_rate, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return
		}

		rate, err := strconv.Atoi(string(_rate))
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(err.Error()))
			return
		}

		if rate <= 0 {
			runtime.SetBlockProfileRate(0)
		} else {
			runtime.SetBlockProfileRate(rate)
		}
	}))

	return mux
}
<commit_msg>Add trace endpoint<commit_after>package debugserver

import (
	"flag"
	"io/ioutil"
	"net/http"
	"net/http/pprof"
	"runtime"
	"strconv"

	"code.cloudfoundry.org/lager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/http_server"
)

const (
	DebugFlag = "debugAddr"
)

type DebugServerConfig struct {
	DebugAddress string `json:"debug_address"`
}

func AddFlags(flags *flag.FlagSet) {
	flags.String(
		DebugFlag,
		"",
		"host:port for serving pprof debugging info",
	)
}

func DebugAddress(flags *flag.FlagSet) string {
	dbgFlag := flags.Lookup(DebugFlag)
	if dbgFlag == nil {
		return ""
	}

	return dbgFlag.Value.String()
}

func Runner(address string, sink *lager.ReconfigurableSink) ifrit.Runner {
	return http_server.New(address, Handler(sink))
}

func Run(address string, sink *lager.ReconfigurableSink) (ifrit.Process, error) {
	p := ifrit.Invoke(Runner(address, sink))
	select {
	case <-p.Ready():
	case err := <-p.Wait():
		return nil, err
	}
	return p, nil
}

func Handler(sink *lager.ReconfigurableSink) http.Handler {
	mux := http.NewServeMux()
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/log-level", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		level, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return
		}

		switch string(level) {
		case "debug", "DEBUG", "d", strconv.Itoa(int(lager.DEBUG)):
			sink.SetMinLevel(lager.DEBUG)
		case "info", "INFO", "i", strconv.Itoa(int(lager.INFO)):
			sink.SetMinLevel(lager.INFO)
		case "error", "ERROR", "e", strconv.Itoa(int(lager.ERROR)):
			sink.SetMinLevel(lager.ERROR)
		case "fatal", "FATAL", "f", strconv.Itoa(int(lager.FATAL)):
			sink.SetMinLevel(lager.FATAL)
		}
	}))
	mux.Handle("/block-profile-rate", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_rate, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return
		}

		rate, err := strconv.Atoi(string(_rate))
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(err.Error()))
			return
		}

		if rate <= 0 {
			runtime.SetBlockProfileRate(0)
		} else {
			runtime.SetBlockProfileRate(rate)
		}
	}))

	return mux
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.crypto/ssh"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// Write a simple http server
// response to the client is only wether or not his request was successfull or what error caused it to fail
// want to maintain state

// first, increment a global variable on every request
// can execute a get to get the variable.

// later: specify which public key to sign, and other parameters, username, permissions, restrictions, etc.
// probably a json object that's marshalled and sent over the wire as an http request.

// server processes that request, creates a certificate, upates the global datastructure

//from the user's computer, periodically execute a get method on your key in the data structure to receive updated copies of stuff.

type CertificateCollection map[string][]*ssh.Certificate

type CertificateParameters struct {
	Username       string
	Permissions    []string // no reason for it to be a map at this stage.
	PrivateKeyPath string
	Key            string // for now it points to the path of the public key to be signed.
}

var Certificates *CertificateCollection

func init() {
	Certificates = new(CertificateCollection)
}

func (c *CertificateCollection) New(params CertificateParameters) {
	// read private key
	privateKeyBytes, err := ioutil.ReadFile(params.PrivateKeyPath)
	check(err)
	authority, err := ssh.ParsePrivateKey(privateKeyBytes) // the private key used to sign the certificate.
	check(err)
	fmt.Printf("associated public key is: %v ", authority.PublicKey())
	// for now, read in public key to be signed.

	keyToSignBytes, err := ioutil.ReadFile(params.Key)
	check(err)
	keyToSign, comment, _, _, err := ssh.ParseAuthorizedKey(keyToSignBytes)
	check(err)

	if keyToSign == nil {
		panic("public key is nil")
		fmt.Println("comment is ", comment)
	}
	// from the params set the permissions and username
	// valid till infinity for now.

	cert := &ssh.Certificate{
		Nonce:       []byte{},
		Key:         keyToSign, // the public key that will be signed
		CertType:    ssh.UserCert,
		KeyId:       "user_" + params.Username,
		ValidBefore: ssh.CertTimeInfinity,
		Permissions: ssh.Permissions{
			CriticalOptions: map[string]string{},
			Extensions:      map[string]string{},
		},
		ValidPrincipals: []string{params.Username},
	}

	fmt.Println("public key is : ", keyToSign.Type())

	// setting the permissions
	for _, v := range params.Permissions {
		cert.Permissions.Extensions[v] = ""
	}

	err = cert.SignCert(rand.Reader, authority)
	check(err)
	// write signed cert to a file:

	err = ioutil.WriteFile("/Users/shantanu/.ssh/id_rsa-cert-server.pub", ssh.MarshalAuthorizedKey(cert), 0600)

	check(err)

	//once created add it to the map.
	// but for now, also write it to file, so that I can use it to connect to the remote server.

}

func incrementHandler(w http.ResponseWriter, r *http.Request) {

	decoder := json.NewDecoder(r.Body)
	var params CertificateParameters
	err := decoder.Decode(&params)
	check(err)
	Certificates.New(params)
	// fmt.Println(params)
	fmt.Fprintf(w, "%d", 200)
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	http.HandleFunc("/inc/", incrementHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>adding cert to global map<commit_after>package main

import (
	"code.google.com/p/go.crypto/ssh"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// Write a simple http server
// response to the client is only wether or not his request was successfull or what error caused it to fail
// want to maintain state

// first, increment a global variable on every request
// can execute a get to get the variable.

// later: specify which public key to sign, and other parameters, username, permissions, restrictions, etc.
// probably a json object that's marshalled and sent over the wire as an http request.

// server processes that request, creates a certificate, upates the global datastructure

//from the user's computer, periodically execute a get method on your key in the data structure to receive updated copies of stuff.

type CertificateCollection map[string][]*ssh.Certificate

type CertificateParameters struct {
	Username       string
	Permissions    []string // no reason for it to be a map at this stage.
	PrivateKeyPath string
	Key            string // for now it points to the path of the public key to be signed.
}

var Certificates CertificateCollection

func init() {
	Certificates = make(CertificateCollection)
}

func (c CertificateCollection) New(params CertificateParameters) {
	// read private key
	privateKeyBytes, err := ioutil.ReadFile(params.PrivateKeyPath)
	check(err)
	authority, err := ssh.ParsePrivateKey(privateKeyBytes) // the private key used to sign the certificate.
	check(err)
	fmt.Printf("associated public key is: %v ", authority.PublicKey())
	// for now, read in public key to be signed.

	keyToSignBytes, err := ioutil.ReadFile(params.Key)
	check(err)
	keyToSign, comment, _, _, err := ssh.ParseAuthorizedKey(keyToSignBytes)
	check(err)

	if keyToSign == nil {
		panic("public key is nil")
		fmt.Println("comment is ", comment)
	}
	// from the params set the permissions and username
	// valid till infinity for now.

	cert := &ssh.Certificate{
		Nonce:       []byte{},
		Key:         keyToSign, // the public key that will be signed
		CertType:    ssh.UserCert,
		KeyId:       "user_" + params.Username,
		ValidBefore: ssh.CertTimeInfinity,
		Permissions: ssh.Permissions{
			CriticalOptions: map[string]string{},
			Extensions:      map[string]string{},
		},
		ValidPrincipals: []string{params.Username},
	}

	fmt.Println("public key is : ", keyToSign.Type())

	// setting the permissions
	for _, v := range params.Permissions {
		cert.Permissions.Extensions[v] = ""
	}

	err = cert.SignCert(rand.Reader, authority)
	check(err)

	//add newly created cert to the file.

	certs, ok := c[params.Username]

	if !ok {
		// key does not exits
		c[params.Username] = []*ssh.Certificate{cert}
	} else {

		c[params.Username] = append(certs, cert)
	}

	// write signed cert to a file:
	err = ioutil.WriteFile("/Users/shantanu/.ssh/id_rsa-cert-server.pub", ssh.MarshalAuthorizedKey(cert), 0600)

	check(err)

	//once created add it to the map.
	// but for now, also write it to file, so that I can use it to connect to the remote server.

}

func incrementHandler(w http.ResponseWriter, r *http.Request) {

	decoder := json.NewDecoder(r.Body)
	var params CertificateParameters
	err := decoder.Decode(&params)
	check(err)
	Certificates.New(params)
	fmt.Println(Certificates)
	fmt.Fprintf(w, "%d", 200)
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	http.HandleFunc("/inc/", incrementHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"path"
	"strconv"
	"time"

	"github.com/boltdb/bolt"
	"github.com/codemodus/chain"
	"github.com/codemodus/formlark/internal/sessmgr"
	"github.com/codemodus/httpcluster"
	"github.com/codemodus/loggers"
	"github.com/codemodus/mixmux"
	"github.com/codemodus/sigmon"
	"golang.org/x/net/context"
)

type node struct {
	*httpcluster.Node
	su *sysUtils
	sm *sessmgr.Manager
}

type cluster struct {
	*httpcluster.Cluster
	su *sysUtils
}

func newCluster(su *sysUtils) *cluster {
	return &cluster{
		Cluster: &httpcluster.Cluster{}, su: su,
	}
}

func (cl *cluster) Configure(linkage bool) {
	p := sessmgr.NewVolatileProvider()
	sm := sessmgr.New("cook-e", 90, p)

	n := &node{
		su: cl.su, sm: sm,
		Node: &httpcluster.Node{
			Timeout: time.Second * 5, Addr: cl.su.conf.ServerPort,
		},
	}
	n.ErrorLog = n.su.logs.Err.Logger
	n.Handler = n.setupMux()

	cl.AddNode(n.Node)
}

func (n *node) setupMux() *mixmux.TreeMux {
	c := chain.New(n.reco, n.initReq, n.log, chain.Convert(n.Node.Wedge))
	s := c.Append(n.sess)
	m := mixmux.NewTreeMux()

	m.Get("/favicon.ico", c.EndFn(n.iconHandler))

	m.Get("/", c.EndFn(n.anonIndexHandler))
	m.Get("/login", c.EndFn(n.authedLoginGetHandler))
	m.Post("/login", c.EndFn(n.authedLoginPostHandler))
	m.Get("/logout", s.EndFn(n.NotFound))

	m.Get("/overview", s.EndFn(n.authedOverviewHandler))
	m.Get("/settings", s.EndFn(n.authedSettingsHandler))

	m.Get("/assets/*x", c.EndFn(n.assetsHandler))
	m.Get("/jspm_packages/*x", c.EndFn(n.assetsFlexHandler))
	m.Get("/app/*x", s.EndFn(n.assetsFlexHandler))

	m.Post(path.Join("/"+n.su.conf.FormPathPrefix+"/*x"), c.EndFn(n.anonPostHandler))

	mA := m.Group("/" + n.su.conf.AdminPathPrefix)
	mA.Get("/", s.EndFn(n.adminOverviewHandler))
	mA.Get("/login", c.EndFn(n.adminLoginGetHandler))
	mA.Post("/login", c.EndFn(n.adminLoginPostHandler))
	mA.Get("/logout", s.EndFn(n.NotFound))

	mA.Get("/overview", s.EndFn(n.adminOverviewHandler))
	mA.Get("/users", s.EndFn(n.adminUsersHandler))
	mA.Get("/settings", s.EndFn(n.adminSettingsHandler))
	mA.Get("/backup", s.EndFn(n.backupHandleFunc))
	mA.Get("/app/*x", s.EndFn(n.assetsFlexHandler))

	mA.Get("/assets/*x", s.EndFn(n.assetsHandler))
	mA.Get("/jspm_packages/*x", s.EndFn(n.assetsFlexHandler))

	mA.Get("/*x", c.EndFn(n.NotFound))
	return m
}

func (n *node) reco(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				log.Printf("panic: %+v", err)
				http.Error(w, http.StatusText(500), 500)
			}
		}()
		next.ServeHTTPContext(ctx, w, r)
	})
}

func (n *node) initReq(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		ctx = n.SetReqStart(ctx, time.Now())
		ctx = n.InitPHFC(ctx)
		next.ServeHTTPContext(ctx, w, r)
	})
}

func (n *node) log(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		next.ServeHTTPContext(ctx, w, r)

		t2 := time.Now()
		t1, _ := n.GetReqStart(ctx)
		dur := t2.Sub(t1)
		str := fmt.Sprintf(loggers.CLF, r.RemoteAddr, r.Host,
			r.Method, r.URL.String(), dur)

		go func(s *node, toLog string) {
			//s.su.logs.Dbg.Will(s.su.logs).Print(toLog)
		}(n, str)

		/*pc, _ := chain.GetPHFC(ctx)
		tx1, _ := startTimeFromCtx(*pc)*/
	})
}

func (n *node) sess(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		s, err := n.sm.SessStart(w, r)
		if err != nil {
			fmt.Println("ouch")
			// TODO
		}

		if r.URL.Path[len(r.URL.Path)-7:] == "/logout" {
			n.sm.SessStop(w, r)
			if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
				http.Redirect(w, r, "/"+n.su.conf.AdminPathPrefix+"/login", 302)
				return
			}
			http.Redirect(w, r, "/login", 302)
			return
		}

		usr, ok := s.Get("user").(string)
		if !ok || usr == "" {
			s.Set("prevReq", r.URL.Path)
			if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
				http.Redirect(w, r, "/"+n.su.conf.AdminPathPrefix+"/login", 302)
				return
			}
			http.Redirect(w, r, "/login", 302)
			return
		}

		ctx = n.SetSess(ctx, s)
		next.ServeHTTPContext(ctx, w, r)
	})
}

func (n *node) NotFound(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	http.NotFound(w, r)
}

func (cl *cluster) signal(sm *sigmon.SignalMonitor) {
	switch sm.Sig() {
	case sigmon.SIGHUP:
		cl.Stop(false)
		cl.Run()
	case sigmon.SIGINT, sigmon.SIGTERM:
		cl.Stop(true)
	case sigmon.SIGUSR1, sigmon.SIGUSR2:
		//
	}
}

func (n *node) iconHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "front/assets/public/icon/"+r.URL.Path)
}

func (n *node) assetsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
		http.ServeFile(w, r, "front/assets/protected/"+r.URL.Path[9+len(n.su.conf.AdminPathPrefix):])
		return
	}
	http.ServeFile(w, r, "front/assets/public/"+r.URL.Path[8:])
}

func (n *node) assetsFlexHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
		http.ServeFile(w, r, "front/assets/"+r.URL.Path[1+len(n.su.conf.AdminPathPrefix):])
		return
	}
	http.ServeFile(w, r, "front/assets/"+r.URL.Path[1:])
}

func (n *node) backupHandleFunc(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	err := n.su.ds.dcbsRsrcs.DB.View(func(tx *bolt.Tx) error {
		w.Header().Set("Content-Type", "application/octet-stream")
		w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
		w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
		_, err := tx.WriteTo(w)
		return err
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<commit_msg>Update cluster func calls according to changes to httpcluster.<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"path"
	"strconv"
	"time"

	"github.com/boltdb/bolt"
	"github.com/codemodus/chain"
	"github.com/codemodus/formlark/internal/sessmgr"
	"github.com/codemodus/httpcluster"
	"github.com/codemodus/loggers"
	"github.com/codemodus/mixmux"
	"github.com/codemodus/sigmon"
	"golang.org/x/net/context"
)

type node struct {
	*httpcluster.Node
	su *sysUtils
	sm *sessmgr.Manager
}

type cluster struct {
	*httpcluster.Cluster
	su *sysUtils
}

func newCluster(su *sysUtils) *cluster {
	return &cluster{
		Cluster: &httpcluster.Cluster{}, su: su,
	}
}

func (cl *cluster) Configure(linkage bool) {
	p := sessmgr.NewVolatileProvider()
	sm := sessmgr.New("cook-e", 90, p)

	n := &node{
		su: cl.su, sm: sm,
		Node: &httpcluster.Node{
			Timeout: time.Second * 5, Addr: cl.su.conf.ServerPort,
		},
	}
	n.ErrorLog = n.su.logs.Err.Logger
	n.Handler = n.setupMux()

	cl.AddNode(n.Node)
}

func (n *node) setupMux() *mixmux.TreeMux {
	c := chain.New(n.reco, n.initReq, n.log, chain.Convert(n.Node.Wedge))
	s := c.Append(n.sess)
	m := mixmux.NewTreeMux()

	m.Get("/favicon.ico", c.EndFn(n.iconHandler))

	m.Get("/", c.EndFn(n.anonIndexHandler))
	m.Get("/login", c.EndFn(n.authedLoginGetHandler))
	m.Post("/login", c.EndFn(n.authedLoginPostHandler))
	m.Get("/logout", s.EndFn(n.NotFound))

	m.Get("/overview", s.EndFn(n.authedOverviewHandler))
	m.Get("/settings", s.EndFn(n.authedSettingsHandler))

	m.Get("/assets/*x", c.EndFn(n.assetsHandler))
	m.Get("/jspm_packages/*x", c.EndFn(n.assetsFlexHandler))
	m.Get("/app/*x", s.EndFn(n.assetsFlexHandler))

	m.Post(path.Join("/"+n.su.conf.FormPathPrefix+"/*x"), c.EndFn(n.anonPostHandler))

	mA := m.Group("/" + n.su.conf.AdminPathPrefix)
	mA.Get("/", s.EndFn(n.adminOverviewHandler))
	mA.Get("/login", c.EndFn(n.adminLoginGetHandler))
	mA.Post("/login", c.EndFn(n.adminLoginPostHandler))
	mA.Get("/logout", s.EndFn(n.NotFound))

	mA.Get("/overview", s.EndFn(n.adminOverviewHandler))
	mA.Get("/users", s.EndFn(n.adminUsersHandler))
	mA.Get("/settings", s.EndFn(n.adminSettingsHandler))
	mA.Get("/backup", s.EndFn(n.backupHandleFunc))
	mA.Get("/app/*x", s.EndFn(n.assetsFlexHandler))

	mA.Get("/assets/*x", s.EndFn(n.assetsHandler))
	mA.Get("/jspm_packages/*x", s.EndFn(n.assetsFlexHandler))

	mA.Get("/*x", c.EndFn(n.NotFound))
	return m
}

func (n *node) reco(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				log.Printf("panic: %+v", err)
				http.Error(w, http.StatusText(500), 500)
			}
		}()
		next.ServeHTTPContext(ctx, w, r)
	})
}

func (n *node) initReq(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		ctx = n.SetReqStart(ctx, time.Now())
		ctx = n.InitPHFC(ctx)
		next.ServeHTTPContext(ctx, w, r)
	})
}

func (n *node) log(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		next.ServeHTTPContext(ctx, w, r)

		t2 := time.Now()
		t1, _ := n.GetReqStart(ctx)
		dur := t2.Sub(t1)
		str := fmt.Sprintf(loggers.CLF, r.RemoteAddr, r.Host,
			r.Method, r.URL.String(), dur)

		go func(s *node, toLog string) {
			//s.su.logs.Dbg.Will(s.su.logs).Print(toLog)
		}(n, str)

		/*pc, _ := chain.GetPHFC(ctx)
		tx1, _ := startTimeFromCtx(*pc)*/
	})
}

func (n *node) sess(next chain.Handler) chain.Handler {
	return chain.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		s, err := n.sm.SessStart(w, r)
		if err != nil {
			fmt.Println("ouch")
			// TODO
		}

		if r.URL.Path[len(r.URL.Path)-7:] == "/logout" {
			n.sm.SessStop(w, r)
			if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
				http.Redirect(w, r, "/"+n.su.conf.AdminPathPrefix+"/login", 302)
				return
			}
			http.Redirect(w, r, "/login", 302)
			return
		}

		usr, ok := s.Get("user").(string)
		if !ok || usr == "" {
			s.Set("prevReq", r.URL.Path)
			if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
				http.Redirect(w, r, "/"+n.su.conf.AdminPathPrefix+"/login", 302)
				return
			}
			http.Redirect(w, r, "/login", 302)
			return
		}

		ctx = n.SetSess(ctx, s)
		next.ServeHTTPContext(ctx, w, r)
	})
}

func (n *node) NotFound(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	http.NotFound(w, r)
}

func (cl *cluster) signal(sm *sigmon.SignalMonitor) {
	switch sm.Sig() {
	case sigmon.SIGHUP:
		cl.Stop()
		cl.Run()
	case sigmon.SIGINT, sigmon.SIGTERM:
		cl.Restart(nil)
	case sigmon.SIGUSR1, sigmon.SIGUSR2:
		//
	}
}

func (n *node) iconHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "front/assets/public/icon/"+r.URL.Path)
}

func (n *node) assetsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
		http.ServeFile(w, r, "front/assets/protected/"+r.URL.Path[9+len(n.su.conf.AdminPathPrefix):])
		return
	}
	http.ServeFile(w, r, "front/assets/public/"+r.URL.Path[8:])
}

func (n *node) assetsFlexHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if r.URL.Path[1:len(n.su.conf.AdminPathPrefix)+1] == n.su.conf.AdminPathPrefix {
		http.ServeFile(w, r, "front/assets/"+r.URL.Path[1+len(n.su.conf.AdminPathPrefix):])
		return
	}
	http.ServeFile(w, r, "front/assets/"+r.URL.Path[1:])
}

func (n *node) backupHandleFunc(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	err := n.su.ds.dcbsRsrcs.DB.View(func(tx *bolt.Tx) error {
		w.Header().Set("Content-Type", "application/octet-stream")
		w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
		w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
		_, err := tx.WriteTo(w)
		return err
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"
	nlogrus "github.com/meatballhat/negroni-logrus"
)

var (
	data      = make(map[string]interface{})
	dataMutex sync.RWMutex
	dirty     = false
	logger    *logrus.Logger
)

func main() {
	logr := nlogrus.NewMiddleware()
	logger = logr.Logger

	parseJsonFile()

	port := ":" + os.Getenv("PORT")
	if port == ":" {
		port = ":3000"
	}

	router := httprouter.New()

	addStaticRoutes(router)
	addDynamicRoutes(router)

	go flushJson()

	n := negroni.Classic()
	n.Use(logr)
	n.UseHandler(router)
	n.Run(port)
}
<commit_msg>More godoc<commit_after>
// Command qrest is a quick RESTful JSON server
//
// How to use
//
// Create a JSON file containing the data you'd like to be part of your server. An example file might look like:
//
//    {
//        "posts": [ { "id": 1, "title": "Foo" } ]
//    }
//
// Start qrest with this file as an argument:
//
//    qrest db.json
//
// Or in a docker container:
//
//    $ docker build -t qrest .
//    $ docker run --rm -p 3000:3000 qrest "db.json" # assuming db.json is in this source directory
//
// This will create the following routes for you to use:
//
//    POST /posts (creates a new post record)
//    GET /posts (returns all post records)
//    GET /posts/:id (returns a specific record)
//    PUT /posts/:id (creates or updates a record with the specified ID)
//    PATCH /posts/:id (updates a record with the specified ID)
//    DELETE /posts/:id (deletes the specified record)
//
//
package main

import (
	"os"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"
	nlogrus "github.com/meatballhat/negroni-logrus"
)

var (
	data      = make(map[string]interface{})
	dataMutex sync.RWMutex
	dirty     = false
	logger    *logrus.Logger
)

func main() {
	logr := nlogrus.NewMiddleware()
	logger = logr.Logger

	parseJsonFile()

	port := ":" + os.Getenv("PORT")
	if port == ":" {
		port = ":3000"
	}

	router := httprouter.New()

	addStaticRoutes(router)
	addDynamicRoutes(router)

	go flushJson()

	n := negroni.Classic()
	n.Use(logr)
	n.UseHandler(router)
	n.Run(port)
}
<|endoftext|>


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

package net

import (
	"reflect"
	"runtime"
	"strings"
	"testing"
	"time"
)

func TestResolveUDPAddr(t *testing.T) {
	for _, tt := range resolveTCPAddrTests {
		net := strings.Replace(tt.net, "tcp", "udp", -1)
		addr, err := ResolveUDPAddr(net, tt.litAddrOrName)
		if err != tt.err {
			t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, tt.litAddrOrName, err)
		}
		if !reflect.DeepEqual(addr, (*UDPAddr)(tt.addr)) {
			t.Fatalf("ResolveUDPAddr(%q, %q) = %#v, want %#v", net, tt.litAddrOrName, addr, tt.addr)
		}
		if err == nil {
			str := addr.String()
			addr1, err := ResolveUDPAddr(net, str)
			if err != nil {
				t.Fatalf("ResolveUDPAddr(%q, %q) [from %q]: %v", net, str, tt.litAddrOrName, err)
			}
			if !reflect.DeepEqual(addr1, addr) {
				t.Fatalf("ResolveUDPAddr(%q, %q) [from %q] = %#v, want %#v", net, str, tt.litAddrOrName, addr1, addr)
			}
		}
	}
}

func TestReadFromUDP(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9":
		t.Skipf("skipping test on %q, see issue 8916", runtime.GOOS)
	}

	ra, err := ResolveUDPAddr("udp", "127.0.0.1:7")
	if err != nil {
		t.Fatal(err)
	}

	la, err := ResolveUDPAddr("udp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}

	c, err := ListenUDP("udp", la)
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	_, err = c.WriteToUDP([]byte("a"), ra)
	if err != nil {
		t.Fatal(err)
	}

	err = c.SetDeadline(time.Now().Add(100 * time.Millisecond))
	if err != nil {
		t.Fatal(err)
	}
	b := make([]byte, 1)
	_, _, err = c.ReadFromUDP(b)
	if err == nil {
		t.Fatal("ReadFromUDP should fail")
	} else if !isTimeout(err) {
		t.Fatal(err)
	}
}

func TestWriteToUDP(t *testing.T) {
	switch runtime.GOOS {
	case "plan9":
		t.Skipf("skipping test on %q", runtime.GOOS)
	}

	c, err := ListenPacket("udp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	testWriteToConn(t, c.LocalAddr().String())
	testWriteToPacketConn(t, c.LocalAddr().String())
}

func testWriteToConn(t *testing.T, raddr string) {
	c, err := Dial("udp", raddr)
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	ra, err := ResolveUDPAddr("udp", raddr)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra)
	if err == nil {
		t.Fatal("WriteToUDP should fail")
	}
	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
		t.Fatalf("WriteToUDP should fail as ErrWriteToConnected: %v", err)
	}

	_, err = c.(*UDPConn).WriteTo([]byte("Connection-oriented mode socket"), ra)
	if err == nil {
		t.Fatal("WriteTo should fail")
	}
	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
		t.Fatalf("WriteTo should fail as ErrWriteToConnected: %v", err)
	}

	_, err = c.Write([]byte("Connection-oriented mode socket"))
	if err != nil {
		t.Fatal(err)
	}

	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, ra)
	if err == nil {
		t.Fatal("WriteMsgUDP should fail")
	}
	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
		t.Fatalf("WriteMsgUDP should fail as ErrWriteToConnected: %v", err)
	}
	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, nil)
	if err != nil {
		t.Fatal(err)
	}
}

func testWriteToPacketConn(t *testing.T, raddr string) {
	c, err := ListenPacket("udp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	ra, err := ResolveUDPAddr("udp", raddr)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.(*UDPConn).WriteToUDP([]byte("Connection-less mode socket"), ra)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.WriteTo([]byte("Connection-less mode socket"), ra)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.(*UDPConn).Write([]byte("Connection-less mode socket"))
	if err == nil {
		t.Fatal("Write should fail")
	}

	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, nil)
	if err == nil {
		t.Fatal("WriteMsgUDP should fail")
	}
	if err != nil && err.(*OpError).Err != errMissingAddress {
		t.Fatalf("WriteMsgUDP should fail as errMissingAddress: %v", err)
	}
	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, ra)
	if err != nil {
		t.Fatal(err)
	}
}

var udpConnLocalNameTests = []struct {
	net   string
	laddr *UDPAddr
}{
	{"udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}},
	{"udp4", &UDPAddr{}},
	{"udp4", nil},
}

func TestUDPConnLocalName(t *testing.T) {
	if testing.Short() || !*testExternal {
		t.Skip("skipping test to avoid external network")
	}

	for _, tt := range udpConnLocalNameTests {
		c, err := ListenUDP(tt.net, tt.laddr)
		if err != nil {
			t.Fatalf("ListenUDP failed: %v", err)
		}
		defer c.Close()
		la := c.LocalAddr()
		if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
			t.Fatalf("got %v; expected a proper address with non-zero port number", la)
		}
	}
}

func TestUDPConnLocalAndRemoteNames(t *testing.T) {
	for _, laddr := range []string{"", "127.0.0.1:0"} {
		c1, err := ListenPacket("udp", "127.0.0.1:0")
		if err != nil {
			t.Fatalf("ListenUDP failed: %v", err)
		}
		defer c1.Close()

		var la *UDPAddr
		if laddr != "" {
			var err error
			if la, err = ResolveUDPAddr("udp", laddr); err != nil {
				t.Fatalf("ResolveUDPAddr failed: %v", err)
			}
		}
		c2, err := DialUDP("udp", la, c1.LocalAddr().(*UDPAddr))
		if err != nil {
			t.Fatalf("DialUDP failed: %v", err)
		}
		defer c2.Close()

		var connAddrs = [4]struct {
			got Addr
			ok  bool
		}{
			{c1.LocalAddr(), true},
			{c1.(*UDPConn).RemoteAddr(), false},
			{c2.LocalAddr(), true},
			{c2.RemoteAddr(), true},
		}
		for _, ca := range connAddrs {
			if a, ok := ca.got.(*UDPAddr); ok != ca.ok || ok && a.Port == 0 {
				t.Fatalf("got %v; expected a proper address with non-zero port number", ca.got)
			}
		}
	}
}

func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
	if testing.Short() || !*testExternal {
		t.Skip("skipping test to avoid external network")
	}
	if !supportsIPv6 {
		t.Skip("ipv6 is not supported")
	}
	ifi := loopbackInterface()
	if ifi == nil {
		t.Skip("loopback interface not found")
	}
	laddr := ipv6LinkLocalUnicastAddr(ifi)
	if laddr == "" {
		t.Skip("ipv6 unicast address on loopback not found")
	}

	type test struct {
		net, addr  string
		nameLookup bool
	}
	var tests = []test{
		{"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
		{"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
	}
	// The first udp test fails on DragonFly - see issue 7473.
	if runtime.GOOS == "dragonfly" {
		tests = tests[1:]
	}
	switch runtime.GOOS {
	case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
		tests = append(tests, []test{
			{"udp", "[localhost%" + ifi.Name + "]:0", true},
			{"udp6", "[localhost%" + ifi.Name + "]:0", true},
		}...)
	case "linux":
		tests = append(tests, []test{
			{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
			{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
		}...)
	}
	for _, tt := range tests {
		c1, err := ListenPacket(tt.net, tt.addr)
		if err != nil {
			// It might return "LookupHost returned no
			// suitable address" error on some platforms.
			t.Logf("ListenPacket failed: %v", err)
			continue
		}
		defer c1.Close()
		if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
			t.Fatalf("got %v; expected a proper address with zone identifier", la)
		}

		c2, err := Dial(tt.net, c1.LocalAddr().String())
		if err != nil {
			t.Fatalf("Dial failed: %v", err)
		}
		defer c2.Close()
		if la, ok := c2.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
			t.Fatalf("got %v; expected a proper address with zone identifier", la)
		}
		if ra, ok := c2.RemoteAddr().(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
			t.Fatalf("got %v; expected a proper address with zone identifier", ra)
		}

		if _, err := c2.Write([]byte("UDP OVER IPV6 LINKLOCAL TEST")); err != nil {
			t.Fatalf("Conn.Write failed: %v", err)
		}
		b := make([]byte, 32)
		if _, from, err := c1.ReadFrom(b); err != nil {
			t.Fatalf("PacketConn.ReadFrom failed: %v", err)
		} else {
			if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
				t.Fatalf("got %v; expected a proper address with zone identifier", ra)
			}
		}
	}
}
<commit_msg>net: disable WriteMsgUDP tests on nacl, windows (fix build)<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import (
	"reflect"
	"runtime"
	"strings"
	"testing"
	"time"
)

func TestResolveUDPAddr(t *testing.T) {
	for _, tt := range resolveTCPAddrTests {
		net := strings.Replace(tt.net, "tcp", "udp", -1)
		addr, err := ResolveUDPAddr(net, tt.litAddrOrName)
		if err != tt.err {
			t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, tt.litAddrOrName, err)
		}
		if !reflect.DeepEqual(addr, (*UDPAddr)(tt.addr)) {
			t.Fatalf("ResolveUDPAddr(%q, %q) = %#v, want %#v", net, tt.litAddrOrName, addr, tt.addr)
		}
		if err == nil {
			str := addr.String()
			addr1, err := ResolveUDPAddr(net, str)
			if err != nil {
				t.Fatalf("ResolveUDPAddr(%q, %q) [from %q]: %v", net, str, tt.litAddrOrName, err)
			}
			if !reflect.DeepEqual(addr1, addr) {
				t.Fatalf("ResolveUDPAddr(%q, %q) [from %q] = %#v, want %#v", net, str, tt.litAddrOrName, addr1, addr)
			}
		}
	}
}

func TestReadFromUDP(t *testing.T) {
	switch runtime.GOOS {
	case "nacl", "plan9":
		t.Skipf("skipping test on %q, see issue 8916", runtime.GOOS)
	}

	ra, err := ResolveUDPAddr("udp", "127.0.0.1:7")
	if err != nil {
		t.Fatal(err)
	}

	la, err := ResolveUDPAddr("udp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}

	c, err := ListenUDP("udp", la)
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	_, err = c.WriteToUDP([]byte("a"), ra)
	if err != nil {
		t.Fatal(err)
	}

	err = c.SetDeadline(time.Now().Add(100 * time.Millisecond))
	if err != nil {
		t.Fatal(err)
	}
	b := make([]byte, 1)
	_, _, err = c.ReadFromUDP(b)
	if err == nil {
		t.Fatal("ReadFromUDP should fail")
	} else if !isTimeout(err) {
		t.Fatal(err)
	}
}

func TestWriteToUDP(t *testing.T) {
	switch runtime.GOOS {
	case "plan9":
		t.Skipf("skipping test on %q", runtime.GOOS)
	}

	c, err := ListenPacket("udp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	testWriteToConn(t, c.LocalAddr().String())
	testWriteToPacketConn(t, c.LocalAddr().String())
}

func testWriteToConn(t *testing.T, raddr string) {
	c, err := Dial("udp", raddr)
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	ra, err := ResolveUDPAddr("udp", raddr)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra)
	if err == nil {
		t.Fatal("WriteToUDP should fail")
	}
	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
		t.Fatalf("WriteToUDP should fail as ErrWriteToConnected: %v", err)
	}

	_, err = c.(*UDPConn).WriteTo([]byte("Connection-oriented mode socket"), ra)
	if err == nil {
		t.Fatal("WriteTo should fail")
	}
	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
		t.Fatalf("WriteTo should fail as ErrWriteToConnected: %v", err)
	}

	_, err = c.Write([]byte("Connection-oriented mode socket"))
	if err != nil {
		t.Fatal(err)
	}

	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, ra)
	if err == nil {
		t.Fatal("WriteMsgUDP should fail")
	}
	if err != nil && err.(*OpError).Err != ErrWriteToConnected {
		t.Fatalf("WriteMsgUDP should fail as ErrWriteToConnected: %v", err)
	}
	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, nil)
	switch runtime.GOOS {
	case "nacl", "windows": // see golang.org/issue/9252
		t.Skipf("not implemented yet on %s", runtime.GOOS)
	default:
		if err != nil {
			t.Fatal(err)
		}
	}
}

func testWriteToPacketConn(t *testing.T, raddr string) {
	c, err := ListenPacket("udp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	ra, err := ResolveUDPAddr("udp", raddr)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.(*UDPConn).WriteToUDP([]byte("Connection-less mode socket"), ra)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.WriteTo([]byte("Connection-less mode socket"), ra)
	if err != nil {
		t.Fatal(err)
	}

	_, err = c.(*UDPConn).Write([]byte("Connection-less mode socket"))
	if err == nil {
		t.Fatal("Write should fail")
	}

	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, nil)
	if err == nil {
		t.Fatal("WriteMsgUDP should fail")
	}
	if err != nil && err.(*OpError).Err != errMissingAddress {
		t.Fatalf("WriteMsgUDP should fail as errMissingAddress: %v", err)
	}
	_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, ra)
	switch runtime.GOOS {
	case "nacl", "windows": // see golang.org/issue/9252
		t.Skipf("not implemented yet on %s", runtime.GOOS)
	default:
		if err != nil {
			t.Fatal(err)
		}
	}
}

var udpConnLocalNameTests = []struct {
	net   string
	laddr *UDPAddr
}{
	{"udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}},
	{"udp4", &UDPAddr{}},
	{"udp4", nil},
}

func TestUDPConnLocalName(t *testing.T) {
	if testing.Short() || !*testExternal {
		t.Skip("skipping test to avoid external network")
	}

	for _, tt := range udpConnLocalNameTests {
		c, err := ListenUDP(tt.net, tt.laddr)
		if err != nil {
			t.Fatalf("ListenUDP failed: %v", err)
		}
		defer c.Close()
		la := c.LocalAddr()
		if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
			t.Fatalf("got %v; expected a proper address with non-zero port number", la)
		}
	}
}

func TestUDPConnLocalAndRemoteNames(t *testing.T) {
	for _, laddr := range []string{"", "127.0.0.1:0"} {
		c1, err := ListenPacket("udp", "127.0.0.1:0")
		if err != nil {
			t.Fatalf("ListenUDP failed: %v", err)
		}
		defer c1.Close()

		var la *UDPAddr
		if laddr != "" {
			var err error
			if la, err = ResolveUDPAddr("udp", laddr); err != nil {
				t.Fatalf("ResolveUDPAddr failed: %v", err)
			}
		}
		c2, err := DialUDP("udp", la, c1.LocalAddr().(*UDPAddr))
		if err != nil {
			t.Fatalf("DialUDP failed: %v", err)
		}
		defer c2.Close()

		var connAddrs = [4]struct {
			got Addr
			ok  bool
		}{
			{c1.LocalAddr(), true},
			{c1.(*UDPConn).RemoteAddr(), false},
			{c2.LocalAddr(), true},
			{c2.RemoteAddr(), true},
		}
		for _, ca := range connAddrs {
			if a, ok := ca.got.(*UDPAddr); ok != ca.ok || ok && a.Port == 0 {
				t.Fatalf("got %v; expected a proper address with non-zero port number", ca.got)
			}
		}
	}
}

func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
	if testing.Short() || !*testExternal {
		t.Skip("skipping test to avoid external network")
	}
	if !supportsIPv6 {
		t.Skip("ipv6 is not supported")
	}
	ifi := loopbackInterface()
	if ifi == nil {
		t.Skip("loopback interface not found")
	}
	laddr := ipv6LinkLocalUnicastAddr(ifi)
	if laddr == "" {
		t.Skip("ipv6 unicast address on loopback not found")
	}

	type test struct {
		net, addr  string
		nameLookup bool
	}
	var tests = []test{
		{"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
		{"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
	}
	// The first udp test fails on DragonFly - see issue 7473.
	if runtime.GOOS == "dragonfly" {
		tests = tests[1:]
	}
	switch runtime.GOOS {
	case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
		tests = append(tests, []test{
			{"udp", "[localhost%" + ifi.Name + "]:0", true},
			{"udp6", "[localhost%" + ifi.Name + "]:0", true},
		}...)
	case "linux":
		tests = append(tests, []test{
			{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
			{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
		}...)
	}
	for _, tt := range tests {
		c1, err := ListenPacket(tt.net, tt.addr)
		if err != nil {
			// It might return "LookupHost returned no
			// suitable address" error on some platforms.
			t.Logf("ListenPacket failed: %v", err)
			continue
		}
		defer c1.Close()
		if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
			t.Fatalf("got %v; expected a proper address with zone identifier", la)
		}

		c2, err := Dial(tt.net, c1.LocalAddr().String())
		if err != nil {
			t.Fatalf("Dial failed: %v", err)
		}
		defer c2.Close()
		if la, ok := c2.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
			t.Fatalf("got %v; expected a proper address with zone identifier", la)
		}
		if ra, ok := c2.RemoteAddr().(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
			t.Fatalf("got %v; expected a proper address with zone identifier", ra)
		}

		if _, err := c2.Write([]byte("UDP OVER IPV6 LINKLOCAL TEST")); err != nil {
			t.Fatalf("Conn.Write failed: %v", err)
		}
		b := make([]byte, 32)
		if _, from, err := c1.ReadFrom(b); err != nil {
			t.Fatalf("PacketConn.ReadFrom failed: %v", err)
		} else {
			if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
				t.Fatalf("got %v; expected a proper address with zone identifier", ra)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright © 2009--2013 The Web.go Authors
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// this file is about the actual handling of a request: it comes in, what
// happens? routing determines which handler is responsible and that is then
// wrapped appropriately and invoked.

package web

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/go.net/websocket"
)

type route struct {
	rex     *regexp.Regexp
	method  string
	handler parametrizedHandler
}

type ServerConfig struct {
	StaticDirs   []string
	Addr         string
	Port         int
	CookieSecret string
	RecoverPanic bool
	Cert         string
	Key          string
	ColorOutput  bool
}

type Server struct {
	Config ServerConfig
	routes []*route
	Logger *log.Logger
	Env    map[string]interface{}
	// Save the listener so it can be closed
	l net.Listener
	// Passed verbatim to every handler on every request
	User interface{}
	// All requests are passed through this wrapper if defined
	Wrappers []Wrapper
}

var mainServer = NewServer()

// Configuration of the shared server
var Config = &mainServer.Config
var exeFile string

//Stops the web server
func (s *Server) Close() {
	if s.l != nil {
		s.l.Close()
	}
}

// Queue response wrapper that is called after all other wrappers
func (s *Server) AddWrapper(wrap Wrapper) {
	s.Wrappers = append(s.Wrappers, wrap)
}

func (s *Server) SetLogger(logger *log.Logger) {
	s.Logger = logger
}

func (s *Server) addRoute(rawrex string, method string, handler interface{}) {
	rex, err := regexp.Compile(rawrex)
	if err != nil {
		s.Logger.Printf("Error in route regex %q: %v", rawrex, err)
		return
	}
	s.routes = append(s.routes, &route{
		rex:     rex,
		method:  method,
		handler: fixHandlerSignature(handler),
	})
}

// Calls function with recover block. The first return value is whatever the
// function returns if it didnt panic. The second is what was passed to panic()
// if it did.
func (s *Server) safelyCall(f func() error) (softerr error, harderr interface{}) {
	defer func() {
		if err := recover(); err != nil {
			if !s.Config.RecoverPanic {
				// go back to panic
				s.Logger.Printf("Panic: %v", err)
				panic(err)
			} else {
				harderr = err
				s.Logger.Println("Handler crashed with error: ", err)
				for i := 1; ; i += 1 {
					_, file, line, ok := runtime.Caller(i)
					if !ok {
						break
					}
					s.Logger.Println(file, line)
				}
			}
		}
	}()
	return f(), nil
}

// Determine if this route matches this request purely on the basis of the method
func matchRouteMethods(req *http.Request, route *route) bool {
	if req.Method == route.method {
		return true
	}
	if req.Method == "HEAD" && route.method == "GET" {
		return true
	}
	if req.Header.Get("Upgrade") == "websocket" && route.method == "WEBSOCKET" {
		return true
	}
	return false
}

// If this request matches this route return the group matches from the regular
// expression otherwise return an empty slice. note on success the return value
// includes the entire match as the first element.
func matchRoute(req *http.Request, route *route) []string {
	if !matchRouteMethods(req, route) {
		return nil
	}
	match := route.rex.FindStringSubmatch(req.URL.Path)
	if match == nil || len(match[0]) != len(req.URL.Path) {
		return nil
	}
	return match
}

func findMatchingRoute(req *http.Request, routes []*route) (*route, []string) {
	for _, route := range routes {
		if match := matchRoute(req, route); match != nil {
			return route, match
		}
	}
	return nil, nil
}

// Apply the handler to this context and try to handle errors where possible
func (s *Server) applyHandler(f SimpleHandler, ctx *Context) (err error) {
	softerr, harderr := s.safelyCall(func() error {
		return f(ctx)
	})
	if harderr != nil {
		//there was an error or panic while calling the handler
		ctx.Abort(500, "Server Error")
		return fmt.Errorf("%v", harderr)
	}
	if softerr != nil {
		s.Logger.Printf("Handler returned error: %v", softerr)
		if werr, ok := softerr.(WebError); ok {
			ctx.Abort(werr.Code, werr.Error())
		} else {
			// Non-web errors are not leaked to the outside
			ctx.Abort(500, "Server Error")
			err = softerr
		}
	} else {
		// flush the writer by ensuring at least one Write call takes place
		_, err = ctx.Write([]byte{})
	}
	if err == nil {
		err = ctx.Response.Close()
	}
	return
}

func dirExists(dir string) bool {
	d, e := os.Stat(dir)
	switch {
	case e != nil:
		return false
	case !d.IsDir():
		return false
	}

	return true
}

func fileExists(dir string) bool {
	info, err := os.Stat(dir)
	if err != nil {
		return false
	}

	return !info.IsDir()
}

// Default
func defaultStaticDir() string {
	root, _ := path.Split(exeFile)
	return path.Join(root, "static")
}

// If this request corresponds to a static file return its path
func (s *Server) findFile(req *http.Request) string {
	//try to serve static files
	staticDirs := s.Config.StaticDirs
	if len(staticDirs) == 0 {
		staticDirs = []string{defaultStaticDir()}
	}
	for _, staticDir := range staticDirs {
		staticFile := path.Join(staticDir, req.URL.Path)
		if fileExists(staticFile) && (req.Method == "GET" || req.Method == "HEAD") {
			return staticFile
		}
	}

	// Try to serve index.html || index.htm
	indexFilenames := []string{"index.html", "index.htm"}
	for _, staticDir := range staticDirs {
		for _, indexFilename := range indexFilenames {
			if indexPath := path.Join(path.Join(staticDir, req.URL.Path), indexFilename); fileExists(indexPath) {
				return indexPath
			}
		}
	}
	return ""
}

// Fully clothed request handler
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := &Context{
		Request:  req,
		RawBody:  nil,
		Params:   map[string]string{},
		Server:   s,
		Response: &ResponseWriter{ResponseWriter: w, BodyWriter: w},
		User:     s.User,
	}

	//log the request
	if s.Config.ColorOutput {
		s.Logger.Printf("\033[32;1m%s %s\033[0m", req.Method, req.URL.Path)
	} else {
		s.Logger.Printf("%s %s", req.Method, req.URL.Path)
	}

	//ignore errors from ParseForm because it's usually harmless.
	req.ParseForm()
	if len(req.Form) > 0 {
		for k, v := range req.Form {
			ctx.Params[k] = v[0]
		}
		if s.Config.ColorOutput {
			s.Logger.Printf("\033[37;1mParams: %v\033[0m\n", ctx.Params)
		} else {
			s.Logger.Printf("Params: %v\n", ctx.Params)
		}

	}

	var simpleh SimpleHandler
	route, match := findMatchingRoute(req, s.routes)
	if route != nil {
		if route.method == "WEBSOCKET" {
			// Wrap websocket handler
			openh := func(ctx *Context, args ...string) (err error) {
				// yo dawg we heard you like wrapped functions
				websocket.Handler(func(ws *websocket.Conn) {
					ctx.WebsockConn = ws
					err = route.handler(ctx, args...)
				}).ServeHTTP(ctx.Response, req)
				return err
			}
			simpleh = closeHandler(openh, match[1:]...)
		} else {
			// Set the default content-type
			ctx.ContentType("text/html; charset=utf-8")
			simpleh = closeHandler(route.handler, match[1:]...)
		}
	} else if path := s.findFile(req); path != "" {
		// no custom handler found but there is a file with this name
		simpleh = func(ctx *Context) error {
			http.ServeFile(ctx.Response, ctx.Request, path)
			return nil
		}
	} else {
		// hopeless, 404
		simpleh = func(ctx *Context) error {
			return WebError{404, "Page not found"}
		}
	}
	for _, wrap := range s.Wrappers {
		simpleh = wrapHandler(wrap, simpleh)
	}
	s.applyHandler(simpleh, ctx)
	return
}

func webTime(t time.Time) string {
	ftime := t.Format(time.RFC1123)
	if strings.HasSuffix(ftime, "UTC") {
		ftime = ftime[0:len(ftime)-3] + "GMT"
	}
	return ftime
}

func NewServer() *Server {
	conf := ServerConfig{
		RecoverPanic: true,
		Cert:         "",
		Key:          "",
		ColorOutput:  true,
	}
	s := &Server{
		Config: conf,
		Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
		Env:    map[string]interface{}{},
	}
	// Set some default headers
	s.AddWrapper(func(h SimpleHandler, ctx *Context) error {
		ctx.Header().Set("Server", "web.go")
		tm := time.Now().UTC()
		ctx.Header().Set("Date", webTime(tm))
		return h(ctx)
	})
	// Compress response data when possible
	s.AddWrapper(CompressWrapper)
	return s
}

// Package wide proxy functions for global web server object

// Stop the global web server
func Close() {
	mainServer.Close()
}

// Set a logger to be used by the global web server
func SetLogger(logger *log.Logger) {
	mainServer.Logger = logger
}

// The global web server as an object implementing the http.Handler interface
func GetHTTPHandler() http.Handler {
	return mainServer
}

func init() {
	// find the location of the executable
	arg0 := path.Clean(os.Args[0])
	wd, _ := os.Getwd()
	if strings.HasPrefix(arg0, "/") {
		exeFile = arg0
	} else {
		// TODO For robustness, search each directory in $PATH
		exeFile = path.Join(wd, arg0)
	}
	registerDefaultMimetypes()
}
<commit_msg>Do not set content-type HTML by default<commit_after>// Copyright © 2009--2013 The Web.go Authors
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// this file is about the actual handling of a request: it comes in, what
// happens? routing determines which handler is responsible and that is then
// wrapped appropriately and invoked.

package web

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/go.net/websocket"
)

type route struct {
	rex     *regexp.Regexp
	method  string
	handler parametrizedHandler
}

type ServerConfig struct {
	StaticDirs   []string
	Addr         string
	Port         int
	CookieSecret string
	RecoverPanic bool
	Cert         string
	Key          string
	ColorOutput  bool
}

type Server struct {
	Config ServerConfig
	routes []*route
	Logger *log.Logger
	Env    map[string]interface{}
	// Save the listener so it can be closed
	l net.Listener
	// Passed verbatim to every handler on every request
	User interface{}
	// All requests are passed through this wrapper if defined
	Wrappers []Wrapper
}

var mainServer = NewServer()

// Configuration of the shared server
var Config = &mainServer.Config
var exeFile string

//Stops the web server
func (s *Server) Close() {
	if s.l != nil {
		s.l.Close()
	}
}

// Queue response wrapper that is called after all other wrappers
func (s *Server) AddWrapper(wrap Wrapper) {
	s.Wrappers = append(s.Wrappers, wrap)
}

func (s *Server) SetLogger(logger *log.Logger) {
	s.Logger = logger
}

func (s *Server) addRoute(rawrex string, method string, handler interface{}) {
	rex, err := regexp.Compile(rawrex)
	if err != nil {
		s.Logger.Printf("Error in route regex %q: %v", rawrex, err)
		return
	}
	s.routes = append(s.routes, &route{
		rex:     rex,
		method:  method,
		handler: fixHandlerSignature(handler),
	})
}

// Calls function with recover block. The first return value is whatever the
// function returns if it didnt panic. The second is what was passed to panic()
// if it did.
func (s *Server) safelyCall(f func() error) (softerr error, harderr interface{}) {
	defer func() {
		if err := recover(); err != nil {
			if !s.Config.RecoverPanic {
				// go back to panic
				s.Logger.Printf("Panic: %v", err)
				panic(err)
			} else {
				harderr = err
				s.Logger.Println("Handler crashed with error: ", err)
				for i := 1; ; i += 1 {
					_, file, line, ok := runtime.Caller(i)
					if !ok {
						break
					}
					s.Logger.Println(file, line)
				}
			}
		}
	}()
	return f(), nil
}

// Determine if this route matches this request purely on the basis of the method
func matchRouteMethods(req *http.Request, route *route) bool {
	if req.Method == route.method {
		return true
	}
	if req.Method == "HEAD" && route.method == "GET" {
		return true
	}
	if req.Header.Get("Upgrade") == "websocket" && route.method == "WEBSOCKET" {
		return true
	}
	return false
}

// If this request matches this route return the group matches from the regular
// expression otherwise return an empty slice. note on success the return value
// includes the entire match as the first element.
func matchRoute(req *http.Request, route *route) []string {
	if !matchRouteMethods(req, route) {
		return nil
	}
	match := route.rex.FindStringSubmatch(req.URL.Path)
	if match == nil || len(match[0]) != len(req.URL.Path) {
		return nil
	}
	return match
}

func findMatchingRoute(req *http.Request, routes []*route) (*route, []string) {
	for _, route := range routes {
		if match := matchRoute(req, route); match != nil {
			return route, match
		}
	}
	return nil, nil
}

// Apply the handler to this context and try to handle errors where possible
func (s *Server) applyHandler(f SimpleHandler, ctx *Context) (err error) {
	softerr, harderr := s.safelyCall(func() error {
		return f(ctx)
	})
	if harderr != nil {
		//there was an error or panic while calling the handler
		ctx.Abort(500, "Server Error")
		return fmt.Errorf("%v", harderr)
	}
	if softerr != nil {
		s.Logger.Printf("Handler returned error: %v", softerr)
		if werr, ok := softerr.(WebError); ok {
			ctx.Abort(werr.Code, werr.Error())
		} else {
			// Non-web errors are not leaked to the outside
			ctx.Abort(500, "Server Error")
			err = softerr
		}
	} else {
		// flush the writer by ensuring at least one Write call takes place
		_, err = ctx.Write([]byte{})
	}
	if err == nil {
		err = ctx.Response.Close()
	}
	return
}

func dirExists(dir string) bool {
	d, e := os.Stat(dir)
	switch {
	case e != nil:
		return false
	case !d.IsDir():
		return false
	}

	return true
}

func fileExists(dir string) bool {
	info, err := os.Stat(dir)
	if err != nil {
		return false
	}

	return !info.IsDir()
}

// Default
func defaultStaticDir() string {
	root, _ := path.Split(exeFile)
	return path.Join(root, "static")
}

// If this request corresponds to a static file return its path
func (s *Server) findFile(req *http.Request) string {
	//try to serve static files
	staticDirs := s.Config.StaticDirs
	if len(staticDirs) == 0 {
		staticDirs = []string{defaultStaticDir()}
	}
	for _, staticDir := range staticDirs {
		staticFile := path.Join(staticDir, req.URL.Path)
		if fileExists(staticFile) && (req.Method == "GET" || req.Method == "HEAD") {
			return staticFile
		}
	}

	// Try to serve index.html || index.htm
	indexFilenames := []string{"index.html", "index.htm"}
	for _, staticDir := range staticDirs {
		for _, indexFilename := range indexFilenames {
			if indexPath := path.Join(path.Join(staticDir, req.URL.Path), indexFilename); fileExists(indexPath) {
				return indexPath
			}
		}
	}
	return ""
}

// Fully clothed request handler
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := &Context{
		Request:  req,
		RawBody:  nil,
		Params:   map[string]string{},
		Server:   s,
		Response: &ResponseWriter{ResponseWriter: w, BodyWriter: w},
		User:     s.User,
	}

	//log the request
	if s.Config.ColorOutput {
		s.Logger.Printf("\033[32;1m%s %s\033[0m", req.Method, req.URL.Path)
	} else {
		s.Logger.Printf("%s %s", req.Method, req.URL.Path)
	}

	//ignore errors from ParseForm because it's usually harmless.
	req.ParseForm()
	if len(req.Form) > 0 {
		for k, v := range req.Form {
			ctx.Params[k] = v[0]
		}
		if s.Config.ColorOutput {
			s.Logger.Printf("\033[37;1mParams: %v\033[0m\n", ctx.Params)
		} else {
			s.Logger.Printf("Params: %v\n", ctx.Params)
		}

	}

	var simpleh SimpleHandler
	route, match := findMatchingRoute(req, s.routes)
	if route != nil {
		if route.method == "WEBSOCKET" {
			// Wrap websocket handler
			openh := func(ctx *Context, args ...string) (err error) {
				// yo dawg we heard you like wrapped functions
				websocket.Handler(func(ws *websocket.Conn) {
					ctx.WebsockConn = ws
					err = route.handler(ctx, args...)
				}).ServeHTTP(ctx.Response, req)
				return err
			}
			simpleh = closeHandler(openh, match[1:]...)
		} else {
			simpleh = closeHandler(route.handler, match[1:]...)
		}
	} else if path := s.findFile(req); path != "" {
		// no custom handler found but there is a file with this name
		simpleh = func(ctx *Context) error {
			http.ServeFile(ctx.Response, ctx.Request, path)
			return nil
		}
	} else {
		// hopeless, 404
		simpleh = func(ctx *Context) error {
			return WebError{404, "Page not found"}
		}
	}
	for _, wrap := range s.Wrappers {
		simpleh = wrapHandler(wrap, simpleh)
	}
	s.applyHandler(simpleh, ctx)
	return
}

func webTime(t time.Time) string {
	ftime := t.Format(time.RFC1123)
	if strings.HasSuffix(ftime, "UTC") {
		ftime = ftime[0:len(ftime)-3] + "GMT"
	}
	return ftime
}

func NewServer() *Server {
	conf := ServerConfig{
		RecoverPanic: true,
		Cert:         "",
		Key:          "",
		ColorOutput:  true,
	}
	s := &Server{
		Config: conf,
		Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
		Env:    map[string]interface{}{},
	}
	// Set some default headers
	s.AddWrapper(func(h SimpleHandler, ctx *Context) error {
		ctx.Header().Set("Server", "web.go")
		tm := time.Now().UTC()
		ctx.Header().Set("Date", webTime(tm))
		return h(ctx)
	})
	// Compress response data when possible
	s.AddWrapper(CompressWrapper)
	return s
}

// Package wide proxy functions for global web server object

// Stop the global web server
func Close() {
	mainServer.Close()
}

// Set a logger to be used by the global web server
func SetLogger(logger *log.Logger) {
	mainServer.Logger = logger
}

// The global web server as an object implementing the http.Handler interface
func GetHTTPHandler() http.Handler {
	return mainServer
}

func init() {
	// find the location of the executable
	arg0 := path.Clean(os.Args[0])
	wd, _ := os.Getwd()
	if strings.HasPrefix(arg0, "/") {
		exeFile = arg0
	} else {
		// TODO For robustness, search each directory in $PATH
		exeFile = path.Join(wd, arg0)
	}
	registerDefaultMimetypes()
}
<|endoftext|>


<commit_before>// Package neptulon is a socket framework with middleware support.
package neptulon

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"sync"

	"github.com/neptulon/cmap"
	"github.com/neptulon/neptulon/client"
)

// Server is a Neptulon server.
type Server struct {
	debug          bool
	tls            bool
	listener       *listener
	clients        *cmap.CMap // conn ID -> Client
	connWG         sync.WaitGroup
	msgWG          sync.WaitGroup
	middlewareIn   []func(ctx *client.Ctx) error
	middlewareOut  []func(ctx *client.Ctx) error
	connHandler    func(c *client.Client)
	disconnHandler func(c *client.Client)
}

// NewTCPServer creates a Neptulon TCP server.
func NewTCPServer(laddr string, debug bool) (*Server, error) {
	l, err := listenTCP(laddr)
	if err != nil {
		return nil, err
	}

	return &Server{
		debug:    debug,
		listener: l,
		clients:  cmap.New(),
	}, nil
}

// NewTLSServer creates a Neptulon server using Transport Layer Security.
func NewTLSServer(cert, privKey, clientCACert []byte, laddr string, debug bool) (*Server, error) {
	l, err := listenTLS(cert, privKey, clientCACert, laddr)
	if err != nil {
		return nil, err
	}

	return &Server{
		debug:    debug,
		listener: l,
		clients:  cmap.New(),
		tls:      true,
	}, nil
}

// Conn registers a function to handle client connection events.
func (s *Server) Conn(handler func(c *client.Client)) {
	s.connHandler = handler
}

// MiddlewareIn registers middleware to handle incoming messages.
func (s *Server) MiddlewareIn(middleware ...func(ctx *client.Ctx) error) {
	s.middlewareIn = append(s.middlewareIn, middleware...)
}

// MiddlewareOut registers middleware to handle/intercept outgoing messages before they are sent.
func (s *Server) MiddlewareOut(middleware ...func(ctx *client.Ctx) error) {
	s.middlewareOut = append(s.middlewareOut, middleware...)
}

// Disconn registers a function to handle client disconnection events.
func (s *Server) Disconn(handler func(c *client.Client)) {
	s.disconnHandler = handler
}

// Start starts accepting connections on the internal listener and handles connections with registered middleware.
// This function blocks and never returns until the server is closed by another goroutine or an internal error occurs.
func (s *Server) Start() error {
	if err := s.listener.Accept(s.handleConn); err != nil {
		return fmt.Errorf("And error occured during or after accepting a new connection: %v", err)
	}

	return nil
}

// Send writes a message to the connection denoted by the connection ID.
func (s *Server) Send(connID string, msg []byte) error {
	if c, ok := s.clients.GetOk(connID); ok {
		return c.(*client.Client).Send(msg)
	}

	return fmt.Errorf("Connection ID not found: %v", connID)
}

// Close closes the network listener and the active connections.
func (s *Server) Close() error {
	err := s.listener.Close()

	// close all active connections discarding any read/writes that is going on currently
	// this is not a problem as we always require an ACK but it will also mean that message deliveries will be at-least-once; to-and-from the server
	s.clients.Range(func(c interface{}) {
		c.(*client.Client).Close()
	})

	if err != nil {
		return fmt.Errorf("And error occured before or while stopping the server: %v", err)
	}

	return nil
}

func (s *Server) handleConn(conn net.Conn) error {
	var c *client.Client
	if s.tls {
		tlsc, ok := conn.(*tls.Conn)
		if !ok {
			conn.Close()
			return errors.New("cannot cast net.Conn interface to tls.Conn type")
		}

		c = client.NewClient(&s.msgWG, s.handleDisconn)
		c.MiddlewareIn(s.middlewareIn...)
		c.MiddlewareOut(s.middlewareOut...)
		if err := c.UseTLSConn(tlsc, s.debug); err != nil {
			return err
		}
	} else {
		tcpc, ok := conn.(*net.TCPConn)
		if !ok {
			conn.Close()
			return errors.New("cannot cast net.Conn interface to net.TCPConn type")
		}

		c = client.NewClient(&s.msgWG, s.handleDisconn)
		c.MiddlewareIn(s.middlewareIn...)
		c.MiddlewareOut(s.middlewareOut...)
		if err := c.UseTCPConn(tcpc, s.debug); err != nil {
			return err
		}
	}

	s.clients.Set(c.ConnID(), c)
	s.connWG.Add(1)

	if s.connHandler != nil {
		s.connHandler(c)
	}

	return nil
}

func (s *Server) handleDisconn(c *client.Client) {
	s.clients.Delete(c.ConnID())
	s.connWG.Done()
	if s.disconnHandler != nil {
		s.disconnHandler(c)
	}
}
<commit_msg>handlers now return errors<commit_after>// Package neptulon is a socket framework with middleware support.
package neptulon

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"sync"

	"github.com/neptulon/cmap"
	"github.com/neptulon/neptulon/client"
)

// Server is a Neptulon server.
type Server struct {
	debug          bool
	tls            bool
	listener       *listener
	clients        *cmap.CMap // conn ID -> Client
	connWG         sync.WaitGroup
	msgWG          sync.WaitGroup
	middlewareIn   []func(ctx *client.Ctx) error
	middlewareOut  []func(ctx *client.Ctx) error
	connHandler    func(c *client.Client) error
	disconnHandler func(c *client.Client)
}

// NewTCPServer creates a Neptulon TCP server.
func NewTCPServer(laddr string, debug bool) (*Server, error) {
	l, err := listenTCP(laddr)
	if err != nil {
		return nil, err
	}

	return &Server{
		debug:    debug,
		listener: l,
		clients:  cmap.New(),
	}, nil
}

// NewTLSServer creates a Neptulon server using Transport Layer Security.
func NewTLSServer(cert, privKey, clientCACert []byte, laddr string, debug bool) (*Server, error) {
	l, err := listenTLS(cert, privKey, clientCACert, laddr)
	if err != nil {
		return nil, err
	}

	return &Server{
		debug:    debug,
		listener: l,
		clients:  cmap.New(),
		tls:      true,
	}, nil
}

// Conn registers a function to handle client connection events.
func (s *Server) Conn(handler func(c *client.Client) error) {
	s.connHandler = handler
}

// MiddlewareIn registers middleware to handle incoming messages.
func (s *Server) MiddlewareIn(middleware ...func(ctx *client.Ctx) error) {
	s.middlewareIn = append(s.middlewareIn, middleware...)
}

// MiddlewareOut registers middleware to handle/intercept outgoing messages before they are sent.
func (s *Server) MiddlewareOut(middleware ...func(ctx *client.Ctx) error) {
	s.middlewareOut = append(s.middlewareOut, middleware...)
}

// Disconn registers a function to handle client disconnection events.
func (s *Server) Disconn(handler func(c *client.Client)) {
	s.disconnHandler = handler
}

// Start starts accepting connections on the internal listener and handles connections with registered middleware.
// This function blocks and never returns until the server is closed by another goroutine or an internal error occurs.
func (s *Server) Start() error {
	if err := s.listener.Accept(s.handleConn); err != nil {
		return fmt.Errorf("And error occured during or after accepting a new connection: %v", err)
	}

	return nil
}

// Send writes a message to the connection denoted by the connection ID.
func (s *Server) Send(connID string, msg []byte) error {
	if c, ok := s.clients.GetOk(connID); ok {
		return c.(*client.Client).Send(msg)
	}

	return fmt.Errorf("Connection ID not found: %v", connID)
}

// Close closes the network listener and the active connections.
func (s *Server) Close() error {
	err := s.listener.Close()

	// close all active connections discarding any read/writes that is going on currently
	// this is not a problem as we always require an ACK but it will also mean that message deliveries will be at-least-once; to-and-from the server
	s.clients.Range(func(c interface{}) {
		c.(*client.Client).Close()
	})

	if err != nil {
		return fmt.Errorf("And error occured before or while stopping the server: %v", err)
	}

	return nil
}

func (s *Server) handleConn(conn net.Conn) error {
	var c *client.Client
	if s.tls {
		tlsc, ok := conn.(*tls.Conn)
		if !ok {
			conn.Close()
			return errors.New("cannot cast net.Conn interface to tls.Conn type")
		}

		c = client.NewClient(&s.msgWG, s.handleDisconn)
		c.MiddlewareIn(s.middlewareIn...)
		c.MiddlewareOut(s.middlewareOut...)
		if err := c.UseTLSConn(tlsc, s.debug); err != nil {
			return err
		}
	} else {
		tcpc, ok := conn.(*net.TCPConn)
		if !ok {
			conn.Close()
			return errors.New("cannot cast net.Conn interface to net.TCPConn type")
		}

		c = client.NewClient(&s.msgWG, s.handleDisconn)
		c.MiddlewareIn(s.middlewareIn...)
		c.MiddlewareOut(s.middlewareOut...)
		if err := c.UseTCPConn(tcpc, s.debug); err != nil {
			return err
		}
	}

	s.clients.Set(c.ConnID(), c)
	s.connWG.Add(1)

	if s.connHandler != nil {
		return s.connHandler(c)
	}

	return nil
}

func (s *Server) handleDisconn(c *client.Client) {
	s.clients.Delete(c.ConnID())
	s.connWG.Done()
	if s.disconnHandler != nil {
		s.disconnHandler(c)
	}
}
<|endoftext|>


<commit_before>package network

import (
	"common"
	"net"
	"strconv"
)

// TCPServer is a MessageSender that communicates over TCP.
// MessageHandlers is a map of Identifiers to MessageHandler interfaces.
type TCPServer struct {
	Addr            common.Address
	MessageHandlers map[common.Identifier]common.MessageHandler
}

// Address returns the address of the server
func (tcp *TCPServer) Address() common.Address {
	return tcp.Addr
}

// SendMessage transmits the payload of a message to its intended recipient.
// It returns without waiting for a response.
func (tcp *TCPServer) SendMessage(m *common.Message) (err error) {
	conn, err := net.Dial("tcp", m.Destination.Host+":"+strconv.Itoa(m.Destination.Port))
	if err != nil {
		return
	}
	defer conn.Close()

	// append identifier to front of payload
	payload := append([]byte{byte(m.Destination.Id)}, m.Payload...)
	_, err = conn.Write(payload)
	if err != nil {
		return
	}
	return
}

// AddMessageHandler adds a MessageHandler to the MessageHandlers map
// If the key already has a MessageHandler associated with it, it is overwritten.
func (tcp *TCPServer) AddMessageHandler(mh common.MessageHandler) {
	tcp.MessageHandlers[mh.Identifier()] = mh
}

// NewTCPServer creates and initializes a server that listens for TCP connections on a specified port.
// It then spawns a serverHandler with a specified message.
// It is the serverHandler's responsibility to close the TCP connection.
func NewTCPServer(port int) (tcp *TCPServer, err error) {
	tcp = new(TCPServer)
	tcpServ, err := net.Listen("tcp", ":"+strconv.Itoa(port))
	if err != nil {
		return
	}

	// initialize struct fields
	tcp.Addr = common.Address{0, "localhost", port}
	tcp.MessageHandlers = make(map[common.Identifier]common.MessageHandler)

	go tcp.serverHandler(tcpServ)
	return
}

// serverHandler accepts incoming connections and spawns a clientHandler for each.
func (tcp *TCPServer) serverHandler(tcpServ net.Listener) {
	defer tcpServ.Close()

	for {
		conn, err := tcpServ.Accept()
		if err != nil {
			return
		} else {
			tcp.clientHandler(conn)
		}
	}
}

// clientHandler reads data sent by a client and processes it.
func (tcp *TCPServer) clientHandler(conn net.Conn) {
	buffer := make([]byte, 1024)
	b, err := conn.Read(buffer)
	if err != nil {
		return
	}
	// look up message handler and call it
	// eventually this will use an unmarshalling function
	handler, exists := tcp.MessageHandlers[common.Identifier(buffer[0])]
	if exists {
		handler.HandleMessage(buffer[1:b])
	}
	// todo: decide on behavior when encountering uninitialized Identifier
}
<commit_msg>add support for messages of arbitrary length<commit_after>package network

import (
	"common"
	"encoding/binary"
	"net"
	"strconv"
)

// TCPServer is a MessageSender that communicates over TCP.
// MessageHandlers is a map of Identifiers to MessageHandler interfaces.
type TCPServer struct {
	Addr            common.Address
	MessageHandlers map[common.Identifier]common.MessageHandler
}

// Address returns the address of the server
func (tcp *TCPServer) Address() common.Address {
	return tcp.Addr
}

// SendMessage transmits the payload of a message to its intended recipient.
// It marshalls the Message struct using a length-prefix scheme.
// It does not wait for a response.
func (tcp *TCPServer) SendMessage(m *common.Message) (err error) {
	conn, err := net.Dial("tcp", net.JoinHostPort(m.Destination.Host, strconv.Itoa(m.Destination.Port)))
	if err != nil {
		return
	}
	defer conn.Close()

	// construct stream to be transmitted
	// bytes 0:3 are the payload length
	// byte 4 is the destination identifier
	// the remainder is the payload
	payloadLength := make([]byte, 4)
	binary.PutUvarint(payloadLength, uint64(len(m.Payload)))
	stream := append(payloadLength, byte(m.Destination.Id))
	stream = append(stream, m.Payload...)

	// transmit stream
	_, err = conn.Write(stream)
	if err != nil {
		return
	}

	return
}

// AddMessageHandler adds a MessageHandler to the MessageHandlers map
// If the key already has a MessageHandler associated with it, it is overwritten.
func (tcp *TCPServer) AddMessageHandler(mh common.MessageHandler) {
	tcp.MessageHandlers[mh.Identifier()] = mh
}

// NewTCPServer creates and initializes a server that listens for TCP connections on a specified port.
// It then spawns a serverHandler with a specified message.
// It is the serverHandler's responsibility to close the TCP connection.
func NewTCPServer(port int) (tcp *TCPServer, err error) {
	tcp = new(TCPServer)
	tcpServ, err := net.Listen("tcp", ":"+strconv.Itoa(port))
	if err != nil {
		return
	}

	// initialize struct fields
	tcp.Addr = common.Address{0, "localhost", port}
	tcp.MessageHandlers = make(map[common.Identifier]common.MessageHandler)

	go tcp.serverHandler(tcpServ)
	return
}

// serverHandler accepts incoming connections and spawns a clientHandler for each.
func (tcp *TCPServer) serverHandler(tcpServ net.Listener) {
	defer tcpServ.Close()

// serverHandler accepts incoming connections and spawns a clientHandler for each.
func (tcp *TCPServer) serverHandler() {
	for {
		conn, err := tcpServ.Accept()
		if err != nil {
			return
		} else {
			tcp.clientHandler(conn)
		}
	}
}

// clientHandler reads data sent by a client and processes it.
func (tcp *TCPServer) clientHandler(conn net.Conn) {
	// read first 1024 bytes
	buffer := make([]byte, 1024)
	b, err := conn.Read(buffer)
	if err != nil {
		return
	}

	// split message into payload length, identifier, and payload
	payloadLength, _ := binary.Uvarint(buffer[:4])
	id := common.Identifier(buffer[4])
	payload := buffer[5:b]

	// read rest of payload, 1024 bytes at a time
	// TODO: add a timeout
	bytesRead := len(payload)
	for uint64(bytesRead) != payloadLength {
		b, err = conn.Read(buffer)
		if err != nil {
			return
		}
		payload = append(payload, buffer[:b]...)
		bytesRead += b
	}

	// look up message handler and call it
	handler, exists := tcp.MessageHandlers[id]
	if exists {
		handler.HandleMessage(payload)
	}
	// todo: decide on behavior when encountering uninitialized Identifier
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"

	"github.com/codegangsta/cli"
	"github.com/toorop/govh"
	"github.com/toorop/govh/dedicated/server"
)

// getFwCmds return commands for firewall subsection
func getServerCmds(client *govh.OVHClient) (serverCmds []cli.Command) {
	sr, err := server.New(client)
	if err != nil {
		return
	}

	serverCmds = []cli.Command{
		{
			Name:        "list",
			Usage:       "Return a list of server ",
			Description: "ovh server list" + NLTAB + "Example: ovh server list",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				servers, err := sr.List()
				dieOnError(err)
				for _, server := range servers {
					fmt.Println(server)
				}
				dieOk()
			},
		},
		{
			Name:        "properties",
			Usage:       "Return properties of a server ",
			Description: "ovh server properties SERVER [--json]" + NLTAB + "Example: ovh server properties ks323462.kimsufi.com",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 1)
				properties, err := sr.GetProperties(c.Args().First())
				dieOnError(err)
				print(formatOutput(properties, c.Bool("json")))
				dieOk()
			},
		},

		{
			Name:        "tasks",
			Usage:       "Return a list of tasks for a server ",
			Description: "ovh server tasks SERVER [--function FUNCTION] [--status STATUS] [--json]" + NLTAB + "Example: ovh server tasks ns309865.ovh.net --function hardReboot --status done --json",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "function", Value: "", Usage: "(optional) - filter by function. See https://api.ovh.com/console/#/dedicated/server/%7BserviceName%%7D/task#GET for availables functions.)"},
				cli.StringFlag{Name: "status", Value: "", Usage: "(optional) : filter by status. See [OVH doc](https://api.ovh.com/console/#/dedicated/server/%7BserviceName%%7D/task#GET) for availables status."},
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 1)
				tasks, err := sr.GetTasks(c.Args().First(), c.String("function"), c.String("status"))
				dieOnError(err)
				if c.Bool("json") {
					r, _ := json.Marshal(tasks)
					dieOk(string(r))
				}
				for _, task := range tasks {
					fmt.Println(task)
				}
				dieOk()
			},
		},

		/*
			        	{
							Name:        "getTaskProperties",
							Usage:       "Return properties of a server task",
							Description: "ovh server getTaskProperties SERVER TASKID" + NLTAB + "Example: ovh server getTaskProperties ns309865.ovh.net 456",
							Action: func(c *cli.Context) {
								dieIfArgsMiss(len(c.Args()), 2)
								taskId, err := strconv.ParseUint(c.Args().Get(1), 10, 64)
								if err != nil {
									dieError(err)
								}
								task, err := sr.GetTaskProperties(c.Args().First(), taskId)
								dieOnError(err)
								fmt.Printf("Task ID: %d%s", task.Id, NL)
								fmt.Printf("Function: %s%s", task.Function, NL)
								fmt.Printf("Status: %s%s", task.Status, NL)
								fmt.Printf("Comment: %s%s", task.Comment, NL)
								fmt.Printf("Last Upadte: %s%s", task.LastUpdate, NL)
								fmt.Printf("Start Date: %s%s", task.StartDate, NL)
								fmt.Printf("Done Date: %s%s", task.DoneDate, NL)
								dieOk()
							},
						},
						{
							Name:        "cancelTask",
							Usage:       "Cancel a server task",
							Description: "ovh server cancelTask SERVER TASKID" + NLTAB + "Example: ovh server cancelTask ks323462.kimsufi.com 4319579",
							Action: func(c *cli.Context) {
								taskId, err := strconv.ParseUint(c.Args().Get(1), 10, 64)
								if err != nil {
									dieError(err)
								}
								err = sr.CancelTask(c.Args().Get(0), taskId)
								dieOnError(err)
								dieOk()
							},
						},
						{
							Name:        "reboot",
							Usage:       "Create a new reboot task",
							Description: "ovh server reboot SERVER" + NLTAB + "Example: ovh server reboot ks323462.kimsufi.com",
							Action: func(c *cli.Context) {
								dieIfArgsMiss(len(c.Args()), 1)
								task, err := sr.Reboot(c.Args().First())
								dieOnError(err)
								fmt.Printf("Task ID: %d%s", task.Id, NL)
								fmt.Printf("Function: %s%s", task.Function, NL)
								fmt.Printf("Status: %s%s", task.Status, NL)
								fmt.Printf("Comment: %s%s", task.Comment, NL)
								fmt.Printf("Last Upadte: %s%s", task.LastUpdate, NL)
								fmt.Printf("Start Date: %s%s", task.StartDate, NL)
								fmt.Printf("Done Date: %s%s", task.DoneDate, NL)
								dieOk()
							},
						},*/
	}

	/*
		switch cmd.Action {
		// List
		case "list":
			servers, err := serverR.List()
			if err != nil {
				return err
			}
			for _, s := range servers {
				resp = fmt.Sprintf("%s%s\r\n", resp, s)
			}
			if len(resp) > 2 {
				resp = resp[0 : len(resp)-2]
			}
			dieOk(resp)
			break
		// get server properties
		// ./ovh server getTasks SERVER_NAME FUNCTION STATUS
		case "properties":
			if len(cmd.Args) != 3 {
				return errors.New("\"server properties\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			properties, err := serverR.GetProperties(strings.ToLower(cmd.Args[2]))
			if err != nil {
				return err
			}
			fmt.Printf("ID: %d%s", properties.Id, NL)
			fmt.Printf("Name: %s%s", properties.Name, NL)
			fmt.Printf("Ip: %s%s", properties.Ip, NL)
			fmt.Printf("Datacenter: %s%s", properties.Datacenter, NL)
			fmt.Printf("ProfessionalUse: %t%s", properties.ProfessionalUse, NL)
			fmt.Printf("CommercialRange: %s%s", properties.CommercialRange, NL)
			fmt.Printf("Os: %s%s", properties.Os, NL)
			fmt.Printf("State: %s%s", properties.State, NL)
			fmt.Printf("Reverse: %s%s", properties.Reverse, NL)
			fmt.Printf("Monitored: %t%s", properties.Monitored, NL)
			fmt.Printf("Rack: %s%s", properties.Rack, NL)
			fmt.Printf("RootDevice: %s%s", properties.RootDevice, NL)
			fmt.Printf("LinkSpeed: %d%s", properties.LinkSpeed, NL)
			fmt.Printf("Bootid: %d%s", properties.BootId, NL)
			dieOk("")
			break

		// Get available netboots ID for this server
		case "availableNetboots":
			if len(cmd.Args) < 3 {
				return errors.New("\"server availableNetboots\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}
			var netbootIds []int
			if len(cmd.Args) == 3 {
				netbootIds, err = serverR.GetNetboots(strings.ToLower(cmd.Args[2]))
			} else {
				netbootIds, err = serverR.GetNetboots(strings.ToLower(cmd.Args[2]), cmd.Args[3])
			}
			if err != nil {
				return
			}
			for _, id := range netbootIds {
				fmt.Println(id)
			}
			break

		// Get server tasks
		case "getTasks":
			function := "all"
			status := "all"

			if len(cmd.Args) < 3 {
				return errors.New("\"server getTasks\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			if len(cmd.Args) > 5 {
				return errors.New("\"server getTasks\" too many arguments - see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			// serverName
			serverName := strings.ToLower(cmd.Args[2])

			// function
			if len(cmd.Args) > 3 {
				function = cmd.Args[3]
				if len(cmd.Args) > 4 {
					status = cmd.Args[4]
				}
			}

			tasks, err := serverR.GetTasks(serverName, function, status)
			if err != nil {
				return err
			}
			resp := ""
			for _, task := range tasks {
				resp = fmt.Sprintf("%s%d\r\n", resp, task)
			}
			if len(resp) > 2 {
				resp = resp[0 : len(resp)-2]
			}
			dieOk(resp)
			break

		// Get task properties
		// ./ovh server getTaskProperties SERVER_NAME TASK_ID
		case "getTaskProperties":
			if len(cmd.Args) != 4 {
				return errors.New("\"server getTaskProperties\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			serverName := strings.ToLower(cmd.Args[2])
			taskId, err := strconv.ParseUint(cmd.Args[3], 10, 64)
			if err != nil {
				return err
			}
			task, err := serverR.GetTaskProperties(serverName, taskId)
			if err != nil {
				return err
			}
			fmt.Printf("Task ID: %d%s", task.Id, NL)
			fmt.Printf("Function: %s%s", task.Function, NL)
			fmt.Printf("Status: %s%s", task.Status, NL)
			fmt.Printf("Comment: %s%s", task.Comment, NL)
			fmt.Printf("Last Upadte: %s%s", task.LastUpdate, NL)
			fmt.Printf("Start Date: %s%s", task.StartDate, NL)
			fmt.Printf("Done Date: %s%s", task.DoneDate, NL)
			dieOk("")
			break

		// Cancel task (if possible)
		// ./ovh server cancelTask SERVER_NAME TASK_ID
		case "cancelTask":
			if len(cmd.Args) != 4 {
				return errors.New("\"server cancel Task\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}
			serverName := strings.ToLower(cmd.Args[2])
			taskId, err := strconv.ParseUint(cmd.Args[3], 10, 64)
			if err != nil {
				return err
			}
			err = serverR.CancelTask(serverName, taskId)
			if err != nil {
				return err
			}
			dieOk("Success task " + cmd.Args[3] + "cancelled")
			break

		// Reboot
		case "reboot":
			if len(cmd.Args) != 3 {
				return errors.New("\"server reboot\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}
			task, err := serverR.Reboot(strings.ToLower(cmd.Args[2]))
			if err != nil {
				return err
			}
			fmt.Printf("Task ID: %d%s", task.Id, NL)
			fmt.Printf("Function: %s%s", task.Function, NL)
			fmt.Printf("Status: %s%s", task.Status, NL)
			fmt.Printf("Comment: %s%s", task.Comment, NL)
			fmt.Printf("Last Upadte: %s%s", task.LastUpdate, NL)
			fmt.Printf("Start Date: %s%s", task.StartDate, NL)
			fmt.Printf("Done Date: %s%s", task.DoneDate, NL)
			dieOk("")
			break

		default:
			return errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
		}*/
	return
}
<commit_msg>server task<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/toorop/govh"
	"github.com/toorop/govh/dedicated/server"
)

// getFwCmds return commands for firewall subsection
func getServerCmds(client *govh.OVHClient) (serverCmds []cli.Command) {
	sr, err := server.New(client)
	if err != nil {
		return
	}

	serverCmds = []cli.Command{
		{
			Name:        "list",
			Usage:       "Return a list of server ",
			Description: "ovh server list" + NLTAB + "Example: ovh server list",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				servers, err := sr.List()
				dieOnError(err)
				for _, server := range servers {
					fmt.Println(server)
				}
				dieOk()
			},
		},
		{
			Name:        "properties",
			Usage:       "Return properties of a server ",
			Description: "ovh server properties SERVER [--json]" + NLTAB + "Example: ovh server properties ks323462.kimsufi.com",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 1)
				properties, err := sr.GetProperties(c.Args().First())
				dieOnError(err)
				println(formatOutput(properties, c.Bool("json")))
				dieOk()
			},
		},

		{
			Name:        "tasks",
			Usage:       "Return a list of tasks for a server ",
			Description: "ovh server tasks SERVER [--function FUNCTION] [--status STATUS] [--json]" + NLTAB + "Example: ovh server tasks ns309865.ovh.net --function hardReboot --status done --json",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "function", Value: "", Usage: "(optional) - filter by function. See https://api.ovh.com/console/#/dedicated/server/%7BserviceName%%7D/task#GET for availables functions.)"},
				cli.StringFlag{Name: "status", Value: "", Usage: "(optional) : filter by status. See [OVH doc](https://api.ovh.com/console/#/dedicated/server/%7BserviceName%%7D/task#GET) for availables status."},
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 1)
				tasks, err := sr.GetTasks(c.Args().First(), c.String("function"), c.String("status"))
				dieOnError(err)
				if c.Bool("json") {
					r, _ := json.Marshal(tasks)
					dieOk(string(r))
				}
				for _, task := range tasks {
					fmt.Println(task)
				}
				dieOk()
			},
		},

		{
			Name:        "task",
			Usage:       "Return properties of a server task",
			Description: "ovh server task SERVER TASKID" + NLTAB + "Example: ovh server task ns309865.ovh.net 456",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "output as JSON"},
			},
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 2)
				taskID, err := strconv.ParseInt(c.Args().Get(1), 10, 64)
				if err != nil {
					dieError(err)
				}
				task, err := sr.GetTask(c.Args().First(), int(taskID))
				dieOnError(err)
				println(formatOutput(task, c.Bool("json")))
				dieOk()
			},
		},

		{
			Name:        "canceltask",
			Usage:       "Cancel a server task",
			Description: "ovh server canceltask SERVER TASKID" + NLTAB + "Example: ovh server canceltask ks323462.kimsufi.com 4319579",
			Action: func(c *cli.Context) {
				taskID, err := strconv.ParseUint(c.Args().Get(1), 10, 64)
				if err != nil {
					dieError(err)
				}
				err = sr.CancelTask(c.Args().Get(0), taskID)
				dieOnError(err)
				dieOk()
			},
		},
		/*
			{
				Name:        "reboot",
				Usage:       "Create a new reboot task",
				Description: "ovh server reboot SERVER" + NLTAB + "Example: ovh server reboot ks323462.kimsufi.com",
				Action: func(c *cli.Context) {
					dieIfArgsMiss(len(c.Args()), 1)
					task, err := sr.Reboot(c.Args().First())
					dieOnError(err)
					fmt.Printf("Task ID: %d%s", task.Id, NL)
					fmt.Printf("Function: %s%s", task.Function, NL)
					fmt.Printf("Status: %s%s", task.Status, NL)
					fmt.Printf("Comment: %s%s", task.Comment, NL)
					fmt.Printf("Last Upadte: %s%s", task.LastUpdate, NL)
					fmt.Printf("Start Date: %s%s", task.StartDate, NL)
					fmt.Printf("Done Date: %s%s", task.DoneDate, NL)
					dieOk()
				},
			},*/
	}

	/*
		switch cmd.Action {
		// List
		case "list":
			servers, err := serverR.List()
			if err != nil {
				return err
			}
			for _, s := range servers {
				resp = fmt.Sprintf("%s%s\r\n", resp, s)
			}
			if len(resp) > 2 {
				resp = resp[0 : len(resp)-2]
			}
			dieOk(resp)
			break
		// get server properties
		// ./ovh server getTasks SERVER_NAME FUNCTION STATUS
		case "properties":
			if len(cmd.Args) != 3 {
				return errors.New("\"server properties\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			properties, err := serverR.GetProperties(strings.ToLower(cmd.Args[2]))
			if err != nil {
				return err
			}
			fmt.Printf("ID: %d%s", properties.Id, NL)
			fmt.Printf("Name: %s%s", properties.Name, NL)
			fmt.Printf("Ip: %s%s", properties.Ip, NL)
			fmt.Printf("Datacenter: %s%s", properties.Datacenter, NL)
			fmt.Printf("ProfessionalUse: %t%s", properties.ProfessionalUse, NL)
			fmt.Printf("CommercialRange: %s%s", properties.CommercialRange, NL)
			fmt.Printf("Os: %s%s", properties.Os, NL)
			fmt.Printf("State: %s%s", properties.State, NL)
			fmt.Printf("Reverse: %s%s", properties.Reverse, NL)
			fmt.Printf("Monitored: %t%s", properties.Monitored, NL)
			fmt.Printf("Rack: %s%s", properties.Rack, NL)
			fmt.Printf("RootDevice: %s%s", properties.RootDevice, NL)
			fmt.Printf("LinkSpeed: %d%s", properties.LinkSpeed, NL)
			fmt.Printf("Bootid: %d%s", properties.BootId, NL)
			dieOk("")
			break

		// Get available netboots ID for this server
		case "availableNetboots":
			if len(cmd.Args) < 3 {
				return errors.New("\"server availableNetboots\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}
			var netbootIds []int
			if len(cmd.Args) == 3 {
				netbootIds, err = serverR.GetNetboots(strings.ToLower(cmd.Args[2]))
			} else {
				netbootIds, err = serverR.GetNetboots(strings.ToLower(cmd.Args[2]), cmd.Args[3])
			}
			if err != nil {
				return
			}
			for _, id := range netbootIds {
				fmt.Println(id)
			}
			break

		// Get server tasks
		case "getTasks":
			function := "all"
			status := "all"

			if len(cmd.Args) < 3 {
				return errors.New("\"server getTasks\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			if len(cmd.Args) > 5 {
				return errors.New("\"server getTasks\" too many arguments - see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			// serverName
			serverName := strings.ToLower(cmd.Args[2])

			// function
			if len(cmd.Args) > 3 {
				function = cmd.Args[3]
				if len(cmd.Args) > 4 {
					status = cmd.Args[4]
				}
			}

			tasks, err := serverR.GetTasks(serverName, function, status)
			if err != nil {
				return err
			}
			resp := ""
			for _, task := range tasks {
				resp = fmt.Sprintf("%s%d\r\n", resp, task)
			}
			if len(resp) > 2 {
				resp = resp[0 : len(resp)-2]
			}
			dieOk(resp)
			break

		// Get task properties
		// ./ovh server getTaskProperties SERVER_NAME TASK_ID
		case "getTaskProperties":
			if len(cmd.Args) != 4 {
				return errors.New("\"server getTaskProperties\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}

			serverName := strings.ToLower(cmd.Args[2])
			taskId, err := strconv.ParseUint(cmd.Args[3], 10, 64)
			if err != nil {
				return err
			}
			task, err := serverR.GetTaskProperties(serverName, taskId)
			if err != nil {
				return err
			}
			fmt.Printf("Task ID: %d%s", task.Id, NL)
			fmt.Printf("Function: %s%s", task.Function, NL)
			fmt.Printf("Status: %s%s", task.Status, NL)
			fmt.Printf("Comment: %s%s", task.Comment, NL)
			fmt.Printf("Last Upadte: %s%s", task.LastUpdate, NL)
			fmt.Printf("Start Date: %s%s", task.StartDate, NL)
			fmt.Printf("Done Date: %s%s", task.DoneDate, NL)
			dieOk("")
			break

		// Cancel task (if possible)
		// ./ovh server cancelTask SERVER_NAME TASK_ID
		case "cancelTask":
			if len(cmd.Args) != 4 {
				return errors.New("\"server cancel Task\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}
			serverName := strings.ToLower(cmd.Args[2])
			taskId, err := strconv.ParseUint(cmd.Args[3], 10, 64)
			if err != nil {
				return err
			}
			err = serverR.CancelTask(serverName, taskId)
			if err != nil {
				return err
			}
			dieOk("Success task " + cmd.Args[3] + "cancelled")
			break

		// Reboot
		case "reboot":
			if len(cmd.Args) != 3 {
				return errors.New("\"server reboot\" needs an argument see doc at https://github.com/Toorop/govh/blob/master/cli/README.md")
			}
			task, err := serverR.Reboot(strings.ToLower(cmd.Args[2]))
			if err != nil {
				return err
			}
			fmt.Printf("Task ID: %d%s", task.Id, NL)
			fmt.Printf("Function: %s%s", task.Function, NL)
			fmt.Printf("Status: %s%s", task.Status, NL)
			fmt.Printf("Comment: %s%s", task.Comment, NL)
			fmt.Printf("Last Upadte: %s%s", task.LastUpdate, NL)
			fmt.Printf("Start Date: %s%s", task.StartDate, NL)
			fmt.Printf("Done Date: %s%s", task.DoneDate, NL)
			dieOk("")
			break

		default:
			return errors.New(fmt.Sprintf("This action : '%s' is not valid or not implemented yet !", strings.Join(cmd.Args, " ")))
		}*/
	return
}
<|endoftext|>