<commit_before>package web

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"regexp/syntax"
	"strings"
)

type regexpPattern struct {
	re     *regexp.Regexp
	prefix string
	names  []string
}

func (p regexpPattern) Prefix() string {
	return p.prefix
}
func (p regexpPattern) Match(r *http.Request, c *C) bool {
	return p.match(r, c, false)
}
func (p regexpPattern) Run(r *http.Request, c *C) {
	p.match(r, c, false)
}

func (p regexpPattern) match(r *http.Request, c *C, dryrun bool) bool {
	matches := p.re.FindStringSubmatch(r.URL.Path)
	if matches == nil || len(matches) == 0 {
		return false
	}

	if c == nil || dryrun || len(matches) == 1 {
		return true
	}

	if c.URLParams == nil {
		c.URLParams = make(map[string]string, len(matches)-1)
	}
	for i := 1; i < len(matches); i++ {
		c.URLParams[p.names[i]] = matches[i]
	}
	return true
}

func (p regexpPattern) String() string {
	return fmt.Sprintf("regexpPattern(%v)", p.re)
}

/*
I'm sorry, dear reader. I really am.

The problem here is to take an arbitrary regular expression and:
1. return a regular expression that is just like it, but left-anchored,
   preferring to return the original if possible.
2. determine a string literal prefix that all matches of this regular expression
   have, much like regexp.Regexp.Prefix(). Unfortunately, Prefix() does not work
   in the presence of anchors, so we need to write it ourselves.

What this actually means is that we need to sketch on the internals of the
standard regexp library to forcefully extract the information we want.

Unfortunately, regexp.Regexp hides a lot of its state, so our abstraction is
going to be pretty leaky. The biggest leak is that we blindly assume that all
regular expressions are perl-style, not POSIX. This is probably Mostly True, and
I think most users of the library probably won't be able to notice.
*/
func sketchOnRegex(re *regexp.Regexp) (*regexp.Regexp, string) {
	rawRe := re.String()
	sRe, err := syntax.Parse(rawRe, syntax.Perl)
	if err != nil {
		log.Printf("WARN(web): unable to parse regexp %v as perl. "+
			"This route might behave unexpectedly.", re)
		return re, ""
	}
	sRe = sRe.Simplify()
	p, err := syntax.Compile(sRe)
	if err != nil {
		log.Printf("WARN(web): unable to compile regexp %v. This "+
			"route might behave unexpectedly.", re)
		return re, ""
	}
	if p.StartCond()&syntax.EmptyBeginText == 0 {
		// I hope doing this is always legal...
		newRe, err := regexp.Compile(`\A` + rawRe)
		if err != nil {
			log.Printf("WARN(web): unable to create a left-"+
				"anchored regexp from %v. This route might "+
				"behave unexpectedly", re)
			return re, ""
		}
		re = newRe
	}

	// Run the regular expression more or less by hand :(
	pc := uint32(p.Start)
	atStart := true
	i := &p.Inst[pc]
	var buf bytes.Buffer
Sadness:
	for {
		switch i.Op {
		case syntax.InstEmptyWidth:
			if !atStart {
				break Sadness
			}
		case syntax.InstCapture, syntax.InstNop:
			// nop!
		case syntax.InstRune, syntax.InstRune1, syntax.InstRuneAny,
			syntax.InstRuneAnyNotNL:

			atStart = false
			if len(i.Rune) != 1 ||
				syntax.Flags(i.Arg)&syntax.FoldCase != 0 {
				break Sadness
			}
			buf.WriteRune(i.Rune[0])
		default:
			break Sadness
		}
		pc = i.Out
		i = &p.Inst[pc]
	}
	return re, buf.String()
}

func parseRegexpPattern(re *regexp.Regexp) regexpPattern {
	re, prefix := sketchOnRegex(re)
	rnames := re.SubexpNames()
	// We have to make our own copy since package regexp forbids us
	// from scribbling over the slice returned by SubexpNames().
	names := make([]string, len(rnames))
	for i, rname := range rnames {
		if rname == "" {
			rname = fmt.Sprintf("$%d", i)
		}
		names[i] = rname
	}
	return regexpPattern{
		re:     re,
		prefix: prefix,
		names:  names,
	}
}

type stringPattern struct {
	raw      string
	pats     []string
	literals []string
	isPrefix bool
}

func (s stringPattern) Prefix() string {
	return s.literals[0]
}
func (s stringPattern) Match(r *http.Request, c *C) bool {
	return s.match(r, c, true)
}
func (s stringPattern) Run(r *http.Request, c *C) {
	s.match(r, c, false)
}
func (s stringPattern) match(r *http.Request, c *C, dryrun bool) bool {
	path := r.URL.Path
	var matches map[string]string
	if !dryrun && len(s.pats) > 0 {
		matches = make(map[string]string, len(s.pats))
	}
	for i := 0; i < len(s.pats); i++ {
		sli := s.literals[i]
		if !strings.HasPrefix(path, sli) {
			return false
		}
		path = path[len(sli):]

		m := strings.IndexRune(path, '/')
		if m == -1 {
			m = len(path)
		}
		if m == 0 {
			// Empty strings are not matches, otherwise routes like
			// "/:foo" would match the path "/"
			return false
		}
		if !dryrun {
			matches[s.pats[i]] = path[:m]
		}
		path = path[m:]
	}
	// There's exactly one more literal than pat.
	if s.isPrefix {
		if !strings.HasPrefix(path, s.literals[len(s.pats)]) {
			return false
		}
	} else {
		if path != s.literals[len(s.pats)] {
			return false
		}
	}

	if c == nil || dryrun {
		return true
	}

	if c.URLParams == nil {
		c.URLParams = matches
	} else {
		for k, v := range matches {
			c.URLParams[k] = v
		}
	}
	return true
}

func (s stringPattern) String() string {
	return fmt.Sprintf("stringPattern(%q, %v)", s.raw, s.isPrefix)
}

var patternRe = regexp.MustCompile(`/:([^/]+)`)

func parseStringPattern(s string) stringPattern {
	var isPrefix bool
	// Routes that end in an asterisk ("*") are prefix routes
	if len(s) > 0 && s[len(s)-1] == '*' {
		s = s[:len(s)-1]
		isPrefix = true
	}

	matches := patternRe.FindAllStringSubmatchIndex(s, -1)
	pats := make([]string, len(matches))
	literals := make([]string, len(matches)+1)
	n := 0
	for i, match := range matches {
		a, b := match[2], match[3]
		literals[i] = s[n : a-1] // Need to leave off the colon
		pats[i] = s[a:b]
		n = b
	}
	literals[len(matches)] = s[n:]
	return stringPattern{
		raw:      s,
		pats:     pats,
		literals: literals,
		isPrefix: isPrefix,
	}
}
<commit_msg>Inline strings.IndexRune call<commit_after>package web

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"regexp/syntax"
	"strings"
)

type regexpPattern struct {
	re     *regexp.Regexp
	prefix string
	names  []string
}

func (p regexpPattern) Prefix() string {
	return p.prefix
}
func (p regexpPattern) Match(r *http.Request, c *C) bool {
	return p.match(r, c, false)
}
func (p regexpPattern) Run(r *http.Request, c *C) {
	p.match(r, c, false)
}

func (p regexpPattern) match(r *http.Request, c *C, dryrun bool) bool {
	matches := p.re.FindStringSubmatch(r.URL.Path)
	if matches == nil || len(matches) == 0 {
		return false
	}

	if c == nil || dryrun || len(matches) == 1 {
		return true
	}

	if c.URLParams == nil {
		c.URLParams = make(map[string]string, len(matches)-1)
	}
	for i := 1; i < len(matches); i++ {
		c.URLParams[p.names[i]] = matches[i]
	}
	return true
}

func (p regexpPattern) String() string {
	return fmt.Sprintf("regexpPattern(%v)", p.re)
}

/*
I'm sorry, dear reader. I really am.

The problem here is to take an arbitrary regular expression and:
1. return a regular expression that is just like it, but left-anchored,
   preferring to return the original if possible.
2. determine a string literal prefix that all matches of this regular expression
   have, much like regexp.Regexp.Prefix(). Unfortunately, Prefix() does not work
   in the presence of anchors, so we need to write it ourselves.

What this actually means is that we need to sketch on the internals of the
standard regexp library to forcefully extract the information we want.

Unfortunately, regexp.Regexp hides a lot of its state, so our abstraction is
going to be pretty leaky. The biggest leak is that we blindly assume that all
regular expressions are perl-style, not POSIX. This is probably Mostly True, and
I think most users of the library probably won't be able to notice.
*/
func sketchOnRegex(re *regexp.Regexp) (*regexp.Regexp, string) {
	rawRe := re.String()
	sRe, err := syntax.Parse(rawRe, syntax.Perl)
	if err != nil {
		log.Printf("WARN(web): unable to parse regexp %v as perl. "+
			"This route might behave unexpectedly.", re)
		return re, ""
	}
	sRe = sRe.Simplify()
	p, err := syntax.Compile(sRe)
	if err != nil {
		log.Printf("WARN(web): unable to compile regexp %v. This "+
			"route might behave unexpectedly.", re)
		return re, ""
	}
	if p.StartCond()&syntax.EmptyBeginText == 0 {
		// I hope doing this is always legal...
		newRe, err := regexp.Compile(`\A` + rawRe)
		if err != nil {
			log.Printf("WARN(web): unable to create a left-"+
				"anchored regexp from %v. This route might "+
				"behave unexpectedly", re)
			return re, ""
		}
		re = newRe
	}

	// Run the regular expression more or less by hand :(
	pc := uint32(p.Start)
	atStart := true
	i := &p.Inst[pc]
	var buf bytes.Buffer
Sadness:
	for {
		switch i.Op {
		case syntax.InstEmptyWidth:
			if !atStart {
				break Sadness
			}
		case syntax.InstCapture, syntax.InstNop:
			// nop!
		case syntax.InstRune, syntax.InstRune1, syntax.InstRuneAny,
			syntax.InstRuneAnyNotNL:

			atStart = false
			if len(i.Rune) != 1 ||
				syntax.Flags(i.Arg)&syntax.FoldCase != 0 {
				break Sadness
			}
			buf.WriteRune(i.Rune[0])
		default:
			break Sadness
		}
		pc = i.Out
		i = &p.Inst[pc]
	}
	return re, buf.String()
}

func parseRegexpPattern(re *regexp.Regexp) regexpPattern {
	re, prefix := sketchOnRegex(re)
	rnames := re.SubexpNames()
	// We have to make our own copy since package regexp forbids us
	// from scribbling over the slice returned by SubexpNames().
	names := make([]string, len(rnames))
	for i, rname := range rnames {
		if rname == "" {
			rname = fmt.Sprintf("$%d", i)
		}
		names[i] = rname
	}
	return regexpPattern{
		re:     re,
		prefix: prefix,
		names:  names,
	}
}

type stringPattern struct {
	raw      string
	pats     []string
	literals []string
	isPrefix bool
}

func (s stringPattern) Prefix() string {
	return s.literals[0]
}
func (s stringPattern) Match(r *http.Request, c *C) bool {
	return s.match(r, c, true)
}
func (s stringPattern) Run(r *http.Request, c *C) {
	s.match(r, c, false)
}
func (s stringPattern) match(r *http.Request, c *C, dryrun bool) bool {
	path := r.URL.Path
	var matches map[string]string
	if !dryrun && len(s.pats) > 0 {
		matches = make(map[string]string, len(s.pats))
	}
	for i := 0; i < len(s.pats); i++ {
		sli := s.literals[i]
		if !strings.HasPrefix(path, sli) {
			return false
		}
		path = path[len(sli):]

		m := 0
		for ; m < len(path); m++ {
			if path[m] == '/' {
				break
			}
		}
		if m == 0 {
			// Empty strings are not matches, otherwise routes like
			// "/:foo" would match the path "/"
			return false
		}
		if !dryrun {
			matches[s.pats[i]] = path[:m]
		}
		path = path[m:]
	}
	// There's exactly one more literal than pat.
	if s.isPrefix {
		if !strings.HasPrefix(path, s.literals[len(s.pats)]) {
			return false
		}
	} else {
		if path != s.literals[len(s.pats)] {
			return false
		}
	}

	if c == nil || dryrun {
		return true
	}

	if c.URLParams == nil {
		c.URLParams = matches
	} else {
		for k, v := range matches {
			c.URLParams[k] = v
		}
	}
	return true
}

func (s stringPattern) String() string {
	return fmt.Sprintf("stringPattern(%q, %v)", s.raw, s.isPrefix)
}

var patternRe = regexp.MustCompile(`/:([^/]+)`)

func parseStringPattern(s string) stringPattern {
	var isPrefix bool
	// Routes that end in an asterisk ("*") are prefix routes
	if len(s) > 0 && s[len(s)-1] == '*' {
		s = s[:len(s)-1]
		isPrefix = true
	}

	matches := patternRe.FindAllStringSubmatchIndex(s, -1)
	pats := make([]string, len(matches))
	literals := make([]string, len(matches)+1)
	n := 0
	for i, match := range matches {
		a, b := match[2], match[3]
		literals[i] = s[n : a-1] // Need to leave off the colon
		pats[i] = s[a:b]
		n = b
	}
	literals[len(matches)] = s[n:]
	return stringPattern{
		raw:      s,
		pats:     pats,
		literals: literals,
		isPrefix: isPrefix,
	}
}
<|endoftext|>


<commit_before>// Package public adds some public routes that can be used to give information
// to anonymous users, or to the not yet authentified cozy owner on its login
// page.
package public

import (
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/statik/fs"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/statik"
	"github.com/cozy/echo"
)

// Avatar returns the default avatar currently.
func Avatar(c echo.Context) error {
	f, ok := fs.Get("/images/default-avatar.png", "")
	if !ok {
		return echo.NewHTTPError(http.StatusNotFound, "Page not found")
	}
	handler := statik.NewHandler()
	handler.ServeFile(c.Response(), c.Request(), f, true)
	return nil
}

// Routes sets the routing for the public service
func Routes(router *echo.Group) {
	cacheControl := middlewares.CacheControl(middlewares.CacheOptions{
		MaxAge: 24 * time.Hour,
	})
	router.GET("/avatar", Avatar, cacheControl)
}
<commit_msg>Allow to customize the default avatar by context<commit_after>// Package public adds some public routes that can be used to give information
// to anonymous users, or to the not yet authentified cozy owner on its login
// page.
package public

import (
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/statik/fs"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/statik"
	"github.com/cozy/echo"
)

// Avatar returns the default avatar currently.
func Avatar(c echo.Context) error {
	inst := middlewares.GetInstance(c)
	f, ok := fs.Get("/images/default-avatar.png", inst.ContextName)
	if !ok {
		f, ok = fs.Get("/images/default-avatar.png", "")
		if !ok {
			return echo.NewHTTPError(http.StatusNotFound, "Page not found")
		}
	}
	handler := statik.NewHandler()
	handler.ServeFile(c.Response(), c.Request(), f, true)
	return nil
}

// Routes sets the routing for the public service
func Routes(router *echo.Group) {
	cacheControl := middlewares.CacheControl(middlewares.CacheOptions{
		MaxAge: 24 * time.Hour,
	})
	router.GET("/avatar", Avatar, cacheControl, middlewares.NeedInstance)
}
<|endoftext|>


<commit_before>package lib

import (
	"compress/gzip"
	"io"
	"os"
	"strings"
)

func Xopen(filename string) (io.ReadCloser, error) {
	if filename == "-" {
		return os.Stdin, nil
	}

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

	gfile, gerr := gzip.NewReader(file)
	if gerr == nil {
		return gfile, nil
	}

	_, err = file.Seek(0, 0)
	if err != nil {
		return nil, err
	}

	return file, nil
}

func Xcreate(args ...string) (io.WriteCloser, error) {
	filename := "-"
	mode := "w"
	switch l := len(args); {
	case l > 1:
		mode = args[1]
		fallthrough
	case l > 0:
		filename = args[0]
	}

	if filename == "-" {
		return os.Stdout, nil
	} else if filename == "@" {
		return os.Stderr, nil
	}

	flag := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
	if mode == "a" {
		flag = os.O_APPEND | os.O_CREATE | os.O_TRUNC
	}

	file, err := os.OpenFile(filename, flag, 0644)
	if err != nil {
		return nil, err
	}

	if strings.HasSuffix(filename, ".gz") {
		gfile := gzip.NewWriter(file)
		return gfile, nil
	}
	return file, nil
}
<commit_msg>add xopen.go<commit_after>// auto read from stdin, gzip, bzip, raw or url
// auto write data to stdout, stderr, gzip or raw

package xopen

import (
	"compress/bzip2"
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

// BzipReadCloser add a Close function for bzip2
type BzipReadCloser struct {
	r    io.Reader
	file *os.File
}

// Read Using bzip2 Reader's Read method
func (bz *BzipReadCloser) Read(buf []byte) (int, error) {
	return bz.r.Read(buf)
}

// Close Close BzipReadCloser
func (bz *BzipReadCloser) Close() error {
	return bz.file.Close()
}

// Xopen read from stdin, raw, gzip, bzip2 or url
func Xopen(filename string) (io.ReadCloser, error) {
	if filename == "-" { // check input is stdin or not
		return os.Stdin, nil
	}

	// check input from an url string or not
	if strings.HasPrefix(filename, "http://") || strings.HasPrefix(filename, "https://") {
		r, err := http.Get(filename)
		if err != nil {
			return nil, err
		}
		if r.StatusCode != http.StatusOK {
			return nil, fmt.Errorf("http error while loading %v. status: %v", filename, r.Status)
		}
		return r.Body, nil
	}

	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	if strings.HasSuffix(filename, ".bz2") || strings.HasSuffix(filename, ".bz") {
		return &BzipReadCloser{r: bzip2.NewReader(file), file: file}, nil
	}

	if gfile, err := gzip.NewReader(file); err == nil {
		return gfile, nil
	}

	if _, err = file.Seek(0, 0); err != nil {
		return nil, err
	}
	return file, nil
}

// Xcreate write data to stdout, stderr, file or gzip file
func Xcreate(args ...string) (io.WriteCloser, error) {
	filename := "-"
	mode := "w"
	switch l := len(args); {
	case l > 1:
		mode = args[1]
		fallthrough
	case l > 0:
		filename = args[0]
	}

	if filename == "-" {
		return os.Stdout, nil
	} else if filename == "@" {
		return os.Stderr, nil
	}

	flag := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
	if mode == "a" {
		flag = os.O_APPEND | os.O_CREATE | os.O_TRUNC
	}

	file, err := os.OpenFile(filename, flag, 0644)
	if err != nil {
		return nil, err
	}

	if strings.HasSuffix(filename, ".gz") {
		gfile := gzip.NewWriter(file)
		return gfile, nil
	}
	return file, nil
}
<|endoftext|>


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

package update

import (
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/web/orchestrator"
	"github.com/andreaskoch/allmark2/web/view/viewmodel"
)

func NewHub(logger logger.Logger, updateOrchestrator orchestrator.UpdateOrchestrator) *Hub {
	hub := &Hub{
		logger: logger,

		updateOrchestrator: updateOrchestrator,

		broadcast:   make(chan Message, 1),
		subscribe:   make(chan *connection, 1),
		unsubscribe: make(chan *connection, 1),
		connections: make(map[*connection]bool),
	}

	// start the hub
	go hub.run()

	return hub
}

type Hub struct {
	logger logger.Logger

	updateOrchestrator orchestrator.UpdateOrchestrator

	// Registered connections.
	connections map[*connection]bool

	// Inbound messages from the connections.
	broadcast chan Message

	// Register requests from the connections.
	subscribe chan *connection

	// Unsubscribe requests from connections.
	unsubscribe chan *connection
}

func (hub *Hub) Message(viewModel viewmodel.Model) {
	go func() {
		hub.logger.Debug("Broadcasting meesage %#v", viewModel)
		hub.broadcast <- NewMessage(viewModel)
	}()
}

func (hub *Hub) Subscribe(connection *connection) {
	hub.logger.Debug("Subscribing connection: %s", connection.String())

	// start watching for changes if there are no connections for this route
	if noConnectionsForRoute := len(hub.connectionsByRoute(connection.Route.Value())) == 0; noConnectionsForRoute {
		hub.updateOrchestrator.StartWatching(connection.Route)
	}

	hub.subscribe <- connection
}

func (hub *Hub) Unsubscribe(connection *connection) {

	// stop watching for changes if there are no more connections for this route
	if noConnectionsForRoute := len(hub.connectionsByRoute(connection.Route.Value())) <= 1; noConnectionsForRoute {
		hub.updateOrchestrator.StopWatching(connection.Route)
	}

	hub.unsubscribe <- connection
}

func (hub *Hub) connectionsByRoute(routeValue string) []*connection {
	connectionsByRoute := make([]*connection, 0)

	for c := range hub.connections {
		if routeValue == c.Route.Value() {
			connectionsByRoute = append(connectionsByRoute, c)
		}
	}

	return connectionsByRoute
}

func (hub *Hub) run() {
	for {
		select {
		case c := <-hub.subscribe:
			{
				hub.logger.Debug("Subscring connection %s", c.String())
				hub.logger.Debug("Number of Connections - Before: %v", len(hub.connections))
				hub.connections[c] = true
				hub.logger.Debug("Number of Connections - After: %v", len(hub.connections))
			}
		case c := <-hub.unsubscribe:
			{
				hub.logger.Debug("Unsubscribing connection %s", c.String())
				hub.logger.Debug("Number of Connections - Before: %v", len(hub.connections))
				delete(hub.connections, c)
				hub.logger.Debug("Number of Connections - After: %v", len(hub.connections))
			}
		case m := <-hub.broadcast:
			{
				hub.logger.Debug("Revieved a broadcast message %#v", m)

				affectedConnections := hub.connectionsByRoute(m.Route)
				for _, c := range affectedConnections {

					select {
					case c.send <- m:
						{
							hub.logger.Debug("Sending an update to: %s", c.String())
						}
					default:
						{
							// todo: find out when this is happening
							hub.logger.Debug("Revieved a non-send message for %s", c.String())
							delete(hub.connections, c)
							go c.ws.Close()
							hub.logger.Debug("Number of Connections: %v", len(hub.connections))
						}
					}

				}
			}
		}
	}
}
<commit_msg>Connection hub debug code improvements<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package update

import (
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/web/orchestrator"
	"github.com/andreaskoch/allmark2/web/view/viewmodel"
)

func NewHub(logger logger.Logger, updateOrchestrator orchestrator.UpdateOrchestrator) *Hub {
	hub := &Hub{
		logger: logger,

		updateOrchestrator: updateOrchestrator,

		broadcast:   make(chan Message, 1),
		subscribe:   make(chan *connection, 1),
		unsubscribe: make(chan *connection, 1),
		connections: make(map[*connection]bool),
	}

	// start the hub
	go hub.run()

	return hub
}

type Hub struct {
	logger logger.Logger

	updateOrchestrator orchestrator.UpdateOrchestrator

	// Registered connections.
	connections map[*connection]bool

	// Inbound messages from the connections.
	broadcast chan Message

	// Register requests from the connections.
	subscribe chan *connection

	// Unsubscribe requests from connections.
	unsubscribe chan *connection
}

func (hub *Hub) Message(viewModel viewmodel.Model) {
	go func() {
		hub.logger.Debug("Broadcasting meesage %#v", viewModel)
		hub.broadcast <- NewMessage(viewModel)
	}()
}

func (hub *Hub) Subscribe(connection *connection) {
	hub.logger.Debug("Subscribing connection: %s", connection.String())

	// start watching for changes if there are no connections for this route
	if noConnectionsForRoute := len(hub.connectionsByRoute(connection.Route.Value())) == 0; noConnectionsForRoute {
		hub.updateOrchestrator.StartWatching(connection.Route)
	}

	hub.subscribe <- connection
}

func (hub *Hub) Unsubscribe(connection *connection) {

	// stop watching for changes if there are no more connections for this route
	if noConnectionsForRoute := len(hub.connectionsByRoute(connection.Route.Value())) <= 1; noConnectionsForRoute {
		hub.updateOrchestrator.StopWatching(connection.Route)
	}

	hub.unsubscribe <- connection
}

func (hub *Hub) connectionsByRoute(routeValue string) []*connection {
	connectionsByRoute := make([]*connection, 0)

	hub.logger.Debug("----- CONNNNECTIONNNNNNNNNNNNNNS -----------")
	hub.logger.Debug("\n%#v\n", hub.connections)

	for connection := range hub.connections {

		hub.logger.Debug("\n%#v\n", connection)

		if routeValue == connection.Route.Value() {
			connectionsByRoute = append(connectionsByRoute, connection)
		}
	}

	return connectionsByRoute
}

func (hub *Hub) run() {
	for {
		select {

		// subscribe a new connection
		case connection := <-hub.subscribe:
			{
				hub.logger.Debug("Subscring connection %s", connection.String())
				hub.logger.Debug("Number of Connections - Before: %v", len(hub.connections))

				// register the connection
				hub.connections[connection] = true

				hub.logger.Debug("Number of Connections - After: %v", len(hub.connections))
			}

		// unsubscribe an existing connection
		case connection := <-hub.unsubscribe:
			{
				hub.logger.Debug("Unsubscribing connection %s", connection.String())
				hub.logger.Debug("Number of Connections - Before: %v", len(hub.connections))

				// remove the connection
				delete(hub.connections, connection)

				hub.logger.Debug("Number of Connections - After: %v", len(hub.connections))
			}

		// handle broadcasts
		case broadcastMsg := <-hub.broadcast:
			{
				affectedConnections := hub.connectionsByRoute(broadcastMsg.Route)

				hub.logger.Debug("Revieved a broadcast message\n%#v", broadcastMsg)
				hub.logger.Debug("Connections affected: %v", len(affectedConnections))

				for _, connection := range affectedConnections {

					select {

					// send the message to the websocket
					case connection.send <- broadcastMsg:
						{
							hub.logger.Debug("Sending an update to: %s", connection.String())
						}

					default:
						{
							// todo: find out when this is happening
							hub.logger.Debug("Revieved a non-send message for %s", connection.String())
							delete(hub.connections, connection)
							go connection.ws.Close()
							hub.logger.Debug("Number of Connections: %v", len(hub.connections))
						}
					}

				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package algoliasearch

func checkQuery(query Map, ignore ...string) error {
Outer:
	for k, v := range query {
		// Continue if `k` is to be ignored.
		for _, s := range ignore {
			if s == k {
				continue Outer
			}
		}

		switch k {
		case "aroundLatLng",
			"exactOnSingleWordQuery",
			"filters",
			"highlightPostTag",
			"highlightPreTag",
			"query",
			"queryType",
			"removeWordsIfNoResults",
			"restrictSources",
			"snippetEllipsisText",
			"sortFacetValuesBy":
			if _, ok := v.(string); !ok {
				return invalidType(k, "string")
			}

		case "alternativesAsExact",
			"attributesToHighlight",
			"attributesToRetrieve",
			"attributesToSnippet",
			"disableExactOnAttributes",
			"disableTypoToleranceOnAttributes",
			"explain",
			"queryLanguages",
			"responseFields":
			if _, ok := v.([]string); !ok {
				return invalidType(k, "[]string")
			}

		case "minWordSizefor1Typo",
			"minWordSizefor2Typos",
			"minProximity",
			"page",
			"hitsPerPage",
			"distinct",
			"maxValuesPerFacet",
			"aroundPrecision",
			"minimumAroundRadius",
			"maxFacetHits",
			"offset",
			"length":
			if _, ok := v.(int); !ok {
				return invalidType(k, "int")
			}

		case "allowTyposOnNumericTokens",
			"advancedSyntax",
			"analytics",
			"clickAnalytics",
			"enableRules",
			"synonyms",
			"replaceSynonymsInHighlight",
			"aroundLatLngViaIP",
			"facetingAfterDistinct",
			"restrictHighlightAndSnippetArrays",
			"percentileComputation",
			"sumOrFiltersScores":
			if _, ok := v.(bool); !ok {
				return invalidType(k, "bool")
			}

		case "ignorePlurals",
			"removeStopWords":
			switch v.(type) {
			case []string, bool:
				// OK
			default:
				return invalidType(k, "[]string or bool")
			}

		case "aroundRadius":
			switch v.(type) {
			case int, string:
				// OK
			default:
				return invalidType(k, "int or string")
			}

		case "getRankingInfo":
			switch v.(type) {
			case int, bool:
				// OK
			default:
				return invalidType(k, "int or bool")
			}

		case "numericFilters",
			"tagFilters":
			switch v.(type) {
			case string, []interface{}:
				// OK
			default:
				return invalidType(k, "string or []interface{}")
			}

		case "facetFilters":
			switch v.(type) {
			case string, []string, [][]string, []interface{}:
				//OK
			default:
				return invalidType(k, "string, []string, [][]string or []interface{}")
			}

		case "analyticsTags",
			"restrictSearchableAttributes",
			"facets",
			"optionalWords":
			switch v.(type) {
			case string, []string:
				// OK
			default:
				return invalidType(k, "string or []string")
			}

		case "insideBoundingBox",
			"insidePolygon":
			switch v.(type) {
			case string, [][]float64:
				// OK
			default:
				return invalidType(k, "string or [][]float64")
			}

		case "typoTolerance":
			switch v.(type) {
			case string, bool:
				// OK
			default:
				return invalidType(k, "string or bool")
			}

		default:
		}

	}
	return nil
}
<commit_msg>added: Type-check enablePersonalization search parameter (boolean)<commit_after>package algoliasearch

func checkQuery(query Map, ignore ...string) error {
Outer:
	for k, v := range query {
		// Continue if `k` is to be ignored.
		for _, s := range ignore {
			if s == k {
				continue Outer
			}
		}

		switch k {
		case "aroundLatLng",
			"exactOnSingleWordQuery",
			"filters",
			"highlightPostTag",
			"highlightPreTag",
			"query",
			"queryType",
			"removeWordsIfNoResults",
			"restrictSources",
			"snippetEllipsisText",
			"sortFacetValuesBy":
			if _, ok := v.(string); !ok {
				return invalidType(k, "string")
			}

		case "alternativesAsExact",
			"attributesToHighlight",
			"attributesToRetrieve",
			"attributesToSnippet",
			"disableExactOnAttributes",
			"disableTypoToleranceOnAttributes",
			"explain",
			"queryLanguages",
			"responseFields":
			if _, ok := v.([]string); !ok {
				return invalidType(k, "[]string")
			}

		case "minWordSizefor1Typo",
			"minWordSizefor2Typos",
			"minProximity",
			"page",
			"hitsPerPage",
			"distinct",
			"maxValuesPerFacet",
			"aroundPrecision",
			"minimumAroundRadius",
			"maxFacetHits",
			"offset",
			"length":
			if _, ok := v.(int); !ok {
				return invalidType(k, "int")
			}

		case "allowTyposOnNumericTokens",
			"advancedSyntax",
			"analytics",
			"clickAnalytics",
			"enableRules",
			"synonyms",
			"replaceSynonymsInHighlight",
			"aroundLatLngViaIP",
			"facetingAfterDistinct",
			"restrictHighlightAndSnippetArrays",
			"percentileComputation",
			"sumOrFiltersScores",
			"enablePersonalization":
			if _, ok := v.(bool); !ok {
				return invalidType(k, "bool")
			}

		case "ignorePlurals",
			"removeStopWords":
			switch v.(type) {
			case []string, bool:
				// OK
			default:
				return invalidType(k, "[]string or bool")
			}

		case "aroundRadius":
			switch v.(type) {
			case int, string:
				// OK
			default:
				return invalidType(k, "int or string")
			}

		case "getRankingInfo":
			switch v.(type) {
			case int, bool:
				// OK
			default:
				return invalidType(k, "int or bool")
			}

		case "numericFilters",
			"tagFilters":
			switch v.(type) {
			case string, []interface{}:
				// OK
			default:
				return invalidType(k, "string or []interface{}")
			}

		case "facetFilters":
			switch v.(type) {
			case string, []string, [][]string, []interface{}:
				//OK
			default:
				return invalidType(k, "string, []string, [][]string or []interface{}")
			}

		case "analyticsTags",
			"restrictSearchableAttributes",
			"facets",
			"optionalWords":
			switch v.(type) {
			case string, []string:
				// OK
			default:
				return invalidType(k, "string or []string")
			}

		case "insideBoundingBox",
			"insidePolygon":
			switch v.(type) {
			case string, [][]float64:
				// OK
			default:
				return invalidType(k, "string or [][]float64")
			}

		case "typoTolerance":
			switch v.(type) {
			case string, bool:
				// OK
			default:
				return invalidType(k, "string or bool")
			}

		default:
		}

	}
	return nil
}
<|endoftext|>


<commit_before>// Run a web server capable of dumping bug reports sent by Riot.
// Requires Go 1.5+
// Usage:   go run rageshake.go PORT
// Example: go run rageshake.go 8080
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

type LogEntry struct {
	ID    string `json:"id"`
	Lines string `json:"lines"`
}

type Payload struct {
	Text      string     `json:"text"`
	Version   string     `json:"version"`
	UserAgent string     `json:"user_agent"`
	Logs      []LogEntry `json:"logs"`
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		if req.Method != "POST" && req.Method != "OPTIONS" {
			w.WriteHeader(405)
			return
		}
        // Set CORS
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
        if req.Method == "OPTIONS" {
            return;
        }
		var p Payload
		if err := json.NewDecoder(req.Body).Decode(&p); err != nil {
			w.WriteHeader(400)
			w.Write([]byte("Body is not JSON"))
			return
		}
        // Dump bug report to disk
		fmt.Println(p)

	})

	port := os.Args[1]
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<commit_msg>gzip bug reports when storing on disk. Set max payload size<commit_after>// Run a web server capable of dumping bug reports sent by Riot.
// Requires Go 1.5+
// Usage:   go run rageshake.go PORT
// Example: go run rageshake.go 8080
package main

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"
)

var maxPayloadSize = 1024 * 1024 * 55 // 55 MB

type LogEntry struct {
	ID    string `json:"id"`
	Lines string `json:"lines"`
}

type Payload struct {
	Text      string     `json:"text"`
	Version   string     `json:"version"`
	UserAgent string     `json:"user_agent"`
	Logs      []LogEntry `json:"logs"`
}

func respond(code int, w http.ResponseWriter) {
	w.WriteHeader(code)
	w.Write([]byte("{}"))
}

func gzipAndSave(data []byte, filepath string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write(data); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}
	if err := ioutil.WriteFile(filepath, b.Bytes(), 0644); err != nil {
		return err
	}
	return nil
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		if req.Method != "POST" && req.Method != "OPTIONS" {
			respond(405, w)
			return
		}
		// Set CORS
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
		if req.Method == "OPTIONS" {
			respond(200, w)
			return
		}
		if length, err := strconv.Atoi(req.Header.Get("Content-Length")); err != nil || length > maxPayloadSize {
			respond(413, w)
			return
		}
		var p Payload
		if err := json.NewDecoder(req.Body).Decode(&p); err != nil {
			respond(400, w)
			return
		}
		// Dump bug report to disk as form:
		//  "bugreport-20170115-112233.log.gz" => user text, version, user agent, # logs
		//  "bugreport-20170115-112233-0.log.gz" => most recent log
		//  "bugreport-20170115-112233-1.log.gz" => ...
		//  "bugreport-20170115-112233-N.log.gz" => oldest log
		t := time.Now()
		prefix := t.Format("bugreport-20060102-150405")
		if err := gzipAndSave([]byte(p.Text), prefix+".log.gz"); err != nil {
			respond(500, w)
			return
		}
		respond(200, w)
	})

	port := os.Args[1]
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<|endoftext|>


<commit_before>package assert

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"
)

type outputT struct {
	buf *bytes.Buffer
}

// Implements TestingT
func (t *outputT) Errorf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	t.buf.WriteString(s)
}

func (t *outputT) get() string {
	return string(t.buf.Bytes())
}

func TestCompare(t *testing.T) {
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		cType   string
	}{
		{less: "a", greater: "b", cType: "string"},
		{less: int(1), greater: int(2), cType: "int"},
		{less: int8(1), greater: int8(2), cType: "int8"},
		{less: int16(1), greater: int16(2), cType: "int16"},
		{less: int32(1), greater: int32(2), cType: "int32"},
		{less: int64(1), greater: int64(2), cType: "int64"},
		{less: uint8(1), greater: uint8(2), cType: "uint8"},
		{less: uint16(1), greater: uint16(2), cType: "uint16"},
		{less: uint32(1), greater: uint32(2), cType: "uint32"},
		{less: uint64(1), greater: uint64(2), cType: "uint64"},
		{less: float32(1), greater: float32(2), cType: "float32"},
		{less: float64(1), greater: float64(2), cType: "float64"},
	} {
		resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind())
		if !isComparable {
			t.Error("object should be comparable for type " + currCase.cType)
		}

		if resLess != compareLess {
			t.Errorf("object less should be less than greater for type " + currCase.cType)
		}

		resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind())
		if !isComparable {
			t.Error("object are comparable for type " + currCase.cType)
		}

		if resGreater != compareGreater {
			t.Errorf("object greater should be greater than less for type " + currCase.cType)
		}

		resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind())
		if !isComparable {
			t.Error("object are comparable for type " + currCase.cType)
		}

		if resEqual != 0 {
			t.Errorf("objects should be equal for type " + currCase.cType)
		}
	}
}

func TestGreater(t *testing.T) {
	mockT := new(testing.T)

	if !Greater(mockT, 2, 1) {
		t.Error("Greater should return true")
	}

	if Greater(mockT, 1, 1) {
		t.Error("Greater should return false")
	}

	if Greater(mockT, 1, 2) {
		t.Error("Greater should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"a" is not greater than "b"`},
		{less: 1, greater: 2, msg: `"1" is not greater than "2"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, Greater(out, currCase.less, currCase.greater))
		Contains(t, out.get(), currCase.msg)
	}
}

func TestGreaterOrEqual(t *testing.T) {
	mockT := new(testing.T)

	if !GreaterOrEqual(mockT, 2, 1) {
		t.Error("GreaterOrEqual should return true")
	}

	if !GreaterOrEqual(mockT, 1, 1) {
		t.Error("GreaterOrEqual should return true")
	}

	if GreaterOrEqual(mockT, 1, 2) {
		t.Error("GreaterOrEqual should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"a" is not greater than or equal to "b"`},
		{less: 1, greater: 2, msg: `"1" is not greater than or equal to "2"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, GreaterOrEqual(out, currCase.less, currCase.greater))
		Contains(t, out.get(), currCase.msg)
	}
}

func TestLess(t *testing.T) {
	mockT := new(testing.T)

	if !Less(mockT, 1, 2) {
		t.Error("Less should return true")
	}

	if Less(mockT, 1, 1) {
		t.Error("Less should return false")
	}

	if Less(mockT, 2, 1) {
		t.Error("Less should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"b" is not less than "a"`},
		{less: 1, greater: 2, msg: `"2" is not less than "1"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, Less(out, currCase.greater, currCase.less))
		Contains(t, out.get(), currCase.msg)
	}
}

func TestLessOrEqual(t *testing.T) {
	mockT := new(testing.T)

	if !LessOrEqual(mockT, 1, 2) {
		t.Error("LessOrEqual should return true")
	}

	if !LessOrEqual(mockT, 1, 1) {
		t.Error("LessOrEqual should return true")
	}

	if LessOrEqual(mockT, 2, 1) {
		t.Error("LessOrEqual should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"b" is not less than or equal to "a"`},
		{less: 1, greater: 2, msg: `"2" is not less than or equal to "1"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, LessOrEqual(out, currCase.greater, currCase.less))
		Contains(t, out.get(), currCase.msg)
	}
}

func Test_compareTwoValuesDifferentValuesTypes(t *testing.T) {
	mockT := new(testing.T)

	for _, currCase := range []struct {
		v1            interface{}
		v2            interface{}
		compareResult bool
	}{
		{v1: 123, v2: "abc"},
		{v1: "abc", v2: 123456},
		{v1: float64(12), v2: "123"},
		{v1: "float(12)", v2: float64(1)},
	} {
		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
		False(t, compareResult)
	}
}

func Test_compareTwoValuesNotComparableValues(t *testing.T) {
	mockT := new(testing.T)

	type CompareStruct struct {
	}

	for _, currCase := range []struct {
		v1 interface{}
		v2 interface{}
	}{
		{v1: CompareStruct{}, v2: CompareStruct{}},
		{v1: map[string]int{}, v2: map[string]int{}},
		{v1: make([]int, 5, 5), v2: make([]int, 5, 5)},
	} {
		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
		False(t, compareResult)
	}
}

func Test_compareTwoValuesCorrectCompareResult(t *testing.T) {
	mockT := new(testing.T)

	for _, currCase := range []struct {
		v1           interface{}
		v2           interface{}
		compareTypes []CompareType
	}{
		{v1: 1, v2: 2, compareTypes: []CompareType{compareLess}},
		{v1: 1, v2: 2, compareTypes: []CompareType{compareLess, compareEqual}},
		{v1: 2, v2: 2, compareTypes: []CompareType{compareGreater, compareEqual}},
		{v1: 2, v2: 2, compareTypes: []CompareType{compareEqual}},
		{v1: 2, v2: 1, compareTypes: []CompareType{compareEqual, compareGreater}},
		{v1: 2, v2: 1, compareTypes: []CompareType{compareGreater}},
	} {
		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, currCase.compareTypes, "testFailMessage")
		True(t, compareResult)
	}
}

func Test_containsValue(t *testing.T) {
	for _, currCase := range []struct {
		values []CompareType
		value  CompareType
		result bool
	}{
		{values: []CompareType{compareGreater}, value: compareGreater, result: true},
		{values: []CompareType{compareGreater, compareLess}, value: compareGreater, result: true},
		{values: []CompareType{compareGreater, compareLess}, value: compareLess, result: true},
		{values: []CompareType{compareGreater, compareLess}, value: compareEqual, result: false},
	} {
		compareResult := containsValue(currCase.values, currCase.value)
		Equal(t, currCase.result, compareResult)
	}
}
<commit_msg>address comments<commit_after>package assert

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"
)

func TestCompare(t *testing.T) {
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		cType   string
	}{
		{less: "a", greater: "b", cType: "string"},
		{less: int(1), greater: int(2), cType: "int"},
		{less: int8(1), greater: int8(2), cType: "int8"},
		{less: int16(1), greater: int16(2), cType: "int16"},
		{less: int32(1), greater: int32(2), cType: "int32"},
		{less: int64(1), greater: int64(2), cType: "int64"},
		{less: uint8(1), greater: uint8(2), cType: "uint8"},
		{less: uint16(1), greater: uint16(2), cType: "uint16"},
		{less: uint32(1), greater: uint32(2), cType: "uint32"},
		{less: uint64(1), greater: uint64(2), cType: "uint64"},
		{less: float32(1.23), greater: float32(2.34), cType: "float32"},
		{less: float64(1.23), greater: float64(2.34), cType: "float64"},
	} {
		resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind())
		if !isComparable {
			t.Error("object should be comparable for type " + currCase.cType)
		}

		if resLess != compareLess {
			t.Errorf("object less should be less than greater for type " + currCase.cType)
		}

		resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind())
		if !isComparable {
			t.Error("object are comparable for type " + currCase.cType)
		}

		if resGreater != compareGreater {
			t.Errorf("object greater should be greater than less for type " + currCase.cType)
		}

		resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind())
		if !isComparable {
			t.Error("object are comparable for type " + currCase.cType)
		}

		if resEqual != 0 {
			t.Errorf("objects should be equal for type " + currCase.cType)
		}
	}
}

type outputT struct {
	buf *bytes.Buffer
}

// Implements TestingT
func (t *outputT) Errorf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	t.buf.WriteString(s)
}

func TestGreater(t *testing.T) {
	mockT := new(testing.T)

	if !Greater(mockT, 2, 1) {
		t.Error("Greater should return true")
	}

	if Greater(mockT, 1, 1) {
		t.Error("Greater should return false")
	}

	if Greater(mockT, 1, 2) {
		t.Error("Greater should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"a" is not greater than "b"`},
		{less: int(1), greater: int(2), msg: `"1" is not greater than "2"`},
		{less: int8(1), greater: int8(2), msg: `"1" is not greater than "2"`},
		{less: int16(1), greater: int16(2), msg: `"1" is not greater than "2"`},
		{less: int32(1), greater: int32(2), msg: `"1" is not greater than "2"`},
		{less: int64(1), greater: int64(2), msg: `"1" is not greater than "2"`},
		{less: uint8(1), greater: uint8(2), msg: `"1" is not greater than "2"`},
		{less: uint16(1), greater: uint16(2), msg: `"1" is not greater than "2"`},
		{less: uint32(1), greater: uint32(2), msg: `"1" is not greater than "2"`},
		{less: uint64(1), greater: uint64(2), msg: `"1" is not greater than "2"`},
		{less: float32(1.23), greater: float32(2.34), msg: `"1.23" is not greater than "2.34"`},
		{less: float64(1.23), greater: float64(2.34), msg: `"1.23" is not greater than "2.34"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, Greater(out, currCase.less, currCase.greater))
		Contains(t, string(out.buf.Bytes()), currCase.msg)
	}
}

func TestGreaterOrEqual(t *testing.T) {
	mockT := new(testing.T)

	if !GreaterOrEqual(mockT, 2, 1) {
		t.Error("GreaterOrEqual should return true")
	}

	if !GreaterOrEqual(mockT, 1, 1) {
		t.Error("GreaterOrEqual should return true")
	}

	if GreaterOrEqual(mockT, 1, 2) {
		t.Error("GreaterOrEqual should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"a" is not greater than or equal to "b"`},
		{less: int(1), greater: int(2), msg: `"1" is not greater than or equal to "2"`},
		{less: int8(1), greater: int8(2), msg: `"1" is not greater than or equal to "2"`},
		{less: int16(1), greater: int16(2), msg: `"1" is not greater than or equal to "2"`},
		{less: int32(1), greater: int32(2), msg: `"1" is not greater than or equal to "2"`},
		{less: int64(1), greater: int64(2), msg: `"1" is not greater than or equal to "2"`},
		{less: uint8(1), greater: uint8(2), msg: `"1" is not greater than or equal to "2"`},
		{less: uint16(1), greater: uint16(2), msg: `"1" is not greater than or equal to "2"`},
		{less: uint32(1), greater: uint32(2), msg: `"1" is not greater than or equal to "2"`},
		{less: uint64(1), greater: uint64(2), msg: `"1" is not greater than or equal to "2"`},
		{less: float32(1.23), greater: float32(2.34), msg: `"1.23" is not greater than or equal to "2.34"`},
		{less: float64(1.23), greater: float64(2.34), msg: `"1.23" is not greater than or equal to "2.34"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, GreaterOrEqual(out, currCase.less, currCase.greater))
		Contains(t, string(out.buf.Bytes()), currCase.msg)
	}
}

func TestLess(t *testing.T) {
	mockT := new(testing.T)

	if !Less(mockT, 1, 2) {
		t.Error("Less should return true")
	}

	if Less(mockT, 1, 1) {
		t.Error("Less should return false")
	}

	if Less(mockT, 2, 1) {
		t.Error("Less should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"b" is not less than "a"`},
		{less: int(1), greater: int(2), msg: `"2" is not less than "1"`},
		{less: int8(1), greater: int8(2), msg: `"2" is not less than "1"`},
		{less: int16(1), greater: int16(2), msg: `"2" is not less than "1"`},
		{less: int32(1), greater: int32(2), msg: `"2" is not less than "1"`},
		{less: int64(1), greater: int64(2), msg: `"2" is not less than "1"`},
		{less: uint8(1), greater: uint8(2), msg: `"2" is not less than "1"`},
		{less: uint16(1), greater: uint16(2), msg: `"2" is not less than "1"`},
		{less: uint32(1), greater: uint32(2), msg: `"2" is not less than "1"`},
		{less: uint64(1), greater: uint64(2), msg: `"2" is not less than "1"`},
		{less: float32(1.23), greater: float32(2.34), msg: `"2.34" is not less than "1.23"`},
		{less: float64(1.23), greater: float64(2.34), msg: `"2.34" is not less than "1.23"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, Less(out, currCase.greater, currCase.less))
		Contains(t, string(out.buf.Bytes()), currCase.msg)
	}
}

func TestLessOrEqual(t *testing.T) {
	mockT := new(testing.T)

	if !LessOrEqual(mockT, 1, 2) {
		t.Error("LessOrEqual should return true")
	}

	if !LessOrEqual(mockT, 1, 1) {
		t.Error("LessOrEqual should return true")
	}

	if LessOrEqual(mockT, 2, 1) {
		t.Error("LessOrEqual should return false")
	}

	// Check error report
	for _, currCase := range []struct {
		less    interface{}
		greater interface{}
		msg     string
	}{
		{less: "a", greater: "b", msg: `"b" is not less than or equal to "a"`},
		{less: int(1), greater: int(2), msg: `"2" is not less than or equal to "1"`},
		{less: int8(1), greater: int8(2), msg: `"2" is not less than or equal to "1"`},
		{less: int16(1), greater: int16(2), msg: `"2" is not less than or equal to "1"`},
		{less: int32(1), greater: int32(2), msg: `"2" is not less than or equal to "1"`},
		{less: int64(1), greater: int64(2), msg: `"2" is not less than or equal to "1"`},
		{less: uint8(1), greater: uint8(2), msg: `"2" is not less than or equal to "1"`},
		{less: uint16(1), greater: uint16(2), msg: `"2" is not less than or equal to "1"`},
		{less: uint32(1), greater: uint32(2), msg: `"2" is not less than or equal to "1"`},
		{less: uint64(1), greater: uint64(2), msg: `"2" is not less than or equal to "1"`},
		{less: float32(1.23), greater: float32(2.34), msg: `"2.34" is not less than or equal to "1.23"`},
		{less: float64(1.23), greater: float64(2.34), msg: `"2.34" is not less than or equal to "1.23"`},
	} {
		out := &outputT{buf: bytes.NewBuffer(nil)}
		False(t, LessOrEqual(out, currCase.greater, currCase.less))
		Contains(t, string(out.buf.Bytes()), currCase.msg)
	}
}

func Test_compareTwoValuesDifferentValuesTypes(t *testing.T) {
	mockT := new(testing.T)

	for _, currCase := range []struct {
		v1            interface{}
		v2            interface{}
		compareResult bool
	}{
		{v1: 123, v2: "abc"},
		{v1: "abc", v2: 123456},
		{v1: float64(12), v2: "123"},
		{v1: "float(12)", v2: float64(1)},
	} {
		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
		False(t, compareResult)
	}
}

func Test_compareTwoValuesNotComparableValues(t *testing.T) {
	mockT := new(testing.T)

	type CompareStruct struct {
	}

	for _, currCase := range []struct {
		v1 interface{}
		v2 interface{}
	}{
		{v1: CompareStruct{}, v2: CompareStruct{}},
		{v1: map[string]int{}, v2: map[string]int{}},
		{v1: make([]int, 5, 5), v2: make([]int, 5, 5)},
	} {
		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
		False(t, compareResult)
	}
}

func Test_compareTwoValuesCorrectCompareResult(t *testing.T) {
	mockT := new(testing.T)

	for _, currCase := range []struct {
		v1           interface{}
		v2           interface{}
		compareTypes []CompareType
	}{
		{v1: 1, v2: 2, compareTypes: []CompareType{compareLess}},
		{v1: 1, v2: 2, compareTypes: []CompareType{compareLess, compareEqual}},
		{v1: 2, v2: 2, compareTypes: []CompareType{compareGreater, compareEqual}},
		{v1: 2, v2: 2, compareTypes: []CompareType{compareEqual}},
		{v1: 2, v2: 1, compareTypes: []CompareType{compareEqual, compareGreater}},
		{v1: 2, v2: 1, compareTypes: []CompareType{compareGreater}},
	} {
		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, currCase.compareTypes, "testFailMessage")
		True(t, compareResult)
	}
}

func Test_containsValue(t *testing.T) {
	for _, currCase := range []struct {
		values []CompareType
		value  CompareType
		result bool
	}{
		{values: []CompareType{compareGreater}, value: compareGreater, result: true},
		{values: []CompareType{compareGreater, compareLess}, value: compareGreater, result: true},
		{values: []CompareType{compareGreater, compareLess}, value: compareLess, result: true},
		{values: []CompareType{compareGreater, compareLess}, value: compareEqual, result: false},
	} {
		compareResult := containsValue(currCase.values, currCase.value)
		Equal(t, currCase.result, compareResult)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"text/template"
)

var tmplEmbeddedBox *template.Template

func init() {
	var err error

	// parse embedded box template
	tmplEmbeddedBox, err = template.New("embeddedBox").Parse(`package {{.Package}}

import (
	"github.com/GeertJohan/go.rice/embedded"
	"time"
)

{{range .Boxes}}
func init() {

	// define files
	{{range .Files}}{{.Identifier}} := &embedded.EmbeddedFile{
		Filename:    {{.FileName | printf "%q"}},
		FileModTime: time.Unix({{.ModTime}}, 0),
		Content:     string({{.Content | printf "%q"}}),
	}
	{{end}}

	// define dirs
	{{range .Dirs}}{{.Identifier}} := &embedded.EmbeddedDir{
		Filename:    {{.FileName | printf "%q"}},
		DirModTime: time.Unix({{.ModTime}}, 0),
		ChildFiles:  []*embedded.EmbeddedFile{
			{{range .ChildFiles}}{{.Identifier}}, // {{.FileName | printf "%q"}}
			{{end}}
		},
	}
	{{end}}

	// link ChildDirs
	{{range .Dirs}}{{.Identifier}}.ChildDirs = []*embedded.EmbeddedDir{
		{{range .ChildDirs}}{{.Identifier}}, // {{.FileName | printf "%q"}}
		{{end}}
	}
	{{end}}

	// register embeddedBox
	embedded.RegisterEmbeddedBox(` + "`" + `{{.BoxName}}` + "`" + `, &embedded.EmbeddedBox{
		Name: ` + "`" + `{{.BoxName}}` + "`" + `,
		Time: time.Unix({{.UnixNow}}, 0),
		Dirs: map[string]*embedded.EmbeddedDir{
			{{range .Dirs}}{{.FileName | printf "%q"}}: {{.Identifier}},
			{{end}}
		},
		Files: map[string]*embedded.EmbeddedFile{
			{{range .Files}}{{.FileName | printf "%q"}}: {{.Identifier}},
			{{end}}
		},
	})
}
{{end}}`)
	if err != nil {
		fmt.Printf("error parsing embedded box template: %s\n", err)
		os.Exit(-1)
	}
}

type embedFileDataType struct {
	Package string
	Boxes   []*boxDataType
}

type boxDataType struct {
	BoxName string
	UnixNow int64
	Files   []*fileDataType
	Dirs    map[string]*dirDataType
}

type fileDataType struct {
	Identifier string
	FileName   string
	Content    []byte
	ModTime    int64
}

type dirDataType struct {
	Identifier string
	FileName   string
	Content    []byte
	ModTime    int64
	ChildDirs  []*dirDataType
	ChildFiles []*fileDataType
}
<commit_msg>goimport-friendly template.<commit_after>package main

import (
	"fmt"
	"os"
	"text/template"
)

var tmplEmbeddedBox *template.Template

func init() {
	var err error

	// parse embedded box template
	tmplEmbeddedBox, err = template.New("embeddedBox").Parse(`package {{.Package}}

import (
	"time"

	"github.com/GeertJohan/go.rice/embedded"
)

{{range .Boxes}}
func init() {

	// define files
	{{range .Files}}{{.Identifier}} := &embedded.EmbeddedFile{
		Filename:    {{.FileName | printf "%q"}},
		FileModTime: time.Unix({{.ModTime}}, 0),
		Content:     string({{.Content | printf "%q"}}),
	}
	{{end}}

	// define dirs
	{{range .Dirs}}{{.Identifier}} := &embedded.EmbeddedDir{
		Filename:    {{.FileName | printf "%q"}},
		DirModTime: time.Unix({{.ModTime}}, 0),
		ChildFiles:  []*embedded.EmbeddedFile{
			{{range .ChildFiles}}{{.Identifier}}, // {{.FileName | printf "%q"}}
			{{end}}
		},
	}
	{{end}}

	// link ChildDirs
	{{range .Dirs}}{{.Identifier}}.ChildDirs = []*embedded.EmbeddedDir{
		{{range .ChildDirs}}{{.Identifier}}, // {{.FileName | printf "%q"}}
		{{end}}
	}
	{{end}}

	// register embeddedBox
	embedded.RegisterEmbeddedBox(` + "`" + `{{.BoxName}}` + "`" + `, &embedded.EmbeddedBox{
		Name: ` + "`" + `{{.BoxName}}` + "`" + `,
		Time: time.Unix({{.UnixNow}}, 0),
		Dirs: map[string]*embedded.EmbeddedDir{
			{{range .Dirs}}{{.FileName | printf "%q"}}: {{.Identifier}},
			{{end}}
		},
		Files: map[string]*embedded.EmbeddedFile{
			{{range .Files}}{{.FileName | printf "%q"}}: {{.Identifier}},
			{{end}}
		},
	})
}
{{end}}`)
	if err != nil {
		fmt.Printf("error parsing embedded box template: %s\n", err)
		os.Exit(-1)
	}
}

type embedFileDataType struct {
	Package string
	Boxes   []*boxDataType
}

type boxDataType struct {
	BoxName string
	UnixNow int64
	Files   []*fileDataType
	Dirs    map[string]*dirDataType
}

type fileDataType struct {
	Identifier string
	FileName   string
	Content    []byte
	ModTime    int64
}

type dirDataType struct {
	Identifier string
	FileName   string
	Content    []byte
	ModTime    int64
	ChildDirs  []*dirDataType
	ChildFiles []*fileDataType
}
<|endoftext|>


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

package server

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"github.com/andreaskoch/allmark2/common/config"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"github.com/andreaskoch/allmark2/common/util/hashutil"
	"github.com/andreaskoch/allmark2/dataaccess"
	"github.com/andreaskoch/allmark2/services/converter"
	"github.com/andreaskoch/allmark2/services/parser"
	"github.com/andreaskoch/allmark2/web/orchestrator"
	"github.com/andreaskoch/allmark2/web/server/handler"
	"github.com/andreaskoch/allmark2/web/server/header"
	"github.com/andreaskoch/allmark2/web/webpaths"
	"github.com/gorilla/mux"
	"github.com/skratchdot/open-golang/open"
	"math"
	"net/http"
	"path/filepath"
	"strings"
)

var (
	BasePath      = "/"
	TagPathPrefix = fmt.Sprintf("%stags.html#", BasePath)

	// Dynamic Routes
	PrintHandlerRoute  = `/{path:.+\.print$|print$}`
	JsonHandlerRoute   = `/{path:.+\.json$|json$}`
	LatestHandlerRoute = `/{path:.+\.latest$|latest$}`
	RtfHandlerRoute    = `/{path:.+\.rtf$|rtf$}`
	UpdateHandlerRoute = `/{path:.+\.ws$|ws$}`

	ItemHandlerRoute = "/{path:.*$}"

	TagmapHandlerRoute                = "/tags.html"
	SitemapHandlerRoute               = "/sitemap.html"
	XmlSitemapHandlerRoute            = "/sitemap.xml"
	RssHandlerRoute                   = "/feed.rss"
	RobotsTxtHandlerRoute             = "/robots.txt"
	SearchHandlerRoute                = "/search"
	OpenSearchDescriptionHandlerRoute = "/opensearch.xml"

	TypeAheadSearchHandlerRoute = "/search.json"
	TypeAheadTitlesHandlerRoute = "/titles.json"

	// Static Routes
	ThemeFolderRoute     = "/theme"
	ThumbnailFolderRoute = "/thumbnails"
)

func New(logger logger.Logger, config config.Config, repository dataaccess.Repository, parser parser.Parser, converter converter.Converter) (*Server, error) {

	// paths
	patherFactory := webpaths.NewFactory(logger, repository)
	itemPathProvider := patherFactory.Absolute(BasePath)
	tagPathProvider := patherFactory.Absolute(TagPathPrefix)
	webPathProvider := webpaths.NewWebPathProvider(patherFactory, itemPathProvider, tagPathProvider)

	// orchestrator
	orchestratorFactory := orchestrator.NewFactory(logger, config, repository, parser, converter, webPathProvider)

	// handlers
	handlerFactory := handler.NewFactory(logger, config, *orchestratorFactory)

	return &Server{
		logger: logger,
		config: config,

		handlerFactory: handlerFactory,
	}, nil

}

type Server struct {
	isRunning bool

	logger logger.Logger
	config config.Config

	handlerFactory *handler.Factory
}

func (server *Server) IsRunning() bool {
	return server.isRunning
}

func (server *Server) Start() chan error {

	// instantiate handlers
	updateHandler := server.handlerFactory.NewUpdateHandler()
	robotsTxtHandler := server.handlerFactory.NewRobotsTxtHandler()
	xmlSitemapHandler := server.handlerFactory.NewXmlSitemapHandler()
	tagsHandler := server.handlerFactory.NewTagsHandler()
	sitemapHandler := server.handlerFactory.NewSitemapHandler()
	rssHandler := server.handlerFactory.NewRssHandler()
	printHandler := server.handlerFactory.NewPrintHandler()
	searchHandler := server.handlerFactory.NewSearchHandler()
	opensearchDescriptionHandler := server.handlerFactory.NewOpenSearchDescriptionHandler()
	typeAheadHandler := server.handlerFactory.NewTypeAheadSearchHandler()
	titlesHandler := server.handlerFactory.NewTypeAheadTitlesHandler()
	rtfHandler := server.handlerFactory.NewRtfHandler()
	jsonHandler := server.handlerFactory.NewJsonHandler()
	latestHanlder := server.handlerFactory.NewLatestHandler()
	itemHandler := server.handlerFactory.NewItemHandler()

	// register requst routers
	requestRouter := mux.NewRouter()
	requestRouter.Handle(UpdateHandlerRoute, websocket.Handler(updateHandler.Func()))

	// serve auxiliary dynamic files
	requestRouter.HandleFunc(RobotsTxtHandlerRoute, robotsTxtHandler.Func())
	requestRouter.HandleFunc(XmlSitemapHandlerRoute, xmlSitemapHandler.Func())
	requestRouter.HandleFunc(TagmapHandlerRoute, tagsHandler.Func())
	requestRouter.HandleFunc(SitemapHandlerRoute, sitemapHandler.Func())
	requestRouter.HandleFunc(RssHandlerRoute, rssHandler.Func())
	requestRouter.HandleFunc(PrintHandlerRoute, printHandler.Func())
	requestRouter.HandleFunc(SearchHandlerRoute, searchHandler.Func())
	requestRouter.HandleFunc(OpenSearchDescriptionHandlerRoute, opensearchDescriptionHandler.Func())
	requestRouter.HandleFunc(TypeAheadSearchHandlerRoute, typeAheadHandler.Func())
	requestRouter.HandleFunc(TypeAheadTitlesHandlerRoute, titlesHandler.Func())

	// theme
	if themeFolder := server.config.ThemeFolder(); fsutil.DirectoryExists(themeFolder) {
		themeFolderHandler := http.FileServer(http.Dir(themeFolder))
		s := http.StripPrefix(ThemeFolderRoute, addStaticFileHeaders(themeFolder, header.STATICCONTENT_CACHEDURATION_SECONDS, themeFolderHandler))
		requestRouter.PathPrefix(ThemeFolderRoute).Handler(s)
	}

	// thumbnails
	if thumbnailsFolder := server.config.ThumbnailsFolder(); fsutil.DirectoryExists(thumbnailsFolder) {
		thumbnailsFolderHandler := http.FileServer(http.Dir(thumbnailsFolder))
		s := http.StripPrefix(ThumbnailFolderRoute, addStaticFileHeaders(thumbnailsFolder, header.STATICCONTENT_CACHEDURATION_SECONDS, thumbnailsFolderHandler))
		requestRouter.PathPrefix(ThumbnailFolderRoute).Handler(s)
	}

	// serve items
	requestRouter.HandleFunc(RtfHandlerRoute, rtfHandler.Func())
	requestRouter.HandleFunc(JsonHandlerRoute, jsonHandler.Func())
	requestRouter.HandleFunc(LatestHandlerRoute, latestHanlder.Func())
	requestRouter.HandleFunc(ItemHandlerRoute, itemHandler.Func())

	result := make(chan error)

	go func() {
		server.isRunning = true

		// start http server: http
		httpBinding := server.getHttpBinding()
		server.logger.Info("Starting http server %q\n", httpBinding)

		if err := http.ListenAndServe(httpBinding, requestRouter); err != nil {
			result <- fmt.Errorf("Server failed with error: %v", err)
		} else {
			result <- nil
		}

		server.isRunning = false
	}()

	// open the repository in the browser
	open.Run(server.getAddress())

	return result
}

func (server *Server) getHttpBinding() string {

	hostname := server.getHostname()
	port := server.getPort()

	if strings.TrimSpace(hostname) == "" {
		fmt.Sprintf(":%v", port)
	}

	return fmt.Sprintf("%s:%v", hostname, port)
}

func (server *Server) getAddress() string {
	hostname := server.getHostname()
	port := server.getPort()

	switch port {
	case 80:
		return fmt.Sprintf("http://%s", hostname)
	default:
		return fmt.Sprintf("http://%s:%v", hostname, port)
	}

	panic("Unreachable")
}

func (server *Server) getHostname() string {
	hostname := strings.ToLower(strings.TrimSpace(server.config.Server.Http.Hostname))
	if hostname == "" {
		return "localhost"
	}

	return hostname
}

func (server *Server) getPort() int {
	port := server.config.Server.Http.Port
	if port < 1 || port > math.MaxUint16 {
		panic(fmt.Sprintf("%q is an invalid value for a port. Ports can only be in the range of %v to %v,", port, 1, math.MaxUint16))
	}

	return port
}

func addStaticFileHeaders(baseFolder string, seconds int, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		// determine the hash
		etag := ""
		filePath := filepath.Join(baseFolder, r.RequestURI)
		if file, err := fsutil.OpenFile(filePath); err == nil {
			defer file.Close()
			if fileHash, hashErr := hashutil.GetHash(file); hashErr == nil {
				etag = fileHash
			}
		}
		if etag != "" {
			header.ETag(w, r, etag)
		}

		header.Cache(w, r, seconds)
		h.ServeHTTP(w, r)
	})
}
<commit_msg>Server Bug Fix: The addStaticFileHeaders func did not work before because the file path was assembled incorrectly. This also caused the phantom files being created in in .allmark/thumbnails/thumbnails/...<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package server

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"github.com/andreaskoch/allmark2/common/config"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"github.com/andreaskoch/allmark2/common/util/hashutil"
	"github.com/andreaskoch/allmark2/dataaccess"
	"github.com/andreaskoch/allmark2/services/converter"
	"github.com/andreaskoch/allmark2/services/parser"
	"github.com/andreaskoch/allmark2/web/orchestrator"
	"github.com/andreaskoch/allmark2/web/server/handler"
	"github.com/andreaskoch/allmark2/web/server/header"
	"github.com/andreaskoch/allmark2/web/webpaths"
	"github.com/gorilla/mux"
	"github.com/skratchdot/open-golang/open"
	"math"
	"net/http"
	"path/filepath"
	"strings"
)

var (
	BasePath      = "/"
	TagPathPrefix = fmt.Sprintf("%stags.html#", BasePath)

	// Dynamic Routes
	PrintHandlerRoute  = `/{path:.+\.print$|print$}`
	JsonHandlerRoute   = `/{path:.+\.json$|json$}`
	LatestHandlerRoute = `/{path:.+\.latest$|latest$}`
	RtfHandlerRoute    = `/{path:.+\.rtf$|rtf$}`
	UpdateHandlerRoute = `/{path:.+\.ws$|ws$}`

	ItemHandlerRoute = "/{path:.*$}"

	TagmapHandlerRoute                = "/tags.html"
	SitemapHandlerRoute               = "/sitemap.html"
	XmlSitemapHandlerRoute            = "/sitemap.xml"
	RssHandlerRoute                   = "/feed.rss"
	RobotsTxtHandlerRoute             = "/robots.txt"
	SearchHandlerRoute                = "/search"
	OpenSearchDescriptionHandlerRoute = "/opensearch.xml"

	TypeAheadSearchHandlerRoute = "/search.json"
	TypeAheadTitlesHandlerRoute = "/titles.json"

	// Static Routes
	ThemeFolderRoute     = "/theme"
	ThumbnailFolderRoute = "/thumbnails"
)

func New(logger logger.Logger, config config.Config, repository dataaccess.Repository, parser parser.Parser, converter converter.Converter) (*Server, error) {

	// paths
	patherFactory := webpaths.NewFactory(logger, repository)
	itemPathProvider := patherFactory.Absolute(BasePath)
	tagPathProvider := patherFactory.Absolute(TagPathPrefix)
	webPathProvider := webpaths.NewWebPathProvider(patherFactory, itemPathProvider, tagPathProvider)

	// orchestrator
	orchestratorFactory := orchestrator.NewFactory(logger, config, repository, parser, converter, webPathProvider)

	// handlers
	handlerFactory := handler.NewFactory(logger, config, *orchestratorFactory)

	return &Server{
		logger: logger,
		config: config,

		handlerFactory: handlerFactory,
	}, nil

}

type Server struct {
	isRunning bool

	logger logger.Logger
	config config.Config

	handlerFactory *handler.Factory
}

func (server *Server) IsRunning() bool {
	return server.isRunning
}

func (server *Server) Start() chan error {

	// instantiate handlers
	updateHandler := server.handlerFactory.NewUpdateHandler()
	robotsTxtHandler := server.handlerFactory.NewRobotsTxtHandler()
	xmlSitemapHandler := server.handlerFactory.NewXmlSitemapHandler()
	tagsHandler := server.handlerFactory.NewTagsHandler()
	sitemapHandler := server.handlerFactory.NewSitemapHandler()
	rssHandler := server.handlerFactory.NewRssHandler()
	printHandler := server.handlerFactory.NewPrintHandler()
	searchHandler := server.handlerFactory.NewSearchHandler()
	opensearchDescriptionHandler := server.handlerFactory.NewOpenSearchDescriptionHandler()
	typeAheadHandler := server.handlerFactory.NewTypeAheadSearchHandler()
	titlesHandler := server.handlerFactory.NewTypeAheadTitlesHandler()
	rtfHandler := server.handlerFactory.NewRtfHandler()
	jsonHandler := server.handlerFactory.NewJsonHandler()
	latestHanlder := server.handlerFactory.NewLatestHandler()
	itemHandler := server.handlerFactory.NewItemHandler()

	// register requst routers
	requestRouter := mux.NewRouter()
	requestRouter.Handle(UpdateHandlerRoute, websocket.Handler(updateHandler.Func()))

	// serve auxiliary dynamic files
	requestRouter.HandleFunc(RobotsTxtHandlerRoute, robotsTxtHandler.Func())
	requestRouter.HandleFunc(XmlSitemapHandlerRoute, xmlSitemapHandler.Func())
	requestRouter.HandleFunc(TagmapHandlerRoute, tagsHandler.Func())
	requestRouter.HandleFunc(SitemapHandlerRoute, sitemapHandler.Func())
	requestRouter.HandleFunc(RssHandlerRoute, rssHandler.Func())
	requestRouter.HandleFunc(PrintHandlerRoute, printHandler.Func())
	requestRouter.HandleFunc(SearchHandlerRoute, searchHandler.Func())
	requestRouter.HandleFunc(OpenSearchDescriptionHandlerRoute, opensearchDescriptionHandler.Func())
	requestRouter.HandleFunc(TypeAheadSearchHandlerRoute, typeAheadHandler.Func())
	requestRouter.HandleFunc(TypeAheadTitlesHandlerRoute, titlesHandler.Func())

	// theme
	if themeFolder := server.config.ThemeFolder(); fsutil.DirectoryExists(themeFolder) {
		themeFolderHandler := http.FileServer(http.Dir(themeFolder))
		s := http.StripPrefix(ThemeFolderRoute, addStaticFileHeaders(themeFolder, "/"+config.ThemeFolderName, header.STATICCONTENT_CACHEDURATION_SECONDS, themeFolderHandler))
		requestRouter.PathPrefix(ThemeFolderRoute).Handler(s)
	}

	// thumbnails
	if thumbnailsFolder := server.config.ThumbnailsFolder(); fsutil.DirectoryExists(thumbnailsFolder) {
		thumbnailsFolderHandler := http.FileServer(http.Dir(thumbnailsFolder))
		s := http.StripPrefix(ThumbnailFolderRoute, addStaticFileHeaders(thumbnailsFolder, "/"+config.ThumbnailsFolderName, header.STATICCONTENT_CACHEDURATION_SECONDS, thumbnailsFolderHandler))
		requestRouter.PathPrefix(ThumbnailFolderRoute).Handler(s)
	}

	// serve items
	requestRouter.HandleFunc(RtfHandlerRoute, rtfHandler.Func())
	requestRouter.HandleFunc(JsonHandlerRoute, jsonHandler.Func())
	requestRouter.HandleFunc(LatestHandlerRoute, latestHanlder.Func())
	requestRouter.HandleFunc(ItemHandlerRoute, itemHandler.Func())

	result := make(chan error)

	go func() {
		server.isRunning = true

		// start http server: http
		httpBinding := server.getHttpBinding()
		server.logger.Info("Starting http server %q\n", httpBinding)

		if err := http.ListenAndServe(httpBinding, requestRouter); err != nil {
			result <- fmt.Errorf("Server failed with error: %v", err)
		} else {
			result <- nil
		}

		server.isRunning = false
	}()

	// open the repository in the browser
	open.Run(server.getAddress())

	return result
}

func (server *Server) getHttpBinding() string {

	hostname := server.getHostname()
	port := server.getPort()

	if strings.TrimSpace(hostname) == "" {
		fmt.Sprintf(":%v", port)
	}

	return fmt.Sprintf("%s:%v", hostname, port)
}

func (server *Server) getAddress() string {
	hostname := server.getHostname()
	port := server.getPort()

	switch port {
	case 80:
		return fmt.Sprintf("http://%s", hostname)
	default:
		return fmt.Sprintf("http://%s:%v", hostname, port)
	}

	panic("Unreachable")
}

func (server *Server) getHostname() string {
	hostname := strings.ToLower(strings.TrimSpace(server.config.Server.Http.Hostname))
	if hostname == "" {
		return "localhost"
	}

	return hostname
}

func (server *Server) getPort() int {
	port := server.config.Server.Http.Port
	if port < 1 || port > math.MaxUint16 {
		panic(fmt.Sprintf("%q is an invalid value for a port. Ports can only be in the range of %v to %v,", port, 1, math.MaxUint16))
	}

	return port
}

func addStaticFileHeaders(baseFolder, requestPrefixToStripFromRequestUri string, seconds int, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		// determine the hash
		etag := ""

		// prepare the request uri
		requestUri := r.RequestURI
		if requestPrefixToStripFromRequestUri != "" {
			requestUri = stripPathFromRequest(r, requestPrefixToStripFromRequestUri)
		}

		// assemble the filepath on disc
		filePath := filepath.Join(baseFolder, requestUri)

		// read the the hash
		if file, err := fsutil.OpenFile(filePath); err == nil {
			defer file.Close()
			if fileHash, hashErr := hashutil.GetHash(file); hashErr == nil {
				etag = fileHash
			}
		}
		if etag != "" {
			header.ETag(w, r, etag)
		}

		header.Cache(w, r, seconds)
		h.ServeHTTP(w, r)
	})
}

func stripPathFromRequest(r *http.Request, path string) string {
	return strings.TrimPrefix(r.RequestURI, path)
}
<|endoftext|>


<commit_before>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package wfe

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"

	"github.com/bifurcation/gose"

	"github.com/letsencrypt/boulder/core"
)

type WebFrontEndImpl struct {
	RA core.RegistrationAuthority
	SA core.StorageGetter

	// URL configuration parameters
	baseURL   string
	authzBase string
	certBase  string
}

func NewWebFrontEndImpl() WebFrontEndImpl {
	return WebFrontEndImpl{}
}

// Method implementations

func verifyPOST(request *http.Request) ([]byte, jose.JsonWebKey, error) {
	zeroKey := jose.JsonWebKey{}

	// Read body
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, zeroKey, err
	}

	// Parse as JWS
	var jws jose.JsonWebSignature
	if err = json.Unmarshal(body, &jws); err != nil {
		return nil, zeroKey, err
	}

	// Verify JWS
	// NOTE: It might seem insecure for the WFE to be trusted to verify
	// client requests, i.e., that the verification should be done at the
	// RA.  However the WFE is the RA's only view of the outside world
	// *anyway*, so it could always lie about what key was used by faking
	// the signature itself.
	if err = jws.Verify(); err != nil {
		return nil, zeroKey, err
	}

	// TODO Return JWS body
	return []byte(jws.Payload), jws.Header.Key, nil
}

// The ID is always the last slash-separated token in the path
func parseIDFromPath(path string) string {
	re := regexp.MustCompile("^.*/")
	return re.ReplaceAllString(path, "")
}

// Problem objects represent problem documents, which are
// returned with HTTP error responses
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
type problem struct {
	Type     string `json:"type,omitempty"`
	Detail   string `json:"detail,omitempty"`
	Instance string `json:"instance,omitempty"`
}

func sendError(response http.ResponseWriter, message string, code int) {
	problem := problem{Detail: message}
	problemDoc, err := json.Marshal(problem)
	if err != nil {
		return
	}
	http.Error(response, string(problemDoc), code)
}

func (wfe *WebFrontEndImpl) SetAuthzBase(base string) {
	wfe.authzBase = base
}

func (wfe *WebFrontEndImpl) SetCertBase(base string) {
	wfe.certBase = base
}

func (wfe *WebFrontEndImpl) NewAuthz(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, key, err := verifyPOST(request)
	if err != nil {
		sendError(response, "Unable to read body", http.StatusBadRequest)
		return
	}

	var init core.Authorization
	if err = json.Unmarshal(body, &init); err != nil {
		sendError(response, "Error unmarshaling JSON", http.StatusBadRequest)
		return
	}

	// TODO: Create new authz and return
	authz, err := wfe.RA.NewAuthorization(init, key)
	if err != nil {
		sendError(response,
			fmt.Sprintf("Error creating new authz: %+v", err),
			http.StatusInternalServerError)
		return
	}

	// Make a URL for this authz, then blow away the ID before serializing
	authzURL := wfe.authzBase + string(authz.ID)
	authz.ID = ""
	responseBody, err := json.Marshal(authz)
	if err != nil {
		sendError(response, "Error marshaling authz", http.StatusInternalServerError)
		return
	}

	response.Header().Add("Location", authzURL)
	response.WriteHeader(http.StatusCreated)
	response.Write(responseBody)
}

func (wfe *WebFrontEndImpl) NewCert(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, key, err := verifyPOST(request)
	if err != nil {
		sendError(response, "Unable to read body", http.StatusBadRequest)
		return
	}

	var init core.CertificateRequest
	if err = json.Unmarshal(body, &init); err != nil {
		sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest)
		return
	}

	// Create new certificate and return
	cert, err := wfe.RA.NewCertificate(init, key)
	if err != nil {
		sendError(response,
			fmt.Sprintf("Error creating new cert: %+v", err),
			http.StatusBadRequest)
		return
	}

	// Make a URL for this authz
	certURL := wfe.certBase + string(cert.ID)

	// TODO: Content negotiation for cert format
	response.Header().Add("Location", certURL)
	response.WriteHeader(http.StatusCreated)
	response.Write(cert.DER)
}

func (wfe *WebFrontEndImpl) Authz(response http.ResponseWriter, request *http.Request) {
	// Requests to this handler should have a path that leads to a known authz
	id := parseIDFromPath(request.URL.Path)
	authz, err := wfe.SA.GetAuthorization(id)
	if err != nil {
		sendError(response,
			fmt.Sprintf("Unable to find authorization: %+v", err),
			http.StatusNotFound)
		return
	}

	switch request.Method {
	default:
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return

	case "POST":
		body, key, err := verifyPOST(request)
		if err != nil {
			sendError(response, "Unable to read body", http.StatusBadRequest)
			return
		}

		var initialAuthz core.Authorization
		if err = json.Unmarshal(body, &initialAuthz); err != nil {
			sendError(response, "Error unmarshaling authorization", http.StatusBadRequest)
			return
		}

		// Check that the signing key is the right key
		if !key.Equals(authz.Key) {
			fmt.Printf("req:   %+v\n", key)
			fmt.Printf("authz: %+v\n", authz.Key)
			sendError(response, "Signing key does not match key in authorization", http.StatusForbidden)
			return
		}

		// Ask the RA to update this authorization
		initialAuthz.ID = authz.ID
		updatedAuthz, err := wfe.RA.UpdateAuthorization(initialAuthz)
		if err != nil {
			sendError(response, "Unable to update authorization", http.StatusInternalServerError)
			return
		}

		jsonReply, err := json.Marshal(updatedAuthz)
		if err != nil {
			sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
			return
		}
		response.WriteHeader(http.StatusAccepted)
		response.Write(jsonReply)

	case "GET":
		jsonReply, err := json.Marshal(authz)
		if err != nil {
			sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
			return
		}
		response.WriteHeader(http.StatusOK)
		response.Write(jsonReply)
	}
}

func (wfe *WebFrontEndImpl) Cert(response http.ResponseWriter, request *http.Request) {
	switch request.Method {
	default:
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return

	case "GET":
		id := parseIDFromPath(request.URL.Path)
		cert, err := wfe.SA.GetCertificate(id)
		if err != nil {
			sendError(response, "Not found", http.StatusNotFound)
			return
		}

		// TODO: Content negotiation
		// TODO: Indicate content type
		// TODO: Link header
		response.WriteHeader(http.StatusOK)
		response.Write(cert)

	case "POST":
		// TODO: Handle revocation in POST
	}
}
<commit_msg>Log problems when writing response bodies<commit_after>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package wfe

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"

	"github.com/bifurcation/gose"

	"github.com/letsencrypt/boulder/core"
)

type WebFrontEndImpl struct {
	RA core.RegistrationAuthority
	SA core.StorageGetter

	// URL configuration parameters
	baseURL   string
	authzBase string
	certBase  string
}

func NewWebFrontEndImpl() WebFrontEndImpl {
	return WebFrontEndImpl{}
}

// Method implementations

func verifyPOST(request *http.Request) ([]byte, jose.JsonWebKey, error) {
	zeroKey := jose.JsonWebKey{}

	// Read body
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, zeroKey, err
	}

	// Parse as JWS
	var jws jose.JsonWebSignature
	if err = json.Unmarshal(body, &jws); err != nil {
		return nil, zeroKey, err
	}

	// Verify JWS
	// NOTE: It might seem insecure for the WFE to be trusted to verify
	// client requests, i.e., that the verification should be done at the
	// RA.  However the WFE is the RA's only view of the outside world
	// *anyway*, so it could always lie about what key was used by faking
	// the signature itself.
	if err = jws.Verify(); err != nil {
		return nil, zeroKey, err
	}

	// TODO Return JWS body
	return []byte(jws.Payload), jws.Header.Key, nil
}

// The ID is always the last slash-separated token in the path
func parseIDFromPath(path string) string {
	re := regexp.MustCompile("^.*/")
	return re.ReplaceAllString(path, "")
}

// Problem objects represent problem documents, which are
// returned with HTTP error responses
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
type problem struct {
	Type     string `json:"type,omitempty"`
	Detail   string `json:"detail,omitempty"`
	Instance string `json:"instance,omitempty"`
}

func sendError(response http.ResponseWriter, message string, code int) {
	problem := problem{Detail: message}
	problemDoc, err := json.Marshal(problem)
	if err != nil {
		return
	}
	http.Error(response, string(problemDoc), code)
}

func (wfe *WebFrontEndImpl) SetAuthzBase(base string) {
	wfe.authzBase = base
}

func (wfe *WebFrontEndImpl) SetCertBase(base string) {
	wfe.certBase = base
}

func (wfe *WebFrontEndImpl) NewAuthz(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, key, err := verifyPOST(request)
	if err != nil {
		sendError(response, "Unable to read body", http.StatusBadRequest)
		return
	}

	var init core.Authorization
	if err = json.Unmarshal(body, &init); err != nil {
		sendError(response, "Error unmarshaling JSON", http.StatusBadRequest)
		return
	}

	// TODO: Create new authz and return
	authz, err := wfe.RA.NewAuthorization(init, key)
	if err != nil {
		sendError(response,
			fmt.Sprintf("Error creating new authz: %+v", err),
			http.StatusInternalServerError)
		return
	}

	// Make a URL for this authz, then blow away the ID before serializing
	authzURL := wfe.authzBase + string(authz.ID)
	authz.ID = ""
	responseBody, err := json.Marshal(authz)
	if err != nil {
		sendError(response, "Error marshaling authz", http.StatusInternalServerError)
		return
	}

	response.Header().Add("Location", authzURL)
	response.WriteHeader(http.StatusCreated)
	if _, err = response.Write(responseBody); err != nil {
		log.Printf("Could not write response: %s", err)
	}
}

func (wfe *WebFrontEndImpl) NewCert(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, key, err := verifyPOST(request)
	if err != nil {
		sendError(response, "Unable to read body", http.StatusBadRequest)
		return
	}

	var init core.CertificateRequest
	if err = json.Unmarshal(body, &init); err != nil {
		sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest)
		return
	}

	// Create new certificate and return
	cert, err := wfe.RA.NewCertificate(init, key)
	if err != nil {
		sendError(response,
			fmt.Sprintf("Error creating new cert: %+v", err),
			http.StatusBadRequest)
		return
	}

	// Make a URL for this authz
	certURL := wfe.certBase + string(cert.ID)

	// TODO: Content negotiation for cert format
	response.Header().Add("Location", certURL)
	response.WriteHeader(http.StatusCreated)
	if _, err = response.Write(cert.DER); err != nil {
		log.Printf("Could not write response: %s", err)
	}
}

func (wfe *WebFrontEndImpl) Authz(response http.ResponseWriter, request *http.Request) {
	// Requests to this handler should have a path that leads to a known authz
	id := parseIDFromPath(request.URL.Path)
	authz, err := wfe.SA.GetAuthorization(id)
	if err != nil {
		sendError(response,
			fmt.Sprintf("Unable to find authorization: %+v", err),
			http.StatusNotFound)
		return
	}

	switch request.Method {
	default:
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return

	case "POST":
		body, key, err := verifyPOST(request)
		if err != nil {
			sendError(response, "Unable to read body", http.StatusBadRequest)
			return
		}

		var initialAuthz core.Authorization
		if err = json.Unmarshal(body, &initialAuthz); err != nil {
			sendError(response, "Error unmarshaling authorization", http.StatusBadRequest)
			return
		}

		// Check that the signing key is the right key
		if !key.Equals(authz.Key) {
			fmt.Printf("req:   %+v\n", key)
			fmt.Printf("authz: %+v\n", authz.Key)
			sendError(response, "Signing key does not match key in authorization", http.StatusForbidden)
			return
		}

		// Ask the RA to update this authorization
		initialAuthz.ID = authz.ID
		updatedAuthz, err := wfe.RA.UpdateAuthorization(initialAuthz)
		if err != nil {
			sendError(response, "Unable to update authorization", http.StatusInternalServerError)
			return
		}

		jsonReply, err := json.Marshal(updatedAuthz)
		if err != nil {
			sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
			return
		}
		response.WriteHeader(http.StatusAccepted)
		if _, err = response.Write(jsonReply); err != nil {
			log.Printf("Could not write response: %s", err)
		}

	case "GET":
		jsonReply, err := json.Marshal(authz)
		if err != nil {
			sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
			return
		}
		response.WriteHeader(http.StatusOK)
		if _, err = response.Write(jsonReply); err != nil {
			log.Printf("Could not write response: %s", err)
		}
	}
}

func (wfe *WebFrontEndImpl) Cert(response http.ResponseWriter, request *http.Request) {
	switch request.Method {
	default:
		sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
		return

	case "GET":
		id := parseIDFromPath(request.URL.Path)
		cert, err := wfe.SA.GetCertificate(id)
		if err != nil {
			sendError(response, "Not found", http.StatusNotFound)
			return
		}

		// TODO: Content negotiation
		// TODO: Indicate content type
		// TODO: Link header
		response.WriteHeader(http.StatusOK)
		if _, err = response.Write(cert); err != nil {
			log.Printf("Could not write response: %s", err)
		}

	case "POST":
		// TODO: Handle revocation in POST
	}
}
<|endoftext|>


<commit_before><commit_msg>Build assets<commit_after><|endoftext|>