<commit_before>// Package edit implements a command line editor.
package edit

import (
	"fmt"
	"os"
	"syscall"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
	"github.com/elves/elvish/util"
)

var Logger = util.GetLogger("[edit] ")

const (
	lackEOLRune = '\u23ce'
	lackEOL     = "\033[7m" + string(lackEOLRune) + "\033[m"
)

// Editor keeps the status of the line editor.
type Editor struct {
	file   *os.File
	writer *writer
	reader *Reader
	sigs   chan os.Signal
	store  *store.Store
	evaler *eval.Evaler
	cmdSeq int
	ps1    Prompt
	rps1   Prompt
	editorState
}

type editorState struct {
	// States used during ReadLine. Reset at the beginning of ReadLine.
	active                bool
	savedTermios          *sys.Termios
	tokens                []Token
	prompt, rprompt, line string
	dot                   int
	notifications         []string
	tips                  []string

	mode Mode

	insert          insert
	command         command
	completion      completion
	completionLines int
	navigation      navigation
	history         historyState
	historyListing  historyListing
	location        location

	isExternal      map[string]bool
	parseErrorAtEnd bool
	// Used for builtins.
	lastKey    Key
	nextAction action
}

// NewEditor creates an Editor.
func NewEditor(file *os.File, sigs chan os.Signal, ev *eval.Evaler, st *store.Store) *Editor {
	seq := -1
	if st != nil {
		var err error
		seq, err = st.NextCmdSeq()
		if err != nil {
			// TODO(xiaq): Also report the error
			seq = -1
		}
	}

	prompt, rprompt := defaultPrompts()

	ed := &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		sigs:   sigs,
		store:  st,
		evaler: ev,
		cmdSeq: seq,
		ps1:    prompt,
		rps1:   rprompt,
	}
	ev.AddModule("le", makeModule(ed))
	return ed
}

func (ed *Editor) flash() {
	// TODO implement fish-like flash effect
}

func (ed *Editor) addTip(format string, args ...interface{}) {
	ed.tips = append(ed.tips, fmt.Sprintf(format, args...))
}

func (ed *Editor) notify(format string, args ...interface{}) {
	ed.notifications = append(ed.notifications, fmt.Sprintf(format, args...))
}

func (ed *Editor) refresh(fullRefresh bool, tips bool) error {
	// Re-lex the line, unless we are in modeCompletion
	src := ed.line
	if ed.mode.Mode() != modeCompletion {
		n, err := parse.Parse(src)
		ed.parseErrorAtEnd = err != nil && atEnd(err, len(src))
		if err != nil {
			// If all the errors happen at the end, it is liekly complaining about missing texts that will eventually be inserted. Don't show such errors.
			// XXX We may need a more reliable criteria.
			if tips && !ed.parseErrorAtEnd {
				ed.addTip("parser error: %s", err)
			}
		}
		if n == nil {
			ed.tokens = []Token{{ParserError, src, nil, ""}}
		} else {
			ed.tokens = tokenize(src, n)
			_, err := ed.evaler.Compile(n)
			if err != nil {
				if tips && !atEnd(err, len(src)) {
					ed.addTip("compiler error: %s", err)
				}
				if err, ok := err.(*util.PosError); ok {
					p := err.Begin
					for i, token := range ed.tokens {
						if token.Node.Begin() <= p && p < token.Node.End() {
							ed.tokens[i].MoreStyle += styleForCompilerError
							break
						}
					}
				}
			}
		}
		for i, t := range ed.tokens {
			for _, stylist := range stylists {
				ed.tokens[i].MoreStyle += stylist(t.Node, ed)
			}
		}
	}
	return ed.writer.refresh(&ed.editorState, fullRefresh)
}

func atEnd(e error, n int) bool {
	switch e := e.(type) {
	case *util.PosError:
		return e.Begin == n
	case *util.Errors:
		for _, child := range e.Errors {
			if !atEnd(child, n) {
				return false
			}
		}
		return true
	default:
		return false
	}
}

// insertAtDot inserts text at the dot and moves the dot after it.
func (ed *Editor) insertAtDot(text string) {
	ed.line = ed.line[:ed.dot] + text + ed.line[ed.dot:]
	ed.dot += len(text)
}

func setupTerminal(file *os.File) (*sys.Termios, error) {
	fd := int(file.Fd())
	term, err := sys.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetICanon(false)
	term.SetEcho(false)
	term.SetVMin(1)
	term.SetVTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	/*
		err = sys.FlushInput(fd)
		if err != nil {
			return nil, fmt.Errorf("can't flush input: %s", err)
		}
	*/

	return savedTermios, nil
}

// startReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := setupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	_, width := sys.GetWinsize(int(ed.file.Fd()))
	// Turn on autowrap, write lackEOL along with enough padding to fill the
	// whole screen. If the cursor was in the first column, we end up in the
	// same line (just off the line boundary); otherwise we are now in the next
	// line. We now rewind to the first column and erase anything there. The
	// final effect is that a lackEOL gets written if and only if the cursor
	// was not in the first column.
	fmt.Fprintf(ed.file, "\033[?7h%s%*s\r \r", lackEOL, width-WcWidth(lackEOLRune), "")

	// Turn off autowrap. The edito has its own wrapping mechanism. Doing
	// wrapping manually means that when the actual width of some characters
	// are greater than what our wcwidth implementation tells us, characters at
	// the end of that line gets hidden -- compared to pushed to the next line,
	// which is more disastrous.
	ed.file.WriteString("\033[?7l")
	// Turn on SGR-style mouse tracking.
	//ed.file.WriteString("\033[?1000;1006h")
	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(addError func(error)) {
	ed.mode = &ed.insert
	ed.tips = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	addError(ed.refresh(false, false))
	ed.file.WriteString("\n")

	// ed.reader.Stop()
	ed.reader.Quit()

	// Turn on autowrap.
	ed.file.WriteString("\033[?7h")
	// Turn off mouse tracking.
	//ed.file.WriteString("\033[?1000;1006l")

	// restore termios
	err := ed.savedTermios.ApplyToFd(int(ed.file.Fd()))

	if err != nil {
		addError(fmt.Errorf("can't restore terminal attribute: %s", err))
	}
	ed.savedTermios = nil
	ed.editorState = editorState{}
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine() (line string, err error) {
	ed.editorState = editorState{active: true}
	ed.mode = &ed.insert

	isExternalCh := make(chan map[string]bool, 1)
	go getIsExternal(ed.evaler, isExternalCh)

	ed.writer.resetOldBuf()
	go ed.reader.Run()

	e := ed.startReadLine()
	if e != nil {
		return "", e
	}
	defer ed.finishReadLine(func(e error) {
		if e != nil {
			err = util.CatError(err, e)
		}
	})

	fullRefresh := false
MainLoop:
	for {
		ed.prompt = ed.ps1.Call(ed)
		ed.rprompt = ed.rps1.Call(ed)

		err := ed.refresh(fullRefresh, true)
		fullRefresh = false
		if err != nil {
			return "", err
		}

		ed.tips = nil

		select {
		case m := <-isExternalCh:
			ed.isExternal = m
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				ed.editorState = editorState{
					savedTermios: ed.savedTermios,
					isExternal:   ed.isExternal,
				}
				goto MainLoop
			case syscall.SIGWINCH:
				fullRefresh = true
				continue MainLoop
			case syscall.SIGCHLD:
				// ignore
			default:
				ed.addTip("ignored signal %s", sig)
			}
		case err := <-ed.reader.ErrorChan():
			ed.notify("reader error: %s", err.Error())
		case mouse := <-ed.reader.MouseChan():
			ed.addTip("mouse: %+v", mouse)
		case <-ed.reader.CPRChan():
			// Ignore CPR
		case k := <-ed.reader.KeyChan():
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode.Mode()]
			if !ok {
				ed.addTip("No binding for current mode")
				continue
			}

			fn, bound := keyBinding[k]
			if !bound {
				fn = keyBinding[Default]
			}

			ed.lastKey = k
			fn.Call(ed)
			act := ed.nextAction
			ed.nextAction = action{}

			switch act.typ {
			case noAction:
				continue
			case reprocessKey:
				err = ed.refresh(false, true)
				if err != nil {
					return "", err
				}
				goto lookupKey
			case exitReadLine:
				if act.returnErr == nil && act.returnLine != "" {
					ed.appendHistory(act.returnLine)
				}
				return act.returnLine, act.returnErr
			}
		}
	}
}
<commit_msg>Cosmetics.<commit_after>// Package edit implements a command line editor.
package edit

import (
	"fmt"
	"os"
	"syscall"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
	"github.com/elves/elvish/util"
)

var Logger = util.GetLogger("[edit] ")

const (
	lackEOLRune = '\u23ce'
	lackEOL     = "\033[7m" + string(lackEOLRune) + "\033[m"
)

// Editor keeps the status of the line editor.
type Editor struct {
	file   *os.File
	writer *writer
	reader *Reader
	sigs   chan os.Signal
	store  *store.Store
	evaler *eval.Evaler
	cmdSeq int
	ps1    Prompt
	rps1   Prompt
	editorState
}

type editorState struct {
	// States used during ReadLine. Reset at the beginning of ReadLine.
	active       bool
	savedTermios *sys.Termios

	notifications []string
	tips          []string

	tokens  []Token
	prompt  string
	rprompt string
	line    string
	dot     int

	mode Mode

	insert          insert
	command         command
	completion      completion
	completionLines int
	navigation      navigation
	history         historyState
	historyListing  historyListing
	location        location

	isExternal      map[string]bool
	parseErrorAtEnd bool
	// Used for builtins.
	lastKey    Key
	nextAction action
}

// NewEditor creates an Editor.
func NewEditor(file *os.File, sigs chan os.Signal, ev *eval.Evaler, st *store.Store) *Editor {
	seq := -1
	if st != nil {
		var err error
		seq, err = st.NextCmdSeq()
		if err != nil {
			// TODO(xiaq): Also report the error
			seq = -1
		}
	}

	prompt, rprompt := defaultPrompts()

	ed := &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		sigs:   sigs,
		store:  st,
		evaler: ev,
		cmdSeq: seq,
		ps1:    prompt,
		rps1:   rprompt,
	}
	ev.AddModule("le", makeModule(ed))
	return ed
}

func (ed *Editor) flash() {
	// TODO implement fish-like flash effect
}

func (ed *Editor) addTip(format string, args ...interface{}) {
	ed.tips = append(ed.tips, fmt.Sprintf(format, args...))
}

func (ed *Editor) notify(format string, args ...interface{}) {
	ed.notifications = append(ed.notifications, fmt.Sprintf(format, args...))
}

func (ed *Editor) refresh(fullRefresh bool, tips bool) error {
	// Re-lex the line, unless we are in modeCompletion
	src := ed.line
	if ed.mode.Mode() != modeCompletion {
		n, err := parse.Parse(src)
		ed.parseErrorAtEnd = err != nil && atEnd(err, len(src))
		if err != nil {
			// If all the errors happen at the end, it is liekly complaining about missing texts that will eventually be inserted. Don't show such errors.
			// XXX We may need a more reliable criteria.
			if tips && !ed.parseErrorAtEnd {
				ed.addTip("parser error: %s", err)
			}
		}
		if n == nil {
			ed.tokens = []Token{{ParserError, src, nil, ""}}
		} else {
			ed.tokens = tokenize(src, n)
			_, err := ed.evaler.Compile(n)
			if err != nil {
				if tips && !atEnd(err, len(src)) {
					ed.addTip("compiler error: %s", err)
				}
				if err, ok := err.(*util.PosError); ok {
					p := err.Begin
					for i, token := range ed.tokens {
						if token.Node.Begin() <= p && p < token.Node.End() {
							ed.tokens[i].MoreStyle += styleForCompilerError
							break
						}
					}
				}
			}
		}
		for i, t := range ed.tokens {
			for _, stylist := range stylists {
				ed.tokens[i].MoreStyle += stylist(t.Node, ed)
			}
		}
	}
	return ed.writer.refresh(&ed.editorState, fullRefresh)
}

func atEnd(e error, n int) bool {
	switch e := e.(type) {
	case *util.PosError:
		return e.Begin == n
	case *util.Errors:
		for _, child := range e.Errors {
			if !atEnd(child, n) {
				return false
			}
		}
		return true
	default:
		return false
	}
}

// insertAtDot inserts text at the dot and moves the dot after it.
func (ed *Editor) insertAtDot(text string) {
	ed.line = ed.line[:ed.dot] + text + ed.line[ed.dot:]
	ed.dot += len(text)
}

func setupTerminal(file *os.File) (*sys.Termios, error) {
	fd := int(file.Fd())
	term, err := sys.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetICanon(false)
	term.SetEcho(false)
	term.SetVMin(1)
	term.SetVTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	/*
		err = sys.FlushInput(fd)
		if err != nil {
			return nil, fmt.Errorf("can't flush input: %s", err)
		}
	*/

	return savedTermios, nil
}

// startReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := setupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	_, width := sys.GetWinsize(int(ed.file.Fd()))
	// Turn on autowrap, write lackEOL along with enough padding to fill the
	// whole screen. If the cursor was in the first column, we end up in the
	// same line (just off the line boundary); otherwise we are now in the next
	// line. We now rewind to the first column and erase anything there. The
	// final effect is that a lackEOL gets written if and only if the cursor
	// was not in the first column.
	fmt.Fprintf(ed.file, "\033[?7h%s%*s\r \r", lackEOL, width-WcWidth(lackEOLRune), "")

	// Turn off autowrap. The edito has its own wrapping mechanism. Doing
	// wrapping manually means that when the actual width of some characters
	// are greater than what our wcwidth implementation tells us, characters at
	// the end of that line gets hidden -- compared to pushed to the next line,
	// which is more disastrous.
	ed.file.WriteString("\033[?7l")
	// Turn on SGR-style mouse tracking.
	//ed.file.WriteString("\033[?1000;1006h")
	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(addError func(error)) {
	ed.mode = &ed.insert
	ed.tips = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	addError(ed.refresh(false, false))
	ed.file.WriteString("\n")

	// ed.reader.Stop()
	ed.reader.Quit()

	// Turn on autowrap.
	ed.file.WriteString("\033[?7h")
	// Turn off mouse tracking.
	//ed.file.WriteString("\033[?1000;1006l")

	// restore termios
	err := ed.savedTermios.ApplyToFd(int(ed.file.Fd()))

	if err != nil {
		addError(fmt.Errorf("can't restore terminal attribute: %s", err))
	}
	ed.savedTermios = nil
	ed.editorState = editorState{}
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine() (line string, err error) {
	ed.editorState = editorState{active: true}
	ed.mode = &ed.insert

	isExternalCh := make(chan map[string]bool, 1)
	go getIsExternal(ed.evaler, isExternalCh)

	ed.writer.resetOldBuf()
	go ed.reader.Run()

	e := ed.startReadLine()
	if e != nil {
		return "", e
	}
	defer ed.finishReadLine(func(e error) {
		if e != nil {
			err = util.CatError(err, e)
		}
	})

	fullRefresh := false
MainLoop:
	for {
		ed.prompt = ed.ps1.Call(ed)
		ed.rprompt = ed.rps1.Call(ed)

		err := ed.refresh(fullRefresh, true)
		fullRefresh = false
		if err != nil {
			return "", err
		}

		ed.tips = nil

		select {
		case m := <-isExternalCh:
			ed.isExternal = m
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				ed.editorState = editorState{
					savedTermios: ed.savedTermios,
					isExternal:   ed.isExternal,
				}
				goto MainLoop
			case syscall.SIGWINCH:
				fullRefresh = true
				continue MainLoop
			case syscall.SIGCHLD:
				// ignore
			default:
				ed.addTip("ignored signal %s", sig)
			}
		case err := <-ed.reader.ErrorChan():
			ed.notify("reader error: %s", err.Error())
		case mouse := <-ed.reader.MouseChan():
			ed.addTip("mouse: %+v", mouse)
		case <-ed.reader.CPRChan():
			// Ignore CPR
		case k := <-ed.reader.KeyChan():
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode.Mode()]
			if !ok {
				ed.addTip("No binding for current mode")
				continue
			}

			fn, bound := keyBinding[k]
			if !bound {
				fn = keyBinding[Default]
			}

			ed.lastKey = k
			fn.Call(ed)
			act := ed.nextAction
			ed.nextAction = action{}

			switch act.typ {
			case noAction:
				continue
			case reprocessKey:
				err = ed.refresh(false, true)
				if err != nil {
					return "", err
				}
				goto lookupKey
			case exitReadLine:
				if act.returnErr == nil && act.returnLine != "" {
					ed.appendHistory(act.returnLine)
				}
				return act.returnLine, act.returnErr
			}
		}
	}
}
<|endoftext|>


<commit_before>package mppuma

import "testing"

func TestGraphDefinition(t *testing.T) {

	var puma PumaPlugin

	graphdef := puma.GraphDefinition()

	if len(graphdef) != 2 {
		t.Errorf("GraphDefinition: %d should be %d", len(graphdef), 2)
	}
}

func TestGraphDefinitionWithGC(t *testing.T) {

	var puma PumaPlugin
	puma.WithGC = true

	graphdef := puma.GraphDefinition()

	if len(graphdef) != 3 {
		t.Errorf("GraphDefinitionWithGC: %d should be %d", len(graphdef), 3)
	}
}
<commit_msg>Fix test<commit_after>package mppuma

import "testing"

func TestGraphDefinition(t *testing.T) {
	desired := 4

	var puma PumaPlugin

	graphdef := puma.GraphDefinition()

	if len(graphdef) != desired {
		t.Errorf("GraphDefinition: %d should be %d", len(graphdef), desired)
	}
}

func TestGraphDefinitionWithGC(t *testing.T) {
	desired := 8

	var puma PumaPlugin
	puma.WithGC = true

	graphdef := puma.GraphDefinition()

	if len(graphdef) != desired {
		t.Errorf("GraphDefinitionWithGC: %d should be %d", len(graphdef), desired)
	}
}
<|endoftext|>


<commit_before>package lib

import (
	"time"
)

const windows = 2

type RateLimit struct {
	Period time.Duration
	Rate   uint
	toks   chan struct{}
	paused bool
}

func (r *RateLimit) Start() {
	r.paused = false
	if r.toks == nil {
		r.toks = make(chan struct{}, windows*r.Rate)
	}
	go func() {
		for true {
			for i := uint(0); i < r.Rate; i++ {
				r.toks <- struct{}{}
			}
			time.Sleep(r.Period)
			if r.paused {
				break
			}
		}
	}()
}

func (r *RateLimit) Stop() {
	r.paused = true
}

func (r *RateLimit) TryGet() bool {
	select {
	case _ = <-r.toks:
		return true
	default:
		return false
	}
}

func (r *RateLimit) Get() {
	_ = <-r.toks
}
<commit_msg>Switch rate limiter windowsize to 1.<commit_after>package lib

import (
	"time"
)

const windows = 1

type RateLimit struct {
	Period time.Duration
	Rate   uint
	toks   chan struct{}
	paused bool
}

func (r *RateLimit) Start() {
	r.paused = false
	if r.toks == nil {
		r.toks = make(chan struct{}, windows*r.Rate)
	}
	go func() {
		for true {
			for i := uint(0); i < r.Rate; i++ {
				r.toks <- struct{}{}
			}
			time.Sleep(r.Period)
			if r.paused {
				break
			}
		}
	}()
}

func (r *RateLimit) Stop() {
	r.paused = true
}

func (r *RateLimit) TryGet() bool {
	select {
	case _ = <-r.toks:
		return true
	default:
		return false
	}
}

func (r *RateLimit) Get() {
	_ = <-r.toks
}
<|endoftext|>


<commit_before>package xpath
/*
#cgo pkg-config: libxml-2.0
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/parser.h>

void xmlXPathContextSetNode(xmlXPathContext *ctx, xmlNode *new_node) {
	ctx->node = new_node;
}

xmlNode* fetchNode(xmlNodeSet *nodeset, int index) {
  	return nodeset->nodeTab[index];
}
*/
import "C"
import "unsafe"

type XPath struct {
	ContextPtr *C.xmlXPathContext
	ResultPtr  *C.xmlXPathObject
}

func NewXPath(docPtr unsafe.Pointer) (xpath *XPath) {
	if docPtr == nil {
		return
	}
	xpath = &XPath{ContextPtr: C.xmlXPathNewContext((*C.xmlDoc)(docPtr)), ResultPtr: nil}
	return
}

func (xpath *XPath) RegisterNamespace(prefix, href string) bool {
	var prefixPtr unsafe.Pointer = nil
	if len(prefix) > 0 {
		prefixBytes := []byte(prefix)
		prefixPtr = unsafe.Pointer(&prefixBytes[0])
	}

	var hrefPtr unsafe.Pointer = nil
	if len(href) > 0 {
		hrefBytes := []byte(href)
		hrefPtr = unsafe.Pointer(&hrefBytes[0])
	}

	result := C.xmlXPathRegisterNs(xpath.ContextPtr, (*C.xmlChar)(prefixPtr), (*C.xmlChar)(hrefPtr))
	return result == 0
}

func (xpath *XPath) Evaluate(nodePtr unsafe.Pointer, xpathExpr *Expression) (nodes []unsafe.Pointer){
	if nodePtr == nil {
		return
	}
	C.xmlXPathContextSetNode(xpath.ContextPtr, (*C.xmlNode)(nodePtr))
	if xpath.ResultPtr != nil {
		C.xmlXPathFreeObject(xpath.ResultPtr)
	}
	xpath.ResultPtr = C.xmlXPathCompiledEval(xpathExpr.Ptr, xpath.ContextPtr)
	nodesetPtr := xpath.ResultPtr.nodesetval
	if nodesetSize := int(nodesetPtr.nodeNr); nodesetSize > 0 {
		nodes = make([]unsafe.Pointer, nodesetSize)
		for i := 0; i < nodesetSize; i ++ {
			nodes[i] = unsafe.Pointer(C.fetchNode(nodesetPtr, C.int(i)))
		}
	}
	return
}

func (xpath *XPath) Free() {
	if xpath.ContextPtr != nil {
		C.xmlXPathFreeContext(xpath.ContextPtr)
	}
	if xpath.ResultPtr != nil {
		C.xmlXPathFreeObject(xpath.ResultPtr)
	}
}<commit_msg>check for nil xpath result<commit_after>package xpath
/*
#cgo pkg-config: libxml-2.0
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/parser.h>

void xmlXPathContextSetNode(xmlXPathContext *ctx, xmlNode *new_node) {
	ctx->node = new_node;
}

xmlNode* fetchNode(xmlNodeSet *nodeset, int index) {
  	return nodeset->nodeTab[index];
}
*/
import "C"
import "unsafe"

type XPath struct {
	ContextPtr *C.xmlXPathContext
	ResultPtr  *C.xmlXPathObject
}

func NewXPath(docPtr unsafe.Pointer) (xpath *XPath) {
	if docPtr == nil {
		return
	}
	xpath = &XPath{ContextPtr: C.xmlXPathNewContext((*C.xmlDoc)(docPtr)), ResultPtr: nil}
	return
}

func (xpath *XPath) RegisterNamespace(prefix, href string) bool {
	var prefixPtr unsafe.Pointer = nil
	if len(prefix) > 0 {
		prefixBytes := []byte(prefix)
		prefixPtr = unsafe.Pointer(&prefixBytes[0])
	}

	var hrefPtr unsafe.Pointer = nil
	if len(href) > 0 {
		hrefBytes := []byte(href)
		hrefPtr = unsafe.Pointer(&hrefBytes[0])
	}

	result := C.xmlXPathRegisterNs(xpath.ContextPtr, (*C.xmlChar)(prefixPtr), (*C.xmlChar)(hrefPtr))
	return result == 0
}

func (xpath *XPath) Evaluate(nodePtr unsafe.Pointer, xpathExpr *Expression) (nodes []unsafe.Pointer){
	if nodePtr == nil {
		return
	}
	C.xmlXPathContextSetNode(xpath.ContextPtr, (*C.xmlNode)(nodePtr))
	if xpath.ResultPtr != nil {
		C.xmlXPathFreeObject(xpath.ResultPtr)
	}
	xpath.ResultPtr = C.xmlXPathCompiledEval(xpathExpr.Ptr, xpath.ContextPtr)
	if nodesetPtr := xpath.ResultPtr.nodesetval; nodesetPtr != nil {
		if nodesetSize := int(nodesetPtr.nodeNr); nodesetSize > 0 {
			nodes = make([]unsafe.Pointer, nodesetSize)
			for i := 0; i < nodesetSize; i ++ {
				nodes[i] = unsafe.Pointer(C.fetchNode(nodesetPtr, C.int(i)))
			}
		}
	}
	return
}

func (xpath *XPath) Free() {
	if xpath.ContextPtr != nil {
		C.xmlXPathFreeContext(xpath.ContextPtr)
	}
	if xpath.ResultPtr != nil {
		C.xmlXPathFreeObject(xpath.ResultPtr)
	}
}<|endoftext|>


<commit_before>/*
 * Copyright 2020 Dgraph Labs, Inc. and Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package z

import (
	"fmt"
	"math"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"

	"github.com/dustin/go-humanize"
)

// Allocator amortizes the cost of small allocations by allocating memory in bigger chunks.
// Internally it uses z.Calloc to allocate memory. Once allocated, the memory is not moved,
// so it is safe to use the allocated bytes to unsafe cast them to Go struct pointers.
type Allocator struct {
	sync.Mutex
	pageSize int
	curBuf   int
	curIdx   int
	buffers  [][]byte
	size     uint64
	Ref      uint64
	Tag      string
	reused   int

	freelist map[int]uint64 // Key is the size. Value is the first node of that size.
}

// allocs keeps references to all Allocators, so we can safely discard them later.
var allocsMu *sync.Mutex
var allocRef uint64
var allocs map[uint64]*Allocator
var calculatedLog2 []int
var allocatorPool chan *Allocator
var numGets int64

func init() {
	allocsMu = new(sync.Mutex)
	allocs = make(map[uint64]*Allocator)

	// Set up a unique Ref per process.
	rand.Seed(time.Now().UnixNano())
	allocRef = uint64(rand.Int63n(1<<16)) << 48

	calculatedLog2 = make([]int, 1025)
	for i := 1; i <= 1024; i++ {
		calculatedLog2[i] = int(math.Log2(float64(i)))
	}
	allocatorPool = make(chan *Allocator, 8)
	go freeupAllocators()
	// fmt.Printf("Using z.Allocator with starting ref: %x\n", allocRef)
}

func freeupAllocators() {
	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop()

	var last int64
	for range ticker.C {
		gets := atomic.LoadInt64(&numGets)
		if gets != last {
			// Some retrievals were made since the last time. So, let's avoid doing a release.
			last = gets
			continue
		}
		select {
		case alloc := <-allocatorPool:
			alloc.Release()
		default:
		}
	}
}

func GetAllocatorFromPool(sz int) *Allocator {
	atomic.AddInt64(&numGets, 1)
	select {
	case alloc := <-allocatorPool:
		alloc.Reset()
		return alloc
	default:
		return NewAllocator(sz)
	}
}
func ReturnAllocator(a *Allocator) {
	a.TrimTo(400 << 20)

	select {
	case allocatorPool <- a:
		return
	default:
		a.Release()
	}
}

// NewAllocator creates an allocator starting with the given size.
func NewAllocator(sz int) *Allocator {
	ref := atomic.AddUint64(&allocRef, 1)
	a := &Allocator{
		pageSize: sz,
		Ref:      ref,
		freelist: make(map[int]uint64),
	}

	allocsMu.Lock()
	allocs[ref] = a
	allocsMu.Unlock()
	return a
}

func (a *Allocator) Reset() {
	a.curBuf, a.curIdx = 0, 0
	a.freelist = make(map[int]uint64)
}

func PrintAllocators() {
	allocsMu.Lock()
	tags := make(map[string]int)
	var total uint64
	for _, ac := range allocs {
		tags[ac.Tag]++
		total += ac.Allocated()
	}
	for tag, count := range tags {
		fmt.Printf("Allocator Tag: %s Count: %d\n", tag, count)
	}
	fmt.Printf("Total allocators: %d. Total Size: %s\n",
		len(allocs), humanize.IBytes(total))
	allocsMu.Unlock()
}

// AllocatorFrom would return the allocator corresponding to the ref.
func AllocatorFrom(ref uint64) *Allocator {
	allocsMu.Lock()
	a := allocs[ref]
	allocsMu.Unlock()
	return a
}

// Size returns the size of the allocations so far.
func (a *Allocator) Size() uint64 {
	a.Lock()
	defer a.Unlock()

	return a.size
}

func log2(sz int) int {
	if sz < len(calculatedLog2) {
		return calculatedLog2[sz]
	}
	pow := 10
	sz >>= 10
	for sz > 1 {
		sz >>= 1
		pow++
	}
	return pow
}

func (a *Allocator) addToFreelist(b []byte) {
	if len(b) < 32 {
		// Don't do anything.
		return
	}

	l2 := log2(len(b))
	root := a.freelist[l2]
	n := node(b)
	// Length would be the first 8 bytes.
	n.setAt(0, uint64(len(b)))
	// Followed by the pointer to the next byte array.
	n.setAt(8, root)
	a.freelist[l2] = uint64(uintptr(unsafe.Pointer(&b[0])))
}

func (a *Allocator) Return(b []byte) {
	// Turning this off for now.
	// a.Lock()
	// defer a.Unlock()
	// a.addToFreelist(b)
}

func getBuf(p uint64, sz int) []byte {
	return (*[MaxArrayLen]byte)(unsafe.Pointer(uintptr(p)))[:sz:sz]
}

func (a *Allocator) fromFreeList(need int) []byte {
	var last uint64
	span := log2(need)
	n := a.freelist[span]
	for n != 0 {
		curBuf := getBuf(n, 16)
		curNode := node(curBuf)
		sz := int(curNode.uint64(0))
		if sz < need {
			last, n = n, curNode.uint64(8)
			continue
		}
		curBuf = getBuf(n, sz)
		use := curBuf[:need]
		left := curBuf[need:]

		next := node(curBuf).uint64(8)
		if last == 0 {
			a.freelist[span] = next
		} else if last > 0 {
			lastBuf := getBuf(last, 16)
			node(lastBuf).setAt(8, next)
		}

		a.addToFreelist(left)
		ZeroOut(use, 0, 16) // just zero out the initial 16 bytes.
		return use
	}
	return nil
}

func (a *Allocator) Allocated() uint64 {
	a.Lock()
	defer a.Unlock()
	var alloc int
	for _, b := range a.buffers {
		alloc += cap(b)
	}
	return uint64(alloc)
}

func (a *Allocator) TrimTo(max int) {
	a.Lock()
	defer a.Unlock()

	var alloc int
	idx := -1
	for i, b := range a.buffers {
		alloc += len(b)
		if alloc >= max {
			idx = i
			break
		}
	}
	if idx == -1 {
		return
	}
	for _, b := range a.buffers[idx:] {
		fmt.Printf("Trim: Removing buffer of size: %d\n", len(b))
		Free(b)
	}
	a.buffers = a.buffers[:idx]
	alloc = 0
	for _, b := range a.buffers {
		alloc += len(b)
	}
	fmt.Printf("Trim: Final size: %d\n", alloc)
}

// Release would release the memory back. Remember to make this call to avoid memory leaks.
func (a *Allocator) Release() {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	var alloc int
	for _, b := range a.buffers {
		alloc += len(b)
		Free(b)
	}
	// ratio := float64(a.reused) / float64(alloc)
	// if ratio == 0.0 || ratio > 0.5 {
	fmt.Printf("Releasing Allocator Size: %s\n",
		humanize.IBytes(uint64(alloc)))
	// }

	allocsMu.Lock()
	delete(allocs, a.Ref)
	allocsMu.Unlock()
}

const maxAlloc = 1 << 30

func (a *Allocator) MaxAlloc() int {
	return maxAlloc
}

const nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1

func (a *Allocator) AllocateAligned(sz int) []byte {
	tsz := sz + nodeAlign
	out := a.Allocate(tsz)
	// TODO: We should align based on out's address.
	aligned := (a.curIdx - tsz + nodeAlign) & ^nodeAlign

	start := tsz - (a.curIdx - aligned)
	return out[start : start+sz]
}

func (a *Allocator) Copy(buf []byte) []byte {
	if a == nil {
		return append([]byte{}, buf...)
	}
	out := a.Allocate(len(buf))
	copy(out, buf)
	return out
}

func (a *Allocator) addBufferWithMinSize(sz int) {
	for {
		a.pageSize *= 2 // Do multiply by 2 here.
		if a.pageSize >= sz {
			break
		}
	}
	if a.pageSize > maxAlloc {
		a.pageSize = maxAlloc
	}

	buf := Calloc(a.pageSize)
	a.buffers = append(a.buffers, buf)
}

// Allocate would allocate a byte slice of length sz. It is safe to use this memory to unsafe cast
// to Go structs.
func (a *Allocator) Allocate(sz int) []byte {
	if a == nil {
		return make([]byte, sz)
	}
	a.Lock()
	defer a.Unlock()

	if len(a.buffers) == 0 {
		buf := Calloc(a.pageSize)
		a.buffers = append(a.buffers, buf)
	}
	if sz >= maxAlloc {
		panic(fmt.Sprintf("Allocate call exceeds max allocation possible."+
			" Requested: %d. Max Allowed: %d\n", sz, maxAlloc))
	}
	// Turning this off for now. This slows down allocations somewhat. Even though it shows a 50%
	// reuse ratio, we get a better bang for the buck by just reusing an entire Allocator for the
	// next cycle.
	//
	// if out := a.fromFreeList(sz); out != nil {
	// 	a.reused += len(out)
	// 	return out
	// }
	cb := a.buffers[a.curBuf]
	for len(cb) < a.curIdx+sz {
		a.curBuf++
		a.curIdx = 0
		if a.curBuf == len(a.buffers) {
			a.addBufferWithMinSize(sz)
		}
		cb = a.buffers[a.curBuf]
	}

	slice := cb[a.curIdx : a.curIdx+sz]
	a.curIdx += sz
	a.size += uint64(sz)
	return slice
}
<commit_msg>Fix infinite loop in allocator (#214)<commit_after>/*
 * Copyright 2020 Dgraph Labs, Inc. and Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package z

import (
	"fmt"
	"math"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"

	"github.com/dustin/go-humanize"
)

// Allocator amortizes the cost of small allocations by allocating memory in bigger chunks.
// Internally it uses z.Calloc to allocate memory. Once allocated, the memory is not moved,
// so it is safe to use the allocated bytes to unsafe cast them to Go struct pointers.
type Allocator struct {
	sync.Mutex
	pageSize int
	curBuf   int
	curIdx   int
	buffers  [][]byte
	size     uint64
	Ref      uint64
	Tag      string
	reused   int

	freelist map[int]uint64 // Key is the size. Value is the first node of that size.
}

// allocs keeps references to all Allocators, so we can safely discard them later.
var allocsMu *sync.Mutex
var allocRef uint64
var allocs map[uint64]*Allocator
var calculatedLog2 []int
var allocatorPool chan *Allocator
var numGets int64

func init() {
	allocsMu = new(sync.Mutex)
	allocs = make(map[uint64]*Allocator)

	// Set up a unique Ref per process.
	rand.Seed(time.Now().UnixNano())
	allocRef = uint64(rand.Int63n(1<<16)) << 48

	calculatedLog2 = make([]int, 1025)
	for i := 1; i <= 1024; i++ {
		calculatedLog2[i] = int(math.Log2(float64(i)))
	}
	allocatorPool = make(chan *Allocator, 8)
	go freeupAllocators()
	// fmt.Printf("Using z.Allocator with starting ref: %x\n", allocRef)
}

func freeupAllocators() {
	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop()

	var last int64
	for range ticker.C {
		gets := atomic.LoadInt64(&numGets)
		if gets != last {
			// Some retrievals were made since the last time. So, let's avoid doing a release.
			last = gets
			continue
		}
		select {
		case alloc := <-allocatorPool:
			alloc.Release()
		default:
		}
	}
}

func GetAllocatorFromPool(sz int) *Allocator {
	atomic.AddInt64(&numGets, 1)
	select {
	case alloc := <-allocatorPool:
		alloc.Reset()
		return alloc
	default:
		return NewAllocator(sz)
	}
}
func ReturnAllocator(a *Allocator) {
	a.TrimTo(400 << 20)

	select {
	case allocatorPool <- a:
		return
	default:
		a.Release()
	}
}

// NewAllocator creates an allocator starting with the given size.
func NewAllocator(sz int) *Allocator {
	ref := atomic.AddUint64(&allocRef, 1)
	// We should not allow a zero sized page because addBufferWithMinSize
	// will run into an infinite loop trying to double the pagesize.
	if sz == 0 {
		sz = smallBufferSize
	}
	a := &Allocator{
		pageSize: sz,
		Ref:      ref,
		freelist: make(map[int]uint64),
	}

	allocsMu.Lock()
	allocs[ref] = a
	allocsMu.Unlock()
	return a
}

func (a *Allocator) Reset() {
	a.curBuf, a.curIdx = 0, 0
	a.freelist = make(map[int]uint64)
}

func PrintAllocators() {
	allocsMu.Lock()
	tags := make(map[string]int)
	var total uint64
	for _, ac := range allocs {
		tags[ac.Tag]++
		total += ac.Allocated()
	}
	for tag, count := range tags {
		fmt.Printf("Allocator Tag: %s Count: %d\n", tag, count)
	}
	fmt.Printf("Total allocators: %d. Total Size: %s\n",
		len(allocs), humanize.IBytes(total))
	allocsMu.Unlock()
}

// AllocatorFrom would return the allocator corresponding to the ref.
func AllocatorFrom(ref uint64) *Allocator {
	allocsMu.Lock()
	a := allocs[ref]
	allocsMu.Unlock()
	return a
}

// Size returns the size of the allocations so far.
func (a *Allocator) Size() uint64 {
	a.Lock()
	defer a.Unlock()

	return a.size
}

func log2(sz int) int {
	if sz < len(calculatedLog2) {
		return calculatedLog2[sz]
	}
	pow := 10
	sz >>= 10
	for sz > 1 {
		sz >>= 1
		pow++
	}
	return pow
}

func (a *Allocator) addToFreelist(b []byte) {
	if len(b) < 32 {
		// Don't do anything.
		return
	}

	l2 := log2(len(b))
	root := a.freelist[l2]
	n := node(b)
	// Length would be the first 8 bytes.
	n.setAt(0, uint64(len(b)))
	// Followed by the pointer to the next byte array.
	n.setAt(8, root)
	a.freelist[l2] = uint64(uintptr(unsafe.Pointer(&b[0])))
}

func (a *Allocator) Return(b []byte) {
	// Turning this off for now.
	// a.Lock()
	// defer a.Unlock()
	// a.addToFreelist(b)
}

func getBuf(p uint64, sz int) []byte {
	return (*[MaxArrayLen]byte)(unsafe.Pointer(uintptr(p)))[:sz:sz]
}

func (a *Allocator) fromFreeList(need int) []byte {
	var last uint64
	span := log2(need)
	n := a.freelist[span]
	for n != 0 {
		curBuf := getBuf(n, 16)
		curNode := node(curBuf)
		sz := int(curNode.uint64(0))
		if sz < need {
			last, n = n, curNode.uint64(8)
			continue
		}
		curBuf = getBuf(n, sz)
		use := curBuf[:need]
		left := curBuf[need:]

		next := node(curBuf).uint64(8)
		if last == 0 {
			a.freelist[span] = next
		} else if last > 0 {
			lastBuf := getBuf(last, 16)
			node(lastBuf).setAt(8, next)
		}

		a.addToFreelist(left)
		ZeroOut(use, 0, 16) // just zero out the initial 16 bytes.
		return use
	}
	return nil
}

func (a *Allocator) Allocated() uint64 {
	a.Lock()
	defer a.Unlock()
	var alloc int
	for _, b := range a.buffers {
		alloc += cap(b)
	}
	return uint64(alloc)
}

func (a *Allocator) TrimTo(max int) {
	a.Lock()
	defer a.Unlock()

	var alloc int
	idx := -1
	for i, b := range a.buffers {
		alloc += len(b)
		if alloc >= max {
			idx = i
			break
		}
	}
	if idx == -1 {
		return
	}
	for _, b := range a.buffers[idx:] {
		fmt.Printf("Trim: Removing buffer of size: %d\n", len(b))
		Free(b)
	}
	a.buffers = a.buffers[:idx]
	alloc = 0
	for _, b := range a.buffers {
		alloc += len(b)
	}
	fmt.Printf("Trim: Final size: %d\n", alloc)
}

// Release would release the memory back. Remember to make this call to avoid memory leaks.
func (a *Allocator) Release() {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	var alloc int
	for _, b := range a.buffers {
		alloc += len(b)
		Free(b)
	}
	// ratio := float64(a.reused) / float64(alloc)
	// if ratio == 0.0 || ratio > 0.5 {
	fmt.Printf("Releasing Allocator Size: %s\n",
		humanize.IBytes(uint64(alloc)))
	// }

	allocsMu.Lock()
	delete(allocs, a.Ref)
	allocsMu.Unlock()
}

const maxAlloc = 1 << 30

func (a *Allocator) MaxAlloc() int {
	return maxAlloc
}

const nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1

func (a *Allocator) AllocateAligned(sz int) []byte {
	tsz := sz + nodeAlign
	out := a.Allocate(tsz)
	// TODO: We should align based on out's address.
	aligned := (a.curIdx - tsz + nodeAlign) & ^nodeAlign

	start := tsz - (a.curIdx - aligned)
	return out[start : start+sz]
}

func (a *Allocator) Copy(buf []byte) []byte {
	if a == nil {
		return append([]byte{}, buf...)
	}
	out := a.Allocate(len(buf))
	copy(out, buf)
	return out
}

func (a *Allocator) addBufferWithMinSize(sz int) {
	for {
		a.pageSize *= 2 // Do multiply by 2 here.
		if a.pageSize >= sz {
			break
		}
	}
	if a.pageSize > maxAlloc {
		a.pageSize = maxAlloc
	}

	buf := Calloc(a.pageSize)
	a.buffers = append(a.buffers, buf)
}

// Allocate would allocate a byte slice of length sz. It is safe to use this memory to unsafe cast
// to Go structs.
func (a *Allocator) Allocate(sz int) []byte {
	if a == nil {
		return make([]byte, sz)
	}
	a.Lock()
	defer a.Unlock()

	if len(a.buffers) == 0 {
		buf := Calloc(a.pageSize)
		a.buffers = append(a.buffers, buf)
	}
	if sz >= maxAlloc {
		panic(fmt.Sprintf("Allocate call exceeds max allocation possible."+
			" Requested: %d. Max Allowed: %d\n", sz, maxAlloc))
	}
	// Turning this off for now. This slows down allocations somewhat. Even though it shows a 50%
	// reuse ratio, we get a better bang for the buck by just reusing an entire Allocator for the
	// next cycle.
	//
	// if out := a.fromFreeList(sz); out != nil {
	// 	a.reused += len(out)
	// 	return out
	// }
	cb := a.buffers[a.curBuf]
	for len(cb) < a.curIdx+sz {
		a.curBuf++
		a.curIdx = 0
		if a.curBuf == len(a.buffers) {
			a.addBufferWithMinSize(sz)
		}
		cb = a.buffers[a.curBuf]
	}

	slice := cb[a.curIdx : a.curIdx+sz]
	a.curIdx += sz
	a.size += uint64(sz)
	return slice
}
<|endoftext|>


<commit_before>package setup

import (
	"github.com/mholt/caddy/middleware/fastcgi"
	"testing"
)

func TestFastCGI(t *testing.T) {

	c := NewTestController(`fastcgi / 127.0.0.1:9000`)

	mid, err := FastCGI(c)

	if err != nil {
		t.Errorf("Expected no errors, got: %v", err)
	}

	if mid == nil {
		t.Fatal("Expected middleware, was nil instead")
	}

	handler := mid(EmptyNext)
	myHandler, ok := handler.(fastcgi.Handler)

	if !ok {
		t.Fatalf("Expected handler to be type , got: %#v", handler)
	}

	if myHandler.Rules[0].Path != "/" {
		t.Errorf("Expected / as the Path")
	}
	if myHandler.Rules[0].Address != "127.0.0.1:9000" {
		t.Errorf("Expected 127.0.0.1:9000 as the Address")
	}

}
<commit_msg>Complete Test set  For config/setup/fastcgi.go<commit_after>package setup

import (
	"fmt"
	"github.com/mholt/caddy/middleware/fastcgi"
	"testing"
)

func TestFastCGI(t *testing.T) {

	c := NewTestController(`fastcgi / 127.0.0.1:9000`)

	mid, err := FastCGI(c)

	if err != nil {
		t.Errorf("Expected no errors, got: %v", err)
	}

	if mid == nil {
		t.Fatal("Expected middleware, was nil instead")
	}

	handler := mid(EmptyNext)
	myHandler, ok := handler.(fastcgi.Handler)

	if !ok {
		t.Fatalf("Expected handler to be type , got: %#v", handler)
	}

	if myHandler.Rules[0].Path != "/" {
		t.Errorf("Expected / as the Path")
	}
	if myHandler.Rules[0].Address != "127.0.0.1:9000" {
		t.Errorf("Expected 127.0.0.1:9000 as the Address")
	}

}

func TestFastcgiParse(t *testing.T) {
	tests := []struct {
		inputFastcgiConfig    string
		shouldErr             bool
		expectedFastcgiConfig []fastcgi.Rule
	}{

		{`fastcgi /blog 127.0.0.1:9000 php`,
			false, []fastcgi.Rule{{
				Path:       "/blog",
				Address:    "127.0.0.1:9000",
				Ext:        ".php",
				SplitPath:  ".php",
				IndexFiles: []string{"index.php"},
			}}},
	}
	for i, test := range tests {
		c := NewTestController(test.inputFastcgiConfig)
		actualFastcgiConfigs, err := fastcgiParse(c)

		if err == nil && test.shouldErr {
			t.Errorf("Test %d didn't error, but it should have", i)
		} else if err != nil && !test.shouldErr {
			t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
		}
		if len(actualFastcgiConfigs) != len(test.expectedFastcgiConfig) {
			t.Fatalf("Test %d expected %d no of FastCGI configs, but got %d ",
				i, len(test.expectedFastcgiConfig), len(actualFastcgiConfigs))
		}
		for j, actualFastcgiConfig := range actualFastcgiConfigs {

			if actualFastcgiConfig.Path != test.expectedFastcgiConfig[j].Path {
				t.Errorf("Test %d expected %dth FastCGI Path to be  %s  , but got %s",
					i, j, test.expectedFastcgiConfig[j].Path, actualFastcgiConfig.Path)
			}

			if actualFastcgiConfig.Address != test.expectedFastcgiConfig[j].Address {
				t.Errorf("Test %d expected %dth FastCGI Address to be  %s  , but got %s",
					i, j, test.expectedFastcgiConfig[j].Address, actualFastcgiConfig.Address)
			}

			if actualFastcgiConfig.Ext != test.expectedFastcgiConfig[j].Ext {
				t.Errorf("Test %d expected %dth FastCGI Ext to be  %s  , but got %s",
					i, j, test.expectedFastcgiConfig[j].Ext, actualFastcgiConfig.Ext)
			}

			if actualFastcgiConfig.SplitPath != test.expectedFastcgiConfig[j].SplitPath {
				t.Errorf("Test %d expected %dth FastCGI SplitPath to be  %s  , but got %s",
					i, j, test.expectedFastcgiConfig[j].SplitPath, actualFastcgiConfig.SplitPath)
			}

			if fmt.Sprint(actualFastcgiConfig.IndexFiles) != fmt.Sprint(test.expectedFastcgiConfig[j].IndexFiles) {
				t.Errorf("Test %d expected %dth FastCGI IndexFiles to be  %s  , but got %s",
					i, j, test.expectedFastcgiConfig[j].IndexFiles, actualFastcgiConfig.IndexFiles)
			}
		}
	}

}
<|endoftext|>


<commit_before>package csv

import (
	"bytes"
	"io"
	"reflect"
	"testing"
	"time"
)

func TestMarshalNested(t *testing.T) {
	type Nested struct {
		V1 string `csv:"v1"`
		A1 int    `csv:"-"`
		A2 int
		S1 struct {
			V2 string `csv:"v2"`
			S2 struct {
				V3 int `csv:"v3"`
			}
		}
	}

	var v Nested
	v.V1 = "a"
	v.S1.V2 = "b"
	v.S1.S2.V3 = 1
	buf, err := Marshal(v)
	if err != nil {
		t.Fatal(err)
	}
	expected := "a,b,1\n"
	actual := string(buf)
	if actual != expected {
		t.Fatalf("expected %s, got %s", expected, actual)
	}
}

type iter struct {
	a     reflect.Value
	i     int
	level int
}

func newIter(v reflect.Value, path [][]string, level int) *iter {
	return &iter{
		a:     findFieldByPath(v, path[level]),
		level: level,
	}
}

func (it *iter) New() reflect.Value {
	return reflect.New(it.a.Type().Elem()).Elem()
}

func (it *iter) Next(v reflect.Value) bool {
	if it.i >= it.a.Len() {
		return false
	}
	v.Set(it.a.Index(it.i))
	it.i++
	return it.i <= it.a.Len()
}

func findFieldByPath(v reflect.Value, path []string) reflect.Value {
	for _, name := range path {
		v = v.FieldByName(name)
	}
	return v
}

func unmarshal(v reflect.Value, delimiter rune, tag string) ([]byte, error) {
	w := new(bytes.Buffer)
	enc := Encoder{w: w, Delimiter: ',', Tag: "csv2"}
	if err := enc.encode(v); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}

func TestMarshalSlice(t *testing.T) {
	type (
		Leaf struct {
			V1 int `csv2:"v1"`
			V2 int `csv2:"v2"`
		}
		Level2 struct {
			V3   int `csv2:"v3"`
			Leaf []Leaf
			V4   int `csv2:"v4"`
		}
		Level1 struct {
			Level2 []Level2
		}
		Struct struct {
			V1     string `csv:"v1" csv2:"v1"`
			Level1 Level1 `csv:"-"`
			V3     string `csv:"v3" csv2:"v3"`
			V4     string `csv:"v4"`
		}
	)
	st := Struct{
		V1: "a",
		Level1: Level1{[]Level2{
			{1, []Leaf{{3, 4}, {5, 6}}, 2},
		}},
		V3: "b",
		V4: "c",
	}

	path := [][]string{[]string{"Level1", "Level2"}, []string{"Leaf"}}
	w := new(bytes.Buffer)
	if err := expand(w, reflect.ValueOf(st), path); err != nil {
		t.Fatal(err)
	}
	actual := w.String()

	expected := "a,b,1,2,3,4\na,b,1,2,5,6\n"
	if actual != expected {
		t.Fatalf("expected %s, got %s", expected, actual)
	}

}

func expand(w io.Writer, v reflect.Value, path [][]string) error {
	var buf [][]byte
	fields, err := unmarshal(v, ',', "csv2")
	if err != nil {
		return err
	}
	its := []*iter{newIter(v, path, 0)}
	buf = append(buf, fields)
	for {
		it := its[len(its)-1]
		v = it.New()
		if !it.Next(v) {
			buf = buf[:len(buf)-1]
			its = its[:len(its)-1]
			break
		}
		fields, err := unmarshal(v, ',', "csv2")
		if err != nil {
			return err
		}
		if it.level+1 < len(path) {
			its = append(its, newIter(v, path, it.level+1))
			buf = append(buf, fields)
		} else {
			buf = append(buf, fields)
			if _, err := w.Write(append(bytes.Join(buf, []byte{','}), '\n')); err != nil {
				return err
			}
			buf = buf[:len(buf)-1]
		}
	}
	return nil
}

type Types struct {
	V1 string    `csv:"v1"`
	V2 int       `csv:"v2"`
	V3 float64   `csv:"v3"`
	V4 time.Time `csv:"v4"`
}
<commit_msg>refactor<commit_after>package csv

import (
	"bytes"
	"io"
	"reflect"
	"testing"
	"time"
)

func TestMarshalNested(t *testing.T) {
	type Nested struct {
		V1 string `csv:"v1"`
		A1 int    `csv:"-"`
		A2 int
		S1 struct {
			V2 string `csv:"v2"`
			S2 struct {
				V3 int `csv:"v3"`
			}
		}
	}

	var v Nested
	v.V1 = "a"
	v.S1.V2 = "b"
	v.S1.S2.V3 = 1
	buf, err := Marshal(v)
	if err != nil {
		t.Fatal(err)
	}
	expected := "a,b,1\n"
	actual := string(buf)
	if actual != expected {
		t.Fatalf("expected %s, got %s", expected, actual)
	}
}

type iter struct {
	a     reflect.Value
	i     int
	level int
}

func newIter(v reflect.Value, path [][]string, level int) *iter {
	return &iter{
		a:     findFieldByPath(v, path[level]),
		level: level,
	}
}

func (it *iter) New() reflect.Value {
	return reflect.New(it.a.Type().Elem()).Elem()
}

func (it *iter) Next(v reflect.Value) bool {
	if it.i >= it.a.Len() {
		return false
	}
	v.Set(it.a.Index(it.i))
	it.i++
	return it.i <= it.a.Len()
}

func findFieldByPath(v reflect.Value, path []string) reflect.Value {
	for _, name := range path {
		v = v.FieldByName(name)
	}
	return v
}

func unmarshal(v reflect.Value, delimiter rune, tag string) ([]byte, error) {
	w := new(bytes.Buffer)
	enc := Encoder{w: w, Delimiter: ',', Tag: "csv2"}
	if err := enc.encode(v); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}

func TestMarshalSlice(t *testing.T) {
	type (
		Leaf struct {
			V1 int `csv2:"v1"`
			V2 int `csv2:"v2"`
		}
		Level2 struct {
			V3   int `csv2:"v3"`
			Leaf []Leaf
			V4   int `csv2:"v4"`
		}
		Level1 struct {
			Level2 []Level2
		}
		Struct struct {
			V1     string `csv:"v1" csv2:"v1"`
			Level1 Level1 `csv:"-"`
			V3     string `csv:"v3" csv2:"v3"`
			V4     string `csv:"v4"`
		}
	)
	st := Struct{
		V1: "a",
		Level1: Level1{[]Level2{
			{1, []Leaf{{3, 4}, {5, 6}}, 2},
		}},
		V3: "b",
		V4: "c",
	}

	path := [][]string{[]string{"Level1", "Level2"}, []string{"Leaf"}}
	w := new(bytes.Buffer)
	if err := expand(w, reflect.ValueOf(st), path); err != nil {
		t.Fatal(err)
	}
	actual := w.String()

	expected := "a,b,1,2,3,4\na,b,1,2,5,6\n"
	if actual != expected {
		t.Fatalf("expected %s, got %s", expected, actual)
	}

}

func expand(w io.Writer, v reflect.Value, path [][]string) error {
	fields, err := unmarshal(v, ',', "csv2")
	if err != nil {
		return err
	}
	its := []*iter{newIter(v, path, 0)}
	buf := [][]byte{fields}
	for {
		it := its[len(its)-1]
		v = it.New()
		if !it.Next(v) {
			buf = buf[:len(buf)-1]
			its = its[:len(its)-1]
			break
		}
		fields, err := unmarshal(v, ',', "csv2")
		if err != nil {
			return err
		}
		if it.level+1 < len(path) {
			its = append(its, newIter(v, path, it.level+1))
			buf = append(buf, fields)
		} else {
			buf = append(buf, fields)
			if _, err := w.Write(append(bytes.Join(buf, []byte{','}), '\n')); err != nil {
				return err
			}
			buf = buf[:len(buf)-1]
		}
	}
	return nil
}

type Types struct {
	V1 string    `csv:"v1"`
	V2 int       `csv:"v2"`
	V3 float64   `csv:"v3"`
	V4 time.Time `csv:"v4"`
}
<|endoftext|>


<commit_before>package gitlab

import "fmt"

// EpicIssuesService handles communication with the epic issue related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_issues.html
type EpicIssuesService struct {
	client *Client
}

// EpicIssueAssignment contains both the Epic and Issue objects returned from
// Gitlab w/ the assignment ID
type EpicIssueAssignment struct {
	ID    int    `json:"id"`
	Epic  *Epic  `json:"epic"`
	Issue *Issue `json:"issue"`
}

// ListEpicIssues get a list of epic issues.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#list-issues-for-an-epic
func (s *EpicIssuesService) ListEpicIssues(gid interface{}, epic int, opt *ListOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var issues []*Issue
	resp, err := s.client.Do(req, &issues)
	if err != nil {
		return nil, resp, err
	}

	return issues, resp, err
}

// AssignEpicIssue assigns an existing issue to an Epic.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#assign-an-issue-to-the-epic
func (s *EpicIssuesService) AssignEpicIssue(gid interface{}, epic, issue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}

	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, issue)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var a *EpicIssueAssignment

	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, err
}

// RemoveEpicIssue removes an issue from an Epic.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#remove-an-issue-from-the-epic
func (s *EpicIssuesService) RemoveEpicIssue(gid interface{}, epic int, epicIssue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}

	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, epicIssue)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var a *EpicIssueAssignment

	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, err
}

// UpdateEpicIsssueAssignmentOptions describes options to move issues within an epic
type UpdateEpicIsssueAssignmentOptions struct {
	*ListOptions
	MoveBeforeID int `json:"move_before_id"`
	MoveAfterID  int `json:"move_after_id"`
}

// UpdateEpicIssueAssignment moves an issue before or after another issue in an
// epic issue list.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#update-epic---issue-association
func (s *EpicIssuesService) UpdateEpicIssueAssignment(gid interface{}, epic int, epicIssue int, opt *UpdateEpicIsssueAssignmentOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, epicIssue)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var issues []*Issue
	resp, err := s.client.Do(req, &issues)
	if err != nil {
		return nil, resp, err
	}

	return issues, resp, err
}
<commit_msg>remove excess newline<commit_after>package gitlab

import "fmt"

// EpicIssuesService handles communication with the epic issue related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_issues.html
type EpicIssuesService struct {
	client *Client
}

// EpicIssueAssignment contains both the Epic and Issue objects returned from
// Gitlab w/ the assignment ID
type EpicIssueAssignment struct {
	ID    int    `json:"id"`
	Epic  *Epic  `json:"epic"`
	Issue *Issue `json:"issue"`
}

// ListEpicIssues get a list of epic issues.
//
// Gitlab API docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#list-issues-for-an-epic
func (s *EpicIssuesService) ListEpicIssues(gid interface{}, epic int, opt *ListOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var issues []*Issue
	resp, err := s.client.Do(req, &issues)
	if err != nil {
		return nil, resp, err
	}

	return issues, resp, err
}

// AssignEpicIssue assigns an existing issue to an Epic.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#assign-an-issue-to-the-epic
func (s *EpicIssuesService) AssignEpicIssue(gid interface{}, epic, issue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, issue)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var a *EpicIssueAssignment

	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, err
}

// RemoveEpicIssue removes an issue from an Epic.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#remove-an-issue-from-the-epic
func (s *EpicIssuesService) RemoveEpicIssue(gid interface{}, epic int, epicIssue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}

	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, epicIssue)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var a *EpicIssueAssignment

	resp, err := s.client.Do(req, &a)
	if err != nil {
		return nil, resp, err
	}

	return a, resp, err
}

// UpdateEpicIsssueAssignmentOptions describes options to move issues within an epic
type UpdateEpicIsssueAssignmentOptions struct {
	*ListOptions
	MoveBeforeID int `json:"move_before_id"`
	MoveAfterID  int `json:"move_after_id"`
}

// UpdateEpicIssueAssignment moves an issue before or after another issue in an
// epic issue list.
//
// Gitlab API Docs:
// https://docs.gitlab.com/ee/api/epic_issues.html#update-epic---issue-association
func (s *EpicIssuesService) UpdateEpicIssueAssignment(gid interface{}, epic int, epicIssue int, opt *UpdateEpicIsssueAssignmentOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, epicIssue)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var issues []*Issue
	resp, err := s.client.Do(req, &issues)
	if err != nil {
		return nil, resp, err
	}

	return issues, resp, err
}
<|endoftext|>


<commit_before>// Copyright 2016 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package twenty48

import (
	"fmt"
	"math/rand"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
)

type Board struct {
	size  int
	tiles map[*Tile]struct{}
}

func NewBoard(size int) *Board {
	b := &Board{
		size:  size,
		tiles: map[*Tile]struct{}{},
	}
	for i := 0; i < 2; i++ {
		b.addRandomTile()
	}
	return b
}

func (b *Board) addRandomTile() bool {
	cells := make([]bool, b.size*b.size)
	for t := range b.tiles {
		i := t.x + t.y*b.size
		cells[i] = true
	}
	availableCells := []int{}
	for i, b := range cells {
		if b {
			continue
		}
		availableCells = append(availableCells, i)
	}
	if len(availableCells) == 0 {
		return false
	}
	c := availableCells[rand.Intn(len(availableCells))]
	v := 2
	if rand.Intn(10) == 0 {
		v = 4
	}
	x := c % b.size
	y := c / b.size
	t := NewTile(v, x, y)
	b.tiles[t] = struct{}{}
	return true
}

func (b *Board) Move(dir Dir) {
	b.addRandomTile()
}

func (b *Board) Draw(screen *ebiten.Image) error {
	posToTile := map[int]*Tile{}
	for t := range b.tiles {
		i := t.x + t.y*b.size
		posToTile[i] = t
	}
	str := ""
	for j := 0; j < b.size; j++ {
		for i := 0; i < b.size; i++ {
			t := posToTile[i+j*b.size]
			if t != nil {
				str += fmt.Sprintf("[%4d]", t.value)
			} else {
				str += "[    ]"
			}
		}
		str += "\n"
	}
	ebitenutil.DebugPrint(screen, str)
	return nil
}
<commit_msg>examples/2048: Implement the rule<commit_after>// Copyright 2016 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package twenty48

import (
	"fmt"
	"math/rand"
	"sort"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
)

type Board struct {
	size  int
	tiles map[*Tile]struct{}
}

func NewBoard(size int) *Board {
	b := &Board{
		size:  size,
		tiles: map[*Tile]struct{}{},
	}
	for i := 0; i < 2; i++ {
		b.addRandomTile()
	}
	return b
}

func (b *Board) addRandomTile() bool {
	cells := make([]bool, b.size*b.size)
	for t := range b.tiles {
		i := t.x + t.y*b.size
		cells[i] = true
	}
	availableCells := []int{}
	for i, b := range cells {
		if b {
			continue
		}
		availableCells = append(availableCells, i)
	}
	if len(availableCells) == 0 {
		return false
	}
	c := availableCells[rand.Intn(len(availableCells))]
	v := 2
	if rand.Intn(10) == 0 {
		v = 4
	}
	x := c % b.size
	y := c / b.size
	t := NewTile(v, x, y)
	b.tiles[t] = struct{}{}
	return true
}

func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
	for t := range tiles {
		if t.x == x && t.y == y {
			return t
		}
	}
	return nil
}

func (b *Board) tileAt(x, y int) *Tile {
	return tileAt(b.tiles, x, y)
}

func (b *Board) Move(dir Dir) {
	vx, vy := dir.Vector()
	tx := []int{}
	ty := []int{}
	for i := 0; i < b.size; i++ {
		tx = append(tx, i)
		ty = append(ty, i)
	}
	if vx > 0 {
		sort.Sort(sort.Reverse(sort.IntSlice(tx)))
	}
	if vy > 0 {
		sort.Sort(sort.Reverse(sort.IntSlice(ty)))
	}
	nextTiles := map[*Tile]struct{}{}
	merged := map[*Tile]bool{}
	for _, j := range ty {
		for _, i := range tx {
			t := b.tileAt(i, j)
			if t == nil {
				continue
			}
			ii := i
			jj := j
			for {
				ni := ii + vx
				nj := jj + vy
				if ni < 0 || ni >= b.size || nj < 0 || nj >= b.size {
					break
				}
				tt := b.tileAt(ni, nj)
				if tt == nil {
					ii = ni
					jj = nj
					continue
				}
				nt := tileAt(nextTiles, ni, nj)
				if t.value == tt.value && (nt == nil || !merged[nt]) {
					ii = ni
					jj = nj
				}
				break
			}
			if tt := b.tileAt(ii, jj); tt != t && tt != nil {
				t.value += tt.value
				merged[t] = true
				delete(nextTiles, tt)
			}
			t.x = ii
			t.y = jj
			nextTiles[t] = struct{}{}
		}
	}
	b.tiles = nextTiles
	b.addRandomTile()
}

func (b *Board) Draw(screen *ebiten.Image) error {
	str := ""
	for j := 0; j < b.size; j++ {
		for i := 0; i < b.size; i++ {
			t := b.tileAt(i, j)
			if t != nil {
				str += fmt.Sprintf("[%4d]", t.value)
			} else {
				str += "[    ]"
			}
		}
		str += "\n"
	}
	ebitenutil.DebugPrint(screen, str)
	return nil
}
<|endoftext|>


<commit_before>// syncwatcher.go
package main

import (
  "code.google.com/p/go.exp/fsnotify"
  "fmt"
  "os"
  "bufio"
  "io/ioutil"
  "net/http"
  "encoding/json"
  "log"
  "flag"
  "net/url"
  "strings"
)

type Configuration struct {
  Version      int
  Repositories []RepositoryConfiguration
}

type RepositoryConfiguration struct {
  ID              string
  Directory       string
  ReadOnly        bool
  RescanIntervalS int
}

// HTTP Parameters
var (
  target    string
  authUser  string
  authPass  string
  csrfToken string
  csrfFile  string
  apiKey    string
)

func main() {
  flag.StringVar(&target, "target", "localhost:8080", "Target")
  flag.StringVar(&authUser, "user", "", "Username")
  flag.StringVar(&authPass, "pass", "", "Password")
  flag.StringVar(&csrfFile, "csrf", "", "CSRF token file")
  flag.StringVar(&apiKey, "api", "", "API key")
  flag.Parse()

  if len(csrfFile) > 0 {
    fd, err := os.Open(csrfFile)
    if err != nil {
      log.Fatal(err)
    }
    s := bufio.NewScanner(fd)
    for s.Scan() {
      csrfToken = s.Text()
    }
    fd.Close()
  }

  repos := getRepos()
  for i := range repos {
    repo := repos[i]
    go watchRepo(repo.ID, repo.Directory)
  }

  println("Press enter to exit")
  fmt.Scanln();
}

func getRepos() []RepositoryConfiguration {
  r, err := http.NewRequest("GET", "http://"+target+"/rest/config", nil)
  if err != nil {
    log.Fatal(err)
  }
  if len(csrfToken) > 0 {
    r.Header.Set("X-CSRF-Token", csrfToken)
  }
  if len(authUser) > 0 {
    r.SetBasicAuth(authUser, authPass)
  }
  if len(apiKey) > 0 {
    r.Header.Set("X-API-Key", apiKey)
  }
  res, err := http.DefaultClient.Do(r)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()
  if res.StatusCode != 200 {
    log.Fatalf("Status %d != 200 for GET", res.StatusCode)
  }
  bs, err := ioutil.ReadAll(res.Body)
  if err != nil {
    log.Fatal(err)
  }
  var cfg Configuration
  err = json.Unmarshal(bs, &cfg)
  if err != nil {
    log.Fatal(err)
  }
  return cfg.Repositories
}

func watchRepo(repo string, directory string) {
  sw, err := NewSyncWatcher()
  if sw == nil || err != nil {
    log.Fatal(err)
  }
  defer sw.Close()
  err = sw.Watch(directory)
  if err != nil {
    log.Fatal(err)
  }
  log.Println("Watching "+repo+": "+directory)
  for {
    ev, ok := waitForEvent(sw)
    if ok && ev != nil {
      sub := strings.TrimPrefix(ev.Name, directory)
      sub = strings.TrimPrefix(sub, string(os.PathSeparator))
      informChange(repo, sub)
    }
  }
}

func waitForEvent(sw *SyncWatcher) (ev *fsnotify.FileEvent, ok bool) {
  select {
  case ev, ok = <-sw.Event:
    if !ok {
      log.Fatal("Event: channel closed")
    }
  case err, eok := <-sw.Error:
    log.Fatal(err, eok)
  }
  return
}

func informChange(repo string, sub string) {
  data := url.Values {}
  data.Set("repo", repo)
  data.Set("sub", sub)
  r, err := http.NewRequest("POST", "http://"+target+"/rest/scan?"+data.Encode(), nil)
  if err != nil {
    log.Fatal(err)
  }
  if len(csrfToken) > 0 {
    r.Header.Set("X-CSRF-Token", csrfToken)
  }
  if len(authUser) > 0 {
    r.SetBasicAuth(authUser, authPass)
  }
  if len(apiKey) > 0 {
    r.Header.Set("X-API-Key", apiKey)
  }
  res, err := http.DefaultClient.Do(r)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()
  if res.StatusCode != 200 {
    log.Fatalf("Status %d != 200 for POST", res.StatusCode)
  } else {
    log.Println("Syncthing will index change in "+repo+": "+sub)
  }
}
<commit_msg>^C to exit<commit_after>// syncwatcher.go
package main

import (
  "code.google.com/p/go.exp/fsnotify"
  "os"
  "bufio"
  "io/ioutil"
  "net/http"
  "encoding/json"
  "log"
  "flag"
  "net/url"
  "strings"
)

type Configuration struct {
  Version      int
  Repositories []RepositoryConfiguration
}

type RepositoryConfiguration struct {
  ID              string
  Directory       string
  ReadOnly        bool
  RescanIntervalS int
}

// HTTP Parameters
var (
  target    string
  authUser  string
  authPass  string
  csrfToken string
  csrfFile  string
  apiKey    string
)

// Main
var (
	stop = make(chan int)
)

func main() {
  flag.StringVar(&target, "target", "localhost:8080", "Target")
  flag.StringVar(&authUser, "user", "", "Username")
  flag.StringVar(&authPass, "pass", "", "Password")
  flag.StringVar(&csrfFile, "csrf", "", "CSRF token file")
  flag.StringVar(&apiKey, "api", "", "API key")
  flag.Parse()

  if len(csrfFile) > 0 {
    fd, err := os.Open(csrfFile)
    if err != nil {
      log.Fatal(err)
    }
    s := bufio.NewScanner(fd)
    for s.Scan() {
      csrfToken = s.Text()
    }
    fd.Close()
  }

  repos := getRepos()
  for i := range repos {
    repo := repos[i]
    go watchRepo(repo.ID, repo.Directory)
  }

  code := <-stop
  println("Exiting")
  os.Exit(code)

}

func getRepos() []RepositoryConfiguration {
  r, err := http.NewRequest("GET", "http://"+target+"/rest/config", nil)
  if err != nil {
    log.Fatal(err)
  }
  if len(csrfToken) > 0 {
    r.Header.Set("X-CSRF-Token", csrfToken)
  }
  if len(authUser) > 0 {
    r.SetBasicAuth(authUser, authPass)
  }
  if len(apiKey) > 0 {
    r.Header.Set("X-API-Key", apiKey)
  }
  res, err := http.DefaultClient.Do(r)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()
  if res.StatusCode != 200 {
    log.Fatalf("Status %d != 200 for GET", res.StatusCode)
  }
  bs, err := ioutil.ReadAll(res.Body)
  if err != nil {
    log.Fatal(err)
  }
  var cfg Configuration
  err = json.Unmarshal(bs, &cfg)
  if err != nil {
    log.Fatal(err)
  }
  return cfg.Repositories
}

func watchRepo(repo string, directory string) {
  sw, err := NewSyncWatcher()
  if sw == nil || err != nil {
    log.Fatal(err)
  }
  defer sw.Close()
  err = sw.Watch(directory)
  if err != nil {
    log.Fatal(err)
  }
  log.Println("Watching "+repo+": "+directory)
  for {
    ev, ok := waitForEvent(sw)
    if ok && ev != nil {
      sub := strings.TrimPrefix(ev.Name, directory)
      sub = strings.TrimPrefix(sub, string(os.PathSeparator))
      informChange(repo, sub)
    }
  }
}

func waitForEvent(sw *SyncWatcher) (ev *fsnotify.FileEvent, ok bool) {
  select {
  case ev, ok = <-sw.Event:
    if !ok {
      log.Fatal("Event: channel closed")
    }
  case err, eok := <-sw.Error:
    log.Fatal(err, eok)
  }
  return
}

func informChange(repo string, sub string) {
  data := url.Values {}
  data.Set("repo", repo)
  data.Set("sub", sub)
  r, err := http.NewRequest("POST", "http://"+target+"/rest/scan?"+data.Encode(), nil)
  if err != nil {
    log.Fatal(err)
  }
  if len(csrfToken) > 0 {
    r.Header.Set("X-CSRF-Token", csrfToken)
  }
  if len(authUser) > 0 {
    r.SetBasicAuth(authUser, authPass)
  }
  if len(apiKey) > 0 {
    r.Header.Set("X-API-Key", apiKey)
  }
  res, err := http.DefaultClient.Do(r)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()
  if res.StatusCode != 200 {
    log.Fatalf("Status %d != 200 for POST", res.StatusCode)
  } else {
    log.Println("Syncthing will index change in "+repo+": "+sub)
  }
}
<|endoftext|>


<commit_before>package fakemetrics

import (
	"fmt"
	"time"

	"github.com/grafana/metrictank/clock"
	"github.com/grafana/metrictank/logger"
	"github.com/grafana/metrictank/schema"
	"github.com/grafana/metrictank/stacktest/fakemetrics/out"
	"github.com/grafana/metrictank/stacktest/fakemetrics/out/carbon"
	"github.com/grafana/metrictank/stacktest/fakemetrics/out/kafkamdm"
	"github.com/raintank/met"
	"github.com/raintank/met/helper"
	log "github.com/sirupsen/logrus"
)

func init() {
	formatter := &logger.TextFormatter{}
	formatter.TimestampFormat = "2006-01-02 15:04:05.000"
	log.SetFormatter(formatter)
	log.SetLevel(log.InfoLevel)
}

func generateMetrics(num int) []*schema.MetricData {
	metrics := make([]*schema.MetricData, num)

	for i := 0; i < num; i++ {
		name := fmt.Sprintf("some.id.of.a.metric.%d", i)
		m := &schema.MetricData{
			OrgId:    1,
			Name:     name,
			Interval: 1,
			Value:    1,
			Unit:     "s",
			Mtype:    "gauge",
		}
		m.SetId()
		metrics[i] = m
	}

	return metrics
}

type FakeMetrics struct {
	o       out.Out
	metrics []*schema.MetricData
	close   chan struct{}
	closed  bool
}

func NewFakeMetrics(metrics []*schema.MetricData, o out.Out, stats met.Backend) *FakeMetrics {
	fm := &FakeMetrics{
		o:       o,
		metrics: metrics,
		close:   make(chan struct{}),
	}
	go fm.run()
	return fm
}

func NewKafka(num int, timeout time.Duration, v2 bool) *FakeMetrics {
	stats, _ := helper.New(false, "", "standard", "", "")
	out, err := kafkamdm.New("mdm", []string{"localhost:9092"}, "none", timeout, stats, "lastNum", v2)
	if err != nil {
		log.Fatalf("failed to create kafka-mdm output. %s", err.Error())
	}
	return NewFakeMetrics(generateMetrics(num), out, stats)
}

func NewCarbon(num int) *FakeMetrics {
	stats, _ := helper.New(false, "", "standard", "", "")
	out, err := carbon.New("localhost:2003", stats)
	if err != nil {
		log.Fatalf("failed to create kafka-mdm output. %s", err.Error())
	}
	return NewFakeMetrics(generateMetrics(num), out, stats)
}

func (f *FakeMetrics) Close() error {
	if f.closed {
		return nil
	}
	f.close <- struct{}{}
	return f.o.Close()
}

func (f *FakeMetrics) run() {
	// advantage over regular ticker:
	// 1) no ticks dropped
	// 2) ticks come asap after the start of a new second, so we can measure better how long it took to get the data
	ticker := clock.AlignedTick(time.Second)

	for {
		select {
		case <-f.close:
			return
		case tick := <-ticker:
			unix := tick.Unix()
			for i := range f.metrics {
				f.metrics[i].Time = unix
			}
			err := f.o.Flush(f.metrics)
			if err != nil {
				panic(fmt.Sprintf("failed to send data to output: %s", err))
			}
		}
	}
}
<commit_msg>fix: make stacktest use an actual lossless ticker<commit_after>package fakemetrics

import (
	"fmt"
	"time"

	"github.com/grafana/metrictank/clock"
	"github.com/grafana/metrictank/logger"
	"github.com/grafana/metrictank/schema"
	"github.com/grafana/metrictank/stacktest/fakemetrics/out"
	"github.com/grafana/metrictank/stacktest/fakemetrics/out/carbon"
	"github.com/grafana/metrictank/stacktest/fakemetrics/out/kafkamdm"
	"github.com/raintank/met"
	"github.com/raintank/met/helper"
	log "github.com/sirupsen/logrus"
)

func init() {
	formatter := &logger.TextFormatter{}
	formatter.TimestampFormat = "2006-01-02 15:04:05.000"
	log.SetFormatter(formatter)
	log.SetLevel(log.InfoLevel)
}

func generateMetrics(num int) []*schema.MetricData {
	metrics := make([]*schema.MetricData, num)

	for i := 0; i < num; i++ {
		name := fmt.Sprintf("some.id.of.a.metric.%d", i)
		m := &schema.MetricData{
			OrgId:    1,
			Name:     name,
			Interval: 1,
			Value:    1,
			Unit:     "s",
			Mtype:    "gauge",
		}
		m.SetId()
		metrics[i] = m
	}

	return metrics
}

type FakeMetrics struct {
	o       out.Out
	metrics []*schema.MetricData
	close   chan struct{}
	closed  bool
}

func NewFakeMetrics(metrics []*schema.MetricData, o out.Out, stats met.Backend) *FakeMetrics {
	fm := &FakeMetrics{
		o:       o,
		metrics: metrics,
		close:   make(chan struct{}),
	}
	go fm.run()
	return fm
}

func NewKafka(num int, timeout time.Duration, v2 bool) *FakeMetrics {
	stats, _ := helper.New(false, "", "standard", "", "")
	out, err := kafkamdm.New("mdm", []string{"localhost:9092"}, "none", timeout, stats, "lastNum", v2)
	if err != nil {
		log.Fatalf("failed to create kafka-mdm output. %s", err.Error())
	}
	return NewFakeMetrics(generateMetrics(num), out, stats)
}

func NewCarbon(num int) *FakeMetrics {
	stats, _ := helper.New(false, "", "standard", "", "")
	out, err := carbon.New("localhost:2003", stats)
	if err != nil {
		log.Fatalf("failed to create kafka-mdm output. %s", err.Error())
	}
	return NewFakeMetrics(generateMetrics(num), out, stats)
}

func (f *FakeMetrics) Close() error {
	if f.closed {
		return nil
	}
	f.close <- struct{}{}
	return f.o.Close()
}

func (f *FakeMetrics) run() {
	// advantage over regular ticker:
	// 1) no ticks dropped: a hiccup in flushing should be handled by still producing all stats and flushing them when we can
	// 2) ticks come asap after the start of a new second, so we can measure better how long it took to get the data
	ticker := clock.AlignedTickLossless(time.Second)

	for {
		select {
		case <-f.close:
			return
		case tick := <-ticker:
			unix := tick.Unix()
			for i := range f.metrics {
				f.metrics[i].Time = unix
			}
			err := f.o.Flush(f.metrics)
			if err != nil {
				panic(fmt.Sprintf("failed to send data to output: %s", err))
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package proxy

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"strings"

	"github.com/golang/glog"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"

	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/apimachinery/pkg/util/sets"
)

// atomsToAttrs states which attributes of which tags require URL substitution.
// Sources: http://www.w3.org/TR/REC-html40/index/attributes.html
//          http://www.w3.org/html/wg/drafts/html/master/index.html#attributes-1
var atomsToAttrs = map[atom.Atom]sets.String{
	atom.A:          sets.NewString("href"),
	atom.Applet:     sets.NewString("codebase"),
	atom.Area:       sets.NewString("href"),
	atom.Audio:      sets.NewString("src"),
	atom.Base:       sets.NewString("href"),
	atom.Blockquote: sets.NewString("cite"),
	atom.Body:       sets.NewString("background"),
	atom.Button:     sets.NewString("formaction"),
	atom.Command:    sets.NewString("icon"),
	atom.Del:        sets.NewString("cite"),
	atom.Embed:      sets.NewString("src"),
	atom.Form:       sets.NewString("action"),
	atom.Frame:      sets.NewString("longdesc", "src"),
	atom.Head:       sets.NewString("profile"),
	atom.Html:       sets.NewString("manifest"),
	atom.Iframe:     sets.NewString("longdesc", "src"),
	atom.Img:        sets.NewString("longdesc", "src", "usemap"),
	atom.Input:      sets.NewString("src", "usemap", "formaction"),
	atom.Ins:        sets.NewString("cite"),
	atom.Link:       sets.NewString("href"),
	atom.Object:     sets.NewString("classid", "codebase", "data", "usemap"),
	atom.Q:          sets.NewString("cite"),
	atom.Script:     sets.NewString("src"),
	atom.Source:     sets.NewString("src"),
	atom.Video:      sets.NewString("poster", "src"),

	// TODO: css URLs hidden in style elements.
}

// Transport is a transport for text/html content that replaces URLs in html
// content with the prefix of the proxy server
type Transport struct {
	Scheme      string
	Host        string
	PathPrepend string

	http.RoundTripper
}

// RoundTrip implements the http.RoundTripper interface
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Add reverse proxy headers.
	forwardedURI := path.Join(t.PathPrepend, req.URL.Path)
	if strings.HasSuffix(req.URL.Path, "/") {
		forwardedURI = forwardedURI + "/"
	}
	req.Header.Set("X-Forwarded-Uri", forwardedURI)
	if len(t.Host) > 0 {
		req.Header.Set("X-Forwarded-Host", t.Host)
	}
	if len(t.Scheme) > 0 {
		req.Header.Set("X-Forwarded-Proto", t.Scheme)
	}

	rt := t.RoundTripper
	if rt == nil {
		rt = http.DefaultTransport
	}
	resp, err := rt.RoundTrip(req)

	if err != nil {
		message := fmt.Sprintf("Error: '%s'\nTrying to reach: '%v'", err.Error(), req.URL.String())
		resp = &http.Response{
			StatusCode: http.StatusServiceUnavailable,
			Body:       ioutil.NopCloser(strings.NewReader(message)),
		}
		return resp, nil
	}

	if redirect := resp.Header.Get("Location"); redirect != "" {
		resp.Header.Set("Location", t.rewriteURL(redirect, req.URL, req.Host))
		return resp, nil
	}

	cType := resp.Header.Get("Content-Type")
	cType = strings.TrimSpace(strings.SplitN(cType, ";", 2)[0])
	if cType != "text/html" {
		// Do nothing, simply pass through
		return resp, nil
	}

	return t.rewriteResponse(req, resp)
}

var _ = net.RoundTripperWrapper(&Transport{})

func (rt *Transport) WrappedRoundTripper() http.RoundTripper {
	return rt.RoundTripper
}

// rewriteURL rewrites a single URL to go through the proxy, if the URL refers
// to the same host as sourceURL, which is the page on which the target URL
// occurred, or if the URL matches the sourceHost. If any error occurs (e.g.
// parsing), it returns targetURL.
func (t *Transport) rewriteURL(targetURL string, sourceURL *url.URL, sourceHost string) string {
	url, err := url.Parse(targetURL)
	if err != nil {
		return targetURL
	}

	isDifferentHost := url.Host != "" && url.Host != sourceURL.Host && url.Host != sourceHost
	isRelative := !strings.HasPrefix(url.Path, "/")
	if isDifferentHost || isRelative {
		return targetURL
	}

	// Do not rewrite scheme and host if the Transport has empty scheme and host
	// when targetURL already contains the sourceHost
	if !(url.Host == sourceHost && t.Scheme == "" && t.Host == "") {
		url.Scheme = t.Scheme
		url.Host = t.Host
	}

	origPath := url.Path
	// Do not rewrite URL if the sourceURL already contains the necessary prefix.
	if strings.HasPrefix(url.Path, t.PathPrepend) {
		return url.String()
	}
	url.Path = path.Join(t.PathPrepend, url.Path)
	if strings.HasSuffix(origPath, "/") {
		// Add back the trailing slash, which was stripped by path.Join().
		url.Path += "/"
	}

	return url.String()
}

// rewriteHTML scans the HTML for tags with url-valued attributes, and updates
// those values with the urlRewriter function. The updated HTML is output to the
// writer.
func rewriteHTML(reader io.Reader, writer io.Writer, urlRewriter func(string) string) error {
	// Note: This assumes the content is UTF-8.
	tokenizer := html.NewTokenizer(reader)

	var err error
	for err == nil {
		tokenType := tokenizer.Next()
		switch tokenType {
		case html.ErrorToken:
			err = tokenizer.Err()
		case html.StartTagToken, html.SelfClosingTagToken:
			token := tokenizer.Token()
			if urlAttrs, ok := atomsToAttrs[token.DataAtom]; ok {
				for i, attr := range token.Attr {
					if urlAttrs.Has(attr.Key) {
						token.Attr[i].Val = urlRewriter(attr.Val)
					}
				}
			}
			_, err = writer.Write([]byte(token.String()))
		default:
			_, err = writer.Write(tokenizer.Raw())
		}
	}
	if err != io.EOF {
		return err
	}
	return nil
}

// rewriteResponse modifies an HTML response by updating absolute links referring
// to the original host to instead refer to the proxy transport.
func (t *Transport) rewriteResponse(req *http.Request, resp *http.Response) (*http.Response, error) {
	origBody := resp.Body
	defer origBody.Close()

	newContent := &bytes.Buffer{}
	var reader io.Reader = origBody
	var writer io.Writer = newContent
	encoding := resp.Header.Get("Content-Encoding")
	switch encoding {
	case "gzip":
		var err error
		reader, err = gzip.NewReader(reader)
		if err != nil {
			return nil, fmt.Errorf("errorf making gzip reader: %v", err)
		}
		gzw := gzip.NewWriter(writer)
		defer gzw.Close()
		writer = gzw
	// TODO: support flate, other encodings.
	case "":
		// This is fine
	default:
		// Some encoding we don't understand-- don't try to parse this
		glog.Errorf("Proxy encountered encoding %v for text/html; can't understand this so not fixing links.", encoding)
		return resp, nil
	}

	urlRewriter := func(targetUrl string) string {
		return t.rewriteURL(targetUrl, req.URL, req.Host)
	}
	err := rewriteHTML(reader, writer, urlRewriter)
	if err != nil {
		glog.Errorf("Failed to rewrite URLs: %v", err)
		return resp, err
	}

	resp.Body = ioutil.NopCloser(newContent)
	// Update header node with new content-length
	// TODO: Remove any hash/signature headers here?
	resp.Header.Del("Content-Length")
	resp.ContentLength = int64(newContent.Len())

	return resp, err
}
<commit_msg>Change name from sourceHost to sourceRequestHost.<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package proxy

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"strings"

	"github.com/golang/glog"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"

	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/apimachinery/pkg/util/sets"
)

// atomsToAttrs states which attributes of which tags require URL substitution.
// Sources: http://www.w3.org/TR/REC-html40/index/attributes.html
//          http://www.w3.org/html/wg/drafts/html/master/index.html#attributes-1
var atomsToAttrs = map[atom.Atom]sets.String{
	atom.A:          sets.NewString("href"),
	atom.Applet:     sets.NewString("codebase"),
	atom.Area:       sets.NewString("href"),
	atom.Audio:      sets.NewString("src"),
	atom.Base:       sets.NewString("href"),
	atom.Blockquote: sets.NewString("cite"),
	atom.Body:       sets.NewString("background"),
	atom.Button:     sets.NewString("formaction"),
	atom.Command:    sets.NewString("icon"),
	atom.Del:        sets.NewString("cite"),
	atom.Embed:      sets.NewString("src"),
	atom.Form:       sets.NewString("action"),
	atom.Frame:      sets.NewString("longdesc", "src"),
	atom.Head:       sets.NewString("profile"),
	atom.Html:       sets.NewString("manifest"),
	atom.Iframe:     sets.NewString("longdesc", "src"),
	atom.Img:        sets.NewString("longdesc", "src", "usemap"),
	atom.Input:      sets.NewString("src", "usemap", "formaction"),
	atom.Ins:        sets.NewString("cite"),
	atom.Link:       sets.NewString("href"),
	atom.Object:     sets.NewString("classid", "codebase", "data", "usemap"),
	atom.Q:          sets.NewString("cite"),
	atom.Script:     sets.NewString("src"),
	atom.Source:     sets.NewString("src"),
	atom.Video:      sets.NewString("poster", "src"),

	// TODO: css URLs hidden in style elements.
}

// Transport is a transport for text/html content that replaces URLs in html
// content with the prefix of the proxy server
type Transport struct {
	Scheme      string
	Host        string
	PathPrepend string

	http.RoundTripper
}

// RoundTrip implements the http.RoundTripper interface
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Add reverse proxy headers.
	forwardedURI := path.Join(t.PathPrepend, req.URL.Path)
	if strings.HasSuffix(req.URL.Path, "/") {
		forwardedURI = forwardedURI + "/"
	}
	req.Header.Set("X-Forwarded-Uri", forwardedURI)
	if len(t.Host) > 0 {
		req.Header.Set("X-Forwarded-Host", t.Host)
	}
	if len(t.Scheme) > 0 {
		req.Header.Set("X-Forwarded-Proto", t.Scheme)
	}

	rt := t.RoundTripper
	if rt == nil {
		rt = http.DefaultTransport
	}
	resp, err := rt.RoundTrip(req)

	if err != nil {
		message := fmt.Sprintf("Error: '%s'\nTrying to reach: '%v'", err.Error(), req.URL.String())
		resp = &http.Response{
			StatusCode: http.StatusServiceUnavailable,
			Body:       ioutil.NopCloser(strings.NewReader(message)),
		}
		return resp, nil
	}

	if redirect := resp.Header.Get("Location"); redirect != "" {
		resp.Header.Set("Location", t.rewriteURL(redirect, req.URL, req.Host))
		return resp, nil
	}

	cType := resp.Header.Get("Content-Type")
	cType = strings.TrimSpace(strings.SplitN(cType, ";", 2)[0])
	if cType != "text/html" {
		// Do nothing, simply pass through
		return resp, nil
	}

	return t.rewriteResponse(req, resp)
}

var _ = net.RoundTripperWrapper(&Transport{})

func (rt *Transport) WrappedRoundTripper() http.RoundTripper {
	return rt.RoundTripper
}

// rewriteURL rewrites a single URL to go through the proxy, if the URL refers
// to the same host as sourceURL, which is the page on which the target URL
// occurred, or if the URL matches the sourceRequestHost. If any error occurs (e.g.
// parsing), it returns targetURL.
func (t *Transport) rewriteURL(targetURL string, sourceURL *url.URL, sourceRequestHost string) string {
	url, err := url.Parse(targetURL)
	if err != nil {
		return targetURL
	}

	isDifferentHost := url.Host != "" && url.Host != sourceURL.Host && url.Host != sourceRequestHost
	isRelative := !strings.HasPrefix(url.Path, "/")
	if isDifferentHost || isRelative {
		return targetURL
	}

	// Do not rewrite scheme and host if the Transport has empty scheme and host
	// when targetURL already contains the sourceRequestHost
	if !(url.Host == sourceRequestHost && t.Scheme == "" && t.Host == "") {
		url.Scheme = t.Scheme
		url.Host = t.Host
	}

	origPath := url.Path
	// Do not rewrite URL if the sourceURL already contains the necessary prefix.
	if strings.HasPrefix(url.Path, t.PathPrepend) {
		return url.String()
	}
	url.Path = path.Join(t.PathPrepend, url.Path)
	if strings.HasSuffix(origPath, "/") {
		// Add back the trailing slash, which was stripped by path.Join().
		url.Path += "/"
	}

	return url.String()
}

// rewriteHTML scans the HTML for tags with url-valued attributes, and updates
// those values with the urlRewriter function. The updated HTML is output to the
// writer.
func rewriteHTML(reader io.Reader, writer io.Writer, urlRewriter func(string) string) error {
	// Note: This assumes the content is UTF-8.
	tokenizer := html.NewTokenizer(reader)

	var err error
	for err == nil {
		tokenType := tokenizer.Next()
		switch tokenType {
		case html.ErrorToken:
			err = tokenizer.Err()
		case html.StartTagToken, html.SelfClosingTagToken:
			token := tokenizer.Token()
			if urlAttrs, ok := atomsToAttrs[token.DataAtom]; ok {
				for i, attr := range token.Attr {
					if urlAttrs.Has(attr.Key) {
						token.Attr[i].Val = urlRewriter(attr.Val)
					}
				}
			}
			_, err = writer.Write([]byte(token.String()))
		default:
			_, err = writer.Write(tokenizer.Raw())
		}
	}
	if err != io.EOF {
		return err
	}
	return nil
}

// rewriteResponse modifies an HTML response by updating absolute links referring
// to the original host to instead refer to the proxy transport.
func (t *Transport) rewriteResponse(req *http.Request, resp *http.Response) (*http.Response, error) {
	origBody := resp.Body
	defer origBody.Close()

	newContent := &bytes.Buffer{}
	var reader io.Reader = origBody
	var writer io.Writer = newContent
	encoding := resp.Header.Get("Content-Encoding")
	switch encoding {
	case "gzip":
		var err error
		reader, err = gzip.NewReader(reader)
		if err != nil {
			return nil, fmt.Errorf("errorf making gzip reader: %v", err)
		}
		gzw := gzip.NewWriter(writer)
		defer gzw.Close()
		writer = gzw
	// TODO: support flate, other encodings.
	case "":
		// This is fine
	default:
		// Some encoding we don't understand-- don't try to parse this
		glog.Errorf("Proxy encountered encoding %v for text/html; can't understand this so not fixing links.", encoding)
		return resp, nil
	}

	urlRewriter := func(targetUrl string) string {
		return t.rewriteURL(targetUrl, req.URL, req.Host)
	}
	err := rewriteHTML(reader, writer, urlRewriter)
	if err != nil {
		glog.Errorf("Failed to rewrite URLs: %v", err)
		return resp, err
	}

	resp.Body = ioutil.NopCloser(newContent)
	// Update header node with new content-length
	// TODO: Remove any hash/signature headers here?
	resp.Header.Del("Content-Length")
	resp.ContentLength = int64(newContent.Len())

	return resp, err
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package events

import (
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/reference"

	"k8s.io/api/events/v1beta1"
	"k8s.io/client-go/tools/record/util"
	"k8s.io/klog"
)

type recorderImpl struct {
	scheme              *runtime.Scheme
	reportingController string
	reportingInstance   string
	*watch.Broadcaster
	clock clock.Clock
}

func (recorder *recorderImpl) Eventf(regarding runtime.Object, related runtime.Object, eventtype, reason, action, note string, args ...interface{}) {
	timestamp := metav1.MicroTime{time.Now()}
	message := fmt.Sprintf(note, args...)
	refRegarding, err := reference.GetReference(recorder.scheme, regarding)
	if err != nil {
		klog.Errorf("Could not construct reference to: '%#v' due to: '%v'. Will not report event: '%v' '%v' '%v'", regarding, err, eventtype, reason, message)
		return
	}
	refRelated, err := reference.GetReference(recorder.scheme, related)
	if err != nil {
		klog.Errorf("Could not construct reference to: '%#v' due to: '%v'.", related, err)
	}
	if !util.ValidateEventType(eventtype) {
		klog.Errorf("Unsupported event type: '%v'", eventtype)
		return
	}
	event := recorder.makeEvent(refRegarding, refRelated, timestamp, eventtype, reason, message, recorder.reportingController, recorder.reportingInstance, action)
	go func() {
		defer utilruntime.HandleCrash()
		recorder.Action(watch.Added, event)
	}()
}

func (recorder *recorderImpl) makeEvent(refRegarding *v1.ObjectReference, refRelated *v1.ObjectReference, timestamp metav1.MicroTime, eventtype, reason, message string, reportingController string, reportingInstance string, action string) *v1beta1.Event {
	t := metav1.Time{Time: recorder.clock.Now()}
	namespace := refRegarding.Namespace
	if namespace == "" {
		namespace = metav1.NamespaceSystem
	}
	return &v1beta1.Event{
		ObjectMeta: metav1.ObjectMeta{
			Name:      fmt.Sprintf("%v.%x", refRegarding.Name, t.UnixNano()),
			Namespace: namespace,
		},
		EventTime:           timestamp,
		Series:              nil,
		ReportingController: reportingController,
		ReportingInstance:   reportingInstance,
		Action:              action,
		Reason:              reason,
		Regarding:           *refRegarding,
		Related:             refRelated,
		Note:                message,
		Type:                eventtype,
		// TODO: remove this when we change conversion to convert eventSource
		// to reportingController
		DeprecatedSource: v1.EventSource{Component: reportingController},
	}
}
<commit_msg>Tolerate the case if `related` event is nil<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package events

import (
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/tools/reference"

	"k8s.io/api/events/v1beta1"
	"k8s.io/client-go/tools/record/util"
	"k8s.io/klog"
)

type recorderImpl struct {
	scheme              *runtime.Scheme
	reportingController string
	reportingInstance   string
	*watch.Broadcaster
	clock clock.Clock
}

func (recorder *recorderImpl) Eventf(regarding runtime.Object, related runtime.Object, eventtype, reason, action, note string, args ...interface{}) {
	timestamp := metav1.MicroTime{time.Now()}
	message := fmt.Sprintf(note, args...)
	refRegarding, err := reference.GetReference(recorder.scheme, regarding)
	if err != nil {
		klog.Errorf("Could not construct reference to: '%#v' due to: '%v'. Will not report event: '%v' '%v' '%v'", regarding, err, eventtype, reason, message)
		return
	}
	refRelated, err := reference.GetReference(recorder.scheme, related)
	if err != nil {
		klog.V(9).Infof("Could not construct reference to: '%#v' due to: '%v'.", related, err)
	}
	if !util.ValidateEventType(eventtype) {
		klog.Errorf("Unsupported event type: '%v'", eventtype)
		return
	}
	event := recorder.makeEvent(refRegarding, refRelated, timestamp, eventtype, reason, message, recorder.reportingController, recorder.reportingInstance, action)
	go func() {
		defer utilruntime.HandleCrash()
		recorder.Action(watch.Added, event)
	}()
}

func (recorder *recorderImpl) makeEvent(refRegarding *v1.ObjectReference, refRelated *v1.ObjectReference, timestamp metav1.MicroTime, eventtype, reason, message string, reportingController string, reportingInstance string, action string) *v1beta1.Event {
	t := metav1.Time{Time: recorder.clock.Now()}
	namespace := refRegarding.Namespace
	if namespace == "" {
		namespace = metav1.NamespaceSystem
	}
	return &v1beta1.Event{
		ObjectMeta: metav1.ObjectMeta{
			Name:      fmt.Sprintf("%v.%x", refRegarding.Name, t.UnixNano()),
			Namespace: namespace,
		},
		EventTime:           timestamp,
		Series:              nil,
		ReportingController: reportingController,
		ReportingInstance:   reportingInstance,
		Action:              action,
		Reason:              reason,
		Regarding:           *refRegarding,
		Related:             refRelated,
		Note:                message,
		Type:                eventtype,
		// TODO: remove this when we change conversion to convert eventSource
		// to reportingController
		DeprecatedSource: v1.EventSource{Component: reportingController},
	}
}
<|endoftext|>