<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"
)

const (
	indexHTML = `<!DOCTYPE html>

		<title>Welcome to my service</title>

Welcome!

A simple 12-factor app showing some environment configuration.

Overview

Typically, this application is run in multiple hosting providers, with different values for PROVIDER in each provider.

This makes it possible to front the application with a CDN / load-balancer and see different values coming back depending on which origin served the request.

It also allows you to demonstrate zero-downtime failover of the CDN/load-balancer, if suitably configured.

Selected environment variables

					{{range .}}

							{{.Key}} : {{.Value}}

					{{end}}

Support links

Source

Built by James Abley

`
)

// KeyValue makes the ENV vars into a first-class data structure
type KeyValue struct {
	Key   string
	Value string
}

// KeyValues is a shorter way of referencing an array
type KeyValues []*KeyValue

var (
	tmpl = template.Must(template.New("index.html").Parse(indexHTML))
	body []byte
)

func main() {
	flag.Parse()

	port := getDefaultConfig("PORT", "8080")

	values := getKeyValues()
	var b bytes.Buffer
	if err := tmpl.Execute(&b, values); err != nil {
		panic(err)
	}
	body = b.Bytes()

	serveMux := http.NewServeMux()

	serveMux.HandleFunc("/", mainHandler)
	serveMux.HandleFunc("/_status", statusHandler)

	srv := &http.Server{
		Addr: ":"+port,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		// New for Go 1.8
		// IdleTimeout:  120 * time.Second,
		Handler: serveMux,
	}

	errorChan := make(chan error, 1)
	signalChan := make(chan os.Signal, 1)

	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		errorChan <- srv.ListenAndServe()
	}()

	for {
		select {
		case err := <-errorChan:
			if err != nil {
				log.Fatal(err)
			}
		case s := <-signalChan:
			log.Println(fmt.Sprintf("Captured %v. Exiting ...", s))
			// TOOD(jabley): shut down HTTP server cleanly
			os.Exit(0)
		}
	}
}

func getDefaultConfig(name, fallback string) string {
	if val := os.Getenv(name); val != "" {
		return val
	}
	return fallback
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	w.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")
	w.Write(body)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")
	w.WriteHeader(http.StatusOK)
	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	json.NewEncoder(w).Encode(mem)
}

func getKeyValues() KeyValues {
	result := make(KeyValues, 2)
	result[0] = &KeyValue{"PORT", os.Getenv("PORT")}
	result[1] = &KeyValue{"PROVIDER", os.Getenv("PROVIDER")}
	return result
}

func newKeyValue(kv string) *KeyValue {
	s := strings.Split(kv, "=")
	return &KeyValue{Key: s[0], Value: s[1]}
}
<commit_msg>Add new Go 1.8 production hardening<commit_after>package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"
)

const (
	indexHTML = `<!DOCTYPE html>

		<title>Welcome to my service</title>

Welcome!

A simple 12-factor app showing some environment configuration.

Overview

Typically, this application is run in multiple hosting providers, with different values for PROVIDER in each provider.

This makes it possible to front the application with a CDN / load-balancer and see different values coming back depending on which origin served the request.

It also allows you to demonstrate zero-downtime failover of the CDN/load-balancer, if suitably configured.

Selected environment variables

					{{range .}}

							{{.Key}} : {{.Value}}

					{{end}}

Support links

Source

Built by James Abley

`
)

// KeyValue makes the ENV vars into a first-class data structure
type KeyValue struct {
	Key   string
	Value string
}

// KeyValues is a shorter way of referencing an array
type KeyValues []*KeyValue

var (
	tmpl = template.Must(template.New("index.html").Parse(indexHTML))
	body []byte
)

func main() {
	flag.Parse()

	port := getDefaultConfig("PORT", "8080")

	values := getKeyValues()
	var b bytes.Buffer
	if err := tmpl.Execute(&b, values); err != nil {
		panic(err)
	}
	body = b.Bytes()

	serveMux := http.NewServeMux()

	serveMux.HandleFunc("/", mainHandler)
	serveMux.HandleFunc("/_status", statusHandler)

	srv := &http.Server{
		Addr:         ":" + port,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
		Handler:      serveMux,
	}

	errorChan := make(chan error, 1)
	signalChan := make(chan os.Signal, 1)

	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		errorChan <- srv.ListenAndServe()
	}()

	for {
		select {
		case err := <-errorChan:
			if err != nil {
				log.Fatal(err)
			}
		case s := <-signalChan:
			log.Println(fmt.Sprintf("Captured %v. Exiting ...", s))
			d := time.Now().Add(1 * time.Second)
			ctx, cancel := context.WithDeadline(context.Background(), d)
			defer cancel()
			srv.Shutdown(ctx)
			os.Exit(0)
		}
	}
}

func getDefaultConfig(name, fallback string) string {
	if val := os.Getenv(name); val != "" {
		return val
	}
	return fallback
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	w.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")
	w.Write(body)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")
	w.WriteHeader(http.StatusOK)
	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	json.NewEncoder(w).Encode(mem)
}

func getKeyValues() KeyValues {
	result := make(KeyValues, 2)
	result[0] = &KeyValue{"PORT", os.Getenv("PORT")}
	result[1] = &KeyValue{"PROVIDER", os.Getenv("PROVIDER")}
	return result
}

func newKeyValue(kv string) *KeyValue {
	s := strings.Split(kv, "=")
	return &KeyValue{Key: s[0], Value: s[1]}
}
<|endoftext|>


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

package lease

import (
	"container/list"

	"github.com/jacobsa/gcloud/syncutil"
)

// A type that manages read and read/write leases for anonymous temporary files.
//
// Safe for concurrent access. Must be created with NewFileLeaser.
type FileLeaser struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	dir   string
	limit int64

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A lock that guards the mutable state in this struct, which must not be
	// held for any blocking operation.
	//
	// LOCK ORDERING
	// -------------
	//
	// Define our strict partial order < as follows:
	//
	//  1. For any two leases L1 and L2 with L1.Id < L2.Id, L1.Mu < L2.Mu.
	//  2. For any lease L, L.Mu < leaser.mu
	//
	mu syncutil.InvariantMutex

	// The unique ID to hand out for the next lease issued.
	nextID uint64

	// The current estimated total size of outstanding read/write leases. This is
	// only an estimate because each time a read/write lease is updated, the
	// updater drops the lock, acquires the FS lock, then adds the delta here.
	// This saves us from needed to serialize I/O through distinct files.
	readWriteOutstanding int64

	// All outstanding read leases, ordered by recency of use.
	//
	// INVARIANT: Each element is of type *readLease
	// INVARIANT: For each x, x.Id < nextID
	readLeases list.List

	// Index of read leases by ID.
	//
	// INVARIANT: For each k, v: v.Value.(*readLease) == k
	// INVARIANT: Contains all and only the lements of readLeases
	readLeasesIndex map[*readLease]*list.Element
}

// Create a new file leaser that uses the supplied directory for temporary
// files (before unlinking them) and attempts to keep usage in bytes below the
// given limit. If dir is empty, the system default will be used.
//
// Usage may exceed the given limit if there are read/write leases whose total
// size exceeds the limit, since such leases cannot be revoked.
func NewFileLeaser(
	dir string,
	limitBytes int64) (fl *FileLeaser) {
	fl = &FileLeaser{
		dir:   dir,
		limit: limitBytes,
	}

	fl.mu = syncutil.NewInvariantMutex(fl.checkInvariants)

	return
}

// Create a new anonymous file, and return a read/write lease for it. The
// read/write lease will pin resources until rwl.Downgrade is called. It need
// not be called if the process is exiting.
func (fl *FileLeaser) New() (rwl ReadWriteLease) {
	panic("TODO")
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (fl *FileLeaser) checkInvariants() {
	panic("TODO")
}
<commit_msg>g cm Updated a comment; we can hold the lock actually.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"container/list"

	"github.com/jacobsa/gcloud/syncutil"
)

// A type that manages read and read/write leases for anonymous temporary files.
//
// Safe for concurrent access. Must be created with NewFileLeaser.
type FileLeaser struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	dir   string
	limit int64

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A lock that guards the mutable state in this struct, which must not be
	// held for any blocking operation.
	//
	// LOCK ORDERING
	// -------------
	//
	// Define our strict partial order < as follows:
	//
	//  1. For any two leases L1 and L2 with L1.Id < L2.Id, L1.Mu < L2.Mu.
	//  2. For any lease L, L.Mu < leaser.mu
	//
	mu syncutil.InvariantMutex

	// The unique ID to hand out for the next lease issued.
	nextID uint64

	// The current estimated total size of outstanding read/write leases. This is
	// only an estimate because we can't synchronize its update with a call to
	// the wrapped file to e.g. write or truncate.
	readWriteOutstanding int64

	// All outstanding read leases, ordered by recency of use.
	//
	// INVARIANT: Each element is of type *readLease
	// INVARIANT: For each x, x.Id < nextID
	readLeases list.List

	// Index of read leases by ID.
	//
	// INVARIANT: For each k, v: v.Value.(*readLease) == k
	// INVARIANT: Contains all and only the lements of readLeases
	readLeasesIndex map[*readLease]*list.Element
}

// Create a new file leaser that uses the supplied directory for temporary
// files (before unlinking them) and attempts to keep usage in bytes below the
// given limit. If dir is empty, the system default will be used.
//
// Usage may exceed the given limit if there are read/write leases whose total
// size exceeds the limit, since such leases cannot be revoked.
func NewFileLeaser(
	dir string,
	limitBytes int64) (fl *FileLeaser) {
	fl = &FileLeaser{
		dir:   dir,
		limit: limitBytes,
	}

	fl.mu = syncutil.NewInvariantMutex(fl.checkInvariants)

	return
}

// Create a new anonymous file, and return a read/write lease for it. The
// read/write lease will pin resources until rwl.Downgrade is called. It need
// not be called if the process is exiting.
func (fl *FileLeaser) New() (rwl ReadWriteLease) {
	panic("TODO")
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (fl *FileLeaser) checkInvariants() {
	panic("TODO")
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/GeertJohan/go.rice"
	"github.com/codegangsta/negroni"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/lann/squirrel"
	"log"
	"net/http"
	"runtime"
	"strconv"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func fetchUsers(db *sql.DB) []User {
	dbUsers, err := db.Query("SELECT * FROM user")
	if nil != err {
		log.Fatal(err)
	}
	defer dbUsers.Close()

	var users []User
	for dbUsers.Next() {
		var id int
		var name string
		var age int
		if dbUsers.Scan(&id, &name, &age); nil != err {
			log.Fatal(err)
		}

		dbUser := User{id, name, age}

		users = append(users, dbUser)
	}

	return users
}

func seedUsers(db *sql.DB) {
	users := []User{
		{0, "Brian", 19},
		{0, "Thomas", 32},
		{0, "Tonny", 99},
	}

	_, err := db.Exec("CREATE TABLE IF NOT EXISTS user (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), age INT)")
	if nil != err {
		log.Fatal(err)
	}

	log.Println("Inserting...")
	for _, user := range users {
		_, err = squirrel.Insert("user").Columns("name", "age").Values(user.Name, user.Age).RunWith(db).Exec()
		if nil != err {
			log.Fatal(err)
		}
	}
}

func toJson(data interface{}) string {
	json, _ := json.MarshalIndent(data, "", "  ")
	return string(json)
}

func main() {
	log.Println("Starting...")
	runtime.GOMAXPROCS(runtime.NumCPU())
	var err error

	// DB conn
	db, err := sql.Open("mysql", "hello_go@/hello_go")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	seedUsers(db)

	r := mux.NewRouter()

	r.Methods("GET", "HEAD").Path("/data").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		users := fetchUsers(db)
		w.Header()["content-type"] = []string{"application/json"}
		w.Header()["x-count"] = []string{strconv.FormatInt(int64(len(users)), 10)}
		if len(users) == 0 {
			w.Write([]byte("[]"))
		} else {
			fmt.Fprint(w, toJson(users))
		}
	})

	r.Methods("GET", "HEAD").Path("/data/{id}").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		vars := mux.Vars(req)
		id, _ := strconv.ParseInt(vars["id"], 10, 0)

		if id <= 0 {
			http.NotFound(w, req)
			return
		}

		row := squirrel.Select("name", "age").From("user").Where(squirrel.Eq{"id": id}).RunWith(db).QueryRow()

		var name string
		var age int
		if err = row.Scan(&name, &age); err != nil {
			log.Println(err)
			http.NotFound(w, req)
			return
		}

		w.Header()["content-type"] = []string{"application/json"}
		fmt.Fprint(w, toJson(User{int(id), name, age}))
	})

	r.Methods("DELETE").Path("/data").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		log.Println("Deleting...")
		_, err := squirrel.Delete("user").RunWith(db).Exec()
		if nil != err {
			log.Println(err)
		}
		w.WriteHeader(http.StatusNoContent)
		fmt.Fprint(w, "[]")
	})

	r.Methods("POST").Path("/data").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		log.Println("Seeding...")
		seedUsers(db)
		w.WriteHeader(http.StatusCreated)
	})

	n := negroni.New(
		negroni.NewRecovery(),
		negroni.NewLogger(),
		negroni.NewStatic(rice.MustFindBox("public").HTTPBox()),
	)
	n.UseHandler(r)
	n.Run(":3000")
}
<commit_msg>Listen from LISTEN env var<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/GeertJohan/go.rice"
	"github.com/codegangsta/negroni"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"github.com/lann/squirrel"
	"log"
	"net/http"
	"os"
	"runtime"
	"strconv"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func fetchUsers(db *sql.DB) []User {
	dbUsers, err := db.Query("SELECT * FROM user")
	if nil != err {
		log.Fatal(err)
	}
	defer dbUsers.Close()

	var users []User
	for dbUsers.Next() {
		var id int
		var name string
		var age int
		if dbUsers.Scan(&id, &name, &age); nil != err {
			log.Fatal(err)
		}

		dbUser := User{id, name, age}

		users = append(users, dbUser)
	}

	return users
}

func seedUsers(db *sql.DB) {
	users := []User{
		{0, "Brian", 19},
		{0, "Thomas", 32},
		{0, "Tonny", 99},
	}

	_, err := db.Exec("CREATE TABLE IF NOT EXISTS user (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), age INT)")
	if nil != err {
		log.Fatal(err)
	}

	log.Println("Inserting...")
	for _, user := range users {
		_, err = squirrel.Insert("user").Columns("name", "age").Values(user.Name, user.Age).RunWith(db).Exec()
		if nil != err {
			log.Fatal(err)
		}
	}
}

func toJson(data interface{}) string {
	json, _ := json.MarshalIndent(data, "", "  ")
	return string(json)
}

func main() {
	log.Println("Starting...")
	runtime.GOMAXPROCS(runtime.NumCPU())
	var err error

	// DB conn
	db, err := sql.Open("mysql", "hello_go@/hello_go")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	seedUsers(db)

	r := mux.NewRouter()

	r.Methods("GET", "HEAD").Path("/data").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		users := fetchUsers(db)
		w.Header()["content-type"] = []string{"application/json"}
		w.Header()["x-count"] = []string{strconv.FormatInt(int64(len(users)), 10)}
		if len(users) == 0 {
			w.Write([]byte("[]"))
		} else {
			fmt.Fprint(w, toJson(users))
		}
	})

	r.Methods("GET", "HEAD").Path("/data/{id}").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		vars := mux.Vars(req)
		id, _ := strconv.ParseInt(vars["id"], 10, 0)

		if id <= 0 {
			http.NotFound(w, req)
			return
		}

		row := squirrel.Select("name", "age").From("user").Where(squirrel.Eq{"id": id}).RunWith(db).QueryRow()

		var name string
		var age int
		if err = row.Scan(&name, &age); err != nil {
			log.Println(err)
			http.NotFound(w, req)
			return
		}

		w.Header()["content-type"] = []string{"application/json"}
		fmt.Fprint(w, toJson(User{int(id), name, age}))
	})

	r.Methods("DELETE").Path("/data").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		log.Println("Deleting...")
		_, err := squirrel.Delete("user").RunWith(db).Exec()
		if nil != err {
			log.Println(err)
		}
		w.WriteHeader(http.StatusNoContent)
		fmt.Fprint(w, "[]")
	})

	r.Methods("POST").Path("/data").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		log.Println("Seeding...")
		seedUsers(db)
		w.WriteHeader(http.StatusCreated)
	})

	n := negroni.New(
		negroni.NewRecovery(),
		negroni.NewLogger(),
		negroni.NewStatic(rice.MustFindBox("public").HTTPBox()),
	)
	n.UseHandler(r)
	listen := os.Getenv("LISTEN")
	if listen == "" {
		listen = ":3000"
	}
	n.Run(listen)
}
<|endoftext|>


<commit_before>package router

import (
	"testing"
)

func TestNewRule(t *testing.T) {
	var newRuleTest = []string{
		`/`,
		`/foo`,
		`/foo/bar`,
		`/foo/<bar>`,
		`/foo/<bar>/baz`,
		`/foo/<bar:int>`,
		`/foo/<bar:int(digits=4)>`,
	}

	for i, path := range newRuleTest {
		rule, err := NewRule(path)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}

		if rule.path != path {
			t.Errorf("%d rule.path have %v, want %v", i, rule.path, path)
		}
	}
}

func TestNewRuleError(t *testing.T) {
	_, err := NewRule(`path`)
	if err != ErrLeadingSlash {
		t.Fatalf("unexpected error\nhave %v\nwant %v", err, ErrLeadingSlash)
	}
}

func TestCompile(t *testing.T) {
	var compileTest = []struct {
		in  string
		out string
	}{
		{`/`, `^/$`},
		{`/foo`, `^/foo$`},
		{`/foo/bar`, `^/foo/bar$`},
		{`/foo/<bar>`, `^/foo/(?P<bar>[^/]{1,})$`},
		{`/foo/<bar>/baz`, `^/foo/(?P<bar>[^/]{1,})/baz$`},
		{`/foo/<bar:int>`, `^/foo/(?P<bar>\d+)$`},
		{`/foo/<bar:int(digits=4)>`, `^/foo/(?P<bar>\d+)$`},
	}

	for i, tt := range compileTest {
		router := New()
		rule, err := NewRule(tt.in)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}

		err = rule.bind(router)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}

		if rule.regexp == nil {
			t.Errorf("%d. rule.regexp\nhave %v\nwant %v", i, nil, tt.out)
			continue
		}
		if regexp := rule.regexp.String(); regexp != tt.out {
			t.Errorf("%d. rule.regexp\nhave %v\nwant %v", i, rule.regexp, tt.out)
		}
	}
}

func TestCompileUnbound(t *testing.T) {
	rule, err := NewRule(`/`)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}

	err = rule.compile()
	if err != ErrUnbound {
		t.Errorf("rule.compile\nhave %v\nwant %v", err, ErrUnbound)
	}
}

func TestCompileErrors(t *testing.T) {
	var compileTests = []struct {
		path string
		err  error
	}{
		{`/<>`, ErrVariableEmpty},
		{`/<foo`, ErrVariableOpen},
		{`/<foo>/<foo>`, ErrVariableDuplicate},
		{`/<foo:int(>`, ErrConverterOpen},
		{`/<foo:int()>`, nil},
		{`/<foo:int(digits)>`, ErrArguments},
		{`/<foo:int(digits=)>`, ErrArguments},
	}

	router := New()
	for i, tt := range compileTests {
		rule, err := NewRule(tt.path)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}
		err = rule.bind(router)
		if err != tt.err {
			t.Errorf("%d. rule.compile\nhave %v\nwant %v", i, err, tt.err)
		}
	}
}
<commit_msg>Add extra compile error test.<commit_after>package router

import (
	"testing"
)

func TestNewRule(t *testing.T) {
	var newRuleTest = []string{
		`/`,
		`/foo`,
		`/foo/bar`,
		`/foo/<bar>`,
		`/foo/<bar>/baz`,
		`/foo/<bar:int>`,
		`/foo/<bar:int(digits=4)>`,
	}

	for i, path := range newRuleTest {
		rule, err := NewRule(path)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}

		if rule.path != path {
			t.Errorf("%d rule.path have %v, want %v", i, rule.path, path)
		}
	}
}

func TestNewRuleError(t *testing.T) {
	_, err := NewRule(`path`)
	if err != ErrLeadingSlash {
		t.Fatalf("unexpected error\nhave %v\nwant %v", err, ErrLeadingSlash)
	}
}

func TestCompile(t *testing.T) {
	var compileTest = []struct {
		in  string
		out string
	}{
		{`/`, `^/$`},
		{`/foo`, `^/foo$`},
		{`/foo/bar`, `^/foo/bar$`},
		{`/foo/<bar>`, `^/foo/(?P<bar>[^/]{1,})$`},
		{`/foo/<bar>/baz`, `^/foo/(?P<bar>[^/]{1,})/baz$`},
		{`/foo/<bar:int>`, `^/foo/(?P<bar>\d+)$`},
		{`/foo/<bar:int(digits=4)>`, `^/foo/(?P<bar>\d+)$`},
	}

	for i, tt := range compileTest {
		router := New()
		rule, err := NewRule(tt.in)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}

		err = rule.bind(router)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}

		if rule.regexp == nil {
			t.Errorf("%d. rule.regexp\nhave %v\nwant %v", i, nil, tt.out)
			continue
		}
		if regexp := rule.regexp.String(); regexp != tt.out {
			t.Errorf("%d. rule.regexp\nhave %v\nwant %v", i, rule.regexp, tt.out)
		}
	}
}

func TestCompileUnbound(t *testing.T) {
	rule, err := NewRule(`/`)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}

	err = rule.compile()
	if err != ErrUnbound {
		t.Errorf("rule.compile\nhave %v\nwant %v", err, ErrUnbound)
	}
}

func TestCompileErrors(t *testing.T) {
	var compileTests = []struct {
		path string
		err  error
	}{
		{`/<>`, ErrVariableEmpty},
		{`/<foo`, ErrVariableOpen},
		{`/<foo>/<foo>`, ErrVariableDuplicate},
		{`/<foo:int(>`, ErrConverterOpen},
		{`/<foo:int()>`, nil},
		{`/<foo:int((>`, ErrConverterOpen},
		{`/<foo:int(digits)>`, ErrArguments},
		{`/<foo:int(digits=)>`, ErrArguments},
	}

	router := New()
	for i, tt := range compileTests {
		rule, err := NewRule(tt.path)
		if err != nil {
			t.Errorf("%d. unexpected error: %v", i, err)
			continue
		}
		err = rule.bind(router)
		if err != tt.err {
			t.Errorf("%d. rule.compile\nhave %v\nwant %v", i, err, tt.err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
  "crypto"
  "crypto/rand"
  "crypto/sha256"
  "errors"
  "flag"
  "fmt"
  "io"
  "os"
//  "crypto/ecdsa"
//  "crypto/rsa"
)

type (
  SignInputFlags struct {
    Message        string // the message to sign
    PrivateKeyPath string // path to the private key
    Output         string // a path or stream to output the private key to

    private_key crypto.Signer
    output_stream io.Writer // the output stream for the CSR
  }
)

func sign_input() {
  flags := parse_sign_input_flags()
  flags.private_key = load_private_key(flags.PrivateKeyPath)

  output_stream, err := open_output_stream(flags.Output)
  if err != nil {
    crash_with_help(2, fmt.Sprintf("Error when creating file %s: %s", flags.Output, err))
  }
  flags.output_stream = output_stream
  defer output_stream.Close()

  if err := create_signature(flags); err != nil {
    fmt.Fprintln(os.Stderr, "Error when creating signature", err)
    os.Exit(3)
  }
}

func parse_sign_input_flags() SignInputFlags {
  flags := SignInputFlags{}
  fs := flag.NewFlagSet("sign-input", flag.ExitOnError)
  fs.StringVar(&flags.PrivateKeyPath, "private-key", "", "path to the private key file")
  fs.StringVar(&flags.Output, "output", "STDOUT", "path where the generated signature should be stored")
  fs.StringVar(&flags.Message, "message", "", "the message to sign")
  fs.Parse(os.Args[2:])

  return flags
}

func create_signature(flags SignInputFlags) error {
  message := []byte(flags.Message)
  // compute sha256 of the message
  hash := sha256.New()
  length, _ := hash.Write(message)
  if length != len(message) { return errors.New("Error when creating hash over message!") }
  fmt.Println(hash.Sum(nil))

  // create signature of the hash using the private key
  signature, err := flags.private_key.Sign(
    rand.Reader,
    hash.Sum([]byte("")),
    nil,
  )
  if err != nil { return err }
  fmt.Println(signature)
  flags.output_stream.Write(signature)
  return nil
}
<commit_msg>remove debug output<commit_after>package main

import (
  "crypto"
  "crypto/rand"
  "crypto/sha256"
  "errors"
  "flag"
  "fmt"
  "io"
  "os"
//  "crypto/ecdsa"
//  "crypto/rsa"
)

type (
  SignInputFlags struct {
    Message        string // the message to sign
    PrivateKeyPath string // path to the private key
    Output         string // a path or stream to output the private key to

    private_key crypto.Signer
    output_stream io.Writer // the output stream for the CSR
  }
)

func sign_input() {
  flags := parse_sign_input_flags()
  flags.private_key = load_private_key(flags.PrivateKeyPath)

  output_stream, err := open_output_stream(flags.Output)
  if err != nil {
    crash_with_help(2, fmt.Sprintf("Error when creating file %s: %s", flags.Output, err))
  }
  flags.output_stream = output_stream
  defer output_stream.Close()

  if err := create_signature(flags); err != nil {
    fmt.Fprintln(os.Stderr, "Error when creating signature", err)
    os.Exit(3)
  }
}

func parse_sign_input_flags() SignInputFlags {
  flags := SignInputFlags{}
  fs := flag.NewFlagSet("sign-input", flag.ExitOnError)
  fs.StringVar(&flags.PrivateKeyPath, "private-key", "", "path to the private key file")
  fs.StringVar(&flags.Output, "output", "STDOUT", "path where the generated signature should be stored")
  fs.StringVar(&flags.Message, "message", "", "the message to sign")
  fs.Parse(os.Args[2:])

  return flags
}

func create_signature(flags SignInputFlags) error {
  message := []byte(flags.Message)
  // compute sha256 of the message
  hash := sha256.New()
  length, _ := hash.Write(message)
  if length != len(message) { return errors.New("Error when creating hash over message!") }

  // create signature of the hash using the private key
  signature, err := flags.private_key.Sign(
    rand.Reader,
    hash.Sum([]byte("")),
    nil,
  )
  if err != nil { return err }
  flags.output_stream.Write(signature)
  return nil
}
<|endoftext|>


<commit_before>package helpers

import (
	"fmt"
)

const (
	// PrestVersionNumber Major and minor version.
	PrestVersionNumber = 0.3

	// PrestPatchVersion Increment this for bug releases
	PrestPatchVersion = 0
)

var (
	// CommitHash for version
	CommitHash string
)

// PrestReleaseVersion is same as pREST Version.
func PrestReleaseVersion() string {
	return fmt.Sprintf("%.2g.%d", PrestVersionNumber, PrestPatchVersion)
}
<commit_msg>change way to show version so goreleaser can understand<commit_after>package helpers

var (
	// PrestVersionNumber repesemts prest version.
	PrestVersionNumber = "0.3.0"
	// CommitHash for version
	CommitHash string
)

// PrestReleaseVersion is same as pREST Version.
func PrestReleaseVersion() string {
	return PrestVersionNumber
}
<|endoftext|>


<commit_before>package main

import (
	"sort"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
)

func init() {
	register("ps", runPs, `
usage: flynn ps [-a]

List flynn jobs.

Options:
  -a, --all      Show all jobs (default is running and pending)

Example:

	$ flynn ps
	ID                                         TYPE  STATE    RELEASE
	318810fb-4679-419b-aed4-b0838c71c0eb       web   pending  d2ab4264-a647-4dc2-ac8d-d5821a475962
	5bc89fe1-d4b5-4021-a337-10dd2b391358       web   pending  d2ab4264-a647-4dc2-ac8d-d5821a475962
	host-93612073-f06e-41d9-bdae-df3e45f8a11d  web   up       d2ab4264-a647-4dc2-ac8d-d5821a475962
	host-53dba8f4-561b-460b-b75b-677e8b6660fb  web   up       d2ab4264-a647-4dc2-ac8d-d5821a475962

	$ flynn ps --all
	ID                                         TYPE  STATE    RELEASE
	318810fb-4679-419b-aed4-b0838c71c0eb       web   pending  d2ab4264-a647-4dc2-ac8d-d5821a475962
	5bc89fe1-d4b5-4021-a337-10dd2b391358       web   pending  d2ab4264-a647-4dc2-ac8d-d5821a475962
	host-93612073-f06e-41d9-bdae-df3e45f8a11d  web   up       d2ab4264-a647-4dc2-ac8d-d5821a475962
	host-53dba8f4-561b-460b-b75b-677e8b6660fb  web   up       d2ab4264-a647-4dc2-ac8d-d5821a475962
	host-0f0472ad-272d-4bf3-a873-bc542cf16e31  web   down     d2ab4264-a647-4dc2-ac8d-d5821a475962
	host-12657e64-adb9-4121-904f-4896231f3bc4  web   down     d2ab4264-a647-4dc2-ac8d-d5821a475962
	host-c7f5d522-974d-4be1-8103-49f7eb309cb5  web   down     1a675dd8-9c44-468b-bef0-b3f8a25f5bdc
	host-380c5ea6-d503-454e-a2a4-6b20dc9f4b81  web   down     1a675dd8-9c44-468b-bef0-b3f8a25f5bdc
	host-cb4fed31-d84d-45d9-a566-c18907101f6f  web   down     1a675dd8-9c44-468b-bef0-b3f8a25f5bdc
	host-9af9b2a9-1c53-4616-985a-e81da943fb95  web   down     1a675dd8-9c44-468b-bef0-b3f8a25f5bdc
`)
}

func runPs(args *docopt.Args, client *controller.Client) error {
	jobs, err := client.JobList(mustApp())
	if err != nil {
		return err
	}
	sort.Sort(jobsByType(jobs))

	w := tabWriter()
	defer w.Flush()

	listRec(w, "ID", "TYPE", "STATE", "RELEASE")
	for _, j := range jobs {
		if j.Type == "" {
			j.Type = "run"
		}
		if !args.Bool["--all"] && j.State != ct.JobStateUp && j.State != ct.JobStatePending {
			continue
		}
		id := j.ID
		if id == "" {
			id = j.UUID
		}
		listRec(w, id, j.Type, j.State, j.ReleaseID)
	}

	return nil
}

type jobsByType []*ct.Job

func (p jobsByType) Len() int           { return len(p) }
func (p jobsByType) Less(i, j int) bool { return p[i].Type < p[j].Type }
func (p jobsByType) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
<commit_msg>cli: Sort jobs in chronological order, add created column<commit_after>package main

import (
	"sort"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/docker/docker/pkg/units"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
)

func init() {
	register("ps", runPs, `
usage: flynn ps [-a]

List flynn jobs.

Options:
  -a, --all      Show all jobs (default is running and pending)

Example:

	$ flynn ps
	ID                                         TYPE  STATE    CREATED         RELEASE
	host-f25797dc-c956-4337-89af-d49eff50f58e  web   up       14 seconds ago  1b1db8ef-ba4d-4314-85c1-d5895a44b27e
	6ec25d6e-2985-4807-8e64-02dc23c348bc       web   pending  7 seconds ago   1b1db8ef-ba4d-4314-85c1-d5895a44b27e
	ab14754c-73b7-4212-a6d9-73b825587fd2       web   pending  2 seconds ago   1b1db8ef-ba4d-4314-85c1-d5895a44b27e

	$ flynn ps --all
	ID                                         TYPE  STATE    CREATED             RELEASE
	host-d84dc657-83b8-4a62-aab8-ad97bb994761  web   down     2 minutes ago       cd698657-2955-4fa4-bc2f-8714b218a7a2
	host-feaff633-a37b-4565-9ade-24bae0cfae03  web   down     About a minute ago  cd698657-2955-4fa4-bc2f-8714b218a7a2
	host-e8b4f9be-e422-481f-928c-4c82f0bb5e8b  web   down     About a minute ago  cd698657-2955-4fa4-bc2f-8714b218a7a2
	host-95747b4f-fdcd-4c44-ab72-b3d9609668e4  run   down     54 seconds ago      cd698657-2955-4fa4-bc2f-8714b218a7a2
	host-cef95d8c-a632-4ae6-8a57-e13cbc24afa9  web   down     25 seconds ago      1b1db8ef-ba4d-4314-85c1-d5895a44b27e
	host-ef6249e3-b463-4fea-a7dd-b1302872f821  web   down     20 seconds ago      1b1db8ef-ba4d-4314-85c1-d5895a44b27e
	host-f25797dc-c956-4337-89af-d49eff50f58e  web   up       14 seconds ago      1b1db8ef-ba4d-4314-85c1-d5895a44b27e
	6ec25d6e-2985-4807-8e64-02dc23c348bc       web   pending  7 seconds ago       1b1db8ef-ba4d-4314-85c1-d5895a44b27e
	ab14754c-73b7-4212-a6d9-73b825587fd2       web   pending  2 seconds ago       1b1db8ef-ba4d-4314-85c1-d5895a44b27e
`)
}

func runPs(args *docopt.Args, client *controller.Client) error {
	jobs, err := client.JobList(mustApp())
	if err != nil {
		return err
	}
	sort.Sort(sortJobs(jobs))

	w := tabWriter()
	defer w.Flush()

	listRec(w, "ID", "TYPE", "STATE", "CREATED", "RELEASE")
	for _, j := range jobs {
		if j.Type == "" {
			j.Type = "run"
		}
		if !args.Bool["--all"] && j.State != ct.JobStateUp && j.State != ct.JobStatePending {
			continue
		}
		id := j.ID
		if id == "" {
			id = j.UUID
		}
		var created string
		if j.CreatedAt != nil {
			created = units.HumanDuration(time.Now().UTC().Sub(*j.CreatedAt)) + " ago"
		}
		listRec(w, id, j.Type, j.State, created, j.ReleaseID)
	}

	return nil
}

// sortJobs sorts Jobs in chronological order based on their CreatedAt time
type sortJobs []*ct.Job

func (s sortJobs) Len() int { return len(s) }
func (s sortJobs) Less(i, j int) bool {
	return s[i].CreatedAt == nil || s[j].CreatedAt != nil && (*s[j].CreatedAt).Sub(*s[i].CreatedAt) > 0
}
func (s sortJobs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
<|endoftext|>


<commit_before>package main

import (
	"sort"
	"strings"
	"time"

	"github.com/docker/go-units"
	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/go-docopt"
)

func init() {
	register("ps", runPs, `
usage: flynn ps [-a] [-c]

List flynn jobs.

Options:
  -a, --all      Show all jobs (default is running and pending)
  -c, --command  Show command

Example:

       $ flynn ps
       ID                                          TYPE  STATE  CREATED             RELEASE
       host0-52aedfbf-e613-40f2-941a-d832d10fc400  web   up     About a minute ago  cf39a906-38d1-4393-a6b1-8ad2befe8142
       host0-205595d8-206a-46a2-be30-2e98f53df272  web   up     25 seconds ago      cf39a906-38d1-4393-a6b1-8ad2befe8142
       host0-0f34548b-72fa-41fe-a425-abc4ac6a3857  web   up     25 seconds ago      cf39a906-38d1-4393-a6b1-8ad2befe8142

       $ flynn ps --all --command
       ID                                          TYPE  STATE  CREATED             RELEASE				  COMMAND
       host0-52aedfbf-e613-40f2-941a-d832d10fc400  web   up     2 minutes ago       cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init start web
       host0-205595d8-206a-46a2-be30-2e98f53df272  web   up     About a minute ago  cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init start web
       host0-0f34548b-72fa-41fe-a425-abc4ac6a3857  web   up     About a minute ago  cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init start web
       host0-129b821f-3195-4b3b-b04b-669196cfbb03  run   down   5 seconds ago       cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init /bin/bash
`)
}

func runPs(args *docopt.Args, client controller.Client) error {
	jobs, err := client.JobList(mustApp())
	if err != nil {
		return err
	}
	sort.Sort(sortJobs(jobs))

	w := tabWriter()
	defer w.Flush()

	headers := []interface{}{"ID", "TYPE", "STATE", "CREATED", "RELEASE"}
	if args.Bool["--command"] {
		headers = append(headers, "COMMAND")
	}
	listRec(w, headers...)
	for _, j := range jobs {
		if j.Type == "" {
			j.Type = "run"
		}
		if !args.Bool["--all"] && j.State != ct.JobStateUp && j.State != ct.JobStatePending {
			continue
		}
		id := j.ID
		if id == "" {
			id = j.UUID
		}
		var created string
		if j.CreatedAt != nil {
			created = units.HumanDuration(time.Now().UTC().Sub(*j.CreatedAt)) + " ago"
		}
		fields := []interface{}{id, j.Type, j.State, created, j.ReleaseID}
		if args.Bool["--command"] {
			fields = append(fields, strings.Join(j.Args, " "))
		}
		listRec(w, fields...)
	}

	return nil
}

// sortJobs sorts Jobs in chronological order based on their CreatedAt time
type sortJobs []*ct.Job

func (s sortJobs) Len() int { return len(s) }
func (s sortJobs) Less(i, j int) bool {
	return s[i].CreatedAt == nil || s[j].CreatedAt != nil && (*s[j].CreatedAt).Sub(*s[i].CreatedAt) > 0
}
func (s sortJobs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
<commit_msg>cli: Add --quiet flag to `flynn ps`<commit_after>package main

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/docker/go-units"
	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/go-docopt"
)

func init() {
	register("ps", runPs, `
usage: flynn ps [-a] [-c] [-q]

List flynn jobs.

Options:
  -a, --all      Show all jobs (default is running and pending)
  -c, --command  Show command
	-q, --quiet    Only display IDs

Example:

       $ flynn ps
       ID                                          TYPE  STATE  CREATED             RELEASE
       host0-52aedfbf-e613-40f2-941a-d832d10fc400  web   up     About a minute ago  cf39a906-38d1-4393-a6b1-8ad2befe8142
       host0-205595d8-206a-46a2-be30-2e98f53df272  web   up     25 seconds ago      cf39a906-38d1-4393-a6b1-8ad2befe8142
       host0-0f34548b-72fa-41fe-a425-abc4ac6a3857  web   up     25 seconds ago      cf39a906-38d1-4393-a6b1-8ad2befe8142

       $ flynn ps --all --command
       ID                                          TYPE  STATE  CREATED             RELEASE				  COMMAND
       host0-52aedfbf-e613-40f2-941a-d832d10fc400  web   up     2 minutes ago       cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init start web
       host0-205595d8-206a-46a2-be30-2e98f53df272  web   up     About a minute ago  cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init start web
       host0-0f34548b-72fa-41fe-a425-abc4ac6a3857  web   up     About a minute ago  cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init start web
       host0-129b821f-3195-4b3b-b04b-669196cfbb03  run   down   5 seconds ago       cf39a906-38d1-4393-a6b1-8ad2befe842	  /runner/init /bin/bash

			 $ flynn ps --all --quiet
       host0-52aedfbf-e613-40f2-941a-d832d10fc400
       host0-205595d8-206a-46a2-be30-2e98f53df272
       host0-0f34548b-72fa-41fe-a425-abc4ac6a3857
       host0-129b821f-3195-4b3b-b04b-669196cfbb03
`)
}

func runPs(args *docopt.Args, client controller.Client) error {
	jobs, err := client.JobList(mustApp())
	if err != nil {
		return err
	}
	all := args.Bool["--all"] || args.Bool["-a"]
	if args.Bool["--quiet"] || args.Bool["-q"] {
		printJobsQuiet(jobs, all)
	} else {
		printJobs(jobs, all, args.Bool["--command"] || args.Bool["-c"])
	}
	return nil
}

func printJobsQuiet(jobs []*ct.Job, all bool) {
	sort.Sort(sortJobs(jobs))
	for _, j := range jobs {
		if !all && j.State != ct.JobStateUp && j.State != ct.JobStatePending {
			continue
		}
		fmt.Println(j.ID)
	}
}

func printJobs(jobs []*ct.Job, all bool, commandFlagOn bool) {
	sort.Sort(sortJobs(jobs))
	w := tabWriter()
	defer w.Flush()

	headers := []interface{}{"ID", "TYPE", "STATE", "CREATED", "RELEASE"}
	if commandFlagOn {
		headers = append(headers, "COMMAND")
	}
	listRec(w, headers...)
	for _, j := range jobs {
		if !all && j.State != ct.JobStateUp && j.State != ct.JobStatePending {
			continue
		}
		if j.Type == "" {
			j.Type = "run"
		}
		id := j.ID
		if id == "" {
			id = j.UUID
		}
		var created string
		if j.CreatedAt != nil {
			created = units.HumanDuration(time.Now().UTC().Sub(*j.CreatedAt)) + " ago"
		}
		fields := []interface{}{id, j.Type, j.State, created, j.ReleaseID}
		if commandFlagOn {
			fields = append(fields, strings.Join(j.Args, " "))
		}
		listRec(w, fields...)
	}
}

// sortJobs sorts Jobs in chronological order based on their CreatedAt time
type sortJobs []*ct.Job

func (s sortJobs) Len() int { return len(s) }
func (s sortJobs) Less(i, j int) bool {
	return s[i].CreatedAt == nil || s[j].CreatedAt != nil && (*s[j].CreatedAt).Sub(*s[i].CreatedAt) > 0
}
func (s sortJobs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
<|endoftext|>


<commit_before>package grafsy

import (
	"log"
	"net"
	"os"
	"path"
	"strings"
	"sync"
	"time"
)

// Client is a class wich sends metrics to the carbon receivers
type Client struct {
	// User config.
	Conf *Config

	// Local config.
	Lc *LocalConfig

	// Pointer to Monitoring structure.
	Mon *Monitoring

	// Monitoring channels per carbon
	monChannels map[string]chan string

	// Main channel per carbon
	mainChannels map[string]chan string
}

var chanLock sync.Mutex

// Create a directory for retry files
func (c Client) createRetryDir() error {
	err := os.MkdirAll(c.Conf.RetryDir, 0750)
	return err
}

// Save []string to file.
func (c Client) saveSliceToRetry(metrics []string, backend string) error {
	//
	// If size of file is bigger, than max size we will remove lines from this file,
	// and will call this function again to check result and write to the file.
	// Recursion:)
	//

	c.Lc.lg.Printf("Resaving %d metrics back to the retry-file", len(metrics))

	retFile := path.Join(c.Conf.RetryDir, backend)
	f, err := os.OpenFile(retFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		c.Lc.lg.Println(err)
		c.Mon.Increase(&c.Mon.stat[backend].dropped, len(metrics))
		return err
	}
	defer f.Close()

	dropped, saved := 0, 0
	for _, metric := range metrics {
		_, err = f.WriteString(metric + "\n")
		if err != nil {
			c.Lc.lg.Println(err)
			dropped++
		} else {
			saved++
		}
	}
	if dropped > 0 {
		c.Mon.Increase(&c.Mon.stat[backend].dropped, dropped)
	}
	if saved > 0 {
		c.Mon.Increase(&c.Mon.stat[backend].saved, saved)
	}
	return c.removeOldDataFromRetryFile(backend)
}

// Save part of entire content of channel to file.
func (c Client) saveChannelToRetry(ch chan string, size int, backend string) {
	//
	// If size of file is bigger, than max size we will remove lines from this file,
	// and will call this function again to check result and write to the file.
	// Recursion:)
	//

	// We save all metrics from channels on the program ending
	// In this case on the size=0 the whole channel is saved
	if size == 0 {
		size = len(ch)
	}

	c.Lc.lg.Printf("Saving %d metrics from channel to the retry-file", size)

	retFile := path.Join(c.Conf.RetryDir, backend)
	f, err := os.OpenFile(retFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		c.Lc.lg.Println(err.Error())
	}
	defer f.Close()

	dropped, saved := 0, 0
	for i := 0; i < size; i++ {
		_, err = f.WriteString(<-ch + "\n")
		if err != nil {
			dropped++
			c.Lc.lg.Println(err.Error())
		} else {
			saved++
		}
	}
	if dropped > 0 {
		c.Mon.Increase(&c.Mon.stat[backend].dropped, dropped)
	}
	if saved > 0 {
		c.Mon.Increase(&c.Mon.stat[backend].saved, saved)
	}
	c.removeOldDataFromRetryFile(backend)
}

// Cleaning up retry-file.
// Entire file is sorted to have newest metrics at the beginning.
func (c Client) removeOldDataFromRetryFile(backend string) error {
	retFile := path.Join(c.Conf.RetryDir, backend)
	currentLinesInFile := getSizeInLinesFromFile(retFile)
	if currentLinesInFile > c.Lc.fileMetricSize {
		c.Lc.lg.Printf("I can not save to %s more, than %d. I will have to drop the rest (%d)",
			retFile, c.Lc.fileMetricSize, currentLinesInFile-c.Lc.fileMetricSize)
		// We save first c.Lc.fileMetricSize of metrics (newest)
		wholeFile, _ := readMetricsFromFile(retFile)
		return c.saveSliceToRetry(wholeFile[:c.Lc.fileMetricSize], backend)
	}
	return nil
}

// Attempt to send metric to graphite server via connection
func (c *Client) tryToSendToGraphite(metric string, conn net.Conn) error {
	// If at any point "HOSTNAME" was used instead of real hostname - replace it
	metric = strings.Replace(metric, "HOSTNAME", c.Lc.hostname, -1)

	_, err := conn.Write([]byte(metric + "\n"))
	if err != nil {
		c.Lc.lg.Println("Write to server failed:", err.Error())
		return err
	}
	backend := conn.RemoteAddr().String()
	c.Mon.Increase(&c.Mon.stat[backend].sent, 1)
	return nil
}

// Run go routine per carbon server to:
//  1) Send data from retryFile to a carbon
//  2) Send metrics from monitoring channel to a carbon
//  3) Send metrics from the main channel to carbon
//
// And save everything to the retryFile on any error
func (c Client) runBackend(backend string) {
	retFile := path.Join(c.Conf.RetryDir, backend)
	chanLock.Lock()
	monChannel := c.monChannels[backend]
	mainChannel := c.mainChannels[backend]
	chanLock.Unlock()
	// TODO: think about graceful shutdown with flush channels

	for ; ; time.Sleep(time.Duration(c.Conf.ClientSendInterval) * time.Second) {
		var connectionFailed bool

		// Try to dial to Graphite server. If ClientSendInterval is 10 seconds - dial should be no longer than 1 second
		conn, err := net.DialTimeout("tcp", backend, time.Duration(c.Conf.ConnectTimeout)*time.Second)
		if err != nil {
			c.Lc.lg.Println("Can not connect to graphite server: ", err.Error())
			c.saveChannelToRetry(monChannel, len(monChannel), backend)
			c.saveChannelToRetry(mainChannel, len(mainChannel), backend)
			c.removeOldDataFromRetryFile(backend)
			continue
		}

		// We set dead line for connection to write. It should be the rest of we have for client interval
		err = conn.SetWriteDeadline(time.Now().Add(time.Duration(c.Conf.ClientSendInterval-c.Conf.ConnectTimeout-1) * time.Second))
		if err != nil {
			c.Lc.lg.Println("Can not set deadline for connection: ", err.Error())
			connectionFailed = true
		}

		// We send retry file first, we have a risk to lose old data
		// Metrics from retry file are counted as extra metrics per second to have a chance to send them
		// Otherwise we would only save new incomming metrics and continuously lose part of buffer
		if !connectionFailed {
			retryFileMetrics, _ := readMetricsFromFile(retFile)
			for numOfMetricFromFile, metricFromFile := range retryFileMetrics {
				if numOfMetricFromFile < c.Lc.mainBufferSize {
					err = c.tryToSendToGraphite(metricFromFile, conn)
					if err != nil {
						c.Lc.lg.Printf("Error happened in the middle of writing retry metrics. Resaving %d metrics\n", len(retryFileMetrics[numOfMetricFromFile:]))
						// If we failed to write a metric to graphite - something is wrong with connection
						c.saveSliceToRetry(retryFileMetrics[numOfMetricFromFile:], backend)
						connectionFailed = true
						break
					} else {
						c.Mon.Increase(&c.Mon.got.retry, 1)
					}

				} else {
					c.Lc.lg.Printf("Can read only %d metrics from %s. Rest %d will be kept for the next run", numOfMetricFromFile, retFile, len(retryFileMetrics[numOfMetricFromFile:]))
					c.saveSliceToRetry(retryFileMetrics[numOfMetricFromFile:], backend)
					break
				}
			}
		}

		// Monitoring. We read it always and we reserved space for it
		bufSize := len(monChannel)
		if !connectionFailed {
			for i := 0; i < bufSize; i++ {
				err = c.tryToSendToGraphite(<-monChannel, conn)
				if err != nil {
					c.Lc.lg.Println("Error happened in the middle of writing monitoring metrics. Saving...")
					c.saveChannelToRetry(monChannel, bufSize-i, backend)
					connectionFailed = true
					break
				}
			}
		} else {
			c.saveChannelToRetry(monChannel, bufSize, backend)
		}

		//
		//  Main Buffer. We read it completely but send only part which fits in mainBufferSize
		//  Rests we save
		//

		bufSize = len(mainChannel)

		if !connectionFailed {
			for processedMainBuff := 0; processedMainBuff < bufSize; processedMainBuff = processedMainBuff + 1 {
				metric := <-mainChannel

				err = c.tryToSendToGraphite(metric, conn)
				if err != nil {
					c.Lc.lg.Printf("Error happened in the middle of writing metrics. Saving %d metrics\n", bufSize-processedMainBuff)
					c.saveChannelToRetry(mainChannel, bufSize-processedMainBuff, backend)
					break
				}
			}
		} else {
			c.saveChannelToRetry(mainChannel, bufSize, backend)
		}
		conn.Close()
	}
}

//Run a client, which:
// 1) Make monitoring and main channels per carbon server
// 2) Launchs go routine per carbon server
// 3) Copy metrics from monitoring and main channels to the carbon server specific
// Should be run in separate goroutine.
func (c Client) Run() {
	err := c.createRetryDir()
	if err != nil {
		log.Fatal(err)
	}
	c.mainChannels = make(map[string]chan string)
	c.monChannels = make(map[string]chan string)

	for _, carbonAddrTCP := range c.Lc.carbonAddrsTCP {
		backend := carbonAddrTCP.String()
		chanLock.Lock()
		c.mainChannels[backend] = make(chan string, cap(c.Lc.mainChannel))
		c.monChannels[backend] = make(chan string, cap(c.Lc.monitoringChannel))
		chanLock.Unlock()
		go c.runBackend(backend)
	}

	sup := supervisor(c.Conf.Supervisor)
	for ; ; time.Sleep(time.Second) {
		// Notify watchdog about aliveness of Client routine
		sup.notify()

		// write metrics from monitoring and main channels to the server specific channels
		for i := 0; i < len(c.Lc.mainChannel); i++ {
			metric := <-c.Lc.mainChannel
			for _, carbonAddrTCP := range c.Lc.carbonAddrsTCP {
				backend := carbonAddrTCP.String()
				select {
				case c.mainChannels[backend] <- metric:
				default:
					c.Mon.Increase(&c.Mon.stat[backend].dropped, 1)
				}
			}
		}

		for i := 0; i < len(c.Lc.monitoringChannel); i++ {
			metric := <-c.Lc.monitoringChannel
			for _, carbonAddrTCP := range c.Lc.carbonAddrsTCP {
				backend := carbonAddrTCP.String()
				select {
				case c.monChannels[backend] <- metric:
				default:
					c.Mon.Increase(&c.Mon.stat[backend].dropped, 1)
				}
			}
		}
	}
}
<commit_msg>We shouldn't recursively increase 'saved' with each resave<commit_after>package grafsy

import (
	"log"
	"net"
	"os"
	"path"
	"strings"
	"sync"
	"time"
)

// Client is a class wich sends metrics to the carbon receivers
type Client struct {
	// User config.
	Conf *Config

	// Local config.
	Lc *LocalConfig

	// Pointer to Monitoring structure.
	Mon *Monitoring

	// Monitoring channels per carbon
	monChannels map[string]chan string

	// Main channel per carbon
	mainChannels map[string]chan string
}

var chanLock sync.Mutex

// Create a directory for retry files
func (c Client) createRetryDir() error {
	err := os.MkdirAll(c.Conf.RetryDir, 0750)
	return err
}

// Save []string to file.
func (c Client) saveSliceToRetry(metrics []string, backend string) error {
	//
	// If size of file is bigger, than max size we will remove lines from this file,
	// and will call this function again to check result and write to the file.
	// Recursion:)
	//

	c.Lc.lg.Printf("Resaving %d metrics back to the retry-file", len(metrics))

	retFile := path.Join(c.Conf.RetryDir, backend)
	f, err := os.OpenFile(retFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		c.Lc.lg.Println(err)
		c.Mon.Increase(&c.Mon.stat[backend].dropped, len(metrics))
		return err
	}
	defer f.Close()

	dropped := 0
	for _, metric := range metrics {
		_, err = f.WriteString(metric + "\n")
		if err != nil {
			c.Lc.lg.Println(err)
			dropped++
		}
	}
	if dropped > 0 {
		c.Mon.Increase(&c.Mon.stat[backend].dropped, dropped)
	}
	return c.removeOldDataFromRetryFile(backend)
}

// Save part of entire content of channel to file.
func (c Client) saveChannelToRetry(ch chan string, size int, backend string) {
	//
	// If size of file is bigger, than max size we will remove lines from this file,
	// and will call this function again to check result and write to the file.
	// Recursion:)
	//

	// We save all metrics from channels on the program ending
	// In this case on the size=0 the whole channel is saved
	if size == 0 {
		size = len(ch)
	}

	c.Lc.lg.Printf("Saving %d metrics from channel to the retry-file", size)

	retFile := path.Join(c.Conf.RetryDir, backend)
	f, err := os.OpenFile(retFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		c.Lc.lg.Println(err.Error())
	}
	defer f.Close()

	dropped, saved := 0, 0
	for i := 0; i < size; i++ {
		_, err = f.WriteString(<-ch + "\n")
		if err != nil {
			dropped++
			c.Lc.lg.Println(err.Error())
		} else {
			saved++
		}
	}
	if dropped > 0 {
		c.Mon.Increase(&c.Mon.stat[backend].dropped, dropped)
	}
	if saved > 0 {
		c.Mon.Increase(&c.Mon.stat[backend].saved, saved)
	}
	c.removeOldDataFromRetryFile(backend)
}

// Cleaning up retry-file.
// Entire file is sorted to have newest metrics at the beginning.
func (c Client) removeOldDataFromRetryFile(backend string) error {
	retFile := path.Join(c.Conf.RetryDir, backend)
	currentLinesInFile := getSizeInLinesFromFile(retFile)
	if currentLinesInFile > c.Lc.fileMetricSize {
		c.Lc.lg.Printf("I can not save to %s more, than %d. I will have to drop the rest (%d)",
			retFile, c.Lc.fileMetricSize, currentLinesInFile-c.Lc.fileMetricSize)
		// We save first c.Lc.fileMetricSize of metrics (newest)
		wholeFile, _ := readMetricsFromFile(retFile)
		return c.saveSliceToRetry(wholeFile[:c.Lc.fileMetricSize], backend)
	}
	return nil
}

// Attempt to send metric to graphite server via connection
func (c *Client) tryToSendToGraphite(metric string, conn net.Conn) error {
	// If at any point "HOSTNAME" was used instead of real hostname - replace it
	metric = strings.Replace(metric, "HOSTNAME", c.Lc.hostname, -1)

	_, err := conn.Write([]byte(metric + "\n"))
	if err != nil {
		c.Lc.lg.Println("Write to server failed:", err.Error())
		return err
	}
	backend := conn.RemoteAddr().String()
	c.Mon.Increase(&c.Mon.stat[backend].sent, 1)
	return nil
}

// Run go routine per carbon server to:
//  1) Send data from retryFile to a carbon
//  2) Send metrics from monitoring channel to a carbon
//  3) Send metrics from the main channel to carbon
//
// And save everything to the retryFile on any error
func (c Client) runBackend(backend string) {
	retFile := path.Join(c.Conf.RetryDir, backend)
	chanLock.Lock()
	monChannel := c.monChannels[backend]
	mainChannel := c.mainChannels[backend]
	chanLock.Unlock()
	// TODO: think about graceful shutdown with flush channels

	for ; ; time.Sleep(time.Duration(c.Conf.ClientSendInterval) * time.Second) {
		var connectionFailed bool

		// Try to dial to Graphite server. If ClientSendInterval is 10 seconds - dial should be no longer than 1 second
		conn, err := net.DialTimeout("tcp", backend, time.Duration(c.Conf.ConnectTimeout)*time.Second)
		if err != nil {
			c.Lc.lg.Println("Can not connect to graphite server: ", err.Error())
			c.saveChannelToRetry(monChannel, len(monChannel), backend)
			c.saveChannelToRetry(mainChannel, len(mainChannel), backend)
			c.removeOldDataFromRetryFile(backend)
			continue
		}

		// We set dead line for connection to write. It should be the rest of we have for client interval
		err = conn.SetWriteDeadline(time.Now().Add(time.Duration(c.Conf.ClientSendInterval-c.Conf.ConnectTimeout-1) * time.Second))
		if err != nil {
			c.Lc.lg.Println("Can not set deadline for connection: ", err.Error())
			connectionFailed = true
		}

		// We send retry file first, we have a risk to lose old data
		// Metrics from retry file are counted as extra metrics per second to have a chance to send them
		// Otherwise we would only save new incomming metrics and continuously lose part of buffer
		if !connectionFailed {
			retryFileMetrics, _ := readMetricsFromFile(retFile)
			for numOfMetricFromFile, metricFromFile := range retryFileMetrics {
				if numOfMetricFromFile < c.Lc.mainBufferSize {
					err = c.tryToSendToGraphite(metricFromFile, conn)
					if err != nil {
						c.Lc.lg.Printf("Error happened in the middle of writing retry metrics. Resaving %d metrics\n", len(retryFileMetrics[numOfMetricFromFile:]))
						// If we failed to write a metric to graphite - something is wrong with connection
						c.saveSliceToRetry(retryFileMetrics[numOfMetricFromFile:], backend)
						connectionFailed = true
						break
					} else {
						c.Mon.Increase(&c.Mon.got.retry, 1)
					}

				} else {
					c.Lc.lg.Printf("Can read only %d metrics from %s. Rest %d will be kept for the next run", numOfMetricFromFile, retFile, len(retryFileMetrics[numOfMetricFromFile:]))
					c.saveSliceToRetry(retryFileMetrics[numOfMetricFromFile:], backend)
					break
				}
			}
		}

		// Monitoring. We read it always and we reserved space for it
		bufSize := len(monChannel)
		if !connectionFailed {
			for i := 0; i < bufSize; i++ {
				err = c.tryToSendToGraphite(<-monChannel, conn)
				if err != nil {
					c.Lc.lg.Println("Error happened in the middle of writing monitoring metrics. Saving...")
					c.saveChannelToRetry(monChannel, bufSize-i, backend)
					connectionFailed = true
					break
				}
			}
		} else {
			c.saveChannelToRetry(monChannel, bufSize, backend)
		}

		//
		//  Main Buffer. We read it completely but send only part which fits in mainBufferSize
		//  Rests we save
		//

		bufSize = len(mainChannel)

		if !connectionFailed {
			for processedMainBuff := 0; processedMainBuff < bufSize; processedMainBuff = processedMainBuff + 1 {
				metric := <-mainChannel

				err = c.tryToSendToGraphite(metric, conn)
				if err != nil {
					c.Lc.lg.Printf("Error happened in the middle of writing metrics. Saving %d metrics\n", bufSize-processedMainBuff)
					c.saveChannelToRetry(mainChannel, bufSize-processedMainBuff, backend)
					break
				}
			}
		} else {
			c.saveChannelToRetry(mainChannel, bufSize, backend)
		}
		conn.Close()
	}
}

//Run a client, which:
// 1) Make monitoring and main channels per carbon server
// 2) Launchs go routine per carbon server
// 3) Copy metrics from monitoring and main channels to the carbon server specific
// Should be run in separate goroutine.
func (c Client) Run() {
	err := c.createRetryDir()
	if err != nil {
		log.Fatal(err)
	}
	c.mainChannels = make(map[string]chan string)
	c.monChannels = make(map[string]chan string)

	for _, carbonAddrTCP := range c.Lc.carbonAddrsTCP {
		backend := carbonAddrTCP.String()
		chanLock.Lock()
		c.mainChannels[backend] = make(chan string, cap(c.Lc.mainChannel))
		c.monChannels[backend] = make(chan string, cap(c.Lc.monitoringChannel))
		chanLock.Unlock()
		go c.runBackend(backend)
	}

	sup := supervisor(c.Conf.Supervisor)
	for ; ; time.Sleep(time.Second) {
		// Notify watchdog about aliveness of Client routine
		sup.notify()

		// write metrics from monitoring and main channels to the server specific channels
		for i := 0; i < len(c.Lc.mainChannel); i++ {
			metric := <-c.Lc.mainChannel
			for _, carbonAddrTCP := range c.Lc.carbonAddrsTCP {
				backend := carbonAddrTCP.String()
				select {
				case c.mainChannels[backend] <- metric:
				default:
					c.Mon.Increase(&c.Mon.stat[backend].dropped, 1)
				}
			}
		}

		for i := 0; i < len(c.Lc.monitoringChannel); i++ {
			metric := <-c.Lc.monitoringChannel
			for _, carbonAddrTCP := range c.Lc.carbonAddrsTCP {
				backend := carbonAddrTCP.String()
				select {
				case c.monChannels[backend] <- metric:
				default:
					c.Mon.Increase(&c.Mon.stat[backend].dropped, 1)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"time"
)

const protoFmt = "ntwrk%s :%s\r\n"
const timeout = time.Duration(15) * time.Second

// testContext holds a test function, action name, and address to connect to.
type testContext struct {
	Action string
	Fn     func(net.Conn, time.Duration) (int64, error)
	Addr   string
}

// startClient starts the network test suite.
func startClient(host string, port int) {
	addr := fmt.Sprintf("%s:%d", host, port)
	perform(testContext{"download", download, addr})
	perform(testContext{"upload", upload, addr})
}

// perform runs a network test and prints the recorded bandwidth.
func perform(ctx testContext) {
	conn := openConn(ctx.Addr, ctx.Action)

	since := time.Now()
	ticker := time.NewTicker(time.Millisecond * 150)
	go func() {
		for t := range ticker.C {
			elapsed := t.Sub(since)
			progress := formatProgress(elapsed, timeout)
			fmt.Printf("\r %9s: %s", ctx.Action, progress)
		}
	}()
	defer ticker.Stop()

	bytes, err := ctx.Fn(conn, timeout)
	if err != nil {
		fmt.Printf("error: %s\n", err.Error())
		return
	}

	elapsed := time.Since(since).Seconds()
	fmt.Printf(" %s\n", formatBytes(bytes, elapsed))
}

// openConn opens a connection to `host` and writes a formatted message to it.
func openConn(host string, action string) (conn net.Conn) {
	conn, err := net.Dial("tcp", host)
	if err != nil {
		panic(err)
	}

	fmt.Fprintf(conn, protoFmt, proto, action)
	return
}

// whoami requests the client's external IP address from `host` and prints it.
func whoami(host string, port int) {
	addr := fmt.Sprintf("%s:%d", host, port)
	resp := make([]byte, 40)
	conn := openConn(addr, "whoami")
	conn.Read(resp)
	fmt.Print(string(resp))
}
<commit_msg>Add error handling to `openConn`<commit_after>package main

import (
	"fmt"
	"net"
	"time"
)

const protoFmt = "ntwrk%s :%s\r\n"
const timeout = time.Duration(15) * time.Second

// testContext holds a test function, action name, and address to connect to.
type testContext struct {
	Action string
	Fn     func(net.Conn, time.Duration) (int64, error)
	Addr   string
}

// startClient starts the network test suite.
func startClient(host string, port int) {
	addr := fmt.Sprintf("%s:%d", host, port)
	perform(testContext{"download", download, addr})
	perform(testContext{"upload", upload, addr})
}

// perform runs a network test and prints the recorded bandwidth.
func perform(ctx testContext) {
	conn, err := openConn(ctx.Addr, ctx.Action)
	if err != nil {
		fmt.Printf("error: %s\n", err.Error())
		return
	}

	since := time.Now()
	ticker := time.NewTicker(time.Millisecond * 150)
	go func() {
		for t := range ticker.C {
			elapsed := t.Sub(since)
			progress := formatProgress(elapsed, timeout)
			fmt.Printf("\r %9s: %s", ctx.Action, progress)
		}
	}()
	defer ticker.Stop()

	bytes, err := ctx.Fn(conn, timeout)
	if err != nil {
		fmt.Printf("error: %s\n", err.Error())
		return
	}

	elapsed := time.Since(since).Seconds()
	fmt.Printf(" %s\n", formatBytes(bytes, elapsed))
}

// openConn opens a connection to `host` and writes a formatted message to it.
func openConn(host string, action string) (conn net.Conn, err error) {
	conn, err = net.Dial("tcp", host)
	if err != nil {
		return
	}

	fmt.Fprintf(conn, protoFmt, proto, action)
	return
}

// whoami requests the client's external IP address from `host` and prints it.
func whoami(host string, port int) {
	addr := fmt.Sprintf("%s:%d", host, port)
	resp := make([]byte, 40)

	conn, err := openConn(addr, "whoami")
	if err != nil {
		fmt.Printf("error: %s\n", err.Error())
		return
	}

	conn.Read(resp)
	fmt.Print(string(resp))
}
<|endoftext|>


<commit_before>package rtmp

import (
	"crypto/tls"
	"github.com/elobuff/goamf"
	"net"
	"net/url"
	"sync"
	"sync/atomic"
	"time"
)

type Client struct {
	url string

	connected bool

	conn net.Conn
	enc  amf.Encoder
	dec  amf.Decoder

	outBytes        uint32
	outMessages     chan *Message
	outWindowSize   uint32
	outChunkSize    uint32
	outChunkStreams map[uint32]*OutboundChunkStream
	inBytes         uint32
	inMessages      chan *Message
	inNotify        chan uint8
	inWindowSize    uint32
	inChunkSize     uint32
	inChunkStreams  map[uint32]*InboundChunkStream

	results      map[uint32]*Result
	resultsMutex sync.Mutex

	lastTransactionId uint32
	connectionId      string
}

func NewClient(url string) (c *Client, err error) {
	c = &Client{
		url: url,
	}

	c.Reset()
	err = c.Connect()
	return
}

func (c *Client) IsAlive() bool {
	if c.connected != true {
		return false
	}

	return true
}

func (c *Client) Reset() {
	c.connected = false

	if c.conn != nil {
		c.conn.Close()
	}

	if c.outMessages != nil {
		close(c.outMessages)
	}

	if c.inMessages != nil {
		close(c.inMessages)
	}

	c.enc = *new(amf.Encoder)
	c.dec = *new(amf.Decoder)
	c.outBytes = 0
	c.outMessages = make(chan *Message, 100)
	c.outChunkSize = DEFAULT_CHUNK_SIZE
	c.outWindowSize = DEFAULT_WINDOW_SIZE
	c.outChunkStreams = make(map[uint32]*OutboundChunkStream)
	c.inBytes = 0
	c.inMessages = make(chan *Message, 100)
	c.inChunkSize = DEFAULT_CHUNK_SIZE
	c.inWindowSize = DEFAULT_WINDOW_SIZE
	c.inChunkStreams = make(map[uint32]*InboundChunkStream)
	c.results = make(map[uint32]*Result)
	c.lastTransactionId = 0
	c.connectionId = ""
}

func (c *Client) Disconnect() {
	c.Reset()
	log.Info("disconnected from %s", c.url, c.outBytes, c.inBytes)
}

func (c *Client) Connect() (err error) {
	log.Info("connecting to %s", c.url)

	url, err := url.Parse(c.url)
	if err != nil {
		return err
	}

	switch url.Scheme {
	case "rtmp":
		c.conn, err = net.Dial("tcp", url.Host)
	case "rtmps":
		config := &tls.Config{InsecureSkipVerify: true}
		c.conn, err = tls.Dial("tcp", url.Host, config)
	default:
		return Error("Unsupported scheme: %s", url.Scheme)
	}

	log.Debug("handshaking with %s", c.url)

	err = c.handshake()
	if err != nil {
		return Error("client connect: could not complete handshake: %s", err)
	}

	log.Debug("sending connect command to %s", c.url)

	go c.receiveLoop()
	go c.sendLoop()
	go c.routeLoop()

	var id string
	id, err = c.connect()
	if err != nil {
		return Error("client connect: could not complete connect: %s", err)
	}

	c.connected = true
	c.connectionId = id

	log.Info("connected to %s (%s)", c.url, c.connectionId)

	return
}

func (c *Client) NextTransactionId() uint32 {
	return atomic.AddUint32(&c.lastTransactionId, 1)
}

func (c *Client) Call(msg *Message, t uint32) (result *Result, err error) {
	c.outMessages <- msg

	tid := msg.TransactionId
	poll := time.Tick(time.Duration(5) * time.Millisecond)
	timeout := time.After(time.Duration(t) * time.Second)

	for {
		select {
		case <-poll:
			log.Trace("client call: polling for result for tid %d", tid)
			c.resultsMutex.Lock()
			result = c.results[tid]
			if result != nil {
				log.Trace("client call: found result for %d", tid)
				delete(c.results, tid)
			}
			c.resultsMutex.Unlock()

			if result != nil {
				return
			}
		case <-timeout:
			return result, Error("timed out after %d seconds", t)
		}
	}

	return
}

func (c *Client) Read(p []byte) (n int, err error) {
	n, err = c.conn.Read(p)
	c.inBytes += uint32(n)
	log.Trace("read %d", n)
	return n, err
}

func (c *Client) Write(p []byte) (n int, err error) {
	n, err = c.conn.Write(p)
	c.outBytes += uint32(n)
	log.Trace("write %d", n)
	return n, err
}
<commit_msg>Remove poll trace<commit_after>package rtmp

import (
	"crypto/tls"
	"github.com/elobuff/goamf"
	"net"
	"net/url"
	"sync"
	"sync/atomic"
	"time"
)

type Client struct {
	url string

	connected bool

	conn net.Conn
	enc  amf.Encoder
	dec  amf.Decoder

	outBytes        uint32
	outMessages     chan *Message
	outWindowSize   uint32
	outChunkSize    uint32
	outChunkStreams map[uint32]*OutboundChunkStream
	inBytes         uint32
	inMessages      chan *Message
	inNotify        chan uint8
	inWindowSize    uint32
	inChunkSize     uint32
	inChunkStreams  map[uint32]*InboundChunkStream

	results      map[uint32]*Result
	resultsMutex sync.Mutex

	lastTransactionId uint32
	connectionId      string
}

func NewClient(url string) (c *Client, err error) {
	c = &Client{
		url: url,
	}

	c.Reset()
	err = c.Connect()
	return
}

func (c *Client) IsAlive() bool {
	if c.connected != true {
		return false
	}

	return true
}

func (c *Client) Reset() {
	c.connected = false

	if c.conn != nil {
		c.conn.Close()
	}

	if c.outMessages != nil {
		close(c.outMessages)
	}

	if c.inMessages != nil {
		close(c.inMessages)
	}

	c.enc = *new(amf.Encoder)
	c.dec = *new(amf.Decoder)
	c.outBytes = 0
	c.outMessages = make(chan *Message, 100)
	c.outChunkSize = DEFAULT_CHUNK_SIZE
	c.outWindowSize = DEFAULT_WINDOW_SIZE
	c.outChunkStreams = make(map[uint32]*OutboundChunkStream)
	c.inBytes = 0
	c.inMessages = make(chan *Message, 100)
	c.inChunkSize = DEFAULT_CHUNK_SIZE
	c.inWindowSize = DEFAULT_WINDOW_SIZE
	c.inChunkStreams = make(map[uint32]*InboundChunkStream)
	c.results = make(map[uint32]*Result)
	c.lastTransactionId = 0
	c.connectionId = ""
}

func (c *Client) Disconnect() {
	c.Reset()
	log.Info("disconnected from %s", c.url, c.outBytes, c.inBytes)
}

func (c *Client) Connect() (err error) {
	log.Info("connecting to %s", c.url)

	url, err := url.Parse(c.url)
	if err != nil {
		return err
	}

	switch url.Scheme {
	case "rtmp":
		c.conn, err = net.Dial("tcp", url.Host)
	case "rtmps":
		config := &tls.Config{InsecureSkipVerify: true}
		c.conn, err = tls.Dial("tcp", url.Host, config)
	default:
		return Error("Unsupported scheme: %s", url.Scheme)
	}

	log.Debug("handshaking with %s", c.url)

	err = c.handshake()
	if err != nil {
		return Error("client connect: could not complete handshake: %s", err)
	}

	log.Debug("sending connect command to %s", c.url)

	go c.receiveLoop()
	go c.sendLoop()
	go c.routeLoop()

	var id string
	id, err = c.connect()
	if err != nil {
		return Error("client connect: could not complete connect: %s", err)
	}

	c.connected = true
	c.connectionId = id

	log.Info("connected to %s (%s)", c.url, c.connectionId)

	return
}

func (c *Client) NextTransactionId() uint32 {
	return atomic.AddUint32(&c.lastTransactionId, 1)
}

func (c *Client) Call(msg *Message, t uint32) (result *Result, err error) {
	c.outMessages <- msg

	tid := msg.TransactionId
	poll := time.Tick(time.Duration(5) * time.Millisecond)
	timeout := time.After(time.Duration(t) * time.Second)

	for {
		select {
		case <-poll:
			c.resultsMutex.Lock()
			result = c.results[tid]
			if result != nil {
				log.Trace("client call: found result for %d", tid)
				delete(c.results, tid)
			}
			c.resultsMutex.Unlock()

			if result != nil {
				return
			}
		case <-timeout:
			return result, Error("timed out (no response after %d seconds)", t)
		}
	}

	return
}

func (c *Client) Read(p []byte) (n int, err error) {
	n, err = c.conn.Read(p)
	c.inBytes += uint32(n)
	log.Trace("read %d", n)
	return n, err
}

func (c *Client) Write(p []byte) (n int, err error) {
	n, err = c.conn.Write(p)
	c.outBytes += uint32(n)
	log.Trace("write %d", n)
	return n, err
}
<|endoftext|>


<commit_before>// Simple Client Implementation of WebFinger
//
// This is a work in progress, The WebFinger spec is changing every day anyway :)
//
// Some links to follow:
// https://code.google.com/p/webfinger/wiki/WebFingerProtocol
// https://groups.google.com/forum/#!forum/webfinger
// https://groups.google.com/d/topic/webfinger/zw-pCRGyuSo/discussion
// http://tools.ietf.org/html/draft-ietf-appsawg-webfinger-04
package webfinger

import (
	"errors"
	"fmt"
	"github.com/ant0ine/go-webfinger/jrd"
	"github.com/ant0ine/go-webfinger/xrd"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

type EmailAddress struct { // XXX rename Id or WebFingerId ? it may not be an email address
	Local  string
	Domain string
}

// Parse the email string and return an *EmailAddress
func MakeEmailAddress(email string) (*EmailAddress, error) {
	// TODO validate address, see http://www.ietf.org/rfc/rfc2822.txt
	// TODO accept an email address URI
	parts := strings.SplitN(email, "@", 2)
	if len(parts) < 2 {
		return nil, errors.New("not a valid email")
	}
	return &EmailAddress{
		Local:  parts[0],
		Domain: parts[1],
	}, nil
}

// Return the email address as an URI (ie: acct:user@domain)
func (self *EmailAddress) AsURI() string {
	return fmt.Sprintf("acct:%s@%s", self.Local, self.Domain)
}

// Build a serie well known host JRD URLs from the domain
// [Compat Note] This includes URLs from previous versions of the spec.
func HostJRDURLs(domain string) []string {
	return []string{
		// last spec: http://tools.ietf.org/html/draft-ietf-appsawg-webfinger-04
		"https://" + domain + "/.well-known/webfinger",
		// first JRD implementation
		"https://" + domain + "/.well-known/host-meta.json",
		// orignal spec: https://code.google.com/p/webfinger/wiki/WebFingerProtocol
		"https://" + domain + "/.well-known/host-meta",
	}
}

// Given an URL, get and parse the JRD.
// [Compat Note] If the payload is in XRD, this method parses it
// and converts it to JRD.
func GetJRD(url string) (*jrd.JRD, error) {
	// TODO try http if https fails
	// TODO verify signature if not https
	// TODO extract http cache info

	// Get follow up to 10 redirects
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}

	if !(200 <= res.StatusCode && res.StatusCode < 300) {
		return nil, errors.New(res.Status)
	}

	content, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return nil, err
	}

	ct := strings.ToLower(res.Header.Get("content-type"))
	if strings.Contains(ct, "application/json") {
		parsed, err := jrd.ParseJRD(content)
		if err != nil {
			return nil, err
		}
		return parsed, nil

	} else if strings.Contains(ct, "application/xrd+xml") {
		parsed, err := xrd.ParseXRD(content)
		if err != nil {
			return nil, err
		}
		return parsed.ConvertToJRD(), nil
	}

	return nil, errors.New(fmt.Sprintf("invalid content-type: %s", ct))
}

// Try to call GetJRD on each url until a successful response.
func FindJRD(urls []string) (*jrd.JRD, error) {
	for _, url := range urls {
		log.Printf("Fetching Host JRD URL: %s", url)
		obj, err := GetJRD(url)
		if err == nil {
			return obj, nil
		}
		log.Print(err)
	}
	return nil, errors.New("JRD not found")
}

// Given a domain, this method gets the host meta JRD data,
// and returns the LRDD user JRD template URL.
func GetUserJRDTemplateURL(domain string) (string, error) {
	// TODO implement heavy HTTP cache around this

	urls := HostJRDURLs(domain)

	host_jrd, err := FindJRD(urls)
	if err != nil {
		return "", err
	}

	template := host_jrd.LrddTemplate()
	if template == "" {
		return "", errors.New("cannot find the template in the JRD data")
	}

	return template, nil
}

// Try to discover the user JRD data from the email
func GetUserJRD(email string) (*jrd.JRD, error) {
	// TODO support the rel query string parameter

	address, err := MakeEmailAddress(email)
	if err != nil {
		return nil, err
	}

	log.Printf("Fetching WebFinger JRD info for: %s", address.AsURI())

	template, err := GetUserJRDTemplateURL(address.Domain)
	if err != nil {
		return nil, err
	}

	log.Printf("template: %s", template)

	jrd_url := strings.Replace(template, "{uri}", url.QueryEscape(address.AsURI()), 1)

	log.Printf("User JRD URL: %s", jrd_url)

	user_jrd, err := GetJRD(jrd_url)
	if err != nil {
		return nil, err
	}

	if user_jrd.Subject != address.AsURI() {
		return nil, errors.New("JRD Subject does not match the email")
	}

	return user_jrd, nil
}
<commit_msg>try HTTP if HTTPS fails, accept more xml content-types<commit_after>// Simple Client Implementation of WebFinger
//
// This is a work in progress, The WebFinger spec is changing every day anyway :)
//
// Some links to follow:
// https://code.google.com/p/webfinger/wiki/WebFingerProtocol
// https://groups.google.com/forum/#!forum/webfinger
// https://groups.google.com/d/topic/webfinger/zw-pCRGyuSo/discussion
// http://tools.ietf.org/html/draft-ietf-appsawg-webfinger-04
package webfinger

import (
	"errors"
	"fmt"
	"github.com/ant0ine/go-webfinger/jrd"
	"github.com/ant0ine/go-webfinger/xrd"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

type EmailAddress struct { // XXX rename Id or WebFingerId ? it may not be an email address
	Local  string
	Domain string
}

// Parse the email string and return an *EmailAddress
func MakeEmailAddress(email string) (*EmailAddress, error) {
	// TODO validate address, see http://www.ietf.org/rfc/rfc2822.txt
	// TODO accept an email address URI
	parts := strings.SplitN(email, "@", 2)
	if len(parts) < 2 {
		return nil, errors.New("not a valid email")
	}
	return &EmailAddress{
		Local:  parts[0],
		Domain: parts[1],
	}, nil
}

// Return the email address as an URI (ie: acct:user@domain)
func (self *EmailAddress) AsURI() string {
	return fmt.Sprintf("acct:%s@%s", self.Local, self.Domain)
}

// Build a serie well known host JRD URLs from the domain
// [Compat Note] This includes URLs from previous versions of the spec.
func HostJRDURLs(domain string) []string {
	return []string{
		// last spec: http://tools.ietf.org/html/draft-ietf-appsawg-webfinger-04
		"https://" + domain + "/.well-known/webfinger",
		"http://" + domain + "/.well-known/webfinger",
		// first JRD implementation
		"https://" + domain + "/.well-known/host-meta.json",
		"http://" + domain + "/.well-known/host-meta.json",
		// orignal spec: https://code.google.com/p/webfinger/wiki/WebFingerProtocol
		"https://" + domain + "/.well-known/host-meta",
		"http://" + domain + "/.well-known/host-meta",
	}
}

// Given an URL, get and parse the JRD.
// [Compat Note] If the payload is in XRD, this method parses it
// and converts it to JRD.
func GetJRD(url string) (*jrd.JRD, error) {
	// TODO verify signature if not https
	// TODO extract http cache info

	// Get follows up to 10 redirects
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}

	if !(200 <= res.StatusCode && res.StatusCode < 300) {
		return nil, errors.New(res.Status)
	}

	content, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return nil, err
	}

	ct := strings.ToLower(res.Header.Get("content-type"))
	if strings.Contains(ct, "application/json") {
		parsed, err := jrd.ParseJRD(content)
		if err != nil {
			return nil, err
		}
		return parsed, nil

	} else if strings.Contains(ct, "application/xrd+xml") ||
		strings.Contains(ct, "application/xml") ||
		strings.Contains(ct, "text/xml") {
		parsed, err := xrd.ParseXRD(content)
		if err != nil {
			return nil, err
		}
		return parsed.ConvertToJRD(), nil
	}

	return nil, errors.New(fmt.Sprintf("invalid content-type: %s", ct))
}

// Try to call GetJRD on each url until a successful response.
func FindJRD(urls []string) (*jrd.JRD, error) {
	for _, url := range urls {
		log.Printf("Fetching Host JRD URL: %s", url)
		obj, err := GetJRD(url)
		if err == nil {
			return obj, nil
		}
		log.Print(err)
	}
	return nil, errors.New("JRD not found")
}

// Given a domain, this method gets the host meta JRD data,
// and returns the LRDD user JRD template URL.
func GetUserJRDTemplateURL(domain string) (string, error) {
	// TODO implement heavy HTTP cache around this

	urls := HostJRDURLs(domain)

	host_jrd, err := FindJRD(urls)
	if err != nil {
		return "", err
	}

	template := host_jrd.LrddTemplate()
	if template == "" {
		return "", errors.New("cannot find the template in the JRD data")
	}

	return template, nil
}

// Try to discover the user JRD data from the email
func GetUserJRD(email string) (*jrd.JRD, error) {
	// TODO support the rel query string parameter

	address, err := MakeEmailAddress(email)
	if err != nil {
		return nil, err
	}

	log.Printf("Fetching WebFinger JRD info for: %s", address.AsURI())

	template, err := GetUserJRDTemplateURL(address.Domain)
	if err != nil {
		return nil, err
	}

	log.Printf("template: %s", template)

	jrd_url := strings.Replace(template, "{uri}", url.QueryEscape(address.AsURI()), 1)

	log.Printf("User JRD URL: %s", jrd_url)

	user_jrd, err := GetJRD(jrd_url)
	if err != nil {
		return nil, err
	}

	if user_jrd.Subject != address.AsURI() {
		return nil, errors.New("JRD Subject does not match the email")
	}

	return user_jrd, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Jason Goecke
// client.go

package wit

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
)

const (
	// UserAgent is the HTTP Uesr Agent sent on HTTP requests
	UserAgent = "WIT (Go net/http)"
	// APIVersion is the version of the Wit API supported
	APIVersion = "v=20140401"
)

// Client represents a client for the Wit API (https://wit.ai/docs/api)
type Client struct {
	APIBase string
}

// HTTPParams represents the HTTP parameters to pass along to the Wit API
type HTTPParams struct {
	Verb        string
	Resource    string
	ContentType string
	Data        []byte
}

// Stores the ApiKey for the Wit API
var APIKey string

// NewClient creates a new client for the Wit API
//
//		client := wit.NewClient("<ACCESS-TOKEN>")
func NewClient(apiKey string) *Client {
	client := &Client{APIBase: "https://api.wit.ai"}
	APIKey = apiKey
	return client
}

// Provides a common facility for doing a DELETE on a Wit resource
//
//		result, err := delete("https://api.wit.ai/entities", "favorite_city")
func delete(resource string, id string) ([]byte, error) {
	httpParams := &HTTPParams{
		Resource: resource + "/" + id,
		Verb:     "DELETE",
	}
	return processRequest(httpParams)
}

// Provides a common facility for doing a GET on a Wit resource
//
//		result, err := get("https://api.wit.ai/entities/favorite_city")
func get(resource string) ([]byte, error) {
	httpParams := &HTTPParams{
		Resource: resource,
		Verb:     "GET",
	}
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST on a Wit resource. Takes
// JSON []byte for the data argument.
//
//		result, err := post("https://api.wit.ai/entities", entity)
func post(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"POST", resource, "application/json", data}
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST with a file on a Wit resource.
//
//		result, err := postFile("https://api.wit.ai/messages", message)
func postFile(resource string, request *MessageRequest) ([]byte, error) {
	if request.File != "" {
		file, err := os.Open(request.File)
		if err != nil {
			return nil, err
		}
		defer file.Close()
		stats, statsErr := file.Stat()
		if statsErr != nil {
			return nil, statsErr
		}
		size := stats.Size()
		data := make([]byte, size)
		file.Read(data)
		httpParams := &HTTPParams{"POST", resource, request.ContentType, data}
		return processRequest(httpParams)
	}

	if request.FileContents != nil {
		httpParams := &HTTPParams{"POST", resource, request.ContentType, request.FileContents}
		return processRequest(httpParams)
		// } else {
		// return nil, errors.New("Must provide a filename or contents")
	}

	return nil, errors.New("Must provide a filename or contents")
}

// Provides a common facility for doing a PUT on a Wit resource.
//
//		result, err := put("https://api.wit.ai/entities", entity)
func put(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"PUT", resource, "application/json", data}
	return processRequest(httpParams)
}

// Processes an HTTP request to the Wit API
func processRequest(httpParams *HTTPParams) ([]byte, error) {
	regex := regexp.MustCompile(`\?`)
	if regex.MatchString(httpParams.Resource) {
		httpParams.Resource += "&" + APIVersion
	} else {
		httpParams.Resource += "?" + APIVersion
	}
	reader := bytes.NewReader(httpParams.Data)
	httpClient := &http.Client{}
	req, err := http.NewRequest(httpParams.Verb, httpParams.Resource, reader)
	if err != nil {
		return nil, err
	}
	setHeaders(req, httpParams.ContentType)
	result, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(result.Body)
	result.Body.Close()
	if result.StatusCode != 200 {
		return nil, errors.New(http.StatusText(result.StatusCode))
	}
	return body, nil
}

// Sets the custom headers required for the Wit.ai API
//
//		setHeaders(req, httpParams.ContentType)
func setHeaders(req *http.Request, contentType string) {
	req.Header.Add("Authorization", "Bearer "+APIKey)
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Accept", "application/json")
}
<commit_msg>Lowercased error message<commit_after>// Copyright (c) 2014 Jason Goecke
// client.go

package wit

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
)

const (
	// UserAgent is the HTTP Uesr Agent sent on HTTP requests
	UserAgent = "WIT (Go net/http)"
	// APIVersion is the version of the Wit API supported
	APIVersion = "v=20140401"
)

// Client represents a client for the Wit API (https://wit.ai/docs/api)
type Client struct {
	APIBase string
}

// HTTPParams represents the HTTP parameters to pass along to the Wit API
type HTTPParams struct {
	Verb        string
	Resource    string
	ContentType string
	Data        []byte
}

// Stores the ApiKey for the Wit API
var APIKey string

// NewClient creates a new client for the Wit API
//
//		client := wit.NewClient("<ACCESS-TOKEN>")
func NewClient(apiKey string) *Client {
	client := &Client{APIBase: "https://api.wit.ai"}
	APIKey = apiKey
	return client
}

// Provides a common facility for doing a DELETE on a Wit resource
//
//		result, err := delete("https://api.wit.ai/entities", "favorite_city")
func delete(resource string, id string) ([]byte, error) {
	httpParams := &HTTPParams{
		Resource: resource + "/" + id,
		Verb:     "DELETE",
	}
	return processRequest(httpParams)
}

// Provides a common facility for doing a GET on a Wit resource
//
//		result, err := get("https://api.wit.ai/entities/favorite_city")
func get(resource string) ([]byte, error) {
	httpParams := &HTTPParams{
		Resource: resource,
		Verb:     "GET",
	}
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST on a Wit resource. Takes
// JSON []byte for the data argument.
//
//		result, err := post("https://api.wit.ai/entities", entity)
func post(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"POST", resource, "application/json", data}
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST with a file on a Wit resource.
//
//		result, err := postFile("https://api.wit.ai/messages", message)
func postFile(resource string, request *MessageRequest) ([]byte, error) {
	if request.File != "" {
		file, err := os.Open(request.File)
		if err != nil {
			return nil, err
		}
		defer file.Close()
		stats, statsErr := file.Stat()
		if statsErr != nil {
			return nil, statsErr
		}
		size := stats.Size()
		data := make([]byte, size)
		file.Read(data)
		httpParams := &HTTPParams{"POST", resource, request.ContentType, data}
		return processRequest(httpParams)
	}

	if request.FileContents != nil {
		httpParams := &HTTPParams{"POST", resource, request.ContentType, request.FileContents}
		return processRequest(httpParams)
		// } else {
		// return nil, errors.New("Must provide a filename or contents")
	}

	return nil, errors.New("must provide a filename or contents")
}

// Provides a common facility for doing a PUT on a Wit resource.
//
//		result, err := put("https://api.wit.ai/entities", entity)
func put(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"PUT", resource, "application/json", data}
	return processRequest(httpParams)
}

// Processes an HTTP request to the Wit API
func processRequest(httpParams *HTTPParams) ([]byte, error) {
	regex := regexp.MustCompile(`\?`)
	if regex.MatchString(httpParams.Resource) {
		httpParams.Resource += "&" + APIVersion
	} else {
		httpParams.Resource += "?" + APIVersion
	}
	reader := bytes.NewReader(httpParams.Data)
	httpClient := &http.Client{}
	req, err := http.NewRequest(httpParams.Verb, httpParams.Resource, reader)
	if err != nil {
		return nil, err
	}
	setHeaders(req, httpParams.ContentType)
	result, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(result.Body)
	result.Body.Close()
	if result.StatusCode != 200 {
		return nil, errors.New(http.StatusText(result.StatusCode))
	}
	return body, nil
}

// Sets the custom headers required for the Wit.ai API
//
//		setHeaders(req, httpParams.ContentType)
func setHeaders(req *http.Request, contentType string) {
	req.Header.Add("Authorization", "Bearer "+APIKey)
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Accept", "application/json")
}
<|endoftext|>