<commit_before>package slack

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
)

type ID interface {
	Equals(ID) bool
	Valid() bool
	String() string
}

type UserID struct {
	UserID string
	TeamID string
}

// SecureID is an opaque, deterministic representation of a Slack user identity
// that can be used in place of UserID to reduce the risk of compromising
// a user's real identity.
//
// A SecureID can be constructed from a UserID
// by calling UserID.Secure()
type SecureID struct {
	HashSum string
}

// Equals indicates if id and o represent the same user identity.
func (id UserID) Equals(o ID) bool {
	switch o := o.(type) {
	case UserID:
		if !(id.Valid() && o.Valid()) {
			return false
		}

		// > Notice that user IDs are not guaranteed to be globally unique across all Slack users.
		// > The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.
		//
		// - Slack API documentation
		return id.UserID == o.UserID && id.TeamID == o.TeamID
	case SecureID:
		return id.Secure().Equals(o)
	}

	return false
}

func (id UserID) Valid() bool {
	return id.UserID != "" && id.TeamID != ""
}

func (id UserID) String() string {
	return id.TeamID + "." + id.UserID
}

// Secure converts id into a SecureID.
func (id UserID) Secure() SecureID {
	if !id.Valid() {
		return SecureID{}
	}

	var buf bytes.Buffer
	buf.WriteString(id.TeamID)
	buf.WriteRune('.')
	buf.WriteString(id.UserID)

	h := sha256.New()
	h.Write(buf.Bytes())
	s := hex.EncodeToString(h.Sum(nil))

	return SecureID{
		HashSum: s,
	}
}

// Equals indicates if id and o represent the same user identity.
func (id SecureID) Equals(o ID) bool {
	switch o := o.(type) {
	case SecureID:
		if !id.Valid() || !o.Valid() {
			return false
		}

		return id.HashSum == o.HashSum
	case UserID:
		return o.Secure().Equals(id)
	}
	return false
}

func (id SecureID) Valid() bool {
	return id.HashSum != ""
}

func (id SecureID) String() string {
	return id.HashSum
}
<commit_msg>Update comment<commit_after>package slack

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
)

type ID interface {
	Equals(ID) bool
	Valid() bool
	String() string
}

type UserID struct {
	UserID string
	TeamID string
}

// SecureID represents an irreversible obfuscation of a Slack user identity
// that can be used in place of UserID to minimize the security implications of
// handling and storing user IDs.
//
// A SecureID can be constructed from a UserID
// by calling (UserID).Secure()
type SecureID struct {
	HashSum string
}

// Equals indicates if id and o represent the same user identity.
func (id UserID) Equals(o ID) bool {
	switch o := o.(type) {
	case UserID:
		if !(id.Valid() && o.Valid()) {
			return false
		}

		// > Notice that user IDs are not guaranteed to be globally unique across all Slack users.
		// > The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.
		//
		// - Slack API documentation
		return id.UserID == o.UserID && id.TeamID == o.TeamID
	case SecureID:
		return id.Secure().Equals(o)
	}

	return false
}

func (id UserID) Valid() bool {
	return id.UserID != "" && id.TeamID != ""
}

func (id UserID) String() string {
	return id.TeamID + "." + id.UserID
}

// Secure converts id into a SecureID.
func (id UserID) Secure() SecureID {
	if !id.Valid() {
		return SecureID{}
	}

	var buf bytes.Buffer
	buf.WriteString(id.TeamID)
	buf.WriteRune('.')
	buf.WriteString(id.UserID)

	h := sha256.New()
	h.Write(buf.Bytes())
	s := hex.EncodeToString(h.Sum(nil))

	return SecureID{
		HashSum: s,
	}
}

// Equals indicates if id and o represent the same user identity.
func (id SecureID) Equals(o ID) bool {
	switch o := o.(type) {
	case SecureID:
		if !id.Valid() || !o.Valid() {
			return false
		}

		return id.HashSum == o.HashSum
	case UserID:
		return o.Secure().Equals(id)
	}
	return false
}

func (id SecureID) Valid() bool {
	return id.HashSum != ""
}

func (id SecureID) String() string {
	return id.HashSum
}
<|endoftext|>


<commit_before>package goslackbot

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"sync/atomic"
	"time"

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

type SlackBot struct {
	ID                string
	rtmToken          string
	wsURL             string
	users             map[string]SlackUser
	channels          map[string]SlackChannel
	groups            map[string]SlackChannel
	ims               map[string]SlackChannel // direct messages or im in slack lingo
	mpims             map[string]SlackChannel
	teams             map[string]SlackTeam
	ws                *websocket.Conn
	OutgoingMessages  chan SlackMessage
	IncomingMessages  map[string]chan SlackMessage
	IncomingFunctions map[string]func(SlackMessage)
	Conversations     map[string]SlackConversation
	ReactionCallbacks map[string]func(SlackMessage)
}

// type SlackReactionCallback func(channel, timestamp string)

type SlackAPIReactionAdd struct {
	Token     string `json:"token"`
	Name      string `json:"name"`
	Channel   string `json:"channel"`
	TimeStamp string `json:"timestamp"`
}

type SlackPostMessageResponse struct {
	Ok        bool   `json:"ok"`
	Channel   string `json:"channel"`
	TimeStamp string `json:"ts"`
}

type SlackConversation struct {
	Messages []SlackMessage
	Ongoing  bool
	State    string
	Started  time.Time
}

type ConversationMap map[string]SlackConversation

var counter uint64

func NewSlackBot(token string) (*SlackBot, error) {

	url := fmt.Sprintf("https://slack.com/api/rtm.start?mpim_aware=1&token=%s", token)
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, err
	}

	var respObj SlackRTMResponse
	err = json.Unmarshal(body, &respObj)
	if err != nil {
		return nil, err
	}

	if !respObj.Ok {
		err = fmt.Errorf("Slack error: %s", respObj.Error)
		return nil, err
	}

	bot := SlackBot{}
	bot.SetURL(respObj.Url)
	bot.SetID((respObj.Self.Id))

	bot.channels = make(map[string]SlackChannel)
	for _, i := range respObj.Channels {
		bot.channels[i.Name] = i
		fmt.Printf("Channel: %s %s\n", i.ID, i.Name)
	}

	bot.teams = make(map[string]SlackTeam)
	for _, i := range respObj.Teams {
		bot.teams[i.Name] = i
		log.Printf("Team: %s %s\n", i.ID, i.Name)
	}
	bot.users = make(map[string]SlackUser)
	for _, u := range respObj.Users {
		bot.users[u.Name] = u
		// fmt.Printf("User: %s\t%s\n", u.ID, u.Name)
	}

	bot.mpims = make(map[string]SlackChannel)
	for _, mpim := range respObj.MPIMs {
		bot.channels[mpim.Name] = mpim
		// fmt.Printf("MPIM: %s\t%s\n", mpim.ID, mpim.Name)
	}

	bot.groups = make(map[string]SlackChannel)
	for _, group := range respObj.Groups {
		bot.channels[group.ID] = group
		// fmt.Printf("Group: %s\t%s\n", group.ID, group.Name)
	}

	bot.ims = make(map[string]SlackChannel)
	for _, im := range respObj.IMs {
		bot.ims[im.ID] = im
	}

	bot.OutgoingMessages = make(chan SlackMessage)
	bot.IncomingMessages = make(map[string]chan SlackMessage, 0)

	bot.rtmToken = token

	bot.ReactionCallbacks = make(map[string]func(SlackMessage))
	return &bot, nil
}

func (s *SlackBot) RemoveReactionCallback(channel, ts string) {
	key := strings.Join([]string{channel, ts}, "+")
	s.ReactionCallbacks[key] = nil
}

func (s *SlackBot) AddReactionCallback(channel, ts string, callback func(SlackMessage)) {

	key := strings.Join([]string{channel, ts}, "+")
	s.ReactionCallbacks[key] = callback
}

func (s *SlackBot) TriggerReactionCallback(m SlackMessage) error {

	key := strings.Join([]string{m.Channel, m.TimeStamp}, "+")
	if callback, ok := s.ReactionCallbacks[key]; ok {
		callback(m)
	}

	return nil
}

func (s *SlackBot) FetchReactionCallback(channel, timestamp string) func(m SlackMessage) {

	key := strings.Join([]string{channel, timestamp}, "+")

	if callback, ok := s.ReactionCallbacks[key]; ok {
		return callback
	}

	return func(m SlackMessage) {
		log.Println("DO NOTHING")
	}
}

func (s *SlackBot) GetUser(id string) SlackUser {

	return s.users[id]
}

func (s *SlackBot) GetChannel(id string) SlackChannel {

	if strings.HasPrefix(id, "G") {
		return s.groups[id]
	} else {
		if strings.HasPrefix(id, "D") {
			return s.ims[id]
		}
		return s.channels[id]
	}
}

func (s *SlackBot) GetChannelByName(name string) SlackChannel {
	if strings.HasPrefix(name, "G") {
		return s.groups[name]
	} else {
		if strings.HasPrefix(name, "D") {
			return s.ims[name]
		}
		return s.channels[name]
	}
}

func (s *SlackBot) RegisterIncomingChannel(name string, incoming chan SlackMessage) error {

	s.IncomingMessages[name] = incoming
	return nil
}

func (s *SlackBot) RegisterIncomingFunction(name string, runme func(SlackMessage)) {

	log.Printf("Registering Incoming Function %s", name)
	c := make(chan SlackMessage)
	s.RegisterIncomingChannel(name, c)

	go func() {
		for {
			m := <-c
			runme(m)
		}
	}()
}

func getMessage(ws *websocket.Conn) (m SlackMessage, err error) {

	// err = websocket.JSON.Receive(ws, &m)
	var message string
	websocket.Message.Receive(ws, &message)

	err = json.Unmarshal([]byte(message), &m)
	// log.Printf("RAW %s\n", message)

	if m.Channel == "" && m.Item.Channel != "" {
		m.Channel = m.Item.Channel
		m.TimeStamp = m.Item.TimeStamp
	}

	return
}

func (s *SlackBot) PostMessage(channel, text string) (*SlackPostMessageResponse, error) {

	v := url.Values{}
	v.Set("token", s.rtmToken)
	v.Set("channel", channel)
	v.Set("text", text)

	req, err := http.NewRequest("GET", "https://slack.com/api/chat.postMessage?"+v.Encode(), nil)

	req.Header.Add("Content-type", "application/json")
	resp, err := http.DefaultClient.Do(req)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	responseBody, err := ioutil.ReadAll(resp.Body)

	response := SlackPostMessageResponse{}
	err = json.Unmarshal(responseBody, &response)

	if err != nil {
		return nil, err
	}

	return &response, nil
}

func (s *SlackBot) AddReaction(channel, timestamp, reaction string) error {

	v := url.Values{}
	v.Set("token", s.rtmToken)
	v.Set("name", reaction)
	v.Set("channel", channel)
	v.Set("timestamp", timestamp)

	req, err := http.NewRequest("GET", "https://slack.com/api/reactions.add?"+v.Encode(), nil)

	req.Header.Add("X-Conversation-ID", "0xf00f6")
	req.Header.Add("Content-type", "application/json")
	resp, err := http.DefaultClient.Do(req)

	if err != nil {
		return err
	}

	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (s *SlackBot) SendMessage(channel, message string) error {

	m := SlackMessage{
		Text:    message,
		Channel: channel,
		Type:    "message",
	}

	s.OutgoingMessages <- m

	return nil
}

func (s *SlackBot) SetID(id string) error {
	s.ID = id

	return nil
}

func (s *SlackBot) SetURL(url string) error {
	s.wsURL = url
	return nil
}

func (s *SlackBot) Connect() error {

	ws, err := websocket.Dial(s.wsURL, "", "https://api.slack.com/")
	if err != nil {
		return err
	}

	s.ws = ws

	go func() {
		for {
			m := <-s.OutgoingMessages
			m.Id = atomic.AddUint64(&counter, 1)
			websocket.JSON.Send(s.ws, m)

		}
	}()

	go func() {
		for {
			m, err := getMessage(ws)

			if err != nil {
				fmt.Errorf("Incoming Error: %s", err)
			}

			// log.Printf("INCOMING MESSAGE: %s", m.Type)
			for _, c := range s.IncomingMessages {
				c <- m
			}
		}
	}()

	return nil
}
<commit_msg>increment the channel specific counter instead of the global counter<commit_after>package goslackbot

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"sync/atomic"
	"time"

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

type SlackBot struct {
	ID                string
	rtmToken          string
	wsURL             string
	users             map[string]SlackUser
	channels          map[string]SlackChannel
	groups            map[string]SlackChannel
	ims               map[string]SlackChannel // direct messages or im in slack lingo
	mpims             map[string]SlackChannel
	teams             map[string]SlackTeam
	ws                *websocket.Conn
	OutgoingMessages  chan SlackMessage
	IncomingMessages  map[string]chan SlackMessage
	IncomingFunctions map[string]func(SlackMessage)
	Conversations     map[string]SlackConversation
	ReactionCallbacks map[string]func(SlackMessage)
}

// type SlackReactionCallback func(channel, timestamp string)

type SlackAPIReactionAdd struct {
	Token     string `json:"token"`
	Name      string `json:"name"`
	Channel   string `json:"channel"`
	TimeStamp string `json:"timestamp"`
}

type SlackPostMessageResponse struct {
	Ok        bool   `json:"ok"`
	Channel   string `json:"channel"`
	TimeStamp string `json:"ts"`
}

type SlackConversation struct {
	Messages []SlackMessage
	Ongoing  bool
	State    string
	Started  time.Time
}

type ConversationMap map[string]SlackConversation

var counter uint64

func NewSlackBot(token string) (*SlackBot, error) {

	url := fmt.Sprintf("https://slack.com/api/rtm.start?mpim_aware=1&token=%s", token)
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, err
	}

	var respObj SlackRTMResponse
	err = json.Unmarshal(body, &respObj)
	if err != nil {
		return nil, err
	}

	if !respObj.Ok {
		err = fmt.Errorf("Slack error: %s", respObj.Error)
		return nil, err
	}

	bot := SlackBot{}
	bot.SetURL(respObj.Url)
	bot.SetID((respObj.Self.Id))

	bot.channels = make(map[string]SlackChannel)
	for _, i := range respObj.Channels {
		bot.channels[i.Name] = i
		fmt.Printf("Channel: %s %s\n", i.ID, i.Name)
	}

	bot.teams = make(map[string]SlackTeam)
	for _, i := range respObj.Teams {
		bot.teams[i.Name] = i
		log.Printf("Team: %s %s\n", i.ID, i.Name)
	}
	bot.users = make(map[string]SlackUser)
	for _, u := range respObj.Users {
		bot.users[u.Name] = u
		// fmt.Printf("User: %s\t%s\n", u.ID, u.Name)
	}

	bot.mpims = make(map[string]SlackChannel)
	for _, mpim := range respObj.MPIMs {
		bot.channels[mpim.Name] = mpim
		// fmt.Printf("MPIM: %s\t%s\n", mpim.ID, mpim.Name)
	}

	bot.groups = make(map[string]SlackChannel)
	for _, group := range respObj.Groups {
		bot.channels[group.ID] = group
		// fmt.Printf("Group: %s\t%s\n", group.ID, group.Name)
	}

	bot.ims = make(map[string]SlackChannel)
	for _, im := range respObj.IMs {
		bot.ims[im.ID] = im
	}

	bot.OutgoingMessages = make(chan SlackMessage)
	bot.IncomingMessages = make(map[string]chan SlackMessage, 0)

	bot.rtmToken = token

	bot.ReactionCallbacks = make(map[string]func(SlackMessage))
	return &bot, nil
}

func (s *SlackBot) RemoveReactionCallback(channel, ts string) {
	key := strings.Join([]string{channel, ts}, "+")
	s.ReactionCallbacks[key] = nil
}

func (s *SlackBot) AddReactionCallback(channel, ts string, callback func(SlackMessage)) {

	key := strings.Join([]string{channel, ts}, "+")
	s.ReactionCallbacks[key] = callback
}

func (s *SlackBot) TriggerReactionCallback(m SlackMessage) error {

	key := strings.Join([]string{m.Channel, m.TimeStamp}, "+")
	if callback, ok := s.ReactionCallbacks[key]; ok {
		callback(m)
	}

	return nil
}

func (s *SlackBot) FetchReactionCallback(channel, timestamp string) func(m SlackMessage) {

	key := strings.Join([]string{channel, timestamp}, "+")

	if callback, ok := s.ReactionCallbacks[key]; ok {
		return callback
	}

	return func(m SlackMessage) {
		log.Println("DO NOTHING")
	}
}

func (s *SlackBot) GetUser(id string) SlackUser {

	return s.users[id]
}

func (s *SlackBot) GetChannel(id string) SlackChannel {

	if strings.HasPrefix(id, "G") {
		return s.groups[id]
	} else {
		if strings.HasPrefix(id, "D") {
			return s.ims[id]
		}
		return s.channels[id]
	}
}

func (s *SlackBot) GetChannelByName(name string) SlackChannel {
	if strings.HasPrefix(name, "G") {
		return s.groups[name]
	} else {
		if strings.HasPrefix(name, "D") {
			return s.ims[name]
		}
		return s.channels[name]
	}
}

func (s *SlackBot) RegisterIncomingChannel(name string, incoming chan SlackMessage) error {

	s.IncomingMessages[name] = incoming
	return nil
}

func (s *SlackBot) RegisterIncomingFunction(name string, runme func(SlackMessage)) {

	log.Printf("Registering Incoming Function %s", name)
	c := make(chan SlackMessage)
	s.RegisterIncomingChannel(name, c)

	go func() {
		for {
			m := <-c
			runme(m)
		}
	}()
}

func getMessage(ws *websocket.Conn) (m SlackMessage, err error) {

	// err = websocket.JSON.Receive(ws, &m)
	var message string
	websocket.Message.Receive(ws, &message)

	err = json.Unmarshal([]byte(message), &m)
	// log.Printf("RAW %s\n", message)

	if m.Channel == "" && m.Item.Channel != "" {
		m.Channel = m.Item.Channel
		m.TimeStamp = m.Item.TimeStamp
	}

	return
}

func (s *SlackBot) PostMessage(channel, text string) (*SlackPostMessageResponse, error) {

	v := url.Values{}
	v.Set("token", s.rtmToken)
	v.Set("channel", channel)
	v.Set("text", text)

	req, err := http.NewRequest("GET", "https://slack.com/api/chat.postMessage?"+v.Encode(), nil)

	req.Header.Add("Content-type", "application/json")
	resp, err := http.DefaultClient.Do(req)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	responseBody, err := ioutil.ReadAll(resp.Body)

	response := SlackPostMessageResponse{}
	err = json.Unmarshal(responseBody, &response)

	if err != nil {
		return nil, err
	}

	return &response, nil
}

func (s *SlackBot) AddReaction(channel, timestamp, reaction string) error {

	v := url.Values{}
	v.Set("token", s.rtmToken)
	v.Set("name", reaction)
	v.Set("channel", channel)
	v.Set("timestamp", timestamp)

	req, err := http.NewRequest("GET", "https://slack.com/api/reactions.add?"+v.Encode(), nil)

	req.Header.Add("X-Conversation-ID", "0xf00f6")
	req.Header.Add("Content-type", "application/json")
	resp, err := http.DefaultClient.Do(req)

	if err != nil {
		return err
	}

	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)

	if err != nil {
		return err
	}

	return nil
}

func (s *SlackBot) SendMessage(channel, message string) error {

	m := SlackMessage{
		Text:    message,
		Channel: channel,
		Type:    "message",
	}

	s.OutgoingMessages <- m

	return nil
}

func (s *SlackBot) SetID(id string) error {
	s.ID = id

	return nil
}

func (s *SlackBot) SetURL(url string) error {
	s.wsURL = url
	return nil
}

func (s *SlackBot) Connect() error {

	ws, err := websocket.Dial(s.wsURL, "", "https://api.slack.com/")
	if err != nil {
		return err
	}

	s.ws = ws

	go func() {
		for {
			m := <-s.OutgoingMessages
			channel := s.GetChannel(m.Channel)
			m.Id = atomic.AddUint64(&channel.LastMessageID, 1)
			websocket.JSON.Send(s.ws, m)

		}
	}()

	go func() {
		for {
			m, err := getMessage(ws)

			if err != nil {
				fmt.Errorf("Incoming Error: %s", err)
			}

			// log.Printf("INCOMING MESSAGE: %s", m.Type)
			for _, c := range s.IncomingMessages {
				c <- m
			}
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The go-hep Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hbook_test

import (
	"database/sql"
	"testing"

	"github.com/go-hep/csvutil/csvdriver"
	"github.com/go-hep/hbook"
)

var (
	nt *hbook.NTuple
)

func TestScanH1D(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	h, err := nt.ScanH1D("x", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestScanH1DWhere(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	h, err := nt.ScanH1D("x where (id > 4 && id < 10)", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}

	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 5,
		len:     10,
		mean:    7,
		rms:     1.4142135623730951,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		want := float64(0)
		if i > 4 {
			want = 1
		}
		if v != want {
			t.Errorf("error bin(%d)=%v. want=%v\n", i, v, want)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestScanH1DInt(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	h, err := nt.ScanH1D("id", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestScan(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	err := nt.Scan("id, x", func(id int64, x float64) error {
		h.Fill(x, 1)
		return nil
	})
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestScanH1DFromCSVWithCommas(t *testing.T) {
	db, err := sql.Open("csv", "testdata/simple-comma.csv")
	if err != nil {
		t.Fatalf("error opening CSV db: %v\n", err)
	}
	defer db.Close()

	nt, err := hbook.OpenNTuple(db, "csv")
	if err != nil {
		t.Fatalf("error opening ntuple: %v\n", err)
	}

	h := hbook.NewH1D(10, 0, 10)
	h, err = nt.ScanH1D("var2", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestScanH1DFromCSV(t *testing.T) {
	db, err := csvdriver.Conn{
		File:    "testdata/simple.csv",
		Comma:   ';',
		Comment: '#',
	}.Open()
	if err != nil {
		t.Fatalf("error opening CSV db: %v\n", err)
	}
	defer db.Close()

	nt, err := hbook.OpenNTuple(db, "csv")
	if err != nil {
		t.Fatalf("error opening ntuple: %v\n", err)
	}

	h := hbook.NewH1D(10, 0, 10)
	h, err = nt.ScanH1D("var2", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func init() {
	var err error
	db, err := sql.Open("ql", "memory://mem.db")
	if err != nil {
		panic(err)
	}

	tx, err := db.Begin()
	_, err = tx.Exec("create table data (id int, x float64);")
	if err != nil {
		panic(err)
	}

	for i := 0; i < 10; i++ {
		x := float64(i)
		_, err = tx.Exec("insert into data values($1, $2);", i, x)
		if err != nil {
			panic(err)
		}
	}

	err = tx.Commit()
	if err != nil {
		panic(err)
	}

	nt, err = hbook.OpenNTuple(db, "data")
	if err != nil {
		panic(err)
	}
}
<commit_msg>test: rename ntuple tests<commit_after>// Copyright 2016 The go-hep Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hbook_test

import (
	"database/sql"
	"testing"

	"github.com/go-hep/csvutil/csvdriver"
	"github.com/go-hep/hbook"
)

var (
	nt *hbook.NTuple
)

func TestNTupleScanH1D(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	h, err := nt.ScanH1D("x", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestNTupleScanH1DWhere(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	h, err := nt.ScanH1D("x where (id > 4 && id < 10)", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}

	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 5,
		len:     10,
		mean:    7,
		rms:     1.4142135623730951,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		want := float64(0)
		if i > 4 {
			want = 1
		}
		if v != want {
			t.Errorf("error bin(%d)=%v. want=%v\n", i, v, want)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestNTupleScanH1DInt(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	h, err := nt.ScanH1D("id", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestNTupleScan(t *testing.T) {
	h := hbook.NewH1D(10, 0, 10)
	err := nt.Scan("id, x", func(id int64, x float64) error {
		h.Fill(x, 1)
		return nil
	})
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestNTupleScanH1DFromCSVWithCommas(t *testing.T) {
	db, err := sql.Open("csv", "testdata/simple-comma.csv")
	if err != nil {
		t.Fatalf("error opening CSV db: %v\n", err)
	}
	defer db.Close()

	nt, err := hbook.OpenNTuple(db, "csv")
	if err != nil {
		t.Fatalf("error opening ntuple: %v\n", err)
	}

	h := hbook.NewH1D(10, 0, 10)
	h, err = nt.ScanH1D("var2", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func TestNTupleScanH1DFromCSV(t *testing.T) {
	db, err := csvdriver.Conn{
		File:    "testdata/simple.csv",
		Comma:   ';',
		Comment: '#',
	}.Open()
	if err != nil {
		t.Fatalf("error opening CSV db: %v\n", err)
	}
	defer db.Close()

	nt, err := hbook.OpenNTuple(db, "csv")
	if err != nil {
		t.Fatalf("error opening ntuple: %v\n", err)
	}

	h := hbook.NewH1D(10, 0, 10)
	h, err = nt.ScanH1D("var2", h)
	if err != nil {
		t.Errorf("error running query: %v\n", err)
	}
	want := struct {
		entries int64
		len     int
		mean    float64
		rms     float64
	}{
		entries: 10,
		len:     10,
		mean:    4.5,
		rms:     2.8722813232690143,
	}

	if h.Entries() != want.entries {
		t.Errorf("error. got %v entries. want=%v\n", h.Entries(), want.entries)
	}
	if h.Len() != want.len {
		t.Errorf("error. got %v bins. want=%d\n", h.Len(), want.len)
	}

	for i := 0; i < h.Len(); i++ {
		v := h.Value(i)
		if v != 1 {
			t.Errorf("error bin(%d)=%v. want=1\n", i, v)
		}
	}

	if mean := h.Mean(); mean != want.mean {
		t.Errorf("error: mean=%v. want=%v\n", mean, want.mean)
	}
	if rms := h.RMS(); rms != want.rms {
		t.Errorf("error: rms=%v. want=%v\n", rms, want.rms)
	}
}

func init() {
	var err error
	db, err := sql.Open("ql", "memory://mem.db")
	if err != nil {
		panic(err)
	}

	tx, err := db.Begin()
	_, err = tx.Exec("create table data (id int, x float64);")
	if err != nil {
		panic(err)
	}

	for i := 0; i < 10; i++ {
		x := float64(i)
		_, err = tx.Exec("insert into data values($1, $2);", i, x)
		if err != nil {
			panic(err)
		}
	}

	err = tx.Commit()
	if err != nil {
		panic(err)
	}

	nt, err = hbook.OpenNTuple(db, "data")
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2013-2016 Aerospike, Inc.
//
// 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 aerospike

import (
	"strconv"
	"strings"

	. "github.com/aerospike/aerospike-client-go/types"
)

// ExecuteTask is used to poll for long running server execute job completion.
type ExecuteTask struct {
	*BaseTask

	taskId uint64
	scan   bool
}

// NewExecuteTask initializes task with fields needed to query server nodes.
func NewExecuteTask(cluster *Cluster, statement *Statement) *ExecuteTask {
	return &ExecuteTask{
		BaseTask: NewTask(cluster, false),
		taskId:   statement.TaskId,
		scan:     statement.IsScan(),
	}
}

// IsDone queries all nodes for task completion status.
func (etsk *ExecuteTask) IsDone() (bool, error) {
	var module string
	if etsk.scan {
		module = "scan"
	} else {
		module = "query"
	}

	command := "jobs:module=" + module + ";cmd=get-job;trid=" + strconv.FormatUint(etsk.taskId, 10)

	nodes := etsk.cluster.GetNodes()

	for _, node := range nodes {
		conn, err := node.GetConnection(0)
		if err != nil {
			return false, err
		}
		responseMap, err := RequestInfo(conn, command)
		if err != nil {
			node.InvalidateConnection(conn)
			return false, err
		}

		node.PutConnection(conn)

		response := responseMap[command]

		if strings.HasPrefix(response, "ERROR:2") {
			// Task not found. This could mean task already completed or
			// task not started yet.  We are going to have to assume that
			// the task already completed...
			continue
		}

		if strings.HasPrefix(response, "ERROR:") {
			// Mark done and quit immediately.
			return false, NewAerospikeError(UDF_BAD_RESPONSE, response)
		}

		find := "job_status="
		index := strings.Index(response, find)

		if index < 0 {
			return false, nil
		}

		begin := index + len(find)
		response = response[begin:]
		find = ":"
		index = strings.Index(response, find)

		if index < 0 {
			continue
		}

		status := strings.ToUpper(response[:index])
		if status != "DONE" {
			return false, nil
		}
	}

	return true, nil
}

// OnComplete returns a channel which will be closed when the task is
// completed.
// If an error is encountered while performing the task, an error
// will be sent on the channel.
func (etsk *ExecuteTask) OnComplete() chan error {
	return etsk.onComplete(etsk)
}
<commit_msg>Update job status check for execute tasks<commit_after>// Copyright 2013-2016 Aerospike, Inc.
//
// 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 aerospike

import (
	"strconv"
	"strings"

	. "github.com/aerospike/aerospike-client-go/types"
)

// ExecuteTask is used to poll for long running server execute job completion.
type ExecuteTask struct {
	*BaseTask

	taskId uint64
	scan   bool
}

// NewExecuteTask initializes task with fields needed to query server nodes.
func NewExecuteTask(cluster *Cluster, statement *Statement) *ExecuteTask {
	return &ExecuteTask{
		BaseTask: NewTask(cluster, false),
		taskId:   statement.TaskId,
		scan:     statement.IsScan(),
	}
}

// IsDone queries all nodes for task completion status.
func (etsk *ExecuteTask) IsDone() (bool, error) {
	var module string
	if etsk.scan {
		module = "scan"
	} else {
		module = "query"
	}

	command := "jobs:module=" + module + ";cmd=get-job;trid=" + strconv.FormatUint(etsk.taskId, 10)

	nodes := etsk.cluster.GetNodes()

	for _, node := range nodes {
		conn, err := node.GetConnection(0)
		if err != nil {
			return false, err
		}
		responseMap, err := RequestInfo(conn, command)
		if err != nil {
			node.InvalidateConnection(conn)
			return false, err
		}

		node.PutConnection(conn)

		response := responseMap[command]

		if strings.HasPrefix(response, "ERROR:2") {
			// Task not found. This could mean task already completed or
			// task not started yet.  We are going to have to assume that
			// the task already completed...
			continue
		}

		if strings.HasPrefix(response, "ERROR:") {
			// Mark done and quit immediately.
			return false, NewAerospikeError(UDF_BAD_RESPONSE, response)
		}

		find := "status="
		index := strings.Index(response, find)

		if index < 0 {
			return false, nil
		}

		begin := index + len(find)
		response = response[begin:]
		find = ":"
		index = strings.Index(response, find)

		if index < 0 {
			continue
		}

		status := strings.ToLower(response[:index])
		if !strings.HasPrefix(status, "done") {
			return false, nil
		}
	}

	return true, nil
}

// OnComplete returns a channel which will be closed when the task is
// completed.
// If an error is encountered while performing the task, an error
// will be sent on the channel.
func (etsk *ExecuteTask) OnComplete() chan error {
	return etsk.onComplete(etsk)
}
<|endoftext|>


<commit_before>package glob
import (
	"testing"
	rGlob "github.com/ryanuber/go-glob"
	"regexp"
	"strings"
)

type test struct {
	pattern, match string
	should         bool
	delimiters     []string
}

func glob(s bool, p, m string, d ...string) test {
	return test{p, m, s, d}
}

func TestFirstIndexOfChars(t *testing.T) {
	for _, test := range []struct{
		s string
		c []string
		i int
		r string
	}{
		{
			"**",
			[]string{"**", "*"},
			0,
			"**",
		},
		{
			"**",
			[]string{"*", "**"},
			0,
			"**",
		},
	}{
		i, r := firstIndexOfChars(test.s, test.c)
		if i != test.i || r != test.r {
			t.Errorf("unexpeted index: expected %q at %v, got %q at %v", test.r, test.i, r, i)
		}
	}
}

func TestGlob(t *testing.T) {
	for _, test := range []test {
		glob(true, "abc", "abc"),
		glob(true, "a*c", "abc"),
		glob(true, "a*c", "a12345c"),
		glob(true, "a?c", "a1c"),
		glob(true, "a.b", "a.b", "."),
		glob(true, "a.*", "a.b", "."),
		glob(true, "a.**", "a.b.c", "."),
		glob(true, "a.?.c", "a.b.c", "."),
		glob(true, "a.?.?", "a.b.c", "."),
		glob(true, "?at", "cat"),
		glob(true, "?at", "fat"),
		glob(true, "*", "abc"),
		glob(true, `\*`, "*"),
		glob(true, "**", "a.b.c", "."),

		glob(false, "?at", "at"),
		glob(false, "?at", "fat", "f"),
		glob(false, "a.*", "a.b.c", "."),
		glob(false, "a.?.c", "a.bb.c", "."),
		glob(false, "*", "a.b.c", "."),

		glob(true, "*test", "this is a test"),
		glob(true, "this*", "this is a test"),
		glob(true, "*is *", "this is a test"),
		glob(true, "*is*a*", "this is a test"),
		glob(true, "**test**", "this is a test"),
		glob(true, "**is**a***test*", "this is a test"),

		glob(false, "*is", "this is a test"),
		glob(false, "*no*", "this is a test"),
	}{
		g := New(test.pattern, test.delimiters...)

		result := g.Match(test.match)
		if result != test.should {
			t.Errorf("pattern %q matching %q should be %v but got %v", test.pattern, test.match, test.should, result)
		}
	}
}

const Pattern = "*cat*eyes*"
const ExpPattern = ".*cat.*eyes.*"
const String = "my cat has very bright eyes"
//const Pattern = "*.google.com"
//const ExpPattern = ".*google\\.com"
//const String = "mail.google.com"

func BenchmarkGobwas(b *testing.B) {
	m := New(Pattern)

	for i := 0; i < b.N; i++ {
		_ = m.Match(String)
	}
}

func BenchmarkRyanuber(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = rGlob.Glob(Pattern, String)
	}
}
func BenchmarkRegExp(b *testing.B) {
	r := regexp.MustCompile(ExpPattern)
	for i := 0; i < b.N; i++ {
		_ = r.Match([]byte(String))
	}
}

var ALPHABET_S = []string{"a", "b", "c"}
const ALPHABET = "abc"
const STR = "faafsdfcsdffc"

func BenchmarkIndexOfAny(b *testing.B) {
	for i := 0; i < b.N; i++ {
		strings.IndexAny(STR, ALPHABET)
	}
}
func BenchmarkFirstIndexOfChars(b *testing.B) {
	for i := 0; i < b.N; i++ {
		firstIndexOfChars(STR, ALPHABET_S)
	}
}<commit_msg>:beers:<commit_after>package glob

import (
	rGlob "github.com/ryanuber/go-glob"
	"regexp"
	"strings"
	"testing"
)

type test struct {
	pattern, match string
	should         bool
	delimiters     []string
}

func glob(s bool, p, m string, d ...string) test {
	return test{p, m, s, d}
}

func TestFirstIndexOfChars(t *testing.T) {
	for _, test := range []struct {
		s string
		c []string
		i int
		r string
	}{
		{
			"**",
			[]string{"**", "*"},
			0,
			"**",
		},
		{
			"**",
			[]string{"*", "**"},
			0,
			"**",
		},
	} {
		i, r := firstIndexOfChars(test.s, test.c)
		if i != test.i || r != test.r {
			t.Errorf("unexpeted index: expected %q at %v, got %q at %v", test.r, test.i, r, i)
		}
	}
}

func TestGlob(t *testing.T) {
	for _, test := range []test{
		glob(true, "abc", "abc"),
		glob(true, "a*c", "abc"),
		glob(true, "a*c", "a12345c"),
		glob(true, "a?c", "a1c"),
		glob(true, "a.b", "a.b", "."),
		glob(true, "a.*", "a.b", "."),
		glob(true, "a.**", "a.b.c", "."),
		glob(true, "a.?.c", "a.b.c", "."),
		glob(true, "a.?.?", "a.b.c", "."),
		glob(true, "?at", "cat"),
		glob(true, "?at", "fat"),
		glob(true, "*", "abc"),
		glob(true, `\*`, "*"),
		glob(true, "**", "a.b.c", "."),

		glob(false, "?at", "at"),
		glob(false, "?at", "fat", "f"),
		glob(false, "a.*", "a.b.c", "."),
		glob(false, "a.?.c", "a.bb.c", "."),
		glob(false, "*", "a.b.c", "."),

		glob(true, "*test", "this is a test"),
		glob(true, "this*", "this is a test"),
		glob(true, "*is *", "this is a test"),
		glob(true, "*is*a*", "this is a test"),
		glob(true, "**test**", "this is a test"),
		glob(true, "**is**a***test*", "this is a test"),

		glob(false, "*is", "this is a test"),
		glob(false, "*no*", "this is a test"),
	} {
		g := New(test.pattern, test.delimiters...)

		result := g.Match(test.match)
		if result != test.should {
			t.Errorf("pattern %q matching %q should be %v but got %v", test.pattern, test.match, test.should, result)
		}
	}
}

const Pattern = "*cat*eyes*"
const ExpPattern = ".*cat.*eyes.*"
const String = "my cat has very bright eyes"

//const Pattern = "*.google.com"
//const ExpPattern = ".*google\\.com"
//const String = "mail.google.com"
// const Pattern = "google.com"
// const ExpPattern = "google\\.com"
// const String = "google.com"

func BenchmarkGobwas(b *testing.B) {
	m := New(Pattern)

	for i := 0; i < b.N; i++ {
		_ = m.Match(String)
	}
}

func BenchmarkRyanuber(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = rGlob.Glob(Pattern, String)
	}
}
func BenchmarkRegExp(b *testing.B) {
	r := regexp.MustCompile(ExpPattern)
	for i := 0; i < b.N; i++ {
		_ = r.Match([]byte(String))
	}
}

var ALPHABET_S = []string{"a", "b", "c"}

const ALPHABET = "abc"
const STR = "faafsdfcsdffc"

func BenchmarkIndexOfAny(b *testing.B) {
	for i := 0; i < b.N; i++ {
		strings.IndexAny(STR, ALPHABET)
	}
}
func BenchmarkFirstIndexOfChars(b *testing.B) {
	for i := 0; i < b.N; i++ {
		firstIndexOfChars(STR, ALPHABET_S)
	}
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"crypto/tls"
	httplib "net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	driver "github.com/arangodb/go-driver"
	"github.com/arangodb/go-driver/http"
	"github.com/arangodb/go-driver/vst"
	"github.com/arangodb/go-driver/vst/protocol"
)

var (
	logEndpointsOnce sync.Once
)

// skipBelowVersion skips the test if the current server version is less than
// the given version.
func skipBelowVersion(c driver.Client, version driver.Version, t *testing.T) {
	x, err := c.Version(nil)
	if err != nil {
		t.Fatalf("Failed to get version info: %s", describe(err))
	}
	if x.Version.CompareTo(version) < 0 {
		t.Skipf("Skipping below version '%s', got version '%s'", version, x.Version)
	}
}

// getEndpointsFromEnv returns the endpoints specified in the TEST_ENDPOINTS
// environment variable.
func getEndpointsFromEnv(t testEnv) []string {
	eps := strings.Split(os.Getenv("TEST_ENDPOINTS"), ",")
	if len(eps) == 0 {
		t.Fatal("No endpoints found in environment variable TEST_ENDPOINTS")
	}
	return eps
}

// getContentTypeFromEnv returns the content-type specified in the TEST_CONTENT_TYPE
// environment variable (json|vpack).
func getContentTypeFromEnv(t testEnv) driver.ContentType {
	switch ct := os.Getenv("TEST_CONTENT_TYPE"); ct {
	case "vpack":
		return driver.ContentTypeVelocypack
	case "json", "":
		return driver.ContentTypeJSON
	default:
		t.Fatalf("Unknown content type '%s'", ct)
		return 0
	}
}

// createAuthenticationFromEnv initializes an authentication specified in the TEST_AUTHENTICATION
// environment variable.
func createAuthenticationFromEnv(t testEnv) driver.Authentication {
	authSpec := os.Getenv("TEST_AUTHENTICATION")
	if authSpec == "" {
		return nil
	}
	parts := strings.Split(authSpec, ":")
	switch parts[0] {
	case "basic":
		if len(parts) != 3 {
			t.Fatalf("Expected username & password for basic authentication")
		}
		return driver.BasicAuthentication(parts[1], parts[2])
	case "jwt":
		if len(parts) != 3 {
			t.Fatalf("Expected username & password for jwt authentication")
		}
		return driver.JWTAuthentication(parts[1], parts[2])
	default:
		t.Fatalf("Unknown authentication: '%s'", parts[0])
		return nil
	}
}

// createConnectionFromEnv initializes a Connection from information specified in environment variables.
func createConnectionFromEnv(t testEnv) driver.Connection {
	connSpec := os.Getenv("TEST_CONNECTION")
	connVer := os.Getenv("TEST_CVERSION")
	switch connSpec {
	case "vst":
		var version protocol.Version
		switch connVer {
		case "1.0", "":
			version = protocol.Version1_0
		case "1.1":
			version = protocol.Version1_1
		default:
			t.Fatalf("Unknown connection version '%s'", connVer)
		}
		config := vst.ConnectionConfig{
			Endpoints: getEndpointsFromEnv(t),
			TLSConfig: &tls.Config{InsecureSkipVerify: true},
			Transport: protocol.TransportConfig{
				Version: version,
			},
		}
		conn, err := vst.NewConnection(config)
		if err != nil {
			t.Fatalf("Failed to create new vst connection: %s", describe(err))
		}
		return conn

	case "http", "":
		config := http.ConnectionConfig{
			Endpoints:   getEndpointsFromEnv(t),
			TLSConfig:   &tls.Config{InsecureSkipVerify: true},
			ContentType: getContentTypeFromEnv(t),
		}
		conn, err := http.NewConnection(config)
		if err != nil {
			t.Fatalf("Failed to create new http connection: %s", describe(err))
		}
		return conn

	default:
		t.Fatalf("Unknown connection type: '%s'", connSpec)
		return nil
	}
}

// createClientFromEnv initializes a Client from information specified in environment variables.
func createClientFromEnv(t testEnv, waitUntilReady bool, connection ...*driver.Connection) driver.Client {
	conn := createConnectionFromEnv(t)
	if len(connection) == 1 {
		*connection[0] = conn
	}
	c, err := driver.NewClient(driver.ClientConfig{
		Connection:     conn,
		Authentication: createAuthenticationFromEnv(t),
	})
	if err != nil {
		t.Fatalf("Failed to create new client: %s", describe(err))
	}
	if waitUntilReady {
		timeout := 3 * time.Minute
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()
		if up := waitUntilServerAvailable(ctx, c, t); !up {
			t.Fatalf("Connection is not available in %s", timeout)
		}
		// Synchronize endpoints
		if err := c.SynchronizeEndpoints(context.Background()); err != nil {
			t.Errorf("Failed to synchronize endpoints: %s", describe(err))
		} else {
			logEndpointsOnce.Do(func() {
				t.Logf("Found endpoints: %v", conn.Endpoints())
			})
		}
	}
	return c
}

// waitUntilServerAvailable keeps waiting until the server/cluster that the client is addressing is available.
func waitUntilServerAvailable(ctx context.Context, c driver.Client, t testEnv) bool {
	instanceUp := make(chan bool)
	go func() {
		for {
			verCtx, cancel := context.WithTimeout(ctx, time.Second*5)
			if _, err := c.Version(verCtx); err == nil {
				//t.Logf("Found version %s", v.Version)
				cancel()
				instanceUp <- true
				return
			} else {
				cancel()
				//t.Logf("Version failed: %s %#v", describe(err), err)
				time.Sleep(time.Second)
			}
		}
	}()
	select {
	case up := <-instanceUp:
		return up
	case <-ctx.Done():
		return false
	}
}

// TestCreateClientHttpConnection creates an HTTP connection to the environment specified
// endpoints and creates a client for that.
func TestCreateClientHttpConnection(t *testing.T) {
	conn, err := http.NewConnection(http.ConnectionConfig{
		Endpoints: getEndpointsFromEnv(t),
		TLSConfig: &tls.Config{InsecureSkipVerify: true},
	})
	if err != nil {
		t.Fatalf("Failed to create new http connection: %s", describe(err))
	}
	_, err = driver.NewClient(driver.ClientConfig{
		Connection:     conn,
		Authentication: createAuthenticationFromEnv(t),
	})
	if err != nil {
		t.Fatalf("Failed to create new client: %s", describe(err))
	}
}

// TestCreateClientHttpConnectionCustomTransport creates an HTTP connection to the environment specified
// endpoints with a custom HTTP roundtripper and creates a client for that.
func TestCreateClientHttpConnectionCustomTransport(t *testing.T) {
	conn, err := http.NewConnection(http.ConnectionConfig{
		Endpoints: getEndpointsFromEnv(t),
		Transport: &httplib.Transport{},
		TLSConfig: &tls.Config{InsecureSkipVerify: true},
	})
	if err != nil {
		t.Fatalf("Failed to create new http connection: %s", describe(err))
	}
	c, err := driver.NewClient(driver.ClientConfig{
		Connection:     conn,
		Authentication: createAuthenticationFromEnv(t),
	})
	if err != nil {
		t.Fatalf("Failed to create new client: %s", describe(err))
	}
	timeout := 3 * time.Minute
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	if up := waitUntilServerAvailable(ctx, c, t); !up {
		t.Fatalf("Connection is not available in %s", timeout)
	}
	if info, err := c.Version(driver.WithDetails(ctx)); err != nil {
		t.Errorf("Version failed: %s", describe(err))
	} else {
		t.Logf("Got server version %s", info)
	}
}

// TestResponseHeader checks the Response.Header function.
func TestResponseHeader(t *testing.T) {
	c := createClientFromEnv(t, true)
	ctx := context.Background()

	version, err := c.Version(nil)
	if err != nil {
		t.Fatalf("Version failed: %s", describe(err))
	}
	isv33p := version.Version.CompareTo("3.3") >= 0
	if !isv33p {
		t.Skip("This test requires version 3.3")
	} else {
		var resp driver.Response
		db := ensureDatabase(ctx, c, "_system", nil, t)
		col := ensureCollection(ctx, db, "response_header_test", nil, t)

		// `ETag` header must contain the `_rev` of the new document in quotes.
		doc := map[string]string{
			"Test":   "TestResponseHeader",
			"Intent": "Check Response.Header",
		}
		meta, err := col.CreateDocument(driver.WithResponse(ctx, &resp), doc)
		if err != nil {
			t.Fatalf("CreateDocument failed: %s", describe(err))
		}
		expectedETag := strconv.Quote(meta.Rev)
		if x := resp.Header("ETag"); x != expectedETag {
			t.Errorf("Unexpected result from Header('ETag'), got '%s', expected '%s'", x, expectedETag)
		}
		if x := resp.Header("etag"); x != expectedETag {
			t.Errorf("Unexpected result from Header('etag'), got '%s', expected '%s'", x, expectedETag)
		}
		if x := resp.Header("ETAG"); x != expectedETag {
			t.Errorf("Unexpected result from Header('ETAG'), got '%s', expected '%s'", x, expectedETag)
		}
	}
}
<commit_msg>Added `Etag` test variant<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"crypto/tls"
	httplib "net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	driver "github.com/arangodb/go-driver"
	"github.com/arangodb/go-driver/http"
	"github.com/arangodb/go-driver/vst"
	"github.com/arangodb/go-driver/vst/protocol"
)

var (
	logEndpointsOnce sync.Once
)

// skipBelowVersion skips the test if the current server version is less than
// the given version.
func skipBelowVersion(c driver.Client, version driver.Version, t *testing.T) {
	x, err := c.Version(nil)
	if err != nil {
		t.Fatalf("Failed to get version info: %s", describe(err))
	}
	if x.Version.CompareTo(version) < 0 {
		t.Skipf("Skipping below version '%s', got version '%s'", version, x.Version)
	}
}

// getEndpointsFromEnv returns the endpoints specified in the TEST_ENDPOINTS
// environment variable.
func getEndpointsFromEnv(t testEnv) []string {
	eps := strings.Split(os.Getenv("TEST_ENDPOINTS"), ",")
	if len(eps) == 0 {
		t.Fatal("No endpoints found in environment variable TEST_ENDPOINTS")
	}
	return eps
}

// getContentTypeFromEnv returns the content-type specified in the TEST_CONTENT_TYPE
// environment variable (json|vpack).
func getContentTypeFromEnv(t testEnv) driver.ContentType {
	switch ct := os.Getenv("TEST_CONTENT_TYPE"); ct {
	case "vpack":
		return driver.ContentTypeVelocypack
	case "json", "":
		return driver.ContentTypeJSON
	default:
		t.Fatalf("Unknown content type '%s'", ct)
		return 0
	}
}

// createAuthenticationFromEnv initializes an authentication specified in the TEST_AUTHENTICATION
// environment variable.
func createAuthenticationFromEnv(t testEnv) driver.Authentication {
	authSpec := os.Getenv("TEST_AUTHENTICATION")
	if authSpec == "" {
		return nil
	}
	parts := strings.Split(authSpec, ":")
	switch parts[0] {
	case "basic":
		if len(parts) != 3 {
			t.Fatalf("Expected username & password for basic authentication")
		}
		return driver.BasicAuthentication(parts[1], parts[2])
	case "jwt":
		if len(parts) != 3 {
			t.Fatalf("Expected username & password for jwt authentication")
		}
		return driver.JWTAuthentication(parts[1], parts[2])
	default:
		t.Fatalf("Unknown authentication: '%s'", parts[0])
		return nil
	}
}

// createConnectionFromEnv initializes a Connection from information specified in environment variables.
func createConnectionFromEnv(t testEnv) driver.Connection {
	connSpec := os.Getenv("TEST_CONNECTION")
	connVer := os.Getenv("TEST_CVERSION")
	switch connSpec {
	case "vst":
		var version protocol.Version
		switch connVer {
		case "1.0", "":
			version = protocol.Version1_0
		case "1.1":
			version = protocol.Version1_1
		default:
			t.Fatalf("Unknown connection version '%s'", connVer)
		}
		config := vst.ConnectionConfig{
			Endpoints: getEndpointsFromEnv(t),
			TLSConfig: &tls.Config{InsecureSkipVerify: true},
			Transport: protocol.TransportConfig{
				Version: version,
			},
		}
		conn, err := vst.NewConnection(config)
		if err != nil {
			t.Fatalf("Failed to create new vst connection: %s", describe(err))
		}
		return conn

	case "http", "":
		config := http.ConnectionConfig{
			Endpoints:   getEndpointsFromEnv(t),
			TLSConfig:   &tls.Config{InsecureSkipVerify: true},
			ContentType: getContentTypeFromEnv(t),
		}
		conn, err := http.NewConnection(config)
		if err != nil {
			t.Fatalf("Failed to create new http connection: %s", describe(err))
		}
		return conn

	default:
		t.Fatalf("Unknown connection type: '%s'", connSpec)
		return nil
	}
}

// createClientFromEnv initializes a Client from information specified in environment variables.
func createClientFromEnv(t testEnv, waitUntilReady bool, connection ...*driver.Connection) driver.Client {
	conn := createConnectionFromEnv(t)
	if len(connection) == 1 {
		*connection[0] = conn
	}
	c, err := driver.NewClient(driver.ClientConfig{
		Connection:     conn,
		Authentication: createAuthenticationFromEnv(t),
	})
	if err != nil {
		t.Fatalf("Failed to create new client: %s", describe(err))
	}
	if waitUntilReady {
		timeout := 3 * time.Minute
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()
		if up := waitUntilServerAvailable(ctx, c, t); !up {
			t.Fatalf("Connection is not available in %s", timeout)
		}
		// Synchronize endpoints
		if err := c.SynchronizeEndpoints(context.Background()); err != nil {
			t.Errorf("Failed to synchronize endpoints: %s", describe(err))
		} else {
			logEndpointsOnce.Do(func() {
				t.Logf("Found endpoints: %v", conn.Endpoints())
			})
		}
	}
	return c
}

// waitUntilServerAvailable keeps waiting until the server/cluster that the client is addressing is available.
func waitUntilServerAvailable(ctx context.Context, c driver.Client, t testEnv) bool {
	instanceUp := make(chan bool)
	go func() {
		for {
			verCtx, cancel := context.WithTimeout(ctx, time.Second*5)
			if _, err := c.Version(verCtx); err == nil {
				//t.Logf("Found version %s", v.Version)
				cancel()
				instanceUp <- true
				return
			} else {
				cancel()
				//t.Logf("Version failed: %s %#v", describe(err), err)
				time.Sleep(time.Second)
			}
		}
	}()
	select {
	case up := <-instanceUp:
		return up
	case <-ctx.Done():
		return false
	}
}

// TestCreateClientHttpConnection creates an HTTP connection to the environment specified
// endpoints and creates a client for that.
func TestCreateClientHttpConnection(t *testing.T) {
	conn, err := http.NewConnection(http.ConnectionConfig{
		Endpoints: getEndpointsFromEnv(t),
		TLSConfig: &tls.Config{InsecureSkipVerify: true},
	})
	if err != nil {
		t.Fatalf("Failed to create new http connection: %s", describe(err))
	}
	_, err = driver.NewClient(driver.ClientConfig{
		Connection:     conn,
		Authentication: createAuthenticationFromEnv(t),
	})
	if err != nil {
		t.Fatalf("Failed to create new client: %s", describe(err))
	}
}

// TestCreateClientHttpConnectionCustomTransport creates an HTTP connection to the environment specified
// endpoints with a custom HTTP roundtripper and creates a client for that.
func TestCreateClientHttpConnectionCustomTransport(t *testing.T) {
	conn, err := http.NewConnection(http.ConnectionConfig{
		Endpoints: getEndpointsFromEnv(t),
		Transport: &httplib.Transport{},
		TLSConfig: &tls.Config{InsecureSkipVerify: true},
	})
	if err != nil {
		t.Fatalf("Failed to create new http connection: %s", describe(err))
	}
	c, err := driver.NewClient(driver.ClientConfig{
		Connection:     conn,
		Authentication: createAuthenticationFromEnv(t),
	})
	if err != nil {
		t.Fatalf("Failed to create new client: %s", describe(err))
	}
	timeout := 3 * time.Minute
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	if up := waitUntilServerAvailable(ctx, c, t); !up {
		t.Fatalf("Connection is not available in %s", timeout)
	}
	if info, err := c.Version(driver.WithDetails(ctx)); err != nil {
		t.Errorf("Version failed: %s", describe(err))
	} else {
		t.Logf("Got server version %s", info)
	}
}

// TestResponseHeader checks the Response.Header function.
func TestResponseHeader(t *testing.T) {
	c := createClientFromEnv(t, true)
	ctx := context.Background()

	version, err := c.Version(nil)
	if err != nil {
		t.Fatalf("Version failed: %s", describe(err))
	}
	isv33p := version.Version.CompareTo("3.3") >= 0
	if !isv33p {
		t.Skip("This test requires version 3.3")
	} else {
		var resp driver.Response
		db := ensureDatabase(ctx, c, "_system", nil, t)
		col := ensureCollection(ctx, db, "response_header_test", nil, t)

		// `ETag` header must contain the `_rev` of the new document in quotes.
		doc := map[string]string{
			"Test":   "TestResponseHeader",
			"Intent": "Check Response.Header",
		}
		meta, err := col.CreateDocument(driver.WithResponse(ctx, &resp), doc)
		if err != nil {
			t.Fatalf("CreateDocument failed: %s", describe(err))
		}
		expectedETag := strconv.Quote(meta.Rev)
		if x := resp.Header("ETag"); x != expectedETag {
			t.Errorf("Unexpected result from Header('ETag'), got '%s', expected '%s'", x, expectedETag)
		}
		if x := resp.Header("Etag"); x != expectedETag {
			t.Errorf("Unexpected result from Header('Etag'), got '%s', expected '%s'", x, expectedETag)
		}
		if x := resp.Header("etag"); x != expectedETag {
			t.Errorf("Unexpected result from Header('etag'), got '%s', expected '%s'", x, expectedETag)
		}
		if x := resp.Header("ETAG"); x != expectedETag {
			t.Errorf("Unexpected result from Header('ETAG'), got '%s', expected '%s'", x, expectedETag)
		}
	}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/Charlesworth/goCounter/concurrentMap"
	"github.com/julienschmidt/httprouter"
)

type Server struct {
	pageViewMap *concurrentMap.Map
}

var saveLocation = "data/savedMap"

func main() {
	loadedPageViewMap, err := concurrentMap.LoadOrCreateIfDoesntExist(saveLocation)
	if err != nil {
		log.Fatal(err)
	}
	server := Server{pageViewMap: loadedPageViewMap}
	server.pageViewMap.SaveEveryInterval(saveLocation, time.Hour)
	http.Handle("/", newRouter(&server))

	//start the server and listen for requests
	log.Println("goCounter Listening on :3000...")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

func newRouter(server *Server) *httprouter.Router {
	router := httprouter.New()
	router.GET("/:pageID/count.js", server.getCountHandler)
	router.PUT("/:pageID", server.setCountHandler)
	router.GET("/", server.getStatsHandler)
	return router
}

func (server *Server) setCountHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	pageName := params.ByName("pageID")
	countString := r.FormValue("count")
	count, err := strconv.Atoi(countString)

	if err != nil {
		log.Println("Error: ", r.RemoteAddr, "put for page", pageName, "returned with error:", err)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Unable to parse PUT form value 'Count'")
		return
	}

	log.Println(r.RemoteAddr, "puts count", count, "for page", pageName)
	server.pageViewMap.Set(pageName, count)
}

func (server *Server) getStatsHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println(r.RemoteAddr + " requests page count statistics")
	viewCountMap := server.pageViewMap.GetMap()

	for page, views := range viewCountMap {
		fmt.Fprintln(w, "Page: [", page, "] Views:", views)
	}
}

func (server *Server) getCountHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	pageName := params.ByName("pageID")
	log.Println(r.RemoteAddr + " requests " + pageName)

	pageViews := server.pageViewMap.Increment(pageName)

	fmt.Fprintf(w, "document.getElementById('viewCount').innerHTML = '%v Page Views';", pageViews)
}
<commit_msg>removed the need for a data folder<commit_after>package main

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

	"github.com/Charlesworth/goCounter/concurrentMap"
	"github.com/julienschmidt/httprouter"
)

type Server struct {
	pageViewMap *concurrentMap.Map
}

var saveLocation = "savedMap"

func main() {
	loadedPageViewMap, err := concurrentMap.LoadOrCreateIfDoesntExist(saveLocation)
	if err != nil {
		log.Fatal(err)
	}
	server := Server{pageViewMap: loadedPageViewMap}
	server.pageViewMap.SaveEveryInterval(saveLocation, time.Hour)
	http.Handle("/", newRouter(&server))

	//start the server and listen for requests
	log.Println("goCounter Listening on :3000...")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

func newRouter(server *Server) *httprouter.Router {
	router := httprouter.New()
	router.GET("/:pageID/count.js", server.getCountHandler)
	router.PUT("/:pageID", server.setCountHandler)
	router.GET("/", server.getStatsHandler)
	return router
}

func (server *Server) setCountHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	pageName := params.ByName("pageID")
	countString := r.FormValue("count")
	count, err := strconv.Atoi(countString)

	if err != nil {
		log.Println("Error: ", r.RemoteAddr, "put for page", pageName, "returned with error:", err)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Unable to parse PUT form value 'Count'")
		return
	}

	log.Println(r.RemoteAddr, "puts count", count, "for page", pageName)
	server.pageViewMap.Set(pageName, count)
}

func (server *Server) getStatsHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println(r.RemoteAddr + " requests page count statistics")
	viewCountMap := server.pageViewMap.GetMap()

	for page, views := range viewCountMap {
		fmt.Fprintln(w, "Page: [", page, "] Views:", views)
	}
}

func (server *Server) getCountHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	pageName := params.ByName("pageID")
	log.Println(r.RemoteAddr + " requests " + pageName)

	pageViews := server.pageViewMap.Increment(pageName)

	fmt.Fprintf(w, "document.getElementById('viewCount').innerHTML = '%v Page Views';", pageViews)
}
<|endoftext|>


<commit_before>package gocaptcha

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"text/template"
)

var captchaHtml *template.Template

func init() {
	var err error
	captchaHtml, err = template.New("CaptchaHtml").Parse(`

	<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
	<input type="hidden" name="recaptcha_response_field" value="manual_challenge">

`)
	if err != nil {
		fmt.Printf("Error parsing CaptchaHtml template.")
		os.Exit(-1)
	}
}

// A GoCaptcha object identifies a single reCAPTCHA session (for one client).
// It is possible to store this object on a session activity and re-use it lateron for verification.
// This object keeps track of faulty verifications and makes sure any newly generated html contains an error message for the end-user, as provided by reCAPTCHA.
// Once a reCAPTCHA response was successfully verified this object should be discarded.
type GoCaptcha struct {
	publickey     string
	privatekey    string
	lastErrorCode string
	lastResult    bool
}

// NewGoCaptha creates a new GoCaptcha object.
// Privatekey is the api key to be used with reCAPTCHA.
func NewGoCaptcha(publickey string, privatekey string) *GoCaptcha {
	gc := &GoCaptcha{
		publickey:  publickey,
		privatekey: privatekey,
	}
	return gc
}

// Generate the reCAPTCHA html for this session and write it to the given io.Writer.
func (gc *GoCaptcha) WriteHtml(w io.Writer) error {
	err := captchaHtml.Execute(w, struct {
		PublicKey string
		ErrorCode string
	}{gc.publickey, gc.lastErrorCode})
	return err
}

// Generate the reCAPTCHA html for this session and return it as string.
// If error is not nil then something went wrong and string is empty.
func (gc *GoCaptcha) HtmlString() (string, error) {
	buf := new(bytes.Buffer)
	err := gc.WriteHtml(buf)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}

// Generate the reCAPTCHA html for this session and return it as byteslice.
// If error is not nil then something went wrong and the byteslice is empty.
func (gc *GoCaptcha) HtmlByteSlice() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := gc.WriteHtml(buf)
	if err != nil {
		return []byte{}, err
	}
	return buf.Bytes(), nil
}

// Verify calls the reCAPTCHA API to verify if the given response by end-user is correct.
// Any returned error indicates a unsuccessfull api call. It does not indicate that the reCAPTCHA response by the end-user was faulty.
// Any returned error value is not to be shown to the end-user.
// When the error is nil, then response=false indicates that the reCAPTCHA response by the end-user was faulty.
// End-user will be notified of a faulty reCAPTCHA response when re-using this GoCaptcha object to generate html code again.
//
// Expected parameters:
// challenge string, form value as sent by the http request. (Set by the reCAPTCHA in the end-users browser.)
// response string, form value as sent by the http request. (The answer given by the end-user.)
// remoteaddr string, The http.Request.RemoteAddr (e.g. "127.0.0.1:45435") from the client's endpoint.
func (gc *GoCaptcha) Verify(challenge string, response string, remoteaddr string) (bool, error) {
	if gc.lastResult {
		return false, errors.New("This GoCaptcha session has already been successfully verified. Please create a new GoCaptcha session.")
	}

	remoteip, _, err := net.SplitHostPort(remoteaddr)
	if err != nil {
		return false, err
	}

	apiRequestValues := url.Values{}
	apiRequestValues.Set("privatekey", gc.privatekey)
	apiRequestValues.Set("remoteip", remoteip)
	apiRequestValues.Set("challenge", challenge)
	apiRequestValues.Set("response", response)
	apiResponse, err := http.PostForm("https://www.google.com/recaptcha/api/verify", apiRequestValues)
	if err != nil {
		return false, err
	}
	defer apiResponse.Body.Close()
	reader := bufio.NewReader(apiResponse.Body)

	// read first line
	line1, err := reader.ReadString('\n')
	if err != nil {
		return false, errors.New("Received unexpected result value from reCAPTCHA API.")
	}
	switch line1 {
	case "true\n":
		gc.lastResult = true
		gc.lastErrorCode = ""
		return true, nil

	case "false\n":
		// read second line
		line2, err := reader.ReadString('\n')
		if err != nil && err != io.EOF {
			return false, errors.New("Received unexpected result value from reCAPTCHA API.")
		}
		gc.lastErrorCode = line2

	default:
		return false, errors.New("Received unexpected result value from reCAPTCHA API.")
	}

	return false, nil
}
<commit_msg>html=HTML. Renamed HtmlByteSlice to HtmlBytes<commit_after>package gocaptcha

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"text/template"
)

var captchaHTML *template.Template

func init() {
	var err error
	captchaHTML, err = template.New("CaptchaHTML").Parse(`

	<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
	<input type="hidden" name="recaptcha_response_field" value="manual_challenge">

`)
	if err != nil {
		fmt.Printf("Error parsing CaptchaHTML template.")
		os.Exit(-1)
	}
}

// A GoCaptcha object identifies a single reCAPTCHA session (for one client).
// It is possible to store this object on a session activity and re-use it lateron for verification.
// This object keeps track of faulty verifications and makes sure any newly generated HTML contains an error message for the end-user, as provided by reCAPTCHA.
// Once a reCAPTCHA response was successfully verified this object should be discarded.
type GoCaptcha struct {
	publickey     string
	privatekey    string
	lastErrorCode string
	lastResult    bool
}

// NewGoCaptha creates a new GoCaptcha object.
// Privatekey is the api key to be used with reCAPTCHA.
func NewGoCaptcha(publickey string, privatekey string) *GoCaptcha {
	gc := &GoCaptcha{
		publickey:  publickey,
		privatekey: privatekey,
	}
	return gc
}

// Generate the reCAPTCHA HTML for this session and write it to the given io.Writer.
func (gc *GoCaptcha) WriteHTML(w io.Writer) error {
	err := captchaHTML.Execute(w, struct {
		PublicKey string
		ErrorCode string
	}{gc.publickey, gc.lastErrorCode})
	return err
}

// Generate the reCAPTCHA HTML for this session and return it as string.
// If error is not nil then something went wrong and string is empty.
func (gc *GoCaptcha) HTMLString() (string, error) {
	buf := new(bytes.Buffer)
	err := gc.WriteHTML(buf)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}

// Generate the reCAPTCHA HTML for this session and return it as byteslice.
// If error is not nil then something went wrong and the byteslice is empty.
func (gc *GoCaptcha) HTMLBytes() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := gc.WriteHTML(buf)
	if err != nil {
		return []byte{}, err
	}
	return buf.Bytes(), nil
}

// Verify calls the reCAPTCHA API to verify if the given response by end-user is correct.
// Any returned error indicates a unsuccessfull api call. It does not indicate that the reCAPTCHA response by the end-user was faulty.
// Any returned error value is not to be shown to the end-user.
// When the error is nil, then response=false indicates that the reCAPTCHA response by the end-user was faulty.
// End-user will be notified of a faulty reCAPTCHA response when re-using this GoCaptcha object to generate HTML code again.
//
// Expected parameters:
// challenge string, form value as sent by the http request. (Set by the reCAPTCHA in the end-users browser.)
// response string, form value as sent by the http request. (The answer given by the end-user.)
// remoteaddr string, The http.Request.RemoteAddr (e.g. "127.0.0.1:45435") from the client's endpoint.
func (gc *GoCaptcha) Verify(challenge string, response string, remoteaddr string) (bool, error) {
	if gc.lastResult {
		return false, errors.New("This GoCaptcha session has already been successfully verified. Please create a new GoCaptcha session.")
	}

	remoteip, _, err := net.SplitHostPort(remoteaddr)
	if err != nil {
		return false, err
	}

	apiRequestValues := url.Values{}
	apiRequestValues.Set("privatekey", gc.privatekey)
	apiRequestValues.Set("remoteip", remoteip)
	apiRequestValues.Set("challenge", challenge)
	apiRequestValues.Set("response", response)
	apiResponse, err := http.PostForm("https://www.google.com/recaptcha/api/verify", apiRequestValues)
	if err != nil {
		return false, err
	}
	defer apiResponse.Body.Close()
	reader := bufio.NewReader(apiResponse.Body)

	// read first line
	line1, err := reader.ReadString('\n')
	if err != nil {
		return false, errors.New("Received unexpected result value from reCAPTCHA API.")
	}
	switch line1 {
	case "true\n":
		gc.lastResult = true
		gc.lastErrorCode = ""
		return true, nil

	case "false\n":
		// read second line
		line2, err := reader.ReadString('\n')
		if err != nil && err != io.EOF {
			return false, errors.New("Received unexpected result value from reCAPTCHA API.")
		}
		gc.lastErrorCode = line2

	default:
		return false, errors.New("Received unexpected result value from reCAPTCHA API.")
	}

	return false, nil
}
<|endoftext|>


<commit_before>package main

import (
	"../libgolb"
	"github.com/docopt/docopt-go"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"strings"
)
var origins = map[string]string{}
func getServer() (server string) {
	server = libgolb.Conf.BackServers[libgolb.RoundRobin]
	libgolb.RoundRobin++
	if libgolb.RoundRobin >= libgolb.NumberBack {
		libgolb.RoundRobin = 0
	}
	return
}

func golbGet(w http.ResponseWriter, req *http.Request) {
	var secondResp *http.Response
	var errsp error

	//serv := strings.Split(req.RemoteAddr, ":") // extract just IP without port
	origin := req.RemoteAddr
	libgolb.Log("misc", "Access From :"+origin)
	server, errGS := origins[origin]
	if errGS == false {
		server = getServer()
	}
	limit := 0
	for limit < libgolb.NumberBack {
		resp, _ := http.NewRequest(req.Method, "http://"+server+"/", nil)
		for k, v := range req.Header {
			resp.Header[k] = v
		}
		resp.Header.Set("X-Forwarded-For", req.RemoteAddr)
		secondResp, errsp = http.DefaultClient.Do(resp)
		if errsp != nil {
			libgolb.Log("error", "Connection with the HTTP file server failed: "+errsp.Error())
			server = getServer()
			limit++
		} else {
			defer secondResp.Body.Close()
			break
		}
	}
	if limit >= libgolb.NumberBack {
		libgolb.HttpResponse(w, 500, "Internal server error\n")
		libgolb.Log("error", "No Backend Server avalaible")
		return
	}
	for k, v := range secondResp.Header {
		w.Header().Add(k, strings.Join(v, ""))
	}
	w.Header().Set("Status", "200")
	io.Copy(w, secondResp.Body)
	origins[origin] = server
	libgolb.Log("ok", "Answer From :"+origin)
	libgolb.LogW3C(w, req, false)
}

func parseArgument(configuration string) {

	// Load configuration
	libgolb.ConfLoad(configuration)
	// Router
	rtr := mux.NewRouter()
	rtr.HandleFunc("/", golbGet).Methods("GET")
	http.Handle("/", rtr)

	// Listening
	libgolb.Log("ok", "Listening on "+libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port)
	err := http.ListenAndServe(libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port, nil)
	libgolb.ErrCatcher("ListenAndServe: ", err)
}

func main() {
	usage := `Golb.

Usage:
  golb <configuration>
  golb -h | --help
  golb --version

Options:
  -h --help     Show this screen.
  --version     Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, "GoLB 0.1", false)
	parseArgument(arguments["<configuration>"].(string))
}
<commit_msg>Add comments and configure it for more performance<commit_after>package main

import (
	"../libgolb"
	"github.com/docopt/docopt-go"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"strings"
)

var origins = map[string]string{} // map of originip/backend

func getServer() (server string) {
	server = libgolb.Conf.BackServers[libgolb.RoundRobin]
	libgolb.RoundRobin++
	if libgolb.RoundRobin >= libgolb.NumberBack {
		libgolb.RoundRobin = 0
	}
	return
}

func golbGet(w http.ResponseWriter, req *http.Request) {
	var secondResp *http.Response
	var errsp error

	serv := strings.Split(req.RemoteAddr, ":") // extract just IP without port, it can be a good idea on a limited system !!!
	//origin := req.RemoteAddr // here is the best solution, but use a lot of memory !!!
	origin := serv[0]
	libgolb.Log("misc", "Access From :"+origin)
	server, errGS := origins[origin]
	if errGS == false {
		server = getServer()
	}
	limit := 0
	for limit < libgolb.NumberBack { // this for is used to check all servers and select the first one available
		resp, _ := http.NewRequest(req.Method, "http://"+server+"/", nil)
		for k, v := range req.Header {
			resp.Header[k] = v
		}
		resp.Header.Set("X-Forwarded-For", req.RemoteAddr)
		secondResp, errsp = http.DefaultClient.Do(resp)
		if errsp != nil {
			libgolb.Log("error", "Connection with the HTTP file server failed: "+errsp.Error())
			server = getServer()
			limit++
		} else {
			defer secondResp.Body.Close() // don't forget to close the Body !!!
			break
		}
	}
	if limit >= libgolb.NumberBack { // No Backend
		libgolb.HttpResponse(w, 500, "Internal server error\n")
		libgolb.Log("error", "No Backend Server avalaible")
		return
	}
	for k, v := range secondResp.Header { // Copy Header
		w.Header().Add(k, strings.Join(v, ""))
	}
	w.Header().Set("Status", "200")
	io.Copy(w, secondResp.Body)
	origins[origin] = server
	libgolb.Log("ok", "Answer From :"+origin)
	libgolb.LogW3C(w, req, false)
}

func parseArgument(configuration string) {

	// Load configuration
	libgolb.ConfLoad(configuration)
	// Router
	rtr := mux.NewRouter()
	rtr.HandleFunc("/", golbGet).Methods("GET")
	http.Handle("/", rtr)

	// Listening
	libgolb.Log("ok", "Listening on "+libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port)
	err := http.ListenAndServe(libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port, nil)
	libgolb.ErrCatcher("ListenAndServe: ", err)
}

func main() {
	usage := `Golb.

Usage:
  golb <configuration>
  golb -h | --help
  golb --version

Options:
  -h --help     Show this screen.
  --version     Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, "GoLB 0.1", false)
	parseArgument(arguments["<configuration>"].(string))
}
<|endoftext|>


<commit_before>// googoauth is an oauth library for command-line tools
//
// It eases the process of connecting to Google Services via oauth, and storing
// credentials across invocations of a command line tool.
//
// It is closely based on the example code from the google-api-go-client, here:
// https://github.com/google/google-api-go-client/blob/master/examples/main.go

package googoauth

import (
	"encoding/gob"
	"flag"
	"fmt"
	"hash/fnv"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

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

// Flags
var (
	debug    = flag.Bool("debug.http", false, "show HTTP traffic")
	authport = flag.String("authport", "12345", "HTTP Server port.  Only needed for the first run, your browser will send credentials here.  Must be accessible to your browser, and authorized in the developer console.")
)

// Client accepts the connection details, and makes an oAuth connection
//
// id and secret are the CLIENT ID and CLIENT SECRET which you can generate at
// the Google Developer Console: http://console.developers.google.com
// You want an "Installed Application" of type "Other".
//
// Scope defines the access you are requesting, it is specific to the application.
// Strings are URLs, eg. "https://www.googleapis.com/auth/calendar", typically
// accessed in Go via the constants in the Go API, eg.
// directory.AdminDirectoryGroupScope
func Client(id, secret string, scope []string) *http.Client {
	config := &oauth2.Config{
		ClientID:     id,
		ClientSecret: secret,
		Scopes:       scope,
		Endpoint:     google.Endpoint,
	}

	ctx := context.Background()
	if *debug {
		ctx = context.WithValue(ctx, oauth2.HTTPClient, &http.Client{
			Transport: &logTransport{http.DefaultTransport},
		})
	}
	return newOAuthClient(ctx, config)
}

func osUserCacheDir() string {
	switch runtime.GOOS {
	case "darwin":
		return filepath.Join(os.Getenv("HOME"), "Library", "Caches")
	case "linux", "freebsd":
		return filepath.Join(os.Getenv("HOME"), ".cache")
	}
	log.Printf("TODO: osUserCacheDir on GOOS %q", runtime.GOOS)
	return "."
}

func tokenCacheFile(config *oauth2.Config) string {
	hash := fnv.New32a()
	hash.Write([]byte(config.ClientID))
	hash.Write([]byte(config.ClientSecret))
	hash.Write([]byte(strings.Join(config.Scopes, " ")))
	fn := fmt.Sprintf("googoauth-tok%v", hash.Sum32())
	return filepath.Join(osUserCacheDir(), url.QueryEscape(fn))
}

func tokenFromFile(file string) (*oauth2.Token, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	t := new(oauth2.Token)
	err = gob.NewDecoder(f).Decode(t)
	return t, err
}

func saveToken(file string, token *oauth2.Token) {
	f, err := os.Create(file)
	if err != nil {
		log.Printf("Warning: failed to cache oauth token: %v", err)
		return
	}
	defer f.Close()
	gob.NewEncoder(f).Encode(token)
}

func newOAuthClient(ctx context.Context, config *oauth2.Config) *http.Client {
	cache := tokenCacheFile(config)
	token, err := tokenFromFile(cache)
	if err != nil {
		token = tokenFromWeb(ctx, config)
		saveToken(cache, token)
	} else {
		// log.Printf("Using cached token %#v from %q", token, cache)
	}

	return config.Client(ctx, token)
}

// Only works for very limited scopes.  I haven't bothered to get it working.
// https://developers.google.com/accounts/docs/OAuth2ForDevices#allowedscopes
func tokenFromConsole(ctx context.Context, config *oauth2.Config) *oauth2.Token {
	url := config.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Printf("Visit the URL to the authorize this application: %v\n", url)

	// Ask the user for the auth code
	var code string
	fmt.Printf("Paste the token you received: ")
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatalf("Failure reading response: %v", err)
	}

	// Exchange it for a token
	token, err := config.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatalf("Token exchange error: %v", err)
	}
	return token
}

func tokenFromWeb(ctx context.Context, config *oauth2.Config) *oauth2.Token {
	go http.ListenAndServe(fmt.Sprintf("localhost:%s", *authport), nil)
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	http.HandleFunc("/auth", func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		log.Printf("no code")
		http.Error(rw, "", 500)
	})

	config.RedirectURL = fmt.Sprintf("http://localhost:%s/auth", *authport)
	authURL := config.AuthCodeURL(randState)
	go openURL(authURL)
	log.Printf("Authorize this app at: %s", authURL)
	code := <-ch
	log.Printf("Got code: %s", code)

	token, err := config.Exchange(ctx, code)
	if err != nil {
		log.Fatalf("Token exchange error: %v", err)
	}
	return token
}

func openURL(url string) {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return
		}
	}
	log.Printf("Error opening URL in browser.")
}
<commit_msg>Support DeviceCode workflow, automatically.<commit_after>// googoauth is an oauth library for command-line tools
//
// It eases the process of connecting to Google Services via oauth, and storing
// credentials across invocations of a command line tool.
//
// It is closely based on the example code from the google-api-go-client, here:
// https://github.com/google/google-api-go-client/blob/master/examples/main.go

package googoauth

import (
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"hash/fnv"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

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

// Flags
var (
	debug    = flag.Bool("debug.http", false, "show HTTP traffic")
	authport = flag.String("authport", "12345", "HTTP Server port.  Only needed for the first run, your browser will send credentials here.  Must be accessible to your browser, and authorized in the developer console.")
)

var (
	DeviceCodeURL    = "https://accounts.google.com/o/oauth2/device/code"
	TokenPollURL     = "https://www.googleapis.com/oauth2/v3/token"
	DeviceGrantType  = "http://oauth.net/grant_type/device/1.0"
	DeviceCodeScopes = map[string]struct{}{
		"profile": struct{}{},
		"openid":  struct{}{},
		"email":   struct{}{},
		"https://www.googleapis.com/auth/analytics":               struct{}{},
		"https://www.googleapis.com/auth/analytics.readonly":      struct{}{},
		"https://www.googleapis.com/auth/calendar":                struct{}{},
		"https://www.googleapis.com/auth/calendar.readonly":       struct{}{},
		"https://www.google.com/m8/feeds":                         struct{}{},
		"https://www.googleapis.com/auth/contacts.readonly":       struct{}{},
		"https://www.googleapis.com/auth/cloudprint":              struct{}{},
		"https://www.googleapis.com/auth/devstorage.full_control": struct{}{},
		"https://www.googleapis.com/auth/devstorage.read_write":   struct{}{},
		"https://www.googleapis.com/auth/fitness.activity.read":   struct{}{},
		"https://www.googleapis.com/auth/fitness.activity.write":  struct{}{},
		"https://www.googleapis.com/auth/fitness.body.read":       struct{}{},
		"https://www.googleapis.com/auth/fitness.body.write":      struct{}{},
		"https://www.googleapis.com/auth/fitness.location.read":   struct{}{},
		"https://www.googleapis.com/auth/fitness.location.write":  struct{}{},
		"https://www.googleapis.com/auth/fusiontables":            struct{}{},
		"https://www.googleapis.com/auth/youtube":                 struct{}{},
		"https://www.googleapis.com/auth/youtube.readonly":        struct{}{},
		"https://www.googleapis.com/auth/youtube.upload":          struct{}{},
		"https://www.googleapis.com/auth/drive.file":              struct{}{},
	}
)

// Client accepts the connection details, and makes an oAuth connection
//
// id and secret are the CLIENT ID and CLIENT SECRET which you can generate at
// the Google Developer Console: http://console.developers.google.com
// You want an "Installed Application" of type "Other".
//
// Scope defines the access you are requesting, it is specific to the application.
// Strings are URLs, eg. "https://www.googleapis.com/auth/calendar", typically
// accessed in Go via the constants in the Go API, eg.
// directory.AdminDirectoryGroupScope
func Client(id, secret string, scope []string) *http.Client {
	config := &oauth2.Config{
		ClientID:     id,
		ClientSecret: secret,
		Scopes:       scope,
		Endpoint:     google.Endpoint,
	}

	ctx := context.Background()
	if *debug {
		ctx = context.WithValue(ctx, oauth2.HTTPClient, &http.Client{
			Transport: &logTransport{http.DefaultTransport},
		})
	}
	return newOAuthClient(ctx, config)
}

func osUserCacheDir() string {
	switch runtime.GOOS {
	case "darwin":
		return filepath.Join(os.Getenv("HOME"), "Library", "Caches")
	case "linux", "freebsd":
		return filepath.Join(os.Getenv("HOME"), ".cache")
	}
	log.Printf("TODO: osUserCacheDir on GOOS %q", runtime.GOOS)
	return "."
}

func tokenCacheFile(config *oauth2.Config) string {
	hash := fnv.New32a()
	hash.Write([]byte(config.ClientID))
	hash.Write([]byte(config.ClientSecret))
	hash.Write([]byte(strings.Join(config.Scopes, " ")))
	fn := fmt.Sprintf("googoauth-tok%v", hash.Sum32())
	return filepath.Join(osUserCacheDir(), url.QueryEscape(fn))
}

func tokenFromFile(file string) (*oauth2.Token, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	t := new(oauth2.Token)
	err = gob.NewDecoder(f).Decode(t)
	return t, err
}

func saveToken(file string, token *oauth2.Token) {
	f, err := os.Create(file)
	if err != nil {
		log.Printf("Warning: failed to cache oauth token: %v", err)
		return
	}
	defer f.Close()
	gob.NewEncoder(f).Encode(token)
}

func newOAuthClient(ctx context.Context, config *oauth2.Config) *http.Client {
	cache := tokenCacheFile(config)
	token, err := tokenFromFile(cache)
	if err != nil {
		token = tokenFromGoogle(ctx, config)
		saveToken(cache, token)
	} else {
		// log.Printf("Using cached token %#v from %q", token, cache)
	}

	return config.Client(ctx, token)
}

// tokenFromGoogle chooses the easiest method for the user to acquire an OAuth
// token for the provided scope.
func tokenFromGoogle(ctx context.Context, config *oauth2.Config) *oauth2.Token {
	for _, scope := range config.Scopes {
		if _, ok := DeviceCodeScopes[scope]; !ok {
			return tokenFromWeb(ctx, config)
		}
	}
	return tokenFromConsole(ctx, config)
}

// tokenFromConsole uses the much easier flow for "Devices", but it only works
// for very limited scopes, named in DeviceCodeScopes.  For more details, see:
// https://developers.google.com/identity/protocols/OAuth2ForDevices#allowedscopes
func tokenFromConsole(ctx context.Context, config *oauth2.Config) *oauth2.Token {
	code, interval, err := getDeviceCode(config)
	if err != nil {
		log.Fatalf("Failed to get Device Code: %v", err)
	}

	token, err := pollOAuthConfirmation(config, code, interval)
	if err != nil {
		log.Fatalf("Failed to get authorization token: %v", err)
	}

	return token
}

// getDeviceCode follows the token acquisition steps outlined here:
// https://developers.google.com/identity/protocols/OAuth2ForDevices
func getDeviceCode(config *oauth2.Config) (string, int, error) {
	form := url.Values{
		"client_id": {config.ClientID},
		"scope":     {strings.Join(config.Scopes, " ")},
	}
	response, err := http.PostForm(DeviceCodeURL, form)
	if err != nil {
		return "", 0, err
	}

	var r struct {
		DeviceCode      string `json:"device_code"`
		UserCode        string `json:"user_code"`
		VerificationURL string `json:"verification_url"`
		ExpiresIn       int    `json:"expires_in"`
		Interval        int    `json:"interval"`
	}
	json.NewDecoder(response.Body).Decode(&r)

	fmt.Printf("Visit %s and enter this code. I'll wait for you.\n%s\n",
		r.VerificationURL, r.UserCode)

	return r.DeviceCode, r.Interval, nil
}

// pollOAuthConfirmation awaits a response token, as described here:
// https://developers.google.com/identity/protocols/OAuth2ForDevices
// deviceCode is the code presented to the user
// interval is the poll interval in seconds allowed by Google's OAuth servers.
func pollOAuthConfirmation(config *oauth2.Config, deviceCode string, interval int) (*oauth2.Token, error) {
	for {
		time.Sleep(time.Duration(interval) * time.Second)

		form := url.Values{
			"client_id":     {config.ClientID},
			"client_secret": {config.ClientSecret},
			"code":          {deviceCode},
			"grant_type":    {DeviceGrantType},
		}
		response, err := http.PostForm(TokenPollURL, form)
		if err != nil {
			return nil, err
		}

		var r struct {
			Error        string `json:"error"`
			AccessToken  string `json:"access_token"`
			ExpiresIn    int    `json:"expires_in"`
			RefreshToken string `json:"refresh_token"`
		}
		json.NewDecoder(response.Body).Decode(&r)

		switch r.Error {
		case "":
			return &oauth2.Token{RefreshToken: r.RefreshToken}, nil
		case "authorization_pending":
		case "slow_down":
			interval *= 2
		default:
			return nil, err
		}
	}

	panic("unreachable")
}

// tokenFromWeb works for all scopes, but requires the user to create a path
// for their webbrowser connect localhost to this process, or requires them to
// paste a URL that failed to load into the console where this process is
// running.  Both are a suboptimal user experience, IMHO.
func tokenFromWeb(ctx context.Context, config *oauth2.Config) *oauth2.Token {
	go http.ListenAndServe(fmt.Sprintf("localhost:%s", *authport), nil)
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	http.HandleFunc("/auth", func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		log.Printf("no code")
		http.Error(rw, "", 500)
	})

	config.RedirectURL = fmt.Sprintf("http://localhost:%s/auth", *authport)
	authURL := config.AuthCodeURL(randState)
	go openURL(authURL)
	log.Printf("Authorize this app at: %s", authURL)
	code := <-ch
	log.Printf("Got code: %s", code)

	token, err := config.Exchange(ctx, code)
	if err != nil {
		log.Fatalf("Token exchange error: %v", err)
	}
	return token
}

func openURL(url string) {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return
		}
	}
	log.Printf("Error opening URL in browser.")
}
<|endoftext|>