<commit_before>// Copyright 2016 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run gen_helper.go

package ptyy

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
)

var _ = fmt.Sprintf

// 医院信息
type HospitalInfo struct {
	Name string // 名称
	City string // 城市
}

// 医院列表
var All []HospitalInfo = _AllHospitalInfoList

// 查询列表
func Search(query string, limits int) []HospitalInfo {
	// 规范化: 删除前后空白
	query = strings.TrimSpace(query)
	query = strings.ToLower(query)

	// 如果为空的话, 返回全部
	if query == "" {
		if limits <= 0 || limits > len(All) {
			return All
		}
		return All[:limits]
	}

	// 根据关键字查询
	if results := searchByKeywords(query, limits); len(results) > 0 {
		return results
	}

	// 如果没有匹配的, 则尝试正则查询
	if re, err := regexp.Compile(query); err == nil {
		if results := searchByRegexp(re, limits); len(results) > 0 {
			return results
		}
	}

	// 没有匹配
	return nil
}

// 根据关键字查询
func SearchByKeywords(keywords string, limits int) []HospitalInfo {
	// 规范化: 删除前后空白
	keywords = strings.TrimSpace(keywords)
	keywords = strings.ToLower(keywords)

	// 如果为空的话, 返回全部
	if keywords == "" {
		if limits <= 0 || limits > len(All) {
			return All
		}
		return All[:limits]
	}

	// 根据关键字查询
	if results := searchByKeywords(keywords, limits); len(results) > 0 {
		return results
	}

	// 没有匹配
	return nil
}

// 根据关键字查询
func SearchByRegexp(query string, limits int) ([]HospitalInfo, error) {
	// 规范化: 删除前后空白
	query = strings.TrimSpace(query)

	// 如果为空的话, 返回全部
	if query == "" || query == ".*" {
		if limits <= 0 || limits > len(All) {
			return All, nil
		}
		return All[:limits], nil
	}

	// 如果没有匹配的, 则尝试正则查询
	re, err := regexp.Compile(query)
	if err != nil {
		return nil, err
	}
	if results := searchByRegexp(re, limits); len(results) > 0 {
		return results, nil
	}

	// 没有匹配
	return nil, nil
}

// 根据关键字查询
// TODO: 以后可扩展为多个关键字, 采用非字符字符分隔
func searchByKeywords(key string, limits int) (results []HospitalInfo) {
	result0Map := make(map[string]HospitalInfo)
	result1Map := make(map[string]HospitalInfo)

	for _, v := range All {
		if limits > 0 && len(result0Map)+len(result1Map) >= limits {
			break
		}
		if strings.HasPrefix(v.Name, key) || strings.HasPrefix(v.City, key) {
			result0Map[v.Name] = v
		}
		if strings.HasPrefix(_NamePinyinLongMap[v.Name], key) || strings.HasPrefix(_NamePinyinShortMap[v.Name], key) {
			result0Map[v.Name] = v
		}
		if strings.HasPrefix(_NamePinyinLongMap[v.City], key) || strings.HasPrefix(_NamePinyinShortMap[v.City], key) {
			result0Map[v.Name] = v
		}
	}
	for _, v := range All {
		if limits > 0 && len(result0Map)+len(result1Map) >= limits {
			break
		}
		if strings.Contains(v.Name, key) || strings.Contains(v.City, key) {
			if _, ok := result0Map[v.Name]; !ok {
				result1Map[v.Name] = v
			}
		}
		if strings.Contains(_NamePinyinLongMap[v.Name], key) || strings.Contains(_NamePinyinShortMap[v.Name], key) {
			if _, ok := result0Map[v.Name]; !ok {
				result1Map[v.Name] = v
			}
		}
		if strings.Contains(_NamePinyinLongMap[v.City], key) || strings.Contains(_NamePinyinShortMap[v.City], key) {
			if _, ok := result0Map[v.Name]; !ok {
				result1Map[v.Name] = v
			}
		}
	}

	var result0List []HospitalInfo
	var result1List []HospitalInfo

	for _, v := range result0Map {
		result0List = append(result0List, v)
	}
	for _, v := range result1Map {
		result1List = append(result1List, v)
	}

	sort.Sort(byHospitalInfo(result0List))
	sort.Sort(byHospitalInfo(result1List))

	results = append(results, result0List...)
	results = append(results, result1List...)

	return
}

// 根据正则表达式查询
func searchByRegexp(re *regexp.Regexp, limits int) []HospitalInfo {
	resultMap := make(map[string]HospitalInfo)

	for _, v := range All {
		if limits > 0 && len(resultMap) >= limits {
			break
		}
		if re.MatchString(v.Name) || re.MatchString(v.City) {
			resultMap[v.Name] = v
		}
		if re.MatchString(_NamePinyinLongMap[v.Name]) || re.MatchString(_NamePinyinShortMap[v.Name]) {
			resultMap[v.Name] = v
		}
		if re.MatchString(_NamePinyinLongMap[v.City]) || re.MatchString(_NamePinyinShortMap[v.City]) {
			resultMap[v.Name] = v
		}
	}

	var resultList []HospitalInfo
	for _, v := range resultMap {
		resultList = append(resultList, v)
	}
	sort.Sort(byHospitalInfo(resultList))
	return resultList
}

// 按unicode排序
type byHospitalInfo []HospitalInfo

func (d byHospitalInfo) Len() int           { return len(d) }
func (d byHospitalInfo) Less(i, j int) bool { return d[i].Name < d[j].Name }
func (d byHospitalInfo) Swap(i, j int)      { d[i], d[j] = d[j], d[i] }
<commit_msg>正则中的元字符可以用对应中文字符代替<commit_after>// Copyright 2016 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run gen_helper.go

package ptyy

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
	"unicode/utf8"
)

var _ = fmt.Sprintf

// 医院信息
type HospitalInfo struct {
	Name string // 名称
	City string // 城市
}

// 医院列表
var All []HospitalInfo = _AllHospitalInfoList

// 查询列表
func Search(query string, limits int) []HospitalInfo {
	// 规范化: 删除前后空白
	query = strings.TrimSpace(query)
	query = strings.ToLower(query)

	// 如果为空的话, 返回全部
	if query == "" {
		if limits <= 0 || limits > len(All) {
			return All
		}
		return All[:limits]
	}

	if !utf8.ValidString(query) {
		return nil
	}

	// 根据关键字查询
	if results := searchByKeywords(query, limits); len(results) > 0 {
		return results
	}

	// 如果没有匹配的, 则尝试正则查询
	if re, err := regexp.Compile(goodRegexpString(query)); err == nil {
		if results := searchByRegexp(re, limits); len(results) > 0 {
			return results
		}
	}

	// 没有匹配
	return nil
}

// 根据关键字查询
func SearchByKeywords(keywords string, limits int) []HospitalInfo {
	if !utf8.ValidString(keywords) {
		return nil
	}

	// 规范化: 删除前后空白
	keywords = strings.TrimSpace(keywords)
	keywords = strings.ToLower(keywords)

	// 如果为空的话, 返回全部
	if keywords == "" {
		if limits <= 0 || limits > len(All) {
			return All
		}
		return All[:limits]
	}

	// 根据关键字查询
	if results := searchByKeywords(keywords, limits); len(results) > 0 {
		return results
	}

	// 没有匹配
	return nil
}

// 根据关键字查询
func SearchByRegexp(query string, limits int) ([]HospitalInfo, error) {
	if !utf8.ValidString(query) {
		return nil, fmt.Errorf("ptzz: bad query: %q", query)
	}

	// 规范化: 删除前后空白
	query = strings.TrimSpace(query)

	// 如果为空的话, 返回全部
	if query == "" || query == ".*" {
		if limits <= 0 || limits > len(All) {
			return All, nil
		}
		return All[:limits], nil
	}

	// 如果没有匹配的, 则尝试正则查询
	re, err := regexp.Compile(goodRegexpString(query))
	if err != nil {
		return nil, err
	}
	if results := searchByRegexp(re, limits); len(results) > 0 {
		return results, nil
	}

	// 没有匹配
	return nil, nil
}

// 根据关键字查询
// TODO: 以后可扩展为多个关键字, 采用非字符字符分隔
func searchByKeywords(key string, limits int) (results []HospitalInfo) {
	result0Map := make(map[string]HospitalInfo)
	result1Map := make(map[string]HospitalInfo)

	for _, v := range All {
		if limits > 0 && len(result0Map)+len(result1Map) >= limits {
			break
		}
		if strings.HasPrefix(v.Name, key) || strings.HasPrefix(v.City, key) {
			result0Map[v.Name] = v
		}
		if strings.HasPrefix(_NamePinyinLongMap[v.Name], key) || strings.HasPrefix(_NamePinyinShortMap[v.Name], key) {
			result0Map[v.Name] = v
		}
		if strings.HasPrefix(_NamePinyinLongMap[v.City], key) || strings.HasPrefix(_NamePinyinShortMap[v.City], key) {
			result0Map[v.Name] = v
		}
	}
	for _, v := range All {
		if limits > 0 && len(result0Map)+len(result1Map) >= limits {
			break
		}
		if strings.Contains(v.Name, key) || strings.Contains(v.City, key) {
			if _, ok := result0Map[v.Name]; !ok {
				result1Map[v.Name] = v
			}
		}
		if strings.Contains(_NamePinyinLongMap[v.Name], key) || strings.Contains(_NamePinyinShortMap[v.Name], key) {
			if _, ok := result0Map[v.Name]; !ok {
				result1Map[v.Name] = v
			}
		}
		if strings.Contains(_NamePinyinLongMap[v.City], key) || strings.Contains(_NamePinyinShortMap[v.City], key) {
			if _, ok := result0Map[v.Name]; !ok {
				result1Map[v.Name] = v
			}
		}
	}

	var result0List []HospitalInfo
	var result1List []HospitalInfo

	for _, v := range result0Map {
		result0List = append(result0List, v)
	}
	for _, v := range result1Map {
		result1List = append(result1List, v)
	}

	sort.Sort(byHospitalInfo(result0List))
	sort.Sort(byHospitalInfo(result1List))

	results = append(results, result0List...)
	results = append(results, result1List...)

	return
}

// 根据正则表达式查询
func searchByRegexp(re *regexp.Regexp, limits int) []HospitalInfo {
	resultMap := make(map[string]HospitalInfo)

	for _, v := range All {
		if limits > 0 && len(resultMap) >= limits {
			break
		}
		if re.MatchString(v.Name) || re.MatchString(v.City) {
			resultMap[v.Name] = v
		}
		if re.MatchString(_NamePinyinLongMap[v.Name]) || re.MatchString(_NamePinyinShortMap[v.Name]) {
			resultMap[v.Name] = v
		}
		if re.MatchString(_NamePinyinLongMap[v.City]) || re.MatchString(_NamePinyinShortMap[v.City]) {
			resultMap[v.Name] = v
		}
	}

	var resultList []HospitalInfo
	for _, v := range resultMap {
		resultList = append(resultList, v)
	}
	sort.Sort(byHospitalInfo(resultList))
	return resultList
}

// 规范化正则
func goodRegexpString(re string) string {
	mapRune := [][2]rune{
		{'，', ','},
		{'。', '.'},
		{'：', ':'},
		{'－', '-'},
		{'＋', '+'},
		{'＊', '*'},
		{'？', '?'},
		{'^', '^'},
		{'¥', '$'},
		{'、', '\\'},
		{'｜', '|'},
		{'［', '['},
		{'］', ']'},
		{'｛', '{'},
		{'｝', '}'},
		{'（', '('},
		{'）', ')'},
		{'《', '<'},
		{'》', '>'},
	}
	runes := []rune(re)
	for i := 0; i < len(runes); i++ {
		for j := 0; j < len(mapRune); j++ {
			if runes[i] == mapRune[j][0] {
				runes[i] = mapRune[j][1]
			}
		}
	}
	return string(runes)
}

// 按unicode排序
type byHospitalInfo []HospitalInfo

func (d byHospitalInfo) Len() int           { return len(d) }
func (d byHospitalInfo) Less(i, j int) bool { return d[i].Name < d[j].Name }
func (d byHospitalInfo) Swap(i, j int)      { d[i], d[j] = d[j], d[i] }
<|endoftext|>


<commit_before>package gopo

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strconv"
)

const defaultEndPoint = "https://api.pushover.net/1/messages.json"

type EndPoint struct {
	URL      string
	AppToken string
}

type Message struct {
	Message string
	ToKey   string
}

type Usage struct {
	Limit     int
	Remaining int
	NextReset string
}

type Response struct {
	Status  int      `json:"status"`
	Request string   `json:"request"`
	Errors  []string `json:"errors"`
	Usage   Usage
}

func headerValueAsInt(headers http.Header, headerKey string) int {
	v, err := strconv.Atoi(headers.Get(headerKey))
	if err != nil {
		v = -1
	}

	return v
}

func createResponse(resp *http.Response) *Response {
	gopoResponse := &Response{}
	json.NewDecoder(resp.Body).Decode(gopoResponse)

	gopoResponse.Usage.Limit = headerValueAsInt(resp.Header, "X-Limit-App-Limit")
	gopoResponse.Usage.Remaining = headerValueAsInt(resp.Header, "X-Limit-App-Remaining")
	gopoResponse.Usage.NextReset = resp.Header.Get("X-Limit-App-Reset")

	return gopoResponse
}

func NewGopo(apiToken string) *EndPoint {
	return &EndPoint{defaultEndPoint, apiToken}
}

func (e EndPoint) Push(message Message) (*Response, error) {
	vals := url.Values{
		"message": {message.Message},
		"user":    {message.ToKey},
		"token":   {e.AppToken}}

	resp, err := http.PostForm(e.URL, vals)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return createResponse(resp), nil
}
<commit_msg>Adding more params to the pushover message.<commit_after>package gopo

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strconv"
)

const defaultEndPoint = "https://api.pushover.net/1/messages.json"

type EndPoint struct {
	URL      string
	AppToken string
}

type Message struct {
	ToKey    string
	Message  string
	Title    string
	URL      string
	URLTitle string
}

type Usage struct {
	Limit     int
	Remaining int
	NextReset string
}

type Response struct {
	Status  int      `json:"status"`
	Request string   `json:"request"`
	Errors  []string `json:"errors"`
	Usage   Usage
}

func headerValueAsInt(headers http.Header, headerKey string) int {
	v, err := strconv.Atoi(headers.Get(headerKey))
	if err != nil {
		v = -1
	}

	return v
}

func createResponse(resp *http.Response) *Response {
	gopoResponse := &Response{}
	json.NewDecoder(resp.Body).Decode(gopoResponse)

	gopoResponse.Usage.Limit = headerValueAsInt(resp.Header, "X-Limit-App-Limit")
	gopoResponse.Usage.Remaining = headerValueAsInt(resp.Header, "X-Limit-App-Remaining")
	gopoResponse.Usage.NextReset = resp.Header.Get("X-Limit-App-Reset")

	return gopoResponse
}

func NewGopo(apiToken string) *EndPoint {
	return &EndPoint{defaultEndPoint, apiToken}
}

func (e EndPoint) Push(message Message) (*Response, error) {
	vals := url.Values{
		"message":   {message.Message},
		"user":      {message.ToKey},
		"token":     {e.AppToken},
		"title":     {message.Title},
		"url":       {message.URL},
		"url_title": {message.URLTitle},
	}

	resp, err := http.PostForm(e.URL, vals)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return createResponse(resp), nil
}
<|endoftext|>


<commit_before>package lib

import (
	"testing"
)

func TestGetTags(t *testing.T) {
	imageString := "https://registry.hub.docker.com/library/redis:*"
	image, err := ParseImage(imageString)
	if err != nil {
		t.Errorf("Error in parsing %s", imageString)
	}
	tagsExpanded, err := image.ExpandWildcard()
	if err != nil {
		t.Errorf("Error in expanding the tags %s", imageString)
	}
	if len(tagsExpanded) <= 1 {
		t.Errorf("Tag expansions didn't work, only %d tags", len(tagsExpanded))
	}
}

func TestGetTagsWithGlob(t *testing.T) {
	imageString := "https://registry.hub.docker.com/vernemq/vernemq:1.9.2*"
	// 1.9.2-1
	// 1.9.2-1-alpine
	// 1.9.2
	// 1.9.2-alpine

	image, err := ParseImage(imageString)
	if err != nil {
		t.Errorf("Error in parsing %s", imageString)
	}
	tagsExpanded, err := image.ExpandWildcard()
	if err != nil {
		t.Errorf("Error in expanding the tags %s", imageString)
	}
	// at the moment exists
	existingTags := []string{"1.9.2-1", "1.9.2-1-alpine", "1.9.2", "1.9.2-alpine"}
	tags := make(map[string]bool)
	for _, tag := range tagsExpanded {
		tags[tag.Tag] = true
	}

	for _, mustBeHereTag := range existingTags {
		if _, ok := tags[mustBeHereTag]; !ok {
			t.Errorf("Expected tag not found. Expected: %s", mustBeHereTag)
		}
	}
}

func TestFilterUsingGlobStarMatchEverything(t *testing.T) {
	garbage := []string{"vnje", "nc.cnrje", "5230.25.83"}
	result, err := filterUsingGlob("*", garbage)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	if len(result) != len(garbage) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(garbage))
	}
	for i, _ := range result {
		if result[i] != garbage[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], garbage[i])
		}
	}
}

func TestFilterUsingGlobStar(t *testing.T) {
	garbage := []string{"aaaa", "aab.12-8", "2.3"}
	result, err := filterUsingGlob("a*", garbage)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"aaaa", "aab.12-8"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(garbage))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], garbage[i])
		}
	}
}

func TestFilterUsingGlobAtBeginning(t *testing.T) {
	garbage := []string{"bar-foo", "ubuntu-foo", "foo-bar"}
	result, err := filterUsingGlob("*-foo", garbage)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"bar-foo", "ubuntu-foo"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(garbage))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], garbage[i])
		}
	}
}

func TestFilterUsingGlobTwice(t *testing.T) {
	garbage := []string{"foo", "ubuntu-foo", "foo-bar", "version-foo-2", "nope"}
	result, err := filterUsingGlob("*foo*", garbage)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"foo", "ubuntu-foo", "foo-bar", "version-foo-2"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(garbage))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], garbage[i])
		}
	}
}
<commit_msg>better naming fro test and added tests<commit_after>package lib

import (
	"testing"
)

func TestGetTags(t *testing.T) {
	imageString := "https://registry.hub.docker.com/library/redis:*"
	image, err := ParseImage(imageString)
	if err != nil {
		t.Errorf("Error in parsing %s", imageString)
	}
	tagsExpanded, err := image.ExpandWildcard()
	if err != nil {
		t.Errorf("Error in expanding the tags %s", imageString)
	}
	if len(tagsExpanded) <= 1 {
		t.Errorf("Tag expansions didn't work, only %d tags", len(tagsExpanded))
	}
}

func TestGetTagsWithGlob(t *testing.T) {
	imageString := "https://registry.hub.docker.com/vernemq/vernemq:1.9.2*"
	// 1.9.2-1
	// 1.9.2-1-alpine
	// 1.9.2
	// 1.9.2-alpine

	image, err := ParseImage(imageString)
	if err != nil {
		t.Errorf("Error in parsing %s", imageString)
	}
	tagsExpanded, err := image.ExpandWildcard()
	if err != nil {
		t.Errorf("Error in expanding the tags %s", imageString)
	}
	// at the moment exists
	existingTags := []string{"1.9.2-1", "1.9.2-1-alpine", "1.9.2", "1.9.2-alpine"}
	tags := make(map[string]bool)
	for _, tag := range tagsExpanded {
		tags[tag.Tag] = true
	}

	for _, mustBeHereTag := range existingTags {
		if _, ok := tags[mustBeHereTag]; !ok {
			t.Errorf("Expected tag not found. Expected: %s", mustBeHereTag)
		}
	}
}

func TestFilterUsingGlobStarMatchEverything(t *testing.T) {
	input := []string{"vnje", "nc.cnrje", "5230.25.83"}
	result, err := filterUsingGlob("*", input)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	if len(result) != len(input) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(input))
	}
	for i, _ := range result {
		if result[i] != input[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], input[i])
		}
	}
}

func TestFilterUsingGlobStar(t *testing.T) {
	input := []string{"aaaa", "aab.12-8", "2.3"}
	result, err := filterUsingGlob("a*", input)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"aaaa", "aab.12-8"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(input))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], input[i])
		}
	}
}

func TestFilterUsingGlobAtBeginning(t *testing.T) {
	input := []string{"bar-foo", "ubuntu-foo", "foo-bar"}
	result, err := filterUsingGlob("*-foo", input)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"bar-foo", "ubuntu-foo"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(input))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], input[i])
		}
	}
}

func TestFilterUsingGlobTwice(t *testing.T) {
	input := []string{"foo", "ubuntu-foo", "foo-bar", "version-foo-2", "nope"}
	result, err := filterUsingGlob("*foo*", input)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"foo", "ubuntu-foo", "foo-bar", "version-foo-2"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(input))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], input[i])
		}
	}
}

func TestFilterUsingGlobRealLifeImages01(t *testing.T) {
	input := []string{"rhel6-m201911", "rhel6-m202001", "rhel6-m202002", "rhel6", "rhel7-m201911", "rhel7-m202001", "rhel7-m202002", "rhel7", "tmp-rhel6-m202002-20200213", "tmp-rhel7-m202002-20200213"}
	result, err := filterUsingGlob("rhel7-m*", input)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"rhel7-m201911", "rhel7-m202001", "rhel7-m202002"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(input))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], input[i])
		}
	}
}

func TestFilterUsingGlobRealLifeImages02(t *testing.T) {
	input := []string{"rhel6-m201911", "rhel6-m202001", "rhel6-m202002", "rhel6", "rhel7-m201911", "rhel7-m202001", "rhel7-m202002", "rhel7", "tmp-rhel6-m202002-20200213", "tmp-rhel7-m202002-20200213"}
	result, err := filterUsingGlob("rhel7", input)
	if err != nil {
		t.Errorf("Error in filtering: %s", err)
	}
	expected := []string{"rhel7"}
	if len(result) != len(expected) {
		t.Errorf("The match is missing something, different lenghts %d != %d", len(result), len(input))
	}
	for i, _ := range result {
		if result[i] != expected[i] {
			t.Errorf("The match is missing something: %s != %s", result[i], input[i])
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/minio/minio-go"
	"github.com/minio/minio-go/pkg/policy"
	"github.com/pborman/uuid"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path"
	"syscall"
)

var (
	rootDir     string
	listenAddr  string
	s3Endpoint  string
	s3Region    string
	s3Bucket    string
	s3AccessKey string
	s3SecretKey string
)

func init() {
	flag.StringVar(&rootDir, "root", "", "http root directory (default: .)")
	flag.StringVar(&listenAddr, "listen", "", "address to listen to (default: :8080)")
	flag.StringVar(&s3Endpoint, "s3-endpoint", "", "s3 endpoint")
	flag.StringVar(&s3Region, "s3-region", "", "s3 region")
	flag.StringVar(&s3Bucket, "s3-bucket", "", "s3 bucket")
	flag.StringVar(&s3AccessKey, "s3-access-key", "", "s3 access key")
	flag.StringVar(&s3SecretKey, "s3-secret-key", "", "s3 secret key")
}

func main() {
	flag.Parse()

	if len(rootDir) == 0 {
		listenAddr = os.Getenv("PUT_ROOT")
	}

	if len(rootDir) == 0 {
		rootDir = "."
	}

	if len(listenAddr) == 0 {
		listenAddr = os.Getenv("PUT_LISTEN")
	}

	if len(listenAddr) == 0 {
		listenAddr = ":8080"
	}

	if len(s3Endpoint) == 0 {
		s3Endpoint = os.Getenv("PUT_S3_ENDPOINT")
	}

	if len(s3Region) == 0 {
		s3Region = os.Getenv("PUT_S3_REGION")
	}

	if len(s3Bucket) == 0 {
		s3Bucket = os.Getenv("PUT_S3_BUCKET")
	}

	if len(s3AccessKey) == 0 {
		s3AccessKey = os.Getenv("PUT_S3_ACCESS_KEY")
	}

	if len(s3SecretKey) == 0 {
		s3SecretKey = os.Getenv("PUT_S3_SECRET_KEY")
	}
	s3Url, err := url.Parse(s3Endpoint)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	s3Client, err := minio.New(s3Url.Host, s3AccessKey, s3SecretKey, s3Url.Scheme == "https")

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	ok, err := s3Client.BucketExists(s3Bucket)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if !ok {
		if err := s3Client.MakeBucket(s3Bucket, s3Region); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}

	if err := s3Client.SetBucketPolicy(s3Bucket, "", policy.BucketPolicyReadOnly); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	l, err := listen(listenAddr)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer l.Close()

	mux := http.NewServeMux()
	mux.Handle("/", NewRequestHandler(rootDir, s3Client, s3Endpoint, s3Bucket))
	mux.Handle("/health", NewHealthHandler(s3Client, s3Bucket))

	go http.Serve(l, mux)

	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	<-sig
}

func listen(addr string) (net.Listener, error) {
	if len(addr) > 0 && addr[0] != '/' {
		return net.Listen("tcp", addr)
	}
	return net.Listen("unix", addr)
}

type RequestHandler struct {
	s3Client   *minio.Client
	s3Endpoint string
	s3Bucket   string
	fileServer http.Handler
}

func NewRequestHandler(rootDir string, s3Client *minio.Client, s3Endpoint, s3Bucket string) *RequestHandler {
	return &RequestHandler{s3Client, s3Endpoint, s3Bucket, http.FileServer(http.Dir(rootDir))}
}

func (rh *RequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		rh.fileServer.ServeHTTP(w, r)
		return
	}

	if r.Method != "POST" && r.Method != "PUT" {
		http.Error(w, "page not found", 404)
		return
	}
	name := uuid.New()

	if _, err := rh.s3Client.PutObject(rh.s3Bucket, name, r.Body, r.Header.Get("Content-Type")); err != nil {
		fmt.Println("could not store object:", err)
		http.Error(w, "internal server error", 500)
		return
	}
	objectUrl, err := url.Parse(rh.s3Endpoint)

	if err != nil {
		fmt.Println("could not parse endpoint url:", err)
		http.Error(w, "internal server error", 500)
		return
	}
	objectUrl.Path = path.Join(rh.s3Bucket, name)

	if _, err := fmt.Fprintln(w, objectUrl.String()); err != nil {
		fmt.Println("could respond with object url:", err)
	}
}

type HealthHandler struct {
	s3Client *minio.Client
	s3Bucket string
}

type HealthPayload struct {
	Status string `json:"status"`
}

func NewHealthHandler(s3Client *minio.Client, s3Bucket string) *HealthHandler {
	return &HealthHandler{s3Client: s3Client, s3Bucket: s3Bucket}
}

func (hh *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	payload := &HealthPayload{Status: "ok"}
	ok, err := hh.s3Client.BucketExists(hh.s3Bucket)

	if err != nil {
		payload.Status = fmt.Sprintf("critical: %s", err)
	} else if !ok {
		payload.Status = fmt.Sprintf("critical: bucket \"%s\" unavailable", hh.s3Bucket)
	}
	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(payload); err != nil {
		fmt.Println("could not respond with health payload:", err)
	}
}
<commit_msg>add metrics endpoint<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/minio/minio-go"
	"github.com/minio/minio-go/pkg/policy"
	"github.com/pborman/uuid"
	"github.com/rcrowley/go-metrics"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path"
	"syscall"
)

var (
	rootDir    string
	listenAddr string
	s3         S3
)

func init() {
	flag.StringVar(&rootDir, "root", "", "http root directory (default: .)")
	flag.StringVar(&listenAddr, "listen", "", "address to listen to (default: :8080)")
	flag.StringVar(&s3.endpoint, "s3-endpoint", "", "s3 endpoint")
	flag.StringVar(&s3.region, "s3-region", "", "s3 region")
	flag.StringVar(&s3.bucket, "s3-bucket", "", "s3 bucket")
	flag.StringVar(&s3.accessKey, "s3-access-key", "", "s3 access key")
	flag.StringVar(&s3.secretKey, "s3-secret-key", "", "s3 secret key")
}

func main() {
	flag.Parse()

	if len(rootDir) == 0 {
		listenAddr = os.Getenv("PUT_ROOT")
	}

	if len(rootDir) == 0 {
		rootDir = "."
	}

	if len(listenAddr) == 0 {
		listenAddr = os.Getenv("PUT_LISTEN")
	}

	if len(listenAddr) == 0 {
		listenAddr = ":8080"
	}

	if len(s3.endpoint) == 0 {
		s3.endpoint = os.Getenv("PUT_S3_ENDPOINT")
	}

	if len(s3.region) == 0 {
		s3.region = os.Getenv("PUT_S3_REGION")
	}

	if len(s3.bucket) == 0 {
		s3.bucket = os.Getenv("PUT_S3_BUCKET")
	}

	if len(s3.accessKey) == 0 {
		s3.accessKey = os.Getenv("PUT_S3_ACCESS_KEY")
	}

	if len(s3.secretKey) == 0 {
		s3.secretKey = os.Getenv("PUT_S3_SECRET_KEY")
	}
	l, err := listen(listenAddr)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer l.Close()

	metrics := NewMetrics()

	mux := http.NewServeMux()
	mux.Handle("/", NewRequestHandler(rootDir, &s3, metrics))
	mux.Handle("/health", NewHealthHandler(&s3))
	mux.Handle("/metrics", NewMetricsHandler(metrics))

	go http.Serve(l, mux)

	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	<-sig
}

func listen(addr string) (net.Listener, error) {
	if len(addr) > 0 && addr[0] != '/' {
		return net.Listen("tcp", addr)
	}
	return net.Listen("unix", addr)
}

type S3 struct {
	minio     *minio.Client
	url       *url.URL
	endpoint  string
	bucket    string
	region    string
	secretKey string
	accessKey string
}

func (s3 *S3) Put(name, content_type string, body io.Reader) (string, error) {
	client, err := s3.client()

	if err != nil {
		return "", err
	}

	if _, err := client.PutObject(s3.bucket, name, body, content_type); err != nil {
		return "", err
	}
	s3.url.Path = path.Join(s3.bucket, name)
	return s3.url.String(), nil
}

func (s3 *S3) HealthCheck() error {
	client, err := s3.client()

	if err != nil {
		return err
	}
	ok, err := client.BucketExists(s3.bucket)

	if err != nil {
		return err
	}

	if !ok {
		fmt.Errorf("bucket unavailable: %s", s3.bucket)
	}
	return nil
}

func (s3 *S3) client() (client *minio.Client, err error) {
	if s3.minio != nil {
		return s3.minio, nil
	}
	s3.url, err = url.Parse(s3.endpoint)

	if err != nil {
		return nil, err
	}
	client, err = minio.New(s3.url.Host, s3.accessKey, s3.secretKey, s3.url.Scheme == "https")

	if err != nil {
		return nil, err
	}
	ok, err := client.BucketExists(s3.bucket)

	if err != nil {
		return nil, err
	}

	if !ok {
		if err := client.MakeBucket(s3.bucket, s3.region); err != nil {
			return nil, err
		}
	}

	if err := client.SetBucketPolicy(s3.bucket, "", policy.BucketPolicyReadOnly); err != nil {
		return nil, err
	}

	s3.minio = client
	return client, nil
}

type RequestHandler struct {
	s3         *S3
	metrics    Metrics
	fileServer http.Handler
}

func NewRequestHandler(rootDir string, s3 *S3, metrics Metrics) *RequestHandler {
	return &RequestHandler{
		s3:         s3,
		metrics:    metrics,
		fileServer: http.FileServer(http.Dir(rootDir)),
	}
}

func (rh *RequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		rh.fileServer.ServeHTTP(w, r)
		return
	}
	rh.metrics.Total.Inc(1)

	if r.Method != "POST" && r.Method != "PUT" {
		rh.metrics.Invalid.Inc(1)
		w.Header().Set("Allow", "GET, POST, PUT")
		http.Error(w, "method not allowed", 405)
		return
	}
	objectUrl, err := rh.s3.Put(uuid.New(), r.Header.Get("Content-Type"), r.Body)

	if err != nil {
		rh.metrics.Failed.Inc(1)
		fmt.Println("could not store object:", err)
		http.Error(w, "internal server error", 500)
		return
	}

	if _, err := fmt.Fprintln(w, objectUrl); err != nil {
		rh.metrics.Failed.Inc(1)
		fmt.Println("could respond with object url:", err)
		return
	}
	rh.metrics.Success.Inc(1)
}

type HealthHandler struct {
	s3 *S3
}

type HealthPayload struct {
	Status string `json:"status"`
}

func NewHealthHandler(s3 *S3) *HealthHandler {
	return &HealthHandler{s3: s3}
}

func (hh *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	payload := &HealthPayload{Status: "ok"}

	if err := hh.s3.HealthCheck(); err != nil {
		payload.Status = fmt.Sprintf("critical: %s", err)
	}
	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(payload); err != nil {
		fmt.Println("could not respond with health payload:", err)
	}
}

type Counter struct {
	metrics.Counter
}

func NewCounter() Counter {
	return Counter{metrics.NewCounter()}
}

func (c Counter) MarshalJSON() ([]byte, error) {
	return json.Marshal(c.Count())
}

type Metrics struct {
	Total   Counter `json:"total"`
	Invalid Counter `json:"invalid"`
	Failed  Counter `json:"failed"`
	Success Counter `json:"success"`
}

func NewMetrics() Metrics {
	return Metrics{
		Total:   NewCounter(),
		Invalid: NewCounter(),
		Failed:  NewCounter(),
		Success: NewCounter(),
	}
}

type MetricsHandler struct {
	metrics Metrics
}

func NewMetricsHandler(metrics Metrics) *MetricsHandler {
	return &MetricsHandler{metrics: metrics}
}

func (mh *MetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	if err := json.NewEncoder(w).Encode(mh.metrics); err != nil {
		fmt.Println("could not respond with metrics payload:", err)
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/getfider/fider/app/models/cmd"
	"github.com/getfider/fider/app/models/entity"
	"github.com/getfider/fider/app/models/enum"

	"github.com/getfider/fider/app/models/query"
	"github.com/getfider/fider/app/pkg/bus"

	"github.com/getfider/fider/app"
	"github.com/getfider/fider/app/pkg/errors"
	"github.com/getfider/fider/app/pkg/jwt"
	"github.com/getfider/fider/app/pkg/log"
	"github.com/getfider/fider/app/pkg/web"
	webutil "github.com/getfider/fider/app/pkg/web/util"
)

// OAuthEcho exchanges OAuth Code for a user profile and return directly to the UI, without storing it
func OAuthEcho() web.HandlerFunc {
	return func(c *web.Context) error {
		provider := c.Param("provider")

		code := c.QueryParam("code")
		if code == "" {
			return c.Redirect("/")
		}

		identifier := c.QueryParam("identifier")
		if identifier == "" || identifier != c.SessionID() {
			log.Warn(c, "OAuth identifier doesn't match with user session ID. Aborting sign in process.")
			return c.Redirect("/")
		}

		rawProfile := &query.GetOAuthRawProfile{Provider: provider, Code: code}
		err := bus.Dispatch(c, rawProfile)
		if err != nil {
			return c.Page(http.StatusOK, web.Props{
				Page:  "OAuthEcho/OAuthEcho.page",
				Title: "OAuth Test Page",
				Data: web.Map{
					"err": errors.Cause(err).Error(),
				},
			})
		}

		parseRawProfile := &cmd.ParseOAuthRawProfile{Provider: provider, Body: rawProfile.Result}
		_ = bus.Dispatch(c, parseRawProfile)

		return c.Page(http.StatusOK, web.Props{
			Page:  "OAuthEcho/OAuthEcho.page",
			Title: "OAuth Test Page",
			Data: web.Map{
				"body":    rawProfile.Result,
				"profile": parseRawProfile.Result,
			},
		})
	}
}

// OAuthToken exchanges OAuth Code for a user profile
// The user profile is then used to either get an existing user on Fider or creating a new one
// Once Fider user is retrieved/created, an authentication cookie is store in user's browser
func OAuthToken() web.HandlerFunc {
	return func(c *web.Context) error {
		provider := c.Param("provider")
		redirectURL, _ := url.ParseRequestURI(c.QueryParam("redirect"))
		redirectURL.ResolveReference(c.Request.URL)

		code := c.QueryParam("code")
		if code == "" {
			return c.Redirect(redirectURL.String())
		}

		identifier := c.QueryParam("identifier")
		if identifier == "" || identifier != c.SessionID() {
			log.Warn(c, "OAuth identifier doesn't match with user session ID. Aborting sign in process.")
			return c.Redirect(redirectURL.String())
		}

		oauthUser := &query.GetOAuthProfile{Provider: provider, Code: code}
		if err := bus.Dispatch(c, oauthUser); err != nil {
			return c.Failure(err)
		}

		var user *entity.User

		userByProvider := &query.GetUserByProvider{Provider: provider, UID: oauthUser.Result.ID}
		err := bus.Dispatch(c, userByProvider)
		user = userByProvider.Result

		if errors.Cause(err) == app.ErrNotFound && oauthUser.Result.Email != "" {
			userByEmail := &query.GetUserByEmail{Email: oauthUser.Result.Email}
			err = bus.Dispatch(c, userByEmail)
			user = userByEmail.Result
		}
		if err != nil {
			if errors.Cause(err) == app.ErrNotFound {
				isTrusted := isTrustedOAuthProvider(c, provider)
				if c.Tenant().IsPrivate && !isTrusted {
					return c.Redirect("/not-invited")
				}

				user = &entity.User{
					Name:   oauthUser.Result.Name,
					Tenant: c.Tenant(),
					Email:  oauthUser.Result.Email,
					Role:   enum.RoleVisitor,
					Providers: []*entity.UserProvider{
						{
							UID:  oauthUser.Result.ID,
							Name: provider,
						},
					},
				}

				if err = bus.Dispatch(c, &cmd.RegisterUser{User: user}); err != nil {
					return c.Failure(err)
				}
			} else {
				return c.Failure(err)
			}
		} else if !user.HasProvider(provider) {
			if err = bus.Dispatch(c, &cmd.RegisterUserProvider{
				UserID:       user.ID,
				ProviderName: provider,
				ProviderUID:  oauthUser.Result.ID,
			}); err != nil {
				return c.Failure(err)
			}
		}

		webutil.AddAuthUserCookie(c, user)

		return c.Redirect(redirectURL.String())
	}
}

func isTrustedOAuthProvider(ctx context.Context, provider string) bool {
	customOAuthConfigByProvider := &query.GetCustomOAuthConfigByProvider{Provider: provider}
	err := bus.Dispatch(ctx, customOAuthConfigByProvider)
	if err != nil {
		return false
	}
	return customOAuthConfigByProvider.Result.IsTrusted
}

// OAuthCallback handles the redirect back from the OAuth provider
// This callback can run on either Tenant or Login address
// If the request is for a sign in, we redirect the user to the tenant address
// If the request is for a sign up, we exchange the OAuth code and get the user profile
func OAuthCallback() web.HandlerFunc {
	return func(c *web.Context) error {
		c.Response.Header().Add("X-Robots-Tag", "noindex")

		provider := c.Param("provider")
		state := c.QueryParam("state")
		parts := strings.Split(state, "|")

		redirectURL, err := url.ParseRequestURI(parts[0])
		if err != nil {
			return c.Failure(err)
		}

		code := c.QueryParam("code")
		if code == "" {
			return c.Redirect(redirectURL.String())
		}

		//Test OAuth
		if redirectURL.Path == fmt.Sprintf("/oauth/%s/echo", provider) {
			var query = redirectURL.Query()
			query.Set("code", code)
			query.Set("identifier", parts[1])
			redirectURL.RawQuery = query.Encode()
			return c.Redirect(redirectURL.String())
		}

		//Sign up process
		if redirectURL.Path == "/signup" {
			oauthUser := &query.GetOAuthProfile{Provider: provider, Code: code}
			if err := bus.Dispatch(c, oauthUser); err != nil {
				return c.Failure(err)
			}

			claims := jwt.OAuthClaims{
				OAuthID:       oauthUser.Result.ID,
				OAuthProvider: provider,
				OAuthName:     oauthUser.Result.Name,
				OAuthEmail:    oauthUser.Result.Email,
				Metadata: jwt.Metadata{
					ExpiresAt: jwt.Time(time.Now().Add(10 * time.Minute)),
				},
			}

			token, err := jwt.Encode(claims)
			if err != nil {
				return c.Failure(err)
			}

			var query = redirectURL.Query()
			query.Set("token", token)
			redirectURL.RawQuery = query.Encode()
			return c.Redirect(redirectURL.String())
		}

		//Sign in process
		var query = redirectURL.Query()
		query.Set("code", code)
		query.Set("redirect", redirectURL.RequestURI())
		query.Set("identifier", parts[1])
		redirectURL.RawQuery = query.Encode()
		redirectURL.Path = fmt.Sprintf("/oauth/%s/token", provider)
		return c.Redirect(redirectURL.String())
	}
}

// SignInByOAuth is responsible for redirecting the user to the OAuth authorization URL for given provider
// A cookie is stored in user's browser with a random identifier that is later used to verify the authenticity of the request
func SignInByOAuth() web.HandlerFunc {
	return func(c *web.Context) error {
		c.Response.Header().Add("X-Robots-Tag", "noindex")

		provider := c.Param("provider")
		redirect := c.QueryParam("redirect")

		if redirect == "" {
			redirect = c.BaseURL()
		}

		redirectURL, _ := url.ParseRequestURI(redirect)
		redirectURL.ResolveReference(c.Request.URL)

		if c.IsAuthenticated() && redirectURL.Path != fmt.Sprintf("/oauth/%s/echo", provider) {
			return c.Redirect(redirect)
		}

		authURL := &query.GetOAuthAuthorizationURL{
			Provider:   provider,
			Redirect:   redirect,
			Identifier: c.SessionID(),
		}
		if err := bus.Dispatch(c, authURL); err != nil {
			return c.Failure(err)
		}
		return c.Redirect(authURL.Result)
	}
}
<commit_msg>fix: add log for missing oauth redirect url<commit_after>package handlers

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/getfider/fider/app/models/cmd"
	"github.com/getfider/fider/app/models/dto"
	"github.com/getfider/fider/app/models/entity"
	"github.com/getfider/fider/app/models/enum"

	"github.com/getfider/fider/app/models/query"
	"github.com/getfider/fider/app/pkg/bus"

	"github.com/getfider/fider/app"
	"github.com/getfider/fider/app/pkg/errors"
	"github.com/getfider/fider/app/pkg/jwt"
	"github.com/getfider/fider/app/pkg/log"
	"github.com/getfider/fider/app/pkg/web"
	webutil "github.com/getfider/fider/app/pkg/web/util"
)

// OAuthEcho exchanges OAuth Code for a user profile and return directly to the UI, without storing it
func OAuthEcho() web.HandlerFunc {
	return func(c *web.Context) error {
		provider := c.Param("provider")

		code := c.QueryParam("code")
		if code == "" {
			return c.Redirect("/")
		}

		identifier := c.QueryParam("identifier")
		if identifier == "" || identifier != c.SessionID() {
			log.Warn(c, "OAuth identifier doesn't match with user session ID. Aborting sign in process.")
			return c.Redirect("/")
		}

		rawProfile := &query.GetOAuthRawProfile{Provider: provider, Code: code}
		err := bus.Dispatch(c, rawProfile)
		if err != nil {
			return c.Page(http.StatusOK, web.Props{
				Page:  "OAuthEcho/OAuthEcho.page",
				Title: "OAuth Test Page",
				Data: web.Map{
					"err": errors.Cause(err).Error(),
				},
			})
		}

		parseRawProfile := &cmd.ParseOAuthRawProfile{Provider: provider, Body: rawProfile.Result}
		_ = bus.Dispatch(c, parseRawProfile)

		return c.Page(http.StatusOK, web.Props{
			Page:  "OAuthEcho/OAuthEcho.page",
			Title: "OAuth Test Page",
			Data: web.Map{
				"body":    rawProfile.Result,
				"profile": parseRawProfile.Result,
			},
		})
	}
}

// OAuthToken exchanges OAuth Code for a user profile
// The user profile is then used to either get an existing user on Fider or creating a new one
// Once Fider user is retrieved/created, an authentication cookie is store in user's browser
func OAuthToken() web.HandlerFunc {
	return func(c *web.Context) error {
		provider := c.Param("provider")
		redirectURL, _ := url.ParseRequestURI(c.QueryParam("redirect"))
		redirectURL.ResolveReference(c.Request.URL)

		code := c.QueryParam("code")
		if code == "" {
			return c.Redirect(redirectURL.String())
		}

		identifier := c.QueryParam("identifier")
		if identifier == "" || identifier != c.SessionID() {
			log.Warn(c, "OAuth identifier doesn't match with user session ID. Aborting sign in process.")
			return c.Redirect(redirectURL.String())
		}

		oauthUser := &query.GetOAuthProfile{Provider: provider, Code: code}
		if err := bus.Dispatch(c, oauthUser); err != nil {
			return c.Failure(err)
		}

		var user *entity.User

		userByProvider := &query.GetUserByProvider{Provider: provider, UID: oauthUser.Result.ID}
		err := bus.Dispatch(c, userByProvider)
		user = userByProvider.Result

		if errors.Cause(err) == app.ErrNotFound && oauthUser.Result.Email != "" {
			userByEmail := &query.GetUserByEmail{Email: oauthUser.Result.Email}
			err = bus.Dispatch(c, userByEmail)
			user = userByEmail.Result
		}
		if err != nil {
			if errors.Cause(err) == app.ErrNotFound {
				isTrusted := isTrustedOAuthProvider(c, provider)
				if c.Tenant().IsPrivate && !isTrusted {
					return c.Redirect("/not-invited")
				}

				user = &entity.User{
					Name:   oauthUser.Result.Name,
					Tenant: c.Tenant(),
					Email:  oauthUser.Result.Email,
					Role:   enum.RoleVisitor,
					Providers: []*entity.UserProvider{
						{
							UID:  oauthUser.Result.ID,
							Name: provider,
						},
					},
				}

				if err = bus.Dispatch(c, &cmd.RegisterUser{User: user}); err != nil {
					return c.Failure(err)
				}
			} else {
				return c.Failure(err)
			}
		} else if !user.HasProvider(provider) {
			if err = bus.Dispatch(c, &cmd.RegisterUserProvider{
				UserID:       user.ID,
				ProviderName: provider,
				ProviderUID:  oauthUser.Result.ID,
			}); err != nil {
				return c.Failure(err)
			}
		}

		webutil.AddAuthUserCookie(c, user)

		return c.Redirect(redirectURL.String())
	}
}

func isTrustedOAuthProvider(ctx context.Context, provider string) bool {
	customOAuthConfigByProvider := &query.GetCustomOAuthConfigByProvider{Provider: provider}
	err := bus.Dispatch(ctx, customOAuthConfigByProvider)
	if err != nil {
		return false
	}
	return customOAuthConfigByProvider.Result.IsTrusted
}

// OAuthCallback handles the redirect back from the OAuth provider
// This callback can run on either Tenant or Login address
// If the request is for a sign in, we redirect the user to the tenant address
// If the request is for a sign up, we exchange the OAuth code and get the user profile
func OAuthCallback() web.HandlerFunc {
	return func(c *web.Context) error {
		c.Response.Header().Add("X-Robots-Tag", "noindex")

		provider := c.Param("provider")
		state := c.QueryParam("state")
		parts := strings.Split(state, "|")

		if parts[0] == "" {
			log.Warnf(c, "Missing redirect URL in OAuth callback state for provider @{Provider}.", dto.Props{"Provider": provider})
			return c.NotFound()
		}

		redirectURL, err := url.ParseRequestURI(parts[0])
		if err != nil {
			return c.Failure(err)
		}

		code := c.QueryParam("code")
		if code == "" {
			return c.Redirect(redirectURL.String())
		}

		//Test OAuth
		if redirectURL.Path == fmt.Sprintf("/oauth/%s/echo", provider) {
			var query = redirectURL.Query()
			query.Set("code", code)
			query.Set("identifier", parts[1])
			redirectURL.RawQuery = query.Encode()
			return c.Redirect(redirectURL.String())
		}

		//Sign up process
		if redirectURL.Path == "/signup" {
			oauthUser := &query.GetOAuthProfile{Provider: provider, Code: code}
			if err := bus.Dispatch(c, oauthUser); err != nil {
				return c.Failure(err)
			}

			claims := jwt.OAuthClaims{
				OAuthID:       oauthUser.Result.ID,
				OAuthProvider: provider,
				OAuthName:     oauthUser.Result.Name,
				OAuthEmail:    oauthUser.Result.Email,
				Metadata: jwt.Metadata{
					ExpiresAt: jwt.Time(time.Now().Add(10 * time.Minute)),
				},
			}

			token, err := jwt.Encode(claims)
			if err != nil {
				return c.Failure(err)
			}

			var query = redirectURL.Query()
			query.Set("token", token)
			redirectURL.RawQuery = query.Encode()
			return c.Redirect(redirectURL.String())
		}

		//Sign in process
		var query = redirectURL.Query()
		query.Set("code", code)
		query.Set("redirect", redirectURL.RequestURI())
		query.Set("identifier", parts[1])
		redirectURL.RawQuery = query.Encode()
		redirectURL.Path = fmt.Sprintf("/oauth/%s/token", provider)
		return c.Redirect(redirectURL.String())
	}
}

// SignInByOAuth is responsible for redirecting the user to the OAuth authorization URL for given provider
// A cookie is stored in user's browser with a random identifier that is later used to verify the authenticity of the request
func SignInByOAuth() web.HandlerFunc {
	return func(c *web.Context) error {
		c.Response.Header().Add("X-Robots-Tag", "noindex")

		provider := c.Param("provider")
		redirect := c.QueryParam("redirect")

		if redirect == "" {
			redirect = c.BaseURL()
		}

		redirectURL, _ := url.ParseRequestURI(redirect)
		redirectURL.ResolveReference(c.Request.URL)

		if c.IsAuthenticated() && redirectURL.Path != fmt.Sprintf("/oauth/%s/echo", provider) {
			return c.Redirect(redirect)
		}

		authURL := &query.GetOAuthAuthorizationURL{
			Provider:   provider,
			Redirect:   redirect,
			Identifier: c.SessionID(),
		}
		if err := bus.Dispatch(c, authURL); err != nil {
			return c.Failure(err)
		}
		return c.Redirect(authURL.Result)
	}
}
<|endoftext|>


<commit_before>package proxy

import (
	"bytes"
	"net"
	"strconv"
	"time"

	"github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/protocol"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type packetData []byte

var _ = Describe("UDP Proxy", func() {
	var serverPort int

	makePacket := func(p protocol.PacketNumber, payload []byte) []byte {
		b := &bytes.Buffer{}
		hdr := quic.PublicHeader{
			PacketNumber:         p,
			PacketNumberLen:      protocol.PacketNumberLen6,
			ConnectionID:         1337,
			TruncateConnectionID: false,
		}
		hdr.WritePublicHeader(b, protocol.Version34)
		raw := b.Bytes()
		raw = append(raw, payload...)
		return raw
	}

	BeforeEach(func() {
		serverPort = 7331
	})

	It("sets up the UDPProxy", func() {
		proxy, err := NewUDPProxy(13370, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		Expect(proxy.clientDict).To(HaveLen(0))

		// check that port 13370 is in use
		addr, err := net.ResolveUDPAddr("udp", ":13370")
		Expect(err).ToNot(HaveOccurred())
		_, err = net.ListenUDP("udp", addr)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("listen udp :13370: bind: address already in use"))

		proxy.Stop() // stopping is tested in the next test
	})

	It("stops the UDPProxy", func() {
		proxy, err := NewUDPProxy(13371, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		proxy.Stop()

		// check that port 13370 is not in use anymore
		addr, err := net.ResolveUDPAddr("udp", ":13371")
		Expect(err).ToNot(HaveOccurred())
		ln, err := net.ListenUDP("udp", addr)
		Expect(err).ToNot(HaveOccurred())
		ln.Close()
	})

	Context("Proxy tests", func() {
		var serverConn *net.UDPConn
		var serverReceivedPackets []packetData
		var serverNumPacketsSent int
		var clientConn *net.UDPConn
		var proxy *UDPProxy

		BeforeEach(func() {
			var err error
			serverReceivedPackets = serverReceivedPackets[:0]
			serverNumPacketsSent = 0

			// setup a UDP server on port 7331
			serverAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(serverPort))
			Expect(err).ToNot(HaveOccurred())
			serverConn, err = net.ListenUDP("udp", serverAddr)
			Expect(err).ToNot(HaveOccurred())

			proxyAddr, err := net.ResolveUDPAddr("udp", ":10001")
			Expect(err).ToNot(HaveOccurred())

			go func() {
				defer GinkgoRecover()

				for {
					buf := make([]byte, 1500)
					n, addr, err2 := serverConn.ReadFromUDP(buf)
					if err2 != nil {
						return
					}
					data := buf[0:n]
					serverReceivedPackets = append(serverReceivedPackets, packetData(data))

					// echo each packet received back to the client
					serverConn.WriteToUDP(data, addr)
					serverNumPacketsSent++
				}
			}()

			// setup the client
			localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
			Expect(err).ToNot(HaveOccurred())
			clientConn, err = net.DialUDP("udp", localAddr, proxyAddr)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			proxy.Stop()
			serverConn.Close()
			time.Sleep(time.Millisecond)
		})

		Context("no packet drop", func() {
			BeforeEach(func() {
				// setup the proxy
				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())
			})

			It("relays packets from the client to the server", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var conn *connection
				for _, conn = range proxy.clientDict {
					Expect(conn.incomingPacketCounter).To(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())
				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(string(serverReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(serverReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})

			It("relays packets from the server to the client", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())
				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Eventually(conn.outgoingPacketCounter).Should(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())

				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() uint64 { return proxy.clientDict[key].outgoingPacketCounter }).Should(Equal(uint64(2)))

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(serverReceivedPackets).To(HaveLen(2))
				Expect(serverNumPacketsSent).To(Equal(2))
				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(2))
				Expect(string(clientReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(clientReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})
		})

		Context("Drop Callbacks", func() {
			It("drops incoming packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, dropper, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(3))
				Expect(string(serverReceivedPackets[0])).To(ContainSubstring("foobar1"))
				Expect(string(serverReceivedPackets[1])).To(ContainSubstring("foobar3"))
				Expect(string(serverReceivedPackets[2])).To(ContainSubstring("foobar5"))
			})

			It("drops outgoing packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, dropper, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(3))
				Expect(string(clientReceivedPackets[0])).To(ContainSubstring("foobar1"))
				Expect(string(clientReceivedPackets[1])).To(ContainSubstring("foobar3"))
				Expect(string(clientReceivedPackets[2])).To(ContainSubstring("foobar5"))
			})
		})
	})
})
<commit_msg>fix failing proxy test<commit_after>package proxy

import (
	"bytes"
	"net"
	"strconv"
	"time"

	"github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/protocol"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type packetData []byte

var _ = Describe("UDP Proxy", func() {
	var serverPort int

	makePacket := func(p protocol.PacketNumber, payload []byte) []byte {
		b := &bytes.Buffer{}
		hdr := quic.PublicHeader{
			PacketNumber:         p,
			PacketNumberLen:      protocol.PacketNumberLen6,
			ConnectionID:         1337,
			TruncateConnectionID: false,
		}
		hdr.WritePublicHeader(b, protocol.Version34)
		raw := b.Bytes()
		raw = append(raw, payload...)
		return raw
	}

	BeforeEach(func() {
		serverPort = 7331
	})

	It("sets up the UDPProxy", func() {
		proxy, err := NewUDPProxy(13370, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		Expect(proxy.clientDict).To(HaveLen(0))

		// check that port 13370 is in use
		addr, err := net.ResolveUDPAddr("udp", ":13370")
		Expect(err).ToNot(HaveOccurred())
		_, err = net.ListenUDP("udp", addr)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("listen udp :13370: bind: address already in use"))

		proxy.Stop() // stopping is tested in the next test
	})

	It("stops the UDPProxy", func() {
		proxy, err := NewUDPProxy(13371, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		proxy.Stop()

		// check that port 13370 is not in use anymore
		addr, err := net.ResolveUDPAddr("udp", ":13371")
		Expect(err).ToNot(HaveOccurred())
		ln, err := net.ListenUDP("udp", addr)
		Expect(err).ToNot(HaveOccurred())
		ln.Close()
	})

	Context("Proxy tests", func() {
		var serverConn *net.UDPConn
		var serverReceivedPackets []packetData
		var serverNumPacketsSent int
		var clientConn *net.UDPConn
		var proxy *UDPProxy

		BeforeEach(func() {
			var err error
			serverReceivedPackets = serverReceivedPackets[:0]
			serverNumPacketsSent = 0

			// setup a UDP server on port 7331
			serverAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(serverPort))
			Expect(err).ToNot(HaveOccurred())
			serverConn, err = net.ListenUDP("udp", serverAddr)
			Expect(err).ToNot(HaveOccurred())

			proxyAddr, err := net.ResolveUDPAddr("udp", ":10001")
			Expect(err).ToNot(HaveOccurred())

			go func() {
				defer GinkgoRecover()

				for {
					buf := make([]byte, 1500)
					n, addr, err2 := serverConn.ReadFromUDP(buf)
					if err2 != nil {
						return
					}
					data := buf[0:n]
					serverReceivedPackets = append(serverReceivedPackets, packetData(data))

					// echo each packet received back to the client
					serverConn.WriteToUDP(data, addr)
					serverNumPacketsSent++
				}
			}()

			// setup the client
			localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
			Expect(err).ToNot(HaveOccurred())
			clientConn, err = net.DialUDP("udp", localAddr, proxyAddr)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			proxy.Stop()
			serverConn.Close()
			time.Sleep(time.Millisecond)
		})

		Context("no packet drop", func() {
			BeforeEach(func() {
				// setup the proxy
				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())
			})

			It("relays packets from the client to the server", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var conn *connection
				for _, conn = range proxy.clientDict {
					Expect(conn.incomingPacketCounter).To(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())
				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(string(serverReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(serverReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})

			It("relays packets from the server to the client", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())
				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Eventually(conn.outgoingPacketCounter).Should(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())

				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() uint64 { return proxy.clientDict[key].outgoingPacketCounter }).Should(Equal(uint64(2)))

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(serverReceivedPackets).To(HaveLen(2))
				Expect(serverNumPacketsSent).To(Equal(2))
				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(2))
				Expect(string(clientReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(clientReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})
		})

		Context("Drop Callbacks", func() {
			It("drops incoming packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, dropper, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(3))
				Expect(string(serverReceivedPackets[0])).To(ContainSubstring("foobar1"))
				Expect(string(serverReceivedPackets[1])).To(ContainSubstring("foobar3"))
				Expect(string(serverReceivedPackets[2])).To(ContainSubstring("foobar5"))
			})

			It("drops outgoing packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, dropper, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(3))
			})
		})
	})
})
<|endoftext|>


<commit_before>package proxy

import (
	"bytes"
	"net"
	"strconv"
	"time"

	"github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/protocol"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type packetData []byte

var _ = Describe("UDP Proxy", func() {
	var serverPort int

	makePacket := func(p protocol.PacketNumber, payload []byte) []byte {
		b := &bytes.Buffer{}
		hdr := quic.PublicHeader{
			PacketNumber:         p,
			PacketNumberLen:      protocol.PacketNumberLen6,
			ConnectionID:         1337,
			TruncateConnectionID: false,
		}
		hdr.WritePublicHeader(b, protocol.Version34)
		raw := b.Bytes()
		raw = append(raw, payload...)
		return raw
	}

	BeforeEach(func() {
		serverPort = 7331
	})

	It("sets up the UDPProxy", func() {
		proxy, err := NewUDPProxy(13370, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		Expect(proxy.clientDict).To(HaveLen(0))

		// check that port 13370 is in use
		addr, err := net.ResolveUDPAddr("udp", ":13370")
		Expect(err).ToNot(HaveOccurred())
		_, err = net.ListenUDP("udp", addr)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("listen udp :13370: bind: address already in use"))

		proxy.Stop() // stopping is tested in the next test
	})

	It("stops the UDPProxy", func() {
		proxy, err := NewUDPProxy(13371, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		proxy.Stop()

		// check that port 13370 is not in use anymore
		addr, err := net.ResolveUDPAddr("udp", ":13371")
		Expect(err).ToNot(HaveOccurred())
		ln, err := net.ListenUDP("udp", addr)
		Expect(err).ToNot(HaveOccurred())
		ln.Close()
	})

	Context("Proxy tests", func() {
		var serverConn *net.UDPConn
		var serverReceivedPackets []packetData
		var serverNumPacketsSent int
		var clientConn *net.UDPConn
		var proxy *UDPProxy

		BeforeEach(func() {
			var err error
			serverReceivedPackets = serverReceivedPackets[:0]
			serverNumPacketsSent = 0

			// setup a UDP server on port 7331
			serverAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(serverPort))
			Expect(err).ToNot(HaveOccurred())
			serverConn, err = net.ListenUDP("udp", serverAddr)
			Expect(err).ToNot(HaveOccurred())

			proxyAddr, err := net.ResolveUDPAddr("udp", ":10001")
			Expect(err).ToNot(HaveOccurred())

			go func() {
				defer GinkgoRecover()

				for {
					buf := make([]byte, 1500)
					n, addr, err2 := serverConn.ReadFromUDP(buf)
					if err2 != nil {
						return
					}
					data := buf[0:n]
					serverReceivedPackets = append(serverReceivedPackets, packetData(data))

					// echo each packet received back to the client
					serverConn.WriteToUDP(data, addr)
					serverNumPacketsSent++
				}
			}()

			// setup the client
			localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
			Expect(err).ToNot(HaveOccurred())
			clientConn, err = net.DialUDP("udp", localAddr, proxyAddr)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			proxy.Stop()
			serverConn.Close()
			time.Sleep(time.Millisecond)
		})

		Context("no packet drop", func() {
			BeforeEach(func() {
				// setup the proxy
				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())
			})

			It("relays packets from the client to the server", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var conn *connection
				for _, conn = range proxy.clientDict {
					Expect(conn.incomingPacketCounter).To(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())
				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(string(serverReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(serverReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})

			It("relays packets from the server to the client", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())
				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Eventually(conn.outgoingPacketCounter).Should(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())

				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() uint64 { return proxy.clientDict[key].outgoingPacketCounter }).Should(Equal(uint64(2)))

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(serverReceivedPackets).To(HaveLen(2))
				Expect(serverNumPacketsSent).To(Equal(2))
				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(2))
				Expect(string(clientReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(clientReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})
		})

		Context("Drop Callbacks", func() {
			It("drops incoming packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, dropper, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(3))
				Expect(string(serverReceivedPackets[0])).To(ContainSubstring("foobar1"))
				Expect(string(serverReceivedPackets[1])).To(ContainSubstring("foobar3"))
				Expect(string(serverReceivedPackets[2])).To(ContainSubstring("foobar5"))
			})

			It("drops outgoing packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, dropper, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
					time.Sleep(time.Millisecond)
				}

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(3))
			})
		})
	})
})
<commit_msg>fix flaky udp proxy tests<commit_after>package proxy

import (
	"bytes"
	"net"
	"strconv"
	"time"

	"github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/protocol"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type packetData []byte

var _ = Describe("UDP Proxy", func() {
	var serverPort int

	makePacket := func(p protocol.PacketNumber, payload []byte) []byte {
		b := &bytes.Buffer{}
		hdr := quic.PublicHeader{
			PacketNumber:         p,
			PacketNumberLen:      protocol.PacketNumberLen6,
			ConnectionID:         1337,
			TruncateConnectionID: false,
		}
		hdr.WritePublicHeader(b, protocol.Version34)
		raw := b.Bytes()
		raw = append(raw, payload...)
		return raw
	}

	BeforeEach(func() {
		serverPort = 7331
	})

	It("sets up the UDPProxy", func() {
		proxy, err := NewUDPProxy(13370, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		Expect(proxy.clientDict).To(HaveLen(0))

		// check that port 13370 is in use
		addr, err := net.ResolveUDPAddr("udp", ":13370")
		Expect(err).ToNot(HaveOccurred())
		_, err = net.ListenUDP("udp", addr)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError("listen udp :13370: bind: address already in use"))

		proxy.Stop() // stopping is tested in the next test
	})

	It("stops the UDPProxy", func() {
		proxy, err := NewUDPProxy(13371, "localhost", serverPort, nil, nil, 0, 0)
		Expect(err).ToNot(HaveOccurred())
		proxy.Stop()

		// check that port 13370 is not in use anymore
		addr, err := net.ResolveUDPAddr("udp", ":13371")
		Expect(err).ToNot(HaveOccurred())
		ln, err := net.ListenUDP("udp", addr)
		Expect(err).ToNot(HaveOccurred())
		ln.Close()
	})

	Context("Proxy tests", func() {
		var serverConn *net.UDPConn
		var serverReceivedPackets []packetData
		var serverNumPacketsSent int
		var clientConn *net.UDPConn
		var proxy *UDPProxy

		BeforeEach(func() {
			var err error
			serverReceivedPackets = serverReceivedPackets[:0]
			serverNumPacketsSent = 0

			// setup a UDP server on port 7331
			serverAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(serverPort))
			Expect(err).ToNot(HaveOccurred())
			serverConn, err = net.ListenUDP("udp", serverAddr)
			Expect(err).ToNot(HaveOccurred())

			proxyAddr, err := net.ResolveUDPAddr("udp", ":10001")
			Expect(err).ToNot(HaveOccurred())

			go func() {
				defer GinkgoRecover()

				for {
					buf := make([]byte, 1500)
					n, addr, err2 := serverConn.ReadFromUDP(buf)
					if err2 != nil {
						return
					}
					data := buf[0:n]
					serverReceivedPackets = append(serverReceivedPackets, packetData(data))

					// echo each packet received back to the client
					serverConn.WriteToUDP(data, addr)
					serverNumPacketsSent++
				}
			}()

			// setup the client
			localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
			Expect(err).ToNot(HaveOccurred())
			clientConn, err = net.DialUDP("udp", localAddr, proxyAddr)
			Expect(err).ToNot(HaveOccurred())
		})

		AfterEach(func() {
			proxy.Stop()
			serverConn.Close()
			time.Sleep(time.Millisecond)
		})

		Context("no packet drop", func() {
			BeforeEach(func() {
				// setup the proxy
				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())
			})

			It("relays packets from the client to the server", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var conn *connection
				for _, conn = range proxy.clientDict {
					Expect(conn.incomingPacketCounter).To(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())
				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(string(serverReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(serverReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})

			It("relays packets from the server to the client", func() {
				_, err := clientConn.Write(makePacket(1, []byte("foobar")))
				Expect(err).ToNot(HaveOccurred())
				Eventually(func() map[string]*connection { return proxy.clientDict }).Should(HaveLen(1))
				var key string
				var conn *connection
				for key, conn = range proxy.clientDict {
					Eventually(conn.outgoingPacketCounter).Should(Equal(uint64(1)))
				}
				_, err = clientConn.Write(makePacket(2, []byte("decafbad")))
				Expect(err).ToNot(HaveOccurred())

				Expect(proxy.clientDict).To(HaveLen(1))
				Eventually(func() uint64 { return proxy.clientDict[key].outgoingPacketCounter }).Should(Equal(uint64(2)))

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(2))
				Expect(serverReceivedPackets).To(HaveLen(2))
				Expect(serverNumPacketsSent).To(Equal(2))
				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(2))
				Expect(string(clientReceivedPackets[0])).To(ContainSubstring("foobar"))
				Expect(string(clientReceivedPackets[1])).To(ContainSubstring("decafbad"))
			})
		})

		Context("Drop Callbacks", func() {
			It("drops incoming packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, dropper, nil, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
				}
				Eventually(func() []packetData { return serverReceivedPackets }).Should(HaveLen(3))
				Consistently(func() []packetData { return serverReceivedPackets }).Should(HaveLen(3))
			})

			It("drops outgoing packets", func() {
				dropper := func(p protocol.PacketNumber) bool {
					return p%2 == 0
				}

				var err error
				proxy, err = NewUDPProxy(10001, "localhost", serverPort, nil, dropper, 0, 0)
				Expect(err).ToNot(HaveOccurred())

				for i := 1; i <= 6; i++ {
					_, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
					Expect(err).ToNot(HaveOccurred())
				}

				var clientReceivedPackets []packetData

				// receive the packets echoed by the server on client side
				go func() {
					defer GinkgoRecover()

					for {
						buf := make([]byte, 1500)
						n, _, err2 := clientConn.ReadFromUDP(buf)
						if err2 != nil {
							return
						}
						data := buf[0:n]
						clientReceivedPackets = append(clientReceivedPackets, packetData(data))
					}
				}()

				Eventually(func() []packetData { return clientReceivedPackets }).Should(HaveLen(3))
				Consistently(func() []packetData { return clientReceivedPackets }).Should(HaveLen(3))
			})
		})
	})
})
<|endoftext|>


<commit_before>package action

import (
	"appengine"
	"appengine/datastore"
	"time"
)

const (
	UpdateNotification ActionType = iota //Client was notified it should update.
	DownloadStart      ActionType = iota //Client startde downloading.
	DownloadFinish     ActionType = iota //Client finished download.
)

type ActionType int

func (at ActionType) String() string {
	switch at {
	case UpdateNotification:
		return "Update notification"
	case DownloadStart:
		return "Download started"
	case DownloadFinish:
		return "Download is finished"
	}
	return "Unknown action"
}

//Action (better name would be Action, but it would be hard to change it now)
//is a type used for recording whe time when clients
//did an action specified by the ActionType.
type Action struct {
	Type         ActionType     //Type of action
	Client       string         //Name of the client
	Time         time.Time      //Time of the action
	Presentation *datastore.Key //What object is the action related to.
	Key          string         `datastore:"-"`
}

//Model is an nterface specifying models - structs which Datastore keys can be obtained.
type Model interface {
	GetKey(appengine.Context) (*datastore.Key, error)
}

func (a Action) GetKey() (k *datastore.Key, err error) {
	k, err = datastore.DecodeKey(a.Key)
	return
}

//New returns a pointer to an action with its fields set according
//to arguments.
func New(k *datastore.Key, at ActionType, client string) (a *Action) {
	a = new(Action)
	a.Presentation = k
	a.Type = at
	a.Client = client
	a.Time = time.Now()
	return a
}

//Make creates a new Action using New and then saves
//it to Datastore.
func Make(m Model, at ActionType, client string, c appengine.Context) (a *Action, err error) {
	k, err := m.GetKey(c)
	if err != nil {
		return
	}
	a = New(k, at, client)
	err = a.Save(c)
	return
}

//Save saves an Action to Datastore.
//If its Key field is set, it will use it, replacing
//existing records. If not, it will use datastore.NewIncompleteKey()
//to create a new key and set the field.
func (a *Action) Save(c appengine.Context) (err error) {
	if a.Key == "" {
		var key *datastore.Key
		key, err = datastore.Put(c, datastore.NewIncompleteKey(c, "Action", a.Presentation), a)
		a.Key = key.Encode()
		return
	} else {
		var key *datastore.Key
		key, err = datastore.DecodeKey(a.Key)
		if err != nil {
			return
		}
		_, err = datastore.Put(c, key, a)
	}
	return
}

//Log works like Make but logs errors instead of returning them.
func Log(m Model, client string, at ActionType, c appengine.Context) {
	if client == "" {
		c.Infof("%v called without client name.", at)
		return
	}

	_, err := Make(m, at, client, c)
	if err != nil {
		c.Infof("Can't log Action to Datastore: %v", err)
	}
}

//GetFor returns a slice containing all the Actions for
//a given Model.
func GetFor(m Model, c appengine.Context) (as []Action, err error) {
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	as = make([]Action, 12)
	keys, err := datastore.NewQuery("Action").Ancestor(key).GetAll(c, &as)
	if err != nil {
		return
	}
	for i := range keys {
		as[i].Key = keys[i].Encode()
	}
	return
}

//GetDownloadCount returns how many times given ActionType was performed on a Model.
func GetCountFor(at ActionType, m Model, c appengine.Context) (count int, err error) {
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	count, err = datastore.NewQuery("Action").Ancestor(key).Filter("Type =", at).Count(c)
	return
}

//WasDownloadedBy returns whether given client downloaded file associated
//with given Model.
func WasDownloadedBy(m Model, client string, c appengine.Context) (bool, error) {
	key, err := m.GetKey(c)
	if err != nil {
		return false, err
	}
	i := datastore.NewQuery("Action").Ancestor(key).Filter("Client =", client).Filter("Type =", DownloadFinish).KeysOnly().Run(c)
	_, err = i.Next(nil)
	if err == datastore.Done {
		return false, nil
	}
	return true, err
}

//DeleteFor deletes all Actions for a specified Model.
func DeleteFor(m Model, c appengine.Context) (err error) {
	var keys []*datastore.Key
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	keys, err = datastore.NewQuery("Action").Ancestor(key).KeysOnly().GetAll(c, nil)
	if err != nil {
		return
	}
	err = datastore.DeleteMulti(c, keys)
	if err != nil {
		return
	}
	return
}
<commit_msg>Renamed Presentation field of Action to Model<commit_after>package action

import (
	"appengine"
	"appengine/datastore"
	"time"
)

const (
	UpdateNotification ActionType = iota //Client was notified it should update.
	DownloadStart      ActionType = iota //Client startde downloading.
	DownloadFinish     ActionType = iota //Client finished download.
)

type ActionType int

func (at ActionType) String() string {
	switch at {
	case UpdateNotification:
		return "Update notification"
	case DownloadStart:
		return "Download started"
	case DownloadFinish:
		return "Download is finished"
	}
	return "Unknown action"
}

//Action (better name would be Action, but it would be hard to change it now)
//is a type used for recording whe time when clients
//did an action specified by the ActionType.
type Action struct {
	Type   ActionType     //Type of action
	Client string         //Name of the client
	Time   time.Time      //Time of the action
	Model  *datastore.Key //What object is the action related to.
	Key    string         `datastore:"-"`
}

//Model is an nterface specifying models - structs which Datastore keys can be obtained.
type Model interface {
	GetKey(appengine.Context) (*datastore.Key, error)
}

func (a Action) GetKey() (k *datastore.Key, err error) {
	k, err = datastore.DecodeKey(a.Key)
	return
}

//New returns a pointer to an action with its fields set according
//to arguments.
func New(k *datastore.Key, at ActionType, client string) (a *Action) {
	a = new(Action)
	a.Model = k
	a.Type = at
	a.Client = client
	a.Time = time.Now()
	return a
}

//Make creates a new Action using New and then saves
//it to Datastore.
func Make(m Model, at ActionType, client string, c appengine.Context) (a *Action, err error) {
	k, err := m.GetKey(c)
	if err != nil {
		return
	}
	a = New(k, at, client)
	err = a.Save(c)
	return
}

//Save saves an Action to Datastore.
//If its Key field is set, it will use it, replacing
//existing records. If not, it will use datastore.NewIncompleteKey()
//to create a new key and set the field.
func (a *Action) Save(c appengine.Context) (err error) {
	if a.Key == "" {
		var key *datastore.Key
		key, err = datastore.Put(c, datastore.NewIncompleteKey(c, "Action", a.Model), a)
		a.Key = key.Encode()
		return
	} else {
		var key *datastore.Key
		key, err = datastore.DecodeKey(a.Key)
		if err != nil {
			return
		}
		_, err = datastore.Put(c, key, a)
	}
	return
}

//Log works like Make but logs errors instead of returning them.
func Log(m Model, client string, at ActionType, c appengine.Context) {
	if client == "" {
		c.Infof("%v called without client name.", at)
		return
	}

	_, err := Make(m, at, client, c)
	if err != nil {
		c.Infof("Can't log Action to Datastore: %v", err)
	}
}

//GetFor returns a slice containing all the Actions for
//a given Model.
func GetFor(m Model, c appengine.Context) (as []Action, err error) {
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	as = make([]Action, 12)
	keys, err := datastore.NewQuery("Action").Ancestor(key).GetAll(c, &as)
	if err != nil {
		return
	}
	for i := range keys {
		as[i].Key = keys[i].Encode()
	}
	return
}

//GetDownloadCount returns how many times given ActionType was performed on a Model.
func GetCountFor(at ActionType, m Model, c appengine.Context) (count int, err error) {
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	count, err = datastore.NewQuery("Action").Ancestor(key).Filter("Type =", at).Count(c)
	return
}

//WasDownloadedBy returns whether given client downloaded file associated
//with given Model.
func WasDownloadedBy(m Model, client string, c appengine.Context) (bool, error) {
	key, err := m.GetKey(c)
	if err != nil {
		return false, err
	}
	i := datastore.NewQuery("Action").Ancestor(key).Filter("Client =", client).Filter("Type =", DownloadFinish).KeysOnly().Run(c)
	_, err = i.Next(nil)
	if err == datastore.Done {
		return false, nil
	}
	return true, err
}

//DeleteFor deletes all Actions for a specified Model.
func DeleteFor(m Model, c appengine.Context) (err error) {
	var keys []*datastore.Key
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	keys, err = datastore.NewQuery("Action").Ancestor(key).KeysOnly().GetAll(c, nil)
	if err != nil {
		return
	}
	err = datastore.DeleteMulti(c, keys)
	if err != nil {
		return
	}
	return
}
<|endoftext|>


<commit_before>package tokenstore

import "errors"
import "github.com/herb-go/herb/cache"
import "sync"
import "time"

var ErrDataNotFound = errors.New("Data not found")
var ErrDataTypeWrong = errors.New("Data type wrong")
var ErrNilPoint = errors.New("Data point to nil")
var ErrDataTypeNotRegister = errors.New("Data type not register")

type TokenData struct {
	data           map[string][]byte
	ExpiredAt      int64
	CreatedTime    int64
	LastActiveTime int64
	cache          map[string]interface{}
	token          string
	oldToken       string
	loaded         bool
	tokenChanged   bool
	updated        bool
	store          *Store
	Mutex          *sync.RWMutex
}
type tokenCachedData struct {
	Data           map[string][]byte
	CreatedTime    int64
	LastActiveTime int64
	ExpiredAt      int64
}

func newTokenData(token string, s *Store) *TokenData {
	t := time.Now().Unix()
	return &TokenData{
		token:          token,
		data:           map[string][]byte{},
		cache:          map[string]interface{}{},
		store:          s,
		tokenChanged:   false,
		Mutex:          &sync.RWMutex{},
		CreatedTime:    t,
		LastActiveTime: t,
		ExpiredAt:      -1,
	}

}
func (t *TokenData) Token() string {
	return t.token
}
func (t *TokenData) SetToken(newToken string) {
	t.token = newToken
	t.tokenChanged = true
	t.updated = true
}
func (t *TokenData) RegenerateToken(owner string) error {
	token, err := t.store.GenerateToken(owner)
	if err != nil {
		return err
	}
	t.data = map[string][]byte{}
	t.cache = map[string]interface{}{}
	t.SetToken(token)
	return nil
}

func (t *TokenData) Load() error {
	if t.token == "" {
		return ErrTokenNotValidated
	}
	if t.loaded {
		return nil
	}
	err := t.store.GetTokenData(t)
	if err == cache.ErrNotFound {
		if t.tokenChanged == false {
			return ErrDataNotFound
		} else {
			err = nil
		}
	}
	if err != nil {
		return err
	}
	return nil
}
func (t *TokenData) Save() error {
	nextUpdateTime := time.Unix(t.LastActiveTime, 0).Add(t.store.UpdateActiveInterval)
	if nextUpdateTime.Before(time.Now()) {
		t.LastActiveTime = time.Now().Unix()
		t.updated = true
	}
	if t.updated && t.token != "" {
		err := t.store.SetTokenData(t)
		if err != nil {
			return err
		}
		t.updated = false
	}
	if t.tokenChanged && t.oldToken != "" {
		err := t.store.DeleteToken(t.oldToken)
		if err != nil {
			return err
		}
	}
	return nil
}
func (t *TokenData) Marshal() ([]byte, error) {
	return cache.MarshalMsgpack(
		tokenCachedData{
			Data:           t.data,
			ExpiredAt:      t.ExpiredAt,
			CreatedTime:    t.CreatedTime,
			LastActiveTime: t.LastActiveTime,
		})
}
func (t *TokenData) Unmarshal(token string, bytes []byte) error {
	var err error
	var Data = tokenCachedData{}
	t.Mutex.Lock()
	defer t.Mutex.Unlock()
	t.token = token
	t.cache = map[string]interface{}{}
	err = cache.UnmarshalMsgpack(bytes, &(Data))
	if err != nil {
		return err
	}
	t.data = Data.Data
	t.ExpiredAt = Data.ExpiredAt
	t.CreatedTime = Data.CreatedTime
	t.LastActiveTime = Data.LastActiveTime
	return nil
}
<commit_msg>add delete to token data<commit_after>package tokenstore

import "errors"
import "github.com/herb-go/herb/cache"
import "sync"
import "time"

var ErrDataNotFound = errors.New("Data not found")
var ErrDataTypeWrong = errors.New("Data type wrong")
var ErrNilPoint = errors.New("Data point to nil")
var ErrDataTypeNotRegister = errors.New("Data type not register")

type TokenData struct {
	data           map[string][]byte
	ExpiredAt      int64
	CreatedTime    int64
	LastActiveTime int64
	cache          map[string]interface{}
	token          string
	oldToken       string
	loaded         bool
	tokenChanged   bool
	updated        bool
	store          *Store
	Mutex          *sync.RWMutex
}
type tokenCachedData struct {
	Data           map[string][]byte
	CreatedTime    int64
	LastActiveTime int64
	ExpiredAt      int64
}

func newTokenData(token string, s *Store) *TokenData {
	t := time.Now().Unix()
	return &TokenData{
		token:          token,
		data:           map[string][]byte{},
		cache:          map[string]interface{}{},
		store:          s,
		tokenChanged:   false,
		Mutex:          &sync.RWMutex{},
		CreatedTime:    t,
		LastActiveTime: t,
		ExpiredAt:      -1,
	}

}
func (t *TokenData) Token() string {
	return t.token
}
func (t *TokenData) SetToken(newToken string) {
	t.token = newToken
	t.tokenChanged = true
	t.updated = true
}
func (t *TokenData) RegenerateToken(owner string) error {
	token, err := t.store.GenerateToken(owner)
	if err != nil {
		return err
	}
	t.data = map[string][]byte{}
	t.cache = map[string]interface{}{}
	t.SetToken(token)
	return nil
}

func (t *TokenData) Load() error {
	if t.token == "" {
		return ErrTokenNotValidated
	}
	if t.loaded {
		return nil
	}
	err := t.store.GetTokenData(t)
	if err == cache.ErrNotFound {
		if t.tokenChanged == false {
			return ErrDataNotFound
		} else {
			err = nil
		}
	}
	if err != nil {
		return err
	}
	return nil
}
func (t *TokenData) Delete() {
	t.SetToken("")
}
func (t *TokenData) Save() error {
	nextUpdateTime := time.Unix(t.LastActiveTime, 0).Add(t.store.UpdateActiveInterval)
	if nextUpdateTime.Before(time.Now()) {
		t.LastActiveTime = time.Now().Unix()
		t.updated = true
	}
	if t.updated && t.token != "" {
		err := t.store.SetTokenData(t)
		if err != nil {
			return err
		}
		t.updated = false
	}
	if t.tokenChanged && t.oldToken != "" {
		err := t.store.DeleteToken(t.oldToken)
		if err != nil {
			return err
		}
	}
	return nil
}
func (t *TokenData) Marshal() ([]byte, error) {
	return cache.MarshalMsgpack(
		tokenCachedData{
			Data:           t.data,
			ExpiredAt:      t.ExpiredAt,
			CreatedTime:    t.CreatedTime,
			LastActiveTime: t.LastActiveTime,
		})
}
func (t *TokenData) Unmarshal(token string, bytes []byte) error {
	var err error
	var Data = tokenCachedData{}
	t.Mutex.Lock()
	defer t.Mutex.Unlock()
	t.token = token
	t.cache = map[string]interface{}{}
	err = cache.UnmarshalMsgpack(bytes, &(Data))
	if err != nil {
		return err
	}
	t.data = Data.Data
	t.ExpiredAt = Data.ExpiredAt
	t.CreatedTime = Data.CreatedTime
	t.LastActiveTime = Data.LastActiveTime
	return nil
}
<|endoftext|>


<commit_before>package models

import (
	"encoding/json"
	"strings"
)

type reference Reference

func (r *Reference) UnmarshalJSON(data []byte) (err error) {
	ref := reference{}
	if err = json.Unmarshal(data, &ref); err == nil {
		splitURL := strings.Split(ref.Reference, "/")
		if len(splitURL) >= 3 {
			ref.ReferencedID = splitURL[len(splitURL)-1]
			ref.Type = splitURL[len(splitURL)-2]
		}
		external := strings.HasPrefix(ref.Reference, "http")
		ref.External = &external
		*r = Reference(ref)
		return
	}
	return err
}
<commit_msg>Fix bug that caused parsing out components of references to fail<commit_after>package models

import (
	"encoding/json"
	"strings"
)

type reference Reference

func (r *Reference) UnmarshalJSON(data []byte) (err error) {
	ref := reference{}
	if err = json.Unmarshal(data, &ref); err == nil {
		splitURL := strings.Split(ref.Reference, "/")
		if len(splitURL) >= 2 {
			ref.ReferencedID = splitURL[len(splitURL)-1]
			ref.Type = splitURL[len(splitURL)-2]
		}
		external := strings.HasPrefix(ref.Reference, "http")
		ref.External = &external
		*r = Reference(ref)
		return
	}
	return err
}
<|endoftext|>