<commit_before>// Copyright © 2013 Steve Francia <spf@spf13.com>.
//
// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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 tpl

type Tmpl struct {
	Name string
	Data string
}

func (t *GoHtmlTemplate) EmbedShortcodes() {
	t.AddInternalShortcode("ref.html", `{{ .Get 0 | ref .Page }}`)
	t.AddInternalShortcode("relref.html", `{{ .Get 0 | relref .Page }}`)
	t.AddInternalShortcode("highlight.html", `{{ .Get 0 | highlight .Inner  }}`)
	t.AddInternalShortcode("test.html", `This is a simple Test`)
	t.AddInternalShortcode("figure.html", `

    {{ with .Get "link"}}{{ end }}

    {{ if .Get "link"}}{{ end }}
    {{ if or (or (.Get "title") (.Get "caption")) (.Get "attr")}}

{{ if isset .Params "title" }}

{{ .Get "title" }}
{{ end }}
        {{ if or (.Get "caption") (.Get "attr")}}

        {{ .Get "caption" }}
        {{ with .Get "attrlink"}} {{ end }}
            {{ .Get "attr" }}
        {{ if .Get "attrlink"}} {{ end }}

 {{ end }}

    {{ end }}

`)
}

func (t *GoHtmlTemplate) EmbedTemplates() {

	t.AddInternalTemplate("_default", "rss.xml", `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}</title>
    <link>{{ .Permalink }}</link>
    <description>Recent content {{ with .Title }}in {{.}} {{ end }}on {{ .Site.Title }}</description>
    <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
    <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
    <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
    <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
    <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
    <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHtml }}</lastBuildDate>{{ end }}
    <atom:link href="{{.Url}}" rel="self" type="application/rss+xml" />
    {{ range first 15 .Data.Pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHtml }}</pubDate>
      {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>
      <description>{{ .Content | html }}</description>
    </item>
    {{ end }}
  </channel>
</rss>`)

	t.AddInternalTemplate("_default", "sitemap.xml", `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  {{ range .Data.Pages }}
  <url>
    <loc>{{ .Permalink }}</loc>{{ if not .Date.IsZero }}
    <lastmod>{{ safeHtml ( .Date.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
    <changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
    <priority>{{ .Sitemap.Priority }}</priority>{{ end }}
  </url>
  {{ end }}
</urlset>`)

	t.AddInternalTemplate("", "pagination.html", `{{ $pag := .Paginator }}
    {{ if gt $pag.TotalPages 1 }}

        {{ with $pag.First }}

            ««

        {{ end }}

        «

        {{ range $pag.Pagers }}

{{ .PageNumber }}

        {{ end }}

        »

        {{ with $pag.Last }}

            »»

        {{ end }}

    {{ end }}`)

	t.AddInternalTemplate("", "disqus.html", `{{ if .Site.DisqusShortname }}

Please enable JavaScript to view the comments powered by Disqus.
comments powered by Disqus{{end}}`)

	// Add SEO & Social metadata
	t.AddInternalTemplate("", "opengraph.html", `<meta property="og:title" content="{{ .Title }}" />
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}" />
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
<meta property="og:url" content="{{ .Permalink }}" />
{{ with .Params.images }}{{ range first 6 . }}
  <meta property="og:image" content="{{ . }}" />
{{ end }}{{ end }}

{{ if not .Date.IsZero }}<meta property="og:updated_time" content="{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHtml }}"/>{{ end }}{{ with .Params.audio }}
<meta property="og:audio" content="{{ . }}" />{{ end }}{{ with .Params.locale }}
<meta property="og:locale" content="{{ . }}" />{{ end }}{{ with .Site.Params.title }}
<meta property="og:site_name" content="{{ . }}" />{{ end }}{{ with .Params.videos }}
{{ range .Params.videos }}
  <meta property="og:video" content="{{ . }}" />
{{ end }}{{ end }}

{{ $permalink := .Permalink }}
{{ $siteSeries := .Site.Taxonomies.series }}{{ with .Params.series }}
{{ range $name := . }}
  {{ $series := index $siteSeries $name }}
  {{ range $page := first 6 $series.Pages }}
    {{ if ne $page.Permalink $permalink }}<meta property="og:see_also" content="{{ $page.Permalink }}" />{{ end }}
  {{ end }}
{{ end }}{{ end }}

{{ if .IsPage }}
{{ range .Site.Authors }}{{ with .Social.facebook }}
<meta property="article:author" content="https://www.facebook.com/{{ . }}" />{{ end }}{{ with .Site.Social.facebook }}
<meta property="article:publisher" content="https://www.facebook.com/{{ . }}" />{{ end }}
<meta property="article:published_time" content="{{ .PublishDate }}" />
<meta property="article:modified_time" content="{{ .Date }}" />
<meta property="article:section" content="{{ .Section }}" />
{{ with .Params.tags }}{{ range first 6 . }}
  <meta property="article:tag" content="{{ . }}" />{{ end }}{{ end }}
{{ end }}{{ end }}

{{ with .Site.Social.facebook_admin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}`)

	t.AddInternalTemplate("", "twitter_cards.html", `{{ if .IsPage }}
{{ with .Params.images }}

  <meta name="twitter:card" content="summary_large_image"/>
  <meta name="twitter:image:src" content="{{ index . 0 }}"/>
{{ else }}
  <meta name="twitter:card" content="summary"/>
{{ end }}

<meta name="twitter:title" content="{{ .Title }}"/>
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}"/>
{{ with .Site.Social.twitter }}<meta name="twitter:site" content="@{{ . }}"/>{{ end }}
{{ with .Site.Social.twitter_domain }}<meta name="twitter:domain" content="{{ . }}"/>{{ end }}
{{ range .Site.Authors }}
  {{ with .twitter }}<meta name="twitter:creator" content="@{{ . }}"/>{{ end }}
{{ end }}{{ end }}`)

	t.AddInternalTemplate("", "google_news.html", `{{ if .IsPage }}{{ with .Params.news_keywords }}
  <meta name="news_keywords" content="{{ range $i, $kw := first 10 . }}{{ if $i }},{{ end }}{{ $kw }}{{ end }}" />
{{ end }}{{ end }}`)

	t.AddInternalTemplate("", "schema.html", `{{ with .Site.Social.GooglePlus }}<link rel="publisher" href="{{ . }}"/>{{ end }}
<meta itemprop="name" content="{{ .Title }}">
<meta itemprop="description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}">

{{if .IsPage}}{{ $ISO8601 := "2006-01-02T15:04:05-07:00" }}{{ if not .PublishDate.IsZero }}
<meta itemprop="datePublished" content="{{ .PublishDate.Format $ISO8601 | safeHtml }}" />{{ end }}
{{ if not .Date.IsZero }}<meta itemprop="dateModified" content="{{ .Date.Format $ISO8601 | safeHtml }}" />{{ end }}
<meta itemprop="wordCount" content="{{ .WordCount }}">
{{ with .Params.images }}{{ range first 6 . }}
  <meta itemprop="image" content="{{ . }}">
{{ end }}{{ end }}

<meta itemprop="keywords" content="{{ range $plural, $terms := .Site.Taxonomies }}{{ range $term, $val := $terms }}{{ printf "%s," $term }}{{ end }}{{ end }}" />
{{ end }}`)

}
<commit_msg>Use $.Paginator in template<commit_after>// Copyright © 2013 Steve Francia <spf@spf13.com>.
//
// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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 tpl

type Tmpl struct {
	Name string
	Data string
}

func (t *GoHtmlTemplate) EmbedShortcodes() {
	t.AddInternalShortcode("ref.html", `{{ .Get 0 | ref .Page }}`)
	t.AddInternalShortcode("relref.html", `{{ .Get 0 | relref .Page }}`)
	t.AddInternalShortcode("highlight.html", `{{ .Get 0 | highlight .Inner  }}`)
	t.AddInternalShortcode("test.html", `This is a simple Test`)
	t.AddInternalShortcode("figure.html", `

    {{ with .Get "link"}}{{ end }}

    {{ if .Get "link"}}{{ end }}
    {{ if or (or (.Get "title") (.Get "caption")) (.Get "attr")}}

{{ if isset .Params "title" }}

{{ .Get "title" }}
{{ end }}
        {{ if or (.Get "caption") (.Get "attr")}}

        {{ .Get "caption" }}
        {{ with .Get "attrlink"}} {{ end }}
            {{ .Get "attr" }}
        {{ if .Get "attrlink"}} {{ end }}

 {{ end }}

    {{ end }}

`)
}

func (t *GoHtmlTemplate) EmbedTemplates() {

	t.AddInternalTemplate("_default", "rss.xml", `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}</title>
    <link>{{ .Permalink }}</link>
    <description>Recent content {{ with .Title }}in {{.}} {{ end }}on {{ .Site.Title }}</description>
    <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
    <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
    <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
    <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
    <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
    <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHtml }}</lastBuildDate>{{ end }}
    <atom:link href="{{.Url}}" rel="self" type="application/rss+xml" />
    {{ range first 15 .Data.Pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHtml }}</pubDate>
      {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>
      <description>{{ .Content | html }}</description>
    </item>
    {{ end }}
  </channel>
</rss>`)

	t.AddInternalTemplate("_default", "sitemap.xml", `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  {{ range .Data.Pages }}
  <url>
    <loc>{{ .Permalink }}</loc>{{ if not .Date.IsZero }}
    <lastmod>{{ safeHtml ( .Date.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
    <changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
    <priority>{{ .Sitemap.Priority }}</priority>{{ end }}
  </url>
  {{ end }}
</urlset>`)

	t.AddInternalTemplate("", "pagination.html", `{{ $pag := $.Paginator }}
    {{ if gt $pag.TotalPages 1 }}

        {{ with $pag.First }}

            ««

        {{ end }}

        «

        {{ range $pag.Pagers }}

{{ .PageNumber }}

        {{ end }}

        »

        {{ with $pag.Last }}

            »»

        {{ end }}

    {{ end }}`)

	t.AddInternalTemplate("", "disqus.html", `{{ if .Site.DisqusShortname }}

Please enable JavaScript to view the comments powered by Disqus.
comments powered by Disqus{{end}}`)

	// Add SEO & Social metadata
	t.AddInternalTemplate("", "opengraph.html", `<meta property="og:title" content="{{ .Title }}" />
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}" />
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
<meta property="og:url" content="{{ .Permalink }}" />
{{ with .Params.images }}{{ range first 6 . }}
  <meta property="og:image" content="{{ . }}" />
{{ end }}{{ end }}

{{ if not .Date.IsZero }}<meta property="og:updated_time" content="{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHtml }}"/>{{ end }}{{ with .Params.audio }}
<meta property="og:audio" content="{{ . }}" />{{ end }}{{ with .Params.locale }}
<meta property="og:locale" content="{{ . }}" />{{ end }}{{ with .Site.Params.title }}
<meta property="og:site_name" content="{{ . }}" />{{ end }}{{ with .Params.videos }}
{{ range .Params.videos }}
  <meta property="og:video" content="{{ . }}" />
{{ end }}{{ end }}

{{ $permalink := .Permalink }}
{{ $siteSeries := .Site.Taxonomies.series }}{{ with .Params.series }}
{{ range $name := . }}
  {{ $series := index $siteSeries $name }}
  {{ range $page := first 6 $series.Pages }}
    {{ if ne $page.Permalink $permalink }}<meta property="og:see_also" content="{{ $page.Permalink }}" />{{ end }}
  {{ end }}
{{ end }}{{ end }}

{{ if .IsPage }}
{{ range .Site.Authors }}{{ with .Social.facebook }}
<meta property="article:author" content="https://www.facebook.com/{{ . }}" />{{ end }}{{ with .Site.Social.facebook }}
<meta property="article:publisher" content="https://www.facebook.com/{{ . }}" />{{ end }}
<meta property="article:published_time" content="{{ .PublishDate }}" />
<meta property="article:modified_time" content="{{ .Date }}" />
<meta property="article:section" content="{{ .Section }}" />
{{ with .Params.tags }}{{ range first 6 . }}
  <meta property="article:tag" content="{{ . }}" />{{ end }}{{ end }}
{{ end }}{{ end }}

{{ with .Site.Social.facebook_admin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}`)

	t.AddInternalTemplate("", "twitter_cards.html", `{{ if .IsPage }}
{{ with .Params.images }}

  <meta name="twitter:card" content="summary_large_image"/>
  <meta name="twitter:image:src" content="{{ index . 0 }}"/>
{{ else }}
  <meta name="twitter:card" content="summary"/>
{{ end }}

<meta name="twitter:title" content="{{ .Title }}"/>
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}"/>
{{ with .Site.Social.twitter }}<meta name="twitter:site" content="@{{ . }}"/>{{ end }}
{{ with .Site.Social.twitter_domain }}<meta name="twitter:domain" content="{{ . }}"/>{{ end }}
{{ range .Site.Authors }}
  {{ with .twitter }}<meta name="twitter:creator" content="@{{ . }}"/>{{ end }}
{{ end }}{{ end }}`)

	t.AddInternalTemplate("", "google_news.html", `{{ if .IsPage }}{{ with .Params.news_keywords }}
  <meta name="news_keywords" content="{{ range $i, $kw := first 10 . }}{{ if $i }},{{ end }}{{ $kw }}{{ end }}" />
{{ end }}{{ end }}`)

	t.AddInternalTemplate("", "schema.html", `{{ with .Site.Social.GooglePlus }}<link rel="publisher" href="{{ . }}"/>{{ end }}
<meta itemprop="name" content="{{ .Title }}">
<meta itemprop="description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}">

{{if .IsPage}}{{ $ISO8601 := "2006-01-02T15:04:05-07:00" }}{{ if not .PublishDate.IsZero }}
<meta itemprop="datePublished" content="{{ .PublishDate.Format $ISO8601 | safeHtml }}" />{{ end }}
{{ if not .Date.IsZero }}<meta itemprop="dateModified" content="{{ .Date.Format $ISO8601 | safeHtml }}" />{{ end }}
<meta itemprop="wordCount" content="{{ .WordCount }}">
{{ with .Params.images }}{{ range first 6 . }}
  <meta itemprop="image" content="{{ . }}">
{{ end }}{{ end }}

<meta itemprop="keywords" content="{{ range $plural, $terms := .Site.Taxonomies }}{{ range $term, $val := $terms }}{{ printf "%s," $term }}{{ end }}{{ end }}" />
{{ end }}`)

}
<|endoftext|>


<commit_before>package tls

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/fastly/go-utils/executable"
	"github.com/fastly/go-utils/vlog"
)

var (
	_certPath string
	_insecure bool
)

// Init sets the CertPath to search for TLS certs and keys. If CertPath is empty, $BIN/../certs
// and $PWD/../../../../certs are searched. Insecure is a flag to ignore cert verification errors.
func Init(certPath string, insecure bool) {
	_certPath = certPath
	_insecure = insecure
}

var packagedCertDir string

// LocatePackagedPEMDir locates the path of the packaged PEM store which is the
// directory named "certs". functions that take a (name string) parameter look
// for files named ${name}-key.pem and/or ${name}-cert.pem in this directory.
func LocatePackagedPEMDir() (dir string, err error) {
	if packagedCertDir != "" {
		dir = packagedCertDir
		return
	} else if _certPath != "" {
		packagedCertDir = _certPath
		dir = packagedCertDir
		return
	}

	var binDir string
	binDir, err = executable.Dir()
	if err != nil {
		return
	}

	cwd, _ := os.Getwd()

	searchList := []string{
		binDir + "../certs", // git and deb: certs is one up from bin
		cwd + "/testcerts",  // tests: certs is 4 up from _test files
	}
	for _, l := range searchList {
		d := filepath.Clean(l)
		var info os.FileInfo
		if info, err = os.Stat(d); err == nil && info.IsDir() {
			packagedCertDir = d
			dir = packagedCertDir
			return
		}
	}
	err = fmt.Errorf("couldn't locate packaged PEMs in any of %v", searchList)
	return
}

// LocatePackagedPEMFile loads a single PEM file (with -cert or -key suffix) from the package store
func LocatePackagedPEMFile(name string) (file string, err error) {
	if strings.IndexRune(name, '/') < 0 {
		var dir string
		if dir, err = LocatePackagedPEMDir(); err != nil {
			return
		}
		f := filepath.Join(dir, name+".pem")
		if _, err = os.Stat(f); err != nil {
			return
		}
		file = f
	} else {
		// explicit path
		if _, err = os.Stat(name); err != nil {
			return "", fmt.Errorf("Cert(s) at path %q couldn't be loaded: %s", name, err)
		}
		file = name
	}
	return
}

// LoadPackagedKeypair loads a cert/key pair from the package store
// It looks for the ${name}-[cert,key].pem files from either the PEM dir
// if just a filename is given or from the fullpath if a path is given.
func LoadPackagedKeypair(name string) (cert tls.Certificate, certFile, keyFile string, err error) {
	certFile, err = LocatePackagedPEMFile(name + "-cert")
	if err != nil {
		return
	}
	keyFile, err = LocatePackagedPEMFile(name + "-key")
	if err != nil {
		return
	}
	cert, err = tls.LoadX509KeyPair(certFile, keyFile)
	return
}

// GenerateConfig returns a *tls.Config for either a client if true or server if client
// is false, the given key pair ${name}-[cert,key].pem files and accepting the caCertNames
// if given.
func GenerateConfig(client bool, keyPairName string, caCertNames []string) (config *tls.Config, err error) {
	config = &tls.Config{
		InsecureSkipVerify: _insecure,
	}

	label := "server"
	if client {
		label = "client"
	}

	var keyPair tls.Certificate
	var cFile, kFile string
	if keyPairName != "" {
		keyPair, cFile, kFile, err = LoadPackagedKeypair(keyPairName)
		if err != nil {
			return
		}

		vlog.VLogf("Loaded packaged %s keypair from %s and %s", label, cFile, kFile)
		config.Certificates = []tls.Certificate{keyPair}
	}

	if len(caCertNames) > 0 {
		caPool := x509.NewCertPool()
		if client {
			config.RootCAs = caPool
		} else {
			config.ClientCAs = caPool
			if _insecure {
				config.ClientAuth = tls.RequestClientCert
			} else {
				config.ClientAuth = tls.RequireAndVerifyClientCert
			}
		}

		for _, name := range caCertNames {
			n := name
			if strings.Index(name, "/") < 0 {
				n = name + "-cert"
			}
			var file string
			if file, err = LocatePackagedPEMFile(n); err != nil {
				return nil, fmt.Errorf("Failed to find cert named %q: %s", name, err)
			}
			var b []byte
			if b, err = ioutil.ReadFile(file); err != nil {
				return nil, fmt.Errorf("Can't read cert from %s: %s", file, err)
			}

			vlog.VLogf("Allowing %s CA cert from %s", label, file)
			if ok := caPool.AppendCertsFromPEM(b); !ok {
				return nil, fmt.Errorf("No cert could be found in %s", file)
			}
		}
	}
	return
}

// ConfigureServer returns a TLS server configuration that presents serverKeyPairName to
// clients. if clientCertNames is non-empty the server will request a client
// certificate and require that it be provided and signed by one of the named
// certs.
func ConfigureServer(serverKeyPairName string, clientCertNames ...string) (config *tls.Config, err error) {
	return GenerateConfig(false, serverKeyPairName, clientCertNames)
}

// ConfigureClient returns a TLS client configuration that presents clientKeyPair
// to the remote server. if serverCertNames is non-empty, server certificates must
// be signed by one of the named certs; otherwise the default system CA list will be
// used.
func ConfigureClient(clientKeyPairName string, serverCertNames ...string) (config *tls.Config, err error) {
	return GenerateConfig(true, clientKeyPairName, serverCertNames)
}
<commit_msg>tls.go: add http.Handler and HandlerFunc basic auth wrapping functions<commit_after>package tls

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/fastly/go-utils/executable"
	"github.com/fastly/go-utils/vlog"
)

var (
	_certPath,
	_adminuser,
	_adminpass,
	_authrealm string
	_insecure bool
)

// Init sets the CertPath to search for TLS certs and keys. If CertPath is empty, $BIN/../certs
// and $PWD/../../../../certs are searched. Insecure is a flag to ignore cert verification errors.
func Init(certPath string, insecure bool) {
	_certPath = certPath
	_insecure = insecure
}

var packagedCertDir string

// LocatePackagedPEMDir locates the path of the packaged PEM store which is the
// directory named "certs". functions that take a (name string) parameter look
// for files named ${name}-key.pem and/or ${name}-cert.pem in this directory.
func LocatePackagedPEMDir() (dir string, err error) {
	if packagedCertDir != "" {
		dir = packagedCertDir
		return
	} else if _certPath != "" {
		packagedCertDir = _certPath
		dir = packagedCertDir
		return
	}

	var binDir string
	binDir, err = executable.Dir()
	if err != nil {
		return
	}

	cwd, _ := os.Getwd()

	searchList := []string{
		binDir + "../certs", // git and deb: certs is one up from bin
		cwd + "/testcerts",  // tests: certs is 4 up from _test files
	}
	for _, l := range searchList {
		d := filepath.Clean(l)
		var info os.FileInfo
		if info, err = os.Stat(d); err == nil && info.IsDir() {
			packagedCertDir = d
			dir = packagedCertDir
			return
		}
	}
	err = fmt.Errorf("couldn't locate packaged PEMs in any of %v", searchList)
	return
}

// LocatePackagedPEMFile loads a single PEM file (with -cert or -key suffix) from the package store
func LocatePackagedPEMFile(name string) (file string, err error) {
	if strings.IndexRune(name, '/') < 0 {
		var dir string
		if dir, err = LocatePackagedPEMDir(); err != nil {
			return
		}
		f := filepath.Join(dir, name+".pem")
		if _, err = os.Stat(f); err != nil {
			return
		}
		file = f
	} else {
		// explicit path
		if _, err = os.Stat(name); err != nil {
			return "", fmt.Errorf("Cert(s) at path %q couldn't be loaded: %s", name, err)
		}
		file = name
	}
	return
}

// LoadPackagedKeypair loads a cert/key pair from the package store
// It looks for the ${name}-[cert,key].pem files from either the PEM dir
// if just a filename is given or from the fullpath if a path is given.
func LoadPackagedKeypair(name string) (cert tls.Certificate, certFile, keyFile string, err error) {
	certFile, err = LocatePackagedPEMFile(name + "-cert")
	if err != nil {
		return
	}
	keyFile, err = LocatePackagedPEMFile(name + "-key")
	if err != nil {
		return
	}
	cert, err = tls.LoadX509KeyPair(certFile, keyFile)
	return
}

// GenerateConfig returns a *tls.Config for either a client if true or server if client
// is false, the given key pair ${name}-[cert,key].pem files and accepting the caCertNames
// if given.
func GenerateConfig(client bool, keyPairName string, caCertNames []string) (config *tls.Config, err error) {
	config = &tls.Config{
		InsecureSkipVerify: _insecure,
	}

	label := "server"
	if client {
		label = "client"
	}

	var keyPair tls.Certificate
	var cFile, kFile string
	if keyPairName != "" {
		keyPair, cFile, kFile, err = LoadPackagedKeypair(keyPairName)
		if err != nil {
			return
		}

		vlog.VLogf("Loaded packaged %s keypair from %s and %s", label, cFile, kFile)
		config.Certificates = []tls.Certificate{keyPair}
	}

	if len(caCertNames) > 0 {
		caPool := x509.NewCertPool()
		if client {
			config.RootCAs = caPool
		} else {
			config.ClientCAs = caPool
			if _insecure {
				config.ClientAuth = tls.RequestClientCert
			} else {
				config.ClientAuth = tls.RequireAndVerifyClientCert
			}
		}

		for _, name := range caCertNames {
			n := name
			if strings.Index(name, "/") < 0 {
				n = name + "-cert"
			}
			var file string
			if file, err = LocatePackagedPEMFile(n); err != nil {
				return nil, fmt.Errorf("Failed to find cert named %q: %s", name, err)
			}
			var b []byte
			if b, err = ioutil.ReadFile(file); err != nil {
				return nil, fmt.Errorf("Can't read cert from %s: %s", file, err)
			}

			vlog.VLogf("Allowing %s CA cert from %s", label, file)
			if ok := caPool.AppendCertsFromPEM(b); !ok {
				return nil, fmt.Errorf("No cert could be found in %s", file)
			}
		}
	}
	return
}

// ConfigureServer returns a TLS server configuration that presents serverKeyPairName to
// clients. if clientCertNames is non-empty the server will request a client
// certificate and require that it be provided and signed by one of the named
// certs.
func ConfigureServer(serverKeyPairName string, clientCertNames ...string) (config *tls.Config, err error) {
	return GenerateConfig(false, serverKeyPairName, clientCertNames)
}

// ConfigureClient returns a TLS client configuration that presents clientKeyPair
// to the remote server. if serverCertNames is non-empty, server certificates must
// be signed by one of the named certs; otherwise the default system CA list will be
// used.
func ConfigureClient(clientKeyPairName string, serverCertNames ...string) (config *tls.Config, err error) {
	return GenerateConfig(true, clientKeyPairName, serverCertNames)
}

// SetWrapCreds stores the adminuser, adminpass, and authrealm. These parameters
// will be used as the credentials and realm in calls to WrapHandleForAuth
// and WrapHandlerFuncForAuth.
func SetWrapCreds(adminuser, adminpass, authrealm string) {
	_adminuser = adminuser
	_adminpass = adminpass
	_authrealm = authrealm
}

// WrapHandlerForAuth calls WrapHandlerForAuthCreds with the currently stored
// adminuser, adminpass, and authrealm. SetWrapCreds should be called before this function
// or else the HAndler will not be wrapped with basic authentication.
func WrapHandlerForAuth(h http.Handler) http.Handler {
	return WrapHandlerForAuthCreds(h, _adminuser, _adminpass, _authrealm)
}

// WrapHandlerFuncForAuth calls WrapHandlerFuncForAuthCreds with the currently stored
// adminuser, adminpass, and authrealm. SetWrapCreds should be called before this function
// or else the HandlerFunc will not be wrapped with basic authentication.
func WrapHandlerFuncForAuth(h http.HandlerFunc) http.HandlerFunc {
	return WrapHandlerFuncForAuthCreds(h, _adminuser, _adminpass, _authrealm)
}

// WrapHandlerForAuthCreds returns the Handler wrapped with basic authentication
// requiring credentials adminuser and adminpass. The authrealm will be used
// for the WWW-Authenticate header's basic realm.
func WrapHandlerForAuthCreds(h http.Handler, adminuser, adminpass, authrealm string) http.Handler {
	if adminuser == "" && adminpass == "" {
		return h
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if isAuthenticated(r, adminuser, adminpass) {
			h.ServeHTTP(w, r)
		} else {
			w.Header().Set("WWW-Authenticate", `Basic realm="`+authrealm+`"`)
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Unauthorized\n"))
		}
	})
}

// WrapHandlerFuncForAuth returns the HandlerFunc wrapped with basic authentication
// requiring credentials adminuser and adminpass. The authrealm will be used
// for the WWW-Authenticate header's basic realm.
func WrapHandlerFuncForAuthCreds(h http.HandlerFunc, adminuser, adminpass, authrealm string) http.HandlerFunc {
	if adminuser == "" && adminpass == "" {
		return h
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if isAuthenticated(r, adminuser, adminpass) {
			h(w, r)
		} else {
			w.Header().Set("WWW-Authenticate", `Basic realm="`+authrealm+`"`)
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Unauthorized\n"))
		}
	})
}

func isAuthenticated(r *http.Request, adminuser, adminpass string) bool {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return false
	}
	pieces := strings.Split(auth, " ")
	if len(pieces) != 2 || pieces[0] != "Basic" {
		return false
	}
	decoded, err := base64.StdEncoding.DecodeString(pieces[1])
	if err != nil {
		return false
	}
	userpass := strings.Split(string(decoded), ":")
	if len(userpass) != 2 || userpass[0] != adminuser || userpass[1] != adminpass {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package df

import (
	"errors"
	"fmt"
	"math"
	"reflect"
	"strings"
)

// Series is the main structure for a series of elements of the same type. It is
// the primary building block of a DataFrame.
type Series struct {
	Name     string             // The name of the series
	elements []elementInterface // The values of the elements
	t        Type               // The type of the series
	err      error
}

// Comparator is a comparator that can be used for filtering Series and DataFrames
type Comparator string

const (
	Eq        Comparator = "=="
	Neq                  = "!="
	Greater              = ">"
	GreaterEq            = ">="
	Less                 = "<"
	LessEq               = "<="
	In                   = "in"
)

// Type represents the type of the elements that can be stored on Series
type Type string

const (
	String Type = "string"
	Int         = "int"
	Float       = "float"
	Bool        = "bool"
)

// CONSTRUCTORS

// NewSeries is the generic Series constructor
func NewSeries(values interface{}, t Type) Series {
	elements := make([]elementInterface, 0)
	ret := Series{
		Name:     "",
		elements: elements,
		t:        t,
	}
	ret.Append(values)
	return ret
}

// Strings is a constructor for a String series
func Strings(values interface{}) Series {
	return NewSeries(values, String)
}

// Ints is a constructor for an Int series
func Ints(values interface{}) Series {
	return NewSeries(values, Int)
}

// Floats is a constructor for a Float series
func Floats(values interface{}) Series {
	return NewSeries(values, Float)
}

// Bools is a constructor for a bools series
func Bools(values interface{}) Series {
	return NewSeries(values, Bool)
}

//// NamedStrings is a constructor for a named String series
//func NamedStrings(name string, args ...interface{}) Series {
//s := Strings(args...)
//s.Name = name
//return s
//}
//// NamedInts is a constructor for a named Int series
//func NamedInts(name string, args ...interface{}) Series {
//s := Ints(args...)
//s.Name = name
//return s
//}
//// NamedFloats is a constructor for a named Float series
//func NamedFloats(name string, args ...interface{}) Series {
//s := Floats(args...)
//s.Name = name
//return s
//}
//// NamedBools is a constructor for a named Bool series
//func NamedBools(name string, args ...interface{}) Series {
//s := Bools(args...)
//s.Name = name
//return s
//}

//// Empty returns an empty Series of the same type
//func (s Series) Empty() Series {
//ret := Series{Name: s.Name, t: s.t}
//switch ret.t {
//case String:
//ret.elements = stringElements{}
//case Int:
//ret.elements = intElements{}
//case Float:
//ret.elements = floatElements{}
//case Bool:
//ret.elements = boolElements{}
//}
//return ret
//}

//func (s Series) set(i int, val elementValue) Series {
//if s.Err() != nil {
//return s
//}
//if i >= s.Len() || i < 0 {
//return Series{err: errors.New("Couldn't set element. Index out of bounds")}
//}
//elems, err := s.elements.Set(i, val)
//if err != nil {
//return Series{err: errors.New("Couldn't set element: " + err.Error())}
//}
//s.elements = elems
//return s
//}

//func (s Series) elem(i int) elementInterface {
//if i >= s.Len() || i < 0 {
//return nil
//}
//return s.elements.Elem(i)
//}

//// Val returns the value of a series for the given index
//func (s Series) Val(i int) (interface{}, error) {
//if i >= s.Len() || i < 0 {
//return nil, errors.New("index out of bounds")
//}
//elem := s.elements.Elem(i).Val()
//return elem, nil
//}

// Append appends elements to the end of the Series. The Series is modified in situ
func (s *Series) Append(values interface{}) {
	appendElements := func(val interface{}) error {
		var newelem elementInterface
		switch s.t {
		case String:
			newelem = stringElement{}
		case Int:
			newelem = intElement{}
		case Float:
			newelem = floatElement{}
		case Bool:
			newelem = boolElement{}
		default:
			return errors.New("can't create series, unknown type")
		}
		s.elements = append(s.elements, newelem.Set(val))
		return nil
	}
	if values == nil {
		appendElements(values)
	} else {
		switch reflect.TypeOf(values).Kind() {
		case reflect.Slice:
			v := reflect.ValueOf(values)
			for i := 0; i < v.Len(); i++ {
				val := v.Index(i).Interface()
				err := appendElements(val)
				if err != nil {
					s.err = err
					return
				}
			}
		default:
			v := reflect.ValueOf(values)
			val := v.Interface()
			switch val.(type) {
			case Series:
				for _, v := range val.(Series).elements {
					err := appendElements(v)
					if err != nil {
						s.err = err
						return
					}
				}
			default:
				err := appendElements(val)
				if err != nil {
					s.err = err
					return
				}
			}
		}
	}
}

// Concat concatenates two series together. It will return a new Series with the
// combined elements of both Series.
func (s Series) Concat(x Series) Series {
	y := s.Copy()
	y.Append(x)
	return y
}

func subsetIndexParse(l int, indexes interface{}) ([]int, error) {
	var idx []int
	switch indexes.(type) {
	case []int:
		idx = indexes.([]int)
	case int:
		idx = []int{indexes.(int)}
	case []bool:
		bools := indexes.([]bool)
		if len(bools) != l {
			return nil, errors.New("subsetting error: index dimensions mismatch")
		}
		for i, b := range bools {
			if b {
				idx = append(idx, i)
			}
		}
	case Series:
		s := indexes.(Series)
		if s.HasNaN() {
			return nil, errors.New("subsetting error: indexes contain NaN")
		}
		switch s.t {
		case Int:
			return s.Int()
		case Bool:
			bools, err := s.Bool()
			if err != nil {
				return nil, fmt.Errorf("subsetting error: %v", err)
			}
			return subsetIndexParse(l, bools)
		default:
			return nil, errors.New("subsetting error: unknown indexing mode")
		}
	default:
		return nil, errors.New("subsetting error: unknown indexing mode")
	}
	return idx, nil
}

// Subset returns a subset of the series based on the given indexes. Currently
// supports numeric indexes in the form of []int or int, boolean []bool and the
// respective Series of types Int/Bool.
func (s Series) Subset(indexes interface{}) Series {
	if s.Err() != nil {
		return s
	}

	idx, err := subsetIndexParse(s.Len(), indexes)
	if err != nil {
		s.err = err
		return s
	}

	elements := make([]elementInterface, 0)
	for _, i := range idx {
		if i < 0 || i >= s.Len() {
			s.err = errors.New("subsetting error: index out of range")
			return s
		}
		elements = append(elements, s.elements[i].Copy())
	}
	return Series{
		Name:     s.Name,
		t:        s.t,
		elements: elements,
	}
}

// HasNaN checks whether the Series contain NaN elements
func (s Series) HasNaN() bool {
	for _, e := range s.elements {
		if e.IsNA() {
			return true
		}
	}
	return false
}

//// Compare compares the values of a Series with other series, scalars, text, etc
//func (s Series) Compare(comparator Comparator, comparando interface{}) ([]bool, error) {
//var comp Series
//switch s.t {
//case String:
//comp = Strings(comparando)
//case Int:
//comp = Ints(comparando)
//case Float:
//comp = Floats(comparando)
//case Bool:
//comp = Bools(comparando)
//default:
//return nil, errors.New("Unknown Series type")
//}
//ret := []bool{}
//switch comparator {
//case Eq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Eq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Eq(comp.elements.Elem(i)))
//}
//case Neq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, !s.elements.Elem(i).Eq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, !s.elements.Elem(i).Eq(comp.elements.Elem(i)))
//}
//case Greater:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Greater(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Greater(comp.elements.Elem(i)))
//}
//case GreaterEq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).GreaterEq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).GreaterEq(comp.elements.Elem(i)))
//}
//case Less:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Less(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Less(comp.elements.Elem(i)))
//}
//case LessEq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).LessEq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).LessEq(comp.elements.Elem(i)))
//}
//case In:
//for i := 0; i < s.Len(); i++ {
//found := false
//for j := 0; j < comp.Len(); j++ {
//if s.elements.Elem(i).Eq(comp.elements.Elem(j)) {
//found = true
//break
//}
//}
//ret = append(ret, found)
//}
//default:
//return nil, errors.New("Unknown comparator")
//}
//return ret, nil
//}

// Copy wil copy the values of a given Series
func (s Series) Copy() Series {
	name := s.Name
	t := s.t
	elements := make([]elementInterface, 0)
	for _, e := range s.elements {
		elements = append(elements, e.Copy())
	}
	ret := Series{
		Name:     name,
		t:        t,
		elements: elements,
	}
	return ret
}

// Records returns the elements of a Series in a []string
func (s Series) Records() []string {
	var ret []string
	for _, e := range s.elements {
		ret = append(ret, e.String())
	}
	return ret
}

// Float returns the elements of a Series in a []float64. If the elements can not
// be converted to float64 or contains a NaN returns the float representation of
// NaN.
func (s Series) Float() []float64 {
	var ret []float64
	for _, e := range s.elements {
		val := e.ToFloat().Val()
		if val == nil {
			ret = append(ret, math.NaN())
		} else {
			ret = append(ret, val.(float64))
		}
	}
	return ret
}

func (s Series) Int() ([]int, error) {
	var ret []int
	for _, e := range s.elements {
		val := e.ToInt().Val()
		if val == nil {
			return nil, errors.New("can't convert NaN to int")
		}
		ret = append(ret, val.(int))
	}
	return ret, nil
}

func (s Series) Bool() ([]bool, error) {
	var ret []bool
	for _, e := range s.elements {
		val := e.ToBool().Val()
		if val == nil {
			return nil, errors.New("can't convert NaN to bool")
		}
		ret = append(ret, val.(bool))
	}
	return ret, nil
}

// Type returns the type of a given series
func (s Series) Type() Type {
	return s.t
}

// Len returns the length of a given Series
func (s Series) Len() int {
	return len(s.elements)
}

// String implements the Stringer interface for Series
func (s Series) String() string {
	return fmt.Sprint(s.elements)
}

// Str prints some extra information about a given series
func (s Series) Str() string {
	var ret []string
	// If name exists print name
	if s.Name != "" {
		ret = append(ret, "Name: "+s.Name)
	}
	ret = append(ret, "Type: "+fmt.Sprint(s.t))
	ret = append(ret, "Length: "+fmt.Sprint(s.Len()))
	if s.Len() != 0 {
		ret = append(ret, "Values: "+fmt.Sprint(s))
	}
	return strings.Join(ret, "\n")
}

// Err returns the error contained in the series
func (s Series) Err() error {
	return s.err
}

func addr(s Series) []string {
	var ret []string
	for _, e := range s.elements {
		ret = append(ret, e.Addr())
	}
	return ret
}
<commit_msg>Add back Series.Empty<commit_after>package df

import (
	"errors"
	"fmt"
	"math"
	"reflect"
	"strings"
)

// Series is the main structure for a series of elements of the same type. It is
// the primary building block of a DataFrame.
type Series struct {
	Name     string             // The name of the series
	elements []elementInterface // The values of the elements
	t        Type               // The type of the series
	err      error
}

// Comparator is a comparator that can be used for filtering Series and DataFrames
type Comparator string

const (
	Eq        Comparator = "=="
	Neq                  = "!="
	Greater              = ">"
	GreaterEq            = ">="
	Less                 = "<"
	LessEq               = "<="
	In                   = "in"
)

// Type represents the type of the elements that can be stored on Series
type Type string

const (
	String Type = "string"
	Int         = "int"
	Float       = "float"
	Bool        = "bool"
)

// CONSTRUCTORS

// NewSeries is the generic Series constructor
func NewSeries(values interface{}, t Type) Series {
	elements := make([]elementInterface, 0)
	ret := Series{
		Name:     "",
		elements: elements,
		t:        t,
	}
	ret.Append(values)
	return ret
}

// Strings is a constructor for a String series
func Strings(values interface{}) Series {
	return NewSeries(values, String)
}

// Ints is a constructor for an Int series
func Ints(values interface{}) Series {
	return NewSeries(values, Int)
}

// Floats is a constructor for a Float series
func Floats(values interface{}) Series {
	return NewSeries(values, Float)
}

// Bools is a constructor for a bools series
func Bools(values interface{}) Series {
	return NewSeries(values, Bool)
}

//// NamedStrings is a constructor for a named String series
//func NamedStrings(name string, args ...interface{}) Series {
//s := Strings(args...)
//s.Name = name
//return s
//}
//// NamedInts is a constructor for a named Int series
//func NamedInts(name string, args ...interface{}) Series {
//s := Ints(args...)
//s.Name = name
//return s
//}
//// NamedFloats is a constructor for a named Float series
//func NamedFloats(name string, args ...interface{}) Series {
//s := Floats(args...)
//s.Name = name
//return s
//}
//// NamedBools is a constructor for a named Bool series
//func NamedBools(name string, args ...interface{}) Series {
//s := Bools(args...)
//s.Name = name
//return s
//}

// Empty returns an empty Series of the same type
func (s Series) Empty() Series {
	return Series{
		Name:     s.Name,
		t:        s.t,
		elements: make([]elementInterface, 0),
	}
}

//func (s Series) set(i int, val elementValue) Series {
//if s.Err() != nil {
//return s
//}
//if i >= s.Len() || i < 0 {
//return Series{err: errors.New("Couldn't set element. Index out of bounds")}
//}
//elems, err := s.elements.Set(i, val)
//if err != nil {
//return Series{err: errors.New("Couldn't set element: " + err.Error())}
//}
//s.elements = elems
//return s
//}

//func (s Series) elem(i int) elementInterface {
//if i >= s.Len() || i < 0 {
//return nil
//}
//return s.elements.Elem(i)
//}

//// Val returns the value of a series for the given index
//func (s Series) Val(i int) (interface{}, error) {
//if i >= s.Len() || i < 0 {
//return nil, errors.New("index out of bounds")
//}
//elem := s.elements.Elem(i).Val()
//return elem, nil
//}

// Append appends elements to the end of the Series. The Series is modified in situ
func (s *Series) Append(values interface{}) {
	appendElements := func(val interface{}) error {
		var newelem elementInterface
		switch s.t {
		case String:
			newelem = stringElement{}
		case Int:
			newelem = intElement{}
		case Float:
			newelem = floatElement{}
		case Bool:
			newelem = boolElement{}
		default:
			return errors.New("can't create series, unknown type")
		}
		s.elements = append(s.elements, newelem.Set(val))
		return nil
	}
	if values == nil {
		appendElements(values)
	} else {
		switch reflect.TypeOf(values).Kind() {
		case reflect.Slice:
			v := reflect.ValueOf(values)
			for i := 0; i < v.Len(); i++ {
				val := v.Index(i).Interface()
				err := appendElements(val)
				if err != nil {
					s.err = err
					return
				}
			}
		default:
			v := reflect.ValueOf(values)
			val := v.Interface()
			switch val.(type) {
			case Series:
				for _, v := range val.(Series).elements {
					err := appendElements(v)
					if err != nil {
						s.err = err
						return
					}
				}
			default:
				err := appendElements(val)
				if err != nil {
					s.err = err
					return
				}
			}
		}
	}
}

// Concat concatenates two series together. It will return a new Series with the
// combined elements of both Series.
func (s Series) Concat(x Series) Series {
	y := s.Copy()
	y.Append(x)
	return y
}

func subsetIndexParse(l int, indexes interface{}) ([]int, error) {
	var idx []int
	switch indexes.(type) {
	case []int:
		idx = indexes.([]int)
	case int:
		idx = []int{indexes.(int)}
	case []bool:
		bools := indexes.([]bool)
		if len(bools) != l {
			return nil, errors.New("subsetting error: index dimensions mismatch")
		}
		for i, b := range bools {
			if b {
				idx = append(idx, i)
			}
		}
	case Series:
		s := indexes.(Series)
		if s.HasNaN() {
			return nil, errors.New("subsetting error: indexes contain NaN")
		}
		switch s.t {
		case Int:
			return s.Int()
		case Bool:
			bools, err := s.Bool()
			if err != nil {
				return nil, fmt.Errorf("subsetting error: %v", err)
			}
			return subsetIndexParse(l, bools)
		default:
			return nil, errors.New("subsetting error: unknown indexing mode")
		}
	default:
		return nil, errors.New("subsetting error: unknown indexing mode")
	}
	return idx, nil
}

// Subset returns a subset of the series based on the given indexes. Currently
// supports numeric indexes in the form of []int or int, boolean []bool and the
// respective Series of types Int/Bool.
func (s Series) Subset(indexes interface{}) Series {
	if s.Err() != nil {
		return s
	}

	idx, err := subsetIndexParse(s.Len(), indexes)
	if err != nil {
		s.err = err
		return s
	}

	elements := make([]elementInterface, 0)
	for _, i := range idx {
		if i < 0 || i >= s.Len() {
			s.err = errors.New("subsetting error: index out of range")
			return s
		}
		elements = append(elements, s.elements[i].Copy())
	}
	return Series{
		Name:     s.Name,
		t:        s.t,
		elements: elements,
	}
}

// HasNaN checks whether the Series contain NaN elements
func (s Series) HasNaN() bool {
	for _, e := range s.elements {
		if e.IsNA() {
			return true
		}
	}
	return false
}

//// Compare compares the values of a Series with other series, scalars, text, etc
//func (s Series) Compare(comparator Comparator, comparando interface{}) ([]bool, error) {
//var comp Series
//switch s.t {
//case String:
//comp = Strings(comparando)
//case Int:
//comp = Ints(comparando)
//case Float:
//comp = Floats(comparando)
//case Bool:
//comp = Bools(comparando)
//default:
//return nil, errors.New("Unknown Series type")
//}
//ret := []bool{}
//switch comparator {
//case Eq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Eq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Eq(comp.elements.Elem(i)))
//}
//case Neq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, !s.elements.Elem(i).Eq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, !s.elements.Elem(i).Eq(comp.elements.Elem(i)))
//}
//case Greater:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Greater(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Greater(comp.elements.Elem(i)))
//}
//case GreaterEq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).GreaterEq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).GreaterEq(comp.elements.Elem(i)))
//}
//case Less:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Less(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).Less(comp.elements.Elem(i)))
//}
//case LessEq:
//if comp.Len() == 1 {
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).LessEq(comp.elements.Elem(0)))
//}
//return ret, nil
//}
//if s.Len() != comp.Len() {
//return nil, errors.New("Can't compare Series: Different dimensions")
//}
//for i := 0; i < s.Len(); i++ {
//ret = append(ret, s.elements.Elem(i).LessEq(comp.elements.Elem(i)))
//}
//case In:
//for i := 0; i < s.Len(); i++ {
//found := false
//for j := 0; j < comp.Len(); j++ {
//if s.elements.Elem(i).Eq(comp.elements.Elem(j)) {
//found = true
//break
//}
//}
//ret = append(ret, found)
//}
//default:
//return nil, errors.New("Unknown comparator")
//}
//return ret, nil
//}

// Copy wil copy the values of a given Series
func (s Series) Copy() Series {
	name := s.Name
	t := s.t
	elements := make([]elementInterface, 0)
	for _, e := range s.elements {
		elements = append(elements, e.Copy())
	}
	ret := Series{
		Name:     name,
		t:        t,
		elements: elements,
	}
	return ret
}

// Records returns the elements of a Series in a []string
func (s Series) Records() []string {
	var ret []string
	for _, e := range s.elements {
		ret = append(ret, e.String())
	}
	return ret
}

// Float returns the elements of a Series in a []float64. If the elements can not
// be converted to float64 or contains a NaN returns the float representation of
// NaN.
func (s Series) Float() []float64 {
	var ret []float64
	for _, e := range s.elements {
		val := e.ToFloat().Val()
		if val == nil {
			ret = append(ret, math.NaN())
		} else {
			ret = append(ret, val.(float64))
		}
	}
	return ret
}

func (s Series) Int() ([]int, error) {
	var ret []int
	for _, e := range s.elements {
		val := e.ToInt().Val()
		if val == nil {
			return nil, errors.New("can't convert NaN to int")
		}
		ret = append(ret, val.(int))
	}
	return ret, nil
}

func (s Series) Bool() ([]bool, error) {
	var ret []bool
	for _, e := range s.elements {
		val := e.ToBool().Val()
		if val == nil {
			return nil, errors.New("can't convert NaN to bool")
		}
		ret = append(ret, val.(bool))
	}
	return ret, nil
}

// Type returns the type of a given series
func (s Series) Type() Type {
	return s.t
}

// Len returns the length of a given Series
func (s Series) Len() int {
	return len(s.elements)
}

// String implements the Stringer interface for Series
func (s Series) String() string {
	return fmt.Sprint(s.elements)
}

// Str prints some extra information about a given series
func (s Series) Str() string {
	var ret []string
	// If name exists print name
	if s.Name != "" {
		ret = append(ret, "Name: "+s.Name)
	}
	ret = append(ret, "Type: "+fmt.Sprint(s.t))
	ret = append(ret, "Length: "+fmt.Sprint(s.Len()))
	if s.Len() != 0 {
		ret = append(ret, "Values: "+fmt.Sprint(s))
	}
	return strings.Join(ret, "\n")
}

// Err returns the error contained in the series
func (s Series) Err() error {
	return s.err
}

func addr(s Series) []string {
	var ret []string
	for _, e := range s.elements {
		ret = append(ret, e.Addr())
	}
	return ret
}
<|endoftext|>


<commit_before>package helpers

import (
	"fmt"
	"os"
	"strconv"

	"github.com/onsi/ginkgo"
)

func RepeatCount() int {
	countVar := os.Getenv("INIGO_REPEAT_COUNT")
	if countVar == "" {
		return 1
	}

	count, err := strconv.Atoi(countVar)
	if err != nil {
		ginkgo.Fail(fmt.Sprintf("Invalid INIGO_REPEAT_COUNT env variable - %s", countVar))
	}

	return count
}
<commit_msg>remove unused RepeatCount helper [#86158802]<commit_after><|endoftext|>


<commit_before>package broker

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/micro/go-micro/errors"
	"github.com/micro/go-micro/registry"
	mls "github.com/micro/misc/lib/tls"
	"github.com/pborman/uuid"

	"golang.org/x/net/context"
)

// HTTP Broker is a placeholder for actual message brokers.
// This should not really be used in production but useful
// in developer where you want zero dependencies.

type httpBroker struct {
	id          string
	address     string
	unsubscribe chan *httpSubscriber
	opts        Options

	mux *http.ServeMux

	c *http.Client
	r registry.Registry

	sync.RWMutex
	subscribers map[string][]*httpSubscriber
	running     bool
	exit        chan chan error
}

type httpSubscriber struct {
	opts  SubscribeOptions
	id    string
	topic string
	ch    chan *httpSubscriber
	fn    Handler
	svc   *registry.Service
}

type httpPublication struct {
	m *Message
	t string
}

var (
	DefaultSubPath   = "/_sub"
	broadcastVersion = "ff.http.broadcast"
	registerTTL      = time.Minute
	registerInterval = time.Second * 30
)

func init() {
	rand.Seed(time.Now().Unix())
}

func newTransport(config *tls.Config) *http.Transport {
	if config == nil {
		config = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     config,
	}
	runtime.SetFinalizer(&t, func(tr **http.Transport) {
		(*tr).CloseIdleConnections()
	})
	return t
}

func newHttpBroker(opts ...Option) Broker {
	options := Options{
		Context: context.TODO(),
	}

	for _, o := range opts {
		o(&options)
	}

	addr := ":0"
	if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
		addr = options.Addrs[0]
	}

	reg, ok := options.Context.Value(registryKey).(registry.Registry)
	if !ok {
		reg = registry.DefaultRegistry
	}

	h := &httpBroker{
		id:          "broker-" + uuid.NewUUID().String(),
		address:     addr,
		opts:        options,
		r:           reg,
		c:           &http.Client{Transport: newTransport(options.TLSConfig)},
		subscribers: make(map[string][]*httpSubscriber),
		unsubscribe: make(chan *httpSubscriber),
		exit:        make(chan chan error),
		mux:         http.NewServeMux(),
	}

	h.mux.Handle(DefaultSubPath, h)
	return h
}

func (h *httpPublication) Ack() error {
	return nil
}

func (h *httpPublication) Message() *Message {
	return h.m
}

func (h *httpPublication) Topic() string {
	return h.t
}

func (h *httpSubscriber) Options() SubscribeOptions {
	return h.opts
}

func (h *httpSubscriber) Topic() string {
	return h.topic
}

func (h *httpSubscriber) Unsubscribe() error {
	h.ch <- h
	// artificial delay
	time.Sleep(time.Millisecond * 10)
	return nil
}

func (h *httpBroker) run(l net.Listener) {
	t := time.NewTicker(registerInterval)
	defer t.Stop()

	for {
		select {
		// heartbeat for each subscriber
		case <-t.C:
			h.RLock()
			for _, subs := range h.subscribers {
				for _, sub := range subs {
					h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
				}
			}
			h.RUnlock()
		// received exit signal
		case ch := <-h.exit:
			ch <- l.Close()
			h.Lock()
			h.running = false
			h.Unlock()
			return
		// unsubscribe subscriber
		case subscriber := <-h.unsubscribe:
			h.Lock()
			var subscribers []*httpSubscriber
			for _, sub := range h.subscribers[subscriber.topic] {
				if sub.id == subscriber.id {
					h.r.Deregister(sub.svc)
				}
				subscribers = append(subscribers, sub)
			}
			h.subscribers[subscriber.topic] = subscribers
			h.Unlock()
		}
	}
}

func (h *httpBroker) start() error {
	h.Lock()
	defer h.Unlock()

	if h.running {
		return nil
	}

	var l net.Listener
	var err error

	if h.opts.Secure || h.opts.TLSConfig != nil {
		config := h.opts.TLSConfig
		if config == nil {
			cert, err := mls.Certificate(h.address)
			if err != nil {
				return err
			}
			config = &tls.Config{Certificates: []tls.Certificate{cert}}
		}
		l, err = tls.Listen("tcp", h.address, config)
	} else {
		l, err = net.Listen("tcp", h.address)
	}

	if err != nil {
		return err
	}

	log.Printf("Broker Listening on %s", l.Addr().String())
	h.address = l.Addr().String()

	go http.Serve(l, h.mux)
	go h.run(l)

	h.running = true
	return nil
}

func (h *httpBroker) stop() error {
	h.Lock()
	defer h.Unlock()

	if !h.running {
		return nil
	}

	ch := make(chan error)
	h.exit <- ch
	err := <-ch
	h.running = false
	return err
}

func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		err := errors.BadRequest("go.micro.broker", "Method not allowed")
		http.Error(w, err.Error(), http.StatusMethodNotAllowed)
		return
	}
	defer req.Body.Close()

	req.ParseForm()

	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error reading request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	var m *Message
	if err = json.Unmarshal(b, &m); err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	topic := m.Header[":topic"]
	delete(m.Header, ":topic")

	if len(topic) == 0 {
		errr := errors.InternalServerError("go.micro.broker", "Topic not found")
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	p := &httpPublication{m: m, t: topic}
	id := req.Form.Get("id")

	h.RLock()

	for _, subscriber := range h.subscribers[topic] {
		if id == subscriber.id {
			// sub is sync; crufty rate limiting
			// so we don't hose the cpu
			subscriber.fn(p)
		}
	}
	h.RUnlock()
}

func (h *httpBroker) Address() string {
	return h.address
}

func (h *httpBroker) Connect() error {
	return h.start()
}

func (h *httpBroker) Disconnect() error {
	return h.stop()
}

func (h *httpBroker) Init(opts ...Option) error {
	for _, o := range opts {
		o(&h.opts)
	}

	if len(h.id) == 0 {
		h.id = "broker-" + uuid.NewUUID().String()
	}

	reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
	if !ok {
		reg = registry.DefaultRegistry
	}

	h.r = reg

	return nil
}

func (h *httpBroker) Options() Options {
	return h.opts
}

func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
	s, err := h.r.GetService("topic:" + topic)
	if err != nil {
		return err
	}

	if msg.Header == nil {
		msg.Header = map[string]string{}
	}

	msg.Header[":topic"] = topic
	b, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	fn := func(node *registry.Node, b []byte) {
		scheme := "http"

		// check if secure is added in metadata
		if node.Metadata["secure"] == "true" {
			scheme = "https"
		}

		vals := url.Values{}
		vals.Add("id", node.Id)

		uri := fmt.Sprintf("%s://%s:%d%s?%s", scheme, node.Address, node.Port, DefaultSubPath, vals.Encode())
		r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
		if err == nil {
			io.Copy(ioutil.Discard, r.Body)
			r.Body.Close()
		}
	}

	for _, service := range s {
		// broadcast version means broadcast to all nodes
		if service.Version == broadcastVersion {
			for _, node := range service.Nodes {
				// publish async
				go fn(node, b)
			}
			return nil
		}

		node := service.Nodes[rand.Int()%len(service.Nodes)]
		// publish async
		go fn(node, b)
	}

	return nil
}

func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
	opt := newSubscribeOptions(opts...)

	// parse address for host, port
	parts := strings.Split(h.Address(), ":")
	host := strings.Join(parts[:len(parts)-1], ":")
	port, _ := strconv.Atoi(parts[len(parts)-1])

	addr, err := extractAddress(host)
	if err != nil {
		return nil, err
	}

	id := uuid.NewUUID().String()

	var secure bool

	if h.opts.Secure || h.opts.TLSConfig != nil {
		secure = true
	}

	// register service
	node := &registry.Node{
		Id:      h.id + "." + id,
		Address: addr,
		Port:    port,
		Metadata: map[string]string{
			"secure": fmt.Sprintf("%t", secure),
		},
	}

	version := opt.Queue
	if len(version) == 0 {
		version = broadcastVersion
	}

	service := &registry.Service{
		Name:    "topic:" + topic,
		Version: version,
		Nodes:   []*registry.Node{node},
	}

	subscriber := &httpSubscriber{
		opts:  opt,
		id:    h.id + "." + id,
		topic: topic,
		ch:    h.unsubscribe,
		fn:    handler,
		svc:   service,
	}

	if err := h.r.Register(service, registry.RegisterTTL(registerTTL)); err != nil {
		return nil, err
	}

	h.Lock()
	h.subscribers[topic] = append(h.subscribers[topic], subscriber)
	h.Unlock()
	return subscriber, nil
}

func (h *httpBroker) String() string {
	return "http"
}
<commit_msg>Concurrent use of message and we modify it, bad practice, copy it<commit_after>package broker

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/micro/go-micro/errors"
	"github.com/micro/go-micro/registry"
	mls "github.com/micro/misc/lib/tls"
	"github.com/pborman/uuid"

	"golang.org/x/net/context"
)

// HTTP Broker is a placeholder for actual message brokers.
// This should not really be used in production but useful
// in developer where you want zero dependencies.

type httpBroker struct {
	id          string
	address     string
	unsubscribe chan *httpSubscriber
	opts        Options

	mux *http.ServeMux

	c *http.Client
	r registry.Registry

	sync.RWMutex
	subscribers map[string][]*httpSubscriber
	running     bool
	exit        chan chan error
}

type httpSubscriber struct {
	opts  SubscribeOptions
	id    string
	topic string
	ch    chan *httpSubscriber
	fn    Handler
	svc   *registry.Service
}

type httpPublication struct {
	m *Message
	t string
}

var (
	DefaultSubPath   = "/_sub"
	broadcastVersion = "ff.http.broadcast"
	registerTTL      = time.Minute
	registerInterval = time.Second * 30
)

func init() {
	rand.Seed(time.Now().Unix())
}

func newTransport(config *tls.Config) *http.Transport {
	if config == nil {
		config = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     config,
	}
	runtime.SetFinalizer(&t, func(tr **http.Transport) {
		(*tr).CloseIdleConnections()
	})
	return t
}

func newHttpBroker(opts ...Option) Broker {
	options := Options{
		Context: context.TODO(),
	}

	for _, o := range opts {
		o(&options)
	}

	addr := ":0"
	if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
		addr = options.Addrs[0]
	}

	reg, ok := options.Context.Value(registryKey).(registry.Registry)
	if !ok {
		reg = registry.DefaultRegistry
	}

	h := &httpBroker{
		id:          "broker-" + uuid.NewUUID().String(),
		address:     addr,
		opts:        options,
		r:           reg,
		c:           &http.Client{Transport: newTransport(options.TLSConfig)},
		subscribers: make(map[string][]*httpSubscriber),
		unsubscribe: make(chan *httpSubscriber),
		exit:        make(chan chan error),
		mux:         http.NewServeMux(),
	}

	h.mux.Handle(DefaultSubPath, h)
	return h
}

func (h *httpPublication) Ack() error {
	return nil
}

func (h *httpPublication) Message() *Message {
	return h.m
}

func (h *httpPublication) Topic() string {
	return h.t
}

func (h *httpSubscriber) Options() SubscribeOptions {
	return h.opts
}

func (h *httpSubscriber) Topic() string {
	return h.topic
}

func (h *httpSubscriber) Unsubscribe() error {
	h.ch <- h
	// artificial delay
	time.Sleep(time.Millisecond * 10)
	return nil
}

func (h *httpBroker) run(l net.Listener) {
	t := time.NewTicker(registerInterval)
	defer t.Stop()

	for {
		select {
		// heartbeat for each subscriber
		case <-t.C:
			h.RLock()
			for _, subs := range h.subscribers {
				for _, sub := range subs {
					h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
				}
			}
			h.RUnlock()
		// received exit signal
		case ch := <-h.exit:
			ch <- l.Close()
			h.Lock()
			h.running = false
			h.Unlock()
			return
		// unsubscribe subscriber
		case subscriber := <-h.unsubscribe:
			h.Lock()
			var subscribers []*httpSubscriber
			for _, sub := range h.subscribers[subscriber.topic] {
				if sub.id == subscriber.id {
					h.r.Deregister(sub.svc)
				}
				subscribers = append(subscribers, sub)
			}
			h.subscribers[subscriber.topic] = subscribers
			h.Unlock()
		}
	}
}

func (h *httpBroker) start() error {
	h.Lock()
	defer h.Unlock()

	if h.running {
		return nil
	}

	var l net.Listener
	var err error

	if h.opts.Secure || h.opts.TLSConfig != nil {
		config := h.opts.TLSConfig
		if config == nil {
			cert, err := mls.Certificate(h.address)
			if err != nil {
				return err
			}
			config = &tls.Config{Certificates: []tls.Certificate{cert}}
		}
		l, err = tls.Listen("tcp", h.address, config)
	} else {
		l, err = net.Listen("tcp", h.address)
	}

	if err != nil {
		return err
	}

	log.Printf("Broker Listening on %s", l.Addr().String())
	h.address = l.Addr().String()

	go http.Serve(l, h.mux)
	go h.run(l)

	h.running = true
	return nil
}

func (h *httpBroker) stop() error {
	h.Lock()
	defer h.Unlock()

	if !h.running {
		return nil
	}

	ch := make(chan error)
	h.exit <- ch
	err := <-ch
	h.running = false
	return err
}

func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		err := errors.BadRequest("go.micro.broker", "Method not allowed")
		http.Error(w, err.Error(), http.StatusMethodNotAllowed)
		return
	}
	defer req.Body.Close()

	req.ParseForm()

	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error reading request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	var m *Message
	if err = json.Unmarshal(b, &m); err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	topic := m.Header[":topic"]
	delete(m.Header, ":topic")

	if len(topic) == 0 {
		errr := errors.InternalServerError("go.micro.broker", "Topic not found")
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	p := &httpPublication{m: m, t: topic}
	id := req.Form.Get("id")

	h.RLock()
	for _, subscriber := range h.subscribers[topic] {
		if id == subscriber.id {
			// sub is sync; crufty rate limiting
			// so we don't hose the cpu
			subscriber.fn(p)
		}
	}
	h.RUnlock()
}

func (h *httpBroker) Address() string {
	return h.address
}

func (h *httpBroker) Connect() error {
	return h.start()
}

func (h *httpBroker) Disconnect() error {
	return h.stop()
}

func (h *httpBroker) Init(opts ...Option) error {
	for _, o := range opts {
		o(&h.opts)
	}

	if len(h.id) == 0 {
		h.id = "broker-" + uuid.NewUUID().String()
	}

	reg, ok := h.opts.Context.Value(registryKey).(registry.Registry)
	if !ok {
		reg = registry.DefaultRegistry
	}

	h.r = reg

	return nil
}

func (h *httpBroker) Options() Options {
	return h.opts
}

func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
	s, err := h.r.GetService("topic:" + topic)
	if err != nil {
		return err
	}

	m := &Message{
		Header: make(map[string]string),
		Body:   msg.Body,
	}

	for k, v := range msg.Header {
		m.Header[k] = v
	}

	m.Header[":topic"] = topic

	b, err := json.Marshal(m)
	if err != nil {
		return err
	}

	fn := func(node *registry.Node, b []byte) {
		scheme := "http"

		// check if secure is added in metadata
		if node.Metadata["secure"] == "true" {
			scheme = "https"
		}

		vals := url.Values{}
		vals.Add("id", node.Id)

		uri := fmt.Sprintf("%s://%s:%d%s?%s", scheme, node.Address, node.Port, DefaultSubPath, vals.Encode())
		r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
		if err == nil {
			io.Copy(ioutil.Discard, r.Body)
			r.Body.Close()
		}
	}

	for _, service := range s {
		// broadcast version means broadcast to all nodes
		if service.Version == broadcastVersion {
			for _, node := range service.Nodes {
				// publish async
				go fn(node, b)
			}
			return nil
		}

		node := service.Nodes[rand.Int()%len(service.Nodes)]
		// publish async
		go fn(node, b)
	}

	return nil
}

func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
	opt := newSubscribeOptions(opts...)

	// parse address for host, port
	parts := strings.Split(h.Address(), ":")
	host := strings.Join(parts[:len(parts)-1], ":")
	port, _ := strconv.Atoi(parts[len(parts)-1])

	addr, err := extractAddress(host)
	if err != nil {
		return nil, err
	}

	id := uuid.NewUUID().String()

	var secure bool

	if h.opts.Secure || h.opts.TLSConfig != nil {
		secure = true
	}

	// register service
	node := &registry.Node{
		Id:      h.id + "." + id,
		Address: addr,
		Port:    port,
		Metadata: map[string]string{
			"secure": fmt.Sprintf("%t", secure),
		},
	}

	version := opt.Queue
	if len(version) == 0 {
		version = broadcastVersion
	}

	service := &registry.Service{
		Name:    "topic:" + topic,
		Version: version,
		Nodes:   []*registry.Node{node},
	}

	subscriber := &httpSubscriber{
		opts:  opt,
		id:    h.id + "." + id,
		topic: topic,
		ch:    h.unsubscribe,
		fn:    handler,
		svc:   service,
	}

	if err := h.r.Register(service, registry.RegisterTTL(registerTTL)); err != nil {
		return nil, err
	}

	h.Lock()
	h.subscribers[topic] = append(h.subscribers[topic], subscriber)
	h.Unlock()
	return subscriber, nil
}

func (h *httpBroker) String() string {
	return "http"
}
<|endoftext|>


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

package main

import (
	"bytes"
	"code.google.com/p/bencode-go"
	"crypto/sha1"
	"errors"
	"launchpad.net/tomb"
	"log"
	"os"
)

type Torrent struct {
	metaInfo MetaInfo
	infoHash []byte
	peer     chan PeerTuple
	Stats    Stats
	t        tomb.Tomb
}

type Stats struct {
	Left       int
	Uploaded   int
	Downloaded int
}

// Metainfo File Structure
type MetaInfo struct {
	Info struct {
		PieceLength int "piece length"
		Pieces      string
		Private     int
		Name        string
		Length      int
		Md5sum      string
		Files       []struct {
			Length int
			Md5sum string
			Path   []string
		}
	}
	Announce     string
	AnnounceList [][]string "announce-list"
	CreationDate int        "creation date"
	Comment      string
	CreatedBy    string "created by"
	Encoding     string
}

// ParseTorrentFile opens the torrent filename specified and parses it,
// returning a Torrent structure with the MetaInfo and SHA-1 hash of the
// Info dictionary.
func ParseTorrentFile(filename string) (torrent Torrent, err error) {
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// Decode the file into a generic bencode representation
	m, err := bencode.Decode(file)
	if err != nil {
		return
	}
	// WTF?: Understand the next line
	metaMap, ok := m.(map[string]interface{})
	if !ok {
		err = errors.New("Couldn't parse torrent file")
		return
	}
	infoDict, ok := metaMap["info"]
	if !ok {
		err = errors.New("Unable to locate info dict in torrent file")
		return
	}

	// Create an Info dict based on the decoded file
	var b bytes.Buffer
	err = bencode.Marshal(&b, infoDict)
	if err != nil {
		return
	}

	// Compute the info hash
	h := sha1.New()
	h.Write(b.Bytes())
	torrent.infoHash = append(torrent.infoHash, h.Sum(nil)...)

	// Populate the metaInfo structure
	file.Seek(0, 0)
	err = bencode.Unmarshal(file, &torrent.metaInfo)
	if err != nil {
		return
	}

	// Print a summary about the torrent file
	log.Printf("Parse : ParseTorrentFile : Successfully parsed %s", filename)
	log.Printf("Parse : ParseTorrentFile : Determined that %d pieces exist in the torrent", (len(torrent.metaInfo.Info.Pieces) / 20))

	return
}

// Init completes the initalization of the Torrent structure
func (t *Torrent) Init() {
	// Initialize bytes left to download
	if len(t.metaInfo.Info.Files) > 0 {
		for _, file := range t.metaInfo.Info.Files {
			t.Stats.Left += file.Length
		}
	} else {
		t.Stats.Left = t.metaInfo.Info.Length
	}
	// TODO: Read in the file and adjust bytes left
}

// Stop stops this Torrent session
func (t *Torrent) Stop() error {
	log.Println("Torrent : Stop : Stopping")
	t.t.Kill(nil)
	return t.t.Wait()
}

// Run starts the Torrent session and orchestrates all the child processes
func (t *Torrent) Run() {
	log.Println("Torrent : Run : Started")
	defer t.t.Done()
	defer log.Println("Torrent : Run : Completed")
	t.Init()

	pieceHashes := make([][]byte, 0)
	for offset := 0; offset <= len(t.metaInfo.Info.Pieces)-20; offset += 20 {
		pieceHashes = append(pieceHashes, []byte(t.metaInfo.Info.Pieces[offset:offset+20]))
	}

	diskIO := NewDiskIO(t.metaInfo)
	diskIO.Init()
	pieces := diskIO.Verify()
	if len(pieces) == 0 {
		// DiskIO wasn't able to read any files from disk. Initialize the pieces slice.
		pieces = make([]bool, len(pieceHashes))
	}

	go diskIO.Run()

	server := NewServer()
	trackerManager := NewTrackerManager(server.Port)
	peerManager := NewPeerManager(t.infoHash, len(pieceHashes), diskIO.peerChans, server.peerChans, trackerManager.peerChans)

	controller := NewController(pieces, pieceHashes, diskIO.contChans, peerManager.contChans, peerManager.peerContChans)

	go controller.Run()
	go peerManager.Run()
	go server.Run()
	go trackerManager.Run(t.metaInfo, t.infoHash)

	for {
		select {
		case <-t.t.Dying():
			server.Stop()
			peerManager.Stop()
			trackerManager.Stop()
			diskIO.Stop()
			return
		}
	}
}
<commit_msg>Sent pieceLength in PeerManger constructor<commit_after>// Copyright 2013 Jari Takkala. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"code.google.com/p/bencode-go"
	"crypto/sha1"
	"errors"
	"launchpad.net/tomb"
	"log"
	"os"
)

type Torrent struct {
	metaInfo MetaInfo
	infoHash []byte
	peer     chan PeerTuple
	Stats    Stats
	t        tomb.Tomb
}

type Stats struct {
	Left       int
	Uploaded   int
	Downloaded int
}

// Metainfo File Structure
type MetaInfo struct {
	Info struct {
		PieceLength int "piece length"
		Pieces      string
		Private     int
		Name        string
		Length      int
		Md5sum      string
		Files       []struct {
			Length int
			Md5sum string
			Path   []string
		}
	}
	Announce     string
	AnnounceList [][]string "announce-list"
	CreationDate int        "creation date"
	Comment      string
	CreatedBy    string "created by"
	Encoding     string
}

// ParseTorrentFile opens the torrent filename specified and parses it,
// returning a Torrent structure with the MetaInfo and SHA-1 hash of the
// Info dictionary.
func ParseTorrentFile(filename string) (torrent Torrent, err error) {
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// Decode the file into a generic bencode representation
	m, err := bencode.Decode(file)
	if err != nil {
		return
	}
	// WTF?: Understand the next line
	metaMap, ok := m.(map[string]interface{})
	if !ok {
		err = errors.New("Couldn't parse torrent file")
		return
	}
	infoDict, ok := metaMap["info"]
	if !ok {
		err = errors.New("Unable to locate info dict in torrent file")
		return
	}

	// Create an Info dict based on the decoded file
	var b bytes.Buffer
	err = bencode.Marshal(&b, infoDict)
	if err != nil {
		return
	}

	// Compute the info hash
	h := sha1.New()
	h.Write(b.Bytes())
	torrent.infoHash = append(torrent.infoHash, h.Sum(nil)...)

	// Populate the metaInfo structure
	file.Seek(0, 0)
	err = bencode.Unmarshal(file, &torrent.metaInfo)
	if err != nil {
		return
	}

	// Print a summary about the torrent file
	log.Printf("Parse : ParseTorrentFile : Successfully parsed %s", filename)
	log.Printf("Parse : ParseTorrentFile : Determined that %d pieces exist in the torrent", (len(torrent.metaInfo.Info.Pieces) / 20))

	return
}

// Init completes the initalization of the Torrent structure
func (t *Torrent) Init() {
	// Initialize bytes left to download
	if len(t.metaInfo.Info.Files) > 0 {
		for _, file := range t.metaInfo.Info.Files {
			t.Stats.Left += file.Length
		}
	} else {
		t.Stats.Left = t.metaInfo.Info.Length
	}
	// TODO: Read in the file and adjust bytes left
}

// Stop stops this Torrent session
func (t *Torrent) Stop() error {
	log.Println("Torrent : Stop : Stopping")
	t.t.Kill(nil)
	return t.t.Wait()
}

// Run starts the Torrent session and orchestrates all the child processes
func (t *Torrent) Run() {
	log.Println("Torrent : Run : Started")
	defer t.t.Done()
	defer log.Println("Torrent : Run : Completed")
	t.Init()

	pieceHashes := make([][]byte, 0)
	for offset := 0; offset <= len(t.metaInfo.Info.Pieces)-20; offset += 20 {
		pieceHashes = append(pieceHashes, []byte(t.metaInfo.Info.Pieces[offset:offset+20]))
	}

	diskIO := NewDiskIO(t.metaInfo)
	diskIO.Init()
	pieces := diskIO.Verify()
	if len(pieces) == 0 {
		// DiskIO wasn't able to read any files from disk. Initialize the pieces slice.
		pieces = make([]bool, len(pieceHashes))
	}

	go diskIO.Run()

	server := NewServer()
	trackerManager := NewTrackerManager(server.Port)
	peerManager := NewPeerManager(t.infoHash, len(pieceHashes), t.metaInfo.Info.PieceLength, diskIO.peerChans, server.peerChans, trackerManager.peerChans)

	controller := NewController(pieces, pieceHashes, diskIO.contChans, peerManager.contChans, peerManager.peerContChans)

	go controller.Run()
	go peerManager.Run()
	go server.Run()
	go trackerManager.Run(t.metaInfo, t.infoHash)

	for {
		select {
		case <-t.t.Dying():
			server.Stop()
			peerManager.Stop()
			trackerManager.Stop()
			diskIO.Stop()
			return
		}
	}
}
<|endoftext|>


<commit_before>package townsita

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

type Townsita struct {
	config *Config
	da     *DataAdapter
}

func New(da *DataAdapter) *Townsita {
	return &Townsita{
		da: da,
	}
}

type appHandler func(http.ResponseWriter, *http.Request) error

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if err := fn(w, r); err != nil {
		httpError, ok := err.(HTTPError)
		if ok {
			http.Error(w, httpError.Message, httpError.Code)
			return
		}
		// Default to 500 Internal Server Error
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (t *Townsita) GetHTTPHandler(args []string) http.Handler {

	t.config = NewConfig()
	if err := t.config.Load(args); err != nil {
		panic(err)
	}

	r := mux.NewRouter()
	r.HandleFunc("/", appHandler(t.indexHandler).ServeHTTP).Methods("GET")
	return r
}

func (t *Townsita) indexHandler(w http.ResponseWriter, r *http.Request) error {
	s := NewSession(t.config)
	s.Set("MessageTypes", t.da.MustGetMessageTypes())
	s.AddPath("/", "Home")
	return s.render(w, r, t.config.templatePath("layout.html"), t.config.templatePath("index.html"))
}
<commit_msg>Un-pointer<commit_after>package townsita

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

type Townsita struct {
	config *Config
	da     DataAdapter
}

func New(da DataAdapter) *Townsita {
	return &Townsita{
		da: da,
	}
}

type appHandler func(http.ResponseWriter, *http.Request) error

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if err := fn(w, r); err != nil {
		httpError, ok := err.(HTTPError)
		if ok {
			http.Error(w, httpError.Message, httpError.Code)
			return
		}
		// Default to 500 Internal Server Error
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (t *Townsita) GetHTTPHandler(args []string) http.Handler {

	t.config = NewConfig()
	if err := t.config.Load(args); err != nil {
		panic(err)
	}

	r := mux.NewRouter()
	r.HandleFunc("/", appHandler(t.indexHandler).ServeHTTP).Methods("GET")
	return r
}

func (t *Townsita) indexHandler(w http.ResponseWriter, r *http.Request) error {
	s := NewSession(t.config)
	s.Set("MessageTypes", t.da.MustGetMessageTypes())
	s.AddPath("/", "Home")
	return s.render(w, r, t.config.templatePath("layout.html"), t.config.templatePath("index.html"))
}
<|endoftext|>


<commit_before>package stripe

// TerminalReaderParams is the set of parameters that can be used for creating or updating a terminal reader.
type TerminalReaderParams struct {
	Params           `form:"*"`
	Label            *string `form:"label"`
	Location         *string `form:"location"`
	RegistrationCode *string `form:"registration_code"`

	// This feature has been deprecated and should not be used anymore.
	OperatorAccount *string `form:"operator_account"`
}

// TerminalReaderGetParams is the set of parameters that can be used to get a terminal reader.
type TerminalReaderGetParams struct {
	Params `form:"*"`

	// This feature has been deprecated and should not be used anymore.
	OperatorAccount *string `form:"operator_account"`
}

// TerminalReaderListParams is the set of parameters that can be used when listing temrinal readers.
type TerminalReaderListParams struct {
	ListParams `form:"*"`
	Location   *string `form:"location"`
	Status     *string `form:"status"`

	// This feature has been deprecated and should not be used anymore.
	OperatorAccount *string `form:"operator_account"`
}

// TerminalReader is the resource representing a Stripe terminal reader.
type TerminalReader struct {
	Deleted         bool   `json:"deleted"`
	DeviceSwVersion string `json:"device_sw_version"`
	DeviceType      string `json:"device_type"`
	ID              string `json:"id"`
	IPAddress       string `json:"ip_address"`
	Label           string `json:"label"`
	Location        string `json:"location"`
	Object          string `json:"object"`
	SerialNumber    string `json:"serial_number"`
	Status          string `json:"status"`
}

// TerminalReaderList is a list of terminal readers as retrieved from a list endpoint.
type TerminalReaderList struct {
	ListMeta
	Data []*TerminalReader `json:"data"`
}
<commit_msg>Add support for `Status` and `Location` filters when listing `Reader`s<commit_after>package stripe

// TerminalReaderParams is the set of parameters that can be used for creating or updating a terminal reader.
type TerminalReaderParams struct {
	Params           `form:"*"`
	Label            *string `form:"label"`
	Location         *string `form:"location"`
	RegistrationCode *string `form:"registration_code"`

	// This feature has been deprecated and should not be used anymore.
	OperatorAccount *string `form:"operator_account"`
}

// TerminalReaderGetParams is the set of parameters that can be used to get a terminal reader.
type TerminalReaderGetParams struct {
	Params `form:"*"`

	// This feature has been deprecated and should not be used anymore.
	OperatorAccount *string `form:"operator_account"`
}

// TerminalReaderListParams is the set of parameters that can be used when listing temrinal readers.
type TerminalReaderListParams struct {
	ListParams `form:"*"`
	Location   *string `form:"location"`
	Status     *string `form:"status"`

	// This feature has been deprecated and should not be used anymore.
	OperatorAccount *string `form:"operator_account"`
}

// TerminalReader is the resource representing a Stripe terminal reader.
type TerminalReader struct {
	Deleted         bool   `json:"deleted"`
	DeviceSwVersion string `json:"device_sw_version"`
	DeviceType      string `json:"device_type"`
	ID              string `json:"id"`
	IPAddress       string `json:"ip_address"`
	Label           string `json:"label"`
	Location        string `json:"location"`
	Object          string `json:"object"`
	SerialNumber    string `json:"serial_number"`
	Status          string `json:"status"`
}

// TerminalReaderList is a list of terminal readers as retrieved from a list endpoint.
type TerminalReaderList struct {
	ListMeta
	Data     []*TerminalReader `json:"data"`
	Location *string           `json:"location"`
	Status   *string           `json:"status"`
}
<|endoftext|>


<commit_before>package termite

import (
	"log"
	"os"
	"sync"
)

var _ = log.Println

type fileSetWaiter struct {
	master *Master
	mirror *mirrorConnection
	sync.Mutex
	channels map[chan int]bool
}

func newFileSetWaiter(master *Master, mc *mirrorConnection) *fileSetWaiter {
	return &fileSetWaiter{
		mirror: mc,
		master: master,
		channels: make(map[chan int]bool),
	}
}

func (me *fileSetWaiter) broadcast(id int, ignore chan int) {
	me.Lock()
	defer me.Unlock()
	for ch, _ := range me.channels {
		if ch != ignore {
			ch <- id
		}
	}
}

func (me *fileSetWaiter) getChannel() chan int {
	me.Lock()
	defer me.Unlock()

	c := make(chan int, 10)	// TODO - what is a good size?
	me.channels[c] = true
	return c
}

func (me *fileSetWaiter) flush() {
	me.Lock()
	defer me.Unlock()
	for ch, _ := range me.channels {
		close(ch)
	}
}

func (me *fileSetWaiter) wait(rep *WorkResponse, completion chan int) (err os.Error) {
	id := rep.FileSetId
	if rep.FileSet != nil {
		err = me.master.replayFileModifications(me.mirror.rpcClient, rep.FileSet.Files)
		if err == nil {
			me.master.fileServer.updateFiles(rep.FileSet.Files)
			me.master.mirrors.queueFiles(me.mirror, rep.FileSet.Files)
			me.broadcast(id, completion)
		} else {
			me.flush()
		}
	} else {
		for {
			c, ok := <-completion
			if !ok {
				return os.NewError("files were never sent.")
			}
			if c == id {
				break
			}
		}
	}

	me.Lock()
	defer me.Unlock()
	me.channels[completion] = false, false
	return err
}
<commit_msg>Do channel sends async, so we don't deadlock.<commit_after>package termite

import (
	"log"
	"os"
	"sync"
)

var _ = log.Println

type fileSetWaiter struct {
	master *Master
	mirror *mirrorConnection
	sync.Mutex
	channels map[chan int]bool
}

func newFileSetWaiter(master *Master, mc *mirrorConnection) *fileSetWaiter {
	return &fileSetWaiter{
		mirror: mc,
		master: master,
		channels: make(map[chan int]bool),
	}
}

func (me *fileSetWaiter) broadcast(id int, ignore chan int) {
	me.Lock()
	defer me.Unlock()
	for ch, _ := range me.channels {
		if ch != ignore {
			go func(ch chan int) { ch <- id }(ch)
		}
	}
}

func (me *fileSetWaiter) getChannel() chan int {
	me.Lock()
	defer me.Unlock()

	c := make(chan int, 10)	// TODO - what is a good size?
	me.channels[c] = true
	return c
}

func (me *fileSetWaiter) flush() {
	me.Lock()
	defer me.Unlock()
	for ch, _ := range me.channels {
		close(ch)
	}
}

func (me *fileSetWaiter) drop(completion chan int) {
	me.Lock()
	defer me.Unlock()
	me.channels[completion] = false, false
}

func (me *fileSetWaiter) wait(rep *WorkResponse, completion chan int) (err os.Error) {
	id := rep.FileSetId
	if rep.FileSet != nil {
		err = me.master.replayFileModifications(me.mirror.rpcClient, rep.FileSet.Files)
		if err == nil {
			me.master.fileServer.updateFiles(rep.FileSet.Files)
			me.master.mirrors.queueFiles(me.mirror, rep.FileSet.Files)
			me.broadcast(id, completion)
		} else {
			me.flush()
		}
	} else {
		for {
			c, ok := <-completion
			if !ok {
				return os.NewError("files were never sent.")
			}
			if c == id {
				break
			}
		}
	}

	me.drop(completion)
	return err
}
<|endoftext|>


<commit_before>package jiraclient

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"

	jira "github.com/andygrunwald/go-jira"

	"github.com/dimfeld/promulgator/commandrouter"
	"github.com/dimfeld/promulgator/model"
)

const (
	Comment = iota
	Assign
	Resolve
	Close
	Reopen
	SetFixVersion
)

var commands []commandrouter.Command = []commandrouter.Command{
	{Comment, "comment", false, false, "Add a comment to an issue -- comment <id> <comment text>"},
	{Comment, "c", false, false, "Add a comment to an issue -- c <id> <comment text>"},
	{Assign, "assign", false, false, "Assign an issue to a user -- assign <id> <user>"},
	{Assign, "a", false, false, "Assign an issue to a user -- a <id> <user>"},
	// {Resolve, "resolve", false, false, "Resolve an issue -- resolve <id>"},
	// {Resolve, "r", false, false, "Resolve an issue -- r <id>"},
	// {Close, "close", false, false, "Close an issue -- close <id>"},
	// {Close, "c", false, false, "Close an issue -- c <id>"},
	// {Reopen, "reopen", false, false, "Reopen an issue -- reopen <id>"},
	// {SetFixVersion, "fixversion", false, false, "Set an issue's fix version -- fixversion <id> <version>"},
}

type parsedCommand struct {
	command string
	issue   string
	rest    string
}

func parseCommon(s string) (parsedCommand, error) {
	var issue string
	var rest string

	words := strings.SplitN(s, " ", 3)

	if len(words) > 1 {
		issue = strings.ToUpper(words[1])
		if strings.IndexAny(issue, "/#?&") != -1 {
			return parsedCommand{}, errors.New("Invalid character in issue ID")
		}
	}

	if len(words) > 2 {
		rest = words[2]
	}

	return parsedCommand{words[0], issue, rest}, nil
}

type JiraCommands struct {
	Client *jira.Client
}

func (jc *JiraCommands) AddComment(fromUser string, cmd parsedCommand) string {
	if cmd.rest == "" {
		return "No comment text provided"
	}

	comment := jira.Comment{
		Body: fmt.Sprintf("%s commented:\n%s", fromUser, cmd.rest),
	}
	url := fmt.Sprintf("/rest/api/2/issue/%s/comment", cmd.issue)
	req, err := jc.Client.NewRequest("POST", url, &comment)
	if err != nil {
		// TODO log here
		return "Internal error, see logs"
	}

	resp, err := jc.Client.Do(req, nil)
	if err != nil {
		switch resp.StatusCode {
		case http.StatusNotFound:
			return "Issue not found"
		default:
			// TODO Log here
			return "Jira server internal error, see logs"
		}
	}

	return fmt.Sprintf("Added comment to issue %s", cmd.issue)
}

func (jc *JiraCommands) Assign(fromUser string, cmd parsedCommand) string {
	// TODO Look up our database of Jira/Slack users once it exists.
	username := cmd.rest
	if username == "" {
		return "No assignee provided. Use `none` to remove the assignee"
	} else if username == "none" {
		username = ""
	}

	url := fmt.Sprintf("/rest/api/2/issue/%s/assignee", cmd.issue)
	assignee := jira.Assignee{
		Name: username,
	}
	req, err := jc.Client.NewRequest("PUT", url, &assignee)
	if err != nil {
		// TODO log here
		return "Internal error, see logs"
	}

	resp, err := jc.Client.Do(req, nil)
	if err != nil {
		switch resp.StatusCode {
		case http.StatusNotFound:
			return "Issue or user not found"
		default:
			// TODO Log here
			return "Jira server internal error, see logs"
		}
	}

	if username == "" {
		return fmt.Sprintf("Removed assignee from %s", cmd.issue)
	} else {
		return fmt.Sprintf("Assigned %s to %s", cmd.issue, username)
	}
}

func (jc *JiraCommands) Resolve(fromUser string, cmd parsedCommand) string {
	return "Unimplemented!"
}

func (jc *JiraCommands) Close(fromUser string, cmd parsedCommand) string {
	return "Unimplemented!"
}

func (jc *JiraCommands) Reopen(fromUser string, cmd parsedCommand) string {
	return "Unimplemented!"
}

func (jc *JiraCommands) Process(command int, message *model.ChatMessage) string {
	parsed, err := parseCommon(message.Text)
	if err != nil {
		return err.Error()
	}

	if parsed.issue == "" {
		return "Missing issue id"
	}

	switch command {
	case Comment:
		return jc.AddComment(message.FromUser, parsed)
	case Assign:
		return jc.Assign(message.FromUser, parsed)
	case Resolve:
		return jc.Resolve(message.FromUser, parsed)
	case Close:
		return jc.Close(message.FromUser, parsed)
	case Reopen:
		return jc.Reopen(message.FromUser, parsed)
	default:
		// TODO Log about unexpected command tag
		return "Internal error"
	}
}

// Start the Jira client goroutine
func Start(config *model.Config, wg *sync.WaitGroup,
	commandrouter *commandrouter.Router,
	outChan chan *model.ChatMessage, done chan struct{}) {

	wg.Add(1)

	//httpClient := NewOAuthClient(${1:key}, ${2:accessToken}, ${3:accessSecret}, config.JiraUrl)
	jiraClient, err := jira.NewClient(nil, config.JiraUrl)
	if err != nil {
		panic(err)
	}

	processor := &JiraCommands{
		Client: jiraClient,
	}

	incoming, err := commandrouter.AddDestination("JiraCommands", commands)
	if err != nil {
		panic(err)
	}

	go func() {
	Loop:
		for {
			select {
			case match := <-incoming:
				match.Response <- processor.Process(match.Tag, match.Message)
			case <-done:
				break Loop
			}
		}
		wg.Done()
	}()

}
<commit_msg>Return better error detail, when available<commit_after>package jiraclient

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"

	jira "github.com/andygrunwald/go-jira"

	"github.com/dimfeld/promulgator/commandrouter"
	"github.com/dimfeld/promulgator/model"
)

const (
	Comment = iota
	Assign
	Resolve
	Close
	Reopen
	SetFixVersion
)

var commands []commandrouter.Command = []commandrouter.Command{
	{Comment, "comment", false, false, "Add a comment to an issue -- comment <id> <comment text>"},
	{Comment, "c", false, false, "Add a comment to an issue -- c <id> <comment text>"},
	{Assign, "assign", false, false, "Assign an issue to a user -- assign <id> <user>"},
	{Assign, "a", false, false, "Assign an issue to a user -- a <id> <user>"},
	// {Resolve, "resolve", false, false, "Resolve an issue -- resolve <id>"},
	// {Resolve, "r", false, false, "Resolve an issue -- r <id>"},
	// {Close, "close", false, false, "Close an issue -- close <id>"},
	// {Close, "c", false, false, "Close an issue -- c <id>"},
	// {Reopen, "reopen", false, false, "Reopen an issue -- reopen <id>"},
	// {SetFixVersion, "fixversion", false, false, "Set an issue's fix version -- fixversion <id> <version>"},
}

type parsedCommand struct {
	command string
	issue   string
	rest    string
}

func parseCommon(s string) (parsedCommand, error) {
	var issue string
	var rest string

	words := strings.SplitN(s, " ", 3)

	if len(words) > 1 {
		issue = strings.ToUpper(words[1])
		if strings.IndexAny(issue, "/#?&") != -1 {
			return parsedCommand{}, errors.New("Invalid character in issue ID")
		}
	}

	if len(words) > 2 {
		rest = words[2]
	}

	return parsedCommand{words[0], issue, rest}, nil
}

type JiraCommands struct {
	Client *jira.Client
}

func (jc *JiraCommands) AddComment(fromUser string, cmd parsedCommand) string {
	if cmd.rest == "" {
		return "No comment text provided"
	}

	comment := jira.Comment{
		Body: fmt.Sprintf("%s commented:\n%s", fromUser, cmd.rest),
	}
	url := fmt.Sprintf("/rest/api/2/issue/%s/comment", cmd.issue)
	req, err := jc.Client.NewRequest("POST", url, &comment)
	if err != nil {
		// TODO log here
		return "Internal error, see logs"
	}

	resp, err := jc.Client.Do(req, nil)
	if err != nil {
		switch resp.StatusCode {
		case http.StatusNotFound:
			return "Issue not found"
		default:
			// TODO Log here
			return "Jira server internal error, see logs"
		}
	}

	return fmt.Sprintf("Added comment to issue %s", cmd.issue)
}

func (jc *JiraCommands) Assign(fromUser string, cmd parsedCommand) string {
	// TODO Look up our database of Jira/Slack users once it exists.
	username := cmd.rest
	if username == "" {
		return "No assignee provided. Use `none` to remove the assignee"
	} else if username == "none" {
		username = ""
	}

	url := fmt.Sprintf("/rest/api/2/issue/%s/assignee", cmd.issue)
	assignee := jira.Assignee{
		Name: username,
	}
	req, err := jc.Client.NewRequest("PUT", url, &assignee)
	if err != nil {
		// TODO log here
		return "Internal error, see logs"
	}

	resp, err := jc.Client.Do(req, nil)
	if err != nil {
		switch resp.StatusCode {
		case http.StatusNotFound:
			return "Issue not found"
		case http.StatusBadRequest:
			if errDetail, ok := err.(*jira.ErrorResponse); ok {
				if e, ok := errDetail.Errors["assignee"]; ok {
					return e
				}
			}

			return "Issue or user not found"
		default:
			// TODO Log here
			return "Jira server internal error, see logs"
		}
	}

	if username == "" {
		return fmt.Sprintf("Removed assignee from %s", cmd.issue)
	} else {
		return fmt.Sprintf("Assigned %s to %s", cmd.issue, username)
	}
}

func (jc *JiraCommands) Resolve(fromUser string, cmd parsedCommand) string {
	return "Unimplemented!"
}

func (jc *JiraCommands) Close(fromUser string, cmd parsedCommand) string {
	return "Unimplemented!"
}

func (jc *JiraCommands) Reopen(fromUser string, cmd parsedCommand) string {
	return "Unimplemented!"
}

func (jc *JiraCommands) Process(command int, message *model.ChatMessage) string {
	parsed, err := parseCommon(message.Text)
	if err != nil {
		return err.Error()
	}

	if parsed.issue == "" {
		return "Missing issue id"
	}

	switch command {
	case Comment:
		return jc.AddComment(message.FromUser, parsed)
	case Assign:
		return jc.Assign(message.FromUser, parsed)
	case Resolve:
		return jc.Resolve(message.FromUser, parsed)
	case Close:
		return jc.Close(message.FromUser, parsed)
	case Reopen:
		return jc.Reopen(message.FromUser, parsed)
	default:
		// TODO Log about unexpected command tag
		return "Internal error"
	}
}

// Start the Jira client goroutine
func Start(config *model.Config, wg *sync.WaitGroup,
	commandrouter *commandrouter.Router,
	outChan chan *model.ChatMessage, done chan struct{}) {

	wg.Add(1)

	//httpClient := NewOAuthClient(${1:key}, ${2:accessToken}, ${3:accessSecret}, config.JiraUrl)
	jiraClient, err := jira.NewClient(nil, config.JiraUrl)
	if err != nil {
		panic(err)
	}

	processor := &JiraCommands{
		Client: jiraClient,
	}

	incoming, err := commandrouter.AddDestination("JiraCommands", commands)
	if err != nil {
		panic(err)
	}

	go func() {
	Loop:
		for {
			select {
			case match := <-incoming:
				match.Response <- processor.Process(match.Tag, match.Message)
			case <-done:
				break Loop
			}
		}
		wg.Done()
	}()

}
<|endoftext|>