<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"io"
	"math/rand"
	"os"

	_ "image/png"
)

func recompress(in io.Reader, out io.Writer, quality int) error {
	img, _, err := image.Decode(in)
	if err != nil {
		return err
	}

	return jpeg.Encode(out, img, &jpeg.Options{Quality: quality})
}

func gifize(in io.Reader, out io.Writer) error {
	img, _, err := image.Decode(in)
	if err != nil {
		return err
	}

	return gif.Encode(out, img, nil)
}

func uglify(in io.Reader, cycles, lowerBound int) (io.Reader, error) {
	randRange := 100 - lowerBound

	var rbuf, wbuf bytes.Buffer

	_, err := io.Copy(&rbuf, in)
	if err != nil {
		return nil, err
	}

	for i := 0; i < cycles; i++ {
		fmt.Fprint(os.Stderr, ".")

		quality := lowerBound + rand.Intn(randRange)
		if err = recompress(&rbuf, &wbuf, quality); err != nil {
			return nil, err
		}
		// Prevent allocations by reseting and swapping
		rbuf.Reset()
		rbuf, wbuf = wbuf, rbuf
	}
	fmt.Fprintln(os.Stderr)
	return &rbuf, nil
}

func die(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage of shitpic:")
		fmt.Fprintln(os.Stderr, "\tshitpic [options] input output")
		flag.PrintDefaults()
	}

	cycles := flag.Uint("cycles", 100, "How many times to reprocess input")
	lowerBound := flag.Int("quality", 75, "Lower bound of quality (0–100)")
	reduceColor := flag.Bool("reduce-colors", false, "Reduce to 256 colors")

	flag.Parse()

	if flag.NArg() != 2 {
		flag.Usage()
		os.Exit(2)
	}

	if *lowerBound < 0 || *lowerBound > 100 {
		flag.Usage()
		os.Exit(2)
	}

	var inf io.Reader = os.Stdin
	if flag.Arg(0) != "-" {
		f, err := os.Open(flag.Arg(0))
		die(err)
		defer f.Close()
		inf = f
	}

	if *reduceColor {
		var buf bytes.Buffer
		die(gifize(inf, &buf))
		inf = &buf
	}

	out, err := uglify(inf, int(*cycles), *lowerBound)
	die(err)

	var outf = os.Stdout
	if flag.Arg(1) != "-" {
		f, err := os.Create(flag.Arg(1))
		die(err)
		defer f.Close()
		outf = f
	}

	_, err = io.Copy(outf, out)
	die(err)
}
<commit_msg>Handle different outputs based on file extension<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"math/rand"
	"os"
	"strings"
)

func recompress(in io.Reader, out io.Writer, quality int) error {
	img, _, err := image.Decode(in)
	if err != nil {
		return err
	}

	return jpeg.Encode(out, img, &jpeg.Options{Quality: quality})
}

func gifize(in io.Reader, out io.Writer) error {
	img, _, err := image.Decode(in)
	if err != nil {
		return err
	}

	return gif.Encode(out, img, nil)
}

func pngerate(in io.Reader, out io.Writer) error {
	img, _, err := image.Decode(in)
	if err != nil {
		return err
	}

	return png.Encode(out, img)
}

func uglify(in io.Reader, cycles, lowerBound int) (io.Reader, error) {
	randRange := 100 - lowerBound

	var rbuf, wbuf bytes.Buffer

	_, err := io.Copy(&rbuf, in)
	if err != nil {
		return nil, err
	}

	for i := 0; i < cycles; i++ {
		fmt.Fprint(os.Stderr, ".")

		quality := lowerBound + rand.Intn(randRange)
		if err = recompress(&rbuf, &wbuf, quality); err != nil {
			return nil, err
		}
		// Prevent allocations by reseting and swapping
		rbuf.Reset()
		rbuf, wbuf = wbuf, rbuf
	}
	fmt.Fprintln(os.Stderr)
	return &rbuf, nil
}

func die(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

func main() {
	flag.Usage = func() {
		usage := `Usage of shitpic:
	shitpic [options] input output

Shitpic accepts and can output JPEG, GIF, and PNG files.
`
		fmt.Fprintln(os.Stderr, usage)
		flag.PrintDefaults()
	}

	cycles := flag.Uint("cycles", 100, "How many times to reprocess input")
	lowerBound := flag.Int("quality", 75, "Lower bound of quality (0–100)")
	reduceColor := flag.Bool("reduce-colors", false, "Reduce to 256 colors")

	flag.Parse()

	if flag.NArg() != 2 {
		flag.Usage()
		os.Exit(2)
	}

	infilename := flag.Arg(0)
	outfilename := flag.Arg(1)

	if *lowerBound < 0 || *lowerBound > 100 {
		flag.Usage()
		os.Exit(2)
	}

	var r io.Reader = os.Stdin
	if infilename != "-" {
		f, err := os.Open(infilename)
		die(err)
		defer f.Close()
		r = f
	}

	if *reduceColor {
		var buf bytes.Buffer
		die(gifize(r, &buf))
		r = &buf
	}

	var err error
	r, err = uglify(r, int(*cycles), *lowerBound)
	die(err)

	if strings.HasSuffix(outfilename, ".png") {
		var buf bytes.Buffer
		die(pngerate(r, &buf))
		r = &buf
	}

	if strings.HasSuffix(outfilename, ".gif") {
		var buf bytes.Buffer
		die(gifize(r, &buf))
		r = &buf
	}

	var outf = os.Stdout
	if outfilename != "-" {
		f, err := os.Create(outfilename)
		die(err)
		defer f.Close()
		outf = f
	}

	_, err = io.Copy(outf, r)
	die(err)
}
<|endoftext|>


<commit_before>// Package xflag is an abstraction around the Go's standard "flag"
// package, INI or other configuration files, and environment variables.
package xflag

import (
	"flag"
	"os"
	"strings"

	"github.com/conveyer/xflag/cflag/types"

	"github.com/conveyer/config"
	"github.com/conveyer/config/ini"
)

// Example:
//
//	package main
//
//	import (
//		"flag"
//		"log"
//
//		"github.com/conveyer/xflag"
//	)
//
//	var sampleFlag = flag.String("test:sample", "default value", "comment here...")
//
//	func main() {
//		err := xflag.Parse("path/to/file1.ini", "path/to/file2.ini")
//		if err != nil {
//			log.Fatalf(err)
//		}
//	}

// Context represents a single instance of xflag.
// It contains available arguments and parsed configuration files.
type Context struct {
	args []string
	conf config.Interface

	separat, arrLit string
}

// New allocates and returns a new Context.
// A slice of input arguments should not include
// the command name.
func New(conf config.Interface, args []string) *Context {
	return &Context{
		args: args,
		conf: conf,
	}
}

// Files method gets a number of INI configuration files and
// parses them. An error is returned if some of the files do not exist
// or their format is not valid.
// Every subsequent file overrides conflicting values of the previous one.
func (c *Context) Files(files ...string) error {
	for i := range files {
		if err := c.conf.Join(files[i]); err != nil {
			return err
		}
	}
	return nil
}

// ParseSet parses flag definitions using the following sources:
// 1. Configuration files (that may contain Environment variables);
// 2. Command line arguments list.
// The latter has higher priority.
func (c *Context) ParseSet(fset *flag.FlagSet) error {
	// Prepare settings for processing flag names.
	// Redefinition of these values by editing the configuration
	// files is possible.
	c.separat = c.conf.Value("@xflag", "flag.name.separator").StringDefault(":")
	c.arrLit = c.conf.Value("@xflag", "flag.name.array.literal").StringDefault("[]")

	// Iterate over all available flags.
	fset.VisitAll(func(f *flag.Flag) {
		// And try to initialize them using values of configuration files.
		c.process(f)
	})

	// Override the flags that are listed in the arguments.
	return fset.Parse(c.args)
}

// Parse is an equivalent of ParseSet with flag.CommandLine
// as a flag set input parameter.
func (c *Context) Parse() error {
	return c.ParseSet(flag.CommandLine)
}

// Parse is a shorthand for the following code:
//	c := xflag.New(INIConfigParser, os.Args[1:])
//	err := c.Files(files...)
//	if err != nil {
//		...
//	}
//	err = c.Parse()
//	if err != nil {
//		...
//	}
func Parse(files ...string) error {
	// Allocate a new context using os.Args as input.
	c := New(ini.New(nil), os.Args[1:])

	// Parse requested configuration files.
	err := c.Files(files...)
	if err != nil {
		return err
	}

	// Parse the default flag set, i.e. flag.CommandLine.
	return c.Parse()
}

// process receives a flag as an input argument and processes it.
func (c *Context) process(f *flag.Flag) {
	// Split the flag name into parts.
	path, arr := c.parseFlagName(f.Name)

	// Receive an associated value.
	v := c.conf.ValuePrefixless(path...)

	// Process the flag depending on the expected type.
	switch arr {
	case true:
		// Make sure a slice can be retrieved from the configuration.
		ss, ok := v.Strings()
		if !ok {
			return
		}

		// Emulate Add behaviour calling Set multiple times.
		// NOTE: This is supported by xflag/cflag package only
		// (standard flag package doesn't allow slice flags).
		for i := range ss {
			f.Value.Set(ss[i])
		}

		// Indicate the end of input by using
		// a special EOI value.
		f.Value.Set(types.EOI)
	default:
		// By default a string value is expected, so just set it.
		if s, ok := v.String(); ok {
			f.Value.Set(s)
		}
	}
}

// parseFlagName splits a flag name into a set of fragments
// using the separator specified in the configuration.
// The second argument is true if the flag name ends with an
// array literal.
func (c *Context) parseFlagName(n string) (path []string, arr bool) {
	// Trim the array literal.
	s := strings.TrimRight(n, c.arrLit)

	// Split the name using the specified separator.
	path = strings.Split(s, c.separat)

	// Return the result.
	return path, s != n
}
<commit_msg>Make separator and arrLiteral exportable<commit_after>// Package xflag is an abstraction around the Go's standard "flag"
// package, INI or other configuration files, and environment variables.
package xflag

import (
	"flag"
	"os"
	"strings"

	"github.com/conveyer/xflag/cflag/types"

	"github.com/conveyer/config"
	"github.com/conveyer/config/ini"
)

// Example:
//
//	package main
//
//	import (
//		"flag"
//		"log"
//
//		"github.com/conveyer/xflag"
//	)
//
//	var sampleFlag = flag.String("test:sample", "default value", "comment here...")
//
//	func main() {
//		err := xflag.Parse("path/to/file1.ini", "path/to/file2.ini")
//		if err != nil {
//			log.Fatalf(err)
//		}
//	}

// Context represents a single instance of xflag.
// It contains available arguments and parsed configuration files.
type Context struct {
	args []string
	conf config.Interface

	// Separator is a string that separates different objects or
	// section from key in flag names.
	// By default ":" is used as a separator if Context is allocated
	// using the New constructor.
	// Flag name with the separator may look as "mySection:myKey".
	Separator string

	// ArrLiteral is a string that is if included at the end of a flag
	// name means that the flag must be treated as an array rather than
	// as a scalar type.
	// By default "[]" is used as an array literal if Context is allocated
	// using the New constructor.
	// Flag name with the array literal may look as "mySection:myKey[]".
	ArrLiteral string
}

// New allocates and returns a new Context.
// A slice of input arguments should not include
// the command name.
func New(conf config.Interface, args []string) *Context {
	return &Context{
		args: args,
		conf: conf,

		Separator:  ":",
		ArrLiteral: "[]",
	}
}

// Files method gets a number of INI configuration files and
// parses them. An error is returned if some of the files do not exist
// or their format is not valid.
// Every subsequent file overrides conflicting values of the previous one.
func (c *Context) Files(files ...string) error {
	for i := range files {
		if err := c.conf.Join(files[i]); err != nil {
			return err
		}
	}
	return nil
}

// ParseSet parses flag definitions using the following sources:
// 1. Configuration files (that may contain Environment variables);
// 2. Command line arguments list.
// The latter has higher priority.
func (c *Context) ParseSet(fset *flag.FlagSet) error {
	// Iterate over all available flags.
	fset.VisitAll(func(f *flag.Flag) {
		// And try to initialize them using values of configuration files.
		c.process(f)
	})

	// Override the flags that are listed in the arguments.
	return fset.Parse(c.args)
}

// Parse is an equivalent of ParseSet with flag.CommandLine
// as a flag set input parameter.
func (c *Context) Parse() error {
	return c.ParseSet(flag.CommandLine)
}

// Parse is a shorthand for the following code:
//	c := xflag.New(INIConfigParser, os.Args[1:])
//	err := c.Files(files...)
//	if err != nil {
//		...
//	}
//	err = c.Parse()
//	if err != nil {
//		...
//	}
func Parse(files ...string) error {
	// Allocate a new context using os.Args as input.
	c := New(ini.New(nil), os.Args[1:])

	// Parse requested configuration files.
	err := c.Files(files...)
	if err != nil {
		return err
	}

	// Parse the default flag set, i.e. flag.CommandLine.
	return c.Parse()
}

// process receives a flag as an input argument and processes it.
func (c *Context) process(f *flag.Flag) {
	// Split the flag name into parts.
	path, arr := c.parseFlagName(f.Name)

	// Receive a value associated with the path.
	var v config.ValueInterface
	switch len(path) > 1 {
	case true:
		// If there are many elements in the path, use the first
		// one as an object path (in terms of config.Interface).
		v = c.conf.At(path[0]).Value(path[1:]...)
	default:
		// Otherwise, use all of them, if any,
		// as an element path.
		v = c.conf.Value(path...)
	}

	// Process the flag depending on the expected type.
	switch arr {
	case true:
		// Make sure a slice can be retrieved from the configuration.
		ss, ok := v.Strings()
		if !ok {
			return
		}

		// Emulate Add behaviour calling Set multiple times.
		// NOTE: This is supported by xflag/cflag package only
		// (standard flag package doesn't allow slice flags).
		for i := range ss {
			f.Value.Set(ss[i])
		}

		// Indicate the end of input by using
		// a special EOI value.
		f.Value.Set(types.EOI)
	default:
		// By default a string value is expected, so just set it.
		if s, ok := v.String(); ok {
			f.Value.Set(s)
		}
	}
}

// parseFlagName splits a flag name into a set of fragments using the
// earlier specified separator.
// The second arr argument is true if the flag name ends with an
// array literal that was expected to be specified earlier as well.
func (c *Context) parseFlagName(n string) (path []string, arr bool) {
	// Trim the array literal.
	s := strings.TrimRight(n, c.ArrLiteral)

	// Split the name using the specified separator.
	path = strings.Split(s, c.Separator)

	// Return the result.
	return path, s != n
}
<|endoftext|>


<commit_before>//go:generate esc -o static.go -prefix=../../client/dist ../../client/dist/
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os"

	"github.com/NYTimes/gziphandler"
	"github.com/justinas/alice"
	"github.com/moocfetcher/moocfetcher-appliance/backend/lib/server"
	"github.com/urfave/cli"

	moocfetcher "github.com/moocfetcher/moocfetcher-appliance/backend/lib"
)

const courseMetadataFile = "/data/courses.json"

func addCorsHeaders(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
		}
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		if r.Method == "OPTIONS" {
			return
		}
		h.ServeHTTP(w, r)
	})
}

func main() {
	app := cli.NewApp()
	app.Name = "moocfetcher-server"
	app.Usage = "MOOCFetcher Appliance Server"

	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:  "port, p",
			Value: 8080,
			Usage: "Run server on `PORT`",
		},
		cli.StringFlag{
			Name:  "courses-dir, d",
			Usage: "Location of courses on filesystem. Load courses from `DIRECTORY`.",
		},
	}

	app.Action = func(c *cli.Context) error {
		coursesDir := c.String("courses-dir")
		port := c.Int("port")

		if coursesDir == "" {
			return errors.New("courses-directory is required")
		}

		// Parse Course Metadata
		cm := FSMustByte(false, courseMetadataFile)
		var courseMetadata moocfetcher.CourseData
		err := json.Unmarshal(cm, &courseMetadata)
		if err != nil {
			return errors.New(fmt.Sprintf("Error parsing course metadata: %s", err))
		}

		s := server.NewServer(coursesDir, courseMetadata)

		// Add handler for static content
		s.Handle("/", http.FileServer(FS(false)))

		http.ListenAndServe(fmt.Sprintf(":%d", port), alice.New(gziphandler.GzipHandler, addCorsHeaders).Then(s))
		return nil
	}

	err := app.Run(os.Args)

	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>Add an argument for setting the log file<commit_after>//go:generate esc -o static.go -prefix=../../client/dist ../../client/dist/
package main

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

	"github.com/NYTimes/gziphandler"
	"github.com/justinas/alice"
	"github.com/moocfetcher/moocfetcher-appliance/backend/lib/server"
	"github.com/urfave/cli"

	moocfetcher "github.com/moocfetcher/moocfetcher-appliance/backend/lib"
)

const courseMetadataFile = "/data/courses.json"

func addCorsHeaders(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
		}
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		if r.Method == "OPTIONS" {
			return
		}
		h.ServeHTTP(w, r)
	})
}

func main() {
	app := cli.NewApp()
	app.Name = "moocfetcher-server"
	app.Usage = "MOOCFetcher Appliance Server"

	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:  "port, p",
			Value: 8080,
			Usage: "Run server on `PORT`",
		},
		cli.StringFlag{
			Name:  "courses-dir, d",
			Usage: "Location of courses on filesystem. Load courses from `DIRECTORY`.",
		},
		cli.StringFlag{
			Name:  "log-file, l",
			Usage: "If set, log all output to `FILE`",
		},
	}

	app.Action = func(c *cli.Context) error {
		logFile := c.String("log-file")
		if logFile != "" {
			f, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
			if err != nil {
				log.Fatalf("error opening log file: %v", err)
			}
			defer f.Close()

			log.SetOutput(f)
		}

		coursesDir := c.String("courses-dir")
		port := c.Int("port")

		if coursesDir == "" {
			return errors.New("courses-directory is required")
		}

		// Parse Course Metadata
		cm := FSMustByte(false, courseMetadataFile)
		var courseMetadata moocfetcher.CourseData
		err := json.Unmarshal(cm, &courseMetadata)
		if err != nil {
			return errors.New(fmt.Sprintf("Error parsing course metadata: %s", err))
		}

		s := server.NewServer(coursesDir, courseMetadata)

		// Add handler for static content
		s.Handle("/", http.FileServer(FS(false)))

		http.ListenAndServe(fmt.Sprintf(":%d", port), alice.New(gziphandler.GzipHandler, addCorsHeaders).Then(s))
		return nil
	}

	err := app.Run(os.Args)

	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package controller

import (
    "path"
    "bytes"
    "github.com/silenceper/go-blog/blog/config"
    "html/template"
)

var FuncMap template.FuncMap

func init(){
    FuncMap=make(template.FuncMap)
}

func Render(tpl string,data map[string]interface{}) ([]byte,error){
    var (
        tpl_path=path.Join(config.CFG_TPL_DIR,tpl)
        t *template.Template
        err error
    )
    t = template.New(tpl_path).Funcs(FuncMap)
    t,err=t.ParseFiles(tpl_path)
    if err!=nil{
        return nil,err
    }
    var buf bytes.Buffer
    err=t.Execute(&buf,data)
    if err!=nil{
        return nil,err
    }
    return buf.Bytes(),nil
}
<commit_msg>update<commit_after>package controller

import (
    "path"
    "bytes"
    "github.com/silenceper/go-blog/blog/config"
    "html/template"
)

var FuncMap template.FuncMap

func init(){
    FuncMap=make(template.FuncMap)
}

func Render(tpl string,data map[string]interface{}) ([]byte,error){
    var (
        tpl_path=path.Join(config.CFG_TPL_DIR,tpl)
        t *template.Template
        err error
    )
    t = template.New(path.Base(tpl_path)).Funcs(FuncMap)
    t,err=t.ParseFiles(tpl_path)
    if err!=nil{
        return nil,err
    }
    var buf bytes.Buffer
    err=t.Execute(&buf,data)
    if err!=nil{
        return nil,err
    }
    return buf.Bytes(),nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"strconv"
	"time"
)

type Field struct {
	cells  [][]int
	width  int
	height int
}

var field *Field

var (
	setfps      int
	setwidth    int
	setheight   int
	setduration int
	setfilename string
	port        string
)

func newField(width, height int) *Field {
	cells := make([][]int, height)
	for cols := range cells {
		cells[cols] = make([]int, width)
	}
	return &Field{cells: cells, width: width, height: height}
}

func (field *Field) setVitality(x, y int, vitality int) {
	field.cells[y][x] = vitality
}

func (field *Field) getVitality(x, y int) int {
	x += field.width
	x %= field.width
	y += field.height
	y %= field.height
	return field.cells[y][x]
}

func (field *Field) nextVitality(x, y int) int {
	alive := 0
	for i := -1; i <= 1; i++ {
		for j := -1; j <= 1; j++ {
			if (j != 0 || i != 0) && (field.getVitality(x+i, y+j) > 0) {
				alive++
			}
		}
	}
	vitality := field.getVitality(x, y)
	if alive == 3 || alive == 2 && (vitality > 0) {
		if vitality < 8 {
			return vitality + 1
		} else {
			return vitality
		}
	}
	return 0
}

func generateFirstRound(width, height int) *Field {
	field := newField(width, height)
	for i := 0; i < (width * height / 4); i++ {
		field.setVitality(rand.Intn(width), rand.Intn(height), 1)
	}
	return field
}

func loadFirstRound(width, height int, filename string) *Field {
	finfo, err := os.Stat(filename)
	if err != nil {
		fmt.Println(filename + " doesn't exist")
		return generateFirstRound(width, height)
	} else {
		if finfo.IsDir() {
			fmt.Println(filename + " is a directory")
			return generateFirstRound(width, height)
		} else {
			field := newField(width, height)
			gofile, _ := ioutil.ReadFile(filename)
			output := []rune(string(gofile))
			x := 0
			y := 0
			for _, char := range output {
				if char == 10 {
					y++
					x = 0
				} else if char == 49 {
					field.setVitality(x, y, 1)
				} else if char == 50 {
					field.setVitality(x, y, 2)
				} else if char == 51 {
					field.setVitality(x, y, 3)
				} else if char == 52 {
					field.setVitality(x, y, 4)
				} else if char == 53 {
					field.setVitality(x, y, 5)
				} else if char == 54 {
					field.setVitality(x, y, 6)
				} else if char == 55 {
					field.setVitality(x, y, 7)
				} else if char == 56 {
					field.setVitality(x, y, 8)
				} else if char == 57 {
					field.setVitality(x, y, 9)
				} else if char != 32 {
					field.setVitality(x, y, 1)
				} else {
					field.setVitality(x, y, 0)
				}
				x++
			}
			return field
		}
	}
	return generateFirstRound(width, height)
}

func (field *Field) nextRound() *Field {
	new_field := newField(field.width, field.height)
	for y := 0; y < field.height; y++ {
		for x := 0; x < field.width; x++ {
			new_field.setVitality(x, y, field.nextVitality(x, y))
		}
	}
	return new_field
}

func (field *Field) printField() string {
	var buffer bytes.Buffer
	for y := 0; y < field.height; y++ {
		for x := 0; x < field.width; x++ {
			if field.getVitality(x, y) > 0 {
				buffer.WriteString(strconv.Itoa(field.getVitality(x, y)))
			} else {
				buffer.WriteByte(byte(' '))
			}
		}
		buffer.WriteByte('\n')
	}
	return buffer.String()
}

func main() {
	flag.IntVar(&setwidth, "w", 80, "terminal width")
	flag.IntVar(&setheight, "h", 20, "terminal height")
	flag.IntVar(&setduration, "d", -1, "game of life duration")
	flag.IntVar(&setfps, "f", 20, "frames per second")
	flag.StringVar(&setfilename, "o", "", "open file")
	flag.Parse()

	if setfilename != "" {
		field = loadFirstRound(setwidth, setheight, setfilename)
	} else {
		field = generateFirstRound(setwidth, setheight)
	}

	for i := 0; i != setduration; i++ {
		field = field.nextRound()
		time.Sleep(time.Second / time.Duration(setfps))
		fmt.Print("\033[2J")
		str := field.printField()
		fmt.Print(str)
	}

	if setfilename != "" {
		ioutil.WriteFile(setfilename, []byte(field.printField()), 0644)
	}
}
<commit_msg>no panic on bigger maps anymore<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"strconv"
	"time"
)

type Field struct {
	cells  [][]int
	width  int
	height int
}

var field *Field

var (
	setfps      int
	setwidth    int
	setheight   int
	setduration int
	setfilename string
	port        string
)

func newField(width, height int) *Field {
	cells := make([][]int, height)
	for cols := range cells {
		cells[cols] = make([]int, width)
	}
	return &Field{cells: cells, width: width, height: height}
}

func (field *Field) setVitality(x, y int, vitality int) {
	x += field.width
	x %= field.width
	y += field.height
	y %= field.height
	field.cells[y][x] = vitality
}

func (field *Field) getVitality(x, y int) int {
	x += field.width
	x %= field.width
	y += field.height
	y %= field.height
	return field.cells[y][x]
}

func (field *Field) nextVitality(x, y int) int {
	alive := 0
	for i := -1; i <= 1; i++ {
		for j := -1; j <= 1; j++ {
			if (j != 0 || i != 0) && (field.getVitality(x+i, y+j) > 0) {
				alive++
			}
		}
	}
	vitality := field.getVitality(x, y)
	if alive == 3 || alive == 2 && (vitality > 0) {
		if vitality < 8 {
			return vitality + 1
		} else {
			return vitality
		}
	}
	return 0
}

func generateFirstRound(width, height int) *Field {
	field := newField(width, height)
	for i := 0; i < (width * height / 4); i++ {
		field.setVitality(rand.Intn(width), rand.Intn(height), 1)
	}
	return field
}

func loadFirstRound(width, height int, filename string) *Field {
	finfo, err := os.Stat(filename)
	if err != nil {
		fmt.Println(filename + " doesn't exist")
		return generateFirstRound(width, height)
	} else {
		if finfo.IsDir() {
			fmt.Println(filename + " is a directory")
			return generateFirstRound(width, height)
		} else {
			field := newField(width, height)
			gofile, _ := ioutil.ReadFile(filename)
			output := []rune(string(gofile))
			x := 0
			y := 0
			for _, char := range output {
				if char == 10 {
					y++
					x = 0
				} else if char == 49 {
					field.setVitality(x, y, 1)
				} else if char == 50 {
					field.setVitality(x, y, 2)
				} else if char == 51 {
					field.setVitality(x, y, 3)
				} else if char == 52 {
					field.setVitality(x, y, 4)
				} else if char == 53 {
					field.setVitality(x, y, 5)
				} else if char == 54 {
					field.setVitality(x, y, 6)
				} else if char == 55 {
					field.setVitality(x, y, 7)
				} else if char == 56 {
					field.setVitality(x, y, 8)
				} else if char == 57 {
					field.setVitality(x, y, 9)
				} else if char != 32 {
					field.setVitality(x, y, 1)
				} else {
					field.setVitality(x, y, 0)
				}
				x++
			}
			return field
		}
	}
	return generateFirstRound(width, height)
}

func (field *Field) nextRound() *Field {
	new_field := newField(field.width, field.height)
	for y := 0; y < field.height; y++ {
		for x := 0; x < field.width; x++ {
			new_field.setVitality(x, y, field.nextVitality(x, y))
		}
	}
	return new_field
}

func (field *Field) printField() string {
	var buffer bytes.Buffer
	for y := 0; y < field.height; y++ {
		for x := 0; x < field.width; x++ {
			if field.getVitality(x, y) > 0 {
				buffer.WriteString(strconv.Itoa(field.getVitality(x, y)))
			} else {
				buffer.WriteByte(byte(' '))
			}
		}
		buffer.WriteByte('\n')
	}
	return buffer.String()
}

func main() {
	flag.IntVar(&setwidth, "w", 80, "terminal width")
	flag.IntVar(&setheight, "h", 20, "terminal height")
	flag.IntVar(&setduration, "d", -1, "game of life duration")
	flag.IntVar(&setfps, "f", 20, "frames per second")
	flag.StringVar(&setfilename, "o", "", "open file")
	flag.Parse()

	if setfilename != "" {
		field = loadFirstRound(setwidth, setheight, setfilename)
	} else {
		field = generateFirstRound(setwidth, setheight)
	}

	for i := 0; i != setduration; i++ {
		field = field.nextRound()
		time.Sleep(time.Second / time.Duration(setfps))
		fmt.Print("\033[2J")
		str := field.printField()
		fmt.Print(str)
	}

	if setfilename != "" {
		ioutil.WriteFile(setfilename, []byte(field.printField()), 0644)
	}
}
<|endoftext|>


<commit_before>package conn

import (
	"context"
	"errors"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

var (
	// ErrMessageChannelFull indicates that the connection's message channel is full.
	ErrMessageChannelFull = errors.New("websocket-conn: Message channel is full")

	closeMessage = Message{websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")}
	pingMessage  = Message{websocket.PingMessage, []byte{}}
	pongMessage  = Message{websocket.PongMessage, []byte{}}
)

// Connect to the peer. the requestHeader argument may be nil.
func Connect(ctx context.Context, settings Settings, url string, requestHeader http.Header) (*Conn, *http.Response, error) {
	dialer := new(websocket.Dialer)
	dialer.ReadBufferSize = settings.ReadBufferSize
	dialer.WriteBufferSize = settings.WriteBufferSize
	dialer.HandshakeTimeout = settings.HandshakeTimeout
	dialer.Subprotocols = settings.Subprotocols
	dialer.NetDial = settings.DialerSettings.NetDial
	dialer.TLSClientConfig = settings.DialerSettings.TLSClientConfig

	conn, response, err := dialer.Dial(url, requestHeader)
	if err != nil {
		return nil, response, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, response, nil
}

// UpgradeFromHTTP upgrades HTTP to WebSocket.
func UpgradeFromHTTP(ctx context.Context, settings Settings, w http.ResponseWriter, r *http.Request) (*Conn, error) {
	upgrader := new(websocket.Upgrader)
	upgrader.ReadBufferSize = settings.ReadBufferSize
	upgrader.WriteBufferSize = settings.WriteBufferSize
	upgrader.HandshakeTimeout = settings.HandshakeTimeout
	upgrader.Subprotocols = settings.Subprotocols
	upgrader.Error = settings.UpgraderSettings.Error
	upgrader.CheckOrigin = settings.UpgraderSettings.CheckOrigin

	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, nil
}

// Conn represents a WebSocket connection.
type Conn struct {
	ctx  context.Context
	conn *websocket.Conn
	err  error

	pingPeriod time.Duration
	writeWait  time.Duration

	messageReceived      chan Message
	sendMessageRequested chan Message
	errored              chan error
	readPumpFinished     chan struct{}
	writePumpFinished    chan struct{}
}

// Stream retrieve the peer's message data from the stream channel.
// If the connection closed, it returns data with true of EOS flag at last.
func (c *Conn) Stream() <-chan Message {
	return c.messageReceived
}

// Err returns the disconnection error if the connection closed.
func (c *Conn) Err() error {
	return c.err
}

// SendBinaryMessage to the peer. This method is goroutine safe.
func (c *Conn) SendBinaryMessage(data []byte) error {
	return c.sendMessage(Message{websocket.BinaryMessage, data})
}

// SendTextMessage to the peer. This method is goroutine safe.
func (c *Conn) SendTextMessage(text string) error {
	return c.sendMessage(Message{websocket.TextMessage, []byte(text)})
}

func (c *Conn) start(ctx context.Context, settings Settings) {
	c.ctx = ctx
	c.conn.SetReadLimit(settings.MaxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	c.conn.SetPingHandler(func(string) error {
		return c.sendMessage(pongMessage)
	})
	c.conn.SetPongHandler(func(string) error {
		return c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	})

	c.pingPeriod = settings.PingPeriod
	c.writeWait = settings.WriteWait

	c.messageReceived = make(chan Message)
	c.errored = make(chan error, 2)
	c.readPumpFinished = make(chan struct{})
	c.writePumpFinished = make(chan struct{})
	c.sendMessageRequested = make(chan Message, settings.MessageChannelBufferSize)

	go c.writePump()
	go c.readPump()
}

func (c *Conn) sendMessage(m Message) error {
	select {
	case c.sendMessageRequested <- m:
		return nil
	default:
		return ErrMessageChannelFull
	}
}

func (c *Conn) writeMessage(m Message) error {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.writeWait)); err != nil {
		return err
	}
	if err := c.conn.WriteMessage(int(m.MessageType), m.Data); err != nil {
		return err
	}
	return nil
}

func (c *Conn) writePump() {
	defer c.conn.Close()

	ticker := time.NewTicker(c.pingPeriod)
	defer ticker.Stop()

loop:
	for {
		select {
		case <-c.ctx.Done():
			messageCount := len(c.sendMessageRequested)
			for i := 0; i < messageCount; i++ {
				m := <-c.sendMessageRequested
				if err := c.writeMessage(m); err != nil {
					c.errored <- err
					break loop
				}
			}
			if err := c.writeMessage(closeMessage); err != nil {
				c.errored <- err
				break loop
			}
			c.errored <- c.ctx.Err()
			break loop
		case <-c.readPumpFinished:
			break loop
		case m := <-c.sendMessageRequested:
			if err := c.writeMessage(m); err != nil {
				c.errored <- err
				break loop
			}
		case <-ticker.C:
			if err := c.writeMessage(pingMessage); err != nil {
				c.errored <- err
				break loop
			}
		}
	}
	close(c.writePumpFinished)
}

func (c *Conn) readPump() {
	defer c.conn.Close()

loop:
	for {
		messageType, data, err := c.conn.ReadMessage()
		if err != nil {
			c.errored <- err
			break loop
		}

		var m Message
		switch messageType {
		case websocket.TextMessage:
			m = Message{TextMessageType, data}
		case websocket.BinaryMessage:
			m = Message{BinaryMessageType, data}
		default:
			continue
		}

		select {
		case <-c.ctx.Done():
			c.errored <- c.ctx.Err()
			break loop
		case <-c.writePumpFinished:
			break loop
		case c.messageReceived <- m:
		}
	}
	close(c.readPumpFinished)
	<-c.writePumpFinished

	c.err = <-c.errored
	close(c.messageReceived)
}
<commit_msg>Remove ctx from Conn<commit_after>package conn

import (
	"context"
	"errors"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

var (
	// ErrMessageChannelFull indicates that the connection's message channel is full.
	ErrMessageChannelFull = errors.New("websocket-conn: Message channel is full")

	closeMessage = Message{websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")}
	pingMessage  = Message{websocket.PingMessage, []byte{}}
	pongMessage  = Message{websocket.PongMessage, []byte{}}
)

// Connect to the peer. the requestHeader argument may be nil.
func Connect(ctx context.Context, settings Settings, url string, requestHeader http.Header) (*Conn, *http.Response, error) {
	dialer := new(websocket.Dialer)
	dialer.ReadBufferSize = settings.ReadBufferSize
	dialer.WriteBufferSize = settings.WriteBufferSize
	dialer.HandshakeTimeout = settings.HandshakeTimeout
	dialer.Subprotocols = settings.Subprotocols
	dialer.NetDial = settings.DialerSettings.NetDial
	dialer.TLSClientConfig = settings.DialerSettings.TLSClientConfig

	conn, response, err := dialer.Dial(url, requestHeader)
	if err != nil {
		return nil, response, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, response, nil
}

// UpgradeFromHTTP upgrades HTTP to WebSocket.
func UpgradeFromHTTP(ctx context.Context, settings Settings, w http.ResponseWriter, r *http.Request) (*Conn, error) {
	upgrader := new(websocket.Upgrader)
	upgrader.ReadBufferSize = settings.ReadBufferSize
	upgrader.WriteBufferSize = settings.WriteBufferSize
	upgrader.HandshakeTimeout = settings.HandshakeTimeout
	upgrader.Subprotocols = settings.Subprotocols
	upgrader.Error = settings.UpgraderSettings.Error
	upgrader.CheckOrigin = settings.UpgraderSettings.CheckOrigin

	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, nil
}

// Conn represents a WebSocket connection.
type Conn struct {
	conn *websocket.Conn
	err  error

	pingPeriod time.Duration
	writeWait  time.Duration

	messageReceived      chan Message
	sendMessageRequested chan Message
	errored              chan error
	readPumpFinished     chan struct{}
	writePumpFinished    chan struct{}
}

// Stream retrieve the peer's message data from the stream channel.
// If the connection closed, it returns data with true of EOS flag at last.
func (c *Conn) Stream() <-chan Message {
	return c.messageReceived
}

// Err returns the disconnection error if the connection closed.
func (c *Conn) Err() error {
	return c.err
}

// SendBinaryMessage to the peer. This method is goroutine safe.
func (c *Conn) SendBinaryMessage(data []byte) error {
	return c.sendMessage(Message{websocket.BinaryMessage, data})
}

// SendTextMessage to the peer. This method is goroutine safe.
func (c *Conn) SendTextMessage(text string) error {
	return c.sendMessage(Message{websocket.TextMessage, []byte(text)})
}

func (c *Conn) start(ctx context.Context, settings Settings) {
	c.conn.SetReadLimit(settings.MaxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	c.conn.SetPingHandler(func(string) error {
		return c.sendMessage(pongMessage)
	})
	c.conn.SetPongHandler(func(string) error {
		return c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	})

	c.pingPeriod = settings.PingPeriod
	c.writeWait = settings.WriteWait

	c.messageReceived = make(chan Message)
	c.errored = make(chan error, 2)
	c.readPumpFinished = make(chan struct{})
	c.writePumpFinished = make(chan struct{})
	c.sendMessageRequested = make(chan Message, settings.MessageChannelBufferSize)

	go c.writePump(ctx)
	go c.readPump(ctx)
}

func (c *Conn) sendMessage(m Message) error {
	select {
	case c.sendMessageRequested <- m:
		return nil
	default:
		return ErrMessageChannelFull
	}
}

func (c *Conn) writeMessage(m Message) error {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.writeWait)); err != nil {
		return err
	}
	if err := c.conn.WriteMessage(int(m.MessageType), m.Data); err != nil {
		return err
	}
	return nil
}

func (c *Conn) writePump(ctx context.Context) {
	defer c.conn.Close()

	ticker := time.NewTicker(c.pingPeriod)
	defer ticker.Stop()

loop:
	for {
		select {
		case <-ctx.Done():
			messageCount := len(c.sendMessageRequested)
			for i := 0; i < messageCount; i++ {
				m := <-c.sendMessageRequested
				if err := c.writeMessage(m); err != nil {
					c.errored <- err
					break loop
				}
			}
			if err := c.writeMessage(closeMessage); err != nil {
				c.errored <- err
				break loop
			}
			c.errored <- ctx.Err()
			break loop
		case <-c.readPumpFinished:
			break loop
		case m := <-c.sendMessageRequested:
			if err := c.writeMessage(m); err != nil {
				c.errored <- err
				break loop
			}
		case <-ticker.C:
			if err := c.writeMessage(pingMessage); err != nil {
				c.errored <- err
				break loop
			}
		}
	}
	close(c.writePumpFinished)
}

func (c *Conn) readPump(ctx context.Context) {
	defer c.conn.Close()

loop:
	for {
		messageType, data, err := c.conn.ReadMessage()
		if err != nil {
			c.errored <- err
			break loop
		}

		var m Message
		switch messageType {
		case websocket.TextMessage:
			m = Message{TextMessageType, data}
		case websocket.BinaryMessage:
			m = Message{BinaryMessageType, data}
		default:
			continue
		}

		select {
		case <-ctx.Done():
			c.errored <- ctx.Err()
			break loop
		case <-c.writePumpFinished:
			break loop
		case c.messageReceived <- m:
		}
	}
	close(c.readPumpFinished)
	<-c.writePumpFinished

	c.err = <-c.errored
	close(c.messageReceived)
}
<|endoftext|>


<commit_before>package gtcp

import (
	"bufio"
	"context"
	"log"
	"net"
	"sync"
	"sync/atomic"
)

type (
	Conn interface {
		net.Conn
		Flush() error
		SetCancelFunc(context.CancelFunc)
		Stats() (int64, int64)
		SetIdle(bool)
		IsIdle() bool
		Peek(int) ([]byte, error)
	}

	NewConn func(Conn) Conn

	// @todo should be private
	BaseConn struct {
		net.Conn
		CancelFunc context.CancelFunc
		idle       atomicBool
	}

	BufferedConn struct {
		Conn
		bufr *bufio.Reader
		bufw *bufio.Writer
		once sync.Once
	}

	StatsConn struct {
		Conn
		InBytes  int64
		OutBytes int64
	}

	DebugConn struct {
		Conn
	}

	atomicBool int32
)

var (
	readerPool sync.Pool
	writerPool sync.Pool
)

func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
func (b *atomicBool) setTrue()    { atomic.StoreInt32((*int32)(b), 1) }
func (b *atomicBool) setFalse()   { atomic.StoreInt32((*int32)(b), 0) }

func NewBaseConn(conn net.Conn) Conn {
	return &BaseConn{
		Conn: conn,
	}
}

func (bc *BaseConn) Read(buf []byte) (n int, err error) {
	n, err = bc.Conn.Read(buf)
	if err != nil && bc.CancelFunc != nil {
		bc.CancelFunc()
	}
	return
}

func (bc *BaseConn) Write(buf []byte) (n int, err error) {
	n, err = bc.Conn.Write(buf)
	if err != nil && bc.CancelFunc != nil {
		bc.CancelFunc()
	}
	return
}

func (bc *BaseConn) Flush() error {
	return nil
}

func (bc *BaseConn) SetCancelFunc(cancel context.CancelFunc) {
	bc.CancelFunc = cancel
}

func (bc *BaseConn) Stats() (int64, int64) {
	return 0, 0
}

func (bc *BaseConn) SetIdle(idle bool) {
	if idle {
		bc.idle.setTrue()
	} else {
		bc.idle.setFalse()
	}
}

func (bc *BaseConn) IsIdle() bool {
	return bc.idle.isSet()
}

func (bc *BaseConn) Peek(int) ([]byte, error) {
	// @todo emulate by Read
	panic("gtcp: Peek not implemented")
}

func NewBufferedConn(conn Conn) Conn {
	var br *bufio.Reader
	var bw *bufio.Writer
	if v := readerPool.Get(); v != nil {
		br = v.(*bufio.Reader)
		br.Reset(conn)
	} else {
		br = bufio.NewReader(conn)
	}
	if v := writerPool.Get(); v != nil {
		bw = v.(*bufio.Writer)
		bw.Reset(conn)
	} else {
		bw = bufio.NewWriter(conn)
	}
	return &BufferedConn{
		Conn: conn,
		bufr: br,
		bufw: bw,
	}
}

func (b *BufferedConn) Read(buf []byte) (n int, err error) {
	n, err = b.bufr.Read(buf)
	return
}

func (b *BufferedConn) Write(buf []byte) (n int, err error) {
	n, err = b.bufw.Write(buf)
	return
}

func (b *BufferedConn) Close() (err error) {
	b.once.Do(func() {
		b.bufr.Reset(nil)
		readerPool.Put(b.bufr)
		b.bufr = nil
		err = b.bufw.Flush()
		b.bufw.Reset(nil)
		writerPool.Put(b.bufw)
		b.bufw = nil
		e := b.Conn.Close()
		if err == nil {
			err = e
		}
	})
	return
}

func (b *BufferedConn) Flush() (err error) {
	return b.bufw.Flush()
}

func (b *BufferedConn) Peek(n int) ([]byte, error) {
	return b.bufr.Peek(n)
}

func NewStatsConn(conn Conn) Conn {
	return &StatsConn{Conn: conn}
}

func (s *StatsConn) Read(buf []byte) (n int, err error) {
	n, err = s.Conn.Read(buf)
	s.InBytes += int64(n)
	return
}

func (s *StatsConn) Write(buf []byte) (n int, err error) {
	n, err = s.Conn.Write(buf)
	s.OutBytes += int64(n)
	return
}

func (s *StatsConn) Stats() (int64, int64) {
	return s.InBytes, s.OutBytes
}

func NewDebugConn(conn Conn) Conn {
	return &DebugConn{Conn: conn}
}

func (d *DebugConn) Read(buf []byte) (n int, err error) {
	log.Printf("Read(%d) = ....", len(buf))
	n, err = d.Conn.Read(buf)
	log.Printf("Read(%d) = %d, %v", len(buf), n, err)
	return
}

func (d *DebugConn) Write(buf []byte) (n int, err error) {
	log.Printf("Write(%d) = ....", len(buf))
	n, err = d.Conn.Write(buf)
	log.Printf("Write(%d) = %d, %v", len(buf), n, err)
	return
}

func (d *DebugConn) Close() (err error) {
	log.Printf("Close() = ...")
	err = d.Conn.Close()
	log.Printf("Close() = %v", err)
	return
}
<commit_msg>Fix #4<commit_after>package gtcp

import (
	"bufio"
	"context"
	"log"
	"net"
	"sync"
	"sync/atomic"
)

type (
	Conn interface {
		net.Conn
		Flush() error
		SetCancelFunc(context.CancelFunc)
		Stats() (int64, int64)
		SetIdle(bool)
		IsIdle() bool
		Peek(int) ([]byte, error)
	}

	NewConn func(Conn) Conn

	baseConn struct {
		net.Conn
		CancelFunc context.CancelFunc
		idle       atomicBool
	}

	BufferedConn struct {
		Conn
		bufr *bufio.Reader
		bufw *bufio.Writer
		once sync.Once
	}

	StatsConn struct {
		Conn
		InBytes  int64
		OutBytes int64
	}

	DebugConn struct {
		Conn
	}

	atomicBool int32
)

var (
	readerPool sync.Pool
	writerPool sync.Pool
)

func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
func (b *atomicBool) setTrue()    { atomic.StoreInt32((*int32)(b), 1) }
func (b *atomicBool) setFalse()   { atomic.StoreInt32((*int32)(b), 0) }

func NewBaseConn(conn net.Conn) Conn {
	return &baseConn{
		Conn: conn,
	}
}

func (bc *baseConn) Read(buf []byte) (n int, err error) {
	n, err = bc.Conn.Read(buf)
	if err != nil && bc.CancelFunc != nil {
		bc.CancelFunc()
	}
	return
}

func (bc *baseConn) Write(buf []byte) (n int, err error) {
	n, err = bc.Conn.Write(buf)
	if err != nil && bc.CancelFunc != nil {
		bc.CancelFunc()
	}
	return
}

func (bc *baseConn) Flush() error {
	return nil
}

func (bc *baseConn) SetCancelFunc(cancel context.CancelFunc) {
	bc.CancelFunc = cancel
}

func (bc *baseConn) Stats() (int64, int64) {
	return 0, 0
}

func (bc *baseConn) SetIdle(idle bool) {
	if idle {
		bc.idle.setTrue()
	} else {
		bc.idle.setFalse()
	}
}

func (bc *baseConn) IsIdle() bool {
	return bc.idle.isSet()
}

func (bc *baseConn) Peek(int) ([]byte, error) {
	// @todo emulate by Read
	panic("gtcp: Peek not implemented")
}

func NewBufferedConn(conn Conn) Conn {
	var br *bufio.Reader
	var bw *bufio.Writer
	if v := readerPool.Get(); v != nil {
		br = v.(*bufio.Reader)
		br.Reset(conn)
	} else {
		br = bufio.NewReader(conn)
	}
	if v := writerPool.Get(); v != nil {
		bw = v.(*bufio.Writer)
		bw.Reset(conn)
	} else {
		bw = bufio.NewWriter(conn)
	}
	return &BufferedConn{
		Conn: conn,
		bufr: br,
		bufw: bw,
	}
}

func (b *BufferedConn) Read(buf []byte) (n int, err error) {
	n, err = b.bufr.Read(buf)
	return
}

func (b *BufferedConn) Write(buf []byte) (n int, err error) {
	n, err = b.bufw.Write(buf)
	return
}

func (b *BufferedConn) Close() (err error) {
	b.once.Do(func() {
		b.bufr.Reset(nil)
		readerPool.Put(b.bufr)
		b.bufr = nil
		err = b.bufw.Flush()
		b.bufw.Reset(nil)
		writerPool.Put(b.bufw)
		b.bufw = nil
		e := b.Conn.Close()
		if err == nil {
			err = e
		}
	})
	return
}

func (b *BufferedConn) Flush() (err error) {
	return b.bufw.Flush()
}

func (b *BufferedConn) Peek(n int) ([]byte, error) {
	return b.bufr.Peek(n)
}

func NewStatsConn(conn Conn) Conn {
	return &StatsConn{Conn: conn}
}

func (s *StatsConn) Read(buf []byte) (n int, err error) {
	n, err = s.Conn.Read(buf)
	s.InBytes += int64(n)
	return
}

func (s *StatsConn) Write(buf []byte) (n int, err error) {
	n, err = s.Conn.Write(buf)
	s.OutBytes += int64(n)
	return
}

func (s *StatsConn) Stats() (int64, int64) {
	return s.InBytes, s.OutBytes
}

func NewDebugConn(conn Conn) Conn {
	return &DebugConn{Conn: conn}
}

func (d *DebugConn) Read(buf []byte) (n int, err error) {
	log.Printf("Read(%d) = ....", len(buf))
	n, err = d.Conn.Read(buf)
	log.Printf("Read(%d) = %d, %v", len(buf), n, err)
	return
}

func (d *DebugConn) Write(buf []byte) (n int, err error) {
	log.Printf("Write(%d) = ....", len(buf))
	n, err = d.Conn.Write(buf)
	log.Printf("Write(%d) = %d, %v", len(buf), n, err)
	return
}

func (d *DebugConn) Close() (err error) {
	log.Printf("Close() = ...")
	err = d.Conn.Close()
	log.Printf("Close() = %v", err)
	return
}
<|endoftext|>


<commit_before>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Conn handles our websocket
type Conn struct {
	// Exported
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst

	// Private
	quit    chan bool
}

const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case <- c.quit:
			// Quit signal was invoked by our Close function
			// The bottle destination wants this connection closed
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}

}

// This function chews through the power cables
func (c *Conn) Close() {
	// Send ourself the quit signal provided by a function
	c.quit <- true
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		quit: make(chan bool),
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start infinite read/write loop
	c.read_write()
}
<commit_msg>Conection and Quit now support a final message before closing<commit_after>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Conn handles our websocket
type Conn struct {
	// Exported
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst
	Quit chan []byte
}

const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}

}

// This function chews through the power cables
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	c.Quit <- []byte("")
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		Quit: make(chan []byte),
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start infinite read/write loop
	c.read_write()
}
<|endoftext|>


<commit_before>// Copyright 2015 Tony Bai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package cmpp

import (
	"encoding/binary"
	"errors"
	"io"
	"net"
	"sync"
	"time"
)

type State uint8

// Errors for conn operations
var (
	ErrConnIsClosed = errors.New("connection is closed")
)

var noDeadline = time.Time{}

// Conn States
const (
	CONN_CLOSED State = iota
	CONN_CONNECTED
	CONN_AUTHOK
)

type Conn struct {
	net.Conn
	State State
	Typ   Type

	// for SeqId generator goroutine
	SeqId <-chan uint32
	done  chan<- struct{}
}

func newSeqIdGenerator() (<-chan uint32, chan<- struct{}) {
	out := make(chan uint32)
	done := make(chan struct{})

	go func() {
		var i uint32
		for {
			select {
			case out <- i:
				i++
			case <-done:
				close(out)
				return
			}
		}
	}()
	return out, done
}

// New returns an abstract structure for successfully
// established underlying net.Conn.
func NewConn(conn net.Conn, typ Type) *Conn {
	seqId, done := newSeqIdGenerator()
	c := &Conn{
		Conn:  conn,
		Typ:   typ,
		SeqId: seqId,
		done:  done,
	}
	tc := c.Conn.(*net.TCPConn) // Always tcpconn
	tc.SetKeepAlive(true)       //Keepalive as default
	return c
}

func (c *Conn) Close() {
	if c != nil {
		if c.State == CONN_CLOSED {
			return
		}
		close(c.done)  // let the SeqId goroutine exit.
		c.Conn.Close() // close the underlying net.Conn
		c.State = CONN_CLOSED
	}
}

func (c *Conn) SetState(state State) {
	c.State = state
}

// SendPkt pack the cmpp packet structure and send it to the other peer.
func (c *Conn) SendPkt(packet Packer, seqId uint32) error {
	if c.State == CONN_CLOSED {
		return ErrConnIsClosed
	}

	data, err := packet.Pack(seqId)
	if err != nil {
		return err
	}

	_, err = c.Conn.Write(data) //block write
	if err != nil {
		return err
	}

	return nil
}

const (
	defaultReadBufferSize = 4096
)

// readBuffer is used to optimize the performance of
// RecvAndUnpackPkt.
type readBuffer struct {
	totalLen  uint32
	commandId CommandId
	leftData  [defaultReadBufferSize]byte
}

var readBufferPool = sync.Pool{
	New: func() interface{} {
		return &readBuffer{}
	},
}

// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
func (c *Conn) RecvAndUnpackPkt(timeout time.Duration) (interface{}, error) {
	if c.State == CONN_CLOSED {
		return nil, ErrConnIsClosed
	}
	defer c.SetReadDeadline(noDeadline)

	rb := readBufferPool.Get().(*readBuffer)
	defer readBufferPool.Put(rb)

	// Total_Length in packet
	if timeout != 0 {
		c.SetReadDeadline(time.Now().Add(timeout))
	}
	err := binary.Read(c.Conn, binary.BigEndian, &rb.totalLen)
	if err != nil {
		return nil, err
	}

	if c.Typ == V30 {
		if rb.totalLen < CMPP3_PACKET_MIN || rb.totalLen > CMPP3_PACKET_MAX {
			return nil, ErrTotalLengthInvalid
		}
	}

	if c.Typ == V21 || c.Typ == V20 {
		if rb.totalLen < CMPP2_PACKET_MIN || rb.totalLen > CMPP2_PACKET_MAX {
			return nil, ErrTotalLengthInvalid
		}
	}

	// Command_Id
	if timeout != 0 {
		c.SetReadDeadline(time.Now().Add(timeout))
	}
	err = binary.Read(c.Conn, binary.BigEndian, &rb.commandId)
	if err != nil {
		return nil, err
	}

	if !((rb.commandId > CMPP_REQUEST_MIN && rb.commandId < CMPP_REQUEST_MAX) ||
		(rb.commandId > CMPP_RESPONSE_MIN && rb.commandId < CMPP_RESPONSE_MAX)) {
		return nil, ErrCommandIdInvalid
	}

	// The left packet data (start from seqId in header).
	if timeout != 0 {
		c.SetReadDeadline(time.Now().Add(timeout))
	}
	var leftData = rb.leftData[0:(rb.totalLen - 8)]
	_, err = io.ReadFull(c.Conn, leftData)
	if err != nil {
		return nil, err
	}

	var p Packer
	switch rb.commandId {
	case CMPP_CONNECT:
		p = &CmppConnReqPkt{}
	case CMPP_CONNECT_RESP:
		if c.Typ == V30 {
			p = &Cmpp3ConnRspPkt{}
		} else {
			p = &Cmpp2ConnRspPkt{}
		}
	case CMPP_TERMINATE:
		p = &CmppTerminateReqPkt{}
	case CMPP_TERMINATE_RESP:
		p = &CmppTerminateRspPkt{}
	case CMPP_SUBMIT:
		if c.Typ == V30 {
			p = &Cmpp3SubmitReqPkt{}
		} else {
			p = &Cmpp2SubmitReqPkt{}
		}
	case CMPP_SUBMIT_RESP:
		if c.Typ == V30 {
			p = &Cmpp3SubmitRspPkt{}
		} else {
			p = &Cmpp2SubmitRspPkt{}
		}
	case CMPP_DELIVER:
		if c.Typ == V30 {
			p = &Cmpp3DeliverReqPkt{}
		} else {
			p = &Cmpp2DeliverReqPkt{}
		}
	case CMPP_DELIVER_RESP:
		if c.Typ == V30 {
			p = &Cmpp3DeliverRspPkt{}
		} else {
			p = &Cmpp2DeliverRspPkt{}
		}
	case CMPP_FWD:
		if c.Typ == V30 {
			p = &Cmpp3FwdReqPkt{}
		} else {
			p = &Cmpp2FwdReqPkt{}
		}
	case CMPP_FWD_RESP:
		if c.Typ == V30 {
			p = &Cmpp3FwdRspPkt{}
		} else {
			p = &Cmpp2FwdRspPkt{}
		}
	case CMPP_ACTIVE_TEST:
		p = &CmppActiveTestReqPkt{}
	case CMPP_ACTIVE_TEST_RESP:
		p = &CmppActiveTestRspPkt{}

	default:
		p = nil
		return nil, ErrCommandIdNotSupported
	}

	err = p.Unpack(leftData)
	if err != nil {
		return nil, err
	}
	return p, nil
}
<commit_msg>add ErrReadCmdIDTimeout and ErrReadPktBodyTimeout for conn.RecvAndUnpackPkt to differentiate the situation read total_len timeout and read cmdid, packet body timeout<commit_after>// Copyright 2015 Tony Bai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package cmpp

import (
	"encoding/binary"
	"errors"
	"io"
	"net"
	"sync"
	"time"
)

type State uint8

// Errors for conn operations
var (
	ErrConnIsClosed       = errors.New("connection is closed")
	ErrReadCmdIDTimeout   = errors.New("read commandId timeout")
	ErrReadPktBodyTimeout = errors.New("read packet body timeout")
)

var noDeadline = time.Time{}

// Conn States
const (
	CONN_CLOSED State = iota
	CONN_CONNECTED
	CONN_AUTHOK
)

type Conn struct {
	net.Conn
	State State
	Typ   Type

	// for SeqId generator goroutine
	SeqId <-chan uint32
	done  chan<- struct{}
}

func newSeqIdGenerator() (<-chan uint32, chan<- struct{}) {
	out := make(chan uint32)
	done := make(chan struct{})

	go func() {
		var i uint32
		for {
			select {
			case out <- i:
				i++
			case <-done:
				close(out)
				return
			}
		}
	}()
	return out, done
}

// New returns an abstract structure for successfully
// established underlying net.Conn.
func NewConn(conn net.Conn, typ Type) *Conn {
	seqId, done := newSeqIdGenerator()
	c := &Conn{
		Conn:  conn,
		Typ:   typ,
		SeqId: seqId,
		done:  done,
	}
	tc := c.Conn.(*net.TCPConn) // Always tcpconn
	tc.SetKeepAlive(true)       //Keepalive as default
	return c
}

func (c *Conn) Close() {
	if c != nil {
		if c.State == CONN_CLOSED {
			return
		}
		close(c.done)  // let the SeqId goroutine exit.
		c.Conn.Close() // close the underlying net.Conn
		c.State = CONN_CLOSED
	}
}

func (c *Conn) SetState(state State) {
	c.State = state
}

// SendPkt pack the cmpp packet structure and send it to the other peer.
func (c *Conn) SendPkt(packet Packer, seqId uint32) error {
	if c.State == CONN_CLOSED {
		return ErrConnIsClosed
	}

	data, err := packet.Pack(seqId)
	if err != nil {
		return err
	}

	_, err = c.Conn.Write(data) //block write
	if err != nil {
		return err
	}

	return nil
}

const (
	defaultReadBufferSize = 4096
)

// readBuffer is used to optimize the performance of
// RecvAndUnpackPkt.
type readBuffer struct {
	totalLen  uint32
	commandId CommandId
	leftData  [defaultReadBufferSize]byte
}

var readBufferPool = sync.Pool{
	New: func() interface{} {
		return &readBuffer{}
	},
}

// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
func (c *Conn) RecvAndUnpackPkt(timeout time.Duration) (interface{}, error) {
	if c.State == CONN_CLOSED {
		return nil, ErrConnIsClosed
	}
	defer c.SetReadDeadline(noDeadline)

	rb := readBufferPool.Get().(*readBuffer)
	defer readBufferPool.Put(rb)

	// Total_Length in packet
	if timeout != 0 {
		c.SetReadDeadline(time.Now().Add(timeout))
	}
	err := binary.Read(c.Conn, binary.BigEndian, &rb.totalLen)
	if err != nil {
		return nil, err
	}

	if c.Typ == V30 {
		if rb.totalLen < CMPP3_PACKET_MIN || rb.totalLen > CMPP3_PACKET_MAX {
			return nil, ErrTotalLengthInvalid
		}
	}

	if c.Typ == V21 || c.Typ == V20 {
		if rb.totalLen < CMPP2_PACKET_MIN || rb.totalLen > CMPP2_PACKET_MAX {
			return nil, ErrTotalLengthInvalid
		}
	}

	// Command_Id
	if timeout != 0 {
		c.SetReadDeadline(time.Now().Add(timeout))
	}
	err = binary.Read(c.Conn, binary.BigEndian, &rb.commandId)
	if err != nil {
		netErr, ok := err.(net.Error)
		if ok {
			if netErr.Timeout() {
				return nil, ErrReadCmdIDTimeout
			}
		}
		return nil, err
	}

	if !((rb.commandId > CMPP_REQUEST_MIN && rb.commandId < CMPP_REQUEST_MAX) ||
		(rb.commandId > CMPP_RESPONSE_MIN && rb.commandId < CMPP_RESPONSE_MAX)) {
		return nil, ErrCommandIdInvalid
	}

	// The left packet data (start from seqId in header).
	if timeout != 0 {
		c.SetReadDeadline(time.Now().Add(timeout))
	}
	var leftData = rb.leftData[0:(rb.totalLen - 8)]
	_, err = io.ReadFull(c.Conn, leftData)
	if err != nil {
		netErr, ok := err.(net.Error)
		if ok {
			if netErr.Timeout() {
				return nil, ErrReadPktBodyTimeout
			}
		}
		return nil, err
	}

	var p Packer
	switch rb.commandId {
	case CMPP_CONNECT:
		p = &CmppConnReqPkt{}
	case CMPP_CONNECT_RESP:
		if c.Typ == V30 {
			p = &Cmpp3ConnRspPkt{}
		} else {
			p = &Cmpp2ConnRspPkt{}
		}
	case CMPP_TERMINATE:
		p = &CmppTerminateReqPkt{}
	case CMPP_TERMINATE_RESP:
		p = &CmppTerminateRspPkt{}
	case CMPP_SUBMIT:
		if c.Typ == V30 {
			p = &Cmpp3SubmitReqPkt{}
		} else {
			p = &Cmpp2SubmitReqPkt{}
		}
	case CMPP_SUBMIT_RESP:
		if c.Typ == V30 {
			p = &Cmpp3SubmitRspPkt{}
		} else {
			p = &Cmpp2SubmitRspPkt{}
		}
	case CMPP_DELIVER:
		if c.Typ == V30 {
			p = &Cmpp3DeliverReqPkt{}
		} else {
			p = &Cmpp2DeliverReqPkt{}
		}
	case CMPP_DELIVER_RESP:
		if c.Typ == V30 {
			p = &Cmpp3DeliverRspPkt{}
		} else {
			p = &Cmpp2DeliverRspPkt{}
		}
	case CMPP_FWD:
		if c.Typ == V30 {
			p = &Cmpp3FwdReqPkt{}
		} else {
			p = &Cmpp2FwdReqPkt{}
		}
	case CMPP_FWD_RESP:
		if c.Typ == V30 {
			p = &Cmpp3FwdRspPkt{}
		} else {
			p = &Cmpp2FwdRspPkt{}
		}
	case CMPP_ACTIVE_TEST:
		p = &CmppActiveTestReqPkt{}
	case CMPP_ACTIVE_TEST_RESP:
		p = &CmppActiveTestRspPkt{}

	default:
		p = nil
		return nil, ErrCommandIdNotSupported
	}

	err = p.Unpack(leftData)
	if err != nil {
		return nil, err
	}
	return p, nil
}
<|endoftext|>


<commit_before>package prestgo

import (
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// Name of the driver to use when calling `sql.Open`
const DriverName = "prestgo"

// Default data source parameters
const (
	DefaultPort     = "8080"
	DefaultCatalog  = "hive"
	DefaultSchema   = "default"
	DefaultUsername = "prestgo"

	TimestampFormat = "2006-01-02 15:04:05.000"
)

var (
	// ErrNotSupported is returned when an unsupported feature is requested.
	ErrNotSupported = errors.New(DriverName + ": not supported")

	// ErrQueryFailed indicates that a network or server failure prevented the driver obtaining a query result.
	ErrQueryFailed = errors.New(DriverName + ": query failed")

	// ErrQueryCanceled indicates that a query was canceled before results could be retrieved.
	ErrQueryCanceled = errors.New(DriverName + ": query canceled")
)

func init() {
	sql.Register(DriverName, &drv{})
}

type drv struct{}

func (*drv) Open(name string) (driver.Conn, error) {
	return Open(name)
}

// Open creates a connection to the specified data source name which should be
// of the form "presto://hostname:port/catalog/schema". http.DefaultClient will
// be used for communicating with the Presto server.
func Open(name string) (driver.Conn, error) {
	return ClientOpen(http.DefaultClient, name)
}

// ClientOpen creates a connection to the specified data source name using the supplied
// HTTP client. The data source name should be of the form
// "presto://hostname:port/catalog/schema".
func ClientOpen(client *http.Client, name string) (driver.Conn, error) {

	conf := make(config)
	conf.parseDataSource(name)

	cn := &conn{
		client:  client,
		addr:    conf["addr"],
		catalog: conf["catalog"],
		schema:  conf["schema"],
		user:    conf["user"],
	}
	return cn, nil
}

type conn struct {
	client  *http.Client
	addr    string
	catalog string
	schema  string
	user    string
}

var _ driver.Conn = &conn{}

func (c *conn) Prepare(query string) (driver.Stmt, error) {
	st := &stmt{
		conn:  c,
		query: query,
	}
	return st, nil
}

func (c *conn) Close() error {
	return nil
}

func (c *conn) Begin() (driver.Tx, error) {
	return nil, ErrNotSupported
}

type stmt struct {
	conn  *conn
	query string
}

var _ driver.Stmt = &stmt{}

func (s *stmt) Close() error {
	return nil
}

func (s *stmt) NumInput() int {
	return -1 // TODO: parse query for parameters
}

func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	return nil, ErrNotSupported
}

func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	// TODO: support query argument substitution
	if len(args) > 0 {
		return nil, ErrNotSupported
	}
	queryURL := fmt.Sprintf("http://%s/v1/statement", s.conn.addr)

	req, err := http.NewRequest("POST", queryURL, strings.NewReader(s.query))
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Presto-User", s.conn.user)
	req.Header.Add("X-Presto-Catalog", s.conn.catalog)
	req.Header.Add("X-Presto-Schema", s.conn.schema)

	resp, err := s.conn.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Presto doesn't use the http response code, parse errors come back as 200
	if resp.StatusCode != 200 {
		return nil, ErrQueryFailed
	}

	var sresp stmtResponse
	err = json.NewDecoder(resp.Body).Decode(&sresp)
	if err != nil {
		return nil, err
	}

	if sresp.Stats.State == "FAILED" {
		return nil, sresp.Error
	}

	time.Sleep(500 * time.Millisecond)

	r := &rows{
		conn:    s.conn,
		nextURI: sresp.NextURI,
	}

	return r, nil
}

type rows struct {
	conn     *conn
	nextURI  string
	fetched  bool
	rowindex int
	columns  []string
	types    []driver.ValueConverter
	data     []queryData
}

var _ driver.Rows = &rows{}

func (r *rows) fetch() error {
	// TODO: timeout
	for {
		qresp, gotData, err := r.waitForData()
		if err != nil {
			return err
		}
		if !gotData {
			time.Sleep(800 * time.Millisecond) // TODO: make this interval configurable
			continue
		}

		r.rowindex = 0
		r.data = qresp.Data

		// Note: qresp.Stats.State will be FINISHED when last page is retrieved
		r.nextURI = qresp.NextURI

		if !r.fetched {
			r.columns = make([]string, len(qresp.Columns))
			r.types = make([]driver.ValueConverter, len(qresp.Columns))
			for i, col := range qresp.Columns {
				r.columns[i] = col.Name
				switch {
				case strings.HasPrefix(col.Type, VarChar):
					r.types[i] = driver.String
				case col.Type == BigInt, col.Type == Integer:
					r.types[i] = bigIntConverter
				case col.Type == Boolean:
					r.types[i] = driver.Bool
				case col.Type == Double:
					r.types[i] = doubleConverter
				case col.Type == Timestamp:
					r.types[i] = timestampConverter
				case TimestampWithTimezone:
					r.types[i] = timestampWithTimezoneConverter

				default:
					return fmt.Errorf("unsupported column type: %s", col.Type)
				}
			}
			r.fetched = true
		}

		if len(qresp.Data) == 0 {
			return io.EOF
		}

		return nil
	}
}

func (r *rows) waitForData() (*queryResponse, bool, error) {
	nextReq, err := http.NewRequest("GET", r.nextURI, nil)
	if err != nil {
		return nil, false, err
	}

	nextResp, err := r.conn.client.Do(nextReq)
	if err != nil {
		return nil, false, err
	}

	if nextResp.StatusCode != 200 {
		nextResp.Body.Close()
		return nil, false, ErrQueryFailed
	}

	var qresp queryResponse
	err = json.NewDecoder(nextResp.Body).Decode(&qresp)
	nextResp.Body.Close()
	if err != nil {
		return nil, false, err
	}

	switch qresp.Stats.State {
	case QueryStateFailed:
		return nil, false, qresp.Error
	case QueryStateCanceled:
		return nil, false, ErrQueryCanceled
	case QueryStatePlanning, QueryStateQueued, QueryStateRunning, QueryStateStarting:
		if len(qresp.Data) == 0 {
			r.nextURI = qresp.NextURI
			return nil, false, nil
		}
	}

	return &qresp, true, nil
}

func (r *rows) Columns() []string {
	if !r.fetched {
		if err := r.fetch(); err != nil {
			return []string{}
		}
	}
	return r.columns
}

func (r *rows) Close() error {
	return nil
}

func (r *rows) Next(dest []driver.Value) error {
	if !r.fetched || r.rowindex >= len(r.data) {
		if r.nextURI == "" {
			return io.EOF
		}
		if err := r.fetch(); err != nil {
			return err
		}
	}

	for i, v := range r.types {
		val, err := v.ConvertValue(r.data[r.rowindex][i])
		if err != nil {
			return err // TODO: more context in error
		}
		dest[i] = val
	}
	r.rowindex++
	return nil
}

type config map[string]string

func (c config) parseDataSource(ds string) error {
	u, err := url.Parse(ds)
	if err != nil {
		return err
	}

	if u.User != nil {
		c["user"] = u.User.Username()
	} else {
		c["user"] = DefaultUsername
	}

	if strings.IndexRune(u.Host, ':') == -1 {
		c["addr"] = u.Host + ":" + DefaultPort
	} else {
		c["addr"] = u.Host
	}

	c["catalog"] = DefaultCatalog
	c["schema"] = DefaultSchema

	pathSegments := strings.FieldsFunc(u.Path, func(c rune) bool { return c == '/' })
	if len(pathSegments) > 0 {
		c["catalog"] = pathSegments[0]
	}
	if len(pathSegments) > 1 {
		c["schema"] = pathSegments[1]
	}
	return nil
}

type valueConverterFunc func(v interface{}) (driver.Value, error)

func (fn valueConverterFunc) ConvertValue(v interface{}) (driver.Value, error) {
	return fn(v)
}

// bigIntConverter converts a value from the underlying json response into an int64.
// The Go JSON decoder uses float64 for generic numeric values
var bigIntConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}

	if vv, ok := val.(float64); ok {
		return int64(vv), nil
	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type int64", DriverName, val, val)
})

// doubleConverter converts a value from the underlying json response into an int64.
// The Go JSON decoder uses float64 for generic numeric values
var doubleConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}

	switch vv := val.(type) {
	case float64:
		return vv, nil
	case string:
		switch vv {
		case "Infinity":
			return math.Inf(1), nil
		case "NaN":
			return math.NaN(), nil
		}

	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type float64", DriverName, val, val)
})

// timestampConverter converts a value from the underlying json response into a time.Time.
var timestampConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}
	if vv, ok := val.(string); ok {
		// BUG: should parse using session time zone.
		if ts, err := time.ParseInLocation(TimestampFormat, vv, time.Local); err == nil {
			return ts, nil
		}
	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type time.Time", DriverName, val, val)
})

// timestampWithTimezoneConverter converts a value from the underlying json response into a time.Time including timezone.
var timestampWithTimezoneConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}
	if vv, ok := val.(string); ok {
		if len(vv) <= len(TimestampFormat) {
			return timestampConverter(val)
		}
		tzOffset := strings.LastIndex(vv, " ")
		if tzOffset == -1 {
			return timestampConverter(val)
		}
		tz, err := time.LoadLocation(strings.TrimSpace(vv[tzOffset:]))
		if err != nil {
			return nil, err
		}
		ts, err := time.ParseInLocation(TimestampFormat, vv[:tzOffset], tz)
		if err != nil {
			return nil, err
		}
		return ts, nil
	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type time.Time", DriverName, val, val)
})
<commit_msg>conn: fix mismatched types caused by merge<commit_after>package prestgo

import (
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// Name of the driver to use when calling `sql.Open`
const DriverName = "prestgo"

// Default data source parameters
const (
	DefaultPort     = "8080"
	DefaultCatalog  = "hive"
	DefaultSchema   = "default"
	DefaultUsername = "prestgo"

	TimestampFormat = "2006-01-02 15:04:05.000"
)

var (
	// ErrNotSupported is returned when an unsupported feature is requested.
	ErrNotSupported = errors.New(DriverName + ": not supported")

	// ErrQueryFailed indicates that a network or server failure prevented the driver obtaining a query result.
	ErrQueryFailed = errors.New(DriverName + ": query failed")

	// ErrQueryCanceled indicates that a query was canceled before results could be retrieved.
	ErrQueryCanceled = errors.New(DriverName + ": query canceled")
)

func init() {
	sql.Register(DriverName, &drv{})
}

type drv struct{}

func (*drv) Open(name string) (driver.Conn, error) {
	return Open(name)
}

// Open creates a connection to the specified data source name which should be
// of the form "presto://hostname:port/catalog/schema". http.DefaultClient will
// be used for communicating with the Presto server.
func Open(name string) (driver.Conn, error) {
	return ClientOpen(http.DefaultClient, name)
}

// ClientOpen creates a connection to the specified data source name using the supplied
// HTTP client. The data source name should be of the form
// "presto://hostname:port/catalog/schema".
func ClientOpen(client *http.Client, name string) (driver.Conn, error) {

	conf := make(config)
	conf.parseDataSource(name)

	cn := &conn{
		client:  client,
		addr:    conf["addr"],
		catalog: conf["catalog"],
		schema:  conf["schema"],
		user:    conf["user"],
	}
	return cn, nil
}

type conn struct {
	client  *http.Client
	addr    string
	catalog string
	schema  string
	user    string
}

var _ driver.Conn = &conn{}

func (c *conn) Prepare(query string) (driver.Stmt, error) {
	st := &stmt{
		conn:  c,
		query: query,
	}
	return st, nil
}

func (c *conn) Close() error {
	return nil
}

func (c *conn) Begin() (driver.Tx, error) {
	return nil, ErrNotSupported
}

type stmt struct {
	conn  *conn
	query string
}

var _ driver.Stmt = &stmt{}

func (s *stmt) Close() error {
	return nil
}

func (s *stmt) NumInput() int {
	return -1 // TODO: parse query for parameters
}

func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	return nil, ErrNotSupported
}

func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	// TODO: support query argument substitution
	if len(args) > 0 {
		return nil, ErrNotSupported
	}
	queryURL := fmt.Sprintf("http://%s/v1/statement", s.conn.addr)

	req, err := http.NewRequest("POST", queryURL, strings.NewReader(s.query))
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Presto-User", s.conn.user)
	req.Header.Add("X-Presto-Catalog", s.conn.catalog)
	req.Header.Add("X-Presto-Schema", s.conn.schema)

	resp, err := s.conn.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Presto doesn't use the http response code, parse errors come back as 200
	if resp.StatusCode != 200 {
		return nil, ErrQueryFailed
	}

	var sresp stmtResponse
	err = json.NewDecoder(resp.Body).Decode(&sresp)
	if err != nil {
		return nil, err
	}

	if sresp.Stats.State == "FAILED" {
		return nil, sresp.Error
	}

	time.Sleep(500 * time.Millisecond)

	r := &rows{
		conn:    s.conn,
		nextURI: sresp.NextURI,
	}

	return r, nil
}

type rows struct {
	conn     *conn
	nextURI  string
	fetched  bool
	rowindex int
	columns  []string
	types    []driver.ValueConverter
	data     []queryData
}

var _ driver.Rows = &rows{}

func (r *rows) fetch() error {
	// TODO: timeout
	for {
		qresp, gotData, err := r.waitForData()
		if err != nil {
			return err
		}
		if !gotData {
			time.Sleep(800 * time.Millisecond) // TODO: make this interval configurable
			continue
		}

		r.rowindex = 0
		r.data = qresp.Data

		// Note: qresp.Stats.State will be FINISHED when last page is retrieved
		r.nextURI = qresp.NextURI

		if !r.fetched {
			r.columns = make([]string, len(qresp.Columns))
			r.types = make([]driver.ValueConverter, len(qresp.Columns))
			for i, col := range qresp.Columns {
				r.columns[i] = col.Name
				switch {
				case strings.HasPrefix(col.Type, VarChar):
					r.types[i] = driver.String
				case col.Type == BigInt, col.Type == Integer:
					r.types[i] = bigIntConverter
				case col.Type == Boolean:
					r.types[i] = driver.Bool
				case col.Type == Double:
					r.types[i] = doubleConverter
				case col.Type == Timestamp:
					r.types[i] = timestampConverter
				case col.Type == TimestampWithTimezone:
					r.types[i] = timestampWithTimezoneConverter

				default:
					return fmt.Errorf("unsupported column type: %s", col.Type)
				}
			}
			r.fetched = true
		}

		if len(qresp.Data) == 0 {
			return io.EOF
		}

		return nil
	}
}

func (r *rows) waitForData() (*queryResponse, bool, error) {
	nextReq, err := http.NewRequest("GET", r.nextURI, nil)
	if err != nil {
		return nil, false, err
	}

	nextResp, err := r.conn.client.Do(nextReq)
	if err != nil {
		return nil, false, err
	}

	if nextResp.StatusCode != 200 {
		nextResp.Body.Close()
		return nil, false, ErrQueryFailed
	}

	var qresp queryResponse
	err = json.NewDecoder(nextResp.Body).Decode(&qresp)
	nextResp.Body.Close()
	if err != nil {
		return nil, false, err
	}

	switch qresp.Stats.State {
	case QueryStateFailed:
		return nil, false, qresp.Error
	case QueryStateCanceled:
		return nil, false, ErrQueryCanceled
	case QueryStatePlanning, QueryStateQueued, QueryStateRunning, QueryStateStarting:
		if len(qresp.Data) == 0 {
			r.nextURI = qresp.NextURI
			return nil, false, nil
		}
	}

	return &qresp, true, nil
}

func (r *rows) Columns() []string {
	if !r.fetched {
		if err := r.fetch(); err != nil {
			return []string{}
		}
	}
	return r.columns
}

func (r *rows) Close() error {
	return nil
}

func (r *rows) Next(dest []driver.Value) error {
	if !r.fetched || r.rowindex >= len(r.data) {
		if r.nextURI == "" {
			return io.EOF
		}
		if err := r.fetch(); err != nil {
			return err
		}
	}

	for i, v := range r.types {
		val, err := v.ConvertValue(r.data[r.rowindex][i])
		if err != nil {
			return err // TODO: more context in error
		}
		dest[i] = val
	}
	r.rowindex++
	return nil
}

type config map[string]string

func (c config) parseDataSource(ds string) error {
	u, err := url.Parse(ds)
	if err != nil {
		return err
	}

	if u.User != nil {
		c["user"] = u.User.Username()
	} else {
		c["user"] = DefaultUsername
	}

	if strings.IndexRune(u.Host, ':') == -1 {
		c["addr"] = u.Host + ":" + DefaultPort
	} else {
		c["addr"] = u.Host
	}

	c["catalog"] = DefaultCatalog
	c["schema"] = DefaultSchema

	pathSegments := strings.FieldsFunc(u.Path, func(c rune) bool { return c == '/' })
	if len(pathSegments) > 0 {
		c["catalog"] = pathSegments[0]
	}
	if len(pathSegments) > 1 {
		c["schema"] = pathSegments[1]
	}
	return nil
}

type valueConverterFunc func(v interface{}) (driver.Value, error)

func (fn valueConverterFunc) ConvertValue(v interface{}) (driver.Value, error) {
	return fn(v)
}

// bigIntConverter converts a value from the underlying json response into an int64.
// The Go JSON decoder uses float64 for generic numeric values
var bigIntConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}

	if vv, ok := val.(float64); ok {
		return int64(vv), nil
	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type int64", DriverName, val, val)
})

// doubleConverter converts a value from the underlying json response into an int64.
// The Go JSON decoder uses float64 for generic numeric values
var doubleConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}

	switch vv := val.(type) {
	case float64:
		return vv, nil
	case string:
		switch vv {
		case "Infinity":
			return math.Inf(1), nil
		case "NaN":
			return math.NaN(), nil
		}

	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type float64", DriverName, val, val)
})

// timestampConverter converts a value from the underlying json response into a time.Time.
var timestampConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}
	if vv, ok := val.(string); ok {
		// BUG: should parse using session time zone.
		if ts, err := time.ParseInLocation(TimestampFormat, vv, time.Local); err == nil {
			return ts, nil
		}
	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type time.Time", DriverName, val, val)
})

// timestampWithTimezoneConverter converts a value from the underlying json response into a time.Time including timezone.
var timestampWithTimezoneConverter = valueConverterFunc(func(val interface{}) (driver.Value, error) {
	if val == nil {
		return nil, nil
	}
	if vv, ok := val.(string); ok {
		if len(vv) <= len(TimestampFormat) {
			return timestampConverter(val)
		}
		tzOffset := strings.LastIndex(vv, " ")
		if tzOffset == -1 {
			return timestampConverter(val)
		}
		tz, err := time.LoadLocation(strings.TrimSpace(vv[tzOffset:]))
		if err != nil {
			return nil, err
		}
		ts, err := time.ParseInLocation(TimestampFormat, vv[:tzOffset], tz)
		if err != nil {
			return nil, err
		}
		return ts, nil
	}
	return nil, fmt.Errorf("%s: failed to convert %v (%T) into type time.Time", DriverName, val, val)
})
<|endoftext|>


<commit_before>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported so everything can be messed with from outside
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst
	Quit chan []byte
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) Send(message string) {
	// Basically just typecasting for convenience
	c.Send <- []byte(message)
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) Quit(message string) {
	c.Quit <- []byte(message)
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}

}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	c.Quit <- []byte("")
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		Quit: make(chan []byte),
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start infinite read/write loop
	c.read_write()
}
<commit_msg>Name change<commit_after>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported so everything can be messed with from outside
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst
	Quit chan []byte
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) Send(message string) {
	// Basically just typecasting for convenience
	c.Send <- []byte(message)
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) CloseMsg(message string) {
	c.Quit <- []byte(message)
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}

}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	c.Quit <- []byte("")
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		Quit: make(chan []byte),
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start infinite read/write loop
	c.read_write()
}
<|endoftext|>