<commit_before>package plumb

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"text/template"
	"github.com/qadium/plumb/shell"
)

type templateContext struct {
	Wrapper string
	Plumb   *PlumbContext
}

const bundleConfig = ".plumb.yml"

// A Dockerfile template for python bundles. We'll make sure this is
// a standalone file in the future, so we can add different language
// types.
const dockerfileTemplate = `
FROM python:2.7.10-slim

RUN mkdir -p /usr/src/bundle
WORKDIR /usr/src/bundle

COPY . /usr/src/bundle
{{ if .Plumb.Install }}
{{ range .Plumb.Install }}
RUN {{ . }}
{{ end }}
{{ else }}
RUN pip install -r requirements.txt
{{ end }}
RUN pip install bottle
EXPOSE 9800
{{ range .Plumb.Env }}
ENV {{ . }}
{{ end }}
CMD ["python", "{{ .Wrapper }}"]
`

const wrapperTemplate = `
import {{ .Plumb.Name }}
from bottle import post, route, run, request, HTTPResponse

__INFO = {'bundle': '{{ .Plumb.Name }}',
  'inputs': {
    {{ range .Plumb.Inputs }}'{{ .Name }}': '{{ .Description }}',{{ end }}
  },
  'outputs': {
    {{ range .Plumb.Outputs }}'{{ .Name }}': '{{ .Description }}',{{ end }}
  }
}

@route('/info')
def info():
    return __INFO

@post('/')
def index():
    data = request.json

    # validate input data
    if data is None:
        raise HTTPResponse(
            body = "No JSON data received; did you forget to set 'Content-Type: application/json'?",
            status = 400
        )

	# Instead of throwing errors when required fields are missing,
	# maybe we just return the payload instead and add a note?
    {{ range .Plumb.Inputs }}
    if not '{{ .Name }}' in data:
        raise HTTPResponse(
            body = "Missing required '{{ .Name }}' field in JSON data.",
            status = 400
        )
    {{ end }}
    # run the enhancer
    output = {{ .Plumb.Name }}.run(data)

    # validate output data
    {{ range .Plumb.Outputs }}
    if not '{{ .Name }}' in output:
        raise HTTPResponse(
            body = "Unexpected output; missing '{{ .Name }}' field.",
            status = 501
        )
    {{ end }}

	# Instead of assuming the output contains a superset of the input
	# fields, we can add the input fields to the output here? It's a
	# performance hit, but it means the output is *always* a superset
	# of the input.

    return output

run(host='0.0.0.0', port=9800)
`

func removeTempFile(f *os.File) {
	filename := f.Name()
	log.Printf(" |  Removing '%s'", filename)
	err := os.RemoveAll(filename)
	if err != nil {
		log.Printf("    %v", err)
	} else {
		log.Printf("    Removed.")
	}
}

func Bundle(path string) error {
	ctx := PlumbContext{}
	log.Printf("==> Creating bundle from '%s'", path)
	defer log.Printf("<== Bundling complete.")

	log.Printf(" |  Parsing bundle config.")
	bytes, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", path, bundleConfig))
	if err != nil {
		return err
	}
	if err := yaml.Unmarshal(bytes, &ctx); err != nil {
		return err
	}
	log.Printf("    %v", ctx)

	log.Printf(" |  Making temp file for python wrapper")
	wrapper, err := ioutil.TempFile(path, "plumb")
	defer removeTempFile(wrapper)
	if err != nil {
		return err
	}
	log.Printf("    Created '%s'", wrapper.Name())

	templateCtx := templateContext{
		Wrapper: wrapper.Name(),
		Plumb:   &ctx,
	}

	log.Printf(" |  Writing wrapper.")
	tmpl, err := template.New("wrapper").Parse(wrapperTemplate)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(wrapper, templateCtx); err != nil {
		return err
	}
	log.Printf("    Done.")

	log.Printf(" |  Making temp file for Dockerfile")
	dockerfile, err := ioutil.TempFile(path, "plumb")
	defer removeTempFile(dockerfile)
	if err != nil {
		return err
	}
	log.Printf("    Created '%s'", dockerfile.Name())

	log.Printf(" |  Writing Dockerfile.")
	tmpl, err = template.New("dockerfile").Parse(dockerfileTemplate)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(dockerfile, templateCtx); err != nil {
		return err
	}
	log.Printf("    Done.")

	log.Printf(" |  Building container.")
	imageName := fmt.Sprintf("plumb/%s", ctx.Name)
	err = shell.RunAndLog("docker", "build", "-t", imageName, "-f", dockerfile.Name(), path)
	if err != nil {
		return err
	}
	log.Printf("    Container 'plumb/%s' built.", ctx.Name)
	return nil
}
<commit_msg>used context helper to parse config<commit_after>package plumb

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"text/template"
	"github.com/qadium/plumb/shell"
)

type templateContext struct {
	Wrapper string
	Plumb   *PlumbContext
}

// A Dockerfile template for python bundles. We'll make sure this is
// a standalone file in the future, so we can add different language
// types.
const dockerfileTemplate = `
FROM python:2.7.10-slim

RUN mkdir -p /usr/src/bundle
WORKDIR /usr/src/bundle

COPY . /usr/src/bundle
{{ if .Plumb.Install }}
{{ range .Plumb.Install }}
RUN {{ . }}
{{ end }}
{{ else }}
RUN pip install -r requirements.txt
{{ end }}
RUN pip install bottle
EXPOSE 9800
{{ range .Plumb.Env }}
ENV {{ . }}
{{ end }}
CMD ["python", "{{ .Wrapper }}"]
`

const wrapperTemplate = `
import {{ .Plumb.Name }}
from bottle import post, route, run, request, HTTPResponse

__INFO = {'bundle': '{{ .Plumb.Name }}',
  'inputs': {
    {{ range .Plumb.Inputs }}'{{ .Name }}': '{{ .Description }}',{{ end }}
  },
  'outputs': {
    {{ range .Plumb.Outputs }}'{{ .Name }}': '{{ .Description }}',{{ end }}
  }
}

@route('/info')
def info():
    return __INFO

@post('/')
def index():
    data = request.json

    # validate input data
    if data is None:
        raise HTTPResponse(
            body = "No JSON data received; did you forget to set 'Content-Type: application/json'?",
            status = 400
        )

	# Instead of throwing errors when required fields are missing,
	# maybe we just return the payload instead and add a note?
    {{ range .Plumb.Inputs }}
    if not '{{ .Name }}' in data:
        raise HTTPResponse(
            body = "Missing required '{{ .Name }}' field in JSON data.",
            status = 400
        )
    {{ end }}
    # run the enhancer
    output = {{ .Plumb.Name }}.run(data)

    # validate output data
    {{ range .Plumb.Outputs }}
    if not '{{ .Name }}' in output:
        raise HTTPResponse(
            body = "Unexpected output; missing '{{ .Name }}' field.",
            status = 501
        )
    {{ end }}

	# Instead of assuming the output contains a superset of the input
	# fields, we can add the input fields to the output here? It's a
	# performance hit, but it means the output is *always* a superset
	# of the input.

    return output

run(host='0.0.0.0', port=9800)
`

func removeTempFile(f *os.File) {
	filename := f.Name()
	log.Printf(" |  Removing '%s'", filename)
	err := os.RemoveAll(filename)
	if err != nil {
		log.Printf("    %v", err)
	} else {
		log.Printf("    Removed.")
	}
}

func Bundle(path string) error {
	log.Printf("==> Creating bundle from '%s'", path)
	defer log.Printf("<== Bundling complete.")

	log.Printf(" |  Parsing bundle config.")
	ctx, err := parseConfig(path)
	if err != nil {
		return err
	}
	log.Printf("    %v", ctx)

	log.Printf(" |  Making temp file for python wrapper")
	wrapper, err := ioutil.TempFile(path, "plumb")
	defer removeTempFile(wrapper)
	if err != nil {
		return err
	}
	log.Printf("    Created '%s'", wrapper.Name())

	templateCtx := templateContext{
		Wrapper: wrapper.Name(),
		Plumb:   ctx,
	}

	log.Printf(" |  Writing wrapper.")
	tmpl, err := template.New("wrapper").Parse(wrapperTemplate)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(wrapper, templateCtx); err != nil {
		return err
	}
	log.Printf("    Done.")

	log.Printf(" |  Making temp file for Dockerfile")
	dockerfile, err := ioutil.TempFile(path, "plumb")
	defer removeTempFile(dockerfile)
	if err != nil {
		return err
	}
	log.Printf("    Created '%s'", dockerfile.Name())

	log.Printf(" |  Writing Dockerfile.")
	tmpl, err = template.New("dockerfile").Parse(dockerfileTemplate)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(dockerfile, templateCtx); err != nil {
		return err
	}
	log.Printf("    Done.")

	log.Printf(" |  Building container.")
	imageName := fmt.Sprintf("plumb/%s", ctx.Name)
	err = shell.RunAndLog("docker", "build", "-t", imageName, "-f", dockerfile.Name(), path)
	if err != nil {
		return err
	}
	log.Printf("    Container 'plumb/%s' built.", ctx.Name)
	return nil
}
<|endoftext|>


<commit_before>package carbon

import (
	"math"
	"time"
)

const (
	DATE_TIME_LAYOUT = "2006-01-02 15:04:05"
)

// form @github.com/jinzhu/now and more
var TimeFormats = []string{
	DATE_TIME_LAYOUT,
	"1/2/2006", "1/2/2006 15:4:5", "2006-1-2 15:4:5", "2006-1-2 15:4", "2006-1-2", "1-2", "15:4:5", "15:4",
	"2013-02-03", "15:4:5 Jan 2, 2006 MST", "15:04:05", "2013/02/03",
	"2013-02-03 19:54:00 PST",
	time.RFC822, time.RubyDate, time.RFC822Z, time.RFC3339,
}

type Carbon struct {
	time.Time
}

// return Carbon with time Now
func Now() *Carbon {
	c := &Carbon{time.Now()}
	return c
}

// create Carbon with year, month, day, hour, minute, second, nanosecond, int,  TZ string
// Create(2001, 12, 01, 15, 25, 55, 0, "UTC") = 2001-12-01 13:25:55 +0000 UTC
func Create(year, month, day int, args ...interface{}) *Carbon {
	var tz string
	Month := time.Month(month)
	hour := time.Now().Hour()
	minute := time.Now().Minute()
	second := time.Now().Second()
	nanosecond := time.Now().Nanosecond()
	location := time.Now().Location()
	for key, val := range args {
		if key == 0 {
			hour = val.(int)
		}
		if key == 1 {
			minute = val.(int)
		}
		if key == 2 {
			second = val.(int)
		}
		if key == 3 {
			nanosecond = val.(int)
		}
		if key == 4 {
			tz = val.(string)
		}
	}
	c := &Carbon{time.Date(year, Month, day, hour, minute, second, nanosecond, location)}
	if tz != "" {
		c.SetTZ(tz)
	}
	return c
}

// create Carbon from time string, return in UTC, if tz not specified
// CreateFrom("2015-01-25 15:4:5") = 2015-01-25 15:04:55 +0000 UTC
func CreateFrom(stringDate string) (*Carbon, error) {
	var carbon = Now()
	var err error
	var tm time.Time
	for _, format := range TimeFormats {
		tm, err = time.Parse(format, stringDate)
		if err == nil {
			carbon.Time = tm
			break
		}
	}

	return carbon, err
}

// set tx location, ex. "UTC"
func (c *Carbon) SetTZ(tz string) *Carbon {
	location, _ := time.LoadLocation(tz)
	if location != nil {
		c.Time = c.Time.In(location)
	}
	return c
}

func (c *Carbon) SubDay() *Carbon {
	return c.SubDays(1)
}

func (c *Carbon) SubDays(days int) *Carbon {
	c.Time = c.Time.AddDate(0, 0, -days)
	return c
}

func (c *Carbon) SubMonth(days int) *Carbon {
	return c.SubMonths(1)
}

func (c *Carbon) SubMonths(months int) *Carbon {
	c.Time = c.Time.AddDate(0, -months, 0)
	return c
}

func (c *Carbon) SubYear(days int) *Carbon {
	return c.SubYears(1)
}

func (c *Carbon) SubYears(years int) *Carbon {
	c.Time = c.Time.AddDate(-years, 0, 0)
	return c
}

func (c *Carbon) AddDay() *Carbon {
	return c.AddDays(1)
}

func (c *Carbon) AddDays(days int) *Carbon {
	c.Time = c.Time.AddDate(0, 0, days)
	return c
}

func (c *Carbon) AddMonth(days int) *Carbon {
	return c.AddMonths(1)
}

func (c *Carbon) AddMonths(months int) *Carbon {
	c.Time = c.Time.AddDate(0, months, 0)
	return c
}

func (c *Carbon) AddYear(days int) *Carbon {
	return c.AddYears(1)
}

func (c *Carbon) AddYears(years int) *Carbon {
	c.AddDate(years, 0, 0)
	return c
}

func (c *Carbon) DiffInSeconds(from Carbon) int {
	return round(c.Sub(from.Time).Seconds())
}

func (c *Carbon) DiffInMinutes(from Carbon) int {
	return round(c.Sub(from.Time).Minutes())
}

func (c *Carbon) DiffInHours(from Carbon) int {
	return round(c.Sub(from.Time).Hours())
}

// Determines if the instance is equal to another
func (c *Carbon) Eq(another Carbon) bool {
	return c.Equal(another.Time)
}

// Determines if the instance is greater (after) than another
func (c *Carbon) Gt(another Carbon) bool {
	return c.After(another.Time)
}

// Determines if the instance is less (Before) than another
func (c *Carbon) Lt(another Carbon) bool {
	return c.Before(another.Time)
}

// Determines if the instance is greater than before and less than after
func (c *Carbon) Between(before, after Carbon) bool {
	return c.After(before.Time) && c.Before(after.Time)
}

func (c *Carbon) StartOfHour() *Carbon {
	c.Time = c.Truncate(time.Hour)
	return c
}

func (c *Carbon) EndOfHour() *Carbon {
	c.StartOfHour()
	c.Time = c.Add(time.Hour - time.Second)
	return c
}

func (c *Carbon) StartOfDay() *Carbon {
	c.Time = c.StartOfHour().Add(-time.Hour * time.Duration(c.Hour()))
	return c
}

func (c *Carbon) EndOfDay() *Carbon {
	c.Time = c.StartOfDay().Add(time.Hour * time.Duration(24) - time.Second)
	return c
}

func (c *Carbon) StartOfWeek(firstDayOfWeekIsMonday ...bool) *Carbon {
	firstDay := time.Monday
	corrFirstDay := 1
	if len(firstDayOfWeekIsMonday) > 0 {
		if !firstDayOfWeekIsMonday[0] {
			firstDay = time.Sunday
			corrFirstDay = 0
		}
	}
	c.StartOfDay()
	if c.Weekday() != firstDay {
		c.Time = c.Add(-time.Hour * 24 * time.Duration(-corrFirstDay+int(c.Weekday())))
	}
	return c
}

func (c *Carbon) EndOfWeek() *Carbon {
	c.Time = c.StartOfWeek().Add(time.Hour * time.Duration(24 * 7) - time.Second)
	return c
}

func (c *Carbon) StartOfMonth() *Carbon {
	year := c.Year()
	Month := c.Month()
	location := time.Now().Location()
	c = &Carbon{time.Date(year, Month, 1, 0, 0, 0, 0, location)}

	return c
}

func (c *Carbon) EndOfMonth() *Carbon {
	c.Time = c.StartOfMonth().AddDate(0,1,0).Add(- time.Second)
	return c
}

func (c *Carbon) StartOfYear() *Carbon {
	year := c.Year()
	location := time.Now().Location()
	c = &Carbon{time.Date(year, time.Month(1), 1, 0, 0, 0, 0, location)}

	return c
}

func (c *Carbon) EndOfYear() *Carbon {
	c.Time = c.StartOfYear().AddDate(1,0,0).Add(- time.Second)
	return c
}

//formatters

// return string with DateTime format "2006-01-25 15:04:05"
func (c *Carbon) ToDateTimeString() string {
	return c.Format(DATE_TIME_LAYOUT)
}

func round(f float64) int {
	if math.Abs(f) < 0.5 {
		return 0
	}
	return int(f + math.Copysign(0.5, f))
}
<commit_msg>fix types<commit_after>package carbon

import (
	"math"
	"time"
)

const (
	DATE_TIME_LAYOUT = "2006-01-02 15:04:05"
)

// form @github.com/jinzhu/now and more
var TimeFormats = []string{
	DATE_TIME_LAYOUT,
	"1/2/2006", "1/2/2006 15:4:5", "2006-1-2 15:4:5", "2006-1-2 15:4", "2006-1-2", "1-2", "15:4:5", "15:4",
	"2013-02-03", "15:4:5 Jan 2, 2006 MST", "15:04:05", "2013/02/03",
	"2013-02-03 19:54:00 PST",
	time.RFC822, time.RubyDate, time.RFC822Z, time.RFC3339,
}

type Carbon struct {
	time.Time
}

// return Carbon with time Now
func Now() *Carbon {
	c := &Carbon{time.Now()}
	return c
}

// return Carbon with time Now
func CreateFromTime(timeObject time.Time) *Carbon {
	c := &Carbon{timeObject}
	return c
}

// create Carbon with year, month, day, hour, minute, second, nanosecond, int,  TZ string
// Create(2001, 12, 01, 15, 25, 55, 0, "UTC") = 2001-12-01 13:25:55 +0000 UTC
func Create(year, month, day int, args ...interface{}) *Carbon {
	var tz string
	Month := time.Month(month)
	hour := time.Now().Hour()
	minute := time.Now().Minute()
	second := time.Now().Second()
	nanosecond := time.Now().Nanosecond()
	location := time.Now().Location()
	for key, val := range args {
		if key == 0 {
			hour = val.(int)
		}
		if key == 1 {
			minute = val.(int)
		}
		if key == 2 {
			second = val.(int)
		}
		if key == 3 {
			nanosecond = val.(int)
		}
		if key == 4 {
			tz = val.(string)
		}
	}
	c := &Carbon{time.Date(year, Month, day, hour, minute, second, nanosecond, location)}
	if tz != "" {
		c.SetTZ(tz)
	}
	return c
}

// create Carbon from time string, return in UTC, if tz not specified
// CreateFrom("2015-01-25 15:4:5") = 2015-01-25 15:04:55 +0000 UTC
func CreateFrom(stringDate string) (*Carbon, error) {
	var carbon = Now()
	var err error
	var tm time.Time
	for _, format := range TimeFormats {
		tm, err = time.Parse(format, stringDate)
		if err == nil {
			carbon.Time = tm
			break
		}
	}

	return carbon, err
}

// set tx location, ex. "UTC"
func (c *Carbon) SetTZ(tz string) *Carbon {
	location, _ := time.LoadLocation(tz)
	if location != nil {
		c.Time = c.Time.In(location)
	}
	return c
}

// set time, ex. "23.59.29"
func (c *Carbon) SetTime(hours, minutes, seconds int) *Carbon {
	c.Time = time.Date(
		c.Year(),
		c.Month(),
		c.Day(),
		hours,
		minutes,
		seconds,
		c.Nanosecond(),
		c.Location(),
	)

	return c
}

func (c *Carbon) SubDay() *Carbon {
	return c.SubDays(1)
}

func (c *Carbon) SubDays(days int) *Carbon {
	c.Time = c.Time.AddDate(0, 0, -days)
	return c
}

func (c *Carbon) SubMonth(days int) *Carbon {
	return c.SubMonths(1)
}

func (c *Carbon) SubMonths(months int) *Carbon {
	c.Time = c.Time.AddDate(0, -months, 0)
	return c
}

func (c *Carbon) SubYear(days int) *Carbon {
	return c.SubYears(1)
}

func (c *Carbon) SubYears(years int) *Carbon {
	c.Time = c.Time.AddDate(-years, 0, 0)
	return c
}

func (c *Carbon) AddDay() *Carbon {
	return c.AddDays(1)
}

func (c *Carbon) AddDays(days int) *Carbon {
	c.Time = c.Time.AddDate(0, 0, days)
	return c
}

func (c *Carbon) AddMonth(days int) *Carbon {
	return c.AddMonths(1)
}

func (c *Carbon) AddMonths(months int) *Carbon {
	c.Time = c.Time.AddDate(0, months, 0)
	return c
}

func (c *Carbon) AddYear(days int) *Carbon {
	return c.AddYears(1)
}

func (c *Carbon) AddYears(years int) *Carbon {
	c.AddDate(years, 0, 0)
	return c
}

func (c *Carbon) DiffInSeconds(from Carbon) int {
	return round(c.Sub(from.Time).Seconds())
}

func (c *Carbon) DiffInMinutes(from Carbon) int {
	return round(c.Sub(from.Time).Minutes())
}

func (c *Carbon) DiffInHours(from Carbon) int {
	return round(c.Sub(from.Time).Hours())
}

// Determines if the instance is equal to another
func (c *Carbon) Eq(another Carbon) bool {
	return c.Equal(another.Time)
}

// Determines if the instance is greater (after) than another
func (c *Carbon) Gt(another *Carbon) bool {
	return c.After(another.Time)
}

// Determines if the instance is less (Before) than another
func (c *Carbon) Lt(another *Carbon) bool {
	return c.Before(another.Time)
}

// Determines if the instance is greater than before and less than after
func (c *Carbon) Between(before, after *Carbon) bool {
	return c.After(before.Time) && c.Before(after.Time)
}

func (c *Carbon) StartOfHour() *Carbon {
	c.Time = c.Truncate(time.Hour)
	return c
}

func (c *Carbon) EndOfHour() *Carbon {
	c.StartOfHour()
	c.Time = c.Add(time.Hour - time.Second)
	return c
}

func (c *Carbon) StartOfDay() *Carbon {
	c.Time = c.StartOfHour().Add(-time.Hour * time.Duration(c.Hour()))
	return c
}

func (c *Carbon) EndOfDay() *Carbon {
	c.Time = c.StartOfDay().Add(time.Hour*time.Duration(24) - time.Second)
	return c
}

func (c *Carbon) StartOfWeek(firstDayOfWeekIsMonday ...bool) *Carbon {
	firstDay := time.Monday
	corrFirstDay := 1
	if len(firstDayOfWeekIsMonday) > 0 {
		if !firstDayOfWeekIsMonday[0] {
			firstDay = time.Sunday
			corrFirstDay = 0
		}
	}
	c.StartOfDay()
	if c.Weekday() != firstDay {
		c.Time = c.Add(-time.Hour * 24 * time.Duration(-corrFirstDay+int(c.Weekday())))
	}
	return c
}

func (c *Carbon) EndOfWeek() *Carbon {
	c.Time = c.StartOfWeek().Add(time.Hour*time.Duration(24*7) - time.Second)
	return c
}

func (c *Carbon) StartOfMonth() *Carbon {
	year := c.Year()
	Month := c.Month()
	location := time.Now().Location()
	c = &Carbon{time.Date(year, Month, 1, 0, 0, 0, 0, location)}

	return c
}

func (c *Carbon) EndOfMonth() *Carbon {
	c.Time = c.StartOfMonth().AddDate(0, 1, 0).Add(-time.Second)
	return c
}

func (c *Carbon) StartOfYear() *Carbon {
	year := c.Year()
	location := time.Now().Location()
	c = &Carbon{time.Date(year, time.Month(1), 1, 0, 0, 0, 0, location)}

	return c
}

func (c *Carbon) EndOfYear() *Carbon {
	c.Time = c.StartOfYear().AddDate(1, 0, 0).Add(-time.Second)
	return c
}

//formatters

// return string with DateTime format "2006-01-25 15:04:05"
func (c *Carbon) ToDateTimeString() string {
	return c.Format(DATE_TIME_LAYOUT)
}

func round(f float64) int {
	if math.Abs(f) < 0.5 {
		return 0
	}
	return int(f + math.Copysign(0.5, f))
}
<|endoftext|>


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

// Package cbauth provides auth{N,Z} for couchbase server services.
package cbauth

import (
	"fmt"
	"net/http"

	"github.com/couchbase/cbauth/cbauthimpl"
)

// TODO: consider API that would allow us to do digest auth behind the
// scene

// TODO: for GetHTTPServiceAuth consider something more generic such
// as GetHTTPAuthHeader. Or even maybe RoundTrip. So that we can
// handle digest auth

// Authenticator is main cbauth interface. It supports both incoming
// and outgoing auth.
type Authenticator interface {
	// AuthWebCreds method extracts credentials from given http request.
	AuthWebCreds(req *http.Request) (creds Creds, err error)
	// Auth method constructs credentials from given user and password pair.
	Auth(user, pwd string) (creds Creds, err error)
	// GetHTTPServiceAuth returns user/password creds giving
	// "admin" access to given http service inside couchbase cluster.
	GetHTTPServiceAuth(hostport string) (user, pwd string, err error)
	// GetMemcachedServiceAuth returns user/password creds given
	// "admin" access to given memcached service.
	GetMemcachedServiceAuth(hostport string) (user, pwd string, err error)
}

// TODO: get rid of unnecessary error returns

// Creds type represents credentials and answers queries on this creds
// authorized actions. Note: it'll become (possibly much) wider API in
// future, but it's main purpose right now is to get us started.
type Creds interface {
	// Name method returns user name (e.g. for auditing)
	Name() string
	// Source method returns user source (for auditing)
	Source() string
	// IsAllowed method returns true if the permission is granted
	// for these credentials
	IsAllowed(permission string) (bool, error)
	// IsAdmin method returns true iff this creds represent valid
	// admin account.
	IsAdmin() (bool, error)
	// CanReadAnyMetadata method returns true iff this creds has
	// permission to read any metadata (i.e. admin or ro-admin).
	CanReadAnyMetadata() bool
	// IsROAdmin is confusing alias for CanReadAnyMetadata. Don't
	// use!
	IsROAdmin() (bool, error)
	// CanAccessBucket method returns true iff this creds
	// represent valid account that can read/write/query docs in given
	// bucket.
	CanAccessBucket(bucket string) (bool, error)
	// CanReadBucket method returns true iff this creds represent
	// valid account that can read (but not necessarily write)
	// docs in given bucket.
	CanReadBucket(bucket string) (bool, error)
	// CanDDLBucket method returns true iff this creds represent
	// valid account that can DDL in given bucket. Note that at
	// this time it delegates to CanAccessBucket in only
	// implementation.
	CanDDLBucket(bucket string) (bool, error)
}

var _ Creds = (*cbauthimpl.CredsImpl)(nil)

type naCreds struct{}

func (na naCreds) Name() string                                { return "" }
func (na naCreds) Source() string                              { return "" }
func (na naCreds) IsAllowed(permission string) (bool, error)   { return false, nil }
func (na naCreds) IsAdmin() (bool, error)                      { return false, nil }
func (na naCreds) IsROAdmin() (bool, error)                    { return false, nil }
func (na naCreds) CanReadAnyMetadata() bool                    { return false }
func (na naCreds) CanAccessBucket(bucket string) (bool, error) { return false, nil }
func (na naCreds) CanReadBucket(bucket string) (bool, error)   { return false, nil }
func (na naCreds) CanDDLBucket(bucket string) (bool, error)    { return false, nil }

// NoAccessCreds is Creds instance that has no access at
// all. Authenticator returns this Creds instance for incoming auth
// that was not recognized at all as valid user.
var NoAccessCreds Creds = naCreds{}

type authImpl struct {
	svc *cbauthimpl.Svc
}

// DBStaleError is kind of error that signals that cbauth internal
// state is not synchronized with ns_server yet or anymore.
type DBStaleError struct {
	Err error
}

func (e *DBStaleError) Error() string {
	if e.Err != nil {
		return fmt.Sprintf("CBAuth database is stale: last reason: %s", e.Err)
	}
	return "CBAuth database is stale. Was never updated yet."
}

// UnknownHostPortError is returned from GetMemcachedServiceAuth and
// GetHTTPServiceAuth calls for unknown host:port arguments.
type UnknownHostPortError string

func (s UnknownHostPortError) Error() string {
	return fmt.Sprintf("Unable to find given hostport in cbauth database: `%s'", string(s))
}

func doOnServer(s *cbauthimpl.Svc, hdr http.Header) (Creds, error) {
	rv, err := cbauthimpl.VerifyOnServer(s, hdr)
	if rv == nil && err == nil {
		return NoAccessCreds, nil
	}
	return rv, err
}

func doAuth(a *authImpl, user, pwd string, hdr http.Header) (Creds, error) {
	ci, err := cbauthimpl.VerifyPassword(a.svc, user, pwd)
	if err != nil {
		return nil, err
	}

	if ci != nil {
		return ci, nil
	}

	if user == "" {
		return NoAccessCreds, nil
	}

	ldapEnabled, err := cbauthimpl.IsLDAPEnabled(a.svc)
	if err != nil {
		return nil, err
	}

	if !ldapEnabled {
		return nil, nil
	}

	if hdr == nil {
		req, err := http.NewRequest("GET", "http://host/", nil)
		if err != nil {
			panic("Must not happen: " + err.Error())
		}
		req.SetBasicAuth(user, pwd)
		hdr = req.Header
	}
	return doOnServer(a.svc, hdr)
}

func (a *authImpl) AuthWebCreds(req *http.Request) (creds Creds, err error) {
	if cbauthimpl.IsAuthTokenPresent(req) {
		return doOnServer(a.svc, req.Header)
	}
	user, pwd, err := ExtractCreds(req)
	if err != nil {
		return nil, err
	}
	return doAuth(a, user, pwd, req.Header)
}

func (a *authImpl) Auth(user, pwd string) (creds Creds, err error) {
	return doAuth(a, user, pwd, nil)
}

func (a *authImpl) GetMemcachedServiceAuth(hostport string) (user, pwd string, err error) {
	host, port, err := SplitHostPort(hostport)
	if err != nil {
		return "", "", err
	}
	user, _, pwd, err = cbauthimpl.GetCreds(a.svc, host, port)
	if err == nil && user == "" && pwd == "" {
		return "", "", UnknownHostPortError(hostport)
	}
	return
}

func (a *authImpl) GetHTTPServiceAuth(hostport string) (user, pwd string, err error) {
	host, port, err := SplitHostPort(hostport)
	if err != nil {
		return "", "", err
	}
	_, user, pwd, err = cbauthimpl.GetCreds(a.svc, host, port)
	if err == nil && user == "" && pwd == "" {
		return "", "", UnknownHostPortError(hostport)
	}
	return
}

var _ Authenticator = (*authImpl)(nil)
<commit_msg>MB-17612 return NoAccessCreds if auth requires LDAP but LDAP is disabled<commit_after>// @author Couchbase <info@couchbase.com>
// @copyright 2014 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package cbauth provides auth{N,Z} for couchbase server services.
package cbauth

import (
	"fmt"
	"net/http"

	"github.com/couchbase/cbauth/cbauthimpl"
)

// TODO: consider API that would allow us to do digest auth behind the
// scene

// TODO: for GetHTTPServiceAuth consider something more generic such
// as GetHTTPAuthHeader. Or even maybe RoundTrip. So that we can
// handle digest auth

// Authenticator is main cbauth interface. It supports both incoming
// and outgoing auth.
type Authenticator interface {
	// AuthWebCreds method extracts credentials from given http request.
	AuthWebCreds(req *http.Request) (creds Creds, err error)
	// Auth method constructs credentials from given user and password pair.
	Auth(user, pwd string) (creds Creds, err error)
	// GetHTTPServiceAuth returns user/password creds giving
	// "admin" access to given http service inside couchbase cluster.
	GetHTTPServiceAuth(hostport string) (user, pwd string, err error)
	// GetMemcachedServiceAuth returns user/password creds given
	// "admin" access to given memcached service.
	GetMemcachedServiceAuth(hostport string) (user, pwd string, err error)
}

// TODO: get rid of unnecessary error returns

// Creds type represents credentials and answers queries on this creds
// authorized actions. Note: it'll become (possibly much) wider API in
// future, but it's main purpose right now is to get us started.
type Creds interface {
	// Name method returns user name (e.g. for auditing)
	Name() string
	// Source method returns user source (for auditing)
	Source() string
	// IsAllowed method returns true if the permission is granted
	// for these credentials
	IsAllowed(permission string) (bool, error)
	// IsAdmin method returns true iff this creds represent valid
	// admin account.
	IsAdmin() (bool, error)
	// CanReadAnyMetadata method returns true iff this creds has
	// permission to read any metadata (i.e. admin or ro-admin).
	CanReadAnyMetadata() bool
	// IsROAdmin is confusing alias for CanReadAnyMetadata. Don't
	// use!
	IsROAdmin() (bool, error)
	// CanAccessBucket method returns true iff this creds
	// represent valid account that can read/write/query docs in given
	// bucket.
	CanAccessBucket(bucket string) (bool, error)
	// CanReadBucket method returns true iff this creds represent
	// valid account that can read (but not necessarily write)
	// docs in given bucket.
	CanReadBucket(bucket string) (bool, error)
	// CanDDLBucket method returns true iff this creds represent
	// valid account that can DDL in given bucket. Note that at
	// this time it delegates to CanAccessBucket in only
	// implementation.
	CanDDLBucket(bucket string) (bool, error)
}

var _ Creds = (*cbauthimpl.CredsImpl)(nil)

type naCreds struct{}

func (na naCreds) Name() string                                { return "" }
func (na naCreds) Source() string                              { return "" }
func (na naCreds) IsAllowed(permission string) (bool, error)   { return false, nil }
func (na naCreds) IsAdmin() (bool, error)                      { return false, nil }
func (na naCreds) IsROAdmin() (bool, error)                    { return false, nil }
func (na naCreds) CanReadAnyMetadata() bool                    { return false }
func (na naCreds) CanAccessBucket(bucket string) (bool, error) { return false, nil }
func (na naCreds) CanReadBucket(bucket string) (bool, error)   { return false, nil }
func (na naCreds) CanDDLBucket(bucket string) (bool, error)    { return false, nil }

// NoAccessCreds is Creds instance that has no access at
// all. Authenticator returns this Creds instance for incoming auth
// that was not recognized at all as valid user.
var NoAccessCreds Creds = naCreds{}

type authImpl struct {
	svc *cbauthimpl.Svc
}

// DBStaleError is kind of error that signals that cbauth internal
// state is not synchronized with ns_server yet or anymore.
type DBStaleError struct {
	Err error
}

func (e *DBStaleError) Error() string {
	if e.Err != nil {
		return fmt.Sprintf("CBAuth database is stale: last reason: %s", e.Err)
	}
	return "CBAuth database is stale. Was never updated yet."
}

// UnknownHostPortError is returned from GetMemcachedServiceAuth and
// GetHTTPServiceAuth calls for unknown host:port arguments.
type UnknownHostPortError string

func (s UnknownHostPortError) Error() string {
	return fmt.Sprintf("Unable to find given hostport in cbauth database: `%s'", string(s))
}

func doOnServer(s *cbauthimpl.Svc, hdr http.Header) (Creds, error) {
	rv, err := cbauthimpl.VerifyOnServer(s, hdr)
	if rv == nil && err == nil {
		return NoAccessCreds, nil
	}
	return rv, err
}

func doAuth(a *authImpl, user, pwd string, hdr http.Header) (Creds, error) {
	ci, err := cbauthimpl.VerifyPassword(a.svc, user, pwd)
	if err != nil {
		return nil, err
	}

	if ci != nil {
		return ci, nil
	}

	if user == "" {
		return NoAccessCreds, nil
	}

	ldapEnabled, err := cbauthimpl.IsLDAPEnabled(a.svc)
	if err != nil {
		return nil, err
	}

	if !ldapEnabled {
		return NoAccessCreds, nil
	}

	if hdr == nil {
		req, err := http.NewRequest("GET", "http://host/", nil)
		if err != nil {
			panic("Must not happen: " + err.Error())
		}
		req.SetBasicAuth(user, pwd)
		hdr = req.Header
	}
	return doOnServer(a.svc, hdr)
}

func (a *authImpl) AuthWebCreds(req *http.Request) (creds Creds, err error) {
	if cbauthimpl.IsAuthTokenPresent(req) {
		return doOnServer(a.svc, req.Header)
	}
	user, pwd, err := ExtractCreds(req)
	if err != nil {
		return nil, err
	}
	return doAuth(a, user, pwd, req.Header)
}

func (a *authImpl) Auth(user, pwd string) (creds Creds, err error) {
	return doAuth(a, user, pwd, nil)
}

func (a *authImpl) GetMemcachedServiceAuth(hostport string) (user, pwd string, err error) {
	host, port, err := SplitHostPort(hostport)
	if err != nil {
		return "", "", err
	}
	user, _, pwd, err = cbauthimpl.GetCreds(a.svc, host, port)
	if err == nil && user == "" && pwd == "" {
		return "", "", UnknownHostPortError(hostport)
	}
	return
}

func (a *authImpl) GetHTTPServiceAuth(hostport string) (user, pwd string, err error) {
	host, port, err := SplitHostPort(hostport)
	if err != nil {
		return "", "", err
	}
	_, user, pwd, err = cbauthimpl.GetCreds(a.svc, host, port)
	if err == nil && user == "" && pwd == "" {
		return "", "", UnknownHostPortError(hostport)
	}
	return
}

var _ Authenticator = (*authImpl)(nil)
<|endoftext|>


<commit_before>package listener

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/stellar/gateway/config"
	"github.com/stellar/gateway/db"
	"github.com/stellar/gateway/horizon"
	"github.com/stellar/go-stellar-base/keypair"
)

type PaymentListener struct {
	config         *config.Config
	entityManager  db.EntityManagerInterface
	horizon        horizon.HorizonInterface
	log            *logrus.Entry
	repository     db.RepositoryInterface
	issuingAccount keypair.KP
	now            func() time.Time
}

const HOOK_TIMEOUT = 10 * time.Second

func NewPaymentListener(
	config *config.Config,
	entityManager db.EntityManagerInterface,
	horizon horizon.HorizonInterface,
	repository db.RepositoryInterface,
	now func() time.Time,
) (pl PaymentListener, err error) {
	pl.config = config
	pl.entityManager = entityManager
	pl.horizon = horizon
	pl.repository = repository
	pl.issuingAccount, err = keypair.Parse(*config.Accounts.IssuingSeed)
	pl.now = now
	pl.log = logrus.WithFields(logrus.Fields{
		"service": "PaymentListener",
	})
	return
}

func (pl PaymentListener) Listen() (err error) {
	accountId := *pl.config.Accounts.ReceivingAccountId

	_, err = pl.horizon.LoadAccount(accountId)
	if err != nil {
		return
	}

	go func() {
		for {
			cursor, err := pl.repository.GetLastCursorValue()
			if err != nil {
				pl.log.WithFields(logrus.Fields{"error": err}).Error("Could not load last cursor from the DB")
				return
			}

			var cursorValue string
			if cursor != nil {
				cursorValue = *cursor
			}

			pl.log.WithFields(logrus.Fields{
				"accountId": accountId,
				"cursor":    cursorValue,
			}).Info("Started listening for new payments")

			err = pl.horizon.StreamPayments(
				accountId,
				cursor,
				pl.onPayment,
			)
			if err != nil {
				pl.log.Error("Error while streaming: ", err)
				pl.log.Info("Sleeping...")
				time.Sleep(10 * time.Second)
			}
			pl.log.Info("Streaming connection closed. Restarting...")
		}
	}()

	return
}

func (pl PaymentListener) onPayment(payment horizon.PaymentResponse) (err error) {
	pl.log.WithFields(logrus.Fields{"id": payment.Id}).Info("New payment")

	dbPayment := db.ReceivedPayment{
		OperationId: payment.Id,
		ProcessedAt: pl.now(),
		PagingToken: payment.PagingToken,
	}

	savePayment := func(payment *db.ReceivedPayment) (err error) {
		err = pl.entityManager.Persist(payment)
		return
	}

	if payment.Type != "payment" {
		dbPayment.Status = "Not a payment operation"
		savePayment(&dbPayment)
		return
	}

	if payment.To != *pl.config.Accounts.ReceivingAccountId {
		dbPayment.Status = "Operation sent not received"
		savePayment(&dbPayment)
		return nil
	}

	if !pl.isAssetAllowed(payment.AssetCode, payment.AssetIssuer) {
		dbPayment.Status = "Asset not allowed"
		savePayment(&dbPayment)
		return nil
	}

	err = pl.horizon.LoadMemo(&payment)
	if err != nil {
		pl.log.Error("Unable to load transaction memo")
		return err
	}

	if payment.Memo.Type == "" || payment.Memo.Value == "" {
		dbPayment.Status = "Transaction does not have memo"
		savePayment(&dbPayment)
		return nil
	}

	client := http.Client{
		Timeout: HOOK_TIMEOUT,
	}
	resp, err := client.PostForm(
		*pl.config.Hooks.Receive,
		url.Values{
			"id":         {payment.Id},
			"from":       {payment.From},
			"amount":     {payment.Amount},
			"asset_code": {payment.AssetCode},
			"memo_type":  {payment.Memo.Type},
			"memo":       {payment.Memo.Value},
		},
	)
	if err != nil {
		pl.log.Error("Error sending request to receive hook")
		return err
	}

	if resp.StatusCode != 200 {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			pl.log.Error("Error reading receive hook response")
			return err
		}

		pl.log.WithFields(logrus.Fields{
			"status": resp.StatusCode,
			"body":   string(body),
		}).Error("Error response from receive hook")
		return errors.New("Error response from receive hook")
	}

	dbPayment.Status = "Success"
	err = savePayment(&dbPayment)
	if err != nil {
		pl.log.Error("Error saving payment to the DB")
		return err
	}

	return nil
}

func (pl PaymentListener) isAssetAllowed(code string, issuer string) bool {
	if issuer != pl.issuingAccount.Address() {
		return false
	}

	for _, b := range pl.config.Assets {
		if b == code {
			return true
		}
	}
	return false
}
<commit_msg>Set cursor to "now" when no cursor saved<commit_after>package listener

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/stellar/gateway/config"
	"github.com/stellar/gateway/db"
	"github.com/stellar/gateway/horizon"
	"github.com/stellar/go-stellar-base/keypair"
)

type PaymentListener struct {
	config         *config.Config
	entityManager  db.EntityManagerInterface
	horizon        horizon.HorizonInterface
	log            *logrus.Entry
	repository     db.RepositoryInterface
	issuingAccount keypair.KP
	now            func() time.Time
}

const HOOK_TIMEOUT = 10 * time.Second

func NewPaymentListener(
	config *config.Config,
	entityManager db.EntityManagerInterface,
	horizon horizon.HorizonInterface,
	repository db.RepositoryInterface,
	now func() time.Time,
) (pl PaymentListener, err error) {
	pl.config = config
	pl.entityManager = entityManager
	pl.horizon = horizon
	pl.repository = repository
	pl.issuingAccount, err = keypair.Parse(*config.Accounts.IssuingSeed)
	pl.now = now
	pl.log = logrus.WithFields(logrus.Fields{
		"service": "PaymentListener",
	})
	return
}

func (pl PaymentListener) Listen() (err error) {
	accountId := *pl.config.Accounts.ReceivingAccountId

	_, err = pl.horizon.LoadAccount(accountId)
	if err != nil {
		return
	}

	go func() {
		for {
			cursor, err := pl.repository.GetLastCursorValue()
			if err != nil {
				pl.log.WithFields(logrus.Fields{"error": err}).Error("Could not load last cursor from the DB")
				return
			}

			var cursorValue string
			if cursor != nil {
				cursorValue = *cursor
			} else {
				// If no last cursor saved set it to: `now`
				cursorValue = "now"
				cursor = &cursorValue
			}

			pl.log.WithFields(logrus.Fields{
				"accountId": accountId,
				"cursor":    cursorValue,
			}).Info("Started listening for new payments")

			err = pl.horizon.StreamPayments(
				accountId,
				cursor,
				pl.onPayment,
			)
			if err != nil {
				pl.log.Error("Error while streaming: ", err)
				pl.log.Info("Sleeping...")
				time.Sleep(10 * time.Second)
			}
			pl.log.Info("Streaming connection closed. Restarting...")
		}
	}()

	return
}

func (pl PaymentListener) onPayment(payment horizon.PaymentResponse) (err error) {
	pl.log.WithFields(logrus.Fields{"id": payment.Id}).Info("New payment")

	dbPayment := db.ReceivedPayment{
		OperationId: payment.Id,
		ProcessedAt: pl.now(),
		PagingToken: payment.PagingToken,
	}

	savePayment := func(payment *db.ReceivedPayment) (err error) {
		err = pl.entityManager.Persist(payment)
		return
	}

	if payment.Type != "payment" {
		dbPayment.Status = "Not a payment operation"
		savePayment(&dbPayment)
		return
	}

	if payment.To != *pl.config.Accounts.ReceivingAccountId {
		dbPayment.Status = "Operation sent not received"
		savePayment(&dbPayment)
		return nil
	}

	if !pl.isAssetAllowed(payment.AssetCode, payment.AssetIssuer) {
		dbPayment.Status = "Asset not allowed"
		savePayment(&dbPayment)
		return nil
	}

	err = pl.horizon.LoadMemo(&payment)
	if err != nil {
		pl.log.Error("Unable to load transaction memo")
		return err
	}

	if payment.Memo.Type == "" || payment.Memo.Value == "" {
		dbPayment.Status = "Transaction does not have memo"
		savePayment(&dbPayment)
		return nil
	}

	client := http.Client{
		Timeout: HOOK_TIMEOUT,
	}
	resp, err := client.PostForm(
		*pl.config.Hooks.Receive,
		url.Values{
			"id":         {payment.Id},
			"from":       {payment.From},
			"amount":     {payment.Amount},
			"asset_code": {payment.AssetCode},
			"memo_type":  {payment.Memo.Type},
			"memo":       {payment.Memo.Value},
		},
	)
	if err != nil {
		pl.log.Error("Error sending request to receive hook")
		return err
	}

	if resp.StatusCode != 200 {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			pl.log.Error("Error reading receive hook response")
			return err
		}

		pl.log.WithFields(logrus.Fields{
			"status": resp.StatusCode,
			"body":   string(body),
		}).Error("Error response from receive hook")
		return errors.New("Error response from receive hook")
	}

	dbPayment.Status = "Success"
	err = savePayment(&dbPayment)
	if err != nil {
		pl.log.Error("Error saving payment to the DB")
		return err
	}

	return nil
}

func (pl PaymentListener) isAssetAllowed(code string, issuer string) bool {
	if issuer != pl.issuingAccount.Address() {
		return false
	}

	for _, b := range pl.config.Assets {
		if b == code {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package lob

import (
	"strconv"
	"time"
)

// Check represents a printed check in Lob's system.
type Check struct {
	Error                Error               `json:"error"`
	Amount               float64             `json:"amount"`
	BankAccount          *BankAccount        `json:"bank_account"`
	CheckBottom          *string             `json:"check_bottom"`
	CheckNumber          int                 `json:"check_number"`
	Data                 map[string]string   `json:"data"`
	DateCreated          string              `json:"date_created"`
	DateModified         string              `json:"date_modified"`
	Description          string              `json:"description"`
	ExpectedDeliveryDate string              `json:"expected_delivery_date"`
	SendDate             time.Time           `json:"send_date"`
	From                 *Address            `json:"from"`
	ID                   string              `json:"id"`
	Logo                 *string             `json:"logo"`
	MailType             *string             `json:"mail_type"`
	Memo                 string              `json:"memo"`
	Message              *string             `json:"message"`
	Metadata             map[string]string   `json:"metadata"`
	Name                 string              `json:"name"`
	Object               string              `json:"object"`
	Thumbnails           []map[string]string `json:"thumbnails"`
	To                   *Address            `json:"to"`
	Tracking             *Tracking           `json:"tracking"`
	URL                  string              `json:"url"`
}

// Tracking provides information on shipment tracking for a check.
type Tracking struct {
	Carrier        string        `json:"carrier"`
	Events         []interface{} `json:"events"`
	ID             string        `json:"id"`
	Link           *string       `json:"link"`
	Object         string        `json:"object"`
	TrackingNumber string        `json:"tracking_number"`
}

// Mail types that lob supports.
const (
	MailTypeUspsFirstClass = "usps_first_class"
	MailTypeUpsNextDayAir  = "ups_next_day_air"
)

// CreateCheckRequest specifies options for creating a check.
type CreateCheckRequest struct {
	Amount        float64           `json:"amount"`
	BankAccountID string            `json:"bank_account"`
	CheckBottom   *string           `json:"check_bottom"` // 400 chars, at bottom (cannot use with message)
	CheckNumber   *string           `json:"check_number"`
	Data          map[string]string `json:"data"`
	Description   *string           `json:"description"`
	FromAddressID string            `json:"from"`
	Logo          *string           `json:"logo"` // url or multiform. Square, RGB / CMYK, >= 100x100, transparent bg, PNG or JPEG, and will be grayscaled
	MailType      *string           `json:"mail_type"`
	Memo          *string           `json:"memo"`    // 40 chars in memo line
	Message       *string           `json:"message"` // 400 chars, at top (cannot use with check_bottom)
	ToAddressID   string            `json:"to"`
}

// CreateCheck requests for a new check to be printed and mailed.
func (lob *lob) CreateCheck(req *CreateCheckRequest) (*Check, error) {
	resp := new(Check)
	if err := lob.post("checks/", json2form(*req), resp); err != nil {
		return resp, err
	}
	return resp, nil
}

type CancelCheckResponse struct {
	ID      string `json:"id"`
	Deleted bool   `json:"deleted"`
}

// GetCheck gets information about a particulr check.
func (lob *lob) GetCheck(id string) (*Check, error) {
	resp := new(Check)
	if err := lob.get("checks/"+id, nil, resp); err != nil {
		return resp, err
	}
	return resp, nil
}

func (lob *lob) CancelCheck(id string) (*CancelCheckResponse, error) {
	resp := new(CancelCheckResponse)
	if err := lob.delete("checks/"+id, &resp); err != nil {
		return resp, err
	}
	return resp, nil
}

// ListChecksResponse details all of the checks we've ever mailed and printed.
type ListChecksResponse struct {
	Data        []Check `json:"data"`
	Object      string  `json:"object"`
	NextURL     string  `json:"next_url"`
	PreviousURL string  `json:"next_url"`
	Count       int     `json:"count"`
}

// ListChecks retrieves information on all checks we've ever made, in reverse chrono order.
func (lob *lob) ListChecks(count, offset int) (*ListChecksResponse, error) {
	if count <= 0 {
		count = 10
	}
	if offset < 0 {
		offset = 0
	}
	resp := new(ListChecksResponse)
	if err := lob.get("checks", map[string]string{
		"limit":  strconv.Itoa(count),
		"offset": strconv.Itoa(offset),
	}, resp); err != nil {
		return nil, err
	}
	return resp, nil
}
<commit_msg>need error to be a pointer (#14)<commit_after>package lob

import (
	"strconv"
	"time"
)

// Check represents a printed check in Lob's system.
type Check struct {
	Error                *Error              `json:"error"`
	Amount               float64             `json:"amount"`
	BankAccount          *BankAccount        `json:"bank_account"`
	CheckBottom          *string             `json:"check_bottom"`
	CheckNumber          int                 `json:"check_number"`
	Data                 map[string]string   `json:"data"`
	DateCreated          string              `json:"date_created"`
	DateModified         string              `json:"date_modified"`
	Description          string              `json:"description"`
	ExpectedDeliveryDate string              `json:"expected_delivery_date"`
	SendDate             time.Time           `json:"send_date"`
	From                 *Address            `json:"from"`
	ID                   string              `json:"id"`
	Logo                 *string             `json:"logo"`
	MailType             *string             `json:"mail_type"`
	Memo                 string              `json:"memo"`
	Message              *string             `json:"message"`
	Metadata             map[string]string   `json:"metadata"`
	Name                 string              `json:"name"`
	Object               string              `json:"object"`
	Thumbnails           []map[string]string `json:"thumbnails"`
	To                   *Address            `json:"to"`
	Tracking             *Tracking           `json:"tracking"`
	URL                  string              `json:"url"`
}

// Tracking provides information on shipment tracking for a check.
type Tracking struct {
	Carrier        string        `json:"carrier"`
	Events         []interface{} `json:"events"`
	ID             string        `json:"id"`
	Link           *string       `json:"link"`
	Object         string        `json:"object"`
	TrackingNumber string        `json:"tracking_number"`
}

// Mail types that lob supports.
const (
	MailTypeUspsFirstClass = "usps_first_class"
	MailTypeUpsNextDayAir  = "ups_next_day_air"
)

// CreateCheckRequest specifies options for creating a check.
type CreateCheckRequest struct {
	Amount        float64           `json:"amount"`
	BankAccountID string            `json:"bank_account"`
	CheckBottom   *string           `json:"check_bottom"` // 400 chars, at bottom (cannot use with message)
	CheckNumber   *string           `json:"check_number"`
	Data          map[string]string `json:"data"`
	Description   *string           `json:"description"`
	FromAddressID string            `json:"from"`
	Logo          *string           `json:"logo"` // url or multiform. Square, RGB / CMYK, >= 100x100, transparent bg, PNG or JPEG, and will be grayscaled
	MailType      *string           `json:"mail_type"`
	Memo          *string           `json:"memo"`    // 40 chars in memo line
	Message       *string           `json:"message"` // 400 chars, at top (cannot use with check_bottom)
	ToAddressID   string            `json:"to"`
}

// CreateCheck requests for a new check to be printed and mailed.
func (lob *lob) CreateCheck(req *CreateCheckRequest) (*Check, error) {
	resp := new(Check)
	if err := lob.post("checks/", json2form(*req), resp); err != nil {
		return resp, err
	}
	return resp, nil
}

type CancelCheckResponse struct {
	ID      string `json:"id"`
	Deleted bool   `json:"deleted"`
}

// GetCheck gets information about a particulr check.
func (lob *lob) GetCheck(id string) (*Check, error) {
	resp := new(Check)
	if err := lob.get("checks/"+id, nil, resp); err != nil {
		return resp, err
	}
	return resp, nil
}

func (lob *lob) CancelCheck(id string) (*CancelCheckResponse, error) {
	resp := new(CancelCheckResponse)
	if err := lob.delete("checks/"+id, &resp); err != nil {
		return resp, err
	}
	return resp, nil
}

// ListChecksResponse details all of the checks we've ever mailed and printed.
type ListChecksResponse struct {
	Data        []Check `json:"data"`
	Object      string  `json:"object"`
	NextURL     string  `json:"next_url"`
	PreviousURL string  `json:"next_url"`
	Count       int     `json:"count"`
}

// ListChecks retrieves information on all checks we've ever made, in reverse chrono order.
func (lob *lob) ListChecks(count, offset int) (*ListChecksResponse, error) {
	if count <= 0 {
		count = 10
	}
	if offset < 0 {
		offset = 0
	}
	resp := new(ListChecksResponse)
	if err := lob.get("checks", map[string]string{
		"limit":  strconv.Itoa(count),
		"offset": strconv.Itoa(offset),
	}, resp); err != nil {
		return nil, err
	}
	return resp, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/cubicdaiya/chocon/proxy"
	"github.com/fukata/golang-stats-api-handler"
	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/go-apache-logformat"
	"github.com/lestrrat/go-file-rotatelogs"
	"github.com/lestrrat/go-server-starter-listener"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

var (
	Version string
)

type cmdOpts struct {
	Listen    string `short:"l" long:"listen" default:"0.0.0.0" description:"address to bind"`
	Port      string `short:"p" long:"port" default:"3000" description:"Port number to bind"`
	LogDir    string `long:"access-log-dir" default:"" description:"directory to store logfiles"`
	LogRotate int64  `long:"access-log-rotate" default:"30" description:"Number of day before remove logs"`
	Version   bool   `short:"v" long:"version" description:"Show version"`
}

func addStatsHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "/.api/stats") == 0 {
			stats_api.Handler(w, r)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}

func addLogHandler(h http.Handler, log_dir string, log_rotate int64) http.Server {
	apache_log, err := apachelog.New(`%h %l %u %t "%r" %>s %b "%v" %{X-Chocon-Req}i`)
	if err != nil {
		panic(fmt.Sprintf("could not create logger: %v", err))
	}

	if log_dir == "stdout" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stdout),
		}
	} else if log_dir == "" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stderr),
		}
	} else if log_dir == "none" {
		return http.Server{
			Handler: h,
		}
	}

	log_file := log_dir
	link_name := log_dir
	if !strings.HasSuffix(log_dir, "/") {
		log_file += "/"
		link_name += "/"

	}
	log_file += "access_log.%Y%m%d%H%M"
	link_name += "current"

	rl := rotatelogs.New(
		log_file,
		rotatelogs.WithLinkName(link_name),
		rotatelogs.WithMaxAge(time.Duration(log_rotate)*86400*time.Second),
		rotatelogs.WithRotationTime(time.Second*86400),
	)

	return http.Server{
		Handler: apache_log.Wrap(h, rl),
	}
}

func main() {
	opts := cmdOpts{}
	psr := flags.NewParser(&opts, flags.Default)
	_, err := psr.Parse()
	if err != nil {
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf(`chocon %s
Compiler: %s %s
`,
			Version,
			runtime.Compiler,
			runtime.Version())
		return

	}

	requestConverter := func(r *http.Request, pr *http.Request, ps *proxy_handler.ProxyStatus) {
		if r.Host == "" {
			ps.Status = http.StatusBadRequest
			return
		}
		originalHost := r.Host
		hostSplited := strings.Split(originalHost, ".")
		lastPartIndex := 0
		for i, hostPart := range hostSplited {
			if hostPart == "ccnproxy-ssl" || hostPart == "ccnproxy-secure" || hostPart == "ccnproxy-https" || hostPart == "ccnproxy" {
				lastPartIndex = i
			}
		}
		if lastPartIndex == 0 {
			ps.Status = http.StatusBadRequest
			return
		}

		pr.URL.Host = strings.Join(hostSplited[0:lastPartIndex], ".")
		if hostSplited[lastPartIndex] == "ccnproxy-https" || hostSplited[lastPartIndex] == "ccnproxy-secure" || hostSplited[lastPartIndex] == "ccnproxy-ssl" {
			pr.URL.Scheme = "https"
		}
	}
	proxyHandler := addStatsHandler(proxy_handler.NewProxyWithRequestConverter(requestConverter))

	l, err := ss.NewListener()
	if l == nil || err != nil {
		// Fallback if not running under Server::Starter
		l, err = net.Listen("tcp", fmt.Sprintf("%s:%s", opts.Listen, opts.Port))
		if err != nil {
			panic(fmt.Sprintf("Failed to listen to port %s:%s", opts.Listen, opts.Port))
		}
	}

	server := addLogHandler(proxyHandler, opts.LogDir, opts.LogRotate)
	server.Serve(l)
}
<commit_msg>fixed repository.<commit_after>package main

import (
	"fmt"
	"github.com/fukata/golang-stats-api-handler"
	"github.com/jessevdk/go-flags"
	"github.com/kazeburo/chocon/proxy"
	"github.com/lestrrat/go-apache-logformat"
	"github.com/lestrrat/go-file-rotatelogs"
	"github.com/lestrrat/go-server-starter-listener"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

var (
	Version string
)

type cmdOpts struct {
	Listen    string `short:"l" long:"listen" default:"0.0.0.0" description:"address to bind"`
	Port      string `short:"p" long:"port" default:"3000" description:"Port number to bind"`
	LogDir    string `long:"access-log-dir" default:"" description:"directory to store logfiles"`
	LogRotate int64  `long:"access-log-rotate" default:"30" description:"Number of day before remove logs"`
	Version   bool   `short:"v" long:"version" description:"Show version"`
}

func addStatsHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "/.api/stats") == 0 {
			stats_api.Handler(w, r)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}

func addLogHandler(h http.Handler, log_dir string, log_rotate int64) http.Server {
	apache_log, err := apachelog.New(`%h %l %u %t "%r" %>s %b "%v" %{X-Chocon-Req}i`)
	if err != nil {
		panic(fmt.Sprintf("could not create logger: %v", err))
	}

	if log_dir == "stdout" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stdout),
		}
	} else if log_dir == "" {
		return http.Server{
			Handler: apache_log.Wrap(h, os.Stderr),
		}
	} else if log_dir == "none" {
		return http.Server{
			Handler: h,
		}
	}

	log_file := log_dir
	link_name := log_dir
	if !strings.HasSuffix(log_dir, "/") {
		log_file += "/"
		link_name += "/"

	}
	log_file += "access_log.%Y%m%d%H%M"
	link_name += "current"

	rl := rotatelogs.New(
		log_file,
		rotatelogs.WithLinkName(link_name),
		rotatelogs.WithMaxAge(time.Duration(log_rotate)*86400*time.Second),
		rotatelogs.WithRotationTime(time.Second*86400),
	)

	return http.Server{
		Handler: apache_log.Wrap(h, rl),
	}
}

func main() {
	opts := cmdOpts{}
	psr := flags.NewParser(&opts, flags.Default)
	_, err := psr.Parse()
	if err != nil {
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf(`chocon %s
Compiler: %s %s
`,
			Version,
			runtime.Compiler,
			runtime.Version())
		return

	}

	requestConverter := func(r *http.Request, pr *http.Request, ps *proxy_handler.ProxyStatus) {
		if r.Host == "" {
			ps.Status = http.StatusBadRequest
			return
		}
		originalHost := r.Host
		hostSplited := strings.Split(originalHost, ".")
		lastPartIndex := 0
		for i, hostPart := range hostSplited {
			if hostPart == "ccnproxy-ssl" || hostPart == "ccnproxy-secure" || hostPart == "ccnproxy-https" || hostPart == "ccnproxy" {
				lastPartIndex = i
			}
		}
		if lastPartIndex == 0 {
			ps.Status = http.StatusBadRequest
			return
		}

		pr.URL.Host = strings.Join(hostSplited[0:lastPartIndex], ".")
		if hostSplited[lastPartIndex] == "ccnproxy-https" || hostSplited[lastPartIndex] == "ccnproxy-secure" || hostSplited[lastPartIndex] == "ccnproxy-ssl" {
			pr.URL.Scheme = "https"
		}
	}
	proxyHandler := addStatsHandler(proxy_handler.NewProxyWithRequestConverter(requestConverter))

	l, err := ss.NewListener()
	if l == nil || err != nil {
		// Fallback if not running under Server::Starter
		l, err = net.Listen("tcp", fmt.Sprintf("%s:%s", opts.Listen, opts.Port))
		if err != nil {
			panic(fmt.Sprintf("Failed to listen to port %s:%s", opts.Listen, opts.Port))
		}
	}

	server := addLogHandler(proxyHandler, opts.LogDir, opts.LogRotate)
	server.Serve(l)
}
<|endoftext|>


<commit_before>// Copyright 2016 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package sleuth

import (
	"net/http"
	"sync"
	"time"

	"github.com/pborman/uuid"
	"github.com/ursiform/logger"
	"github.com/zeromq/gyre"
)

type listeners struct {
	*sync.Mutex
	handles map[string]chan *http.Response
}

type notifier struct {
	*sync.Mutex
	notify chan struct{}
}

// Client is the peer on the sleuth network that makes requests and, if a
// handler has been provided, responds to peer requests.
type Client struct {
	// Timeout is the duration to wait before an outstanding request times out.
	// By default, it is set to 500ms.
	Timeout time.Duration

	additions *notifier
	handler   http.Handler
	listeners *listeners
	log       *logger.Logger
	node      *gyre.Gyre

	directory map[string]string          // map[node-name]service-type
	services  map[string]*serviceWorkers // map[service-type]service-workers
}

func (c *Client) add(gid, name, node, service, version string) error {
	if gid != group {
		c.log.Debug("sleuth: no group header for %s, client-only", name)
		return nil
	}
	// Node and service are required. Version is optional.
	if len(node) == 0 || len(service) == 0 {
		return newError(errAdd, "failed to add %s node?=%t, type?=%t",
			name, len(node) > 0, len(service) > 0)
	}
	// Associate the node name with its service in the directory.
	c.directory[name] = service
	// Create a service workers collection if necessary.
	if c.services[service] == nil {
		c.services[service] = newWorkers()
	}
	// Add peer to the service workers.
	p := &peer{Name: name, Node: node, Service: service, Version: version}
	c.services[service].add(p)
	// If necessary, notify the additions channel that a peer has been added.
	if c.additions.notify != nil {
		c.additions.notify <- struct{}{}
	}
	c.log.Info("sleuth: add %s/%s %s to %s", service, version, name, group)
	return nil
}

// Returns true if it had to block and false if it returns immediately.
func (c *Client) block(services ...string) bool {
	// Block until the required services are available in the pool.
	c.additions.Lock()
	defer c.additions.Unlock()
	// Even though the client may have just checked to see if services exist,
	// the check is performed here in case there was a delay waiting for the
	// additions mutex to become available.
	if c.has(services...) {
		return false
	}
	c.log.Blocked("sleuth: waiting for client to find services %s", services)
	c.additions.notify = make(chan struct{})
	for range c.additions.notify {
		if c.has(services...) {
			break
		}
	}
	c.additions.notify = nil
	return true
}

// Close leaves the sleuth network and stops the Gyre/Zyre node.
func (c *Client) Close() error {
	c.log.Info("%s leaving %s...", c.node.Name(), group)
	if err := c.node.Leave(group); err != nil {
		return newError(errLeave, err.Error())
	}
	if err := c.node.Stop(); err != nil {
		c.log.Warn("sleuth: %s %s [%d]",
			c.node.Name(), err.Error(), warnClose)
	}
	return nil
}

func (c *Client) dispatch(payload []byte) error {
	// Returned responses (RECV command) and outstanding requests (REPL command)
	// have these headers, respectively: SLEUTH-V0RECV and SLEUTH-V0REPL
	groupLength := len(group)
	dispatchLength := 4
	headerLength := groupLength + dispatchLength
	// If the message header does not match the group, bail.
	if len(payload) < headerLength || string(payload[0:groupLength]) != group {
		return newError(errDispatchHeader, "bad header")
	}
	action := string(payload[groupLength : groupLength+dispatchLength])
	switch action {
	case recv:
		return c.receive(payload[headerLength:])
	case repl:
		return c.reply(payload[headerLength:])
	default:
		return newError(errDispatchAction, "bad action: %s", action)
	}
}

// Do sends an HTTP request to a service and returns and HTTP response. The URL
// for requests needs to use the following format:
// 	sleuth://service-name/requested-path
// For example, a request to the path /bar?baz=qux of a service called
// foo-service would have the URL:
// 	sleuth://foo-service/bar?baz=qux
func (c *Client) Do(req *http.Request) (*http.Response, error) {
	handle := uuid.New()
	to := req.URL.Host
	if req.URL.Scheme != scheme {
		err := newError(errScheme,
			"URL scheme must be \"%s\" in %s", scheme, req.URL.String())
		return nil, err
	}
	services, ok := c.services[to]
	if !ok {
		return nil, newError(errUnknownService, "%s is an unknown service", to)
	}
	p := services.next()
	receiver := c.node.UUID()
	payload, err := marshalRequest(receiver, handle, req)
	if err != nil {
		return nil, err
	}
	c.log.Debug("sleuth: %s %s://%s@%s%s",
		req.Method, scheme, to, p.Name, req.URL.String())
	if err = c.node.Whisper(p.Node, payload); err != nil {
		return nil, newError(errReqWhisper, err.Error())
	}
	listener := make(chan *http.Response, 1)
	c.listen(handle, listener)
	response := <-listener
	if response != nil {
		return response, nil
	}
	return nil, newError(errTimeout,
		"%s {%s}%s timed out", req.Method, to, req.URL.String())
}

func (c *Client) has(services ...string) bool {
	// Check to see if required services are already registered.
	verified := make(map[string]bool)
	available := 0
	for _, service := range services {
		verified[service] = false
	}
	total := len(verified)
	for service := range verified {
		if workers, ok := c.services[service]; ok && workers.available() {
			verified[service] = true
			available += 1
		}
	}
	return available == total
}

func (c *Client) listen(handle string, listener chan *http.Response) {
	c.listeners.Lock()
	defer c.listeners.Unlock()
	c.listeners.handles[handle] = listener
	go c.timeout(handle)
}

func (c *Client) receive(payload []byte) error {
	handle, res, err := unmarshalResponse(payload)
	if err != nil {
		return err.(*Error).escalate(errRECV)
	}
	c.listeners.Lock()
	defer c.listeners.Unlock()
	if listener, ok := c.listeners.handles[handle]; ok {
		listener <- res
		delete(c.listeners.handles, handle)
	} else {
		return newError(errRECV, "unknown handle %s", handle)
	}
	return nil
}

func (c *Client) remove(name string) {
	if service, ok := c.directory[name]; ok {
		remaining, _ := c.services[service].remove(name)
		if remaining == 0 {
			delete(c.services, service)
		}
		delete(c.directory, name)
		c.log.Info("sleuth: remove %s (%s) from %s", service, name, group)
	}
}

func (c *Client) reply(payload []byte) error {
	dest, req, err := unmarshalRequest(payload)
	if err != nil {
		return err.(*Error).escalate(errREPL)
	}
	c.handler.ServeHTTP(newResponseWriter(c.node, dest), req)
	return nil
}

func (c *Client) timeout(handle string) {
	<-time.After(c.Timeout)
	c.listeners.Lock()
	defer c.listeners.Unlock()
	if listener, ok := c.listeners.handles[handle]; ok {
		listener <- nil
		delete(c.listeners.handles, handle)
	}
}

// WaitFor blocks until the required services are available in the pool.
func (c *Client) WaitFor(services ...string) {
	if !c.has(services...) {
		c.block(services...)
	}
}

func newClient(node *gyre.Gyre, out *logger.Logger) *Client {
	return &Client{
		additions: &notifier{Mutex: new(sync.Mutex)},
		directory: make(map[string]string),
		listeners: &listeners{
			new(sync.Mutex),
			make(map[string]chan *http.Response)},
		log:      out,
		node:     node,
		Timeout:  time.Millisecond * 500,
		services: make(map[string]*serviceWorkers)}
}
<commit_msg>listeners => listener<commit_after>// Copyright 2016 Afshin Darian. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package sleuth

import (
	"net/http"
	"sync"
	"time"

	"github.com/pborman/uuid"
	"github.com/ursiform/logger"
	"github.com/zeromq/gyre"
)

type listener struct {
	*sync.Mutex
	handles map[string]chan *http.Response
}

type notifier struct {
	*sync.Mutex
	notify chan struct{}
}

// Client is the peer on the sleuth network that makes requests and, if a
// handler has been provided, responds to peer requests.
type Client struct {
	// Timeout is the duration to wait before an outstanding request times out.
	// By default, it is set to 500ms.
	Timeout time.Duration

	additions *notifier
	handler   http.Handler
	listener  *listener
	log       *logger.Logger
	node      *gyre.Gyre

	directory map[string]string          // map[node-name]service-type
	services  map[string]*serviceWorkers // map[service-type]service-workers
}

func (c *Client) add(gid, name, node, service, version string) error {
	if gid != group {
		c.log.Debug("sleuth: no group header for %s, client-only", name)
		return nil
	}
	// Node and service are required. Version is optional.
	if len(node) == 0 || len(service) == 0 {
		return newError(errAdd, "failed to add %s node?=%t, type?=%t",
			name, len(node) > 0, len(service) > 0)
	}
	// Associate the node name with its service in the directory.
	c.directory[name] = service
	// Create a service workers collection if necessary.
	if c.services[service] == nil {
		c.services[service] = newWorkers()
	}
	// Add peer to the service workers.
	p := &peer{Name: name, Node: node, Service: service, Version: version}
	c.services[service].add(p)
	// If necessary, notify the additions channel that a peer has been added.
	if c.additions.notify != nil {
		c.additions.notify <- struct{}{}
	}
	c.log.Info("sleuth: add %s/%s %s to %s", service, version, name, group)
	return nil
}

// Returns true if it had to block and false if it returns immediately.
func (c *Client) block(services ...string) bool {
	// Block until the required services are available in the pool.
	c.additions.Lock()
	defer c.additions.Unlock()
	// Even though the client may have just checked to see if services exist,
	// the check is performed here in case there was a delay waiting for the
	// additions mutex to become available.
	if c.has(services...) {
		return false
	}
	c.log.Blocked("sleuth: waiting for client to find services %s", services)
	c.additions.notify = make(chan struct{})
	for range c.additions.notify {
		if c.has(services...) {
			break
		}
	}
	c.additions.notify = nil
	return true
}

// Close leaves the sleuth network and stops the Gyre/Zyre node.
func (c *Client) Close() error {
	c.log.Info("%s leaving %s...", c.node.Name(), group)
	if err := c.node.Leave(group); err != nil {
		return newError(errLeave, err.Error())
	}
	if err := c.node.Stop(); err != nil {
		c.log.Warn("sleuth: %s %s [%d]",
			c.node.Name(), err.Error(), warnClose)
	}
	return nil
}

func (c *Client) dispatch(payload []byte) error {
	// Returned responses (RECV command) and outstanding requests (REPL command)
	// have these headers, respectively: SLEUTH-V0RECV and SLEUTH-V0REPL
	groupLength := len(group)
	dispatchLength := 4
	headerLength := groupLength + dispatchLength
	// If the message header does not match the group, bail.
	if len(payload) < headerLength || string(payload[0:groupLength]) != group {
		return newError(errDispatchHeader, "bad header")
	}
	action := string(payload[groupLength : groupLength+dispatchLength])
	switch action {
	case recv:
		return c.receive(payload[headerLength:])
	case repl:
		return c.reply(payload[headerLength:])
	default:
		return newError(errDispatchAction, "bad action: %s", action)
	}
}

// Do sends an HTTP request to a service and returns and HTTP response. The URL
// for requests needs to use the following format:
// 	sleuth://service-name/requested-path
// For example, a request to the path /bar?baz=qux of a service called
// foo-service would have the URL:
// 	sleuth://foo-service/bar?baz=qux
func (c *Client) Do(req *http.Request) (*http.Response, error) {
	handle := uuid.New()
	to := req.URL.Host
	if req.URL.Scheme != scheme {
		err := newError(errScheme,
			"URL scheme must be \"%s\" in %s", scheme, req.URL.String())
		return nil, err
	}
	services, ok := c.services[to]
	if !ok {
		return nil, newError(errUnknownService, "%s is an unknown service", to)
	}
	p := services.next()
	receiver := c.node.UUID()
	payload, err := marshalRequest(receiver, handle, req)
	if err != nil {
		return nil, err
	}
	c.log.Debug("sleuth: %s %s://%s@%s%s",
		req.Method, scheme, to, p.Name, req.URL.String())
	if err = c.node.Whisper(p.Node, payload); err != nil {
		return nil, newError(errReqWhisper, err.Error())
	}
	listener := make(chan *http.Response, 1)
	c.listen(handle, listener)
	response := <-listener
	if response != nil {
		return response, nil
	}
	return nil, newError(errTimeout,
		"%s {%s}%s timed out", req.Method, to, req.URL.String())
}

func (c *Client) has(services ...string) bool {
	// Check to see if required services are already registered.
	verified := make(map[string]bool)
	available := 0
	for _, service := range services {
		verified[service] = false
	}
	total := len(verified)
	for service := range verified {
		if workers, ok := c.services[service]; ok && workers.available() {
			verified[service] = true
			available += 1
		}
	}
	return available == total
}

func (c *Client) listen(handle string, listener chan *http.Response) {
	c.listener.Lock()
	defer c.listener.Unlock()
	c.listener.handles[handle] = listener
	go c.timeout(handle)
}

func (c *Client) receive(payload []byte) error {
	handle, res, err := unmarshalResponse(payload)
	if err != nil {
		return err.(*Error).escalate(errRECV)
	}
	c.listener.Lock()
	defer c.listener.Unlock()
	if listener, ok := c.listener.handles[handle]; ok {
		listener <- res
		delete(c.listener.handles, handle)
	} else {
		return newError(errRECV, "unknown handle %s", handle)
	}
	return nil
}

func (c *Client) remove(name string) {
	if service, ok := c.directory[name]; ok {
		remaining, _ := c.services[service].remove(name)
		if remaining == 0 {
			delete(c.services, service)
		}
		delete(c.directory, name)
		c.log.Info("sleuth: remove %s (%s) from %s", service, name, group)
	}
}

func (c *Client) reply(payload []byte) error {
	dest, req, err := unmarshalRequest(payload)
	if err != nil {
		return err.(*Error).escalate(errREPL)
	}
	c.handler.ServeHTTP(newResponseWriter(c.node, dest), req)
	return nil
}

func (c *Client) timeout(handle string) {
	<-time.After(c.Timeout)
	c.listener.Lock()
	defer c.listener.Unlock()
	if listener, ok := c.listener.handles[handle]; ok {
		listener <- nil
		delete(c.listener.handles, handle)
	}
}

// WaitFor blocks until the required services are available in the pool.
func (c *Client) WaitFor(services ...string) {
	if !c.has(services...) {
		c.block(services...)
	}
}

func newClient(node *gyre.Gyre, out *logger.Logger) *Client {
	return &Client{
		additions: &notifier{Mutex: new(sync.Mutex)},
		directory: make(map[string]string),
		listener: &listener{
			new(sync.Mutex),
			make(map[string]chan *http.Response)},
		log:      out,
		node:     node,
		Timeout:  time.Millisecond * 500,
		services: make(map[string]*serviceWorkers)}
}
<|endoftext|>


<commit_before>package dns

// A concurrent client implementation.
// Client sends query to a channel which
// will then handle the query. Returned replys
// are return on another channel. Ready for handling --- same
// setup for server - a HANDLER function that gets run
// when the query returns.

import (
	"os"
	"io"
	"net"
)

type QueryHandler interface {
	QueryDNS(w RequestWriter, q *Msg)
}

// The RequestWriter interface is used by a DNS query handler to
// construct a DNS request.
type RequestWriter interface {
	WriteMessages([]*Msg)
	Write(*Msg)
	Send(*Msg) os.Error
	Receive() (*Msg, os.Error)
}

// hijacked connections...?
type reply struct {
	client         *Client
	addr           string
	req            *Msg
	conn           net.Conn
	tsigRequestMAC string
	tsigTimersOnly bool
}

// A Request is a incoming message from a Client
type Request struct {
	Request *Msg
	Addr    string
	Client  *Client
}

// QueryMux is an DNS request multiplexer. It matches the
// zone name of each incoming request against a list of
// registered patterns add calls the handler for the pattern
// that most closely matches the zone name.
type QueryMux struct {
	m map[string]QueryHandler
}

// NewQueryMux allocates and returns a new QueryMux.
func NewQueryMux() *QueryMux { return &QueryMux{make(map[string]QueryHandler)} }

// DefaultQueryMux is the default QueryMux used by Query.
var DefaultQueryMux = NewQueryMux()

func newQueryChanSlice() chan []*Msg { return make(chan []*Msg) }
func newQueryChan() chan *Request    { return make(chan *Request) }

// Default channels to use for the resolver
var DefaultReplyChan = newQueryChanSlice()
var DefaultQueryChan = newQueryChan()

// The HandlerQueryFunc type is an adapter to allow the use of
// ordinary functions as DNS query handlers.  If f is a function
// with the appropriate signature, HandlerQueryFunc(f) is a
// QueryHandler object that calls f.
type HandlerQueryFunc func(RequestWriter, *Msg)

// QueryDNS calls f(w, reg)
func (f HandlerQueryFunc) QueryDNS(w RequestWriter, r *Msg) {
	go f(w, r)
}

func HandleQueryFunc(pattern string, handler func(RequestWriter, *Msg)) {
	DefaultQueryMux.HandleQueryFunc(pattern, handler)
}

// reusing zoneMatch from server.go
func (mux *QueryMux) match(zone string) QueryHandler {
	var h QueryHandler
	var n = 0
	for k, v := range mux.m {
		if !zoneMatch(k, zone) {
			continue
		}
		if h == nil || len(k) > n {
			n = len(k)
			h = v
		}
	}
	return h
}

func (mux *QueryMux) Handle(pattern string, handler QueryHandler) {
	if pattern == "" {
		panic("dns: invalid pattern " + pattern)
	}
	mux.m[pattern] = handler
}

func (mux *QueryMux) HandleQueryFunc(pattern string, handler func(RequestWriter, *Msg)) {
	mux.Handle(pattern, HandlerQueryFunc(handler))
}

func (mux *QueryMux) QueryDNS(w RequestWriter, r *Msg) {
	h := mux.match(r.Question[0].Name)
	if h == nil {
		panic("dns: no handler found for " + r.Question[0].Name)
	}
	h.QueryDNS(w, r)
}

type Client struct {
	Net          string            // if "tcp" a TCP query will be initiated, otherwise an UDP one
	Attempts     int               // number of attempts
	Retry        bool              // retry with TCP
	ChannelQuery chan *Request     // read DNS request from this channel
	ChannelReply chan []*Msg       // read DNS request from this channel
	ReadTimeout  int64             // the net.Conn.SetReadTimeout value for new connections
	WriteTimeout int64             // the net.Conn.SetWriteTimeout value for new connections
	TsigSecret   map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>
	// LocalAddr string            // Local address to use
}

// NewClient creates a new client, with Net set to "udp" and Attempts to 1.
func NewClient() *Client {
	c := new(Client)
	c.Net = "udp"
	c.Attempts = 1
	c.ChannelReply = DefaultReplyChan
	return c
}

type Query struct {
	ChannelQuery chan *Request // read DNS request from this channel
	Handler      QueryHandler  // handler to invoke, dns.DefaultQueryMux if nil
}

func (q *Query) Query() os.Error {
	handler := q.Handler
	if handler == nil {
		handler = DefaultQueryMux
	}
	//forever:
	for {
		select {
		case in := <-q.ChannelQuery:
			w := new(reply)
			w.req = in.Request
			w.addr = in.Addr
			w.client = in.Client
			handler.QueryDNS(w, in.Request)
		}
	}
	return nil
}

func (q *Query) ListenAndQuery() os.Error {
	if q.ChannelQuery == nil {
		q.ChannelQuery = DefaultQueryChan
	}
	return q.Query()
}

// Start listener for firing off the queries. If
// c is nil DefaultQueryChan is used. If handler is nil
// DefaultQueryMux is used.
func ListenAndQuery(c chan *Request, handler QueryHandler) {
	q := &Query{ChannelQuery: c, Handler: handler}
	go q.ListenAndQuery()
}

func (w *reply) Write(m *Msg) {
	w.Client().ChannelReply <- []*Msg{w.req, m}
}

// Do performs an asynchronous query. The result is returned on the
// channel set in the c. If no channel is set DefaultQueryChan is used.
func (c *Client) Do(m *Msg, a string) {
	if c.ChannelQuery == nil {
		DefaultQueryChan <- &Request{Client: c, Addr: a, Request: m}
	} else {
		c.ChannelQuery <- &Request{Client: c, Addr: a, Request: m}
	}
}

// ExchangeBuf performs a synchronous query. It sends the buffer m to the
// address (net.Addr?) contained in a
func (c *Client) ExchangeBuffer(inbuf []byte, a string, outbuf []byte) bool {
	w := new(reply)
	w.client = c
	w.addr = a
	_, err := w.writeClient(inbuf)
//	defer w.closeClient() // XXX here?? what about TCP which should remain open
	if err != nil {
		println(err.String())
                w.closeClient()
		return false
	}

	// udp / tcp TODO
	n, err := w.readClient(outbuf)
	if err != nil {
                w.closeClient()
		return false
	}
                w.closeClient()
	outbuf = outbuf[:n]
        return true
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply.
func (c *Client) Exchange(m *Msg, a string) *Msg {
	out, ok := m.Pack()
	if !ok {
		panic("failed to pack message")
	}
        in := make([]byte, DefaultMsgSize)
        if ok := c.ExchangeBuffer(out, a, in); !ok {
                return nil
        }
	r := new(Msg)
	if ok := r.Unpack(in); !ok {
		return nil
	}
        return r
}

func (w *reply) WriteMessages(m []*Msg) {
	m1 := append([]*Msg{w.req}, m...)
	w.Client().ChannelReply <- m1
}

func (w *reply) Client() *Client {
	return w.client
}

func (w *reply) Request() *Msg {
	return w.req
}

func (w *reply) Receive() (*Msg, os.Error) {
	var p []byte
	m := new(Msg)
	switch w.Client().Net {
	case "tcp", "tcp4", "tcp6":
		p = make([]byte, MaxMsgSize)
	case "udp", "udp4", "udp6":
		p = make([]byte, DefaultMsgSize)
	}
	n, err := w.readClient(p)
	if err != nil {
		return nil, err
	}
	p = p[:n]
	if ok := m.Unpack(p); !ok {
		return nil, ErrUnpack
	}
	// Tsig
	if m.IsTsig() {
		secret := m.Extra[len(m.Extra)-1].(*RR_TSIG).Hdr.Name
		_, ok := w.Client().TsigSecret[secret]
		if !ok {
			return m, ErrNoSig
		}
		ok, err := TsigVerify(p, w.Client().TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
		if !ok {
			return m, err
		}
	}
	return m, nil
}

func (w *reply) readClient(p []byte) (n int, err os.Error) {
	if w.conn == nil {
		panic("no connection")
	}
	switch w.Client().Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 1 {
			return 0, io.ErrShortBuffer
		}
		n, err = w.conn.(*net.TCPConn).Read(p[0:2])
		if err != nil || n != 2 {
			return n, err
		}
		l, _ := unpackUint16(p[0:2], 0)
		if l == 0 {
			return 0, ErrShortRead
		}
		if int(l) > len(p) {
			return int(l), io.ErrShortBuffer
		}
		n, err = w.conn.(*net.TCPConn).Read(p[:l])
		if err != nil {
			return n, err
		}
		i := n
		for i < int(l) {
			j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)])
			if err != nil {
				return i, err
			}
			i += j
		}
		n = i
	case "udp", "udp4", "udp6":
		n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p)
		if err != nil {
			return n, err
		}
	}
	return
}

// Send a msg to the address specified in w.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (w *reply) Send(m *Msg) os.Error {
	if m.IsTsig() {
		secret := m.Extra[len(m.Extra)-1].(*RR_TSIG).Hdr.Name
		_, ok := w.Client().TsigSecret[secret]
		if !ok {
			return ErrNoSig
		}
		m, _ = TsigGenerate(m, w.Client().TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
		w.tsigRequestMAC = m.Extra[len(m.Extra)-1].(*RR_TSIG).MAC // Safe the requestMAC
	}
	out, ok := m.Pack()
	if !ok {
		return ErrPack
	}
	_, err := w.writeClient(out)
	if err != nil {
		return err
	}
	return nil
}

func (w *reply) writeClient(p []byte) (n int, err os.Error) {
	if w.Client().Attempts == 0 {
		panic("c.Attempts 0")
	}
	if w.Client().Net == "" {
		panic("c.Net empty")
	}

	conn, err := net.Dial(w.Client().Net, w.addr)
	if err != nil {
		return 0, err
	}
	w.conn = conn
	switch w.Client().Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 2 {
			return 0, io.ErrShortBuffer
		}
		for a := 0; a < w.Client().Attempts; a++ {
			l := make([]byte, 2)
			l[0], l[1] = packUint16(uint16(len(p)))
			n, err = conn.Write(l)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			if n != 2 {
				return n, io.ErrShortWrite
			}
			n, err = conn.Write(p)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			if i < len(p) {
				j, err := conn.Write(p[i:len(p)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our write...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "udp", "udp4", "udp6":
		for a := 0; a < w.Client().Attempts; a++ {
			n, err = conn.(*net.UDPConn).WriteTo(p, conn.RemoteAddr())
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return 0, err
			}
		}
	}
	return 0, nil
}

// UDP/TCP stuff
func (w *reply) closeClient() (err os.Error) {
	return w.conn.Close()
}
<commit_msg>put defer back in<commit_after>package dns

// A concurrent client implementation.
// Client sends query to a channel which
// will then handle the query. Returned replys
// are return on another channel. Ready for handling --- same
// setup for server - a HANDLER function that gets run
// when the query returns.

import (
	"os"
	"io"
	"net"
)

type QueryHandler interface {
	QueryDNS(w RequestWriter, q *Msg)
}

// The RequestWriter interface is used by a DNS query handler to
// construct a DNS request.
type RequestWriter interface {
	WriteMessages([]*Msg)
	Write(*Msg)
	Send(*Msg) os.Error
	Receive() (*Msg, os.Error)
}

// hijacked connections...?
type reply struct {
	client         *Client
	addr           string
	req            *Msg
	conn           net.Conn
	tsigRequestMAC string
	tsigTimersOnly bool
}

// A Request is a incoming message from a Client
type Request struct {
	Request *Msg
	Addr    string
	Client  *Client
}

// QueryMux is an DNS request multiplexer. It matches the
// zone name of each incoming request against a list of
// registered patterns add calls the handler for the pattern
// that most closely matches the zone name.
type QueryMux struct {
	m map[string]QueryHandler
}

// NewQueryMux allocates and returns a new QueryMux.
func NewQueryMux() *QueryMux { return &QueryMux{make(map[string]QueryHandler)} }

// DefaultQueryMux is the default QueryMux used by Query.
var DefaultQueryMux = NewQueryMux()

func newQueryChanSlice() chan []*Msg { return make(chan []*Msg) }
func newQueryChan() chan *Request    { return make(chan *Request) }

// Default channels to use for the resolver
var DefaultReplyChan = newQueryChanSlice()
var DefaultQueryChan = newQueryChan()

// The HandlerQueryFunc type is an adapter to allow the use of
// ordinary functions as DNS query handlers.  If f is a function
// with the appropriate signature, HandlerQueryFunc(f) is a
// QueryHandler object that calls f.
type HandlerQueryFunc func(RequestWriter, *Msg)

// QueryDNS calls f(w, reg)
func (f HandlerQueryFunc) QueryDNS(w RequestWriter, r *Msg) {
	go f(w, r)
}

func HandleQueryFunc(pattern string, handler func(RequestWriter, *Msg)) {
	DefaultQueryMux.HandleQueryFunc(pattern, handler)
}

// reusing zoneMatch from server.go
func (mux *QueryMux) match(zone string) QueryHandler {
	var h QueryHandler
	var n = 0
	for k, v := range mux.m {
		if !zoneMatch(k, zone) {
			continue
		}
		if h == nil || len(k) > n {
			n = len(k)
			h = v
		}
	}
	return h
}

func (mux *QueryMux) Handle(pattern string, handler QueryHandler) {
	if pattern == "" {
		panic("dns: invalid pattern " + pattern)
	}
	mux.m[pattern] = handler
}

func (mux *QueryMux) HandleQueryFunc(pattern string, handler func(RequestWriter, *Msg)) {
	mux.Handle(pattern, HandlerQueryFunc(handler))
}

func (mux *QueryMux) QueryDNS(w RequestWriter, r *Msg) {
	h := mux.match(r.Question[0].Name)
	if h == nil {
		panic("dns: no handler found for " + r.Question[0].Name)
	}
	h.QueryDNS(w, r)
}

type Client struct {
	Net          string            // if "tcp" a TCP query will be initiated, otherwise an UDP one
	Attempts     int               // number of attempts
	Retry        bool              // retry with TCP
	ChannelQuery chan *Request     // read DNS request from this channel
	ChannelReply chan []*Msg       // read DNS request from this channel
	ReadTimeout  int64             // the net.Conn.SetReadTimeout value for new connections
	WriteTimeout int64             // the net.Conn.SetWriteTimeout value for new connections
	TsigSecret   map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>
	// LocalAddr string            // Local address to use
}

// NewClient creates a new client, with Net set to "udp" and Attempts to 1.
func NewClient() *Client {
	c := new(Client)
	c.Net = "udp"
	c.Attempts = 1
	c.ChannelReply = DefaultReplyChan
	return c
}

type Query struct {
	ChannelQuery chan *Request // read DNS request from this channel
	Handler      QueryHandler  // handler to invoke, dns.DefaultQueryMux if nil
}

func (q *Query) Query() os.Error {
	handler := q.Handler
	if handler == nil {
		handler = DefaultQueryMux
	}
	//forever:
	for {
		select {
		case in := <-q.ChannelQuery:
			w := new(reply)
			w.req = in.Request
			w.addr = in.Addr
			w.client = in.Client
			handler.QueryDNS(w, in.Request)
		}
	}
	return nil
}

func (q *Query) ListenAndQuery() os.Error {
	if q.ChannelQuery == nil {
		q.ChannelQuery = DefaultQueryChan
	}
	return q.Query()
}

// Start listener for firing off the queries. If
// c is nil DefaultQueryChan is used. If handler is nil
// DefaultQueryMux is used.
func ListenAndQuery(c chan *Request, handler QueryHandler) {
	q := &Query{ChannelQuery: c, Handler: handler}
	go q.ListenAndQuery()
}

func (w *reply) Write(m *Msg) {
	w.Client().ChannelReply <- []*Msg{w.req, m}
}

// Do performs an asynchronous query. The result is returned on the
// channel set in the c. If no channel is set DefaultQueryChan is used.
func (c *Client) Do(m *Msg, a string) {
	if c.ChannelQuery == nil {
		DefaultQueryChan <- &Request{Client: c, Addr: a, Request: m}
	} else {
		c.ChannelQuery <- &Request{Client: c, Addr: a, Request: m}
	}
}

// ExchangeBuf performs a synchronous query. It sends the buffer m to the
// address (net.Addr?) contained in a
func (c *Client) ExchangeBuffer(inbuf []byte, a string, outbuf []byte) bool {
	w := new(reply)
	w.client = c
	w.addr = a
	_, err := w.writeClient(inbuf)
	defer w.closeClient() // XXX here?? what about TCP which should remain open
	if err != nil {
		println(err.String())
		return false
	}

	// udp / tcp TODO
	n, err := w.readClient(outbuf)
	if err != nil {
		return false
	}
	outbuf = outbuf[:n]
        return true
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply.
func (c *Client) Exchange(m *Msg, a string) *Msg {
	out, ok := m.Pack()
	if !ok {
		panic("failed to pack message")
	}
        in := make([]byte, DefaultMsgSize)
        if ok := c.ExchangeBuffer(out, a, in); !ok {
                return nil
        }
	r := new(Msg)
	if ok := r.Unpack(in); !ok {
		return nil
	}
        return r
}

func (w *reply) WriteMessages(m []*Msg) {
	m1 := append([]*Msg{w.req}, m...)
	w.Client().ChannelReply <- m1
}

func (w *reply) Client() *Client {
	return w.client
}

func (w *reply) Request() *Msg {
	return w.req
}

func (w *reply) Receive() (*Msg, os.Error) {
	var p []byte
	m := new(Msg)
	switch w.Client().Net {
	case "tcp", "tcp4", "tcp6":
		p = make([]byte, MaxMsgSize)
	case "udp", "udp4", "udp6":
		p = make([]byte, DefaultMsgSize)
	}
	n, err := w.readClient(p)
	if err != nil {
		return nil, err
	}
	p = p[:n]
	if ok := m.Unpack(p); !ok {
		return nil, ErrUnpack
	}
	// Tsig
	if m.IsTsig() {
		secret := m.Extra[len(m.Extra)-1].(*RR_TSIG).Hdr.Name
		_, ok := w.Client().TsigSecret[secret]
		if !ok {
			return m, ErrNoSig
		}
		ok, err := TsigVerify(p, w.Client().TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
		if !ok {
			return m, err
		}
	}
	return m, nil
}

func (w *reply) readClient(p []byte) (n int, err os.Error) {
	if w.conn == nil {
		panic("no connection")
	}
	switch w.Client().Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 1 {
			return 0, io.ErrShortBuffer
		}
		n, err = w.conn.(*net.TCPConn).Read(p[0:2])
		if err != nil || n != 2 {
			return n, err
		}
		l, _ := unpackUint16(p[0:2], 0)
		if l == 0 {
			return 0, ErrShortRead
		}
		if int(l) > len(p) {
			return int(l), io.ErrShortBuffer
		}
		n, err = w.conn.(*net.TCPConn).Read(p[:l])
		if err != nil {
			return n, err
		}
		i := n
		for i < int(l) {
			j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)])
			if err != nil {
				return i, err
			}
			i += j
		}
		n = i
	case "udp", "udp4", "udp6":
		n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p)
		if err != nil {
			return n, err
		}
	}
	return
}

// Send a msg to the address specified in w.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (w *reply) Send(m *Msg) os.Error {
	if m.IsTsig() {
		secret := m.Extra[len(m.Extra)-1].(*RR_TSIG).Hdr.Name
		_, ok := w.Client().TsigSecret[secret]
		if !ok {
			return ErrNoSig
		}
		m, _ = TsigGenerate(m, w.Client().TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
		w.tsigRequestMAC = m.Extra[len(m.Extra)-1].(*RR_TSIG).MAC // Safe the requestMAC
	}
	out, ok := m.Pack()
	if !ok {
		return ErrPack
	}
	_, err := w.writeClient(out)
	if err != nil {
		return err
	}
	return nil
}

func (w *reply) writeClient(p []byte) (n int, err os.Error) {
	if w.Client().Attempts == 0 {
		panic("c.Attempts 0")
	}
	if w.Client().Net == "" {
		panic("c.Net empty")
	}

	conn, err := net.Dial(w.Client().Net, w.addr)
	if err != nil {
		return 0, err
	}
	w.conn = conn
	switch w.Client().Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 2 {
			return 0, io.ErrShortBuffer
		}
		for a := 0; a < w.Client().Attempts; a++ {
			l := make([]byte, 2)
			l[0], l[1] = packUint16(uint16(len(p)))
			n, err = conn.Write(l)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			if n != 2 {
				return n, io.ErrShortWrite
			}
			n, err = conn.Write(p)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			if i < len(p) {
				j, err := conn.Write(p[i:len(p)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our write...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "udp", "udp4", "udp6":
		for a := 0; a < w.Client().Attempts; a++ {
			n, err = conn.(*net.UDPConn).WriteTo(p, conn.RemoteAddr())
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return 0, err
			}
		}
	}
	return 0, nil
}

// UDP/TCP stuff
func (w *reply) closeClient() (err os.Error) {
	return w.conn.Close()
}
<|endoftext|>


<commit_before>package kcl

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/kinesis"
	"github.com/aws/aws-sdk-go/service/kinesis/kinesisiface"
)

type Client struct {
	kinesis    kinesisiface.KinesisAPI
	distlock   Locker
	checkpoint Checkpointer
}

type Reader struct {
	records    <-chan *kinesis.Record
	checkpoint string
}

func New(awsKey, awsSecret, awsRegion string, distlock Locker, checkpoint Checkpointer) *Client {
	awsConfig := &aws.Config{
		Region: aws.String(awsRegion),
		Credentials: credentials.NewStaticCredentials(
			awsKey,
			awsSecret,
			"",
		),
	}

	return &Client{
		kinesis: kinesis.New(session.New(awsConfig)),
	}
}

func (c *Client) NewLockedShardReader(streamName, shard, checkpointName string) (*Reader, error) {
	// TODO
	return nil, nil
}

func (c *Client) PutRecord(streamName, partitionKey string, record []byte) error {
	// TODO
	return nil
}

func (r *Reader) SetCheckpoint() error {
	// TODO
	return nil
}
<commit_msg>put record, stream description<commit_after>package kcl

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/kinesis"
	"github.com/aws/aws-sdk-go/service/kinesis/kinesisiface"
)

type Client struct {
	kinesis    kinesisiface.KinesisAPI
	distlock   Locker
	checkpoint Checkpointer
}

type Reader struct {
	records    <-chan *kinesis.Record
	checkpoint string
}

func New(awsKey, awsSecret, awsRegion string, distlock Locker, checkpoint Checkpointer) *Client {
	awsConfig := &aws.Config{
		Region: aws.String(awsRegion),
		Credentials: credentials.NewStaticCredentials(
			awsKey,
			awsSecret,
			"",
		),
	}

	return &Client{
		kinesis: kinesis.New(session.New(awsConfig)),
	}
}

func (c *Client) NewLockedShardReader(streamName, shard, checkpointName string) (*Reader, error) {
	// TODO
	return nil, nil
}

func (c *Client) PutRecord(streamName, partitionKey string, record []byte) error {
	_, err := c.kinesis.PutRecord(&kinesis.PutRecordInput{
		Data:         record,
		StreamName:   aws.String(streamName),
		PartitionKey: aws.String(partitionKey),
	})
	if err != nil {
		return err
	}

	return nil
}

func (c *Client) StreamDescription(streamName string) (*kinesis.StreamDescription, error) {
	out, err := c.kinesis.DescribeStream(&kinesis.DescribeStreamInput{
		StreamName: aws.String(streamName),
	})
	if err != nil {
		return nil, err
	}

	return out.StreamDescription, nil
}

func (r *Reader) SetCheckpoint() error {
	// TODO
	return nil
}
<|endoftext|>