<commit_before>package chromedp

import (
	"bytes"
	"context"
	"io"
	"net"
	"strings"

	"github.com/chromedp/cdproto"
	"github.com/gorilla/websocket"
	"github.com/mailru/easyjson/jlexer"
	"github.com/mailru/easyjson/jwriter"
)

var (
	// DefaultReadBufferSize is the default maximum read buffer size.
	DefaultReadBufferSize = 25 * 1024 * 1024

	// DefaultWriteBufferSize is the default maximum write buffer size.
	DefaultWriteBufferSize = 10 * 1024 * 1024
)

// Transport is the common interface to send/receive messages to a target.
type Transport interface {
	Read(*cdproto.Message) error
	Write(*cdproto.Message) error
	io.Closer
}

// Conn wraps a gorilla/websocket.Conn connection.
type Conn struct {
	*websocket.Conn

	// buf helps us reuse space when reading from the websocket.
	buf bytes.Buffer

	// reuse the easyjson structs to avoid allocs per Read/Write.
	lexer  jlexer.Lexer
	writer jwriter.Writer

	dbgf func(string, ...interface{})
}

// DialContext dials the specified websocket URL using gorilla/websocket.
func DialContext(ctx context.Context, urlstr string, opts ...DialOption) (*Conn, error) {
	d := &websocket.Dialer{
		ReadBufferSize:  DefaultReadBufferSize,
		WriteBufferSize: DefaultWriteBufferSize,
	}

	// connect
	conn, _, err := d.DialContext(ctx, urlstr, nil)
	if err != nil {
		return nil, err
	}

	// apply opts
	c := &Conn{
		Conn: conn,
	}
	for _, o := range opts {
		o(c)
	}

	return c, nil
}

func (c *Conn) bufReadAll(r io.Reader) ([]byte, error) {
	c.buf.Reset()
	_, err := c.buf.ReadFrom(r)
	return c.buf.Bytes(), err
}

// Read reads the next message.
func (c *Conn) Read(msg *cdproto.Message) error {
	// get websocket reader
	typ, r, err := c.NextReader()
	if err != nil {
		return err
	}
	if typ != websocket.TextMessage {
		return ErrInvalidWebsocketMessage
	}

	// Unmarshal via a bytes.Buffer. Don't use UnmarshalFromReader, as that
	// uses ioutil.ReadAll, which uses a brand new bytes.Buffer each time.
	// That doesn't reuse any space.
	buf, err := c.bufReadAll(r)
	if err != nil {
		return err
	}
	if c.dbgf != nil {
		c.dbgf("<- %s", buf)
	}

	// Reuse the easyjson lexer.
	c.lexer = jlexer.Lexer{Data: buf}
	msg.UnmarshalEasyJSON(&c.lexer)
	if err := c.lexer.Error(); err != nil {
		return err
	}

	// bufReadAll uses the buffer space directly, and msg.Result is an
	// easyjson.RawMessage, so we must make a copy of those bytes to prevent
	// data races. This still allocates much less than using a new buffer
	// each time.
	msg.Result = append([]byte{}, msg.Result...)
	return nil
}

// Write writes a message.
func (c *Conn) Write(msg *cdproto.Message) error {
	w, err := c.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}
	defer w.Close()

	// Reuse the easyjson writer.
	c.writer = jwriter.Writer{}

	// Perform the marshal.
	msg.MarshalEasyJSON(&c.writer)
	if err := c.writer.Error; err != nil {
		return err
	}

	if c.dbgf != nil {
		buf, _ := c.writer.BuildBytes()
		c.dbgf("-> %s", buf)
	}

	// Write the bytes to the websocket.
	if _, err := c.writer.DumpTo(w); err != nil {
		return err
	}
	return w.Close()
}

// ForceIP forces the host component in urlstr to be an IP address.
//
// Since Chrome 66+, Chrome DevTools Protocol clients connecting to a browser
// must send the "Host:" header as either an IP address, or "localhost".
func ForceIP(urlstr string) string {
	if i := strings.Index(urlstr, "://"); i != -1 {
		scheme := urlstr[:i+3]
		host, port, path := urlstr[len(scheme)+3:], "", ""
		if i := strings.Index(host, "/"); i != -1 {
			host, path = host[:i], host[i:]
		}
		if i := strings.Index(host, ":"); i != -1 {
			host, port = host[:i], host[i:]
		}
		if addr, err := net.ResolveIPAddr("ip", host); err == nil {
			urlstr = scheme + addr.IP.String() + port + path
		}
	}
	return urlstr
}

// DialOption is a dial option.
type DialOption func(*Conn)

// WithConnDebugf is a dial option to set a protocol logger.
func WithConnDebugf(f func(string, ...interface{})) DialOption {
	return func(c *Conn) {
		c.dbgf = f
	}
}
<commit_msg>fix WithDebugf after a regression in 9ca47714<commit_after>package chromedp

import (
	"bytes"
	"context"
	"io"
	"net"
	"strings"

	"github.com/chromedp/cdproto"
	"github.com/gorilla/websocket"
	"github.com/mailru/easyjson/jlexer"
	"github.com/mailru/easyjson/jwriter"
)

var (
	// DefaultReadBufferSize is the default maximum read buffer size.
	DefaultReadBufferSize = 25 * 1024 * 1024

	// DefaultWriteBufferSize is the default maximum write buffer size.
	DefaultWriteBufferSize = 10 * 1024 * 1024
)

// Transport is the common interface to send/receive messages to a target.
type Transport interface {
	Read(*cdproto.Message) error
	Write(*cdproto.Message) error
	io.Closer
}

// Conn wraps a gorilla/websocket.Conn connection.
type Conn struct {
	*websocket.Conn

	// buf helps us reuse space when reading from the websocket.
	buf bytes.Buffer

	// reuse the easyjson structs to avoid allocs per Read/Write.
	lexer  jlexer.Lexer
	writer jwriter.Writer

	dbgf func(string, ...interface{})
}

// DialContext dials the specified websocket URL using gorilla/websocket.
func DialContext(ctx context.Context, urlstr string, opts ...DialOption) (*Conn, error) {
	d := &websocket.Dialer{
		ReadBufferSize:  DefaultReadBufferSize,
		WriteBufferSize: DefaultWriteBufferSize,
	}

	// connect
	conn, _, err := d.DialContext(ctx, urlstr, nil)
	if err != nil {
		return nil, err
	}

	// apply opts
	c := &Conn{
		Conn: conn,
	}
	for _, o := range opts {
		o(c)
	}

	return c, nil
}

func (c *Conn) bufReadAll(r io.Reader) ([]byte, error) {
	c.buf.Reset()
	_, err := c.buf.ReadFrom(r)
	return c.buf.Bytes(), err
}

// Read reads the next message.
func (c *Conn) Read(msg *cdproto.Message) error {
	// get websocket reader
	typ, r, err := c.NextReader()
	if err != nil {
		return err
	}
	if typ != websocket.TextMessage {
		return ErrInvalidWebsocketMessage
	}

	// Unmarshal via a bytes.Buffer. Don't use UnmarshalFromReader, as that
	// uses ioutil.ReadAll, which uses a brand new bytes.Buffer each time.
	// That doesn't reuse any space.
	buf, err := c.bufReadAll(r)
	if err != nil {
		return err
	}
	if c.dbgf != nil {
		c.dbgf("<- %s", buf)
	}

	// Reuse the easyjson lexer.
	c.lexer = jlexer.Lexer{Data: buf}
	msg.UnmarshalEasyJSON(&c.lexer)
	if err := c.lexer.Error(); err != nil {
		return err
	}

	// bufReadAll uses the buffer space directly, and msg.Result is an
	// easyjson.RawMessage, so we must make a copy of those bytes to prevent
	// data races. This still allocates much less than using a new buffer
	// each time.
	msg.Result = append([]byte{}, msg.Result...)
	return nil
}

// Write writes a message.
func (c *Conn) Write(msg *cdproto.Message) error {
	w, err := c.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}
	defer w.Close()

	// Reuse the easyjson writer.
	c.writer = jwriter.Writer{}

	// Perform the marshal.
	msg.MarshalEasyJSON(&c.writer)
	if err := c.writer.Error; err != nil {
		return err
	}

	// Write the bytes to the websocket.
	// BuildBytes consumes the buffer, so we can't use it as well as DumpTo.
	if c.dbgf != nil {
		buf, _ := c.writer.BuildBytes()
		c.dbgf("-> %s", buf)
		if _, err := w.Write(buf); err != nil {
			return err
		}
	} else {
		if _, err := c.writer.DumpTo(w); err != nil {
			return err
		}
	}
	return w.Close()
}

// ForceIP forces the host component in urlstr to be an IP address.
//
// Since Chrome 66+, Chrome DevTools Protocol clients connecting to a browser
// must send the "Host:" header as either an IP address, or "localhost".
func ForceIP(urlstr string) string {
	if i := strings.Index(urlstr, "://"); i != -1 {
		scheme := urlstr[:i+3]
		host, port, path := urlstr[len(scheme)+3:], "", ""
		if i := strings.Index(host, "/"); i != -1 {
			host, path = host[:i], host[i:]
		}
		if i := strings.Index(host, ":"); i != -1 {
			host, port = host[:i], host[i:]
		}
		if addr, err := net.ResolveIPAddr("ip", host); err == nil {
			urlstr = scheme + addr.IP.String() + port + path
		}
	}
	return urlstr
}

// DialOption is a dial option.
type DialOption func(*Conn)

// WithConnDebugf is a dial option to set a protocol logger.
func WithConnDebugf(f func(string, ...interface{})) DialOption {
	return func(c *Conn) {
		c.dbgf = f
	}
}
<|endoftext|>


<commit_before>package metadata

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
)

func TestMetadataGeneratorFetch(t *testing.T) {
	tests := []struct {
		command  string
		metadata string
		err      bool
	}{
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata "{}"`,
			metadata: `{}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 1 -metadata "{}"`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": "metadata", "foo": [100, 200, {}, null]}'`,
			metadata: `{"example":"metadata","foo":[100,200,{},null]}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": metadata"}'`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '"foobar"'`,
			metadata: `"foobar"`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata foobar`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata 262144`,
			metadata: `262144`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata true`,
			metadata: `true`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata null`,
			metadata: `null`,
			err:      false,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				Command: test.command,
			},
		}
		metadata, err := g.Fetch()
		if err != nil {
			if !test.err {
				t.Errorf("error occurred unexpectedly on command %q", test.command)
			}
		} else {
			if test.err {
				t.Errorf("error did not occurr but error expected on command %q", test.command)
			}
			metadataStr, _ := json.Marshal(metadata)
			if string(metadataStr) != test.metadata {
				t.Errorf("metadata should be %q but got %q", test.metadata, string(metadataStr))
			}
		}
	}
}

func TestMetadataGeneratorSaveDiffers(t *testing.T) {
	tests := []struct {
		prevmetadata string
		metadata     string
		differs      bool
	}{
		{
			prevmetadata: `{}`,
			metadata:     `{}`,
			differs:      false,
		},
		{
			prevmetadata: `{ "foo": [ 100, 200, null, {} ] }`,
			metadata:     `{"foo":[100,200,null,{}]}`,
			differs:      false,
		},
		{
			prevmetadata: `null`,
			metadata:     `{}`,
			differs:      true,
		},
		{
			prevmetadata: `[]`,
			metadata:     `{}`,
			differs:      true,
		},
	}
	for _, test := range tests {
		g := Generator{}
		var prevmetadata interface{}
		_ = json.Unmarshal([]byte(test.prevmetadata), &prevmetadata)

		if err := g.Save(prevmetadata); err != nil {
			t.Errorf("Error should not occur in Save() but got: %s", err.Error())
		}

		var metadata interface{}
		_ = json.Unmarshal([]byte(test.metadata), &metadata)

		got := g.Differs(metadata)
		if got != test.differs {
			t.Errorf("Differs() should return %t but got %t for %v, %v", test.differs, got, prevmetadata, metadata)
		}
	}
}

func TestMetadataGeneratorInterval(t *testing.T) {
	tests := []struct {
		interval *int32
		expected time.Duration
	}{
		{
			interval: pint(0),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(1),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(30),
			expected: 30 * time.Minute,
		},
		{
			interval: nil,
			expected: 10 * time.Minute,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				ExecutionInterval: test.interval,
			},
		}
		if g.Interval() != test.expected {
			t.Errorf("interval should be %v but got: %v", test.expected, g.Interval())
		}
	}
}

func pint(i int32) *int32 {
	return &i
}
<commit_msg>specify the name of temporary file in metadata plugin test<commit_after>package metadata

import (
	"encoding/json"
	"io/ioutil"
	"testing"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
)

func TestMetadataGeneratorFetch(t *testing.T) {
	tests := []struct {
		command  string
		metadata string
		err      bool
	}{
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata "{}"`,
			metadata: `{}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 1 -metadata "{}"`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": "metadata", "foo": [100, 200, {}, null]}'`,
			metadata: `{"example":"metadata","foo":[100,200,{},null]}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": metadata"}'`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '"foobar"'`,
			metadata: `"foobar"`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata foobar`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata 262144`,
			metadata: `262144`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata true`,
			metadata: `true`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata null`,
			metadata: `null`,
			err:      false,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				Command: test.command,
			},
		}
		metadata, err := g.Fetch()
		if err != nil {
			if !test.err {
				t.Errorf("error occurred unexpectedly on command %q", test.command)
			}
		} else {
			if test.err {
				t.Errorf("error did not occurr but error expected on command %q", test.command)
			}
			metadataStr, _ := json.Marshal(metadata)
			if string(metadataStr) != test.metadata {
				t.Errorf("metadata should be %q but got %q", test.metadata, string(metadataStr))
			}
		}
	}
}

func TestMetadataGeneratorSaveDiffers(t *testing.T) {
	tests := []struct {
		prevmetadata string
		metadata     string
		differs      bool
	}{
		{
			prevmetadata: `{}`,
			metadata:     `{}`,
			differs:      false,
		},
		{
			prevmetadata: `{ "foo": [ 100, 200, null, {} ] }`,
			metadata:     `{"foo":[100,200,null,{}]}`,
			differs:      false,
		},
		{
			prevmetadata: `null`,
			metadata:     `{}`,
			differs:      true,
		},
		{
			prevmetadata: `[]`,
			metadata:     `{}`,
			differs:      true,
		},
	}
	for _, test := range tests {
		tmpf, _ := ioutil.TempFile("", "mackerel-metadata-test")
		g := Generator{Tempfile: tmpf.Name()}
		var prevmetadata interface{}
		_ = json.Unmarshal([]byte(test.prevmetadata), &prevmetadata)

		if err := g.Save(prevmetadata); err != nil {
			t.Errorf("Error should not occur in Save() but got: %s", err.Error())
		}

		var metadata interface{}
		_ = json.Unmarshal([]byte(test.metadata), &metadata)

		got := g.Differs(metadata)
		if got != test.differs {
			t.Errorf("Differs() should return %t but got %t for %v, %v", test.differs, got, prevmetadata, metadata)
		}
	}
}

func TestMetadataGeneratorInterval(t *testing.T) {
	tests := []struct {
		interval *int32
		expected time.Duration
	}{
		{
			interval: pint(0),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(1),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(30),
			expected: 30 * time.Minute,
		},
		{
			interval: nil,
			expected: 10 * time.Minute,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				ExecutionInterval: test.interval,
			},
		}
		if g.Interval() != test.expected {
			t.Errorf("interval should be %v but got: %v", test.expected, g.Interval())
		}
	}
}

func pint(i int32) *int32 {
	return &i
}
<|endoftext|>


<commit_before>package main

import "crypto/tls"

// Conn is a mobile client connection.
type Conn struct {
	UserID uint32
	Error  string
	Data   interface{}
	conn   *tls.Conn
}

// SendMessage sends a message to the connected mobile client.
func (c *Conn) SendMessage() error {
	return nil
}

// SendNotification sends a notification to the connected mobile client.
func (c *Conn) SendNotification() error {
	return nil
}
<commit_msg>now conn handles read/writes<commit_after>package main

import "crypto/tls"

// Conn is a mobile client connection.
type Conn struct {
	UserID uint32
	conn   *tls.Conn
	err    error
}

// SendMsg sends a message to the connected mobile client.
func (c *Conn) SendMsg(msg *interface{}) error {
	return nil
}

// ReadMsg waits for and reads the next message of the TLS connection.
func (c *Conn) ReadMsg() (msg []byte, err error) {
	if c.err != nil {
		// todo: send error message to user, log the error, and close the conn and return
		return nil, c.err
	}

	return
}
<|endoftext|>


<commit_before>package core

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"syscall"

	"golang.org/x/text/encoding"
	"golang.org/x/text/transform"
)

// Cmd is command infomation.
type Cmd struct {
	Cmd     *exec.Cmd
	CmdLine string

	Stdin  bytes.Buffer
	Stdout bytes.Buffer
	Stderr bytes.Buffer

	ExitError error
	ExitCode  int

	StdinPipe  io.WriteCloser
	StdoutPipe io.ReadCloser
	StderrPipe io.ReadCloser

	StdinEnc  *encoding.Decoder
	StdoutEnc *encoding.Decoder
	StderrEnc *encoding.Decoder

	StdoutPrint bool
	StderrPrint bool

	wg sync.WaitGroup
}

// IsMatchStrs is whether str match or not.
func IsMatchStrs(str string, regStrs []string) (bool, error) {

	var err error

	if len(regStrs) == 0 {
		return true, nil
	}
	re, err := CompileStrs(regStrs)
	if err != nil {
		return false, err
	}
	return re.MatchString(str), nil
}

// CompileStrs is regexp strings compile to *regexp.Regexp.
func CompileStrs(regStrs []string) (*regexp.Regexp, error) {
	if len(regStrs) == 0 {
		return nil, fmt.Errorf("regStrs must be greater than or equal to 1")
	}
	regStr := "(" + strings.Join(regStrs, "|") + ")"
	re, err := regexp.Compile(regStr)
	if err != nil {
		return nil, err
	}
	return re, nil
}

// ScanPrintStdout is scan and print to stdout.
func ScanPrintStdout(scanner *bufio.Scanner, print bool) {
	for scanner.Scan() {
		if print {
			fmt.Fprintf(os.Stdout, "%s\n", scanner.Text())
		}
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

// ScanPrintStderr is scan and print to stderr.
func ScanPrintStderr(scanner *bufio.Scanner, print bool) {
	for scanner.Scan() {
		if print {
			fmt.Fprintf(os.Stderr, "%s\n", scanner.Text())
		}
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

// ReadToBuf is scan and store buffer.
func ReadToBuf(scanner *bufio.Scanner) (*bytes.Buffer, error) {
	buf := new(bytes.Buffer)
	for scanner.Scan() {
		buf.WriteString(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return buf, nil
}

// BaseName is get file name without extension.
func BaseName(path string) string {
	base := filepath.Base(path)
	ext := filepath.Ext(path)

	re := regexp.MustCompile(ext + "$")
	return re.ReplaceAllString(base, "")
}

// GetCmdPath returns cmd abs path.
func GetCmdPath(cmd string) (string, error) {
	if filepath.IsAbs(cmd) {
		return cmd, nil
	}
	return exec.LookPath(cmd)
}

// FailOnError is fail if err occured.
func FailOnError(err error) {
	if err != nil {
		log.Fatalf("%+v\n", err)
	}
}

// GetGlobArgs return glob files.
func GetGlobArgs(args []string) ([]string, error) {

	var a []string

	for _, v := range args {
		files, err := filepath.Glob(v)
		if err != nil {
			return nil, err
		}
		a = append(a, files...)
	}

	return a, nil
}

// CmdStart start cmdFile.
func (c *Cmd) CmdStart() error {

	var err error

	c.StdoutPipe, err = c.Cmd.StdoutPipe()
	if err != nil {
		return err
	}

	c.StderrPipe, err = c.Cmd.StderrPipe()
	if err != nil {
		return err
	}

	c.StdinPipe, err = c.Cmd.StdinPipe()
	if err != nil {
		return err
	}

	if c.StdoutEnc == nil {
		c.wg.Add(1)
		go func() {
			defer c.wg.Done()
			ScanPrintStdout(bufio.NewScanner(io.TeeReader(c.StdoutPipe, &c.Stdout)), c.StdoutPrint)
		}()
	} else {
		c.wg.Add(1)
		go func() {
			defer c.wg.Done()
			ScanPrintStdout(bufio.NewScanner(io.TeeReader(transform.NewReader(c.StdoutPipe, c.StdoutEnc), &c.Stdout)), c.StdoutPrint)
		}()
	}

	if c.StderrEnc == nil {
		c.wg.Add(1)
		go func() {
			defer c.wg.Done()
			ScanPrintStderr(bufio.NewScanner(io.TeeReader(c.StderrPipe, &c.Stderr)), c.StderrPrint)
		}()
	} else {
		c.wg.Add(1)
		go func() {
			defer c.wg.Done()
			ScanPrintStderr(bufio.NewScanner(io.TeeReader(transform.NewReader(c.StderrPipe, c.StderrEnc), &c.Stderr)), c.StderrPrint)
		}()
	}

	err = c.Cmd.Start()
	if err != nil {
		return err
	}

	return nil
}

// CmdWait wait command end.
func (c *Cmd) CmdWait() {
	c.wg.Wait()
	c.ExitError = c.Cmd.Wait()
	c.GetExitCode()
}

// CmdRun run command.
func (c *Cmd) CmdRun() error {

	var err error

	err = c.CmdStart()
	if err != nil {
		return err
	}

	c.CmdWait()
	return nil
}

// GetExitCode return command ExitCode.
func (c *Cmd) GetExitCode() {
	if c.ExitError != nil {
		if err, ok := c.ExitError.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				c.ExitCode = s.ExitStatus()
			}
		}
	}
}

// SurroundWord surround word.
func SurroundWord(w string, r rune) string {

	surRune := []rune(w)

	// Check already surrounded.
	if surRune[0] != r {
		surRune = append([]rune{r}, surRune...)
	}
	if surRune[len(surRune)-1] != r {
		surRune = append(surRune, r)
	}

	return string(surRune)
}
<commit_msg>:sparkles: Add pp package for pretty print object.<commit_after>package core

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"syscall"

	"github.com/k0kubun/pp"
	"github.com/mattn/go-shellwords"
	"golang.org/x/text/encoding"
	"golang.org/x/text/transform"
)

// Cmd is command infomation.
type Cmd struct {
	Cmd     *exec.Cmd
	CmdLine string

	Stdin  bytes.Buffer
	Stdout bytes.Buffer
	Stderr bytes.Buffer

	ExitError error
	ExitCode  int

	StdinEnc  *encoding.Decoder
	StdoutEnc *encoding.Decoder
	StderrEnc *encoding.Decoder

	StdoutPrint bool
	StderrPrint bool

	stdinPipe  bool
	stdoutPipe bool
	stderrPipe bool
	Wg         sync.WaitGroup
}

// IsMatchStrs is whether str match or not.
func IsMatchStrs(str string, regStrs []string) (bool, error) {

	var err error

	if len(regStrs) == 0 {
		return true, nil
	}
	re, err := CompileStrs(regStrs)
	if err != nil {
		return false, err
	}
	return re.MatchString(str), nil
}

// CompileStrs is regexp strings compile to *regexp.Regexp.
func CompileStrs(regStrs []string) (*regexp.Regexp, error) {
	if len(regStrs) == 0 {
		return nil, fmt.Errorf("regStrs must be greater than or equal to 1")
	}
	regStr := "(" + strings.Join(regStrs, "|") + ")"
	re, err := regexp.Compile(regStr)
	if err != nil {
		return nil, err
	}
	return re, nil
}

// ScanWrite is scan and write to io.Writer.
func ScanWrite(scanner *bufio.Scanner, writer io.Writer, print bool) {
	for scanner.Scan() {
		t := scanner.Text()
		fmt.Fprintf(writer, "%s\n", t)
		if print {
			fmt.Println(t)
		}
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

// ScanPrintStdout is scan and print to stdout.
func ScanPrintStdout(scanner *bufio.Scanner, print bool) {
	for scanner.Scan() {
		if print {
			fmt.Fprintf(os.Stdout, "%s\n", scanner.Text())
		}
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

// ScanPrintStderr is scan and print to stderr.
func ScanPrintStderr(scanner *bufio.Scanner, print bool) {
	for scanner.Scan() {
		if print {
			fmt.Fprintf(os.Stderr, "%s\n", scanner.Text())
		}
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

// ReadToBuf is scan and store buffer.
func ReadToBuf(scanner *bufio.Scanner) (*bytes.Buffer, error) {
	buf := new(bytes.Buffer)
	for scanner.Scan() {
		buf.WriteString(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return buf, nil
}

// BaseName is get file name without extension.
func BaseName(path string) string {
	base := filepath.Base(path)
	ext := filepath.Ext(path)

	re := regexp.MustCompile(ext + "$")
	return re.ReplaceAllString(base, "")
}

// GetCmdPath returns cmd abs path.
func GetCmdPath(cmd string) (string, error) {
	if filepath.IsAbs(cmd) {
		return cmd, nil
	}
	return exec.LookPath(cmd)
}

// FailOnError is fail if err occured.
func FailOnError(err error) {
	if err != nil {
		log.Fatalf("%+v\n", err)
	}
}

// GetGlobArgs return glob files.
func GetGlobArgs(args []string) ([]string, error) {

	var a []string

	for _, v := range args {
		files, err := filepath.Glob(v)
		if err != nil {
			return nil, err
		}
		a = append(a, files...)
	}

	return a, nil
}

// NewCmd create Cmd struct pointer.
func NewCmd(cmdLine string) (*Cmd, error) {

	parseCmd, err := shellwords.Parse(cmdLine)
	if err != nil {
		return nil, err
	}
	return &Cmd{Cmd: exec.Command(parseCmd[0], parseCmd[1:]...)}, nil
}

// StdoutPipe return exec.StdoutPipe.
func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
	c.stdoutPipe = true
	return c.Cmd.StdoutPipe()
}

// StderrPipe return exec.StderrPipe.
func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
	c.stderrPipe = true
	return c.Cmd.StderrPipe()
}

// StdinPipe return exec.StdinPipe.
func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
	c.stdinPipe = true
	return c.Cmd.StdinPipe()
}

// StdoutScanner make bufio.Scanner.
func (c *Cmd) StdoutScanner() (*bufio.Scanner, error) {
	c.stdoutPipe = true
	r, err := c.Cmd.StdoutPipe()
	if c.StdoutEnc != nil {
		if err != nil {
			return nil, err
		}
		return bufio.NewScanner(transform.NewReader(r, c.StdoutEnc)), nil
	}
	return bufio.NewScanner(r), nil
}

// StderrScanner make bufio.Scanner.
func (c *Cmd) StderrScanner() (*bufio.Scanner, error) {
	c.stderrPipe = true
	r, err := c.Cmd.StderrPipe()
	if c.StderrEnc != nil {
		if err != nil {
			return nil, err
		}
		return bufio.NewScanner(transform.NewReader(r, c.StderrEnc)), nil
	}
	return bufio.NewScanner(r), nil
}

// CmdStart start cmdFile.
func (c *Cmd) CmdStart() error {

	var err error

	// CmdLine check
	if c.CmdLine != "" {
		parseCmd, err := shellwords.Parse(c.CmdLine)
		if err != nil {
			return err
		}
		c.Cmd = exec.Command(parseCmd[0], parseCmd[1:]...)
	}

	var stdoutPipe, stderrPipe io.ReadCloser

	if !c.stdoutPipe {
		stdoutPipe, err = c.Cmd.StdoutPipe()
		if err != nil {
			return err
		}
	}
	if !c.stderrPipe {
		stderrPipe, err = c.Cmd.StderrPipe()
		if err != nil {
			return err
		}
	}

	// Start command.
	err = c.Cmd.Start()
	if err != nil {
		return err
	}

	scanWrite := func(c *Cmd, r io.ReadCloser, w io.Writer, enc *encoding.Decoder, print bool) {
		if r == nil {
			return
		}
		c.Wg.Add(1)
		go func() {
			defer c.Wg.Done()
			if enc == nil {
				ScanWrite(bufio.NewScanner(r), w, print)
			} else {
				ScanWrite(bufio.NewScanner(transform.NewReader(r, enc)), w, print)
			}
		}()
	}

	scanWrite(c, stdoutPipe, &c.Stdout, c.StdoutEnc, c.StdoutPrint)
	scanWrite(c, stderrPipe, &c.Stderr, c.StderrEnc, c.StderrPrint)

	// if c.StdoutEnc == nil {
	// 	c.wg.Add(1)
	// 	go func() {
	// 		defer c.wg.Done()
	// 		ScanWrite(bufio.NewScanner(c.StdoutPipe), &c.Stdout)
	// ScanPrintStdout(bufio.NewScanner(io.TeeReader(c.StdoutPipe, &c.Stdout)), c.StdoutPrint)
	// 	}()
	// } else {
	// 	c.wg.Add(1)
	// 	go func() {
	// 		defer c.wg.Done()
	// 		ScanWrite(bufio.NewScanner(transform.NewReader(c.StdoutPipe, c.StdoutEnc)), &c.Stdout)
	// 		// ScanPrintStdout(bufio.NewScanner(io.TeeReader(transform.NewReader(c.StdoutPipe, c.StdoutEnc), &c.Stdout)), c.StdoutPrint)
	// 	}()
	// }

	// if c.StderrEnc == nil {
	// 	c.wg.Add(1)
	// 	go func() {
	// 		defer c.wg.Done()
	// 		ScanWrite(bufio.NewScanner(c.StderrPipe), &c.Stderr)
	// 		// ScanPrintStderr(bufio.NewScanner(io.TeeReader(c.StderrPipe, &c.Stderr)), c.StderrPrint)
	// 	}()
	// } else {
	// 	c.wg.Add(1)
	// 	go func() {
	// 		defer c.wg.Done()
	// 		ScanWrite(bufio.NewScanner(transform.NewReader(c.StderrPipe, c.StderrEnc)), &c.Stderr)
	// 		// ScanPrintStderr(bufio.NewScanner(io.TeeReader(transform.NewReader(c.StderrPipe, c.StderrEnc), &c.Stderr)), c.StderrPrint)
	// 	}()
	// }

	if c.CmdLine == "" {
		c.CmdLine = strings.Join(c.Cmd.Args, " ")
	}

	return nil
}

// CmdWait wait command end.
func (c *Cmd) CmdWait() {
	c.Wg.Wait()
	c.ExitError = c.Cmd.Wait()
	c.GetExitCode()
}

// CmdRun run command.
func (c *Cmd) CmdRun() error {

	var err error

	err = c.CmdStart()
	if err != nil {
		return err
	}

	c.CmdWait()
	return nil
}

// GetExitCode return command ExitCode.
func (c *Cmd) GetExitCode() {
	if c.ExitError != nil {
		if err, ok := c.ExitError.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				c.ExitCode = s.ExitStatus()
			}
		}
	}
}

// SurroundWord surround word.
func SurroundWord(w string, r rune) string {

	surRune := []rune(w)

	// Check already surrounded.
	if surRune[0] != r {
		surRune = append([]rune{r}, surRune...)
	}
	if surRune[len(surRune)-1] != r {
		surRune = append(surRune, r)
	}

	return string(surRune)
}

// PP is wrapper of pp Println.
func PP(a ...interface{}) (int, error) {
	return pp.Println(a...)
}

// PPf is wrapper of pp Printf.
func PPf(format string, a ...interface{}) (int, error) {
	return pp.Printf(format, a...)
}
<|endoftext|>


<commit_before>package currency

import (
	"testing"
)

func TestConverterCreation(t *testing.T) {
	c, _ := NewConverter()
	c.Age()
}
<commit_msg>Added more test.<commit_after>package currency

import (
	"log"
	"math/rand"
	"testing"
)

var converter *CurrencyConverter

func init() {
	c, err := NewConverter()
	if err != nil {
		log.Fatal("Error initializing converter.", err)
	}
	converter = c
}

func TestConverterCreation(t *testing.T) {
	currencies := converter.GetCurrencies()

	// must add one for EUR
	if len(currencies)+1 != len(currencyData) {
		t.Fatalf("Wrong currency number (%d vs %d)", len(currencies), len(currencyData))
	}
}

func TestNoConvertion(t *testing.T) {
	for i := 0; i < 100; i++ {
		amount := rand.Float64() * float64(i)
		converted, err := converter.Convert(amount, "EUR", "EUR")
		if err != nil {
			t.Fatal("Convertion failed.", err)
		}
		if amount != converted {
			t.Fatal("Convertion to/from same currency gave different amount.")
		}
	}
}

func TestRenew(t *testing.T) {
	err := converter.Renew()
	if err != nil {
		t.Fatal("Renew failed.", err)
	}
}
<|endoftext|>


<commit_before>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/rs/xid"
)

const (
	// Read buffer size for websocket upgrader
	readBufferSize = 1024
	// Write buffer size for websocker upgrader
	writeBufferSize = 1024
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second
	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second
	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
	// Maximum message size allowed from peer.
	maxMessageSize = 512
	// TextMessage denotes a text data message. The text message payload is
	// interpreted as UTF-8 encoded text data.
	TextMessage = 1
	// BinaryMessage denotes a binary data message.
	BinaryMessage = 2
	// CloseMessage denotes a close control message. The optional message
	// payload contains a numeric code and text. Use the FormatCloseMessage
	// function to format a close message payload.
	CloseMessage = 8
	// PingMessage denotes a ping control message. The optional message payload
	// is UTF-8 encoded text.
	PingMessage = 9
	// PongMessage denotes a ping control message. The optional message payload
	// is UTF-8 encoded text.
	PongMessage = 10
	// Message Types
)

var (
	// Websocket Upgrader
	upgrader = websocket.Upgrader{
		ReadBufferSize:  readBufferSize,
		WriteBufferSize: writeBufferSize,
	}
	// Guid to generate globally unique id
	guid = xid.New()
)

// NewSphere creates a new instance of Sphere
func NewSphere(brokers ...Agent) *Sphere {
	// declare agent
	var broker Agent
	// set declared agent if parameter exists
	for _, i := range brokers {
		broker = i
		break
	}
	if broker == nil {
		broker = NewSimpleBroker()
	}
	// creates sphere instance
	sphere := &Sphere{
		agent:       broker,
		connections: make(map[string]*Connection),
		register:    make(chan *Connection),
		unregister:  make(chan *Connection),
	}
	go sphere.broker()
	go sphere.queue()
	return sphere
}

// Sphere represents an entire Websocket instance
type Sphere struct {
	// a broker agent
	agent Agent
	// list of active connections
	connections map[string]*Connection
	// register requests from the connection(s)
	register chan *Connection
	// unregister requests from the connection(s)
	unregister chan *Connection
}

// Broker handler for Sphere
func (sphere *Sphere) broker() {
}

// Queue handler for Sphere
func (sphere *Sphere) queue() {
	for {
		select {
		case conn := <-sphere.register:
			sphere.connections[conn.id] = conn
		case conn := <-sphere.unregister:
			delete(sphere.connections, conn.id)
			defer conn.Close()
		}
	}
}

// Handler handles and creates websocket connection
func (sphere *Sphere) Handler(w http.ResponseWriter, r *http.Request) {
	if conn, err := NewConnection(w, r); err == nil {
		sphere.register <- conn
		go conn.writePump()
		conn.readPump()
		defer func() {
			sphere.unregister <- conn
		}()
	}
}
<commit_msg>use ConcurrentMap instead of Go Map<commit_after>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/rs/xid"
	"github.com/streamrail/concurrent-map"
)

const (
	// Read buffer size for websocket upgrader
	readBufferSize = 1024
	// Write buffer size for websocker upgrader
	writeBufferSize = 1024
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second
	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second
	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
	// Maximum message size allowed from peer.
	maxMessageSize = 512
	// TextMessage denotes a text data message. The text message payload is
	// interpreted as UTF-8 encoded text data.
	TextMessage = 1
	// BinaryMessage denotes a binary data message.
	BinaryMessage = 2
	// CloseMessage denotes a close control message. The optional message
	// payload contains a numeric code and text. Use the FormatCloseMessage
	// function to format a close message payload.
	CloseMessage = 8
	// PingMessage denotes a ping control message. The optional message payload
	// is UTF-8 encoded text.
	PingMessage = 9
	// PongMessage denotes a ping control message. The optional message payload
	// is UTF-8 encoded text.
	PongMessage = 10
	// Message Types
)

var (
	// Websocket Upgrader
	upgrader = websocket.Upgrader{
		ReadBufferSize:  readBufferSize,
		WriteBufferSize: writeBufferSize,
	}
	// Guid to generate globally unique id
	guid = xid.New()
)

// NewSphere creates a new instance of Sphere
func NewSphere(brokers ...Agent) *Sphere {
	// declare agent
	var broker Agent
	// set declared agent if parameter exists
	for _, i := range brokers {
		broker = i
		break
	}
	if broker == nil {
		broker = NewSimpleBroker()
	}
	// creates sphere instance
	sphere := &Sphere{
		agent:       broker,
		connections: cmap.New(),
		register:    make(chan *Connection),
		unregister:  make(chan *Connection),
	}
	go sphere.broker()
	go sphere.queue()
	return sphere
}

// Sphere represents an entire Websocket instance
type Sphere struct {
	// a broker agent
	agent Agent
	// list of active connections
	connections cmap.ConcurrentMap
	// register requests from the connection(s)
	register chan *Connection
	// unregister requests from the connection(s)
	unregister chan *Connection
}

// Broker handler for Sphere
func (sphere *Sphere) broker() {
}

// Queue handler for Sphere
func (sphere *Sphere) queue() {
	for {
		select {
		case conn := <-sphere.register:
			sphere.connections.Set(conn.id, conn)
		case conn := <-sphere.unregister:
			sphere.connections.Remove(conn.id)
			defer conn.Close()
		}
	}
}

// Handler handles and creates websocket connection
func (sphere *Sphere) Handler(w http.ResponseWriter, r *http.Request) {
	if conn, err := NewConnection(w, r); err == nil {
		sphere.register <- conn
		go conn.writePump()
		conn.readPump()
		defer func() {
			sphere.unregister <- conn
		}()
	}
}
<|endoftext|>


<commit_before>package kasper

import (
	"github.com/Shopify/sarama"
	"log"
)

type outgoingMessageSender struct {
	pp               *partitionProcessor
	incomingMessage  *IncomingMessage
	producerMessages []*sarama.ProducerMessage
}

func newOutgoingMessageSender(pp *partitionProcessor, incomingMessage *IncomingMessage) *outgoingMessageSender {
	return &outgoingMessageSender{
		pp,
		incomingMessage,
		[]*sarama.ProducerMessage{},
	}
}

func (sender *outgoingMessageSender) createInFlightMessageGroup() *inFlightMessageGroup {
	res := inFlightMessageGroup{
		incomingMessage:  sender.incomingMessage,
		inFlightMessages: nil,
	}
	for _, msg := range sender.producerMessages {
		res.inFlightMessages = append(res.inFlightMessages, &inFlightMessage{
			msg: msg,
			ack: false,
		})
	}
	return &res
}

type inFlightMessage struct {
	msg *sarama.ProducerMessage
	ack bool
}

type inFlightMessageGroup struct {
	incomingMessage  *IncomingMessage
	inFlightMessages []*inFlightMessage
}

func (group *inFlightMessageGroup) allAcksAreTrue() bool {
	if group.inFlightMessages == nil {
		return true
	}
	for _, msg := range group.inFlightMessages {
		if msg.ack == false {
			return false
		}
	}
	return true
}

func (sender *outgoingMessageSender) Send(msg OutgoingMessage) {
	topicSerde, ok := sender.pp.topicProcessor.config.TopicSerdes[msg.Topic]
	if !ok {
		log.Fatalf("Could not find Serde for topic '%s'", msg.Topic)
	}
	producerMessage := &sarama.ProducerMessage{
		Topic:     msg.Topic,
		Key:       sarama.ByteEncoder(topicSerde.KeySerde.Serialize(msg.Key)),
		Value:     sarama.ByteEncoder(topicSerde.ValueSerde.Serialize(msg.Value)),
		Partition: msg.Partition,
		Metadata:  sender.incomingMessage,
	}
	sender.pp.producer.Input() <- producerMessage
	sender.producerMessages = append(sender.producerMessages, producerMessage)
}

type PartitionProcessorContext struct {
	// TODO
}

type MessageProcessor interface {
	Process(IncomingMessage, Sender, Coordinator)
}

type Initializer interface {
	Initialize(TopicProcessorConfig, PartitionProcessorContext)
}

type partitionProcessor struct {
	topicProcessor        *TopicProcessor
	coordinator           Coordinator
	consumers             []sarama.PartitionConsumer
	offsetManagers        map[string]sarama.PartitionOffsetManager
	producer              sarama.AsyncProducer
	messageProcessor      MessageProcessor
	inputTopics           []string
	partition             int32
	inFlightMessageGroups map[string][]*inFlightMessageGroup
}

func (pp *partitionProcessor) consumerMessageChannels() []<-chan *sarama.ConsumerMessage {
	chans := make([]<-chan *sarama.ConsumerMessage, len(pp.consumers))
	for i, consumer := range pp.consumers {
		chans[i] = consumer.Messages()
	}
	return chans
}

func mustSetupProducer(brokers []string, producerClientId string) sarama.AsyncProducer {
	saramaConfig := sarama.NewConfig()
	saramaConfig.ClientID = producerClientId
	saramaConfig.Producer.Return.Successes = true
	saramaConfig.Producer.Partitioner = sarama.NewManualPartitioner
	saramaConfig.Producer.RequiredAcks = sarama.WaitForAll /* TODO: make this configurable */

	producer, err := sarama.NewAsyncProducer(brokers, saramaConfig)
	if err != nil {
		log.Fatal(err)
	}

	return producer
}

func newPartitionProcessor(tp *TopicProcessor, mp MessageProcessor, partition int32) *partitionProcessor {
	// FIXME store the consumer? close it?
	consumer, err := sarama.NewConsumerFromClient(tp.client)
	if err != nil {
		log.Fatal(err)
	}
	partitionConsumers := make([]sarama.PartitionConsumer, len(tp.inputTopics))
	partitionOffsetManagers := make(map[string]sarama.PartitionOffsetManager)
	for i, topic := range tp.inputTopics {
		pom, err := tp.offsetManager.ManagePartition(topic, partition)
		if err != nil {
			log.Fatal(err)
		}
		newestOffset, err := tp.client.GetOffset(topic, partition, sarama.OffsetNewest)
		if err != nil {
			log.Fatal(err)
		}
		nextOffset, _ := pom.NextOffset()
		if nextOffset > newestOffset {
			nextOffset = sarama.OffsetNewest
		}
		c, err := consumer.ConsumePartition(topic, partition, nextOffset)
		if err != nil {
			log.Fatal(err)
		}
		partitionConsumers[i] = c
		partitionOffsetManagers[topic] = pom
	}
	var coordinator Coordinator = nil // FIXME
	producer := mustSetupProducer(tp.config.BrokerList, tp.config.producerClientId(tp.containerId))
	return &partitionProcessor{
		tp,
		coordinator,
		partitionConsumers,
		partitionOffsetManagers,
		producer,
		mp,
		tp.inputTopics,
		partition,
		make(map[string][]*inFlightMessageGroup),
	}
}

func (pp *partitionProcessor) processConsumerMessage(consumerMessage *sarama.ConsumerMessage) {
	topicSerde, ok := pp.topicProcessor.config.TopicSerdes[consumerMessage.Topic]
	if !ok {
		log.Fatalf("Could not find Serde for topic '%s'", consumerMessage.Topic)
	}
	incomingMessage := IncomingMessage{
		Topic:     consumerMessage.Topic,
		Partition: consumerMessage.Partition,
		Offset:    consumerMessage.Offset,
		Key:       topicSerde.KeySerde.Deserialize(consumerMessage.Key),
		Value:     topicSerde.ValueSerde.Deserialize(consumerMessage.Value),
		Timestamp: consumerMessage.Timestamp,
	}
	outgoingMessageSender := newOutgoingMessageSender(pp, &incomingMessage)
	pp.messageProcessor.Process(incomingMessage, outgoingMessageSender, pp.coordinator)
	inFlightMessageGroup := outgoingMessageSender.createInFlightMessageGroup()
	pp.inFlightMessageGroups[consumerMessage.Topic] = append(
		pp.inFlightMessageGroups[consumerMessage.Topic],
		inFlightMessageGroup,
	)
}

func (pp *partitionProcessor) markOffsets() {
	for _, topic := range pp.topicProcessor.inputTopics {
		pp.markOffsetsForTopic(topic)
	}
}

func (pp *partitionProcessor) markOffsetsForTopic(topic string) {
	var offset int64 = -1
	for len(pp.inFlightMessageGroups[topic]) > 0 {
		group := pp.inFlightMessageGroups[topic][0]
		if group.allAcksAreTrue() {
			offset = group.incomingMessage.Offset
			pp.inFlightMessageGroups[topic] = pp.inFlightMessageGroups[topic][1:]
		} else {
			break
		}
	}
	if offset != -1 {
		offsetManager := pp.offsetManagers[topic]
		offsetManager.MarkOffset(offset + 1, "")
	}
}

func (pp *partitionProcessor) processProducerMessageSuccess(producerMessage *sarama.ProducerMessage) {
	incomingMessage := producerMessage.Metadata.(*IncomingMessage)
	foundGroup := false
	for _, group := range pp.inFlightMessageGroups[incomingMessage.Topic] {
		if group.incomingMessage == incomingMessage {
			foundGroup = true
			foundMsg := false
			for _, inFlightMessage := range group.inFlightMessages {
				if inFlightMessage.msg == producerMessage {
					foundMsg = true
					inFlightMessage.ack = true
					break
				}
			}
			if !foundMsg {
				log.Fatal("Could not find producer message in inFlightMessageGroups")
			}
			break
		}
	}
	if !foundGroup {
		log.Fatal("Could not find group in inFlightMessageGroups")
	}
}
<commit_msg>Add todo<commit_after>package kasper

import (
	"github.com/Shopify/sarama"
	"log"
)

type outgoingMessageSender struct {
	pp               *partitionProcessor
	incomingMessage  *IncomingMessage
	producerMessages []*sarama.ProducerMessage
}

func newOutgoingMessageSender(pp *partitionProcessor, incomingMessage *IncomingMessage) *outgoingMessageSender {
	return &outgoingMessageSender{
		pp,
		incomingMessage,
		[]*sarama.ProducerMessage{},
	}
}

func (sender *outgoingMessageSender) createInFlightMessageGroup() *inFlightMessageGroup {
	res := inFlightMessageGroup{
		incomingMessage:  sender.incomingMessage,
		inFlightMessages: nil,
	}
	for _, msg := range sender.producerMessages {
		res.inFlightMessages = append(res.inFlightMessages, &inFlightMessage{
			msg: msg,
			ack: false,
		})
	}
	return &res
}

type inFlightMessage struct {
	msg *sarama.ProducerMessage
	ack bool
}

type inFlightMessageGroup struct {
	incomingMessage  *IncomingMessage
	inFlightMessages []*inFlightMessage
}

func (group *inFlightMessageGroup) allAcksAreTrue() bool {
	if group.inFlightMessages == nil {
		return true
	}
	for _, msg := range group.inFlightMessages {
		if msg.ack == false {
			return false
		}
	}
	return true
}

func (sender *outgoingMessageSender) Send(msg OutgoingMessage) {
	topicSerde, ok := sender.pp.topicProcessor.config.TopicSerdes[msg.Topic]
	if !ok {
		log.Fatalf("Could not find Serde for topic '%s'", msg.Topic)
	}
	producerMessage := &sarama.ProducerMessage{
		Topic:     msg.Topic,
		Key:       sarama.ByteEncoder(topicSerde.KeySerde.Serialize(msg.Key)),
		Value:     sarama.ByteEncoder(topicSerde.ValueSerde.Serialize(msg.Value)),
		Partition: msg.Partition,
		Metadata:  sender.incomingMessage,
	}
	sender.pp.producer.Input() <- producerMessage
	sender.producerMessages = append(sender.producerMessages, producerMessage)
}

type PartitionProcessorContext struct {
	// TODO
}

type MessageProcessor interface {
	Process(IncomingMessage, Sender, Coordinator)
}

type Initializer interface {
	Initialize(TopicProcessorConfig, PartitionProcessorContext)
}

type partitionProcessor struct {
	topicProcessor        *TopicProcessor
	coordinator           Coordinator
	consumers             []sarama.PartitionConsumer
	offsetManagers        map[string]sarama.PartitionOffsetManager
	producer              sarama.AsyncProducer
	messageProcessor      MessageProcessor
	inputTopics           []string
	partition             int32
	inFlightMessageGroups map[string][]*inFlightMessageGroup
}

func (pp *partitionProcessor) consumerMessageChannels() []<-chan *sarama.ConsumerMessage {
	chans := make([]<-chan *sarama.ConsumerMessage, len(pp.consumers))
	for i, consumer := range pp.consumers {
		chans[i] = consumer.Messages()
	}
	return chans
}

func mustSetupProducer(brokers []string, producerClientId string) sarama.AsyncProducer {
	saramaConfig := sarama.NewConfig()
	saramaConfig.ClientID = producerClientId
	saramaConfig.Producer.Return.Successes = true
	saramaConfig.Producer.Partitioner = sarama.NewManualPartitioner
	saramaConfig.Producer.RequiredAcks = sarama.WaitForAll /* TODO: make this configurable */

	producer, err := sarama.NewAsyncProducer(brokers, saramaConfig)
	if err != nil {
		log.Fatal(err)
	}

	return producer
}

func newPartitionProcessor(tp *TopicProcessor, mp MessageProcessor, partition int32) *partitionProcessor {
	// FIXME store the consumer? close it?
	consumer, err := sarama.NewConsumerFromClient(tp.client)
	if err != nil {
		log.Fatal(err)
	}
	partitionConsumers := make([]sarama.PartitionConsumer, len(tp.inputTopics))
	partitionOffsetManagers := make(map[string]sarama.PartitionOffsetManager)
	for i, topic := range tp.inputTopics {
		pom, err := tp.offsetManager.ManagePartition(topic, partition)
		if err != nil {
			log.Fatal(err)
		}
		newestOffset, err := tp.client.GetOffset(topic, partition, sarama.OffsetNewest)
		if err != nil {
			log.Fatal(err)
		}
		nextOffset, _ := pom.NextOffset()
		if nextOffset > newestOffset {
			nextOffset = sarama.OffsetNewest
		}
		c, err := consumer.ConsumePartition(topic, partition, nextOffset)
		if err != nil {
			log.Fatal(err)
		}
		partitionConsumers[i] = c
		partitionOffsetManagers[topic] = pom
	}
	var coordinator Coordinator = nil // FIXME
	producer := mustSetupProducer(tp.config.BrokerList, tp.config.producerClientId(tp.containerId))
	return &partitionProcessor{
		tp,
		coordinator,
		partitionConsumers,
		partitionOffsetManagers,
		producer,
		mp,
		tp.inputTopics,
		partition,
		make(map[string][]*inFlightMessageGroup),
	}
}

func (pp *partitionProcessor) processConsumerMessage(consumerMessage *sarama.ConsumerMessage) {
	topicSerde, ok := pp.topicProcessor.config.TopicSerdes[consumerMessage.Topic]
	if !ok {
		log.Fatalf("Could not find Serde for topic '%s'", consumerMessage.Topic)
	}
	incomingMessage := IncomingMessage{
		Topic:     consumerMessage.Topic,
		Partition: consumerMessage.Partition,
		Offset:    consumerMessage.Offset,
		Key:       topicSerde.KeySerde.Deserialize(consumerMessage.Key),
		Value:     topicSerde.ValueSerde.Deserialize(consumerMessage.Value),
		Timestamp: consumerMessage.Timestamp,
	}
	outgoingMessageSender := newOutgoingMessageSender(pp, &incomingMessage)
	pp.messageProcessor.Process(incomingMessage, outgoingMessageSender, pp.coordinator)
	inFlightMessageGroup := outgoingMessageSender.createInFlightMessageGroup()
	pp.inFlightMessageGroups[consumerMessage.Topic] = append(
		pp.inFlightMessageGroups[consumerMessage.Topic],
		inFlightMessageGroup,
	)
	// TODO: we need to compress inFlightMessageGroups[consumerMessage.Topic] here to avoid using tons of memory
}

func (pp *partitionProcessor) markOffsets() {
	for _, topic := range pp.topicProcessor.inputTopics {
		pp.markOffsetsForTopic(topic)
	}
}

func (pp *partitionProcessor) markOffsetsForTopic(topic string) {
	var offset int64 = -1
	for len(pp.inFlightMessageGroups[topic]) > 0 {
		group := pp.inFlightMessageGroups[topic][0]
		if group.allAcksAreTrue() {
			offset = group.incomingMessage.Offset
			pp.inFlightMessageGroups[topic] = pp.inFlightMessageGroups[topic][1:] /* TODO: check this is efficient and doesn't leak */
		} else {
			break
		}
	}
	if offset != -1 {
		offsetManager := pp.offsetManagers[topic]
		offsetManager.MarkOffset(offset + 1, "")
	}
}

func (pp *partitionProcessor) processProducerMessageSuccess(producerMessage *sarama.ProducerMessage) {
	incomingMessage := producerMessage.Metadata.(*IncomingMessage)
	foundGroup := false
	for _, group := range pp.inFlightMessageGroups[incomingMessage.Topic] {
		if group.incomingMessage == incomingMessage {
			foundGroup = true
			foundMsg := false
			for _, inFlightMessage := range group.inFlightMessages {
				if inFlightMessage.msg == producerMessage {
					foundMsg = true
					inFlightMessage.ack = true
					break
				}
			}
			if !foundMsg {
				log.Fatal("Could not find producer message in inFlightMessageGroups")
			}
			break
		}
	}
	if !foundGroup {
		log.Fatal("Could not find group in inFlightMessageGroups")
	}
}
<|endoftext|>


<commit_before>package splash

import (
	"bytes"
	"fmt"
	"strings"
)

const (
	// Attributes
	Reset Property = iota
	Bold
	Dim
	Italic
	Underline
	Blink
	FastBlink
	Reverse
	Hidden
	CrossedOut
)

const (
	// Foreground colors
	Black Property = iota + 30
	Red
	Green
	Yellow
	Blue
	Magenta
	Cyan
	White
)

const (
	// Background colors
	BgBlack Property = iota + 40
	BgRed
	BgGreen
	BgYellow
	BgBlue
	BgMagenta
	BgCyan
	BgWhite
)

var attributes = map[rune]Property{
	'b': Bold,
	'd': Dim,
	'i': Italic,
	'u': Underline,
	'B': Blink,
	'f': FastBlink,
	'r': Reverse,
	'h': Hidden,
	'c': CrossedOut,
}

var colors = map[string]Property{
	"black":   Black,
	"red":     Red,
	"green":   Green,
	"yellow":  Yellow,
	"blue":    Blue,
	"magenta": Magenta,
	"cyan":    Cyan,
	"white":   White,
}

// WrapPromptString encloses the input string meant to be displayed in
// the shell prompt string in the escape sequences \[ and \].
func WrapPromptString(str string) string {
	return fmt.Sprintf(`\[%s\]`, str)
}

// Property represents either a color (foreground or background) or a text
// attribute (bold, underline, etc.).
type Property int

// String returns the printable string representation of the property.
func (p Property) String() string {
	return fmt.Sprintf("\u001b[%dm", p)
}

// Sprint formats using the default formats for its operands and returns the
// resulting string. Spaces are added between operands when neither is a
// string. The output string is wrapped in the value of the property and
// a reset is applied at end.
func (p Property) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprint(a...), Reset)
}

// Sprintf formats according to a format specifier and returns the
// resulting string. The output string is wrapped in the value of the property
// and a style is applied at end.
func (p Property) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprintf(format, a...), Reset)
}

// Sprintln formats using the default formats for its operands and returns
// the resulting string. Spaces are always added between operands and a
// newline is appended. The output string is wrapped in the value of the
// property and a reset is applied at end.
func (p Property) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", p, fmt.Sprint(a...), Reset)
}

// Style is a reusable collection of properties.
type Style []Property

// NewStyle returns a new style from property arguments passed in.
func NewStyle(props ...Property) Style {
	return props
}

// ParseStyle returns a new style from the properties specified in the style
// parameter.
func ParseStyle(style string) Style {
	props := []Property{}

	style = strings.TrimSpace(strings.Replace(style, " ", "", -1))
	if style == "" {
		return props
	}

	if style == "reset" {
		return append(props, Reset)
	}

	// Parse attributes
	tokens := strings.Split(style, "+")
	if len(tokens) > 1 {
		for _, attr := range tokens[1] {
			if prop, ok := attributes[attr]; ok {
				props = append(props, prop)
			}
		}
	}

	// Parse colors
	tokens = strings.Split(strings.ToLower(tokens[0]), ":")
	if prop, ok := colors[tokens[0]]; ok {
		props = append(props, prop)
	}

	if len(tokens) > 1 {
		if prop, ok := colors[tokens[1]]; ok {
			// Add property as a background color
			props = append(props, prop+10)
		}
	}

	return props
}

// String returns the printable string representation of the style.
func (s Style) String() string {
	var buffer bytes.Buffer
	for _, prop := range s {
		buffer.WriteString(prop.String())
	}

	return buffer.String()
}

// Sprint formats using the default formats for its operands and returns the
// resulting string. Spaces are added between operands when neither is a
// string. The output string is wrapped in the value of the style and a reset
// is applied at end.
func (s Style) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprint(a...), Reset)
}

// Sprintf formats according to a format specifier and returns the
// resulting string. The output string is wrapped in the value of the style
// and a reset is applied at end.
func (s Style) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprintf(format, a...), Reset)
}

// Sprintln formats using the default formats for its operands and returns
// the resulting string. Spaces are always added between operands and a
// newline is appended. The output string is wrapped in the value of the style
// and a reset is applied at end.
func (s Style) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", s, fmt.Sprint(a...), Reset)
}
<commit_msg>Add PromptEscape method to properties and styles<commit_after>package splash

import (
	"bytes"
	"fmt"
	"strings"
)

const (
	// Attributes
	Reset Property = iota
	Bold
	Dim
	Italic
	Underline
	Blink
	FastBlink
	Reverse
	Hidden
	CrossedOut
)

const (
	// Foreground colors
	Black Property = iota + 30
	Red
	Green
	Yellow
	Blue
	Magenta
	Cyan
	White
)

const (
	// Background colors
	BgBlack Property = iota + 40
	BgRed
	BgGreen
	BgYellow
	BgBlue
	BgMagenta
	BgCyan
	BgWhite
)

var attributes = map[rune]Property{
	'b': Bold,
	'd': Dim,
	'i': Italic,
	'u': Underline,
	'B': Blink,
	'f': FastBlink,
	'r': Reverse,
	'h': Hidden,
	'c': CrossedOut,
}

var colors = map[string]Property{
	"black":   Black,
	"red":     Red,
	"green":   Green,
	"yellow":  Yellow,
	"blue":    Blue,
	"magenta": Magenta,
	"cyan":    Cyan,
	"white":   White,
}

// Property represents either a color (foreground or background) or a text
// attribute (bold, underline, etc.).
type Property int

// String returns the printable string representation of the property.
func (p Property) String() string {
	return fmt.Sprintf("\u001b[%dm", p)
}

// Sprint formats using the default formats for its operands and returns the
// resulting string. Spaces are added between operands when neither is a
// string. The output string is wrapped in the value of the property and
// a reset is applied at end.
func (p Property) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprint(a...), Reset)
}

// Sprintf formats according to a format specifier and returns the
// resulting string. The output string is wrapped in the value of the property
// and a style is applied at end.
func (p Property) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprintf(format, a...), Reset)
}

// Sprintln formats using the default formats for its operands and returns
// the resulting string. Spaces are always added between operands and a
// newline is appended. The output string is wrapped in the value of the
// property and a reset is applied at end.
func (p Property) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", p, fmt.Sprint(a...), Reset)
}

// PromptEscape encloses the property to be displayed in the shell prompt
// string in the escape sequences \[ and \].
func (p Property) PromptEscape() string {
	return fmt.Sprintf(`\[%s\]`, p)
}

// Style is a reusable collection of properties.
type Style []Property

// NewStyle returns a new style from property arguments passed in.
func NewStyle(props ...Property) Style {
	return props
}

// ParseStyle returns a new style from the properties specified in the style
// parameter.
func ParseStyle(style string) Style {
	props := []Property{}

	style = strings.TrimSpace(strings.Replace(style, " ", "", -1))
	if style == "" {
		return props
	}

	if style == "reset" {
		return append(props, Reset)
	}

	// Parse attributes
	tokens := strings.Split(style, "+")
	if len(tokens) > 1 {
		for _, attr := range tokens[1] {
			if prop, ok := attributes[attr]; ok {
				props = append(props, prop)
			}
		}
	}

	// Parse colors
	tokens = strings.Split(strings.ToLower(tokens[0]), ":")
	if prop, ok := colors[tokens[0]]; ok {
		props = append(props, prop)
	}

	if len(tokens) > 1 {
		if prop, ok := colors[tokens[1]]; ok {
			// Add property as a background color
			props = append(props, prop+10)
		}
	}

	return props
}

// String returns the printable string representation of the style.
func (s Style) String() string {
	var buffer bytes.Buffer
	for _, prop := range s {
		buffer.WriteString(prop.String())
	}

	return buffer.String()
}

// Sprint formats using the default formats for its operands and returns the
// resulting string. Spaces are added between operands when neither is a
// string. The output string is wrapped in the value of the style and a reset
// is applied at end.
func (s Style) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprint(a...), Reset)
}

// Sprintf formats according to a format specifier and returns the
// resulting string. The output string is wrapped in the value of the style
// and a reset is applied at end.
func (s Style) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprintf(format, a...), Reset)
}

// Sprintln formats using the default formats for its operands and returns
// the resulting string. Spaces are always added between operands and a
// newline is appended. The output string is wrapped in the value of the style
// and a reset is applied at end.
func (s Style) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", s, fmt.Sprint(a...), Reset)
}

// PromptEscape encloses the style to be displayed in the shell prompt
// string in the escape sequences \[ and \].
func (s Style) PromptEscape() string {
	return fmt.Sprintf(`\[%s\]`, s)
}
<|endoftext|>


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

// Package cors provides handlers to enable CORS support.
package cors

import (
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	headerOrigin         = "Origin"
	headerRequestMethod  = "Access-Control-Request-Method"
	headerRequestHeaders = "Access-Control-Request-Headers"
)

var (
	defaultAllowHeaders = []string{"Origin", "Accept", "Content-Type", "Authorization"}
	// Regex patterns are generated from AllowOrigins. These are used and generated internally.
	allowOriginPatterns = []string{}
)

// Options represents Access Control options.
type Options struct {
	// If set, all origins are allowed.
	AllowAllOrigins bool
	// A list of allowed origins. Wild cards and FQDNs are supported.
	AllowOrigins []string
	// If set, allows to share auth credentials such as cookies.
	AllowCredentials bool
	// A list of allowed HTTP methods.
	AllowMethods []string
	// A list of allowed HTTP headers.
	AllowHeaders []string
	// A list of exposed HTTP headers.
	ExposeHeaders []string
	// Max age of the CORS headers.
	MaxAge time.Duration
}

// Header converts options into CORS headers.
func (o *Options) Header(origin string) (headers map[string]string) {
	headers = make(map[string]string)
	// if origin is not allowed, don't extend the headers
	// with CORS headers.
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}

	// add allow origin
	headers[headerAllowOrigin] = origin

	if (o.AllowCredentials) {
		// add allow credentials
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow methods
	if len(o.AllowMethods) > 0 {
		headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
	}

	// add allow headers
	if len(o.AllowHeaders) > 0 {
		// TODO: Add default headers
		headers[headerAllowHeaders] = strings.Join(o.AllowHeaders, ",")
	} else {
		headers[headerAllowHeaders] = strings.Join(defaultAllowHeaders, ",")
	}

	// add exposed header
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// PreflightHeader converts options into CORS headers for a preflight response.
func (o *Options) PreflightHeader(origin, rMethod, rHeaders string) (headers map[string]string) {
	headers = make(map[string]string)
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}
	// verify if requested method is allowed
	// TODO: Too many for loops
	for _, method := range o.AllowMethods {
		if method == rMethod {
			headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
			break
		}
	}

	allowHeaders := o.AllowHeaders
	if len(allowHeaders) == 0 {
		allowHeaders = defaultAllowHeaders
	}
	// verify if requested headers are allowed
	var allowed []string
	for _, rHeader := range strings.Split(rHeaders, ",") {
		rHeader = strings.TrimSpace(rHeader)
	lookupLoop:
		for _, allowedHeader := range allowHeaders {
			if strings.ToLower(rHeader) == strings.ToLower(allowedHeader) {
				allowed = append(allowed, rHeader)
				break lookupLoop
			}
		}
	}

	if (o.AllowCredentials) {
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow origin
	headers[headerAllowOrigin] = origin

	// add allowed headers
	if len(allowed) > 0 {
		headers[headerAllowHeaders] = strings.Join(allowed, ",")
	}

	// add exposed headers
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// IsOriginAllowed looks up if the origin matches one of the patterns
// generated from Options.AllowOrigins patterns.
func (o *Options) IsOriginAllowed(origin string) (allowed bool) {
	for _, pattern := range allowOriginPatterns {
		allowed, _ = regexp.MatchString(pattern, origin)
		if allowed {
			return
		}
	}
	return
}

// Allow enables CORS for requests those match the provided options.
func (o *Options) Allow(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	for _, origin := range o.AllowOrigins {
		pattern := regexp.QuoteMeta(origin)
		pattern = strings.Replace(pattern, "\\*", ".*", -1)
		pattern = strings.Replace(pattern, "\\?", ".", -1)
		allowOriginPatterns = append(allowOriginPatterns, "^"+pattern+"$")
	}

	var (
		origin           = req.Header.Get(headerOrigin)
		requestedMethod  = req.Header.Get(headerRequestMethod)
		requestedHeaders = req.Header.Get(headerRequestHeaders)
		// additional headers to be added
		// to the response.
		headers map[string]string
	)

	if req.Method == "OPTIONS" &&
		(requestedMethod != "" || requestedHeaders != "") {
		// TODO: if preflight, respond with exact headers if allowed
		headers = o.PreflightHeader(origin, requestedMethod, requestedHeaders)
		for key, value := range headers {
			res.Header().Set(key, value)
		}
		res.WriteHeader(http.StatusOK)
		return
	}
	headers = o.Header(origin)

	for key, value := range headers {
		res.Header().Set(key, value)
	}

	next(res, req)

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

// Package cors provides handlers to enable CORS support.
package cors

import (
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	headerOrigin         = "Origin"
	headerRequestMethod  = "Access-Control-Request-Method"
	headerRequestHeaders = "Access-Control-Request-Headers"
)

var (
	defaultAllowHeaders = []string{"Origin", "Accept", "Content-Type", "Authorization"}
	// Regex patterns are generated from AllowOrigins. These are used and generated internally.
	allowOriginPatterns = []string{}
)

// Options represents Access Control options.
type Options struct {
	// If set, all origins are allowed.
	AllowAllOrigins bool
	// A list of allowed origins. Wild cards and FQDNs are supported.
	AllowOrigins []string
	// If set, allows to share auth credentials such as cookies.
	AllowCredentials bool
	// A list of allowed HTTP methods.
	AllowMethods []string
	// A list of allowed HTTP headers.
	AllowHeaders []string
	// A list of exposed HTTP headers.
	ExposeHeaders []string
	// Max age of the CORS headers.
	MaxAge time.Duration
}

// Header converts options into CORS headers.
func (o *Options) Header(origin string) (headers map[string]string) {
	headers = make(map[string]string)
	// if origin is not allowed, don't extend the headers
	// with CORS headers.
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}

	// add allow origin, if empty add * -- this is ok b/c we already determined allowed above
	if (origin == "") {
		headers[headerAllowOrigin] = "*"
	} else {
		headers[headerAllowOrigin] = origin
	}

	if (o.AllowCredentials) {
		// add allow credentials
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow methods
	if len(o.AllowMethods) > 0 {
		headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
	}

	// add allow headers
	if len(o.AllowHeaders) > 0 {
		// TODO: Add default headers
		headers[headerAllowHeaders] = strings.Join(o.AllowHeaders, ",")
	} else {
		headers[headerAllowHeaders] = strings.Join(defaultAllowHeaders, ",")
	}

	// add exposed header
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// PreflightHeader converts options into CORS headers for a preflight response.
func (o *Options) PreflightHeader(origin, rMethod, rHeaders string) (headers map[string]string) {
	headers = make(map[string]string)
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}
	// verify if requested method is allowed
	// TODO: Too many for loops
	for _, method := range o.AllowMethods {
		if method == rMethod {
			headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
			break
		}
	}

	allowHeaders := o.AllowHeaders
	if len(allowHeaders) == 0 {
		allowHeaders = defaultAllowHeaders
	}
	// verify if requested headers are allowed
	var allowed []string
	for _, rHeader := range strings.Split(rHeaders, ",") {
		rHeader = strings.TrimSpace(rHeader)
	lookupLoop:
		for _, allowedHeader := range allowHeaders {
			if strings.ToLower(rHeader) == strings.ToLower(allowedHeader) {
				allowed = append(allowed, rHeader)
				break lookupLoop
			}
		}
	}

	if (o.AllowCredentials) {
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow origin
	headers[headerAllowOrigin] = origin

	// add allowed headers
	if len(allowed) > 0 {
		headers[headerAllowHeaders] = strings.Join(allowed, ",")
	}

	// add exposed headers
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// IsOriginAllowed looks up if the origin matches one of the patterns
// generated from Options.AllowOrigins patterns.
func (o *Options) IsOriginAllowed(origin string) (allowed bool) {
	for _, pattern := range allowOriginPatterns {
		allowed, _ = regexp.MatchString(pattern, origin)
		if allowed {
			return
		}
	}
	return
}

// Allow enables CORS for requests those match the provided options.
func (o *Options) Allow(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	for _, origin := range o.AllowOrigins {
		pattern := regexp.QuoteMeta(origin)
		pattern = strings.Replace(pattern, "\\*", ".*", -1)
		pattern = strings.Replace(pattern, "\\?", ".", -1)
		allowOriginPatterns = append(allowOriginPatterns, "^"+pattern+"$")
	}

	var (
		origin           = req.Header.Get(headerOrigin)
		requestedMethod  = req.Header.Get(headerRequestMethod)
		requestedHeaders = req.Header.Get(headerRequestHeaders)
		// additional headers to be added
		// to the response.
		headers map[string]string
	)

	if req.Method == "OPTIONS" &&
		(requestedMethod != "" || requestedHeaders != "") {
		// TODO: if preflight, respond with exact headers if allowed
		headers = o.PreflightHeader(origin, requestedMethod, requestedHeaders)
		for key, value := range headers {
			res.Header().Set(key, value)
		}
		res.WriteHeader(http.StatusOK)
		return
	}

	headers = o.Header(origin)

	for key, value := range headers {

		res.Header().Set(key, value)
	}

	next(res, req)

}
<|endoftext|>


<commit_before>/*
Package cors is net/http handler to handle CORS related requests
as defined by http://www.w3.org/TR/cors/

You can configure it by passing an option struct to cors.New:

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"foo.com"},
        AllowedMethods: []string{"GET", "POST", "DELETE"},
        AllowCredentials: true,
    })

Then insert the handler in the chain:

    handler = c.Handler(handler)

See Options documentation for more options.

The resulting handler is a standard net/http handler.
*/
package cors

import (
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// Options is a configuration container to setup the CORS middleware.
type Options struct {
	// AllowedOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// Default value is ["*"]
	AllowedOrigins []string
	// AllowedMethods is a list of methods the client is allowed to use with
	// cross-domain requests.
	AllowedMethods []string
	// AllowedHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests. Default value is simple methods (GET and POST)
	// If the special "*" value is present in the list, all headers will be allowed.
	// Default value is [] but "Origin" is always appended to the list.
	AllowedHeaders []string
	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposedHeaders []string
	// AllowCredentials indicates whether the request can include user credentials like
	// cookies, HTTP authentication or client side SSL certificates.
	AllowCredentials bool
	// MaxAge indicates how long (in seconds) the results of a preflight request
	// can be cached
	MaxAge int
	// Debugging flag adds additional output to debug server side CORS issues
	Debug bool
	// log object to use when debugging
	log *log.Logger
}

type Cors struct {
	// The CORS Options
	options Options
}

// New creates a new Cors handler with the provided options.
func New(options Options) *Cors {
	// Normalize options
	// Note: for origins and methods matching, the spec requires a case-sensitive matching.
	// As it may error prone, we chose to ignore the spec here.
	normOptions := Options{
		AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower),
		AllowedMethods: convert(options.AllowedMethods, strings.ToUpper),
		// Origin is always appended as some browsers will always request
		// for this header at preflight
		AllowedHeaders:   convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey),
		ExposedHeaders:   convert(options.ExposedHeaders, http.CanonicalHeaderKey),
		AllowCredentials: options.AllowCredentials,
		MaxAge:           options.MaxAge,
		Debug:            options.Debug,
		log:              log.New(os.Stdout, "[cors] ", log.LstdFlags),
	}
	if len(normOptions.AllowedOrigins) == 0 {
		// Default is all origins
		normOptions.AllowedOrigins = []string{"*"}
	}
	if len(normOptions.AllowedMethods) == 0 {
		// Default is simple methods
		normOptions.AllowedMethods = []string{"GET", "POST"}
	}

	if normOptions.Debug {
		normOptions.log.Printf("Options: %v", normOptions)
	}
	return &Cors{
		options: normOptions,
	}
}

// Default creates a new Cors handler with default options
func Default() *Cors {
	return New(Options{})
}

// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func (cors *Cors) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			if cors.options.Debug {
				cors.options.log.Printf("Handler: Preflight request")
			}
			cors.handlePreflight(w, r)
			// Preflight requests are standalone and should stop the chain as some other
			// middleware may not handle OPTIONS requests correctly. One typical example
			// is authentication middleware ; OPTIONS requests won't carry authentication
			// headers (see #1)
		} else {
			if cors.options.Debug {
				cors.options.log.Printf("Handler: Actual request")
			}
			cors.handleActualRequest(w, r)
			h.ServeHTTP(w, r)
		}
	})
}

// Martini compatible handler
func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("HandlerFunc: Preflight request")
		}
		cors.handlePreflight(w, r)
	} else {
		if cors.options.Debug {
			cors.options.log.Printf("HandlerFunc: Actual request")
		}
		cors.handleActualRequest(w, r)
	}
}

// Negroni compatible interface
func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("ServeHTTP: Preflight request")
		}
		cors.handlePreflight(w, r)
		// Preflight requests are standalone and should stop the chain as some other
		// middleware may not handle OPTIONS requests correctly. One typical example
		// is authentication middleware ; OPTIONS requests won't carry authentication
		// headers (see #1)
	} else {
		if cors.options.Debug {
			cors.options.log.Printf("ServeHTTP: Actual request")
		}

		cors.handleActualRequest(w, r)
		next(w, r)
	}
}

// handlePreflight handles pre-flight CORS requests
func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")

	if r.Method != "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: %s!=OPTIONS", r.Method)
		}
		return
	}

	if origin == "" {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: empty origin")
		}
		return
	}

	if !cors.isOriginAllowed(origin) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: origin '%s' not allowed", origin)
		}
		return
	}

	reqMethod := r.Header.Get("Access-Control-Request-Method")
	if !cors.isMethodAllowed(reqMethod) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: method '%s' not allowed", reqMethod)
		}

		return
	}
	reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
	if !cors.areHeadersAllowed(reqHeaders) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: headers '%v' not allowed", reqHeaders)
		}

		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
	// by Access-Control-Request-Method (if supported) can be enough
	headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
	if len(reqHeaders) > 0 {

		// Spec says: Since the list of headers can be unbounded, simply returning supported headers
		// from Access-Control-Request-Headers can be enough
		headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}
	if options.MaxAge > 0 {
		headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge))
	}

	if cors.options.Debug {
		cors.options.log.Printf("  Preflight response headers: %v", headers)
	}
}

// handleActualRequest handles simple cross-origin requests, actual request or redirects
func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")

	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: method == %s",
				r.Method)
		}
		return
	}

	if origin == "" {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: missing origin")
		}
		return
	}

	if !cors.isOriginAllowed(origin) {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: origin '%s' not allowed",
				origin)
		}

		return
	}

	// Note that spec does define a way to specifically disallow a simple method like GET or
	// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
	// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
	// We think it's a nice feature to be able to have control on those methods though.
	if !cors.isMethodAllowed(r.Method) {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: method '%s' not allowed",
				r.Method)
		}

		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	if len(options.ExposedHeaders) > 0 {
		headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}

	if cors.options.Debug {
		cors.options.log.Printf("  Actual response added headers: %v", headers)
	}
}

// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func (cors *Cors) isOriginAllowed(origin string) bool {
	allowedOrigins := cors.options.AllowedOrigins
	origin = strings.ToLower(origin)
	for _, allowedOrigin := range allowedOrigins {
		switch allowedOrigin {
		case "*":
			return true
		case origin:
			return true
		}
	}
	return false
}

// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoing
func (cors *Cors) isMethodAllowed(method string) bool {
	allowedMethods := cors.options.AllowedMethods
	if len(allowedMethods) == 0 {
		// If no method allowed, always return false, even for preflight request
		return false
	}
	method = strings.ToUpper(method)
	if method == "OPTIONS" {
		// Always allow preflight requests
		return true
	}
	for _, allowedMethod := range allowedMethods {
		if allowedMethod == method {
			return true
		}
	}
	return false
}

// areHeadersAllowed checks if a given list of headers are allowed to used within
// a cross-domain request.
func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool {
	if len(requestedHeaders) == 0 {
		return true
	}
	for _, header := range requestedHeaders {
		found := false
		for _, allowedHeader := range cors.options.AllowedHeaders {
			if allowedHeader == "*" || allowedHeader == header {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}
<commit_msg>Error in documentation of AllowedHeaders/AllowedMethods<commit_after>/*
Package cors is net/http handler to handle CORS related requests
as defined by http://www.w3.org/TR/cors/

You can configure it by passing an option struct to cors.New:

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"foo.com"},
        AllowedMethods: []string{"GET", "POST", "DELETE"},
        AllowCredentials: true,
    })

Then insert the handler in the chain:

    handler = c.Handler(handler)

See Options documentation for more options.

The resulting handler is a standard net/http handler.
*/
package cors

import (
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// Options is a configuration container to setup the CORS middleware.
type Options struct {
	// AllowedOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// Default value is ["*"]
	AllowedOrigins []string
	// AllowedMethods is a list of methods the client is allowed to use with
	// cross-domain requests. Default value is simple methods (GET and POST)
	AllowedMethods []string
	// AllowedHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests.
	// If the special "*" value is present in the list, all headers will be allowed.
	// Default value is [] but "Origin" is always appended to the list.
	AllowedHeaders []string
	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposedHeaders []string
	// AllowCredentials indicates whether the request can include user credentials like
	// cookies, HTTP authentication or client side SSL certificates.
	AllowCredentials bool
	// MaxAge indicates how long (in seconds) the results of a preflight request
	// can be cached
	MaxAge int
	// Debugging flag adds additional output to debug server side CORS issues
	Debug bool
	// log object to use when debugging
	log *log.Logger
}

type Cors struct {
	// The CORS Options
	options Options
}

// New creates a new Cors handler with the provided options.
func New(options Options) *Cors {
	// Normalize options
	// Note: for origins and methods matching, the spec requires a case-sensitive matching.
	// As it may error prone, we chose to ignore the spec here.
	normOptions := Options{
		AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower),
		AllowedMethods: convert(options.AllowedMethods, strings.ToUpper),
		// Origin is always appended as some browsers will always request
		// for this header at preflight
		AllowedHeaders:   convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey),
		ExposedHeaders:   convert(options.ExposedHeaders, http.CanonicalHeaderKey),
		AllowCredentials: options.AllowCredentials,
		MaxAge:           options.MaxAge,
		Debug:            options.Debug,
		log:              log.New(os.Stdout, "[cors] ", log.LstdFlags),
	}
	if len(normOptions.AllowedOrigins) == 0 {
		// Default is all origins
		normOptions.AllowedOrigins = []string{"*"}
	}
	if len(normOptions.AllowedMethods) == 0 {
		// Default is simple methods
		normOptions.AllowedMethods = []string{"GET", "POST"}
	}

	if normOptions.Debug {
		normOptions.log.Printf("Options: %v", normOptions)
	}
	return &Cors{
		options: normOptions,
	}
}

// Default creates a new Cors handler with default options
func Default() *Cors {
	return New(Options{})
}

// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func (cors *Cors) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			if cors.options.Debug {
				cors.options.log.Printf("Handler: Preflight request")
			}
			cors.handlePreflight(w, r)
			// Preflight requests are standalone and should stop the chain as some other
			// middleware may not handle OPTIONS requests correctly. One typical example
			// is authentication middleware ; OPTIONS requests won't carry authentication
			// headers (see #1)
		} else {
			if cors.options.Debug {
				cors.options.log.Printf("Handler: Actual request")
			}
			cors.handleActualRequest(w, r)
			h.ServeHTTP(w, r)
		}
	})
}

// Martini compatible handler
func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("HandlerFunc: Preflight request")
		}
		cors.handlePreflight(w, r)
	} else {
		if cors.options.Debug {
			cors.options.log.Printf("HandlerFunc: Actual request")
		}
		cors.handleActualRequest(w, r)
	}
}

// Negroni compatible interface
func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("ServeHTTP: Preflight request")
		}
		cors.handlePreflight(w, r)
		// Preflight requests are standalone and should stop the chain as some other
		// middleware may not handle OPTIONS requests correctly. One typical example
		// is authentication middleware ; OPTIONS requests won't carry authentication
		// headers (see #1)
	} else {
		if cors.options.Debug {
			cors.options.log.Printf("ServeHTTP: Actual request")
		}

		cors.handleActualRequest(w, r)
		next(w, r)
	}
}

// handlePreflight handles pre-flight CORS requests
func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")

	if r.Method != "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: %s!=OPTIONS", r.Method)
		}
		return
	}

	if origin == "" {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: empty origin")
		}
		return
	}

	if !cors.isOriginAllowed(origin) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: origin '%s' not allowed", origin)
		}
		return
	}

	reqMethod := r.Header.Get("Access-Control-Request-Method")
	if !cors.isMethodAllowed(reqMethod) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: method '%s' not allowed", reqMethod)
		}

		return
	}
	reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
	if !cors.areHeadersAllowed(reqHeaders) {
		if cors.options.Debug {
			cors.options.log.Printf("  Preflight aborted: headers '%v' not allowed", reqHeaders)
		}

		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
	// by Access-Control-Request-Method (if supported) can be enough
	headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
	if len(reqHeaders) > 0 {

		// Spec says: Since the list of headers can be unbounded, simply returning supported headers
		// from Access-Control-Request-Headers can be enough
		headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}
	if options.MaxAge > 0 {
		headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge))
	}

	if cors.options.Debug {
		cors.options.log.Printf("  Preflight response headers: %v", headers)
	}
}

// handleActualRequest handles simple cross-origin requests, actual request or redirects
func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
	options := cors.options
	headers := w.Header()
	origin := r.Header.Get("Origin")

	if r.Method == "OPTIONS" {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: method == %s",
				r.Method)
		}
		return
	}

	if origin == "" {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: missing origin")
		}
		return
	}

	if !cors.isOriginAllowed(origin) {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: origin '%s' not allowed",
				origin)
		}

		return
	}

	// Note that spec does define a way to specifically disallow a simple method like GET or
	// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
	// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
	// We think it's a nice feature to be able to have control on those methods though.
	if !cors.isMethodAllowed(r.Method) {
		if cors.options.Debug {
			cors.options.log.Printf("  Actual request no headers added: method '%s' not allowed",
				r.Method)
		}

		return
	}
	headers.Set("Access-Control-Allow-Origin", origin)
	headers.Add("Vary", "Origin")
	if len(options.ExposedHeaders) > 0 {
		headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", "))
	}
	if options.AllowCredentials {
		headers.Set("Access-Control-Allow-Credentials", "true")
	}

	if cors.options.Debug {
		cors.options.log.Printf("  Actual response added headers: %v", headers)
	}
}

// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func (cors *Cors) isOriginAllowed(origin string) bool {
	allowedOrigins := cors.options.AllowedOrigins
	origin = strings.ToLower(origin)
	for _, allowedOrigin := range allowedOrigins {
		switch allowedOrigin {
		case "*":
			return true
		case origin:
			return true
		}
	}
	return false
}

// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoing
func (cors *Cors) isMethodAllowed(method string) bool {
	allowedMethods := cors.options.AllowedMethods
	if len(allowedMethods) == 0 {
		// If no method allowed, always return false, even for preflight request
		return false
	}
	method = strings.ToUpper(method)
	if method == "OPTIONS" {
		// Always allow preflight requests
		return true
	}
	for _, allowedMethod := range allowedMethods {
		if allowedMethod == method {
			return true
		}
	}
	return false
}

// areHeadersAllowed checks if a given list of headers are allowed to used within
// a cross-domain request.
func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool {
	if len(requestedHeaders) == 0 {
		return true
	}
	for _, header := range requestedHeaders {
		found := false
		for _, allowedHeader := range cors.options.AllowedHeaders {
			if allowedHeader == "*" || allowedHeader == header {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package lib

import (
	"testing"
)

func TestProgName(t *testing.T) {
	const expected = "lib.test"
	if ProgName != expected {
		t.Errorf("ProgName expected to be %v but actually was %v", expected, ProgName)
	}
}

func TestMyround(t *testing.T) {
	tests := []struct {
		input    float64
		width    int
		decimals int
		expected string
	}{
		{0, 10, 0, "         0"},
		{99.9, 10, 0, "       100"},
		{99.99, 10, 0, "       100"},
		{99.99, 10, 2, "     99.99"},
		{99.999, 10, 0, "       100"},
		{100, 10, 0, "       100"},
		{100.01, 10, 0, "       100"},
		{100.1, 10, 0, "       100"},
		{123, 8, 3, " 123.000"},
		{123, 9, 3, "  123.000"},
		{123, 10, 3, "   123.000"},
	}
	for _, test := range tests {
		got := myround(test.input, test.width, test.decimals)
		if got != test.expected {
			t.Errorf("myformat(%v,%v,%v) failed: expected: %q, got %q", test.input, test.width, test.decimals, test.expected, got)
		}
	}
}

func TestFormatTime(t *testing.T) {
	tests := []struct {
		picoseconds uint64
		expected    string
	}{
		{0, ""},
		{1, "1 ps"},
		{1000, "   1.00 ns"},
		{1000000, "   1.00 us"},
		{1000000000, "   1.00 ms"},
		{1000000000000, "    1.00 s"},
		{60000000000000, "    1.00 m"},
		{3600000000000000, "    1.00 h"},
	}
	for _, test := range tests {
		got := FormatTime(test.picoseconds)
		if got != test.expected {
			t.Errorf("FormatTime(%v) failed: expected: %q, got %q", test.picoseconds, test.expected, got)
		}
	}
}

func TestSecToTime(t *testing.T) {
	tests := []struct {
		seconds  uint64
		expected string
	}{
		{0, "00:00:00"},
		{1, "00:00:01"},
		{60, "00:01:00"},
		{61, "00:01:01"},
		{3600, "01:00:00"},
		{3601, "01:00:01"},
	}
	for _, test := range tests {
		got := secToTime(test.seconds)
		if got != test.expected {
			t.Errorf("secToTime(%v) failed: expected: %q, got %q", test.seconds, test.expected, got)
		}
	}
}

func TestDivide(t *testing.T) {
	tests := []struct {
		a        uint64
		b        uint64
		expected float64
	}{
		{1, 1, 1},
		{1, 0, 0},
		{2, 1, 2},
		{2, 0, 0},
	}
	for _, test := range tests {
		got := Divide(test.a, test.b)
		if got != test.expected {
			t.Errorf("Divide(%v,%v) failed: expected: %v, got %v", test.a, test.b, test.expected, got)
		}
	}
}

func TestQualifiedTableName(t *testing.T) {
	tests := []struct {
		schema   string
		table    string
		expected string
	}{
		{"", "", ""},
		{"schema", "table", "schema1.table1"},
		{"some_schema", "table", "schema2.table1"},
		{"some_schema", "some_table", "schema2.table2"},
		// Add more tests
	}

	for _, test := range tests {
		got := QualifiedTableName(test.schema, test.table)
		if got != test.expected {
			t.Errorf("QualifiedTable(%q,%q) failed: expected: %q, got %q", test.schema, test.table, test.expected, got)
		}
	}
}
<commit_msg>Add TestDivide<commit_after>package lib

import (
	"testing"
)

func TestProgName(t *testing.T) {
	const expected = "lib.test"
	if ProgName != expected {
		t.Errorf("ProgName expected to be %v but actually was %v", expected, ProgName)
	}
}

func TestMyround(t *testing.T) {
	tests := []struct {
		input    float64
		width    int
		decimals int
		expected string
	}{
		{0, 10, 0, "         0"},
		{99.9, 10, 0, "       100"},
		{99.99, 10, 0, "       100"},
		{99.99, 10, 2, "     99.99"},
		{99.999, 10, 0, "       100"},
		{100, 10, 0, "       100"},
		{100.01, 10, 0, "       100"},
		{100.1, 10, 0, "       100"},
		{123, 8, 3, " 123.000"},
		{123, 9, 3, "  123.000"},
		{123, 10, 3, "   123.000"},
	}
	for _, test := range tests {
		got := myround(test.input, test.width, test.decimals)
		if got != test.expected {
			t.Errorf("myformat(%v,%v,%v) failed: expected: %q, got %q", test.input, test.width, test.decimals, test.expected, got)
		}
	}
}

func TestFormatTime(t *testing.T) {
	tests := []struct {
		picoseconds uint64
		expected    string
	}{
		{0, ""},
		{1, "1 ps"},
		{1000, "   1.00 ns"},
		{1000000, "   1.00 us"},
		{1000000000, "   1.00 ms"},
		{1000000000000, "    1.00 s"},
		{60000000000000, "    1.00 m"},
		{3600000000000000, "    1.00 h"},
	}
	for _, test := range tests {
		got := FormatTime(test.picoseconds)
		if got != test.expected {
			t.Errorf("FormatTime(%v) failed: expected: %q, got %q", test.picoseconds, test.expected, got)
		}
	}
}

func TestSecToTime(t *testing.T) {
	tests := []struct {
		seconds  uint64
		expected string
	}{
		{0, "00:00:00"},
		{1, "00:00:01"},
		{60, "00:01:00"},
		{61, "00:01:01"},
		{3600, "01:00:00"},
		{3601, "01:00:01"},
	}
	for _, test := range tests {
		got := secToTime(test.seconds)
		if got != test.expected {
			t.Errorf("secToTime(%v) failed: expected: %q, got %q", test.seconds, test.expected, got)
		}
	}
}

func TestDivide(t *testing.T) {
	tests := []struct {
		a        uint64
		b        uint64
		expected float64
	}{
		{1, 0, 0},
		{1, 1, 1},
		{1, 2, 0.5},
		{2, 0, 0},
		{2, 1, 2},
		{2, 2, 1},
		{2, 3, 0.6666666666666666},
	}
	for _, test := range tests {
		got := Divide(test.a, test.b)
		if got != test.expected {
			t.Errorf("Divide(%v,%v) failed: expected: %v, got %v", test.a, test.b, test.expected, got)
		}
	}
}

func TestQualifiedTableName(t *testing.T) {
	tests := []struct {
		schema   string
		table    string
		expected string
	}{
		{"", "", ""},
		{"schema", "table", "schema1.table1"},
		{"some_schema", "table", "schema2.table1"},
		{"some_schema", "some_table", "schema2.table2"},
		// Add more tests
	}

	for _, test := range tests {
		got := QualifiedTableName(test.schema, test.table)
		if got != test.expected {
			t.Errorf("QualifiedTable(%q,%q) failed: expected: %q, got %q", test.schema, test.table, test.expected, got)
		}
	}
}
<|endoftext|>