<commit_before>package controllers

import (
	"github.com/robfig/revel"

	"github.com/PacketFire/goqdb/app/models"
	"github.com/PacketFire/goqdb/app/routes"

	"net/http"

	"reflect"
	"strings"
	_"fmt"
	"errors"
)

type App struct {
	GorpController
}

var (
	// order input -> order column
	OrderCol = map[string]string{
		     "date": ` Created `,
		   "rating": ` Rating `,
		"relevance": ` CASE ` +
			` WHEN Quote LIKE :search_leading THEN 0 ` +
			` WHEN Quote LIKE :search THEN 1 ` +
			` WHEN Tags LIKE :search THEN 2 ` +
			` ELSE 3 END `,
	}

	// form input "c"sv tags binder
	TagsBinder = revel.Binder{

		Bind: revel.ValueBinder(func (val string, typ reflect.Type) reflect.Value {
			if len(val) == 0 {
				return reflect.Zero(typ)
			}
			s := strings.Split(val, INPUT_TAG_DELIM)

			return reflect.ValueOf(s)
		}),
		Unbind: nil,
	}

	PaginationBinder = revel.Binder{
		Bind: func (params *revel.Params, name string, typ reflect.Type) reflect.Value {
			var p models.Pagination

			params.Bind(&p.Page, "page")

			if p.Page == 0 {
				p.Page = 1
			}

			params.Bind(&p.Size, "size")

			if p.Size != 0 && p.Size > VIEW_SIZE_MAX {
				p.Size = VIEW_SIZE_DEFAULT
			}

			params.Bind(&p.Search, "search")
			p.Search = strings.TrimSpace(p.Search)

			params.Bind(&p.Tag, "tag")
			p.Tag = strings.TrimSpace(p.Tag)

			params.Bind(&p.Order, "order")
			p.Order = strings.TrimSpace(p.Order)

			params.Bind(&p.Asc, "asc")

			p.HasNext = false
			p.HasPrev = false

			return reflect.ValueOf(p)
		},
		Unbind: func (output map[string]string, key string, val interface{}) {
			p := val.(models.Pagination)

			if p.Page != 0 && p.Page != 1 {
				revel.Unbind(output, "page", p.Page)
			}

			if p.Size != 0 && p.Size != VIEW_SIZE_DEFAULT {
				revel.Unbind(output, "size", p.Size)
			}

			if p.Search != "" {
				revel.Unbind(output, "search", p.Search)
			}

			if p.Tag != "" {
				revel.Unbind(output, "tag", p.Tag)
			}

			if p.Order != "" && p.Order != "date" {
				revel.Unbind(output, "order", p.Order)
			}

			if p.Asc {
				revel.Unbind(output, "asc", p.Asc)
			}

		},
	}

)

func init () {
	revel.ERROR_CLASS = "has-error"

	revel.TypeBinders[reflect.TypeOf(models.TagArray{})] = TagsBinder
	revel.TypeBinders[reflect.TypeOf(models.Pagination{})] = PaginationBinder
}

func (c App) Index (page models.Pagination) revel.Result {

	var savedAuthor string

	if author, ok := c.Session["author"]; ok {
		savedAuthor = author
	}

	params := make(map[string]interface{})

	params["search"] = "%"+page.Search+"%"
	params["search_leading"] = page.Search+"%"
	params["tag"]    = page.Tag

	var where string

	if page.Tag != "" {
		where = `
		WHERE QuoteId IN (
			SELECT TagEntry.QuoteId FROM TagEntry
			WHERE TagEntry.Tag = :tag
		) `
	} else {
		where = ` WHERE Quote LIKE :search OR Tags LIKE :search `
	}

	count, err := c.Txn.SelectInt(`SELECT COUNT(*) FROM QdbView ` + where, params)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Print("error retreiving page entries count from db")
		revel.ERROR.Print(err)
		panic(err)
	}

	order := ` ORDER BY `

	if col, ok := OrderCol[page.Order]; ok {
		order += col
	} else {
		order += OrderCol["date"]
		page.Order = ""
	}

	if page.Asc {
		order += ` ASC `
	} else {
		order += ` DESC `
	}

	var size int

	if page.Size == 0 {
		size = VIEW_SIZE_DEFAULT
	} else {
		size = page.Size
	}

	offset := size * (page.Page - 1)

	params["offset"] = offset
	params["size"]   = size

	var entries []models.QdbView

	_, err = c.Txn.Select(&entries,
		`SELECT * FROM QdbView ` + where + ` ` + order + ` LIMIT :offset, :size`,
		params,
	)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Print("error retreiving page entries from db")
		revel.ERROR.Print(err)
		panic(err)
	}

	page.HasPrev = offset > 0

	page.HasNext = int64(offset + size) < count

	var tagcloud []string

	_, err = c.Txn.Select(&tagcloud,
		`SELECT Tag From TagCloud LIMIT ?`, TAG_CLOUD_MAX)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Print("error retreiving tag cloud entries from db")
		revel.ERROR.Print(err)
		panic(err)
	}

	return c.Render(entries, page, tagcloud, savedAuthor)
}

// post
func (c *App) Post (quote models.QdbView, page models.Pagination) revel.Result {
	quote.Validate(c.Validation)

	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
	} else {
		err := c.insertView(&quote)

		if err != nil {
			c.Response.Status = http.StatusInternalServerError
			revel.ERROR.Print("error inserting quote to db")
			revel.ERROR.Print(err)
			panic(err)
		}
	}

	c.Session["author"] = quote.Author

	return c.Redirect(routes.App.Index(page))
}

func (c *App) One (id int) revel.Result {
	obj, err := c.Txn.Get(models.QdbEntry{}, id)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Printf("error retreiving entry from db: %d", id)
		revel.ERROR.Print(err)
		panic(err)
		return c.RenderError(nil)
	}

	if obj == nil {
		c.Response.Status = http.StatusNotFound
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	entry := obj.(*models.QdbEntry)

	return Utf8Result(entry.Quote)
}

func (c *App) UpVote (id int, page models.Pagination) revel.Result {
	found, err := c.upVote(id)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Printf("error upvoting: %d", id)
		revel.ERROR.Print(err)
		panic(err)
		return c.RenderError(nil)
	}

	if !found {
		c.Response.Status = http.StatusNotFound
		return c.RenderError(nil)
	}

	return c.Redirect(routes.App.Index(page))
}

func (c *App) DownVote (id int, page models.Pagination) revel.Result {
	found, err := c.downVote(id)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Printf("error downvoting: %d", id)
		revel.ERROR.Print(err)
		panic(err)
		return c.RenderError(nil)
	}

	if !found {
		c.Response.Status = http.StatusNotFound
		return c.RenderError(nil)
	}

	return c.Redirect(routes.App.Index(page))
}
<commit_msg>missing error return<commit_after>package controllers

import (
	"github.com/robfig/revel"

	"github.com/PacketFire/goqdb/app/models"
	"github.com/PacketFire/goqdb/app/routes"

	"net/http"

	"reflect"
	"strings"
	_"fmt"
	"errors"
)

type App struct {
	GorpController
}

var (
	// order input -> order column
	OrderCol = map[string]string{
		     "date": ` Created `,
		   "rating": ` Rating `,
		"relevance": ` CASE ` +
			` WHEN Quote LIKE :search_leading THEN 0 ` +
			` WHEN Quote LIKE :search THEN 1 ` +
			` WHEN Tags LIKE :search THEN 2 ` +
			` ELSE 3 END `,
	}

	// form input "c"sv tags binder
	TagsBinder = revel.Binder{

		Bind: revel.ValueBinder(func (val string, typ reflect.Type) reflect.Value {
			if len(val) == 0 {
				return reflect.Zero(typ)
			}
			s := strings.Split(val, INPUT_TAG_DELIM)

			return reflect.ValueOf(s)
		}),
		Unbind: nil,
	}

	PaginationBinder = revel.Binder{
		Bind: func (params *revel.Params, name string, typ reflect.Type) reflect.Value {
			var p models.Pagination

			params.Bind(&p.Page, "page")

			if p.Page == 0 {
				p.Page = 1
			}

			params.Bind(&p.Size, "size")

			if p.Size != 0 && p.Size > VIEW_SIZE_MAX {
				p.Size = VIEW_SIZE_DEFAULT
			}

			params.Bind(&p.Search, "search")
			p.Search = strings.TrimSpace(p.Search)

			params.Bind(&p.Tag, "tag")
			p.Tag = strings.TrimSpace(p.Tag)

			params.Bind(&p.Order, "order")
			p.Order = strings.TrimSpace(p.Order)

			params.Bind(&p.Asc, "asc")

			p.HasNext = false
			p.HasPrev = false

			return reflect.ValueOf(p)
		},
		Unbind: func (output map[string]string, key string, val interface{}) {
			p := val.(models.Pagination)

			if p.Page != 0 && p.Page != 1 {
				revel.Unbind(output, "page", p.Page)
			}

			if p.Size != 0 && p.Size != VIEW_SIZE_DEFAULT {
				revel.Unbind(output, "size", p.Size)
			}

			if p.Search != "" {
				revel.Unbind(output, "search", p.Search)
			}

			if p.Tag != "" {
				revel.Unbind(output, "tag", p.Tag)
			}

			if p.Order != "" && p.Order != "date" {
				revel.Unbind(output, "order", p.Order)
			}

			if p.Asc {
				revel.Unbind(output, "asc", p.Asc)
			}

		},
	}

)

func init () {
	revel.ERROR_CLASS = "has-error"

	revel.TypeBinders[reflect.TypeOf(models.TagArray{})] = TagsBinder
	revel.TypeBinders[reflect.TypeOf(models.Pagination{})] = PaginationBinder
}

func (c App) Index (page models.Pagination) revel.Result {

	var savedAuthor string

	if author, ok := c.Session["author"]; ok {
		savedAuthor = author
	}

	params := make(map[string]interface{})

	params["search"] = "%"+page.Search+"%"
	params["search_leading"] = page.Search+"%"
	params["tag"]    = page.Tag

	var where string

	if page.Tag != "" {
		where = `
		WHERE QuoteId IN (
			SELECT TagEntry.QuoteId FROM TagEntry
			WHERE TagEntry.Tag = :tag
		) `
	} else {
		where = ` WHERE Quote LIKE :search OR Tags LIKE :search `
	}

	count, err := c.Txn.SelectInt(`SELECT COUNT(*) FROM QdbView ` + where, params)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Print("error retreiving page entries count from db")
		revel.ERROR.Print(err)
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	order := ` ORDER BY `

	if col, ok := OrderCol[page.Order]; ok {
		order += col
	} else {
		order += OrderCol["date"]
		page.Order = ""
	}

	if page.Asc {
		order += ` ASC `
	} else {
		order += ` DESC `
	}

	var size int

	if page.Size == 0 {
		size = VIEW_SIZE_DEFAULT
	} else {
		size = page.Size
	}

	offset := size * (page.Page - 1)

	params["offset"] = offset
	params["size"]   = size

	var entries []models.QdbView

	_, err = c.Txn.Select(&entries,
		`SELECT * FROM QdbView ` + where + ` ` + order + ` LIMIT :offset, :size`,
		params,
	)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Print("error retreiving page entries from db")
		revel.ERROR.Print(err)
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	page.HasPrev = offset > 0

	page.HasNext = int64(offset + size) < count

	var tagcloud []string

	_, err = c.Txn.Select(&tagcloud,
		`SELECT Tag From TagCloud LIMIT ?`, TAG_CLOUD_MAX)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Print("error retreiving tag cloud entries from db")
		revel.ERROR.Print(err)
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	return c.Render(entries, page, tagcloud, savedAuthor)
}

// post
func (c *App) Post (quote models.QdbView, page models.Pagination) revel.Result {
	quote.Validate(c.Validation)

	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
	} else {
		err := c.insertView(&quote)

		if err != nil {
			c.Response.Status = http.StatusInternalServerError
			revel.ERROR.Print("error inserting quote to db")
			revel.ERROR.Print(err)
			return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
		}
	}

	c.Session["author"] = quote.Author

	return c.Redirect(routes.App.Index(page))
}

func (c *App) One (id int) revel.Result {
	obj, err := c.Txn.Get(models.QdbEntry{}, id)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Printf("error retreiving entry from db: %d", id)
		revel.ERROR.Print(err)
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	if obj == nil {
		c.Response.Status = http.StatusNotFound
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	entry := obj.(*models.QdbEntry)

	return Utf8Result(entry.Quote)
}

func (c *App) UpVote (id int, page models.Pagination) revel.Result {
	found, err := c.upVote(id)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Printf("error upvoting: %d", id)
		revel.ERROR.Print(err)
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	if !found {
		c.Response.Status = http.StatusNotFound
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	return c.Redirect(routes.App.Index(page))
}

func (c *App) DownVote (id int, page models.Pagination) revel.Result {
	found, err := c.downVote(id)

	if err != nil {
		c.Response.Status = http.StatusInternalServerError
		revel.ERROR.Printf("error downvoting: %d", id)
		revel.ERROR.Print(err)
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	if !found {
		c.Response.Status = http.StatusNotFound
		return c.RenderError(errors.New(http.StatusText(c.Response.Status)))
	}

	return c.Redirect(routes.App.Index(page))
}
<|endoftext|>


<commit_before>package bitswap

import (
	"bytes"
	"testing"
	"time"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
	bstore "github.com/jbenet/go-ipfs/blockstore"
	exchange "github.com/jbenet/go-ipfs/exchange"
	notifications "github.com/jbenet/go-ipfs/exchange/bitswap/notifications"
	strategy "github.com/jbenet/go-ipfs/exchange/bitswap/strategy"
	testnet "github.com/jbenet/go-ipfs/exchange/bitswap/testnet"
	peer "github.com/jbenet/go-ipfs/peer"
	testutil "github.com/jbenet/go-ipfs/util/testutil"
)

func TestGetBlockTimeout(t *testing.T) {

	net := testnet.VirtualNetwork()
	rs := testnet.VirtualRoutingServer()

	self := session(net, rs, []byte("peer id"))

	ctx, _ := context.WithTimeout(context.Background(), time.Nanosecond)
	block := testutil.NewBlockOrFail(t, "block")
	_, err := self.exchange.Block(ctx, block.Key())

	if err != context.DeadlineExceeded {
		t.Fatal("Expected DeadlineExceeded error")
	}
}

func TestProviderForKeyButNetworkCannotFind(t *testing.T) {

	net := testnet.VirtualNetwork()
	rs := testnet.VirtualRoutingServer()

	block := testutil.NewBlockOrFail(t, "block")
	rs.Announce(&peer.Peer{}, block.Key()) // but not on network

	solo := session(net, rs, []byte("peer id"))

	ctx, _ := context.WithTimeout(context.Background(), time.Nanosecond)
	_, err := solo.exchange.Block(ctx, block.Key())

	if err != context.DeadlineExceeded {
		t.Fatal("Expected DeadlineExceeded error")
	}
}

// TestGetBlockAfterRequesting...

func TestGetBlockFromPeerAfterPeerAnnounces(t *testing.T) {

	net := testnet.VirtualNetwork()
	rs := testnet.VirtualRoutingServer()
	block := testutil.NewBlockOrFail(t, "block")

	hasBlock := session(net, rs, []byte("hasBlock"))

	if err := hasBlock.blockstore.Put(block); err != nil {
		t.Fatal(err)
	}
	if err := hasBlock.exchange.HasBlock(context.Background(), block); err != nil {
		t.Fatal(err)
	}

	wantsBlock := session(net, rs, []byte("wantsBlock"))

	ctx, _ := context.WithTimeout(context.Background(), time.Second)
	received, err := wantsBlock.exchange.Block(ctx, block.Key())
	if err != nil {
		t.Log(err)
		t.Fatal("Expected to succeed")
	}

	if !bytes.Equal(block.Data, received.Data) {
		t.Fatal("Data doesn't match")
	}
}

type testnetBitSwap struct {
	peer       *peer.Peer
	exchange   exchange.Interface
	blockstore bstore.Blockstore
}

func session(net testnet.Network, rs testnet.RoutingServer, id peer.ID) testnetBitSwap {
	p := &peer.Peer{ID: id}

	adapter := net.Adapter(p)
	htc := rs.Client(p)

	blockstore := bstore.NewBlockstore(ds.NewMapDatastore())
	bs := &bitswap{
		blockstore:    blockstore,
		notifications: notifications.New(),
		strategy:      strategy.New(),
		routing:       htc,
		sender:        adapter,
	}
	adapter.SetDelegate(bs)
	return testnetBitSwap{
		peer:       p,
		exchange:   bs,
		blockstore: blockstore,
	}
}

func TestSendToWantingPeer(t *testing.T) {
	t.Log("Peer |w| tells me it wants file, but I don't have it")
	t.Log("Then another peer |o| sends it to me")
	t.Log("After receiving the file from |o|, I send it to the wanting peer |w|")
}
<commit_msg>test(bitswap) add SessionGenerator<commit_after>package bitswap

import (
	"bytes"
	"testing"
	"time"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
	bstore "github.com/jbenet/go-ipfs/blockstore"
	exchange "github.com/jbenet/go-ipfs/exchange"
	notifications "github.com/jbenet/go-ipfs/exchange/bitswap/notifications"
	strategy "github.com/jbenet/go-ipfs/exchange/bitswap/strategy"
	tn "github.com/jbenet/go-ipfs/exchange/bitswap/testnet"
	peer "github.com/jbenet/go-ipfs/peer"
	testutil "github.com/jbenet/go-ipfs/util/testutil"
)

func TestGetBlockTimeout(t *testing.T) {

	net := tn.VirtualNetwork()
	rs := tn.VirtualRoutingServer()
	g := NewSessionGenerator(net, rs)

	self := g.Next()

	ctx, _ := context.WithTimeout(context.Background(), time.Nanosecond)
	block := testutil.NewBlockOrFail(t, "block")
	_, err := self.exchange.Block(ctx, block.Key())

	if err != context.DeadlineExceeded {
		t.Fatal("Expected DeadlineExceeded error")
	}
}

func TestProviderForKeyButNetworkCannotFind(t *testing.T) {

	net := tn.VirtualNetwork()
	rs := tn.VirtualRoutingServer()
	g := NewSessionGenerator(net, rs)

	block := testutil.NewBlockOrFail(t, "block")
	rs.Announce(&peer.Peer{}, block.Key()) // but not on network

	solo := g.Next()

	ctx, _ := context.WithTimeout(context.Background(), time.Nanosecond)
	_, err := solo.exchange.Block(ctx, block.Key())

	if err != context.DeadlineExceeded {
		t.Fatal("Expected DeadlineExceeded error")
	}
}

// TestGetBlockAfterRequesting...

func TestGetBlockFromPeerAfterPeerAnnounces(t *testing.T) {

	net := tn.VirtualNetwork()
	rs := tn.VirtualRoutingServer()
	block := testutil.NewBlockOrFail(t, "block")
	g := NewSessionGenerator(net, rs)

	hasBlock := g.Next()

	if err := hasBlock.blockstore.Put(block); err != nil {
		t.Fatal(err)
	}
	if err := hasBlock.exchange.HasBlock(context.Background(), block); err != nil {
		t.Fatal(err)
	}

	wantsBlock := g.Next()

	ctx, _ := context.WithTimeout(context.Background(), time.Second)
	received, err := wantsBlock.exchange.Block(ctx, block.Key())
	if err != nil {
		t.Log(err)
		t.Fatal("Expected to succeed")
	}

	if !bytes.Equal(block.Data, received.Data) {
		t.Fatal("Data doesn't match")
	}
}

func TestSendToWantingPeer(t *testing.T) {
	t.Log("I get a file from peer |w|. In this message, I receive |w|'s wants")
	t.Log("Peer |w| tells me it wants file |f|, but I don't have it")
	t.Log("Later, peer |o| sends |f| to me")
	t.Log("After receiving |f| from |o|, I send it to the wanting peer |w|")
}

func NewSessionGenerator(
	net tn.Network, rs tn.RoutingServer) SessionGenerator {
	return SessionGenerator{
		net: net,
		rs:  rs,
		seq: 0,
	}
}

type SessionGenerator struct {
	seq int
	net tn.Network
	rs  tn.RoutingServer
}

func (g *SessionGenerator) Next() testnetBitSwap {
	g.seq++
	return session(g.net, g.rs, []byte(string(g.seq)))
}

type testnetBitSwap struct {
	peer       *peer.Peer
	exchange   exchange.Interface
	blockstore bstore.Blockstore
}

// session creates a test bitswap session.
//
// NB: It's easy make mistakes by providing the same peer ID to two different
// sessions. To safeguard, use the SessionGenerator to generate sessions. It's
// just a much better idea.
func session(net tn.Network, rs tn.RoutingServer, id peer.ID) testnetBitSwap {
	p := &peer.Peer{ID: id}

	adapter := net.Adapter(p)
	htc := rs.Client(p)

	blockstore := bstore.NewBlockstore(ds.NewMapDatastore())
	bs := &bitswap{
		blockstore:    blockstore,
		notifications: notifications.New(),
		strategy:      strategy.New(),
		routing:       htc,
		sender:        adapter,
	}
	adapter.SetDelegate(bs)
	return testnetBitSwap{
		peer:       p,
		exchange:   bs,
		blockstore: blockstore,
	}
}
<|endoftext|>


<commit_before>package duck

import (
	"fmt"
	"io/ioutil"
	"log"
	"runtime/debug"
	"time"

	"github.com/TimothyYe/godns"
)

var (
	// DuckUrl the API address for Duck DNS
	DuckUrl = "https://www.duckdns.org/update?domains=%s&token=%s&ip=%s"
)

// Handler struct
type Handler struct {
	Configuration *godns.Settings
}

// SetConfiguration pass dns settings and store it to handler instance
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
	handler.Configuration = conf
}

// DomainLoop the main logic loop
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("Recovered in %v: %v\n", err, debug.Stack())
			panicChan <- *domain
		}
	}()

	var lastIP string

	for {
		currentIP, err := godns.GetCurrentIP(handler.Configuration)

		if err != nil {
			log.Println("get_currentIP:", err)
			continue
		}
		log.Println("currentIP is:", currentIP)

		//check against locally cached IP, if no change, skip update
		if currentIP == lastIP {
			log.Printf("IP is the same as cached one. Skip update.\n")
		} else {
			lastIP = currentIP
			client := godns.GetHttpClient(handler.Configuration)

			for _, subDomain := range domain.SubDomains {
				// update IP with HTTP GET request
				resp, err := client.Get(fmt.Sprintf(DuckUrl, subDomain, handler.Configuration.LoginToken, currentIP))
				if err != nil {
					// handle error
					log.Print("Failed to update sub domain:", subDomain)
					continue
				}

				defer resp.Body.Close()

				body, err := ioutil.ReadAll(resp.Body)
				if err != nil || string(body) != "OK" {
					// handle error
					log.Print("Failed to update sub domain:", subDomain, err.Error())
					continue
				} else {
					log.Print("IP updated to:", currentIP)
				}

				// Send mail notification if notify is enabled
				if handler.Configuration.Notify.Enabled {
					log.Print("Sending notification to:", handler.Configuration.Notify.SendTo)
					if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
						log.Println("Failed to send notification")
					}
				}
			}
		}

		// Sleep with interval
		log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
		time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
	}
}
<commit_msg>add IPv6 support for DuckDNS<commit_after>package duck

import (
	"fmt"
	"io/ioutil"
	"log"
	"runtime/debug"
	"strings"
	"time"

	"github.com/TimothyYe/godns"
)

var (
	// DuckUrl the API address for Duck DNS
	DuckUrl = "https://www.duckdns.org/update?domains=%s&token=%s&%s"
)

// Handler struct
type Handler struct {
	Configuration *godns.Settings
}

// SetConfiguration pass dns settings and store it to handler instance
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
	handler.Configuration = conf
}

// DomainLoop the main logic loop
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("Recovered in %v: %v\n", err, debug.Stack())
			panicChan <- *domain
		}
	}()

	var lastIP string

	for {
		currentIP, err := godns.GetCurrentIP(handler.Configuration)

		if err != nil {
			log.Println("get_currentIP:", err)
			continue
		}
		log.Println("currentIP is:", currentIP)

		//check against locally cached IP, if no change, skip update
		if currentIP == lastIP {
			log.Printf("IP is the same as cached one. Skip update.\n")
		} else {
			lastIP = currentIP
			client := godns.GetHttpClient(handler.Configuration)
			var ip string

			if strings.ToUpper(handler.Configuration.IPType) == godns.IPV4 {
				ip = fmt.Sprintf("ip=%s", currentIP)
			} else if strings.ToUpper(handler.Configuration.IPType) == godns.IPV6 {
				ip = fmt.Sprintf("ipv6=%s", currentIP)
			}

			for _, subDomain := range domain.SubDomains {
				// update IP with HTTP GET request
				resp, err := client.Get(fmt.Sprintf(DuckUrl, subDomain, handler.Configuration.LoginToken, ip))
				if err != nil {
					// handle error
					log.Print("Failed to update sub domain:", subDomain)
					continue
				}

				defer resp.Body.Close()

				body, err := ioutil.ReadAll(resp.Body)
				if err != nil || string(body) != "OK" {
					log.Println("Failed to update the IP")
					continue
				} else {
					log.Print("IP updated to:", currentIP)
				}

				// Send mail notification if notify is enabled
				if handler.Configuration.Notify.Enabled {
					log.Print("Sending notification to:", handler.Configuration.Notify.SendTo)
					if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
						log.Println("Failed to send notification")
					}
				}
			}
		}

		// Sleep with interval
		log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
		time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
	}
}
<|endoftext|>


<commit_before>package chefclient

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/mitchellh/packer/packer"
)

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"server_url": "foo",
	}
}

func TestProvisioner_Impl(t *testing.T) {
	var raw interface{}
	raw = &Provisioner{}
	if _, ok := raw.(packer.Provisioner); !ok {
		t.Fatalf("must be a Provisioner")
	}
}

func TestProvisionerPrepare_configTemplate(t *testing.T) {
	var err error
	var p Provisioner

	// Test no config template
	config := testConfig()
	delete(config, "config_template")
	err = p.Prepare(config)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a file
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf.Name())

	config = testConfig()
	config["config_template"] = tf.Name()
	p = Provisioner{}
	err = p.Prepare(config)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a directory
	td, err := ioutil.TempDir("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(td)

	config = testConfig()
	config["config_template"] = td
	p = Provisioner{}
	err = p.Prepare(config)
	if err == nil {
		t.Fatal("should have err")
	}
}

func TestProvisionerPrepare_commands(t *testing.T) {
	commands := []string{
		"execute_command",
		"install_command",
	}

	for _, command := range commands {
		var p Provisioner

		// Test not set
		config := testConfig()
		delete(config, command)
		err := p.Prepare(config)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		// Test invalid template
		config = testConfig()
		config[command] = "{{if NOPE}}"
		err = p.Prepare(config)
		if err == nil {
			t.Fatal("should error")
		}

		// Test good template
		config = testConfig()
		config[command] = "{{.Foo}}"
		err = p.Prepare(config)
		if err != nil {
			t.Fatalf("err: %s", err)
		}
	}
}

func TestProvisionerPrepare_serverUrl(t *testing.T) {
	var p Provisioner

	// Test not set
	config := testConfig()
	delete(config, "server_url")
	err := p.Prepare(config)
	if err == nil {
		t.Fatal("should error")
	}

	// Test set
	config = testConfig()
	config["server_url"] = "foo"
	err = p.Prepare(config)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}
<commit_msg>add chef_environment test<commit_after>package chefclient

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/mitchellh/packer/packer"
)

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"server_url": "foo",
	}
}

func TestProvisioner_Impl(t *testing.T) {
	var raw interface{}
	raw = &Provisioner{}
	if _, ok := raw.(packer.Provisioner); !ok {
		t.Fatalf("must be a Provisioner")
	}
}

func TestProvisionerPrepare_chefEnvironment(t *testing.T) {
	var p Provisioner

	config := testConfig()
	config["chef_environment"] = "some-env"

	err := p.Prepare(config)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if p.config.ChefEnvironment != "some-env" {
		t.Fatalf("unexpected: %#v", p.config.ChefEnvironment)
	}
}

func TestProvisionerPrepare_configTemplate(t *testing.T) {
	var err error
	var p Provisioner

	// Test no config template
	config := testConfig()
	delete(config, "config_template")
	err = p.Prepare(config)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a file
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf.Name())

	config = testConfig()
	config["config_template"] = tf.Name()
	p = Provisioner{}
	err = p.Prepare(config)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a directory
	td, err := ioutil.TempDir("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(td)

	config = testConfig()
	config["config_template"] = td
	p = Provisioner{}
	err = p.Prepare(config)
	if err == nil {
		t.Fatal("should have err")
	}
}

func TestProvisionerPrepare_commands(t *testing.T) {
	commands := []string{
		"execute_command",
		"install_command",
	}

	for _, command := range commands {
		var p Provisioner

		// Test not set
		config := testConfig()
		delete(config, command)
		err := p.Prepare(config)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		// Test invalid template
		config = testConfig()
		config[command] = "{{if NOPE}}"
		err = p.Prepare(config)
		if err == nil {
			t.Fatal("should error")
		}

		// Test good template
		config = testConfig()
		config[command] = "{{.Foo}}"
		err = p.Prepare(config)
		if err != nil {
			t.Fatalf("err: %s", err)
		}
	}
}

func TestProvisionerPrepare_serverUrl(t *testing.T) {
	var p Provisioner

	// Test not set
	config := testConfig()
	delete(config, "server_url")
	err := p.Prepare(config)
	if err == nil {
		t.Fatal("should error")
	}

	// Test set
	config = testConfig()
	config["server_url"] = "foo"
	err = p.Prepare(config)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}
<|endoftext|>


<commit_before>package autotee

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/deckarep/golang-set"
	"github.com/juju/errors"
)

type IcecastUrls struct {
	mapset.Set
}

type Icecast struct {
	config *Config
	client http.Client
}

func NewIcecast(config *Config) Server {
	return &Icecast{
		config: config,
		client: http.Client{Timeout: config.Times.ServerRequestTimeout},
	}
}

// Get a list of active streams of a specific app from an Icecast server.
func (nr *Icecast) GetActiveStreams() (mapset.Set, error) {

	// Make HTTP request
	resp, err := nr.client.Get(nr.config.Server.Url)
	if err != nil {
		return nil, errors.Trace(err)
	}
	defer resp.Body.Close()

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Trace(err)
	}

	urls := IcecastUrls{mapset.NewSet()}
	err = json.Unmarshal(bytes, &urls)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return urls, nil
}

func (iu *IcecastUrls) UnmarshalJSON(bytes []byte) error {

	var aux struct {
		Icestats struct {
			// No source => missing (nil)
			// One source => dict (map[string]interface{})
			// More sources => list ([]interface{})
			Source interface{} `json:"source"`
		} `json:"icestats"`
	}

	if err := json.Unmarshal(bytes, &aux); err != nil {
		return errors.Trace(err)
	}

	switch obj := aux.Icestats.Source.(type) {

	// No source?
	case nil:
		return nil

	// Exactly one source?
	default:
		return iu.addSourceObj(obj)

	// More than one source?
	case []interface{}:
		for _, subobj := range obj {
			if err := iu.addSourceObj(subobj); err != nil {
				return errors.Trace(err)
			}
		}
		return nil
	}
}

func (iu *IcecastUrls) addSourceObj(sourceObj interface{}) error {
	m, ok := sourceObj.(map[string]interface{})
	if !ok {
		return errors.New("source wasn't a JSON object")
	}

	urlObj, ok := m["listenurl"]
	if !ok {
		return errors.New("listenurl field not present")
	}

	urlStr, ok := urlObj.(string)
	if !ok {
		return errors.New("listenurl field did not contain a string")
	}

	url, err := url.Parse(urlStr)
	if !ok {
		return errors.Annotate(err, "failed to parse listenurl")
	}

	iu.Add(strings.TrimLeft(url.Path, "/"))
	return nil
}
<commit_msg>Fix panic: interface conversion: mapset.Set is autotee.IcecastUrls, not *mapset.threadSafeSet<commit_after>package autotee

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/deckarep/golang-set"
	"github.com/juju/errors"
)

type IcecastUrls struct {
	mapset.Set
}

type Icecast struct {
	config *Config
	client http.Client
}

func NewIcecast(config *Config) Server {
	return &Icecast{
		config: config,
		client: http.Client{Timeout: config.Times.ServerRequestTimeout},
	}
}

// Get a list of active streams of a specific app from an Icecast server.
func (nr *Icecast) GetActiveStreams() (mapset.Set, error) {

	// Make HTTP request
	resp, err := nr.client.Get(nr.config.Server.Url)
	if err != nil {
		return nil, errors.Trace(err)
	}
	defer resp.Body.Close()

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Trace(err)
	}

	urls := IcecastUrls{mapset.NewSet()}
	err = json.Unmarshal(bytes, &urls)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return urls.Set, nil
}

func (iu *IcecastUrls) UnmarshalJSON(bytes []byte) error {

	var aux struct {
		Icestats struct {
			// No source => missing (nil)
			// One source => dict (map[string]interface{})
			// More sources => list ([]interface{})
			Source interface{} `json:"source"`
		} `json:"icestats"`
	}

	if err := json.Unmarshal(bytes, &aux); err != nil {
		return errors.Trace(err)
	}

	switch obj := aux.Icestats.Source.(type) {

	// No source?
	case nil:
		return nil

	// Exactly one source?
	default:
		return iu.addSourceObj(obj)

	// More than one source?
	case []interface{}:
		for _, subobj := range obj {
			if err := iu.addSourceObj(subobj); err != nil {
				return errors.Trace(err)
			}
		}
		return nil
	}
}

func (iu *IcecastUrls) addSourceObj(sourceObj interface{}) error {
	m, ok := sourceObj.(map[string]interface{})
	if !ok {
		return errors.New("source wasn't a JSON object")
	}

	urlObj, ok := m["listenurl"]
	if !ok {
		return errors.New("listenurl field not present")
	}

	urlStr, ok := urlObj.(string)
	if !ok {
		return errors.New("listenurl field did not contain a string")
	}

	url, err := url.Parse(urlStr)
	if !ok {
		return errors.Annotate(err, "failed to parse listenurl")
	}

	iu.Add(strings.TrimLeft(url.Path, "/"))
	return nil
}
<|endoftext|>


<commit_before>package appstore

import "encoding/json"

type numericString string

func (n *numericString) UnmarshalJSON(b []byte) error {
	var number json.Number
	if err := json.Unmarshal(b, &number); err != nil {
		return err
	}
	*n = numericString(number.String())
	return nil
}

type Environment string

const (
	Sandbox    Environment = "Sandbox"
	Production Environment = "Production"
)

type (
	// https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
	// The IAPRequest type has the request parameter
	IAPRequest struct {
		ReceiptData string `json:"receipt-data"`
		// Only used for receipts that contain auto-renewable subscriptions.
		Password string `json:"password,omitempty"`
		// Only used for iOS7 style app receipts that contain auto-renewable or non-renewing subscriptions.
		// If value is true, response includes only the latest renewal transaction for any subscriptions.
		ExcludeOldTransactions bool `json:"exclude-old-transactions"`
	}

	// The ReceiptCreationDate type indicates the date when the app receipt was created.
	ReceiptCreationDate struct {
		CreationDate    string `json:"receipt_creation_date"`
		CreationDateMS  string `json:"receipt_creation_date_ms"`
		CreationDatePST string `json:"receipt_creation_date_pst"`
	}

	// The RequestDate type indicates the date and time that the request was sent
	RequestDate struct {
		RequestDate    string `json:"request_date"`
		RequestDateMS  string `json:"request_date_ms"`
		RequestDatePST string `json:"request_date_pst"`
	}

	// The PurchaseDate type indicates the date and time that the item was purchased
	PurchaseDate struct {
		PurchaseDate    string `json:"purchase_date"`
		PurchaseDateMS  string `json:"purchase_date_ms"`
		PurchaseDatePST string `json:"purchase_date_pst"`
	}

	// The OriginalPurchaseDate type indicates the beginning of the subscription period
	OriginalPurchaseDate struct {
		OriginalPurchaseDate    string `json:"original_purchase_date"`
		OriginalPurchaseDateMS  string `json:"original_purchase_date_ms"`
		OriginalPurchaseDatePST string `json:"original_purchase_date_pst"`
	}

	// The ExpiresDate type indicates the expiration date for the subscription
	ExpiresDate struct {
		ExpiresDate             string `json:"expires_date,omitempty"`
		ExpiresDateMS           string `json:"expires_date_ms,omitempty"`
		ExpiresDatePST          string `json:"expires_date_pst,omitempty"`
		ExpiresDateFormatted    string `json:"expires_date_formatted,omitempty"`
		ExpiresDateFormattedPST string `json:"expires_date_formatted_pst,omitempty"`
	}

	// The CancellationDate type indicates the time and date of the cancellation by Apple customer support
	CancellationDate struct {
		CancellationDate    string `json:"cancellation_date,omitempty"`
		CancellationDateMS  string `json:"cancellation_date_ms,omitempty"`
		CancellationDatePST string `json:"cancellation_date_pst,omitempty"`
	}

	// The GracePeriodDate type indicates the grace period date for the subscription
	GracePeriodDate struct {
		GracePeriodDate    string `json:"grace_period_expires_date,omitempty"`
		GracePeriodDateMS  string `json:"grace_period_expires_date_ms,omitempty"`
		GracePeriodDatePST string `json:"grace_period_expires_date_pst,omitempty"`
	}

	// The InApp type has the receipt attributes
	InApp struct {
		Quantity                    string `json:"quantity"`
		ProductID                   string `json:"product_id"`
		TransactionID               string `json:"transaction_id"`
		OriginalTransactionID       string `json:"original_transaction_id"`
		WebOrderLineItemID          string `json:"web_order_line_item_id,omitempty"`
		PromotionalOfferID          string `json:"promotional_offer_id"`
		SubscriptionGroupIdentifier string `json:"subscription_group_identifier"`

		IsTrialPeriod        string `json:"is_trial_period"`
		IsInIntroOfferPeriod string `json:"is_in_intro_offer_period,omitempty"`
		IsUpgraded           string `json:"is_upgraded,omitempty"`

		ExpiresDate

		PurchaseDate
		OriginalPurchaseDate

		CancellationDate
		CancellationReason string `json:"cancellation_reason,omitempty"`
	}

	// The Receipt type has whole data of receipt
	Receipt struct {
		ReceiptType                string        `json:"receipt_type"`
		AdamID                     int64         `json:"adam_id"`
		AppItemID                  numericString `json:"app_item_id"`
		BundleID                   string        `json:"bundle_id"`
		ApplicationVersion         string        `json:"application_version"`
		DownloadID                 int64         `json:"download_id"`
		VersionExternalIdentifier  numericString `json:"version_external_identifier"`
		OriginalApplicationVersion string        `json:"original_application_version"`
		InApp                      []InApp       `json:"in_app"`
		ReceiptCreationDate
		RequestDate
		OriginalPurchaseDate
	}

	// A pending renewal may refer to a renewal that is scheduled in the future or a renewal that failed in the past for some reason.
	PendingRenewalInfo struct {
		SubscriptionExpirationIntent   string `json:"expiration_intent"`
		SubscriptionAutoRenewProductID string `json:"auto_renew_product_id"`
		SubscriptionRetryFlag          string `json:"is_in_billing_retry_period"`
		SubscriptionAutoRenewStatus    string `json:"auto_renew_status"`
		SubscriptionPriceConsentStatus string `json:"price_consent_status"`
		ProductID                      string `json:"product_id"`
		OriginalTransactionID          string `json:"original_transaction_id"`

		GracePeriodDate
	}

	// The IAPResponse type has the response properties
	// We defined each field by the current IAP response, but some fields are not mentioned
	// in the following Apple's document;
	// https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
	// If you get other types or fields from the IAP response, you should use the struct you defined.
	IAPResponse struct {
		Status             int                  `json:"status"`
		Environment        Environment          `json:"environment"`
		Receipt            Receipt              `json:"receipt"`
		LatestReceiptInfo  []InApp              `json:"latest_receipt_info,omitempty"`
		LatestReceipt      string               `json:"latest_receipt,omitempty"`
		PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info,omitempty"`
		IsRetryable        bool                 `json:"is-retryable,omitempty"`
	}

	// The HttpStatusResponse struct contains the status code returned by the store
	// Used as a workaround to detect when to hit the production appstore or sandbox appstore regardless of receipt type
	StatusResponse struct {
		Status int `json:"status"`
	}

	// IAPResponseForIOS6 is iOS 6 style receipt schema.
	IAPResponseForIOS6 struct {
		AutoRenewProductID     string         `json:"auto_renew_product_id"`
		AutoRenewStatus        int            `json:"auto_renew_status"`
		CancellationReason     string         `json:"cancellation_reason,omitempty"`
		ExpirationIntent       string         `json:"expiration_intent,omitempty"`
		IsInBillingRetryPeriod string         `json:"is_in_billing_retry_period,omitempty"`
		LatestReceiptInfo      ReceiptForIOS6 `json:"latest_expired_receipt_info"`
		Receipt                ReceiptForIOS6 `json:"receipt"`
		Status                 int            `json:"status"`
	}

	ReceiptForIOS6 struct {
		AppItemID numericString `json:"app_item_id"`
		BID       string        `json:"bid"`
		BVRS      string        `json:"bvrs"`
		CancellationDate
		ExpiresDate
		IsTrialPeriod        string `json:"is_trial_period"`
		IsInIntroOfferPeriod string `json:"is_in_intro_offer_period"`
		ItemID               string `json:"item_id"`
		ProductID            string `json:"product_id"`
		PurchaseDate
		OriginalTransactionID string `json:"original_transaction_id"`
		OriginalPurchaseDate
		Quantity                  string        `json:"quantity"`
		TransactionID             string        `json:"transaction_id"`
		UniqueIdentifier          string        `json:"unique_identifier"`
		UniqueVendorIdentifier    string        `json:"unique_vendor_identifier"`
		VersionExternalIdentifier numericString `json:"version_external_identifier,omitempty"`
		WebOrderLineItemID        string        `json:"web_order_line_item_id"`
	}
)
<commit_msg>Add missing fields in IAPResponseForIOS6<commit_after>package appstore

import "encoding/json"

type numericString string

func (n *numericString) UnmarshalJSON(b []byte) error {
	var number json.Number
	if err := json.Unmarshal(b, &number); err != nil {
		return err
	}
	*n = numericString(number.String())
	return nil
}

type Environment string

const (
	Sandbox    Environment = "Sandbox"
	Production Environment = "Production"
)

type (
	// https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
	// The IAPRequest type has the request parameter
	IAPRequest struct {
		ReceiptData string `json:"receipt-data"`
		// Only used for receipts that contain auto-renewable subscriptions.
		Password string `json:"password,omitempty"`
		// Only used for iOS7 style app receipts that contain auto-renewable or non-renewing subscriptions.
		// If value is true, response includes only the latest renewal transaction for any subscriptions.
		ExcludeOldTransactions bool `json:"exclude-old-transactions"`
	}

	// The ReceiptCreationDate type indicates the date when the app receipt was created.
	ReceiptCreationDate struct {
		CreationDate    string `json:"receipt_creation_date"`
		CreationDateMS  string `json:"receipt_creation_date_ms"`
		CreationDatePST string `json:"receipt_creation_date_pst"`
	}

	// The RequestDate type indicates the date and time that the request was sent
	RequestDate struct {
		RequestDate    string `json:"request_date"`
		RequestDateMS  string `json:"request_date_ms"`
		RequestDatePST string `json:"request_date_pst"`
	}

	// The PurchaseDate type indicates the date and time that the item was purchased
	PurchaseDate struct {
		PurchaseDate    string `json:"purchase_date"`
		PurchaseDateMS  string `json:"purchase_date_ms"`
		PurchaseDatePST string `json:"purchase_date_pst"`
	}

	// The OriginalPurchaseDate type indicates the beginning of the subscription period
	OriginalPurchaseDate struct {
		OriginalPurchaseDate    string `json:"original_purchase_date"`
		OriginalPurchaseDateMS  string `json:"original_purchase_date_ms"`
		OriginalPurchaseDatePST string `json:"original_purchase_date_pst"`
	}

	// The ExpiresDate type indicates the expiration date for the subscription
	ExpiresDate struct {
		ExpiresDate             string `json:"expires_date,omitempty"`
		ExpiresDateMS           string `json:"expires_date_ms,omitempty"`
		ExpiresDatePST          string `json:"expires_date_pst,omitempty"`
		ExpiresDateFormatted    string `json:"expires_date_formatted,omitempty"`
		ExpiresDateFormattedPST string `json:"expires_date_formatted_pst,omitempty"`
	}

	// The CancellationDate type indicates the time and date of the cancellation by Apple customer support
	CancellationDate struct {
		CancellationDate    string `json:"cancellation_date,omitempty"`
		CancellationDateMS  string `json:"cancellation_date_ms,omitempty"`
		CancellationDatePST string `json:"cancellation_date_pst,omitempty"`
	}

	// The GracePeriodDate type indicates the grace period date for the subscription
	GracePeriodDate struct {
		GracePeriodDate    string `json:"grace_period_expires_date,omitempty"`
		GracePeriodDateMS  string `json:"grace_period_expires_date_ms,omitempty"`
		GracePeriodDatePST string `json:"grace_period_expires_date_pst,omitempty"`
	}

	// The InApp type has the receipt attributes
	InApp struct {
		Quantity                    string `json:"quantity"`
		ProductID                   string `json:"product_id"`
		TransactionID               string `json:"transaction_id"`
		OriginalTransactionID       string `json:"original_transaction_id"`
		WebOrderLineItemID          string `json:"web_order_line_item_id,omitempty"`
		PromotionalOfferID          string `json:"promotional_offer_id"`
		SubscriptionGroupIdentifier string `json:"subscription_group_identifier"`

		IsTrialPeriod        string `json:"is_trial_period"`
		IsInIntroOfferPeriod string `json:"is_in_intro_offer_period,omitempty"`
		IsUpgraded           string `json:"is_upgraded,omitempty"`

		ExpiresDate

		PurchaseDate
		OriginalPurchaseDate

		CancellationDate
		CancellationReason string `json:"cancellation_reason,omitempty"`
	}

	// The Receipt type has whole data of receipt
	Receipt struct {
		ReceiptType                string        `json:"receipt_type"`
		AdamID                     int64         `json:"adam_id"`
		AppItemID                  numericString `json:"app_item_id"`
		BundleID                   string        `json:"bundle_id"`
		ApplicationVersion         string        `json:"application_version"`
		DownloadID                 int64         `json:"download_id"`
		VersionExternalIdentifier  numericString `json:"version_external_identifier"`
		OriginalApplicationVersion string        `json:"original_application_version"`
		InApp                      []InApp       `json:"in_app"`
		ReceiptCreationDate
		RequestDate
		OriginalPurchaseDate
	}

	// A pending renewal may refer to a renewal that is scheduled in the future or a renewal that failed in the past for some reason.
	PendingRenewalInfo struct {
		SubscriptionExpirationIntent   string `json:"expiration_intent"`
		SubscriptionAutoRenewProductID string `json:"auto_renew_product_id"`
		SubscriptionRetryFlag          string `json:"is_in_billing_retry_period"`
		SubscriptionAutoRenewStatus    string `json:"auto_renew_status"`
		SubscriptionPriceConsentStatus string `json:"price_consent_status"`
		ProductID                      string `json:"product_id"`
		OriginalTransactionID          string `json:"original_transaction_id"`

		GracePeriodDate
	}

	// The IAPResponse type has the response properties
	// We defined each field by the current IAP response, but some fields are not mentioned
	// in the following Apple's document;
	// https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
	// If you get other types or fields from the IAP response, you should use the struct you defined.
	IAPResponse struct {
		Status             int                  `json:"status"`
		Environment        Environment          `json:"environment"`
		Receipt            Receipt              `json:"receipt"`
		LatestReceiptInfo  []InApp              `json:"latest_receipt_info,omitempty"`
		LatestReceipt      string               `json:"latest_receipt,omitempty"`
		PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info,omitempty"`
		IsRetryable        bool                 `json:"is-retryable,omitempty"`
	}

	// The HttpStatusResponse struct contains the status code returned by the store
	// Used as a workaround to detect when to hit the production appstore or sandbox appstore regardless of receipt type
	StatusResponse struct {
		Status int `json:"status"`
	}

	// IAPResponseForIOS6 is iOS 6 style receipt schema.
	IAPResponseForIOS6 struct {
		AutoRenewProductID       string         `json:"auto_renew_product_id"`
		AutoRenewStatus          int            `json:"auto_renew_status"`
		CancellationReason       string         `json:"cancellation_reason,omitempty"`
		ExpirationIntent         string         `json:"expiration_intent,omitempty"`
		IsInBillingRetryPeriod   string         `json:"is_in_billing_retry_period,omitempty"`
		Receipt                  ReceiptForIOS6 `json:"receipt"`
		LatestExpiredReceiptInfo ReceiptForIOS6 `json:"latest_expired_receipt_info"`
		LatestReceipt            string         `json:"latest_receipt"`
		LatestReceiptInfo        ReceiptForIOS6 `json:"latest_receipt_info"`
		Status                   int            `json:"status"`
	}

	ReceiptForIOS6 struct {
		AppItemID numericString `json:"app_item_id"`
		BID       string        `json:"bid"`
		BVRS      string        `json:"bvrs"`
		CancellationDate
		ExpiresDate
		IsTrialPeriod        string `json:"is_trial_period"`
		IsInIntroOfferPeriod string `json:"is_in_intro_offer_period"`
		ItemID               string `json:"item_id"`
		ProductID            string `json:"product_id"`
		PurchaseDate
		OriginalTransactionID string `json:"original_transaction_id"`
		OriginalPurchaseDate
		Quantity                  string        `json:"quantity"`
		TransactionID             string        `json:"transaction_id"`
		UniqueIdentifier          string        `json:"unique_identifier"`
		UniqueVendorIdentifier    string        `json:"unique_vendor_identifier"`
		VersionExternalIdentifier numericString `json:"version_external_identifier,omitempty"`
		WebOrderLineItemID        string        `json:"web_order_line_item_id"`
	}
)
<|endoftext|>


<commit_before>//
//
//
package muta

import (
	"errors"
	"fmt"
	"reflect"
)

type TaskHandler func()

var DefaultTasker *Tasker = NewTasker()

func Task(name string, deps []string, h TaskHandler) error {
	return DefaultTasker.Task(name, deps, h)
}

func Run() {
	DefaultTasker.Run()
}

func NewTasker() *Tasker {
	return &Tasker{
		Tasks: make(map[string]*TaskerTask),
	}
}

type Tasker struct {
	Tasks map[string]*TaskerTask
}

func (tr *Tasker) Task(n string, args ...interface{}) error {
	ds := []string{}
	var h TaskHandler
	for _, arg := range args {
		v := reflect.ValueOf(arg)
		switch v.Type().String() {
		case "string":
			ds = append(ds, v.String())
		case "[]string":
			ds = append(ds, v.Interface().([]string)...)
		case "func()":
			h = v.Interface().(func())
			// Break on the first func found
			break
		default:
			return errors.New(fmt.Sprintf(
				"unsupported task argument type '%s'", v.Type().String(),
			))
		}
	}
	return tr.TaskStrict(n, ds, h)
}

func (tr *Tasker) TaskStrict(n string, ds []string, h TaskHandler) error {
	if tr.Tasks[n] != nil {
		return errors.New("Task already exists")
	}

	tr.Tasks[n] = &TaskerTask{
		Name:         n,
		Dependencies: ds,
		Handler:      h,
	}
	return nil
}

func (tr *Tasker) Run() error {
	return tr.RunTask("default")
}

func (tr *Tasker) RunTask(tn string) error {
	t := tr.Tasks[tn]
	if t == nil {
		return errors.New(fmt.Sprintf("Task \"%s\" does not exist.", tn))
	}

	if t.Dependencies != nil {
		for _, d := range t.Dependencies {
			tr.RunTask(d)
		}
	}

	if t.Handler != nil {
		t.Handler()
	}
	return nil
}

type TaskerTask struct {
	Name         string
	Dependencies []string
	Handler      TaskHandler
}
<commit_msg>Made the default Task() func use the reflected task<commit_after>//
//
//
package muta

import (
	"errors"
	"fmt"
	"reflect"
)

type TaskHandler func()

var DefaultTasker *Tasker = NewTasker()

func Task(name string, args ...interface{}) error {
	return DefaultTasker.Task(name, args...)
}

func Run() {
	DefaultTasker.Run()
}

func NewTasker() *Tasker {
	return &Tasker{
		Tasks: make(map[string]*TaskerTask),
	}
}

type Tasker struct {
	Tasks map[string]*TaskerTask
}

func (tr *Tasker) Task(n string, args ...interface{}) error {
	ds := []string{}
	var h TaskHandler
	for _, arg := range args {
		v := reflect.ValueOf(arg)
		switch v.Type().String() {
		case "string":
			ds = append(ds, v.String())
		case "[]string":
			ds = append(ds, v.Interface().([]string)...)
		case "func()":
			h = v.Interface().(func())
			// Break on the first func found
			break
		default:
			return errors.New(fmt.Sprintf(
				"unsupported task argument type '%s'", v.Type().String(),
			))
		}
	}
	return tr.TaskStrict(n, ds, h)
}

func (tr *Tasker) TaskStrict(n string, ds []string, h TaskHandler) error {
	if tr.Tasks[n] != nil {
		return errors.New("Task already exists")
	}

	tr.Tasks[n] = &TaskerTask{
		Name:         n,
		Dependencies: ds,
		Handler:      h,
	}
	return nil
}

func (tr *Tasker) Run() error {
	return tr.RunTask("default")
}

func (tr *Tasker) RunTask(tn string) error {
	t := tr.Tasks[tn]
	if t == nil {
		return errors.New(fmt.Sprintf("Task \"%s\" does not exist.", tn))
	}

	if t.Dependencies != nil {
		for _, d := range t.Dependencies {
			tr.RunTask(d)
		}
	}

	if t.Handler != nil {
		t.Handler()
	}
	return nil
}

type TaskerTask struct {
	Name         string
	Dependencies []string
	Handler      TaskHandler
}
<|endoftext|>


<commit_before>package nats_emitter

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/metric"
	"github.com/cloudfoundry/gibson"
	"github.com/cloudfoundry/gunk/diegonats"
	"github.com/pivotal-golang/lager"
)

type NATSEmitterInterface interface {
	Emit(messagesToEmit routing_table.MessagesToEmit, registrationCounter, unregistrationCounter *metric.Counter) error
}

type NATSEmitter struct {
	natsClient diegonats.NATSClient
	logger     lager.Logger
}

func New(natsClient diegonats.NATSClient, logger lager.Logger) *NATSEmitter {
	return &NATSEmitter{
		natsClient: natsClient,
		logger:     logger.Session("nats-emitter"),
	}
}

func (n *NATSEmitter) Emit(messagesToEmit routing_table.MessagesToEmit, registrationCounter, unregistrationCounter *metric.Counter) error {
	errors := make(chan error)
	for _, message := range messagesToEmit.RegistrationMessages {
		go n.emit("router.register", message, errors)
	}
	for _, message := range messagesToEmit.UnregistrationMessages {
		go n.emit("router.unregister", message, errors)
	}

	updateCounter(registrationCounter, messagesToEmit.RegistrationMessages)
	updateCounter(unregistrationCounter, messagesToEmit.UnregistrationMessages)

	var finalError error
	for i := 0; i < len(messagesToEmit.RegistrationMessages)+len(messagesToEmit.UnregistrationMessages); i++ {
		err := <-errors
		if err != nil && finalError == nil {
			finalError = err
		}
	}
	return finalError
}

func (n *NATSEmitter) emit(subject string, message gibson.RegistryMessage, errors chan<- error) {
	var err error
	defer func() {
		errors <- err
	}()

	n.logger.Info("emit", lager.Data{
		"subject": subject,
		"message": message,
	})

	payload, err := json.Marshal(message)
	if err != nil {
		n.logger.Error("failed-to-marshal", err, lager.Data{
			"message": message,
			"subject": subject,
		})
		return
	}

	err = n.natsClient.Publish(subject, payload)
	if err != nil {
		n.logger.Error("failed-to-publish", err, lager.Data{
			"message": message,
			"subject": subject,
		})
		return
	}
}

func updateCounter(counter *metric.Counter, messages []gibson.RegistryMessage) {
	if counter != nil {
		count := 0
		for _, message := range messages {
			count += len(message.URIs)
		}
		counter.Add(uint64(count))
	}
}
<commit_msg>Change emit log level from Info to Debug<commit_after>package nats_emitter

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/metric"
	"github.com/cloudfoundry/gibson"
	"github.com/cloudfoundry/gunk/diegonats"
	"github.com/pivotal-golang/lager"
)

type NATSEmitterInterface interface {
	Emit(messagesToEmit routing_table.MessagesToEmit, registrationCounter, unregistrationCounter *metric.Counter) error
}

type NATSEmitter struct {
	natsClient diegonats.NATSClient
	logger     lager.Logger
}

func New(natsClient diegonats.NATSClient, logger lager.Logger) *NATSEmitter {
	return &NATSEmitter{
		natsClient: natsClient,
		logger:     logger.Session("nats-emitter"),
	}
}

func (n *NATSEmitter) Emit(messagesToEmit routing_table.MessagesToEmit, registrationCounter, unregistrationCounter *metric.Counter) error {
	errors := make(chan error)
	for _, message := range messagesToEmit.RegistrationMessages {
		go n.emit("router.register", message, errors)
	}
	for _, message := range messagesToEmit.UnregistrationMessages {
		go n.emit("router.unregister", message, errors)
	}

	updateCounter(registrationCounter, messagesToEmit.RegistrationMessages)
	updateCounter(unregistrationCounter, messagesToEmit.UnregistrationMessages)

	var finalError error
	for i := 0; i < len(messagesToEmit.RegistrationMessages)+len(messagesToEmit.UnregistrationMessages); i++ {
		err := <-errors
		if err != nil && finalError == nil {
			finalError = err
		}
	}
	return finalError
}

func (n *NATSEmitter) emit(subject string, message gibson.RegistryMessage, errors chan<- error) {
	var err error
	defer func() {
		errors <- err
	}()

	n.logger.Debug("emit", lager.Data{
		"subject": subject,
		"message": message,
	})

	payload, err := json.Marshal(message)
	if err != nil {
		n.logger.Error("failed-to-marshal", err, lager.Data{
			"message": message,
			"subject": subject,
		})
		return
	}

	err = n.natsClient.Publish(subject, payload)
	if err != nil {
		n.logger.Error("failed-to-publish", err, lager.Data{
			"message": message,
			"subject": subject,
		})
		return
	}
}

func updateCounter(counter *metric.Counter, messages []gibson.RegistryMessage) {
	if counter != nil {
		count := 0
		for _, message := range messages {
			count += len(message.URIs)
		}
		counter.Add(uint64(count))
	}
}
<|endoftext|>


<commit_before>package bitswap

import (
	"bytes"
	"sync"
	"testing"
	"time"

	detectrace "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-detect-race"
	travis "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"

	blocks "github.com/ipfs/go-ipfs/blocks"
	blockstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil"
	key "github.com/ipfs/go-ipfs/blocks/key"
	tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet"
	mockrouting "github.com/ipfs/go-ipfs/routing/mock"
	delay "github.com/ipfs/go-ipfs/thirdparty/delay"
	p2ptestutil "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/test/util"
)

// FIXME the tests are really sensitive to the network delay. fix them to work
// well under varying conditions
const kNetworkDelay = 0 * time.Millisecond

func TestClose(t *testing.T) {
	vnet := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sesgen := NewTestSessionGenerator(vnet)
	defer sesgen.Close()
	bgen := blocksutil.NewBlockGenerator()

	block := bgen.Next()
	bitswap := sesgen.Next()

	bitswap.Exchange.Close()
	bitswap.Exchange.GetBlock(context.Background(), block.Key())
}

func TestProviderForKeyButNetworkCannotFind(t *testing.T) { // TODO revisit this

	rs := mockrouting.NewServer()
	net := tn.VirtualNetwork(rs, delay.Fixed(kNetworkDelay))
	g := NewTestSessionGenerator(net)
	defer g.Close()

	block := blocks.NewBlock([]byte("block"))
	pinfo := p2ptestutil.RandTestBogusIdentityOrFatal(t)
	rs.Client(pinfo).Provide(context.Background(), block.Key()) // but not on network

	solo := g.Next()
	defer solo.Exchange.Close()

	ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
	defer cancel()
	_, err := solo.Exchange.GetBlock(ctx, block.Key())

	if err != context.DeadlineExceeded {
		t.Fatal("Expected DeadlineExceeded error")
	}
}

func TestGetBlockFromPeerAfterPeerAnnounces(t *testing.T) {

	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	block := blocks.NewBlock([]byte("block"))
	g := NewTestSessionGenerator(net)
	defer g.Close()

	peers := g.Instances(2)
	hasBlock := peers[0]
	defer hasBlock.Exchange.Close()

	if err := hasBlock.Exchange.HasBlock(block); err != nil {
		t.Fatal(err)
	}

	wantsBlock := peers[1]
	defer wantsBlock.Exchange.Close()

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	received, err := wantsBlock.Exchange.GetBlock(ctx, block.Key())
	if err != nil {
		t.Log(err)
		t.Fatal("Expected to succeed")
	}

	if !bytes.Equal(block.Data(), received.Data()) {
		t.Fatal("Data doesn't match")
	}
}

func TestLargeSwarm(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	numInstances := 100
	numBlocks := 2
	if detectrace.WithRace() {
		// when running with the race detector, 500 instances launches
		// well over 8k goroutines. This hits a race detector limit.
		numInstances = 100
	} else if travis.IsRunning() {
		numInstances = 200
	} else {
		t.Parallel()
	}
	PerformDistributionTest(t, numInstances, numBlocks)
}

func TestLargeFile(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	if !travis.IsRunning() {
		t.Parallel()
	}

	numInstances := 10
	numBlocks := 100
	PerformDistributionTest(t, numInstances, numBlocks)
}

func TestLargeFileNoRebroadcast(t *testing.T) {
	rbd := rebroadcastDelay.Get()
	rebroadcastDelay.Set(time.Hour * 24 * 365 * 10) // ten years should be long enough
	if testing.Short() {
		t.SkipNow()
	}
	numInstances := 10
	numBlocks := 100
	PerformDistributionTest(t, numInstances, numBlocks)
	rebroadcastDelay.Set(rbd)
}

func TestLargeFileTwoPeers(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	numInstances := 2
	numBlocks := 100
	PerformDistributionTest(t, numInstances, numBlocks)
}

func PerformDistributionTest(t *testing.T, numInstances, numBlocks int) {
	ctx := context.Background()
	if testing.Short() {
		t.SkipNow()
	}
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	instances := sg.Instances(numInstances)
	blocks := bg.Blocks(numBlocks)

	t.Log("Give the blocks to the first instance")

	nump := len(instances) - 1
	// assert we're properly connected
	for _, inst := range instances {
		peers := inst.Exchange.wm.ConnectedPeers()
		for i := 0; i < 10 && len(peers) != nump; i++ {
			time.Sleep(time.Millisecond * 50)
			peers = inst.Exchange.wm.ConnectedPeers()
		}
		if len(peers) != nump {
			t.Fatal("not enough peers connected to instance")
		}
	}

	var blkeys []key.Key
	first := instances[0]
	for _, b := range blocks {
		blkeys = append(blkeys, b.Key())
		first.Exchange.HasBlock(b)
	}

	t.Log("Distribute!")

	wg := sync.WaitGroup{}
	errs := make(chan error)

	for _, inst := range instances[1:] {
		wg.Add(1)
		go func(inst Instance) {
			defer wg.Done()
			outch, err := inst.Exchange.GetBlocks(ctx, blkeys)
			if err != nil {
				errs <- err
			}
			for _ = range outch {
			}
		}(inst)
	}

	go func() {
		wg.Wait()
		close(errs)
	}()

	for err := range errs {
		if err != nil {
			t.Fatal(err)
		}
	}

	t.Log("Verify!")

	for _, inst := range instances {
		for _, b := range blocks {
			if _, err := inst.Blockstore().Get(b.Key()); err != nil {
				t.Fatal(err)
			}
		}
	}
}

func getOrFail(bitswap Instance, b blocks.Block, t *testing.T, wg *sync.WaitGroup) {
	if _, err := bitswap.Blockstore().Get(b.Key()); err != nil {
		_, err := bitswap.Exchange.GetBlock(context.Background(), b.Key())
		if err != nil {
			t.Fatal(err)
		}
	}
	wg.Done()
}

// TODO simplify this test. get to the _essence_!
func TestSendToWantingPeer(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	prev := rebroadcastDelay.Set(time.Second / 2)
	defer func() { rebroadcastDelay.Set(prev) }()

	peers := sg.Instances(2)
	peerA := peers[0]
	peerB := peers[1]

	t.Logf("Session %v\n", peerA.Peer)
	t.Logf("Session %v\n", peerB.Peer)

	waitTime := time.Second * 5

	alpha := bg.Next()
	// peerA requests and waits for block alpha
	ctx, cancel := context.WithTimeout(context.Background(), waitTime)
	defer cancel()
	alphaPromise, err := peerA.Exchange.GetBlocks(ctx, []key.Key{alpha.Key()})
	if err != nil {
		t.Fatal(err)
	}

	// peerB announces to the network that he has block alpha
	err = peerB.Exchange.HasBlock(alpha)
	if err != nil {
		t.Fatal(err)
	}

	// At some point, peerA should get alpha (or timeout)
	blkrecvd, ok := <-alphaPromise
	if !ok {
		t.Fatal("context timed out and broke promise channel!")
	}

	if blkrecvd.Key() != alpha.Key() {
		t.Fatal("Wrong block!")
	}

}

func TestEmptyKey(t *testing.T) {
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bs := sg.Instances(1)[0].Exchange

	_, err := bs.GetBlock(context.Background(), key.Key(""))
	if err != blockstore.ErrNotFound {
		t.Error("empty str key should return ErrNotFound")
	}
}

func TestBasicBitswap(t *testing.T) {
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	t.Log("Test a one node trying to get one block from another")

	instances := sg.Instances(2)
	blocks := bg.Blocks(1)
	err := instances[0].Exchange.HasBlock(blocks[0])
	if err != nil {
		t.Fatal(err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()
	blk, err := instances[1].Exchange.GetBlock(ctx, blocks[0].Key())
	if err != nil {
		t.Fatal(err)
	}

	t.Log(blk)
	for _, inst := range instances {
		err := inst.Exchange.Close()
		if err != nil {
			t.Fatal(err)
		}
	}
}

func TestDoubleGet(t *testing.T) {
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	t.Log("Test a one node trying to get one block from another")

	instances := sg.Instances(2)
	blocks := bg.Blocks(1)

	ctx1, cancel1 := context.WithCancel(context.Background())

	blkch1, err := instances[1].Exchange.GetBlocks(ctx1, []key.Key{blocks[0].Key()})
	if err != nil {
		t.Fatal(err)
	}

	ctx2, cancel2 := context.WithCancel(context.Background())
	defer cancel2()

	blkch2, err := instances[1].Exchange.GetBlocks(ctx2, []key.Key{blocks[0].Key()})
	if err != nil {
		t.Fatal(err)
	}

	// ensure both requests make it into the wantlist at the same time
	time.Sleep(time.Millisecond * 100)
	cancel1()

	_, ok := <-blkch1
	if ok {
		t.Fatal("expected channel to be closed")
	}

	err = instances[0].Exchange.HasBlock(blocks[0])
	if err != nil {
		t.Fatal(err)
	}

	blk, ok := <-blkch2
	if !ok {
		t.Fatal("expected to get the block here")
	}
	t.Log(blk)

	for _, inst := range instances {
		err := inst.Exchange.Close()
		if err != nil {
			t.Fatal(err)
		}
	}
}
<commit_msg>bitswap: add better tests around wantlist clearing<commit_after>package bitswap

import (
	"bytes"
	"sync"
	"testing"
	"time"

	detectrace "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-detect-race"
	travis "github.com/ipfs/go-ipfs/thirdparty/testutil/ci/travis"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"

	blocks "github.com/ipfs/go-ipfs/blocks"
	blockstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	blocksutil "github.com/ipfs/go-ipfs/blocks/blocksutil"
	key "github.com/ipfs/go-ipfs/blocks/key"
	tn "github.com/ipfs/go-ipfs/exchange/bitswap/testnet"
	mockrouting "github.com/ipfs/go-ipfs/routing/mock"
	delay "github.com/ipfs/go-ipfs/thirdparty/delay"
	p2ptestutil "gx/ipfs/QmVCe3SNMjkcPgnpFhZs719dheq6xE7gJwjzV7aWcUM4Ms/go-libp2p/p2p/test/util"
)

// FIXME the tests are really sensitive to the network delay. fix them to work
// well under varying conditions
const kNetworkDelay = 0 * time.Millisecond

func TestClose(t *testing.T) {
	vnet := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sesgen := NewTestSessionGenerator(vnet)
	defer sesgen.Close()
	bgen := blocksutil.NewBlockGenerator()

	block := bgen.Next()
	bitswap := sesgen.Next()

	bitswap.Exchange.Close()
	bitswap.Exchange.GetBlock(context.Background(), block.Key())
}

func TestProviderForKeyButNetworkCannotFind(t *testing.T) { // TODO revisit this

	rs := mockrouting.NewServer()
	net := tn.VirtualNetwork(rs, delay.Fixed(kNetworkDelay))
	g := NewTestSessionGenerator(net)
	defer g.Close()

	block := blocks.NewBlock([]byte("block"))
	pinfo := p2ptestutil.RandTestBogusIdentityOrFatal(t)
	rs.Client(pinfo).Provide(context.Background(), block.Key()) // but not on network

	solo := g.Next()
	defer solo.Exchange.Close()

	ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
	defer cancel()
	_, err := solo.Exchange.GetBlock(ctx, block.Key())

	if err != context.DeadlineExceeded {
		t.Fatal("Expected DeadlineExceeded error")
	}
}

func TestGetBlockFromPeerAfterPeerAnnounces(t *testing.T) {

	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	block := blocks.NewBlock([]byte("block"))
	g := NewTestSessionGenerator(net)
	defer g.Close()

	peers := g.Instances(2)
	hasBlock := peers[0]
	defer hasBlock.Exchange.Close()

	if err := hasBlock.Exchange.HasBlock(block); err != nil {
		t.Fatal(err)
	}

	wantsBlock := peers[1]
	defer wantsBlock.Exchange.Close()

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	received, err := wantsBlock.Exchange.GetBlock(ctx, block.Key())
	if err != nil {
		t.Log(err)
		t.Fatal("Expected to succeed")
	}

	if !bytes.Equal(block.Data(), received.Data()) {
		t.Fatal("Data doesn't match")
	}
}

func TestLargeSwarm(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	numInstances := 100
	numBlocks := 2
	if detectrace.WithRace() {
		// when running with the race detector, 500 instances launches
		// well over 8k goroutines. This hits a race detector limit.
		numInstances = 100
	} else if travis.IsRunning() {
		numInstances = 200
	} else {
		t.Parallel()
	}
	PerformDistributionTest(t, numInstances, numBlocks)
}

func TestLargeFile(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	if !travis.IsRunning() {
		t.Parallel()
	}

	numInstances := 10
	numBlocks := 100
	PerformDistributionTest(t, numInstances, numBlocks)
}

func TestLargeFileNoRebroadcast(t *testing.T) {
	rbd := rebroadcastDelay.Get()
	rebroadcastDelay.Set(time.Hour * 24 * 365 * 10) // ten years should be long enough
	if testing.Short() {
		t.SkipNow()
	}
	numInstances := 10
	numBlocks := 100
	PerformDistributionTest(t, numInstances, numBlocks)
	rebroadcastDelay.Set(rbd)
}

func TestLargeFileTwoPeers(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	numInstances := 2
	numBlocks := 100
	PerformDistributionTest(t, numInstances, numBlocks)
}

func PerformDistributionTest(t *testing.T, numInstances, numBlocks int) {
	ctx := context.Background()
	if testing.Short() {
		t.SkipNow()
	}
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	instances := sg.Instances(numInstances)
	blocks := bg.Blocks(numBlocks)

	t.Log("Give the blocks to the first instance")

	nump := len(instances) - 1
	// assert we're properly connected
	for _, inst := range instances {
		peers := inst.Exchange.wm.ConnectedPeers()
		for i := 0; i < 10 && len(peers) != nump; i++ {
			time.Sleep(time.Millisecond * 50)
			peers = inst.Exchange.wm.ConnectedPeers()
		}
		if len(peers) != nump {
			t.Fatal("not enough peers connected to instance")
		}
	}

	var blkeys []key.Key
	first := instances[0]
	for _, b := range blocks {
		blkeys = append(blkeys, b.Key())
		first.Exchange.HasBlock(b)
	}

	t.Log("Distribute!")

	wg := sync.WaitGroup{}
	errs := make(chan error)

	for _, inst := range instances[1:] {
		wg.Add(1)
		go func(inst Instance) {
			defer wg.Done()
			outch, err := inst.Exchange.GetBlocks(ctx, blkeys)
			if err != nil {
				errs <- err
			}
			for _ = range outch {
			}
		}(inst)
	}

	go func() {
		wg.Wait()
		close(errs)
	}()

	for err := range errs {
		if err != nil {
			t.Fatal(err)
		}
	}

	t.Log("Verify!")

	for _, inst := range instances {
		for _, b := range blocks {
			if _, err := inst.Blockstore().Get(b.Key()); err != nil {
				t.Fatal(err)
			}
		}
	}
}

func getOrFail(bitswap Instance, b blocks.Block, t *testing.T, wg *sync.WaitGroup) {
	if _, err := bitswap.Blockstore().Get(b.Key()); err != nil {
		_, err := bitswap.Exchange.GetBlock(context.Background(), b.Key())
		if err != nil {
			t.Fatal(err)
		}
	}
	wg.Done()
}

// TODO simplify this test. get to the _essence_!
func TestSendToWantingPeer(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	prev := rebroadcastDelay.Set(time.Second / 2)
	defer func() { rebroadcastDelay.Set(prev) }()

	peers := sg.Instances(2)
	peerA := peers[0]
	peerB := peers[1]

	t.Logf("Session %v\n", peerA.Peer)
	t.Logf("Session %v\n", peerB.Peer)

	waitTime := time.Second * 5

	alpha := bg.Next()
	// peerA requests and waits for block alpha
	ctx, cancel := context.WithTimeout(context.Background(), waitTime)
	defer cancel()
	alphaPromise, err := peerA.Exchange.GetBlocks(ctx, []key.Key{alpha.Key()})
	if err != nil {
		t.Fatal(err)
	}

	// peerB announces to the network that he has block alpha
	err = peerB.Exchange.HasBlock(alpha)
	if err != nil {
		t.Fatal(err)
	}

	// At some point, peerA should get alpha (or timeout)
	blkrecvd, ok := <-alphaPromise
	if !ok {
		t.Fatal("context timed out and broke promise channel!")
	}

	if blkrecvd.Key() != alpha.Key() {
		t.Fatal("Wrong block!")
	}

}

func TestEmptyKey(t *testing.T) {
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bs := sg.Instances(1)[0].Exchange

	_, err := bs.GetBlock(context.Background(), key.Key(""))
	if err != blockstore.ErrNotFound {
		t.Error("empty str key should return ErrNotFound")
	}
}

func TestBasicBitswap(t *testing.T) {
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	t.Log("Test a one node trying to get one block from another")

	instances := sg.Instances(2)
	blocks := bg.Blocks(1)
	err := instances[0].Exchange.HasBlock(blocks[0])
	if err != nil {
		t.Fatal(err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()
	blk, err := instances[1].Exchange.GetBlock(ctx, blocks[0].Key())
	if err != nil {
		t.Fatal(err)
	}

	t.Log(blk)
	for _, inst := range instances {
		err := inst.Exchange.Close()
		if err != nil {
			t.Fatal(err)
		}
	}
}

func TestDoubleGet(t *testing.T) {
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	t.Log("Test a one node trying to get one block from another")

	instances := sg.Instances(2)
	blocks := bg.Blocks(1)

	ctx1, cancel1 := context.WithCancel(context.Background())
	blkch1, err := instances[1].Exchange.GetBlocks(ctx1, []key.Key{blocks[0].Key()})
	if err != nil {
		t.Fatal(err)
	}

	ctx2, cancel2 := context.WithCancel(context.Background())
	defer cancel2()

	blkch2, err := instances[1].Exchange.GetBlocks(ctx2, []key.Key{blocks[0].Key()})
	if err != nil {
		t.Fatal(err)
	}

	// ensure both requests make it into the wantlist at the same time
	time.Sleep(time.Millisecond * 100)
	cancel1()

	_, ok := <-blkch1
	if ok {
		t.Fatal("expected channel to be closed")
	}

	err = instances[0].Exchange.HasBlock(blocks[0])
	if err != nil {
		t.Fatal(err)
	}

	select {
	case blk, ok := <-blkch2:
		if !ok {
			t.Fatal("expected to get the block here")
		}
		t.Log(blk)
	case <-time.After(time.Second * 5):
		t.Fatal("timed out waiting on block")
	}

	for _, inst := range instances {
		err := inst.Exchange.Close()
		if err != nil {
			t.Fatal(err)
		}
	}
}

func TestWantlistCleanup(t *testing.T) {
	net := tn.VirtualNetwork(mockrouting.NewServer(), delay.Fixed(kNetworkDelay))
	sg := NewTestSessionGenerator(net)
	defer sg.Close()
	bg := blocksutil.NewBlockGenerator()

	instances := sg.Instances(1)[0]
	bswap := instances.Exchange
	blocks := bg.Blocks(20)

	var keys []key.Key
	for _, b := range blocks {
		keys = append(keys, b.Key())
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()
	_, err := bswap.GetBlock(ctx, keys[0])
	if err != context.DeadlineExceeded {
		t.Fatal("shouldnt have fetched any blocks")
	}

	time.Sleep(time.Millisecond * 50)

	if len(bswap.GetWantlist()) > 0 {
		t.Fatal("should not have anyting in wantlist")
	}

	ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond*50)
	defer cancel()
	_, err = bswap.GetBlocks(ctx, keys[:10])
	if err != nil {
		t.Fatal(err)
	}

	<-ctx.Done()
	time.Sleep(time.Millisecond * 50)

	if len(bswap.GetWantlist()) > 0 {
		t.Fatal("should not have anyting in wantlist")
	}

	_, err = bswap.GetBlocks(context.Background(), keys[:1])
	if err != nil {
		t.Fatal(err)
	}

	ctx, cancel = context.WithCancel(context.Background())
	_, err = bswap.GetBlocks(ctx, keys[10:])
	if err != nil {
		t.Fatal(err)
	}

	time.Sleep(time.Millisecond * 50)
	if len(bswap.GetWantlist()) != 11 {
		t.Fatal("should have 11 keys in wantlist")
	}

	cancel()
	time.Sleep(time.Millisecond * 50)
	if !(len(bswap.GetWantlist()) == 1 && bswap.GetWantlist()[0] == keys[0]) {
		t.Fatal("should only have keys[0] in wantlist")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/hanjm/log"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
	//"syscall"
	"net/url"
)

type TasksManager struct {
	tasks               []Task
	ConnectionsManger   *ConnectionsManger
	downloadDir         string
	limitByteSize       int64
	limitTimeout        time.Duration
	PushTasksUpdateChan chan struct{}
}

func NewTasksManager(downloadDir string, limitByteSize int64, limitTimeout time.Duration) *TasksManager {
	return &TasksManager{
		tasks:               make([]Task, 0, 64),
		ConnectionsManger:   NewConnectionsManger(),
		PushTasksUpdateChan: make(chan struct{}, 1),
		downloadDir:         downloadDir,
		limitByteSize:       limitByteSize,
		limitTimeout:        limitTimeout,
	}
}

func (m *TasksManager) WebSocketHandler(w http.ResponseWriter, r *http.Request) {
	var upgrader = websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Errorf("websocket upgrader.Upgrade error:%s", err)
		return
	}
	log.Infof("new connection from %s, number of active connections %d", conn.RemoteAddr(), len(m.ConnectionsManger.conns)+1)
	m.ConnectionsManger.Add(conn)
	// 首次连接,推送文件信息
	m.PushTasksUpdateChan <- struct{}{}
}

func (m *TasksManager) TaskHandler(w http.ResponseWriter, r *http.Request) {
	// filename有特殊字符如&时无法正常通过r.URL.Query()获取
	filename := strings.TrimPrefix(r.URL.RawQuery, "filename=")
	filename, err := url.QueryUnescape(filename)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("param filename is invalid:" + r.URL.RawQuery))
		return
	}
	filename = strings.Replace(filename, "/", "", -1)
	switch r.Method {
	case http.MethodGet:
		log.Infof("[TaskHandler]download %s", filename)
		// 下载文件
		if filename == "" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("param filename is empty"))
			return
		}
		http.Redirect(w, r, m.downloadDir+filename, http.StatusTemporaryRedirect)
	case http.MethodPost:
		// 新建任务
		sourceURL := strings.TrimSpace(r.PostFormValue("url"))
		log.Infof("[TaskHandler]create task:%s", sourceURL)
		if sourceURL == "" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("param url is empty"))
			return
		}
		// check total size
		filesSize := m.ListFiles()
		if filesSize > m.limitByteSize {
			w.WriteHeader(http.StatusServiceUnavailable)
			w.Write([]byte(fmt.Sprintf("There are too many files in server, please delete some files, FilesSize:%s", getHumanSizeString(filesSize))))
			return
		}
		task, err := NewDownloadTask(sourceURL)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(err.Error()))
			return
		}
		m.AddTask(task)
		go func(m *TasksManager) {
			defer func() {
				if rec := recover(); rec != nil {
					log.Errorf("download worker panic:%s", rec)
				}
			}()
			err := task.Download(m.downloadDir, m.limitByteSize, m.limitTimeout)
			if err != nil {
				log.Errorf("task download error:%s, task name:%s", err, task.FileName())
			}
			m.PushTasksUpdateChan <- struct{}{}
		}(m)
		// 添加任务后,推送文件信息
		w.WriteHeader(http.StatusCreated)
		w.Write([]byte("CREATE OK"))
		m.PushTasksUpdateChan <- struct{}{}
		return
	case http.MethodDelete:
		log.Infof("[TaskHandler]delete %s", filename)
		// 删除文件
		defer func() {
			m.PushTasksUpdateChan <- struct{}{}
		}()
		if filename == "" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("param filename is empty"))
			return
		}
		// 正在下载不能删
		if task := m.GetTask(filename); task != nil && task.IsCompleted() {
			w.WriteHeader(http.StatusBadRequest)
			log.Infof("[TaskHandler]delete fail,task is downloading %s", filename)
			w.Write([]byte("task is downloading"))
			return
		}
		err := m.RemoveTask(filename)
		if err != nil {
			log.Errorf("delete '%s' error:%s", filename, err)
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(fmt.Sprintf("delete error:%s", err)))
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("DELETE OK"))
		log.Infof("[TaskHandler]delete ok, %s", filename)
		m.PushTasksUpdateChan <- struct{}{}
	default:
		w.WriteHeader(http.StatusBadRequest)
	}
}

// HasDownloadingTask 检查是否有正在下载的任务
func (m *TasksManager) HasDownloadingTask() bool {
	for _, v := range m.tasks {
		if !v.IsCompleted() {
			return true
		}
	}
	return false
}

func (m *TasksManager) GetTasks() []Task {
	return m.tasks
}

func (m *TasksManager) GetTask(filename string) Task {
	for _, v := range m.tasks {
		if v.FileName() == filename {
			return v
		}
	}
	return nil
}

func (m *TasksManager) AddTask(t Task) {
	m.tasks = append(m.tasks, t)
}

func (m *TasksManager) RemoveTask(filename string) error {
	temp := make([]Task, 0, len(m.tasks))
	for _, v := range m.tasks {
		if v.FileName() != filename {
			temp = append(temp, v)
		}
	}
	m.tasks = temp
	err := os.RemoveAll(m.downloadDir + "/" + filename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	return nil
}

// backup and restore
const backupFilename = "tasks.json"

func (m *TasksManager) BackupToJSON() error {
	data, err := json.Marshal(m.tasks)
	if err != nil {
		return fmt.Errorf("json.Marshal error:%s", err)
	}
	os.Remove(backupFilename)
	fp, err := os.Create(backupFilename)
	if err != nil {
		return fmt.Errorf("os.Create error:%s", err)
	}
	defer fp.Close()
	defer fp.Sync()
	_, err = fp.Write(data)
	if err != nil {
		return fmt.Errorf("fp.Write error:%s", err)
	}
	return nil
}

func (m *TasksManager) RestoreFromJSON() error {
	fileData, err := ioutil.ReadFile(backupFilename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("ReadFile error:%s", err)
	}
	var httpTasks []*HTTPTask
	err = json.Unmarshal(fileData, &httpTasks)
	if err != nil {
		return fmt.Errorf("json.Unmarshal error:%s", err)
	}
	for _, ht := range httpTasks {
		// 删除不存在的
		if _, err := os.Stat(fmt.Sprintf("%s/%s", m.downloadDir, ht.TaskInfo.FileName)); err != nil && os.IsNotExist(err) {
			continue
		}
		switch ht.TaskType {
		case DownloadTaskTypeHTTP:
			m.AddTask(ht)
		case DownloadTaskTypeMagnet:
			mt := &MagnetTask{
				TaskType: DownloadTaskTypeMagnet,
				TaskInfo: ht.TaskInfo,
			}
			m.AddTask(mt)
		}
	}
	return nil
}

// 如果有未完成的, 继续下载
func (m *TasksManager) ReDownloadUncompleted() {
	for _, task := range m.tasks {
		if !task.IsCompleted() {
			log.Infof("ReDownloadUncompleted task:%s", task.FileName())
			go func(m *TasksManager) {
				defer func() {
					if rec := recover(); rec != nil {
						log.Errorf("download worker panic:%s", rec)
					}
				}()
				os.Remove(m.downloadDir + "/" + task.FileName())
				err := task.Download(m.downloadDir, m.limitByteSize, m.limitTimeout)
				if err != nil {
					log.Errorf("task download error:%s, task name:%s", err, task.FileName())
				}
				m.PushTasksUpdateChan <- struct{}{}
			}(m)
		}
	}
}
func (m *TasksManager) ListFiles() (fileTotalSize int64) {
	files, _ := ioutil.ReadDir(m.downloadDir)
	for _, file := range files {
		filename := file.Name()
		if strings.HasSuffix(filename, ".torrent") ||
			strings.HasSuffix(filename, ".aria2") {
			continue
		}
		task := m.GetTask(filename)
		if task == nil {
			//rebuild new local file
			fileSize := file.Size()
			newLocalTask := NewHTTPTask("Local")
			newLocalTask.TaskInfo.FileName = filename
			newLocalTask.TaskInfo.Size = fileSize
			newLocalTask.TaskInfo.ContentLength = fileSize
			// todo: syscall.Stat_t is different between linux and macos
			//if fs, ok := file.Sys().(syscall.Stat_t); ok {
			//	newLocalTask.TaskInfo.StartTime = time.Unix(fs.Ctimespec.Sec, fs.Ctimespec.Nsec)
			//	if delta := fs.Mtimespec.Sec - fs.Ctimespec.Sec; delta > 0 {
			//		newLocalTask.TaskInfo.Duration = delta
			//		newLocalTask.Speed = fileSize / delta
			//	}
			//} else {
			//	newLocalTask.TaskInfo.StartTime = file.ModTime()
			//}
			newLocalTask.TaskInfo.StartTime = file.ModTime()
			newLocalTask.TaskInfo.IsCompleted = true
			m.AddTask(newLocalTask)
			fileTotalSize += file.Size()
		}
	}
	return fileTotalSize
}

func (m *TasksManager) PushTasksUpdateWorker() {
	defer func() {
		if rec := recover(); rec != nil {
			log.Errorf("pushTasksUpdateWorker panic:%s", rec)
		}
	}()
	go func() {
		defer func() {
			if rec := recover(); rec != nil {
				log.Errorf("pushTasksUpdateWorker panic:%s", rec)
			}
		}()
		for {
			select {
			case <-m.PushTasksUpdateChan:
				log.Debugf("m.PushTasksUpdateChan received")
				m.ConnectionsManger.Broadcast(m.GetTasks())
			}
		}
	}()
	for {
		ticker := time.NewTicker(time.Second)
		select {
		case <-ticker.C:
			if m.HasDownloadingTask() && m.ConnectionsManger.Count() > 0 {
				// 当有文件在下载且有连接时, 推送下载任务更新信息
				m.PushTasksUpdateChan <- struct{}{}
			}
		}
	}
}
<commit_msg>fix delete task<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/hanjm/log"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
	//"syscall"
	"net/url"
)

type TasksManager struct {
	tasks               []Task
	ConnectionsManger   *ConnectionsManger
	downloadDir         string
	limitByteSize       int64
	limitTimeout        time.Duration
	PushTasksUpdateChan chan struct{}
}

func NewTasksManager(downloadDir string, limitByteSize int64, limitTimeout time.Duration) *TasksManager {
	return &TasksManager{
		tasks:               make([]Task, 0, 64),
		ConnectionsManger:   NewConnectionsManger(),
		PushTasksUpdateChan: make(chan struct{}, 1),
		downloadDir:         downloadDir,
		limitByteSize:       limitByteSize,
		limitTimeout:        limitTimeout,
	}
}

func (m *TasksManager) WebSocketHandler(w http.ResponseWriter, r *http.Request) {
	var upgrader = websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Errorf("websocket upgrader.Upgrade error:%s", err)
		return
	}
	log.Infof("new connection from %s, number of active connections %d", conn.RemoteAddr(), len(m.ConnectionsManger.conns)+1)
	m.ConnectionsManger.Add(conn)
	// 首次连接,推送文件信息
	m.PushTasksUpdateChan <- struct{}{}
}

func (m *TasksManager) TaskHandler(w http.ResponseWriter, r *http.Request) {
	// filename有特殊字符如&时无法正常通过r.URL.Query()获取
	filename := strings.TrimPrefix(r.URL.RawQuery, "filename=")
	filename, err := url.QueryUnescape(filename)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("param filename is invalid:" + r.URL.RawQuery))
		return
	}
	filename = strings.Replace(filename, "/", "", -1)
	switch r.Method {
	case http.MethodGet:
		log.Infof("[TaskHandler]download %s", filename)
		// 下载文件
		if filename == "" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("param filename is empty"))
			return
		}
		http.Redirect(w, r, m.downloadDir+filename, http.StatusTemporaryRedirect)
	case http.MethodPost:
		// 新建任务
		sourceURL := strings.TrimSpace(r.PostFormValue("url"))
		log.Infof("[TaskHandler]create task:%s", sourceURL)
		if sourceURL == "" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("param url is empty"))
			return
		}
		// check total size
		filesSize := m.ListFiles()
		if filesSize > m.limitByteSize {
			w.WriteHeader(http.StatusServiceUnavailable)
			w.Write([]byte(fmt.Sprintf("There are too many files in server, please delete some files, FilesSize:%s", getHumanSizeString(filesSize))))
			return
		}
		task, err := NewDownloadTask(sourceURL)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(err.Error()))
			return
		}
		m.AddTask(task)
		go func(m *TasksManager) {
			defer func() {
				if rec := recover(); rec != nil {
					log.Errorf("download worker panic:%s", rec)
				}
			}()
			err := task.Download(m.downloadDir, m.limitByteSize, m.limitTimeout)
			if err != nil {
				log.Errorf("task download error:%s, task name:%s", err, task.FileName())
			}
			m.PushTasksUpdateChan <- struct{}{}
		}(m)
		// 添加任务后,推送文件信息
		w.WriteHeader(http.StatusCreated)
		w.Write([]byte("CREATE OK"))
		m.PushTasksUpdateChan <- struct{}{}
		return
	case http.MethodDelete:
		log.Infof("[TaskHandler]delete %s", filename)
		// 删除文件
		defer func() {
			m.PushTasksUpdateChan <- struct{}{}
		}()
		if filename == "" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("param filename is empty"))
			return
		}
		// 正在下载不能删
		if task := m.GetTask(filename); task != nil && !task.IsCompleted() {
			w.WriteHeader(http.StatusBadRequest)
			log.Infof("[TaskHandler]delete fail,task is downloading %s", filename)
			w.Write([]byte("task is downloading"))
			return
		}
		err := m.RemoveTask(filename)
		if err != nil {
			log.Errorf("delete '%s' error:%s", filename, err)
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(fmt.Sprintf("delete error:%s", err)))
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("DELETE OK"))
		log.Infof("[TaskHandler]delete ok, %s", filename)
		m.PushTasksUpdateChan <- struct{}{}
	default:
		w.WriteHeader(http.StatusBadRequest)
	}
}

// HasDownloadingTask 检查是否有正在下载的任务
func (m *TasksManager) HasDownloadingTask() bool {
	for _, v := range m.tasks {
		if !v.IsCompleted() {
			return true
		}
	}
	return false
}

func (m *TasksManager) GetTasks() []Task {
	return m.tasks
}

func (m *TasksManager) GetTask(filename string) Task {
	for _, v := range m.tasks {
		if v.FileName() == filename {
			return v
		}
	}
	return nil
}

func (m *TasksManager) AddTask(t Task) {
	m.tasks = append(m.tasks, t)
}

func (m *TasksManager) RemoveTask(filename string) error {
	temp := make([]Task, 0, len(m.tasks))
	for _, v := range m.tasks {
		if v.FileName() != filename {
			temp = append(temp, v)
		}
	}
	m.tasks = temp
	err := os.RemoveAll(m.downloadDir + "/" + filename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	return nil
}

// backup and restore
const backupFilename = "tasks.json"

func (m *TasksManager) BackupToJSON() error {
	data, err := json.Marshal(m.tasks)
	if err != nil {
		return fmt.Errorf("json.Marshal error:%s", err)
	}
	os.Remove(backupFilename)
	fp, err := os.Create(backupFilename)
	if err != nil {
		return fmt.Errorf("os.Create error:%s", err)
	}
	defer fp.Close()
	defer fp.Sync()
	_, err = fp.Write(data)
	if err != nil {
		return fmt.Errorf("fp.Write error:%s", err)
	}
	return nil
}

func (m *TasksManager) RestoreFromJSON() error {
	fileData, err := ioutil.ReadFile(backupFilename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("ReadFile error:%s", err)
	}
	var httpTasks []*HTTPTask
	err = json.Unmarshal(fileData, &httpTasks)
	if err != nil {
		return fmt.Errorf("json.Unmarshal error:%s", err)
	}
	for _, ht := range httpTasks {
		// 删除不存在的
		if _, err := os.Stat(fmt.Sprintf("%s/%s", m.downloadDir, ht.TaskInfo.FileName)); err != nil && os.IsNotExist(err) {
			continue
		}
		switch ht.TaskType {
		case DownloadTaskTypeHTTP:
			m.AddTask(ht)
		case DownloadTaskTypeMagnet:
			mt := &MagnetTask{
				TaskType: DownloadTaskTypeMagnet,
				TaskInfo: ht.TaskInfo,
			}
			m.AddTask(mt)
		}
	}
	return nil
}

// 如果有未完成的, 继续下载
func (m *TasksManager) ReDownloadUncompleted() {
	for _, task := range m.tasks {
		if !task.IsCompleted() {
			log.Infof("ReDownloadUncompleted task:%s", task.FileName())
			go func(m *TasksManager) {
				defer func() {
					if rec := recover(); rec != nil {
						log.Errorf("download worker panic:%s", rec)
					}
				}()
				os.Remove(m.downloadDir + "/" + task.FileName())
				err := task.Download(m.downloadDir, m.limitByteSize, m.limitTimeout)
				if err != nil {
					log.Errorf("task download error:%s, task name:%s", err, task.FileName())
				}
				m.PushTasksUpdateChan <- struct{}{}
			}(m)
		}
	}
}
func (m *TasksManager) ListFiles() (fileTotalSize int64) {
	files, _ := ioutil.ReadDir(m.downloadDir)
	for _, file := range files {
		filename := file.Name()
		if strings.HasSuffix(filename, ".torrent") ||
			strings.HasSuffix(filename, ".aria2") {
			continue
		}
		task := m.GetTask(filename)
		if task == nil {
			//rebuild new local file
			fileSize := file.Size()
			newLocalTask := NewHTTPTask("Local")
			newLocalTask.TaskInfo.FileName = filename
			newLocalTask.TaskInfo.Size = fileSize
			newLocalTask.TaskInfo.ContentLength = fileSize
			// todo: syscall.Stat_t is different between linux and macos
			//if fs, ok := file.Sys().(syscall.Stat_t); ok {
			//	newLocalTask.TaskInfo.StartTime = time.Unix(fs.Ctimespec.Sec, fs.Ctimespec.Nsec)
			//	if delta := fs.Mtimespec.Sec - fs.Ctimespec.Sec; delta > 0 {
			//		newLocalTask.TaskInfo.Duration = delta
			//		newLocalTask.Speed = fileSize / delta
			//	}
			//} else {
			//	newLocalTask.TaskInfo.StartTime = file.ModTime()
			//}
			newLocalTask.TaskInfo.StartTime = file.ModTime()
			newLocalTask.TaskInfo.IsCompleted = true
			m.AddTask(newLocalTask)
			fileTotalSize += file.Size()
		}
	}
	return fileTotalSize
}

func (m *TasksManager) PushTasksUpdateWorker() {
	defer func() {
		if rec := recover(); rec != nil {
			log.Errorf("pushTasksUpdateWorker panic:%s", rec)
		}
	}()
	go func() {
		defer func() {
			if rec := recover(); rec != nil {
				log.Errorf("pushTasksUpdateWorker panic:%s", rec)
			}
		}()
		for {
			select {
			case <-m.PushTasksUpdateChan:
				log.Debugf("m.PushTasksUpdateChan received")
				m.ConnectionsManger.Broadcast(m.GetTasks())
			}
		}
	}()
	for {
		ticker := time.NewTicker(time.Second)
		select {
		case <-ticker.C:
			if m.HasDownloadingTask() && m.ConnectionsManger.Count() > 0 {
				// 当有文件在下载且有连接时, 推送下载任务更新信息
				m.PushTasksUpdateChan <- struct{}{}
			}
		}
	}
}
<|endoftext|>