<commit_before>package rabbitmq

import "github.com/streadway/amqp"

type Producer struct {
	// Base struct for Producer
	*RabbitMQ

	// A notifiyng channel for publishings
	done chan error

	// Current producer connection settings
	session Session
}

type PublishingOptions struct {
	// The key that when publishing a message to a exchange/queue will be only delivered to
	// given routing key listeners
	RoutingKey string

	// Publishing tag
	Tag string

	// Queue should be on the server/broker
	Mandatory bool

	// Consumer should be bound to server
	Immediate bool
}

// NewProducer is a constructor function for producer creation
// Accepts Exchange, Queue, PublishingOptions. On the other hand
// we are not declaring our topology on both the publisher and consumer
// to be able to change the settings only in one place.
// We can declare those settings on both place to ensure they are same. But this
// package will not support it.
func (r *RabbitMQ) NewProducer(e Exchange, q Queue, po PublishingOptions) (*Producer, error) {
	rmq, err := r.Connect(po.Tag)
	if err != nil {
		return nil, err
	}

	return &Producer{
		RabbitMQ: rmq,
		session: Session{
			Exchange:          e,
			Queue:             q,
			PublishingOptions: po,
		},
	}, nil
}

// Publish sends a Publishing from the client to an exchange on the server.
func (p *Producer) Publish(publishing amqp.Publishing) error {
	e := p.session.Exchange
	q := p.session.Queue
	po := p.session.PublishingOptions

	routingKey := po.RoutingKey
	// if exchange name is empty, this means we are gonna publish
	// this mesage to a queue, every queue has a binding to default exchange
	if e.Name == "" {
		routingKey = q.Name
	}

	err := p.channel.Publish(
		e.Name,       // publish to an exchange(it can be default exchange)
		routingKey,   // routing to 0 or more queues
		po.Mandatory, // mandatory, if no queue than err
		po.Immediate, // immediate, if no consumer than err
		publishing,
		// amqp.Publishing {
		//        // Application or exchange specific fields,
		//        // the headers exchange will inspect this field.
		//        Headers Table

		//        // Properties
		//        ContentType     string    // MIME content type
		//        ContentEncoding string    // MIME content encoding
		//        DeliveryMode    uint8     // Transient (0 or 1) or Persistent (2)
		//        Priority        uint8     // 0 to 9
		//        CorrelationId   string    // correlation identifier
		//        ReplyTo         string    // address to to reply to (ex: RPC)
		//        Expiration      string    // message expiration spec
		//        MessageId       string    // message identifier
		//        Timestamp       time.Time // message timestamp
		//        Type            string    // message type name
		//        UserId          string    // creating user id - ex: "guest"
		//        AppId           string    // creating application id

		//        // The application specific payload of the message
		//        Body []byte
		// }
	)

	return err
}

// NotifyReturn captures a message when a Publishing is unable to be
// delivered either due to the `mandatory` flag set
// and no route found, or `immediate` flag set and no free consumer.
func (p *Producer) NotifyReturn(notifier func(message amqp.Return)) {
	go func() {
		for res := range p.channel.NotifyReturn(make(chan amqp.Return)) {
			notifier(res)
		}
	}()

}

// Shutdown gracefully closes all connections
func (p *Producer) Shutdown() error {
	err := shutdown(p.conn, p.channel, p.tag)

	// Since publishing is asynchronous this can happen
	// instantly without waiting for a done message.
	defer p.RabbitMQ.log.Info("Producer shutdown OK")
	return err
}

// RegisterSignalHandler watchs for interrupt signals
// and gracefully closes consumer
func (p *Producer) RegisterSignalHandler() {
	registerSignalHandler(p)
}
<commit_msg>koding/rabbitmq: producer shutdown should only close the underlying channel, make sure to use already established connection<commit_after>package rabbitmq

import "github.com/streadway/amqp"

type Producer struct {
	// Base struct for Producer
	*RabbitMQ

	// The communication channel over connection
	channel *amqp.Channel

	// A notifiyng channel for publishings
	done chan error

	// Current producer connection settings
	session Session
}

type PublishingOptions struct {
	// The key that when publishing a message to a exchange/queue will be only delivered to
	// given routing key listeners
	RoutingKey string

	// Publishing tag
	Tag string

	// Queue should be on the server/broker
	Mandatory bool

	// Consumer should be bound to server
	Immediate bool
}

// NewProducer is a constructor function for producer creation
// Accepts Exchange, Queue, PublishingOptions. On the other hand
// we are not declaring our topology on both the publisher and consumer
// to be able to change the settings only in one place.
// We can declare those settings on both place to ensure they are same. But this
// package will not support it.
func (r *RabbitMQ) NewProducer(e Exchange, q Queue, po PublishingOptions) (*Producer, error) {
	rmq, err := r.Connect()
	if err != nil {
		return nil, err
	}

	// getting a channel
	channel, err := r.conn.Channel()
	if err != nil {
		return nil, err
	}

	return &Producer{
		RabbitMQ: rmq,
		channel:  channel,
		session: Session{
			Exchange:          e,
			Queue:             q,
			PublishingOptions: po,
		},
	}, nil
}

// Publish sends a Publishing from the client to an exchange on the server.
func (p *Producer) Publish(publishing amqp.Publishing) error {
	e := p.session.Exchange
	q := p.session.Queue
	po := p.session.PublishingOptions

	routingKey := po.RoutingKey
	// if exchange name is empty, this means we are gonna publish
	// this mesage to a queue, every queue has a binding to default exchange
	if e.Name == "" {
		routingKey = q.Name
	}

	err := p.channel.Publish(
		e.Name,       // publish to an exchange(it can be default exchange)
		routingKey,   // routing to 0 or more queues
		po.Mandatory, // mandatory, if no queue than err
		po.Immediate, // immediate, if no consumer than err
		publishing,
		// amqp.Publishing {
		//        // Application or exchange specific fields,
		//        // the headers exchange will inspect this field.
		//        Headers Table

		//        // Properties
		//        ContentType     string    // MIME content type
		//        ContentEncoding string    // MIME content encoding
		//        DeliveryMode    uint8     // Transient (0 or 1) or Persistent (2)
		//        Priority        uint8     // 0 to 9
		//        CorrelationId   string    // correlation identifier
		//        ReplyTo         string    // address to to reply to (ex: RPC)
		//        Expiration      string    // message expiration spec
		//        MessageId       string    // message identifier
		//        Timestamp       time.Time // message timestamp
		//        Type            string    // message type name
		//        UserId          string    // creating user id - ex: "guest"
		//        AppId           string    // creating application id

		//        // The application specific payload of the message
		//        Body []byte
		// }
	)

	return err
}

// NotifyReturn captures a message when a Publishing is unable to be
// delivered either due to the `mandatory` flag set
// and no route found, or `immediate` flag set and no free consumer.
func (p *Producer) NotifyReturn(notifier func(message amqp.Return)) {
	go func() {
		for res := range p.channel.NotifyReturn(make(chan amqp.Return)) {
			notifier(res)
		}
	}()

}

// Shutdown gracefully closes all connections
func (p *Producer) Shutdown() error {
	co := p.session.ConsumerOptions
	if err := shutdownChannel(p.channel, co.Tag); err != nil {
		return err
	}

	// Since publishing is asynchronous this can happen
	// instantly without waiting for a done message.
	defer p.RabbitMQ.log.Info("Producer shutdown OK")
	return nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"socialapi/request"
	"time"

	"github.com/koding/bongo"
)

type ChannelMessageList struct {
	// unique identifier of the channel message list
	Id int64 `json:"id,string"`

	// Id of the channel
	ChannelId int64 `json:"channelId,string"     sql:"NOT NULL"`

	// Id of the message
	MessageId int64 `json:"messageId,string"     sql:"NOT NULL"`

	// holds troll, unsafe, etc
	MetaBits MetaBits `json:"metaBits"`

	// Addition date of the message to the channel
	// this date will be update whever message added/removed/re-added to the channel
	AddedAt time.Time `json:"addedAt"            sql:"NOT NULL"`

	// Update time of the message/list
	RevisedAt time.Time `json:"revisedAt"        sql:"NOT NULL"`

	// Deletion date of the channel
	DeletedAt time.Time `json:"deletedAt"`
}

func (c *ChannelMessageList) UnreadCount(cp *ChannelParticipant) (int, error) {
	if cp.ChannelId == 0 {
		return 0, ErrChannelIdIsNotSet
	}

	if cp.AccountId == 0 {
		return 0, ErrAccountIdIsNotSet
	}

	if cp.LastSeenAt.IsZero() {
		return 0, ErrLastSeenAtIsNotSet
	}

	// checks if channel participant is a troll, if so we show all messages
	isExempt, err := cp.isExempt()
	if err != nil {
		return 0, err
	}

	query := "channel_id = ? and added_at > ?"

	var metaBits MetaBits
	if isExempt {
		query += " and meta_bits >= ?"
	} else {
		query += " and meta_bits = ?"
	}

	return bongo.B.Count(c,
		query,
		cp.ChannelId,
		// todo change this format to get from a specific place
		cp.LastSeenAt.UTC().Format(time.RFC3339Nano),
		metaBits,
	)
}

func (c *ChannelMessageList) CreateRaw() error {
	insertSql := "INSERT INTO " +
		c.TableName() +
		` ("channel_id","message_id","added_at","revised_at") VALUES ($1,$2,$3,$4) ` +
		"RETURNING ID"

	return bongo.B.DB.CommonDB().
		QueryRow(insertSql, c.ChannelId, c.MessageId, c.AddedAt, c.RevisedAt).
		Scan(&c.Id)
}

func (c *ChannelMessageList) List(q *request.Query, populateUnreadCount bool) (*HistoryResponse, error) {
	messageList, err := c.getMessages(q)
	if err != nil {
		return nil, err
	}

	if populateUnreadCount {
		messageList = c.populateUnreadCount(messageList)
	}

	hr := NewHistoryResponse()
	hr.MessageList = messageList
	return hr, nil
}

// populateUnreadCount adds unread count into message containers
func (c *ChannelMessageList) populateUnreadCount(messageList []*ChannelMessageContainer) []*ChannelMessageContainer {
	channel := NewChannel()
	channel.Id = c.ChannelId

	for i, message := range messageList {
		cml, err := channel.FetchMessageList(message.Message.Id)
		if err != nil {
			// helper.MustGetLogger().Error(err.Error())
			continue
		}

		count, err := NewMessageReply().UnreadCount(cml.MessageId, cml.RevisedAt, cml.MetaBits.Is(Troll))
		if err != nil {
			// helper.MustGetLogger().Error(err.Error())
			continue
		}
		messageList[i].UnreadRepliesCount = count
	}

	return messageList
}

func (c *ChannelMessageList) getMessages(q *request.Query) ([]*ChannelMessageContainer, error) {
	var messages []int64

	if c.ChannelId == 0 {
		return nil, errors.New("ChannelId is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": c.ChannelId,
		},
		Pluck:      "message_id",
		Pagination: *bongo.NewPagination(q.Limit, q.Skip),
	}

	query.AddScope(SortedByAddedAt)
	query.AddScope(RemoveTrollContent(c, q.ShowExempt))

	bongoQuery := bongo.B.BuildQuery(c, query)
	if !q.From.IsZero() {
		bongoQuery = bongoQuery.Where("added_at < ?", q.From)
	}

	if err := bongo.CheckErr(
		bongoQuery.Pluck(query.Pluck, &messages),
	); err != nil {
		return nil, err
	}

	populatedChannelMessages, err := c.populateChannelMessages(messages, q)
	if err != nil {
		return nil, err
	}

	return populatedChannelMessages, nil
}

func (c *ChannelMessageList) IsInChannel(messageId, channelId int64) (bool, error) {
	if messageId == 0 || channelId == 0 {
		return false, errors.New("channelId/messageId is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
			"message_id": messageId,
		},
	}

	err := c.One(query)
	if err == nil {
		return true, nil
	}

	if err == bongo.RecordNotFound {
		return false, nil
	}

	return false, err
}

func (c *ChannelMessageList) populateChannelMessages(channelMessageIds []int64, query *request.Query) ([]*ChannelMessageContainer, error) {
	channelMessageCount := len(channelMessageIds)

	populatedChannelMessages := make([]*ChannelMessageContainer, channelMessageCount)

	if channelMessageCount == 0 {
		return populatedChannelMessages, nil
	}

	for i := 0; i < channelMessageCount; i++ {
		cm := NewChannelMessage()
		cm.Id = channelMessageIds[i]
		cmc, err := cm.BuildMessage(query)
		if err != nil {
			return nil, err
		}

		populatedChannelMessages[i] = cmc
	}

	return populatedChannelMessages, nil
}

func (c *ChannelMessageList) FetchMessageChannelIds(messageId int64) ([]int64, error) {
	var channelIds []int64

	q := &bongo.Query{
		Selector: map[string]interface{}{
			"message_id": messageId,
		},
		Pluck: "channel_id",
	}

	err := bongo.B.Some(c, &channelIds, q)
	if err != nil {
		return nil, err
	}

	return channelIds, nil
}

func (c *ChannelMessageList) FetchMessageChannels(messageId int64) ([]Channel, error) {
	var channels []Channel

	q := &bongo.Query{
		Selector: map[string]interface{}{
			"message_id": messageId,
		},
	}

	err := bongo.B.Some(c, &channels, q)
	if err != nil {
		return nil, err
	}

	return channels, nil
}

func (c *ChannelMessageList) FetchMessageIdsByChannelId(channelId int64, q *request.Query) ([]int64, error) {
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
		},
		Pluck:      "message_id",
		Pagination: *bongo.NewPagination(q.Limit, q.Skip),
		Sort: map[string]string{
			"added_at": "DESC",
		},
	}

	// remove troll content
	query.AddScope(RemoveTrollContent(c, q.ShowExempt))

	var messageIds []int64
	if err := c.Some(&messageIds, query); err != nil {
		return nil, err
	}

	if messageIds == nil {
		return make([]int64, 0), nil
	}

	return messageIds, nil
}

// seperate this fucntion into modelhelper
// as setting it to a variadic function
func (c *ChannelMessageList) DeleteMessagesBySelector(selector map[string]interface{}) error {
	var cmls []ChannelMessageList

	err := bongo.B.Some(c, &cmls, &bongo.Query{Selector: selector})
	if err != nil {
		return err
	}

	for _, cml := range cmls {
		if err := cml.Delete(); err != nil {
			return err
		}
	}
	return nil
}

func (c *ChannelMessageList) UpdateAddedAt(channelId, messageId int64) error {
	if messageId == 0 || channelId == 0 {
		return errors.New("channelId/messageId is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
			"message_id": messageId,
		},
	}

	err := c.One(query)
	if err != nil {
		return err
	}

	c.AddedAt = time.Now().UTC()
	return c.Update()
}

func (c *ChannelMessageList) MarkIfExempt() error {
	isExempt, err := c.isExempt()
	if err != nil {
		return err
	}

	if isExempt {
		c.MetaBits.Mark(Troll)
	}

	return nil
}

func (c *ChannelMessageList) isExempt() (bool, error) {
	// return early if channel is already exempt
	if c.MetaBits.Is(Troll) {
		return true, nil
	}

	if c.MessageId == 0 {
		return false, errors.New("message id is not set for exempt check")
	}

	cm := NewChannelMessage()
	cm.Id = c.MessageId

	return cm.isExempt()

}

func (c *ChannelMessageList) Count(channelId int64) (int, error) {
	if channelId == 0 {
		return 0, errors.New("channel id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
		},
	}

	query.AddScope(RemoveTrollContent(
		// dont show trolls
		c, false,
	))

	return c.CountWithQuery(query)
}

// this glance can cause problems..
func (c *ChannelMessageList) Glance() error {
	// why we are aggin one second?
	c.RevisedAt = time.Now().Add((time.Second * 1)).UTC()

	if err := c.Update(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Social: fix message channel fetching<commit_after>package models

import (
	"errors"
	"socialapi/request"
	"time"

	"github.com/koding/bongo"
)

type ChannelMessageList struct {
	// unique identifier of the channel message list
	Id int64 `json:"id,string"`

	// Id of the channel
	ChannelId int64 `json:"channelId,string"     sql:"NOT NULL"`

	// Id of the message
	MessageId int64 `json:"messageId,string"     sql:"NOT NULL"`

	// holds troll, unsafe, etc
	MetaBits MetaBits `json:"metaBits"`

	// Addition date of the message to the channel
	// this date will be update whever message added/removed/re-added to the channel
	AddedAt time.Time `json:"addedAt"            sql:"NOT NULL"`

	// Update time of the message/list
	RevisedAt time.Time `json:"revisedAt"        sql:"NOT NULL"`

	// Deletion date of the channel
	DeletedAt time.Time `json:"deletedAt"`
}

func (c *ChannelMessageList) UnreadCount(cp *ChannelParticipant) (int, error) {
	if cp.ChannelId == 0 {
		return 0, ErrChannelIdIsNotSet
	}

	if cp.AccountId == 0 {
		return 0, ErrAccountIdIsNotSet
	}

	if cp.LastSeenAt.IsZero() {
		return 0, ErrLastSeenAtIsNotSet
	}

	// checks if channel participant is a troll, if so we show all messages
	isExempt, err := cp.isExempt()
	if err != nil {
		return 0, err
	}

	query := "channel_id = ? and added_at > ?"

	var metaBits MetaBits
	if isExempt {
		query += " and meta_bits >= ?"
	} else {
		query += " and meta_bits = ?"
	}

	return bongo.B.Count(c,
		query,
		cp.ChannelId,
		// todo change this format to get from a specific place
		cp.LastSeenAt.UTC().Format(time.RFC3339Nano),
		metaBits,
	)
}

func (c *ChannelMessageList) CreateRaw() error {
	insertSql := "INSERT INTO " +
		c.TableName() +
		` ("channel_id","message_id","added_at","revised_at") VALUES ($1,$2,$3,$4) ` +
		"RETURNING ID"

	return bongo.B.DB.CommonDB().
		QueryRow(insertSql, c.ChannelId, c.MessageId, c.AddedAt, c.RevisedAt).
		Scan(&c.Id)
}

func (c *ChannelMessageList) List(q *request.Query, populateUnreadCount bool) (*HistoryResponse, error) {
	messageList, err := c.getMessages(q)
	if err != nil {
		return nil, err
	}

	if populateUnreadCount {
		messageList = c.populateUnreadCount(messageList)
	}

	hr := NewHistoryResponse()
	hr.MessageList = messageList
	return hr, nil
}

// populateUnreadCount adds unread count into message containers
func (c *ChannelMessageList) populateUnreadCount(messageList []*ChannelMessageContainer) []*ChannelMessageContainer {
	channel := NewChannel()
	channel.Id = c.ChannelId

	for i, message := range messageList {
		cml, err := channel.FetchMessageList(message.Message.Id)
		if err != nil {
			// helper.MustGetLogger().Error(err.Error())
			continue
		}

		count, err := NewMessageReply().UnreadCount(cml.MessageId, cml.RevisedAt, cml.MetaBits.Is(Troll))
		if err != nil {
			// helper.MustGetLogger().Error(err.Error())
			continue
		}
		messageList[i].UnreadRepliesCount = count
	}

	return messageList
}

func (c *ChannelMessageList) getMessages(q *request.Query) ([]*ChannelMessageContainer, error) {
	var messages []int64

	if c.ChannelId == 0 {
		return nil, errors.New("ChannelId is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": c.ChannelId,
		},
		Pluck:      "message_id",
		Pagination: *bongo.NewPagination(q.Limit, q.Skip),
	}

	query.AddScope(SortedByAddedAt)
	query.AddScope(RemoveTrollContent(c, q.ShowExempt))

	bongoQuery := bongo.B.BuildQuery(c, query)
	if !q.From.IsZero() {
		bongoQuery = bongoQuery.Where("added_at < ?", q.From)
	}

	if err := bongo.CheckErr(
		bongoQuery.Pluck(query.Pluck, &messages),
	); err != nil {
		return nil, err
	}

	populatedChannelMessages, err := c.populateChannelMessages(messages, q)
	if err != nil {
		return nil, err
	}

	return populatedChannelMessages, nil
}

func (c *ChannelMessageList) IsInChannel(messageId, channelId int64) (bool, error) {
	if messageId == 0 || channelId == 0 {
		return false, errors.New("channelId/messageId is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
			"message_id": messageId,
		},
	}

	err := c.One(query)
	if err == nil {
		return true, nil
	}

	if err == bongo.RecordNotFound {
		return false, nil
	}

	return false, err
}

func (c *ChannelMessageList) populateChannelMessages(channelMessageIds []int64, query *request.Query) ([]*ChannelMessageContainer, error) {
	channelMessageCount := len(channelMessageIds)

	populatedChannelMessages := make([]*ChannelMessageContainer, channelMessageCount)

	if channelMessageCount == 0 {
		return populatedChannelMessages, nil
	}

	for i := 0; i < channelMessageCount; i++ {
		cm := NewChannelMessage()
		cm.Id = channelMessageIds[i]
		cmc, err := cm.BuildMessage(query)
		if err != nil {
			return nil, err
		}

		populatedChannelMessages[i] = cmc
	}

	return populatedChannelMessages, nil
}

func (c *ChannelMessageList) FetchMessageChannelIds(messageId int64) ([]int64, error) {
	var channelIds []int64

	q := &bongo.Query{
		Selector: map[string]interface{}{
			"message_id": messageId,
		},
		Pluck: "channel_id",
	}

	err := bongo.B.Some(c, &channelIds, q)
	if err != nil {
		return nil, err
	}

	return channelIds, nil
}

func (c *ChannelMessageList) FetchMessageChannels(messageId int64) ([]Channel, error) {
	channelIds, err := c.FetchMessageChannelIds(messageId)
	if err != nil {
		return nil, err
	}

	return NewChannel().FetchByIds(channelIds)
}

func (c *ChannelMessageList) FetchMessageIdsByChannelId(channelId int64, q *request.Query) ([]int64, error) {
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
		},
		Pluck:      "message_id",
		Pagination: *bongo.NewPagination(q.Limit, q.Skip),
		Sort: map[string]string{
			"added_at": "DESC",
		},
	}

	// remove troll content
	query.AddScope(RemoveTrollContent(c, q.ShowExempt))

	var messageIds []int64
	if err := c.Some(&messageIds, query); err != nil {
		return nil, err
	}

	if messageIds == nil {
		return make([]int64, 0), nil
	}

	return messageIds, nil
}

// seperate this fucntion into modelhelper
// as setting it to a variadic function
func (c *ChannelMessageList) DeleteMessagesBySelector(selector map[string]interface{}) error {
	var cmls []ChannelMessageList

	err := bongo.B.Some(c, &cmls, &bongo.Query{Selector: selector})
	if err != nil {
		return err
	}

	for _, cml := range cmls {
		if err := cml.Delete(); err != nil {
			return err
		}
	}
	return nil
}

func (c *ChannelMessageList) UpdateAddedAt(channelId, messageId int64) error {
	if messageId == 0 || channelId == 0 {
		return errors.New("channelId/messageId is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
			"message_id": messageId,
		},
	}

	err := c.One(query)
	if err != nil {
		return err
	}

	c.AddedAt = time.Now().UTC()
	return c.Update()
}

func (c *ChannelMessageList) MarkIfExempt() error {
	isExempt, err := c.isExempt()
	if err != nil {
		return err
	}

	if isExempt {
		c.MetaBits.Mark(Troll)
	}

	return nil
}

func (c *ChannelMessageList) isExempt() (bool, error) {
	// return early if channel is already exempt
	if c.MetaBits.Is(Troll) {
		return true, nil
	}

	if c.MessageId == 0 {
		return false, errors.New("message id is not set for exempt check")
	}

	cm := NewChannelMessage()
	cm.Id = c.MessageId

	return cm.isExempt()

}

func (c *ChannelMessageList) Count(channelId int64) (int, error) {
	if channelId == 0 {
		return 0, errors.New("channel id is not set")
	}

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"channel_id": channelId,
		},
	}

	query.AddScope(RemoveTrollContent(
		// dont show trolls
		c, false,
	))

	return c.CountWithQuery(query)
}

// this glance can cause problems..
func (c *ChannelMessageList) Glance() error {
	// why we are aggin one second?
	c.RevisedAt = time.Now().Add((time.Second * 1)).UTC()

	if err := c.Update(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package heff

import (
	"io"
	"log"
	"net/http"
	"sync"
)

// DefaultHoneypot is an http.HandlerFunc that serves random HTML from the
// DefaultMarkovMap, 100KB at a time.
var DefaultHoneypot = NewHoneypot(DefaultMarkovMap, 100*1<<10)

// NewHoneypot creates an http.HandlerFunc from a MarkovMap
func NewHoneypot(mm MarkovMap, buffsize int) http.HandlerFunc {
	var pool sync.Pool

	getBuffer := func() []byte {
		x := pool.Get()
		if buf, ok := x.([]byte); ok {
			return buf
		}
		return make([]byte, buffsize)
	}

	putBuffer := func(buf []byte) {
		pool.Put(buf)
	}

	return func(w http.ResponseWriter, r *http.Request) {
		log.Printf("Started writing: %v", r.URL)
		buf := getBuffer()
		defer putBuffer(buf)
		io.WriteString(w, "<HTML>\n<BODY>\n")
		n, err := io.CopyBuffer(w, DefaultMarkovMap, buf)
		log.Printf("Wrote: %d (%v)", n, err)
	}
}
<commit_msg>Handler: Fix bug of using DefaultMarkovMap<commit_after>package heff

import (
	"io"
	"log"
	"net/http"
	"sync"
)

// DefaultHoneypot is an http.HandlerFunc that serves random HTML from the
// DefaultMarkovMap, 100KB at a time.
var DefaultHoneypot = NewHoneypot(DefaultMarkovMap, 100*1<<10)

// NewHoneypot creates an http.HandlerFunc from a MarkovMap
func NewHoneypot(mm MarkovMap, buffsize int) http.HandlerFunc {
	var pool sync.Pool

	getBuffer := func() []byte {
		x := pool.Get()
		if buf, ok := x.([]byte); ok {
			return buf
		}
		return make([]byte, buffsize)
	}

	putBuffer := func(buf []byte) {
		pool.Put(buf)
	}

	return func(w http.ResponseWriter, r *http.Request) {
		log.Printf("Started writing: %v", r.URL)
		buf := getBuffer()
		defer putBuffer(buf)
		io.WriteString(w, "<HTML>\n<BODY>\n")
		n, err := io.CopyBuffer(w, mm, buf)
		log.Printf("Wrote: %d (%v)", n, err)
	}
}
<|endoftext|>


<commit_before>package logrus_cloudwatchlogs

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/sirupsen/logrus"
)

type Hook struct {
	svc               *cloudwatchlogs.CloudWatchLogs
	groupName         string
	streamName        string
	nextSequenceToken *string
	m                 sync.Mutex
	ch                chan *cloudwatchlogs.InputLogEvent
	err               chan error
}

func NewHook(groupName, streamName string, cfg *aws.Config) (*Hook, error) {
	return NewBatchingHook(groupName, streamName, cfg, 0)
}

func (h *Hook) getOrCreateCloudWatchLogGroup() (*cloudwatchlogs.DescribeLogStreamsOutput, error) {
	resp, err := h.svc.DescribeLogStreams(&cloudwatchlogs.DescribeLogStreamsInput{
		LogGroupName:        aws.String(h.groupName),
		LogStreamNamePrefix: aws.String(h.streamName),
	})

	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case cloudwatchlogs.ErrCodeResourceNotFoundException:
				_, err = h.svc.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{
					LogGroupName: aws.String(h.groupName),
				})
				if err != nil {
					return nil, err
				}
				return h.getCloudWatchLogGroup()
			default:
				return nil, err
			}
		} else {
			return nil, err
		}
	}
	return resp, nil

}

func NewBatchingHook(groupName, streamName string, cfg *aws.Config, batchFrequency time.Duration) (*Hook, error) {
	h := &Hook{
		svc:        cloudwatchlogs.New(session.New(cfg)),
		groupName:  groupName,
		streamName: streamName,
	}

	resp, err := h.getCloudWatchLogGroup()
	if err != nil {
		return nil, err
	}

	// grab the next sequence token
	if len(resp.LogStreams) > 0 {
		h.nextSequenceToken = resp.LogStreams[0].UploadSequenceToken
		return h, nil
	}

	// create stream if it doesn't exist. the next sequence token will be null
	_, err = h.svc.CreateLogStream(&cloudwatchlogs.CreateLogStreamInput{
		LogGroupName:  aws.String(groupName),
		LogStreamName: aws.String(streamName),
	})
	if err != nil {
		return nil, err
	}

	if batchFrequency > 0 {
		h.ch = make(chan *cloudwatchlogs.InputLogEvent, 10000)
		go h.putBatches(time.Tick(batchFrequency))
	}

	return h, nil
}

func (h *Hook) Fire(entry *logrus.Entry) error {
	line, err := entry.String()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
		return err
	}

	switch entry.Level {
	case logrus.PanicLevel:
		fallthrough
	case logrus.FatalLevel:
		fallthrough
	case logrus.ErrorLevel:
		fallthrough
	case logrus.WarnLevel:
		fallthrough
	case logrus.InfoLevel:
		fallthrough
	case logrus.DebugLevel:
		_, err := h.Write([]byte(line))
		return err
	default:
		return nil
	}
}

func (h *Hook) putBatches(ticker <-chan time.Time) {
	var batch []*cloudwatchlogs.InputLogEvent
	for {
		select {
		case p := <-h.ch:
			batch = append(batch, p)
		case <-ticker:
			params := &cloudwatchlogs.PutLogEventsInput{
				LogEvents:     batch,
				LogGroupName:  aws.String(h.groupName),
				LogStreamName: aws.String(h.streamName),
				SequenceToken: h.nextSequenceToken,
			}
			resp, err := h.svc.PutLogEvents(params)
			if err != nil {
				h.err <- err
			} else {
				h.nextSequenceToken = resp.NextSequenceToken
				batch = nil
			}
		}
	}
}

func (h *Hook) Write(p []byte) (n int, err error) {
	event := &cloudwatchlogs.InputLogEvent{
		Message:   aws.String(string(p)),
		Timestamp: aws.Int64(int64(time.Nanosecond) * time.Now().UnixNano() / int64(time.Millisecond)),
	}

	if h.ch != nil {
		h.ch <- event
		if lastErr, ok := <-h.err; ok {
			return 0, fmt.Errorf("%v", lastErr)
		}
	}

	h.m.Lock()
	defer h.m.Unlock()

	params := &cloudwatchlogs.PutLogEventsInput{
		LogEvents:     []*cloudwatchlogs.InputLogEvent{event},
		LogGroupName:  aws.String(h.groupName),
		LogStreamName: aws.String(h.streamName),
		SequenceToken: h.nextSequenceToken,
	}
	resp, err := h.svc.PutLogEvents(params)

	if err != nil {
		//fmt.Println(reflect.TypeOf(err))
		return 0, err
	}

	h.nextSequenceToken = resp.NextSequenceToken

	return len(p), nil
}

func (h *Hook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
		logrus.WarnLevel,
		logrus.InfoLevel,
		logrus.DebugLevel,
	}
}

// WriterHook is a hook that just outputs to an io.Writer.
// This is useful because our formatter outputs the file
// and line where it was called, and the callstack for a hook
// is different from the callstack for just writing to logrus.Logger.Out.
type WriterHook struct {
	w io.Writer
}

func NewWriterHook(w io.Writer) *WriterHook {
	return &WriterHook{w: w}
}

func (h *WriterHook) Fire(entry *logrus.Entry) error {
	line, err := entry.String()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
		return err
	}

	_, err = h.w.Write([]byte(line))
	return err
}

func (h *WriterHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
		logrus.WarnLevel,
		logrus.InfoLevel,
		logrus.DebugLevel,
	}
}
<commit_msg>fixed function name typo<commit_after>package logrus_cloudwatchlogs

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/sirupsen/logrus"
)

type Hook struct {
	svc               *cloudwatchlogs.CloudWatchLogs
	groupName         string
	streamName        string
	nextSequenceToken *string
	m                 sync.Mutex
	ch                chan *cloudwatchlogs.InputLogEvent
	err               chan error
}

func NewHook(groupName, streamName string, cfg *aws.Config) (*Hook, error) {
	return NewBatchingHook(groupName, streamName, cfg, 0)
}

func (h *Hook) getOrCreateCloudWatchLogGroup() (*cloudwatchlogs.DescribeLogStreamsOutput, error) {
	resp, err := h.svc.DescribeLogStreams(&cloudwatchlogs.DescribeLogStreamsInput{
		LogGroupName:        aws.String(h.groupName),
		LogStreamNamePrefix: aws.String(h.streamName),
	})

	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case cloudwatchlogs.ErrCodeResourceNotFoundException:
				_, err = h.svc.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{
					LogGroupName: aws.String(h.groupName),
				})
				if err != nil {
					return nil, err
				}
				return h.getOrCreateCloudWatchLogGroup()
			default:
				return nil, err
			}
		} else {
			return nil, err
		}
	}
	return resp, nil

}

func NewBatchingHook(groupName, streamName string, cfg *aws.Config, batchFrequency time.Duration) (*Hook, error) {
	h := &Hook{
		svc:        cloudwatchlogs.New(session.New(cfg)),
		groupName:  groupName,
		streamName: streamName,
	}

	resp, err := h.getOrCreateCloudWatchLogGroup()
	if err != nil {
		return nil, err
	}

	// grab the next sequence token
	if len(resp.LogStreams) > 0 {
		h.nextSequenceToken = resp.LogStreams[0].UploadSequenceToken
		return h, nil
	}

	// create stream if it doesn't exist. the next sequence token will be null
	_, err = h.svc.CreateLogStream(&cloudwatchlogs.CreateLogStreamInput{
		LogGroupName:  aws.String(groupName),
		LogStreamName: aws.String(streamName),
	})
	if err != nil {
		return nil, err
	}

	if batchFrequency > 0 {
		h.ch = make(chan *cloudwatchlogs.InputLogEvent, 10000)
		go h.putBatches(time.Tick(batchFrequency))
	}

	return h, nil
}

func (h *Hook) Fire(entry *logrus.Entry) error {
	line, err := entry.String()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
		return err
	}

	switch entry.Level {
	case logrus.PanicLevel:
		fallthrough
	case logrus.FatalLevel:
		fallthrough
	case logrus.ErrorLevel:
		fallthrough
	case logrus.WarnLevel:
		fallthrough
	case logrus.InfoLevel:
		fallthrough
	case logrus.DebugLevel:
		_, err := h.Write([]byte(line))
		return err
	default:
		return nil
	}
}

func (h *Hook) putBatches(ticker <-chan time.Time) {
	var batch []*cloudwatchlogs.InputLogEvent
	for {
		select {
		case p := <-h.ch:
			batch = append(batch, p)
		case <-ticker:
			params := &cloudwatchlogs.PutLogEventsInput{
				LogEvents:     batch,
				LogGroupName:  aws.String(h.groupName),
				LogStreamName: aws.String(h.streamName),
				SequenceToken: h.nextSequenceToken,
			}
			resp, err := h.svc.PutLogEvents(params)
			if err != nil {
				h.err <- err
			} else {
				h.nextSequenceToken = resp.NextSequenceToken
				batch = nil
			}
		}
	}
}

func (h *Hook) Write(p []byte) (n int, err error) {
	event := &cloudwatchlogs.InputLogEvent{
		Message:   aws.String(string(p)),
		Timestamp: aws.Int64(int64(time.Nanosecond) * time.Now().UnixNano() / int64(time.Millisecond)),
	}

	if h.ch != nil {
		h.ch <- event
		if lastErr, ok := <-h.err; ok {
			return 0, fmt.Errorf("%v", lastErr)
		}
	}

	h.m.Lock()
	defer h.m.Unlock()

	params := &cloudwatchlogs.PutLogEventsInput{
		LogEvents:     []*cloudwatchlogs.InputLogEvent{event},
		LogGroupName:  aws.String(h.groupName),
		LogStreamName: aws.String(h.streamName),
		SequenceToken: h.nextSequenceToken,
	}
	resp, err := h.svc.PutLogEvents(params)

	if err != nil {
		//fmt.Println(reflect.TypeOf(err))
		return 0, err
	}

	h.nextSequenceToken = resp.NextSequenceToken

	return len(p), nil
}

func (h *Hook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
		logrus.WarnLevel,
		logrus.InfoLevel,
		logrus.DebugLevel,
	}
}

// WriterHook is a hook that just outputs to an io.Writer.
// This is useful because our formatter outputs the file
// and line where it was called, and the callstack for a hook
// is different from the callstack for just writing to logrus.Logger.Out.
type WriterHook struct {
	w io.Writer
}

func NewWriterHook(w io.Writer) *WriterHook {
	return &WriterHook{w: w}
}

func (h *WriterHook) Fire(entry *logrus.Entry) error {
	line, err := entry.String()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
		return err
	}

	_, err = h.w.Write([]byte(line))
	return err
}

func (h *WriterHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.PanicLevel,
		logrus.FatalLevel,
		logrus.ErrorLevel,
		logrus.WarnLevel,
		logrus.InfoLevel,
		logrus.DebugLevel,
	}
}
<|endoftext|>


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

package ssa

import (
	"bytes"
	"cmd/internal/src"
	"fmt"
	"html"
	"io"
	"os"
)

type HTMLWriter struct {
	Logger
	*os.File
}

func NewHTMLWriter(path string, logger Logger, funcname string) *HTMLWriter {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		logger.Fatalf(src.NoXPos, "%v", err)
	}
	html := HTMLWriter{File: out, Logger: logger}
	html.start(funcname)
	return &html
}

func (w *HTMLWriter) start(name string) {
	if w == nil {
		return
	}
	w.WriteString("")
	w.WriteString(`
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

`)
	// TODO: Add javascript click handlers for blocks
	// to outline that block across all phases
	w.WriteString("")
	w.WriteString("
")
	w.WriteString(html.EscapeString(name))
	w.WriteString("
")
	w.WriteString(`
help

Click on a value or block to toggle highlighting of that value/block
and its uses.  (Values and blocks are highlighted by ID, and IDs of
dead items may be reused, so not all highlights necessarily correspond
to the clicked item.)

Faded out values and blocks are dead code that has not been eliminated.

Values printed in italics have a dependency cycle.

`)
	w.WriteString("
")
	w.WriteString("
")
}

func (w *HTMLWriter) Close() {
	if w == nil {
		return
	}
	w.WriteString("
")
	w.WriteString("
")
	w.WriteString("")
	w.WriteString("")
	w.File.Close()
}

// WriteFunc writes f in a column headed by title.
func (w *HTMLWriter) WriteFunc(title string, f *Func) {
	if w == nil {
		return // avoid generating HTML just to discard it
	}
	w.WriteColumn(title, f.HTML())
	// TODO: Add visual representation of f's CFG.
}

// WriteColumn writes raw HTML in a column headed by title.
// It is intended for pre- and post-compilation log output.
func (w *HTMLWriter) WriteColumn(title string, html string) {
	if w == nil {
		return
	}
	w.WriteString("")
	w.WriteString("
" + title + "
")
	w.WriteString(html)
	w.WriteString("	")
}

func (w *HTMLWriter) Printf(msg string, v ...interface{}) {
	if _, err := fmt.Fprintf(w.File, msg, v...); err != nil {
		w.Fatalf(src.NoXPos, "%v", err)
	}
}

func (w *HTMLWriter) WriteString(s string) {
	if _, err := w.File.WriteString(s); err != nil {
		w.Fatalf(src.NoXPos, "%v", err)
	}
}

func (v *Value) HTML() string {
	// TODO: Using the value ID as the class ignores the fact
	// that value IDs get recycled and that some values
	// are transmuted into other values.
	s := v.String()
	return fmt.Sprintf("%s", s, s)
}

func (v *Value) LongHTML() string {
	// TODO: Any intra-value formatting?
	// I'm wary of adding too much visual noise,
	// but a little bit might be valuable.
	// We already have visual noise in the form of punctuation
	// maybe we could replace some of that with formatting.
	s := fmt.Sprintf("", v.String())
	s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String())
	s += " <" + html.EscapeString(v.Type.String()) + ">"
	s += html.EscapeString(v.auxString())
	for _, a := range v.Args {
		s += fmt.Sprintf(" %s", a.HTML())
	}
	r := v.Block.Func.RegAlloc
	if int(v.ID) < len(r) && r[v.ID] != nil {
		s += " : " + html.EscapeString(r[v.ID].Name())
	}
	s += ""
	return s
}

func (b *Block) HTML() string {
	// TODO: Using the value ID as the class ignores the fact
	// that value IDs get recycled and that some values
	// are transmuted into other values.
	s := html.EscapeString(b.String())
	return fmt.Sprintf("%s", s, s)
}

func (b *Block) LongHTML() string {
	// TODO: improve this for HTML?
	s := fmt.Sprintf("%s", html.EscapeString(b.String()), html.EscapeString(b.Kind.String()))
	if b.Aux != nil {
		s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux))
	}
	if b.Control != nil {
		s += fmt.Sprintf(" %s", b.Control.HTML())
	}
	if len(b.Succs) > 0 {
		s += " →" // right arrow
		for _, e := range b.Succs {
			c := e.b
			s += " " + c.HTML()
		}
	}
	switch b.Likely {
	case BranchUnlikely:
		s += " (unlikely)"
	case BranchLikely:
		s += " (likely)"
	}
	return s
}

func (f *Func) HTML() string {
	var buf bytes.Buffer
	fmt.Fprint(&buf, "")
	p := htmlFuncPrinter{w: &buf}
	fprintFunc(p, f)

	// fprintFunc(&buf, f) // TODO: HTML, not text,
 for line breaks, etc.
	fmt.Fprint(&buf, "")
	return buf.String()
}

type htmlFuncPrinter struct {
	w io.Writer
}

func (p htmlFuncPrinter) header(f *Func) {}

func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
	// TODO: Make blocks collapsable?
	var dead string
	if !reachable {
		dead = "dead-block"
	}
	fmt.Fprintf(p.w, "
", b, dead)
	fmt.Fprintf(p.w, "
%s:", b.HTML())
	if len(b.Preds) > 0 {
		io.WriteString(p.w, " ←") // left arrow
		for _, e := range b.Preds {
			pred := e.b
			fmt.Fprintf(p.w, " %s", pred.HTML())
		}
	}
	io.WriteString(p.w, "
")
	if len(b.Values) > 0 { // start list of values
		io.WriteString(p.w, "
")
		io.WriteString(p.w, "
")
	}
}

func (p htmlFuncPrinter) endBlock(b *Block) {
	if len(b.Values) > 0 { // end list of values
		io.WriteString(p.w, "
")
		io.WriteString(p.w, "
")
	}
	io.WriteString(p.w, "
")
	fmt.Fprint(p.w, b.LongHTML())
	io.WriteString(p.w, "
")
	io.WriteString(p.w, "
")
	// io.WriteString(p.w, "")
}

func (p htmlFuncPrinter) value(v *Value, live bool) {
	var dead string
	if !live {
		dead = "dead-value"
	}
	fmt.Fprintf(p.w, "
", dead)
	fmt.Fprint(p.w, v.LongHTML())
	io.WriteString(p.w, "
")
}

func (p htmlFuncPrinter) startDepCycle() {
	fmt.Fprintln(p.w, "")
}

func (p htmlFuncPrinter) endDepCycle() {
	fmt.Fprintln(p.w, "")
}

func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) {
	// TODO
}
<commit_msg>cmd/compile/internal/ssa: display NamedValues in SSA html output.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ssa

import (
	"bytes"
	"cmd/internal/src"
	"fmt"
	"html"
	"io"
	"os"
)

type HTMLWriter struct {
	Logger
	*os.File
}

func NewHTMLWriter(path string, logger Logger, funcname string) *HTMLWriter {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		logger.Fatalf(src.NoXPos, "%v", err)
	}
	html := HTMLWriter{File: out, Logger: logger}
	html.start(funcname)
	return &html
}

func (w *HTMLWriter) start(name string) {
	if w == nil {
		return
	}
	w.WriteString("")
	w.WriteString(`
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

`)
	// TODO: Add javascript click handlers for blocks
	// to outline that block across all phases
	w.WriteString("")
	w.WriteString("
")
	w.WriteString(html.EscapeString(name))
	w.WriteString("
")
	w.WriteString(`
help

Click on a value or block to toggle highlighting of that value/block
and its uses.  (Values and blocks are highlighted by ID, and IDs of
dead items may be reused, so not all highlights necessarily correspond
to the clicked item.)

Faded out values and blocks are dead code that has not been eliminated.

Values printed in italics have a dependency cycle.

`)
	w.WriteString("
")
	w.WriteString("
")
}

func (w *HTMLWriter) Close() {
	if w == nil {
		return
	}
	w.WriteString("
")
	w.WriteString("
")
	w.WriteString("")
	w.WriteString("")
	w.File.Close()
}

// WriteFunc writes f in a column headed by title.
func (w *HTMLWriter) WriteFunc(title string, f *Func) {
	if w == nil {
		return // avoid generating HTML just to discard it
	}
	w.WriteColumn(title, f.HTML())
	// TODO: Add visual representation of f's CFG.
}

// WriteColumn writes raw HTML in a column headed by title.
// It is intended for pre- and post-compilation log output.
func (w *HTMLWriter) WriteColumn(title string, html string) {
	if w == nil {
		return
	}
	w.WriteString("")
	w.WriteString("
" + title + "
")
	w.WriteString(html)
	w.WriteString("	")
}

func (w *HTMLWriter) Printf(msg string, v ...interface{}) {
	if _, err := fmt.Fprintf(w.File, msg, v...); err != nil {
		w.Fatalf(src.NoXPos, "%v", err)
	}
}

func (w *HTMLWriter) WriteString(s string) {
	if _, err := w.File.WriteString(s); err != nil {
		w.Fatalf(src.NoXPos, "%v", err)
	}
}

func (v *Value) HTML() string {
	// TODO: Using the value ID as the class ignores the fact
	// that value IDs get recycled and that some values
	// are transmuted into other values.
	s := v.String()
	return fmt.Sprintf("%s", s, s)
}

func (v *Value) LongHTML() string {
	// TODO: Any intra-value formatting?
	// I'm wary of adding too much visual noise,
	// but a little bit might be valuable.
	// We already have visual noise in the form of punctuation
	// maybe we could replace some of that with formatting.
	s := fmt.Sprintf("", v.String())
	s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String())
	s += " <" + html.EscapeString(v.Type.String()) + ">"
	s += html.EscapeString(v.auxString())
	for _, a := range v.Args {
		s += fmt.Sprintf(" %s", a.HTML())
	}
	r := v.Block.Func.RegAlloc
	if int(v.ID) < len(r) && r[v.ID] != nil {
		s += " : " + html.EscapeString(r[v.ID].Name())
	}
	s += ""
	return s
}

func (b *Block) HTML() string {
	// TODO: Using the value ID as the class ignores the fact
	// that value IDs get recycled and that some values
	// are transmuted into other values.
	s := html.EscapeString(b.String())
	return fmt.Sprintf("%s", s, s)
}

func (b *Block) LongHTML() string {
	// TODO: improve this for HTML?
	s := fmt.Sprintf("%s", html.EscapeString(b.String()), html.EscapeString(b.Kind.String()))
	if b.Aux != nil {
		s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux))
	}
	if b.Control != nil {
		s += fmt.Sprintf(" %s", b.Control.HTML())
	}
	if len(b.Succs) > 0 {
		s += " →" // right arrow
		for _, e := range b.Succs {
			c := e.b
			s += " " + c.HTML()
		}
	}
	switch b.Likely {
	case BranchUnlikely:
		s += " (unlikely)"
	case BranchLikely:
		s += " (likely)"
	}
	return s
}

func (f *Func) HTML() string {
	var buf bytes.Buffer
	fmt.Fprint(&buf, "")
	p := htmlFuncPrinter{w: &buf}
	fprintFunc(p, f)

	// fprintFunc(&buf, f) // TODO: HTML, not text,
 for line breaks, etc.
	fmt.Fprint(&buf, "")
	return buf.String()
}

type htmlFuncPrinter struct {
	w io.Writer
}

func (p htmlFuncPrinter) header(f *Func) {}

func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
	// TODO: Make blocks collapsable?
	var dead string
	if !reachable {
		dead = "dead-block"
	}
	fmt.Fprintf(p.w, "
", b, dead)
	fmt.Fprintf(p.w, "
%s:", b.HTML())
	if len(b.Preds) > 0 {
		io.WriteString(p.w, " ←") // left arrow
		for _, e := range b.Preds {
			pred := e.b
			fmt.Fprintf(p.w, " %s", pred.HTML())
		}
	}
	io.WriteString(p.w, "
")
	if len(b.Values) > 0 { // start list of values
		io.WriteString(p.w, "
")
		io.WriteString(p.w, "
")
	}
}

func (p htmlFuncPrinter) endBlock(b *Block) {
	if len(b.Values) > 0 { // end list of values
		io.WriteString(p.w, "
")
		io.WriteString(p.w, "
")
	}
	io.WriteString(p.w, "
")
	fmt.Fprint(p.w, b.LongHTML())
	io.WriteString(p.w, "
")
	io.WriteString(p.w, "
")
	// io.WriteString(p.w, "")
}

func (p htmlFuncPrinter) value(v *Value, live bool) {
	var dead string
	if !live {
		dead = "dead-value"
	}
	fmt.Fprintf(p.w, "
", dead)
	fmt.Fprint(p.w, v.LongHTML())
	io.WriteString(p.w, "
")
}

func (p htmlFuncPrinter) startDepCycle() {
	fmt.Fprintln(p.w, "")
}

func (p htmlFuncPrinter) endDepCycle() {
	fmt.Fprintln(p.w, "")
}

func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) {
	fmt.Fprintf(p.w, "
name %s: ", n.Name())
	for _, val := range vals {
		fmt.Fprintf(p.w, "%s ", val.HTML())
	}
	fmt.Fprintf(p.w, "
")
}
<|endoftext|>


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

package riscv64

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"cmd/link/internal/ld"
)

func Init() (*sys.Arch, ld.Arch) {
	arch := sys.ArchRISCV64

	theArch := ld.Arch{
		Funcalign:  funcAlign,
		Maxalign:   maxAlign,
		Minalign:   minAlign,
		Dwarfregsp: dwarfRegSP,
		Dwarfreglr: dwarfRegLR,

		Archinit:         archinit,
		Archreloc:        archreloc,
		Archrelocvariant: archrelocvariant,
		Extreloc:         extreloc,
		Elfreloc1:        elfreloc1,
		ElfrelocSize:     24,
		Elfsetupplt:      elfsetupplt,

		// TrampLimit is set such that we always run the trampoline
		// generation code. This is necessary since calls to external
		// symbols require the use of trampolines, regardless of the
		// text size.
		TrampLimit: 1,
		Trampoline: trampoline,

		Gentext:     gentext,
		GenSymsLate: genSymsLate,
		Machoreloc1: machoreloc1,

		Linuxdynld: "/lib/ld.so.1",

		Freebsddynld:   "XXX",
		Netbsddynld:    "XXX",
		Openbsddynld:   "XXX",
		Dragonflydynld: "XXX",
		Solarisdynld:   "XXX",
	}

	return arch, theArch
}

func archinit(ctxt *ld.Link) {
	switch ctxt.HeadType {
	case objabi.Hlinux:
		ld.Elfinit(ctxt)
		ld.HEADR = ld.ELFRESERVE
		if *ld.FlagTextAddr == -1 {
			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
		}
		if *ld.FlagRound == -1 {
			*ld.FlagRound = 0x10000
		}
	default:
		ld.Exitf("unknown -H option: %v", ctxt.HeadType)
	}
}
<commit_msg>cmd/link/internal/riscv64: add support for freebsd/riscv64<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package riscv64

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"cmd/link/internal/ld"
)

func Init() (*sys.Arch, ld.Arch) {
	arch := sys.ArchRISCV64

	theArch := ld.Arch{
		Funcalign:  funcAlign,
		Maxalign:   maxAlign,
		Minalign:   minAlign,
		Dwarfregsp: dwarfRegSP,
		Dwarfreglr: dwarfRegLR,

		Archinit:         archinit,
		Archreloc:        archreloc,
		Archrelocvariant: archrelocvariant,
		Extreloc:         extreloc,
		Elfreloc1:        elfreloc1,
		ElfrelocSize:     24,
		Elfsetupplt:      elfsetupplt,

		// TrampLimit is set such that we always run the trampoline
		// generation code. This is necessary since calls to external
		// symbols require the use of trampolines, regardless of the
		// text size.
		TrampLimit: 1,
		Trampoline: trampoline,

		Gentext:     gentext,
		GenSymsLate: genSymsLate,
		Machoreloc1: machoreloc1,

		Linuxdynld: "/lib/ld.so.1",

		Freebsddynld:   "/usr/libexec/ld-elf.so.1",
		Netbsddynld:    "XXX",
		Openbsddynld:   "XXX",
		Dragonflydynld: "XXX",
		Solarisdynld:   "XXX",
	}

	return arch, theArch
}

func archinit(ctxt *ld.Link) {
	switch ctxt.HeadType {
	case objabi.Hlinux, objabi.Hfreebsd:
		ld.Elfinit(ctxt)
		ld.HEADR = ld.ELFRESERVE
		if *ld.FlagTextAddr == -1 {
			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
		}
		if *ld.FlagRound == -1 {
			*ld.FlagRound = 0x10000
		}
	default:
		ld.Exitf("unknown -H option: %v", ctxt.HeadType)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/dgryski/go-tsz"
)

// Chunk is a chunk of data. not concurrency safe.
type Chunk struct {
	*tsz.Series
	T0        uint32
	LastTs    uint32 // last TS seen, not computed or anything
	NumPoints uint32
	Saved     bool
	Saving    bool
	LastWrite uint32
}

func NewChunk(t0 uint32) *Chunk {
	// we must set LastWrite here as well to make sure a new Chunk doesn't get immediately
	// garbage collected right after creating it, before we can push to it
	return &Chunk{tsz.New(t0), t0, 0, 0, false, false, uint32(time.Now().Unix())}
}

func (c *Chunk) String() string {
	return fmt.Sprintf("<chunk T0=%d, LastTs=%d, NumPoints=%d, Saved=%t>", c.T0, c.LastTs, c.NumPoints, c.Saved)

}
func (c *Chunk) Push(t uint32, v float64) error {
	if t <= c.LastTs {
		return fmt.Errorf("Point must be newer than already added points. t:%d lastTs: %d", t, c.LastTs)
	}
	c.Series.Push(t, v)
	c.NumPoints += 1
	c.LastTs = t
	c.LastWrite = uint32(time.Now().Unix())
	totalPoints <- 1
	return nil
}
<commit_msg>use the series T0 attribute<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/dgryski/go-tsz"
)

// Chunk is a chunk of data. not concurrency safe.
type Chunk struct {
	*tsz.Series
	LastTs    uint32 // last TS seen, not computed or anything
	NumPoints uint32
	Saved     bool
	Saving    bool
	LastWrite uint32
}

func NewChunk(t0 uint32) *Chunk {
	// we must set LastWrite here as well to make sure a new Chunk doesn't get immediately
	// garbage collected right after creating it, before we can push to it
	return &Chunk{tsz.New(t0), 0, 0, false, false, uint32(time.Now().Unix())}
}

func (c *Chunk) String() string {
	return fmt.Sprintf("<chunk T0=%d, LastTs=%d, NumPoints=%d, Saved=%t>", c.T0, c.LastTs, c.NumPoints, c.Saved)

}
func (c *Chunk) Push(t uint32, v float64) error {
	if t <= c.LastTs {
		return fmt.Errorf("Point must be newer than already added points. t:%d lastTs: %d", t, c.LastTs)
	}
	c.Series.Push(t, v)
	c.NumPoints += 1
	c.LastTs = t
	c.LastWrite = uint32(time.Now().Unix())
	totalPoints <- 1
	return nil
}
<|endoftext|>


<commit_before>// Go clone of http(1)
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"unicode"

	flag "launchpad.net/gnuflag"
	"launchpad.net/rjson"

	"code.google.com/p/go.net/publicsuffix"
	"github.com/juju/persistent-cookiejar"
	"gopkg.in/macaroon-bakery.v0/httpbakery"
)

var flags struct {
	json       bool
	form       bool
	headers    bool
	body       bool
	rjson      bool
	raw        bool
	noBrowser  bool
	basicAuth  string
	cookieFile string

	// TODO auth, verify, proxy, file, timeout
}

const helpMessage = `usage: http [flag...] [METHOD] URL [REQUEST_ITEM [REQUEST_ITEM...]]

  METHOD
      The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...).

      This argument can be omitted in which case HTTPie will use POST if there
      is some data to be sent, otherwise GET:

          $ http example.org               # => GET
          $ http example.org hello=world   # => POST

  URL
      The scheme defaults to 'http://' if the URL does not include one.

      You can also use a shorthand for localhost

          $ http :3000                    # => http://localhost:3000
          $ http :/foo                    # => http://localhost/foo

  REQUEST_ITEM
      Optional key-value pairs to be included in the request. The separator used
      determines the type:

      ':' HTTP headers:

          Referer:http://httpie.org  Cookie:foo=bar  User-Agent:bacon/1.0

      '==' URL parameters to be appended to the request URI:

          search==httpie

      '=' Data fields to be serialized into a JSON object (with --json, -j)
          or form data (with --form, -f):

          name=HTTPie  language=Python  description='CLI HTTP client'

      ':=' Non-string JSON data fields (only with --json, -j):

          awesome:=true  amount:=42  colors:='["red", "green", "blue"]'

      '@' Form file fields (only with --form, -f):

          cs@~/Documents/CV.pdf

      '=@' A data field like '=', but takes a file path and embeds its content:

           essay=@Documents/essay.txt

      ':=@' A raw JSON field like ':=', but takes a file path and embeds its content:

          package:=@./package.json

      You can use a backslash to escape a colliding separator in the field name:

          field-name-with\:colon=value
`

func main() {
	flag.BoolVar(&flags.json, "j", false, "serialize  data  items  as a JSON object")
	flag.BoolVar(&flags.json, "json", false, "")

	flag.BoolVar(&flags.form, "f", false, "serialize data items as form values")
	flag.BoolVar(&flags.form, "form", false, "")

	flag.BoolVar(&flags.headers, "t", false, "print only the response headers")
	flag.BoolVar(&flags.headers, "headers", false, "")

	flag.BoolVar(&flags.body, "b", false, "print only the response body")
	flag.BoolVar(&flags.body, "body", false, "")

	flag.BoolVar(&flags.noBrowser, "B", false, "do not open URLs in web browser")
	flag.BoolVar(&flags.noBrowser, "no-browser", false, "")

	flag.BoolVar(&flags.raw, "raw", false, "print response body without any JSON post-processing")

	flag.StringVar(&flags.basicAuth, "a", "", "http basic auth (username:password)")
	flag.StringVar(&flags.basicAuth, "auth", "", "")

	flag.StringVar(&flags.cookieFile, "cookiefile", filepath.Join(os.Getenv("HOME"), ".go-cookies"), "file to store persistent cookies in")

	// TODO --file (multipart upload)
	// TODO --timeout
	// TODO --proxy
	// TODO (??) --verify

	flag.Usage = func() {
		fmt.Fprint(os.Stderr, helpMessage)
		flag.PrintDefaults()
		os.Exit(2)
	}

	flag.Parse(true)
	args := flag.Args()
	if len(args) == 0 {
		flag.Usage()
	}
	req := &http.Request{
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
	}
	if isAllCaps(args[0]) {
		req.Method, args = args[0], args[1:]
	}
	if len(args) == 0 {
		flag.Usage()
	}
	u, err := url.Parse(args[0])
	if err != nil {
		fatalf("invalid URL %q: %v", args[0], err)
	}
	args = args[1:]
	req.URL = u
	req.Host = req.URL.Host

	ctxt := &context{
		useJSON:   flags.json,
		method:    "GET",
		header:    make(http.Header),
		urlValues: make(url.Values),
		form:      make(url.Values),
		jsonObj:   make(map[string]interface{}),
	}
	for _, arg := range args {
		if err := ctxt.addArg(arg); err != nil {
			fatalf("%s", err)
		}
	}
	if err := ctxt.doRequest(u, req); err != nil {
		fatalf("%s", err)
	}
}

func (ctxt *context) doRequest(u *url.URL, req *http.Request) error {
	if req.Method == "" {
		req.Method = ctxt.method
	}
	if len(ctxt.urlValues) > 0 {
		if u.RawQuery != "" {
			u.RawQuery += "&"
		}
		u.RawQuery += ctxt.urlValues.Encode()
	}
	if ctxt.useJSON {
		maybeSetContentType(req, "application/json")
	}
	var body []byte
	req.Header = ctxt.header
	if flags.basicAuth != "" {
		req.Header["Authorization"] = []string{flags.basicAuth}
	}
	switch {
	case len(ctxt.form) > 0:
		maybeSetContentType(req, "application/x-www-form-urlencoded")
		body = []byte(ctxt.form.Encode())

	case len(ctxt.jsonObj) > 0:
		data, err := json.Marshal(ctxt.jsonObj)
		if err != nil {
			return fmt.Errorf("cannot marshal JSON: %v", err)
		}
		body = data
	case req.Method != "GET" && req.Method != "HEAD":
		// No fields specified and it looks like we need a body.

		// TODO check if it's seekable or make a temp file.
		data, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return fmt.Errorf("error reading stdin: %v", err)
		}
		// TODO if we're expecting JSON, accept rjson too.
		body = data
	}
	getBody := httpbakery.SeekerBody(bytes.NewReader(body))

	jar, client, err := httpClient(flags.cookieFile)
	if err != nil {
		return fmt.Errorf("cannot make HTTP client: %v", err)
	}
	defer cookiejar.SaveToFile(jar, flags.cookieFile)
	resp, err := httpbakery.DoWithBody(client, req, getBody, visitWebPage)
	if err != nil {
		return fmt.Errorf("cannot do HTTP request: %v", err)
	}
	defer resp.Body.Close()
	isJSONResp := false
	if ctype := resp.Header.Get("Content-Type"); ctype != "" {
		mediaType, _, err := mime.ParseMediaType(ctype)
		if err != nil {
			warningf("invalid content type %q in response", ctype)
		} else {
			isJSONResp = mediaType == "application/json"
		}
	}
	if !isJSONResp {
		io.Copy(os.Stdout, resp.Body)
		return nil
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read response body: %v", err)
	}
	var indented bytes.Buffer
	if err := rjson.Indent(&indented, data, "", "\t"); err != nil {
		warningf("cannot pretty print JSON response: %v", err)
		if len(data) > 0 && data[len(data)-1] != '\n' {
			data = append(data, '\n')
		}
		os.Stdout.Write(data)
		return nil
	}
	os.Stdout.Write(indented.Bytes())
	return nil
}

func visitWebPage(url *url.URL) error {
	fmt.Printf("please visit this URL:\n%s\n", url)
	return nil
}

func httpClient(cookieFile string) (*cookiejar.Jar, *http.Client, error) {
	jar, err := cookiejar.New(&cookiejar.Options{
		PublicSuffixList: publicsuffix.List,
	})
	if err != nil {
		panic(err)
	}
	// TODO allow disabling of persistent cookies
	err = cookiejar.LoadFromFile(jar, cookieFile)
	if err != nil {
		return nil, nil, err
	}
	client := httpbakery.NewHTTPClient()
	client.Jar = jar
	return jar, client, nil
}

func maybeSetContentType(req *http.Request, t string) {
	if _, ok := req.Header["Content-Type"]; !ok {
		req.Header.Set("Content-Type", t)
	}
}

type context struct {
	useJSON bool

	method    string
	header    http.Header
	urlValues url.Values
	form      url.Values
	jsonObj   map[string]interface{}
}

var ops = map[string]func(ctxt *context, key, val string) error{
	":":  (*context).httpHeader,
	"==": (*context).urlParam,
	"=":  (*context).dataString,
	":=": (*context).jsonOther,
}

var argPat = regexp.MustCompile(`^((\\.)|[^:=@])(:|==|=|:=|@|=@|:=@)(.*)$`)

func (ctxt *context) addArg(s string) error {
	m := argPat.FindStringSubmatch(s)
	if m == nil {
		return fmt.Errorf("unrecognized key pair %q", s)
	}
	key, op, val := m[1], m[2], m[3]
	key = unquoteKey(key)
	f := ops[op]
	if f == nil {
		return fmt.Errorf("key value type %q not yet recognized", op)
	}
	return f(ctxt, key, val)
}

// key:val
func (ctxt *context) httpHeader(key, val string) error {
	ctxt.header.Add(key, val)
	return nil
}

// key==val
func (ctxt *context) urlParam(key, val string) error {
	ctxt.urlValues.Add(key, val)
	return nil
}

// key=val
func (ctxt *context) dataString(key, val string) error {
	ctxt.method = "POST"
	if ctxt.useJSON {
		ctxt.jsonObj[key] = val
	} else {
		ctxt.form.Add(key, val)
	}
	return nil
}

// key:=val
func (ctxt *context) jsonOther(key, val string) error {
	if !ctxt.useJSON {
		return fmt.Errorf("cannot specify non-string key unless --json is specified")
	}
	var m json.RawMessage
	if err := json.Unmarshal([]byte(val), &m); err != nil {
		return fmt.Errorf("invalid JSON in key %s: %v", key, err)
	}
	ctxt.jsonObj[key] = &m
	return nil
}

func unquoteKey(key string) string {
	nkey := make([]byte, 0, len(key))
	for i := 0; i < len(key); i++ {
		if i < len(key)-1 && key[i] == '\\' {
			nkey = append(nkey, key[i+1])
			i++
		} else {
			nkey = append(nkey, key[i])
		}
	}
	return string(nkey)

}

func fatalf(f string, a ...interface{}) {
	if strings.HasSuffix(f, "\n") {
		f = f[0 : len(f)-1]
	}
	fmt.Fprintf(os.Stderr, "http: %s\n", fmt.Sprintf(f, a...))
	os.Exit(1)
}

func warningf(f string, a ...interface{}) {
	if strings.HasSuffix(f, "\n") {
		f = f[0 : len(f)-1]
	}
	fmt.Fprintf(os.Stderr, "http: warning: %s\n", fmt.Sprintf(f, a...))
}

func isAllCaps(s string) bool {
	for _, r := range s {
		if !unicode.IsUpper(r) {
			return false
		}
	}
	return true
}
<commit_msg>add newline to JSON indented output<commit_after>// Go clone of http(1)
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"unicode"

	"code.google.com/p/go.net/publicsuffix"
	"github.com/juju/persistent-cookiejar"
	"gopkg.in/macaroon-bakery.v0/httpbakery"
	flag "launchpad.net/gnuflag"
	"launchpad.net/rjson"
)

var flags struct {
	json       bool
	form       bool
	headers    bool
	body       bool
	rjson      bool
	raw        bool
	noBrowser  bool
	basicAuth  string
	cookieFile string

	// TODO auth, verify, proxy, file, timeout
}

const helpMessage = `usage: http [flag...] [METHOD] URL [REQUEST_ITEM [REQUEST_ITEM...]]

  METHOD
      The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...).

      This argument can be omitted in which case HTTPie will use POST if there
      is some data to be sent, otherwise GET:

          $ http example.org               # => GET
          $ http example.org hello=world   # => POST

  URL
      The scheme defaults to 'http://' if the URL does not include one.

      You can also use a shorthand for localhost

          $ http :3000                    # => http://localhost:3000
          $ http :/foo                    # => http://localhost/foo

  REQUEST_ITEM
      Optional key-value pairs to be included in the request. The separator used
      determines the type:

      ':' HTTP headers:

          Referer:http://httpie.org  Cookie:foo=bar  User-Agent:bacon/1.0

      '==' URL parameters to be appended to the request URI:

          search==httpie

      '=' Data fields to be serialized into a JSON object (with --json, -j)
          or form data (with --form, -f):

          name=HTTPie  language=Python  description='CLI HTTP client'

      ':=' Non-string JSON data fields (only with --json, -j):

          awesome:=true  amount:=42  colors:='["red", "green", "blue"]'

      '@' Form file fields (only with --form, -f):

          cs@~/Documents/CV.pdf

      '=@' A data field like '=', but takes a file path and embeds its content:

           essay=@Documents/essay.txt

      ':=@' A raw JSON field like ':=', but takes a file path and embeds its content:

          package:=@./package.json

      You can use a backslash to escape a colliding separator in the field name:

          field-name-with\:colon=value
`

func main() {
	flag.BoolVar(&flags.json, "j", false, "serialize  data  items  as a JSON object")
	flag.BoolVar(&flags.json, "json", false, "")

	flag.BoolVar(&flags.form, "f", false, "serialize data items as form values")
	flag.BoolVar(&flags.form, "form", false, "")

	flag.BoolVar(&flags.headers, "t", false, "print only the response headers")
	flag.BoolVar(&flags.headers, "headers", false, "")

	flag.BoolVar(&flags.body, "b", false, "print only the response body")
	flag.BoolVar(&flags.body, "body", false, "")

	flag.BoolVar(&flags.noBrowser, "B", false, "do not open URLs in web browser")
	flag.BoolVar(&flags.noBrowser, "no-browser", false, "")

	flag.BoolVar(&flags.raw, "raw", false, "print response body without any JSON post-processing")

	flag.StringVar(&flags.basicAuth, "a", "", "http basic auth (username:password)")
	flag.StringVar(&flags.basicAuth, "auth", "", "")

	flag.StringVar(&flags.cookieFile, "cookiefile", filepath.Join(os.Getenv("HOME"), ".go-cookies"), "file to store persistent cookies in")

	// TODO --file (multipart upload)
	// TODO --timeout
	// TODO --proxy
	// TODO (??) --verify

	flag.Usage = func() {
		fmt.Fprint(os.Stderr, helpMessage)
		flag.PrintDefaults()
		os.Exit(2)
	}

	flag.Parse(true)
	args := flag.Args()
	if len(args) == 0 {
		flag.Usage()
	}
	req := &http.Request{
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
	}
	if isAllCaps(args[0]) {
		req.Method, args = args[0], args[1:]
	}
	if len(args) == 0 {
		flag.Usage()
	}
	u, err := url.Parse(args[0])
	if err != nil {
		fatalf("invalid URL %q: %v", args[0], err)
	}
	args = args[1:]
	req.URL = u
	req.Host = req.URL.Host

	ctxt := &context{
		useJSON:   flags.json,
		method:    "GET",
		header:    make(http.Header),
		urlValues: make(url.Values),
		form:      make(url.Values),
		jsonObj:   make(map[string]interface{}),
	}
	for _, arg := range args {
		if err := ctxt.addArg(arg); err != nil {
			fatalf("%s", err)
		}
	}
	if err := ctxt.doRequest(u, req); err != nil {
		fatalf("%s", err)
	}
}

func (ctxt *context) doRequest(u *url.URL, req *http.Request) error {
	if req.Method == "" {
		req.Method = ctxt.method
	}
	if len(ctxt.urlValues) > 0 {
		if u.RawQuery != "" {
			u.RawQuery += "&"
		}
		u.RawQuery += ctxt.urlValues.Encode()
	}
	if ctxt.useJSON {
		maybeSetContentType(req, "application/json")
	}
	var body []byte
	req.Header = ctxt.header
	if flags.basicAuth != "" {
		req.Header["Authorization"] = []string{flags.basicAuth}
	}
	switch {
	case len(ctxt.form) > 0:
		maybeSetContentType(req, "application/x-www-form-urlencoded")
		body = []byte(ctxt.form.Encode())

	case len(ctxt.jsonObj) > 0:
		data, err := json.Marshal(ctxt.jsonObj)
		if err != nil {
			return fmt.Errorf("cannot marshal JSON: %v", err)
		}
		body = data
	case req.Method != "GET" && req.Method != "HEAD":
		// No fields specified and it looks like we need a body.

		// TODO check if it's seekable or make a temp file.
		data, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return fmt.Errorf("error reading stdin: %v", err)
		}
		// TODO if we're expecting JSON, accept rjson too.
		body = data
	}
	getBody := httpbakery.SeekerBody(bytes.NewReader(body))

	jar, client, err := httpClient(flags.cookieFile)
	if err != nil {
		return fmt.Errorf("cannot make HTTP client: %v", err)
	}
	defer cookiejar.SaveToFile(jar, flags.cookieFile)
	resp, err := httpbakery.DoWithBody(client, req, getBody, visitWebPage)
	if err != nil {
		return fmt.Errorf("cannot do HTTP request: %v", err)
	}
	defer resp.Body.Close()
	isJSONResp := false
	if ctype := resp.Header.Get("Content-Type"); ctype != "" {
		mediaType, _, err := mime.ParseMediaType(ctype)
		if err != nil {
			warningf("invalid content type %q in response", ctype)
		} else {
			isJSONResp = mediaType == "application/json"
		}
	}
	if !isJSONResp {
		io.Copy(os.Stdout, resp.Body)
		return nil
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read response body: %v", err)
	}
	var indented bytes.Buffer
	if err := rjson.Indent(&indented, data, "", "\t"); err != nil {
		warningf("cannot pretty print JSON response: %v", err)
		os.Stdout.Write(data)
		return nil
	}
	data = indented.Bytes()
	if len(data) > 0 && data[len(data)-1] != '\n' {
		data = append(data, '\n')
	}
	os.Stdout.Write(data)
	return nil
}

func visitWebPage(url *url.URL) error {
	fmt.Printf("please visit this URL:\n%s\n", url)
	return nil
}

func httpClient(cookieFile string) (*cookiejar.Jar, *http.Client, error) {
	jar, err := cookiejar.New(&cookiejar.Options{
		PublicSuffixList: publicsuffix.List,
	})
	if err != nil {
		panic(err)
	}
	// TODO allow disabling of persistent cookies
	err = cookiejar.LoadFromFile(jar, cookieFile)
	if err != nil {
		return nil, nil, err
	}
	client := httpbakery.NewHTTPClient()
	client.Jar = jar
	return jar, client, nil
}

func maybeSetContentType(req *http.Request, t string) {
	if _, ok := req.Header["Content-Type"]; !ok {
		req.Header.Set("Content-Type", t)
	}
}

type context struct {
	useJSON bool

	method    string
	header    http.Header
	urlValues url.Values
	form      url.Values
	jsonObj   map[string]interface{}
}

var ops = map[string]func(ctxt *context, key, val string) error{
	":":  (*context).httpHeader,
	"==": (*context).urlParam,
	"=":  (*context).dataString,
	":=": (*context).jsonOther,
}

var argPat = regexp.MustCompile(`^((\\.)|[^:=@])(:|==|=|:=|@|=@|:=@)(.*)$`)

func (ctxt *context) addArg(s string) error {
	m := argPat.FindStringSubmatch(s)
	if m == nil {
		return fmt.Errorf("unrecognized key pair %q", s)
	}
	key, op, val := m[1], m[2], m[3]
	key = unquoteKey(key)
	f := ops[op]
	if f == nil {
		return fmt.Errorf("key value type %q not yet recognized", op)
	}
	return f(ctxt, key, val)
}

// key:val
func (ctxt *context) httpHeader(key, val string) error {
	ctxt.header.Add(key, val)
	return nil
}

// key==val
func (ctxt *context) urlParam(key, val string) error {
	ctxt.urlValues.Add(key, val)
	return nil
}

// key=val
func (ctxt *context) dataString(key, val string) error {
	ctxt.method = "POST"
	if ctxt.useJSON {
		ctxt.jsonObj[key] = val
	} else {
		ctxt.form.Add(key, val)
	}
	return nil
}

// key:=val
func (ctxt *context) jsonOther(key, val string) error {
	if !ctxt.useJSON {
		return fmt.Errorf("cannot specify non-string key unless --json is specified")
	}
	var m json.RawMessage
	if err := json.Unmarshal([]byte(val), &m); err != nil {
		return fmt.Errorf("invalid JSON in key %s: %v", key, err)
	}
	ctxt.jsonObj[key] = &m
	return nil
}

func unquoteKey(key string) string {
	nkey := make([]byte, 0, len(key))
	for i := 0; i < len(key); i++ {
		if i < len(key)-1 && key[i] == '\\' {
			nkey = append(nkey, key[i+1])
			i++
		} else {
			nkey = append(nkey, key[i])
		}
	}
	return string(nkey)

}

func fatalf(f string, a ...interface{}) {
	if strings.HasSuffix(f, "\n") {
		f = f[0 : len(f)-1]
	}
	fmt.Fprintf(os.Stderr, "http: %s\n", fmt.Sprintf(f, a...))
	os.Exit(1)
}

func warningf(f string, a ...interface{}) {
	if strings.HasSuffix(f, "\n") {
		f = f[0 : len(f)-1]
	}
	fmt.Fprintf(os.Stderr, "http: warning: %s\n", fmt.Sprintf(f, a...))
}

func isAllCaps(s string) bool {
	for _, r := range s {
		if !unicode.IsUpper(r) {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package agent

// the HTTP interface

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/pprof"
	"os"
	"runtime"
	runtime_pprof "runtime/pprof"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/bakins/logrus-middleware"
	"github.com/bakins/net-http-recover"
	"github.com/gorilla/context"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/mistifyio/kvite"
	"github.com/mistifyio/mistify-agent/client"
)

type (
	// HTTPRequest is a container for an http request and its context
	HTTPRequest struct {
		ResponseWriter http.ResponseWriter
		Request        *http.Request
		Context        *Context
		vars           map[string]string
		Guest          *client.Guest
		GuestRunner    *GuestRunner
	}

	// HTTPResponse is a wrapper for http.ResponseWriter which provides access
	// to several convenience methods
	HTTPResponse struct {
		http.ResponseWriter
	}

	// HTTPErrorMessage is an enhanced error struct for http error responses
	HTTPErrorMessage struct {
		Message string   `json:"message"`
		Code    int      `json:"code"`
		Stack   []string `json:"stack"`
	}

	// Chain is a middleware chain
	Chain struct {
		alice.Chain
		ctx *Context
	}
)

const ctxKey string = "agentContext"

var (
	// ErrNotFound is the error for a resouce not found
	ErrNotFound = errors.New("not found")
)

// AttachProfiler enables debug profiling exposed on http api endpoints
func AttachProfiler(router *mux.Router) {
	router.HandleFunc("/debug/pprof/", pprof.Index)
	for _, profile := range runtime_pprof.Profiles() {
		router.Handle(fmt.Sprintf("/debug/pprof/%s", profile.Name()), pprof.Handler(profile.Name()))
	}
	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
}

// Run prepares and runs the http server
func Run(ctx *Context, address string) error {
	r := mux.NewRouter()
	r.StrictSlash(true)

	AttachProfiler(r)

	logrusMiddleware := logrusmiddleware.Middleware{
		Name: "agent",
	}
	commonMiddleware := alice.New(
		func(h http.Handler) http.Handler {
			return logrusMiddleware.Handler(h, "")
		},
		handlers.CompressHandler,
		func(h http.Handler) http.Handler {
			return recovery.Handler(os.Stderr, h, true)
		},
		func(h http.Handler) http.Handler {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				context.Set(r, ctxKey, ctx)
				h.ServeHTTP(w, r)
			})
		},
	)

	chain := Chain{
		ctx: ctx,
		Chain: alice.New(
			func(h http.Handler) http.Handler {
				return handlers.CombinedLoggingHandler(os.Stdout, h)
			},
			handlers.CompressHandler,
			func(h http.Handler) http.Handler {
				return recovery.Handler(os.Stderr, h, true)
			}),
	}

	// General
	r.HandleFunc("/metadata", chain.RequestWrapper(getMetadata)).Methods("GET")
	r.HandleFunc("/metadata", chain.RequestWrapper(setMetadata)).Methods("PATCH")

	r.HandleFunc("/images", chain.RequestWrapper(listImages)).Queries("type", "{type:[a-zA-Z]+}").Methods("GET")
	r.HandleFunc("/images", chain.RequestWrapper(listImages)).Methods("GET")
	r.HandleFunc("/images", chain.RequestWrapper(fetchImage)).Methods("POST")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(getImage)).Queries("type", "{type:[a-zA-Z]+}").Methods("GET")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(getImage)).Methods("GET")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(deleteImage)).Queries("type", "{type:[a-zA-Z]+}").Methods("DELETE")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(deleteImage)).Methods("DELETE")

	r.HandleFunc("/jobs", chain.RequestWrapper(getLatestJobs)).Methods("GET").Queries("limit", "{limit:[0-9]+}").Methods("GET")
	r.HandleFunc("/jobs", chain.RequestWrapper(getLatestJobs)).Methods("GET")
	r.HandleFunc("/jobs/{jobID}", chain.RequestWrapper(getJobStatus)).Methods("GET")

	r.HandleFunc("/guests", chain.RequestWrapper(listGuests)).Methods("GET")

	// Specific Guest
	r.HandleFunc("/guests", chain.RequestWrapper(createGuest)).Methods("POST") // Special setup
	r.HandleFunc("/guests/{id}", chain.GuestRunnerWrapper(getGuest)).Methods("GET")
	r.HandleFunc("/guests/{id}/jobs", chain.GuestRunnerWrapper(getLatestGuestJobs)).Queries("limit", "{limit:[0-9]+}").Methods("GET")
	r.HandleFunc("/guests/{id}/jobs", chain.GuestRunnerWrapper(getLatestGuestJobs)).Methods("GET")
	r.HandleFunc("/guests/{id}/jobs/{jobID}", chain.GuestRunnerWrapper(getJobStatus)).Methods("GET")
	//r.HandleFunc("/guests/{id}", chain.GuestRunnerWrapper(deleteGuest)).Methods("DELETE")
	r.HandleFunc("/guests/{id}/metadata", chain.GuestRunnerWrapper(getGuestMetadata)).Methods("GET")
	r.HandleFunc("/guests/{id}/metadata", chain.GuestRunnerWrapper(setGuestMetadata)).Methods("PATCH")

	r.HandleFunc("/guests/{id}/metrics/cpu", chain.GuestRunnerWrapper(getCPUMetrics)).Methods("GET")
	r.HandleFunc("/guests/{id}/metrics/disk", chain.GuestRunnerWrapper(getDiskMetrics)).Methods("GET")
	r.HandleFunc("/guests/{id}/metrics/nic", chain.GuestRunnerWrapper(getNicMetrics)).Methods("GET")

	for _, action := range []string{"shutdown", "reboot", "restart", "poweroff", "start", "suspend", "delete"} {
		r.HandleFunc(fmt.Sprintf("/guests/{id}/%s", action), chain.GuestActionWrapper(action)).Methods("POST")
	}

	for _, prefix := range []string{"/guests/{id}", "/guests/{id}/disks/{disk}"} {
		r.HandleFunc(fmt.Sprintf("%s/snapshots", prefix), chain.GuestRunnerWrapper(listSnapshots)).Methods("GET")
		r.HandleFunc(fmt.Sprintf("%s/snapshots", prefix), chain.GuestRunnerWrapper(createSnapshot)).Methods("POST")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}", prefix), chain.GuestRunnerWrapper(getSnapshot)).Methods("GET")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}", prefix), chain.GuestRunnerWrapper(deleteSnapshot)).Methods("DELETE")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}/rollback", prefix), chain.GuestRunnerWrapper(rollbackSnapshot)).Methods("POST")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}/download", prefix), chain.GuestRunnerWrapper(downloadSnapshot)).Methods("GET")
	}

	/*
		guest := guests.PathPrefix("/{id}").Subrouter()
		guest.HandleFunc("/vnc", RequestWrapper(ctx, vncGuest))
		guest.HandleFunc("/history", RequestWrapper(ctx, getGuestHistory)).Methods("GET")

	*/

	s := &http.Server{
		Addr:           address,
		Handler:        r,
		MaxHeaderBytes: 1 << 20,
	}
	return s.ListenAndServe()
}

// RequestWrapper turns a basic http request into an HTTPRequest
func (c *Chain) RequestWrapper(fn func(*HTTPRequest) *HTTPErrorMessage) http.HandlerFunc {
	return c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		req := HTTPRequest{
			Context:        c.ctx,
			ResponseWriter: w,
			Request:        r,
		}
		if err := fn(&req); err != nil {
			log.WithFields(log.Fields{
				"error": err,
				"func":  "agent.RequestWrapper",
				"stack": strings.Join(err.Stack, "\t\n\t"),
				"path":  r.URL.Path,
			}).Error(err)
			req.JSON(err.Code, err)
		}
	})).ServeHTTP
}

// Parameter retrieves request parameter
func (r *HTTPRequest) Parameter(key string) string {
	vars := r.vars

	if vars == nil {
		vars = mux.Vars(r.Request)
		r.vars = vars
	}

	if vars == nil {
		return ""
	}

	return vars[key]
}

// SetHeader sets an http response header
func (r *HTTPRequest) SetHeader(key, val string) {
	r.ResponseWriter.Header().Set(key, val)
}

// JSON sends an http response with a json body
func (r *HTTPRequest) JSON(code int, obj interface{}) *HTTPErrorMessage {
	r.SetHeader("Content-Type", "application/json")
	r.ResponseWriter.WriteHeader(code)
	encoder := json.NewEncoder(r.ResponseWriter)
	if err := encoder.Encode(obj); err != nil {
		return r.NewError(err, http.StatusInternalServerError)
	}
	return nil
}

// NewError creates an HTTPErrorMessage
func (r *HTTPRequest) NewError(err error, code int) *HTTPErrorMessage {
	if code <= 0 {
		code = http.StatusInternalServerError
	}
	msg := HTTPErrorMessage{
		Message: err.Error(),
		Code:    code,
		Stack:   make([]string, 0, 4),
	}
	for i := 1; ; i++ { //
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		// Print this much at least.  If we can't find the source, it won't show.
		msg.Stack = append(msg.Stack, fmt.Sprintf("%s:%d (0x%x)", file, line, pc))
	}
	return &msg
}

func getMetadata(r *HTTPRequest) *HTTPErrorMessage {
	metadata := make(map[string]string)

	err := r.Context.db.Transaction(func(tx *kvite.Tx) error {
		b, err := tx.Bucket("hypervisor-metadata")
		if err != nil {
			return err
		}
		return b.ForEach(func(k string, v []byte) error {
			metadata[string(k)] = string(v)
			return nil
		})
	})

	if err != nil {
		return r.NewError(err, http.StatusInternalServerError)
	}

	return r.JSON(http.StatusOK, metadata)
}

func setMetadata(r *HTTPRequest) *HTTPErrorMessage {
	var metadata map[string]string

	err := json.NewDecoder(r.Request.Body).Decode(&metadata)
	if err != nil {
		return r.NewError(err, http.StatusBadRequest)
	}

	err = r.Context.db.Transaction(func(tx *kvite.Tx) error {
		for key, value := range metadata {
			b, err := tx.Bucket("hypervisor-metadata")
			if err != nil {
				return err
			}
			if value == "" {
				if err := b.Delete(key); err != nil {
					return err
				}
			} else {
				if err := b.Put(key, []byte(value)); err != nil {
					return err
				}
			}
		}
		return nil
	})

	if err != nil {
		return r.NewError(err, http.StatusInternalServerError)
	}

	return getMetadata(r)
}

// JSON writes appropriate headers and JSON body to the http response
func (hr *HTTPResponse) JSON(code int, obj interface{}) {
	hr.Header().Set("Content-Type", "application/json")
	hr.WriteHeader(code)
	encoder := json.NewEncoder(hr)
	if err := encoder.Encode(obj); err != nil {
		hr.JSONError(http.StatusInternalServerError, err)
	}
}

// JSONError prepares an HTTPError with a stack trace and writes it with
// HTTPResponse.JSON
func (hr *HTTPResponse) JSONError(code int, err error) {
	httpError := &HTTPErrorMessage{
		Message: err.Error(),
		Code:    code,
		Stack:   make([]string, 0, 4),
	}
	for i := 1; ; i++ { //
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		// Print this much at least.  If we can't find the source, it won't show.
		httpError.Stack = append(httpError.Stack, fmt.Sprintf("%s:%d (0x%x)", file, line, pc))
	}
	hr.JSON(code, httpError)
}

// JSONMsg is a convenience method to write a JSON response with just a message
// string
func (hr *HTTPResponse) JSONMsg(code int, msg string) {
	msgObj := map[string]string{
		"message": msg,
	}
	hr.JSON(code, msgObj)
}

// GetContext retrieves a Context value for a request
func GetContext(r *http.Request) *Context {
	if value := context.Get(r, ctxKey); value != nil {
		return value.(*Context)
	}
	return nil
}
<commit_msg>Use commonMiddleware and empty out chain.Chain for the transition<commit_after>package agent

// the HTTP interface

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/pprof"
	"os"
	"runtime"
	runtime_pprof "runtime/pprof"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/bakins/logrus-middleware"
	"github.com/bakins/net-http-recover"
	"github.com/gorilla/context"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/mistifyio/kvite"
	"github.com/mistifyio/mistify-agent/client"
)

type (
	// HTTPRequest is a container for an http request and its context
	HTTPRequest struct {
		ResponseWriter http.ResponseWriter
		Request        *http.Request
		Context        *Context
		vars           map[string]string
		Guest          *client.Guest
		GuestRunner    *GuestRunner
	}

	// HTTPResponse is a wrapper for http.ResponseWriter which provides access
	// to several convenience methods
	HTTPResponse struct {
		http.ResponseWriter
	}

	// HTTPErrorMessage is an enhanced error struct for http error responses
	HTTPErrorMessage struct {
		Message string   `json:"message"`
		Code    int      `json:"code"`
		Stack   []string `json:"stack"`
	}

	// Chain is a middleware chain
	Chain struct {
		alice.Chain
		ctx *Context
	}
)

const ctxKey string = "agentContext"

var (
	// ErrNotFound is the error for a resouce not found
	ErrNotFound = errors.New("not found")
)

// AttachProfiler enables debug profiling exposed on http api endpoints
func AttachProfiler(router *mux.Router) {
	router.HandleFunc("/debug/pprof/", pprof.Index)
	for _, profile := range runtime_pprof.Profiles() {
		router.Handle(fmt.Sprintf("/debug/pprof/%s", profile.Name()), pprof.Handler(profile.Name()))
	}
	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
}

// Run prepares and runs the http server
func Run(ctx *Context, address string) error {
	r := mux.NewRouter()
	r.StrictSlash(true)

	AttachProfiler(r)

	logrusMiddleware := logrusmiddleware.Middleware{
		Name: "agent",
	}
	commonMiddleware := alice.New(
		func(h http.Handler) http.Handler {
			return logrusMiddleware.Handler(h, "")
		},
		handlers.CompressHandler,
		func(h http.Handler) http.Handler {
			return recovery.Handler(os.Stderr, h, true)
		},
		func(h http.Handler) http.Handler {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				context.Set(r, ctxKey, ctx)
				h.ServeHTTP(w, r)
			})
		},
	)

	chain := Chain{
		ctx:   ctx,
		Chain: alice.New(),
	}

	// General
	r.HandleFunc("/metadata", chain.RequestWrapper(getMetadata)).Methods("GET")
	r.HandleFunc("/metadata", chain.RequestWrapper(setMetadata)).Methods("PATCH")

	r.HandleFunc("/images", chain.RequestWrapper(listImages)).Queries("type", "{type:[a-zA-Z]+}").Methods("GET")
	r.HandleFunc("/images", chain.RequestWrapper(listImages)).Methods("GET")
	r.HandleFunc("/images", chain.RequestWrapper(fetchImage)).Methods("POST")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(getImage)).Queries("type", "{type:[a-zA-Z]+}").Methods("GET")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(getImage)).Methods("GET")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(deleteImage)).Queries("type", "{type:[a-zA-Z]+}").Methods("DELETE")
	r.HandleFunc("/images/{id}", chain.RequestWrapper(deleteImage)).Methods("DELETE")

	r.HandleFunc("/jobs", chain.RequestWrapper(getLatestJobs)).Methods("GET").Queries("limit", "{limit:[0-9]+}").Methods("GET")
	r.HandleFunc("/jobs", chain.RequestWrapper(getLatestJobs)).Methods("GET")
	r.HandleFunc("/jobs/{jobID}", chain.RequestWrapper(getJobStatus)).Methods("GET")

	r.HandleFunc("/guests", chain.RequestWrapper(listGuests)).Methods("GET")

	// Specific Guest
	r.HandleFunc("/guests", chain.RequestWrapper(createGuest)).Methods("POST") // Special setup
	r.HandleFunc("/guests/{id}", chain.GuestRunnerWrapper(getGuest)).Methods("GET")
	r.HandleFunc("/guests/{id}/jobs", chain.GuestRunnerWrapper(getLatestGuestJobs)).Queries("limit", "{limit:[0-9]+}").Methods("GET")
	r.HandleFunc("/guests/{id}/jobs", chain.GuestRunnerWrapper(getLatestGuestJobs)).Methods("GET")
	r.HandleFunc("/guests/{id}/jobs/{jobID}", chain.GuestRunnerWrapper(getJobStatus)).Methods("GET")
	//r.HandleFunc("/guests/{id}", chain.GuestRunnerWrapper(deleteGuest)).Methods("DELETE")
	r.HandleFunc("/guests/{id}/metadata", chain.GuestRunnerWrapper(getGuestMetadata)).Methods("GET")
	r.HandleFunc("/guests/{id}/metadata", chain.GuestRunnerWrapper(setGuestMetadata)).Methods("PATCH")

	r.HandleFunc("/guests/{id}/metrics/cpu", chain.GuestRunnerWrapper(getCPUMetrics)).Methods("GET")
	r.HandleFunc("/guests/{id}/metrics/disk", chain.GuestRunnerWrapper(getDiskMetrics)).Methods("GET")
	r.HandleFunc("/guests/{id}/metrics/nic", chain.GuestRunnerWrapper(getNicMetrics)).Methods("GET")

	for _, action := range []string{"shutdown", "reboot", "restart", "poweroff", "start", "suspend", "delete"} {
		r.HandleFunc(fmt.Sprintf("/guests/{id}/%s", action), chain.GuestActionWrapper(action)).Methods("POST")
	}

	for _, prefix := range []string{"/guests/{id}", "/guests/{id}/disks/{disk}"} {
		r.HandleFunc(fmt.Sprintf("%s/snapshots", prefix), chain.GuestRunnerWrapper(listSnapshots)).Methods("GET")
		r.HandleFunc(fmt.Sprintf("%s/snapshots", prefix), chain.GuestRunnerWrapper(createSnapshot)).Methods("POST")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}", prefix), chain.GuestRunnerWrapper(getSnapshot)).Methods("GET")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}", prefix), chain.GuestRunnerWrapper(deleteSnapshot)).Methods("DELETE")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}/rollback", prefix), chain.GuestRunnerWrapper(rollbackSnapshot)).Methods("POST")
		r.HandleFunc(fmt.Sprintf("%s/snapshots/{name}/download", prefix), chain.GuestRunnerWrapper(downloadSnapshot)).Methods("GET")
	}

	/*
		guest := guests.PathPrefix("/{id}").Subrouter()
		guest.HandleFunc("/vnc", RequestWrapper(ctx, vncGuest))
		guest.HandleFunc("/history", RequestWrapper(ctx, getGuestHistory)).Methods("GET")

	*/

	s := &http.Server{
		Addr:           address,
		Handler:        commonMiddleware.Then(r),
		MaxHeaderBytes: 1 << 20,
	}
	return s.ListenAndServe()
}

// RequestWrapper turns a basic http request into an HTTPRequest
func (c *Chain) RequestWrapper(fn func(*HTTPRequest) *HTTPErrorMessage) http.HandlerFunc {
	return c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		req := HTTPRequest{
			Context:        c.ctx,
			ResponseWriter: w,
			Request:        r,
		}
		if err := fn(&req); err != nil {
			log.WithFields(log.Fields{
				"error": err,
				"func":  "agent.RequestWrapper",
				"stack": strings.Join(err.Stack, "\t\n\t"),
				"path":  r.URL.Path,
			}).Error(err)
			req.JSON(err.Code, err)
		}
	})).ServeHTTP
}

// Parameter retrieves request parameter
func (r *HTTPRequest) Parameter(key string) string {
	vars := r.vars

	if vars == nil {
		vars = mux.Vars(r.Request)
		r.vars = vars
	}

	if vars == nil {
		return ""
	}

	return vars[key]
}

// SetHeader sets an http response header
func (r *HTTPRequest) SetHeader(key, val string) {
	r.ResponseWriter.Header().Set(key, val)
}

// JSON sends an http response with a json body
func (r *HTTPRequest) JSON(code int, obj interface{}) *HTTPErrorMessage {
	r.SetHeader("Content-Type", "application/json")
	r.ResponseWriter.WriteHeader(code)
	encoder := json.NewEncoder(r.ResponseWriter)
	if err := encoder.Encode(obj); err != nil {
		return r.NewError(err, http.StatusInternalServerError)
	}
	return nil
}

// NewError creates an HTTPErrorMessage
func (r *HTTPRequest) NewError(err error, code int) *HTTPErrorMessage {
	if code <= 0 {
		code = http.StatusInternalServerError
	}
	msg := HTTPErrorMessage{
		Message: err.Error(),
		Code:    code,
		Stack:   make([]string, 0, 4),
	}
	for i := 1; ; i++ { //
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		// Print this much at least.  If we can't find the source, it won't show.
		msg.Stack = append(msg.Stack, fmt.Sprintf("%s:%d (0x%x)", file, line, pc))
	}
	return &msg
}

func getMetadata(r *HTTPRequest) *HTTPErrorMessage {
	metadata := make(map[string]string)

	err := r.Context.db.Transaction(func(tx *kvite.Tx) error {
		b, err := tx.Bucket("hypervisor-metadata")
		if err != nil {
			return err
		}
		return b.ForEach(func(k string, v []byte) error {
			metadata[string(k)] = string(v)
			return nil
		})
	})

	if err != nil {
		return r.NewError(err, http.StatusInternalServerError)
	}

	return r.JSON(http.StatusOK, metadata)
}

func setMetadata(r *HTTPRequest) *HTTPErrorMessage {
	var metadata map[string]string

	err := json.NewDecoder(r.Request.Body).Decode(&metadata)
	if err != nil {
		return r.NewError(err, http.StatusBadRequest)
	}

	err = r.Context.db.Transaction(func(tx *kvite.Tx) error {
		for key, value := range metadata {
			b, err := tx.Bucket("hypervisor-metadata")
			if err != nil {
				return err
			}
			if value == "" {
				if err := b.Delete(key); err != nil {
					return err
				}
			} else {
				if err := b.Put(key, []byte(value)); err != nil {
					return err
				}
			}
		}
		return nil
	})

	if err != nil {
		return r.NewError(err, http.StatusInternalServerError)
	}

	return getMetadata(r)
}

// JSON writes appropriate headers and JSON body to the http response
func (hr *HTTPResponse) JSON(code int, obj interface{}) {
	hr.Header().Set("Content-Type", "application/json")
	hr.WriteHeader(code)
	encoder := json.NewEncoder(hr)
	if err := encoder.Encode(obj); err != nil {
		hr.JSONError(http.StatusInternalServerError, err)
	}
}

// JSONError prepares an HTTPError with a stack trace and writes it with
// HTTPResponse.JSON
func (hr *HTTPResponse) JSONError(code int, err error) {
	httpError := &HTTPErrorMessage{
		Message: err.Error(),
		Code:    code,
		Stack:   make([]string, 0, 4),
	}
	for i := 1; ; i++ { //
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		// Print this much at least.  If we can't find the source, it won't show.
		httpError.Stack = append(httpError.Stack, fmt.Sprintf("%s:%d (0x%x)", file, line, pc))
	}
	hr.JSON(code, httpError)
}

// JSONMsg is a convenience method to write a JSON response with just a message
// string
func (hr *HTTPResponse) JSONMsg(code int, msg string) {
	msgObj := map[string]string{
		"message": msg,
	}
	hr.JSON(code, msgObj)
}

// GetContext retrieves a Context value for a request
func GetContext(r *http.Request) *Context {
	if value := context.Get(r, ctxKey); value != nil {
		return value.(*Context)
	}
	return nil
}
<|endoftext|>


<commit_before>package alicloud

import (
	"fmt"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/ecs"
	"github.com/denverdino/aliyungo/rds"
	"github.com/denverdino/aliyungo/slb"
)

// Config of aliyun
type Config struct {
	AccessKey string
	SecretKey string
	Region    common.Region
}

// AliyunClient of aliyun
type AliyunClient struct {
	Region  common.Region
	ecsconn *ecs.Client
	rdsconn *rds.Client
	// use new version
	ecsNewconn *ecs.Client
	vpcconn    *ecs.Client
	slbconn    *slb.Client
}

// Client for AliyunClient
func (c *Config) Client() (*AliyunClient, error) {
	err := c.loadAndValidate()
	if err != nil {
		return nil, err
	}

	ecsconn, err := c.ecsConn()
	if err != nil {
		return nil, err
	}

	ecsNewconn, err := c.ecsConn()
	if err != nil {
		return nil, err
	}
	ecsNewconn.SetVersion(EcsApiVersion20160314)

	rdsconn, err := c.rdsConn()
	if err != nil {
		return nil, err
	}

	slbconn, err := c.slbConn()
	if err != nil {
		return nil, err
	}

	vpcconn, err := c.vpcConn()
	if err != nil {
		return nil, err
	}

	return &AliyunClient{
		Region:     c.Region,
		ecsconn:    ecsconn,
		ecsNewconn: ecsNewconn,
		vpcconn:    vpcconn,
		slbconn:    slbconn,
		rdsconn:    rdsconn,
	}, nil
}

// return new ecs Client
// when you need new client not global client, use this method
func (c *Config) NewEcsConn() (*ecs.Client, error) {
	client := ecs.NewClient(c.AccessKey, c.SecretKey)
	client.SetBusinessInfo("Terraform")
	_, err := client.DescribeRegions()

	if err != nil {
		return nil, err
	}

	return client, nil
}

func (c *Config) loadAndValidate() error {
	err := c.validateRegion()
	if err != nil {
		return err
	}

	return nil
}

func (c *Config) validateRegion() error {

	for _, valid := range common.ValidRegions {
		if c.Region == valid {
			return nil
		}
	}

	return fmt.Errorf("Not a valid region: %s", c.Region)
}

func (c *Config) ecsConn() (*ecs.Client, error) {
	client := ecs.NewClient(c.AccessKey, c.SecretKey)
	_, err := client.DescribeRegions()

	if err != nil {
		return nil, err
	}

	return client, nil
}

func (c *Config) rdsConn() (*rds.Client, error) {
	client := rds.NewClient(c.AccessKey, c.SecretKey)
	return client, nil
}

func (c *Config) slbConn() (*slb.Client, error) {
	client := slb.NewClient(c.AccessKey, c.SecretKey)

	return client, nil
}

func (c *Config) vpcConn() (*ecs.Client, error) {
	_, err := c.ecsConn()

	if err != nil {
		return nil, err
	}

	client := &ecs.Client{}
	client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey)
	return client, nil
}
<commit_msg>update config add businessinfo<commit_after>package alicloud

import (
	"fmt"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/ecs"
	"github.com/denverdino/aliyungo/rds"
	"github.com/denverdino/aliyungo/slb"
)

// Config of aliyun
type Config struct {
	AccessKey string
	SecretKey string
	Region    common.Region
}

// AliyunClient of aliyun
type AliyunClient struct {
	Region  common.Region
	ecsconn *ecs.Client
	rdsconn *rds.Client
	// use new version
	ecsNewconn *ecs.Client
	vpcconn    *ecs.Client
	slbconn    *slb.Client
}

// Client for AliyunClient
func (c *Config) Client() (*AliyunClient, error) {
	err := c.loadAndValidate()
	if err != nil {
		return nil, err
	}

	ecsconn, err := c.ecsConn()
	if err != nil {
		return nil, err
	}

	ecsNewconn, err := c.ecsConn()
	if err != nil {
		return nil, err
	}
	ecsNewconn.SetVersion(EcsApiVersion20160314)

	rdsconn, err := c.rdsConn()
	if err != nil {
		return nil, err
	}

	slbconn, err := c.slbConn()
	if err != nil {
		return nil, err
	}

	vpcconn, err := c.vpcConn()
	if err != nil {
		return nil, err
	}

	return &AliyunClient{
		Region:     c.Region,
		ecsconn:    ecsconn,
		ecsNewconn: ecsNewconn,
		vpcconn:    vpcconn,
		slbconn:    slbconn,
		rdsconn:    rdsconn,
	}, nil
}

const BusinessInfoKey = "Terraform"

// return new ecs Client
// when you need new client not global client, use this method
func (c *Config) NewEcsConn() (*ecs.Client, error) {
	client := ecs.NewClient(c.AccessKey, c.SecretKey)
	client.SetBusinessInfo(BusinessInfoKey)
	_, err := client.DescribeRegions()

	if err != nil {
		return nil, err
	}

	return client, nil
}

func (c *Config) loadAndValidate() error {
	err := c.validateRegion()
	if err != nil {
		return err
	}

	return nil
}

func (c *Config) validateRegion() error {

	for _, valid := range common.ValidRegions {
		if c.Region == valid {
			return nil
		}
	}

	return fmt.Errorf("Not a valid region: %s", c.Region)
}

func (c *Config) ecsConn() (*ecs.Client, error) {
	client := ecs.NewClient(c.AccessKey, c.SecretKey)
	client.SetBusinessInfo(BusinessInfoKey)
	_, err := client.DescribeRegions()

	if err != nil {
		return nil, err
	}

	return client, nil
}

func (c *Config) rdsConn() (*rds.Client, error) {
	client := rds.NewClient(c.AccessKey, c.SecretKey)
	client.SetBusinessInfo(BusinessInfoKey)
	return client, nil
}

func (c *Config) slbConn() (*slb.Client, error) {
	client := slb.NewClient(c.AccessKey, c.SecretKey)
	client.SetBusinessInfo(BusinessInfoKey)
	return client, nil
}

func (c *Config) vpcConn() (*ecs.Client, error) {
	_, err := c.ecsConn()

	if err != nil {
		return nil, err
	}

	client := &ecs.Client{}
	client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey)
	client.SetBusinessInfo(BusinessInfoKey)
	return client, nil
}
<|endoftext|>