<commit_before>package sync

import (
	"fmt"
	"log"
	"time"

	"github.com/svdberg/syncmysport-runkeeper/Godeps/_workspace/src/github.com/newrelic/go-agent"
	dm "github.com/svdberg/syncmysport-runkeeper/datamodel"
	rk "github.com/svdberg/syncmysport-runkeeper/runkeeper"
	stv "github.com/svdberg/syncmysport-runkeeper/strava"
)

type Syncer interface {
	Sync() (int, int, error)
}

/*
 * Returns the set of activities that are in RunKeeper, but not in Strava.
 * So if the set of Runkeeper activites is A, and the set of Strava activities is B,
 * this function calculates B\A.
 */
func CalculateRKDifference(rkActivities dm.ActivitySet, stvActivities dm.ActivitySet) *dm.ActivitySet {
	return rkActivities.ApproxSubtract(stvActivities)
}

type SyncTask struct {
	StravaToken       string `json:"stv_token"`
	RunkeeperToken    string `json:"rk_token"`
	LastSeenTimestamp int    `json:"last_seen_ts"`
	Uid               int64  `json:"id"`
	Environment       string `json:"environment"`
}

func CreateSyncTask(rkToken string, stvToken string, lastSeenTS int, environment string) *SyncTask {
	return &SyncTask{stvToken, rkToken, lastSeenTS, -1, environment}
}

/*
 * return the Total difference and the number of Activites created
 */
func (st SyncTask) Sync(stvClient stv.StravaClientInt, rkClient rk.RunkeeperCientInt, txn newrelic.Transaction) (int, int, error) {
	//get activities from strava
	//normalize time to the start of the day, because Runkeeper only supports days as offset, not timestamps
	tsOfStartOfDay := calculateTsAtStartOfDay(st.LastSeenTimestamp)
	segment := newrelic.Segment{}
	segment.Name = "strava-retrieve-activities"
	segment.StartTime = newrelic.StartSegmentNow(txn)
	activities, err := stvClient.GetSTVActivitiesSince(tsOfStartOfDay)
	segment.End()
	if err != nil {
		log.Printf("Error: %e while retrieving Strava activitites since %s, aborting this run", err, time.Unix(int64(st.LastSeenTimestamp), 0))
		return 0, 0, err
	}
	stvDetailedActivities := dm.NewActivitySet()
	for _, actSummary := range activities {
		//get Detailed Actv
		detailedAct, _ := stvClient.GetSTVDetailedActivity(actSummary.Id)

		//get associated Streams
		timeStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "Time")
		if err != nil {
			log.Printf("Error while retrieving time series from Strava: %s", err)
			return 0, 0, err
		}
		log.Printf("timeStream: %s", timeStream)

		//Optional streams
		// var locStream, hrStream, altStream *stv.StreamSet
		locStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "GPS")
		if err != nil {
			log.Printf("Error while reading lat/long for activity id %d: %e", actSummary.Id, err)
		}
		log.Printf("locStream: %s", locStream)
		hrStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "Heartrate")
		if err != nil {
			log.Printf("Error while reading Heartrate for activity id %d: %e", actSummary.Id, err)
		}

		altStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "Altitude")
		if err != nil {
			log.Printf("Error while reading Altitude for activity id %d: %e", actSummary.Id, err)
		}

		stvDetailedActivities.Add(*stv.ConvertToActivity(detailedAct, timeStream, locStream, hrStream, altStream))
	}
	log.Printf("Got %d items from Strava", stvDetailedActivities.NumElements())
	for i := 0; i < stvDetailedActivities.NumElements(); i++ {
		log.Printf("Strava Activity: %s", stvDetailedActivities.Get(i))
	}
	//stvDetailedActivities should be in UTC

	//get activities from runkeeper
	segment = newrelic.Segment{}
	segment.Name = "runkeeper-retrieve-activities"
	segment.StartTime = newrelic.StartSegmentNow(txn)
	rkActivitiesOverview, err := rkClient.GetRKActivitiesSince(st.LastSeenTimestamp)
	rkDetailActivities := rkClient.EnrichRKActivities(rkActivitiesOverview)
	segment.End()
	//log.Printf("rk detail activities: %s", rkDetailActivities)

	rkActivities := dm.NewActivitySet()
	for _, item := range rkDetailActivities {
		rkActivities.Add(*rk.ConvertToActivity(&item))
	}
	if err != nil {
		log.Printf("%s", err)
	}
	log.Printf("Got %d items from RunKeeper", rkActivities.NumElements())
	// rkActivities are in UTC (or should be)

	for i := 0; i < rkActivities.NumElements(); i++ {
		log.Printf("Runkeeper Activity: %s", rkActivities.Get(i))
	}

	//caclulate difference
	itemsToSyncToRk := stvDetailedActivities.ApproxSubtract(rkActivities)
	log.Printf("Difference between Runkeeper and Strava is %d items", itemsToSyncToRk.NumElements())

	//write to runkeeper
	segment = newrelic.Segment{}
	segment.Name = "runkeeper-write-activities"
	segment.StartTime = newrelic.StartSegmentNow(txn)
	totalItemsCreated := 0
	for i := 0; i < itemsToSyncToRk.NumElements(); i++ {
		log.Printf("Now storing item %s to RunKeeper", itemsToSyncToRk.Get(i))
		var (
			uri string
			err error
		)
		if st.Environment == "Prod" {
			uri, err = rkClient.PostActivity(rk.ConvertToRkActivity(itemsToSyncToRk.Get(i)))
		} else {
			log.Print("Assuming DEBUG/TEST mode, not actually writing to Runkeeper")
			err = nil
			uri = "fake_uri"
		}

		if err != nil {
			log.Printf("Something failed during the write to Runkeeper: %s", err)
			return itemsToSyncToRk.NumElements(), totalItemsCreated, err
		}
		if uri != "" {
			log.Printf("URI of activity: %s", uri)
			totalItemsCreated++
		}
	}
	segment.End()
	return itemsToSyncToRk.NumElements(), totalItemsCreated, nil
}

func calculateTsAtStartOfDay(timestamp int) int {
	timeAtTimestamp := time.Unix(int64(timestamp), 0).UTC()
	year, month, day := timeAtTimestamp.Date()
	//	Mon Jan 2 15:04:05 -0700 MST 2006
	ts, err := time.Parse("2006-1-2 15:04:05 MST", fmt.Sprintf("%d-%d-%d 00:00:00 UTC", year, month, day))
	if err != nil {
		log.Printf("Error parsing time from y/m/d to ts of current day: %s", err)
	}
	ts = ts.Add(time.Duration(1) * time.Minute)
	return int(ts.Unix())
}
<commit_msg>Reduce logging<commit_after>package sync

import (
	"fmt"
	"log"
	"time"

	"github.com/svdberg/syncmysport-runkeeper/Godeps/_workspace/src/github.com/newrelic/go-agent"
	dm "github.com/svdberg/syncmysport-runkeeper/datamodel"
	rk "github.com/svdberg/syncmysport-runkeeper/runkeeper"
	stv "github.com/svdberg/syncmysport-runkeeper/strava"
)

type Syncer interface {
	Sync() (int, int, error)
}

/*
 * Returns the set of activities that are in RunKeeper, but not in Strava.
 * So if the set of Runkeeper activites is A, and the set of Strava activities is B,
 * this function calculates B\A.
 */
func CalculateRKDifference(rkActivities dm.ActivitySet, stvActivities dm.ActivitySet) *dm.ActivitySet {
	return rkActivities.ApproxSubtract(stvActivities)
}

type SyncTask struct {
	StravaToken       string `json:"stv_token"`
	RunkeeperToken    string `json:"rk_token"`
	LastSeenTimestamp int    `json:"last_seen_ts"`
	Uid               int64  `json:"id"`
	Environment       string `json:"environment"`
}

func CreateSyncTask(rkToken string, stvToken string, lastSeenTS int, environment string) *SyncTask {
	return &SyncTask{stvToken, rkToken, lastSeenTS, -1, environment}
}

/*
 * return the Total difference and the number of Activites created
 */
func (st SyncTask) Sync(stvClient stv.StravaClientInt, rkClient rk.RunkeeperCientInt, txn newrelic.Transaction) (int, int, error) {
	//get activities from strava
	//normalize time to the start of the day, because Runkeeper only supports days as offset, not timestamps
	tsOfStartOfDay := calculateTsAtStartOfDay(st.LastSeenTimestamp)
	segment := newrelic.Segment{}
	segment.Name = "strava-retrieve-activities"
	segment.StartTime = newrelic.StartSegmentNow(txn)
	activities, err := stvClient.GetSTVActivitiesSince(tsOfStartOfDay)
	segment.End()
	if err != nil {
		log.Printf("Error: %e while retrieving Strava activitites since %s, aborting this run", err, time.Unix(int64(st.LastSeenTimestamp), 0))
		return 0, 0, err
	}
	stvDetailedActivities := dm.NewActivitySet()
	for _, actSummary := range activities {
		//get Detailed Actv
		detailedAct, _ := stvClient.GetSTVDetailedActivity(actSummary.Id)

		//get associated Streams
		timeStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "Time")
		if err != nil {
			log.Printf("Error while retrieving time series from Strava: %s", err)
			return 0, 0, err
		}

		//Optional streams
		// var locStream, hrStream, altStream *stv.StreamSet
		locStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "GPS")
		if err != nil {
			log.Printf("Error while reading lat/long for activity id %d: %e", actSummary.Id, err)
		}
		hrStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "Heartrate")
		if err != nil {
			log.Printf("Error while reading Heartrate for activity id %d: %e", actSummary.Id, err)
		}

		altStream, err := stvClient.GetSTVActivityStream(actSummary.Id, "Altitude")
		if err != nil {
			log.Printf("Error while reading Altitude for activity id %d: %e", actSummary.Id, err)
		}

		stvDetailedActivities.Add(*stv.ConvertToActivity(detailedAct, timeStream, locStream, hrStream, altStream))
	}
	log.Printf("Got %d items from Strava", stvDetailedActivities.NumElements())
	for i := 0; i < stvDetailedActivities.NumElements(); i++ {
		log.Printf("Strava Activity: %s", stvDetailedActivities.Get(i))
	}
	//stvDetailedActivities should be in UTC

	//get activities from runkeeper
	segment = newrelic.Segment{}
	segment.Name = "runkeeper-retrieve-activities"
	segment.StartTime = newrelic.StartSegmentNow(txn)
	rkActivitiesOverview, err := rkClient.GetRKActivitiesSince(st.LastSeenTimestamp)
	rkDetailActivities := rkClient.EnrichRKActivities(rkActivitiesOverview)
	segment.End()
	//log.Printf("rk detail activities: %s", rkDetailActivities)

	rkActivities := dm.NewActivitySet()
	for _, item := range rkDetailActivities {
		rkActivities.Add(*rk.ConvertToActivity(&item))
	}
	if err != nil {
		log.Printf("%s", err)
	}
	log.Printf("Got %d items from RunKeeper", rkActivities.NumElements())
	// rkActivities are in UTC (or should be)

	for i := 0; i < rkActivities.NumElements(); i++ {
		log.Printf("Runkeeper Activity: %s", rkActivities.Get(i))
	}

	//caclulate difference
	itemsToSyncToRk := stvDetailedActivities.ApproxSubtract(rkActivities)
	log.Printf("Difference between Runkeeper and Strava is %d items", itemsToSyncToRk.NumElements())

	//write to runkeeper
	segment = newrelic.Segment{}
	segment.Name = "runkeeper-write-activities"
	segment.StartTime = newrelic.StartSegmentNow(txn)
	totalItemsCreated := 0
	for i := 0; i < itemsToSyncToRk.NumElements(); i++ {
		log.Printf("Now storing item %s to RunKeeper", itemsToSyncToRk.Get(i))
		var (
			uri string
			err error
		)
		if st.Environment == "Prod" {
			uri, err = rkClient.PostActivity(rk.ConvertToRkActivity(itemsToSyncToRk.Get(i)))
		} else {
			log.Print("Assuming DEBUG/TEST mode, not actually writing to Runkeeper")
			err = nil
			uri = "fake_uri"
		}

		if err != nil {
			log.Printf("Something failed during the write to Runkeeper: %s", err)
			return itemsToSyncToRk.NumElements(), totalItemsCreated, err
		}
		if uri != "" {
			log.Printf("URI of activity: %s", uri)
			totalItemsCreated++
		}
	}
	segment.End()
	return itemsToSyncToRk.NumElements(), totalItemsCreated, nil
}

func calculateTsAtStartOfDay(timestamp int) int {
	timeAtTimestamp := time.Unix(int64(timestamp), 0).UTC()
	year, month, day := timeAtTimestamp.Date()
	//	Mon Jan 2 15:04:05 -0700 MST 2006
	ts, err := time.Parse("2006-1-2 15:04:05 MST", fmt.Sprintf("%d-%d-%d 00:00:00 UTC", year, month, day))
	if err != nil {
		log.Printf("Error parsing time from y/m/d to ts of current day: %s", err)
	}
	ts = ts.Add(time.Duration(1) * time.Minute)
	return int(ts.Unix())
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package output

import (
	"html/template"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/caixw/apidoc/app"
	"github.com/caixw/apidoc/doc"
	"github.com/caixw/apidoc/output/static"
)

// 输出的 html 文件后缀名
const htmlSuffix = ".html"

// 用于页首和页脚的附加信息
type htmlPage struct {
	Content        string                // 索引文件的其它内容
	Groups         map[string][]*doc.API // 按组名形式组织的文档集合
	GroupName      string                // 当前分组名称
	Group          []*doc.API            // 当前组的文档集合
	Date           time.Time             // 生成日期
	Version        string                // 文档版本
	AppVersion     string                // apidoc 的版本号
	AppName        string                // 程序名称
	AppRepoURL     string                // 仓库地址
	AppOfficialURL string                // 官网地址
	Title          string                // 标题
	Elapsed        time.Duration         // 生成文档所用的时间
}

// 将 docs 的内容以 html 格式输出。
func renderHTML(docs *doc.Doc, opt *Options) error {
	t, err := compileHTMLTemplate(opt.Template)
	if err != nil {
		return err
	}

	p := buildHTMLPage(docs, opt)

	return renderHTMLGroups(p, t, opt.Dir)
}

// renderHTML 的调试模式
func renderHTMLPlus(docs *doc.Doc, opt *Options) error {
	app.Info("当前为模板调试模式，调试端口为：", opt.Port)
	app.Info("当前为模板调试模式，调试模板为：", opt.Template)

	p := buildHTMLPage(docs, opt)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 获取分组名称
		groupName := path.Base(r.URL.Path)
		if path.Ext(groupName) == htmlSuffix {
			groupName = strings.TrimSuffix(groupName, htmlSuffix)
		}

		// 存在该分组，则重新编译模板，并输出内容。
		if group, found := p.Groups[groupName]; found {
			p.GroupName = groupName
			p.Group = group
			handleGroup(w, r, opt.Template, p)
			return
		}

		// 否则当作普通的文件请求
		http.FileServer(http.Dir(opt.Template)).ServeHTTP(w, r)
	})

	return http.ListenAndServe(opt.Port, nil)
}

// 编译模板，并输出 p 的内容。
func handleGroup(w http.ResponseWriter, r *http.Request, tplDir string, p *htmlPage) {
	t, err := compileHTMLTemplate(tplDir)
	if err != nil {
		app.Error(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	tplName := "group"
	if p.GroupName == app.DefaultGroupName {
		tplName = "index"
	}

	if err = t.ExecuteTemplate(w, tplName, p); err != nil {
		app.Error(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func buildHTMLPage(docs *doc.Doc, opt *Options) *htmlPage {
	p := &htmlPage{
		Content:        docs.Content,
		Title:          docs.Title,
		Version:        docs.Version,
		AppVersion:     app.Version,
		AppName:        app.Name,
		AppRepoURL:     app.RepoURL,
		AppOfficialURL: app.OfficialURL,
		Elapsed:        opt.Elapsed,
		Date:           time.Now(),
		Groups:         make(map[string][]*doc.API, 100),
	}

	for _, api := range docs.Apis { // 按分组名称进行分类
		name := strings.ToLower(api.Group)
		if p.Groups[name] == nil {
			p.Groups[name] = []*doc.API{}
		}
		p.Groups[name] = append(p.Groups[name], api)
	}

	return p
}

// 编译模板
//
// tplDir 模板所在的路径，其目录下所有的 .html 文件会被编译，不查找子目录。
// 若 tplDir 这空，则使用系统默认的模板。
func compileHTMLTemplate(tplDir string) (*template.Template, error) {
	t := template.New("html").
		Funcs(template.FuncMap{
			"groupURL": func(name string) string { // 根据分组名称，获取其相应的 URL
				return path.Join(".", name+htmlSuffix)
			},
			"dateFormat": func(t time.Time) string { // 格式化日期
				return t.Format(app.TimeFormat)
			},
			"nl2br": func(str string) string { // 将字符串的换行符转成

				return strings.Replace(str, "\n", "
", -1)
			},
			"html": func(str string) interface{} { // 转换成 html
				return template.HTML(str)
			},
			"upper": func(str string) string { // 转大写
				return strings.ToUpper(str)
			},
			"lower": func(str string) string { // 转大写
				return strings.ToLower(str)
			},
		})

	if len(tplDir) > 0 { // 自定义模板
		if _, err := t.ParseGlob(path.Join(tplDir, "*.html")); err != nil {
			return nil, err
		}
	} else { // 系统模板
		for _, content := range static.Templates {
			template.Must(t.Parse(content))
		}
	}

	return t, nil
}

// 根据模板 t 将页面输出到 destDir 目录
func renderHTMLGroups(p *htmlPage, t *template.Template, destDir string) error {
	for name, group := range p.Groups {
		path := filepath.Join(destDir, name+htmlSuffix)
		file, err := os.Create(path)
		if err != nil {
			return err
		}
		defer file.Close()

		p.GroupName = name
		p.Group = group

		tplName := "group"
		if name == app.DefaultGroupName {
			tplName = "index"
		}
		if err = t.ExecuteTemplate(file, tplName, p); err != nil {
			return err
		}
	}

	// 输出static
	return static.Output(destDir)
}
<commit_msg>修正函数调用错误<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package output

import (
	"html/template"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/caixw/apidoc/app"
	"github.com/caixw/apidoc/doc"
	"github.com/caixw/apidoc/output/static"
)

// 输出的 html 文件后缀名
const htmlSuffix = ".html"

// 用于页首和页脚的附加信息
type htmlPage struct {
	Content        string                // 索引文件的其它内容
	Groups         map[string][]*doc.API // 按组名形式组织的文档集合
	GroupName      string                // 当前分组名称
	Group          []*doc.API            // 当前组的文档集合
	Date           time.Time             // 生成日期
	Version        string                // 文档版本
	AppVersion     string                // apidoc 的版本号
	AppName        string                // 程序名称
	AppRepoURL     string                // 仓库地址
	AppOfficialURL string                // 官网地址
	Title          string                // 标题
	Elapsed        time.Duration         // 生成文档所用的时间
}

// 将 docs 的内容以 html 格式输出。
func renderHTML(docs *doc.Doc, opt *Options) error {
	t, err := compileHTMLTemplate(opt.Template)
	if err != nil {
		return err
	}

	p := buildHTMLPage(docs, opt)

	return renderHTMLGroups(p, t, opt.Dir)
}

// renderHTML 的调试模式
func renderHTMLPlus(docs *doc.Doc, opt *Options) error {
	app.Infoln("当前为模板调试模式，调试端口为：", opt.Port)
	app.Infoln("当前为模板调试模式，调试模板为：", opt.Template)

	p := buildHTMLPage(docs, opt)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 获取分组名称
		groupName := path.Base(r.URL.Path)
		if path.Ext(groupName) == htmlSuffix {
			groupName = strings.TrimSuffix(groupName, htmlSuffix)
		}

		// 存在该分组，则重新编译模板，并输出内容。
		if group, found := p.Groups[groupName]; found {
			p.GroupName = groupName
			p.Group = group
			handleGroup(w, r, opt.Template, p)
			return
		}

		// 否则当作普通的文件请求
		http.FileServer(http.Dir(opt.Template)).ServeHTTP(w, r)
	})

	return http.ListenAndServe(opt.Port, nil)
}

// 编译模板，并输出 p 的内容。
func handleGroup(w http.ResponseWriter, r *http.Request, tplDir string, p *htmlPage) {
	t, err := compileHTMLTemplate(tplDir)
	if err != nil {
		app.Errorln(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	tplName := "group"
	if p.GroupName == app.DefaultGroupName {
		tplName = "index"
	}

	if err = t.ExecuteTemplate(w, tplName, p); err != nil {
		app.Errorln(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func buildHTMLPage(docs *doc.Doc, opt *Options) *htmlPage {
	p := &htmlPage{
		Content:        docs.Content,
		Title:          docs.Title,
		Version:        docs.Version,
		AppVersion:     app.Version,
		AppName:        app.Name,
		AppRepoURL:     app.RepoURL,
		AppOfficialURL: app.OfficialURL,
		Elapsed:        opt.Elapsed,
		Date:           time.Now(),
		Groups:         make(map[string][]*doc.API, 100),
	}

	for _, api := range docs.Apis { // 按分组名称进行分类
		name := strings.ToLower(api.Group)
		if p.Groups[name] == nil {
			p.Groups[name] = []*doc.API{}
		}
		p.Groups[name] = append(p.Groups[name], api)
	}

	return p
}

// 编译模板
//
// tplDir 模板所在的路径，其目录下所有的 .html 文件会被编译，不查找子目录。
// 若 tplDir 这空，则使用系统默认的模板。
func compileHTMLTemplate(tplDir string) (*template.Template, error) {
	t := template.New("html").
		Funcs(template.FuncMap{
			"groupURL": func(name string) string { // 根据分组名称，获取其相应的 URL
				return path.Join(".", name+htmlSuffix)
			},
			"dateFormat": func(t time.Time) string { // 格式化日期
				return t.Format(app.TimeFormat)
			},
			"nl2br": func(str string) string { // 将字符串的换行符转成

				return strings.Replace(str, "\n", "
", -1)
			},
			"html": func(str string) interface{} { // 转换成 html
				return template.HTML(str)
			},
			"upper": func(str string) string { // 转大写
				return strings.ToUpper(str)
			},
			"lower": func(str string) string { // 转大写
				return strings.ToLower(str)
			},
		})

	if len(tplDir) > 0 { // 自定义模板
		if _, err := t.ParseGlob(path.Join(tplDir, "*.html")); err != nil {
			return nil, err
		}
	} else { // 系统模板
		for _, content := range static.Templates {
			template.Must(t.Parse(content))
		}
	}

	return t, nil
}

// 根据模板 t 将页面输出到 destDir 目录
func renderHTMLGroups(p *htmlPage, t *template.Template, destDir string) error {
	for name, group := range p.Groups {
		path := filepath.Join(destDir, name+htmlSuffix)
		file, err := os.Create(path)
		if err != nil {
			return err
		}
		defer file.Close()

		p.GroupName = name
		p.Group = group

		tplName := "group"
		if name == app.DefaultGroupName {
			tplName = "index"
		}
		if err = t.ExecuteTemplate(file, tplName, p); err != nil {
			return err
		}
	}

	// 输出static
	return static.Output(destDir)
}
<|endoftext|>


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

package router_test

import (
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/router"
	_ "github.com/tsuru/tsuru/router/hipache"
	_ "github.com/tsuru/tsuru/router/testing"
	ttesting "github.com/tsuru/tsuru/testing"
	"launchpad.net/gocheck"
)

type SwapSuite struct {
	conn *db.Storage
}

var _ = gocheck.Suite(&SwapSuite{})

func (s *SwapSuite) SetUpSuite(c *gocheck.C) {
	config.Set("hipache:domain", "swaptest.org")
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "router_swap_tests")
}

func (s *SwapSuite) SetUpTest(c *gocheck.C) {
	var err error
	s.conn, err = db.Conn()
	c.Assert(err, gocheck.IsNil)
	ttesting.ClearAllCollections(s.conn.Collection("router").Database)
}

func (s *SwapSuite) TestSwap(c *gocheck.C) {
	backend1 := "b1"
	backend2 := "b2"
	r, err := router.Get("fake")
	c.Assert(err, gocheck.IsNil)
	r.AddBackend(backend1)
	r.AddRoute(backend1, "http://127.0.0.1")
	r.AddBackend(backend2)
	r.AddRoute(backend2, "http://10.10.10.10")
	err = router.Swap(r, backend1, backend2)
	c.Assert(err, gocheck.IsNil)
	routes1, err := r.Routes(backend1)
	c.Assert(err, gocheck.IsNil)
	c.Assert(routes1, gocheck.DeepEquals, []string{"http://127.0.0.1"})
	routes2, err := r.Routes(backend2)
	c.Assert(err, gocheck.IsNil)
	c.Assert(routes2, gocheck.DeepEquals, []string{"http://10.10.10.10"})
	name1, err := router.Retrieve(backend1)
	c.Assert(err, gocheck.IsNil)
	c.Assert(name1, gocheck.Equals, backend2)
	name2, err := router.Retrieve(backend2)
	c.Assert(err, gocheck.IsNil)
	c.Assert(name2, gocheck.Equals, backend1)
}

func (s *SwapSuite) TestSwapWithDifferentRouterKinds(c *gocheck.C) {
	backend1 := "bb1"
	backend2 := "bb2"
	r1, err := router.Get("fake")
	c.Assert(err, gocheck.IsNil)
	r2, err := router.Get("hipache")
	c.Assert(err, gocheck.IsNil)
	err = r1.AddBackend(backend1)
	c.Assert(err, gocheck.IsNil)
	err = r1.AddRoute(backend1, "http://127.0.0.1")
	c.Assert(err, gocheck.IsNil)
	err = r2.AddBackend(backend2)
	c.Assert(err, gocheck.IsNil)
	err = r2.AddRoute(backend2, "http://10.10.10.10")
	c.Assert(err, gocheck.IsNil)
	err = router.Swap(r1, backend1, backend2)
	c.Assert(err, gocheck.ErrorMatches, `swap is only allowed between routers of the same kind. "bb1" uses "fake", "bb2" uses "hipache"`)
	err = router.Swap(r2, backend1, backend2)
	c.Assert(err, gocheck.ErrorMatches, `swap is only allowed between routers of the same kind. "bb1" uses "fake", "bb2" uses "hipache"`)
}
<commit_msg>router: remove unnecessary import alias in test<commit_after>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package router_test

import (
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/router"
	_ "github.com/tsuru/tsuru/router/hipache"
	_ "github.com/tsuru/tsuru/router/testing"
	"github.com/tsuru/tsuru/testing"
	"launchpad.net/gocheck"
)

type SwapSuite struct {
	conn *db.Storage
}

var _ = gocheck.Suite(&SwapSuite{})

func (s *SwapSuite) SetUpSuite(c *gocheck.C) {
	config.Set("hipache:domain", "swaptest.org")
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "router_swap_tests")
}

func (s *SwapSuite) SetUpTest(c *gocheck.C) {
	var err error
	s.conn, err = db.Conn()
	c.Assert(err, gocheck.IsNil)
	testing.ClearAllCollections(s.conn.Collection("router").Database)
}

func (s *SwapSuite) TestSwap(c *gocheck.C) {
	backend1 := "b1"
	backend2 := "b2"
	r, err := router.Get("fake")
	c.Assert(err, gocheck.IsNil)
	r.AddBackend(backend1)
	r.AddRoute(backend1, "http://127.0.0.1")
	r.AddBackend(backend2)
	r.AddRoute(backend2, "http://10.10.10.10")
	err = router.Swap(r, backend1, backend2)
	c.Assert(err, gocheck.IsNil)
	routes1, err := r.Routes(backend1)
	c.Assert(err, gocheck.IsNil)
	c.Assert(routes1, gocheck.DeepEquals, []string{"http://127.0.0.1"})
	routes2, err := r.Routes(backend2)
	c.Assert(err, gocheck.IsNil)
	c.Assert(routes2, gocheck.DeepEquals, []string{"http://10.10.10.10"})
	name1, err := router.Retrieve(backend1)
	c.Assert(err, gocheck.IsNil)
	c.Assert(name1, gocheck.Equals, backend2)
	name2, err := router.Retrieve(backend2)
	c.Assert(err, gocheck.IsNil)
	c.Assert(name2, gocheck.Equals, backend1)
}

func (s *SwapSuite) TestSwapWithDifferentRouterKinds(c *gocheck.C) {
	backend1 := "bb1"
	backend2 := "bb2"
	r1, err := router.Get("fake")
	c.Assert(err, gocheck.IsNil)
	r2, err := router.Get("hipache")
	c.Assert(err, gocheck.IsNil)
	err = r1.AddBackend(backend1)
	c.Assert(err, gocheck.IsNil)
	err = r1.AddRoute(backend1, "http://127.0.0.1")
	c.Assert(err, gocheck.IsNil)
	err = r2.AddBackend(backend2)
	c.Assert(err, gocheck.IsNil)
	err = r2.AddRoute(backend2, "http://10.10.10.10")
	c.Assert(err, gocheck.IsNil)
	err = router.Swap(r1, backend1, backend2)
	c.Assert(err, gocheck.ErrorMatches, `swap is only allowed between routers of the same kind. "bb1" uses "fake", "bb2" uses "hipache"`)
	err = router.Swap(r2, backend1, backend2)
	c.Assert(err, gocheck.ErrorMatches, `swap is only allowed between routers of the same kind. "bb1" uses "fake", "bb2" uses "hipache"`)
}
<|endoftext|>


<commit_before>package git

/*
#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
#include <git2/pack.h>
#include <stdlib.h>

extern int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload);
*/
import "C"
import (
	"runtime"
	"unsafe"
	"io"
)

type Packbuilder struct {
	ptr *C.git_packbuilder
}

func (repo *Repository) NewPackbuilder() (*Packbuilder, error) {
	builder := &Packbuilder{}
	ret := C.git_packbuilder_new(&builder.ptr, repo.ptr)
	if ret != 0 {
		return nil, LastError()
	}
	runtime.SetFinalizer(builder, (*Packbuilder).Free)
	return builder, nil
}

func (pb *Packbuilder) Free() {
	runtime.SetFinalizer(pb, nil)
	C.git_packbuilder_free(pb.ptr)
}

func (pb *Packbuilder) Insert(id *Oid, name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	ret := C.git_packbuilder_insert(pb.ptr, id.toC(), cname)
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) InsertCommit(id *Oid) error {
	ret := C.git_packbuilder_insert_commit(pb.ptr, id.toC())
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) InsertTree(id *Oid) error {
	ret := C.git_packbuilder_insert_tree(pb.ptr, id.toC())
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) ObjectCount() uint32 {
	return uint32(C.git_packbuilder_object_count(pb.ptr))
}

func (pb *Packbuilder) WriteToFile(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	ret := C.git_packbuilder_write(pb.ptr, cname)
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) Write(w io.Writer) error {
	ch := pb.ForEach()
	for slice := range ch {
		_, err := w.Write(slice)
		if err != nil {
			return err
		}
	}
	return nil
}

func (pb *Packbuilder) Written() uint32 {
	return uint32(C.git_packbuilder_written(pb.ptr))
}

//export packbuilderForEachCb
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Pointer) int {
	ch := *(*chan []byte)(payload)

	slice := C.GoBytes(buf, C.int(size))
	ch <- slice
	return 0
}

func (pb *Packbuilder) forEachWrap(ch chan []byte) {
	C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&ch))
	close(ch)
}

func (pb *Packbuilder) ForEach() chan []byte {
	ch := make(chan []byte, 0)
	go pb.forEachWrap(ch)
	return ch
}
<commit_msg>Add nil for optional parameters for pack_write<commit_after>package git

/*
#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
#include <git2/pack.h>
#include <stdlib.h>

extern int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload);
*/
import "C"
import (
	"io"
	"runtime"
	"unsafe"
)

type Packbuilder struct {
	ptr *C.git_packbuilder
}

func (repo *Repository) NewPackbuilder() (*Packbuilder, error) {
	builder := &Packbuilder{}
	ret := C.git_packbuilder_new(&builder.ptr, repo.ptr)
	if ret != 0 {
		return nil, LastError()
	}
	runtime.SetFinalizer(builder, (*Packbuilder).Free)
	return builder, nil
}

func (pb *Packbuilder) Free() {
	runtime.SetFinalizer(pb, nil)
	C.git_packbuilder_free(pb.ptr)
}

func (pb *Packbuilder) Insert(id *Oid, name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	ret := C.git_packbuilder_insert(pb.ptr, id.toC(), cname)
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) InsertCommit(id *Oid) error {
	ret := C.git_packbuilder_insert_commit(pb.ptr, id.toC())
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) InsertTree(id *Oid) error {
	ret := C.git_packbuilder_insert_tree(pb.ptr, id.toC())
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) ObjectCount() uint32 {
	return uint32(C.git_packbuilder_object_count(pb.ptr))
}

func (pb *Packbuilder) WriteToFile(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	ret := C.git_packbuilder_write(pb.ptr, cname, nil, nil)
	if ret != 0 {
		return LastError()
	}
	return nil
}

func (pb *Packbuilder) Write(w io.Writer) error {
	ch := pb.ForEach()
	for slice := range ch {
		_, err := w.Write(slice)
		if err != nil {
			return err
		}
	}
	return nil
}

func (pb *Packbuilder) Written() uint32 {
	return uint32(C.git_packbuilder_written(pb.ptr))
}

//export packbuilderForEachCb
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Pointer) int {
	ch := *(*chan []byte)(payload)

	slice := C.GoBytes(buf, C.int(size))
	ch <- slice
	return 0
}

func (pb *Packbuilder) forEachWrap(ch chan []byte) {
	C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&ch))
	close(ch)
}

func (pb *Packbuilder) ForEach() chan []byte {
	ch := make(chan []byte, 0)
	go pb.forEachWrap(ch)
	return ch
}
<|endoftext|>


<commit_before>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple xt.Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General xt.Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A xt.PARTICULAR xt.PURPOSE.  See the
GNU Lesser General xt.Public License for more details.

You should have received a copy of the GNU Lesser General xt.Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package xing

import (
	"github.com/ghts/ghts/lib"
	"github.com/ghts/ghts/xing/base"
	"math"
	"strings"
	"testing"
)

func TestT3341_재무_순위_종합(t *testing.T) {
	t.Parallel()

	시장구분_모음 := []lib.T시장구분{lib.P시장구분_전체, lib.P시장구분_코스피, lib.P시장구분_코스닥}
	시장구분 := 시장구분_모음[lib.F임의_범위_이내_정수값(0, 2)]

	재무순위_구분_모음 := []xt.T재무순위_구분{xt.P재무순위_매출액증가율,
		xt.P재무순위_영업이익증가율, xt.P재무순위_세전계속이익증가율, xt.P재무순위_부채비율,
		xt.P재무순위_유보율, xt.P재무순위_EPS, xt.P재무순위_BPS, xt.P재무순위_ROE,
		xt.P재무순위_PER, xt.P재무순위_PBR, xt.P재무순위_PEG}
	재무순위_구분 := 재무순위_구분_모음[lib.F임의_범위_이내_정수값(0, len(재무순위_구분_모음)-1)]

	const 수량 = 100
	실수값 := lib.F2실수_단순형

	값_모음, 에러 := TrT3341_재무_순위_종합(시장구분, 재무순위_구분, 수량)
	lib.F테스트_에러없음(t, 에러)

	for _, 값 := range 값_모음 {
		lib.F테스트_참임(t, 값.M순위 > 0 && 값.M순위 <= 수량)
		lib.F테스트_참임(t, F종목코드_존재함(값.M종목코드))
		lib.F테스트_참임(t, strings.TrimSpace(값.M기업명) != "")
		//lib.F테스트_참임(t, math.Abs(실수값(값.M매출액_증가율)) < 10000, 값.M종목코드, 값.M매출액_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M영업이익_증가율)) < 10000, 값.M종목코드, 값.M영업이익_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M경상이익_증가율)) < 10000, 값.M종목코드, 값.M경상이익_증가율)
		lib.F테스트_참임(t, 실수값(값.M부채비율) >= 0 && 실수값(값.M부채비율) < 10000, 값.M종목코드)
		lib.F테스트_참임(t, 실수값(값.M유보율) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.PER) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.ROE) >= 0)
		lib.F테스트_참임(t, 실수값(값.BPS) > 0)
		lib.F테스트_참임(t, math.Abs(실수값(값.ROE)) < 100000, 값.M종목코드, 값.ROE)
		lib.F테스트_참임(t, math.Abs(실수값(값.PER)) < 100000, 값.M종목코드, 값.PER)
		lib.F테스트_참임(t, math.Abs(실수값(값.PBR)) < 100000, 값.M종목코드, 값.PBR)
		lib.F테스트_참임(t, math.Abs(실수값(값.PEG)) < 100000, 값.M종목코드, 값.PEG)
	}
}
<commit_msg>Xing API t3341 테스트 코드 보완.<commit_after>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple xt.Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General xt.Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A xt.PARTICULAR xt.PURPOSE.  See the
GNU Lesser General xt.Public License for more details.

You should have received a copy of the GNU Lesser General xt.Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package xing

import (
	"github.com/ghts/ghts/lib"
	"github.com/ghts/ghts/xing/base"
	"math"
	"strings"
	"testing"
)

func TestT3341_재무_순위_종합(t *testing.T) {
	t.Parallel()

	시장구분_모음 := []lib.T시장구분{lib.P시장구분_전체, lib.P시장구분_코스피, lib.P시장구분_코스닥}
	시장구분 := 시장구분_모음[lib.F임의_범위_이내_정수값(0, 2)]

	재무순위_구분_모음 := []xt.T재무순위_구분{xt.P재무순위_매출액증가율,
		xt.P재무순위_영업이익증가율, xt.P재무순위_세전계속이익증가율, xt.P재무순위_부채비율,
		xt.P재무순위_유보율, xt.P재무순위_EPS, xt.P재무순위_BPS, xt.P재무순위_ROE,
		xt.P재무순위_PER, xt.P재무순위_PBR, xt.P재무순위_PEG}
	재무순위_구분 := 재무순위_구분_모음[lib.F임의_범위_이내_정수값(0, len(재무순위_구분_모음)-1)]

	const 수량 = 100
	실수값 := lib.F2실수_단순형

	값_모음, 에러 := TrT3341_재무_순위_종합(시장구분, 재무순위_구분, 수량)
	lib.F테스트_에러없음(t, 에러)

	for _, 값 := range 값_모음 {
		lib.F테스트_참임(t, 값.M순위 > 0 && 값.M순위 <= 수량)
		lib.F테스트_참임(t, F종목코드_존재함(값.M종목코드))
		lib.F테스트_참임(t, strings.TrimSpace(값.M기업명) != "")
		//lib.F테스트_참임(t, math.Abs(실수값(값.M매출액_증가율)) < 10000, 값.M종목코드, 값.M매출액_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M영업이익_증가율)) < 10000, 값.M종목코드, 값.M영업이익_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M경상이익_증가율)) < 10000, 값.M종목코드, 값.M경상이익_증가율)
		lib.F테스트_참임(t, 실수값(값.M부채비율) >= 0 && 실수값(값.M부채비율) < 10000, 값.M종목코드, 실수값(값.M부채비율))
		lib.F테스트_참임(t, 실수값(값.M유보율) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.PER) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.ROE) >= 0)
		lib.F테스트_참임(t, 실수값(값.BPS) > 0)
		lib.F테스트_참임(t, math.Abs(실수값(값.ROE)) < 100000, 값.M종목코드, 값.ROE)
		lib.F테스트_참임(t, math.Abs(실수값(값.PER)) < 100000, 값.M종목코드, 값.PER)
		lib.F테스트_참임(t, math.Abs(실수값(값.PBR)) < 100000, 값.M종목코드, 값.PBR)
		lib.F테스트_참임(t, math.Abs(실수값(값.PEG)) < 100000, 값.M종목코드, 값.PEG)
	}
}
<|endoftext|>


<commit_before>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple xt.Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General xt.Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A xt.PARTICULAR xt.PURPOSE.  See the
GNU Lesser General xt.Public License for more details.

You should have received a copy of the GNU Lesser General xt.Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package xing

import (
	"github.com/ghts/ghts/lib"
	"github.com/ghts/ghts/xing/base"
	"math"
	"strings"
	"testing"
)

func TestT3341_재무_순위_종합(t *testing.T) {
	t.Parallel()

	시장구분_모음 := []lib.T시장구분{lib.P시장구분_전체, lib.P시장구분_코스피, lib.P시장구분_코스닥}
	시장구분 := 시장구분_모음[lib.F임의_범위_이내_정수값(0, 2)]

	재무순위_구분_모음 := []xt.T재무순위_구분{xt.P재무순위_매출액증가율,
		xt.P재무순위_영업이익증가율, xt.P재무순위_세전계속이익증가율, xt.P재무순위_부채비율,
		xt.P재무순위_유보율, xt.P재무순위_EPS, xt.P재무순위_BPS, xt.P재무순위_ROE,
		xt.P재무순위_PER, xt.P재무순위_PBR, xt.P재무순위_PEG}
	재무순위_구분 := 재무순위_구분_모음[lib.F임의_범위_이내_정수값(0, len(재무순위_구분_모음)-1)]

	const 수량 = 100
	실수값 := lib.F2실수_단순형

	값_모음, 에러 := TrT3341_재무_순위_종합(시장구분, 재무순위_구분, 수량)
	lib.F테스트_에러없음(t, 에러)

	for _, 값 := range 값_모음 {
		lib.F테스트_참임(t, 값.M순위 > 0 && 값.M순위 <= 수량)
		lib.F테스트_참임(t, F종목코드_존재함(값.M종목코드))
		lib.F테스트_참임(t, strings.TrimSpace(값.M기업명) != "")
		//lib.F테스트_참임(t, math.Abs(실수값(값.M매출액_증가율)) < 10000, 값.M종목코드, 값.M매출액_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M영업이익_증가율)) < 10000, 값.M종목코드, 값.M영업이익_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M경상이익_증가율)) < 10000, 값.M종목코드, 값.M경상이익_증가율)
		lib.F테스트_참임(t, 실수값(값.M부채비율) >= 0 && 실수값(값.M부채비율) < 10000, 값.M종목코드, 실수값(값.M부채비율))
		lib.F테스트_참임(t, 실수값(값.M유보율) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.PER) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.ROE) >= 0)
		lib.F테스트_참임(t, 실수값(값.BPS) > 0)
		lib.F테스트_참임(t, math.Abs(실수값(값.ROE)) < 100000, 값.M종목코드, 값.ROE)
		lib.F테스트_참임(t, math.Abs(실수값(값.PER)) < 100000, 값.M종목코드, 값.PER)
		lib.F테스트_참임(t, math.Abs(실수값(값.PBR)) < 100000, 값.M종목코드, 값.PBR)
		lib.F테스트_참임(t, math.Abs(실수값(값.PEG)) < 100000, 값.M종목코드, 값.PEG)
	}
}
<commit_msg>t3341 테스트 케이스 디버깅<commit_after>/* Copyright (C) 2015-2020 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple xt.Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2020년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General xt.Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A xt.PARTICULAR xt.PURPOSE.  See the
GNU Lesser General xt.Public License for more details.

You should have received a copy of the GNU Lesser General xt.Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package xing

import (
	"github.com/ghts/ghts/lib"
	"github.com/ghts/ghts/xing/base"
	"math"
	"strings"
	"testing"
)

func TestT3341_재무_순위_종합(t *testing.T) {
	t.Parallel()

	시장구분_모음 := []lib.T시장구분{lib.P시장구분_전체, lib.P시장구분_코스피, lib.P시장구분_코스닥}
	시장구분 := 시장구분_모음[lib.F임의_범위_이내_정수값(0, 2)]

	재무순위_구분_모음 := []xt.T재무순위_구분{xt.P재무순위_매출액증가율,
		xt.P재무순위_영업이익증가율, xt.P재무순위_세전계속이익증가율, xt.P재무순위_부채비율,
		xt.P재무순위_유보율, xt.P재무순위_EPS, xt.P재무순위_BPS, xt.P재무순위_ROE,
		xt.P재무순위_PER, xt.P재무순위_PBR, xt.P재무순위_PEG}
	재무순위_구분 := 재무순위_구분_모음[lib.F임의_범위_이내_정수값(0, len(재무순위_구분_모음)-1)]

	const 수량 = 100
	실수값 := lib.F2실수_단순형

	값_모음, 에러 := TrT3341_재무_순위_종합(시장구분, 재무순위_구분, 수량)
	lib.F테스트_에러없음(t, 에러)

	for _, 값 := range 값_모음 {
		lib.F테스트_참임(t, 값.M순위 > 0 && 값.M순위 <= 수량)
		lib.F테스트_참임(t, F종목코드_존재함(값.M종목코드))
		lib.F테스트_참임(t, strings.TrimSpace(값.M기업명) != "")
		//lib.F테스트_참임(t, math.Abs(실수값(값.M매출액_증가율)) < 10000, 값.M종목코드, 값.M매출액_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M영업이익_증가율)) < 10000, 값.M종목코드, 값.M영업이익_증가율)
		//lib.F테스트_참임(t, math.Abs(실수값(값.M경상이익_증가율)) < 10000, 값.M종목코드, 값.M경상이익_증가율)
		//lib.F테스트_참임(t, 실수값(값.M부채비율) >= 0 && 실수값(값.M부채비율) < 10000, 값.M종목코드, 실수값(값.M부채비율))
		lib.F테스트_참임(t, 실수값(값.M부채비율) < 10000, 값.M종목코드, 실수값(값.M부채비율))
		//lib.F테스트_참임(t, 실수값(값.M유보율) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.PER) >= 0)
		lib.F테스트_참임(t, 실수값(값.EPS)*실수값(값.ROE) >= 0)
		lib.F테스트_참임(t, 실수값(값.BPS) > 0)
		lib.F테스트_참임(t, math.Abs(실수값(값.ROE)) < 100000, 값.M종목코드, 값.ROE)
		lib.F테스트_참임(t, math.Abs(실수값(값.PER)) < 100000, 값.M종목코드, 값.PER)
		lib.F테스트_참임(t, math.Abs(실수값(값.PBR)) < 100000, 값.M종목코드, 값.PBR)
		lib.F테스트_참임(t, math.Abs(실수값(값.PEG)) < 100000, 값.M종목코드, 값.PEG)
	}
}
<|endoftext|>


<commit_before>package packer

import (
	"fmt"
	"strings"
)

// MultiError is an error type to track multiple errors. This is used to
// accumulate errors in cases such as configuration parsing, and returning
// them as a single error.
type MultiError struct {
	Errors []error
}

func (e *MultiError) Error() string {
	points := make([]string, len(e.Errors))
	for i, err := range e.Errors {
		points[i] = fmt.Sprintf("* %s", err)
	}

	return fmt.Sprintf(
		"%d error(s) occurred:\n\n%s",
		len(e.Errors), strings.Join(points, "\n"))
}

// MultiErrorAppend is a helper function that will append more errors
// onto a MultiError in order to create a larger multi-error. If the
// original error is not a MultiError, it will be turned into one.
func MultiErrorAppend(err error, errs ...error) *MultiError {
	if err == nil {
		err = new(MultiError)
	}

	switch err := err.(type) {
	case *MultiError:
		if err == nil {
			err = new(MultiError)
		}

		if err.Errors == nil {
			err.Errors = make([]error, 0, len(errs))
		}

		err.Errors = append(err.Errors, errs...)
		return err
	default:
		newErrs := make([]error, len(errs)+1)
		newErrs[0] = err
		copy(newErrs[1:], errs)
		return &MultiError{
			Errors: newErrs,
		}
	}
}
<commit_msg>packer: no need to check if nil since we're appending to slice<commit_after>package packer

import (
	"fmt"
	"strings"
)

// MultiError is an error type to track multiple errors. This is used to
// accumulate errors in cases such as configuration parsing, and returning
// them as a single error.
type MultiError struct {
	Errors []error
}

func (e *MultiError) Error() string {
	points := make([]string, len(e.Errors))
	for i, err := range e.Errors {
		points[i] = fmt.Sprintf("* %s", err)
	}

	return fmt.Sprintf(
		"%d error(s) occurred:\n\n%s",
		len(e.Errors), strings.Join(points, "\n"))
}

// MultiErrorAppend is a helper function that will append more errors
// onto a MultiError in order to create a larger multi-error. If the
// original error is not a MultiError, it will be turned into one.
func MultiErrorAppend(err error, errs ...error) *MultiError {
	if err == nil {
		err = new(MultiError)
	}

	switch err := err.(type) {
	case *MultiError:
		if err == nil {
			err = new(MultiError)
		}

		err.Errors = append(err.Errors, errs...)
		return err
	default:
		newErrs := make([]error, len(errs)+1)
		newErrs[0] = err
		copy(newErrs[1:], errs)
		return &MultiError{
			Errors: newErrs,
		}
	}
}
<|endoftext|>


<commit_before>package rpc

import (
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"sync"
	"time"
)

// MuxConn is able to multiplex multiple streams on top of any
// io.ReadWriteCloser. These streams act like TCP connections (Dial, Accept,
// Close, full duplex, etc.).
//
// The underlying io.ReadWriteCloser is expected to guarantee delivery
// and ordering, such as TCP. Congestion control and such aren't implemented
// by the streams, so that is also up to the underlying connection.
//
// MuxConn works using a fairly dumb multiplexing technique of simply
// framing every piece of data sent into a prefix + data format. Streams
// are established using a subset of the TCP protocol. Only a subset is
// necessary since we assume ordering on the underlying RWC.
type MuxConn struct {
	curId   uint32
	rwc     io.ReadWriteCloser
	streams map[uint32]*Stream
	mu      sync.RWMutex
	wlock   sync.Mutex
}

type muxPacketType byte

const (
	muxPacketSyn muxPacketType = iota
	muxPacketAck
	muxPacketFin
	muxPacketData
)

// Create a new MuxConn around any io.ReadWriteCloser.
func NewMuxConn(rwc io.ReadWriteCloser) *MuxConn {
	m := &MuxConn{
		rwc:     rwc,
		streams: make(map[uint32]*Stream),
	}

	go m.loop()

	return m
}

// Close closes the underlying io.ReadWriteCloser. This will also close
// all streams that are open.
func (m *MuxConn) Close() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	// Close all the streams
	for _, w := range m.streams {
		w.Close()
	}
	m.streams = make(map[uint32]*Stream)

	// Close the actual connection. This will also force the loop
	// to end since it'll read EOF or closed connection.
	return m.rwc.Close()
}

// Accept accepts a multiplexed connection with the given ID. This
// will block until a request is made to connect.
func (m *MuxConn) Accept(id uint32) (io.ReadWriteCloser, error) {
	stream, err := m.openStream(id)
	if err != nil {
		return nil, err
	}

	// If the stream isn't closed, then it is already open somehow
	stream.mu.Lock()
	if stream.state != streamStateSynRecv && stream.state != streamStateClosed {
		stream.mu.Unlock()
		return nil, fmt.Errorf("Stream %d already open in bad state: %d", id, stream.state)
	}

	if stream.state == streamStateSynRecv {
		// Fast track establishing since we already got the syn
		stream.setState(streamStateEstablished)
		stream.mu.Unlock()
	}

	if stream.state != streamStateEstablished {
		// Go into the listening state
		stream.setState(streamStateListen)

		// Register a state change listener to wait for changes
		stateCh := make(chan streamState, 10)
		stream.registerStateListener(stateCh)
		defer func() {
			stream.mu.Lock()
			defer stream.mu.Unlock()
			stream.deregisterStateListener(stateCh)
		}()

		stream.mu.Unlock()

		// Wait for the connection to establish
	ACCEPT_ESTABLISH_LOOP:
		for {
			state := <-stateCh
			switch state {
			case streamStateListen:
			case streamStateEstablished:
				break ACCEPT_ESTABLISH_LOOP
			default:
				defer stream.mu.Unlock()
				return nil, fmt.Errorf("Stream %d went to bad state: %d", id, stream.state)
			}
		}
	}

	// Send the ack down
	if _, err := m.write(stream.id, muxPacketAck, nil); err != nil {
		return nil, err
	}

	return stream, nil
}

// Dial opens a connection to the remote end using the given stream ID.
// An Accept on the remote end will only work with if the IDs match.
func (m *MuxConn) Dial(id uint32) (io.ReadWriteCloser, error) {
	stream, err := m.openStream(id)
	if err != nil {
		return nil, err
	}

	// If the stream isn't closed, then it is already open somehow
	stream.mu.Lock()
	if stream.state != streamStateClosed {
		stream.mu.Unlock()
		return nil, fmt.Errorf("Stream %d already open in bad state: %d", id, stream.state)
	}

	// Open a connection
	if _, err := m.write(stream.id, muxPacketSyn, nil); err != nil {
		return nil, err
	}
	stream.setState(streamStateSynSent)

	// Register a state change listener to wait for changes
	stateCh := make(chan streamState, 10)
	stream.registerStateListener(stateCh)
	defer func() {
		stream.mu.Lock()
		defer stream.mu.Unlock()
		stream.deregisterStateListener(stateCh)
	}()

	stream.mu.Unlock()

	for {
		state := <-stateCh
		switch state {
		case streamStateSynSent:
		case streamStateEstablished:
			return stream, nil
		default:
			defer stream.mu.Unlock()
			return nil, fmt.Errorf("Stream %d went to bad state: %d", id, stream.state)
		}
	}
}

// NextId returns the next available stream ID that isn't currently
// taken.
func (m *MuxConn) NextId() uint32 {
	m.mu.Lock()
	defer m.mu.Unlock()

	for {
		result := m.curId
		m.curId++
		if _, ok := m.streams[result]; !ok {
			return result
		}
	}
}

func (m *MuxConn) openStream(id uint32) (*Stream, error) {
	// First grab a read-lock if we have the stream already we can
	// cheaply return it.
	m.mu.RLock()
	if stream, ok := m.streams[id]; ok {
		m.mu.RUnlock()
		return stream, nil
	}

	// Now acquire a full blown write lock so we can create the stream
	m.mu.RUnlock()
	m.mu.Lock()
	defer m.mu.Unlock()

	// We have to check this again because there is a time period
	// above where we couldn't lost this lock.
	if stream, ok := m.streams[id]; ok {
		return stream, nil
	}

	// Create the stream object and channel where data will be sent to
	dataR, dataW := io.Pipe()
	writeCh := make(chan []byte, 256)

	// Set the data channel so we can write to it.
	stream := &Stream{
		id:          id,
		mux:         m,
		reader:      dataR,
		writeCh:     writeCh,
		stateChange: make(map[chan<- streamState]struct{}),
	}
	stream.setState(streamStateClosed)

	// Start the goroutine that will read from the queue and write
	// data out.
	go func() {
		defer dataW.Close()

		for {
			data := <-writeCh
			if data == nil {
				// A nil is a tombstone letting us know we're done
				// accepting data.
				return
			}

			if _, err := dataW.Write(data); err != nil {
				return
			}
		}
	}()

	m.streams[id] = stream
	return m.streams[id], nil
}

func (m *MuxConn) loop() {
	// Force close every stream that we know about when we exit so
	// that they all read EOF and don't block forever.
	defer func() {
		m.mu.Lock()
		defer m.mu.Unlock()
		for _, w := range m.streams {
			w.mu.Lock()
			w.remoteClose()
			w.mu.Unlock()
		}
	}()

	var id uint32
	var packetType muxPacketType
	var length int32
	for {
		if err := binary.Read(m.rwc, binary.BigEndian, &id); err != nil {
			log.Printf("[ERR] Error reading stream ID: %s", err)
			return
		}
		if err := binary.Read(m.rwc, binary.BigEndian, &packetType); err != nil {
			log.Printf("[ERR] Error reading packet type: %s", err)
			return
		}
		if err := binary.Read(m.rwc, binary.BigEndian, &length); err != nil {
			log.Printf("[ERR] Error reading length: %s", err)
			return
		}

		// TODO(mitchellh): probably would be better to re-use a buffer...
		data := make([]byte, length)
		if length > 0 {
			if _, err := m.rwc.Read(data); err != nil {
				log.Printf("[ERR] Error reading data: %s", err)
				return
			}
		}

		stream, err := m.openStream(id)
		if err != nil {
			log.Printf("[ERR] Error opening stream %d: %s", id, err)
			return
		}

		//log.Printf("[DEBUG] Stream %d received packet %d", id, packetType)
		switch packetType {
		case muxPacketAck:
			stream.mu.Lock()
			switch stream.state {
			case streamStateSynSent:
				stream.setState(streamStateEstablished)
			case streamStateFinWait1:
				stream.setState(streamStateFinWait2)
			default:
				log.Printf("[ERR] Ack received for stream in state: %d", stream.state)
			}
			stream.mu.Unlock()
		case muxPacketSyn:
			stream.mu.Lock()
			switch stream.state {
			case streamStateClosed:
				stream.setState(streamStateSynRecv)
			case streamStateListen:
				stream.setState(streamStateEstablished)
			default:
				log.Printf("[ERR] Syn received for stream in state: %d", stream.state)
			}
			stream.mu.Unlock()
		case muxPacketFin:
			stream.mu.Lock()
			switch stream.state {
			case streamStateEstablished:
				stream.setState(streamStateCloseWait)
				m.write(id, muxPacketAck, nil)

				// Close the writer on our end since we won't receive any
				// more data.
				stream.writeCh <- nil
			case streamStateFinWait1:
				fallthrough
			case streamStateFinWait2:
				stream.remoteClose()

				// Remove this stream from being active so that it
				// can be re-used
				m.mu.Lock()
				delete(m.streams, stream.id)
				m.mu.Unlock()
			default:
				log.Printf("[ERR] Fin received for stream %d in state: %d", id, stream.state)
			}
			stream.mu.Unlock()

		case muxPacketData:
			stream.mu.Lock()
			switch stream.state {
			case streamStateFinWait1:
				fallthrough
			case streamStateFinWait2:
				fallthrough
			case streamStateEstablished:
				select {
				case stream.writeCh <- data:
				default:
					panic(fmt.Sprintf("Failed to write data, buffer full for stream %d", id))
				}
			default:
				log.Printf("[ERR] Data received for stream in state: %d", stream.state)
			}
			stream.mu.Unlock()
		}
	}
}

func (m *MuxConn) write(id uint32, dataType muxPacketType, p []byte) (int, error) {
	m.wlock.Lock()
	defer m.wlock.Unlock()

	if err := binary.Write(m.rwc, binary.BigEndian, id); err != nil {
		return 0, err
	}
	if err := binary.Write(m.rwc, binary.BigEndian, byte(dataType)); err != nil {
		return 0, err
	}
	if err := binary.Write(m.rwc, binary.BigEndian, int32(len(p))); err != nil {
		return 0, err
	}
	if len(p) == 0 {
		return 0, nil
	}
	return m.rwc.Write(p)
}

// Stream is a single stream of data and implements io.ReadWriteCloser.
// A Stream is full-duplex so you can write data as well as read data.
type Stream struct {
	id           uint32
	mux          *MuxConn
	reader       io.Reader
	state        streamState
	stateChange  map[chan<- streamState]struct{}
	stateUpdated time.Time
	mu           sync.Mutex
	writeCh      chan<- []byte
}

type streamState byte

const (
	streamStateClosed streamState = iota
	streamStateListen
	streamStateSynRecv
	streamStateSynSent
	streamStateEstablished
	streamStateFinWait1
	streamStateFinWait2
	streamStateCloseWait
)

func (s *Stream) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.state != streamStateEstablished && s.state != streamStateCloseWait {
		return fmt.Errorf("Stream in bad state: %d", s.state)
	}

	if s.state == streamStateEstablished {
		s.setState(streamStateFinWait1)
	} else {
		s.remoteClose()
	}

	s.mux.write(s.id, muxPacketFin, nil)
	return nil
}

func (s *Stream) Read(p []byte) (int, error) {
	return s.reader.Read(p)
}

func (s *Stream) Write(p []byte) (int, error) {
	s.mu.Lock()
	state := s.state
	s.mu.Unlock()

	if state != streamStateEstablished {
		return 0, fmt.Errorf("Stream %d in bad state to send: %d", s.id, state)
	}

	return s.mux.write(s.id, muxPacketData, p)
}

func (s *Stream) remoteClose() {
	s.setState(streamStateClosed)
	s.writeCh <- nil
}

func (s *Stream) registerStateListener(ch chan<- streamState) {
	s.stateChange[ch] = struct{}{}
}

func (s *Stream) deregisterStateListener(ch chan<- streamState) {
	delete(s.stateChange, ch)
}

func (s *Stream) setState(state streamState) {
	s.state = state
	s.stateUpdated = time.Now().UTC()
	for ch, _ := range s.stateChange {
		select {
		case ch <- state:
		default:
		}
	}
}
<commit_msg>packer/rpc: can write while MuxConn is in CloseWait state<commit_after>package rpc

import (
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"sync"
	"time"
)

// MuxConn is able to multiplex multiple streams on top of any
// io.ReadWriteCloser. These streams act like TCP connections (Dial, Accept,
// Close, full duplex, etc.).
//
// The underlying io.ReadWriteCloser is expected to guarantee delivery
// and ordering, such as TCP. Congestion control and such aren't implemented
// by the streams, so that is also up to the underlying connection.
//
// MuxConn works using a fairly dumb multiplexing technique of simply
// framing every piece of data sent into a prefix + data format. Streams
// are established using a subset of the TCP protocol. Only a subset is
// necessary since we assume ordering on the underlying RWC.
type MuxConn struct {
	curId   uint32
	rwc     io.ReadWriteCloser
	streams map[uint32]*Stream
	mu      sync.RWMutex
	wlock   sync.Mutex
}

type muxPacketType byte

const (
	muxPacketSyn muxPacketType = iota
	muxPacketAck
	muxPacketFin
	muxPacketData
)

// Create a new MuxConn around any io.ReadWriteCloser.
func NewMuxConn(rwc io.ReadWriteCloser) *MuxConn {
	m := &MuxConn{
		rwc:     rwc,
		streams: make(map[uint32]*Stream),
	}

	go m.loop()

	return m
}

// Close closes the underlying io.ReadWriteCloser. This will also close
// all streams that are open.
func (m *MuxConn) Close() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	// Close all the streams
	for _, w := range m.streams {
		w.Close()
	}
	m.streams = make(map[uint32]*Stream)

	// Close the actual connection. This will also force the loop
	// to end since it'll read EOF or closed connection.
	return m.rwc.Close()
}

// Accept accepts a multiplexed connection with the given ID. This
// will block until a request is made to connect.
func (m *MuxConn) Accept(id uint32) (io.ReadWriteCloser, error) {
	stream, err := m.openStream(id)
	if err != nil {
		return nil, err
	}

	// If the stream isn't closed, then it is already open somehow
	stream.mu.Lock()
	if stream.state != streamStateSynRecv && stream.state != streamStateClosed {
		stream.mu.Unlock()
		return nil, fmt.Errorf("Stream %d already open in bad state: %d", id, stream.state)
	}

	if stream.state == streamStateSynRecv {
		// Fast track establishing since we already got the syn
		stream.setState(streamStateEstablished)
		stream.mu.Unlock()
	}

	if stream.state != streamStateEstablished {
		// Go into the listening state
		stream.setState(streamStateListen)

		// Register a state change listener to wait for changes
		stateCh := make(chan streamState, 10)
		stream.registerStateListener(stateCh)
		defer func() {
			stream.mu.Lock()
			defer stream.mu.Unlock()
			stream.deregisterStateListener(stateCh)
		}()

		stream.mu.Unlock()

		// Wait for the connection to establish
	ACCEPT_ESTABLISH_LOOP:
		for {
			state := <-stateCh
			switch state {
			case streamStateListen:
			case streamStateEstablished:
				break ACCEPT_ESTABLISH_LOOP
			default:
				defer stream.mu.Unlock()
				return nil, fmt.Errorf("Stream %d went to bad state: %d", id, stream.state)
			}
		}
	}

	// Send the ack down
	if _, err := m.write(stream.id, muxPacketAck, nil); err != nil {
		return nil, err
	}

	return stream, nil
}

// Dial opens a connection to the remote end using the given stream ID.
// An Accept on the remote end will only work with if the IDs match.
func (m *MuxConn) Dial(id uint32) (io.ReadWriteCloser, error) {
	stream, err := m.openStream(id)
	if err != nil {
		return nil, err
	}

	// If the stream isn't closed, then it is already open somehow
	stream.mu.Lock()
	if stream.state != streamStateClosed {
		stream.mu.Unlock()
		return nil, fmt.Errorf("Stream %d already open in bad state: %d", id, stream.state)
	}

	// Open a connection
	if _, err := m.write(stream.id, muxPacketSyn, nil); err != nil {
		return nil, err
	}
	stream.setState(streamStateSynSent)

	// Register a state change listener to wait for changes
	stateCh := make(chan streamState, 10)
	stream.registerStateListener(stateCh)
	defer func() {
		stream.mu.Lock()
		defer stream.mu.Unlock()
		stream.deregisterStateListener(stateCh)
	}()

	stream.mu.Unlock()

	for {
		state := <-stateCh
		switch state {
		case streamStateSynSent:
		case streamStateEstablished:
			return stream, nil
		default:
			defer stream.mu.Unlock()
			return nil, fmt.Errorf("Stream %d went to bad state: %d", id, stream.state)
		}
	}
}

// NextId returns the next available stream ID that isn't currently
// taken.
func (m *MuxConn) NextId() uint32 {
	m.mu.Lock()
	defer m.mu.Unlock()

	for {
		result := m.curId
		m.curId++
		if _, ok := m.streams[result]; !ok {
			return result
		}
	}
}

func (m *MuxConn) openStream(id uint32) (*Stream, error) {
	// First grab a read-lock if we have the stream already we can
	// cheaply return it.
	m.mu.RLock()
	if stream, ok := m.streams[id]; ok {
		m.mu.RUnlock()
		return stream, nil
	}

	// Now acquire a full blown write lock so we can create the stream
	m.mu.RUnlock()
	m.mu.Lock()
	defer m.mu.Unlock()

	// We have to check this again because there is a time period
	// above where we couldn't lost this lock.
	if stream, ok := m.streams[id]; ok {
		return stream, nil
	}

	// Create the stream object and channel where data will be sent to
	dataR, dataW := io.Pipe()
	writeCh := make(chan []byte, 256)

	// Set the data channel so we can write to it.
	stream := &Stream{
		id:          id,
		mux:         m,
		reader:      dataR,
		writeCh:     writeCh,
		stateChange: make(map[chan<- streamState]struct{}),
	}
	stream.setState(streamStateClosed)

	// Start the goroutine that will read from the queue and write
	// data out.
	go func() {
		defer dataW.Close()

		for {
			data := <-writeCh
			if data == nil {
				// A nil is a tombstone letting us know we're done
				// accepting data.
				return
			}

			if _, err := dataW.Write(data); err != nil {
				return
			}
		}
	}()

	m.streams[id] = stream
	return m.streams[id], nil
}

func (m *MuxConn) loop() {
	// Force close every stream that we know about when we exit so
	// that they all read EOF and don't block forever.
	defer func() {
		m.mu.Lock()
		defer m.mu.Unlock()
		for _, w := range m.streams {
			w.mu.Lock()
			w.remoteClose()
			w.mu.Unlock()
		}
	}()

	var id uint32
	var packetType muxPacketType
	var length int32
	for {
		if err := binary.Read(m.rwc, binary.BigEndian, &id); err != nil {
			log.Printf("[ERR] Error reading stream ID: %s", err)
			return
		}
		if err := binary.Read(m.rwc, binary.BigEndian, &packetType); err != nil {
			log.Printf("[ERR] Error reading packet type: %s", err)
			return
		}
		if err := binary.Read(m.rwc, binary.BigEndian, &length); err != nil {
			log.Printf("[ERR] Error reading length: %s", err)
			return
		}

		// TODO(mitchellh): probably would be better to re-use a buffer...
		data := make([]byte, length)
		if length > 0 {
			if _, err := m.rwc.Read(data); err != nil {
				log.Printf("[ERR] Error reading data: %s", err)
				return
			}
		}

		stream, err := m.openStream(id)
		if err != nil {
			log.Printf("[ERR] Error opening stream %d: %s", id, err)
			return
		}

		//log.Printf("[DEBUG] Stream %d received packet %d", id, packetType)
		switch packetType {
		case muxPacketAck:
			stream.mu.Lock()
			switch stream.state {
			case streamStateSynSent:
				stream.setState(streamStateEstablished)
			case streamStateFinWait1:
				stream.setState(streamStateFinWait2)
			default:
				log.Printf("[ERR] Ack received for stream in state: %d", stream.state)
			}
			stream.mu.Unlock()
		case muxPacketSyn:
			stream.mu.Lock()
			switch stream.state {
			case streamStateClosed:
				stream.setState(streamStateSynRecv)
			case streamStateListen:
				stream.setState(streamStateEstablished)
			default:
				log.Printf("[ERR] Syn received for stream in state: %d", stream.state)
			}
			stream.mu.Unlock()
		case muxPacketFin:
			stream.mu.Lock()
			switch stream.state {
			case streamStateEstablished:
				stream.setState(streamStateCloseWait)
				m.write(id, muxPacketAck, nil)

				// Close the writer on our end since we won't receive any
				// more data.
				stream.writeCh <- nil
			case streamStateFinWait1:
				fallthrough
			case streamStateFinWait2:
				stream.remoteClose()

				// Remove this stream from being active so that it
				// can be re-used
				m.mu.Lock()
				delete(m.streams, stream.id)
				m.mu.Unlock()
			default:
				log.Printf("[ERR] Fin received for stream %d in state: %d", id, stream.state)
			}
			stream.mu.Unlock()

		case muxPacketData:
			stream.mu.Lock()
			switch stream.state {
			case streamStateFinWait1:
				fallthrough
			case streamStateFinWait2:
				fallthrough
			case streamStateEstablished:
				select {
				case stream.writeCh <- data:
				default:
					panic(fmt.Sprintf("Failed to write data, buffer full for stream %d", id))
				}
			default:
				log.Printf("[ERR] Data received for stream in state: %d", stream.state)
			}
			stream.mu.Unlock()
		}
	}
}

func (m *MuxConn) write(id uint32, dataType muxPacketType, p []byte) (int, error) {
	m.wlock.Lock()
	defer m.wlock.Unlock()

	if err := binary.Write(m.rwc, binary.BigEndian, id); err != nil {
		return 0, err
	}
	if err := binary.Write(m.rwc, binary.BigEndian, byte(dataType)); err != nil {
		return 0, err
	}
	if err := binary.Write(m.rwc, binary.BigEndian, int32(len(p))); err != nil {
		return 0, err
	}
	if len(p) == 0 {
		return 0, nil
	}
	return m.rwc.Write(p)
}

// Stream is a single stream of data and implements io.ReadWriteCloser.
// A Stream is full-duplex so you can write data as well as read data.
type Stream struct {
	id           uint32
	mux          *MuxConn
	reader       io.Reader
	state        streamState
	stateChange  map[chan<- streamState]struct{}
	stateUpdated time.Time
	mu           sync.Mutex
	writeCh      chan<- []byte
}

type streamState byte

const (
	streamStateClosed streamState = iota
	streamStateListen
	streamStateSynRecv
	streamStateSynSent
	streamStateEstablished
	streamStateFinWait1
	streamStateFinWait2
	streamStateCloseWait
)

func (s *Stream) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.state != streamStateEstablished && s.state != streamStateCloseWait {
		return fmt.Errorf("Stream in bad state: %d", s.state)
	}

	if s.state == streamStateEstablished {
		s.setState(streamStateFinWait1)
	} else {
		s.remoteClose()
	}

	s.mux.write(s.id, muxPacketFin, nil)
	return nil
}

func (s *Stream) Read(p []byte) (int, error) {
	return s.reader.Read(p)
}

func (s *Stream) Write(p []byte) (int, error) {
	s.mu.Lock()
	state := s.state
	s.mu.Unlock()

	if state != streamStateEstablished && state != streamStateCloseWait {
		return 0, fmt.Errorf("Stream %d in bad state to send: %d", s.id, state)
	}

	return s.mux.write(s.id, muxPacketData, p)
}

func (s *Stream) remoteClose() {
	s.setState(streamStateClosed)
	s.writeCh <- nil
}

func (s *Stream) registerStateListener(ch chan<- streamState) {
	s.stateChange[ch] = struct{}{}
}

func (s *Stream) deregisterStateListener(ch chan<- streamState) {
	delete(s.stateChange, ch)
}

func (s *Stream) setState(state streamState) {
	s.state = state
	s.stateUpdated = time.Now().UTC()
	for ch, _ := range s.stateChange {
		select {
		case ch <- state:
		default:
		}
	}
}
<|endoftext|>


<commit_before>package tracker

import (
	"errors"
	"net"
	"net/url"
)

// Marshalled as binary by the UDP client, so be careful making changes.
type AnnounceRequest struct {
	InfoHash   [20]byte
	PeerId     [20]byte
	Downloaded int64
	Left       uint64
	Uploaded   int64
	Event      AnnounceEvent
	IPAddress  int32
	Key        int32
	NumWant    int32 // How many peer addresses are desired. -1 for default.
	Port       uint16
} // 82 bytes

type AnnounceResponse struct {
	Interval int32 // Minimum seconds the local peer should wait before next announce.
	Leechers int32
	Seeders  int32
	Peers    []Peer
}

type AnnounceEvent int32

func (e AnnounceEvent) String() string {
	// See BEP 3, "event".
	return []string{"empty", "completed", "started", "stopped"}[e]
}

type Peer struct {
	IP   net.IP
	Port int
}

const (
	None      AnnounceEvent = iota
	Completed               // The local peer just completed the torrent.
	Started                 // The local peer has just resumed this torrent.
	Stopped                 // The local peer is leaving the swarm.
)

var (
	ErrBadScheme = errors.New("unknown scheme")
)

func Announce(urlStr string, req *AnnounceRequest) (res AnnounceResponse, err error) {
	return AnnounceHost(urlStr, req, "")
}

func AnnounceHost(urlStr string, req *AnnounceRequest, host string) (res AnnounceResponse, err error) {
	_url, err := url.Parse(urlStr)
	if err != nil {
		return
	}
	switch _url.Scheme {
	case "http", "https":
		return announceHTTP(req, _url, host)
	case "udp":
		return announceUDP(req, _url)
	default:
		err = ErrBadScheme
		return
	}
}
<commit_msg>Comment on tracker.AnnounceRequest.Event<commit_after>package tracker

import (
	"errors"
	"net"
	"net/url"
)

// Marshalled as binary by the UDP client, so be careful making changes.
type AnnounceRequest struct {
	InfoHash   [20]byte
	PeerId     [20]byte
	Downloaded int64
	Left       uint64
	Uploaded   int64
	// Apparently this is optional. None can be used for announces done at
	// regular intervals.
	Event     AnnounceEvent
	IPAddress int32
	Key       int32
	NumWant   int32 // How many peer addresses are desired. -1 for default.
	Port      uint16
} // 82 bytes

type AnnounceResponse struct {
	Interval int32 // Minimum seconds the local peer should wait before next announce.
	Leechers int32
	Seeders  int32
	Peers    []Peer
}

type AnnounceEvent int32

func (e AnnounceEvent) String() string {
	// See BEP 3, "event".
	return []string{"empty", "completed", "started", "stopped"}[e]
}

type Peer struct {
	IP   net.IP
	Port int
}

const (
	None      AnnounceEvent = iota
	Completed               // The local peer just completed the torrent.
	Started                 // The local peer has just resumed this torrent.
	Stopped                 // The local peer is leaving the swarm.
)

var (
	ErrBadScheme = errors.New("unknown scheme")
)

func Announce(urlStr string, req *AnnounceRequest) (res AnnounceResponse, err error) {
	return AnnounceHost(urlStr, req, "")
}

func AnnounceHost(urlStr string, req *AnnounceRequest, host string) (res AnnounceResponse, err error) {
	_url, err := url.Parse(urlStr)
	if err != nil {
		return
	}
	switch _url.Scheme {
	case "http", "https":
		return announceHTTP(req, _url, host)
	case "udp":
		return announceUDP(req, _url)
	default:
		err = ErrBadScheme
		return
	}
}
<|endoftext|>


<commit_before>package shop

import (
	"net/http"
	"sort"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
	"github.com/animenotifier/notify.moe/utils"
)

// PurchaseHistory ...
func PurchaseHistory(ctx *aero.Context) string {
	user := utils.GetUser(ctx)

	if user == nil {
		return ctx.Error(http.StatusUnauthorized, "Not logged in", nil)
	}

	purchases, err := arn.AllPurchases()

	if err != nil {
		return ctx.Error(http.StatusInternalServerError, "Error fetching shop item data", err)
	}

	sort.Slice(purchases, func(i, j int) bool {
		return purchases[i].Date > purchases[j].Date
	})

	return ctx.HTML(components.PurchaseHistory(purchases, user))
}
<commit_msg>Fixed purchase history<commit_after>package shop

import (
	"net/http"
	"sort"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
	"github.com/animenotifier/notify.moe/utils"
)

// PurchaseHistory ...
func PurchaseHistory(ctx *aero.Context) string {
	user := utils.GetUser(ctx)

	if user == nil {
		return ctx.Error(http.StatusUnauthorized, "Not logged in", nil)
	}

	purchases, err := arn.FilterPurchases(func(purchase *arn.Purchase) bool {
		return purchase.UserID == user.ID
	})

	if err != nil {
		return ctx.Error(http.StatusInternalServerError, "Error fetching shop item data", err)
	}

	sort.Slice(purchases, func(i, j int) bool {
		return purchases[i].Date > purchases[j].Date
	})

	return ctx.HTML(components.PurchaseHistory(purchases, user))
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"os"
	"strings"
	"sync"

	"github.com/cyfdecyf/bufio"
)

type DirectList struct {
	Domain map[string]DomainType
	sync.RWMutex
}

type DomainType byte

const (
	domainTypeUnknown DomainType = iota
	domainTypeDirect
	domainTypeProxy
	domainTypeReject
)
const MAX_URL_DOT = 5

func newDirectList() *DirectList {
	return &DirectList{
		Domain: map[string]DomainType{},
	}
}

func charIndex(url string, c byte) []int {
	indexes := make([]int, 0, MAX_URL_DOT+1)
	n := len(url)

	if n <= 1 {
		return indexes
	}

	for i := 0; i < n-1; i++ {
		if url[i] == c {
			indexes = append(indexes, i)
		}
	}
	return indexes
}

func domainSearch(Domain map[string]DomainType, url string, isIP bool) (domainType DomainType, ok bool) {
	if domainType, ok = Domain[url]; !ok && !isIP {
		indexes := charIndex(url, '.')
		n := len(indexes)
		if n > MAX_URL_DOT {
			indexes = indexes[n-MAX_URL_DOT:]
			n = MAX_URL_DOT
		}

		for i := 0; i < n; i++ {
			url_suffix := url[indexes[i]+1:]
			if domainType, ok = Domain[url_suffix]; ok {
				break
			}
		}
	}

	if parentProxy.empty() {
		if domainType == domainTypeReject {
			return domainTypeReject, true
		} else {
			return domainTypeDirect, true
		}
	}
	return
}

func (directList *DirectList) shouldDirect(url *URL) (domainType DomainType) {
	debug.Printf("judging host: %s", url.Host)

	if url.Domain == "" { // simple host or private ip
		return domainTypeDirect
	}

	isIP, isPrivate := hostIsIP(url.Host)
	if v, ok := domainSearch(directList.Domain, url.Host, isIP); ok {
		return v
	}

	if !config.JudgeByIP {
		return domainTypeProxy
	}

	var ip string
	if isIP {
		if isPrivate {
			directList.add(url.Host, domainTypeDirect)
			return domainTypeDirect
		}
		ip = url.Host
	} else {
		hostIPs, err := net.LookupIP(url.Host)
		if err != nil {
			errl.Printf("error looking up host ip %s, err %s", url.Host, err)
			return domainTypeProxy
		}
		ip = hostIPs[0].String()
	}

	if ipShouldDirect(ip) {
		directList.add(url.Host, domainTypeDirect)
		return domainTypeDirect
	} else {
		directList.add(url.Host, domainTypeProxy)
		return domainTypeProxy
	}
}

func (directList *DirectList) add(host string, domainType DomainType) {
	directList.Lock()
	defer directList.Unlock()
	directList.Domain[host] = domainType
}

func (directList *DirectList) GetDirectList() []string {
	lst := make([]string, 0)
	for site, domainType := range directList.Domain {
		if domainType == domainTypeDirect {
			lst = append(lst, site)
		}
	}
	return lst
}

var directList = newDirectList()

func initDomainList(domainListFile string, domainType DomainType) {
	var err error
	if err = isFileExists(domainListFile); err != nil {
		return
	}
	f, err := os.Open(domainListFile)
	if err != nil {
		errl.Println("Error opening domain list:", err)
		return
	}
	defer f.Close()

	directList.Lock()
	defer directList.Unlock()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		domain := strings.TrimSpace(scanner.Text())
		if domain == "" {
			continue
		}
		debug.Printf("Loaded domain %s as type %v", domain, domainType)
		directList.Domain[domain] = domainType
	}
	if scanner.Err() != nil {
		errl.Printf("Error reading domain list %s: %v\n", domainListFile, scanner.Err())
	}
}
<commit_msg>code clean<commit_after>package main

import (
	"net"
	"os"
	"strings"
	"sync"

	"github.com/cyfdecyf/bufio"
)

type DirectList struct {
	Domain map[string]DomainType
	sync.RWMutex
}

type DomainType byte

const (
	domainTypeUnknown DomainType = iota
	domainTypeDirect
	domainTypeProxy
	domainTypeReject
)
const maxHostPart = 5

func newDirectList() *DirectList {
	return &DirectList{
		Domain: map[string]DomainType{},
	}
}

func charIndex(host string, c byte) []int {
	indices := make([]int, 0, maxHostPart+1)
	n := len(host)

	if n <= 1 {
		return indices
	}

	for i := 0; i < n-1; i++ {
		if host[i] == c {
			indices = append(indices, i)
		}
	}
	return indices
}

func domainSearch(domain map[string]DomainType, host string, isIP bool) (domainType DomainType, ok bool) {
	if domainType, ok = domain[host]; !ok && !isIP {
		indices := charIndex(host, '.')
		n := len(indices)
		if n > maxHostPart {
			indices = indices[n-maxHostPart:]
			n = maxHostPart
		}

		for i := 0; i < n; i++ {
			suffix := host[indices[i]+1:]
			if domainType, ok = domain[suffix]; ok {
				break
			}
		}
	}

	if parentProxy.empty() {
		if domainType == domainTypeReject {
			return domainTypeReject, true
		} else {
			return domainTypeDirect, true
		}
	}
	return
}

func (directList *DirectList) shouldDirect(url *URL) (domainType DomainType) {
	debug.Printf("judging host: %s", url.Host)

	if url.Domain == "" { // simple host or private ip
		return domainTypeDirect
	}

	isIP, isPrivate := hostIsIP(url.Host)
	if v, ok := domainSearch(directList.Domain, url.Host, isIP); ok {
		return v
	}

	if !config.JudgeByIP {
		return domainTypeProxy
	}

	var ip string
	if isIP {
		if isPrivate {
			directList.add(url.Host, domainTypeDirect)
			return domainTypeDirect
		}
		ip = url.Host
	} else {
		hostIPs, err := net.LookupIP(url.Host)
		if err != nil {
			errl.Printf("error looking up host ip %s, err %s", url.Host, err)
			return domainTypeProxy
		}
		ip = hostIPs[0].String()
	}

	if ipShouldDirect(ip) {
		directList.add(url.Host, domainTypeDirect)
		return domainTypeDirect
	} else {
		directList.add(url.Host, domainTypeProxy)
		return domainTypeProxy
	}
}

func (directList *DirectList) add(host string, domainType DomainType) {
	directList.Lock()
	defer directList.Unlock()
	directList.Domain[host] = domainType
}

func (directList *DirectList) GetDirectList() []string {
	lst := make([]string, 0)
	for site, domainType := range directList.Domain {
		if domainType == domainTypeDirect {
			lst = append(lst, site)
		}
	}
	return lst
}

var directList = newDirectList()

func initDomainList(domainListFile string, domainType DomainType) {
	var err error
	if err = isFileExists(domainListFile); err != nil {
		return
	}
	f, err := os.Open(domainListFile)
	if err != nil {
		errl.Println("Error opening domain list:", err)
		return
	}
	defer f.Close()

	directList.Lock()
	defer directList.Unlock()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		domain := strings.TrimSpace(scanner.Text())
		if domain == "" {
			continue
		}
		debug.Printf("Loaded domain %s as type %v", domain, domainType)
		directList.Domain[domain] = domainType
	}
	if scanner.Err() != nil {
		errl.Printf("Error reading domain list %s: %v\n", domainListFile, scanner.Err())
	}
}
<|endoftext|>


<commit_before>package goproxy

import (
	"fmt"
	"net/http"
	"strings"
)

// HandleConnectFunc and HandleConnect mimic the `net/http` handlers, and register handlers for CONNECT proxy calls.
func (proxy *ProxyHttpServer) HandleConnectFunc(f func(ctx *ProxyCtx) Next) {
	proxy.connectHandlers = append(proxy.connectHandlers, HandlerFunc(f))
}

func (proxy *ProxyHttpServer) HandleConnect(f Handler) {
	proxy.connectHandlers = append(proxy.connectHandlers, f)
}

// HandleRequestFunc and HandleRequest put hooks to handle certain requests. Note that MITM'd and HTTP requests that go through a CONNECT'd connection also go through those Request Handlers.
func (proxy *ProxyHttpServer) HandleRequestFunc(f func(ctx *ProxyCtx) Next) {
	proxy.requestHandlers = append(proxy.requestHandlers, HandlerFunc(f))
}

func (proxy *ProxyHttpServer) HandleRequest(f Handler) {
	proxy.requestHandlers = append(proxy.requestHandlers, f)
}

// HandleResponseFunc and HandleResponse put hooks to handle certain requests. Note that MITM'd and HTTP requests that go through a CONNECT'd connection also go through those Response Handlers.
func (proxy *ProxyHttpServer) HandleResponseFunc(f func(ctx *ProxyCtx) Next) {
	proxy.responseHandlers = append(proxy.responseHandlers, HandlerFunc(f))
}

func (proxy *ProxyHttpServer) HandleResponse(f Handler) {
	proxy.responseHandlers = append(proxy.responseHandlers, f)
}

//////
////// dispatchers section //////
//////

func (proxy *ProxyHttpServer) dispatchConnectHandlers(ctx *ProxyCtx) {
	hij, ok := ctx.ResponseWriter.(http.Hijacker)
	if !ok {
		panic("httpserver does not support hijacking")
	}

	conn, _, err := hij.Hijack()
	if err != nil {
		panic("cannot hijack connection " + err.Error())
	}

	ctx.Conn = conn

	var then Next
	for _, handler := range proxy.connectHandlers {
		then = handler.Handle(ctx)
		switch then {
		case NEXT:
			continue

		case FORWARD:
			break

		case MITM:
			host := ctx.host
			if strings.IndexRune(host, ':') == -1 {
				addPort := "80"
				if ctx.Req.URL.Scheme == "https" {
					addPort = "443"
				}
				host = host + addPort
			}

			err := ctx.ManInTheMiddle(host)
			if err != nil {
				ctx.Logf("error MITM'ing: %s", err)
			}
			return

		case REJECT:
			ctx.RejectConnect()

		default:
			panic(fmt.Sprintf("Invalid value %v for Next after calling %v", then, handler))
		}
	}

	if err := ctx.ForwardConnect(ctx.host); err != nil {
		ctx.Logf("Failed forwarding in fallback clause: %s", err)
	}
}

func (proxy *ProxyHttpServer) dispatchRequestHandlers(ctx *ProxyCtx) {
	var then Next
	for _, handler := range proxy.requestHandlers {
		then = handler.Handle(ctx)
		switch then {
		case DONE:
			// TODO: ensure everything is properly shut down
			return
		case NEXT:
			continue
		case FORWARD:
			break
		case MITM:
			panic("MITM doesn't make sense when we are already parsing the request")
		case REJECT:
			ctx.ResponseWriter.WriteHeader(502)
			ctx.ResponseWriter.Write([]byte("Rejected by proxy"))
			return
		default:
			panic(fmt.Sprintf("Invalid value %v for Next after calling %v", then, handler))
		}
	}

	ctx.ForwardRequest(ctx.host)
	ctx.DispatchResponseHandlers()
}
<commit_msg>Added support for "DONE" in CONNECT... if you handle the whole thing.<commit_after>package goproxy

import (
	"fmt"
	"net/http"
	"strings"
)

// HandleConnectFunc and HandleConnect mimic the `net/http` handlers, and register handlers for CONNECT proxy calls.
func (proxy *ProxyHttpServer) HandleConnectFunc(f func(ctx *ProxyCtx) Next) {
	proxy.connectHandlers = append(proxy.connectHandlers, HandlerFunc(f))
}

func (proxy *ProxyHttpServer) HandleConnect(f Handler) {
	proxy.connectHandlers = append(proxy.connectHandlers, f)
}

// HandleRequestFunc and HandleRequest put hooks to handle certain requests. Note that MITM'd and HTTP requests that go through a CONNECT'd connection also go through those Request Handlers.
func (proxy *ProxyHttpServer) HandleRequestFunc(f func(ctx *ProxyCtx) Next) {
	proxy.requestHandlers = append(proxy.requestHandlers, HandlerFunc(f))
}

func (proxy *ProxyHttpServer) HandleRequest(f Handler) {
	proxy.requestHandlers = append(proxy.requestHandlers, f)
}

// HandleResponseFunc and HandleResponse put hooks to handle certain requests. Note that MITM'd and HTTP requests that go through a CONNECT'd connection also go through those Response Handlers.
func (proxy *ProxyHttpServer) HandleResponseFunc(f func(ctx *ProxyCtx) Next) {
	proxy.responseHandlers = append(proxy.responseHandlers, HandlerFunc(f))
}

func (proxy *ProxyHttpServer) HandleResponse(f Handler) {
	proxy.responseHandlers = append(proxy.responseHandlers, f)
}

//////
////// dispatchers section //////
//////

func (proxy *ProxyHttpServer) dispatchConnectHandlers(ctx *ProxyCtx) {
	hij, ok := ctx.ResponseWriter.(http.Hijacker)
	if !ok {
		panic("httpserver does not support hijacking")
	}

	conn, _, err := hij.Hijack()
	if err != nil {
		panic("cannot hijack connection " + err.Error())
	}

	ctx.Conn = conn

	var then Next
	for _, handler := range proxy.connectHandlers {
		then = handler.Handle(ctx)
		switch then {
		case NEXT:
			continue

		case FORWARD:
			break

		case MITM:
			host := ctx.host
			if strings.IndexRune(host, ':') == -1 {
				addPort := "80"
				if ctx.Req.URL.Scheme == "https" {
					addPort = "443"
				}
				host = host + addPort
			}

			err := ctx.ManInTheMiddle(host)
			if err != nil {
				ctx.Logf("error MITM'ing: %s", err)
			}
			return

		case REJECT:
			ctx.RejectConnect()

		case DONE:
			return

		default:
			panic(fmt.Sprintf("Invalid value %v for Next after calling %v", then, handler))
		}
	}

	if err := ctx.ForwardConnect(ctx.host); err != nil {
		ctx.Logf("Failed forwarding in fallback clause: %s", err)
	}
}

func (proxy *ProxyHttpServer) dispatchRequestHandlers(ctx *ProxyCtx) {
	var then Next
	for _, handler := range proxy.requestHandlers {
		then = handler.Handle(ctx)
		switch then {
		case DONE:
			// TODO: ensure everything is properly shut down
			return
		case NEXT:
			continue
		case FORWARD:
			break
		case MITM:
			panic("MITM doesn't make sense when we are already parsing the request")
		case REJECT:
			ctx.ResponseWriter.WriteHeader(502)
			ctx.ResponseWriter.Write([]byte("Rejected by proxy"))
			return
		default:
			panic(fmt.Sprintf("Invalid value %v for Next after calling %v", then, handler))
		}
	}

	ctx.ForwardRequest(ctx.host)
	ctx.DispatchResponseHandlers()
}
<|endoftext|>


<commit_before>package tabs

import (
	"github.com/MJKWoolnough/gopherjs/xjs"
	"honnef.co/go/js/dom"
)

type Tab struct {
	Name string
	Func func(dom.Element)
}

func MakeStyle() *dom.HTMLStyleElement {
	s := xjs.CreateElement("style").(*dom.HTMLStyleElement)
	s.SetTextContent(`.tabs {
	line-height : 24px;
	position : relative;
	width : 100%;
	overflow : hidden;
	margin : 0;
	padding : 0 0 0 20px;
}

.tabs:after {
	position : absolute;
	content : "";
	width : 100%;
	bottom : 0;
	left : 0;
	border-bottom: 1px solid #000;
	z-index : 1;
	overflow : hidden;
	text-align : center;
	transform : translateX(-20px);
}

.tabs > div {
	border : 1px solid #000;
	display : inline-block;
	position : relative;
	z-index : 1;
	margin : 0 -5px;
	padding : 0 20px;
	border-top-right-radius: 6px;
	border-top-left-radius: 6px;
	background : linear-gradient(to bottom, #ececec 50%, #d1d1d1 100%);
	box-shadow : 0 3px 3px rgba(0, 0, 0, 0.4), inset 0 1px 0 #fff;
	text-shadow : 0 1px #fff;
	user-select : none;
	-moz-user-select : none;
	-webkit-user-select : none;
}

.tabs > div:hover {
	background : linear-gradient(to bottom, #faa 1%, #ffecec 50%, #d1d1d1 100%);
	cursor : pointer;
}

.tabs > div:before, .tabs > div:after {
	position : absolute;
	bottom : -1px;
	width : 6px;
	height : 6px;
	content : " ";
	border : 1px solid #000;
}

.tabs > div:before {
	left : -7px;
	border-bottom-right-radius : 6px;
	border-width : 0 1px 1px 0;
	box-shadow: 2px 2px 0 #d1d1d1;
}

.tabs > div:after {
	right : -7px;
	border-bottom-left-radius : 6px;
	border-width : 0 0 1px 1px;
	box-shadow: -2px 2px 0 #d1d1d1;
}

.tabs > div.selected {
	border-bottom-color : #fff;
	z-index : 2;
	background : #fff;
}

.tabs > div.selected:hover {
	background : #fff;
	cursor : default;
}

.tabs > div.selected:before {
	box-shadow: 2px 2px 0 #fff;
}

.tabs > div.selected:after {
	box-shadow: -2px 2px 0 #fff;
}

.tabs + .content {
	margin-top : 10px;
	padding : 10px;
}`)
	return s
}

func MakeTabs(t []Tab) dom.Node {
	f := xjs.DocumentFragment()
	if len(t) < 0 {
		return f
	}
	tabsDiv := xjs.CreateElement("div")
	contents := xjs.CreateElement("div")
	tabsDiv.Class().SetString("tabs")
	contents.Class().SetString("content")
	tabs := make([]dom.Element, len(t))
	for n := range t {
		tabs[n] = xjs.SetInnerText(xjs.CreateElement("div"), t[n].Name).(dom.Element)
		tabs[n].AddEventListener("click", false, func() func(dom.Event) {
			i := n
			return func(e dom.Event) {
				if tabs[i].Class().String() == "selected" {
					return
				}
				for _, tab := range tabs {
					tab.Class().Remove("selected")
				}
				newContents := contents.CloneNode(false).(dom.Element)
				contents.ParentNode().ReplaceChild(newContents, contents)
				contents = newContents
				tabs[i].Class().Add("selected")
				t[i].Func(contents)
			}
		}())
		tabsDiv.AppendChild(tabs[n])
	}
	t[0].Func(contents)
	tabs[0].Class().Add("selected")
	f.AppendChild(tabsDiv)
	f.AppendChild(contents)
	return f
}
<commit_msg>Tabs now runs the tab funcs in their own gorountines (helps with blocking calls)<commit_after>package tabs

import (
	"github.com/MJKWoolnough/gopherjs/xjs"
	"honnef.co/go/js/dom"
)

type Tab struct {
	Name string
	Func func(dom.Element)
}

func MakeStyle() *dom.HTMLStyleElement {
	s := xjs.CreateElement("style").(*dom.HTMLStyleElement)
	s.SetTextContent(`.tabs {
	line-height : 24px;
	position : relative;
	width : 100%;
	overflow : hidden;
	margin : 0;
	padding : 0 0 0 20px;
}

.tabs:after {
	position : absolute;
	content : "";
	width : 100%;
	bottom : 0;
	left : 0;
	border-bottom: 1px solid #000;
	z-index : 1;
	overflow : hidden;
	text-align : center;
	transform : translateX(-20px);
}

.tabs > div {
	border : 1px solid #000;
	display : inline-block;
	position : relative;
	z-index : 1;
	margin : 0 -5px;
	padding : 0 20px;
	border-top-right-radius: 6px;
	border-top-left-radius: 6px;
	background : linear-gradient(to bottom, #ececec 50%, #d1d1d1 100%);
	box-shadow : 0 3px 3px rgba(0, 0, 0, 0.4), inset 0 1px 0 #fff;
	text-shadow : 0 1px #fff;
	user-select : none;
	-moz-user-select : none;
	-webkit-user-select : none;
}

.tabs > div:hover {
	background : linear-gradient(to bottom, #faa 1%, #ffecec 50%, #d1d1d1 100%);
	cursor : pointer;
}

.tabs > div:before, .tabs > div:after {
	position : absolute;
	bottom : -1px;
	width : 6px;
	height : 6px;
	content : " ";
	border : 1px solid #000;
}

.tabs > div:before {
	left : -7px;
	border-bottom-right-radius : 6px;
	border-width : 0 1px 1px 0;
	box-shadow: 2px 2px 0 #d1d1d1;
}

.tabs > div:after {
	right : -7px;
	border-bottom-left-radius : 6px;
	border-width : 0 0 1px 1px;
	box-shadow: -2px 2px 0 #d1d1d1;
}

.tabs > div.selected {
	border-bottom-color : #fff;
	z-index : 2;
	background : #fff;
}

.tabs > div.selected:hover {
	background : #fff;
	cursor : default;
}

.tabs > div.selected:before {
	box-shadow: 2px 2px 0 #fff;
}

.tabs > div.selected:after {
	box-shadow: -2px 2px 0 #fff;
}

.tabs + .content {
	margin-top : 10px;
	padding : 10px;
}`)
	return s
}

func MakeTabs(t []Tab) dom.Node {
	f := xjs.DocumentFragment()
	if len(t) < 0 {
		return f
	}
	tabsDiv := xjs.CreateElement("div")
	contents := xjs.CreateElement("div")
	tabsDiv.Class().SetString("tabs")
	contents.Class().SetString("content")
	tabs := make([]dom.Element, len(t))
	for n := range t {
		tabs[n] = xjs.SetInnerText(xjs.CreateElement("div"), t[n].Name).(dom.Element)
		tabs[n].AddEventListener("click", false, func() func(dom.Event) {
			i := n
			return func(e dom.Event) {
				if tabs[i].Class().String() == "selected" {
					return
				}
				for _, tab := range tabs {
					tab.Class().Remove("selected")
				}
				newContents := contents.CloneNode(false).(dom.Element)
				contents.ParentNode().ReplaceChild(newContents, contents)
				contents = newContents
				tabs[i].Class().Add("selected")
				go t[i].Func(contents)
			}
		}())
		tabsDiv.AppendChild(tabs[n])
	}
	t[0].Func(contents)
	tabs[0].Class().Add("selected")
	f.AppendChild(tabsDiv)
	f.AppendChild(contents)
	return f
}
<|endoftext|>


<commit_before>package acronym

const testVersion = 2

func Abbreviate(string) string
<commit_msg>Solution for exercise Acronym<commit_after>// Package acronym contains the implementation of the Exercism's go exercise 'acronym'
package acronym

import (
	"bytes"
	"regexp"
	"strings"
)

// testVersion is the current version of the test
const testVersion = 2

// Abbreviate returns the acronym of the given input phrase
func Abbreviate(input string) string {
	var result bytes.Buffer

	// split the input into tokens (maybe yperText could be handled differently)
	fields := regexp.MustCompile("\\s|-|yperTex").Split(input, -1)
	for _, field := range fields {
		// and get the first char of each token
		result.WriteString(string(field[0]))
	}

	// return converting the acronym to uppercase
	return strings.ToUpper(result.String())
}
<|endoftext|>