<commit_before>package relpipes

import "sync"

// a set of tests and benchmarks for group

// note that if you are adapting this to your own use, you should probably
// chose a different name for the tuple (other than fooBar!) as it is just
// a placeholder.

type predicate func(t1, t2 *fooBar) bool

func p(t1, t2 *fooBar) bool { return t1.foo == t2.foo }

// we can only perform grouping on equality when we're using a hash
type subFooBar struct{ foo int }

func proj(t *fooBar) subFooBar { return subFooBar{t.foo} }

// groupOrdered takes two channels, one for input and one for
// results, and passes tuples that belong to the same group
// to the appropriate output channel.  It also takes a
// predicate, which when evaluated on two input tuples returns
// a true if they belong to the same group, otherwise false.
// It also takes n, which is the buffer size that should be
// used for the inner result channels.
func groupOrdered(in <-chan fooBar, res chan chan fooBar, p predicate, n int) {
	go func() {

		// initialization is a bit trickier here.
		// We can't compare the first tuple to any
		// other tuples, but that's fine because
		// it means we can sent it immeidately.
		inner := make(chan fooBar, n)
		prev, ok := <-in
		// On the other hand, in might have been
		// closed and never sent any values, so we
		// have to check for that case.
		if ok {
			res <- inner
			inner <- prev
		} else {
			close(res)
			return
		}

		// read each input,
		for v := range in {
			// if it is has not already been
			// encountered, send it on the results
			// channel and then update the prev
			// variable.  If it has not been
			// encountered, then close the results
			// channel and create a new one
			if p(&v, &prev) {
				inner <- v
			} else {
				close(inner)
				inner = make(chan fooBar, n)
				res <- inner
				inner <- v
				prev = v
			}
		}
		// close the resulting channels
		close(inner)
		close(res)
	}()
	return
}

// groupUnordered takes two channels, one for input and one for
// results, and passes tuples that belong to the same group
// to the appropriate output channel. It also takes n, which is
// the buffer size that should be used for the inner result channels.
func groupUnordered(in <-chan fooBar, res chan chan fooBar, n int) {

	go func() {
		m := make(map[subFooBar]chan fooBar)
		var wg sync.WaitGroup

		// read each input,
		for v := range in {
			// if it is has not already been
			// encountered, create a new goroutine
			// to handle it
			sub := proj(&v)
			if _, exist := m[sub]; !exist {
				wg.Add(1)
				g := make(chan fooBar)
				m[sub] = g
				go makeGroup(g, res, &wg, n)
			}
			m[sub] <- v
		}
		// close the grouping channels
		for _, ch := range m {
			close(ch)
		}
		// once the groups are all finished,
		// close the result group channel
		wg.Wait()
		close(res)
	}()
	return
}

// makeGroup handles an individual group.  It will never block on
// a recv from groupChan
func makeGroup(in chan fooBar, res chan chan fooBar, wg *sync.WaitGroup, n int) {

	groupChan := make(chan fooBar, n)
	sendChan := groupChan
	sentTup := true
	tupBuf := make([]fooBar, 0) // a stack of tuples
	var v fooBar

	for {
		if bufLen := len(tupBuf); sentTup && bufLen > 0 {
			// there is a need to send a new value
			// pop one of the tuples out of the buffer
			v, tupBuf = tupBuf[bufLen-1], tupBuf[:bufLen-1]
			sentTup = false
			sendChan = groupChan
		} else {
			if sentTup {
				if in == nil {
					// there is nothing left to send
					break
				}
				sendChan = nil
			}
		}
		select {
		case res <- groupChan:
			res = nil
		case sendChan <- v:
			sentTup = true
		case tup, ok := <-in:
			if !ok {
				// input channel is closed
				in = nil
				break
			}
			tupBuf = append(tupBuf, tup)
		}
	}
	wg.Done()
	close(groupChan)
}
<commit_msg>added benchmarks for group<commit_after>package relpipes

import (
	"runtime"
	"sort"
	"testing"
	"sync"
	)
// a set of tests and benchmarks for group

// note that if you are adapting this to your own use, you should probably
// chose a different name for the tuple (other than fooBar!) as it is just
// a placeholder.

type predicate func(t1, t2 *fooBar) bool

func p(t1, t2 *fooBar) bool { return t1.foo == t2.foo }

// we can only perform grouping on equality when we're using a hash
type subFooBar struct{ foo int }

func proj(t *fooBar) subFooBar { return subFooBar{t.foo} }

// groupOrdered takes two channels, one for input and one for
// results, and passes tuples that belong to the same group
// to the appropriate output channel.  It also takes a
// predicate, which when evaluated on two input tuples returns
// a true if they belong to the same group, otherwise false.
// It also takes n, which is the buffer size that should be
// used for the inner result channels.
func groupOrdered(in <-chan fooBar, res chan (chan fooBar), p predicate, n int) {
	go func() {

		// initialization is a bit trickier here.
		// We can't compare the first tuple to any
		// other tuples, but that's fine because
		// it means we can sent it immeidately.
		inner := make(chan fooBar, n)
		prev, ok := <-in
		// On the other hand, in might have been
		// closed and never sent any values, so we
		// have to check for that case.
		if ok {
			res <- inner
			inner <- prev
		} else {
			close(res)
			return
		}

		// read each input,
		for v := range in {
			// if it is has not already been
			// encountered, send it on the results
			// channel and then update the prev
			// variable.  If it has not been
			// encountered, then close the results
			// channel and create a new one
			if p(&v, &prev) {
				inner <- v
			} else {
				close(inner)
				inner = make(chan fooBar, n)
				res <- inner
				inner <- v
				prev = v
			}
		}
		// close the resulting channels
		close(inner)
		close(res)
	}()
	return
}

// ordered benchmarks

func emptyFooBarGroup(ch chan (chan fooBar)) {
	for inner := range ch {
		for _ = range inner {
			// do nothing, just recv
		}
	}
}

func benchGroupOrdered(b *testing.B, tupN, fooN, barN int) {
	in := makeFooBar(tupN, fooN, barN)
	sort.Sort(fooBarByBar(in))
	b.StopTimer()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		res := make(chan (chan fooBar), 1)

		b.StartTimer()
		groupOrdered(fooBarChan(in, 1), res, p, 2)

		emptyFooBarGroup(res)
		b.StopTimer()
	}
}

// benchmarks for group with cardinality ~3
func BenchmarkGroupOrdered_10_3_1(b *testing.B) {
	benchGroupOrdered(b, 10, 3, 1)
}
func BenchmarkGroupOrdered_100_3_1(b *testing.B) {
	benchGroupOrdered(b, 100, 3, 1)
}
func BenchmarkGroupOrdered_1000_3_1(b *testing.B) {
	benchGroupOrdered(b, 1000, 3, 1)
}
func BenchmarkGroupOrdered_10000_3_1(b *testing.B) {
	benchGroupOrdered(b, 10000, 3, 1)
}
func BenchmarkGroupOrdered_100000_3_1(b *testing.B) {
	benchGroupOrdered(b, 100000, 3, 1)
}
func BenchmarkGroupOrdered_1000000_3_1(b *testing.B) {
	benchGroupOrdered(b, 1000000, 3, 1)
}

// benchmarks for group with cardinality ~1000
func BenchmarkGroupOrdered_10_1000_1(b *testing.B) {
	benchGroupOrdered(b, 10, 1000, 1)
}
func BenchmarkGroupOrdered_100_1000_1(b *testing.B) {
	benchGroupOrdered(b, 100, 1000, 1)
}
func BenchmarkGroupOrdered_1000_1000_1(b *testing.B) {
	benchGroupOrdered(b, 1000, 1000, 1)
}
func BenchmarkGroupOrdered_10000_1000_1(b *testing.B) {
	benchGroupOrdered(b, 10000, 1000, 1)
}
func BenchmarkGroupOrdered_100000_1000_1(b *testing.B) {
	benchGroupOrdered(b, 100000, 1000, 1)
}
func BenchmarkGroupOrdered_1000000_1000_1(b *testing.B) {
	benchGroupOrdered(b, 1000000, 1000, 1)
}

// benchmarks for group with cardinality ~100000
func BenchmarkGroupOrdered_10_100000_1(b *testing.B) {
	benchGroupOrdered(b, 10, 100000, 1)
}
func BenchmarkGroupOrdered_100_100000_1(b *testing.B) {
	benchGroupOrdered(b, 100, 100000, 1)
}
func BenchmarkGroupOrdered_1000_100000_1(b *testing.B) {
	benchGroupOrdered(b, 1000, 100000, 1)
}
func BenchmarkGroupOrdered_10000_100000_1(b *testing.B) {
	benchGroupOrdered(b, 10000, 100000, 1)
}
func BenchmarkGroupOrdered_100000_100000_1(b *testing.B) {
	benchGroupOrdered(b, 100000, 100000, 1)
}
func BenchmarkGroupOrdered_1000000_100000_1(b *testing.B) {
	benchGroupOrdered(b, 1000000, 100000, 1)
}

// groupUnordered takes two channels, one for input and one for
// results, and passes tuples that belong to the same group
// to the appropriate output channel. It also takes n, which is
// the buffer size that should be used for the inner result channels.
func groupUnordered(in <-chan fooBar, res chan (chan fooBar), n int) {

	go func() {
		m := make(map[subFooBar]chan fooBar)
		var wg sync.WaitGroup

		// read each input,
		for v := range in {
			// if it is has not already been
			// encountered, create a new goroutine
			// to handle it
			sub := proj(&v)
			if _, exist := m[sub]; !exist {
				wg.Add(1)
				g := make(chan fooBar)
				m[sub] = g
				go makeGroup(g, res, &wg, n)
			}
			m[sub] <- v
		}
		// close the grouping channels
		for _, ch := range m {
			close(ch)
		}
		// once the groups are all finished,
		// close the result group channel
		wg.Wait()
		close(res)
	}()
	return
}

// makeGroup handles an individual group.  It will never block on
// a recv from groupChan
func makeGroup(in chan fooBar, res chan (chan fooBar), wg *sync.WaitGroup, n int) {

	groupChan := make(chan fooBar, n)
	sendChan := groupChan
	sentTup := true
	tupBuf := make([]fooBar, 0) // a stack of tuples
	var v fooBar

	for {
		if bufLen := len(tupBuf); sentTup && bufLen > 0 {
			// there is a need to send a new value
			// pop one of the tuples out of the buffer
			v, tupBuf = tupBuf[bufLen-1], tupBuf[:bufLen-1]
			sentTup = false
			sendChan = groupChan
		} else {
			if sentTup {
				if in == nil {
					// there is nothing left to send
					break
				}
				sendChan = nil
			}
		}
		select {
		case res <- groupChan:
			res = nil
		case sendChan <- v:
			sentTup = true
		case tup, ok := <-in:
			if !ok {
				// input channel is closed
				in = nil
				break
			}
			tupBuf = append(tupBuf, tup)
		}
	}
	wg.Done()
	close(groupChan)
}

// benchmark

func benchGroupUnordered(b *testing.B, tupN, fooN, barN int) {
	mc := runtime.GOMAXPROCS(2)
	in := makeFooBar(tupN, fooN, barN)
	b.StopTimer()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		res := make(chan (chan fooBar), 1)

		b.StartTimer()
		groupUnordered(fooBarChan(in, 1), res, 2)

		emptyFooBarGroup(res)
		b.StopTimer()
	}
	runtime.GOMAXPROCS(mc)

}

// benchmarks for group with cardinality ~3
func BenchmarkGroupUnordered_10_3_1(b *testing.B) {
	benchGroupUnordered(b, 10, 3, 1)
}
func BenchmarkGroupUnordered_100_3_1(b *testing.B) {
	benchGroupUnordered(b, 100, 3, 1)
}
func BenchmarkGroupUnordered_1000_3_1(b *testing.B) {
	benchGroupUnordered(b, 1000, 3, 1)
}
func BenchmarkGroupUnordered_10000_3_1(b *testing.B) {
	benchGroupUnordered(b, 10000, 3, 1)
}
func BenchmarkGroupUnordered_100000_3_1(b *testing.B) {
	benchGroupUnordered(b, 100000, 3, 1)
}
func BenchmarkGroupUnordered_1000000_3_1(b *testing.B) {
	benchGroupUnordered(b, 1000000, 3, 1)
}

// benchmarks for group with cardinality ~1000
func BenchmarkGroupUnordered_10_1000_1(b *testing.B) {
	benchGroupUnordered(b, 10, 1000, 1)
}
func BenchmarkGroupUnordered_100_1000_1(b *testing.B) {
	benchGroupUnordered(b, 100, 1000, 1)
}
func BenchmarkGroupUnordered_1000_1000_1(b *testing.B) {
	benchGroupUnordered(b, 1000, 1000, 1)
}
func BenchmarkGroupUnordered_10000_1000_1(b *testing.B) {
	benchGroupUnordered(b, 10000, 1000, 1)
}
func BenchmarkGroupUnordered_100000_1000_1(b *testing.B) {
	benchGroupUnordered(b, 100000, 1000, 1)
}
func BenchmarkGroupUnordered_1000000_1000_1(b *testing.B) {
	benchGroupUnordered(b, 1000000, 1000, 1)
}

// benchmarks for group with cardinality ~100000
func BenchmarkGroupUnordered_10_100000_1(b *testing.B) {
	benchGroupUnordered(b, 10, 100000, 1)
}
func BenchmarkGroupUnordered_100_100000_1(b *testing.B) {
	benchGroupUnordered(b, 100, 100000, 1)
}
func BenchmarkGroupUnordered_1000_100000_1(b *testing.B) {
	benchGroupUnordered(b, 1000, 100000, 1)
}
func BenchmarkGroupUnordered_10000_100000_1(b *testing.B) {
	benchGroupUnordered(b, 10000, 100000, 1)
}
func BenchmarkGroupUnordered_100000_100000_1(b *testing.B) {
	benchGroupUnordered(b, 100000, 100000, 1)
}
func BenchmarkGroupUnordered_1000000_100000_1(b *testing.B) {
	benchGroupUnordered(b, 1000000, 100000, 1)
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = true
	DEFAULT_MESSAGE_UNFURL_MEDIA = false
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
)

type chatResponseFull struct {
	ChannelId string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Fallback string `json:"fallback"`

	Color string `json:"color,omitempty"`

	Pretext string `json:"pretext,omitempty"`

	AuthorName string `json:"author_name,omitempty"`
	AuthorLink string `json:"author_link,omitempty"`
	AuthorIcon string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`

	Text string `json:"text"`

	ImageURL string `json:"image_url,omitempty"`

	Fields []AttachmentField `json:"fields,omitempty"`
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string
	Username    string
	AsUser      bool `json:"as_user,omitempty"`
	Parse       string
	LinkNames   int
	Attachments []Attachment
	UnfurlLinks bool
	UnfurlMedia bool
	IconURL     string
	IconEmoji   string
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Slack) DeleteMessage(channelId, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

func escapeMessage(message string) string {
	/*
		& replaced with &
		< replaced with <
		> replaced with >
	*/
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel
// Message is automatically escaped according to https://api.slack.com/docs/formatting
func (api *Slack) PostMessage(channelId string, text string, params PostMessageParameters) (channel string, timestamp string, err error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "true")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Slack) UpdateMessage(channelId, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.ChannelId, response.Timestamp, response.Text, nil
}
<commit_msg>Add markdown support<commit_after>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = true
	DEFAULT_MESSAGE_UNFURL_MEDIA = false
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
	DEFAULT_MESSAGE_MARKDOWN     = true
)

type chatResponseFull struct {
	ChannelId string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Fallback string `json:"fallback"`

	Color string `json:"color,omitempty"`

	Pretext string `json:"pretext,omitempty"`

	AuthorName string `json:"author_name,omitempty"`
	AuthorLink string `json:"author_link,omitempty"`
	AuthorIcon string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`

	Text string `json:"text"`

	ImageURL string `json:"image_url,omitempty"`

	Fields []AttachmentField `json:"fields,omitempty"`

	MarkdownIn []string `json:"mrkdwn_in,omitempty"`
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string
	Username    string
	AsUser      bool `json:"as_user,omitempty"`
	Parse       string
	LinkNames   int
	Attachments []Attachment
	UnfurlLinks bool
	UnfurlMedia bool
	IconURL     string
	IconEmoji   string
	Markdown    bool `json:"mrkdwn,omitempty"`
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Slack) DeleteMessage(channelId, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

func escapeMessage(message string) string {
	/*
		& replaced with &
		< replaced with <
		> replaced with >
	*/
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel
// Message is automatically escaped according to https://api.slack.com/docs/formatting
func (api *Slack) PostMessage(channelId string, text string, params PostMessageParameters) (channel string, timestamp string, err error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "true")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
		values.Set("mrkdwn", "false")
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Slack) UpdateMessage(channelId, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.ChannelId, response.Timestamp, response.Text, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"fmt"
	"os"
	"strings"
	"text/template"
)

var tpl = `package {{.Package}}

import (
	"io/ioutil"
	"net/http"

	"code.uber.internal/rt/edge-gateway"
	{{.DownstreamService}}Client "code.uber.internal/rt/edge-gateway/clients/{{.DownstreamService}}"

	"github.com/pkg/errors"
)

func Handle{{.MyHandler | Title}}Request(
	inc *gateway.IncomingMessage,
	gateway *gateway.EdgeGateway,
) {
	rawBody, ok := inc.ReadAll()
	if !ok {
		return
	}

	var body {{.DownstreamService}}Client.{{.MyHandler | Title}}
	if ok := inc.UnmarshalBody(&body, rawBody); !ok {
		return
	}

	h := make(http.Header)
	h.Set("x-uber-uuid", inc.Header.Get("x-uber-uuid"))

	clientBody := convertToClient(&body)
	clientResp, err := g.Clients.{{.DownstreamService}}.{{.DownstreamMethod | Title}}(&body, h)
	if err != nil {
		gateway.Logger.Error("Could not make client request",
			zap.String("error", err.Error()),
		)
		inc.SendError(500, errors.Wrap(err, "Could not make client request:"))
		return
	}

	defer func() {
		if err := clientResp.Body.Close(); err != nil {
			inc.SendError(500, errors.Wrap(err, "Could not close client response body:"))
			return
		}
	}()
	b, err := ioutil.ReadAll(clientResp.Body)
	if err != nil {
		inc.SendError(500, errors.Wrap(err, "Could not read client response body:"))
		return
	}

	if !isOKResponse(clientResp.StatusCode, []int{200, 202, 204}) {
		inc.SendErrorString(clientResp.StatusCode, string(b))
		return
	}

	// TODO(sindelar): Apply response filtering and translation.
	inc.CopyJSON(res.Res.StatusCode, res.Res.Body)
}

func convertToClient(
	body *{{.MyHandler | Title}},
) *{{.DownstreamService}}Client.{{.DownstreamMethod | Title}} {
	// TODO(sindelar): Implement this for more complex mappings.
	}
}
`

func main() {
	funcMap := template.FuncMap{
		"Title": strings.Title,
	}

	tt := template.Must(template.New("Handler").Funcs(funcMap).Parse(tpl))
    prefix := os.Args[1]

    // Iterate over all passed in endpoints.
	for i := 2; i < len(os.Args); i++ {
        endpoint := os.Args[i]
        endpointDir := prefix + string(os.PathSeparator) + strings.ToLower(endpoint)
        os.Mkdir(endpointDir, 0755)
        // PLACEHOLDER, REPLACE WITH HANDLERS FROM GENERATED CODE.
        // Read all handlers for an endpoint and generate each.
		handlers := []string{"foo"}
 		for j := 0; j < len(handlers); j++ {
			dest := endpointDir + string(os.PathSeparator) + strings.ToLower(handlers[j]) + "_handler.go"
			file, err := os.Create(dest)
			if err != nil {
				fmt.Printf("Could not create %s: %s (skip)\n", dest, err)
				continue
			}
			// TODO(sindelar): Use an endpoint to client map.
			downstreamService := endpoint
			downstreamMethod := os.Args[i]

			vals := map[string]string{
				"MyHandler": handlers[j],
				"Package": endpoint,
				"DownstreamService": downstreamService,
				"DownstreamMethod": downstreamMethod,
			}
			tt.Execute(file, vals)

			file.Close()
		}
	}
}
<commit_msg>lib/gencode/endpoint.go<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"fmt"
	"os"
	"strings"
	"text/template"
)

var tpl = `package {{.Package}}

import (
	"io/ioutil"
	"net/http"

	"code.uber.internal/rt/edge-gateway"
	{{.DownstreamService}}Client "code.uber.internal/rt/edge-gateway/clients/{{.DownstreamService}}"

	"github.com/pkg/errors"
)

func Handle{{.MyHandler | Title}}Request(
	inc *gateway.IncomingMessage,
	gateway *gateway.EdgeGateway,
) {
	rawBody, ok := inc.ReadAll()
	if !ok {
		return
	}

	var body {{.DownstreamService}}Client.{{.MyHandler | Title}}
	if ok := inc.UnmarshalBody(&body, rawBody); !ok {
		return
	}

	h := make(http.Header)
	h.Set("x-uber-uuid", inc.Header.Get("x-uber-uuid"))

	clientBody := convertToClient(&body)
	clientResp, err := g.Clients.{{.DownstreamService}}.{{.DownstreamMethod | Title}}(&body, h)
	if err != nil {
		gateway.Logger.Error("Could not make client request",
			zap.String("error", err.Error()),
		)
		inc.SendError(500, errors.Wrap(err, "Could not make client request:"))
		return
	}

	defer func() {
		if err := clientResp.Body.Close(); err != nil {
			inc.SendError(500, errors.Wrap(err, "Could not close client response body:"))
			return
		}
	}()
	b, err := ioutil.ReadAll(clientResp.Body)
	if err != nil {
		inc.SendError(500, errors.Wrap(err, "Could not read client response body:"))
		return
	}

	if !isOKResponse(clientResp.StatusCode, []int{200, 202, 204}) {
		inc.SendErrorString(clientResp.StatusCode, string(b))
		return
	}

	// TODO(sindelar): Apply response filtering and translation.
	inc.CopyJSON(res.Res.StatusCode, res.Res.Body)
}

func convertToClient(
	body *{{.MyHandler | Title}},
) *{{.DownstreamService}}Client.{{.DownstreamMethod | Title}} {
	clientBody := &{{.DownstreamService}}Client.{{.DownstreamMethod | Title}}
    // TODO(sindelar): Add field mappings here.
    return body
	}
}
`

func main() {
	funcMap := template.FuncMap{
		"Title": strings.Title,
	}

	tt := template.Must(template.New("Handler").Funcs(funcMap).Parse(tpl))
    prefix := os.Args[1]

    // Iterate over all passed in endpoints.
	for i := 2; i < len(os.Args); i++ {
        endpoint := os.Args[i]
        endpointDir := prefix + string(os.PathSeparator) + strings.ToLower(endpoint)
        os.Mkdir(endpointDir, 0755)
        // PLACEHOLDER, REPLACE WITH HANDLERS FROM GENERATED CODE.
        // Read all handlers for an endpoint and generate each.
		handlers := []string{"foo"}
 		for j := 0; j < len(handlers); j++ {
			dest := endpointDir + string(os.PathSeparator) + strings.ToLower(handlers[j]) + "_handler.go"
			file, err := os.Create(dest)
			if err != nil {
				fmt.Printf("Could not create %s: %s (skip)\n", dest, err)
				continue
			}
			// TODO(sindelar): Use an endpoint to client map.
			downstreamService := endpoint
			downstreamMethod := os.Args[i]

			vals := map[string]string{
				"MyHandler": handlers[j],
				"Package": endpoint,
				"DownstreamService": downstreamService,
				"DownstreamMethod": downstreamMethod,
			}
			tt.Execute(file, vals)

			file.Close()
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package cors provides handlers to enable CORS support.
package cors

import (
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	headerOrigin         = "Origin"
	headerRequestMethod  = "Access-Control-Request-Method"
	headerRequestHeaders = "Access-Control-Request-Headers"
)

var (
	defaultAllowHeaders = []string{"Origin", "Accept", "Content-Type", "Authorization"}
	// Regex patterns are generated from AllowOrigins. These are used and generated internally.
	allowOriginPatterns = []string{}
)

// Options represents Access Control options.
type Options struct {
	// If set, all origins are allowed.
	AllowAllOrigins bool
	// A list of allowed origins. Wild cards and FQDNs are supported.
	AllowOrigins []string
	// If set, allows to share auth credentials such as cookies.
	AllowCredentials bool
	// A list of allowed HTTP methods.
	AllowMethods []string
	// A list of allowed HTTP headers.
	AllowHeaders []string
	// A list of exposed HTTP headers.
	ExposeHeaders []string
	// Max age of the CORS headers.
	MaxAge time.Duration
}

// Header converts options into CORS headers.
func (o *Options) Header(origin string) (headers map[string]string) {
	headers = make(map[string]string)
	// if origin is not allowed, don't extend the headers
	// with CORS headers.
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}

	// add allow origin
	if o.AllowAllOrigins {
		headers[headerAllowOrigin] = "*"
	} else {
		headers[headerAllowOrigin] = origin
	}

	if (o.AllowCredentials) {
		// add allow credentials
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow methods
	if len(o.AllowMethods) > 0 {
		headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
	}

	// add allow headers
	if len(o.AllowHeaders) > 0 {
		// TODO: Add default headers
		headers[headerAllowHeaders] = strings.Join(o.AllowHeaders, ",")
	}

	// add exposed header
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// PreflightHeader converts options into CORS headers for a preflight response.
func (o *Options) PreflightHeader(origin, rMethod, rHeaders string) (headers map[string]string) {
	headers = make(map[string]string)
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}
	// verify if requested method is allowed
	// TODO: Too many for loops
	for _, method := range o.AllowMethods {
		if method == rMethod {
			headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
			break
		}
	}

	// verify if requested headers are allowed
	var allowed []string
	for _, rHeader := range strings.Split(rHeaders, ",") {
		rHeader = strings.TrimSpace(rHeader)
	lookupLoop:
		for _, allowedHeader := range o.AllowHeaders {
			if strings.ToLower(rHeader) == strings.ToLower(allowedHeader) {
				allowed = append(allowed, rHeader)
				break lookupLoop
			}
		}
	}

	if (o.AllowCredentials) {
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow origin
	if o.AllowAllOrigins {
		headers[headerAllowOrigin] = "*"
	} else {
		headers[headerAllowOrigin] = origin
	}

	// add allowed headers
	if len(allowed) > 0 {
		headers[headerAllowHeaders] = strings.Join(allowed, ",")
	}

	// add exposed headers
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// IsOriginAllowed looks up if the origin matches one of the patterns
// generated from Options.AllowOrigins patterns.
func (o *Options) IsOriginAllowed(origin string) (allowed bool) {
	for _, pattern := range allowOriginPatterns {
		allowed, _ = regexp.MatchString(pattern, origin)
		if allowed {
			return
		}
	}
	return
}

// Allow enables CORS for requests those match the provided options.
func (o *Options) Allow(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	// Allow default headers if nothing is specified.
	if len(o.AllowHeaders) == 0 {
		o.AllowHeaders = defaultAllowHeaders
	}

	for _, origin := range o.AllowOrigins {
		pattern := regexp.QuoteMeta(origin)
		pattern = strings.Replace(pattern, "\\*", ".*", -1)
		pattern = strings.Replace(pattern, "\\?", ".", -1)
		allowOriginPatterns = append(allowOriginPatterns, "^"+pattern+"$")
	}

	var (
		origin           = req.Header.Get(headerOrigin)
		requestedMethod  = req.Header.Get(headerRequestMethod)
		requestedHeaders = req.Header.Get(headerRequestHeaders)
		// additional headers to be added
		// to the response.
		headers map[string]string
	)

	if req.Method == "OPTIONS" &&
		(requestedMethod != "" || requestedHeaders != "") {
		// TODO: if preflight, respond with exact headers if allowed
		headers = o.PreflightHeader(origin, requestedMethod, requestedHeaders)
		for key, value := range headers {
			res.Header().Set(key, value)
		}
		res.WriteHeader(http.StatusOK)
		return
	}
	headers = o.Header(origin)

	for key, value := range headers {
		res.Header().Set(key, value)
	}

	next(res, req)

}
<commit_msg>Set origin to requesting origin if allowed even if allow all is on. Helps w/ credential flag which doesn't support '*' allow origin.<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package cors provides handlers to enable CORS support.
package cors

import (
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	headerOrigin         = "Origin"
	headerRequestMethod  = "Access-Control-Request-Method"
	headerRequestHeaders = "Access-Control-Request-Headers"
)

var (
	defaultAllowHeaders = []string{"Origin", "Accept", "Content-Type", "Authorization"}
	// Regex patterns are generated from AllowOrigins. These are used and generated internally.
	allowOriginPatterns = []string{}
)

// Options represents Access Control options.
type Options struct {
	// If set, all origins are allowed.
	AllowAllOrigins bool
	// A list of allowed origins. Wild cards and FQDNs are supported.
	AllowOrigins []string
	// If set, allows to share auth credentials such as cookies.
	AllowCredentials bool
	// A list of allowed HTTP methods.
	AllowMethods []string
	// A list of allowed HTTP headers.
	AllowHeaders []string
	// A list of exposed HTTP headers.
	ExposeHeaders []string
	// Max age of the CORS headers.
	MaxAge time.Duration
}

// Header converts options into CORS headers.
func (o *Options) Header(origin string) (headers map[string]string) {
	headers = make(map[string]string)
	// if origin is not allowed, don't extend the headers
	// with CORS headers.
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}

	// add allow origin
	headers[headerAllowOrigin] = origin

	if (o.AllowCredentials) {
		// add allow credentials
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow methods
	if len(o.AllowMethods) > 0 {
		headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
	}

	// add allow headers
	if len(o.AllowHeaders) > 0 {
		// TODO: Add default headers
		headers[headerAllowHeaders] = strings.Join(o.AllowHeaders, ",")
	} else {
		headers[headerAllowHeaders] = strings.Join(defaultAllowHeaders, ",")
	}

	// add exposed header
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// PreflightHeader converts options into CORS headers for a preflight response.
func (o *Options) PreflightHeader(origin, rMethod, rHeaders string) (headers map[string]string) {
	headers = make(map[string]string)
	if !o.AllowAllOrigins && !o.IsOriginAllowed(origin) {
		return
	}
	// verify if requested method is allowed
	// TODO: Too many for loops
	for _, method := range o.AllowMethods {
		if method == rMethod {
			headers[headerAllowMethods] = strings.Join(o.AllowMethods, ",")
			break
		}
	}

	allowHeaders := o.AllowHeaders
	if len(allowHeaders) == 0 {
		allowHeaders = defaultAllowHeaders
	}
	// verify if requested headers are allowed
	var allowed []string
	for _, rHeader := range strings.Split(rHeaders, ",") {
		rHeader = strings.TrimSpace(rHeader)
	lookupLoop:
		for _, allowedHeader := range allowHeaders {
			if strings.ToLower(rHeader) == strings.ToLower(allowedHeader) {
				allowed = append(allowed, rHeader)
				break lookupLoop
			}
		}
	}

	if (o.AllowCredentials) {
		headers[headerAllowCredentials] = strconv.FormatBool(o.AllowCredentials)
	}

	// add allow origin
	headers[headerAllowOrigin] = origin

	// add allowed headers
	if len(allowed) > 0 {
		headers[headerAllowHeaders] = strings.Join(allowed, ",")
	}

	// add exposed headers
	if len(o.ExposeHeaders) > 0 {
		headers[headerExposeHeaders] = strings.Join(o.ExposeHeaders, ",")
	}
	// add a max age header
	if o.MaxAge > time.Duration(0) {
		headers[headerMaxAge] = strconv.FormatInt(int64(o.MaxAge/time.Second), 10)
	}
	return
}

// IsOriginAllowed looks up if the origin matches one of the patterns
// generated from Options.AllowOrigins patterns.
func (o *Options) IsOriginAllowed(origin string) (allowed bool) {
	for _, pattern := range allowOriginPatterns {
		allowed, _ = regexp.MatchString(pattern, origin)
		if allowed {
			return
		}
	}
	return
}

// Allow enables CORS for requests those match the provided options.
func (o *Options) Allow(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	for _, origin := range o.AllowOrigins {
		pattern := regexp.QuoteMeta(origin)
		pattern = strings.Replace(pattern, "\\*", ".*", -1)
		pattern = strings.Replace(pattern, "\\?", ".", -1)
		allowOriginPatterns = append(allowOriginPatterns, "^"+pattern+"$")
	}

	var (
		origin           = req.Header.Get(headerOrigin)
		requestedMethod  = req.Header.Get(headerRequestMethod)
		requestedHeaders = req.Header.Get(headerRequestHeaders)
		// additional headers to be added
		// to the response.
		headers map[string]string
	)

	if req.Method == "OPTIONS" &&
		(requestedMethod != "" || requestedHeaders != "") {
		// TODO: if preflight, respond with exact headers if allowed
		headers = o.PreflightHeader(origin, requestedMethod, requestedHeaders)
		for key, value := range headers {
			res.Header().Set(key, value)
		}
		res.WriteHeader(http.StatusOK)
		return
	}
	headers = o.Header(origin)

	for key, value := range headers {
		res.Header().Set(key, value)
	}

	next(res, req)

}
<|endoftext|>


<commit_before>package cors

import "net/http"

const (
	options           string = "OPTIONS"
	allow_origin      string = "Access-Control-Allow-Origin"
	allow_methods     string = "Access-Control-Allow-Methods"
	allow_headers     string = "Access-Control-Allow-Headers"
	allow_credentials string = "Access-Control-Allow-Credentials"
	expose_headers    string = "Access-Control-Expose-Headers"
	credentials       string = "true"
	origin            string = "Origin"
	methods           string = "POST, GET, OPTIONS, PUT, DELETE, HEAD, PATCH"

	// If you want to expose some other headers add it here
	headers string = "Accept, Accept-Encoding, Authorization, Content-Length, Content-Type, X-CSRF-Token"
)

// Handler will allow cross-origin HTTP requests
func CORS(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Set allow origin to match origin of our request or fall back to *
		if o := r.Header.Get(origin); o != "" {
			w.Header().Set(allow_origin, o)
		} else {
			w.Header().Set(allow_origin, "*")
		}

		// Set other headers
		w.Header().Set(allow_headers, headers)
		w.Header().Set(allow_methods, methods)
		w.Header().Set(allow_credentials, credentials)
		w.Header().Set(expose_headers, headers)

		// If this was preflight options request let's write empty ok response and return
		if r.Method == options {
			w.WriteHeader(http.StatusOK)
			w.Write(nil)
			return
		}

		next.ServeHTTP(w, r)
	})
}
<commit_msg>Added Access-Control-Allow-Origin to allowed request headers<commit_after>package cors

import "net/http"

const (
	options           string = "OPTIONS"
	allow_origin      string = "Access-Control-Allow-Origin"
	allow_methods     string = "Access-Control-Allow-Methods"
	allow_headers     string = "Access-Control-Allow-Headers"
	allow_credentials string = "Access-Control-Allow-Credentials"
	expose_headers    string = "Access-Control-Expose-Headers"
	credentials       string = "true"
	origin            string = "Origin"
	methods           string = "POST, GET, OPTIONS, PUT, DELETE, HEAD, PATCH"

	// If you want to expose some other headers add it here
	headers string = "Access-Control-Allow-Origin, Accept, Accept-Encoding, Authorization, Content-Length, Content-Type, X-CSRF-Token"
)

// Handler will allow cross-origin HTTP requests
func CORS(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Set allow origin to match origin of our request or fall back to *
		if o := r.Header.Get(origin); o != "" {
			w.Header().Set(allow_origin, o)
		} else {
			w.Header().Set(allow_origin, "*")
		}

		// Set other headers
		w.Header().Set(allow_headers, headers)
		w.Header().Set(allow_methods, methods)
		w.Header().Set(allow_credentials, credentials)
		w.Header().Set(expose_headers, headers)

		// If this was preflight options request let's write empty ok response and return
		if r.Method == options {
			w.WriteHeader(http.StatusOK)
			w.Write(nil)
			return
		}

		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package thermal

import (
	"code.google.com/p/go.crypto/nacl/box"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"code.google.com/p/go.crypto/poly1305"
	"crypto/rand"
	"crypto/sha256"
	"fmt"
	"log"
)

// cs3a is an implementation of the NaCl based cipher set 3a
type cs3a struct {
	id             string
	fingerprintBin []byte
	fingerprintHex string
	publicKey      [32]byte
	privateKey     [32]byte
}

// initialize generates a key pair and sets up the cipher set
func (cs *cs3a) initialize() error {

	// generate the key pair
	publicKey, privateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair in cs3a initialization")
		return err
	}

	// generate the fingerprint hash
	hash256 := sha256.New()
	hash256.Write(publicKey[:])
	fingerprintBin := hash256.Sum(nil)
	fingerprintHex := fmt.Sprintf("%x", fingerprintBin)

	// initialize the struct
	cs.id = "cs3a"
	cs.fingerprintBin = fingerprintBin
	cs.fingerprintHex = fingerprintHex
	cs.publicKey = *publicKey
	cs.privateKey = *privateKey

	return nil

}

func (cs *cs3a) String() string {
	return fmt.Sprintf("%s: %x", cs.id, cs.fingerprint)
}

func (cs *cs3a) csid() string {
	return cs.id
}

// fingerprint returns the csid and fingerprint for use in a 'parts' set
func (cs *cs3a) fingerprint() (string, string) {
	return cs.id, cs.fingerprintHex
}

func (cs *cs3a) encryptOpenPacket(packet []byte, receiverPublicKey *[32]byte) (openPacketBody []byte, err error) {

	// todo - store or return the line shared key for use in line packet encryption

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// line key pair
	linePublicKey, linePrivateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair for line")
		return openPacketBody, err
	}

	// Encrypt the inner packet
	var nonce [24]byte
	var lineSharedKey [32]byte
	var encInnerPacket []byte

	box.Precompute(&lineSharedKey, receiverPublicKey, linePrivateKey)
	secretbox.Seal(encInnerPacket, packet, &nonce, &lineSharedKey)

	// Generate the mac and assemble the body for the outer packet
	// <mac><sender-line-public-key><encrypted-inner-packet-data>
	var macKey [32]byte
	var mac [16]byte
	var openPacketData []byte

	box.Precompute(&macKey, receiverPublicKey, &cs.privateKey)
	openPacketData = append(linePublicKey[:], encInnerPacket...)
	poly1305.Sum(&mac, openPacketData, &macKey)
	openPacketBody = append(mac[:], openPacketData...)

	return openPacketBody, nil

}

func (cs *cs3a) decryptOpenPacket(openPacketBody []byte, senderPublicKey *[32]byte) (packet []byte, err error) {

	// todo - store or return the line shared key for use in line packet encryption

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// Unpack the outer packet body
	// <mac><sender-line-public-key><encrypted-inner-packet-data>
	var mac [16]byte
	var senderLinePublicKey [32]byte
	var encInnerPacket []byte
	var openPacketData []byte

	copy(mac[:], openPacketBody[:16])
	copy(senderLinePublicKey[:], openPacketBody[16:48])
	copy(encInnerPacket[:], openPacketBody[48:])
	openPacketData = append(senderLinePublicKey[:], encInnerPacket...)

	// Verify the mac
	var authenticated bool
	var macKey [32]byte

	box.Precompute(&macKey, senderPublicKey, &cs.privateKey)
	authenticated = poly1305.Verify(&mac, openPacketData, &macKey)
	if !authenticated {
		msg := "Incoming open packet failed MAC authentication"
		log.Println(msg)
		err = fmt.Errorf(msg)
		return packet, err
	}

	// Decrypt the inner packet
	var nonce [24]byte
	var lineSharedKey [32]byte

	box.Precompute(&lineSharedKey, &senderLinePublicKey, &cs.privateKey)
	secretbox.Open(packet, encInnerPacket, &nonce, &lineSharedKey)

	return packet, nil
}

func (cs *cs3a) encryptLinePacket(packet []byte) (linePacketBody []byte) {
	return linePacketBody
}

func (cs *cs3a) decryptLinePacket(linePacketBody []byte) (packet []byte) {
	return packet
}
<commit_msg>clarifcation of two different shared line keys<commit_after>package thermal

import (
	"code.google.com/p/go.crypto/nacl/box"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"code.google.com/p/go.crypto/poly1305"
	"crypto/rand"
	"crypto/sha256"
	"fmt"
	"log"
)

// cs3a is an implementation of the NaCl based cipher set 3a
type cs3a struct {
	id             string
	fingerprintBin []byte
	fingerprintHex string
	publicKey      [32]byte
	privateKey     [32]byte
}

// initialize generates a key pair and sets up the cipher set
func (cs *cs3a) initialize() error {

	// generate the key pair
	publicKey, privateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair in cs3a initialization")
		return err
	}

	// generate the fingerprint hash
	hash256 := sha256.New()
	hash256.Write(publicKey[:])
	fingerprintBin := hash256.Sum(nil)
	fingerprintHex := fmt.Sprintf("%x", fingerprintBin)

	// initialize the struct
	cs.id = "cs3a"
	cs.fingerprintBin = fingerprintBin
	cs.fingerprintHex = fingerprintHex
	cs.publicKey = *publicKey
	cs.privateKey = *privateKey

	return nil

}

func (cs *cs3a) String() string {
	return fmt.Sprintf("%s: %x", cs.id, cs.fingerprint)
}

func (cs *cs3a) csid() string {
	return cs.id
}

// fingerprint returns the csid and fingerprint for use in a 'parts' set
func (cs *cs3a) fingerprint() (string, string) {
	return cs.id, cs.fingerprintHex
}

func (cs *cs3a) encryptOpenPacket(packet []byte, receiverPublicKey *[32]byte) (openPacketBody []byte, err error) {

	// todo - store the egress line shared key for use in line packet encryption

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// line key pair
	linePublicKey, linePrivateKey, err := box.GenerateKey(rand.Reader)
	if err != nil {
		log.Println("Error generating NaCl keypair for line")
		return openPacketBody, err
	}

	// Encrypt the inner packet
	var nonce [24]byte
	var lineSharedKey [32]byte
	var encInnerPacket []byte

	box.Precompute(&lineSharedKey, receiverPublicKey, linePrivateKey)
	secretbox.Seal(encInnerPacket, packet, &nonce, &lineSharedKey)

	// Generate the mac and assemble the body for the outer packet
	// <mac><sender-line-public-key><encrypted-inner-packet-data>
	var macKey [32]byte
	var mac [16]byte
	var openPacketData []byte

	box.Precompute(&macKey, receiverPublicKey, &cs.privateKey)
	openPacketData = append(linePublicKey[:], encInnerPacket...)
	poly1305.Sum(&mac, openPacketData, &macKey)
	openPacketBody = append(mac[:], openPacketData...)

	return openPacketBody, nil

}

func (cs *cs3a) decryptOpenPacket(openPacketBody []byte, senderPublicKey *[32]byte) (packet []byte, err error) {

	// todo - store the ingress line shared key for use in line packet decryption

	// switch key pair
	// cs.publicKey and cs.privateKey should already be populated

	// Unpack the outer packet body
	// <mac><sender-line-public-key><encrypted-inner-packet-data>
	var mac [16]byte
	var senderLinePublicKey [32]byte
	var encInnerPacket []byte
	var openPacketData []byte

	copy(mac[:], openPacketBody[:16])
	copy(senderLinePublicKey[:], openPacketBody[16:48])
	copy(encInnerPacket[:], openPacketBody[48:])
	openPacketData = append(senderLinePublicKey[:], encInnerPacket...)

	// Verify the mac
	var authenticated bool
	var macKey [32]byte

	box.Precompute(&macKey, senderPublicKey, &cs.privateKey)
	authenticated = poly1305.Verify(&mac, openPacketData, &macKey)
	if !authenticated {
		msg := "Incoming open packet failed MAC authentication"
		log.Println(msg)
		err = fmt.Errorf(msg)
		return packet, err
	}

	// Decrypt the inner packet
	var nonce [24]byte
	var lineSharedKey [32]byte

	box.Precompute(&lineSharedKey, &senderLinePublicKey, &cs.privateKey)
	secretbox.Open(packet, encInnerPacket, &nonce, &lineSharedKey)

	return packet, nil
}

func (cs *cs3a) encryptLinePacket(packet []byte) (linePacketBody []byte) {
	return linePacketBody
}

func (cs *cs3a) decryptLinePacket(linePacketBody []byte) (packet []byte) {
	return packet
}
<|endoftext|>


<commit_before>package acquia

import (
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"net/http"
	"net"
)

type AcquiaServerState struct {
	Subscriptions []*Subscription
	Tasks TaskList
}

func (ss *AcquiaServerState) Version() string {
	return "1.0"
}

// Starts up and serves an httpd of this API server, binding to the provided address.
//
// This function blocks; it should typically be called in a goroutine.
func (ss *AcquiaServerState) Serve(l net.Listener) {
	// The Addr prop shouldn't actually be used, but set it to avoid triggering defaults
	n := negroni.New()
	n.Use(negroni.NewRecovery())
	n.Use(negroni.NewLogger())
	n.UseHandler(ss.Router())

	srv := &http.Server{Addr: l.Addr().String(), Handler: n}
	srv.Serve(l) // blocks inside here
}

type Subscription struct {
	Name string
	Environments []*Environment
	Databases []*Database
	Users []*User
}

type Database struct {
	Name string
}

type Environment struct {
	Name string
	Domains []string
	CodeVersion string
}

type User struct {
	Name     string
	Email    string
	Password string
	Key      string
}

type Task struct {
	Id int
	Created int
	Started int
	Description string
	Completed bool
	Logs string
	Queue string
	Result string
	Sender string
	State int
}

type TaskList []*Task

func (tl TaskList) AddTask() *Task {
	t := &Task{Id: len(tl)}
	tl = append(tl, t)
	return t
}

// Creates a new AcquiaServerState, which acts as the basis for a mock API endpoint.
func NewServerInstance(subname string) *AcquiaServerState {
	aqs := &AcquiaServerState{
		Subscriptions: make([]*Subscription, 0),
		Tasks: make([]*Task, 0),
	}

	// Make a task to take up the zero index
	aqs.Tasks.AddTask()

	aqs.Subscriptions = append(aqs.Subscriptions, NewSubscription(subname))
	return aqs
}

func NewSubscription(name string) *Subscription {
	s := &Subscription{
		Name: name,
		Environments: []*Environment{
			&Environment{Name: "dev", Domains: make([]string, 0), CodeVersion: "tags/WELCOME"},
			&Environment{Name: "test", Domains: make([]string, 0), CodeVersion: "tags/WELCOME"},
			&Environment{Name: "prod", Domains: make([]string, 0), CodeVersion: "tags/WELCOME"},
		},
	}

	return s
}

func (ss *AcquiaServerState) Router() *mux.Router {
	router := mux.NewRouter()

	// Drush
	router.HandleFunc("/me/drushrc", ss.drushRcHandler).Methods("GET")
	// Tasks
	router.HandleFunc("/sites/{site}/tasks", ss.taskListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/tasks/{task}", ss.taskRecordHandler).Methods("GET")
	// Domains
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}", ss.domainDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}/cache", ss.domainVarnishPurgeHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/envs/{env}/domains", ss.domainListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}", ss.domainRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}", ss.domainAddHandler).Methods("POST")
	// Servers
	router.HandleFunc("/sites/{site}/envs/{env}/servers", ss.serverEnvListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/servers/{server}", ss.serverRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/servers/{server}/php-procs", ss.serverPhpProcsHandler).Methods("GET")
	// SSH Keys
	router.HandleFunc("/sites/{site}/sshkeys/{sshkeyid}", ss.sshkeyDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/sshkeys", ss.sshkeyListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/sshkeys/{sshkeyid}", ss.sshkeyRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/sshkeys", ss.sshkeyAddHandler).Methods("POST")
	// Databases
	router.HandleFunc("/sites/{site}/dbs/{db}", ss.dbDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/dbs", ss.dbListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/dbs", ss.dbCreateHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/dbs/{db}", ss.dbRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs", ss.dbEnvInstanceListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}", ss.dbEnvInstanceRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups", ss.dbEnvInstanceBackupListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups", ss.dbEnvInstanceBackupCreateHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}", ss.dbEnvInstanceBackupRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}", ss.dbEnvInstanceBackupDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}/download", ss.dbEnvInstanceBackupDownloadHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}/restore", ss.dbEnvInstanceBackupRestoreHandler).Methods("POST")
	// Workflow
	router.HandleFunc("/sites/{site}/code-deploy/{source}/{target}", ss.workflowCrossEnvCodeDeployHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/dbs/{db}/db-copy/{source}/{target}", ss.workflowCrossEnvDbCopyHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/domain-move/{source}/{target}", ss.workflowMoveDomainAcrossEnvsHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/envs/{env}/code-deploy", ss.workflowCodeDeployHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/files-copy/{source}/{target}", ss.workflowCopyFilesAcrossEnvsHandler).Methods("POST")
	// VCS users
	router.HandleFunc("/sites/{site}/svnusers", ss.vcsusersListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/svnusers/{svnuserid}", ss.vcsusersRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/svnusers/{svnuserid}", ss.vcsusersDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/svnusers/{username}", ss.vcsusersCreateHandler).Methods("POST")
	// Sites and Environments
	router.HandleFunc("/sites", ss.sitesListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}", ss.sitesRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs", ss.sitesEnvListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}", ss.sitesEnvRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/logstream", ss.sitesEnvLogstreamHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/install/{type}", ss.sitesEnvInstallHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/livedev/{action}", ss.sitesLivedevHandler).Methods("GET")

	return router
}

func (ss *AcquiaServerState) taskListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) drushRcHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) taskRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainVarnishPurgeHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainAddHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) serverEnvListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) serverRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) serverPhpProcsHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyAddHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbCreateHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupCreateHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupDownloadHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupRestoreHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCrossEnvCodeDeployHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCrossEnvDbCopyHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowMoveDomainAcrossEnvsHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCodeDeployHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCopyFilesAcrossEnvsHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersCreateHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvLogstreamHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvInstallHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesLivedevHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

<commit_msg>Add a buncha struct tags<commit_after>package acquia

import (
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"net/http"
	"net"
)

type AcquiaServerState struct {
	*http.Server `json:"-"`
	Subscriptions []*Subscription
	Tasks TaskList `json:"tasks"`
	Users []*User `json:"users"`
}

func (ss *AcquiaServerState) Version() string {
	return "1.0"
}

// Starts up and serves an httpd of this API server, binding to the provided address.
//
// This function blocks; it should typically be called in a goroutine.
func (ss *AcquiaServerState) Serve(l net.Listener) {
	// The Addr prop shouldn't actually be used, but set it to avoid triggering defaults
	n := negroni.New()
	n.Use(negroni.NewRecovery())
	n.Use(negroni.NewLogger())
	n.UseHandler(ss.Router())

	srv := &http.Server{Addr: l.Addr().String(), Handler: n}
	srv.Serve(l) // blocks inside here
}

type Subscription struct {
	Name string `json:"name"`
	Environments []*Environment `json:"environments"`
	Databases []*Database `json:"databases"`
	Users []*User `json:"users"`
}

type Database struct {
	Name string
}

type Environment struct {
	Name string `json:"name"`
	Domains []string `json:"domains"`
	CodeVersion string `json:"vcs_path"`
}

type User struct {
	Name     string
	Email    string
	Password string
	Key      string
}

type Task struct {
	Id int `json:"name"`
	Created int `json:"created"`
	Started int `json:"started"`
	Description string `json:"description"`
	Completed bool `json:"completed"`
	Logs string `json:"logs"`
	Queue string `json:"queue"`
	Result string `json:"result"`
	Sender string `json:"sender"`
	State int `json:"state"`
}

type TaskList []*Task

func (tl TaskList) AddTask() *Task {
	t := &Task{Id: len(tl)}
	tl = append(tl, t)
	return t
}

// Creates a new AcquiaServerState, which acts as the basis for a mock API endpoint.
func NewServerInstance(subname string) *AcquiaServerState {
	aqs := &AcquiaServerState{
		Subscriptions: make([]*Subscription, 0),
		Tasks: make([]*Task, 0),
	}

	// Make a task to take up the zero index
	aqs.Tasks.AddTask()

	aqs.Subscriptions = append(aqs.Subscriptions, NewSubscription(subname))
	return aqs
}

func NewSubscription(name string) *Subscription {
	s := &Subscription{
		Name: name,
		Environments: []*Environment{
			&Environment{Name: "dev", Domains: make([]string, 0), CodeVersion: "tags/WELCOME"},
			&Environment{Name: "test", Domains: make([]string, 0), CodeVersion: "tags/WELCOME"},
			&Environment{Name: "prod", Domains: make([]string, 0), CodeVersion: "tags/WELCOME"},
		},
	}

	return s
}

func (ss *AcquiaServerState) Router() *mux.Router {
	router := mux.NewRouter()

	// Drush
	router.HandleFunc("/me/drushrc", ss.drushRcHandler).Methods("GET")
	// Tasks
	router.HandleFunc("/sites/{site}/tasks", ss.taskListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/tasks/{task}", ss.taskRecordHandler).Methods("GET")
	// Domains
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}", ss.domainDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}/cache", ss.domainVarnishPurgeHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/envs/{env}/domains", ss.domainListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}", ss.domainRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/domains/{domain}", ss.domainAddHandler).Methods("POST")
	// Servers
	router.HandleFunc("/sites/{site}/envs/{env}/servers", ss.serverEnvListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/servers/{server}", ss.serverRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/servers/{server}/php-procs", ss.serverPhpProcsHandler).Methods("GET")
	// SSH Keys
	router.HandleFunc("/sites/{site}/sshkeys/{sshkeyid}", ss.sshkeyDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/sshkeys", ss.sshkeyListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/sshkeys/{sshkeyid}", ss.sshkeyRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/sshkeys", ss.sshkeyAddHandler).Methods("POST")
	// Databases
	router.HandleFunc("/sites/{site}/dbs/{db}", ss.dbDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/dbs", ss.dbListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/dbs", ss.dbCreateHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/dbs/{db}", ss.dbRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs", ss.dbEnvInstanceListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}", ss.dbEnvInstanceRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups", ss.dbEnvInstanceBackupListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups", ss.dbEnvInstanceBackupCreateHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}", ss.dbEnvInstanceBackupRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}", ss.dbEnvInstanceBackupDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}/download", ss.dbEnvInstanceBackupDownloadHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/dbs/{db}/backups/{backup}/restore", ss.dbEnvInstanceBackupRestoreHandler).Methods("POST")
	// Workflow
	router.HandleFunc("/sites/{site}/code-deploy/{source}/{target}", ss.workflowCrossEnvCodeDeployHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/dbs/{db}/db-copy/{source}/{target}", ss.workflowCrossEnvDbCopyHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/domain-move/{source}/{target}", ss.workflowMoveDomainAcrossEnvsHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/envs/{env}/code-deploy", ss.workflowCodeDeployHandler).Methods("POST")
	router.HandleFunc("/sites/{site}/files-copy/{source}/{target}", ss.workflowCopyFilesAcrossEnvsHandler).Methods("POST")
	// VCS users
	router.HandleFunc("/sites/{site}/svnusers", ss.vcsusersListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/svnusers/{svnuserid}", ss.vcsusersRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/svnusers/{svnuserid}", ss.vcsusersDeleteHandler).Methods("DELETE")
	router.HandleFunc("/sites/{site}/svnusers/{username}", ss.vcsusersCreateHandler).Methods("POST")
	// Sites and Environments
	router.HandleFunc("/sites", ss.sitesListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}", ss.sitesRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs", ss.sitesEnvListHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}", ss.sitesEnvRecordHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/logstream", ss.sitesEnvLogstreamHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/install/{type}", ss.sitesEnvInstallHandler).Methods("GET")
	router.HandleFunc("/sites/{site}/envs/{env}/livedev/{action}", ss.sitesLivedevHandler).Methods("GET")

	return router
}

func (ss *AcquiaServerState) taskListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) drushRcHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) taskRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainVarnishPurgeHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) domainAddHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) serverEnvListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) serverRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) serverPhpProcsHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sshkeyAddHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbCreateHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupCreateHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupDownloadHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) dbEnvInstanceBackupRestoreHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCrossEnvCodeDeployHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCrossEnvDbCopyHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowMoveDomainAcrossEnvsHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCodeDeployHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) workflowCopyFilesAcrossEnvsHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersDeleteHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) vcsusersCreateHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvListHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvRecordHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvLogstreamHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesEnvInstallHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

func (ss *AcquiaServerState) sitesLivedevHandler(w http.ResponseWriter, r *http.Request) {
	panic("not yet implemented")
}

<|endoftext|>


<commit_before>package gin

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"runtime"
	"time"
)

type Runner interface {
	Run() (*exec.Cmd, error)
	Info() (os.FileInfo, error)
	SetWriter(io.Writer)
	Kill() error
}

type runner struct {
	bin       string
	args      []string
	writer    io.Writer
	command   *exec.Cmd
	starttime time.Time
}

func NewRunner(bin string, args ...string) Runner {
	return &runner{
		bin:       bin,
		args:      args,
		writer:    ioutil.Discard,
		starttime: time.Now(),
	}
}

func (r *runner) Run() (*exec.Cmd, error) {
	if r.needsRefresh() {
		r.Kill()
	}

	if r.command == nil || r.Exited() {
		err := r.runBin()
		time.Sleep(1000 * time.Millisecond)
		return r.command, err
	} else {
		return r.command, nil
	}

}

func (r *runner) Info() (os.FileInfo, error) {
	return os.Stat(r.bin)
}

func (r *runner) SetWriter(writer io.Writer) {
	r.writer = writer
}

func (r *runner) Kill() error {
	if r.command != nil && r.command.Process != nil {
		done := make(chan error)
		go func() {
			r.command.Wait()
			close(done)
		}()

		//Trying a "soft" kill first
		if runtime.GOOS == "windows" {
			if err := r.command.Process.Kill(); err != nil {
				return err
			}
		} else if err := r.command.Process.Signal(os.Interrupt); err != nil {
			return err
		}

		//Wait for our process to die before we return or hard kill after 3 sec
		select {
		case <-time.After(3 * time.Second):
			if err := r.command.Process.Kill(); err != nil {
				log.Println("failed to kill: ", err)
			}
		case <-done:
		}
		r.command = nil
	}

	return nil
}

func (r *runner) Exited() bool {
	return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
}

func (r *runner) runBin() error {
	r.command = exec.Command(r.bin, r.args...)
	stdout, err := r.command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := r.command.StderrPipe()
	if err != nil {
		return err
	}

	err = r.command.Start()
	if err != nil {
		return err
	}

	r.starttime = time.Now()

	go io.Copy(r.writer, stdout)
	go io.Copy(r.writer, stderr)
	go r.command.Wait()

	return nil
}

func (r *runner) needsRefresh() bool {
	info, err := r.Info()
	if err != nil {
		return false
	} else {
		return info.ModTime().After(r.starttime)
	}
}
<commit_msg>Wait for server<commit_after>package gin

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"runtime"
	"time"
)

type Runner interface {
	Run() (*exec.Cmd, error)
	Info() (os.FileInfo, error)
	SetWriter(io.Writer)
	Kill() error
}

type runner struct {
	bin       string
	args      []string
	writer    io.Writer
	command   *exec.Cmd
	starttime time.Time
}

func NewRunner(bin string, args ...string) Runner {
	return &runner{
		bin:       bin,
		args:      args,
		writer:    ioutil.Discard,
		starttime: time.Now(),
	}
}

func (r *runner) Run() (*exec.Cmd, error) {
	if r.needsRefresh() {
		r.Kill()
	}

	for time.Now().Sub(r.starttime) < 1000*time.Millisecond {
		time.Sleep(250 * time.Millisecond)
	}

	if r.command == nil || r.Exited() {
		err := r.runBin()
		for time.Now().Sub(r.starttime) < 1000*time.Millisecond {
			time.Sleep(250 * time.Millisecond)
		}
		return r.command, err
	} else {
		return r.command, nil
	}

}

func (r *runner) Info() (os.FileInfo, error) {
	return os.Stat(r.bin)
}

func (r *runner) SetWriter(writer io.Writer) {
	r.writer = writer
}

func (r *runner) Kill() error {
	if r.command != nil && r.command.Process != nil {
		done := make(chan error)
		go func() {
			r.command.Wait()
			close(done)
		}()

		//Trying a "soft" kill first
		if runtime.GOOS == "windows" {
			if err := r.command.Process.Kill(); err != nil {
				return err
			}
		} else if err := r.command.Process.Signal(os.Interrupt); err != nil {
			return err
		}

		//Wait for our process to die before we return or hard kill after 3 sec
		select {
		case <-time.After(3 * time.Second):
			if err := r.command.Process.Kill(); err != nil {
				log.Println("failed to kill: ", err)
			}
		case <-done:
		}
		r.command = nil
	}

	return nil
}

func (r *runner) Exited() bool {
	return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
}

func (r *runner) runBin() error {
	r.command = exec.Command(r.bin, r.args...)
	stdout, err := r.command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := r.command.StderrPipe()
	if err != nil {
		return err
	}

	err = r.command.Start()
	if err != nil {
		return err
	}

	r.starttime = time.Now()

	go io.Copy(r.writer, stdout)
	go io.Copy(r.writer, stderr)
	go r.command.Wait()

	return nil
}

func (r *runner) needsRefresh() bool {
	info, err := r.Info()
	if err != nil {
		return false
	} else {
		return info.ModTime().After(r.starttime)
	}
}
<|endoftext|>


<commit_before>package ims

import (
	"fmt"
	"image"
	"io"
	"net/http"
	"net/http/pprof"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/disintegration/imaging"
	"github.com/pkg/errors"
	"github.com/urfave/negroni"
)

// ProcessImage uses the github.com/disintegration/imaging lib to perform the
// image transformations.
func ProcessImage(timeout time.Duration, input io.Reader, w http.ResponseWriter, r *http.Request) error {
	srcImage, format, err := image.Decode(input)
	if err != nil {
		return errors.Wrap(err, "can't decode the image")
	}

	var filter imaging.ResampleFilter
	switch r.URL.Query().Get("resize-filter") {
	case "lanczos":
		filter = imaging.Lanczos
	case "nearest":
		filter = imaging.NearestNeighbor
	case "linear":
		filter = imaging.Linear
	case "netravali":
		filter = imaging.MitchellNetravali
	case "box":
		filter = imaging.Box
	default:
		filter = imaging.Lanczos
	}

	width, err := strconv.Atoi(r.URL.Query().Get("width"))
	if err == nil {
		srcImage = imaging.Resize(srcImage, width, 0, filter)
	} else {
		height, err := strconv.Atoi(r.URL.Query().Get("height"))
		if err == nil {
			srcImage = imaging.Resize(srcImage, 0, height, filter)
		}
	}

	if timeout != 0 {
		w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int64(timeout.Seconds())))
		w.Header().Set("Expires", time.Now().Add(timeout).Format(http.TimeFormat))
	}

	w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))

	encoder := GetEncoder(format, r)
	if err := encoder.Encode(srcImage, w); err != nil {
		return errors.Wrap(err, "can't encode the image")
	}

	return nil
}

// GetFilename fetches the filename from the request path.
func GetFilename(r *http.Request) (string, error) {

	// We expect that the router sends us requests in the form `/resize/:filename`
	// so we check to see if the path contains the image url that we want to
	// parse. In this case, we check to see that the path is at least 9 characters
	// long, which will ensure that the filename has at least 1 character.
	if len(r.URL.Path) < 9 {
		return "", errors.New("filename too short")
	}

	return r.URL.Path[8:], nil
}

// HandleFileSystemResize performs the actual resizing by loading the image
// from the filesystem.
func HandleFileSystemResize(timeout time.Duration, dir http.Dir) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		// Extract the filename from the request.
		filename, err := GetFilename(r)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Try to open the image from the virtual filesystem.
		f, err := dir.Open(filename)
		if err != nil {
			if _, ok := err.(*os.PathError); ok {
				http.Error(w, "Not Found", http.StatusNotFound)
				return
			}

			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer f.Close()

		// If an error occurred during the image processing, return with an internal
		// server error.
		if err := ProcessImage(timeout, f, w, r); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

// HandleOriginResize performs the actual resizing by loading the image
// from the origin.
func HandleOriginResize(timeout time.Duration, origin string) (http.HandlerFunc, error) {
	originURL, err := url.Parse(origin)
	if err != nil {
		return nil, errors.Wrap(err, "can't parse the origin url")
	}

	return func(w http.ResponseWriter, r *http.Request) {

		// Extract the filename from the request.
		filename, err := GetFilename(r)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Parse the incomming url.
		filenameURL, err := url.Parse(filename)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Resolve it relative to the origin url.
		fileURL := originURL.ResolveReference(filenameURL)

		// TODO: improve, this is quite naive.

		// Perform the GET to the origin server.
		req, err := http.NewRequest("GET", fileURL.String(), nil)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		res, err := http.DefaultClient.Do(req)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadGateway)
			return
		}
		defer res.Body.Close()

		// If an error occurred during the image processing, return with an internal
		// server error.
		if err := ProcessImage(timeout, res.Body, w, r); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}, nil
}

// Serve creates and starts a new server to provide image resizing services.
func Serve(addr string, debug bool, directory, origin string, timeout time.Duration) error {
	mux := http.NewServeMux()

	if timeout != 0 {
		logrus.WithField("timeout", timeout.String()).Debug("cache headers enabled")
	} else {
		logrus.Debug("cache headers disabled")
	}

	// When debug mode is enabled, mount the debug handlers on this router.
	if debug {
		mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
		mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
		mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
		mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
		mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
	}

	// By default, we'll try to use the directory resize, otherwise, if the origin
	// url is provided, use it.
	if origin == "" {
		logrus.WithField("directory", directory).Debug("serving from the filesystem")
		mux.HandleFunc("/resize/", HandleFileSystemResize(timeout, http.Dir(directory)))
	} else {
		logrus.WithField("origin", origin).Debug("serving from the origin")

		handler, err := HandleOriginResize(timeout, origin)
		if err != nil {
			return errors.Wrap(err, "can't create origin resize handler")
		}

		mux.HandleFunc("/", handler)
	}

	n := negroni.Classic() // Includes some default middlewares

	n.UseHandler(mux)

	logrus.Debugf("Now listening on %s", addr)
	return http.ListenAndServe(addr, n)
}
<commit_msg>fixed image route<commit_after>package ims

import (
	"fmt"
	"image"
	"io"
	"net/http"
	"net/http/pprof"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/disintegration/imaging"
	"github.com/pkg/errors"
	"github.com/urfave/negroni"
)

// ProcessImage uses the github.com/disintegration/imaging lib to perform the
// image transformations.
func ProcessImage(timeout time.Duration, input io.Reader, w http.ResponseWriter, r *http.Request) error {
	srcImage, format, err := image.Decode(input)
	if err != nil {
		return errors.Wrap(err, "can't decode the image")
	}

	var filter imaging.ResampleFilter
	switch r.URL.Query().Get("resize-filter") {
	case "lanczos":
		filter = imaging.Lanczos
	case "nearest":
		filter = imaging.NearestNeighbor
	case "linear":
		filter = imaging.Linear
	case "netravali":
		filter = imaging.MitchellNetravali
	case "box":
		filter = imaging.Box
	default:
		filter = imaging.Lanczos
	}

	width, err := strconv.Atoi(r.URL.Query().Get("width"))
	if err == nil {
		srcImage = imaging.Resize(srcImage, width, 0, filter)
	} else {
		height, err := strconv.Atoi(r.URL.Query().Get("height"))
		if err == nil {
			srcImage = imaging.Resize(srcImage, 0, height, filter)
		}
	}

	if timeout != 0 {
		w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int64(timeout.Seconds())))
		w.Header().Set("Expires", time.Now().Add(timeout).Format(http.TimeFormat))
	}

	w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))

	encoder := GetEncoder(format, r)
	if err := encoder.Encode(srcImage, w); err != nil {
		return errors.Wrap(err, "can't encode the image")
	}

	return nil
}

// GetFilename fetches the filename from the request path.
func GetFilename(r *http.Request) (string, error) {

	// We expect that the router sends us requests in the form `/resize/:filename`
	// so we check to see if the path contains the image url that we want to
	// parse. In this case, we check to see that the path is at least 9 characters
	// long, which will ensure that the filename has at least 1 character.
	if len(r.URL.Path) < 9 {
		return "", errors.New("filename too short")
	}

	return r.URL.Path[8:], nil
}

// HandleFileSystemResize performs the actual resizing by loading the image
// from the filesystem.
func HandleFileSystemResize(timeout time.Duration, dir http.Dir) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		// Extract the filename from the request.
		filename, err := GetFilename(r)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Try to open the image from the virtual filesystem.
		f, err := dir.Open(filename)
		if err != nil {
			if _, ok := err.(*os.PathError); ok {
				http.Error(w, "Not Found", http.StatusNotFound)
				return
			}

			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer f.Close()

		// If an error occurred during the image processing, return with an internal
		// server error.
		if err := ProcessImage(timeout, f, w, r); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

// HandleOriginResize performs the actual resizing by loading the image
// from the origin.
func HandleOriginResize(timeout time.Duration, origin string) (http.HandlerFunc, error) {
	originURL, err := url.Parse(origin)
	if err != nil {
		return nil, errors.Wrap(err, "can't parse the origin url")
	}

	return func(w http.ResponseWriter, r *http.Request) {

		// Extract the filename from the request.
		filename, err := GetFilename(r)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Parse the incomming url.
		filenameURL, err := url.Parse(filename)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Resolve it relative to the origin url.
		fileURL := originURL.ResolveReference(filenameURL)

		// TODO: improve, this is quite naive.

		// Perform the GET to the origin server.
		req, err := http.NewRequest("GET", fileURL.String(), nil)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		res, err := http.DefaultClient.Do(req)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadGateway)
			return
		}
		defer res.Body.Close()

		// If an error occurred during the image processing, return with an internal
		// server error.
		if err := ProcessImage(timeout, res.Body, w, r); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}, nil
}

// Serve creates and starts a new server to provide image resizing services.
func Serve(addr string, debug bool, directory, origin string, timeout time.Duration) error {
	mux := http.NewServeMux()

	if timeout != 0 {
		logrus.WithField("timeout", timeout.String()).Debug("cache headers enabled")
	} else {
		logrus.Debug("cache headers disabled")
	}

	// When debug mode is enabled, mount the debug handlers on this router.
	if debug {
		mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
		mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
		mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
		mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
		mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
	}

	// By default, we'll try to use the directory resize, otherwise, if the origin
	// url is provided, use it.
	if origin == "" {
		logrus.WithField("directory", directory).Debug("serving from the filesystem")
		mux.HandleFunc("/", HandleFileSystemResize(timeout, http.Dir(directory)))
	} else {
		logrus.WithField("origin", origin).Debug("serving from the origin")

		handler, err := HandleOriginResize(timeout, origin)
		if err != nil {
			return errors.Wrap(err, "can't create origin resize handler")
		}

		mux.HandleFunc("/", handler)
	}

	n := negroni.Classic() // Includes some default middlewares

	n.UseHandler(mux)

	logrus.Debugf("Now listening on %s", addr)
	return http.ListenAndServe(addr, n)
}
<|endoftext|>


<commit_before>package binext

import "testing"

func TestGet(t *testing.T) {

}
<commit_msg>Update test<commit_after>package binext

import "testing"

func TestGet(t *testing.T) {
	n := Get()
	if len(n) <= 0 {
		t.Fatalf("There must be atleast one extension provided")
	}
}
<|endoftext|>