<commit_before>package main

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/systemd"
	"github.com/coreos/go-systemd/dbus"
	"github.com/pborman/uuid"
	"github.com/shirou/gopsutil/host"
)

func (s *statsPusher) bundleHeartbeats() error {
	serial, err := s.getSerial()
	if err != nil {
		return err
	}
	ip, err := s.getIP()
	if err != nil {
		return err
	}
	bundles, err := s.getBundles()
	if err != nil {
		return err
	}
	healthy, err := s.runHealthChecks(bundles)
	if err != nil {
		return err
	}
	return s.sendBundleHeartbeats(healthy, serial, ip)
}

func (s *statsPusher) getBundles() ([]*clusterconf.Bundle, error) {
	requests := make(map[string]*acomm.Request)
	localReq, err := acomm.NewRequest(acomm.RequestOptions{Task: "systemd-list"})
	if err != nil {
		return nil, err
	}
	requests["local"] = localReq
	knownReq, err := acomm.NewRequest(acomm.RequestOptions{
		Task:    "list-bundles",
		TaskURL: s.config.heartbeatURL(),
	})
	requests["known"] = knownReq

	multiRequest := acomm.NewMultiRequest(s.tracker, s.config.requestTimeout())
	for name, req := range requests {
		if err := multiRequest.AddRequest(name, req); err != nil {
			break
		}
		if err := acomm.Send(s.config.coordinatorURL(), req); err != nil {
			multiRequest.RemoveRequest(req)
			break
		}
	}

	responses := multiRequest.Responses()

	var localUnits systemd.ListResult
	if err := responses["local"].UnmarshalResult(&localUnits); err != nil {
		return nil, err
	}
	localBundles := extractBundles(localUnits.Units)
	var knownBundles clusterconf.BundleListResult
	if err := responses["known"].UnmarshalResult(&knownBundles); err != nil {
		return nil, err
	}

	bundles := make([]*clusterconf.Bundle, 0, len(localBundles))
	for _, local := range localBundles {
		for _, known := range knownBundles.Bundles {
			if known.ID == local {
				bundles = append(bundles, known)
				break
			}
		}
	}

	return bundles, nil
}

func (s *statsPusher) getSerial() (string, error) {
	doneChan := make(chan *acomm.Response, 1)
	rh := func(_ *acomm.Request, resp *acomm.Response) {
		doneChan <- resp
	}
	req, err := acomm.NewRequest(acomm.RequestOptions{
		Task:           "metrics-host",
		ResponseHook:   s.tracker.URL(),
		SuccessHandler: rh,
		ErrorHandler:   rh,
	})
	if err != nil {
		return "", err
	}
	if err := s.tracker.TrackRequest(req, s.config.requestTimeout()); err != nil {
		return "", err
	}
	if err := acomm.Send(s.config.coordinatorURL(), req); err != nil {
		return "", err
	}

	resp := <-doneChan
	if resp.Error != nil {
		return "", resp.Error
	}

	var data host.InfoStat
	if err := resp.UnmarshalResult(&data); err != nil {
		return "", err
	}

	return data.Hostname, nil
}

func (s *statsPusher) sendBundleHeartbeats(bundles []uint64, serial string, ip net.IP) error {
	errored := make([]uint64, 0, len(bundles))

	multiRequest := acomm.NewMultiRequest(s.tracker, s.config.requestTimeout())
	for _, bundle := range bundles {
		req, err := acomm.NewRequest(acomm.RequestOptions{
			Task:    "bundle-heartbeat",
			TaskURL: s.config.heartbeatURL(),
			Args: clusterconf.BundleHeartbeatArgs{
				ID:     bundle,
				Serial: serial,
				IP:     ip,
			},
		})
		if err != nil {
			errored = append(errored, bundle)
			continue
		}
		if err := multiRequest.AddRequest(strconv.FormatUint(bundle, 10), req); err != nil {
			errored = append(errored, bundle)
			continue
		}
		if err := acomm.Send(s.config.coordinatorURL(), req); err != nil {
			multiRequest.RemoveRequest(req)
			errored = append(errored, bundle)
			continue
		}
	}

	responses := multiRequest.Responses()
	for name, resp := range responses {
		if resp.Error != nil {
			bundle, _ := strconv.ParseUint(name, 10, 64)
			errored = append(errored, bundle)
			break
		}
	}

	if len(errored) > 0 {
		return fmt.Errorf("one or more bundle heartbeats unsuccessful: %+v", errored)
	}
	return nil
}

// TODO: Make this actually run health checks
func (s *statsPusher) runHealthChecks(bundles []*clusterconf.Bundle) ([]uint64, error) {
	healthy := make([]uint64, len(bundles))
	for i, bundle := range bundles {
		healthy[i] = bundle.ID
	}
	return healthy, nil
}

func extractBundles(units []dbus.UnitStatus) []uint64 {
	dedupe := make(map[uint64]bool)
	for _, unit := range units {
		// bundleID:serviceID
		parts := strings.Split(unit.Name, ":")
		bundleID, err := strconv.ParseUint(parts[0], 10, 64)
		if err == nil && len(parts) == 2 && uuid.Parse(parts[1]) != nil {
			dedupe[bundleID] = true
		}
	}
	ids := make([]uint64, 0, len(dedupe))
	for id := range dedupe {
		ids = append(ids, id)
	}
	return ids
}
<commit_msg>Add issue number to the health check TODO<commit_after>package main

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/systemd"
	"github.com/coreos/go-systemd/dbus"
	"github.com/pborman/uuid"
	"github.com/shirou/gopsutil/host"
)

func (s *statsPusher) bundleHeartbeats() error {
	serial, err := s.getSerial()
	if err != nil {
		return err
	}
	ip, err := s.getIP()
	if err != nil {
		return err
	}
	bundles, err := s.getBundles()
	if err != nil {
		return err
	}
	healthy, err := s.runHealthChecks(bundles)
	if err != nil {
		return err
	}
	return s.sendBundleHeartbeats(healthy, serial, ip)
}

func (s *statsPusher) getBundles() ([]*clusterconf.Bundle, error) {
	requests := make(map[string]*acomm.Request)
	localReq, err := acomm.NewRequest(acomm.RequestOptions{Task: "systemd-list"})
	if err != nil {
		return nil, err
	}
	requests["local"] = localReq
	knownReq, err := acomm.NewRequest(acomm.RequestOptions{
		Task:    "list-bundles",
		TaskURL: s.config.heartbeatURL(),
	})
	requests["known"] = knownReq

	multiRequest := acomm.NewMultiRequest(s.tracker, s.config.requestTimeout())
	for name, req := range requests {
		if err := multiRequest.AddRequest(name, req); err != nil {
			break
		}
		if err := acomm.Send(s.config.coordinatorURL(), req); err != nil {
			multiRequest.RemoveRequest(req)
			break
		}
	}

	responses := multiRequest.Responses()

	var localUnits systemd.ListResult
	if err := responses["local"].UnmarshalResult(&localUnits); err != nil {
		return nil, err
	}
	localBundles := extractBundles(localUnits.Units)
	var knownBundles clusterconf.BundleListResult
	if err := responses["known"].UnmarshalResult(&knownBundles); err != nil {
		return nil, err
	}

	bundles := make([]*clusterconf.Bundle, 0, len(localBundles))
	for _, local := range localBundles {
		for _, known := range knownBundles.Bundles {
			if known.ID == local {
				bundles = append(bundles, known)
				break
			}
		}
	}

	return bundles, nil
}

func (s *statsPusher) getSerial() (string, error) {
	doneChan := make(chan *acomm.Response, 1)
	rh := func(_ *acomm.Request, resp *acomm.Response) {
		doneChan <- resp
	}
	req, err := acomm.NewRequest(acomm.RequestOptions{
		Task:           "metrics-host",
		ResponseHook:   s.tracker.URL(),
		SuccessHandler: rh,
		ErrorHandler:   rh,
	})
	if err != nil {
		return "", err
	}
	if err := s.tracker.TrackRequest(req, s.config.requestTimeout()); err != nil {
		return "", err
	}
	if err := acomm.Send(s.config.coordinatorURL(), req); err != nil {
		return "", err
	}

	resp := <-doneChan
	if resp.Error != nil {
		return "", resp.Error
	}

	var data host.InfoStat
	if err := resp.UnmarshalResult(&data); err != nil {
		return "", err
	}

	return data.Hostname, nil
}

func (s *statsPusher) sendBundleHeartbeats(bundles []uint64, serial string, ip net.IP) error {
	errored := make([]uint64, 0, len(bundles))

	multiRequest := acomm.NewMultiRequest(s.tracker, s.config.requestTimeout())
	for _, bundle := range bundles {
		req, err := acomm.NewRequest(acomm.RequestOptions{
			Task:    "bundle-heartbeat",
			TaskURL: s.config.heartbeatURL(),
			Args: clusterconf.BundleHeartbeatArgs{
				ID:     bundle,
				Serial: serial,
				IP:     ip,
			},
		})
		if err != nil {
			errored = append(errored, bundle)
			continue
		}
		if err := multiRequest.AddRequest(strconv.FormatUint(bundle, 10), req); err != nil {
			errored = append(errored, bundle)
			continue
		}
		if err := acomm.Send(s.config.coordinatorURL(), req); err != nil {
			multiRequest.RemoveRequest(req)
			errored = append(errored, bundle)
			continue
		}
	}

	responses := multiRequest.Responses()
	for name, resp := range responses {
		if resp.Error != nil {
			bundle, _ := strconv.ParseUint(name, 10, 64)
			errored = append(errored, bundle)
			break
		}
	}

	if len(errored) > 0 {
		return fmt.Errorf("one or more bundle heartbeats unsuccessful: %+v", errored)
	}
	return nil
}

// TODO: Make this actually run health checks
// Issue: #189
func (s *statsPusher) runHealthChecks(bundles []*clusterconf.Bundle) ([]uint64, error) {
	healthy := make([]uint64, len(bundles))
	for i, bundle := range bundles {
		healthy[i] = bundle.ID
	}
	return healthy, nil
}

func extractBundles(units []dbus.UnitStatus) []uint64 {
	dedupe := make(map[uint64]bool)
	for _, unit := range units {
		// bundleID:serviceID
		parts := strings.Split(unit.Name, ":")
		bundleID, err := strconv.ParseUint(parts[0], 10, 64)
		if err == nil && len(parts) == 2 && uuid.Parse(parts[1]) != nil {
			dedupe[bundleID] = true
		}
	}
	ids := make([]uint64, 0, len(dedupe))
	for id := range dedupe {
		ids = append(ids, id)
	}
	return ids
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"time"

	"github.com/nsf/termbox-go"
)

func run() {
	f, err := os.Create(gLogPath)
	if err != nil {
		panic(err)
	}
	defer os.Remove(gLogPath)
	defer f.Close()
	log.SetOutput(f)

	log.Print("hi!")

	if err := termbox.Init(); err != nil {
		log.Fatalf("initializing termbox: %s", err)
	}
	defer termbox.Close()

	termbox.SetOutputMode(termbox.Output256)

	app := newApp()

	for _, path := range gConfigPaths {
		if _, err := os.Stat(path); !os.IsNotExist(err) {
			app.readFile(path)
		}
	}

	if err := app.nav.readMarks(); err != nil {
		app.ui.printf("reading marks file: %s", err)
	}

	if err := app.readHistory(); err != nil {
		app.ui.printf("reading history file: %s", err)
	}

	app.loop()
}

func readExpr() <-chan expr {
	ch := make(chan expr)

	go func() {
		duration := 1 * time.Second

		c, err := net.Dial(gSocketProt, gSocketPath)
		for err != nil {
			log.Printf(fmt.Sprintf("connecting server: %s", err))
			c, err = net.Dial(gSocketProt, gSocketPath)
			time.Sleep(duration)
			duration *= 2
		}

		fmt.Fprintf(c, "conn %d\n", gClientID)

		ch <- &callExpr{"sync", nil, 1}

		s := bufio.NewScanner(c)
		for s.Scan() {
			log.Printf("recv: %s", s.Text())
			p := newParser(strings.NewReader(s.Text()))
			if p.parse() {
				ch <- p.expr
			}
		}

		c.Close()
	}()

	return ch
}

func saveFiles(list []string, cp bool) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to save files: %s", err)
	}
	defer c.Close()

	log.Printf("saving files: %v", list)

	fmt.Fprintln(c, "save")

	if cp {
		fmt.Fprintln(c, "copy")
	} else {
		fmt.Fprintln(c, "move")
	}

	for _, f := range list {
		fmt.Fprintln(c, f)
	}
	fmt.Fprintln(c)

	return nil
}

func loadFiles() (list []string, cp bool, err error) {
	c, e := net.Dial(gSocketProt, gSocketPath)
	if e != nil {
		err = fmt.Errorf("dialing to load files: %s", e)
		return
	}
	defer c.Close()

	fmt.Fprintln(c, "load")

	s := bufio.NewScanner(c)

	s.Scan()

	switch s.Text() {
	case "copy":
		cp = true
	case "move":
		cp = false
	default:
		err = fmt.Errorf("unexpected option to copy file(s): %s", s.Text())
		return
	}

	for s.Scan() && s.Text() != "" {
		list = append(list, s.Text())
	}

	if s.Err() != nil {
		err = fmt.Errorf("scanning file list: %s", s.Err())
		return
	}

	log.Printf("loading files: %v", list)

	return
}

func sendRemote(cmd string) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to send server: %s", err)
	}

	fmt.Fprintln(c, cmd)

	// XXX: Standard net.Conn interface does not include a CloseWrite method
	// but net.UnixConn and net.TCPConn implement it so the following should be
	// safe as long as we do not use other types of connections. We need
	// CloseWrite to notify the server that this is not a persistent connection
	// and it should be closed after the response.
	if v, ok := c.(interface {
		CloseWrite() error
	}); ok {
		v.CloseWrite()
	}

	io.Copy(os.Stdout, c)

	c.Close()

	return nil
}
<commit_msg>try to reconnect after sleep<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"time"

	"github.com/nsf/termbox-go"
)

func run() {
	f, err := os.Create(gLogPath)
	if err != nil {
		panic(err)
	}
	defer os.Remove(gLogPath)
	defer f.Close()
	log.SetOutput(f)

	log.Print("hi!")

	if err := termbox.Init(); err != nil {
		log.Fatalf("initializing termbox: %s", err)
	}
	defer termbox.Close()

	termbox.SetOutputMode(termbox.Output256)

	app := newApp()

	for _, path := range gConfigPaths {
		if _, err := os.Stat(path); !os.IsNotExist(err) {
			app.readFile(path)
		}
	}

	if err := app.nav.readMarks(); err != nil {
		app.ui.printf("reading marks file: %s", err)
	}

	if err := app.readHistory(); err != nil {
		app.ui.printf("reading history file: %s", err)
	}

	app.loop()
}

func readExpr() <-chan expr {
	ch := make(chan expr)

	go func() {
		duration := 1 * time.Second

		c, err := net.Dial(gSocketProt, gSocketPath)
		for err != nil {
			log.Printf(fmt.Sprintf("connecting server: %s", err))
			time.Sleep(duration)
			duration *= 2
			c, err = net.Dial(gSocketProt, gSocketPath)
		}

		fmt.Fprintf(c, "conn %d\n", gClientID)

		ch <- &callExpr{"sync", nil, 1}

		s := bufio.NewScanner(c)
		for s.Scan() {
			log.Printf("recv: %s", s.Text())
			p := newParser(strings.NewReader(s.Text()))
			if p.parse() {
				ch <- p.expr
			}
		}

		c.Close()
	}()

	return ch
}

func saveFiles(list []string, cp bool) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to save files: %s", err)
	}
	defer c.Close()

	log.Printf("saving files: %v", list)

	fmt.Fprintln(c, "save")

	if cp {
		fmt.Fprintln(c, "copy")
	} else {
		fmt.Fprintln(c, "move")
	}

	for _, f := range list {
		fmt.Fprintln(c, f)
	}
	fmt.Fprintln(c)

	return nil
}

func loadFiles() (list []string, cp bool, err error) {
	c, e := net.Dial(gSocketProt, gSocketPath)
	if e != nil {
		err = fmt.Errorf("dialing to load files: %s", e)
		return
	}
	defer c.Close()

	fmt.Fprintln(c, "load")

	s := bufio.NewScanner(c)

	s.Scan()

	switch s.Text() {
	case "copy":
		cp = true
	case "move":
		cp = false
	default:
		err = fmt.Errorf("unexpected option to copy file(s): %s", s.Text())
		return
	}

	for s.Scan() && s.Text() != "" {
		list = append(list, s.Text())
	}

	if s.Err() != nil {
		err = fmt.Errorf("scanning file list: %s", s.Err())
		return
	}

	log.Printf("loading files: %v", list)

	return
}

func sendRemote(cmd string) error {
	c, err := net.Dial(gSocketProt, gSocketPath)
	if err != nil {
		return fmt.Errorf("dialing to send server: %s", err)
	}

	fmt.Fprintln(c, cmd)

	// XXX: Standard net.Conn interface does not include a CloseWrite method
	// but net.UnixConn and net.TCPConn implement it so the following should be
	// safe as long as we do not use other types of connections. We need
	// CloseWrite to notify the server that this is not a persistent connection
	// and it should be closed after the response.
	if v, ok := c.(interface {
		CloseWrite() error
	}); ok {
		v.CloseWrite()
	}

	io.Copy(os.Stdout, c)

	c.Close()

	return nil
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"sort"

	"github.com/vdobler/ht/ht"
)

var cmdHelp = &Command{
	RunArgs:     runHelp,
	Usage:       "help [subcommand]",
	Description: "print help information",
	Flag:        flag.NewFlagSet("help", flag.ContinueOnError),
	Help: `
Help shows help for ht as well as for the different subcommands.
Running 'ht help checks' displays the list of builtin checks.
	`,
}

func runHelp(cmd *Command, args []string) {
	if len(args) == 0 {
		usage()
		os.Exit(0)
	}

	if len(args) > 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s\n", cmd.Usage)
		os.Exit(9)
	}

	arg := args[0]
	if arg == "check" || arg == "checks" {
		displayChecks()
	}

	for _, cmd := range commands {
		if cmd.Name() == arg {
			fmt.Printf(`Usage:

    ht %s
%s
Flags:
`, cmd.Usage, cmd.Help)
			cmd.Flag.PrintDefaults()
			os.Exit(0)
		}
	}

	fmt.Fprintf(os.Stderr, "Unknown help topic %#q.  Run 'ht help'.\n", arg)
	os.Exit(9) // failed at 'go help cmd'

}

func displayChecks() {
	checkNames := []string{}
	for name := range ht.CheckRegistry {
		checkNames = append(checkNames, name)
	}
	sort.Strings(checkNames)
	for _, name := range checkNames {
		fmt.Printf("%s := {\n", name)
		typ := ht.CheckRegistry[name]
		displayTypeAsPseudoJSON(typ)
		fmt.Printf("}\n\n")
	}
	fmt.Printf("Condition := {\n")
	displayTypeAsPseudoJSON(reflect.TypeOf(ht.Condition{}))
	fmt.Printf("}\n\n")
	os.Exit(0)
}

func displayTypeAsPseudoJSON(typ reflect.Type) {
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}
	for f := 0; f < typ.NumField(); f++ {
		field := typ.Field(f)
		c := field.Name[0]
		if c < 'A' || c > 'Z' {
			continue
		}
		fmt.Printf("    %s: ", field.Name)
		switch field.Type.Kind() {
		case reflect.Slice:
			e := field.Type.Elem()
			fmt.Printf("[ %s... ],\n", e.Name())
		case reflect.Map:
			fmt.Printf("{ ... },\n")
		default:
			fmt.Printf("%s,\n", field.Type.Name())
		}

	}
}
<commit_msg>cmd/ht: add list of extractors to help<commit_after>// Copyright 2015 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"sort"

	"github.com/vdobler/ht/ht"
)

var cmdHelp = &Command{
	RunArgs:     runHelp,
	Usage:       "help [subcommand]",
	Description: "print help information",
	Flag:        flag.NewFlagSet("help", flag.ContinueOnError),
	Help: `
Help shows help for ht as well as for the different subcommands.
Running 'ht help checks' displays the list of builtin checks and
'ht help extractors' displays the builtin variable extractors.
Running 'ht help doc <type>' displays detail information of <type>.
`,
}

func runHelp(cmd *Command, args []string) {
	if len(args) == 0 {
		usage()
		os.Exit(0)
	}

	if len(args) > 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s\n", cmd.Usage)
		os.Exit(9)
	}

	arg := args[0]
	if arg == "check" || arg == "checks" {
		displayChecks()
	}
	if arg == "extractor" || arg == "extractors" {
		displayExtractors()
	}

	for _, cmd := range commands {
		if cmd.Name() == arg {
			fmt.Printf(`Usage:

    ht %s
%s
Flags:
`, cmd.Usage, cmd.Help)
			cmd.Flag.PrintDefaults()
			os.Exit(0)
		}
	}

	fmt.Fprintf(os.Stderr, "Unknown help topic %#q.  Run 'ht help'.\n", arg)
	os.Exit(9) // failed at 'go help cmd'

}

func displayChecks() {
	checkNames := []string{}
	for name := range ht.CheckRegistry {
		checkNames = append(checkNames, name)
	}
	sort.Strings(checkNames)
	for _, name := range checkNames {
		fmt.Printf("%s := {\n", name)
		typ := ht.CheckRegistry[name]
		displayTypeAsPseudoJSON(typ)
		fmt.Printf("}\n\n")
	}
	fmt.Printf("Condition := {\n")
	displayTypeAsPseudoJSON(reflect.TypeOf(ht.Condition{}))
	fmt.Printf("}\n\n")
	os.Exit(0)
}

func displayExtractors() {
	exNames := []string{}
	for name := range ht.ExtractorRegistry {
		exNames = append(exNames, name)
	}
	sort.Strings(exNames)
	for _, name := range exNames {
		fmt.Printf("%s := {\n", name)
		typ := ht.ExtractorRegistry[name]
		displayTypeAsPseudoJSON(typ)
		fmt.Printf("}\n\n")
	}
	os.Exit(0)
}

func displayTypeAsPseudoJSON(typ reflect.Type) {
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}
	for f := 0; f < typ.NumField(); f++ {
		field := typ.Field(f)
		c := field.Name[0]
		if c < 'A' || c > 'Z' {
			continue
		}
		fmt.Printf("    %s: ", field.Name)
		switch field.Type.Kind() {
		case reflect.Slice:
			e := field.Type.Elem()
			fmt.Printf("[ %s... ],\n", e.Name())
		case reflect.Map:
			fmt.Printf("{ ... },\n")
		default:
			fmt.Printf("%s,\n", field.Type.Name())
		}

	}
}
<|endoftext|>


<commit_before>package gols

import (
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
)

// entry is one layer in a symbol table.
// Don't put lists in here -> runtime panic.
// Are nums allowed?  Can we limit the keys to strings?
type entry map[interface{}]interface{}

// lookup finds the value of a name in an entry.
func (e entry) lookup(name interface{}) (interface{}, bool) {
	if res, ok := e[name]; ok {
		return res, true
	}
	return nil, false
}

// table is a symbol table.
type table []entry

// lookup finds the value of a name in a table.
func (t table) lookup(name interface{}) (interface{}, bool) {
	for _, e := range t {
		if val, ok := e.lookup(name); ok {
			return val, true
		}
	}
	return nil, false
}

func quoteAction(list []interface{}, t table) (interface{}, error) {
	if len(list) != 2 {
		return nil, errors.New("quote must be a list with two elements")
	} else {
		return list[1], nil
	}
}

func identifierAction(sexp interface{}, t table) (interface{}, error) {
	if name, ok := sexp.(string); !ok {
		// is this a bug in the interpreter?
		return nil, errors.New("identifiers must be atoms")
	} else if val, ok := t.lookup(name); !ok {
		return nil, fmt.Errorf("unrecognized identifier: %q", name)
	} else {
		return val, nil
	}
}

func lambdaAction(lambda []interface{}, t table) (interface{}, error) {
	if len(lambda) != 3 {
		return nil, errors.New("lambda requires a list with three elements")
	}
	return newLambda(t, lambda[1], lambda[2])
}

func condAction(cond []interface{}, t table) (interface{}, error) {
	lines := cond[1:] // skip "cond" keyword
	for _, line := range lines {
		if cline, ok := line.([]interface{}); !ok {
			return nil, errors.New("cond lines must be lists")
		} else if len(cline) != 2 {
			return nil, errors.New("cond lines must be lists with two elements")
		} else if cline[0] == "else" {
			return meaning(cline[1], t)
		} else {
			matches, err := meaning(cline[0], t)
			if err != nil {
				return nil, err
			}
			// Only place where booleans are significant in
			// the language?
			// Is it an error if the meaning isn't boolean?
			if matches == "#t" {
				return meaning(cline[1], t)
			}
		}
		// do we want to validate the syntax of what comes after
		// a match?  eg, missing else, stuff after an else, etc
	}
	return nil, errors.New("cond must have an else line")
}

func applicationAction(list []interface{}, t table) (interface{}, error) {
	if len(list) == 0 {
		return nil, errors.New("application requires a non-empty list")
	}

	fMeaning, err := meaning(list[0], t)
	if err != nil {
		return nil, err
	}

	args := list[1:]
	argVals := []interface{}{}
	for _, arg := range args {
		argVal, err := meaning(arg, t)
		if err != nil {
			return nil, err
		}
		argVals = append(argVals, argVal)
	}

	// either (primitive foo) or lambda
	if f, ok := fMeaning.([]interface{}); ok {
		if f[0] != "primitive" {
			return nil, fmt.Errorf("unsupported application type: %q", f[0])
		}
		if name, ok := f[1].(string); !ok {
			return nil, errors.New("name of primitive function must be a string")
		} else {
			return applyPrimitive(name, argVals)
		}
	} else if lambda, ok := fMeaning.(*lambda); ok {
		return lambda.meaning(argVals)
	} else {
		// interpreter bug
		return nil, fmt.Errorf("unsupported application type: %T", fMeaning)
	}
}

func meaning(sexp interface{}, t table) (interface{}, error) {
	if list, ok := sexp.([]interface{}); ok {
		if len(list) > 0 {
			if first, ok := list[0].(string); ok {
				switch first {
				case "quote":
					return quoteAction(list, t)
				case "lambda":
					return lambdaAction(list, t)
				case "cond":
					return condAction(list, t)
				}
			}
		}
		// applicationAction is going to have to do quite a
		// lot of error handling!
		return applicationAction(list, t)
	} else {
		if num, ok := sexp.(uint64); ok {
			return num, nil
		}
		switch sexp {
		case "#t", "#f":
			return sexp, nil
		case "cons", "car", "cdr",
			"null?", "eq?", "atom?",
			"zero?", "add1", "sub1",
			"number?":
			return []interface{}{"primitive", sexp}, nil
		default:
			return identifierAction(sexp, t)
		}
	}
}

func value(sexp interface{}) (interface{}, error) {
	return meaning(sexp, table([]entry{}))
}

// applyPrimitive applies a primitive function.
func applyPrimitive(name string, vals []interface{}) (interface{}, error) {
	bToSexp := func(b bool) interface{} {
		if b {
			return "#t"
		}
		return "#f"
	}

	switch name {
	case "cons":
		if len(vals) != 2 {
			return nil, errors.New("cons takes two arguments")
		} else if to, ok := vals[1].([]interface{}); !ok {
			return nil, errors.New("second argument to cons must be a list")
		} else {
			return append([]interface{}{vals[0]}, to...), nil
		}
	case "car":
		if len(vals) != 1 {
			return nil, errors.New("car takes one argument")
		} else if from, ok := vals[0].([]interface{}); !ok {
			return nil, errors.New("car takes one list")
		} else if len(from) < 1 {
			return nil, errors.New("cannot take car of empty list")
		} else {
			return from[0], nil
		}
	case "cdr":
		if len(vals) != 1 {
			return nil, errors.New("cdr takes one argument")
		} else if from, ok := vals[0].([]interface{}); !ok {
			return nil, errors.New("cdr takes one list")
		} else if len(from) < 1 {
			return nil, errors.New("cannot take cdr of empty list")
		} else {
			return from[1:], nil
		}
	case "null?":
		if len(vals) != 1 {
			return nil, errors.New("null? takes one argument")
		} else if from, ok := vals[0].([]interface{}); !ok {
			return nil, errors.New("null? takes one list")
		} else {
			return bToSexp(len(from) == 0), nil
		}
	case "eq?":
		if len(vals) != 2 {
			return nil, errors.New("eq? takes two arguments")
		} else if first, ok := vals[0].(string); !ok {
			return nil, errors.New("eq? takes two atoms")
		} else if second, ok := vals[1].(string); !ok {
			return nil, errors.New("eq? takes two atoms")
		} else {
			return bToSexp(first == second), nil
		}
	case "atom?":
		if len(vals) != 1 {
			return nil, errors.New("atom? takes one argument")
		}
		// Hmm, support for (primitive x) and (non-privitive x)?
		// The book suggests these are atoms.  How do we hit that case?
		_, ok := vals[0].([]interface{})
		return bToSexp(!ok), nil
	case "zero?":
		if len(vals) != 1 {
			return nil, errors.New("zero? takes one argument")
		} else if num, ok := vals[0].(uint64); !ok {
			return nil, errors.New("zero? takes one number")
		} else {
			return bToSexp(num == 0), nil
		}
	case "add1":
		if len(vals) != 1 {
			return nil, errors.New("add1 takes one argument")
		} else if num, ok := vals[0].(uint64); !ok {
			return nil, errors.New("add1 takes one number")
		} else if num == math.MaxUint64 {
			return nil, errors.New("add1 would cause overflow")
		} else {
			return num + 1, nil
		}
	case "sub1":
		if len(vals) != 1 {
			return nil, errors.New("sub1 takes one argument")
		} else if num, ok := vals[0].(uint64); !ok {
			return nil, errors.New("sub1 takes one number")
		} else if num == 0 {
			return nil, errors.New("sub1 would cause underflow")
		} else {
			return num - 1, nil
		}
	case "number?":
		if len(vals) != 1 {
			return nil, errors.New("number? takes one argument")
		}
		_, ok := vals[0].(uint64)
		return bToSexp(ok), nil
	default:
		return nil, fmt.Errorf("unknown primitive: %q", name)
	}
}

// parsing implementation below copied from http://norvig.com/lispy.html

// tokenize tokenizes an s-expression where only unicode whitespace and
// ()s are considered significant.
func tokenize(src string) []string {
	src = strings.Replace(src, "(", " ( ", -1)
	src = strings.Replace(src, ")", " ) ", -1)
	return strings.Fields(src)
}

// readFromTokens builds an abstract syntax tree from a list of tokens.
// Atoms are either a string or a uint64.  Lists are a []interface{}.
// TODO: consider #f and #t as bool types?
func readFromTokens(tokens []string) (interface{}, []string, error) {
	if len(tokens) == 0 {
		return nil, nil, errors.New("unexpected EOF")
	}

	token := tokens[0]
	tokens = tokens[1:]

	switch token {
	case "(":
		l := []interface{}{} // NB: empty list, not nil
		for len(tokens) > 0 && tokens[0] != ")" {
			sexp, remainder, err := readFromTokens(tokens)
			if err != nil {
				return nil, nil, err
			}
			tokens = remainder
			l = append(l, sexp)
		}
		if len(tokens) < 1 {
			return nil, nil, errors.New("unfinished list")
		}
		return l, tokens[1:], nil
	case ")":
		return nil, nil, errors.New("unexpected )")
	default:
		if num, err := strconv.ParseUint(token, 10, 64); err != nil {
			return token, tokens, nil
		} else {
			return num, tokens, nil
		}
	}
}

// parse tokenizes and builds a syntax tree from an s-expression.
func parse(src string) (interface{}, error) {
	tokens := tokenize(src)
	ast, remainder, err := readFromTokens(tokens)
	if err != nil {
		return nil, err
	}
	if len(remainder) > 0 {
		return nil, errors.New("unexpected trailing tokens")
	}
	return ast, nil
}
<commit_msg>give type to primitive functions<commit_after>package gols

import (
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
)

// entry is one layer in a symbol table.
// Don't put lists in here -> runtime panic.
// Are nums allowed?  Can we limit the keys to strings?
type entry map[interface{}]interface{}

// lookup finds the value of a name in an entry.
func (e entry) lookup(name interface{}) (interface{}, bool) {
	if res, ok := e[name]; ok {
		return res, true
	}
	return nil, false
}

// table is a symbol table.
type table []entry

// lookup finds the value of a name in a table.
func (t table) lookup(name interface{}) (interface{}, bool) {
	for _, e := range t {
		if val, ok := e.lookup(name); ok {
			return val, true
		}
	}
	return nil, false
}

func quoteAction(list []interface{}, t table) (interface{}, error) {
	if len(list) != 2 {
		return nil, errors.New("quote must be a list with two elements")
	} else {
		return list[1], nil
	}
}

func identifierAction(sexp interface{}, t table) (interface{}, error) {
	if name, ok := sexp.(string); !ok {
		// is this a bug in the interpreter?
		return nil, errors.New("identifiers must be atoms")
	} else if val, ok := t.lookup(name); !ok {
		return nil, fmt.Errorf("unrecognized identifier: %q", name)
	} else {
		return val, nil
	}
}

func lambdaAction(lambda []interface{}, t table) (interface{}, error) {
	if len(lambda) != 3 {
		return nil, errors.New("lambda requires a list with three elements")
	}
	return newLambda(t, lambda[1], lambda[2])
}

func condAction(cond []interface{}, t table) (interface{}, error) {
	lines := cond[1:] // skip "cond" keyword
	for _, line := range lines {
		if cline, ok := line.([]interface{}); !ok {
			return nil, errors.New("cond lines must be lists")
		} else if len(cline) != 2 {
			return nil, errors.New("cond lines must be lists with two elements")
		} else if cline[0] == "else" {
			return meaning(cline[1], t)
		} else {
			matches, err := meaning(cline[0], t)
			if err != nil {
				return nil, err
			}
			// Only place where booleans are significant in
			// the language?
			// Is it an error if the meaning isn't boolean?
			if matches == "#t" {
				return meaning(cline[1], t)
			}
		}
		// do we want to validate the syntax of what comes after
		// a match?  eg, missing else, stuff after an else, etc
	}
	return nil, errors.New("cond must have an else line")
}

func applicationAction(list []interface{}, t table) (interface{}, error) {
	if len(list) == 0 {
		return nil, errors.New("application requires a non-empty list")
	}

	fMeaning, err := meaning(list[0], t)
	if err != nil {
		return nil, err
	}

	type function interface {
		meaning([]interface{}) (interface{}, error)
	}

	f, ok := fMeaning.(function)
	if !ok {
		return nil, fmt.Errorf("unsupported application type: %T", fMeaning)
	}

	args := list[1:]
	argVals := []interface{}{}
	for _, arg := range args {
		argVal, err := meaning(arg, t)
		if err != nil {
			return nil, err
		}
		argVals = append(argVals, argVal)
	}

	return f.meaning(argVals)
}

func meaning(sexp interface{}, t table) (interface{}, error) {
	if list, ok := sexp.([]interface{}); ok {
		if len(list) > 0 {
			if first, ok := list[0].(string); ok {
				switch first {
				case "quote":
					return quoteAction(list, t)
				case "lambda":
					return lambdaAction(list, t)
				case "cond":
					return condAction(list, t)
				}
			}
		}
		// applicationAction is going to have to do quite a
		// lot of error handling!
		return applicationAction(list, t)
	} else {
		if num, ok := sexp.(uint64); ok {
			return num, nil
		}
		switch sexp {
		case "#t", "#f":
			return sexp, nil
		case "cons", "car", "cdr",
			"null?", "eq?", "atom?",
			"zero?", "add1", "sub1",
			"number?":
			return primitive(sexp.(string)), nil
		default:
			return identifierAction(sexp, t)
		}
	}
}

func value(sexp interface{}) (interface{}, error) {
	return meaning(sexp, table([]entry{}))
}

type primitive string

func (p primitive) meaning(args []interface{}) (interface{}, error) {
	return applyPrimitive(string(p), args)
}

// applyPrimitive applies a primitive function.
func applyPrimitive(name string, vals []interface{}) (interface{}, error) {
	bToSexp := func(b bool) interface{} {
		if b {
			return "#t"
		}
		return "#f"
	}

	switch name {
	case "cons":
		if len(vals) != 2 {
			return nil, errors.New("cons takes two arguments")
		} else if to, ok := vals[1].([]interface{}); !ok {
			return nil, errors.New("second argument to cons must be a list")
		} else {
			return append([]interface{}{vals[0]}, to...), nil
		}
	case "car":
		if len(vals) != 1 {
			return nil, errors.New("car takes one argument")
		} else if from, ok := vals[0].([]interface{}); !ok {
			return nil, errors.New("car takes one list")
		} else if len(from) < 1 {
			return nil, errors.New("cannot take car of empty list")
		} else {
			return from[0], nil
		}
	case "cdr":
		if len(vals) != 1 {
			return nil, errors.New("cdr takes one argument")
		} else if from, ok := vals[0].([]interface{}); !ok {
			return nil, errors.New("cdr takes one list")
		} else if len(from) < 1 {
			return nil, errors.New("cannot take cdr of empty list")
		} else {
			return from[1:], nil
		}
	case "null?":
		if len(vals) != 1 {
			return nil, errors.New("null? takes one argument")
		} else if from, ok := vals[0].([]interface{}); !ok {
			return nil, errors.New("null? takes one list")
		} else {
			return bToSexp(len(from) == 0), nil
		}
	case "eq?":
		if len(vals) != 2 {
			return nil, errors.New("eq? takes two arguments")
		} else if first, ok := vals[0].(string); !ok {
			return nil, errors.New("eq? takes two atoms")
		} else if second, ok := vals[1].(string); !ok {
			return nil, errors.New("eq? takes two atoms")
		} else {
			return bToSexp(first == second), nil
		}
	case "atom?":
		if len(vals) != 1 {
			return nil, errors.New("atom? takes one argument")
		}
		// Hmm, support for (primitive x) and (non-privitive x)?
		// The book suggests these are atoms.  How do we hit that case?
		_, ok := vals[0].([]interface{})
		return bToSexp(!ok), nil
	case "zero?":
		if len(vals) != 1 {
			return nil, errors.New("zero? takes one argument")
		} else if num, ok := vals[0].(uint64); !ok {
			return nil, errors.New("zero? takes one number")
		} else {
			return bToSexp(num == 0), nil
		}
	case "add1":
		if len(vals) != 1 {
			return nil, errors.New("add1 takes one argument")
		} else if num, ok := vals[0].(uint64); !ok {
			return nil, errors.New("add1 takes one number")
		} else if num == math.MaxUint64 {
			return nil, errors.New("add1 would cause overflow")
		} else {
			return num + 1, nil
		}
	case "sub1":
		if len(vals) != 1 {
			return nil, errors.New("sub1 takes one argument")
		} else if num, ok := vals[0].(uint64); !ok {
			return nil, errors.New("sub1 takes one number")
		} else if num == 0 {
			return nil, errors.New("sub1 would cause underflow")
		} else {
			return num - 1, nil
		}
	case "number?":
		if len(vals) != 1 {
			return nil, errors.New("number? takes one argument")
		}
		_, ok := vals[0].(uint64)
		return bToSexp(ok), nil
	default:
		return nil, fmt.Errorf("unknown primitive: %q", name)
	}
}

// parsing implementation below copied from http://norvig.com/lispy.html

// tokenize tokenizes an s-expression where only unicode whitespace and
// ()s are considered significant.
func tokenize(src string) []string {
	src = strings.Replace(src, "(", " ( ", -1)
	src = strings.Replace(src, ")", " ) ", -1)
	return strings.Fields(src)
}

// readFromTokens builds an abstract syntax tree from a list of tokens.
// Atoms are either a string or a uint64.  Lists are a []interface{}.
// TODO: consider #f and #t as bool types?
func readFromTokens(tokens []string) (interface{}, []string, error) {
	if len(tokens) == 0 {
		return nil, nil, errors.New("unexpected EOF")
	}

	token := tokens[0]
	tokens = tokens[1:]

	switch token {
	case "(":
		l := []interface{}{} // NB: empty list, not nil
		for len(tokens) > 0 && tokens[0] != ")" {
			sexp, remainder, err := readFromTokens(tokens)
			if err != nil {
				return nil, nil, err
			}
			tokens = remainder
			l = append(l, sexp)
		}
		if len(tokens) < 1 {
			return nil, nil, errors.New("unfinished list")
		}
		return l, tokens[1:], nil
	case ")":
		return nil, nil, errors.New("unexpected )")
	default:
		if num, err := strconv.ParseUint(token, 10, 64); err != nil {
			return token, tokens, nil
		} else {
			return num, tokens, nil
		}
	}
}

// parse tokenizes and builds a syntax tree from an s-expression.
func parse(src string) (interface{}, error) {
	tokens := tokenize(src)
	ast, remainder, err := readFromTokens(tokens)
	if err != nil {
		return nil, err
	}
	if len(remainder) > 0 {
		return nil, errors.New("unexpected trailing tokens")
	}
	return ast, nil
}
<|endoftext|>


<commit_before>package gremgo

import (
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

// Client is a container for the gremgo interface TODO: Fix this doc
type Client struct {
	host       string
	connection bool
	reqchan    chan []byte
	reschan    map[string]chan int
	results    map[string]map[string]interface{}
}

// NewClient returns a gremgo client for database interaction
func NewClient(host string) (c Client, err error) {

	// Initializes client

	c.host = "ws://" + host
	c.reqchan = make(chan []byte, 25)
	c.reschan = make(map[string]chan int)
	c.results = make(map[string]map[string]interface{})
	c.connection = true

	// Connect to websocket

	d := websocket.Dialer{}
	ws, _, err := d.Dial(c.host, http.Header{})

	// Write worker
	go func() {
		for c.connection == true {
			select {
			case msg := <-c.reqchan:
				err = ws.WriteMessage(2, msg)
				if err != nil {
					log.Fatal(err)
				}
			default:
			}
		}
	}()

	// Read worker
	go func() {
		for c.connection == true {
			_, msg, err := ws.ReadMessage()
			if err != nil {
				log.Fatal(err)
			}
			if msg != nil {
				go sortResponse(&c, msg) // Send data to sorter
			}
		}
	}()

	return
}
<commit_msg>Update client.go<commit_after>package gremgo

import (
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

// Client is a container for the gremgo interface TODO: Fix this doc
type Client struct {
	host       string
	connection bool
	reqchan    chan []byte
	reschan    map[string]chan int
	results    map[string]map[string]interface{}
}

// NewClient returns a gremgo client for database interaction
func NewClient(host string) (c Client, err error) {

	// Initializes client

	c.host = "ws://" + host
	c.reqchan = make(chan []byte, 1)
	c.reschan = make(map[string]chan int)
	c.results = make(map[string]map[string]interface{})
	c.connection = true

	// Connect to websocket

	d := websocket.Dialer{}
	ws, _, err := d.Dial(c.host, http.Header{})

	// Write worker
	go func() {
		for c.connection == true {
			select {
			case msg := <-c.reqchan:
				err = ws.WriteMessage(2, msg)
				if err != nil {
					log.Fatal(err)
				}
			default:
			}
		}
	}()

	// Read worker
	go func() {
		for c.connection == true {
			_, msg, err := ws.ReadMessage()
			if err != nil {
				log.Fatal(err)
			}
			if msg != nil {
				go sortResponse(&c, msg) // Send data to sorter
			}
		}
	}()

	return
}
<|endoftext|>


<commit_before>//go:build ignore
// +build ignore

/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
 *
 * 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,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func getcwd() string {
	path, _ := os.Getwd()
	return path
}

func checkPathExist(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

var gopRoot = getcwd()
var initCommandExecuteEnv = os.Environ()
var commandExecuteEnv = initCommandExecuteEnv

func execCommand(command string, arg ...string) (string, string, error) {
	var stdout, stderr bytes.Buffer
	cmd := exec.Command(command, arg...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Env = commandExecuteEnv
	err := cmd.Run()
	return stdout.String(), stderr.String(), err
}

func getRevCommit(tag string) string {
	commit, stderr, err := execCommand("git", "rev-parse", "--verify", tag)
	if err != nil || stderr != "" {
		return ""
	}
	return strings.TrimRight(commit, "\n")
}

func getGitInfo() (string, bool) {
	gitDir := filepath.Join(gopRoot, ".git")
	if checkPathExist(gitDir) {
		return getRevCommit("HEAD"), true
	}
	return "", false
}

func getBuildDateTime() string {
	now := time.Now()
	return now.Format("2006-01-02_15-04-05")
}

func getBuildVer() string {
	tagRet, tagErr, err := execCommand("git", "describe", "--tags")
	if err != nil || tagErr != "" {
		return ""
	}
	return strings.TrimRight(tagRet, "\n")
}

/*
func findTag(commit string) string {
	tagRet, tagErr, err := execCommand("git", "tag")
	if err != nil || tagErr != "" {
		return ""
	}
	var prefix = "v" + env.MainVersion + "."
	for _, tag := range strings.Split(tagRet, "\n") {
		if strings.HasPrefix(tag, prefix) {
			if getRevCommit(tag) == commit {
				return tag
			}
		}
	}
	return ""
}
*/

func getGopBuildFlags() string {
	defaultGopRoot := gopRoot
	if gopRootFinal := os.Getenv("GOPROOT_FINAL"); gopRootFinal != "" {
		defaultGopRoot = gopRootFinal
	}
	buildFlags := fmt.Sprintf("-X github.com/goplus/gop/env.defaultGopRoot=%s", defaultGopRoot)
	buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildDate=%s", getBuildDateTime())
	if commit, ok := getGitInfo(); ok {
		buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildCommit=%s", commit)
		if buildVer := getBuildVer(); buildVer != "" {
			buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildVersion=%s", buildVer)
		}
	}
	return buildFlags
}

/*
	func detectGoBinPath() string {
		goBin, ok := os.LookupEnv("GOBIN")
		if ok {
			return goBin
		}

		goPath, ok := os.LookupEnv("GOPATH")
		if ok {
			return filepath.Join(goPath, "bin")
		}

		homeDir, _ := os.UserHomeDir()
		return filepath.Join(homeDir, "go", "bin")
	}
*/

func detectGopBinPath() string {
	return filepath.Join(gopRoot, "bin")
}

func buildGoplusTools(useGoProxy bool) {
	commandsDir := filepath.Join(gopRoot, "cmd")
	if !checkPathExist(commandsDir) {
		println("Error: This script should be run at the root directory of gop repository.")
		os.Exit(1)
	}

	buildFlags := getGopBuildFlags()

	if useGoProxy {
		println("Info: we will use goproxy.cn as a Go proxy to accelerate installing process.")
		commandExecuteEnv = append(commandExecuteEnv,
			"GOPROXY=https://goproxy.cn,direct",
		)
	}

	// Install Go+ binary files under current ./bin directory.
	commandExecuteEnv = append(commandExecuteEnv, "GOBIN="+detectGopBinPath())

	println("Installing Go+ tools...")
	os.Chdir(commandsDir)
	buildOutput, buildErr, err := execCommand("go", "install", "-v", "-ldflags", buildFlags, "./...")
	println(buildErr)
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
	println(buildOutput)

	println("Go+ tools installed successfully!")
	showHelpPostInstall()
}

func showHelpPostInstall() {
	println("Next:")
	println("We just installed Go+ into the directory: ", detectGopBinPath())
	message := `
To setup a better Go+ development environment,
we recommend you add the above install directory into your PATH environment variable.
	`
	println(message)
}

func runTestcases() {
	println("Start running testcases.")
	os.Chdir(gopRoot)

	coverage := "-coverprofile=coverage.txt"
	gopCommand := filepath.Join(detectGopBinPath(), "gop")
	testOutput, testErr, err := execCommand(gopCommand, "test", coverage, "-covermode=atomic", "./...")
	println(testOutput)
	println(testErr)
	if err != nil {
		println(err.Error())
	}

	println("End running testcases.")
}

func uninstall() {
	println("Uninstalling Go+ and related tools.")

	gopBinPath := detectGopBinPath()
	if checkPathExist(gopBinPath) {
		if err := os.RemoveAll(gopBinPath); err != nil {
			println(err.Error())
		}
	}

	println("Go+ and related tools uninstalled successfully.")
}

func isInChina() bool {
	const prefix = "LANG=\""
	out, errMsg, err := execCommand("locale")
	if err != nil || errMsg != "" {
		return false
	}
	if strings.HasPrefix(out, prefix) {
		out = out[len(prefix):]
		return strings.HasPrefix(out, "zh_CN") || strings.HasPrefix(out, "zh_HK")
	}
	return false
}

func main() {
	isInstall := flag.Bool("install", false, "Install Go+")
	isTest := flag.Bool("test", false, "Run testcases")
	isUninstall := flag.Bool("uninstall", false, "Uninstall Go+")
	isGoProxy := flag.Bool("proxy", false, "Set GOPROXY for people in China")
	isAutoProxy := flag.Bool("autoproxy", false, "Check to set GOPROXY automatically")

	flag.Parse()

	useGoProxy := *isGoProxy
	if !useGoProxy && *isAutoProxy {
		useGoProxy = isInChina()
	}
	flagActionMap := map[*bool]func(){
		isInstall:   func() { buildGoplusTools(useGoProxy) },
		isUninstall: uninstall,
		isTest:      runTestcases,
	}

	for flag, action := range flagActionMap {
		if *flag {
			action()
			return
		}
	}

	println("Usage:\n")
	flag.PrintDefaults()
}
<commit_msg>Fix build to the bin/ directory<commit_after>//go:build ignore
// +build ignore

/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
 *
 * 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,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func getcwd() string {
	path, _ := os.Getwd()
	return path
}

func checkPathExist(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

var gopRoot = getcwd()
var initCommandExecuteEnv = os.Environ()
var commandExecuteEnv = initCommandExecuteEnv

func execCommand(command string, arg ...string) (string, string, error) {
	var stdout, stderr bytes.Buffer
	cmd := exec.Command(command, arg...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Env = commandExecuteEnv
	err := cmd.Run()
	return stdout.String(), stderr.String(), err
}

func getRevCommit(tag string) string {
	commit, stderr, err := execCommand("git", "rev-parse", "--verify", tag)
	if err != nil || stderr != "" {
		return ""
	}
	return strings.TrimRight(commit, "\n")
}

func getGitInfo() (string, bool) {
	gitDir := filepath.Join(gopRoot, ".git")
	if checkPathExist(gitDir) {
		return getRevCommit("HEAD"), true
	}
	return "", false
}

func getBuildDateTime() string {
	now := time.Now()
	return now.Format("2006-01-02_15-04-05")
}

func getBuildVer() string {
	tagRet, tagErr, err := execCommand("git", "describe", "--tags")
	if err != nil || tagErr != "" {
		return ""
	}
	return strings.TrimRight(tagRet, "\n")
}

/*
func findTag(commit string) string {
	tagRet, tagErr, err := execCommand("git", "tag")
	if err != nil || tagErr != "" {
		return ""
	}
	var prefix = "v" + env.MainVersion + "."
	for _, tag := range strings.Split(tagRet, "\n") {
		if strings.HasPrefix(tag, prefix) {
			if getRevCommit(tag) == commit {
				return tag
			}
		}
	}
	return ""
}
*/

func getGopBuildFlags() string {
	defaultGopRoot := gopRoot
	if gopRootFinal := os.Getenv("GOPROOT_FINAL"); gopRootFinal != "" {
		defaultGopRoot = gopRootFinal
	}
	buildFlags := fmt.Sprintf("-X github.com/goplus/gop/env.defaultGopRoot=%s", defaultGopRoot)
	buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildDate=%s", getBuildDateTime())
	if commit, ok := getGitInfo(); ok {
		buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildCommit=%s", commit)
		if buildVer := getBuildVer(); buildVer != "" {
			buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildVersion=%s", buildVer)
		}
	}
	return buildFlags
}

/*
	func detectGoBinPath() string {
		goBin, ok := os.LookupEnv("GOBIN")
		if ok {
			return goBin
		}

		goPath, ok := os.LookupEnv("GOPATH")
		if ok {
			return filepath.Join(goPath, "bin")
		}

		homeDir, _ := os.UserHomeDir()
		return filepath.Join(homeDir, "go", "bin")
	}
*/

func detectGopBinPath() string {
	return filepath.Join(gopRoot, "bin")
}

func buildGoplusTools(useGoProxy bool) {
	commandsDir := filepath.Join(gopRoot, "cmd")
	if !checkPathExist(commandsDir) {
		println("Error: This script should be run at the root directory of gop repository.")
		os.Exit(1)
	}

	buildFlags := getGopBuildFlags()

	if useGoProxy {
		println("Info: we will use goproxy.cn as a Go proxy to accelerate installing process.")
		commandExecuteEnv = append(commandExecuteEnv,
			"GOPROXY=https://goproxy.cn,direct",
		)
	}

	// Install Go+ binary files under current ./bin directory.
	println("Installing Go+ tools...")
	os.Chdir(commandsDir)
	buildOutput, buildErr, err := execCommand("go", "build", "-o", detectGopBinPath(), "-v", "-ldflags", buildFlags, "./...")
	println(buildErr)
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
	println(buildOutput)

	println("Go+ tools installed successfully!")
	showHelpPostInstall()
}

func showHelpPostInstall() {
	println("Next:")
	println("We just installed Go+ into the directory: ", detectGopBinPath())
	message := `
To setup a better Go+ development environment,
we recommend you add the above install directory into your PATH environment variable.
	`
	println(message)
}

func runTestcases() {
	println("Start running testcases.")
	os.Chdir(gopRoot)

	coverage := "-coverprofile=coverage.txt"
	gopCommand := filepath.Join(detectGopBinPath(), "gop")
	testOutput, testErr, err := execCommand(gopCommand, "test", coverage, "-covermode=atomic", "./...")
	println(testOutput)
	println(testErr)
	if err != nil {
		println(err.Error())
	}

	println("End running testcases.")
}

func uninstall() {
	println("Uninstalling Go+ and related tools.")

	gopBinPath := detectGopBinPath()
	if checkPathExist(gopBinPath) {
		if err := os.RemoveAll(gopBinPath); err != nil {
			println(err.Error())
		}
	}

	println("Go+ and related tools uninstalled successfully.")
}

func isInChina() bool {
	const prefix = "LANG=\""
	out, errMsg, err := execCommand("locale")
	if err != nil || errMsg != "" {
		return false
	}
	if strings.HasPrefix(out, prefix) {
		out = out[len(prefix):]
		return strings.HasPrefix(out, "zh_CN") || strings.HasPrefix(out, "zh_HK")
	}
	return false
}

func main() {
	isInstall := flag.Bool("install", false, "Install Go+")
	isTest := flag.Bool("test", false, "Run testcases")
	isUninstall := flag.Bool("uninstall", false, "Uninstall Go+")
	isGoProxy := flag.Bool("proxy", false, "Set GOPROXY for people in China")
	isAutoProxy := flag.Bool("autoproxy", false, "Check to set GOPROXY automatically")

	flag.Parse()

	useGoProxy := *isGoProxy
	if !useGoProxy && *isAutoProxy {
		useGoProxy = isInChina()
	}
	flagActionMap := map[*bool]func(){
		isInstall:   func() { buildGoplusTools(useGoProxy) },
		isUninstall: uninstall,
		isTest:      runTestcases,
	}

	for flag, action := range flagActionMap {
		if *flag {
			action()
			return
		}
	}

	println("Usage:\n")
	flag.PrintDefaults()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"log"
	"math"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/chadweimer/gomp/models"
	"github.com/chadweimer/gomp/modules/conf"
	"github.com/chadweimer/gomp/modules/context"
	"github.com/chadweimer/gomp/modules/upload"
	"github.com/chadweimer/gomp/routers"
	"github.com/gorilla/sessions"
	"github.com/julienschmidt/httprouter"
	"github.com/phyber/negroni-gzip/gzip"
	"github.com/urfave/negroni"
	"gopkg.in/tylerb/graceful.v1"
	"gopkg.in/unrolled/render.v1"
)

func main() {
	cfg := conf.Load("conf/app.json")
	if err := cfg.Validate(); err != nil {
		log.Fatalf("[config] %s", err.Error())
	}
	model := models.New(cfg)
	sessionStore := sessions.NewCookieStore([]byte(cfg.SecretKey))
	renderer := render.New(render.Options{
		Layout: "shared/layout",
		Funcs: []template.FuncMap{map[string]interface{}{
			"RootUrlPath":      func() string { return cfg.RootURLPath },
			"ApplicationTitle": func() string { return cfg.ApplicationTitle },

			"ToLower":     strings.ToLower,
			"QueryEscape": url.QueryEscape,
			"Add":         func(a, b int64) int64 { return a + b },
			"TimeEqual":   func(a, b time.Time) bool { return a == b },
			"Paginate":    getPageNumbersForPagination,
			"ColumnizeRecipes": func(recipes *models.Recipes, numSplits int) [][]interface{} {
				slice := make([]interface{}, len(*recipes))
				for i, v := range *recipes {
					slice[i] = v
				}
				return splitSlice(slice, numSplits)
			},
		}}})
	rc := routers.NewController(renderer, cfg, model, sessionStore)

	authMux := httprouter.New()
	authMux.GET("/login", rc.Login)
	authMux.POST("/login", rc.LoginPost)
	authMux.GET("/logout", rc.Logout)
	// Do nothing if this route isn't matched. Let the later handlers/routes get processed
	authMux.NotFound = http.HandlerFunc(rc.NoOp)

	n := negroni.New()
	n.Use(negroni.NewRecovery())
	if cfg.IsDevelopment {
		n.Use(negroni.NewLogger())
	}
	n.Use(gzip.Gzip(gzip.DefaultCompression))
	n.Use(negroni.NewStatic(http.Dir("public")))
	n.Use(context.NewContexter(cfg, model, sessionStore))
	n.UseHandler(authMux)

	// !!!! IMPORTANT !!!!
	// Everything before this is valid with or without authentication.
	// Everything after this requires authentication

	if cfg.UploadDriver == "fs" {
		static := negroni.NewStatic(http.Dir(cfg.UploadPath))
		static.Prefix = "/uploads"
		n.UseFunc(rc.RequireAuthentication(static))
	} else if cfg.UploadDriver == "s3" {
		s3Static := upload.NewS3Static(cfg)
		s3Static.Prefix = "/uploads"
		n.UseFunc(rc.RequireAuthentication(s3Static))
	}

	recipeMux := httprouter.New()
	recipeMux.GET("/", rc.Home)
	recipeMux.GET("/new", rc.CreateRecipe)
	recipeMux.GET("/recipes", rc.ListRecipes)
	recipeMux.POST("/recipes", rc.CreateRecipePost)
	recipeMux.GET("/recipes/:id", rc.GetRecipe)
	recipeMux.GET("/recipes/:id/edit", rc.EditRecipe)
	recipeMux.POST("/recipes/:id", rc.EditRecipePost)
	recipeMux.GET("/recipes/:id/delete", rc.DeleteRecipe)
	recipeMux.POST("/recipes/:id/images", rc.AttachImagePost)
	recipeMux.GET("/recipes/:id/images/:image_id/delete", rc.DeleteImage)
	recipeMux.GET("/recipes/:id/images/:image_id/main", rc.SetMainImage)
	recipeMux.POST("/recipes/:id/notes", rc.CreateNotePost)
	recipeMux.POST("/recipes/:id/notes/:note_id", rc.EditNotePost)
	recipeMux.GET("/recipes/:id/notes/:note_id/delete", rc.DeleteNote)
	recipeMux.POST("/recipes/:id/ratings", rc.RateRecipePost)
	recipeMux.NotFound = http.HandlerFunc(rc.NotFound)
	n.UseFunc(rc.RequireAuthentication(negroni.Wrap(recipeMux)))

	log.Printf("Starting server on port :%d", cfg.Port)
	timeout := 10 * time.Second
	if cfg.IsDevelopment {
		timeout = 1 * time.Second
	}
	graceful.Run(fmt.Sprintf(":%d", cfg.Port), timeout, n)

	// Make sure to close the database connection
	model.TearDown()
}

func getPageNumbersForPagination(pageNum, numPages, num int64) []int64 {
	if numPages == 0 {
		return []int64{1}
	}

	if numPages < num {
		num = numPages
	}

	startPage := pageNum - num/2
	endPage := pageNum + num/2
	if startPage < 1 {
		startPage = 1
		endPage = startPage + num - 1
	} else if endPage > numPages {
		endPage = numPages
		startPage = endPage - num + 1
	}

	pageNums := make([]int64, num, num)
	for i := int64(0); i < num; i++ {
		pageNums[i] = i + startPage
	}
	return pageNums
}

func splitSlice(slice []interface{}, numSplits int) [][]interface{} {
	count := len(slice)
	splitCount := int(math.Ceil(float64(count) / float64(numSplits)))

	slices := make([][]interface{}, numSplits, numSplits)
	sliceIndex := 0

	for i, v := range slice {
		if i >= (sliceIndex+1)*splitCount {
			sliceIndex = sliceIndex + 1
		}
		slices[sliceIndex] = append(slices[sliceIndex], v)
	}

	return slices
}
<commit_msg>Pulled in unrolled/secure in order to enforce use of SSL.<commit_after>package main

import (
	"fmt"
	"html/template"
	"log"
	"math"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/chadweimer/gomp/models"
	"github.com/chadweimer/gomp/modules/conf"
	"github.com/chadweimer/gomp/modules/context"
	"github.com/chadweimer/gomp/modules/upload"
	"github.com/chadweimer/gomp/routers"
	"github.com/gorilla/sessions"
	"github.com/julienschmidt/httprouter"
	"github.com/phyber/negroni-gzip/gzip"
	"github.com/urfave/negroni"
	"gopkg.in/tylerb/graceful.v1"
	"gopkg.in/unrolled/render.v1"
	"gopkg.in/unrolled/secure.v1"
)

func main() {
	cfg := conf.Load("conf/app.json")
	if err := cfg.Validate(); err != nil {
		log.Fatalf("[config] %s", err.Error())
	}
	model := models.New(cfg)
	sessionStore := sessions.NewCookieStore([]byte(cfg.SecretKey))
	renderer := render.New(render.Options{
		Layout: "shared/layout",
		Funcs: []template.FuncMap{map[string]interface{}{
			"RootUrlPath":      func() string { return cfg.RootURLPath },
			"ApplicationTitle": func() string { return cfg.ApplicationTitle },

			"ToLower":     strings.ToLower,
			"QueryEscape": url.QueryEscape,
			"Add":         func(a, b int64) int64 { return a + b },
			"TimeEqual":   func(a, b time.Time) bool { return a == b },
			"Paginate":    getPageNumbersForPagination,
			"ColumnizeRecipes": func(recipes *models.Recipes, numSplits int) [][]interface{} {
				slice := make([]interface{}, len(*recipes))
				for i, v := range *recipes {
					slice[i] = v
				}
				return splitSlice(slice, numSplits)
			},
		}}})
	rc := routers.NewController(renderer, cfg, model, sessionStore)

	authMux := httprouter.New()
	authMux.GET("/login", rc.Login)
	authMux.POST("/login", rc.LoginPost)
	authMux.GET("/logout", rc.Logout)
	// Do nothing if this route isn't matched. Let the later handlers/routes get processed
	authMux.NotFound = http.HandlerFunc(rc.NoOp)

	sm := secure.New(secure.Options{
		SSLRedirect:     true,
		SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
		IsDevelopment:   cfg.IsDevelopment,
	})

	n := negroni.New()
	n.Use(negroni.NewRecovery())
	if cfg.IsDevelopment {
		n.Use(negroni.NewLogger())
	}
	n.Use(gzip.Gzip(gzip.DefaultCompression))
	n.Use(negroni.HandlerFunc(sm.HandlerFuncWithNext))
	n.Use(negroni.NewStatic(http.Dir("public")))
	n.Use(context.NewContexter(cfg, model, sessionStore))
	n.UseHandler(authMux)

	// !!!! IMPORTANT !!!!
	// Everything before this is valid with or without authentication.
	// Everything after this requires authentication

	if cfg.UploadDriver == "fs" {
		static := negroni.NewStatic(http.Dir(cfg.UploadPath))
		static.Prefix = "/uploads"
		n.UseFunc(rc.RequireAuthentication(static))
	} else if cfg.UploadDriver == "s3" {
		s3Static := upload.NewS3Static(cfg)
		s3Static.Prefix = "/uploads"
		n.UseFunc(rc.RequireAuthentication(s3Static))
	}

	recipeMux := httprouter.New()
	recipeMux.GET("/", rc.Home)
	recipeMux.GET("/new", rc.CreateRecipe)
	recipeMux.GET("/recipes", rc.ListRecipes)
	recipeMux.POST("/recipes", rc.CreateRecipePost)
	recipeMux.GET("/recipes/:id", rc.GetRecipe)
	recipeMux.GET("/recipes/:id/edit", rc.EditRecipe)
	recipeMux.POST("/recipes/:id", rc.EditRecipePost)
	recipeMux.GET("/recipes/:id/delete", rc.DeleteRecipe)
	recipeMux.POST("/recipes/:id/images", rc.AttachImagePost)
	recipeMux.GET("/recipes/:id/images/:image_id/delete", rc.DeleteImage)
	recipeMux.GET("/recipes/:id/images/:image_id/main", rc.SetMainImage)
	recipeMux.POST("/recipes/:id/notes", rc.CreateNotePost)
	recipeMux.POST("/recipes/:id/notes/:note_id", rc.EditNotePost)
	recipeMux.GET("/recipes/:id/notes/:note_id/delete", rc.DeleteNote)
	recipeMux.POST("/recipes/:id/ratings", rc.RateRecipePost)
	recipeMux.NotFound = http.HandlerFunc(rc.NotFound)
	n.UseFunc(rc.RequireAuthentication(negroni.Wrap(recipeMux)))

	log.Printf("Starting server on port :%d", cfg.Port)
	timeout := 10 * time.Second
	if cfg.IsDevelopment {
		timeout = 1 * time.Second
	}
	graceful.Run(fmt.Sprintf(":%d", cfg.Port), timeout, n)

	// Make sure to close the database connection
	model.TearDown()
}

func getPageNumbersForPagination(pageNum, numPages, num int64) []int64 {
	if numPages == 0 {
		return []int64{1}
	}

	if numPages < num {
		num = numPages
	}

	startPage := pageNum - num/2
	endPage := pageNum + num/2
	if startPage < 1 {
		startPage = 1
		endPage = startPage + num - 1
	} else if endPage > numPages {
		endPage = numPages
		startPage = endPage - num + 1
	}

	pageNums := make([]int64, num, num)
	for i := int64(0); i < num; i++ {
		pageNums[i] = i + startPage
	}
	return pageNums
}

func splitSlice(slice []interface{}, numSplits int) [][]interface{} {
	count := len(slice)
	splitCount := int(math.Ceil(float64(count) / float64(numSplits)))

	slices := make([][]interface{}, numSplits, numSplits)
	sliceIndex := 0

	for i, v := range slice {
		if i >= (sliceIndex+1)*splitCount {
			sliceIndex = sliceIndex + 1
		}
		slices[sliceIndex] = append(slices[sliceIndex], v)
	}

	return slices
}
<|endoftext|>


<commit_before>package glesys

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const version = "5.0.0"

type httpClientInterface interface {
	Do(*http.Request) (*http.Response, error)
}

type clientInterface interface {
	get(ctx context.Context, path string, v interface{}) error
	post(ctx context.Context, path string, v interface{}, params interface{}) error
}

// Client is used to interact with the GleSYS API
type Client struct {
	apiKey     string
	baseURL    *url.URL
	httpClient httpClientInterface
	project    string
	userAgent  string

	DNSDomains      *DNSDomainService
	EmailDomains    *EmailDomainService
	IPs             *IPService
	LoadBalancers   *LoadBalancerService
	ObjectStorages  *ObjectStorageService
	Servers         *ServerService
	Networks        *NetworkService
	NetworkAdapters *NetworkAdapterService
}

// NewClient creates a new Client for interacting with the GleSYS API. This is
// the main entrypoint for API interactions.
func NewClient(project, apiKey, userAgent string) *Client {
	baseURL, _ := url.Parse("https://api.glesys.com")

	c := &Client{
		apiKey:     apiKey,
		baseURL:    baseURL,
		httpClient: http.DefaultClient,
		project:    project,
		userAgent:  userAgent,
	}

	c.DNSDomains = &DNSDomainService{client: c}
	c.EmailDomains = &EmailDomainService{client: c}
	c.IPs = &IPService{client: c}
	c.LoadBalancers = &LoadBalancerService{client: c}
	c.ObjectStorages = &ObjectStorageService{client: c}
	c.Servers = &ServerService{client: c}
	c.Networks = &NetworkService{client: c}
	c.NetworkAdapters = &NetworkAdapterService{client: c}

	return c
}

func (c *Client) get(ctx context.Context, path string, v interface{}) error {
	request, err := c.newRequest(ctx, "GET", path, nil)
	if err != nil {
		return err
	}
	return c.do(request, v)
}

func (c *Client) post(ctx context.Context, path string, v interface{}, params interface{}) error {
	request, err := c.newRequest(ctx, "POST", path, params)
	if err != nil {
		return err
	}
	return c.do(request, v)
}

func (c *Client) newRequest(ctx context.Context, method, path string, params interface{}) (*http.Request, error) {
	u, err := url.Parse(path)
	if err != nil {
		return nil, err
	}

	if c.baseURL != nil {
		u = c.baseURL.ResolveReference(u)
	}

	buffer := new(bytes.Buffer)

	if params != nil {
		err = json.NewEncoder(buffer).Encode(params)
		if err != nil {
			return nil, err
		}
	}

	request, err := http.NewRequest(method, u.String(), buffer)
	if err != nil {
		return nil, err
	}

	userAgent := strings.TrimSpace(fmt.Sprintf("%s glesys-go/%s", c.userAgent, version))

	request = request.WithContext(ctx)
	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("User-Agent", userAgent)
	request.SetBasicAuth(c.project, c.apiKey)

	return request, nil
}

func (c *Client) do(request *http.Request, v interface{}) error {
	response, err := c.httpClient.Do(request)
	if err != nil {
		return err
	}

	if response.StatusCode != http.StatusOK {
		return c.handleResponseError(response)
	}

	return c.parseResponseBody(response, v)
}

func (c *Client) handleResponseError(response *http.Response) error {
	data := struct {
		Response struct {
			Status struct {
				Text string `json:"text"`
			} `json:"status"`
		} `json:"response"`
	}{}

	err := c.parseResponseBody(response, &data)
	if err != nil {
		return err
	}

	return fmt.Errorf("Request failed with HTTP error: %v (%v)", response.StatusCode, strings.TrimSpace(data.Response.Status.Text))
}

func (c *Client) parseResponseBody(response *http.Response, v interface{}) error {
	if v == nil {
		return nil
	}

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

	return json.Unmarshal(body, v)
}
<commit_msg>Use http.NewRequestWithContext<commit_after>package glesys

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const version = "5.0.0"

type httpClientInterface interface {
	Do(*http.Request) (*http.Response, error)
}

type clientInterface interface {
	get(ctx context.Context, path string, v interface{}) error
	post(ctx context.Context, path string, v interface{}, params interface{}) error
}

// Client is used to interact with the GleSYS API
type Client struct {
	apiKey     string
	baseURL    *url.URL
	httpClient httpClientInterface
	project    string
	userAgent  string

	DNSDomains      *DNSDomainService
	EmailDomains    *EmailDomainService
	IPs             *IPService
	LoadBalancers   *LoadBalancerService
	ObjectStorages  *ObjectStorageService
	Servers         *ServerService
	Networks        *NetworkService
	NetworkAdapters *NetworkAdapterService
}

// NewClient creates a new Client for interacting with the GleSYS API. This is
// the main entrypoint for API interactions.
func NewClient(project, apiKey, userAgent string) *Client {
	baseURL, _ := url.Parse("https://api.glesys.com")

	c := &Client{
		apiKey:     apiKey,
		baseURL:    baseURL,
		httpClient: http.DefaultClient,
		project:    project,
		userAgent:  userAgent,
	}

	c.DNSDomains = &DNSDomainService{client: c}
	c.EmailDomains = &EmailDomainService{client: c}
	c.IPs = &IPService{client: c}
	c.LoadBalancers = &LoadBalancerService{client: c}
	c.ObjectStorages = &ObjectStorageService{client: c}
	c.Servers = &ServerService{client: c}
	c.Networks = &NetworkService{client: c}
	c.NetworkAdapters = &NetworkAdapterService{client: c}

	return c
}

func (c *Client) get(ctx context.Context, path string, v interface{}) error {
	request, err := c.newRequest(ctx, "GET", path, nil)
	if err != nil {
		return err
	}
	return c.do(request, v)
}

func (c *Client) post(ctx context.Context, path string, v interface{}, params interface{}) error {
	request, err := c.newRequest(ctx, "POST", path, params)
	if err != nil {
		return err
	}
	return c.do(request, v)
}

func (c *Client) newRequest(ctx context.Context, method, path string, params interface{}) (*http.Request, error) {
	u, err := url.Parse(path)
	if err != nil {
		return nil, err
	}

	if c.baseURL != nil {
		u = c.baseURL.ResolveReference(u)
	}

	buffer := new(bytes.Buffer)

	if params != nil {
		err = json.NewEncoder(buffer).Encode(params)
		if err != nil {
			return nil, err
		}
	}

	request, err := http.NewRequestWithContext(ctx, method, u.String(), buffer)
	if err != nil {
		return nil, err
	}

	userAgent := strings.TrimSpace(fmt.Sprintf("%s glesys-go/%s", c.userAgent, version))

	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("User-Agent", userAgent)
	request.SetBasicAuth(c.project, c.apiKey)

	return request, nil
}

func (c *Client) do(request *http.Request, v interface{}) error {
	response, err := c.httpClient.Do(request)
	if err != nil {
		return err
	}

	if response.StatusCode != http.StatusOK {
		return c.handleResponseError(response)
	}

	return c.parseResponseBody(response, v)
}

func (c *Client) handleResponseError(response *http.Response) error {
	data := struct {
		Response struct {
			Status struct {
				Text string `json:"text"`
			} `json:"status"`
		} `json:"response"`
	}{}

	err := c.parseResponseBody(response, &data)
	if err != nil {
		return err
	}

	return fmt.Errorf("Request failed with HTTP error: %v (%v)", response.StatusCode, strings.TrimSpace(data.Response.Status.Text))
}

func (c *Client) parseResponseBody(response *http.Response, v interface{}) error {
	if v == nil {
		return nil
	}

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

	return json.Unmarshal(body, v)
}
<|endoftext|>


<commit_before>// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"

	"github.com/wandoulabs/redis-port/pkg/libs/atomic2"
	"github.com/wandoulabs/redis-port/pkg/libs/log"
	"github.com/wandoulabs/redis-port/pkg/redis"
)

type cmdRestore struct {
	rbytes, ebytes, nentry, ignore atomic2.Int64

	forward, nbypass atomic2.Int64
}

type cmdRestoreStat struct {
	rbytes, ebytes, nentry, ignore int64

	forward, nbypass int64
}

func (cmd *cmdRestore) Stat() *cmdRestoreStat {
	return &cmdRestoreStat{
		rbytes: cmd.rbytes.Get(),
		ebytes: cmd.ebytes.Get(),
		nentry: cmd.nentry.Get(),
		ignore: cmd.ignore.Get(),

		forward: cmd.forward.Get(),
		nbypass: cmd.nbypass.Get(),
	}
}

func (cmd *cmdRestore) Main() {
	input, target := args.input, args.target
	if len(target) == 0 {
		log.Panic("invalid argument: target")
	}
	if len(input) == 0 {
		input = "/dev/stdin"
	}

	log.Infof("restore from '%s' to '%s'\n", input, target)

	var readin io.ReadCloser
	var nsize int64
	if input != "/dev/stdin" {
		readin, nsize = openReadFile(input)
		defer readin.Close()
	} else {
		readin, nsize = os.Stdin, 0
	}

	reader := bufio.NewReaderSize(readin, ReaderBufferSize)

	cmd.RestoreRDBFile(reader, target, args.auth, nsize)

	if !args.extra {
		return
	}

	if nsize != 0 && nsize == cmd.rbytes.Get() {
		return
	}

	cmd.RestoreCommand(reader, target, args.auth)
}

func (cmd *cmdRestore) RestoreRDBFile(reader *bufio.Reader, target, passwd string, nsize int64) {
	pipe := newRDBLoader(reader, &cmd.rbytes, args.parallel*32)
	wait := make(chan struct{})
	go func() {
		defer close(wait)
		group := make(chan int, args.parallel)
		for i := 0; i < cap(group); i++ {
			go func() {
				defer func() {
					group <- 0
				}()
				c := openRedisConn(target, passwd)
				defer c.Close()
				var lastdb uint32 = 0
				for e := range pipe {
					if !acceptDB(e.DB) {
						cmd.ignore.Incr()
					} else {
						cmd.nentry.Incr()
						if e.DB != lastdb {
							lastdb = e.DB
							selectDB(c, lastdb)
						}
						restoreRdbEntry(c, e)
					}
				}
			}()
		}
		for i := 0; i < cap(group); i++ {
			<-group
		}
	}()

	for done := false; !done; {
		select {
		case <-wait:
			done = true
		case <-time.After(time.Second):
		}
		stat := cmd.Stat()
		var b bytes.Buffer
		if nsize != 0 {
			fmt.Fprintf(&b, "total = %d - %12d [%3d%%]", nsize, stat.rbytes, 100*stat.rbytes/nsize)
		} else {
			fmt.Fprintf(&b, "total = %12d", stat.rbytes)
		}
		fmt.Fprintf(&b, "  entry=%-12d", stat.nentry)
		if stat.ignore != 0 {
			fmt.Fprintf(&b, "  ignore=%-12d", stat.ignore)
		}
		log.Info(b.String())
	}
	log.Info("restore: rdb done")
}

func (cmd *cmdRestore) RestoreCommand(reader *bufio.Reader, target, passwd string) {
	c := openNetConn(target, passwd)
	defer c.Close()

	writer := bufio.NewWriterSize(c, WriterBufferSize)
	defer flushWriter(writer)

	go func() {
		p := make([]byte, ReaderBufferSize)
		for {
			iocopy(c, ioutil.Discard, p, len(p))
		}
	}()

	go func() {
		var bypass bool = false
		for {
			resp := redis.MustDecode(reader)
			if scmd, args, err := redis.ParseArgs(resp); err != nil {
				log.PanicError(err, "parse command arguments failed")
			} else if scmd != "ping" {
				if scmd == "select" {
					if len(args) != 1 {
						log.Panicf("select command len(args) = %d", len(args))
					}
					s := string(args[0])
					n, err := parseInt(s, MinDB, MaxDB)
					if err != nil {
						log.PanicErrorf(err, "parse db = %s failed", s)
					}
					bypass = !acceptDB(uint32(n))
				}
				if bypass {
					cmd.nbypass.Incr()
					continue
				}
			}
			cmd.forward.Incr()
			redis.MustEncode(writer, resp)
			flushWriter(writer)
		}
	}()

	for lstat := cmd.Stat(); ; {
		time.Sleep(time.Second)
		nstat := cmd.Stat()
		var b bytes.Buffer
		fmt.Fprintf(&b, "restore: ")
		fmt.Fprintf(&b, " +forward=%-6d", nstat.forward-lstat.forward)
		fmt.Fprintf(&b, " +nbypass=%-6d", nstat.nbypass-lstat.nbypass)
		log.Info(b.String())
		lstat = nstat
	}
}
<commit_msg>ignore big key+value<commit_after>// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"

	"github.com/wandoulabs/redis-port/pkg/libs/atomic2"
	"github.com/wandoulabs/redis-port/pkg/libs/log"
	"github.com/wandoulabs/redis-port/pkg/redis"
)

type cmdRestore struct {
	rbytes, ebytes, nentry, ignore atomic2.Int64

	forward, nbypass atomic2.Int64
}

type cmdRestoreStat struct {
	rbytes, ebytes, nentry, ignore int64

	forward, nbypass int64
}

func (cmd *cmdRestore) Stat() *cmdRestoreStat {
	return &cmdRestoreStat{
		rbytes: cmd.rbytes.Get(),
		ebytes: cmd.ebytes.Get(),
		nentry: cmd.nentry.Get(),
		ignore: cmd.ignore.Get(),

		forward: cmd.forward.Get(),
		nbypass: cmd.nbypass.Get(),
	}
}

func (cmd *cmdRestore) Main() {
	input, target := args.input, args.target
	if len(target) == 0 {
		log.Panic("invalid argument: target")
	}
	if len(input) == 0 {
		input = "/dev/stdin"
	}

	log.Infof("restore from '%s' to '%s'\n", input, target)

	var readin io.ReadCloser
	var nsize int64
	if input != "/dev/stdin" {
		readin, nsize = openReadFile(input)
		defer readin.Close()
	} else {
		readin, nsize = os.Stdin, 0
	}

	reader := bufio.NewReaderSize(readin, ReaderBufferSize)

	cmd.RestoreRDBFile(reader, target, args.auth, nsize)

	if !args.extra {
		return
	}

	if nsize != 0 && nsize == cmd.rbytes.Get() {
		return
	}

	cmd.RestoreCommand(reader, target, args.auth)
}

func (cmd *cmdRestore) RestoreRDBFile(reader *bufio.Reader, target, passwd string, nsize int64) {
	pipe := newRDBLoader(reader, &cmd.rbytes, args.parallel*32)
	wait := make(chan struct{})
	go func() {
		defer close(wait)
		group := make(chan int, args.parallel)
		for i := 0; i < cap(group); i++ {
			go func() {
				defer func() {
					group <- 0
				}()
				c := openRedisConn(target, passwd)
				defer c.Close()
				var lastdb uint32 = 0
				for e := range pipe {
					if !acceptDB(e.DB) {
						cmd.ignore.Incr()
					} else {
						cmd.nentry.Incr()
						if e.DB != lastdb {
							lastdb = e.DB
							selectDB(c, lastdb)
						}

	                    if len(e.Value) > 512*1024*1024 {
	                        s := e.Value[:1024]
	                        fmt.Printf(">>>>>>>>>>>>> key:[%s] %v, has value len=%d, value dump=%v\n", e.Key, e.Key, len(e.Value), s)
	                    } else {
	                        restoreRdbEntry(c, e)
	                    }
					}
				}
			}()
		}
		for i := 0; i < cap(group); i++ {
			<-group
		}
	}()

	for done := false; !done; {
		select {
		case <-wait:
			done = true
		case <-time.After(time.Second):
		}
		stat := cmd.Stat()
		var b bytes.Buffer
		if nsize != 0 {
			fmt.Fprintf(&b, "total = %d - %12d [%3d%%]", nsize, stat.rbytes, 100*stat.rbytes/nsize)
		} else {
			fmt.Fprintf(&b, "total = %12d", stat.rbytes)
		}
		fmt.Fprintf(&b, "  entry=%-12d", stat.nentry)
		if stat.ignore != 0 {
			fmt.Fprintf(&b, "  ignore=%-12d", stat.ignore)
		}
		log.Info(b.String())
	}
	log.Info("restore: rdb done")
}

func (cmd *cmdRestore) RestoreCommand(reader *bufio.Reader, target, passwd string) {
	c := openNetConn(target, passwd)
	defer c.Close()

	writer := bufio.NewWriterSize(c, WriterBufferSize)
	defer flushWriter(writer)

	go func() {
		p := make([]byte, ReaderBufferSize)
		for {
			iocopy(c, ioutil.Discard, p, len(p))
		}
	}()

	go func() {
		var bypass bool = false
		for {
			resp := redis.MustDecode(reader)
			if scmd, args, err := redis.ParseArgs(resp); err != nil {
				log.PanicError(err, "parse command arguments failed")
			} else if scmd != "ping" {
				if scmd == "select" {
					if len(args) != 1 {
						log.Panicf("select command len(args) = %d", len(args))
					}
					s := string(args[0])
					n, err := parseInt(s, MinDB, MaxDB)
					if err != nil {
						log.PanicErrorf(err, "parse db = %s failed", s)
					}
					bypass = !acceptDB(uint32(n))
				}
				if bypass {
					cmd.nbypass.Incr()
					continue
				}
			}
			cmd.forward.Incr()
			redis.MustEncode(writer, resp)
			flushWriter(writer)
		}
	}()

	for lstat := cmd.Stat(); ; {
		time.Sleep(time.Second)
		nstat := cmd.Stat()
		var b bytes.Buffer
		fmt.Fprintf(&b, "restore: ")
		fmt.Fprintf(&b, " +forward=%-6d", nstat.forward-lstat.forward)
		fmt.Fprintf(&b, " +nbypass=%-6d", nstat.nbypass-lstat.nbypass)
		log.Info(b.String())
		lstat = nstat
	}
}
<|endoftext|>


<commit_before>/**
    Copyright (C) 2013 Yann GUIBET <yannguibet@gmail.com>
    See LICENSE for details.
**/

package gomq

import (
	"bytes"
	"container/list"
	"errors"
	zmq "github.com/alecthomas/gozmq"
	"log"
	"runtime"
)

const (
	PULL  = zmq.PULL
	PUSH  = zmq.PUSH
	_SALT = "\x0c\x199\xe5yn\xe8\xa1"
)

type _ConnectionInfo struct {
	Host *list.List
	Type zmq.SocketType
	Sock *zmq.Socket
}

func newConnectionInfo(host string, _type zmq.SocketType) *_ConnectionInfo {
	res := &_ConnectionInfo{Type: _type, Sock: nil}
	res.Host = list.New()
	res.Host.PushBack(host)
	return res
}

type Args interface{}
type Pfunc func(Args)

type _Message struct {
	Job,
	UUID string
	Params   Args
	Priority uint
}

func newMessage(job, uuid string, params Args, priority uint) *_Message {
	return &_Message{job, uuid, params, priority}
}

type GOMQ struct {
	uuid        string
	context     *zmq.Context
	jobs        map[string]Pfunc
	connections map[string]*_ConnectionInfo
	pool        chan byte
	key         []byte
	localsock   *zmq.Socket
	Run         bool
}

func NewGOMQ(uuid string) *GOMQ {
	res := &GOMQ{uuid: uuid}
	res.jobs = map[string]Pfunc{}
	res.connections = map[string]*_ConnectionInfo{}
	res.context, _ = zmq.NewContext()
	res.pool = nil
	res.key = nil
	return res
}

func (self *GOMQ) CreatePool(size int) {
	self.pool = make(chan byte, size)
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func (self *GOMQ) createSock(sock_infos *_ConnectionInfo) (*zmq.Socket, error) {
	if sock_infos.Sock == nil {
		sock, err := self.context.NewSocket(sock_infos.Type)
		if err != nil {
			return nil, err
		}
		for e := sock_infos.Host.Front(); e != nil; e = e.Next() {
			err := sock.Connect(e.Value.(string))
			if err != nil {
				return nil, err
			}
		}
		sock_infos.Sock = sock
		return sock, nil
	} else {
		return sock_infos.Sock, nil
	}
}

func (self *GOMQ) CreateConnection(name, host string, sock_type zmq.SocketType) {
	if self.connections[name] == nil {
		self.connections[name] = newConnectionInfo(host, sock_type)
	} else {
		self.connections[name].Host.PushBack(host)
	}
}

func (self *GOMQ) SendJob(connection_name, job string, params Args) error {
	uuid, err := newUUID()
	if err != nil {
		return err
	}
	sock_infos := self.connections[connection_name]
	sock, err := self.createSock(sock_infos)
	if err != nil {
		return err
	}
	msg := newMessage(job, uuid, params, 0)
	buff, err := encodeMessage(msg)
	if err != nil {
		return err
	}
	buff, err = self.encrypt(buff)
	if err != nil {
		return err
	}
	for i := 0; i < len(buff); i += 4096 {
		limit := i + 4096
		if limit > len(buff) {
			limit = len(buff)
		}
		err = sock.Send(buff[i:limit], zmq.SNDMORE)
		if err != nil {
			return err
		}
	}
	err = sock.Send([]byte(nil), 0)
	return err
}

func (self *GOMQ) SendLocalJob(job string, params Args) error {
	if self.localsock == nil {
		return errors.New("GOMQ:SendLocalJob:daemon is not start")
	}
	uuid, err := newUUID()
	if err != nil {
		return err
	}
	msg := newMessage(job, uuid, params, 0)
	buff, err := encodeMessage(msg)
	if err != nil {
		return err
	}
	buff, err = self.encrypt(buff)
	if err != nil {
		return err
	}
	for i := 0; i < len(buff); i += 4096 {
		limit := i + 4096
		if limit > len(buff) {
			limit = len(buff)
		}
		err = self.localsock.Send(buff[i:limit], zmq.SNDMORE)
		if err != nil {
			return err
		}
	}
	err = self.localsock.Send([]byte(nil), 0)
	return err
}

func (self *GOMQ) handle(buff []byte) {
	self.pool <- '0'
	go func() {
		defer func() {
			<-self.pool
		}()
		buff, err := self.decrypt(buff)
		if err != nil {
			log.Println("GOMQ:handle:decrypt", err)
			return
		}
		msg, err := decodeMessage(buff)
		if err != nil {
			log.Println("GOMQ:handle:decodeMessage", err)
		} else {
			job := self.getJob(msg.Job)
			job(msg.Params)
		}
	}()
}

func (self *GOMQ) Loop(host string, sock_type zmq.SocketType) error {
	if self.pool == nil {
		return errors.New("GOMQ:Loop:Pool not created")
	}
	s, err := self.context.NewSocket(sock_type)
	if err != nil {
		return err
	}
	err = s.Bind(host)
	if err != nil {
		return err
	}
	err = s.Bind("ipc://GOMQ:" + self.uuid)
	if err != nil {
		return err
	}
	self.localsock, err = self.context.NewSocket(zmq.PUSH)
	if err != nil {
		return err
	}
	err = self.localsock.Connect("ipc://GOMQ:" + self.uuid)
	if err != nil {
		return err
	}
	self.Run = true
	for self.Run {
		buff, err := s.RecvMultipart(zmq.SNDMORE)
		if err != nil {
			log.Println("GOMQ:Loop:RecvMultipart", err)
			continue
		}
		self.handle(bytes.Join(buff, []byte(nil)))
	}
	return nil
}

func (self *GOMQ) AddJob(job string, action Pfunc) {
	self.jobs[job] = action
}

func (self *GOMQ) getJob(job string) Pfunc {
	return self.jobs[job]
}

func (self *GOMQ) SetMasterKey(key []byte) {
	_, self.key = _PBKDF2_SHA256(key, []byte(_SALT))
}

func (self *GOMQ) encrypt(data []byte) ([]byte, error) {
	var buffer bytes.Buffer
	if self.key == nil {
		return nil, errors.New("GOMQ:encrypt:Master Key is not define")
	}
	iv := _rand(blockSizeAES())
	_, err := buffer.Write(iv)
	if err != nil {
		return nil, err
	}
	ctx, err := newAES(self.key, iv)
	if err != nil {
		return nil, err
	}
	ciphertext := ctx.update(data)
	_, err = buffer.Write(ciphertext)
	if err != nil {
		return nil, err
	}
	hmac := _HMAC_SHA256(data, self.key)
	_, err = buffer.Write(hmac)
	if err != nil {
		return nil, err
	}
	return buffer.Bytes(), nil
}

func (self *GOMQ) decrypt(buff []byte) ([]byte, error) {
	if self.key == nil {
		return nil, errors.New("GOMQ:encrypt:Master Key is not define")
	}
	length := len(buff)
	buffer := bytes.NewBuffer(buff)
	iv := make([]byte, blockSizeAES())
	hmac := make([]byte, 32)
	data := make([]byte, length-(len(iv)+len(hmac)))
	i, err := buffer.Read(iv)
	if err != nil || i < len(iv) {
		return nil, err
	}
	i, err = buffer.Read(data)
	if err != nil || i < len(iv) {
		return nil, err
	}
	i, err = buffer.Read(hmac)
	if err != nil || i < len(iv) {
		return nil, err
	}
	ctx, err := newAES(self.key, iv)
	if err != nil {
		return nil, err
	}
	plaintext := ctx.update(data)
	hmac2 := _HMAC_SHA256(plaintext, self.key)
	if !bytes.Equal(hmac, hmac2) {
		return nil, errors.New("GOMQ:decrypt:HMAC check fail")
	}
	return plaintext, nil
}

func (self *GOMQ) Close() {
	for _, sock_infos := range self.connections {
		sock_infos.Sock.Close()
	}
	self.context.Close()
}
<commit_msg>close localsock in GOMQ.Close()<commit_after>/**
    Copyright (C) 2013 Yann GUIBET <yannguibet@gmail.com>
    See LICENSE for details.
**/

package gomq

import (
	"bytes"
	"container/list"
	"errors"
	zmq "github.com/alecthomas/gozmq"
	"log"
	"runtime"
)

const (
	PULL  = zmq.PULL
	PUSH  = zmq.PUSH
	_SALT = "\x0c\x199\xe5yn\xe8\xa1"
)

type _ConnectionInfo struct {
	Host *list.List
	Type zmq.SocketType
	Sock *zmq.Socket
}

func newConnectionInfo(host string, _type zmq.SocketType) *_ConnectionInfo {
	res := &_ConnectionInfo{Type: _type, Sock: nil}
	res.Host = list.New()
	res.Host.PushBack(host)
	return res
}

type Args interface{}
type Pfunc func(Args)

type _Message struct {
	Job,
	UUID string
	Params   Args
	Priority uint
}

func newMessage(job, uuid string, params Args, priority uint) *_Message {
	return &_Message{job, uuid, params, priority}
}

type GOMQ struct {
	uuid        string
	context     *zmq.Context
	jobs        map[string]Pfunc
	connections map[string]*_ConnectionInfo
	pool        chan byte
	key         []byte
	localsock   *zmq.Socket
	Run         bool
}

func NewGOMQ(uuid string) *GOMQ {
	res := &GOMQ{uuid: uuid}
	res.jobs = map[string]Pfunc{}
	res.connections = map[string]*_ConnectionInfo{}
	res.context, _ = zmq.NewContext()
	res.pool = nil
	res.key = nil
	return res
}

func (self *GOMQ) CreatePool(size int) {
	self.pool = make(chan byte, size)
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func (self *GOMQ) createSock(sock_infos *_ConnectionInfo) (*zmq.Socket, error) {
	if sock_infos.Sock == nil {
		sock, err := self.context.NewSocket(sock_infos.Type)
		if err != nil {
			return nil, err
		}
		for e := sock_infos.Host.Front(); e != nil; e = e.Next() {
			err := sock.Connect(e.Value.(string))
			if err != nil {
				return nil, err
			}
		}
		sock_infos.Sock = sock
		return sock, nil
	} else {
		return sock_infos.Sock, nil
	}
}

func (self *GOMQ) CreateConnection(name, host string, sock_type zmq.SocketType) {
	if self.connections[name] == nil {
		self.connections[name] = newConnectionInfo(host, sock_type)
	} else {
		self.connections[name].Host.PushBack(host)
	}
}

func (self *GOMQ) SendJob(connection_name, job string, params Args) error {
	uuid, err := newUUID()
	if err != nil {
		return err
	}
	sock_infos := self.connections[connection_name]
	sock, err := self.createSock(sock_infos)
	if err != nil {
		return err
	}
	msg := newMessage(job, uuid, params, 0)
	buff, err := encodeMessage(msg)
	if err != nil {
		return err
	}
	buff, err = self.encrypt(buff)
	if err != nil {
		return err
	}
	for i := 0; i < len(buff); i += 4096 {
		limit := i + 4096
		if limit > len(buff) {
			limit = len(buff)
		}
		err = sock.Send(buff[i:limit], zmq.SNDMORE)
		if err != nil {
			return err
		}
	}
	err = sock.Send([]byte(nil), 0)
	return err
}

func (self *GOMQ) SendLocalJob(job string, params Args) error {
	if self.localsock == nil {
		return errors.New("GOMQ:SendLocalJob:daemon is not start")
	}
	uuid, err := newUUID()
	if err != nil {
		return err
	}
	msg := newMessage(job, uuid, params, 0)
	buff, err := encodeMessage(msg)
	if err != nil {
		return err
	}
	buff, err = self.encrypt(buff)
	if err != nil {
		return err
	}
	for i := 0; i < len(buff); i += 4096 {
		limit := i + 4096
		if limit > len(buff) {
			limit = len(buff)
		}
		err = self.localsock.Send(buff[i:limit], zmq.SNDMORE)
		if err != nil {
			return err
		}
	}
	err = self.localsock.Send([]byte(nil), 0)
	return err
}

func (self *GOMQ) handle(buff []byte) {
	self.pool <- '0'
	go func() {
		defer func() {
			<-self.pool
		}()
		buff, err := self.decrypt(buff)
		if err != nil {
			log.Println("GOMQ:handle:decrypt", err)
			return
		}
		msg, err := decodeMessage(buff)
		if err != nil {
			log.Println("GOMQ:handle:decodeMessage", err)
		} else {
			job := self.getJob(msg.Job)
			job(msg.Params)
		}
	}()
}

func (self *GOMQ) Loop(host string, sock_type zmq.SocketType) error {
	if self.pool == nil {
		return errors.New("GOMQ:Loop:Pool not created")
	}
	s, err := self.context.NewSocket(sock_type)
	if err != nil {
		return err
	}
	err = s.Bind(host)
	if err != nil {
		return err
	}
	err = s.Bind("ipc://GOMQ:" + self.uuid)
	if err != nil {
		return err
	}
	self.localsock, err = self.context.NewSocket(zmq.PUSH)
	if err != nil {
		return err
	}
	err = self.localsock.Connect("ipc://GOMQ:" + self.uuid)
	if err != nil {
		return err
	}
	self.Run = true
	for self.Run {
		buff, err := s.RecvMultipart(zmq.SNDMORE)
		if err != nil {
			log.Println("GOMQ:Loop:RecvMultipart", err)
			continue
		}
		self.handle(bytes.Join(buff, []byte(nil)))
	}
	return nil
}

func (self *GOMQ) AddJob(job string, action Pfunc) {
	self.jobs[job] = action
}

func (self *GOMQ) getJob(job string) Pfunc {
	return self.jobs[job]
}

func (self *GOMQ) SetMasterKey(key []byte) {
	_, self.key = _PBKDF2_SHA256(key, []byte(_SALT))
}

func (self *GOMQ) encrypt(data []byte) ([]byte, error) {
	var buffer bytes.Buffer
	if self.key == nil {
		return nil, errors.New("GOMQ:encrypt:Master Key is not define")
	}
	iv := _rand(blockSizeAES())
	_, err := buffer.Write(iv)
	if err != nil {
		return nil, err
	}
	ctx, err := newAES(self.key, iv)
	if err != nil {
		return nil, err
	}
	ciphertext := ctx.update(data)
	_, err = buffer.Write(ciphertext)
	if err != nil {
		return nil, err
	}
	hmac := _HMAC_SHA256(data, self.key)
	_, err = buffer.Write(hmac)
	if err != nil {
		return nil, err
	}
	return buffer.Bytes(), nil
}

func (self *GOMQ) decrypt(buff []byte) ([]byte, error) {
	if self.key == nil {
		return nil, errors.New("GOMQ:encrypt:Master Key is not define")
	}
	length := len(buff)
	buffer := bytes.NewBuffer(buff)
	iv := make([]byte, blockSizeAES())
	hmac := make([]byte, 32)
	data := make([]byte, length-(len(iv)+len(hmac)))
	i, err := buffer.Read(iv)
	if err != nil || i < len(iv) {
		return nil, err
	}
	i, err = buffer.Read(data)
	if err != nil || i < len(iv) {
		return nil, err
	}
	i, err = buffer.Read(hmac)
	if err != nil || i < len(iv) {
		return nil, err
	}
	ctx, err := newAES(self.key, iv)
	if err != nil {
		return nil, err
	}
	plaintext := ctx.update(data)
	hmac2 := _HMAC_SHA256(plaintext, self.key)
	if !bytes.Equal(hmac, hmac2) {
		return nil, errors.New("GOMQ:decrypt:HMAC check fail")
	}
	return plaintext, nil
}

func (self *GOMQ) Close() {
	for _, sock_infos := range self.connections {
		sock_infos.Sock.Close()
	}
	if self.localsock != nil {
		self.localsock.Close()
		self.localsock = nil
	}
	self.context.Close()
}
<|endoftext|>


<commit_before>package http2

import (
	"fmt"
	"net"
)

func Connect(addr string) (client Session) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Successflly connected to %s\n", addr)

	client = NewSession(conn)
	return client
}
<commit_msg>send CONNECTION_PREFACE automatically<commit_after>package http2

import (
	"fmt"
	"net"
)

func Connect(addr string) (client Session) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		panic(err)
	}
	client = NewSession(conn)
	client.Send(CONNECTION_PREFACE)
	fmt.Printf("Successflly connected to %s\n", addr)

	return client
}
<|endoftext|>