<commit_before>package main

type NullFile struct {
	data    []byte
	content string
}

// NotFoundFiles to return HTTP 404, not found
var NotFoundFiles = map[string]bool{
	"7z":   true,
	"avi":  true,
	"bin":  true,
	"bz2":  true,
	"doc":  true,
	"docx": true,
	"exe":  true,
	"gz":   true,
	"iso":  true,
	"java": true,
	"mov":  true,
	"mp3":  true,
	"mp4":  true,
	"pdf":  true,
	"ppt":  true,
	"pptx": true,
	"lz":   true,
	"lzma": true,
	"sh":   true,
	"swf":  true,
	"rar":  true,
	"tar":  true,
	"tb2":  true,
	"tbz":  true,
	"tbz2": true,
	"tgz":  true,
	"txz":  true,
	"webp": true,
	"webm": true,
	"xls":  true,
	"xlsx": true,
	"xz":   true,
	"zip":  true,
}

// AltSuffix maps alternative extensions to other valid NullFiles
var AltSuffix = map[string]string{
	"asp":  "html",
	"aspx": "html",
	"cgi":  "html",
	"dll":  "html",
	"do":   "html",
	"htm":  "html",
	"jpeg": "jpg",
	"m4a":  "mp4",
	"m4b":  "mp4",
	"m4p":  "mp4",
	"m4r":  "mp4",
	"m4v":  "mp4",
	"tif":  "tiff",
	"xht":  "xhtml",
}

// All known nullfiles
var NullFiles = map[string]NullFile{
	"": NullFile{nil, "text/plain"}, // Catch-all empty suffix
	"bmp": NullFile{[]byte{
		'\x42', '\x4d', '\x1e', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x1a', '\x00', '\x00', '\x00', '\x0c', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x00',
		'\x18', '\x00', '\x00', '\x00', '\xff', '\x00',
	}, "image/bmp"},
	"css": NullFile{nil, "text/css"},
	"csv": NullFile{nil, "text/csv"},
	"gif": NullFile{[]byte{
		'\x47', '\x49', '\x46', '\x38', '\x39', '\x61', '\x01', '\x00',
		'\x01', '\x00', '\x80', '\x00', '\x00', '\xff', '\xff', '\xff',
		'\x00', '\x00', '\x00', '\x21', '\xf9', '\x04', '\x01', '\x00',
		'\x00', '\x00', '\x00', '\x2c', '\x00', '\x00', '\x00', '\x00',
		'\x01', '\x00', '\x01', '\x00', '\x00', '\x02', '\x02', '\x44',
		'\x01', '\x00', '\x3b',
	}, "image/gif"},
	"html": NullFile{[]byte("<!DOCTYPE html><title>x</title>"),
		"text/html"},
	"ico": NullFile{[]byte{
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x01',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x30', '\x00',
		'\x00', '\x00', '\x16', '\x00', '\x00', '\x00', '\x28', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x02', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\xff', '\x00', '\x00', '\x00', '\x00', '\x00',
	}, "image/x-icon"},
	"jpg": NullFile{[]byte{
		'\xff', '\xd8', '\xff', '\xe0', '\x00', '\x10', '\x4a', '\x46',
		'\x49', '\x46', '\x00', '\x01', '\x01', '\x01', '\x00', '\x01',
		'\x00', '\x01', '\x00', '\x00', '\xff', '\xdb', '\x00', '\x43',
		'\x00', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xc0', '\x00', '\x0b', '\x08', '\x00', '\x01',
		'\x00', '\x01', '\x01', '\x01', '\x11', '\x00', '\xff', '\xc4',
		'\x00', '\x14', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x03', '\xff', '\xc4', '\x00', '\x14',
		'\x10', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\xff', '\xda', '\x00', '\x08', '\x01', '\x01',
		'\x00', '\x00', '\x3f', '\x00', '\x47', '\xff', '\xd9',
	}, "image/jpg"},
	"js":   NullFile{nil, "application/javascript"},
	"json": NullFile{[]byte("{}"), "application/json"},
	"png": NullFile{[]byte{
		'\x89', '\x50', '\x4e', '\x47', '\x0d', '\x0a', '\x1a', '\x0a',
		'\x00', '\x00', '\x00', '\x0d', '\x49', '\x48', '\x44', '\x52',
		'\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01',
		'\x08', '\x06', '\x00', '\x00', '\x00', '\x1f', '\x15', '\xc4',
		'\x89', '\x00', '\x00', '\x00', '\x0a', '\x49', '\x44', '\x41',
		'\x54', '\x78', '\x9c', '\x63', '\x00', '\x01', '\x00', '\x00',
		'\x05', '\x00', '\x01', '\x0d', '\x0a', '\x2d', '\xb4', '\x00',
		'\x00', '\x00', '\x00', '\x49', '\x45', '\x4e', '\x44', '\xae',
		'\x42', '\x60', '\x82',
	}, "image/png"},
	"tiff": NullFile{[]byte{
		'\x4d', '\x4d', '\x00', '\x2a', '\x00', '\x00', '\x00', '\x08',
		'\x00', '\x07', '\x01', '\x00', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x01',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x01',
		'\x00', '\x00', '\x01', '\x06', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x11',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x00', '\x01', '\x17', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x1a',
		'\x00', '\x05', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x64', '\x01', '\x1b', '\x00', '\x05', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x64', '\x00', '\x00',
		'\x00', '\x00', '\x20', '\x20', '\x20', '\x62', '\x79', '\x20',
		'\x61', '\x6c', '\x6f', '\x6b',
	}, "image/tiff"},
	"svg": NullFile{[]byte{
		'\x3c', '\x73', '\x76', '\x67', '\x20', '\x78', '\x6d', '\x6c',
		'\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74', '\x70',
		'\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e', '\x77',
		'\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x32', '\x30',
		'\x30', '\x30', '\x2f', '\x73', '\x76', '\x67', '\x22', '\x2f',
		'\x3e',
	}, "image/svg+xml"},
	"ver": NullFile{[]byte("{\n" +
		"    \"date\"    : \"" + BuildInfo["date"] + "\",\n" +
		"    \"sha\"     : \"" + BuildInfo["sha"] + "\",\n" +
		"    \"state\"   : \"" + BuildInfo["state"] + "\",\n" +
		"    \"version\" : \"" + BuildInfo["version"] + "\"\n" +
		"}",
	), "application/json"},
	"wasm": NullFile{[]byte{
		'\x00', '\x61', '\x73', '\x6d', '\x01', '\x00', '\x00', '\x00',
	}, "application/wasm"},
	"xhtml": NullFile{[]byte{
		'\x3c', '\x68', '\x74', '\x6d', '\x6c', '\x20', '\x78', '\x6d',
		'\x6c', '\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74',
		'\x70', '\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e',
		'\x77', '\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x31',
		'\x39', '\x39', '\x39', '\x2f', '\x78', '\x68', '\x74', '\x6d',
		'\x6c', '\x22', '\x2f', '\x3e',
	}, "application/xhtml+xml"},
	"xml": NullFile{[]byte{
		'\x3c', '\x3f', '\x78', '\x6d', '\x6c', '\x20', '\x76', '\x65',
		'\x72', '\x73', '\x69', '\x6f', '\x6e', '\x3d', '\x22', '\x31',
		'\x2e', '\x31', '\x22', '\x3f', '\x3e', '\x3c', '\x21', '\x44',
		'\x4f', '\x43', '\x54', '\x59', '\x50', '\x45', '\x20', '\x5f',
		'\x5b', '\x3c', '\x21', '\x45', '\x4c', '\x45', '\x4d', '\x45',
		'\x4e', '\x54', '\x20', '\x5f', '\x20', '\x45', '\x4d', '\x50',
		'\x54', '\x59', '\x3e', '\x5d', '\x3e', '\x3c', '\x5f', '\x2f',
		'\x3e',
	}, "application/xml"},
}
<commit_msg>Make suffix .stat and .stats equivalent<commit_after>package main

type NullFile struct {
	data    []byte
	content string
}

// NotFoundFiles to return HTTP 404, not found
var NotFoundFiles = map[string]bool{
	"7z":   true,
	"avi":  true,
	"bin":  true,
	"bz2":  true,
	"doc":  true,
	"docx": true,
	"exe":  true,
	"gz":   true,
	"iso":  true,
	"java": true,
	"mov":  true,
	"mp3":  true,
	"mp4":  true,
	"pdf":  true,
	"ppt":  true,
	"pptx": true,
	"lz":   true,
	"lzma": true,
	"sh":   true,
	"swf":  true,
	"rar":  true,
	"tar":  true,
	"tb2":  true,
	"tbz":  true,
	"tbz2": true,
	"tgz":  true,
	"txz":  true,
	"webp": true,
	"webm": true,
	"xls":  true,
	"xlsx": true,
	"xz":   true,
	"zip":  true,
}

// AltSuffix maps alternative extensions to other valid NullFiles
var AltSuffix = map[string]string{
	"asp":  "html",
	"aspx": "html",
	"cgi":  "html",
	"dll":  "html",
	"do":   "html",
	"htm":  "html",
	"jpeg": "jpg",
	"m4a":  "mp4",
	"m4b":  "mp4",
	"m4p":  "mp4",
	"m4r":  "mp4",
	"m4v":  "mp4",
	"stat": "stats",
	"tif":  "tiff",
	"xht":  "xhtml",
}

// All known nullfiles
var NullFiles = map[string]NullFile{
	"": NullFile{nil, "text/plain"}, // Catch-all empty suffix
	"bmp": NullFile{[]byte{
		'\x42', '\x4d', '\x1e', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x1a', '\x00', '\x00', '\x00', '\x0c', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x00',
		'\x18', '\x00', '\x00', '\x00', '\xff', '\x00',
	}, "image/bmp"},
	"css": NullFile{nil, "text/css"},
	"csv": NullFile{nil, "text/csv"},
	"gif": NullFile{[]byte{
		'\x47', '\x49', '\x46', '\x38', '\x39', '\x61', '\x01', '\x00',
		'\x01', '\x00', '\x80', '\x00', '\x00', '\xff', '\xff', '\xff',
		'\x00', '\x00', '\x00', '\x21', '\xf9', '\x04', '\x01', '\x00',
		'\x00', '\x00', '\x00', '\x2c', '\x00', '\x00', '\x00', '\x00',
		'\x01', '\x00', '\x01', '\x00', '\x00', '\x02', '\x02', '\x44',
		'\x01', '\x00', '\x3b',
	}, "image/gif"},
	"html": NullFile{[]byte("<!DOCTYPE html><title>x</title>"),
		"text/html"},
	"ico": NullFile{[]byte{
		'\x00', '\x00', '\x01', '\x00', '\x01', '\x00', '\x01', '\x01',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x30', '\x00',
		'\x00', '\x00', '\x16', '\x00', '\x00', '\x00', '\x28', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x02', '\x00',
		'\x00', '\x00', '\x01', '\x00', '\x18', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\xff', '\x00', '\x00', '\x00', '\x00', '\x00',
	}, "image/x-icon"},
	"jpg": NullFile{[]byte{
		'\xff', '\xd8', '\xff', '\xe0', '\x00', '\x10', '\x4a', '\x46',
		'\x49', '\x46', '\x00', '\x01', '\x01', '\x01', '\x00', '\x01',
		'\x00', '\x01', '\x00', '\x00', '\xff', '\xdb', '\x00', '\x43',
		'\x00', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
		'\xff', '\xff', '\xc0', '\x00', '\x0b', '\x08', '\x00', '\x01',
		'\x00', '\x01', '\x01', '\x01', '\x11', '\x00', '\xff', '\xc4',
		'\x00', '\x14', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x03', '\xff', '\xc4', '\x00', '\x14',
		'\x10', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
		'\x00', '\x00', '\xff', '\xda', '\x00', '\x08', '\x01', '\x01',
		'\x00', '\x00', '\x3f', '\x00', '\x47', '\xff', '\xd9',
	}, "image/jpg"},
	"js":   NullFile{nil, "application/javascript"},
	"json": NullFile{[]byte("{}"), "application/json"},
	"png": NullFile{[]byte{
		'\x89', '\x50', '\x4e', '\x47', '\x0d', '\x0a', '\x1a', '\x0a',
		'\x00', '\x00', '\x00', '\x0d', '\x49', '\x48', '\x44', '\x52',
		'\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01',
		'\x08', '\x06', '\x00', '\x00', '\x00', '\x1f', '\x15', '\xc4',
		'\x89', '\x00', '\x00', '\x00', '\x0a', '\x49', '\x44', '\x41',
		'\x54', '\x78', '\x9c', '\x63', '\x00', '\x01', '\x00', '\x00',
		'\x05', '\x00', '\x01', '\x0d', '\x0a', '\x2d', '\xb4', '\x00',
		'\x00', '\x00', '\x00', '\x49', '\x45', '\x4e', '\x44', '\xae',
		'\x42', '\x60', '\x82',
	}, "image/png"},
	"tiff": NullFile{[]byte{
		'\x4d', '\x4d', '\x00', '\x2a', '\x00', '\x00', '\x00', '\x08',
		'\x00', '\x07', '\x01', '\x00', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x01',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x01',
		'\x00', '\x00', '\x01', '\x06', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x11',
		'\x00', '\x03', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x00', '\x01', '\x17', '\x00', '\x03', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x01', '\x00', '\x00', '\x01', '\x1a',
		'\x00', '\x05', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00',
		'\x00', '\x64', '\x01', '\x1b', '\x00', '\x05', '\x00', '\x00',
		'\x00', '\x01', '\x00', '\x00', '\x00', '\x64', '\x00', '\x00',
		'\x00', '\x00', '\x20', '\x20', '\x20', '\x62', '\x79', '\x20',
		'\x61', '\x6c', '\x6f', '\x6b',
	}, "image/tiff"},
	"svg": NullFile{[]byte{
		'\x3c', '\x73', '\x76', '\x67', '\x20', '\x78', '\x6d', '\x6c',
		'\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74', '\x70',
		'\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e', '\x77',
		'\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x32', '\x30',
		'\x30', '\x30', '\x2f', '\x73', '\x76', '\x67', '\x22', '\x2f',
		'\x3e',
	}, "image/svg+xml"},
	"ver": NullFile{[]byte("{\n" +
		"    \"date\"    : \"" + BuildInfo["date"] + "\",\n" +
		"    \"sha\"     : \"" + BuildInfo["sha"] + "\",\n" +
		"    \"state\"   : \"" + BuildInfo["state"] + "\",\n" +
		"    \"version\" : \"" + BuildInfo["version"] + "\"\n" +
		"}",
	), "application/json"},
	"wasm": NullFile{[]byte{
		'\x00', '\x61', '\x73', '\x6d', '\x01', '\x00', '\x00', '\x00',
	}, "application/wasm"},
	"xhtml": NullFile{[]byte{
		'\x3c', '\x68', '\x74', '\x6d', '\x6c', '\x20', '\x78', '\x6d',
		'\x6c', '\x6e', '\x73', '\x3d', '\x22', '\x68', '\x74', '\x74',
		'\x70', '\x3a', '\x2f', '\x2f', '\x77', '\x77', '\x77', '\x2e',
		'\x77', '\x33', '\x2e', '\x6f', '\x72', '\x67', '\x2f', '\x31',
		'\x39', '\x39', '\x39', '\x2f', '\x78', '\x68', '\x74', '\x6d',
		'\x6c', '\x22', '\x2f', '\x3e',
	}, "application/xhtml+xml"},
	"xml": NullFile{[]byte{
		'\x3c', '\x3f', '\x78', '\x6d', '\x6c', '\x20', '\x76', '\x65',
		'\x72', '\x73', '\x69', '\x6f', '\x6e', '\x3d', '\x22', '\x31',
		'\x2e', '\x31', '\x22', '\x3f', '\x3e', '\x3c', '\x21', '\x44',
		'\x4f', '\x43', '\x54', '\x59', '\x50', '\x45', '\x20', '\x5f',
		'\x5b', '\x3c', '\x21', '\x45', '\x4c', '\x45', '\x4d', '\x45',
		'\x4e', '\x54', '\x20', '\x5f', '\x20', '\x45', '\x4d', '\x50',
		'\x54', '\x59', '\x3e', '\x5d', '\x3e', '\x3c', '\x5f', '\x2f',
		'\x3e',
	}, "application/xml"},
}
<|endoftext|>


<commit_before>package main

import (
	log "code.google.com/p/log4go"
	"crypto/tls"
	"github.com/Terry-Mao/goim/libs/define"
	itime "github.com/Terry-Mao/goim/libs/time"
	"golang.org/x/net/websocket"
	"math/rand"
	"net"
	"net/http"
	"time"
)

func InitWebsocket(addrs []string) (err error) {
	var (
		bind         string
		listener     *net.TCPListener
		addr         *net.TCPAddr
		httpServeMux = http.NewServeMux()
		server       *http.Server
	)
	httpServeMux.Handle("/sub", websocket.Handler(serveWebsocket))
	for _, bind = range addrs {
		if addr, err = net.ResolveTCPAddr("tcp4", bind); err != nil {
			log.Error("net.ResolveTCPAddr(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		if listener, err = net.ListenTCP("tcp4", addr); err != nil {
			log.Error("net.ListenTCP(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		server = &http.Server{Handler: httpServeMux}
		if Debug {
			log.Debug("start websocket listen: \"%s\"", bind)
		}
		go func() {
			if err = server.Serve(listener); err != nil {
				log.Error("server.Serve(\"%s\") error(%v)", bind, err)
				panic(err)
			}
		}()
	}
	return
}

func InitWebsocketWithTLS(addrs []string, cert, priv string) (err error) {
	var (
		httpServeMux = http.NewServeMux()
	)
	httpServeMux.Handle("/sub", websocket.Handler(serveWebsocket))
	config := &tls.Config{}
	config.Certificates = make([]tls.Certificate, 1)
	if config.Certificates[0], err = tls.LoadX509KeyPair(cert, priv); err != nil {
		return
	}
	for _, bind := range addrs {
		server := &http.Server{Addr: bind, Handler: httpServeMux}
		server.SetKeepAlivesEnabled(true)
		if Debug {
			log.Debug("start websocket wss listen: \"%s\"", bind)
		}
		go func() {
			ln, err := net.Listen("tcp", bind)
			if err != nil {
				return
			}

			tlsListener := tls.NewListener(ln, config)
			if err = server.Serve(tlsListener); err != nil {
				log.Error("server.Serve(\"%s\") error(%v)", bind, err)
				return
			}
		}()
	}
	return
}

func serveWebsocket(conn *websocket.Conn) {
	var (
		// ip addr
		lAddr = conn.LocalAddr()
		rAddr = conn.RemoteAddr()
		// timer
		tr = DefaultServer.round.Timer(rand.Int())
	)
	log.Debug("start websocket serve \"%s\" with \"%s\"", lAddr, rAddr)
	DefaultServer.serveWebsocket(conn, tr)
}

func (server *Server) serveWebsocket(conn *websocket.Conn, tr *itime.Timer) {
	var (
		b   *Bucket
		hb  time.Duration // heartbeat
		key string
		err error
		trd *itime.TimerData
		ch  = NewChannel(server.Options.Proto, define.NoRoom)
		p   = &ch.CliProto
	)
	// handshake
	trd = tr.Add(server.Options.HandshakeTimeout, func() {
		conn.Close()
	})
	if key, ch.RoomId, hb, err = server.authWebsocket(conn, p); err == nil {
		trd.Key = key
		tr.Set(trd, hb)
	}
	if err != nil {
		log.Error("handshake failed error(%v)", err)
		tr.Del(trd)
		conn.Close()
		return
	}
	// register key->channel
	b = server.Bucket(key)
	b.Put(key, ch, tr)
	// hanshake ok start dispatch goroutine
	go server.dispatchWebsocket(key, conn, ch)
	for {
		// parse request protocol
		if err = server.readWebsocketRequest(conn, p); err != nil {
			break
		}
		if p.Operation == define.OP_HEARTBEAT {
			// heartbeat
			tr.Set(trd, hb)
			p.Body = nil
			p.Operation = define.OP_HEARTBEAT_REPLY
		} else {
			// process message
			if err = server.operator.Operate(p); err != nil {
				break
			}
		}
		if err = ch.Reply(); err != nil {
			break
		}
	}
	conn.Close()
	ch.Close()
	b.Del(key)
	if err = server.operator.Disconnect(key, ch.RoomId); err != nil {
		log.Error("%s operator do disconnect error(%v)", key, err)
	}
	if Debug {
		log.Debug("%s serverconn goroutine exit", key)
	}
	return
}

// dispatch accepts connections on the listener and serves requests
// for each incoming connection.  dispatch blocks; the caller typically
// invokes it in a go statement.
func (server *Server) dispatchWebsocket(key string, conn *websocket.Conn, ch *Channel) {
	var (
		p   *Proto
		err error
	)
	if Debug {
		log.Debug("start dispatch goroutine")
	}
	for {
		if !ch.Ready() {
			if Debug {
				log.Debug("key: %s wakeup exit dispatch goroutine", key)
			}
			break
		}
		// fetch message from svrbox(server send)
		for {
			if p, err = ch.SvrProto.Get(); err != nil {
				log.Warn("ch.SvrProto.Get() error(%v)", err)
				err = nil
				break
			}
			// just forward the message
			if err = server.writeWebsocketResponse(conn, p); err != nil {
				log.Error("server.sendTCPResponse() error(%v)", err)
				goto failed
			}
			p.Body = nil // avoid memory leak
			ch.SvrProto.GetAdv()
		}
	}
failed:
	log.Error("key: %s dispatch websocket error(%v)", key, err)
	if err = conn.Close(); err != nil {
		log.Warn("conn.Close() error(%v)", err)
	}
	if Debug {
		log.Debug("dispatch goroutine exit")
	}
	return
}

func (server *Server) authWebsocket(conn *websocket.Conn, p *Proto) (key string, rid int32, heartbeat time.Duration, err error) {
	if err = server.readWebsocketRequest(conn, p); err != nil {
		return
	}
	if p.Operation != define.OP_AUTH {
		err = ErrOperation
		return
	}
	if key, rid, heartbeat, err = server.operator.Connect(p); err != nil {
		return
	}
	p.Body = nil
	p.Operation = define.OP_AUTH_REPLY
	err = server.writeWebsocketResponse(conn, p)
	return
}

func (server *Server) readWebsocketRequest(conn *websocket.Conn, p *Proto) (err error) {
	err = websocket.JSON.Receive(conn, p)
	return
}

func (server *Server) writeWebsocketResponse(conn *websocket.Conn, p *Proto) (err error) {
	if p.Body == nil {
		p.Body = emptyJSONBody
	}
	err = websocket.JSON.Send(conn, p)
	return
}
<commit_msg>websocket add more log<commit_after>package main

import (
	log "code.google.com/p/log4go"
	"crypto/tls"
	"github.com/Terry-Mao/goim/libs/define"
	itime "github.com/Terry-Mao/goim/libs/time"
	"golang.org/x/net/websocket"
	"math/rand"
	"net"
	"net/http"
	"time"
)

func InitWebsocket(addrs []string) (err error) {
	var (
		bind         string
		listener     *net.TCPListener
		addr         *net.TCPAddr
		httpServeMux = http.NewServeMux()
		server       *http.Server
	)
	httpServeMux.Handle("/sub", websocket.Handler(serveWebsocket))
	for _, bind = range addrs {
		if addr, err = net.ResolveTCPAddr("tcp4", bind); err != nil {
			log.Error("net.ResolveTCPAddr(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		if listener, err = net.ListenTCP("tcp4", addr); err != nil {
			log.Error("net.ListenTCP(\"tcp4\", \"%s\") error(%v)", bind, err)
			return
		}
		server = &http.Server{Handler: httpServeMux}
		if Debug {
			log.Debug("start websocket listen: \"%s\"", bind)
		}
		go func() {
			if err = server.Serve(listener); err != nil {
				log.Error("server.Serve(\"%s\") error(%v)", bind, err)
				panic(err)
			}
		}()
	}
	return
}

func InitWebsocketWithTLS(addrs []string, cert, priv string) (err error) {
	var (
		httpServeMux = http.NewServeMux()
	)
	httpServeMux.Handle("/sub", websocket.Handler(serveWebsocket))
	config := &tls.Config{}
	config.Certificates = make([]tls.Certificate, 1)
	if config.Certificates[0], err = tls.LoadX509KeyPair(cert, priv); err != nil {
		return
	}
	for _, bind := range addrs {
		server := &http.Server{Addr: bind, Handler: httpServeMux}
		server.SetKeepAlivesEnabled(true)
		if Debug {
			log.Debug("start websocket wss listen: \"%s\"", bind)
		}
		go func() {
			ln, err := net.Listen("tcp", bind)
			if err != nil {
				return
			}

			tlsListener := tls.NewListener(ln, config)
			if err = server.Serve(tlsListener); err != nil {
				log.Error("server.Serve(\"%s\") error(%v)", bind, err)
				return
			}
		}()
	}
	return
}

func serveWebsocket(conn *websocket.Conn) {
	var (
		// ip addr
		lAddr = conn.LocalAddr()
		rAddr = conn.RemoteAddr()
		// timer
		tr = DefaultServer.round.Timer(rand.Int())
	)
	log.Debug("start websocket serve \"%s\" with \"%s\"", lAddr, rAddr)
	DefaultServer.serveWebsocket(conn, tr)
}

func (server *Server) serveWebsocket(conn *websocket.Conn, tr *itime.Timer) {
	var (
		b   *Bucket
		hb  time.Duration // heartbeat
		key string
		err error
		trd *itime.TimerData
		ch  = NewChannel(server.Options.Proto, define.NoRoom)
		p   = &ch.CliProto
	)
	// handshake
	trd = tr.Add(server.Options.HandshakeTimeout, func() {
		conn.Close()
	})
	if key, ch.RoomId, hb, err = server.authWebsocket(conn, p); err == nil {
		trd.Key = key
		tr.Set(trd, hb)
	}
	if err != nil {
		tr.Del(trd)
		conn.Close()
		log.Error("handshake failed error(%v)", err)
		return
	}
	// register key->channel
	b = server.Bucket(key)
	b.Put(key, ch, tr)
	// hanshake ok start dispatch goroutine
	go server.dispatchWebsocket(key, conn, ch)
	for {
		// parse request protocol
		if err = server.readWebsocketRequest(conn, p); err != nil {
			break
		}
		if p.Operation == define.OP_HEARTBEAT {
			// heartbeat
			tr.Set(trd, hb)
			p.Body = nil
			p.Operation = define.OP_HEARTBEAT_REPLY
		} else {
			// process message
			if err = server.operator.Operate(p); err != nil {
				break
			}
		}
		if err = ch.Reply(); err != nil {
			break
		}
	}
	log.Error("key: %s server websocket failed error(%v)", key, err)
	conn.Close()
	ch.Close()
	b.Del(key)
	if err = server.operator.Disconnect(key, ch.RoomId); err != nil {
		log.Error("key: %s operator do disconnect error(%v)", key, err)
	}
	if Debug {
		log.Debug("key: %s server websocket goroutine exit", key)
	}
	return
}

// dispatch accepts connections on the listener and serves requests
// for each incoming connection.  dispatch blocks; the caller typically
// invokes it in a go statement.
func (server *Server) dispatchWebsocket(key string, conn *websocket.Conn, ch *Channel) {
	var (
		p   *Proto
		err error
	)
	if Debug {
		log.Debug("key: %s start dispatch websocket goroutine", key)
	}
	for {
		if !ch.Ready() {
			if Debug {
				log.Debug("key: %s wakeup exit dispatch goroutine", key)
			}
			break
		}
		// fetch message from svrbox(server send)
		for {
			if p, err = ch.SvrProto.Get(); err != nil {
				log.Warn("ch.SvrProto.Get() error(%v)", err)
				err = nil
				break
			}
			// just forward the message
			if err = server.writeWebsocketResponse(conn, p); err != nil {
				log.Error("server.sendTCPResponse() error(%v)", err)
				goto failed
			}
			p.Body = nil // avoid memory leak
			ch.SvrProto.GetAdv()
		}
	}
failed:
	log.Error("key: %s dispatch websocket error(%v)", key, err)
	if err = conn.Close(); err != nil {
		log.Warn("conn.Close() error(%v)", err)
	}
	if Debug {
		log.Debug("key: %s dispatch goroutine exit", key)
	}
	return
}

func (server *Server) authWebsocket(conn *websocket.Conn, p *Proto) (key string, rid int32, heartbeat time.Duration, err error) {
	if err = server.readWebsocketRequest(conn, p); err != nil {
		return
	}
	if p.Operation != define.OP_AUTH {
		err = ErrOperation
		return
	}
	if key, rid, heartbeat, err = server.operator.Connect(p); err != nil {
		return
	}
	p.Body = nil
	p.Operation = define.OP_AUTH_REPLY
	err = server.writeWebsocketResponse(conn, p)
	return
}

func (server *Server) readWebsocketRequest(conn *websocket.Conn, p *Proto) (err error) {
	err = websocket.JSON.Receive(conn, p)
	return
}

func (server *Server) writeWebsocketResponse(conn *websocket.Conn, p *Proto) (err error) {
	if p.Body == nil {
		p.Body = emptyJSONBody
	}
	err = websocket.JSON.Send(conn, p)
	return
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"net"
	"regexp"
	"sort"
	"strings"

	consulapi "github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/base"
	"github.com/hashicorp/serf/serf"
	"github.com/ryanuber/columnize"
)

// MembersCommand is a Command implementation that queries a running
// Consul agent what members are part of the cluster currently.
type MembersCommand struct {
	base.Command
}

func (c *MembersCommand) Help() string {
	helpText := `
Usage: consul members [options]

  Outputs the members of a running Consul agent.

` + c.Command.Help()

	return strings.TrimSpace(helpText)
}

func (c *MembersCommand) Run(args []string) int {
	var detailed bool
	var wan bool
	var statusFilter string

	f := c.Command.NewFlagSet(c)
	f.BoolVar(&detailed, "detailed", false,
		"Provides detailed information about nodes.")
	f.BoolVar(&wan, "wan", false,
		"If the agent is in server mode, this can be used to return the other "+
			"peers in the WAN pool")
	f.StringVar(&statusFilter, "status", ".*",
		"If provided, output is filtered to only nodes matching the regular "+
			"expression for status")

	if err := c.Command.Parse(args); err != nil {
		return 1
	}

	// Compile the regexp
	statusRe, err := regexp.Compile(statusFilter)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to compile status regexp: %v", err))
		return 1
	}

	client, err := c.Command.HTTPClient()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}

	members, err := client.Agent().Members(wan)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error retrieving members: %s", err))
		return 1
	}

	// Filter the results
	n := len(members)
	for i := 0; i < n; i++ {
		member := members[i]
		statusString := serf.MemberStatus(member.Status).String()
		if !statusRe.MatchString(statusString) {
			members[i], members[n-1] = members[n-1], members[i]
			i--
			n--
			continue
		}
	}
	members = members[:n]

	// No matching members
	if len(members) == 0 {
		return 2
	}

	sort.Sort(ByMemberName(members))

	// Generate the output
	var result []string
	if detailed {
		result = c.detailedOutput(members)
	} else {
		result = c.standardOutput(members)
	}

	// Generate the columnized version
	output := columnize.SimpleFormat(result)
	c.Ui.Output(output)

	return 0
}

// so we can sort members by name
type ByMemberName []*consulapi.AgentMember

func (m ByMemberName) Len() int           { return len(m) }
func (m ByMemberName) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
func (m ByMemberName) Less(i, j int) bool { return m[i].Name < m[j].Name }

// standardOutput is used to dump the most useful information about nodes
// in a more human-friendly format
func (c *MembersCommand) standardOutput(members []*consulapi.AgentMember) []string {
	result := make([]string, 0, len(members))
	header := "Node|Address|Status|Type|Build|Protocol|DC"
	result = append(result, header)
	for _, member := range members {
		addr := net.TCPAddr{IP: net.ParseIP(member.Addr), Port: int(member.Port)}
		protocol := member.Tags["vsn"]
		build := member.Tags["build"]
		if build == "" {
			build = "< 0.3"
		} else if idx := strings.Index(build, ":"); idx != -1 {
			build = build[:idx]
		}
		dc := member.Tags["dc"]

		statusString := serf.MemberStatus(member.Status).String()
		switch member.Tags["role"] {
		case "node":
			line := fmt.Sprintf("%s|%s|%s|client|%s|%s|%s",
				member.Name, addr.String(), statusString, build, protocol, dc)
			result = append(result, line)
		case "consul":
			line := fmt.Sprintf("%s|%s|%s|server|%s|%s|%s",
				member.Name, addr.String(), statusString, build, protocol, dc)
			result = append(result, line)
		default:
			line := fmt.Sprintf("%s|%s|%s|unknown|||",
				member.Name, addr.String(), statusString)
			result = append(result, line)
		}
	}
	return result
}

// detailedOutput is used to dump all known information about nodes in
// their raw format
func (c *MembersCommand) detailedOutput(members []*consulapi.AgentMember) []string {
	result := make([]string, 0, len(members))
	header := "Node|Address|Status|Tags"
	result = append(result, header)
	for _, member := range members {
		// Get the tags sorted by key
		tagKeys := make([]string, 0, len(member.Tags))
		for key := range member.Tags {
			tagKeys = append(tagKeys, key)
		}
		sort.Strings(tagKeys)

		// Format the tags as tag1=v1,tag2=v2,...
		var tagPairs []string
		for _, key := range tagKeys {
			tagPairs = append(tagPairs, fmt.Sprintf("%s=%s", key, member.Tags[key]))
		}

		tags := strings.Join(tagPairs, ",")

		addr := net.TCPAddr{IP: net.ParseIP(member.Addr), Port: int(member.Port)}
		line := fmt.Sprintf("%s|%s|%s|%s",
			member.Name, addr.String(), serf.MemberStatus(member.Status).String(), tags)
		result = append(result, line)
	}
	return result
}

func (c *MembersCommand) Synopsis() string {
	return "Lists the members of a Consul cluster"
}
<commit_msg>Formatting fix in members output<commit_after>package command

import (
	"fmt"
	"net"
	"regexp"
	"sort"
	"strings"

	consulapi "github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/base"
	"github.com/hashicorp/serf/serf"
	"github.com/ryanuber/columnize"
)

// MembersCommand is a Command implementation that queries a running
// Consul agent what members are part of the cluster currently.
type MembersCommand struct {
	base.Command
}

func (c *MembersCommand) Help() string {
	helpText := `
Usage: consul members [options]

  Outputs the members of a running Consul agent.

` + c.Command.Help()

	return strings.TrimSpace(helpText)
}

func (c *MembersCommand) Run(args []string) int {
	var detailed bool
	var wan bool
	var statusFilter string

	f := c.Command.NewFlagSet(c)
	f.BoolVar(&detailed, "detailed", false,
		"Provides detailed information about nodes.")
	f.BoolVar(&wan, "wan", false,
		"If the agent is in server mode, this can be used to return the other "+
			"peers in the WAN pool.")
	f.StringVar(&statusFilter, "status", ".*",
		"If provided, output is filtered to only nodes matching the regular "+
			"expression for status.")

	if err := c.Command.Parse(args); err != nil {
		return 1
	}

	// Compile the regexp
	statusRe, err := regexp.Compile(statusFilter)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to compile status regexp: %v", err))
		return 1
	}

	client, err := c.Command.HTTPClient()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}

	members, err := client.Agent().Members(wan)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error retrieving members: %s", err))
		return 1
	}

	// Filter the results
	n := len(members)
	for i := 0; i < n; i++ {
		member := members[i]
		statusString := serf.MemberStatus(member.Status).String()
		if !statusRe.MatchString(statusString) {
			members[i], members[n-1] = members[n-1], members[i]
			i--
			n--
			continue
		}
	}
	members = members[:n]

	// No matching members
	if len(members) == 0 {
		return 2
	}

	sort.Sort(ByMemberName(members))

	// Generate the output
	var result []string
	if detailed {
		result = c.detailedOutput(members)
	} else {
		result = c.standardOutput(members)
	}

	// Generate the columnized version
	output := columnize.SimpleFormat(result)
	c.Ui.Output(output)

	return 0
}

// so we can sort members by name
type ByMemberName []*consulapi.AgentMember

func (m ByMemberName) Len() int           { return len(m) }
func (m ByMemberName) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
func (m ByMemberName) Less(i, j int) bool { return m[i].Name < m[j].Name }

// standardOutput is used to dump the most useful information about nodes
// in a more human-friendly format
func (c *MembersCommand) standardOutput(members []*consulapi.AgentMember) []string {
	result := make([]string, 0, len(members))
	header := "Node|Address|Status|Type|Build|Protocol|DC"
	result = append(result, header)
	for _, member := range members {
		addr := net.TCPAddr{IP: net.ParseIP(member.Addr), Port: int(member.Port)}
		protocol := member.Tags["vsn"]
		build := member.Tags["build"]
		if build == "" {
			build = "< 0.3"
		} else if idx := strings.Index(build, ":"); idx != -1 {
			build = build[:idx]
		}
		dc := member.Tags["dc"]

		statusString := serf.MemberStatus(member.Status).String()
		switch member.Tags["role"] {
		case "node":
			line := fmt.Sprintf("%s|%s|%s|client|%s|%s|%s",
				member.Name, addr.String(), statusString, build, protocol, dc)
			result = append(result, line)
		case "consul":
			line := fmt.Sprintf("%s|%s|%s|server|%s|%s|%s",
				member.Name, addr.String(), statusString, build, protocol, dc)
			result = append(result, line)
		default:
			line := fmt.Sprintf("%s|%s|%s|unknown|||",
				member.Name, addr.String(), statusString)
			result = append(result, line)
		}
	}
	return result
}

// detailedOutput is used to dump all known information about nodes in
// their raw format
func (c *MembersCommand) detailedOutput(members []*consulapi.AgentMember) []string {
	result := make([]string, 0, len(members))
	header := "Node|Address|Status|Tags"
	result = append(result, header)
	for _, member := range members {
		// Get the tags sorted by key
		tagKeys := make([]string, 0, len(member.Tags))
		for key := range member.Tags {
			tagKeys = append(tagKeys, key)
		}
		sort.Strings(tagKeys)

		// Format the tags as tag1=v1,tag2=v2,...
		var tagPairs []string
		for _, key := range tagKeys {
			tagPairs = append(tagPairs, fmt.Sprintf("%s=%s", key, member.Tags[key]))
		}

		tags := strings.Join(tagPairs, ",")

		addr := net.TCPAddr{IP: net.ParseIP(member.Addr), Port: int(member.Port)}
		line := fmt.Sprintf("%s|%s|%s|%s",
			member.Name, addr.String(), serf.MemberStatus(member.Status).String(), tags)
		result = append(result, line)
	}
	return result
}

func (c *MembersCommand) Synopsis() string {
	return "Lists the members of a Consul cluster"
}
<|endoftext|>


<commit_before>// Package jsql provides glue between database/sql and json.
package jsql

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"regexp"

	"github.com/gorilla/mux"
)

const jsonContentType = "application/json;charset=UTF-8"

var reVars = regexp.MustCompile(`\${([^}]+)}`)

// PositionalQueryVars controls wether
// the generated query strings will use '?'
// or $1, $2... syntax.
var PositionalQueryVars = true

// TODO(lvd) less simplistic parsing of query.  worry about quoting etc.
func rewrite(q string) (qq string, varnames []string) {
	idx := reVars.FindAllStringSubmatchIndex(q, -1)
	l := 0
	var b bytes.Buffer
	for _, v := range idx {
		varnames = append(varnames, q[v[2]:v[3]])
		b.WriteString(q[l:v[0]])
		l = v[1]
		if PositionalQueryVars {
			b.WriteString(fmt.Sprintf("$%d", len(varnames)))
		} else {
			b.WriteString("?")
		}
	}
	b.WriteString(q[l:])
	return b.String(), varnames
}

// TODO:make Q send records on a channel, and provide json/bson as encoders
// then have the Handler switch returned encoding depending on http headers

// A queryfunc is the type of function generated by Q.
// It executes a query substituting arguments from the provided args map,
// and writes a JSON array of objects to the provided writer.
// It returns the number of array elements written and any error that may have occured.
//
// If w is an http.ResponseWriter (has a Header() http.Header method), then it will set
// the Content-Type header to application/json;charset=UTF-8 before the first write.
//
// If n == 0, nothing, not even the opening bracket, will have been written to w,
// and the header will not have been set, meaning it is not too late to call e.g.
//
//    http.Error(w, err.Error(), http.StatusInternalError)
//
// otherwise the error will have to be tacked on to the already produced output or
// reported out of band.  If any elements have been written the queryfunc will
// always generate a proper closing bracket.
type QueryFunc func(args map[string]interface{}, w io.Writer) (n int, err error)

// Q builds a function that can execute the query on a database given
// a map of parameters, and writes the result as json to a writer.
//
// The query must be of the form 'SELECT foo FROM bar WHERE x = ${varname}'
// that is, any query accepted by the driver that will provide db,
// but with the normal '$1, $2...' parameters replace by ${varname}.
//
// The generated function will extract the corresponding values from the args map
// and supply them to the db.Stmt.Execute method in the correct order.
// The query will be rewritten to use $1, $2 .. etc.
//
// The generated json will be an array of objects, where
// each object has a name taken from the columns as named in the SQL query.
//
func Q(db *sql.DB, query string) (QueryFunc, error) {
	q, argn := rewrite(query)
	stmt, err := db.Prepare(q)
	if err != nil {
		return nil, err
	}
	return func(args map[string]interface{}, w io.Writer) (n int, err error) {
		var argv []interface{}
		for _, v := range argn {
			argv = append(argv, args[v])
		}
		rows, err := stmt.Query(argv...)
		if err != nil {
			return 0, err
		}
		defer rows.Close()
		retn, err := rows.Columns()
		if err != nil {
			return 0, err
		}

		var (
			retv  = make([]interface{}, len(retn))
			retvv = make([]interface{}, len(retn))
			retm  = make(map[string]interface{})
		)
		for i := range retv {
			retvv[i] = &retv[i]
			retm[retn[i]] = &retv[i]
		}
		for rows.Next() {
			if err := rows.Scan(retvv...); err != nil {
				return n, err
			}
			// convert []byte to strings, because json uuencodes []byte
			for i, v := range retv {
				if vv, ok := v.([]byte); ok {
					retv[i] = string(vv)
				}
			}

			b, err := json.Marshal(retm)
			if err != nil {
				return n, err
			}

			if n == 0 {
				if rw, ok := w.(http.ResponseWriter); ok {
					rw.Header().Set("Content-type", jsonContentType)
				}
				w.Write([]byte("[\n"))
				defer w.Write([]byte("\n]"))
			} else {
				w.Write([]byte(",\n"))
			}

			w.Write(b)
			n++
		}

		return n, rows.Err()
	}, nil
}

// Handler is a convenience wrapper around MkHandler
// that will die on error.
func Handler(db *sql.DB, query string) http.Handler {
	h, err := MkHandler(db, query)
	if err != nil {
		log.Fatal(err)
	}
	return h
}

// MkHandler produces a http.Handler that takes arguments from
// the request, and runs Q, producing a nice error message if needed.
//
// The arguments are taken from the request's formvalues or any
// input json object, depending on the requests content type, merged with
// the "github.com/gorilla/mux".Vars(r), which take precedence
// in case of name conflicts.
func MkHandler(db *sql.DB, query string) (http.Handler, error) {
	_, names := rewrite(query)

	qf, err := Q(db, query)
	if err != nil {
		return nil, err
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		args := make(map[string]interface{})

		var jsonargs map[string]interface{}
		ct := r.Header.Get("Content-Type")
		if ct == "" {
			ct = "application/octet-stream"
		}
		ct, _, err = mime.ParseMediaType(ct)
		if ct == "application/json" {
			defer r.Body.Close()
			if err := json.NewDecoder(io.LimitReader(r.Body, 128<<10)).Decode(&jsonargs); err != nil {
				http.Error(w, fmt.Sprintf("Can't decode json request: %v", err), http.StatusBadRequest)
			}
		}

		muxargs := mux.Vars(r)

		for _, n := range names {
			if v, ok := muxargs[n]; ok {
				args[n] = v
				continue
			}
			if v := r.FormValue(n); v != "" {
				args[n] = v
				continue
			}
			if v, ok := jsonargs[n]; ok {
				args[n] = v
				continue
			}
		}
		// TBD: what if not all names set? can postgres $n deal with nil?
		n, err := qf(args, w)
		if err != nil {
			if n == 0 {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			log.Print(r.Method, r.URL, ": ", err)
		}
	}), nil
}
<commit_msg>only parse body for PUT or POST<commit_after>// Package jsql provides glue between database/sql and json.
package jsql

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"regexp"

	"github.com/gorilla/mux"
)

const jsonContentType = "application/json;charset=UTF-8"

var reVars = regexp.MustCompile(`\${([^}]+)}`)

// PositionalQueryVars controls wether
// the generated query strings will use '?'
// or $1, $2... syntax.
var PositionalQueryVars = true

// TODO(lvd) less simplistic parsing of query.  worry about quoting etc.
func rewrite(q string) (qq string, varnames []string) {
	idx := reVars.FindAllStringSubmatchIndex(q, -1)
	l := 0
	var b bytes.Buffer
	for _, v := range idx {
		varnames = append(varnames, q[v[2]:v[3]])
		b.WriteString(q[l:v[0]])
		l = v[1]
		if PositionalQueryVars {
			b.WriteString(fmt.Sprintf("$%d", len(varnames)))
		} else {
			b.WriteString("?")
		}
	}
	b.WriteString(q[l:])
	return b.String(), varnames
}

// TODO:make Q send records on a channel, and provide json/bson as encoders
// then have the Handler switch returned encoding depending on http headers

// A queryfunc is the type of function generated by Q.
// It executes a query substituting arguments from the provided args map,
// and writes a JSON array of objects to the provided writer.
// It returns the number of array elements written and any error that may have occured.
//
// If w is an http.ResponseWriter (has a Header() http.Header method), then it will set
// the Content-Type header to application/json;charset=UTF-8 before the first write.
//
// If n == 0, nothing, not even the opening bracket, will have been written to w,
// and the header will not have been set, meaning it is not too late to call e.g.
//
//    http.Error(w, err.Error(), http.StatusInternalError)
//
// otherwise the error will have to be tacked on to the already produced output or
// reported out of band.  If any elements have been written the queryfunc will
// always generate a proper closing bracket.
type QueryFunc func(args map[string]interface{}, w io.Writer) (n int, err error)

// Q builds a function that can execute the query on a database given
// a map of parameters, and writes the result as json to a writer.
//
// The query must be of the form 'SELECT foo FROM bar WHERE x = ${varname}'
// that is, any query accepted by the driver that will provide db,
// but with the normal '$1, $2...' parameters replace by ${varname}.
//
// The generated function will extract the corresponding values from the args map
// and supply them to the db.Stmt.Execute method in the correct order.
// The query will be rewritten to use $1, $2 .. etc.
//
// The generated json will be an array of objects, where
// each object has a name taken from the columns as named in the SQL query.
//
func Q(db *sql.DB, query string) (QueryFunc, error) {
	q, argn := rewrite(query)
	stmt, err := db.Prepare(q)
	if err != nil {
		return nil, err
	}
	return func(args map[string]interface{}, w io.Writer) (n int, err error) {
		var argv []interface{}
		for _, v := range argn {
			argv = append(argv, args[v])
		}
		rows, err := stmt.Query(argv...)
		if err != nil {
			return 0, err
		}
		defer rows.Close()
		retn, err := rows.Columns()
		if err != nil {
			return 0, err
		}

		var (
			retv  = make([]interface{}, len(retn))
			retvv = make([]interface{}, len(retn))
			retm  = make(map[string]interface{})
		)
		for i := range retv {
			retvv[i] = &retv[i]
			retm[retn[i]] = &retv[i]
		}
		for rows.Next() {
			if err := rows.Scan(retvv...); err != nil {
				return n, err
			}
			// convert []byte to strings, because json uuencodes []byte
			for i, v := range retv {
				if vv, ok := v.([]byte); ok {
					retv[i] = string(vv)
				}
			}

			b, err := json.Marshal(retm)
			if err != nil {
				return n, err
			}

			if n == 0 {
				if rw, ok := w.(http.ResponseWriter); ok {
					rw.Header().Set("Content-type", jsonContentType)
				}
				w.Write([]byte("[\n"))
				defer w.Write([]byte("\n]"))
			} else {
				w.Write([]byte(",\n"))
			}

			w.Write(b)
			n++
		}

		return n, rows.Err()
	}, nil
}

// Handler is a convenience wrapper around MkHandler
// that will die on error.
func Handler(db *sql.DB, query string) http.Handler {
	h, err := MkHandler(db, query)
	if err != nil {
		log.Fatal(err)
	}
	return h
}

// MkHandler produces a http.Handler that takes arguments from
// the request, and runs Q, producing a nice error message if needed.
//
// The arguments are taken from the request's formvalues or any
// input json object, depending on the requests content type, merged with
// the "github.com/gorilla/mux".Vars(r), which take precedence
// in case of name conflicts.
func MkHandler(db *sql.DB, query string) (http.Handler, error) {
	_, names := rewrite(query)

	qf, err := Q(db, query)
	if err != nil {
		return nil, err
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		args := make(map[string]interface{})

		var jsonargs map[string]interface{}
		if r.Method == "POST" || r.Method == "PUT" {
			ct := r.Header.Get("Content-Type")
			if ct == "" {
				ct = "application/octet-stream"
			}
			ct, _, err = mime.ParseMediaType(ct)
			if ct == "application/json" {
				defer r.Body.Close()
				if err := json.NewDecoder(io.LimitReader(r.Body, 128<<10)).Decode(&jsonargs); err != nil {
					http.Error(w, fmt.Sprintf("Can't decode json request: %v", err), http.StatusBadRequest)
				}
			}
		}
		muxargs := mux.Vars(r)

		for _, n := range names {
			if v, ok := muxargs[n]; ok {
				args[n] = v
				continue
			}
			if v := r.FormValue(n); v != "" {
				args[n] = v
				continue
			}
			if v, ok := jsonargs[n]; ok {
				args[n] = v
				continue
			}
		}
		// TBD: what if not all names set? can postgres $n deal with nil?
		n, err := qf(args, w)
		if err != nil {
			if n == 0 {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			log.Print(r.Method, r.URL, ": ", err)
		}
	}), nil
}
<|endoftext|>


<commit_before>package main

import (
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/jackc/pgx"

	"fknsrs.biz/p/jishaku-web/lib/scraper"
)

var (
	SELECT_QUERY = `select "info_hash", "trackers" from "torrents" where "torrents"."last_scrape" is null or "torrents"."last_scrape" < (now() - interval '1 day') order by "torrents"."last_scrape" asc nulls first, "torrents"."first_seen" desc limit 1000`
	INSERT_QUERY = `insert into "scrapes" ("info_hash", "tracker", "time", "success", "downloaded", "complete", "incomplete") values ($1, $2, now(), $3, $4, $5, $6)`
	UPDATE_QUERY = `update "torrents" set "last_scrape" = now() where "info_hash" = $1`
)

func scraperCommandFunction(databaseDSN string, debug bool) {
	connConfig, err := pgx.ParseDSN(databaseDSN)
	if err != nil {
		panic(err)
	}

	dbConfig := pgx.ConnPoolConfig{
		ConnConfig:     connConfig,
		MaxConnections: 4,
	}

	if debug {
		dbConfig.Logger = (*wrappedLogger)(logrus.StandardLogger())
	}

	db, err := pgx.NewConnPool(dbConfig)
	if err != nil {
		panic(err)
	}
	defer db.Close()

	s := scraper.New()
	go s.Run()

	for {
		rows, err := db.Query(SELECT_QUERY)
		if err != nil {
			panic(err)
		}

		var wg1 sync.WaitGroup

		for rows.Next() {
			var infoHash string
			var trackers []string
			if err := rows.Scan(&infoHash, &trackers); err != nil {
				panic(err)
				// continue
			}

			h, err := scraper.HashFromString(infoHash)
			if err != nil {
				panic(err)
			}

			wg1.Add(1)

			go func() {
				defer wg1.Done()

				var wg2 sync.WaitGroup

				for _, t := range trackers {
					wg2.Add(1)

					go func(t string) {
						defer wg2.Done()

						l := logrus.WithFields(logrus.Fields{
							"info_hash": infoHash,
							"tracker":   t,
						})

						res, err := s.Scrape(t, h)
						if err != nil {
							if _, ok := err.(scraper.UnsupportedProtocolError); !ok {
								l.Error(err.Error())
							}

							if _, err := db.Exec(INSERT_QUERY, infoHash, t, false, 0, 0, 0); err != nil {
								panic(err)
							}

							return
						}

						if _, err := db.Exec(INSERT_QUERY, infoHash, t, true, res.Downloaded, res.Complete, res.Incomplete); err != nil {
							panic(err)
						}
					}(t)
				}

				wg2.Wait()

				if _, err := db.Exec(UPDATE_QUERY, infoHash); err != nil {
					panic(err)
				}
			}()
		}

		logrus.Info("waiting")

		wg1.Wait()
	}
}
<commit_msg>properly scope variables for goroutine<commit_after>package main

import (
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/jackc/pgx"

	"fknsrs.biz/p/jishaku-web/lib/scraper"
)

var (
	SELECT_QUERY = `select "info_hash", "trackers" from "torrents" where "torrents"."last_scrape" is null or "torrents"."last_scrape" < (now() - interval '1 day') order by "torrents"."last_scrape" asc nulls first, "torrents"."first_seen" desc limit 1000`
	INSERT_QUERY = `insert into "scrapes" ("info_hash", "tracker", "time", "success", "downloaded", "complete", "incomplete") values ($1, $2, now(), $3, $4, $5, $6)`
	UPDATE_QUERY = `update "torrents" set "last_scrape" = now() where "info_hash" = $1`
)

func scraperCommandFunction(databaseDSN string, debug bool) {
	connConfig, err := pgx.ParseDSN(databaseDSN)
	if err != nil {
		panic(err)
	}

	dbConfig := pgx.ConnPoolConfig{
		ConnConfig:     connConfig,
		MaxConnections: 4,
	}

	if debug {
		dbConfig.Logger = (*wrappedLogger)(logrus.StandardLogger())
	}

	db, err := pgx.NewConnPool(dbConfig)
	if err != nil {
		panic(err)
	}
	defer db.Close()

	s := scraper.New()
	go s.Run()

	for {
		rows, err := db.Query(SELECT_QUERY)
		if err != nil {
			panic(err)
		}

		var wg1 sync.WaitGroup

		for rows.Next() {
			var infoHash string
			var trackers []string
			if err := rows.Scan(&infoHash, &trackers); err != nil {
				panic(err)
				// continue
			}

			wg1.Add(1)

			go func(infoHash string, trackers []string) {
				defer wg1.Done()

				var wg2 sync.WaitGroup

				h, err := scraper.HashFromString(infoHash)
				if err != nil {
					panic(err)
				}

				for _, t := range trackers {
					wg2.Add(1)

					go func(t string) {
						defer wg2.Done()

						l := logrus.WithFields(logrus.Fields{
							"info_hash": infoHash,
							"tracker":   t,
						})

						res, err := s.Scrape(t, h)
						if err != nil {
							if _, ok := err.(scraper.UnsupportedProtocolError); !ok {
								l.Error(err.Error())
							}

							if _, err := db.Exec(INSERT_QUERY, infoHash, t, false, 0, 0, 0); err != nil {
								panic(err)
							}

							return
						}

						if _, err := db.Exec(INSERT_QUERY, infoHash, t, true, res.Downloaded, res.Complete, res.Incomplete); err != nil {
							panic(err)
						}
					}(t)
				}

				wg2.Wait()

				if _, err := db.Exec(UPDATE_QUERY, infoHash); err != nil {
					panic(err)
				}
			}(infoHash, trackers)
		}

		logrus.Info("waiting")

		wg1.Wait()
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/github/hub/git"
	"github.com/github/hub/github"
	"github.com/github/hub/ui"
	"github.com/github/hub/utils"
)

var cmdCreate = &Command{
	Run:   create,
	Usage: "create [-poc] [-d <DESCRIPTION>] [-h <HOMEPAGE>] [[<ORGANIZATION>/]<NAME>]",
	Long: `Create a new repository on GitHub and add a git remote for it.

## Options:
	-p
		Create a private repository.

	-d <DESCRIPTION>
		Use this text as the description of the GitHub repository.

	-h <HOMEPAGE>
		Use this text as the URL of the GitHub repository.

	-o, --browse
		Open the new repository in a web browser.

	-c, --copy
		Put the URL of the new repository to clipboard instead of printing it.

	[<ORGANIZATION>/]<NAME>
		The name for the repository on GitHub (default: name of the current working
		directory).

		Optionally, create the repository within <ORGANIZATION>.

## Examples:
		$ hub create
		[ repo created on GitHub ]
		> git remote add -f origin git@github.com:USER/REPO.git

		$ hub create sinatra/recipes
		[ repo created in GitHub organization ]
		> git remote add -f origin git@github.com:sinatra/recipes.git

## See also:

hub-init(1), hub(1)
`,
}

var (
	flagCreatePrivate,
	flagCreateBrowse,
	flagCreateCopy bool

	flagCreateDescription,
	flagCreateHomepage string
)

func init() {
	cmdCreate.Flag.BoolVarP(&flagCreatePrivate, "private", "p", false, "PRIVATE")
	cmdCreate.Flag.BoolVarP(&flagCreateBrowse, "browse", "o", false, "BROWSE")
	cmdCreate.Flag.BoolVarP(&flagCreateCopy, "copy", "c", false, "COPY")
	cmdCreate.Flag.StringVarP(&flagCreateDescription, "description", "d", "", "DESCRIPTION")
	cmdCreate.Flag.StringVarP(&flagCreateHomepage, "homepage", "h", "", "HOMEPAGE")

	CmdRunner.Use(cmdCreate)
}

func create(command *Command, args *Args) {
	_, err := git.Dir()
	if err != nil {
		err = fmt.Errorf("'create' must be run from inside a git repository")
		utils.Check(err)
	}

	var newRepoName string
	if args.IsParamsEmpty() {
		dirName, err := git.WorkdirName()
		utils.Check(err)
		newRepoName = github.SanitizeProjectName(dirName)
	} else {
		reg := regexp.MustCompile("^[^-]")
		if !reg.MatchString(args.FirstParam()) {
			err = fmt.Errorf("invalid argument: %s", args.FirstParam())
			utils.Check(err)
		}
		newRepoName = args.FirstParam()
	}

	config := github.CurrentConfig()
	host, err := config.DefaultHost()
	if err != nil {
		utils.Check(github.FormatError("creating repository", err))
	}

	owner := host.User
	if strings.Contains(newRepoName, "/") {
		split := strings.SplitN(newRepoName, "/", 2)
		owner = split[0]
		newRepoName = split[1]
	}

	project := github.NewProject(owner, newRepoName, host.Host)
	gh := github.NewClient(project.Host)

	if gh.IsRepositoryExist(project) {
		ui.Errorln("Existing repository detected. Updating git remote")
	} else {
		if !args.Noop {
			repo, err := gh.CreateRepository(project, flagCreateDescription, flagCreateHomepage, flagCreatePrivate)
			utils.Check(err)
			project = github.NewProject(repo.FullName, "", project.Host)
		}
	}

	localRepo, err := github.LocalRepo()
	utils.Check(err)

	remote, _ := localRepo.OriginRemote()
	if remote == nil || remote.Name != "origin" {
		url := project.GitURL("", "", true)
		args.Before("git", "remote", "add", "-f", "origin", url)
	}

	webUrl := project.WebURL("", "", "")
	args.NoForward()
	printBrowseOrCopy(args, webUrl, flagCreateBrowse, flagCreateCopy)
}
<commit_msg>Prevent setting a public upstream when creating a private repo.<commit_after>package commands

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/github/hub/git"
	"github.com/github/hub/github"
	"github.com/github/hub/ui"
	"github.com/github/hub/utils"
)

var cmdCreate = &Command{
	Run:   create,
	Usage: "create [-poc] [-d <DESCRIPTION>] [-h <HOMEPAGE>] [[<ORGANIZATION>/]<NAME>]",
	Long: `Create a new repository on GitHub and add a git remote for it.

## Options:
	-p
		Create a private repository.

	-d <DESCRIPTION>
		Use this text as the description of the GitHub repository.

	-h <HOMEPAGE>
		Use this text as the URL of the GitHub repository.

	-o, --browse
		Open the new repository in a web browser.

	-c, --copy
		Put the URL of the new repository to clipboard instead of printing it.

	[<ORGANIZATION>/]<NAME>
		The name for the repository on GitHub (default: name of the current working
		directory).

		Optionally, create the repository within <ORGANIZATION>.

## Examples:
		$ hub create
		[ repo created on GitHub ]
		> git remote add -f origin git@github.com:USER/REPO.git

		$ hub create sinatra/recipes
		[ repo created in GitHub organization ]
		> git remote add -f origin git@github.com:sinatra/recipes.git

## See also:

hub-init(1), hub(1)
`,
}

var (
	flagCreatePrivate,
	flagCreateBrowse,
	flagCreateCopy bool

	flagCreateDescription,
	flagCreateHomepage string
)

func init() {
	cmdCreate.Flag.BoolVarP(&flagCreatePrivate, "private", "p", false, "PRIVATE")
	cmdCreate.Flag.BoolVarP(&flagCreateBrowse, "browse", "o", false, "BROWSE")
	cmdCreate.Flag.BoolVarP(&flagCreateCopy, "copy", "c", false, "COPY")
	cmdCreate.Flag.StringVarP(&flagCreateDescription, "description", "d", "", "DESCRIPTION")
	cmdCreate.Flag.StringVarP(&flagCreateHomepage, "homepage", "h", "", "HOMEPAGE")

	CmdRunner.Use(cmdCreate)
}

func create(command *Command, args *Args) {
	_, err := git.Dir()
	if err != nil {
		err = fmt.Errorf("'create' must be run from inside a git repository")
		utils.Check(err)
	}

	var newRepoName string
	if args.IsParamsEmpty() {
		dirName, err := git.WorkdirName()
		utils.Check(err)
		newRepoName = github.SanitizeProjectName(dirName)
	} else {
		reg := regexp.MustCompile("^[^-]")
		if !reg.MatchString(args.FirstParam()) {
			err = fmt.Errorf("invalid argument: %s", args.FirstParam())
			utils.Check(err)
		}
		newRepoName = args.FirstParam()
	}

	config := github.CurrentConfig()
	host, err := config.DefaultHost()
	if err != nil {
		utils.Check(github.FormatError("creating repository", err))
	}

	owner := host.User
	if strings.Contains(newRepoName, "/") {
		split := strings.SplitN(newRepoName, "/", 2)
		owner = split[0]
		newRepoName = split[1]
	}

	project := github.NewProject(owner, newRepoName, host.Host)
	gh := github.NewClient(project.Host)

	if gh.IsRepositoryExist(project) {

		repo, err := gh.Repository(project)
		if err != nil {
			utils.Check(err)
		}

		if !repo.Private && flagCreatePrivate {
			err = fmt.Errorf("Existing public repository detected. git remote not updated")
			utils.Check(err)
		} else {
			ui.Errorln("Existing repository detected. Updating git remote")
		}
	} else {
		if !args.Noop {
			repo, err := gh.CreateRepository(project, flagCreateDescription, flagCreateHomepage, flagCreatePrivate)
			utils.Check(err)
			project = github.NewProject(repo.FullName, "", project.Host)
		}
	}

	localRepo, err := github.LocalRepo()
	utils.Check(err)

	remote, _ := localRepo.OriginRemote()
	if remote == nil || remote.Name != "origin" {
		url := project.GitURL("", "", true)
		args.Before("git", "remote", "add", "-f", "origin", url)
	}

	webUrl := project.WebURL("", "", "")
	args.NoForward()
	printBrowseOrCopy(args, webUrl, flagCreateBrowse, flagCreateCopy)
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"net"
)

type NetIn struct {
	Port uint16 `short:"p" long:"port" description:"container port" required:"true"`
}

func (command *NetIn) Execute(maybeHandle []string) error {
	container, err := globalClient().Lookup(handle(maybeHandle))
	failIf(err)

	hostPort, _, err := container.NetIn(0, uint32(command.Port))
	failIf(err)

	host, _, err := net.SplitHostPort(Globals.Target.Address)
	failIf(err)

	fmt.Println(net.JoinHostPort(host, fmt.Sprintf("%d", hostPort)))

	return nil
}
<commit_msg>don't try to SplitHostPort when the target garden is on a unix socket<commit_after>package commands

import (
	"fmt"
	"net"
	"os"
)

type NetIn struct {
	Port uint16 `short:"p" long:"port" description:"container port" required:"true"`
}

func (command *NetIn) Execute(maybeHandle []string) error {
	container, err := globalClient().Lookup(handle(maybeHandle))
	failIf(err)

	hostPort, _, err := container.NetIn(0, uint32(command.Port))
	failIf(err)

	host := Globals.Target.Address
	if _, err := os.Stat(host); err == nil {
		host = "127.0.0.1"
	} else {
		host, _, err = net.SplitHostPort(host)
		failIf(err)
	}

	fmt.Println(net.JoinHostPort(host, fmt.Sprintf("%d", hostPort)))

	return nil
}
<|endoftext|>


<commit_before>package discover

import (
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/calmh/syncthing/buffers"
)

const (
	AnnouncementPort = 21025
	Debug            = false
)

type Discoverer struct {
	MyID             string
	ListenPort       int
	BroadcastIntv    time.Duration
	ExtBroadcastIntv time.Duration

	conn         *net.UDPConn
	registry     map[string][]string
	registryLock sync.RWMutex
	extServer    string

	localBroadcastTick  <-chan time.Time
	forcedBroadcastTick chan time.Time
}

var (
	ErrIncorrectMagic = errors.New("incorrect magic number")
)

// We tolerate a certain amount of errors because we might be running on
// laptops that sleep and wake, have intermittent network connectivity, etc.
// When we hit this many errors in succession, we stop.
const maxErrors = 30

func NewDiscoverer(id string, port int, extServer string) (*Discoverer, error) {
	local := &net.UDPAddr{IP: nil, Port: AnnouncementPort}
	conn, err := net.ListenUDP("udp", local)
	if err != nil {
		return nil, err
	}

	disc := &Discoverer{
		MyID:             id,
		ListenPort:       port,
		BroadcastIntv:    30 * time.Second,
		ExtBroadcastIntv: 1800 * time.Second,

		conn:      conn,
		registry:  make(map[string][]string),
		extServer: extServer,
	}

	go disc.recvAnnouncements()

	if disc.ListenPort > 0 {
		disc.localBroadcastTick = time.Tick(disc.BroadcastIntv)
		disc.forcedBroadcastTick = make(chan time.Time)
		go disc.sendAnnouncements()
	}
	if len(disc.extServer) > 0 {
		go disc.sendExtAnnouncements()
	}

	return disc, nil
}

func (d *Discoverer) sendAnnouncements() {
	var pkt = AnnounceV2{AnnouncementMagicV2, d.MyID, []Address{{nil, 22000}}}
	var buf = pkt.MarshalXDR()
	var errCounter = 0
	var err error

	for errCounter < maxErrors {
		for _, ipStr := range allBroadcasts() {
			var addrStr = ipStr + ":21025"

			remote, err := net.ResolveUDPAddr("udp4", addrStr)
			if err != nil {
				log.Printf("discover/external: %v; no external announcements", err)
				return
			}

			if Debug {
				log.Println("send announcement -> ", remote)
			}
			_, _, err = d.conn.WriteMsgUDP(buf, nil, remote)
			if err != nil {
				log.Println("discover/write: warning:", err)
				errCounter++
			} else {
				errCounter = 0
			}
		}

		select {
		case <-d.localBroadcastTick:
		case <-d.forcedBroadcastTick:
		}
	}
	log.Println("discover/write: local: stopping due to too many errors:", err)
}

func (d *Discoverer) sendExtAnnouncements() {
	remote, err := net.ResolveUDPAddr("udp", d.extServer)
	if err != nil {
		log.Printf("discover/external: %v; no external announcements", err)
		return
	}

	var pkt = AnnounceV2{AnnouncementMagicV2, d.MyID, []Address{{nil, 22000}}}
	var buf = pkt.MarshalXDR()
	var errCounter = 0

	for errCounter < maxErrors {
		if Debug {
			log.Println("send announcement -> ", remote)
		}
		_, _, err = d.conn.WriteMsgUDP(buf, nil, remote)
		if err != nil {
			log.Println("discover/write: warning:", err)
			errCounter++
		} else {
			errCounter = 0
		}
		time.Sleep(d.ExtBroadcastIntv)
	}
	log.Printf("discover/write: %v: stopping due to too many errors: %v", remote, err)
}

func (d *Discoverer) recvAnnouncements() {
	var buf = make([]byte, 1024)
	var errCounter = 0
	var err error
	for errCounter < maxErrors {
		n, addr, err := d.conn.ReadFromUDP(buf)
		if err != nil {
			errCounter++
			time.Sleep(time.Second)
			continue
		}

		if Debug {
			log.Printf("read announcement:\n%s", hex.Dump(buf[:n]))
		}

		var pkt AnnounceV2
		err = pkt.UnmarshalXDR(buf[:n])
		if err != nil {
			errCounter++
			time.Sleep(time.Second)
			continue
		}

		if Debug {
			log.Printf("read announcement: %#v", pkt)
		}

		errCounter = 0

		if pkt.NodeID != d.MyID {
			var addrs []string
			for _, a := range pkt.Addresses {
				var nodeAddr string
				if len(a.IP) > 0 {
					nodeAddr = fmt.Sprintf("%s:%d", ipStr(a.IP), a.Port)
				} else {
					nodeAddr = fmt.Sprintf("%s:%d", addr.IP.String(), a.Port)
				}
				addrs = append(addrs, nodeAddr)
			}
			if Debug {
				log.Printf("register: %#v", addrs)
			}
			d.registryLock.Lock()
			_, seen := d.registry[pkt.NodeID]
			if !seen {
				select {
				case d.forcedBroadcastTick <- time.Now():
				}
			}
			d.registry[pkt.NodeID] = addrs
			d.registryLock.Unlock()
		}
	}
	log.Println("discover/read: stopping due to too many errors:", err)
}

func (d *Discoverer) externalLookup(node string) []string {
	extIP, err := net.ResolveUDPAddr("udp", d.extServer)
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}

	conn, err := net.DialUDP("udp", nil, extIP)
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}
	defer conn.Close()

	err = conn.SetDeadline(time.Now().Add(5 * time.Second))
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}

	buf := QueryV2{QueryMagicV2, node}.MarshalXDR()
	_, err = conn.Write(buf)
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}
	buffers.Put(buf)

	buf = buffers.Get(256)
	defer buffers.Put(buf)

	n, err := conn.Read(buf)
	if err != nil {
		if err, ok := err.(net.Error); ok && err.Timeout() {
			// Expected if the server doesn't know about requested node ID
			return nil
		}
		log.Printf("discover/external/read: %v; no external lookup", err)
		return nil
	}

	if Debug {
		log.Printf("read external:\n%s", hex.Dump(buf[:n]))
	}

	var pkt AnnounceV2
	err = pkt.UnmarshalXDR(buf[:n])
	if err != nil {
		log.Println("discover/external/decode:", err)
		return nil
	}

	if Debug {
		log.Printf("read external: %#v", pkt)
	}

	var addrs []string
	for _, a := range pkt.Addresses {
		var nodeAddr string
		if len(a.IP) > 0 {
			nodeAddr = fmt.Sprintf("%s:%d", ipStr(a.IP), a.Port)
		}
		addrs = append(addrs, nodeAddr)
	}
	return addrs
}

func (d *Discoverer) Lookup(node string) []string {
	d.registryLock.Lock()
	addr, ok := d.registry[node]
	d.registryLock.Unlock()

	if ok {
		return addr
	} else if len(d.extServer) != 0 {
		// We might want to cache this, but not permanently so it needs some intelligence
		return d.externalLookup(node)
	}
	return nil
}

func ipStr(ip []byte) string {
	var f = "%d"
	var s = "."
	if len(ip) > 4 {
		f = "%x"
		s = ":"
	}
	var ss = make([]string, len(ip))
	for i := range ip {
		ss[i] = fmt.Sprintf(f, ip[i])
	}
	return strings.Join(ss, s)
}

func allBroadcasts() []string {
	var bcasts = make(map[string]bool)
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil
	}

	for _, addr := range addrs {
		switch {
		case strings.HasPrefix(addr.String(), "127."):
			// Ignore v4 localhost

		case strings.Contains(addr.String(), ":"):
			// Ignore all v6, because we need link local multicast there which I haven't implemented

		default:
			if in, ok := addr.(*net.IPNet); ok {
				il := len(in.IP) - 1
				ml := len(in.Mask) - 1
				for i := range in.Mask {
					in.IP[il-i] = in.IP[il-i] | ^in.Mask[ml-i]
				}
				parts := strings.Split(in.String(), "/")
				bcasts[parts[0]] = true
			}
		}
	}

	var l []string
	for ip := range bcasts {
		l = append(l, ip)
	}
	return l
}
<commit_msg>Use undirected broadcast and WriteTo (fixes #75)<commit_after>package discover

import (
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/calmh/syncthing/buffers"
)

const (
	AnnouncementPort = 21025
	Debug            = false
)

type Discoverer struct {
	MyID             string
	ListenPort       int
	BroadcastIntv    time.Duration
	ExtBroadcastIntv time.Duration

	conn         *net.UDPConn
	registry     map[string][]string
	registryLock sync.RWMutex
	extServer    string

	localBroadcastTick  <-chan time.Time
	forcedBroadcastTick chan time.Time
}

var (
	ErrIncorrectMagic = errors.New("incorrect magic number")
)

// We tolerate a certain amount of errors because we might be running on
// laptops that sleep and wake, have intermittent network connectivity, etc.
// When we hit this many errors in succession, we stop.
const maxErrors = 30

func NewDiscoverer(id string, port int, extServer string) (*Discoverer, error) {
	local := &net.UDPAddr{IP: nil, Port: AnnouncementPort}
	conn, err := net.ListenUDP("udp", local)
	if err != nil {
		return nil, err
	}

	disc := &Discoverer{
		MyID:             id,
		ListenPort:       port,
		BroadcastIntv:    30 * time.Second,
		ExtBroadcastIntv: 1800 * time.Second,

		conn:      conn,
		registry:  make(map[string][]string),
		extServer: extServer,
	}

	go disc.recvAnnouncements()

	if disc.ListenPort > 0 {
		disc.localBroadcastTick = time.Tick(disc.BroadcastIntv)
		disc.forcedBroadcastTick = make(chan time.Time)
		go disc.sendAnnouncements()
	}
	if len(disc.extServer) > 0 {
		go disc.sendExtAnnouncements()
	}

	return disc, nil
}

func (d *Discoverer) sendAnnouncements() {
	var pkt = AnnounceV2{AnnouncementMagicV2, d.MyID, []Address{{nil, 22000}}}
	var buf = pkt.MarshalXDR()
	var errCounter = 0
	var err error

	remote := &net.UDPAddr{
		IP:   net.IP{255, 255, 255, 255},
		Port: AnnouncementPort,
	}

	for errCounter < maxErrors {
		intfs, err := net.Interfaces()
		if err != nil {
			log.Printf("discover/listInterfaces: %v; no local announcements", err)
			return
		}

		for _, intf := range intfs {
			if intf.Flags&(net.FlagBroadcast|net.FlagLoopback) == net.FlagBroadcast {
				addrs, err := intf.Addrs()
				if err != nil {
					log.Println("discover/listAddrs: warning:", err)
					errCounter++
					continue
				}

				var srcAddr string
				for _, addr := range addrs {
					if strings.Contains(addr.String(), ".") {
						// Found an IPv4 adress
						parts := strings.Split(addr.String(), "/")
						srcAddr = parts[0]
						break
					}
				}
				if len(srcAddr) == 0 {
					if Debug {
						log.Println("discover: debug: no source address found on interface", intf.Name)
					}
					continue
				}

				iaddr, err := net.ResolveUDPAddr("udp4", srcAddr+":0")
				if err != nil {
					log.Println("discover/resolve: warning:", err)
					errCounter++
					continue
				}

				conn, err := net.ListenUDP("udp4", iaddr)
				if err != nil {
					log.Println("discover/listen: warning:", err)
					errCounter++
					continue
				}

				if Debug {
					log.Println("discover: debug: send announcement from", conn.LocalAddr(), "to", remote, "on", intf.Name)
				}

				_, err = conn.WriteTo(buf, remote)
				if err != nil {
					// Some interfaces don't seem to support broadcast even though the flags claims they do, i.e. vmnet
					conn.Close()

					if Debug {
						log.Println("discover/write: debug:", err)
					}

					errCounter++
					continue
				}

				conn.Close()
				errCounter = 0
			}
		}

		select {
		case <-d.localBroadcastTick:
		case <-d.forcedBroadcastTick:
		}
	}
	log.Println("discover/write: local: stopping due to too many errors:", err)
}

func (d *Discoverer) sendExtAnnouncements() {
	remote, err := net.ResolveUDPAddr("udp", d.extServer)
	if err != nil {
		log.Printf("discover/external: %v; no external announcements", err)
		return
	}

	var pkt = AnnounceV2{AnnouncementMagicV2, d.MyID, []Address{{nil, 22000}}}
	var buf = pkt.MarshalXDR()
	var errCounter = 0

	for errCounter < maxErrors {
		if Debug {
			log.Println("send announcement -> ", remote)
		}
		_, err = d.conn.WriteTo(buf, remote)
		if err != nil {
			log.Println("discover/write: warning:", err)
			errCounter++
		} else {
			errCounter = 0
		}
		time.Sleep(d.ExtBroadcastIntv)
	}
	log.Printf("discover/write: %v: stopping due to too many errors: %v", remote, err)
}

func (d *Discoverer) recvAnnouncements() {
	var buf = make([]byte, 1024)
	var errCounter = 0
	var err error
	for errCounter < maxErrors {
		n, addr, err := d.conn.ReadFromUDP(buf)
		if err != nil {
			errCounter++
			time.Sleep(time.Second)
			continue
		}

		if Debug {
			log.Printf("read announcement:\n%s", hex.Dump(buf[:n]))
		}

		var pkt AnnounceV2
		err = pkt.UnmarshalXDR(buf[:n])
		if err != nil {
			errCounter++
			time.Sleep(time.Second)
			continue
		}

		if Debug {
			log.Printf("read announcement: %#v", pkt)
		}

		errCounter = 0

		if pkt.NodeID != d.MyID {
			var addrs []string
			for _, a := range pkt.Addresses {
				var nodeAddr string
				if len(a.IP) > 0 {
					nodeAddr = fmt.Sprintf("%s:%d", ipStr(a.IP), a.Port)
				} else {
					nodeAddr = fmt.Sprintf("%s:%d", addr.IP.String(), a.Port)
				}
				addrs = append(addrs, nodeAddr)
			}
			if Debug {
				log.Printf("register: %#v", addrs)
			}
			d.registryLock.Lock()
			_, seen := d.registry[pkt.NodeID]
			if !seen {
				select {
				case d.forcedBroadcastTick <- time.Now():
				}
			}
			d.registry[pkt.NodeID] = addrs
			d.registryLock.Unlock()
		}
	}
	log.Println("discover/read: stopping due to too many errors:", err)
}

func (d *Discoverer) externalLookup(node string) []string {
	extIP, err := net.ResolveUDPAddr("udp", d.extServer)
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}

	conn, err := net.DialUDP("udp", nil, extIP)
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}
	defer conn.Close()

	err = conn.SetDeadline(time.Now().Add(5 * time.Second))
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}

	buf := QueryV2{QueryMagicV2, node}.MarshalXDR()
	_, err = conn.Write(buf)
	if err != nil {
		log.Printf("discover/external: %v; no external lookup", err)
		return nil
	}
	buffers.Put(buf)

	buf = buffers.Get(256)
	defer buffers.Put(buf)

	n, err := conn.Read(buf)
	if err != nil {
		if err, ok := err.(net.Error); ok && err.Timeout() {
			// Expected if the server doesn't know about requested node ID
			return nil
		}
		log.Printf("discover/external/read: %v; no external lookup", err)
		return nil
	}

	if Debug {
		log.Printf("read external:\n%s", hex.Dump(buf[:n]))
	}

	var pkt AnnounceV2
	err = pkt.UnmarshalXDR(buf[:n])
	if err != nil {
		log.Println("discover/external/decode:", err)
		return nil
	}

	if Debug {
		log.Printf("read external: %#v", pkt)
	}

	var addrs []string
	for _, a := range pkt.Addresses {
		var nodeAddr string
		if len(a.IP) > 0 {
			nodeAddr = fmt.Sprintf("%s:%d", ipStr(a.IP), a.Port)
		}
		addrs = append(addrs, nodeAddr)
	}
	return addrs
}

func (d *Discoverer) Lookup(node string) []string {
	d.registryLock.Lock()
	addr, ok := d.registry[node]
	d.registryLock.Unlock()

	if ok {
		return addr
	} else if len(d.extServer) != 0 {
		// We might want to cache this, but not permanently so it needs some intelligence
		return d.externalLookup(node)
	}
	return nil
}

func ipStr(ip []byte) string {
	var f = "%d"
	var s = "."
	if len(ip) > 4 {
		f = "%x"
		s = ":"
	}
	var ss = make([]string, len(ip))
	for i := range ip {
		ss[i] = fmt.Sprintf(f, ip[i])
	}
	return strings.Join(ss, s)
}
<|endoftext|>


<commit_before>package convey

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"path"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"github.com/smartystreets/goconvey/convey/reporting"
)

func TestSingleScopeReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(1, ShouldEqual, 1)
	})

	expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory())
}

func TestNestedScopeReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("B", func() {
			So(1, ShouldEqual, 1)
		})
	})

	expectEqual(t, "Begin|A|B|Success|Exit|Exit|End", myReporter.wholeStory())
}

func TestFailureReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(1, ShouldBeNil)
	})

	expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory())
}

func TestFirstFailureEndsScopeExecution(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(1, ShouldBeNil)
		So(nil, ShouldBeNil)
	})

	expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory())
}

func TestComparisonFailureDeserializedAndReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So("hi", ShouldEqual, "bye")
	})

	expectEqual(t, "Begin|A|Failure(bye/hi)|Exit|End", myReporter.wholeStory())
}

func TestNestedFailureReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("B", func() {
			So(2, ShouldBeNil)
		})
	})

	expectEqual(t, "Begin|A|B|Failure|Exit|Exit|End", myReporter.wholeStory())
}

func TestSuccessAndFailureReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(nil, ShouldBeNil)
		So(1, ShouldBeNil)
	})

	expectEqual(t, "Begin|A|Success|Failure|Exit|End", myReporter.wholeStory())
}

func TestIncompleteActionReportedAsSkipped(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("B", nil)
	})

	expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory())
}

func TestSkippedConveyReportedAsSkipped(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		SkipConvey("B", func() {
			So(1, ShouldEqual, 1)
		})
	})

	expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory())
}

func TestMultipleSkipsAreReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("0", func() {
			So(nil, ShouldBeNil)
		})

		SkipConvey("1", func() {})
		SkipConvey("2", func() {})

		Convey("3", nil)
		Convey("4", nil)

		Convey("5", func() {
			So(nil, ShouldBeNil)
		})
	})

	expected := "Begin" +
		"|A|0|Success|Exit|Exit" +
		"|A|1|Skipped|Exit|Exit" +
		"|A|2|Skipped|Exit|Exit" +
		"|A|3|Skipped|Exit|Exit" +
		"|A|4|Skipped|Exit|Exit" +
		"|A|5|Success|Exit|Exit" +
		"|End"

	expectEqual(t, expected, myReporter.wholeStory())
}

func TestSkippedAssertionIsNotReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		SkipSo(1, ShouldEqual, 1)
	})

	expectEqual(t, "Begin|A|Skipped|Exit|End", myReporter.wholeStory())
}

func TestMultipleSkippedAssertionsAreNotReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		SkipSo(1, ShouldEqual, 1)
		So(1, ShouldEqual, 1)
		SkipSo(1, ShouldEqual, 1)
	})

	expectEqual(t, "Begin|A|Skipped|Success|Skipped|Exit|End", myReporter.wholeStory())
}

func TestErrorByManualPanicReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		panic("Gopher alert!")
	})

	expectEqual(t, "Begin|A|Error|Exit|End", myReporter.wholeStory())
}

func TestIterativeConveysReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		for x := 0; x < 3; x++ {
			Convey(strconv.Itoa(x), func() {
				So(x, ShouldEqual, x)
			})
		}
	})

	expectEqual(t, "Begin|A|0|Success|Exit|Exit|A|1|Success|Exit|Exit|A|2|Success|Exit|Exit|End", myReporter.wholeStory())
}

func SkipTestEmbeddedAssertionReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			So(r.FormValue("msg"), ShouldEqual, "ping")
		}))
		http.DefaultClient.Get(ts.URL + "?msg=ping")
	})

	expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory())
}

func expectEqual(t *testing.T, expected interface{}, actual interface{}) {
	if expected != actual {
		_, file, line, _ := runtime.Caller(1)
		t.Errorf("Expected '%v' to be '%v' but it wasn't. See '%s' at line %d.",
			actual, expected, path.Base(file), line)
	}
}

func setupFakeReporter() (*fakeReporter, *fakeGoTest) {
	myReporter := new(fakeReporter)
	myReporter.calls = []string{}
	testReporter = myReporter
	return myReporter, new(fakeGoTest)
}

type fakeReporter struct {
	calls []string
}

func (self *fakeReporter) BeginStory(story *reporting.StoryReport) {
	self.calls = append(self.calls, "Begin")
}

func (self *fakeReporter) Enter(scope *reporting.ScopeReport) {
	self.calls = append(self.calls, scope.Title)
}

func (self *fakeReporter) Report(report *reporting.AssertionResult) {
	if report.Error != nil {
		self.calls = append(self.calls, "Error")
	} else if report.Failure != "" {
		message := "Failure"
		if report.Expected != "" || report.Actual != "" {
			message += fmt.Sprintf("(%s/%s)", report.Expected, report.Actual)
		}
		self.calls = append(self.calls, message)
	} else if report.Skipped {
		self.calls = append(self.calls, "Skipped")
	} else {
		self.calls = append(self.calls, "Success")
	}
}

func (self *fakeReporter) Exit() {
	self.calls = append(self.calls, "Exit")
}

func (self *fakeReporter) EndStory() {
	self.calls = append(self.calls, "End")
}

func (self *fakeReporter) Write(content []byte) (int, error) {
	return len(content), nil // no-op
}

func (self *fakeReporter) wholeStory() string {
	return strings.Join(self.calls, "|")
}

////////////////////////////////

type fakeGoTest struct{}

func (self *fakeGoTest) Fail()                                     {}
func (self *fakeGoTest) Fatalf(format string, args ...interface{}) {}

var test t = new(fakeGoTest)
<commit_msg>Add test for nested iterative convey reports<commit_after>package convey

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"path"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"github.com/smartystreets/goconvey/convey/reporting"
)

func TestSingleScopeReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(1, ShouldEqual, 1)
	})

	expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory())
}

func TestNestedScopeReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("B", func() {
			So(1, ShouldEqual, 1)
		})
	})

	expectEqual(t, "Begin|A|B|Success|Exit|Exit|End", myReporter.wholeStory())
}

func TestFailureReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(1, ShouldBeNil)
	})

	expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory())
}

func TestFirstFailureEndsScopeExecution(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(1, ShouldBeNil)
		So(nil, ShouldBeNil)
	})

	expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory())
}

func TestComparisonFailureDeserializedAndReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So("hi", ShouldEqual, "bye")
	})

	expectEqual(t, "Begin|A|Failure(bye/hi)|Exit|End", myReporter.wholeStory())
}

func TestNestedFailureReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("B", func() {
			So(2, ShouldBeNil)
		})
	})

	expectEqual(t, "Begin|A|B|Failure|Exit|Exit|End", myReporter.wholeStory())
}

func TestSuccessAndFailureReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		So(nil, ShouldBeNil)
		So(1, ShouldBeNil)
	})

	expectEqual(t, "Begin|A|Success|Failure|Exit|End", myReporter.wholeStory())
}

func TestIncompleteActionReportedAsSkipped(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("B", nil)
	})

	expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory())
}

func TestSkippedConveyReportedAsSkipped(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		SkipConvey("B", func() {
			So(1, ShouldEqual, 1)
		})
	})

	expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory())
}

func TestMultipleSkipsAreReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		Convey("0", func() {
			So(nil, ShouldBeNil)
		})

		SkipConvey("1", func() {})
		SkipConvey("2", func() {})

		Convey("3", nil)
		Convey("4", nil)

		Convey("5", func() {
			So(nil, ShouldBeNil)
		})
	})

	expected := "Begin" +
		"|A|0|Success|Exit|Exit" +
		"|A|1|Skipped|Exit|Exit" +
		"|A|2|Skipped|Exit|Exit" +
		"|A|3|Skipped|Exit|Exit" +
		"|A|4|Skipped|Exit|Exit" +
		"|A|5|Success|Exit|Exit" +
		"|End"

	expectEqual(t, expected, myReporter.wholeStory())
}

func TestSkippedAssertionIsNotReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		SkipSo(1, ShouldEqual, 1)
	})

	expectEqual(t, "Begin|A|Skipped|Exit|End", myReporter.wholeStory())
}

func TestMultipleSkippedAssertionsAreNotReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		SkipSo(1, ShouldEqual, 1)
		So(1, ShouldEqual, 1)
		SkipSo(1, ShouldEqual, 1)
	})

	expectEqual(t, "Begin|A|Skipped|Success|Skipped|Exit|End", myReporter.wholeStory())
}

func TestErrorByManualPanicReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		panic("Gopher alert!")
	})

	expectEqual(t, "Begin|A|Error|Exit|End", myReporter.wholeStory())
}

func TestIterativeConveysReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		for x := 0; x < 3; x++ {
			Convey(strconv.Itoa(x), func() {
				So(x, ShouldEqual, x)
			})
		}
	})

	expectEqual(t, "Begin|A|0|Success|Exit|Exit|A|1|Success|Exit|Exit|A|2|Success|Exit|Exit|End", myReporter.wholeStory())
}

func TestNestedIterativeConveysReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		for x := 0; x < 3; x++ {
			Convey(strconv.Itoa(x), func() {
				for y := 0; y < 3; y++ {
					Convey("< "+strconv.Itoa(y), func() {
						So(x, ShouldBeLessThan, y)
					})
				}
			})
		}
	})

	expectEqual(t, ("Begin|" +
		"A|0|< 0|Failure|Exit|Exit|Exit|" +
		"A|0|< 1|Success|Exit|Exit|Exit|" +
		"A|0|< 2|Success|Exit|Exit|Exit|" +
		"A|1|< 0|Failure|Exit|Exit|Exit|" +
		"A|1|< 1|Failure|Exit|Exit|Exit|" +
		"A|1|< 2|Success|Exit|Exit|Exit|" +
		"A|2|< 0|Failure|Exit|Exit|Exit|" +
		"A|2|< 1|Failure|Exit|Exit|Exit|" +
		"A|2|< 2|Failure|Exit|Exit|Exit|" +
		"End"), myReporter.wholeStory())
}

func SkipTestEmbeddedAssertionReported(t *testing.T) {
	myReporter, test := setupFakeReporter()

	Convey("A", test, func() {
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			So(r.FormValue("msg"), ShouldEqual, "ping")
		}))
		http.DefaultClient.Get(ts.URL + "?msg=ping")
	})

	expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory())
}

func expectEqual(t *testing.T, expected interface{}, actual interface{}) {
	if expected != actual {
		_, file, line, _ := runtime.Caller(1)
		t.Errorf("Expected '%v' to be '%v' but it wasn't. See '%s' at line %d.",
			actual, expected, path.Base(file), line)
	}
}

func setupFakeReporter() (*fakeReporter, *fakeGoTest) {
	myReporter := new(fakeReporter)
	myReporter.calls = []string{}
	testReporter = myReporter
	return myReporter, new(fakeGoTest)
}

type fakeReporter struct {
	calls []string
}

func (self *fakeReporter) BeginStory(story *reporting.StoryReport) {
	self.calls = append(self.calls, "Begin")
}

func (self *fakeReporter) Enter(scope *reporting.ScopeReport) {
	self.calls = append(self.calls, scope.Title)
}

func (self *fakeReporter) Report(report *reporting.AssertionResult) {
	if report.Error != nil {
		self.calls = append(self.calls, "Error")
	} else if report.Failure != "" {
		message := "Failure"
		if report.Expected != "" || report.Actual != "" {
			message += fmt.Sprintf("(%s/%s)", report.Expected, report.Actual)
		}
		self.calls = append(self.calls, message)
	} else if report.Skipped {
		self.calls = append(self.calls, "Skipped")
	} else {
		self.calls = append(self.calls, "Success")
	}
}

func (self *fakeReporter) Exit() {
	self.calls = append(self.calls, "Exit")
}

func (self *fakeReporter) EndStory() {
	self.calls = append(self.calls, "End")
}

func (self *fakeReporter) Write(content []byte) (int, error) {
	return len(content), nil // no-op
}

func (self *fakeReporter) wholeStory() string {
	return strings.Join(self.calls, "|")
}

////////////////////////////////

type fakeGoTest struct{}

func (self *fakeGoTest) Fail()                                     {}
func (self *fakeGoTest) Fatalf(format string, args ...interface{}) {}

var test t = new(fakeGoTest)
<|endoftext|>


<commit_before>package boltdb

import (
	"io"

	"bytes"

	"fmt"

	"github.com/boltdb/bolt"
	"github.com/fader2/platform/consts"
	"github.com/fader2/platform/objects"
	uuid "github.com/satori/go.uuid"
)

var _ objects.Storer = (*BoltdbStorage)(nil)

func NewBlobStorage(db *bolt.DB, name string) (s *BoltdbStorage) {
	s = &BoltdbStorage{db, name}
	return
}

type BoltdbStorage struct {
	db     *bolt.DB
	bucket string
}

func (o *BoltdbStorage) NewEncodedObject(id uuid.UUID) objects.EncodedObject {
	return objects.NewObject(id)
}

func (s *BoltdbStorage) EncodedObject(_type objects.ObjectType, id uuid.UUID) (objects.EncodedObject, error) {
	var buf = new(bytes.Buffer)
	err := s.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(s.bucket))
		_, err := buf.Write(b.Get(id.Bytes()))
		return err
	})
	if err != nil {
		return nil, err
	}
	if buf.Len() == 0 {
		return nil, consts.ErrNotFound
	}
	or := objects.NewReader(buf)
	defer or.Close()
	gotType, gotMeta, err := or.Header()
	if err != nil {
		return nil, fmt.Errorf("read header %s", err)
	}

	if gotType != _type {
		return nil, fmt.Errorf("not expected obj type, got type %s", gotType)
	}

	obj := s.NewEncodedObject(id)
	obj.SetType(gotType)
	obj.SetMeta(gotMeta)
	obj.SetSize(int64(buf.Len()))
	w, err := obj.Writer()
	if err != nil {
		return nil, fmt.Errorf("get object writer %s", err)
	}
	defer w.Close()

	io.Copy(w, or)

	return obj, nil
}

func (s *BoltdbStorage) SetEncodedObject(obj objects.EncodedObject) (
	id uuid.UUID,
	err error,
) {
	id = obj.ID()
	buf := new(bytes.Buffer)
	ow := objects.NewWriter(buf)
	if err := ow.WriteHeader(obj.Type(), obj.Meta()); err != nil {
		return id, fmt.Errorf("write header %s", err)
	}

	r, _ := obj.Reader()
	io.Copy(ow, r)

	err = s.db.Update(func(tx *bolt.Tx) error {
		return tx.Bucket([]byte(s.bucket)).Put(id.Bytes(), buf.Bytes())
	})

	return
}
<commit_msg>fixed test<commit_after>package boltdb

import (
	"io"

	"bytes"

	"fmt"

	"github.com/boltdb/bolt"
	"github.com/fader2/platform/consts"
	"github.com/fader2/platform/objects"
	uuid "github.com/satori/go.uuid"
)

var _ objects.Storer = (*BoltdbStorage)(nil)

func NewBlobStorage(db *bolt.DB, name string) (s *BoltdbStorage) {
	s = &BoltdbStorage{db, name}
	return
}

type BoltdbStorage struct {
	db     *bolt.DB
	bucket string
}

func (o *BoltdbStorage) NewEncodedObject(id uuid.UUID) objects.EncodedObject {
	return objects.NewObject(id)
}

func (s *BoltdbStorage) EncodedObject(_type objects.ObjectType, id uuid.UUID) (objects.EncodedObject, error) {
	var buf = new(bytes.Buffer)
	err := s.db.View(func(tx *bolt.Tx) (err error) {
	again:
		b := tx.Bucket([]byte(s.bucket))
		if b == nil {
			done := s.createBucket([]byte(s.bucket))
			<-done
			goto again
		}
		_, err = buf.Write(b.Get(id.Bytes()))
		return
	})
	if err != nil {
		return nil, err
	}
	if buf.Len() == 0 {
		return nil, consts.ErrNotFound
	}
	or := objects.NewReader(buf)
	defer or.Close()
	gotType, gotMeta, err := or.Header()
	if err != nil {
		return nil, fmt.Errorf("read header %s", err)
	}

	if gotType != _type {
		return nil, fmt.Errorf("not expected obj type, got type %s", gotType)
	}

	obj := s.NewEncodedObject(id)
	obj.SetType(gotType)
	obj.SetMeta(gotMeta)
	obj.SetSize(int64(buf.Len()))
	w, err := obj.Writer()
	if err != nil {
		return nil, fmt.Errorf("get object writer %s", err)
	}
	defer w.Close()

	io.Copy(w, or)

	return obj, nil
}

func (s *BoltdbStorage) SetEncodedObject(obj objects.EncodedObject) (
	id uuid.UUID,
	err error,
) {
	id = obj.ID()
	buf := new(bytes.Buffer)
	ow := objects.NewWriter(buf)
	if err := ow.WriteHeader(obj.Type(), obj.Meta()); err != nil {
		return id, fmt.Errorf("write header %s", err)
	}

	r, _ := obj.Reader()
	io.Copy(ow, r)

	err = s.db.Update(func(tx *bolt.Tx) error {
	again:
		b := tx.Bucket([]byte(s.bucket))
		if b == nil {
			done := s.createBucket([]byte(s.bucket))
			<-done
			goto again
		}
		return b.Put(id.Bytes(), buf.Bytes())
	})

	return
}

func (s *BoltdbStorage) createBucket(name []byte) (done chan struct{}) {
	done = make(chan struct{}, 1)
	go s.db.Update(func(tx *bolt.Tx) (err error) {
		_, err = tx.CreateBucketIfNotExists(name)
		done <- struct{}{}
		return
	})
	return done
}
<|endoftext|>


<commit_before>package company

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"sync"
)

// Workspace ...
type Workspace struct {
	Accuracy    int     `json:"accuracy"`
	Result      string  `json:"result"`
	Code        int     `json:"code"`
	Description float64 `json:"Description"`
}

// Scientist ...
type Scientist struct {
	ID              int       `json:"id"`
	Name            string    `json:"name"`
	Rating          int       `json:"rating"`
	Counterproposal float64   `json:"counterproposal,omitempty"`
	Workspace       Workspace `json:"workspace"`
}

// Job ...
type Job struct {
	ID               int          `json:"id"`
	Title            string       `json:"title"`
	Description      string       `json:"description"`
	Proposed         float64      `json:"proposed"`
	AccuracyRequired float64      `json:"accuracyRequired"`
	Deadline         string       `json:"deadline"`
	Status           string       `json:"status"`
	LastUpdate       string       `json:"lastUpdate"`
	Candidates       []*Scientist `json:"candidates,omitempty"`
	Scientists       []*Scientist `json:"scientists,omitempty"`
}

// Company ...
type Company struct {
	mutex *sync.RWMutex
	jobs  []*Job
}

// NewCompany ...
func NewCompany() *Company {
	jobs := loadJobs()
	mutex := &sync.RWMutex{}

	return &Company{
		jobs:  jobs,
		mutex: mutex,
	}
}

func loadJobs() []*Job {
	raw, err := ioutil.ReadFile("./db/company/jobs.json")
	if err != nil {
		return nil
	}

	var jobs []*Job
	if err := json.Unmarshal(raw, &jobs); err != nil {
		return nil
	}
	return jobs
}

func saveJobs(jobs []*Job) {
	buffer, err := json.Marshal(jobs)
	if err != nil {
		return
	}

	err = ioutil.WriteFile("./db/company/jobs.json", buffer, 0644)
	if err != nil {
		return
	}
}

// GetJobsByStatus ...
func (c *Company) GetJobsByStatus(status string) []*Job {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	jobs := make([]*Job, 0, len(c.jobs))

	for _, job := range c.jobs {
		if job.Status == status {
			jobs = append(jobs, job)
		}
	}

	return jobs
}

// AddNewJob ...
func (c *Company) AddNewJob(job *Job) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	job.ID = len(c.jobs)
	job.Status = "pending"

	c.jobs = append(c.jobs, job)

	saveJobs(c.jobs)
}

// GetJob ...
func (c *Company) GetJob(id int) *Job {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	if id >= len(c.jobs) {
		return nil
	}

	return c.jobs[id]
}

// StartJob ...
func (c *Company) StartJob(id int, scientists []*Scientist) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if id >= len(c.jobs) {
		return fmt.Errorf("Job %d not found", id)
	}

	job := c.jobs[id]

	job.Scientists = make([]*Scientist, 0, len(scientists))

	for _, scientist := range scientists {
		for _, candidate := range job.Candidates {
			if candidate.ID == scientist.ID {
				//candidate.Workspace = Workspace{}
				job.Scientists = append(job.Scientists, candidate)
				break
			}
		}
	}

	job.Candidates = nil
	job.Status = "doing"

	saveJobs(c.jobs)
	return nil
}

// ApplyScientist ...
func (c *Company) ApplyScientist(id int, candidate *Scientist) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if id >= len(c.jobs) {
		return fmt.Errorf("Job %d not found", id)
	}

	job := c.jobs[id]
	job.Candidates = append(job.Candidates, candidate)

	saveJobs(c.jobs)
	return nil
}
<commit_msg>Fixed json<commit_after>package company

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"sync"
)

// Workspace ...
type Workspace struct {
	Accuracy    int     `json:"accuracy"`
	Result      string  `json:"result"`
	Code        int     `json:"code"`
	Description float64 `json:"description"`
}

// Scientist ...
type Scientist struct {
	ID              int       `json:"id"`
	Name            string    `json:"name"`
	Rating          int       `json:"rating"`
	Counterproposal float64   `json:"counterproposal,omitempty"`
	Workspace       Workspace `json:"workspace"`
}

// Job ...
type Job struct {
	ID               int          `json:"id"`
	Title            string       `json:"title"`
	Description      string       `json:"description"`
	Proposed         float64      `json:"proposed"`
	AccuracyRequired float64      `json:"accuracyRequired"`
	Deadline         string       `json:"deadline"`
	Status           string       `json:"status"`
	LastUpdate       string       `json:"lastUpdate"`
	Candidates       []*Scientist `json:"candidates,omitempty"`
	Scientists       []*Scientist `json:"scientists,omitempty"`
}

// Company ...
type Company struct {
	mutex *sync.RWMutex
	jobs  []*Job
}

// NewCompany ...
func NewCompany() *Company {
	jobs := loadJobs()
	mutex := &sync.RWMutex{}

	return &Company{
		jobs:  jobs,
		mutex: mutex,
	}
}

func loadJobs() []*Job {
	raw, err := ioutil.ReadFile("./db/company/jobs.json")
	if err != nil {
		return nil
	}

	var jobs []*Job
	if err := json.Unmarshal(raw, &jobs); err != nil {
		return nil
	}
	return jobs
}

func saveJobs(jobs []*Job) {
	buffer, err := json.Marshal(jobs)
	if err != nil {
		return
	}

	err = ioutil.WriteFile("./db/company/jobs.json", buffer, 0644)
	if err != nil {
		return
	}
}

// GetJobsByStatus ...
func (c *Company) GetJobsByStatus(status string) []*Job {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	jobs := make([]*Job, 0, len(c.jobs))

	for _, job := range c.jobs {
		if job.Status == status {
			jobs = append(jobs, job)
		}
	}

	return jobs
}

// AddNewJob ...
func (c *Company) AddNewJob(job *Job) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	job.ID = len(c.jobs)
	job.Status = "pending"

	c.jobs = append(c.jobs, job)

	saveJobs(c.jobs)
}

// GetJob ...
func (c *Company) GetJob(id int) *Job {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	if id >= len(c.jobs) {
		return nil
	}

	return c.jobs[id]
}

// StartJob ...
func (c *Company) StartJob(id int, scientists []*Scientist) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if id >= len(c.jobs) {
		return fmt.Errorf("Job %d not found", id)
	}

	job := c.jobs[id]

	job.Scientists = make([]*Scientist, 0, len(scientists))

	for _, scientist := range scientists {
		for _, candidate := range job.Candidates {
			if candidate.ID == scientist.ID {
				//candidate.Workspace = Workspace{}
				job.Scientists = append(job.Scientists, candidate)
				break
			}
		}
	}

	job.Candidates = nil
	job.Status = "doing"

	saveJobs(c.jobs)
	return nil
}

// ApplyScientist ...
func (c *Company) ApplyScientist(id int, candidate *Scientist) error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if id >= len(c.jobs) {
		return fmt.Errorf("Job %d not found", id)
	}

	job := c.jobs[id]
	job.Candidates = append(job.Candidates, candidate)

	saveJobs(c.jobs)
	return nil
}
<|endoftext|>