<commit_before>// 28 july 2014

package ui

import (
	"fmt"
	"unsafe"
	"reflect"
)

// #include "winapi_windows.h"
import "C"

type table struct {
	*tablebase
	_hwnd		C.HWND
	noautosize	bool
	colcount		C.int
	hotrow		C.int
	hotcol		C.int
	pushedrow	C.int
	pushedcol	C.int
}

func finishNewTable(b *tablebase, ty reflect.Type) Table {
	t := &table{
		_hwnd:		C.newControl(C.xWC_LISTVIEW,
			C.LVS_REPORT | C.LVS_OWNERDATA | C.LVS_NOSORTHEADER | C.LVS_SHOWSELALWAYS | C.WS_HSCROLL | C.WS_VSCROLL | C.WS_TABSTOP,
			C.WS_EX_CLIENTEDGE),		// WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
		tablebase:		b,
		hotrow:		-1,
		hotcol:		-1,
		pushedrow:	-1,
		pushedcol:	-1,
	}
	C.setTableSubclass(t._hwnd, unsafe.Pointer(t))
	// LVS_EX_FULLROWSELECT gives us selection across the whole row, not just the leftmost column; this makes the list view work like on other platforms
	// LVS_EX_SUBITEMIMAGES gives us images in subitems, which will be important when both images and checkboxes are added
	C.tableAddExtendedStyles(t._hwnd, C.LVS_EX_FULLROWSELECT | C.LVS_EX_SUBITEMIMAGES)
	C.tableSetCheckboxImageList(t._hwnd)
	for i := 0; i < ty.NumField(); i++ {
		C.tableAppendColumn(t._hwnd, C.int(i), toUTF16(ty.Field(i).Name))
	}
	t.colcount = C.int(ty.NumField())
	return t
}

func (t *table) Unlock() {
	t.unlock()
	// there's a possibility that user actions can happen at this point, before the view is updated
	// alas, this is something we have to deal with, because Unlock() can be called from any thread
	go func() {
		Do(func() {
			t.RLock()
			defer t.RUnlock()
			C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
		})
	}()
}

func (t *table) LoadImageList(il ImageList) {
	il.apply(t._hwnd, C.LVM_SETIMAGELIST, C.LVSIL_SMALL)
}

func (t *table) Selected() int {
	t.RLock()
	defer t.RUnlock()
	return int(C.tableSelectedItem(t._hwnd))
}

func (t *table) Select(index int) {
	t.RLock()
	defer t.RUnlock()
	C.tableSelectItem(t._hwnd, C.intptr_t(index))
}

//export tableGetCell
func tableGetCell(data unsafe.Pointer, item *C.LVITEMW) {
	t := (*table)(data)
	t.RLock()
	defer t.RUnlock()
	d := reflect.Indirect(reflect.ValueOf(t.data))
	datum := d.Index(int(item.iItem)).Field(int(item.iSubItem))
	// TODO figure out why changing item.mask causes crashes or why "it just works"
	switch {
	case datum.Type() == reflect.TypeOf(ImageIndex(0)):
		item.iImage = C.int(datum.Interface().(ImageIndex))
	case datum.Kind() == reflect.Bool:
		item.stateMask = C.LVIS_STATEIMAGEMASK
		// state image index is 1-based
		curstate := ((item.state & C.LVIS_STATEIMAGEMASK) >> 12)
		if curstate > 0 {
			curstate--
		}
		if datum.Bool() == true {
			curstate |= C.checkboxStateChecked
		} else {
			curstate &^= C.checkboxStateChecked
		}
		if item.iItem == t.hotrow && item.iSubItem == t.hotcol {
			curstate |= C.checkboxStateHot
		} else {
			curstate &^= C.checkboxStateHot
		}
		if item.iItem == t.pushedrow && item.iSubItem == t.pushedcol {
			curstate |= C.checkboxStatePushed
		} else {
			curstate &^= C.checkboxStatePushed
		}
		item.state = (curstate + 1) << 12
	default:
		s := fmt.Sprintf("%v", datum)
		item.pszText = toUTF16(s)
	}
}

// the column autoresize policy is simple:
// on every table.commitResize() call, if the columns have not been resized by the user, autoresize
func (t *table) autoresize() {
	t.RLock()
	defer t.RUnlock()
	if !t.noautosize {
		C.tableAutosizeColumns(t._hwnd, t.colcount)
	}
}

//export tableStopColumnAutosize
func tableStopColumnAutosize(data unsafe.Pointer) {
	t := (*table)(data)
	t.noautosize = true
}

//export tableColumnCount
func tableColumnCount(data unsafe.Pointer) C.int {
	t := (*table)(data)
	return t.colcount
}

//export tableSetHot
func tableSetHot(data unsafe.Pointer, row C.int, col C.int) {
	t := (*table)(data)
	redraw := (row != t.hotrow || col != t.hotcol)
	t.hotrow = row
	t.hotcol = col
	if redraw {
		C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
	}
}

//export tablePushed
func tablePushed(data unsafe.Pointer, row C.int, col C.int) {
	t := (*table)(data)
	t.pushedrow = row
	t.pushedcol = col
	C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
}

//export tableToggled
func tableToggled(data unsafe.Pointer, row C.int, col C.int) {
	t := (*table)(data)
	t.Lock()
	defer func() {
		// reset for next time
		t.pushedrow = -1
		t.pushedcol = -1
		// and THEN unlock so the reset takes effect
		t.Unlock()
	}()
	if row == -1 || col == -1 {		// discard extras sent by handle() in table_windows.c
		return
	}
	if row != t.pushedrow || col != t.pushedcol {				// mouse moved out
		return
	}
	d := reflect.Indirect(reflect.ValueOf(t.data))
	datum := d.Index(int(row)).Field(int(col))
	if datum.Kind() == reflect.Bool {
		datum.SetBool(!datum.Bool())
		return
	}
	panic(fmt.Errorf("tableSetHot() on non-checkbox at (%d, %d)", row, col))
}

func (t *table) hwnd() C.HWND {
	return t._hwnd
}

func (t *table) setParent(p *controlParent) {
	basesetParent(t, p)
}

func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
	return baseallocate(t, x, y, width, height, d)
}

const (
	// from C++ Template 05 in http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx as this is the best I can do for now
	// there IS a message LVM_APPROXIMATEVIEWRECT that can do calculations, but it doesn't seem to work right when asked to base its calculations on the current width/height on Windows and wine...
	tableWidth = 183
	tableHeight = 50
)

func (t *table) preferredSize(d *sizing) (width, height int) {
	return fromdlgunitsX(tableWidth, d), fromdlgunitsY(tableHeight, d)
}

func (t *table) commitResize(a *allocation, d *sizing) {
	basecommitResize(t, a, d)
	t.RLock()
	defer t.RUnlock()
	t.autoresize()
}

func (t *table) getAuxResizeInfo(d *sizing) {
	basegetAuxResizeInfo(t, d)
}
<commit_msg>Made Tables on Windows single-selection.<commit_after>// 28 july 2014

package ui

import (
	"fmt"
	"unsafe"
	"reflect"
)

// #include "winapi_windows.h"
import "C"

type table struct {
	*tablebase
	_hwnd		C.HWND
	noautosize	bool
	colcount		C.int
	hotrow		C.int
	hotcol		C.int
	pushedrow	C.int
	pushedcol	C.int
}

func finishNewTable(b *tablebase, ty reflect.Type) Table {
	t := &table{
		_hwnd:		C.newControl(C.xWC_LISTVIEW,
			C.LVS_REPORT | C.LVS_OWNERDATA | C.LVS_NOSORTHEADER | C.LVS_SHOWSELALWAYS | C.LVS_SINGLESEL | C.WS_HSCROLL | C.WS_VSCROLL | C.WS_TABSTOP,
			C.WS_EX_CLIENTEDGE),		// WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
		tablebase:		b,
		hotrow:		-1,
		hotcol:		-1,
		pushedrow:	-1,
		pushedcol:	-1,
	}
	C.setTableSubclass(t._hwnd, unsafe.Pointer(t))
	// LVS_EX_FULLROWSELECT gives us selection across the whole row, not just the leftmost column; this makes the list view work like on other platforms
	// LVS_EX_SUBITEMIMAGES gives us images in subitems, which will be important when both images and checkboxes are added
	C.tableAddExtendedStyles(t._hwnd, C.LVS_EX_FULLROWSELECT | C.LVS_EX_SUBITEMIMAGES)
	C.tableSetCheckboxImageList(t._hwnd)
	for i := 0; i < ty.NumField(); i++ {
		C.tableAppendColumn(t._hwnd, C.int(i), toUTF16(ty.Field(i).Name))
	}
	t.colcount = C.int(ty.NumField())
	return t
}

func (t *table) Unlock() {
	t.unlock()
	// there's a possibility that user actions can happen at this point, before the view is updated
	// alas, this is something we have to deal with, because Unlock() can be called from any thread
	go func() {
		Do(func() {
			t.RLock()
			defer t.RUnlock()
			C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
		})
	}()
}

func (t *table) LoadImageList(il ImageList) {
	il.apply(t._hwnd, C.LVM_SETIMAGELIST, C.LVSIL_SMALL)
}

func (t *table) Selected() int {
	t.RLock()
	defer t.RUnlock()
	return int(C.tableSelectedItem(t._hwnd))
}

func (t *table) Select(index int) {
	t.RLock()
	defer t.RUnlock()
	C.tableSelectItem(t._hwnd, C.intptr_t(index))
}

//export tableGetCell
func tableGetCell(data unsafe.Pointer, item *C.LVITEMW) {
	t := (*table)(data)
	t.RLock()
	defer t.RUnlock()
	d := reflect.Indirect(reflect.ValueOf(t.data))
	datum := d.Index(int(item.iItem)).Field(int(item.iSubItem))
	// TODO figure out why changing item.mask causes crashes or why "it just works"
	switch {
	case datum.Type() == reflect.TypeOf(ImageIndex(0)):
		item.iImage = C.int(datum.Interface().(ImageIndex))
	case datum.Kind() == reflect.Bool:
		item.stateMask = C.LVIS_STATEIMAGEMASK
		// state image index is 1-based
		curstate := ((item.state & C.LVIS_STATEIMAGEMASK) >> 12)
		if curstate > 0 {
			curstate--
		}
		if datum.Bool() == true {
			curstate |= C.checkboxStateChecked
		} else {
			curstate &^= C.checkboxStateChecked
		}
		if item.iItem == t.hotrow && item.iSubItem == t.hotcol {
			curstate |= C.checkboxStateHot
		} else {
			curstate &^= C.checkboxStateHot
		}
		if item.iItem == t.pushedrow && item.iSubItem == t.pushedcol {
			curstate |= C.checkboxStatePushed
		} else {
			curstate &^= C.checkboxStatePushed
		}
		item.state = (curstate + 1) << 12
	default:
		s := fmt.Sprintf("%v", datum)
		item.pszText = toUTF16(s)
	}
}

// the column autoresize policy is simple:
// on every table.commitResize() call, if the columns have not been resized by the user, autoresize
func (t *table) autoresize() {
	t.RLock()
	defer t.RUnlock()
	if !t.noautosize {
		C.tableAutosizeColumns(t._hwnd, t.colcount)
	}
}

//export tableStopColumnAutosize
func tableStopColumnAutosize(data unsafe.Pointer) {
	t := (*table)(data)
	t.noautosize = true
}

//export tableColumnCount
func tableColumnCount(data unsafe.Pointer) C.int {
	t := (*table)(data)
	return t.colcount
}

//export tableSetHot
func tableSetHot(data unsafe.Pointer, row C.int, col C.int) {
	t := (*table)(data)
	redraw := (row != t.hotrow || col != t.hotcol)
	t.hotrow = row
	t.hotcol = col
	if redraw {
		C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
	}
}

//export tablePushed
func tablePushed(data unsafe.Pointer, row C.int, col C.int) {
	t := (*table)(data)
	t.pushedrow = row
	t.pushedcol = col
	C.tableUpdate(t._hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
}

//export tableToggled
func tableToggled(data unsafe.Pointer, row C.int, col C.int) {
	t := (*table)(data)
	t.Lock()
	defer func() {
		// reset for next time
		t.pushedrow = -1
		t.pushedcol = -1
		// and THEN unlock so the reset takes effect
		t.Unlock()
	}()
	if row == -1 || col == -1 {		// discard extras sent by handle() in table_windows.c
		return
	}
	if row != t.pushedrow || col != t.pushedcol {				// mouse moved out
		return
	}
	d := reflect.Indirect(reflect.ValueOf(t.data))
	datum := d.Index(int(row)).Field(int(col))
	if datum.Kind() == reflect.Bool {
		datum.SetBool(!datum.Bool())
		return
	}
	panic(fmt.Errorf("tableSetHot() on non-checkbox at (%d, %d)", row, col))
}

func (t *table) hwnd() C.HWND {
	return t._hwnd
}

func (t *table) setParent(p *controlParent) {
	basesetParent(t, p)
}

func (t *table) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
	return baseallocate(t, x, y, width, height, d)
}

const (
	// from C++ Template 05 in http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx as this is the best I can do for now
	// there IS a message LVM_APPROXIMATEVIEWRECT that can do calculations, but it doesn't seem to work right when asked to base its calculations on the current width/height on Windows and wine...
	tableWidth = 183
	tableHeight = 50
)

func (t *table) preferredSize(d *sizing) (width, height int) {
	return fromdlgunitsX(tableWidth, d), fromdlgunitsY(tableHeight, d)
}

func (t *table) commitResize(a *allocation, d *sizing) {
	basecommitResize(t, a, d)
	t.RLock()
	defer t.RUnlock()
	t.autoresize()
}

func (t *table) getAuxResizeInfo(d *sizing) {
	basegetAuxResizeInfo(t, d)
}
<|endoftext|>


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

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the list set-like primitive functions.

package golisp

import (
	"fmt"
)

func RegisterListSetPrimitives() {
	MakePrimitiveFunction("union", -1, UnionImpl)
	MakePrimitiveFunction("intersection", -1, IntersectionImpl)
}

func memp(i *Data, l *Data) bool {
	for c := l; NotNilP(c); c = Cdr(c) {
		if IsEqual(i, Car(c)) {
			return true
		}
	}
	return false
}

func UnionImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var col *Data
	for a := args; NotNilP(a); a = Cdr(a) {
		col, err = Eval(Car(a), env)
		if err != nil {
			return
		}
		if !ListP(col) {
			err = ProcessError(fmt.Sprintf("union needs lists as its arguments, but got %s.", String(col)), env)
			return
		}
		for cell := col; NotNilP(cell); cell = Cdr(cell) {
			if !memp(Car(cell), result) {
				result = Append(result, Car(cell))
			}
		}
	}
	return
}

func IntersectionImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var col *Data
	for a := args; NotNilP(a); a = Cdr(a) {
		col, err = Eval(Car(a), env)
		if err != nil {
			return
		}
		if !ListP(col) {
			err = ProcessError(fmt.Sprintf("union needs lists as its arguments, but got %s.", String(col)), env)
			return
		}
		if NilP(result) {
			result = col
		} else {
			for cell := result; NotNilP(cell); cell = Cdr(cell) {
				if !memp(Car(cell), col) {
					result = RemoveFromListBang(result, Car(cell))
				}
			}
		}
	}
	return
}
<commit_msg>Fixed intersection.<commit_after>// Copyright 2015 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the list set-like primitive functions.

package golisp

import (
	"fmt"
)

func RegisterListSetPrimitives() {
	MakePrimitiveFunction("union", -1, UnionImpl)
	MakePrimitiveFunction("intersection", -1, IntersectionImpl)
}

func memp(i *Data, l *Data) bool {
	for c := l; NotNilP(c); c = Cdr(c) {
		if IsEqual(i, Car(c)) {
			return true
		}
	}
	return false
}

func UnionImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var col *Data
	for a := args; NotNilP(a); a = Cdr(a) {
		col, err = Eval(Car(a), env)
		if err != nil {
			return
		}
		if !ListP(col) {
			err = ProcessError(fmt.Sprintf("union needs lists as its arguments, but got %s.", String(col)), env)
			return
		}
		for cell := col; NotNilP(cell); cell = Cdr(cell) {
			if !memp(Car(cell), result) {
				result = Append(result, Car(cell))
			}
		}
	}
	return
}

func IntersectionImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var col *Data
	result = Car(args)
	for a := Cdr(args); NotNilP(a); a = Cdr(a) {
		col, err = Eval(Car(a), env)
		if err != nil {
			return
		}
		if !ListP(col) {
			err = ProcessError(fmt.Sprintf("union needs lists as its arguments, but got %s.", String(col)), env)
			return
		}
		for cell := result; NotNilP(cell); cell = Cdr(cell) {
			if !memp(Car(cell), col) {
				result = RemoveFromListBang(result, Car(cell))
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package mock

import (
	"github.com/micro/go-micro/registry"
)

type MockRegistry struct {
	Services map[string][]*registry.Service
}

var (
	mockData = map[string][]*registry.Service{
		"foo": []*registry.Service{
			{
				Name:    "foo",
				Version: "1.0.0",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.0-123",
						Address: "localhost",
						Port:    9999,
					},
					{
						Id:      "foo-1.0.0-321",
						Address: "localhost",
						Port:    9999,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.1",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.1-321",
						Address: "localhost",
						Port:    6666,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.3",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.3-345",
						Address: "localhost",
						Port:    8888,
					},
				},
			},
		},
	}
)

func (m *MockRegistry) init() {
	// add some mock data
	m.Services = mockData
}

func (m *MockRegistry) GetService(service string) ([]*registry.Service, error) {
	s, ok := m.Services[service]
	if !ok {
		return nil, registry.ErrNotFound
	}
	return s, nil

}

func (m *MockRegistry) ListServices() ([]*registry.Service, error) {
	var services []*registry.Service
	for _, service := range m.Services {
		services = append(services, service...)
	}
	return services, nil
}

func (m *MockRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
	services := addServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *MockRegistry) Deregister(s *registry.Service) error {
	services := delServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *MockRegistry) Watch() (registry.Watcher, error) {
	return nil, nil
}

func (m *MockRegistry) String() string {
	return "mock"
}

func NewRegistry() *MockRegistry {
	m := &MockRegistry{Services: make(map[string][]*registry.Service)}
	m.init()
	return m
}
<commit_msg>unexport mock registry<commit_after>package mock

import (
	"github.com/micro/go-micro/registry"
)

type mockRegistry struct {
	Services map[string][]*registry.Service
}

var (
	mockData = map[string][]*registry.Service{
		"foo": []*registry.Service{
			{
				Name:    "foo",
				Version: "1.0.0",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.0-123",
						Address: "localhost",
						Port:    9999,
					},
					{
						Id:      "foo-1.0.0-321",
						Address: "localhost",
						Port:    9999,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.1",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.1-321",
						Address: "localhost",
						Port:    6666,
					},
				},
			},
			{
				Name:    "foo",
				Version: "1.0.3",
				Nodes: []*registry.Node{
					{
						Id:      "foo-1.0.3-345",
						Address: "localhost",
						Port:    8888,
					},
				},
			},
		},
	}
)

func (m *mockRegistry) init() {
	// add some mock data
	m.Services = mockData
}

func (m *mockRegistry) GetService(service string) ([]*registry.Service, error) {
	s, ok := m.Services[service]
	if !ok {
		return nil, registry.ErrNotFound
	}
	return s, nil

}

func (m *mockRegistry) ListServices() ([]*registry.Service, error) {
	var services []*registry.Service
	for _, service := range m.Services {
		services = append(services, service...)
	}
	return services, nil
}

func (m *mockRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
	services := addServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *mockRegistry) Deregister(s *registry.Service) error {
	services := delServices(m.Services[s.Name], []*registry.Service{s})
	m.Services[s.Name] = services
	return nil
}

func (m *mockRegistry) Watch() (registry.Watcher, error) {
	return nil, nil
}

func (m *mockRegistry) String() string {
	return "mock"
}

func NewRegistry() registry.Registry {
	m := &mockRegistry{Services: make(map[string][]*registry.Service)}
	m.init()
	return m
}
<|endoftext|>


<commit_before>package progress

func Noop() Meter {
	return &nonMeter{}
}

type nonMeter struct{}

func (m *nonMeter) Start()                                                               {}
func (m *nonMeter) Pause()                                                               {}
func (m *nonMeter) Add(size int64)                                                       {}
func (m *nonMeter) Skip(size int64)                                                      {}
func (m *nonMeter) StartTransfer(name string)                                            {}
func (m *nonMeter) TransferBytes(direction, name string, read, total int64, current int) {}
func (m *nonMeter) FinishTransfer(name string)                                           {}
func (m *nonMeter) Finish()                                                              {}
<commit_msg>progress: make Noop() meter implement log.Task<commit_after>package progress

import "github.com/git-lfs/git-lfs/git/githistory/log"

func Noop() Meter {
	return &nonMeter{
		updates: make(chan *log.Update),
	}
}

type nonMeter struct {
	updates chan *log.Update
}

func (m *nonMeter) Start()                                                               {}
func (m *nonMeter) Pause()                                                               {}
func (m *nonMeter) Add(size int64)                                                       {}
func (m *nonMeter) Skip(size int64)                                                      {}
func (m *nonMeter) StartTransfer(name string)                                            {}
func (m *nonMeter) TransferBytes(direction, name string, read, total int64, current int) {}
func (m *nonMeter) FinishTransfer(name string)                                           {}
func (m *nonMeter) Finish() {
	close(m.updates)
}

func (m *nonMeter) Updates() <-chan *log.Update { return m.updates }
func (m *nonMeter) Throttled() bool             { return false }
<|endoftext|>


<commit_before>package mpb_test

import (
	"bytes"
	"context"
	"errors"
	"io"
	"math/rand"
	"strings"
	"testing"
	"time"

	"github.com/vbauerster/mpb/v8"
	"github.com/vbauerster/mpb/v8/decor"
)

const (
	timeout = 200 * time.Millisecond
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func TestBarCount(t *testing.T) {
	p := mpb.New(mpb.WithOutput(io.Discard))

	b := p.AddBar(0, mpb.BarRemoveOnComplete())

	if count := p.BarCount(); count != 1 {
		t.Errorf("BarCount want: %d, got: %d\n", 1, count)
	}

	b.SetTotal(100, true)

	b.Wait()

	if count := p.BarCount(); count != 0 {
		t.Errorf("BarCount want: %d, got: %d\n", 0, count)
	}

	p.Wait()
}

func TestBarAbort(t *testing.T) {
	shutdown := make(chan struct{})
	p := mpb.New(mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(io.Discard))
	n := 2
	bars := make([]*mpb.Bar, n)
	for i := 0; i < n; i++ {
		b := p.AddBar(100)
		switch i {
		case n - 1:
			var abortCalledTimes int
			for j := 0; !b.Aborted(); j++ {
				if j >= 10 {
					b.Abort(true)
					abortCalledTimes++
				} else {
					b.Increment()
				}
			}
			if abortCalledTimes != 1 {
				t.Errorf("Expected abortCalledTimes: %d, got: %d\n", 1, abortCalledTimes)
			}
			b.Wait()
			count := p.BarCount()
			if count != 1 {
				t.Errorf("BarCount want: %d, got: %d\n", 1, count)
			}
		default:
			go func() {
				for !b.Completed() {
					b.Increment()
					time.Sleep(randomDuration(100 * time.Millisecond))
				}
			}()
		}
		bars[i] = b
	}

	bars[0].Abort(false)

	go p.Wait()

	select {
	case <-shutdown:
	case <-time.After(timeout):
		t.Errorf("Progress didn't shutdown after %v", timeout)
	}
}

func TestWithContext(t *testing.T) {
	shutdown := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())
	p := mpb.NewWithContext(ctx,
		mpb.WithShutdownNotifier(shutdown),
		mpb.WithOutput(io.Discard),
	)
	_ = p.AddBar(0) // never complete bar
	_ = p.AddBar(0) // never complete bar
	go func() {
		time.Sleep(randomDuration(100 * time.Millisecond))
		cancel()
		p.Wait()
	}()

	select {
	case <-shutdown:
	case <-time.After(timeout):
		t.Errorf("Progress didn't shutdown after %v", timeout)
	}
}

// MaxWidthDistributor shouldn't stuck in the middle while removing or aborting a bar
func TestMaxWidthDistributor(t *testing.T) {

	makeWrapper := func(f func([]chan int), start, end chan struct{}) func([]chan int) {
		return func(column []chan int) {
			start <- struct{}{}
			f(column)
			<-end
		}
	}

	ready := make(chan struct{})
	start := make(chan struct{})
	end := make(chan struct{})
	mpb.MaxWidthDistributor = makeWrapper(mpb.MaxWidthDistributor, start, end)

	total := 100
	numBars := 6
	p := mpb.New(mpb.WithOutput(io.Discard))
	for i := 0; i < numBars; i++ {
		bar := p.AddBar(int64(total),
			mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0),
			mpb.PrependDecorators(decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace)),
		)
		go func() {
			<-ready
			for i := 0; i < total; i++ {
				start := time.Now()
				if id := bar.ID(); id > 1 && i >= 32 {
					if id&1 == 1 {
						bar.Abort(true)
					} else {
						bar.Abort(false)
					}
				}
				time.Sleep(randomDuration(100 * time.Millisecond))
				bar.EwmaIncrInt64(rand.Int63n(5)+1, time.Since(start))
			}
		}()
	}

	go func() {
		<-ready
		p.Wait()
		close(start)
	}()

	res := t.Run("maxWidthDistributor", func(t *testing.T) {
		close(ready)
		for v := range start {
			timer := time.NewTimer(100 * time.Millisecond)
			select {
			case end <- v:
				timer.Stop()
			case <-timer.C:
				t.FailNow()
			}
		}
	})

	if !res {
		t.Error("maxWidthDistributor stuck in the middle")
	}
}

func TestProgressShutdownsWithErrFiller(t *testing.T) {
	var debug bytes.Buffer
	shutdown := make(chan struct{})
	p := mpb.New(
		mpb.WithShutdownNotifier(shutdown),
		mpb.WithOutput(io.Discard),
		mpb.WithDebugOutput(&debug),
	)

	testError := errors.New("test error")
	bar := p.AddBar(100,
		mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
			return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
				if st.Current >= 42 {
					return testError
				}
				return base.Fill(w, st)
			})
		}),
	)

	for bar.IsRunning() {
		time.Sleep(randomDuration(100 * time.Millisecond))
		bar.Increment()
	}

	go p.Wait()

	select {
	case <-shutdown:
		if err := strings.TrimSpace(debug.String()); err != testError.Error() {
			t.Errorf("Expected err: %q, got %q\n", testError.Error(), err)
		}
	case <-time.After(timeout):
		t.Errorf("Progress didn't shutdown after %v", timeout)
	}
}

func randomDuration(max time.Duration) time.Duration {
	return time.Duration(rand.Intn(10)+1) * max / 10
}
<commit_msg>refactoring: TestBarAbort<commit_after>package mpb_test

import (
	"bytes"
	"context"
	"errors"
	"io"
	"math/rand"
	"strings"
	"testing"
	"time"

	"github.com/vbauerster/mpb/v8"
	"github.com/vbauerster/mpb/v8/decor"
)

const (
	timeout = 200 * time.Millisecond
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func TestBarCount(t *testing.T) {
	p := mpb.New(mpb.WithOutput(io.Discard))

	b := p.AddBar(0, mpb.BarRemoveOnComplete())

	if count := p.BarCount(); count != 1 {
		t.Errorf("BarCount want: %d, got: %d\n", 1, count)
	}

	b.SetTotal(100, true)

	b.Wait()

	if count := p.BarCount(); count != 0 {
		t.Errorf("BarCount want: %d, got: %d\n", 0, count)
	}

	p.Wait()
}

func TestBarAbort(t *testing.T) {
	shutdown := make(chan struct{})
	p := mpb.New(mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(io.Discard))
	n := 2
	bars := make([]*mpb.Bar, n)
	for i := 0; i < n; i++ {
		b := p.AddBar(100)
		switch i {
		case n - 1:
			var abortCalledTimes int
			for j := 0; !b.Aborted(); j++ {
				if j >= 10 {
					b.Abort(true)
					abortCalledTimes++
				} else {
					b.Increment()
				}
			}
			if abortCalledTimes != 1 {
				t.Errorf("Expected abortCalledTimes: %d, got: %d\n", 1, abortCalledTimes)
			}
			b.Wait()
			count := p.BarCount()
			if count != 1 {
				t.Errorf("BarCount want: %d, got: %d\n", 1, count)
			}
		default:
			go func() {
				for !b.Completed() {
					b.Increment()
					time.Sleep(randomDuration(100 * time.Millisecond))
				}
			}()
		}
		bars[i] = b
	}

	go p.Wait()

	bars[0].Abort(false)

	select {
	case <-shutdown:
	case <-time.After(timeout):
		t.Errorf("Progress didn't shutdown after %v", timeout)
	}
}

func TestWithContext(t *testing.T) {
	shutdown := make(chan struct{})
	ctx, cancel := context.WithCancel(context.Background())
	p := mpb.NewWithContext(ctx,
		mpb.WithShutdownNotifier(shutdown),
		mpb.WithOutput(io.Discard),
	)
	_ = p.AddBar(0) // never complete bar
	_ = p.AddBar(0) // never complete bar
	go func() {
		time.Sleep(randomDuration(100 * time.Millisecond))
		cancel()
		p.Wait()
	}()

	select {
	case <-shutdown:
	case <-time.After(timeout):
		t.Errorf("Progress didn't shutdown after %v", timeout)
	}
}

// MaxWidthDistributor shouldn't stuck in the middle while removing or aborting a bar
func TestMaxWidthDistributor(t *testing.T) {

	makeWrapper := func(f func([]chan int), start, end chan struct{}) func([]chan int) {
		return func(column []chan int) {
			start <- struct{}{}
			f(column)
			<-end
		}
	}

	ready := make(chan struct{})
	start := make(chan struct{})
	end := make(chan struct{})
	mpb.MaxWidthDistributor = makeWrapper(mpb.MaxWidthDistributor, start, end)

	total := 100
	numBars := 6
	p := mpb.New(mpb.WithOutput(io.Discard))
	for i := 0; i < numBars; i++ {
		bar := p.AddBar(int64(total),
			mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0),
			mpb.PrependDecorators(decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace)),
		)
		go func() {
			<-ready
			for i := 0; i < total; i++ {
				start := time.Now()
				if id := bar.ID(); id > 1 && i >= 32 {
					if id&1 == 1 {
						bar.Abort(true)
					} else {
						bar.Abort(false)
					}
				}
				time.Sleep(randomDuration(100 * time.Millisecond))
				bar.EwmaIncrInt64(rand.Int63n(5)+1, time.Since(start))
			}
		}()
	}

	go func() {
		<-ready
		p.Wait()
		close(start)
	}()

	res := t.Run("maxWidthDistributor", func(t *testing.T) {
		close(ready)
		for v := range start {
			timer := time.NewTimer(100 * time.Millisecond)
			select {
			case end <- v:
				timer.Stop()
			case <-timer.C:
				t.FailNow()
			}
		}
	})

	if !res {
		t.Error("maxWidthDistributor stuck in the middle")
	}
}

func TestProgressShutdownsWithErrFiller(t *testing.T) {
	var debug bytes.Buffer
	shutdown := make(chan struct{})
	p := mpb.New(
		mpb.WithShutdownNotifier(shutdown),
		mpb.WithOutput(io.Discard),
		mpb.WithDebugOutput(&debug),
	)

	testError := errors.New("test error")
	bar := p.AddBar(100,
		mpb.BarFillerMiddleware(func(base mpb.BarFiller) mpb.BarFiller {
			return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
				if st.Current >= 42 {
					return testError
				}
				return base.Fill(w, st)
			})
		}),
	)

	for bar.IsRunning() {
		time.Sleep(randomDuration(100 * time.Millisecond))
		bar.Increment()
	}

	go p.Wait()

	select {
	case <-shutdown:
		if err := strings.TrimSpace(debug.String()); err != testError.Error() {
			t.Errorf("Expected err: %q, got %q\n", testError.Error(), err)
		}
	case <-time.After(timeout):
		t.Errorf("Progress didn't shutdown after %v", timeout)
	}
}

func randomDuration(max time.Duration) time.Duration {
	return time.Duration(rand.Intn(10)+1) * max / 10
}
<|endoftext|>


<commit_before>package gerrit_test

import (
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"testing"

	"github.com/andygrunwald/go-gerrit"
)

func TestProjectsService_ListProjects(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, testValues{
			"r": "(arch|benchmarks)",
			"n": "2",
		})

		fmt.Fprint(w, `)]}'`+"\n"+`{"arch":{"id":"arch","state":"ACTIVE"},"benchmarks":{"id":"benchmarks","state":"ACTIVE"}}`)
	})

	opt := &gerrit.ProjectOptions{
		Regex: "(arch|benchmarks)",
	}
	opt.Limit = 2
	project, _, err := testClient.Projects.ListProjects(opt)
	if err != nil {
		t.Errorf("Projects.ListProjects returned error: %v", err)
	}

	want := &map[string]gerrit.ProjectInfo{
		"arch": {
			ID:    "arch",
			State: "ACTIVE",
		},
		"benchmarks": {
			ID:    "benchmarks",
			State: "ACTIVE",
		},
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.ListProjects returned %+v, want %+v", project, want)
	}
}

func TestProjectsService_GetProject(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/go/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `)]}'`+"\n"+`{"id":"go","name":"go","parent":"All-Projects","description":"The Go Programming Language","state":"ACTIVE"}`)
	})

	project, _, err := testClient.Projects.GetProject("go")
	if err != nil {
		t.Errorf("Projects.GetProject returned error: %v", err)
	}

	want := &gerrit.ProjectInfo{
		ID:          "go",
		Name:        "go",
		Parent:      "All-Projects",
		Description: "The Go Programming Language",
		State:       "ACTIVE",
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.GetProject returned %+v, want %+v", project, want)
	}
}

func TestProjectsService_GetProject_WithSlash(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/plugins/delete-project", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testRequestURL(t, r, "/projects/plugins%2Fdelete-project")

		fmt.Fprint(w, `)]}'`+"\n"+`{"id":"plugins%2Fdelete-project","name":"plugins/delete-project","parent":"Public-Plugins","description":"A plugin which allows projects to be deleted from Gerrit via an SSH command","state":"ACTIVE"}`)
	})
	project, _, err := testClient.Projects.GetProject("plugins/delete-project")
	if err != nil {
		t.Errorf("Projects.GetProject returned error: %v", err)
	}

	want := &gerrit.ProjectInfo{
		ID:          "plugins%2Fdelete-project",
		Name:        "plugins/delete-project",
		Parent:      "Public-Plugins",
		Description: "A plugin which allows projects to be deleted from Gerrit via an SSH command",
		State:       "ACTIVE",
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.GetProject returned %+v, want %+v", project, want)
	}
}

// +func (s *ProjectsService) CreateProject(name string, input *ProjectInput) (*ProjectInfo, *Response, error) {
func TestProjectsService_CreateProject(t *testing.T) {
	setup()
	defer teardown()

	input := &gerrit.ProjectInput{
		Description: "The Go Programming Language",
	}

	testMux.HandleFunc("/projects/go/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")

		v := new(gerrit.ProjectInput)
		if err := json.NewDecoder(r.Body).Decode(v); err != nil {
			t.Error(err)
		}

		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `)]}'`+"\n"+`{"id":"go","name":"go","parent":"All-Projects","description":"The Go Programming Language"}`)
	})

	project, _, err := testClient.Projects.CreateProject("go", input)
	if err != nil {
		t.Errorf("Projects.CreateProject returned error: %v", err)
	}

	want := &gerrit.ProjectInfo{
		ID:          "go",
		Name:        "go",
		Parent:      "All-Projects",
		Description: "The Go Programming Language",
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.CreateProject returned %+v, want %+v", project, want)
	}
}

// +func (s *ProjectsService) GetProjectDescription(name string) (*string, *Response, error) {
func TestProjectsService_GetProjectDescription(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/go/description/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `)]}'`+"\n"+`"The Go Programming Language"`)
	})

	description, _, err := testClient.Projects.GetProjectDescription("go")
	if err != nil {
		t.Errorf("Projects.GetProjectDescription returned error: %v", err)
	}

	want := "The Go Programming Language"

	if !reflect.DeepEqual(description, want) {
		t.Errorf("Projects.GetProjectDescription returned %+v, want %+v", description, want)
	}
}

func ExampleProjectsService_ListProjects() {
	instance := "https://chromium-review.googlesource.com/"
	client, err := gerrit.NewClient(instance, nil)
	if err != nil {
		panic(err)
	}

	opt := &gerrit.ProjectOptions{
		Description: true,
		Prefix:      "infra/infra/infra_l",
	}
	projects, _, err := client.Projects.ListProjects(opt)
	if err != nil {
		panic(err)
	}

	for name, p := range *projects {
		fmt.Printf("%s - State: %s\n", name, p.State)
	}

	// Output:
	// infra/infra/infra_libs - State: ACTIVE
}

func TestProjectsService_GetBranch(t *testing.T) {
	setup()
	defer teardown()

	existBranches := map[string]*gerrit.BranchInfo{
		"branch": {
			Ref:       "123",
			Revision:  "abcd1234",
			CanDelete: true,
		},
		"branch/foo": {
			Ref:       "456",
			Revision:  "deadbeef",
			CanDelete: false,
		},
	}

	testMux.HandleFunc("/projects/go/branches/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		branchName := r.URL.Path[len("/projects/go/branches/"):]

		branchInfo, ok := existBranches[branchName]
		if !ok {
			http.Error(w, branchName, http.StatusBadRequest)
		}

		branchInfoRaw, err := json.Marshal(&branchInfo)
		if err != nil {
			http.Error(w, branchName, http.StatusBadRequest)
		}

		fmt.Fprint(w, `)]}'`+"\n"+string(branchInfoRaw))
	})

	var tests = []struct {
		name     string
		branch   string
		expected *gerrit.BranchInfo
	}{
		{
			name:   "branch without slash",
			branch: "branch",
			expected: &gerrit.BranchInfo{
				Ref:       "123",
				Revision:  "abcd1234",
				CanDelete: true,
			},
		},
		{
			name:   "branch with slash",
			branch: "branch/foo",
			expected: &gerrit.BranchInfo{
				Ref:       "456",
				Revision:  "deadbeef",
				CanDelete: false,
			},
		},
	}

	for _, tc := range tests {
		branchInfo, _, err := testClient.Projects.GetBranch("go", tc.branch)
		if err != nil {
			t.Errorf("tc %s: Projects.GetProject returned error: %v", tc.name, err)
		}

		if !reflect.DeepEqual(branchInfo, tc.expected) {
			t.Errorf("tc %s: Projects.GetBranch returned %+v, want %+v", tc.name, branchInfo, tc.expected)
		}
	}
}
<commit_msg>Escape % character in fm.Fprint (#106)<commit_after>package gerrit_test

import (
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"testing"

	"github.com/andygrunwald/go-gerrit"
)

func TestProjectsService_ListProjects(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, testValues{
			"r": "(arch|benchmarks)",
			"n": "2",
		})

		fmt.Fprint(w, `)]}'`+"\n"+`{"arch":{"id":"arch","state":"ACTIVE"},"benchmarks":{"id":"benchmarks","state":"ACTIVE"}}`)
	})

	opt := &gerrit.ProjectOptions{
		Regex: "(arch|benchmarks)",
	}
	opt.Limit = 2
	project, _, err := testClient.Projects.ListProjects(opt)
	if err != nil {
		t.Errorf("Projects.ListProjects returned error: %v", err)
	}

	want := &map[string]gerrit.ProjectInfo{
		"arch": {
			ID:    "arch",
			State: "ACTIVE",
		},
		"benchmarks": {
			ID:    "benchmarks",
			State: "ACTIVE",
		},
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.ListProjects returned %+v, want %+v", project, want)
	}
}

func TestProjectsService_GetProject(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/go/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `)]}'`+"\n"+`{"id":"go","name":"go","parent":"All-Projects","description":"The Go Programming Language","state":"ACTIVE"}`)
	})

	project, _, err := testClient.Projects.GetProject("go")
	if err != nil {
		t.Errorf("Projects.GetProject returned error: %v", err)
	}

	want := &gerrit.ProjectInfo{
		ID:          "go",
		Name:        "go",
		Parent:      "All-Projects",
		Description: "The Go Programming Language",
		State:       "ACTIVE",
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.GetProject returned %+v, want %+v", project, want)
	}
}

func TestProjectsService_GetProject_WithSlash(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/plugins/delete-project", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testRequestURL(t, r, "/projects/plugins%2Fdelete-project")

		c := `)]}'` + "\n" + `{"id":"plugins%2Fdelete-project","name":"plugins/delete-project","parent":"Public-Plugins","description":"A plugin which allows projects to be deleted from Gerrit via an SSH command","state":"ACTIVE"}`
		fmt.Fprint(w, c)
	})
	project, _, err := testClient.Projects.GetProject("plugins/delete-project")
	if err != nil {
		t.Errorf("Projects.GetProject returned error: %v", err)
	}

	want := &gerrit.ProjectInfo{
		ID:          "plugins%2Fdelete-project",
		Name:        "plugins/delete-project",
		Parent:      "Public-Plugins",
		Description: "A plugin which allows projects to be deleted from Gerrit via an SSH command",
		State:       "ACTIVE",
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.GetProject returned %+v, want %+v", project, want)
	}
}

// +func (s *ProjectsService) CreateProject(name string, input *ProjectInput) (*ProjectInfo, *Response, error) {
func TestProjectsService_CreateProject(t *testing.T) {
	setup()
	defer teardown()

	input := &gerrit.ProjectInput{
		Description: "The Go Programming Language",
	}

	testMux.HandleFunc("/projects/go/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")

		v := new(gerrit.ProjectInput)
		if err := json.NewDecoder(r.Body).Decode(v); err != nil {
			t.Error(err)
		}

		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `)]}'`+"\n"+`{"id":"go","name":"go","parent":"All-Projects","description":"The Go Programming Language"}`)
	})

	project, _, err := testClient.Projects.CreateProject("go", input)
	if err != nil {
		t.Errorf("Projects.CreateProject returned error: %v", err)
	}

	want := &gerrit.ProjectInfo{
		ID:          "go",
		Name:        "go",
		Parent:      "All-Projects",
		Description: "The Go Programming Language",
	}

	if !reflect.DeepEqual(project, want) {
		t.Errorf("Projects.CreateProject returned %+v, want %+v", project, want)
	}
}

// +func (s *ProjectsService) GetProjectDescription(name string) (*string, *Response, error) {
func TestProjectsService_GetProjectDescription(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/projects/go/description/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `)]}'`+"\n"+`"The Go Programming Language"`)
	})

	description, _, err := testClient.Projects.GetProjectDescription("go")
	if err != nil {
		t.Errorf("Projects.GetProjectDescription returned error: %v", err)
	}

	want := "The Go Programming Language"

	if !reflect.DeepEqual(description, want) {
		t.Errorf("Projects.GetProjectDescription returned %+v, want %+v", description, want)
	}
}

func ExampleProjectsService_ListProjects() {
	instance := "https://chromium-review.googlesource.com/"
	client, err := gerrit.NewClient(instance, nil)
	if err != nil {
		panic(err)
	}

	opt := &gerrit.ProjectOptions{
		Description: true,
		Prefix:      "infra/infra/infra_l",
	}
	projects, _, err := client.Projects.ListProjects(opt)
	if err != nil {
		panic(err)
	}

	for name, p := range *projects {
		fmt.Printf("%s - State: %s\n", name, p.State)
	}

	// Output:
	// infra/infra/infra_libs - State: ACTIVE
}

func TestProjectsService_GetBranch(t *testing.T) {
	setup()
	defer teardown()

	existBranches := map[string]*gerrit.BranchInfo{
		"branch": {
			Ref:       "123",
			Revision:  "abcd1234",
			CanDelete: true,
		},
		"branch/foo": {
			Ref:       "456",
			Revision:  "deadbeef",
			CanDelete: false,
		},
	}

	testMux.HandleFunc("/projects/go/branches/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		branchName := r.URL.Path[len("/projects/go/branches/"):]

		branchInfo, ok := existBranches[branchName]
		if !ok {
			http.Error(w, branchName, http.StatusBadRequest)
		}

		branchInfoRaw, err := json.Marshal(&branchInfo)
		if err != nil {
			http.Error(w, branchName, http.StatusBadRequest)
		}

		fmt.Fprint(w, `)]}'`+"\n"+string(branchInfoRaw))
	})

	var tests = []struct {
		name     string
		branch   string
		expected *gerrit.BranchInfo
	}{
		{
			name:   "branch without slash",
			branch: "branch",
			expected: &gerrit.BranchInfo{
				Ref:       "123",
				Revision:  "abcd1234",
				CanDelete: true,
			},
		},
		{
			name:   "branch with slash",
			branch: "branch/foo",
			expected: &gerrit.BranchInfo{
				Ref:       "456",
				Revision:  "deadbeef",
				CanDelete: false,
			},
		},
	}

	for _, tc := range tests {
		branchInfo, _, err := testClient.Projects.GetBranch("go", tc.branch)
		if err != nil {
			t.Errorf("tc %s: Projects.GetProject returned error: %v", tc.name, err)
		}

		if !reflect.DeepEqual(branchInfo, tc.expected) {
			t.Errorf("tc %s: Projects.GetBranch returned %+v, want %+v", tc.name, branchInfo, tc.expected)
		}
	}
}
<|endoftext|>


<commit_before>package repos

import (
	"net/http"

	"github.com/gorilla/sessions"
	mongostore "gopkg.in/go-playground/mongostore.v4"
)

var sessionStore = mongostore.New(dbSession, "sessions", &sessions.Options{MaxAge: 3600, Secure: true}, true,
	[]byte("secret-key"))

// GetSession is configured object for reading http sessions
func GetSession(r *http.Request) (*sessions.Session, error) {
	return sessionStore.Get(r, "sessionkey")
}
<commit_msg>debugging sessions<commit_after>package repos

import (
	"net/http"

	"github.com/gorilla/sessions"
)

//mongostore "gopkg.in/go-playground/mongostore.v4"

//var sessionStore = mongostore.New(dbSession, "sessions", &sessions.Options{MaxAge: 3600, Secure: true}, true,
//[]byte("secret-key"))
var sessionStore = sessions.NewCookieStore([]byte("ASFasfafasfasfSfAS"))

// GetSession is configured object for reading http sessions
func GetSession(r *http.Request) (*sessions.Session, error) {
	return sessionStore.Get(r, "sessionkey")
}
<|endoftext|>


<commit_before>// GraphicsMagick processor
package graphicsmagick

import (
	"container/list"
	"fmt"
	"github.com/pierrre/imageserver"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"
)

const (
	globalParameterName = "graphicsmagick"
	tempDirPrefix       = "imageserver_"
)

// Processes an image with GraphicsMagick command line (mogrify command)
//
// All parameters are extracted from the "graphicsmagick" node parameter and are optionals.
//
// See GraphicsMagick documentation for more information about arguments.
//
// Parameters
//
// - width / height: sizes for "-resize" argument (both optionals)
//
// - fill: "^" for "-resize" argument
//
// - ignore_ratio: "!" for "-resize" argument
//
// - only_shrink_larger: ">" for "-resize" argument
//
// - only_enlarge_smaller: "<" for "-resize" argument
//
// - background: color for "-background" argument, 3/4/6/8 lower case hexadecimal characters
//
// - extent: "-extent" parameter, uses width/height parameters and add "-gravity center" argument
//
// - format: "-format" parameter
//
// - quality: "-quality" parameter
type GraphicsMagickProcessor struct {
	Executable string // path to "gm" executable, usually "/usr/bin/gm"

	Timeout          time.Duration     // timeout for process, optional
	TempDir          string            // temp directory for image files, optional
	AllowedFormats   []string          // allowed format list, optional
	DefaultQualities map[string]string // default qualities by format, optional
}

func (processor *GraphicsMagickProcessor) Process(sourceImage *imageserver.Image, parameters imageserver.Parameters) (*imageserver.Image, error) {
	parameters, err := processor.getParameters(parameters)
	if err != nil {
		return nil, err
	}
	if parameters == nil || parameters.Empty() {
		return sourceImage, nil
	}

	arguments := list.New()

	width, height, err := processor.buildArgumentsResize(arguments, parameters)
	if err != nil {
		return nil, err
	}

	err = processor.buildArgumentsBackground(arguments, parameters)
	if err != nil {
		return nil, err
	}

	err = processor.buildArgumentsExtent(arguments, parameters, width, height)
	if err != nil {
		return nil, err
	}

	format, formatSpecified, err := processor.buildArgumentsFormat(arguments, parameters, sourceImage)
	if err != nil {
		return nil, err
	}

	err = processor.buildArgumentsQuality(arguments, parameters, format)
	if err != nil {
		return nil, err
	}

	if arguments.Len() == 0 {
		return sourceImage, nil
	}

	arguments.PushFront("mogrify")

	tempDir, err := ioutil.TempDir(processor.TempDir, tempDirPrefix)
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(tempDir)

	file := filepath.Join(tempDir, "image")
	arguments.PushBack(file)
	err = ioutil.WriteFile(file, sourceImage.Data, os.FileMode(0600))
	if err != nil {
		return nil, err
	}

	argumentSlice := processor.convertArgumentsToSlice(arguments)

	cmd := exec.Command(processor.Executable, argumentSlice...)

	err = processor.runCommandTimeout(cmd)
	if err != nil {
		return nil, err
	}

	if formatSpecified {
		file = fmt.Sprintf("%s.%s", file, format)
	}
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	image := &imageserver.Image{}
	image.Data = data
	image.Type = format

	return image, nil
}

func (processor *GraphicsMagickProcessor) getParameters(parameters imageserver.Parameters) (imageserver.Parameters, error) {
	if !parameters.Has(globalParameterName) {
		return nil, nil
	}

	return parameters.GetParameters(globalParameterName)
}

func (processor *GraphicsMagickProcessor) buildArgumentsResize(arguments *list.List, parameters imageserver.Parameters) (width int, height int, err error) {
	width, _ = parameters.GetInt("width")
	if width < 0 {
		return 0, 0, imageserver.NewError("Invalid width parameter")
	}

	height, _ = parameters.GetInt("height")
	if height < 0 {
		return 0, 0, imageserver.NewError("Invalid height parameter")
	}

	if width != 0 || height != 0 {
		widthString := ""
		if width != 0 {
			widthString = strconv.Itoa(width)
		}
		heightString := ""
		if height != 0 {
			heightString = strconv.Itoa(height)
		}
		resize := fmt.Sprintf("%sx%s", widthString, heightString)

		if fill, _ := parameters.GetBool("fill"); fill {
			resize = resize + "^"
		}

		if ignoreRatio, _ := parameters.GetBool("ignore_ratio"); ignoreRatio {
			resize = resize + "!"
		}

		if onlyShrinkLarger, _ := parameters.GetBool("only_shrink_larger"); onlyShrinkLarger {
			resize = resize + ">"
		}

		if onlyEnlargeSmaller, _ := parameters.GetBool("only_enlarge_smaller"); onlyEnlargeSmaller {
			resize = resize + "<"
		}

		arguments.PushBack("-resize")
		arguments.PushBack(resize)
	}

	return width, height, nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsBackground(arguments *list.List, parameters imageserver.Parameters) error {
	background, _ := parameters.GetString("background")

	if backgroundLength := len(background); backgroundLength > 0 {
		if backgroundLength != 6 && backgroundLength != 8 && backgroundLength != 3 && backgroundLength != 4 {
			return imageserver.NewError("Invalid background parameter")
		}

		for _, r := range background {
			if (r < '0' || r > '9') && (r < 'a' || r > 'f') {
				return imageserver.NewError("Invalid background parameter")
			}
		}

		arguments.PushBack("-background")
		arguments.PushBack(fmt.Sprintf("#%s", background))
	}

	return nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsExtent(arguments *list.List, parameters imageserver.Parameters, width int, height int) error {
	if width != 0 && height != 0 {
		if extent, _ := parameters.GetBool("extent"); extent {
			arguments.PushBack("-gravity")
			arguments.PushBack("center")

			arguments.PushBack("-extent")
			arguments.PushBack(fmt.Sprintf("%dx%d", width, height))
		}
	}

	return nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsFormat(arguments *list.List, parameters imageserver.Parameters, sourceImage *imageserver.Image) (format string, formatSpecified bool, err error) {
	format, _ = parameters.GetString("format")

	formatSpecified = true
	if len(format) == 0 {
		format = sourceImage.Type
		formatSpecified = false
	}

	if processor.AllowedFormats != nil {
		ok := false
		for _, f := range processor.AllowedFormats {
			if f == format {
				ok = true
				break
			}
		}
		if !ok {
			return "", false, imageserver.NewError("Invalid format parameter")
		}
	}

	if formatSpecified {
		arguments.PushBack("-format")
		arguments.PushBack(format)
	}

	return format, formatSpecified, nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsQuality(arguments *list.List, parameters imageserver.Parameters, format string) error {
	quality, _ := parameters.GetString("quality")

	if len(quality) == 0 && arguments.Len() == 0 {
		return nil
	}

	if len(quality) == 0 && processor.DefaultQualities != nil {
		if q, ok := processor.DefaultQualities[format]; ok {
			quality = q
		}
	}

	if len(quality) > 0 {
		qualityInt, err := strconv.Atoi(quality)
		if err != nil {
			return imageserver.NewError("Invalid quality parameter (parse int error)")
		}

		if qualityInt < 0 {
			return imageserver.NewError("Invalid quality parameter (less than 0)")
		}

		if format == "jpeg" {
			if qualityInt < 0 || qualityInt > 100 {
				return imageserver.NewError("Invalid quality parameter (must be between 0 and 100)")
			}
		}

		arguments.PushBack("-quality")
		arguments.PushBack(quality)
	}

	return nil
}

func (processor *GraphicsMagickProcessor) convertArgumentsToSlice(arguments *list.List) []string {
	argumentSlice := make([]string, 0, arguments.Len())
	for e := arguments.Front(); e != nil; e = e.Next() {
		argumentSlice = append(argumentSlice, e.Value.(string))
	}
	return argumentSlice
}

func (processor *GraphicsMagickProcessor) runCommandTimeout(cmd *exec.Cmd) error {
	err := cmd.Start()
	if err != nil {
		return err
	}

	cmdChan := make(chan error)
	go func() {
		cmdChan <- cmd.Wait()
	}()

	var timeoutChan <-chan time.Time
	if processor.Timeout != 0 {
		timeoutChan = time.After(processor.Timeout)
	}

	select {
	case err = <-cmdChan:
		if err != nil {
			return err
		}

		return nil
	case <-timeoutChan:
		err = cmd.Process.Kill()
		if err != nil {
			return err
		}

		<-cmdChan

		return fmt.Errorf("GraphicsMagickProcessor command timeout after %s: %+v", processor.Timeout, cmd)
	}
}
<commit_msg>use struct "constructor"<commit_after>// GraphicsMagick processor
package graphicsmagick

import (
	"container/list"
	"fmt"
	"github.com/pierrre/imageserver"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"
)

const (
	globalParameterName = "graphicsmagick"
	tempDirPrefix       = "imageserver_"
)

// Processes an image with GraphicsMagick command line (mogrify command)
//
// All parameters are extracted from the "graphicsmagick" node parameter and are optionals.
//
// See GraphicsMagick documentation for more information about arguments.
//
// Parameters
//
// - width / height: sizes for "-resize" argument (both optionals)
//
// - fill: "^" for "-resize" argument
//
// - ignore_ratio: "!" for "-resize" argument
//
// - only_shrink_larger: ">" for "-resize" argument
//
// - only_enlarge_smaller: "<" for "-resize" argument
//
// - background: color for "-background" argument, 3/4/6/8 lower case hexadecimal characters
//
// - extent: "-extent" parameter, uses width/height parameters and add "-gravity center" argument
//
// - format: "-format" parameter
//
// - quality: "-quality" parameter
type GraphicsMagickProcessor struct {
	Executable string // path to "gm" executable, usually "/usr/bin/gm"

	Timeout          time.Duration     // timeout for process, optional
	TempDir          string            // temp directory for image files, optional
	AllowedFormats   []string          // allowed format list, optional
	DefaultQualities map[string]string // default qualities by format, optional
}

func (processor *GraphicsMagickProcessor) Process(sourceImage *imageserver.Image, parameters imageserver.Parameters) (*imageserver.Image, error) {
	parameters, err := processor.getParameters(parameters)
	if err != nil {
		return nil, err
	}
	if parameters == nil || parameters.Empty() {
		return sourceImage, nil
	}

	arguments := list.New()

	width, height, err := processor.buildArgumentsResize(arguments, parameters)
	if err != nil {
		return nil, err
	}

	err = processor.buildArgumentsBackground(arguments, parameters)
	if err != nil {
		return nil, err
	}

	err = processor.buildArgumentsExtent(arguments, parameters, width, height)
	if err != nil {
		return nil, err
	}

	format, formatSpecified, err := processor.buildArgumentsFormat(arguments, parameters, sourceImage)
	if err != nil {
		return nil, err
	}

	err = processor.buildArgumentsQuality(arguments, parameters, format)
	if err != nil {
		return nil, err
	}

	if arguments.Len() == 0 {
		return sourceImage, nil
	}

	arguments.PushFront("mogrify")

	tempDir, err := ioutil.TempDir(processor.TempDir, tempDirPrefix)
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(tempDir)

	file := filepath.Join(tempDir, "image")
	arguments.PushBack(file)
	err = ioutil.WriteFile(file, sourceImage.Data, os.FileMode(0600))
	if err != nil {
		return nil, err
	}

	argumentSlice := processor.convertArgumentsToSlice(arguments)

	cmd := exec.Command(processor.Executable, argumentSlice...)

	err = processor.runCommandTimeout(cmd)
	if err != nil {
		return nil, err
	}

	if formatSpecified {
		file = fmt.Sprintf("%s.%s", file, format)
	}
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	image := &imageserver.Image{
		Type: format,
		Data: data,
	}

	return image, nil
}

func (processor *GraphicsMagickProcessor) getParameters(parameters imageserver.Parameters) (imageserver.Parameters, error) {
	if !parameters.Has(globalParameterName) {
		return nil, nil
	}

	return parameters.GetParameters(globalParameterName)
}

func (processor *GraphicsMagickProcessor) buildArgumentsResize(arguments *list.List, parameters imageserver.Parameters) (width int, height int, err error) {
	width, _ = parameters.GetInt("width")
	if width < 0 {
		return 0, 0, imageserver.NewError("Invalid width parameter")
	}

	height, _ = parameters.GetInt("height")
	if height < 0 {
		return 0, 0, imageserver.NewError("Invalid height parameter")
	}

	if width != 0 || height != 0 {
		widthString := ""
		if width != 0 {
			widthString = strconv.Itoa(width)
		}
		heightString := ""
		if height != 0 {
			heightString = strconv.Itoa(height)
		}
		resize := fmt.Sprintf("%sx%s", widthString, heightString)

		if fill, _ := parameters.GetBool("fill"); fill {
			resize = resize + "^"
		}

		if ignoreRatio, _ := parameters.GetBool("ignore_ratio"); ignoreRatio {
			resize = resize + "!"
		}

		if onlyShrinkLarger, _ := parameters.GetBool("only_shrink_larger"); onlyShrinkLarger {
			resize = resize + ">"
		}

		if onlyEnlargeSmaller, _ := parameters.GetBool("only_enlarge_smaller"); onlyEnlargeSmaller {
			resize = resize + "<"
		}

		arguments.PushBack("-resize")
		arguments.PushBack(resize)
	}

	return width, height, nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsBackground(arguments *list.List, parameters imageserver.Parameters) error {
	background, _ := parameters.GetString("background")

	if backgroundLength := len(background); backgroundLength > 0 {
		if backgroundLength != 6 && backgroundLength != 8 && backgroundLength != 3 && backgroundLength != 4 {
			return imageserver.NewError("Invalid background parameter")
		}

		for _, r := range background {
			if (r < '0' || r > '9') && (r < 'a' || r > 'f') {
				return imageserver.NewError("Invalid background parameter")
			}
		}

		arguments.PushBack("-background")
		arguments.PushBack(fmt.Sprintf("#%s", background))
	}

	return nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsExtent(arguments *list.List, parameters imageserver.Parameters, width int, height int) error {
	if width != 0 && height != 0 {
		if extent, _ := parameters.GetBool("extent"); extent {
			arguments.PushBack("-gravity")
			arguments.PushBack("center")

			arguments.PushBack("-extent")
			arguments.PushBack(fmt.Sprintf("%dx%d", width, height))
		}
	}

	return nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsFormat(arguments *list.List, parameters imageserver.Parameters, sourceImage *imageserver.Image) (format string, formatSpecified bool, err error) {
	format, _ = parameters.GetString("format")

	formatSpecified = true
	if len(format) == 0 {
		format = sourceImage.Type
		formatSpecified = false
	}

	if processor.AllowedFormats != nil {
		ok := false
		for _, f := range processor.AllowedFormats {
			if f == format {
				ok = true
				break
			}
		}
		if !ok {
			return "", false, imageserver.NewError("Invalid format parameter")
		}
	}

	if formatSpecified {
		arguments.PushBack("-format")
		arguments.PushBack(format)
	}

	return format, formatSpecified, nil
}

func (processor *GraphicsMagickProcessor) buildArgumentsQuality(arguments *list.List, parameters imageserver.Parameters, format string) error {
	quality, _ := parameters.GetString("quality")

	if len(quality) == 0 && arguments.Len() == 0 {
		return nil
	}

	if len(quality) == 0 && processor.DefaultQualities != nil {
		if q, ok := processor.DefaultQualities[format]; ok {
			quality = q
		}
	}

	if len(quality) > 0 {
		qualityInt, err := strconv.Atoi(quality)
		if err != nil {
			return imageserver.NewError("Invalid quality parameter (parse int error)")
		}

		if qualityInt < 0 {
			return imageserver.NewError("Invalid quality parameter (less than 0)")
		}

		if format == "jpeg" {
			if qualityInt < 0 || qualityInt > 100 {
				return imageserver.NewError("Invalid quality parameter (must be between 0 and 100)")
			}
		}

		arguments.PushBack("-quality")
		arguments.PushBack(quality)
	}

	return nil
}

func (processor *GraphicsMagickProcessor) convertArgumentsToSlice(arguments *list.List) []string {
	argumentSlice := make([]string, 0, arguments.Len())
	for e := arguments.Front(); e != nil; e = e.Next() {
		argumentSlice = append(argumentSlice, e.Value.(string))
	}
	return argumentSlice
}

func (processor *GraphicsMagickProcessor) runCommandTimeout(cmd *exec.Cmd) error {
	err := cmd.Start()
	if err != nil {
		return err
	}

	cmdChan := make(chan error)
	go func() {
		cmdChan <- cmd.Wait()
	}()

	var timeoutChan <-chan time.Time
	if processor.Timeout != 0 {
		timeoutChan = time.After(processor.Timeout)
	}

	select {
	case err = <-cmdChan:
		if err != nil {
			return err
		}

		return nil
	case <-timeoutChan:
		err = cmd.Process.Kill()
		if err != nil {
			return err
		}

		<-cmdChan

		return fmt.Errorf("GraphicsMagickProcessor command timeout after %s: %+v", processor.Timeout, cmd)
	}
}
<|endoftext|>


<commit_before>package radius

import (
	"encoding/binary"
	"io"
)

// A PacketCode is a code that defines the type of the packet
type PacketCode int64

const (

	// Packet codes for RFC2866/Accounting

	AccountingRequest  PacketCode = 4
	AccountingResponse PacketCode = 5

	// --
)

// Write writes the packet code to the writer
func (code PacketCode) Write(w io.Writer) error {
	return binary.Write(w, binary.BigEndian, int8(code))
}
<commit_msg>Remove unnecessary endianness from packet code writer<commit_after>package radius

import "io"

// A PacketCode is a code that defines the type of the packet
type PacketCode int64

const (

	// Packet codes for RFC2866/Accounting

	AccountingRequest  PacketCode = 4
	AccountingResponse PacketCode = 5

	// --
)

// Write writes the packet code to the writer
func (code PacketCode) Write(w io.Writer) error {
	_, err := w.Write([]byte{byte(code)})
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"time"

	"github.com/dchest/spipe"
)

type BufferedSpipeConn struct {
	net.Conn
	packets chan []byte
	quit    chan struct{}
}

func newBufferedSpipeConn(conn net.Conn) BufferedSpipeConn {
	bsc := BufferedSpipeConn{conn, make(chan []byte), make(chan struct{})}

	go func() {
		var buf [1024]byte
		buflen := 0

		// experimentally, the callers don't batch faster than every
		// 10ms, so 50ms is a safe amount of time to wait
		tick := time.Tick(50 * time.Millisecond)

		for {
			select {
			case packet := <-bsc.packets:
				if buflen+len(packet) < len(buf) {
					n := copy(buf[buflen:], packet)
					buflen += n
					break
				}

				copy(buf[buflen:], packet[:1024-buflen])
				conn.Write(buf[:])

				packet = packet[1024-buflen:]
				for len(packet) > 1024 {
					conn.Write(packet[:1024])
					packet = packet[1024:]
				}

				buflen = copy(buf[:], packet)
			case <-tick:
				if buflen == 0 {
					break
				}

				conn.Write(buf[:buflen])
				buflen = 0
			case <-bsc.quit:
				return
			}
		}
	}()

	return bsc
}

func (bsc BufferedSpipeConn) Write(p []byte) (n int, err error) {
	bsc.packets <- p

	// MEH
	return len(p), nil
}

func (bsc BufferedSpipeConn) Close() error {
	bsc.quit <- struct{}{}
	return bsc.Conn.Close()
}

func NewTCPConn(key []byte, peer string) (conn net.Conn, err error) {
	sconn, err := spipe.Dial(key, "tcp", peer)
	if err != nil {
		return
	}

	return newBufferedSpipeConn(sconn), nil
}
<commit_msg>Buffer quit chan for bufferedSpipeConn<commit_after>package main

import (
	"net"
	"time"

	"github.com/dchest/spipe"
)

type BufferedSpipeConn struct {
	net.Conn
	packets chan []byte
	quit    chan struct{}
}

func newBufferedSpipeConn(conn net.Conn) BufferedSpipeConn {
	bsc := BufferedSpipeConn{conn, make(chan []byte), make(chan struct{}, 1)}

	go func() {
		var buf [1024]byte
		buflen := 0

		// experimentally, the callers don't batch faster than every
		// 10ms, so 50ms is a safe amount of time to wait
		tick := time.Tick(50 * time.Millisecond)

		for {
			select {
			case packet := <-bsc.packets:
				if buflen+len(packet) < len(buf) {
					n := copy(buf[buflen:], packet)
					buflen += n
					break
				}

				copy(buf[buflen:], packet[:1024-buflen])
				conn.Write(buf[:])

				packet = packet[1024-buflen:]
				for len(packet) > 1024 {
					conn.Write(packet[:1024])
					packet = packet[1024:]
				}

				buflen = copy(buf[:], packet)
			case <-tick:
				if buflen == 0 {
					break
				}

				conn.Write(buf[:buflen])
				buflen = 0
			case <-bsc.quit:
				return
			}
		}
	}()

	return bsc
}

func (bsc BufferedSpipeConn) Write(p []byte) (n int, err error) {
	bsc.packets <- p

	// MEH
	return len(p), nil
}

func (bsc BufferedSpipeConn) Close() error {
	bsc.quit <- struct{}{}
	return bsc.Conn.Close()
}

func NewTCPConn(key []byte, peer string) (conn net.Conn, err error) {
	sconn, err := spipe.Dial(key, "tcp", peer)
	if err != nil {
		return
	}

	return newBufferedSpipeConn(sconn), nil
}
<|endoftext|>


<commit_before>package styx

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"

	"aqwari.net/net/styx/internal/qidpool"
	"aqwari.net/net/styx/internal/styxfile"
	"aqwari.net/net/styx/internal/tracing"
	"aqwari.net/net/styx/internal/util"
	"aqwari.net/net/styx/styxproto"

	"golang.org/x/net/context"
)

var (
	errFidInUse     = errors.New("fid already in use")
	errTagInUse     = errors.New("tag in use")
	errNoFid        = errors.New("no such fid")
	errNotSupported = errors.New("not supported")
)

type fcall interface {
	styxproto.Msg
	Fid() uint32
}

// A note on identifiers (fids & tags)
//
// identifiers are chosen by the client, not by the server.  Therefore,
// it is important that the performance and behavior of a server does
// *not* change based on the fid or tag a client chooses. This is why
// a map is used; its performance is good, and doesn't change based
// on the input a client chooses (rather, it does not change in a way
// a client can predict).

// A conn receives and sends 9P messages across a single network connection.
// Multiple "sessions" may take place over a single connection. The conn
// struct contains the necessary information to route 9P messages to their
// established sessions.
type conn struct {
	// These wrap the network connection to read and write messages.
	*styxproto.Decoder
	*styxproto.Encoder

	// The Server a connection was spawned from. Contains configuration
	// settings and the authentication function, if any.
	srv *Server

	// The network connection itself. We expose it in the struct so that
	// it is available for transport-based auth and any timeouts we need
	// to implement.
	rwc io.ReadWriteCloser

	// This serves as the parent context for the context attached to all
	// requests.
	cx context.Context

	// While srv.MaxSize holds the *desired* 9P protocol message
	// size, msize will contain the actual maximum negotiated with
	// the client, through a Tversion/Rversion exchange.
	msize int64

	// There is no "session id" in 9P. However, because all fids
	// for a connection must be derived from the fid established
	// in a Tattach call, any message that contains a fid can be
	// traced back to the original Tattach message.
	sessionFid *util.Map

	// Qids for the file tree, added on-demand.
	qidpool *qidpool.Pool

	// used to implement request cancellation when a Tflush
	// message is received.
	pendingReq *util.Map
}

func (c *conn) remoteAddr() net.Addr {
	type hasRemote interface {
		RemoteAddr() net.Addr
	}
	if nc, ok := c.rwc.(hasRemote); ok {
		return nc.RemoteAddr()
	}
	return nil
}

func (c *conn) sessionByFid(fid uint32) (*Session, bool) {
	if v, ok := c.sessionFid.Get(fid); ok {
		return v.(*Session), true
	}
	return nil, false
}

// Close the connection
func (c *conn) close() error {
	// Cancel all pending requests
	c.pendingReq.Do(func(m map[interface{}]interface{}) {
		for tag, cancel := range m {
			cancel.(context.CancelFunc)()
			delete(m, tag)
		}
	})

	// Close all open files and sessions
	c.sessionFid.Do(func(m map[interface{}]interface{}) {
		seen := make(map[*Session]struct{}, len(m))
		for k, v := range m {
			session := v.(*Session)
			if _, ok := seen[session]; !ok {
				seen[session] = struct{}{}
				session.endSession()
			}
			// Should probably let the GC take care of this
			delete(m, k)
		}
	})

	return c.rwc.Close()
}

func newConn(srv *Server, rwc io.ReadWriteCloser) *conn {
	var msize int64 = styxproto.DefaultMaxSize
	if srv.MaxSize > 0 {
		if srv.MaxSize > styxproto.MinBufSize {
			msize = srv.MaxSize
		} else {
			msize = styxproto.MinBufSize
		}
	}
	var enc *styxproto.Encoder
	var dec *styxproto.Decoder
	if srv.TraceLog != nil {
		enc = tracing.Encoder(rwc, func(m styxproto.Msg) {
			srv.TraceLog.Printf("← %03d %s", m.Tag(), m)
		})
		dec = tracing.Decoder(rwc, func(m styxproto.Msg) {
			srv.TraceLog.Printf("→ %03d %s", m.Tag(), m)
		})
	} else {
		enc = styxproto.NewEncoder(rwc)
		dec = styxproto.NewDecoder(rwc)
	}
	return &conn{
		Decoder:    dec,
		Encoder:    enc,
		srv:        srv,
		rwc:        rwc,
		cx:         context.TODO(),
		msize:      msize,
		sessionFid: util.NewMap(),
		pendingReq: util.NewMap(),
		qidpool:    qidpool.New(),
	}
}

func (c *conn) qid(name string, qtype uint8) styxproto.Qid {
	return c.qidpool.Put(name, qtype)
}

// All request contexts must have their cancel functions
// called, to free up resources in the context.
func (c *conn) clearTag(tag uint16) {
	var cancel context.CancelFunc
	if c.pendingReq.Fetch(tag, &cancel) {
		cancel()
		c.pendingReq.Del(tag)
	}
}

// runs in its own goroutine, one per connection.
func (c *conn) serve() {
	defer c.close()

	if !c.acceptTversion() {
		return
	}

	for c.Next() && c.Encoder.Err() == nil {
		if !c.handleMessage(c.Msg()) {
			break
		}
	}
	if err := c.Encoder.Err(); err != nil {
		c.srv.logf("write error: %s", err)
	}
	c.srv.logf("closed connection from %s", c.remoteAddr())
}

func (c *conn) handleMessage(m styxproto.Msg) bool {
	if _, ok := c.pendingReq.Get(m.Tag()); ok {
		c.Rerror(m.Tag(), "%s", errTagInUse)
		return false
	}
	cx, cancel := context.WithCancel(c.cx)
	c.pendingReq.Put(m.Tag(), cancel)

	switch m := m.(type) {
	case styxproto.Tauth:
		return c.handleTauth(cx, m)
	case styxproto.Tattach:
		return c.handleTattach(cx, m)
	case styxproto.Tflush:
		return c.handleTflush(cx, m)
	case fcall:
		return c.handleFcall(cx, m)
	case styxproto.BadMessage:
		c.srv.logf("got bad message from %s: %s", c.remoteAddr(), m.Err)
		c.clearTag(m.Tag())
		c.Rerror(m.Tag(), "bad message: %s", m.Err)
		return true
	default:
		c.Rerror(m.Tag(), "unexpected %T message", m)
		return false
	}
	return true
}

// This is the first thing we do on a new connection. The first
// message a client sends *must* be a Tversion message.
func (c *conn) acceptTversion() bool {
	c.Encoder.MaxSize = c.msize
	c.Decoder.MaxSize = c.msize

	for c.Next() && c.Encoder.Err() == nil {
		tver, ok := c.Msg().(styxproto.Tversion)
		if !ok {
			c.Rerror(tver.Tag(), "need Tversion")
			break
		}
		msize := tver.Msize()
		if msize < styxproto.MinBufSize {
			c.Rerror(tver.Tag(), "buffer too small")
			break
		}
		if msize < c.msize {
			c.msize = msize
			c.Encoder.MaxSize = msize
			c.Decoder.MaxSize = msize
		}
		if !bytes.HasPrefix(tver.Version(), []byte("9P2000")) {
			c.Rversion(uint32(c.msize), "unknown")
			c.Flush()
		} else {
			c.Rversion(uint32(c.msize), "9P2000")
			c.Flush()
			return true
		}
	}
	c.Flush()
	c.srv.logf("%s version negotiation failed", c.remoteAddr())
	return false
}

// NOTE(droyo) consider a scenario where a malicious actor connects
// to the server that repeatedly spams Tauth requests. It can quickly
// use up resources on the server. Consider the following measures:
//
// - rate-limiting Tauth requests
// - Setting a per-connection session limit
// - close connections that have not established a session in N seconds
func (c *conn) handleTauth(cx context.Context, m styxproto.Tauth) bool {
	defer c.Flush()
	if c.srv.Auth == nil {
		c.clearTag(m.Tag())
		c.Rerror(m.Tag(), "%s", errNotSupported)
		return true
	}
	if _, ok := c.sessionFid.Get(m.Afid()); ok {
		c.clearTag(m.Tag())
		c.Rerror(m.Tag(), "fid %x in use", m.Afid())
		return false
	}
	client, server := net.Pipe()
	ch := &Channel{
		Context:         c.cx,
		ReadWriteCloser: server,
	}
	rwc, err := styxfile.New(client)
	if err != nil {
		// This should never happen
		panic(err)
	}
	s := newSession(c, m)
	go func() {
		s.authC <- c.srv.Auth(ch, s.User, s.Access)
		close(s.authC)
	}()

	c.sessionFid.Put(m.Afid(), s)
	s.files.Put(m.Afid(), file{rwc: rwc, auth: true})
	s.IncRef()
	return true
}

func (c *conn) handleTattach(cx context.Context, m styxproto.Tattach) bool {
	defer c.Flush()
	var handler Handler = HandlerFunc(func(s *Session) {
		for s.Next() {
		}
	})
	if c.srv.Handler != nil {
		handler = c.srv.Handler
	}
	var s *Session
	if c.srv.Auth == nil {
		s = newSession(c, m)
	} else {
		// TODO(droyo) when a transport-based authentication scheme
		// is in use, the client should not have to do a Tauth request.
		// We should call the Auth handler if Afid is NOFID, passing it
		// a util.BlackHole.
		if !c.sessionFid.Fetch(s, m.Afid()) {
			c.clearTag(m.Tag())
			c.Rerror(m.Tag(), "invalid afid %x", m.Afid())
			return false
		}
		// From attach(5): The same validated afid may be used for
		// multiple attach messages with the same uname and aname.
		if s.User != string(m.Uname()) || s.Access != string(m.Aname()) {
			c.clearTag(m.Tag())
			c.Rerror(m.Tag(), "afid mismatch for %s on %s", m.Uname(), m.Aname())
			return false
		}
		if err := <-s.authC; err != nil {
			c.clearTag(m.Tag())
			c.Rerror(m.Tag(), "auth failed: %s", err)
			return false
		}
	}
	go func() {
		handler.Serve9P(s)
		s.cleanupHandler()
	}()
	c.sessionFid.Put(m.Fid(), s)
	s.IncRef()
	s.files.Put(m.Fid(), file{name: "/", rwc: nil})
	c.clearTag(m.Tag())
	c.Rattach(m.Tag(), c.qid(".", styxproto.QTDIR))
	return true
}

func (c *conn) handleTflush(cx context.Context, m styxproto.Tflush) bool {
	c.clearTag(m.Oldtag())
	c.clearTag(m.Tag())

	c.Rflush(m.Tag())
	c.Flush()
	return true
}

func (c *conn) handleFcall(cx context.Context, msg fcall) bool {
	s, ok := c.sessionByFid(msg.Fid())
	if !ok {
		c.Rerror(msg.Tag(), "%s", errNoFid)
		c.Flush()
		return false
	}

	file, ok := s.fetchFile(msg.Fid())
	if !ok {
		panic("bug: fid in session map, but no file associated")
		return false
	}

	// NOTE(droyo) on security and anonymous users: On a server with
	// authentication enabled, a client can only ever establish a handle
	// to the auth file.  At this point, we have checked that the fid
	// is valid, so *file can only be an auth file if the user has not
	// completed a Tattach.
	if file.auth {
		// Limit the number of request handlers we have to
		// audit.
		switch msg := msg.(type) {
		case styxproto.Twrite:
		case styxproto.Tread:
		case styxproto.Tstat:
		case styxproto.Tclunk:
		default:
			c.clearTag(msg.Tag())
			c.Rerror(msg.Tag(), "%T not allowed on afid", msg)
			c.Flush()
			return false
		}
	}

	switch msg := msg.(type) {
	case styxproto.Twalk:
		return s.handleTwalk(cx, msg, file)
	case styxproto.Topen:
		return s.handleTopen(cx, msg, file)
	case styxproto.Tcreate:
		return s.handleTcreate(cx, msg, file)
	case styxproto.Tread:
		return s.handleTread(cx, msg, file)
	case styxproto.Twrite:
		return s.handleTwrite(cx, msg, file)
	case styxproto.Tremove:
		return s.handleTremove(cx, msg, file)
	case styxproto.Tstat:
		return s.handleTstat(cx, msg, file)
	case styxproto.Twstat:
		return s.handleTwstat(cx, msg, file)
	case styxproto.Tclunk:
		return s.handleTclunk(cx, msg, file)
	}
	// invalid messages should have been caught
	// in the conn.serve loop, so we should never
	// reach this point.
	panic(fmt.Errorf("unhandled message type %T", msg))
}
<commit_msg>Flush responses to erroneous messages<commit_after>package styx

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"

	"aqwari.net/net/styx/internal/qidpool"
	"aqwari.net/net/styx/internal/styxfile"
	"aqwari.net/net/styx/internal/tracing"
	"aqwari.net/net/styx/internal/util"
	"aqwari.net/net/styx/styxproto"

	"golang.org/x/net/context"
)

var (
	errFidInUse     = errors.New("fid already in use")
	errTagInUse     = errors.New("tag in use")
	errNoFid        = errors.New("no such fid")
	errNotSupported = errors.New("not supported")
)

type fcall interface {
	styxproto.Msg
	Fid() uint32
}

// A note on identifiers (fids & tags)
//
// identifiers are chosen by the client, not by the server.  Therefore,
// it is important that the performance and behavior of a server does
// *not* change based on the fid or tag a client chooses. This is why
// a map is used; its performance is good, and doesn't change based
// on the input a client chooses (rather, it does not change in a way
// a client can predict).

// A conn receives and sends 9P messages across a single network connection.
// Multiple "sessions" may take place over a single connection. The conn
// struct contains the necessary information to route 9P messages to their
// established sessions.
type conn struct {
	// These wrap the network connection to read and write messages.
	*styxproto.Decoder
	*styxproto.Encoder

	// The Server a connection was spawned from. Contains configuration
	// settings and the authentication function, if any.
	srv *Server

	// The network connection itself. We expose it in the struct so that
	// it is available for transport-based auth and any timeouts we need
	// to implement.
	rwc io.ReadWriteCloser

	// This serves as the parent context for the context attached to all
	// requests.
	cx context.Context

	// While srv.MaxSize holds the *desired* 9P protocol message
	// size, msize will contain the actual maximum negotiated with
	// the client, through a Tversion/Rversion exchange.
	msize int64

	// There is no "session id" in 9P. However, because all fids
	// for a connection must be derived from the fid established
	// in a Tattach call, any message that contains a fid can be
	// traced back to the original Tattach message.
	sessionFid *util.Map

	// Qids for the file tree, added on-demand.
	qidpool *qidpool.Pool

	// used to implement request cancellation when a Tflush
	// message is received.
	pendingReq *util.Map
}

func (c *conn) remoteAddr() net.Addr {
	type hasRemote interface {
		RemoteAddr() net.Addr
	}
	if nc, ok := c.rwc.(hasRemote); ok {
		return nc.RemoteAddr()
	}
	return nil
}

func (c *conn) sessionByFid(fid uint32) (*Session, bool) {
	if v, ok := c.sessionFid.Get(fid); ok {
		return v.(*Session), true
	}
	return nil, false
}

// Close the connection
func (c *conn) close() error {
	// Cancel all pending requests
	c.pendingReq.Do(func(m map[interface{}]interface{}) {
		for tag, cancel := range m {
			cancel.(context.CancelFunc)()
			delete(m, tag)
		}
	})

	// Close all open files and sessions
	c.sessionFid.Do(func(m map[interface{}]interface{}) {
		seen := make(map[*Session]struct{}, len(m))
		for k, v := range m {
			session := v.(*Session)
			if _, ok := seen[session]; !ok {
				seen[session] = struct{}{}
				session.endSession()
			}
			// Should probably let the GC take care of this
			delete(m, k)
		}
	})

	return c.rwc.Close()
}

func newConn(srv *Server, rwc io.ReadWriteCloser) *conn {
	var msize int64 = styxproto.DefaultMaxSize
	if srv.MaxSize > 0 {
		if srv.MaxSize > styxproto.MinBufSize {
			msize = srv.MaxSize
		} else {
			msize = styxproto.MinBufSize
		}
	}
	var enc *styxproto.Encoder
	var dec *styxproto.Decoder
	if srv.TraceLog != nil {
		enc = tracing.Encoder(rwc, func(m styxproto.Msg) {
			srv.TraceLog.Printf("← %03d %s", m.Tag(), m)
		})
		dec = tracing.Decoder(rwc, func(m styxproto.Msg) {
			srv.TraceLog.Printf("→ %03d %s", m.Tag(), m)
		})
	} else {
		enc = styxproto.NewEncoder(rwc)
		dec = styxproto.NewDecoder(rwc)
	}
	return &conn{
		Decoder:    dec,
		Encoder:    enc,
		srv:        srv,
		rwc:        rwc,
		cx:         context.TODO(),
		msize:      msize,
		sessionFid: util.NewMap(),
		pendingReq: util.NewMap(),
		qidpool:    qidpool.New(),
	}
}

func (c *conn) qid(name string, qtype uint8) styxproto.Qid {
	return c.qidpool.Put(name, qtype)
}

// All request contexts must have their cancel functions
// called, to free up resources in the context.
func (c *conn) clearTag(tag uint16) {
	var cancel context.CancelFunc
	if c.pendingReq.Fetch(tag, &cancel) {
		cancel()
		c.pendingReq.Del(tag)
	}
}

// runs in its own goroutine, one per connection.
func (c *conn) serve() {
	defer c.close()

	if !c.acceptTversion() {
		return
	}

	for c.Next() && c.Encoder.Err() == nil {
		if !c.handleMessage(c.Msg()) {
			break
		}
	}
	if err := c.Encoder.Err(); err != nil {
		c.srv.logf("write error: %s", err)
	}
	c.srv.logf("closed connection from %s", c.remoteAddr())
}

func (c *conn) handleMessage(m styxproto.Msg) bool {
	if _, ok := c.pendingReq.Get(m.Tag()); ok {
		c.Rerror(m.Tag(), "%s", errTagInUse)
		return false
	}
	cx, cancel := context.WithCancel(c.cx)
	c.pendingReq.Put(m.Tag(), cancel)

	switch m := m.(type) {
	case styxproto.Tauth:
		return c.handleTauth(cx, m)
	case styxproto.Tattach:
		return c.handleTattach(cx, m)
	case styxproto.Tflush:
		return c.handleTflush(cx, m)
	case fcall:
		return c.handleFcall(cx, m)
	case styxproto.BadMessage:
		c.srv.logf("got bad message from %s: %s", c.remoteAddr(), m.Err)
		c.clearTag(m.Tag())
		c.Rerror(m.Tag(), "bad message: %s", m.Err)
		c.Flush()
		return true
	default:
		c.Rerror(m.Tag(), "unexpected %T message", m)
		c.Flush()
		return false
	}
	return true
}

// This is the first thing we do on a new connection. The first
// message a client sends *must* be a Tversion message.
func (c *conn) acceptTversion() bool {
	c.Encoder.MaxSize = c.msize
	c.Decoder.MaxSize = c.msize

	for c.Next() && c.Encoder.Err() == nil {
		tver, ok := c.Msg().(styxproto.Tversion)
		if !ok {
			c.Rerror(tver.Tag(), "need Tversion")
			break
		}
		msize := tver.Msize()
		if msize < styxproto.MinBufSize {
			c.Rerror(tver.Tag(), "buffer too small")
			break
		}
		if msize < c.msize {
			c.msize = msize
			c.Encoder.MaxSize = msize
			c.Decoder.MaxSize = msize
		}
		if !bytes.HasPrefix(tver.Version(), []byte("9P2000")) {
			c.Rversion(uint32(c.msize), "unknown")
			c.Flush()
		} else {
			c.Rversion(uint32(c.msize), "9P2000")
			c.Flush()
			return true
		}
	}
	c.Flush()
	c.srv.logf("%s version negotiation failed", c.remoteAddr())
	return false
}

// NOTE(droyo) consider a scenario where a malicious actor connects
// to the server that repeatedly spams Tauth requests. It can quickly
// use up resources on the server. Consider the following measures:
//
// - rate-limiting Tauth requests
// - Setting a per-connection session limit
// - close connections that have not established a session in N seconds
func (c *conn) handleTauth(cx context.Context, m styxproto.Tauth) bool {
	defer c.Flush()
	if c.srv.Auth == nil {
		c.clearTag(m.Tag())
		c.Rerror(m.Tag(), "%s", errNotSupported)
		return true
	}
	if _, ok := c.sessionFid.Get(m.Afid()); ok {
		c.clearTag(m.Tag())
		c.Rerror(m.Tag(), "fid %x in use", m.Afid())
		return false
	}
	client, server := net.Pipe()
	ch := &Channel{
		Context:         c.cx,
		ReadWriteCloser: server,
	}
	rwc, err := styxfile.New(client)
	if err != nil {
		// This should never happen
		panic(err)
	}
	s := newSession(c, m)
	go func() {
		s.authC <- c.srv.Auth(ch, s.User, s.Access)
		close(s.authC)
	}()

	c.sessionFid.Put(m.Afid(), s)
	s.files.Put(m.Afid(), file{rwc: rwc, auth: true})
	s.IncRef()
	return true
}

func (c *conn) handleTattach(cx context.Context, m styxproto.Tattach) bool {
	defer c.Flush()
	var handler Handler = HandlerFunc(func(s *Session) {
		for s.Next() {
		}
	})
	if c.srv.Handler != nil {
		handler = c.srv.Handler
	}
	var s *Session
	if c.srv.Auth == nil {
		s = newSession(c, m)
	} else {
		// TODO(droyo) when a transport-based authentication scheme
		// is in use, the client should not have to do a Tauth request.
		// We should call the Auth handler if Afid is NOFID, passing it
		// a util.BlackHole.
		if !c.sessionFid.Fetch(s, m.Afid()) {
			c.clearTag(m.Tag())
			c.Rerror(m.Tag(), "invalid afid %x", m.Afid())
			return false
		}
		// From attach(5): The same validated afid may be used for
		// multiple attach messages with the same uname and aname.
		if s.User != string(m.Uname()) || s.Access != string(m.Aname()) {
			c.clearTag(m.Tag())
			c.Rerror(m.Tag(), "afid mismatch for %s on %s", m.Uname(), m.Aname())
			return false
		}
		if err := <-s.authC; err != nil {
			c.clearTag(m.Tag())
			c.Rerror(m.Tag(), "auth failed: %s", err)
			return false
		}
	}
	go func() {
		handler.Serve9P(s)
		s.cleanupHandler()
	}()
	c.sessionFid.Put(m.Fid(), s)
	s.IncRef()
	s.files.Put(m.Fid(), file{name: "/", rwc: nil})
	c.clearTag(m.Tag())
	c.Rattach(m.Tag(), c.qid(".", styxproto.QTDIR))
	return true
}

func (c *conn) handleTflush(cx context.Context, m styxproto.Tflush) bool {
	c.clearTag(m.Oldtag())
	c.clearTag(m.Tag())

	c.Rflush(m.Tag())
	c.Flush()
	return true
}

func (c *conn) handleFcall(cx context.Context, msg fcall) bool {
	s, ok := c.sessionByFid(msg.Fid())
	if !ok {
		c.Rerror(msg.Tag(), "%s", errNoFid)
		c.Flush()
		return false
	}

	file, ok := s.fetchFile(msg.Fid())
	if !ok {
		panic("bug: fid in session map, but no file associated")
		return false
	}

	// NOTE(droyo) on security and anonymous users: On a server with
	// authentication enabled, a client can only ever establish a handle
	// to the auth file.  At this point, we have checked that the fid
	// is valid, so *file can only be an auth file if the user has not
	// completed a Tattach.
	if file.auth {
		// Limit the number of request handlers we have to
		// audit.
		switch msg := msg.(type) {
		case styxproto.Twrite:
		case styxproto.Tread:
		case styxproto.Tstat:
		case styxproto.Tclunk:
		default:
			c.clearTag(msg.Tag())
			c.Rerror(msg.Tag(), "%T not allowed on afid", msg)
			c.Flush()
			return false
		}
	}

	switch msg := msg.(type) {
	case styxproto.Twalk:
		return s.handleTwalk(cx, msg, file)
	case styxproto.Topen:
		return s.handleTopen(cx, msg, file)
	case styxproto.Tcreate:
		return s.handleTcreate(cx, msg, file)
	case styxproto.Tread:
		return s.handleTread(cx, msg, file)
	case styxproto.Twrite:
		return s.handleTwrite(cx, msg, file)
	case styxproto.Tremove:
		return s.handleTremove(cx, msg, file)
	case styxproto.Tstat:
		return s.handleTstat(cx, msg, file)
	case styxproto.Twstat:
		return s.handleTwstat(cx, msg, file)
	case styxproto.Tclunk:
		return s.handleTclunk(cx, msg, file)
	}
	// invalid messages should have been caught
	// in the conn.serve loop, so we should never
	// reach this point.
	panic(fmt.Errorf("unhandled message type %T", msg))
}
<|endoftext|>


<commit_before>package osc

import (
	"context"
	"errors"
	"net"
	"strings"
)

const (
	bufSize = 4096 // Size of read and write buffers.
)

// Common errors.
var (
	ErrNilDispatcher  = errors.New("nil dispatcher")
	ErrPrematureClose = errors.New("server cannot be closed before calling Listen")
)

// Conn defines the methods
type Conn interface {
	net.Conn

	Context() context.Context
	Serve(Dispatcher) error
	Send(Packet) error
	SendTo(net.Addr, Packet) error
}

var invalidAddressRunes = []rune{'*', '?', ',', '[', ']', '{', '}', '#', ' '}

// ValidateAddress returns an error if addr contains
// characters that are disallowed by the OSC spec.
func ValidateAddress(addr string) error {
	for _, chr := range invalidAddressRunes {
		if strings.ContainsRune(addr, chr) {
			return ErrInvalidAddress
		}
	}
	return nil
}
<commit_msg>bump bufSize to 64K<commit_after>package osc

import (
	"context"
	"errors"
	"net"
	"strings"
)

const (
	// bufSize is the size of read and write buffers.
	// SuperCollider synthdef messages can easily have as much as 64K of data.
	bufSize = 65536
)

// Common errors.
var (
	ErrNilDispatcher  = errors.New("nil dispatcher")
	ErrPrematureClose = errors.New("server cannot be closed before calling Listen")
)

// Conn defines the methods
type Conn interface {
	net.Conn

	Context() context.Context
	Serve(Dispatcher) error
	Send(Packet) error
	SendTo(net.Addr, Packet) error
}

var invalidAddressRunes = []rune{'*', '?', ',', '[', ']', '{', '}', '#', ' '}

// ValidateAddress returns an error if addr contains
// characters that are disallowed by the OSC spec.
func ValidateAddress(addr string) error {
	for _, chr := range invalidAddressRunes {
		if strings.ContainsRune(addr, chr) {
			return ErrInvalidAddress
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Supports Windows, Linux, Mac, and Raspberry Pi

package main

import (
	"bytes"
	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/arduino/arduino-create-agent/utilities"
	"github.com/gin-gonic/gin"
	"github.com/googollee/go-socket.io"
)

type connection struct {
	// The websocket connection.
	ws socketio.Socket

	// Buffered channel of outbound messages.
	send     chan []byte
	incoming chan []byte
}

func (c *connection) writer() {
	for message := range c.send {
		err := c.ws.Emit("message", string(message))
		if err != nil {
			break
		}
	}
}

// WsServer overrides socket.io server to set the CORS
type WsServer struct {
	Server *socketio.Server
}

func (s *WsServer) ServeHTTP(c *gin.Context) {
	s.Server.ServeHTTP(c.Writer, c.Request)
}

// Upload contains the data to upload a sketch onto a board
type Upload struct {
	Port        string         `json:"port"`
	Board       string         `json:"board"`
	Rewrite     string         `json:"rewrite"`
	Commandline string         `json:"commandline"`
	Signature   string         `json:"signature"`
	Extra       boardExtraInfo `json:"extra"`
	Hex         []byte         `json:"hex"`
	Filename    string         `json:"filename"`
}

func uploadHandler(c *gin.Context) {
	data := new(Upload)
	c.BindJSON(data)

	log.Printf("%+v", data)

	if data.Port == "" {
		c.String(http.StatusBadRequest, "port is required")
		return
	}

	if data.Board == "" {
		c.String(http.StatusBadRequest, "board is required")
		log.Error("board is required")
		return
	}

	if data.Extra.Network == false {
		if data.Signature == "" {
			c.String(http.StatusBadRequest, "signature is required")
			return
		}

		if data.Commandline == "" {
			c.String(http.StatusBadRequest, "commandline is required for local board")
			return
		}

		err := verifyCommandLine(data.Commandline, data.Signature)

		if err != nil {
			c.String(http.StatusBadRequest, "signature is invalid")
			return
		}
	}

	buffer := bytes.NewBuffer(data.Hex)

	path, err := utilities.SaveFileonTempDir(data.Filename, buffer)
	if err != nil {
		c.String(http.StatusBadRequest, err.Error())
		return
	}

	if data.Rewrite != "" {
		data.Board = data.Rewrite
	}

	go spProgramRW(data.Port, data.Board, path, data.Commandline, data.Extra)

	c.String(http.StatusAccepted, "")
}

func verifyCommandLine(input string, signature string) error {
	sign, _ := hex.DecodeString(signature)
	block, _ := pem.Decode([]byte(*signatureKey))
	if block == nil {
		return errors.New("invalid key")
	}
	key, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return err
	}
	rsaKey := key.(*rsa.PublicKey)
	h := sha256.New()
	h.Write([]byte(input))
	d := h.Sum(nil)
	return rsa.VerifyPKCS1v15(rsaKey, crypto.SHA256, d, sign)
}

func wsHandler() *WsServer {
	server, err := socketio.NewServer(nil)
	if err != nil {
		log.Fatal(err)
	}

	server.On("connection", func(so socketio.Socket) {
		c := &connection{send: make(chan []byte, 256*10), ws: so}
		h.register <- c
		so.On("command", func(message string) {
			h.broadcast <- []byte(message)
		})

		so.On("disconnection", func() {
			h.unregister <- c
		})
		go c.writer()
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Println("error:", err)
	})

	wrapper := WsServer{
		Server: server,
	}

	return &wrapper
}
<commit_msg>Allow attaching additional files in the POST<commit_after>// Supports Windows, Linux, Mac, and Raspberry Pi

package main

import (
	"bytes"
	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"net/http"
	"path"

	log "github.com/Sirupsen/logrus"
	"github.com/arduino/arduino-create-agent/utilities"
	"github.com/gin-gonic/gin"
	"github.com/googollee/go-socket.io"
)

type connection struct {
	// The websocket connection.
	ws socketio.Socket

	// Buffered channel of outbound messages.
	send     chan []byte
	incoming chan []byte
}

func (c *connection) writer() {
	for message := range c.send {
		err := c.ws.Emit("message", string(message))
		if err != nil {
			break
		}
	}
}

// WsServer overrides socket.io server to set the CORS
type WsServer struct {
	Server *socketio.Server
}

func (s *WsServer) ServeHTTP(c *gin.Context) {
	s.Server.ServeHTTP(c.Writer, c.Request)
}

type AdditionalFile struct {
	Hex      []byte `json:"hex"`
	Filename string `json:"filename"`
}

// Upload contains the data to upload a sketch onto a board
type Upload struct {
	Port        string           `json:"port"`
	Board       string           `json:"board"`
	Rewrite     string           `json:"rewrite"`
	Commandline string           `json:"commandline"`
	Signature   string           `json:"signature"`
	Extra       boardExtraInfo   `json:"extra"`
	Hex         []byte           `json:"hex"`
	Filename    string           `json:"filename"`
	ExtraFiles  []AdditionalFile `json:"extrafiles"`
}

func uploadHandler(c *gin.Context) {
	data := new(Upload)
	c.BindJSON(data)

	log.Printf("%+v", data)

	if data.Port == "" {
		c.String(http.StatusBadRequest, "port is required")
		return
	}

	if data.Board == "" {
		c.String(http.StatusBadRequest, "board is required")
		log.Error("board is required")
		return
	}

	if data.Extra.Network == false {
		if data.Signature == "" {
			c.String(http.StatusBadRequest, "signature is required")
			return
		}

		if data.Commandline == "" {
			c.String(http.StatusBadRequest, "commandline is required for local board")
			return
		}

		err := verifyCommandLine(data.Commandline, data.Signature)

		if err != nil {
			c.String(http.StatusBadRequest, "signature is invalid")
			return
		}
	}

	buffer := bytes.NewBuffer(data.Hex)

	filepath, err := utilities.SaveFileonTempDir(data.Filename, buffer)
	if err != nil {
		c.String(http.StatusBadRequest, err.Error())
		return
	}

	for _, extraFile := range data.ExtraFiles {
		ioutil.WriteFile(path.Join(path.Dir(filepath), extraFile.Filename), extraFile.Hex, 0644)
	}

	if data.Rewrite != "" {
		data.Board = data.Rewrite
	}

	go spProgramRW(data.Port, data.Board, filepath, data.Commandline, data.Extra)

	c.String(http.StatusAccepted, "")
}

func verifyCommandLine(input string, signature string) error {
	sign, _ := hex.DecodeString(signature)
	block, _ := pem.Decode([]byte(*signatureKey))
	if block == nil {
		return errors.New("invalid key")
	}
	key, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return err
	}
	rsaKey := key.(*rsa.PublicKey)
	h := sha256.New()
	h.Write([]byte(input))
	d := h.Sum(nil)
	return rsa.VerifyPKCS1v15(rsaKey, crypto.SHA256, d, sign)
}

func wsHandler() *WsServer {
	server, err := socketio.NewServer(nil)
	if err != nil {
		log.Fatal(err)
	}

	server.On("connection", func(so socketio.Socket) {
		c := &connection{send: make(chan []byte, 256*10), ws: so}
		h.register <- c
		so.On("command", func(message string) {
			h.broadcast <- []byte(message)
		})

		so.On("disconnection", func() {
			h.unregister <- c
		})
		go c.writer()
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Println("error:", err)
	})

	wrapper := WsServer{
		Server: server,
	}

	return &wrapper
}
<|endoftext|>