<commit_before>package vm

import (
	"fmt"
	"github.com/rooby-lang/rooby/bytecode"
	"github.com/rooby-lang/rooby/parser"
	"io/ioutil"
	"path"
	"regexp"
	"strconv"
	"strings"
)

// bytecodeParser is responsible for parsing bytecodes
type bytecodeParser struct {
	line       int
	labelTable map[labelType]map[string][]*instructionSet
	vm         *VM
	filename   filename
	blockTable map[string]*instructionSet
	program    *instructionSet
}

// newBytecodeParser initializes bytecodeParser and its label table then returns it
func newBytecodeParser(file filename) *bytecodeParser {
	p := &bytecodeParser{filename: file}
	p.blockTable = make(map[string]*instructionSet)
	p.labelTable = map[labelType]map[string][]*instructionSet{
		bytecode.LabelDef:      make(map[string][]*instructionSet),
		bytecode.LabelDefClass: make(map[string][]*instructionSet),
	}

	return p
}

// parseBytecode parses given bytecodes and transfer them into a sequence of instruction set.
func (p *bytecodeParser) parseBytecode(bytecodes string) []*instructionSet {
	iss := []*instructionSet{}
	bytecodes = removeEmptyLine(strings.TrimSpace(bytecodes))
	bytecodesByLine := strings.Split(bytecodes, "\n")
	p.parseSection(iss, bytecodesByLine)

	return iss
}

func (p *bytecodeParser) parseSection(iss []*instructionSet, bytecodesByLine []string) {
	is := &instructionSet{filename: p.filename}
	count := 0

	// First line is label
	p.parseLabel(is, bytecodesByLine[0])

	for _, text := range bytecodesByLine[1:] {
		count++
		l := strings.TrimSpace(text)
		if strings.HasPrefix(l, "<") {
			p.parseSection(iss, bytecodesByLine[count:])
			break
		} else {
			p.parseInstruction(is, l)
		}
	}

	iss = append(iss, is)
}

func (p *bytecodeParser) parseLabel(is *instructionSet, line string) {
	line = strings.Trim(line, "<")
	line = strings.Trim(line, ">")
	p.setLabel(is, line)
}

func (p *bytecodeParser) setLabel(is *instructionSet, name string) {
	var l *label
	var ln string
	var lt labelType

	if name == bytecode.Program {
		p.program = is
		return
	} else {
		ln = strings.Split(name, ":")[1]
		lt = labelType(strings.Split(name, ":")[0])
	}

	l = &label{name: name, Type: lt}
	is.label = l

	if lt == bytecode.Block {
		p.blockTable[ln] = is
		return
	}

	p.labelTable[lt][ln] = append(p.labelTable[lt][ln], is)
}

// parseInstruction transfer a line of bytecode into an instruction and append it into given instruction set.
func (p *bytecodeParser) parseInstruction(is *instructionSet, line string) {
	var params []interface{}
	var rawParams []string

	tokens := strings.Split(line, " ")
	lineNum, act := tokens[0], tokens[1]
	ln, _ := strconv.ParseInt(lineNum, 0, 64)
	action := builtInActions[operationType(act)]

	if act == bytecode.PutString {
		text := strings.Split(line, "\"")[1]
		params = append(params, text)
	} else if act == bytecode.RequireRelative {
		filepath := tokens[2]
		filepath = path.Join(p.vm.fileDir, filepath)

		file, err := ioutil.ReadFile(filepath + ".ro")

		if err != nil {
			panic(err)
		}

		program := parser.BuildAST(file)
		g := bytecode.NewGenerator(program)
		bytecodes := g.GenerateByteCode(program)
		p.vm.ExecBytecodes(bytecodes, filepath)
		return
	} else if len(tokens) > 2 {
		rawParams = tokens[2:]

		for _, param := range rawParams {
			params = append(params, p.parseParam(param))
		}
	} else if action == nil {
		panic(fmt.Sprintf("Unknown command: %s. line: %d", act, ln))
	}

	is.define(int(ln), action, params...)
}

func (p *bytecodeParser) parseParam(param string) interface{} {
	integer, e := strconv.ParseInt(param, 0, 64)
	if e != nil {
		return param
	}

	i := int(integer)

	return i
}

func removeEmptyLine(s string) string {
	regex, err := regexp.Compile("\n+")
	if err != nil {
		panic(err)
	}
	s = regex.ReplaceAllString(s, "\n")

	return s
}
<commit_msg>Remove "removeEmptyLine" method which uses regex in the wrong way.<commit_after>package vm

import (
	"fmt"
	"github.com/rooby-lang/rooby/bytecode"
	"github.com/rooby-lang/rooby/parser"
	"io/ioutil"
	"path"
	"strconv"
	"strings"
)

// bytecodeParser is responsible for parsing bytecodes
type bytecodeParser struct {
	line       int
	labelTable map[labelType]map[string][]*instructionSet
	vm         *VM
	filename   filename
	blockTable map[string]*instructionSet
	program    *instructionSet
}

// newBytecodeParser initializes bytecodeParser and its label table then returns it
func newBytecodeParser(file filename) *bytecodeParser {
	p := &bytecodeParser{filename: file}
	p.blockTable = make(map[string]*instructionSet)
	p.labelTable = map[labelType]map[string][]*instructionSet{
		bytecode.LabelDef:      make(map[string][]*instructionSet),
		bytecode.LabelDefClass: make(map[string][]*instructionSet),
	}

	return p
}

// parseBytecode parses given bytecodes and transfer them into a sequence of instruction set.
func (p *bytecodeParser) parseBytecode(bytecodes string) []*instructionSet {
	iss := []*instructionSet{}
	bytecodes = strings.TrimSpace(bytecodes)
	bytecodesByLine := strings.Split(bytecodes, "\n")
	p.parseSection(iss, bytecodesByLine)

	return iss
}

func (p *bytecodeParser) parseSection(iss []*instructionSet, bytecodesByLine []string) {
	is := &instructionSet{filename: p.filename}
	count := 0

	// First line is label
	p.parseLabel(is, bytecodesByLine[0])

	for _, text := range bytecodesByLine[1:] {
		count++
		l := strings.TrimSpace(text)
		if strings.HasPrefix(l, "<") {
			p.parseSection(iss, bytecodesByLine[count:])
			break
		} else {
			p.parseInstruction(is, l)
		}
	}

	iss = append(iss, is)
}

func (p *bytecodeParser) parseLabel(is *instructionSet, line string) {
	line = strings.Trim(line, "<")
	line = strings.Trim(line, ">")
	p.setLabel(is, line)
}

func (p *bytecodeParser) setLabel(is *instructionSet, name string) {
	var l *label
	var ln string
	var lt labelType

	if name == bytecode.Program {
		p.program = is
		return
	} else {
		ln = strings.Split(name, ":")[1]
		lt = labelType(strings.Split(name, ":")[0])
	}

	l = &label{name: name, Type: lt}
	is.label = l

	if lt == bytecode.Block {
		p.blockTable[ln] = is
		return
	}

	p.labelTable[lt][ln] = append(p.labelTable[lt][ln], is)
}

// parseInstruction transfer a line of bytecode into an instruction and append it into given instruction set.
func (p *bytecodeParser) parseInstruction(is *instructionSet, line string) {
	var params []interface{}
	var rawParams []string

	tokens := strings.Split(line, " ")
	lineNum, act := tokens[0], tokens[1]
	ln, _ := strconv.ParseInt(lineNum, 0, 64)
	action := builtInActions[operationType(act)]

	if act == bytecode.PutString {
		text := strings.Split(line, "\"")[1]
		params = append(params, text)
	} else if act == bytecode.RequireRelative {
		filepath := tokens[2]
		filepath = path.Join(p.vm.fileDir, filepath)

		file, err := ioutil.ReadFile(filepath + ".ro")

		if err != nil {
			panic(err)
		}

		program := parser.BuildAST(file)
		g := bytecode.NewGenerator(program)
		bytecodes := g.GenerateByteCode(program)
		p.vm.ExecBytecodes(bytecodes, filepath)
		return
	} else if len(tokens) > 2 {
		rawParams = tokens[2:]

		for _, param := range rawParams {
			params = append(params, p.parseParam(param))
		}
	} else if action == nil {
		panic(fmt.Sprintf("Unknown command: %s. line: %d", act, ln))
	}

	is.define(int(ln), action, params...)
}

func (p *bytecodeParser) parseParam(param string) interface{} {
	integer, e := strconv.ParseInt(param, 0, 64)
	if e != nil {
		return param
	}

	i := int(integer)

	return i
}<|endoftext|>


<commit_before>package neurgo

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/couchbaselabs/logg"
	"log"
	"sync"
)

type SensorFunction func(int) []float64

type Sensor struct {
	NodeId         *NodeId
	Outbound       []*OutboundConnection
	VectorLength   int
	Closing        chan chan bool
	SyncChan       chan bool
	SensorFunction SensorFunction
	wg             *sync.WaitGroup
	Cortex         *Cortex
}

func (sensor *Sensor) Init() {
	if sensor.Closing == nil {
		sensor.Closing = make(chan chan bool)
	}

	if sensor.SyncChan == nil {
		sensor.SyncChan = make(chan bool)
	}

	if sensor.SensorFunction == nil {
		// if there is no SensorFunction, create a default
		// function which emits a 0-vector
		sensorFunc := func(syncCounter int) []float64 {
			return make([]float64, sensor.VectorLength)
		}
		sensor.SensorFunction = sensorFunc
	}

	if sensor.wg == nil {
		sensor.wg = &sync.WaitGroup{}
		sensor.wg.Add(1)
	}

}

func (sensor *Sensor) Run() {

	defer sensor.wg.Done()

	sensor.checkRunnable()

	closed := false
	syncCounter := 0

	for {
		select {
		case responseChan := <-sensor.Closing:
			closed = true
			responseChan <- true
			break // TODO: do we need this for anything??
		case _ = <-sensor.SyncChan:
			logmsg := fmt.Sprintf("%v", sensor.NodeId.UUID)
			logg.LogTo("SENSOR_SYNC", logmsg)
			input := sensor.SensorFunction(syncCounter)
			syncCounter += 1
			dataMessage := &DataMessage{
				SenderId: sensor.NodeId,
				Inputs:   input,
			}
			sensor.scatterOutput(dataMessage)
		}

		if closed {
			sensor.Closing = nil
			sensor.SyncChan = nil
			break
		}
	}

}

func (sensor *Sensor) Shutdown() {

	closingResponse := make(chan bool)
	sensor.Closing <- closingResponse
	response := <-closingResponse
	if response != true {
		log.Panicf("Got unexpected response on closing channel")
	}

	sensor.shutdownOutboundConnections()

	sensor.wg.Wait()
	sensor.wg = nil
}

func (s *Sensor) ConnectOutbound(connectable OutboundConnectable) *OutboundConnection {
	return ConnectOutbound(s, connectable)
}

func (sensor *Sensor) MarshalJSON() ([]byte, error) {
	return json.Marshal(
		struct {
			NodeId       *NodeId
			VectorLength int
			Outbound     []*OutboundConnection
		}{
			NodeId:       sensor.NodeId,
			VectorLength: sensor.VectorLength,
			Outbound:     sensor.Outbound,
		})
}

func (sensor *Sensor) String() string {
	return JsonString(sensor)
}

func (sensor *Sensor) outbound() []*OutboundConnection {
	return sensor.Outbound
}

func (sensor *Sensor) setOutbound(newOutbound []*OutboundConnection) {
	sensor.Outbound = newOutbound
}

func (sensor *Sensor) checkRunnable() {
	if sensor.NodeId == nil {
		msg := fmt.Sprintf("not expecting sensor.NodeId to be nil")
		panic(msg)
	}

	if sensor.Closing == nil {
		msg := fmt.Sprintf("not expecting sensor.Closing to be nil")
		panic(msg)
	}

	if sensor.SyncChan == nil {
		msg := fmt.Sprintf("not expecting sensor.SyncChan to be nil")
		panic(msg)
	}

	if sensor.SensorFunction == nil {
		msg := fmt.Sprintf("not expecting sensor.SensorFunction to be nil")
		panic(msg)
	}

	if err := sensor.validateOutbound(); err != nil {
		msg := fmt.Sprintf("invalid outbound connection(s): %v", err.Error())
		panic(msg)
	}

}

func (sensor *Sensor) validateOutbound() error {
	for _, connection := range sensor.Outbound {
		if connection.DataChan == nil {
			msg := fmt.Sprintf("%v has empty DataChan", connection)
			return errors.New(msg)
		}
	}
	return nil
}

func (sensor *Sensor) scatterOutput(dataMessage *DataMessage) {
	for _, outboundConnection := range sensor.Outbound {
		logmsg := fmt.Sprintf("%v -> %v: %v", sensor.NodeId.UUID,
			outboundConnection.NodeId.UUID, dataMessage)
		logg.LogTo("NODE_PRE_SEND", logmsg)
		dataChan := outboundConnection.DataChan
		dataChan <- dataMessage
		logg.LogTo("NODE_POST_SEND", logmsg)
	}
}

func (sensor *Sensor) nodeId() *NodeId {
	return sensor.NodeId
}

func (sensor *Sensor) initOutboundConnections(nodeIdToDataMsg nodeIdToDataMsgMap) {
	for _, outboundConnection := range sensor.Outbound {
		if outboundConnection.DataChan == nil {
			dataChan := nodeIdToDataMsg[outboundConnection.NodeId.UUID]
			if dataChan != nil {
				outboundConnection.DataChan = dataChan
			}
		}
	}
}

func (sensor *Sensor) shutdownOutboundConnections() {
	for _, outboundConnection := range sensor.Outbound {
		outboundConnection.DataChan = nil
	}
}
<commit_msg>add guard to protect from a sensor scattering empty output<commit_after>package neurgo

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/couchbaselabs/logg"
	"log"
	"sync"
)

type SensorFunction func(int) []float64

type Sensor struct {
	NodeId         *NodeId
	Outbound       []*OutboundConnection
	VectorLength   int
	Closing        chan chan bool
	SyncChan       chan bool
	SensorFunction SensorFunction
	wg             *sync.WaitGroup
	Cortex         *Cortex
}

func (sensor *Sensor) Init() {
	if sensor.Closing == nil {
		sensor.Closing = make(chan chan bool)
	}

	if sensor.SyncChan == nil {
		sensor.SyncChan = make(chan bool)
	}

	if sensor.SensorFunction == nil {
		// if there is no SensorFunction, create a default
		// function which emits a 0-vector
		sensorFunc := func(syncCounter int) []float64 {
			return make([]float64, sensor.VectorLength)
		}
		sensor.SensorFunction = sensorFunc
	}

	if sensor.wg == nil {
		sensor.wg = &sync.WaitGroup{}
		sensor.wg.Add(1)
	}

}

func (sensor *Sensor) Run() {

	defer sensor.wg.Done()

	sensor.checkRunnable()

	closed := false
	syncCounter := 0

	for {
		select {
		case responseChan := <-sensor.Closing:
			closed = true
			responseChan <- true
			break // TODO: do we need this for anything??
		case _ = <-sensor.SyncChan:
			logmsg := fmt.Sprintf("%v", sensor.NodeId.UUID)
			logg.LogTo("SENSOR_SYNC", logmsg)
			input := sensor.SensorFunction(syncCounter)
			syncCounter += 1
			dataMessage := &DataMessage{
				SenderId: sensor.NodeId,
				Inputs:   input,
			}
			sensor.scatterOutput(dataMessage)
		}

		if closed {
			sensor.Closing = nil
			sensor.SyncChan = nil
			break
		}
	}

}

func (sensor *Sensor) Shutdown() {

	closingResponse := make(chan bool)
	sensor.Closing <- closingResponse
	response := <-closingResponse
	if response != true {
		log.Panicf("Got unexpected response on closing channel")
	}

	sensor.shutdownOutboundConnections()

	sensor.wg.Wait()
	sensor.wg = nil
}

func (s *Sensor) ConnectOutbound(connectable OutboundConnectable) *OutboundConnection {
	return ConnectOutbound(s, connectable)
}

func (sensor *Sensor) MarshalJSON() ([]byte, error) {
	return json.Marshal(
		struct {
			NodeId       *NodeId
			VectorLength int
			Outbound     []*OutboundConnection
		}{
			NodeId:       sensor.NodeId,
			VectorLength: sensor.VectorLength,
			Outbound:     sensor.Outbound,
		})
}

func (sensor *Sensor) String() string {
	return JsonString(sensor)
}

func (sensor *Sensor) outbound() []*OutboundConnection {
	return sensor.Outbound
}

func (sensor *Sensor) setOutbound(newOutbound []*OutboundConnection) {
	sensor.Outbound = newOutbound
}

func (sensor *Sensor) checkRunnable() {
	if sensor.NodeId == nil {
		msg := fmt.Sprintf("not expecting sensor.NodeId to be nil")
		panic(msg)
	}

	if sensor.Closing == nil {
		msg := fmt.Sprintf("not expecting sensor.Closing to be nil")
		panic(msg)
	}

	if sensor.SyncChan == nil {
		msg := fmt.Sprintf("not expecting sensor.SyncChan to be nil")
		panic(msg)
	}

	if sensor.SensorFunction == nil {
		msg := fmt.Sprintf("not expecting sensor.SensorFunction to be nil")
		panic(msg)
	}

	if err := sensor.validateOutbound(); err != nil {
		msg := fmt.Sprintf("invalid outbound connection(s): %v", err.Error())
		panic(msg)
	}

}

func (sensor *Sensor) validateOutbound() error {
	for _, connection := range sensor.Outbound {
		if connection.DataChan == nil {
			msg := fmt.Sprintf("%v has empty DataChan", connection)
			return errors.New(msg)
		}
	}
	return nil
}

func (sensor *Sensor) scatterOutput(dataMessage *DataMessage) {

	if len(dataMessage.Inputs) == 0 {
		logg.LogPanic("cannot scatter empty data message")
	}

	for _, outboundConnection := range sensor.Outbound {
		logmsg := fmt.Sprintf("%v -> %v: %v", sensor.NodeId.UUID,
			outboundConnection.NodeId.UUID, dataMessage)
		logg.LogTo("NODE_PRE_SEND", logmsg)
		dataChan := outboundConnection.DataChan
		dataChan <- dataMessage
		logg.LogTo("NODE_POST_SEND", logmsg)
	}
}

func (sensor *Sensor) nodeId() *NodeId {
	return sensor.NodeId
}

func (sensor *Sensor) initOutboundConnections(nodeIdToDataMsg nodeIdToDataMsgMap) {
	for _, outboundConnection := range sensor.Outbound {
		if outboundConnection.DataChan == nil {
			dataChan := nodeIdToDataMsg[outboundConnection.NodeId.UUID]
			if dataChan != nil {
				outboundConnection.DataChan = dataChan
			}
		}
	}
}

func (sensor *Sensor) shutdownOutboundConnections() {
	for _, outboundConnection := range sensor.Outbound {
		outboundConnection.DataChan = nil
	}
}
<|endoftext|>


<commit_before>package devd

import (
	"crypto/tls"
	"fmt"
	"html/template"
	"net"
	"net/http"
	"os"
	"os/signal"
	"regexp"
	"strings"
	"syscall"
	"time"

	"golang.org/x/net/context"

	"github.com/GeertJohan/go.rice"
	"github.com/goji/httpauth"

	"github.com/cortesi/devd/httpctx"
	"github.com/cortesi/devd/inject"
	"github.com/cortesi/devd/livereload"
	"github.com/cortesi/devd/ricetemp"
	"github.com/cortesi/devd/slowdown"
	"github.com/cortesi/devd/timer"
	"github.com/cortesi/termlog"
)

const (
	// Version is the current version of devd
	Version  = "0.4"
	portLow  = 8000
	portHigh = 10000
)

func pickPort(addr string, low int, high int, tls bool) (net.Listener, error) {
	firstTry := 80
	if tls {
		firstTry = 443
	}
	hl, err := net.Listen("tcp", fmt.Sprintf("%v:%d", addr, firstTry))
	if err == nil {
		return hl, nil
	}
	for i := low; i < high; i++ {
		hl, err := net.Listen("tcp", fmt.Sprintf("%v:%d", addr, i))
		if err == nil {
			return hl, nil
		}
	}
	return nil, fmt.Errorf("Could not find open port.")
}

func getTLSConfig(path string) (t *tls.Config, err error) {
	config := &tls.Config{}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(path, path)
	if err != nil {
		return nil, err
	}
	return config, nil
}

// This filthy hack works in conjunction with hostPortStrip to restore the
// original request host after mux match.
func revertOriginalHost(r *http.Request) {
	original := r.Header.Get("_devd_original_host")
	if original != "" {
		r.Host = original
		r.Header.Del("_devd_original_host")
	}
}

// We can remove the mangling once this is fixed:
// 		https://github.com/golang/go/issues/10463
func hostPortStrip(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		host, _, err := net.SplitHostPort(r.Host)
		if err == nil {
			original := r.Host
			r.Host = host
			r.Header.Set("_devd_original_host", original)
		}
		next.ServeHTTP(w, r)
	})
}

func matchStringAny(regexps []*regexp.Regexp, s string) bool {
	for _, r := range regexps {
		if r.MatchString(s) {
			return true
		}
	}
	return false
}

func formatURL(tls bool, httpIP string, port int) string {
	proto := "http"
	if tls {
		proto = "https"
	}
	host := httpIP
	if httpIP == "0.0.0.0" || httpIP == "127.0.0.1" {
		host = "devd.io"
	}
	if port == 443 && tls {
		return fmt.Sprintf("https://%s", host)
	}
	if port == 80 && !tls {
		return fmt.Sprintf("http://%s", host)
	}
	return fmt.Sprintf("%s://%s:%d", proto, host, port)
}

// Credentials is a simple username/password pair
type Credentials struct {
	username string
	password string
}

// CredentialsFromSpec creates a set of credentials from a spec
func CredentialsFromSpec(spec string) (*Credentials, error) {
	parts := strings.SplitN(spec, ":", 2)
	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return nil, fmt.Errorf("Invalid credential spec: %s", spec)
	}
	return &Credentials{parts[0], parts[1]}, nil
}

// Devd represents the devd server options
type Devd struct {
	Routes RouteCollection

	// Shaping
	Latency  int
	DownKbps uint
	UpKbps   uint

	// Livereload and watch static routes
	LivereloadRoutes bool
	// Livereload, but don't watch static routes
	Livereload bool
	WatchPaths []string
	Excludes   []string

	// Logging
	IgnoreLogs []*regexp.Regexp

	// Password protection
	Credentials *Credentials

	lrserver *livereload.Server
}

// WrapHandler wraps an httpctx.Handler in the paraphernalia needed by devd for
// logging, latency, and so forth.
func (dd *Devd) WrapHandler(log termlog.Logger, next httpctx.Handler) http.Handler {
	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		revertOriginalHost(r)
		timr := timer.Timer{}
		sublog := log.Group()
		defer func() {
			timing := termlog.DefaultPalette.Timestamp.SprintFunc()("timing: ")
			sublog.SayAs("timer", timing+timr.String())
			sublog.Done()
		}()
		if matchStringAny(dd.IgnoreLogs, fmt.Sprintf("%s%s", r.URL.Host, r.RequestURI)) {
			sublog.Quiet()
		}
		timr.RequestHeaders()
		time.Sleep(time.Millisecond * time.Duration(dd.Latency))
		sublog.Say("%s %s", r.Method, r.URL)
		LogHeader(sublog, r.Header)
		ctx := timr.NewContext(context.Background())
		ctx = termlog.NewContext(ctx, sublog)
		next.ServeHTTPContext(
			ctx,
			&ResponseLogWriter{Log: sublog, Resp: w, Timer: &timr},
			r,
		)
	})
	return h
}

// HasLivereload tells us if liverload is enabled
func (dd *Devd) HasLivereload() bool {
	if dd.Livereload || dd.LivereloadRoutes || len(dd.WatchPaths) > 0 {
		return true
	}
	return false
}

// AddRoutes adds route specifications to the server
func (dd *Devd) AddRoutes(specs []string) error {
	dd.Routes = make(RouteCollection)
	for _, s := range specs {
		err := dd.Routes.Add(s)
		if err != nil {
			return fmt.Errorf("Invalid route specification: %s", err)
		}
	}
	return nil
}

// AddIgnores adds log ignore patterns to the server
func (dd *Devd) AddIgnores(specs []string) error {
	dd.IgnoreLogs = make([]*regexp.Regexp, 0, 0)
	for _, expr := range specs {
		v, err := regexp.Compile(expr)
		if err != nil {
			return fmt.Errorf("%s", err)
		}
		dd.IgnoreLogs = append(dd.IgnoreLogs, v)
	}
	return nil
}

// HandleNotFound handles pages not found. In particular, this handler is used
// when we have no matching route for a request. This also means it's not
// useful to inject the livereload paraphernalia here.
func HandleNotFound(templates *template.Template) httpctx.Handler {
	return httpctx.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		err := templates.Lookup("404.html").Execute(w, nil)
		if err != nil {
			logger := termlog.FromContext(ctx)
			logger.Shout("Could not execute template: %s", err)
		}
	})
}

// Router constructs the main Devd router that serves all requests
func (dd *Devd) Router(logger termlog.Logger, templates *template.Template) (http.Handler, error) {
	mux := http.NewServeMux()
	hasGlobal := false

	ci := inject.CopyInject{}
	if dd.HasLivereload() {
		ci = livereload.Injector
	}

	for match, route := range dd.Routes {
		if match == "/" {
			hasGlobal = true
		}
		handler := dd.WrapHandler(
			logger,
			route.Endpoint.Handler(templates, ci),
		)
		handler = http.StripPrefix(route.Path, handler)
		mux.Handle(match, handler)
	}
	if dd.HasLivereload() {
		lr := livereload.NewServer("livereload", logger)
		mux.Handle(livereload.EndpointPath, lr)
		mux.Handle(livereload.ScriptPath, http.HandlerFunc(lr.ServeScript))
		if dd.LivereloadRoutes {
			err := WatchRoutes(dd.Routes, lr, dd.Excludes, logger)
			if err != nil {
				return nil, fmt.Errorf("Could not watch routes for livereload: %s", err)
			}
		}
		if len(dd.WatchPaths) > 0 {
			err := WatchPaths(dd.WatchPaths, dd.Excludes, lr, logger)
			if err != nil {
				return nil, fmt.Errorf("Could not watch path for livereload: %s", err)
			}
		}
		dd.lrserver = lr
	}
	if !hasGlobal {
		mux.Handle(
			"/",
			dd.WrapHandler(logger, HandleNotFound(templates)),
		)
	}
	var h = http.Handler(mux)
	if dd.Credentials != nil {
		h = httpauth.SimpleBasicAuth(
			dd.Credentials.username, dd.Credentials.password,
		)(h)
	}
	return hostPortStrip(h), nil
}

// Serve starts the devd server. The callback is called with the serving URL
// just before service starts.
func (dd *Devd) Serve(address string, port int, certFile string, logger termlog.Logger, callback func(string)) error {
	templates, err := ricetemp.MakeTemplates(rice.MustFindBox("templates"))
	if err != nil {
		return fmt.Errorf("Error loading templates: %s", err)
	}
	mux, err := dd.Router(logger, templates)
	if err != nil {
		return err
	}
	var tlsConfig *tls.Config
	var tlsEnabled bool
	if certFile != "" {
		tlsConfig, err = getTLSConfig(certFile)
		if err != nil {
			return fmt.Errorf("Could not load certs: %s", err)
		}
		tlsEnabled = true
	}

	var hl net.Listener
	if port > 0 {
		hl, err = net.Listen("tcp", fmt.Sprintf("%v:%d", address, port))
	} else {
		hl, err = pickPort(address, portLow, portHigh, tlsEnabled)
	}
	if err != nil {
		return err
	}

	if tlsConfig != nil {
		hl = tls.NewListener(hl, tlsConfig)
	}

	hl = slowdown.NewSlowListener(hl, dd.UpKbps*1024, dd.DownKbps*1024)
	url := formatURL(tlsEnabled, address, hl.Addr().(*net.TCPAddr).Port)
	logger.Say("Listening on %s (%s)", url, hl.Addr().String())
	server := &http.Server{Addr: hl.Addr().String(), Handler: mux}
	callback(url)

	if dd.HasLivereload() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGHUP)
		go func() {
			for {
				<-c
				logger.Say("Received signal - reloading")
				dd.lrserver.Reload([]string{"*"})
			}
		}()
	}

	err = server.Serve(hl)
	logger.Shout("Server stopped: %v", err)
	return nil
}
<commit_msg>termlog interface changes<commit_after>package devd

import (
	"crypto/tls"
	"fmt"
	"html/template"
	"net"
	"net/http"
	"os"
	"os/signal"
	"regexp"
	"strings"
	"syscall"
	"time"

	"golang.org/x/net/context"

	"github.com/GeertJohan/go.rice"
	"github.com/goji/httpauth"

	"github.com/cortesi/devd/httpctx"
	"github.com/cortesi/devd/inject"
	"github.com/cortesi/devd/livereload"
	"github.com/cortesi/devd/ricetemp"
	"github.com/cortesi/devd/slowdown"
	"github.com/cortesi/devd/timer"
	"github.com/cortesi/termlog"
)

const (
	// Version is the current version of devd
	Version  = "0.4"
	portLow  = 8000
	portHigh = 10000
)

func pickPort(addr string, low int, high int, tls bool) (net.Listener, error) {
	firstTry := 80
	if tls {
		firstTry = 443
	}
	hl, err := net.Listen("tcp", fmt.Sprintf("%v:%d", addr, firstTry))
	if err == nil {
		return hl, nil
	}
	for i := low; i < high; i++ {
		hl, err := net.Listen("tcp", fmt.Sprintf("%v:%d", addr, i))
		if err == nil {
			return hl, nil
		}
	}
	return nil, fmt.Errorf("Could not find open port.")
}

func getTLSConfig(path string) (t *tls.Config, err error) {
	config := &tls.Config{}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(path, path)
	if err != nil {
		return nil, err
	}
	return config, nil
}

// This filthy hack works in conjunction with hostPortStrip to restore the
// original request host after mux match.
func revertOriginalHost(r *http.Request) {
	original := r.Header.Get("_devd_original_host")
	if original != "" {
		r.Host = original
		r.Header.Del("_devd_original_host")
	}
}

// We can remove the mangling once this is fixed:
// 		https://github.com/golang/go/issues/10463
func hostPortStrip(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		host, _, err := net.SplitHostPort(r.Host)
		if err == nil {
			original := r.Host
			r.Host = host
			r.Header.Set("_devd_original_host", original)
		}
		next.ServeHTTP(w, r)
	})
}

func matchStringAny(regexps []*regexp.Regexp, s string) bool {
	for _, r := range regexps {
		if r.MatchString(s) {
			return true
		}
	}
	return false
}

func formatURL(tls bool, httpIP string, port int) string {
	proto := "http"
	if tls {
		proto = "https"
	}
	host := httpIP
	if httpIP == "0.0.0.0" || httpIP == "127.0.0.1" {
		host = "devd.io"
	}
	if port == 443 && tls {
		return fmt.Sprintf("https://%s", host)
	}
	if port == 80 && !tls {
		return fmt.Sprintf("http://%s", host)
	}
	return fmt.Sprintf("%s://%s:%d", proto, host, port)
}

// Credentials is a simple username/password pair
type Credentials struct {
	username string
	password string
}

// CredentialsFromSpec creates a set of credentials from a spec
func CredentialsFromSpec(spec string) (*Credentials, error) {
	parts := strings.SplitN(spec, ":", 2)
	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return nil, fmt.Errorf("Invalid credential spec: %s", spec)
	}
	return &Credentials{parts[0], parts[1]}, nil
}

// Devd represents the devd server options
type Devd struct {
	Routes RouteCollection

	// Shaping
	Latency  int
	DownKbps uint
	UpKbps   uint

	// Livereload and watch static routes
	LivereloadRoutes bool
	// Livereload, but don't watch static routes
	Livereload bool
	WatchPaths []string
	Excludes   []string

	// Logging
	IgnoreLogs []*regexp.Regexp

	// Password protection
	Credentials *Credentials

	lrserver *livereload.Server
}

// WrapHandler wraps an httpctx.Handler in the paraphernalia needed by devd for
// logging, latency, and so forth.
func (dd *Devd) WrapHandler(log termlog.TermLog, next httpctx.Handler) http.Handler {
	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		revertOriginalHost(r)
		timr := timer.Timer{}
		sublog := log.Group()
		defer func() {
			timing := termlog.DefaultPalette.Timestamp.SprintFunc()("timing: ")
			sublog.SayAs("timer", timing+timr.String())
			sublog.Done()
		}()
		if matchStringAny(dd.IgnoreLogs, fmt.Sprintf("%s%s", r.URL.Host, r.RequestURI)) {
			sublog.Quiet()
		}
		timr.RequestHeaders()
		time.Sleep(time.Millisecond * time.Duration(dd.Latency))
		sublog.Say("%s %s", r.Method, r.URL)
		LogHeader(sublog, r.Header)
		ctx := timr.NewContext(context.Background())
		ctx = termlog.NewContext(ctx, sublog)
		next.ServeHTTPContext(
			ctx,
			&ResponseLogWriter{Log: sublog, Resp: w, Timer: &timr},
			r,
		)
	})
	return h
}

// HasLivereload tells us if liverload is enabled
func (dd *Devd) HasLivereload() bool {
	if dd.Livereload || dd.LivereloadRoutes || len(dd.WatchPaths) > 0 {
		return true
	}
	return false
}

// AddRoutes adds route specifications to the server
func (dd *Devd) AddRoutes(specs []string) error {
	dd.Routes = make(RouteCollection)
	for _, s := range specs {
		err := dd.Routes.Add(s)
		if err != nil {
			return fmt.Errorf("Invalid route specification: %s", err)
		}
	}
	return nil
}

// AddIgnores adds log ignore patterns to the server
func (dd *Devd) AddIgnores(specs []string) error {
	dd.IgnoreLogs = make([]*regexp.Regexp, 0, 0)
	for _, expr := range specs {
		v, err := regexp.Compile(expr)
		if err != nil {
			return fmt.Errorf("%s", err)
		}
		dd.IgnoreLogs = append(dd.IgnoreLogs, v)
	}
	return nil
}

// HandleNotFound handles pages not found. In particular, this handler is used
// when we have no matching route for a request. This also means it's not
// useful to inject the livereload paraphernalia here.
func HandleNotFound(templates *template.Template) httpctx.Handler {
	return httpctx.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		err := templates.Lookup("404.html").Execute(w, nil)
		if err != nil {
			logger := termlog.FromContext(ctx)
			logger.Shout("Could not execute template: %s", err)
		}
	})
}

// Router constructs the main Devd router that serves all requests
func (dd *Devd) Router(logger termlog.TermLog, templates *template.Template) (http.Handler, error) {
	mux := http.NewServeMux()
	hasGlobal := false

	ci := inject.CopyInject{}
	if dd.HasLivereload() {
		ci = livereload.Injector
	}

	for match, route := range dd.Routes {
		if match == "/" {
			hasGlobal = true
		}
		handler := dd.WrapHandler(
			logger,
			route.Endpoint.Handler(templates, ci),
		)
		handler = http.StripPrefix(route.Path, handler)
		mux.Handle(match, handler)
	}
	if dd.HasLivereload() {
		lr := livereload.NewServer("livereload", logger)
		mux.Handle(livereload.EndpointPath, lr)
		mux.Handle(livereload.ScriptPath, http.HandlerFunc(lr.ServeScript))
		if dd.LivereloadRoutes {
			err := WatchRoutes(dd.Routes, lr, dd.Excludes, logger)
			if err != nil {
				return nil, fmt.Errorf("Could not watch routes for livereload: %s", err)
			}
		}
		if len(dd.WatchPaths) > 0 {
			err := WatchPaths(dd.WatchPaths, dd.Excludes, lr, logger)
			if err != nil {
				return nil, fmt.Errorf("Could not watch path for livereload: %s", err)
			}
		}
		dd.lrserver = lr
	}
	if !hasGlobal {
		mux.Handle(
			"/",
			dd.WrapHandler(logger, HandleNotFound(templates)),
		)
	}
	var h = http.Handler(mux)
	if dd.Credentials != nil {
		h = httpauth.SimpleBasicAuth(
			dd.Credentials.username, dd.Credentials.password,
		)(h)
	}
	return hostPortStrip(h), nil
}

// Serve starts the devd server. The callback is called with the serving URL
// just before service starts.
func (dd *Devd) Serve(address string, port int, certFile string, logger termlog.TermLog, callback func(string)) error {
	templates, err := ricetemp.MakeTemplates(rice.MustFindBox("templates"))
	if err != nil {
		return fmt.Errorf("Error loading templates: %s", err)
	}
	mux, err := dd.Router(logger, templates)
	if err != nil {
		return err
	}
	var tlsConfig *tls.Config
	var tlsEnabled bool
	if certFile != "" {
		tlsConfig, err = getTLSConfig(certFile)
		if err != nil {
			return fmt.Errorf("Could not load certs: %s", err)
		}
		tlsEnabled = true
	}

	var hl net.Listener
	if port > 0 {
		hl, err = net.Listen("tcp", fmt.Sprintf("%v:%d", address, port))
	} else {
		hl, err = pickPort(address, portLow, portHigh, tlsEnabled)
	}
	if err != nil {
		return err
	}

	if tlsConfig != nil {
		hl = tls.NewListener(hl, tlsConfig)
	}

	hl = slowdown.NewSlowListener(hl, dd.UpKbps*1024, dd.DownKbps*1024)
	url := formatURL(tlsEnabled, address, hl.Addr().(*net.TCPAddr).Port)
	logger.Say("Listening on %s (%s)", url, hl.Addr().String())
	server := &http.Server{Addr: hl.Addr().String(), Handler: mux}
	callback(url)

	if dd.HasLivereload() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGHUP)
		go func() {
			for {
				<-c
				logger.Say("Received signal - reloading")
				dd.lrserver.Reload([]string{"*"})
			}
		}()
	}

	err = server.Serve(hl)
	logger.Shout("Server stopped: %v", err)
	return nil
}
<|endoftext|>


<commit_before>package hamgo

import (
	"fmt"
	"net/http"
)

const (
	//DefaultPort : default port to listen
	defaultPort = "8080"
	confPort    = "port"
)

//Server : web server interface
type Server interface {
	//base
	RunAt(port string) error
	Run() error
	GetPort() string
	GetMux() *http.ServeMux
	//method
	Get(path string, handler func(ctx *WebContext)) Server
	Post(path string, handler func(ctx *WebContext)) Server
	Put(path string, handler func(ctx *WebContext)) Server
	Delete(path string, handler func(ctx *WebContext)) Server
	Head(path string, handler func(ctx *WebContext)) Server
	//get AOP
	GetBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	GetAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	GetBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//post AOP
	PostBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	PostAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	PostBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//put AOP
	PutBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	PutAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	PutBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//delete AOP
	DeleteBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	DeleteAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	DeleteBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//head AOP
	HeadBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	HeadAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	HeadBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//static folder
	Static(folder string) Server
	//common handler
	Handler(path string, handler func(ctx *WebContext), method string) Server
	HandlerBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), method string) Server
	HandlerAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server
	HandlerBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server
	//filter
	Filter(handler func(ctx *WebContext) bool) *filter
}

//webServer : a web server implements Server interface
type webServer struct {
	port   string
	mux    *http.ServeMux
	filter *filter
}

//NewServer : creat a web server
func newServer() Server {
	return &webServer{mux: http.NewServeMux()}
}

//RunAt : let server run at port
func (s *webServer) RunAt(port string) error {
	s.port = ":" + port
	fmt.Printf("Server started on port :%s\n", port)
	return http.ListenAndServe(s.port, s.mux)
}

//Run : server run at default port 8080
func (s *webServer) Run() error {
	return s.RunAt(Conf.DefaultString(confPort, defaultPort))
}

//GetPort : get server run port
func (s *webServer) GetPort() string {
	return s.port
}

//
func (s *webServer) GetMux() *http.ServeMux {
	return s.mux
}

//Get : set GET method handler
func (s *webServer) Get(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodGet)
}

//Post : set POST method handler
func (s *webServer) Post(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodPost)
}

//Put : set PUT method handler
func (s *webServer) Put(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodPut)
}

//Delete : set DELETE method handler
func (s *webServer) Delete(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodDelete)
}

//Head : set HEAD method handler
func (s *webServer) Head(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodHead)
}

//GetBefore : set func before GET method handler
func (s *webServer) GetBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//GetAfter : set func after GET method handler
func (s *webServer) GetAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//GetBeforeAfter : set func after & before GET method handler
func (s *webServer) GetBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//PostBefore : set func before POST method handler
func (s *webServer) PostBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodPost)
}

//PostAfter : set func after POST method handler
func (s *webServer) PostAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodPost)
}

//PostBeforeAfter : set func after & before POST method handler
func (s *webServer) PostBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodPost)
}

//PutBefore :
func (s *webServer) PutBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//PutAfter :
func (s *webServer) PutAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//PutBeforeAfter :
func (s *webServer) PutBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//DeleteBefore :
func (s *webServer) DeleteBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//DeleteAfter :
func (s *webServer) DeleteAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//DeleteBeforeAfter :
func (s *webServer) DeleteBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//HeadBefore :
func (s *webServer) HeadBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//HeadAfter :
func (s *webServer) HeadAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//HeadBeforeAfter :
func (s *webServer) HeadBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//Static :
func (s *webServer) Static(folder string) Server {
	s.mux.Handle("/"+folder+"/", http.StripPrefix("/"+folder+"/", http.FileServer(http.Dir(folder))))
	return s
}

//Handler :
func (s *webServer) Handler(path string, handler func(ctx *WebContext), method string) Server {

	r := newRoute(path, method, s.filter, handler)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//HandlerBefore :
func (s *webServer) HandlerBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), method string) Server {

	r := newBeforeRoute(path, method, s.filter, handlerBefore, handler)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//HandlerAfter :
func (s *webServer) HandlerAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server {

	r := newAfterRoute(path, method, s.filter, handler, handlerAfter)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//HandlerBeforeAfter :
func (s *webServer) HandlerBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server {

	r := newBeforeAfterRoute(path, method, s.filter, handlerBefore, handler, handlerAfter)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//Filter : true is pass filter , false is not pass
func (s *webServer) Filter(handler func(ctx *WebContext) bool) *filter {
	s.filter = &filter{annoURL: make([]string, 1), handler: handler}
	return s.filter
}
<commit_msg>debug Run() at 8080<commit_after>package hamgo

import (
	"fmt"
	"net/http"
)

const (
	//DefaultPort : default port to listen
	defaultPort = "8080"
	confPort    = "port"
)

//Server : web server interface
type Server interface {
	//base
	RunAt(port string) error
	Run() error
	GetPort() string
	GetMux() *http.ServeMux
	//method
	Get(path string, handler func(ctx *WebContext)) Server
	Post(path string, handler func(ctx *WebContext)) Server
	Put(path string, handler func(ctx *WebContext)) Server
	Delete(path string, handler func(ctx *WebContext)) Server
	Head(path string, handler func(ctx *WebContext)) Server
	//get AOP
	GetBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	GetAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	GetBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//post AOP
	PostBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	PostAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	PostBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//put AOP
	PutBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	PutAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	PutBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//delete AOP
	DeleteBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	DeleteAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	DeleteBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//head AOP
	HeadBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server
	HeadAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	HeadBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server
	//static folder
	Static(folder string) Server
	//common handler
	Handler(path string, handler func(ctx *WebContext), method string) Server
	HandlerBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), method string) Server
	HandlerAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server
	HandlerBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server
	//filter
	Filter(handler func(ctx *WebContext) bool) *filter
}

//webServer : a web server implements Server interface
type webServer struct {
	port   string
	mux    *http.ServeMux
	filter *filter
}

//NewServer : creat a web server
func newServer() Server {
	return &webServer{mux: http.NewServeMux()}
}

//RunAt : let server run at port
func (s *webServer) RunAt(port string) error {
	s.port = ":" + port
	fmt.Printf("Server started on port :%s\n", port)
	return http.ListenAndServe(s.port, s.mux)
}

//Run : server run at default port 8080
func (s *webServer) Run() error {
	return s.RunAt(defaultPort)
}

//GetPort : get server run port
func (s *webServer) GetPort() string {
	return s.port
}

//
func (s *webServer) GetMux() *http.ServeMux {
	return s.mux
}

//Get : set GET method handler
func (s *webServer) Get(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodGet)
}

//Post : set POST method handler
func (s *webServer) Post(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodPost)
}

//Put : set PUT method handler
func (s *webServer) Put(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodPut)
}

//Delete : set DELETE method handler
func (s *webServer) Delete(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodDelete)
}

//Head : set HEAD method handler
func (s *webServer) Head(path string, handler func(ctx *WebContext)) Server {
	return s.Handler(path, handler, http.MethodHead)
}

//GetBefore : set func before GET method handler
func (s *webServer) GetBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//GetAfter : set func after GET method handler
func (s *webServer) GetAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//GetBeforeAfter : set func after & before GET method handler
func (s *webServer) GetBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//PostBefore : set func before POST method handler
func (s *webServer) PostBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodPost)
}

//PostAfter : set func after POST method handler
func (s *webServer) PostAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodPost)
}

//PostBeforeAfter : set func after & before POST method handler
func (s *webServer) PostBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodPost)
}

//PutBefore :
func (s *webServer) PutBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//PutAfter :
func (s *webServer) PutAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//PutBeforeAfter :
func (s *webServer) PutBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//DeleteBefore :
func (s *webServer) DeleteBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//DeleteAfter :
func (s *webServer) DeleteAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//DeleteBeforeAfter :
func (s *webServer) DeleteBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//HeadBefore :
func (s *webServer) HeadBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext)) Server {
	return s.HandlerBefore(path, handlerBefore, handler, http.MethodGet)
}

//HeadAfter :
func (s *webServer) HeadAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerAfter(path, handler, handlerAfter, http.MethodGet)
}

//HeadBeforeAfter :
func (s *webServer) HeadBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext)) Server {
	return s.HandlerBeforeAfter(path, handlerBefore, handler, handlerAfter, http.MethodGet)
}

//Static :
func (s *webServer) Static(folder string) Server {
	s.mux.Handle("/"+folder+"/", http.StripPrefix("/"+folder+"/", http.FileServer(http.Dir(folder))))
	return s
}

//Handler :
func (s *webServer) Handler(path string, handler func(ctx *WebContext), method string) Server {

	r := newRoute(path, method, s.filter, handler)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//HandlerBefore :
func (s *webServer) HandlerBefore(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), method string) Server {

	r := newBeforeRoute(path, method, s.filter, handlerBefore, handler)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//HandlerAfter :
func (s *webServer) HandlerAfter(path string, handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server {

	r := newAfterRoute(path, method, s.filter, handler, handlerAfter)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//HandlerBeforeAfter :
func (s *webServer) HandlerBeforeAfter(path string, handlerBefore func(ctx *WebContext), handler func(ctx *WebContext), handlerAfter func(ctx *WebContext), method string) Server {

	r := newBeforeAfterRoute(path, method, s.filter, handlerBefore, handler, handlerAfter)
	s.mux.Handle(newPath(path).Route(), r)
	return s
}

//Filter : true is pass filter , false is not pass
func (s *webServer) Filter(handler func(ctx *WebContext) bool) *filter {
	s.filter = &filter{annoURL: make([]string, 1), handler: handler}
	return s.filter
}
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"log"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be
	return self.difficultyHelper(200.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	//TODO: implement this
	return "ERROR: NOT IMPLEMENTED"
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.
	//TODO: Implement this.

	getter := grid.queue().DefaultGetter()

	_MAX_IMPLICATION_STEPS := 6

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)
		secondAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum,
			firstAccumulator)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum,
			secondAccumulator)

		//TODO:Check if the sets overlap.

		doPrint := candidateCell.Row() == 1 && candidateCell.Col() == 0

		//For these debugging purposes, only print out the candidateCell we know to be interesting in the test case.
		if doPrint {
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//accumulate forward, so the last generation has ALL cells affected in any generation on that branch
		firstAccumulator.accumulateGenerations()
		secondAccumulator.accumulateGenerations()

		if doPrint {
			log.Println("Accumulators after accumulating generations:")
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		foundOne := false

		for generation := _MAX_IMPLICATION_STEPS - 1; generation >= 0 && !foundOne; generation-- {

			//Check for any overlap at the last generation
			firstAffectedCells := firstAccumulator[generation]
			secondAffectedCells := secondAccumulator[generation]

			for key, val := range firstAffectedCells {

				//Skip the candidateCell, because that's not a meaningful overlap--we set that one as a way of branching!
				if key == candidateCell.ref() {
					continue
				}

				if num, ok := secondAffectedCells[key]; ok {
					//Found cell overlap! ... is the forced number the same?
					if val == num {
						//Yup, seems like we've found a cell that is forced to the same value on either branch.
						step := &SolveStep{self,
							CellSlice{key.Cell(grid)},
							IntSlice{val},
							CellSlice{candidateCell},
							candidateCell.Possibilities(),
						}

						if doPrint {
							log.Println(step)
							log.Println("Candidate Cell", candidateCell.ref())
						}

						if step.IsUseful(grid) {
							foundOne = true
							if doPrint {
								log.Println("Found solution on generation: ", generation)
							}
							select {
							case results <- step:
							case <-done:
								return
							}
						}
					}
				}
			}
		}

		//TODO: figure out why the tests are coming back with different answers, even when only looking at the key cell
		//that should work from the example.
		//TODO: we should prefer solutions where the total implications on both branches are minimized.
		//For example, if only one implication is requried on left, but 4 are on right, that's preferable to one where
		//three implications are required on both sides.
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

	}
}

type chainSearcherGenerationDetails map[cellRef]int

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map\n"
	for cell, num := range c {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

//accumulateGenerations goes through each generation (youngest to newest)
//and squaches older generation maps into each generation, so each
//generation's map represents the totality of all cells seen at that point.
func (c chainSearcherAccumulator) accumulateGenerations() {
	for i := len(c) - 2; i >= 0; i-- {
		lastGeneration := c[i+1]
		currentGeneration := c[i]
		for key, val := range lastGeneration {
			if currentVal, ok := currentGeneration[key]; ok {
				if currentVal != val {
					log.Println("We were about to overwrite a value from an earlier generation... this shouldn't happen.")
				}
			}
			currentGeneration[key] = val
		}
	}
}

func makeChainSeacherAccumulator(size int) chainSearcherAccumulator {
	result := make(chainSearcherAccumulator, size)
	for i := 0; i < size; i++ {
		result[i] = make(map[cellRef]int)
	}
	return result
}

func chainSearcher(i int, cell *Cell, numToApply int, accumulator chainSearcherAccumulator) {
	if i <= 0 || cell == nil {
		//Base case
		return
	}

	if i-1 >= len(accumulator) {
		panic("The accumulator provided was not big enough for the i provided.")
	}

	generationDetails := accumulator[i-1]

	//Find the nextCells that WILL have their numbers forced by the cell we're thinking of fillint.
	cellsToVisit := cell.Neighbors().FilterByPossible(numToApply).FilterByNumPossibilities(2)

	//Now that we know which cells will be affected and what their next number will be,
	//set the number in the given cell and then recurse downward down each branch.
	cell.SetNumber(numToApply)

	generationDetails[cell.ref()] = numToApply

	for _, cellToVisit := range cellsToVisit {

		possibilities := cellToVisit.Possibilities()

		if len(possibilities) != 1 {
			panic("Expected the cell to have one possibility")
		}

		forcedNum := possibilities[0]

		//Each branch modifies the grid, so create a new copy
		newGrid := cellToVisit.grid.Copy()
		cellToVisit = cellToVisit.InGrid(newGrid)

		//Recurse downward
		chainSearcher(i-1, cellToVisit, forcedNum, accumulator)

	}

}
<commit_msg>TESTS FAIL. Added a note about why the overlapping values happen (it's to be expected)<commit_after>package sudoku

import (
	"fmt"
	"log"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be
	return self.difficultyHelper(200.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	//TODO: implement this
	return "ERROR: NOT IMPLEMENTED"
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.
	//TODO: Implement this.

	getter := grid.queue().DefaultGetter()

	_MAX_IMPLICATION_STEPS := 6

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)
		secondAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum,
			firstAccumulator)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum,
			secondAccumulator)

		//TODO:Check if the sets overlap.

		doPrint := candidateCell.Row() == 1 && candidateCell.Col() == 0

		//For these debugging purposes, only print out the candidateCell we know to be interesting in the test case.
		if doPrint {
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//accumulate forward, so the last generation has ALL cells affected in any generation on that branch
		firstAccumulator.accumulateGenerations()
		secondAccumulator.accumulateGenerations()

		if doPrint {
			log.Println("Accumulators after accumulating generations:")
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		foundOne := false

		for generation := _MAX_IMPLICATION_STEPS - 1; generation >= 0 && !foundOne; generation-- {

			//Check for any overlap at the last generation
			firstAffectedCells := firstAccumulator[generation]
			secondAffectedCells := secondAccumulator[generation]

			for key, val := range firstAffectedCells {

				//Skip the candidateCell, because that's not a meaningful overlap--we set that one as a way of branching!
				if key == candidateCell.ref() {
					continue
				}

				if num, ok := secondAffectedCells[key]; ok {
					//Found cell overlap! ... is the forced number the same?
					if val == num {
						//Yup, seems like we've found a cell that is forced to the same value on either branch.
						step := &SolveStep{self,
							CellSlice{key.Cell(grid)},
							IntSlice{val},
							CellSlice{candidateCell},
							candidateCell.Possibilities(),
						}

						if doPrint {
							log.Println(step)
							log.Println("Candidate Cell", candidateCell.ref())
						}

						if step.IsUseful(grid) {
							foundOne = true
							if doPrint {
								log.Println("Found solution on generation: ", generation)
							}
							select {
							case results <- step:
							case <-done:
								return
							}
						}
					}
				}
			}
		}

		//TODO: figure out why the tests are coming back with different answers, even when only looking at the key cell
		//that should work from the example.
		//TODO: we should prefer solutions where the total implications on both branches are minimized.
		//For example, if only one implication is requried on left, but 4 are on right, that's preferable to one where
		//three implications are required on both sides.
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

	}
}

type chainSearcherGenerationDetails map[cellRef]int

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map\n"
	for cell, num := range c {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

//accumulateGenerations goes through each generation (youngest to newest)
//and squaches older generation maps into each generation, so each
//generation's map represents the totality of all cells seen at that point.
func (c chainSearcherAccumulator) accumulateGenerations() {
	for i := len(c) - 2; i >= 0; i-- {
		lastGeneration := c[i+1]
		currentGeneration := c[i]
		for key, val := range lastGeneration {
			if currentVal, ok := currentGeneration[key]; ok {
				if currentVal != val {
					//No, this should be expected to happen when we get to an invalid grid state,
					//which we should expect to happen down one of the two branches (at least as explore
					//far enough.)
					log.Println("We were about to overwrite a value from an earlier generation... this shouldn't happen.")
				}
			}
			currentGeneration[key] = val
		}
	}
}

func makeChainSeacherAccumulator(size int) chainSearcherAccumulator {
	result := make(chainSearcherAccumulator, size)
	for i := 0; i < size; i++ {
		result[i] = make(map[cellRef]int)
	}
	return result
}

func chainSearcher(i int, cell *Cell, numToApply int, accumulator chainSearcherAccumulator) {
	if i <= 0 || cell == nil {
		//Base case
		return
	}

	if i-1 >= len(accumulator) {
		panic("The accumulator provided was not big enough for the i provided.")
	}

	generationDetails := accumulator[i-1]

	//Find the nextCells that WILL have their numbers forced by the cell we're thinking of fillint.
	cellsToVisit := cell.Neighbors().FilterByPossible(numToApply).FilterByNumPossibilities(2)

	//Now that we know which cells will be affected and what their next number will be,
	//set the number in the given cell and then recurse downward down each branch.
	cell.SetNumber(numToApply)

	generationDetails[cell.ref()] = numToApply

	for _, cellToVisit := range cellsToVisit {

		possibilities := cellToVisit.Possibilities()

		if len(possibilities) != 1 {
			panic("Expected the cell to have one possibility")
		}

		forcedNum := possibilities[0]

		//Each branch modifies the grid, so create a new copy
		newGrid := cellToVisit.grid.Copy()
		cellToVisit = cellToVisit.InGrid(newGrid)

		//Recurse downward
		chainSearcher(i-1, cellToVisit, forcedNum, accumulator)

	}

}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"
	"net/http"
	"io/ioutil"
	"encoding/base64"

	"golang.org/x/crypto/ssh"
)

const (
	maxNameLength        = 32
	historyLength        = 20
	systemMessageFormat  = "\033[1;90m"
	privateMessageFormat = "\033[1m"
	highlightFormat      = Bold + "\033[48;5;11m\033[38;5;16m"
	beep                 = "\007"
)

var (
	reStripText = regexp.MustCompile("[^0-9A-Za-z_.-]")
)

// Clients is a map of clients
type Clients map[string]*Client

// Server holds all the fields used by a server
type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	count     int
	history   *History
	motd      string
	whitelist map[string]struct{}   // fingerprint lookup
	admins    map[string]struct{}   // fingerprint lookup
	bannedPK  map[string]*time.Time // fingerprint lookup
	started   time.Time
	sync.Mutex
}

// NewServer constructs a new server
func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:      make(chan struct{}),
		clients:   Clients{},
		count:     0,
		history:   NewHistory(historyLength),
		motd:      "",
		whitelist: map[string]struct{}{},
		admins:    map[string]struct{}{},
		bannedPK:  map[string]*time.Time{},
		started:   time.Now(),
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			if !server.IsWhitelisted(fingerprint) {
				return nil, fmt.Errorf("Not Whitelisted.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
		KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
			return nil, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

// Len returns the number of clients
func (s *Server) Len() int {
	return len(s.clients)
}

// SysMsg broadcasts the given message to everyone
func (s *Server) SysMsg(msg string, args ...interface{}) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, " * "+fmt.Sprintf(msg, args...)), nil)
}

// Broadcast broadcasts the given message to everyone except for the given client
func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}

		if strings.Contains(msg, client.Name) {
			// Turn message red if client's name is mentioned, and send BEL if they have enabled beeping
			personalMsg := strings.Replace(msg, client.Name, highlightFormat+client.Name+Reset, -1)
			if client.beepMe {
				personalMsg += beep
			}
			client.Send(personalMsg)
		} else {
			client.Send(msg)
		}
	}
}

// Privmsg sends a message to a particular nick, if it exists
func (s *Server) Privmsg(nick, message string, sender *Client) error {
	// Get the recipient
	target, ok := s.clients[strings.ToLower(nick)]
	if !ok {
		return fmt.Errorf("no client with that nick")
	}
	// Send the message
	target.Msg <- fmt.Sprintf(beep+"[PM from %v] %s%v%s", sender.ColoredName(), privateMessageFormat, message, Reset)
	logger.Debugf("PM from %v to %v: %v", sender.Name, nick, message)
	return nil
}

// SetMotd sets the Message of the Day (MOTD)
func (s *Server) SetMotd(motd string) {
	s.Lock()
	s.motd = motd
	s.Unlock()
}

// MotdUnicast sends the MOTD as a SysMsg
func (s *Server) MotdUnicast(client *Client) {
	if s.motd == "" {
		return
	}
	client.SysMsg(s.motd)
}

// MotdBroadcast broadcasts the MOTD
func (s *Server) MotdBroadcast(client *Client) {
	if s.motd == "" {
		return
	}
	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * New MOTD set by %s.", client.ColoredName())), client)
	s.Broadcast(s.motd, client)
}

// Add adds the client to the list of clients
func (s *Server) Add(client *Client) {
	go func() {
		s.MotdUnicast(client)
		client.SendLines(s.history.Get(10))
	}()

	s.Lock()
	s.count++

	newName, err := s.proposeName(client.Name)
	if err != nil {
		client.SysMsg("Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.Name, ColorString(client.Color, newName))
	}

	client.Rename(newName)
	s.clients[strings.ToLower(client.Name)] = client
	num := len(s.clients)
	s.Unlock()

	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * %s joined. (Total connected: %d)", client.Name, num)), client)
}

// Remove removes the given client from the list of clients
func (s *Server) Remove(client *Client) {
	s.Lock()
	delete(s.clients, strings.ToLower(client.Name))
	s.Unlock()

	s.SysMsg("%s left.", client.Name)
}

func (s *Server) proposeName(name string) (string, error) {
	// Assumes caller holds lock.
	var err error
	name = reStripText.ReplaceAllString(name, "")

	if len(name) > maxNameLength {
		name = name[:maxNameLength]
	} else if len(name) == 0 {
		name = fmt.Sprintf("Guest%d", s.count)
	}

	_, collision := s.clients[strings.ToLower(name)]
	if collision {
		err = fmt.Errorf("%s is not available", name)
		name = fmt.Sprintf("Guest%d", s.count)
	}

	return name, err
}

// Rename renames the given client (user)
func (s *Server) Rename(client *Client, newName string) {
	s.Lock()
	var oldName string
	if strings.ToLower(newName) == strings.ToLower(client.Name) {
		oldName = client.Name
		client.Rename(newName)
	} else {
		newName, err := s.proposeName(newName)
		if err != nil {
			client.SysMsg("%s", err)
			s.Unlock()
			return
		}

		// TODO: Use a channel/goroutine for adding clients, rather than locks?
		delete(s.clients, strings.ToLower(client.Name))
		oldName = client.Name
		client.Rename(newName)
		s.clients[strings.ToLower(client.Name)] = client
		s.Unlock()
	}
	s.SysMsg("%s is now known as %s.", ColorString(client.Color, oldName), ColorString(client.Color, newName))
}

// List lists the clients with the given prefix
func (s *Server) List(prefix *string) []string {
	r := []string{}

	for name := range s.clients {
		if prefix != nil && !strings.HasPrefix(name, *prefix) {
			continue
		}
		r = append(r, name)
	}

	return r
}

// Who returns the client with a given name
func (s *Server) Who(name string) *Client {
	return s.clients[strings.ToLower(name)]
}

// Op adds the given fingerprint to the list of admins
func (s *Server) Op(fingerprint string) {
	logger.Infof("Adding admin: %s", fingerprint)
	s.Lock()
	s.admins[fingerprint] = struct{}{}
	s.Unlock()
}

// Whitelist adds the given fingerprint to the whitelist
func (s *Server) Whitelist(fingerprint string) error {
	if strings.HasPrefix(fingerprint, "github.com/") {
		logger.Infof("Adding github account %s to whitelist", fingerprint)

		keys, err := getGithubPubKeys(fingerprint)
		if err != nil {
			return err
		}
		if len(keys) == 0 {
			return fmt.Errorf("No github user %s", fingerprint)
		}
		for _, key := range keys {
			fingerprint = Fingerprint(key)
			logger.Infof("Adding whitelist: %s", fingerprint)
			s.Lock()
			s.whitelist[fingerprint] = struct{}{}
			s.Unlock()
		}
	} else {
		logger.Infof("Adding whitelist: %s", fingerprint)
		s.Lock()
		s.whitelist[fingerprint] = struct{}{}
		s.Unlock()
	}
	return nil
}

var pubKeyRegex = regexp.MustCompile(`ssh-rsa ([A-Za-z0-9\+=\/]+)\s*`)
// Returns an array of public keys for the given github user URL
func getGithubPubKeys(url string) ([]ssh.PublicKey, error) {
	timeout := time.Duration(10 * time.Second)
	client := http.Client{
	    Timeout: timeout,
	}
	resp, err := client.Get("http://" + url + ".keys")

	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	bodyStr := string(body)
	keys := pubKeyRegex.FindAllStringSubmatch(bodyStr, -1)
	pubs := make([]ssh.PublicKey, 0, 3)
	for _, key := range keys {
		if(len(key) < 2) {
			continue
		}

		bodyDecoded, err := base64.StdEncoding.DecodeString(key[1])
		if err != nil {
			return nil, err
		}

		pub, err := ssh.ParsePublicKey(bodyDecoded)
		if err != nil {
			return nil, err
		}

		pubs = append(pubs, pub)
	}
	return pubs, nil
}

// Uptime returns the time since the server was started
func (s *Server) Uptime() string {
	return time.Now().Sub(s.started).String()
}

// IsOp checks if the given client is Op
func (s *Server) IsOp(client *Client) bool {
	_, r := s.admins[client.Fingerprint()]
	return r
}

// IsWhitelisted checks if the given fingerprint is whitelisted
func (s *Server) IsWhitelisted(fingerprint string) bool {
	/* if no whitelist, anyone is welcome */
	if len(s.whitelist) == 0 {
		return true
	}

	/* otherwise, check for whitelist presence */
	_, r := s.whitelist[fingerprint]
	return r
}

// IsBanned checks if the given fingerprint is banned
func (s *Server) IsBanned(fingerprint string) bool {
	ban, hasBan := s.bannedPK[fingerprint]
	if !hasBan {
		return false
	}
	if ban == nil {
		return true
	}
	if ban.Before(time.Now()) {
		s.Unban(fingerprint)
		return false
	}
	return true
}

// Ban bans a fingerprint for the given duration
func (s *Server) Ban(fingerprint string, duration *time.Duration) {
	var until *time.Time
	s.Lock()
	if duration != nil {
		when := time.Now().Add(*duration)
		until = &when
	}
	s.bannedPK[fingerprint] = until
	s.Unlock()
}

// Unban unbans a banned fingerprint
func (s *Server) Unban(fingerprint string) {
	s.Lock()
	delete(s.bannedPK, fingerprint)
	s.Unlock()
}

// Start starts the server
func (s *Server) Start(laddr string) error {
	// Once a ServerConfig has been configured, connections can be
	// accepted.
	socket, err := net.Listen("tcp", laddr)
	if err != nil {
		return err
	}

	logger.Infof("Listening on %s", laddr)

	go func() {
		defer socket.Close()
		for {
			conn, err := socket.Accept()

			if err != nil {
				logger.Errorf("Failed to accept connection: %v", err)
				if err == syscall.EINVAL {
					// TODO: Handle shutdown more gracefully?
					return
				}
			}

			// Goroutineify to resume accepting sockets early.
			go func() {
				// From a standard TCP connection to an encrypted SSH connection
				sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
				if err != nil {
					logger.Errorf("Failed to handshake: %v", err)
					return
				}

				version := reStripText.ReplaceAllString(string(sshConn.ClientVersion()), "")
				if len(version) > 100 {
					version = "Evil Jerk with a superlong string"
				}
				logger.Infof("Connection #%d from: %s, %s, %s", s.count+1, sshConn.RemoteAddr(), sshConn.User(), version)

				go ssh.DiscardRequests(requests)

				client := NewClient(s, sshConn)
				go client.handleChannels(channels)
			}()
		}
	}()

	go func() {
		<-s.done
		socket.Close()
	}()

	return nil
}

// AutoCompleteFunction handles auto completion of nicks
func (s *Server) AutoCompleteFunction(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
	if key == 9 {
		shortLine := strings.Split(line[:pos], " ")
		partialNick := shortLine[len(shortLine)-1]

		nicks := s.List(&partialNick)
		if len(nicks) > 0 {
			nick := nicks[len(nicks)-1]
			posPartialNick := pos - len(partialNick)
			if len(shortLine) < 2 {
				nick += ": "
			} else {
				nick += " "
			}
			newLine = strings.Replace(line[posPartialNick:],
				partialNick, nick, 1)
			newLine = line[:posPartialNick] + newLine
			newPos = pos + (len(nick) - len(partialNick))
			ok = true
		}
	} else {
		ok = false
	}
	return
}

// Stop stops the server
func (s *Server) Stop() {
	for _, client := range s.clients {
		client.Conn.Close()
	}

	close(s.done)
}

// Fingerprint returns the fingerprint based on a public key
func Fingerprint(k ssh.PublicKey) string {
	hash := md5.Sum(k.Marshal())
	r := fmt.Sprintf("% x", hash)
	return strings.Replace(r, " ", ":", -1)
}
<commit_msg>Removed regex, added timeout.<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"
	"net/http"
	"io/ioutil"
	"encoding/base64"

	"golang.org/x/crypto/ssh"
)

const (
	maxNameLength        = 32
	historyLength        = 20
	systemMessageFormat  = "\033[1;90m"
	privateMessageFormat = "\033[1m"
	highlightFormat      = Bold + "\033[48;5;11m\033[38;5;16m"
	beep                 = "\007"
)

var (
	reStripText = regexp.MustCompile("[^0-9A-Za-z_.-]")
)

// Clients is a map of clients
type Clients map[string]*Client

// Server holds all the fields used by a server
type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	count     int
	history   *History
	motd      string
	whitelist map[string]struct{}   // fingerprint lookup
	admins    map[string]struct{}   // fingerprint lookup
	bannedPK  map[string]*time.Time // fingerprint lookup
	started   time.Time
	sync.Mutex
}

// NewServer constructs a new server
func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:      make(chan struct{}),
		clients:   Clients{},
		count:     0,
		history:   NewHistory(historyLength),
		motd:      "",
		whitelist: map[string]struct{}{},
		admins:    map[string]struct{}{},
		bannedPK:  map[string]*time.Time{},
		started:   time.Now(),
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			if !server.IsWhitelisted(fingerprint) {
				return nil, fmt.Errorf("Not Whitelisted.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
		KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
			return nil, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

// Len returns the number of clients
func (s *Server) Len() int {
	return len(s.clients)
}

// SysMsg broadcasts the given message to everyone
func (s *Server) SysMsg(msg string, args ...interface{}) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, " * "+fmt.Sprintf(msg, args...)), nil)
}

// Broadcast broadcasts the given message to everyone except for the given client
func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}

		if strings.Contains(msg, client.Name) {
			// Turn message red if client's name is mentioned, and send BEL if they have enabled beeping
			personalMsg := strings.Replace(msg, client.Name, highlightFormat+client.Name+Reset, -1)
			if client.beepMe {
				personalMsg += beep
			}
			client.Send(personalMsg)
		} else {
			client.Send(msg)
		}
	}
}

// Privmsg sends a message to a particular nick, if it exists
func (s *Server) Privmsg(nick, message string, sender *Client) error {
	// Get the recipient
	target, ok := s.clients[strings.ToLower(nick)]
	if !ok {
		return fmt.Errorf("no client with that nick")
	}
	// Send the message
	target.Msg <- fmt.Sprintf(beep+"[PM from %v] %s%v%s", sender.ColoredName(), privateMessageFormat, message, Reset)
	logger.Debugf("PM from %v to %v: %v", sender.Name, nick, message)
	return nil
}

// SetMotd sets the Message of the Day (MOTD)
func (s *Server) SetMotd(motd string) {
	s.Lock()
	s.motd = motd
	s.Unlock()
}

// MotdUnicast sends the MOTD as a SysMsg
func (s *Server) MotdUnicast(client *Client) {
	if s.motd == "" {
		return
	}
	client.SysMsg(s.motd)
}

// MotdBroadcast broadcasts the MOTD
func (s *Server) MotdBroadcast(client *Client) {
	if s.motd == "" {
		return
	}
	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * New MOTD set by %s.", client.ColoredName())), client)
	s.Broadcast(s.motd, client)
}

// Add adds the client to the list of clients
func (s *Server) Add(client *Client) {
	go func() {
		s.MotdUnicast(client)
		client.SendLines(s.history.Get(10))
	}()

	s.Lock()
	s.count++

	newName, err := s.proposeName(client.Name)
	if err != nil {
		client.SysMsg("Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.Name, ColorString(client.Color, newName))
	}

	client.Rename(newName)
	s.clients[strings.ToLower(client.Name)] = client
	num := len(s.clients)
	s.Unlock()

	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * %s joined. (Total connected: %d)", client.Name, num)), client)
}

// Remove removes the given client from the list of clients
func (s *Server) Remove(client *Client) {
	s.Lock()
	delete(s.clients, strings.ToLower(client.Name))
	s.Unlock()

	s.SysMsg("%s left.", client.Name)
}

func (s *Server) proposeName(name string) (string, error) {
	// Assumes caller holds lock.
	var err error
	name = reStripText.ReplaceAllString(name, "")

	if len(name) > maxNameLength {
		name = name[:maxNameLength]
	} else if len(name) == 0 {
		name = fmt.Sprintf("Guest%d", s.count)
	}

	_, collision := s.clients[strings.ToLower(name)]
	if collision {
		err = fmt.Errorf("%s is not available", name)
		name = fmt.Sprintf("Guest%d", s.count)
	}

	return name, err
}

// Rename renames the given client (user)
func (s *Server) Rename(client *Client, newName string) {
	s.Lock()
	var oldName string
	if strings.ToLower(newName) == strings.ToLower(client.Name) {
		oldName = client.Name
		client.Rename(newName)
	} else {
		newName, err := s.proposeName(newName)
		if err != nil {
			client.SysMsg("%s", err)
			s.Unlock()
			return
		}

		// TODO: Use a channel/goroutine for adding clients, rather than locks?
		delete(s.clients, strings.ToLower(client.Name))
		oldName = client.Name
		client.Rename(newName)
		s.clients[strings.ToLower(client.Name)] = client
		s.Unlock()
	}
	s.SysMsg("%s is now known as %s.", ColorString(client.Color, oldName), ColorString(client.Color, newName))
}

// List lists the clients with the given prefix
func (s *Server) List(prefix *string) []string {
	r := []string{}

	for name := range s.clients {
		if prefix != nil && !strings.HasPrefix(name, *prefix) {
			continue
		}
		r = append(r, name)
	}

	return r
}

// Who returns the client with a given name
func (s *Server) Who(name string) *Client {
	return s.clients[strings.ToLower(name)]
}

// Op adds the given fingerprint to the list of admins
func (s *Server) Op(fingerprint string) {
	logger.Infof("Adding admin: %s", fingerprint)
	s.Lock()
	s.admins[fingerprint] = struct{}{}
	s.Unlock()
}

// Whitelist adds the given fingerprint to the whitelist
func (s *Server) Whitelist(fingerprint string) error {
	if strings.HasPrefix(fingerprint, "github.com/") {
		logger.Infof("Adding github account %s to whitelist", fingerprint)

		keys, err := getGithubPubKeys(fingerprint)
		if err != nil {
			return err
		}
		if len(keys) == 0 {
			return fmt.Errorf("No github user %s", fingerprint)
		}
		for _, key := range keys {
			fingerprint = Fingerprint(key)
			logger.Infof("Adding whitelist: %s", fingerprint)
			s.Lock()
			s.whitelist[fingerprint] = struct{}{}
			s.Unlock()
		}
	} else {
		logger.Infof("Adding whitelist: %s", fingerprint)
		s.Lock()
		s.whitelist[fingerprint] = struct{}{}
		s.Unlock()
	}
	return nil
}

// Client for getting github pub keys
var timeout = time.Duration(10 * time.Second)
var client = http.Client{
    Timeout: timeout,
}
// Returns an array of public keys for the given github user URL
func getGithubPubKeys(url string) ([]ssh.PublicKey, error) {
	resp, err := client.Get("http://" + url + ".keys")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

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

	bodyStr := string(body)
	pubs := []ssh.PublicKey{}
	for _, key := range strings.SplitN(bodyStr, "\n", -1) {
		splitKey := strings.SplitN(key, " ", -1)

		// In case of malformated key
		if len(splitKey) < 2 {
			continue
		}

		bodyDecoded, err := base64.StdEncoding.DecodeString(splitKey[1])
		if err != nil {
			return nil, err
		}

		pub, err := ssh.ParsePublicKey(bodyDecoded)
		if err != nil {
			return nil, err
		}

		pubs = append(pubs, pub)
	}
	return pubs, nil
}

// Uptime returns the time since the server was started
func (s *Server) Uptime() string {
	return time.Now().Sub(s.started).String()
}

// IsOp checks if the given client is Op
func (s *Server) IsOp(client *Client) bool {
	_, r := s.admins[client.Fingerprint()]
	return r
}

// IsWhitelisted checks if the given fingerprint is whitelisted
func (s *Server) IsWhitelisted(fingerprint string) bool {
	/* if no whitelist, anyone is welcome */
	if len(s.whitelist) == 0 {
		return true
	}

	/* otherwise, check for whitelist presence */
	_, r := s.whitelist[fingerprint]
	return r
}

// IsBanned checks if the given fingerprint is banned
func (s *Server) IsBanned(fingerprint string) bool {
	ban, hasBan := s.bannedPK[fingerprint]
	if !hasBan {
		return false
	}
	if ban == nil {
		return true
	}
	if ban.Before(time.Now()) {
		s.Unban(fingerprint)
		return false
	}
	return true
}

// Ban bans a fingerprint for the given duration
func (s *Server) Ban(fingerprint string, duration *time.Duration) {
	var until *time.Time
	s.Lock()
	if duration != nil {
		when := time.Now().Add(*duration)
		until = &when
	}
	s.bannedPK[fingerprint] = until
	s.Unlock()
}

// Unban unbans a banned fingerprint
func (s *Server) Unban(fingerprint string) {
	s.Lock()
	delete(s.bannedPK, fingerprint)
	s.Unlock()
}

// Start starts the server
func (s *Server) Start(laddr string) error {
	// Once a ServerConfig has been configured, connections can be
	// accepted.
	socket, err := net.Listen("tcp", laddr)
	if err != nil {
		return err
	}

	logger.Infof("Listening on %s", laddr)

	go func() {
		defer socket.Close()
		for {
			conn, err := socket.Accept()

			if err != nil {
				logger.Errorf("Failed to accept connection: %v", err)
				if err == syscall.EINVAL {
					// TODO: Handle shutdown more gracefully?
					return
				}
			}

			// Goroutineify to resume accepting sockets early.
			go func() {
				// From a standard TCP connection to an encrypted SSH connection
				sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
				if err != nil {
					logger.Errorf("Failed to handshake: %v", err)
					return
				}

				version := reStripText.ReplaceAllString(string(sshConn.ClientVersion()), "")
				if len(version) > 100 {
					version = "Evil Jerk with a superlong string"
				}
				logger.Infof("Connection #%d from: %s, %s, %s", s.count+1, sshConn.RemoteAddr(), sshConn.User(), version)

				go ssh.DiscardRequests(requests)

				client := NewClient(s, sshConn)
				go client.handleChannels(channels)
			}()
		}
	}()

	go func() {
		<-s.done
		socket.Close()
	}()

	return nil
}

// AutoCompleteFunction handles auto completion of nicks
func (s *Server) AutoCompleteFunction(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
	if key == 9 {
		shortLine := strings.Split(line[:pos], " ")
		partialNick := shortLine[len(shortLine)-1]

		nicks := s.List(&partialNick)
		if len(nicks) > 0 {
			nick := nicks[len(nicks)-1]
			posPartialNick := pos - len(partialNick)
			if len(shortLine) < 2 {
				nick += ": "
			} else {
				nick += " "
			}
			newLine = strings.Replace(line[posPartialNick:],
				partialNick, nick, 1)
			newLine = line[:posPartialNick] + newLine
			newPos = pos + (len(nick) - len(partialNick))
			ok = true
		}
	} else {
		ok = false
	}
	return
}

// Stop stops the server
func (s *Server) Stop() {
	for _, client := range s.clients {
		client.Conn.Close()
	}

	close(s.done)
}

// Fingerprint returns the fingerprint based on a public key
func Fingerprint(k ssh.PublicKey) string {
	hash := md5.Sum(k.Marshal())
	r := fmt.Sprintf("% x", hash)
	return strings.Replace(r, " ", ":", -1)
}
<|endoftext|>