<commit_before>package googlecompute

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"code.google.com/p/google-api-go-client/compute/v1"
	"github.com/golang/oauth2"
	"github.com/golang/oauth2/google"
	"github.com/mitchellh/packer/packer"
)

// driverGCE is a Driver implementation that actually talks to GCE.
// Create an instance using NewDriverGCE.
type driverGCE struct {
	projectId string
	service   *compute.Service
	ui        packer.Ui
}

var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}

func NewDriverGCE(ui packer.Ui, p string, a *accountFile) (Driver, error) {
	var f *oauth2.Options
	var err error

	// Auth with AccountFile first if provided
	if a.PrivateKey != "" {
		log.Printf("[INFO] Requesting Google token via AccountFile...")
		log.Printf("[INFO]   -- Email: %s", a.ClientEmail)
		log.Printf("[INFO]   -- Scopes: %s", DriverScopes)
		log.Printf("[INFO]   -- Private Key Length: %d", len(a.PrivateKey))

		f, err = oauth2.New(
			oauth2.JWTClient(a.ClientEmail, []byte(a.PrivateKey)),
			oauth2.Scope(DriverScopes...),
			google.JWTEndpoint())
	} else {
		log.Printf("[INFO] Requesting Google token via GCE Service Role...")

		f, err = oauth2.New(google.ComputeEngineAccount(""))
	}

	if err != nil {
		return nil, err
	}

	log.Printf("[INFO] Instantiating GCE client using...")
	service, err := compute.New(&http.Client{Transport: f.NewTransport()})
	if err != nil {
		return nil, err
	}

	return &driverGCE{
		projectId: p,
		service:   service,
		ui:        ui,
	}, nil
}

func (d *driverGCE) CreateImage(name, description, zone, disk string) <-chan error {
	image := &compute.Image{
		Description: description,
		Name:        name,
		SourceDisk:  fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk),
		SourceType:  "RAW",
	}

	errCh := make(chan error, 1)
	op, err := d.service.Images.Insert(d.projectId, image).Do()
	if err != nil {
		errCh <- err
	} else {
		go waitForState(errCh, "DONE", d.refreshGlobalOp(op))
	}

	return errCh
}

func (d *driverGCE) DeleteImage(name string) <-chan error {
	errCh := make(chan error, 1)
	op, err := d.service.Images.Delete(d.projectId, name).Do()
	if err != nil {
		errCh <- err
	} else {
		go waitForState(errCh, "DONE", d.refreshGlobalOp(op))
	}

	return errCh
}

func (d *driverGCE) DeleteInstance(zone, name string) (<-chan error, error) {
	op, err := d.service.Instances.Delete(d.projectId, zone, name).Do()
	if err != nil {
		return nil, err
	}

	errCh := make(chan error, 1)
	go waitForState(errCh, "DONE", d.refreshZoneOp(zone, op))
	return errCh, nil
}

func (d *driverGCE) DeleteDisk(zone, name string) (<-chan error, error) {
	op, err := d.service.Disks.Delete(d.projectId, zone, name).Do()
	if err != nil {
		return nil, err
	}

	errCh := make(chan error, 1)
	go waitForState(errCh, "DONE", d.refreshZoneOp(zone, op))
	return errCh, nil
}

func (d *driverGCE) GetNatIP(zone, name string) (string, error) {
	instance, err := d.service.Instances.Get(d.projectId, zone, name).Do()
	if err != nil {
		return "", err
	}

	for _, ni := range instance.NetworkInterfaces {
		if ni.AccessConfigs == nil {
			continue
		}

		for _, ac := range ni.AccessConfigs {
			if ac.NatIP != "" {
				return ac.NatIP, nil
			}
		}
	}

	return "", nil
}

func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
	// Get the zone
	d.ui.Message(fmt.Sprintf("Loading zone: %s", c.Zone))
	zone, err := d.service.Zones.Get(d.projectId, c.Zone).Do()
	if err != nil {
		return nil, err
	}

	// Get the image
	d.ui.Message(fmt.Sprintf("Loading image: %s in project %s", c.Image.Name, c.Image.ProjectId))
	image, err := d.getImage(c.Image)
	if err != nil {
		return nil, err
	}

	// Get the machine type
	d.ui.Message(fmt.Sprintf("Loading machine type: %s", c.MachineType))
	machineType, err := d.service.MachineTypes.Get(
		d.projectId, zone.Name, c.MachineType).Do()
	if err != nil {
		return nil, err
	}
	// TODO(mitchellh): deprecation warnings

	// Get the network
	d.ui.Message(fmt.Sprintf("Loading network: %s", c.Network))
	network, err := d.service.Networks.Get(d.projectId, c.Network).Do()
	if err != nil {
		return nil, err
	}

	// Build up the metadata
	metadata := make([]*compute.MetadataItems, len(c.Metadata))
	for k, v := range c.Metadata {
		metadata = append(metadata, &compute.MetadataItems{
			Key:   k,
			Value: v,
		})
	}

	// Create the instance information
	instance := compute.Instance{
		Description: c.Description,
		Disks: []*compute.AttachedDisk{
			&compute.AttachedDisk{
				Type:       "PERSISTENT",
				Mode:       "READ_WRITE",
				Kind:       "compute#attachedDisk",
				Boot:       true,
				AutoDelete: false,
				InitializeParams: &compute.AttachedDiskInitializeParams{
					SourceImage: image.SelfLink,
					DiskSizeGb:  c.DiskSizeGb,
				},
			},
		},
		MachineType: machineType.SelfLink,
		Metadata: &compute.Metadata{
			Items: metadata,
		},
		Name: c.Name,
		NetworkInterfaces: []*compute.NetworkInterface{
			&compute.NetworkInterface{
				AccessConfigs: []*compute.AccessConfig{
					&compute.AccessConfig{
						Name: "AccessConfig created by Packer",
						Type: "ONE_TO_ONE_NAT",
					},
				},
				Network: network.SelfLink,
			},
		},
		ServiceAccounts: []*compute.ServiceAccount{
			&compute.ServiceAccount{
				Email: "default",
				Scopes: []string{
					"https://www.googleapis.com/auth/userinfo.email",
					"https://www.googleapis.com/auth/compute",
					"https://www.googleapis.com/auth/devstorage.full_control",
				},
			},
		},
		Tags: &compute.Tags{
			Items: c.Tags,
		},
	}

	d.ui.Message("Requesting instance creation...")
	op, err := d.service.Instances.Insert(d.projectId, zone.Name, &instance).Do()
	if err != nil {
		return nil, err
	}

	errCh := make(chan error, 1)
	go waitForState(errCh, "DONE", d.refreshZoneOp(zone.Name, op))
	return errCh, nil
}

func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error {
	errCh := make(chan error, 1)
	go waitForState(errCh, state, d.refreshInstanceState(zone, name))
	return errCh
}

func (d *driverGCE) getImage(img Image) (image *compute.Image, err error) {
	projects := []string{img.ProjectId, "centos-cloud", "coreos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "ubuntu-os-cloud", "windows-cloud"}
	for _, project := range projects {
		image, err = d.service.Images.Get(project, img.Name).Do()
		if err == nil && image != nil && image.SelfLink != "" {
			return
		}
		image = nil
	}

	err = fmt.Errorf("Image %s could not be found in any of these projects: %s", img.Name, projects)
	return
}

func (d *driverGCE) refreshInstanceState(zone, name string) stateRefreshFunc {
	return func() (string, error) {
		instance, err := d.service.Instances.Get(d.projectId, zone, name).Do()
		if err != nil {
			return "", err
		}
		return instance.Status, nil
	}
}

func (d *driverGCE) refreshGlobalOp(op *compute.Operation) stateRefreshFunc {
	return func() (string, error) {
		newOp, err := d.service.GlobalOperations.Get(d.projectId, op.Name).Do()
		if err != nil {
			return "", err
		}

		// If the op is done, check for errors
		err = nil
		if newOp.Status == "DONE" {
			if newOp.Error != nil {
				for _, e := range newOp.Error.Errors {
					err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message))
				}
			}
		}

		return newOp.Status, err
	}
}

func (d *driverGCE) refreshZoneOp(zone string, op *compute.Operation) stateRefreshFunc {
	return func() (string, error) {
		newOp, err := d.service.ZoneOperations.Get(d.projectId, zone, op.Name).Do()
		if err != nil {
			return "", err
		}

		// If the op is done, check for errors
		err = nil
		if newOp.Status == "DONE" {
			if newOp.Error != nil {
				for _, e := range newOp.Error.Errors {
					err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message))
				}
			}
		}

		return newOp.Status, err
	}
}

// stateRefreshFunc is used to refresh the state of a thing and is
// used in conjunction with waitForState.
type stateRefreshFunc func() (string, error)

// waitForState will spin in a loop forever waiting for state to
// reach a certain target.
func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) {
	for {
		state, err := refresh()
		if err != nil {
			errCh <- err
			return
		}
		if state == target {
			errCh <- nil
			return
		}

		time.Sleep(2 * time.Second)
	}
}
<commit_msg>fix googlecompute oauth dependency<commit_after>package googlecompute

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"code.google.com/p/google-api-go-client/compute/v1"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"github.com/mitchellh/packer/packer"
)

// driverGCE is a Driver implementation that actually talks to GCE.
// Create an instance using NewDriverGCE.
type driverGCE struct {
	projectId string
	service   *compute.Service
	ui        packer.Ui
}

var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}

func NewDriverGCE(ui packer.Ui, p string, a *accountFile) (Driver, error) {
	var f *oauth2.Options
	var err error

	// Auth with AccountFile first if provided
	if a.PrivateKey != "" {
		log.Printf("[INFO] Requesting Google token via AccountFile...")
		log.Printf("[INFO]   -- Email: %s", a.ClientEmail)
		log.Printf("[INFO]   -- Scopes: %s", DriverScopes)
		log.Printf("[INFO]   -- Private Key Length: %d", len(a.PrivateKey))

		f, err = oauth2.New(
			oauth2.JWTClient(a.ClientEmail, []byte(a.PrivateKey)),
			oauth2.Scope(DriverScopes...),
			google.JWTEndpoint())
	} else {
		log.Printf("[INFO] Requesting Google token via GCE Service Role...")

		f, err = oauth2.New(google.ComputeEngineAccount(""))
	}

	if err != nil {
		return nil, err
	}

	log.Printf("[INFO] Instantiating GCE client using...")
	service, err := compute.New(&http.Client{Transport: f.NewTransport()})
	if err != nil {
		return nil, err
	}

	return &driverGCE{
		projectId: p,
		service:   service,
		ui:        ui,
	}, nil
}

func (d *driverGCE) CreateImage(name, description, zone, disk string) <-chan error {
	image := &compute.Image{
		Description: description,
		Name:        name,
		SourceDisk:  fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk),
		SourceType:  "RAW",
	}

	errCh := make(chan error, 1)
	op, err := d.service.Images.Insert(d.projectId, image).Do()
	if err != nil {
		errCh <- err
	} else {
		go waitForState(errCh, "DONE", d.refreshGlobalOp(op))
	}

	return errCh
}

func (d *driverGCE) DeleteImage(name string) <-chan error {
	errCh := make(chan error, 1)
	op, err := d.service.Images.Delete(d.projectId, name).Do()
	if err != nil {
		errCh <- err
	} else {
		go waitForState(errCh, "DONE", d.refreshGlobalOp(op))
	}

	return errCh
}

func (d *driverGCE) DeleteInstance(zone, name string) (<-chan error, error) {
	op, err := d.service.Instances.Delete(d.projectId, zone, name).Do()
	if err != nil {
		return nil, err
	}

	errCh := make(chan error, 1)
	go waitForState(errCh, "DONE", d.refreshZoneOp(zone, op))
	return errCh, nil
}

func (d *driverGCE) DeleteDisk(zone, name string) (<-chan error, error) {
	op, err := d.service.Disks.Delete(d.projectId, zone, name).Do()
	if err != nil {
		return nil, err
	}

	errCh := make(chan error, 1)
	go waitForState(errCh, "DONE", d.refreshZoneOp(zone, op))
	return errCh, nil
}

func (d *driverGCE) GetNatIP(zone, name string) (string, error) {
	instance, err := d.service.Instances.Get(d.projectId, zone, name).Do()
	if err != nil {
		return "", err
	}

	for _, ni := range instance.NetworkInterfaces {
		if ni.AccessConfigs == nil {
			continue
		}

		for _, ac := range ni.AccessConfigs {
			if ac.NatIP != "" {
				return ac.NatIP, nil
			}
		}
	}

	return "", nil
}

func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
	// Get the zone
	d.ui.Message(fmt.Sprintf("Loading zone: %s", c.Zone))
	zone, err := d.service.Zones.Get(d.projectId, c.Zone).Do()
	if err != nil {
		return nil, err
	}

	// Get the image
	d.ui.Message(fmt.Sprintf("Loading image: %s in project %s", c.Image.Name, c.Image.ProjectId))
	image, err := d.getImage(c.Image)
	if err != nil {
		return nil, err
	}

	// Get the machine type
	d.ui.Message(fmt.Sprintf("Loading machine type: %s", c.MachineType))
	machineType, err := d.service.MachineTypes.Get(
		d.projectId, zone.Name, c.MachineType).Do()
	if err != nil {
		return nil, err
	}
	// TODO(mitchellh): deprecation warnings

	// Get the network
	d.ui.Message(fmt.Sprintf("Loading network: %s", c.Network))
	network, err := d.service.Networks.Get(d.projectId, c.Network).Do()
	if err != nil {
		return nil, err
	}

	// Build up the metadata
	metadata := make([]*compute.MetadataItems, len(c.Metadata))
	for k, v := range c.Metadata {
		metadata = append(metadata, &compute.MetadataItems{
			Key:   k,
			Value: v,
		})
	}

	// Create the instance information
	instance := compute.Instance{
		Description: c.Description,
		Disks: []*compute.AttachedDisk{
			&compute.AttachedDisk{
				Type:       "PERSISTENT",
				Mode:       "READ_WRITE",
				Kind:       "compute#attachedDisk",
				Boot:       true,
				AutoDelete: false,
				InitializeParams: &compute.AttachedDiskInitializeParams{
					SourceImage: image.SelfLink,
					DiskSizeGb:  c.DiskSizeGb,
				},
			},
		},
		MachineType: machineType.SelfLink,
		Metadata: &compute.Metadata{
			Items: metadata,
		},
		Name: c.Name,
		NetworkInterfaces: []*compute.NetworkInterface{
			&compute.NetworkInterface{
				AccessConfigs: []*compute.AccessConfig{
					&compute.AccessConfig{
						Name: "AccessConfig created by Packer",
						Type: "ONE_TO_ONE_NAT",
					},
				},
				Network: network.SelfLink,
			},
		},
		ServiceAccounts: []*compute.ServiceAccount{
			&compute.ServiceAccount{
				Email: "default",
				Scopes: []string{
					"https://www.googleapis.com/auth/userinfo.email",
					"https://www.googleapis.com/auth/compute",
					"https://www.googleapis.com/auth/devstorage.full_control",
				},
			},
		},
		Tags: &compute.Tags{
			Items: c.Tags,
		},
	}

	d.ui.Message("Requesting instance creation...")
	op, err := d.service.Instances.Insert(d.projectId, zone.Name, &instance).Do()
	if err != nil {
		return nil, err
	}

	errCh := make(chan error, 1)
	go waitForState(errCh, "DONE", d.refreshZoneOp(zone.Name, op))
	return errCh, nil
}

func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error {
	errCh := make(chan error, 1)
	go waitForState(errCh, state, d.refreshInstanceState(zone, name))
	return errCh
}

func (d *driverGCE) getImage(img Image) (image *compute.Image, err error) {
	projects := []string{img.ProjectId, "centos-cloud", "coreos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "ubuntu-os-cloud", "windows-cloud"}
	for _, project := range projects {
		image, err = d.service.Images.Get(project, img.Name).Do()
		if err == nil && image != nil && image.SelfLink != "" {
			return
		}
		image = nil
	}

	err = fmt.Errorf("Image %s could not be found in any of these projects: %s", img.Name, projects)
	return
}

func (d *driverGCE) refreshInstanceState(zone, name string) stateRefreshFunc {
	return func() (string, error) {
		instance, err := d.service.Instances.Get(d.projectId, zone, name).Do()
		if err != nil {
			return "", err
		}
		return instance.Status, nil
	}
}

func (d *driverGCE) refreshGlobalOp(op *compute.Operation) stateRefreshFunc {
	return func() (string, error) {
		newOp, err := d.service.GlobalOperations.Get(d.projectId, op.Name).Do()
		if err != nil {
			return "", err
		}

		// If the op is done, check for errors
		err = nil
		if newOp.Status == "DONE" {
			if newOp.Error != nil {
				for _, e := range newOp.Error.Errors {
					err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message))
				}
			}
		}

		return newOp.Status, err
	}
}

func (d *driverGCE) refreshZoneOp(zone string, op *compute.Operation) stateRefreshFunc {
	return func() (string, error) {
		newOp, err := d.service.ZoneOperations.Get(d.projectId, zone, op.Name).Do()
		if err != nil {
			return "", err
		}

		// If the op is done, check for errors
		err = nil
		if newOp.Status == "DONE" {
			if newOp.Error != nil {
				for _, e := range newOp.Error.Errors {
					err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message))
				}
			}
		}

		return newOp.Status, err
	}
}

// stateRefreshFunc is used to refresh the state of a thing and is
// used in conjunction with waitForState.
type stateRefreshFunc func() (string, error)

// waitForState will spin in a loop forever waiting for state to
// reach a certain target.
func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) {
	for {
		state, err := refresh()
		if err != nil {
			errCh <- err
			return
		}
		if state == target {
			errCh <- nil
			return
		}

		time.Sleep(2 * time.Second)
	}
}
<|endoftext|>


<commit_before>package statistics

import (
	"fmt"
	"sync"
)

type StatisticsItem struct {
	// Method        string
	RequestTimes  int
	ResponseTimes int
}

type StatisticsMap struct {
	lock      sync.RWMutex
	methodMap map[string]*StatisticsItem
}

func (s *StatisticsMap) AddRequest(method string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	if _, ok := s.methodMap[method]; ok {
		s.methodMap[method].RequestTimes++
	} else {
		s.methodMap[method] = &StatisticsItem{
			RequestTimes:  0,
			ResponseTimes: 0,
		}
	}
}

func (s *StatisticsMap) AddResponse(method string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	if _, ok := s.methodMap[method]; ok {
		s.methodMap[method].ResponseTimes++
	} else {
		s.methodMap[method] = &StatisticsItem{
			RequestTimes:  0,
			ResponseTimes: 0,
		}
	}
}

func (s *StatisticsMap) Get() {
	s.lock.RLock()
	defer s.lock.RUnlock()
	for k, item := range s.methodMap {
		fmt.Println(k, item.RequestTimes, item.ResponseTimes)
	}
}

var Stat *StatisticsMap

func init() {
	Stat = &StatisticsMap{
		methodMap: make(map[string]*StatisticsItem),
	}
}
<commit_msg>update<commit_after><|endoftext|>


<commit_before>package sockets

import (
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"io"
	"net/http"
	"net/http/httptest"
	"runtime"
	"strings"
	"sync"
	"testing"
	"time"
)

const (
	host            string = "http://localhost:3000"
	endpoint        string = "ws://localhost:3000"
	recvPath        string = "/receiver"
	sendPath        string = "/sender"
	pingPath        string = "/ping"
	recvStringsPath string = "/strings/receiver"
	sendStringsPath string = "/strings/sender"
	pingStringsPath string = "/strings/ping"
	crossOriginPath string = "/cross/origin"
)

type Message struct {
	Text string `json:"text"`
}

var (
	once             sync.Once
	recvMessages     []*Message
	recvCount        int
	recvDone         bool
	sendMessages     []*Message
	sendCount        int
	sendDone         bool
	recvStrings      []string
	recvStringsCount int
	recvStringsDone  bool
	sendStrings      []string
	sendStringsCount int
	sendStringsDone  bool
)

// Test Helpers
func expectSame(t *testing.T, a interface{}, b interface{}) {
	if a != b {
		t.Errorf("Expected %T: %v to be %T: %v", b, b, a, a)
	}
}

func expectStringsToBeEmpty(t *testing.T, strings []string) {
	if len(strings) > 0 {
		t.Errorf("Expected strings array to be empty, but they contained %d values", len(strings))
	}
}

func expectMessagesToBeEmpty(t *testing.T, messages []*Message) {
	if len(messages) > 0 {
		t.Errorf("Expected messages array to be empty, but they contained %d values", len(messages))
	}
}

func expectStringsToHaveArrived(t *testing.T, count int, strings []string) {
	if len(strings) < count {
		t.Errorf("Expected strings array to contain 3 values, but contained %d", len(strings))
	} else {
		for i, s := range strings {
			if s != "Hello World" {
				t.Errorf("Expected string %d to be \"Hello World\", but was \"%v\"", i+1, s)
			}
		}
	}
}

func expectMessagesToHaveArrived(t *testing.T, count int, messages []*Message) {
	if len(messages) < count {
		t.Errorf("Expected messages array to contain 3 values, but contained %d", len(messages))
	} else {
		for i, m := range messages {
			if m.Text != "Hello World" {
				t.Errorf("Expected message %d to contain \"Hello World\", but contained %v", i+1, m)
			}
		}
	}
}

func expectPingsToHaveBeenExecuted(t *testing.T, count int, messages []*Message) {
	if len(messages) < count {
		t.Errorf("Expected messages array to contain 3 ping values, but contained %d", len(messages))
	} else {
		for i, m := range messages {
			if m.Text != "" {
				t.Errorf("Expected message %d to contain \"\", but contained %v", i+1, m)
			}
		}
	}
}

func expectStatusCode(t *testing.T, expectedStatusCode int, actualStatusCode int) {
	if actualStatusCode != expectedStatusCode {
		t.Errorf("Expected StatusCode %d, but received %d", expectedStatusCode, actualStatusCode)
	}
}

func expectIsDone(t *testing.T, done bool) {
	if !done {
		t.Errorf("Expected to be done, but was not")
	}
}

func startServer() {
	m := martini.Classic()

	m.Get(recvPath, JSON(Message{}), func(context martini.Context, receiver <-chan *Message, done <-chan bool) int {
		for {
			select {
			case msg := <-receiver:
				recvMessages = append(recvMessages, msg)
			case <-done:
				recvDone = true
				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	m.Get(sendPath, JSON(Message{}), func(context martini.Context, sender chan<- *Message, done <-chan bool, disconnect chan<- int) int {
		ticker := time.NewTicker(1 * time.Millisecond)
		bomb := time.After(4 * time.Millisecond)

		for {
			select {
			case <-ticker.C:
				sender <- &Message{"Hello World"}
			case <-done:
				ticker.Stop()
				sendDone = true
				return http.StatusOK
			case <-bomb:
				disconnect <- websocket.CloseGoingAway
				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	m.Get(recvStringsPath, Messages(), func(context martini.Context, receiver <-chan string, done <-chan bool) int {
		for {
			select {
			case msg := <-receiver:
				recvStrings = append(recvStrings, msg)
			case <-done:
				recvStringsDone = true
				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	m.Get(sendStringsPath, Messages(), func(context martini.Context, sender chan<- string, done <-chan bool, disconnect chan<- int) int {
		ticker := time.NewTicker(1 * time.Millisecond)
		bomb := time.After(4 * time.Millisecond)

		for {
			select {
			case <-ticker.C:
				sender <- "Hello World"
			case <-done:
				ticker.Stop()
				sendStringsDone = true
				return http.StatusOK
			case <-bomb:
				disconnect <- websocket.CloseGoingAway

				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	go m.Run()
	time.Sleep(5 * time.Millisecond)
}

func connectSocket(t *testing.T, path string) (*websocket.Conn, *http.Response) {
	header := make(http.Header)
	header.Add("Origin", host)
	ws, resp, err := websocket.DefaultDialer.Dial(endpoint+path, header)
	if err != nil {
		t.Fatalf("Connecting the socket failed: %s", err.Error())
	}

	return ws, resp
}

func TestStringReceive(t *testing.T) {
	once.Do(func() {
		runtime.GOMAXPROCS(runtime.NumCPU() * 2)
		startServer()
	})
	expectStringsToBeEmpty(t, recvStrings)

	ws, resp := connectSocket(t, recvStringsPath)

	ticker := time.NewTicker(time.Millisecond)

	for {
		<-ticker.C
		s := "Hello World"
		err := ws.WriteMessage(websocket.TextMessage, []byte(s))
		if err != nil {
			t.Errorf("Writing to the socket failed with %s", err.Error())
		}
		recvStringsCount++
		if recvStringsCount == 4 {
			ws.Close()
			return
		}
	}

	expectStringsToHaveArrived(t, 3, recvStrings)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, recvStringsDone)
}

func TestStringSend(t *testing.T) {
	once.Do(startServer)
	expectStringsToBeEmpty(t, sendStrings)

	ws, resp := connectSocket(t, sendStringsPath)
	defer ws.Close()

	for {
		_, msgArray, err := ws.ReadMessage()
		msg := string(msgArray)
		sendStrings = append(sendStrings, msg)
		if err != nil && err != io.EOF {
			t.Errorf("Receiving from the socket failed with %v", err)
		}
		if sendStringsCount == 3 {
			return
		}
		sendStringsCount++
	}
	expectStringsToHaveArrived(t, 3, sendStrings)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, sendStringsDone)
}

func TestJSONReceive(t *testing.T) {
	once.Do(startServer)
	expectMessagesToBeEmpty(t, recvMessages)

	ws, resp := connectSocket(t, recvPath)

	message := &Message{"Hello World"}

	ticker := time.NewTicker(time.Millisecond)

	for {
		<-ticker.C
		err := ws.WriteJSON(message)
		if err != nil {
			t.Errorf("Writing to the socket failed with %v", err)
		}
		recvCount++
		if recvCount == 4 {
			ws.Close()
			return
		}
	}

	expectMessagesToHaveArrived(t, 3, recvMessages)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, recvDone)
}

func TestJSONSend(t *testing.T) {
	once.Do(startServer)
	expectMessagesToBeEmpty(t, sendMessages)

	ws, resp := connectSocket(t, sendPath)
	defer ws.Close()

	for {
		msg := &Message{}
		err := ws.ReadJSON(msg)
		sendMessages = append(sendMessages, msg)
		if err != nil && err != io.EOF {
			t.Errorf("Receiving from the socket failed with %v", err)
		}
		if sendCount == 3 {
			return
		}
		sendCount++
	}
	expectMessagesToHaveArrived(t, 3, sendMessages)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, sendDone)
}

func TestOptionsDefaultHandling(t *testing.T) {
	o := newOptions([]*Options{
		&Options{
			LogLevel:   LogLevelDebug,
			WriteWait:  15 * time.Second,
			PingPeriod: 10 * time.Second,
		},
	})
	expectSame(t, o.LogLevel, LogLevelDebug)
	expectSame(t, o.PingPeriod, 10*time.Second)
	expectSame(t, o.WriteWait, 15*time.Second)
	expectSame(t, o.MaxMessageSize, defaultMaxMessageSize)
	expectSame(t, o.SendChannelBuffer, defaultSendChannelBuffer)
	expectSame(t, o.RecvChannelBuffer, defaultRecvChannelBuffer)
}

func TestAllowedCrossOrigin(t *testing.T) {
	m := martini.Classic()

	recorder := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "/test", strings.NewReader(""))
	req.Header.Add("Origin", "http://allowed.com")

	if err != nil {
		t.Error(err)
	}

	m.Any("/test", Messages(&Options{AllowedOrigin: "https?://allowed\\.com$"}), func(context martini.Context, receiver <-chan string, done <-chan bool) int {
		return http.StatusOK
	})

	m.ServeHTTP(recorder, req)
	// Fails at handshake stage, so we expect 400 Bad Request
	expectStatusCode(t, http.StatusBadRequest, recorder.Code)
}

func TestDisallowedCrossOrigin(t *testing.T) {
	m := martini.Classic()

	recorder := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "/test", strings.NewReader(""))
	req.Header.Add("Origin", "http://somewhere.com")

	if err != nil {
		t.Error(err)
	}

	m.Any("/test", Messages(), func() int {
		return http.StatusOK
	})

	m.ServeHTTP(recorder, req)
	expectStatusCode(t, http.StatusForbidden, recorder.Code)
}

func TestDisallowedMethods(t *testing.T) {
	m := martini.Classic()

	recorder := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/test", strings.NewReader(""))

	if err != nil {
		t.Error(err)
	}

	m.Any("/test", Messages(), func() int {
		return http.StatusOK
	})

	m.ServeHTTP(recorder, req)
	expectStatusCode(t, http.StatusMethodNotAllowed, recorder.Code)
}
<commit_msg>Ignore ws close after EOF in tests<commit_after>package sockets

import (
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync"
	"testing"
	"time"
)

const (
	host            string = "http://localhost:3000"
	endpoint        string = "ws://localhost:3000"
	recvPath        string = "/receiver"
	sendPath        string = "/sender"
	pingPath        string = "/ping"
	recvStringsPath string = "/strings/receiver"
	sendStringsPath string = "/strings/sender"
	pingStringsPath string = "/strings/ping"
	crossOriginPath string = "/cross/origin"
)

type Message struct {
	Text string `json:"text"`
}

var (
	once             sync.Once
	recvMessages     []*Message
	recvCount        int
	recvDone         bool
	sendMessages     []*Message
	sendCount        int
	sendDone         bool
	recvStrings      []string
	recvStringsCount int
	recvStringsDone  bool
	sendStrings      []string
	sendStringsCount int
	sendStringsDone  bool
)

// Test Helpers
func expectSame(t *testing.T, a interface{}, b interface{}) {
	if a != b {
		t.Errorf("Expected %T: %v to be %T: %v", b, b, a, a)
	}
}

func expectStringsToBeEmpty(t *testing.T, strings []string) {
	if len(strings) > 0 {
		t.Errorf("Expected strings array to be empty, but they contained %d values", len(strings))
	}
}

func expectMessagesToBeEmpty(t *testing.T, messages []*Message) {
	if len(messages) > 0 {
		t.Errorf("Expected messages array to be empty, but they contained %d values", len(messages))
	}
}

func expectStringsToHaveArrived(t *testing.T, count int, strings []string) {
	if len(strings) < count {
		t.Errorf("Expected strings array to contain 3 values, but contained %d", len(strings))
	} else {
		for i, s := range strings {
			if s != "Hello World" {
				t.Errorf("Expected string %d to be \"Hello World\", but was \"%v\"", i+1, s)
			}
		}
	}
}

func expectMessagesToHaveArrived(t *testing.T, count int, messages []*Message) {
	if len(messages) < count {
		t.Errorf("Expected messages array to contain 3 values, but contained %d", len(messages))
	} else {
		for i, m := range messages {
			if m.Text != "Hello World" {
				t.Errorf("Expected message %d to contain \"Hello World\", but contained %v", i+1, m)
			}
		}
	}
}

func expectPingsToHaveBeenExecuted(t *testing.T, count int, messages []*Message) {
	if len(messages) < count {
		t.Errorf("Expected messages array to contain 3 ping values, but contained %d", len(messages))
	} else {
		for i, m := range messages {
			if m.Text != "" {
				t.Errorf("Expected message %d to contain \"\", but contained %v", i+1, m)
			}
		}
	}
}

func expectStatusCode(t *testing.T, expectedStatusCode int, actualStatusCode int) {
	if actualStatusCode != expectedStatusCode {
		t.Errorf("Expected StatusCode %d, but received %d", expectedStatusCode, actualStatusCode)
	}
}

func expectIsDone(t *testing.T, done bool) {
	if !done {
		t.Errorf("Expected to be done, but was not")
	}
}

func startServer() {
	m := martini.Classic()

	m.Get(recvPath, JSON(Message{}), func(context martini.Context, receiver <-chan *Message, done <-chan bool) int {
		for {
			select {
			case msg := <-receiver:
				recvMessages = append(recvMessages, msg)
			case <-done:
				recvDone = true
				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	m.Get(sendPath, JSON(Message{}), func(context martini.Context, sender chan<- *Message, done <-chan bool, disconnect chan<- int) int {
		ticker := time.NewTicker(1 * time.Millisecond)
		bomb := time.After(4 * time.Millisecond)

		for {
			select {
			case <-ticker.C:
				sender <- &Message{"Hello World"}
			case <-done:
				ticker.Stop()
				sendDone = true
				return http.StatusOK
			case <-bomb:
				disconnect <- websocket.CloseGoingAway
				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	m.Get(recvStringsPath, Messages(), func(context martini.Context, receiver <-chan string, done <-chan bool) int {
		for {
			select {
			case msg := <-receiver:
				recvStrings = append(recvStrings, msg)
			case <-done:
				recvStringsDone = true
				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	m.Get(sendStringsPath, Messages(), func(context martini.Context, sender chan<- string, done <-chan bool, disconnect chan<- int) int {
		ticker := time.NewTicker(1 * time.Millisecond)
		bomb := time.After(4 * time.Millisecond)

		for {
			select {
			case <-ticker.C:
				sender <- "Hello World"
			case <-done:
				ticker.Stop()
				sendStringsDone = true
				return http.StatusOK
			case <-bomb:
				disconnect <- websocket.CloseGoingAway

				return http.StatusOK
			}
		}

		return http.StatusOK
	})

	go m.Run()
	time.Sleep(5 * time.Millisecond)
}

func connectSocket(t *testing.T, path string) (*websocket.Conn, *http.Response) {
	header := make(http.Header)
	header.Add("Origin", host)
	ws, resp, err := websocket.DefaultDialer.Dial(endpoint+path, header)
	if err != nil {
		t.Fatalf("Connecting the socket failed: %s", err.Error())
	}

	return ws, resp
}

func TestStringReceive(t *testing.T) {
	once.Do(startServer)
	expectStringsToBeEmpty(t, recvStrings)

	ws, resp := connectSocket(t, recvStringsPath)

	ticker := time.NewTicker(time.Millisecond)

	for {
		<-ticker.C
		s := "Hello World"
		err := ws.WriteMessage(websocket.TextMessage, []byte(s))
		if err != nil {
			t.Errorf("Writing to the socket failed with %s", err.Error())
		}
		recvStringsCount++
		if recvStringsCount == 4 {
			ws.Close()
			return
		}
	}

	expectStringsToHaveArrived(t, 3, recvStrings)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, recvStringsDone)
}

func TestStringSend(t *testing.T) {
	once.Do(startServer)
	expectStringsToBeEmpty(t, sendStrings)

	ws, resp := connectSocket(t, sendStringsPath)
	defer ws.Close()

	for {
		_, msgArray, err := ws.ReadMessage()
		msg := string(msgArray)
		sendStrings = append(sendStrings, msg)
		if sendStringsCount == 3 {
			return
		}
		if err != nil && err != io.EOF {
			t.Errorf("Receiving from the socket failed with %v", err)
		}
		sendStringsCount++
	}
	expectStringsToHaveArrived(t, 3, sendStrings)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, sendStringsDone)
}

func TestJSONReceive(t *testing.T) {
	once.Do(startServer)
	expectMessagesToBeEmpty(t, recvMessages)

	ws, resp := connectSocket(t, recvPath)

	message := &Message{"Hello World"}

	ticker := time.NewTicker(time.Millisecond)

	for {
		<-ticker.C
		err := ws.WriteJSON(message)
		if err != nil {
			t.Errorf("Writing to the socket failed with %v", err)
		}
		recvCount++
		if recvCount == 4 {
			ws.Close()
			return
		}
	}

	expectMessagesToHaveArrived(t, 3, recvMessages)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, recvDone)
}

func TestJSONSend(t *testing.T) {
	once.Do(startServer)
	expectMessagesToBeEmpty(t, sendMessages)

	ws, resp := connectSocket(t, sendPath)
	defer ws.Close()

	for {
		msg := &Message{}
		err := ws.ReadJSON(msg)
		sendMessages = append(sendMessages, msg)
		if sendCount == 3 {
			return
		}
		if err != nil && err != io.EOF {
			t.Errorf("Receiving from the socket failed with %v", err)
		}
		sendCount++
	}
	expectMessagesToHaveArrived(t, 3, sendMessages)
	expectStatusCode(t, http.StatusSwitchingProtocols, resp.StatusCode)
	expectIsDone(t, sendDone)
}

func TestOptionsDefaultHandling(t *testing.T) {
	o := newOptions([]*Options{
		&Options{
			LogLevel:   LogLevelDebug,
			WriteWait:  15 * time.Second,
			PingPeriod: 10 * time.Second,
		},
	})
	expectSame(t, o.LogLevel, LogLevelDebug)
	expectSame(t, o.PingPeriod, 10*time.Second)
	expectSame(t, o.WriteWait, 15*time.Second)
	expectSame(t, o.MaxMessageSize, defaultMaxMessageSize)
	expectSame(t, o.SendChannelBuffer, defaultSendChannelBuffer)
	expectSame(t, o.RecvChannelBuffer, defaultRecvChannelBuffer)
}

func TestAllowedCrossOrigin(t *testing.T) {
	m := martini.Classic()

	recorder := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "/test", strings.NewReader(""))
	req.Header.Add("Origin", "http://allowed.com")

	if err != nil {
		t.Error(err)
	}

	m.Any("/test", Messages(&Options{AllowedOrigin: "https?://allowed\\.com$"}), func(context martini.Context, receiver <-chan string, done <-chan bool) int {
		return http.StatusOK
	})

	m.ServeHTTP(recorder, req)
	// Fails at handshake stage, so we expect 400 Bad Request
	expectStatusCode(t, http.StatusBadRequest, recorder.Code)
}

func TestDisallowedCrossOrigin(t *testing.T) {
	m := martini.Classic()

	recorder := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "/test", strings.NewReader(""))
	req.Header.Add("Origin", "http://somewhere.com")

	if err != nil {
		t.Error(err)
	}

	m.Any("/test", Messages(), func() int {
		return http.StatusOK
	})

	m.ServeHTTP(recorder, req)
	expectStatusCode(t, http.StatusForbidden, recorder.Code)
}

func TestDisallowedMethods(t *testing.T) {
	m := martini.Classic()

	recorder := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/test", strings.NewReader(""))

	if err != nil {
		t.Error(err)
	}

	m.Any("/test", Messages(), func() int {
		return http.StatusOK
	})

	m.ServeHTTP(recorder, req)
	expectStatusCode(t, http.StatusMethodNotAllowed, recorder.Code)
}
<|endoftext|>


<commit_before>package questions

import r "github.com/dancannon/gorethink"

type Repository struct {
	session  *r.Session
	database string
	table    string
}

func (re Repository) Table() r.Term {
	return r.DB(re.database).Table(re.table)
}

func NewRepository(server string, database string) Repository {
	var session *r.Session
	session, err := r.Connect(r.ConnectOpts{
		Address: server,
	})
	if err != nil {
		panic(err)
	}
	return Repository{session, database, "questions"}
}

func (re Repository) GetAll() ([]Question, error) {
	res, err := re.Table().Run(re.session)
	if err != nil {
		return nil, err
	}
	var questions []Question
	err = res.All(&questions)
	if err != nil {
		return nil, err
	}
	return questions, err
}

func (re Repository) Create(q *Question) error {
	res, err := re.Table().Insert(q).RunWrite(re.session)
	if err != nil {
		return err
	}
	q.ID = res.GeneratedKeys[0]
	return nil
}

func (re Repository) Update(q *Question) error {
	if _, err := re.Table().Get(q.ID).Update(q).RunWrite(re.session); err != nil {
		return err
	}
	return nil
}

func (re Repository) Remove(id string) error {
	if _, err := re.Table().Get(id).Delete().RunWrite(re.session); err != nil {
		return err
	}
	return nil
}
<commit_msg>feat(questions): add auto creation of table and database<commit_after>package questions

import r "github.com/dancannon/gorethink"

import "log"

type Repository struct {
	session  *r.Session
	database string
	table    string
}

func (re Repository) Table() r.Term {
	return r.DB(re.database).Table(re.table)
}

func initDB(session *r.Session, dbName string) error {
	log.Println("initDB", dbName)
	dbListRes, err := r.DBList().Run(session)
	if err != nil {
		return err
	}
	var dbs []string
	if err := dbListRes.All(&dbs); err != nil {
		return err
	}
	if !isStringInArray(dbName, dbs) {
		log.Println("Create DB", dbName)
		_, errCreateDB := r.DBCreate(dbName).RunWrite(session)
		if errCreateDB != nil {
			return errCreateDB
		}
	}
	return nil
}

func initTable(session *r.Session, dbName string, table string) error {
	log.Println("initTable", table, "in DB", dbName)
	tableListRes, err := r.DB(dbName).TableList().Run(session)
	if err != nil {
		return err
	}
	var tables []string
	if err := tableListRes.All(&tables); err != nil {
		return err
	}
	if !isStringInArray(table, tables) {
		log.Println("Create table", table, "in DB", dbName)
		_, errCreateDB := r.DB(dbName).TableCreate(table).RunWrite(session)
		if errCreateDB != nil {
			panic(errCreateDB)
		}
	}
	return nil
}

func initRepository(repo Repository) error {
	if err := initDB(repo.session, repo.database); err != nil {
		return err
	}
	if err := initTable(repo.session, repo.database, repo.table); err != nil {
		return err
	}
	return nil
}

func isStringInArray(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func NewRepository(server string, database string) Repository {
	var session *r.Session
	session, err := r.Connect(r.ConnectOpts{
		Address: server,
	})
	if err != nil {
		panic(err)
	}
	var repo = Repository{session, database, "questions"}
	if err := initRepository(repo); err != nil {
		panic(err)
	}
	return repo
}

func (re Repository) GetAll() ([]Question, error) {
	re.session.Query(r.Query{})
	res, err := re.Table().Run(re.session)
	if err != nil {
		return nil, err
	}
	var questions []Question
	err = res.All(&questions)
	if err != nil {
		return nil, err
	}
	return questions, err
}

func (re Repository) Create(q *Question) error {
	res, err := re.Table().Insert(q).RunWrite(re.session)
	if err != nil {
		return err
	}
	q.ID = res.GeneratedKeys[0]
	return nil
}

func (re Repository) Update(q *Question) error {
	if _, err := re.Table().Get(q.ID).Update(q).RunWrite(re.session); err != nil {
		return err
	}
	return nil
}

func (re Repository) Remove(id string) error {
	if _, err := re.Table().Get(id).Delete().RunWrite(re.session); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Marek Dolgos
//
//   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 topoutil

import (
	"bufio"
	"fmt"
	"net/http"
	"sync"

	"github.com/mdmarek/topo"
)

type mesg struct {
	key  uint64
	body string
}

func (m *mesg) Key() uint64 {
	return m.key
}

func (m *mesg) Body() interface{} {
	return m.body
}

// Sink reads from the work chan and prints the body of each message. When
// the work chan is closed it joins the wait group.
func Sink(name int, wg *sync.WaitGroup, work <-chan topo.Mesg) {
	defer wg.Done()
	fmt.Printf("Sink %d starting...\n", name)
	for w := range work {
		switch b := w.Body().(type) {
		default:
			fmt.Printf("Sink %d: unknown body type: %T :: %v\n", name, b, b)
		case string:
			fmt.Printf("Sink %d: %v\n", name, b)
		}

	}
	fmt.Printf("Sink %d finished.\n", name)
}

// NewMeetup creates a channel of messags sourced from meetup.com's public stream.
// More info at: http://www.meetup.com/meetup_api/docs/stream/2/rsvps/
func NewMeetupSource(t topo.Topo) (<-chan topo.Mesg, error) {
	return NewChunkedHttpSource("http://stream.meetup.com/2/rsvps", t, "meetup")
}

// NewUsaGov creates a channel of messags sourced from USA.gov's public stream of bit.ly clicks.
// More info at: http://www.usa.gov/About/developer-resources/1usagov.shtml
func NewUsaGovSource(t topo.Topo) (<-chan topo.Mesg, error) {
	return NewChunkedHttpSource("http://developer.usa.gov/1usagov", t, "usagov")
}

// NewChunkedHttpSource creates a channel of messages sourced from a chunked HTTP connection
// which sends each message delimited by a newline. Paremter 'url' is the source, and 'name'
// is included in errors printed.
func NewChunkedHttpSource(url string, t topo.Topo, name string) (<-chan topo.Mesg, error) {
	if resp, err := http.Get(url); err != nil {
		return nil, err
	} else {
		out := make(chan topo.Mesg)
		go func(exit <-chan int) {
			defer close(out)
			// Scanner by default will split on newlines, if the chunked HTTP source
			// delimits by newline then this scanner will work.
			scanner := bufio.NewScanner(resp.Body)
			for {
				scanner.Scan()
				err = scanner.Err()
				if err != nil {
					fmt.Printf("error: source: %v: %v\n", name, err)
					return
				}
				// Read the text body of the scan, since there
				// was no error.
				body := scanner.Text()
				select {
				case out <- &mesg{0, body}:
				case <-exit:
					return
				}
			}
		}(t.ExitChan())
		return out, nil
	}
}
<commit_msg>Compile fix for exit chan.<commit_after>// Copyright 2014 Marek Dolgos
//
//   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 topoutil

import (
	"bufio"
	"fmt"
	"net/http"
	"sync"

	"github.com/mdmarek/topo"
)

type mesg struct {
	key  uint64
	body string
}

func (m *mesg) Key() uint64 {
	return m.key
}

func (m *mesg) Body() interface{} {
	return m.body
}

// Sink reads from the work chan and prints the body of each message. When
// the work chan is closed it joins the wait group.
func Sink(name int, wg *sync.WaitGroup, work <-chan topo.Mesg) {
	defer wg.Done()
	fmt.Printf("Sink %d starting...\n", name)
	for w := range work {
		switch b := w.Body().(type) {
		default:
			fmt.Printf("Sink %d: unknown body type: %T :: %v\n", name, b, b)
		case string:
			fmt.Printf("Sink %d: %v\n", name, b)
		}

	}
	fmt.Printf("Sink %d finished.\n", name)
}

// NewMeetup creates a channel of messags sourced from meetup.com's public stream.
// More info at: http://www.meetup.com/meetup_api/docs/stream/2/rsvps/
func NewMeetupSource(t topo.Topo) (<-chan topo.Mesg, error) {
	return NewChunkedHttpSource("http://stream.meetup.com/2/rsvps", t, "meetup")
}

// NewUsaGov creates a channel of messags sourced from USA.gov's public stream of bit.ly clicks.
// More info at: http://www.usa.gov/About/developer-resources/1usagov.shtml
func NewUsaGovSource(t topo.Topo) (<-chan topo.Mesg, error) {
	return NewChunkedHttpSource("http://developer.usa.gov/1usagov", t, "usagov")
}

// NewChunkedHttpSource creates a channel of messages sourced from a chunked HTTP connection
// which sends each message delimited by a newline. Paremter 'url' is the source, and 'name'
// is included in errors printed.
func NewChunkedHttpSource(url string, t topo.Topo, name string) (<-chan topo.Mesg, error) {
	if resp, err := http.Get(url); err != nil {
		return nil, err
	} else {
		out := make(chan topo.Mesg)
		go func(exit <-chan bool) {
			defer close(out)
			// Scanner by default will split on newlines, if the chunked HTTP source
			// delimits by newline then this scanner will work.
			scanner := bufio.NewScanner(resp.Body)
			for {
				scanner.Scan()
				err = scanner.Err()
				if err != nil {
					fmt.Printf("error: source: %v: %v\n", name, err)
					return
				}
				// Read the text body of the scan, since there
				// was no error.
				body := scanner.Text()
				select {
				case out <- &mesg{0, body}:
				case <-exit:
					return
				}
			}
		}(t.ExitChan())
		return out, nil
	}
}
<|endoftext|>


<commit_before>package arena

import (
	"encoding/json"
	"fmt"
	"io"
	"time"

	"github.com/asciimoo/solary/arena/board"
	"github.com/asciimoo/solary/arena/coord"
	"github.com/asciimoo/solary/player"
)

const ROUND_TIMEOUT = 2

var battle_id uint = 0
var player_id uint = 0

type Arena struct {
	Id      uint
	Round   uint
	Players []*player.Player
	Board   *board.Board
}

func Create() *Arena {
	a := &Arena{
		battle_id,
		0,
		make([]*player.Player, 0),
		board.Create(),
	}
	battle_id += 1
	return a
}

func (a *Arena) Play() {
	fmt.Println("Game", a.Id, "started")
	ch := make(chan *player.Move)
	for _, p := range a.Players {
		defer p.Conn.Close()
		go p.Read(ch)
	}
	for {
		a.Round += 1
		a.broadcastStatus()
		if a.Round == 500 || a.getActivePlayersNum() == 0 {
			fmt.Println("Game", a.Id, "finished")
			return
		}
		if a.Round%100 == 0 {
			a.Board.PopulateRandomLoot()
		}
		fmt.Println("Round", a.Round, "started")
		// collect moves
		moves := a.getMoves(ch)
		// activate laser beams and traps
		for _, move := range moves {
			if move.Item == "" {
				continue
			}
			c, ok := move.Player.Inventory[move.Item]
			if !ok || c <= 0 {
				continue
			}
			move.Player.Inventory[move.Item] -= 1
			switch move.Item {
			case "trap":
				a.Board.FieldByCoord(move.Player.Position).AddTrap()
			case "laser beam":
				laser_x := 0
				laser_y := 0
				switch move.Direction {
				case "up":
					laser_y -= 1
				case "down":
					laser_y += 1
				case "left":
					laser_x -= 1
				case "right":
					laser_x += 1
				}
				for i := 1; i <= 2; i++ {
					laser_coord := coord.Coord{
						uint(laser_x*i) + move.Player.Position.X,
						uint(laser_y*i) + move.Player.Position.Y,
					}
					for _, p := range a.Players {
						if p.Position.X == laser_coord.X && p.Position.Y == laser_coord.Y {
							p.Life -= 25
						}
					}
				}
			case "oil":
				if move.Player.Life > 80 {
					move.Player.Life = 100
				} else {
					move.Player.Life += 20
				}
			}
		}

		a.movePlayers(&moves)

		a.checkDeath()

		// helper coord->user map
		player_coords := make(map[coord.Coord][]*player.Player)
		for _, p := range a.Players {
			player_coords[p.Position] = append(player_coords[p.Position], p)
		}

		// activate destination traps && collect loot
		for c, players := range player_coords {
			field := a.Board.FieldByCoord(c)
			if len(players) == 1 {
				for _, l := range a.Board.FieldByCoord(c).Loot {
					players[0].Inventory[l] += 1
				}
				field.ClearLoot()
			}
			if field.Traps > 0 {
				for _, p := range players {
					p.Life -= 50 * a.Board.FieldByCoord(c).Traps
				}
				field.ClearTraps()
			}
		}

		a.checkDeath()

		// trigger inventory items
		for _, p := range a.Players {
			p.Score += p.Inventory["solar panel"]
		}
	}
}

func (a *Arena) movePlayers(moves *[]*player.Move) {
	for _, move := range *moves {
		a.movePlayer(move)
	}
}

func (a *Arena) checkDeath() {
	for _, p := range a.Players {
		if p.Life <= 0 {
			field := a.Board.FieldByCoord(p.Position)
			for loot, loot_count := range p.Inventory {
				p.Inventory[loot] = 0
				for i := 0; i < loot_count; i++ {
					field.Loot = append(field.Loot, loot)
				}
			}
			p.Life = 100
			p.Position.X = 0
			p.Position.Y = 0
		}
	}
}

func (a *Arena) movePlayer(m *player.Move) {
	new_y := m.Player.Position.Y
	new_x := m.Player.Position.X
	distance := uint(1)
	if m.Item == "pogo stick" {
		distance = 2
	}
	switch m.Direction {
	case "up":
		new_y -= distance
	case "down":
		new_y += distance
	case "left":
		new_x -= distance
	case "right":
		new_x += distance
	}
	if a.Board.IsValidLocation(int(new_x), int(new_y)) {
		m.Player.Position.X = new_x
		m.Player.Position.Y = new_y
	}
	fmt.Println("player", m.Player.Id, "moves to", m.Direction)
}

func (a *Arena) getMoves(ch chan *player.Move) []*player.Move {
	moves := []*player.Move{}
	timeout := make(chan bool)
	go func() {
		time.Sleep(ROUND_TIMEOUT * time.Second)
		timeout <- true
	}()
	recv_break := false
	for !recv_break && len(moves) < a.getActivePlayersNum() {
		select {
		case move := <-ch:
			if move.Error != nil {
				fmt.Println("user error:", move.Error)
				break
			}
			can_move := true
			for _, m := range moves {
				if m.Player == move.Player {
					can_move = false
					break
				}
			}
			if can_move {
				moves = append(moves, move)
			} else {
				fmt.Println("Error: already moved")
			}
		case <-timeout:
			fmt.Println("Timeout")
			recv_break = true
		}
	}
	return moves
}

func (a *Arena) getActivePlayersNum() int {
	active_players := 0
	for _, player := range a.Players {
		if !player.Disconnected {
			active_players += 1
		}
	}
	return active_players
}

func (a *Arena) broadcastStatus() {
	b, err := json.Marshal(a)
	if err != nil {
		fmt.Println(err)
	}
	for _, p := range a.Players {
		p.Write(b)
	}
}

func (a *Arena) AddPlayer(conn io.ReadWriteCloser) {
	p := player.Create(player_id, conn)
	msg, _ := json.Marshal(p)
	p.Write(msg)
	player_id += 1
	a.Players = append(a.Players, p)
}
<commit_msg>[fix] remove debug messages<commit_after>package arena

import (
	"encoding/json"
	"fmt"
	"io"
	"time"

	"github.com/asciimoo/solary/arena/board"
	"github.com/asciimoo/solary/arena/coord"
	"github.com/asciimoo/solary/player"
)

const ROUND_TIMEOUT = 2

var battle_id uint = 0
var player_id uint = 0

type Arena struct {
	Id      uint
	Round   uint
	Players []*player.Player
	Board   *board.Board
}

func Create() *Arena {
	a := &Arena{
		battle_id,
		0,
		make([]*player.Player, 0),
		board.Create(),
	}
	battle_id += 1
	return a
}

func (a *Arena) Play() {
	ch := make(chan *player.Move)
	for _, p := range a.Players {
		defer p.Conn.Close()
		go p.Read(ch)
	}
	for {
		a.Round += 1
		a.broadcastStatus()
		if a.Round == 500 || a.getActivePlayersNum() == 0 {
			fmt.Println("Game", a.Id, "finished")
			return
		}
		if a.Round%100 == 0 {
			a.Board.PopulateRandomLoot()
		}
		fmt.Println("Round", a.Round, "started")
		// collect moves
		moves := a.getMoves(ch)
		// activate laser beams and traps
		for _, move := range moves {
			if move.Item == "" {
				continue
			}
			c, ok := move.Player.Inventory[move.Item]
			if !ok || c <= 0 {
				continue
			}
			move.Player.Inventory[move.Item] -= 1
			switch move.Item {
			case "trap":
				a.Board.FieldByCoord(move.Player.Position).AddTrap()
			case "laser beam":
				laser_x := 0
				laser_y := 0
				switch move.Direction {
				case "up":
					laser_y -= 1
				case "down":
					laser_y += 1
				case "left":
					laser_x -= 1
				case "right":
					laser_x += 1
				}
				for i := 1; i <= 2; i++ {
					laser_coord := coord.Coord{
						uint(laser_x*i) + move.Player.Position.X,
						uint(laser_y*i) + move.Player.Position.Y,
					}
					for _, p := range a.Players {
						if p.Position.X == laser_coord.X && p.Position.Y == laser_coord.Y {
							p.Life -= 25
						}
					}
				}
			case "oil":
				if move.Player.Life > 80 {
					move.Player.Life = 100
				} else {
					move.Player.Life += 20
				}
			}
		}

		a.movePlayers(&moves)

		a.checkDeath()

		// helper coord->user map
		player_coords := make(map[coord.Coord][]*player.Player)
		for _, p := range a.Players {
			player_coords[p.Position] = append(player_coords[p.Position], p)
		}

		// activate destination traps && collect loot
		for c, players := range player_coords {
			field := a.Board.FieldByCoord(c)
			if len(players) == 1 {
				for _, l := range a.Board.FieldByCoord(c).Loot {
					players[0].Inventory[l] += 1
				}
				field.ClearLoot()
			}
			if field.Traps > 0 {
				for _, p := range players {
					p.Life -= 50 * a.Board.FieldByCoord(c).Traps
				}
				field.ClearTraps()
			}
		}

		a.checkDeath()

		// trigger inventory items
		for _, p := range a.Players {
			p.Score += p.Inventory["solar panel"]
		}
	}
}

func (a *Arena) movePlayers(moves *[]*player.Move) {
	for _, move := range *moves {
		a.movePlayer(move)
	}
}

func (a *Arena) checkDeath() {
	for _, p := range a.Players {
		if p.Life <= 0 {
			field := a.Board.FieldByCoord(p.Position)
			for loot, loot_count := range p.Inventory {
				p.Inventory[loot] = 0
				for i := 0; i < loot_count; i++ {
					field.Loot = append(field.Loot, loot)
				}
			}
			p.Life = 100
			p.Position.X = 0
			p.Position.Y = 0
		}
	}
}

func (a *Arena) movePlayer(m *player.Move) {
	new_y := m.Player.Position.Y
	new_x := m.Player.Position.X
	distance := uint(1)
	if m.Item == "pogo stick" {
		distance = 2
	}
	switch m.Direction {
	case "up":
		new_y -= distance
	case "down":
		new_y += distance
	case "left":
		new_x -= distance
	case "right":
		new_x += distance
	}
	if a.Board.IsValidLocation(int(new_x), int(new_y)) {
		m.Player.Position.X = new_x
		m.Player.Position.Y = new_y
	}
}

func (a *Arena) getMoves(ch chan *player.Move) []*player.Move {
	moves := []*player.Move{}
	timeout := make(chan bool)
	go func() {
		time.Sleep(ROUND_TIMEOUT * time.Second)
		timeout <- true
	}()
	recv_break := false
	for !recv_break && len(moves) < a.getActivePlayersNum() {
		select {
		case move := <-ch:
			if move.Error != nil {
				fmt.Println("user error:", move.Error)
				break
			}
			can_move := true
			for _, m := range moves {
				if m.Player == move.Player {
					can_move = false
					break
				}
			}
			if can_move {
				moves = append(moves, move)
			} else {
				fmt.Println("Error: already moved")
			}
		case <-timeout:
			fmt.Println("Timeout")
			recv_break = true
		}
	}
	return moves
}

func (a *Arena) getActivePlayersNum() int {
	active_players := 0
	for _, player := range a.Players {
		if !player.Disconnected {
			active_players += 1
		}
	}
	return active_players
}

func (a *Arena) broadcastStatus() {
	b, err := json.Marshal(a)
	if err != nil {
		fmt.Println(err)
	}
	for _, p := range a.Players {
		p.Write(b)
	}
}

func (a *Arena) AddPlayer(conn io.ReadWriteCloser) {
	p := player.Create(player_id, conn)
	msg, _ := json.Marshal(p)
	p.Write(msg)
	player_id += 1
	a.Players = append(a.Players, p)
}
<|endoftext|>


<commit_before>package engine

import (
    "fmt"
    "os"
    "os/exec"
    "strings"
)

type AsciidoctorOpts struct {
    files []*os.File
    backend string
    doctype string
    outFile *os.File
    noHeaderFooter bool
    sectionNumbers bool
    attributes map[string]string
    baseDir string
    destinationDir string
}

func Opts() *AsciidoctorOpts {
    return new(AsciidoctorOpts)
}

func Invoke(opts *AsciidoctorOpts) (out []byte, err error) {
    command := make([]string, 30)

    if opts.backend != "" {
        command = append(command, "-b", opts.backend)
    }

    if opts.doctype != "" {
        command = append(command, "-d", opts.doctype)
    }

    if opts.outFile != nil && opts.outFile.Name() != "" {
        command = append(command, "-o", opts.outFile.Name())
    }

    if opts.noHeaderFooter  {
        command = append(command, "-s")
    }
    if opts.sectionNumbers {
        command = append(command, "-n")
    }
    for key, value := range opts.attributes {
        command = append(command, "-a", key + "=" + value)
    }
    if opts.baseDir != "" {
        command = append(command, "-B", opts.baseDir)
    }
    if opts.destinationDir != "" {
        command = append(command, "-D", opts.destinationDir)
    }

    if opts.files != nil {
        for _,file := range opts.files {
            command = append(command, file.Name())
        }
    }
    out, err = exec.Command("asciidoctor", strings.TrimSpace(strings.Join(command, " "))).CombinedOutput()
    if err != nil {
        return out, err
    }
    fmt.Printf("%s", out)
    return out, nil
}
<commit_msg>Fix asciidoctor command preparation<commit_after>package engine

import (
    "fmt"
    "os"
    "os/exec"
//    "strings"
)

type AsciidoctorOpts struct {
    files []*os.File
    backend string
    doctype string
    outFile *os.File
    noHeaderFooter bool
    sectionNumbers bool
    attributes map[string]string
    baseDir string
    destinationDir string
}

func Opts() *AsciidoctorOpts {
    return new(AsciidoctorOpts)
}

func Invoke(opts *AsciidoctorOpts) (out []byte, err error) {
    command := make([]string, 0)

    if opts.backend != "" {
        command = append(command, "-b", opts.backend)
    }

    if opts.doctype != "" {
        command = append(command, "-d", opts.doctype)
    }

    if opts.outFile != nil && opts.outFile.Name() != "" {
        command = append(command, "-o", opts.outFile.Name())
    }

    if opts.noHeaderFooter  {
        command = append(command, "-s")
    }
    if opts.sectionNumbers {
        command = append(command, "-n")
    }
    for key, value := range opts.attributes {
        command = append(command, "-a", key + "=" + value)
    }
    if opts.baseDir != "" {
        command = append(command, "-B", opts.baseDir)
    }
    if opts.destinationDir != "" {
        command = append(command, "-D", opts.destinationDir)
    }

    if opts.files != nil {
        for _,file := range opts.files {
            command = append(command, file.Name())
        }
    }
    fmt.Printf("Executing command %v \n", command);
    out, err = exec.Command("asciidoctor", command...).CombinedOutput()
    if err != nil {
        return out, err
    }
    fmt.Printf("%s", out)
    return out, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/hfern/goseq"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type MasterQueryOptions struct {
	Region string `long:"region" short:"r" default:"USW" description:"Region code to get results for. See --list-regions"`
	Serial bool   `long:"serial" short:"s" default:"false" description:"Force serial querying of the servers."`
	Fields string `long:"fields" default:"ip=21,name" description:"The fields to be included. Optionally includes the min-length space. See --list-fields" `
	// TODO(hunter): Add this
	ShowFields bool `long:"show-fields" default:"false" description:"Print details on each available field."`
	// TODO(hunter): Add this
	MasterIP string `long:"ip" default:"" description:"IP of the Master server to query."`
	Divider  string `long:"divider" default:" ¦ " description:"Characters used to seperate fields."`
	// TODO(hunter): Add this
	StartIP          string `long:"start" default:"" description:"Where to start reading IPs from. Defaults to start of list."`
	Limit            int    `long:"limit" short:"l" default:"0" description:"Limit the result set to n successful rows."`
	NoHeader         bool   `long:"no-header" default:"false" description:"Don't show header w/ column names."`
	ShowUnreachable  bool   `long:"unreachable" short:"U" default:"false" description:"Show unreachable servers (couldn't be connected to)."`
	ShowErrorSummary bool   `long:"errors" short:"E" default:"false" description:"Show error summary at end of list."`
	// TODO(hunter): Add this
	Filters map[string]string `long:"filter" short:"f" description:"Filters to use. See --list-filters"`
	// TODO(hunter): Add this
	ListFilters bool `long:"list-filters" default:"false" description:"List known filters." group:"Lists"`
	ListRegions bool `long:"list-regions" default:"false" description:"List Regions." group:"Lists"`
	ListFields  bool `long:"list-fields" default:"false" description:"List Server Fields." group:"Lists"`
	Json        bool `long:"json" default:"false" description:"Output as JSON to StdOut"`
}

var masterOptions MasterQueryOptions

var _fieldregexp = regexp.MustCompile(`\s*(([a-z]+)(=(\d+))?)\s*,?\s*`)

type infoPrinter func()

func masterctx() {
	log.SetFlags(0)

	if printInfo() {
		return
	}

	unreachable := 0
	errorsEncountererd := make([]error, 0)

	userRegionStr := strings.ToUpper(masterOptions.Region)

	region, found := regionData[userRegionStr]

	if !found {
		fmt.Errorf("Region '%s' does not exist.", masterOptions.Region)
		return
	}

	fields, err := parseFields(masterOptions.Fields, serverFieldProperties)

	if err != nil {
		log.Fatal(err)
	}

	master := goseq.NewMasterServer()
	master.SetRegion(region)

	if masterOptions.MasterIP != "" {
		master.SetAddr(masterOptions.MasterIP)
	} else {
		master.SetAddr(goseq.MasterSourceServers[0])
	}

	startIp := string(goseq.NoAddress)

	if masterOptions.StartIP != "" {
		startIp = masterOptions.StartIP
	}

	// Apply filters
	filt := master.GetFilter()
	for fname, fval := range masterOptions.Filters {
		filt.Set(fname, fval)
	}
	master.SetFilter(filt)

	servers, err := master.Query(startIp)

	if err != nil {
		log.Fatal(err)
	}

	numServers := len(servers)

	rec := make(chan SvResponse)
	printer := make(chan SvResponse)

	//tups := make([]SvResponse, 0, numServers)

	if !masterOptions.Serial {
		go AsyncQueryServers(rec, servers, 1*time.Second)
	} else {
		go serialQueryServers(rec, servers, 1*time.Second)
	}

	var writer Printer

	if masterOptions.Json {
		writer = &jsonWriter{}
	} else {
		writer = &textWriter{}
	}

	writer.Init(fields, printer)
	go writer.Run()

	if !masterOptions.NoHeader && !masterOptions.Json {
		printHeaderLine(fields, serverFieldProperties)
	}

	for i := 0; i < numServers; i++ {
		//tups = append(tups, <-rec)
		recd := <-rec

		if recd.err != nil {
			errorsEncountererd = append(errorsEncountererd, recd.err)
		}

		if recd.err != nil && !masterOptions.ShowUnreachable {
			unreachable++
			continue
		}

		if masterOptions.Limit > 0 && i >= masterOptions.Limit {
			continue
		}

		printer <- recd
	}

	writer.Done()

	close(rec)
	close(printer)

	log.Println()

	if !masterOptions.ShowUnreachable && !masterOptions.Json {
		log.Println(unreachable, "unreachable servers were hidden.")
	}

	if masterOptions.ShowErrorSummary {
		log.Printf("Errors Encountered (%dx):\n", len(errorsEncountererd))

		for _, detail := range errorsEncountererd {
			log.Println("\t", detail)
		}
	}
}

func serialQueryServers(send chan SvResponse, servers []goseq.Server, timeout time.Duration) {
	for _, server := range servers {
		info, err := server.Info(timeout)
		if err != nil {
			send <- SvResponse{err: err, server: server}
		}
		send <- SvResponse{err: err, server: server, info: info}
	}
}

func AsyncQueryServers(send chan SvResponse, servers []goseq.Server, timeout time.Duration) {
	for _, server := range servers {
		go serialQueryServers(send, []goseq.Server{server}, timeout)
	}
}

func parseFields(spec string, properties map[string]FieldProperty) ([]FieldSpec, error) {
	specs := make([]FieldSpec, 0, 1)
	found := _fieldregexp.FindAllStringSubmatch(spec, -1)

	if len(found) == 0 && len(strings.TrimSpace(spec)) != 0 {
		return nil, errors.New("Couldn't parse field list.")
	}

	for _, match := range found {

		if _, ok := serverMethodAccessors[match[2]]; !ok {
			if _, ok = serverProperties[match[2]]; !ok {
				return nil, errors.New("Attempted to use an unregistered field!")
			}
		}

		sp := FieldSpec{
			name:   match[2],
			length: -1,
		}

		if def, ok := properties[match[2]]; ok {
			sp.length = def.size
		}

		if match[4] != "" {
			val, err := strconv.Atoi(match[4])
			if err == nil {
				sp.length = val
			}
		}

		specs = append(specs, sp)
	}

	return specs, nil
}

func printHeaderLine(fields []FieldSpec, props map[string]FieldProperty) {
	for i, field := range fields {

		if i > 0 {
			fmt.Print(masterOptions.Divider)
		}

		title := field.name

		if prop, ok := props[field.name]; ok {
			title = prop.name
		}

		sz := len(title)

		var padL int = 0
		var padR int = 0

		if sz < field.length {
			var rem int = field.length - sz
			padL = rem / 2
			padR = rem - padL
		}

		for j := 0; j < padL; j++ {
			fmt.Print(" ")
		}

		fmt.Print(title)

		for j := 0; j < padR; j++ {
			fmt.Print(" ")
		}
	}
	fmt.Print("\n")
}

type ErrorCount struct {
	err   error
	count int
}

func printInfo() (done bool) {
	done = false

	infos := []struct {
		f       infoPrinter
		enabled bool
	}{
		{printKnownFiltersInfo, masterOptions.ListFilters},
		{printRegionInfo, masterOptions.ListRegions},
		{printServerFieldProperties, masterOptions.ListFields},
	}

	for _, info := range infos {
		if info.enabled {
			info.f()
			done = true
		}
	}

	return
}
<commit_msg>Use host resolution if no master ip supplied<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/hfern/goseq"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type MasterQueryOptions struct {
	Region string `long:"region" short:"r" default:"USW" description:"Region code to get results for. See --list-regions"`
	Serial bool   `long:"serial" short:"s" default:"false" description:"Force serial querying of the servers."`
	Fields string `long:"fields" default:"ip=21,name" description:"The fields to be included. Optionally includes the min-length space. See --list-fields" `
	// TODO(hunter): Add this
	ShowFields bool `long:"show-fields" default:"false" description:"Print details on each available field."`
	// TODO(hunter): Add this
	MasterIP string `long:"ip" default:"hl2master.steampowered.com:27011" description:"host:port of the Master server to query."`
	Divider  string `long:"divider" default:" ¦ " description:"Characters used to seperate fields."`
	// TODO(hunter): Add this
	StartIP          string `long:"start" default:"" description:"Where to start reading IPs from. Defaults to start of list."`
	Limit            int    `long:"limit" short:"l" default:"0" description:"Limit the result set to n successful rows."`
	NoHeader         bool   `long:"no-header" default:"false" description:"Don't show header w/ column names."`
	ShowUnreachable  bool   `long:"unreachable" short:"U" default:"false" description:"Show unreachable servers (couldn't be connected to)."`
	ShowErrorSummary bool   `long:"errors" short:"E" default:"false" description:"Show error summary at end of list."`
	// TODO(hunter): Add this
	Filters map[string]string `long:"filter" short:"f" description:"Filters to use. See --list-filters"`
	// TODO(hunter): Add this
	ListFilters bool `long:"list-filters" default:"false" description:"List known filters." group:"Lists"`
	ListRegions bool `long:"list-regions" default:"false" description:"List Regions." group:"Lists"`
	ListFields  bool `long:"list-fields" default:"false" description:"List Server Fields." group:"Lists"`
	Json        bool `long:"json" default:"false" description:"Output as JSON to StdOut"`
}

var masterOptions MasterQueryOptions

var _fieldregexp = regexp.MustCompile(`\s*(([a-z]+)(=(\d+))?)\s*,?\s*`)

type infoPrinter func()

func masterctx() {
	log.SetFlags(0)

	if printInfo() {
		return
	}

	unreachable := 0
	errorsEncountererd := make([]error, 0)

	userRegionStr := strings.ToUpper(masterOptions.Region)

	region, found := regionData[userRegionStr]

	if !found {
		fmt.Errorf("Region '%s' does not exist.", masterOptions.Region)
		return
	}

	fields, err := parseFields(masterOptions.Fields, serverFieldProperties)

	if err != nil {
		log.Fatal(err)
	}

	master := goseq.NewMasterServer()
	master.SetRegion(region)

	master.SetAddr(masterOptions.MasterIP)

	startIp := string(goseq.NoAddress)

	if masterOptions.StartIP != "" {
		startIp = masterOptions.StartIP
	}

	// Apply filters
	filt := master.GetFilter()
	for fname, fval := range masterOptions.Filters {
		filt.Set(fname, fval)
	}
	master.SetFilter(filt)

	servers, err := master.Query(startIp)

	if err != nil {
		log.Fatal(err)
	}

	numServers := len(servers)

	rec := make(chan SvResponse)
	printer := make(chan SvResponse)

	//tups := make([]SvResponse, 0, numServers)

	if !masterOptions.Serial {
		go AsyncQueryServers(rec, servers, 1*time.Second)
	} else {
		go serialQueryServers(rec, servers, 1*time.Second)
	}

	var writer Printer

	if masterOptions.Json {
		writer = &jsonWriter{}
	} else {
		writer = &textWriter{}
	}

	writer.Init(fields, printer)
	go writer.Run()

	if !masterOptions.NoHeader && !masterOptions.Json {
		printHeaderLine(fields, serverFieldProperties)
	}

	for i := 0; i < numServers; i++ {
		//tups = append(tups, <-rec)
		recd := <-rec

		if recd.err != nil {
			errorsEncountererd = append(errorsEncountererd, recd.err)
		}

		if recd.err != nil && !masterOptions.ShowUnreachable {
			unreachable++
			continue
		}

		if masterOptions.Limit > 0 && i >= masterOptions.Limit {
			continue
		}

		printer <- recd
	}

	writer.Done()

	close(rec)
	close(printer)

	log.Println()

	if !masterOptions.ShowUnreachable && !masterOptions.Json {
		log.Println(unreachable, "unreachable servers were hidden.")
	}

	if masterOptions.ShowErrorSummary {
		log.Printf("Errors Encountered (%dx):\n", len(errorsEncountererd))

		for _, detail := range errorsEncountererd {
			log.Println("\t", detail)
		}
	}
}

func serialQueryServers(send chan SvResponse, servers []goseq.Server, timeout time.Duration) {
	for _, server := range servers {
		info, err := server.Info(timeout)
		if err != nil {
			send <- SvResponse{err: err, server: server}
		}
		send <- SvResponse{err: err, server: server, info: info}
	}
}

func AsyncQueryServers(send chan SvResponse, servers []goseq.Server, timeout time.Duration) {
	for _, server := range servers {
		go serialQueryServers(send, []goseq.Server{server}, timeout)
	}
}

func parseFields(spec string, properties map[string]FieldProperty) ([]FieldSpec, error) {
	specs := make([]FieldSpec, 0, 1)
	found := _fieldregexp.FindAllStringSubmatch(spec, -1)

	if len(found) == 0 && len(strings.TrimSpace(spec)) != 0 {
		return nil, errors.New("Couldn't parse field list.")
	}

	for _, match := range found {

		if _, ok := serverMethodAccessors[match[2]]; !ok {
			if _, ok = serverProperties[match[2]]; !ok {
				return nil, errors.New("Attempted to use an unregistered field!")
			}
		}

		sp := FieldSpec{
			name:   match[2],
			length: -1,
		}

		if def, ok := properties[match[2]]; ok {
			sp.length = def.size
		}

		if match[4] != "" {
			val, err := strconv.Atoi(match[4])
			if err == nil {
				sp.length = val
			}
		}

		specs = append(specs, sp)
	}

	return specs, nil
}

func printHeaderLine(fields []FieldSpec, props map[string]FieldProperty) {
	for i, field := range fields {

		if i > 0 {
			fmt.Print(masterOptions.Divider)
		}

		title := field.name

		if prop, ok := props[field.name]; ok {
			title = prop.name
		}

		sz := len(title)

		var padL int = 0
		var padR int = 0

		if sz < field.length {
			var rem int = field.length - sz
			padL = rem / 2
			padR = rem - padL
		}

		for j := 0; j < padL; j++ {
			fmt.Print(" ")
		}

		fmt.Print(title)

		for j := 0; j < padR; j++ {
			fmt.Print(" ")
		}
	}
	fmt.Print("\n")
}

type ErrorCount struct {
	err   error
	count int
}

func printInfo() (done bool) {
	done = false

	infos := []struct {
		f       infoPrinter
		enabled bool
	}{
		{printKnownFiltersInfo, masterOptions.ListFilters},
		{printRegionInfo, masterOptions.ListRegions},
		{printServerFieldProperties, masterOptions.ListFields},
	}

	for _, info := range infos {
		if info.enabled {
			info.f()
			done = true
		}
	}

	return
}
<|endoftext|>


<commit_before>package netlink

import (
	"fmt"
	"log"
	"net"
	"strings"
	"syscall"

	"github.com/vishvananda/netlink/nl"
)

// IFA_FLAGS is a u32 attribute.
const IFA_FLAGS = 0x8

// AddrAdd will add an IP address to a link device.
// Equivalent to: `ip addr add $addr dev $link`
func AddrAdd(link Link, addr *Addr) error {

	req := nl.NewNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
	return addrHandle(link, addr, req)
}

// AddrDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func AddrDel(link Link, addr *Addr) error {
	req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK)
	return addrHandle(link, addr, req)
}

func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
	base := link.Attrs()
	if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
		return fmt.Errorf("label must begin with interface name")
	}
	ensureIndex(base)

	family := nl.GetIPFamily(addr.IP)

	msg := nl.NewIfAddrmsg(family)
	msg.Index = uint32(base.Index)
	msg.Scope = uint8(addr.Scope)
	prefixlen, _ := addr.Mask.Size()
	msg.Prefixlen = uint8(prefixlen)
	req.AddData(msg)

	var addrData []byte
	if family == FAMILY_V4 {
		addrData = addr.IP.To4()
	} else {
		addrData = addr.IP.To16()
	}

	localData := nl.NewRtAttr(syscall.IFA_LOCAL, addrData)
	req.AddData(localData)

	addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData)
	req.AddData(addressData)

	if addr.Flags != 0 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(addr.Flags))
		flagsData := nl.NewRtAttr(IFA_FLAGS, b)
		req.AddData(flagsData)
	}

	if addr.Label != "" {
		labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
		req.AddData(labelData)
	}

	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
	return err
}

// AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family.
func AddrList(link Link, family int) ([]Addr, error) {
	req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP)
	msg := nl.NewIfInfomsg(family)
	req.AddData(msg)

	msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR)
	if err != nil {
		return nil, err
	}

	index := 0
	if link != nil {
		base := link.Attrs()
		ensureIndex(base)
		index = base.Index
	}

	var res []Addr
	for _, m := range msgs {
		msg := nl.DeserializeIfAddrmsg(m)

		if link != nil && msg.Index != uint32(index) {
			// Ignore messages from other interfaces
			continue
		}

		if family != FAMILY_ALL && msg.Family != uint8(family) {
			continue
		}

		attrs, err := nl.ParseRouteAttr(m[msg.Len():])
		if err != nil {
			return nil, err
		}

		var local, dst *net.IPNet
		var addr Addr
		for _, attr := range attrs {
			switch attr.Attr.Type {
			case syscall.IFA_ADDRESS:
				dst = &net.IPNet{
					IP:   attr.Value,
					Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
				}
			case syscall.IFA_LOCAL:
				local = &net.IPNet{
					IP:   attr.Value,
					Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
				}
			case syscall.IFA_LABEL:
				addr.Label = string(attr.Value[:len(attr.Value)-1])
			case IFA_FLAGS:
				addr.Flags = int(native.Uint32(attr.Value[0:4]))
			}
		}

		// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
		if local != nil {
			addr.IPNet = local
		} else {
			addr.IPNet = dst
		}
		addr.Scope = int(msg.Scope)

		res = append(res, addr)
	}

	return res, nil
}

type AddrUpdate struct {
	LinkAddress net.IPNet
	LinkIndex   int
	NewAddr     bool // true=added false=deleted
}

// AddrSubscribe takes a chan down which notifications will be sent
// when addresses change.  Close the 'done' chan to stop subscription.
func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR)
	if err != nil {
		return err
	}
	if done != nil {
		go func() {
			<-done
			s.Close()
		}()
	}
	go func() {
		defer close(ch)
		for {
			msgs, err := s.Receive()
			if err != nil {
				log.Printf("netlink.AddrSubscribe: Receive() error: %v", err)
				return
			}
			for _, m := range msgs {
				msgType := m.Header.Type
				if msgType != syscall.RTM_NEWADDR && msgType != syscall.RTM_DELADDR {
					log.Printf("netlink.AddrSubscribe: bad message type: %d", msgType)
					continue
				}

				msg := nl.DeserializeIfAddrmsg(m.Data)

				attrs, err := nl.ParseRouteAttr(m.Data[msg.Len():])
				if err != nil {
					log.Printf("netlink.AddrSubscribe: nl.ParseRouteAttr() error: %v", err)
					return
				}

				var local, dst *net.IPNet
				var addr Addr
				for _, attr := range attrs {
					switch attr.Attr.Type {
					case syscall.IFA_ADDRESS:
						dst = &net.IPNet{
							IP:   attr.Value,
							Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
						}
					case syscall.IFA_LOCAL:
						local = &net.IPNet{
							IP:   attr.Value,
							Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
						}
					case syscall.IFA_LABEL:
						addr.Label = string(attr.Value[:len(attr.Value)-1])
					case IFA_FLAGS:
						addr.Flags = int(native.Uint32(attr.Value[0:4]))
					}
				}

				// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
				if local != nil {
					addr.IPNet = local
				} else {
					addr.IPNet = dst
				}

				ch <- AddrUpdate{LinkAddress: *addr.IPNet, LinkIndex: int(msg.Index), NewAddr: msgType == syscall.RTM_NEWADDR}
			}
		}
	}()

	return nil
}
<commit_msg>Move common code from AddrList,AddrSubscribe into new helper func parseAddr.<commit_after>package netlink

import (
	"fmt"
	"log"
	"net"
	"strings"
	"syscall"

	"github.com/vishvananda/netlink/nl"
)

// IFA_FLAGS is a u32 attribute.
const IFA_FLAGS = 0x8

// AddrAdd will add an IP address to a link device.
// Equivalent to: `ip addr add $addr dev $link`
func AddrAdd(link Link, addr *Addr) error {

	req := nl.NewNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
	return addrHandle(link, addr, req)
}

// AddrDel will delete an IP address from a link device.
// Equivalent to: `ip addr del $addr dev $link`
func AddrDel(link Link, addr *Addr) error {
	req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK)
	return addrHandle(link, addr, req)
}

func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
	base := link.Attrs()
	if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
		return fmt.Errorf("label must begin with interface name")
	}
	ensureIndex(base)

	family := nl.GetIPFamily(addr.IP)

	msg := nl.NewIfAddrmsg(family)
	msg.Index = uint32(base.Index)
	msg.Scope = uint8(addr.Scope)
	prefixlen, _ := addr.Mask.Size()
	msg.Prefixlen = uint8(prefixlen)
	req.AddData(msg)

	var addrData []byte
	if family == FAMILY_V4 {
		addrData = addr.IP.To4()
	} else {
		addrData = addr.IP.To16()
	}

	localData := nl.NewRtAttr(syscall.IFA_LOCAL, addrData)
	req.AddData(localData)

	addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData)
	req.AddData(addressData)

	if addr.Flags != 0 {
		b := make([]byte, 4)
		native.PutUint32(b, uint32(addr.Flags))
		flagsData := nl.NewRtAttr(IFA_FLAGS, b)
		req.AddData(flagsData)
	}

	if addr.Label != "" {
		labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
		req.AddData(labelData)
	}

	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
	return err
}

// AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family.
func AddrList(link Link, family int) ([]Addr, error) {
	req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP)
	msg := nl.NewIfInfomsg(family)
	req.AddData(msg)

	msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR)
	if err != nil {
		return nil, err
	}

	indexFilter := 0
	if link != nil {
		base := link.Attrs()
		ensureIndex(base)
		indexFilter = base.Index
	}

	var res []Addr
	for _, m := range msgs {
		addr, family, ifindex, err := parseAddr(m)
		if err != nil {
			return res, err
		}

		if link != nil && ifindex != indexFilter {
			// Ignore messages from other interfaces
			continue
		}

		if family != FAMILY_ALL && msg.Family != uint8(family) {
			continue
		}

		res = append(res, addr)
	}

	return res, nil
}

func parseAddr(m []byte) (addr Addr, family, index int, err error) {
	msg := nl.DeserializeIfAddrmsg(m)

	family = -1
	index = -1

	attrs, err := nl.ParseRouteAttr(m[msg.Len():])
	if err != nil {
		return
	}

	index = int(msg.Index)

	var local, dst *net.IPNet
	for _, attr := range attrs {
		switch attr.Attr.Type {
		case syscall.IFA_ADDRESS:
			dst = &net.IPNet{
				IP:   attr.Value,
				Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
			}
		case syscall.IFA_LOCAL:
			local = &net.IPNet{
				IP:   attr.Value,
				Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
			}
		case syscall.IFA_LABEL:
			addr.Label = string(attr.Value[:len(attr.Value)-1])
		case IFA_FLAGS:
			addr.Flags = int(native.Uint32(attr.Value[0:4]))
		}
	}

	// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
	if local != nil {
		addr.IPNet = local
	} else {
		addr.IPNet = dst
	}
	addr.Scope = int(msg.Scope)

	return
}

type AddrUpdate struct {
	LinkAddress net.IPNet
	LinkIndex   int
	NewAddr     bool // true=added false=deleted
}

// AddrSubscribe takes a chan down which notifications will be sent
// when addresses change.  Close the 'done' chan to stop subscription.
func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR)
	if err != nil {
		return err
	}
	if done != nil {
		go func() {
			<-done
			s.Close()
		}()
	}
	go func() {
		defer close(ch)
		for {
			msgs, err := s.Receive()
			if err != nil {
				log.Printf("netlink.AddrSubscribe: Receive() error: %v", err)
				return
			}
			for _, m := range msgs {
				msgType := m.Header.Type
				if msgType != syscall.RTM_NEWADDR && msgType != syscall.RTM_DELADDR {
					log.Printf("netlink.AddrSubscribe: bad message type: %d", msgType)
					continue
				}

				addr, _, ifindex, err := parseAddr(m.Data)
				if err != nil {
					log.Printf("netlink.AddrSubscribe: could not parse address: %v", err)
					continue
				}

				ch <- AddrUpdate{LinkAddress: *addr.IPNet, LinkIndex: ifindex, NewAddr: msgType == syscall.RTM_NEWADDR}
			}
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors.  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 (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"time"

	// Imports so that go build/install automatically installs them.
	_ "code.google.com/p/go-tour/pic"
	_ "code.google.com/p/go-tour/tree"
	_ "code.google.com/p/go-tour/wc"
)

const basePkg = "code.google.com/p/go-tour/"

var (
	httpListen  = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput  = flag.Bool("html", false, "render program output as HTML")
	openBrowser = flag.Bool("openbrowser", true, "open browser automatically")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
)

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// find and serve the go tour files
	p, err := build.Default.Import(basePkg, "", build.FindOnly)
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}
	root := p.Dir
	log.Println("Serving content from", root)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/favicon.ico" {
			fn := filepath.Join(root, "static", r.URL.Path[1:])
			http.ServeFile(w, r, fn)
			return
		} else if r.URL.Path == "/" {
			err := renderTour(w, root)
			if err != nil {
				log.Println(err)
			}
			return
		}
		http.Error(w, "not found", 404)
	})
	http.Handle("/static/", http.FileServer(http.Dir(root)))
	http.Handle("/talks/", http.FileServer(http.Dir(root)))
	http.HandleFunc("/kill", kill)

	host, port, err := net.SplitHostPort(*httpListen)
	if err != nil {
		log.Fatal(err)
	}
	if host == "" {
		host = "localhost"
	}
	if host != "127.0.0.1" && host != "localhost" {
		log.Print(localhostWarning)
	}

	httpAddr := host + ":" + port
	go func() {
		url := "http://" + httpAddr
		if waitServer(url) && *openBrowser && startBrowser(url) {
			log.Printf("A browser window should open. If not, please visit %s", url)
		} else {
			log.Printf("Please open your web browser and visit %s", url)
		}
	}()
	log.Fatal(http.ListenAndServe(httpAddr, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

var running struct {
	sync.Mutex
	cmd *exec.Cmd
}

func stopRun() {
	running.Lock()
	if running.cmd != nil {
		running.cmd.Process.Kill()
		running.cmd = nil
	}
	running.Unlock()
}

func kill(w http.ResponseWriter, r *http.Request) {
	stopRun()
}

var (
	commentRe = regexp.MustCompile(`(?m)^#.*\n`)
	tmpdir    string
)

func init() {
	// find real temporary directory (for rewriting filename in output)
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

func compile(req *http.Request) (out []byte, err error) {
	stopRun()

	// x is the base name for .go, .6, executable files
	x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := x + ".go"
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// rewrite filename in error output
	defer func() {
		if err != nil {
			// drop messages from the go tool like '# _/compile0'
			out = commentRe.ReplaceAll(out, nil)
		}
		out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
	}()

	// write body to x.go
	body := []byte(req.FormValue("body"))
	defer os.Remove(src)
	if err = ioutil.WriteFile(src, body, 0666); err != nil {
		return
	}

	// build x.go, creating x
	dir, file := filepath.Split(src)
	out, err = run(dir, "go", "build", "-o", bin, file)
	defer os.Remove(bin)
	if err != nil {
		return
	}

	// run x
	return run("", bin)
}

// run executes the specified command and returns its output and an error.
func run(dir string, args ...string) ([]byte, error) {
	var buf bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Stdout = &buf
	cmd.Stderr = cmd.Stdout

	// Start command and leave in 'running'.
	running.Lock()
	if running.cmd != nil {
		defer running.Unlock()
		return nil, fmt.Errorf("already running %s", running.cmd.Path)
	}
	if err := cmd.Start(); err != nil {
		running.Unlock()
		return nil, err
	}
	running.cmd = cmd
	running.Unlock()

	// Wait for the command.  Clean up,
	err := cmd.Wait()
	running.Lock()
	if running.cmd == cmd {
		running.cmd = nil
	}
	running.Unlock()
	return buf.Bytes(), err
}

// waitServer waits some time for the http Server to start
// serving url and returns whether it starts
func waitServer(url string) bool {
	tries := 20
	for tries > 0 {
		resp, err := http.Get(url)
		if err == nil {
			resp.Body.Close()
			return true
		}
		time.Sleep(100 * time.Millisecond)
		tries--
	}
	return false
}

// startBrowser tries to open the URL in a browser, and returns
// whether it succeed.
func startBrowser(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}
<commit_msg>[x/tour] go-tour/gotour: find html root through running directory if package lookup fails.<commit_after>// Copyright 2011 The Go Authors.  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 (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	// Imports so that go build/install automatically installs them.
	_ "code.google.com/p/go-tour/pic"
	_ "code.google.com/p/go-tour/tree"
	_ "code.google.com/p/go-tour/wc"
)

const basePkg = "code.google.com/p/go-tour/"

var (
	httpListen  = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput  = flag.Bool("html", false, "render program output as HTML")
	openBrowser = flag.Bool("openbrowser", true, "open browser automatically")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)

	// GOPATH containing the tour packages
	gopath = os.Getenv("GOPATH")
)

func isRoot(path string) bool {
	_, err := os.Stat(filepath.Join(path, "tour.article"))
	return err == nil
}

func findRoot() (string, error) {
	ctx := build.Default
	p, err := ctx.Import(basePkg, "", build.FindOnly)
	if err == nil && isRoot(p.Dir) {
		return p.Dir, nil
	}
	tourRoot := filepath.Join(runtime.GOROOT(), "misc", "tour")
	ctx.GOPATH = tourRoot
	p, err = ctx.Import(basePkg, "", build.FindOnly)
	if err == nil && isRoot(tourRoot) {
		gopath = tourRoot
		return tourRoot, nil
	}
	return "", fmt.Errorf("could not find go-tour content; check $GOROOT and $GOPATH")
}

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// find and serve the go tour files
	root, err := findRoot()
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}

	log.Println("Serving content from", root)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/favicon.ico" {
			fn := filepath.Join(root, "static", r.URL.Path[1:])
			http.ServeFile(w, r, fn)
			return
		} else if r.URL.Path == "/" {
			err := renderTour(w, root)
			if err != nil {
				log.Println(err)
			}
			return
		}
		http.Error(w, "not found", 404)
	})
	http.Handle("/static/", http.FileServer(http.Dir(root)))
	http.Handle("/talks/", http.FileServer(http.Dir(root)))
	http.HandleFunc("/kill", kill)

	host, port, err := net.SplitHostPort(*httpListen)
	if err != nil {
		log.Fatal(err)
	}
	if host == "" {
		host = "localhost"
	}
	if host != "127.0.0.1" && host != "localhost" {
		log.Print(localhostWarning)
	}

	httpAddr := host + ":" + port
	go func() {
		url := "http://" + httpAddr
		if waitServer(url) && *openBrowser && startBrowser(url) {
			log.Printf("A browser window should open. If not, please visit %s", url)
		} else {
			log.Printf("Please open your web browser and visit %s", url)
		}
	}()
	log.Fatal(http.ListenAndServe(httpAddr, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

var running struct {
	sync.Mutex
	cmd *exec.Cmd
}

func stopRun() {
	running.Lock()
	if running.cmd != nil {
		running.cmd.Process.Kill()
		running.cmd = nil
	}
	running.Unlock()
}

func kill(w http.ResponseWriter, r *http.Request) {
	stopRun()
}

var (
	commentRe = regexp.MustCompile(`(?m)^#.*\n`)
	tmpdir    string
)

func init() {
	// find real temporary directory (for rewriting filename in output)
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

func compile(req *http.Request) (out []byte, err error) {
	stopRun()

	// x is the base name for .go, .6, executable files
	x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := x + ".go"
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// rewrite filename in error output
	defer func() {
		if err != nil {
			// drop messages from the go tool like '# _/compile0'
			out = commentRe.ReplaceAll(out, nil)
		}
		out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
	}()

	// write body to x.go
	body := []byte(req.FormValue("body"))
	defer os.Remove(src)
	if err = ioutil.WriteFile(src, body, 0666); err != nil {
		return
	}

	// build x.go, creating x
	dir, file := filepath.Split(src)
	out, err = run(dir, "go", "build", "-o", bin, file)
	defer os.Remove(bin)
	if err != nil {
		return
	}

	// run x
	return run("", bin)
}

// run executes the specified command and returns its output and an error.
func run(dir string, args ...string) ([]byte, error) {
	var buf bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Stdout = &buf
	cmd.Stderr = cmd.Stdout
	cmd.Env = environ()

	// Start command and leave in 'running'.
	running.Lock()
	if running.cmd != nil {
		defer running.Unlock()
		return nil, fmt.Errorf("already running %s", running.cmd.Path)
	}
	if err := cmd.Start(); err != nil {
		running.Unlock()
		return nil, err
	}
	running.cmd = cmd
	running.Unlock()

	// Wait for the command.  Clean up,
	err := cmd.Wait()
	running.Lock()
	if running.cmd == cmd {
		running.cmd = nil
	}
	running.Unlock()
	return buf.Bytes(), err
}

// environ returns an execution environment containing only GO* variables
// and replacing GOPATH with the value of the global var gopath.
func environ() (env []string) {
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GO") {
			continue
		}
		if strings.HasPrefix(v, "GOPATH=") {
			v = "GOPATH=" + gopath
		}
		env = append(env, v)
	}
	return
}

// waitServer waits some time for the http Server to start
// serving url and returns whether it starts
func waitServer(url string) bool {
	tries := 20
	for tries > 0 {
		resp, err := http.Get(url)
		if err == nil {
			resp.Body.Close()
			return true
		}
		time.Sleep(100 * time.Millisecond)
		tries--
	}
	return false
}

// startBrowser tries to open the URL in a browser, and returns
// whether it succeed.
func startBrowser(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}
<|endoftext|>