<commit_before>package main

import (
	//import libraries
	"fmt"
	"net/http"
	"io/ioutil"
	"strings"
	"time"
	"bufio"
	"os"
)

type Server struct {
	health bool
	service string
}

type Servers map[string]*Server

func main() {
	servers := make(Servers)

	addserver(servers, "http://cheesy-fries.mit.edu/health", "service")
	addserver(servers, "http://strawberry-habanero.mit.edu/health", "service")

	loopservers(servers, 100, 500)

	//takes user input command to add or remove server
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		var input = scanner.Text()
    	fmt.Println("Executing: ",input)
    	words := strings.Fields(input)

	    if strings.Contains(input, "rmserver"){
	    	rmserver(servers, words[1])
	    	fmt.Println(servers)
	    }

	    if strings.Contains(input, "addserver"){
	    	addserver(servers, words[1], words[2])
	    	fmt.Println(servers)
    	}
	}
}

//adds server to servers hash table
func addserver(servers map[string]*Server, url string, service string) {
	servers[url] = &Server{false, service}
}

//removes server from servers hash table
func rmserver(servers map[string]*Server, url string){
	delete(servers, url)
}

//runs health checks on all servers
func loopservers(servers map[string]*Server, num float64, timeout int){
	for k:= range servers{
		go loop(servers, k, num, timeout)
	}
}

//runs health check on a single server
func loop(servers map[string]*Server, url string, num float64, timeout int) {
	count := 0
	boo := true

	for boo{
		num := time.Duration(num)

		time.Sleep(num * time.Millisecond)
		//fmt.Println(url, health(url), "\n", count, servers)

		if health(url) != true{
			count += 1
			fmt.Println(count)
		}

		if health(url) == true {
			count = 0
			servers[url].health = true
		}

		if count >= timeout{ //change this later
			servers[url].health = false
		}

	}
}

//checks health of server
func health(url string) bool{
	resp, _ := http.Get(url)
	bytes, _ := ioutil.ReadAll(resp.Body)

	resp.Body.Close()

	if resp == nil {
		return false
	}

	if strings.Contains(string(bytes),"healthy") {
		return true
	}

	return false
}<commit_msg>converted spaces to tabs<commit_after>package main

import (
	//import libraries
	"fmt"
	"net/http"
	"io/ioutil"
	"strings"
	"time"
	"bufio"
	"os"
)

type Server struct {
	health bool
	service string
}

type Servers map[string]*Server

func main() {
	servers := make(Servers)

	addserver(servers, "http://cheesy-fries.mit.edu/health", "service")
	addserver(servers, "http://strawberry-habanero.mit.edu/health", "service")

	loopservers(servers, 100, 500)

	//takes user input command to add or remove server
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		var input = scanner.Text()
		fmt.Println("Executing: ",input)
		words := strings.Fields(input)

		if strings.Contains(input, "rmserver"){
			rmserver(servers, words[1])
			fmt.Println(servers)
		}

		if strings.Contains(input, "addserver"){
			addserver(servers, words[1], words[2])
			fmt.Println(servers)
		}
	}
}

//adds server to servers hash table
func addserver(servers map[string]*Server, url string, service string) {
	servers[url] = &Server{false, service}
}

//removes server from servers hash table
func rmserver(servers map[string]*Server, url string){
	delete(servers, url)
}

//runs health checks on all servers
func loopservers(servers map[string]*Server, num float64, timeout int){
	for k:= range servers{
		go loop(servers, k, num, timeout)
	}
}

//runs health check on a single server
func loop(servers map[string]*Server, url string, num float64, timeout int) {
	count := 0
	boo := true

	for boo{
		num := time.Duration(num)

		time.Sleep(num * time.Millisecond)
		//fmt.Println(url, health(url), "\n", count, servers)

		if health(url) != true{
			count += 1
			fmt.Println(count)
		}

		if health(url) == true {
			count = 0
			servers[url].health = true
		}

		if count >= timeout{ //change this later
			servers[url].health = false
		}

	}
}

//checks health of server
func health(url string) bool{
	resp, _ := http.Get(url)
	bytes, _ := ioutil.ReadAll(resp.Body)

	resp.Body.Close()

	if resp == nil {
		return false
	}

	if strings.Contains(string(bytes),"healthy") {
		return true
	}

	return false
}<|endoftext|>


<commit_before>package gogadgets

import (
	"time"
)

//Heater represents an electic heating element.  It
//provides a way to heat up something to a target
//temperature. In order to use this there must be
//a thermometer in the same Location.
type Heater struct {
	onTime      time.Duration
	offTime     time.Duration
	toggleTime  time.Duration
	waitTime    time.Duration
	t1          time.Time
	target      float64
	currentTemp float64
	duration    time.Duration
	status      bool
	gpioStatus  bool
	doPWM       bool
	gpio        OutputDevice
	io          chan *Value
	update      chan *Message
	started     bool
}

func NewHeater(pin *Pin) (OutputDevice, error) {
	var h *Heater
	var err error
	var dev OutputDevice
	doPWM := pin.Args["pwm"] == true
	if pin.Frequency == 0 {
		pin.Frequency = 1
	}
	dev, err = newGPIO(pin)
	if err == nil {
		h = &Heater{
			toggleTime: 100 * time.Hour,
			gpio:       dev,
			target:     100.0,
			doPWM:      doPWM,
			io:         make(chan *Value),
			update:     make(chan *Message),
		}
	}
	return h, err
}

func (h *Heater) Commands(location, name string) *Commands {
	return nil
}

func (h *Heater) Update(msg *Message) bool {
	var ret bool
	if h.status && msg.Name == "temperature" {
		ret = true
		h.update <- msg
	} else {
		h.readTemperature(msg)
	}
	return ret
}

func (h *Heater) On(val *Value) error {
	h.status = true
	if !h.started {
		h.started = true
		go h.toggle(h.io, h.update)
	}
	if val == nil {
		val = &Value{Value: true}
	}
	h.io <- val
	return nil
}

func (h *Heater) Status() map[string]bool {
	return h.gpio.Status()
}

func (h *Heater) Off() error {
	if h.started {
		h.target = 0.0
		h.status = false
		h.io <- &Value{Value: false}
	}
	return nil
}

/*
The pwm drivers on beaglebone black seem to be
broken.  This function brings the same functionality
using gpio.
*/
func (h *Heater) toggle(value chan *Value, update chan *Message) {
	for {
		select {
		case val := <-value:
			switch v := val.Value.(type) {
			case float64:
				h.waitTime = 100 * time.Millisecond
				h.getTarget(val)
				h.setDuty()
				h.status = true
				h.gpioStatus = true
				h.gpio.On(nil)
				h.t1 = time.Now()
			case bool:
				h.waitTime = 100 * time.Hour
				if v == true {
					h.status = true
					h.gpio.On(nil)
				} else {

					h.gpio.Off()
					h.target = 1000.0
					h.status = false
				}
			}
		case m := <-update:
			h.readTemperature(m)
		case _ = <-time.After(h.waitTime):
			n := time.Now()
			diff := n.Sub(h.t1)
			if h.doPWM && diff > h.toggleTime {
				h.t1 = n
				if h.gpioStatus && h.offTime > 0.0 {
					h.toggleTime = h.offTime
					h.gpio.Off()
					h.gpioStatus = false
				} else if !h.gpioStatus && h.onTime > 0.0 {
					h.toggleTime = h.onTime
					h.gpio.On(nil)
					h.gpioStatus = true
				} else {
					h.toggleTime = h.offTime
					h.gpio.Off()
					h.gpioStatus = false
				}
			}
		}
	}
}

func (h *Heater) getTarget(val *Value) {
	if val != nil {
		t, ok := val.ToFloat()
		if ok {
			h.target = t
		}
	}
}

func (h *Heater) readTemperature(msg *Message) {
	if msg.Name != "temperature" {
		return
	}
	temp, ok := msg.Value.ToFloat()
	if ok {
		h.currentTemp = temp
		if h.status {
			h.setDuty()
		}
	}
}

//Once the heater approaches the target temperature the electricity
//is applied PWM style so the target temperature isn't overshot.
//This functionality is geared towards heating up a tank of water
//and can be disabled if you are using this component to heat something
//else, like a house.
func (h *Heater) setDuty() {
	diff := h.target - h.currentTemp
	if diff <= 0.0 {
		h.onTime = 0
		h.offTime = 1 * time.Second
	} else if diff <= 1.0 {
		h.onTime = 1 * time.Second
		h.offTime = 3 * time.Second
	} else if diff <= 2.0 {
		h.onTime = 2 * time.Second
		h.offTime = 2 * time.Second
	} else {
		h.onTime = 4 * time.Second
		h.offTime = 0 * time.Second
	}
	if h.gpioStatus {
		h.toggleTime = h.onTime
	} else {
		h.toggleTime = h.offTime
	}
}
<commit_msg>heater can be turned on with '%' as units<commit_after>package gogadgets

import (
	"time"
)

//Heater represents an electic heating element.  It
//provides a way to heat up something to a target
//temperature. In order to use this there must be
//a thermometer in the same Location.
type Heater struct {
	onTime      time.Duration
	offTime     time.Duration
	toggleTime  time.Duration
	waitTime    time.Duration
	t1          time.Time
	target      float64
	percentage  bool
	currentTemp float64
	duration    time.Duration
	status      bool
	gpioStatus  bool
	doPWM       bool
	gpio        OutputDevice
	io          chan *Value
	update      chan *Message
	started     bool
}

func NewHeater(pin *Pin) (OutputDevice, error) {
	var h *Heater
	var err error
	var dev OutputDevice
	doPWM := pin.Args["pwm"] == true
	if pin.Frequency == 0 {
		pin.Frequency = 1
	}
	dev, err = newGPIO(pin)
	if err == nil {
		h = &Heater{
			toggleTime: 100 * time.Hour,
			gpio:       dev,
			target:     100.0,
			doPWM:      doPWM,
			io:         make(chan *Value),
			update:     make(chan *Message),
		}
	}
	return h, err
}

func (h *Heater) Commands(location, name string) *Commands {
	return nil
}

func (h *Heater) Update(msg *Message) bool {
	var ret bool
	if h.status && msg.Name == "temperature" {
		ret = true
		h.update <- msg
	} else {
		h.readTemperature(msg)
	}
	return ret
}

func (h *Heater) On(val *Value) error {
	h.status = true
	if !h.started {
		h.started = true
		go h.toggle(h.io, h.update)
	}
	if val == nil {
		val = &Value{Value: true}
	}
	h.io <- val
	return nil
}

func (h *Heater) Status() map[string]bool {
	return h.gpio.Status()
}

func (h *Heater) Off() error {
	if h.started {
		h.target = 0.0
		h.percentage = false
		h.status = false
		h.io <- &Value{Value: false}
	}
	return nil
}

/*
The pwm drivers on beaglebone black seem to be
broken.  This function brings the same functionality
using gpio.
*/
func (h *Heater) toggle(value chan *Value, update chan *Message) {
	for {
		select {
		case val := <-value:
			switch v := val.Value.(type) {
			case float64:
				if val.Units == "%" {
					h.target = 1000.0
					h.percentage = true
					d := time.Duration(v) * time.Millisecond * 10
					h.onTime = d * 4
					h.offTime = (4 * time.Second) - h.onTime
				} else {
					h.percentage = false
					h.getTarget(val)
					h.setDuty()
					h.t1 = time.Now()
				}

				h.waitTime = 100 * time.Millisecond
				h.status = true
				h.gpioStatus = true
				h.gpio.On(nil)
			case bool:
				h.waitTime = 100 * time.Hour
				if v == true {
					h.status = true
					h.gpio.On(nil)
				} else {
					h.gpio.Off()
					h.target = 1000.0
					h.percentage = false
					h.status = false
				}
			}
		case m := <-update:
			h.readTemperature(m)
		case _ = <-time.After(h.waitTime):
			n := time.Now()
			diff := n.Sub(h.t1)
			if h.doPWM && diff > h.toggleTime {
				h.t1 = n
				if h.gpioStatus && h.offTime > 0.0 {
					h.toggleTime = h.offTime
					h.gpio.Off()
					h.gpioStatus = false
				} else if !h.gpioStatus && h.onTime > 0.0 {
					h.toggleTime = h.onTime
					h.gpio.On(nil)
					h.gpioStatus = true
				} else {
					h.toggleTime = h.offTime
					h.gpio.Off()
					h.gpioStatus = false
				}
			}
		}
	}
}

func (h *Heater) getTarget(val *Value) {
	if val != nil {
		t, ok := val.ToFloat()
		if ok {
			h.target = t
			h.percentage = false
		}
	}
}

func (h *Heater) readTemperature(msg *Message) {
	if msg.Name != "temperature" {
		return
	}
	temp, ok := msg.Value.ToFloat()
	if ok {
		h.currentTemp = temp
		if h.status {
			h.setDuty()
		}
	}
}

//Once the heater approaches the target temperature the electricity
//is applied PWM style so the target temperature isn't overshot.
//This functionality is geared towards heating up a tank of water
//and can be disabled if you are using this component to heat something
//else, like a house.
func (h *Heater) setDuty() {
	if h.percentage {
		return
	}

	diff := h.target - h.currentTemp
	if diff <= 0.0 {
		h.onTime = 0
		h.offTime = 1 * time.Second
	} else if diff <= 1.0 {
		h.onTime = 1 * time.Second
		h.offTime = 3 * time.Second
	} else if diff <= 2.0 {
		h.onTime = 2 * time.Second
		h.offTime = 2 * time.Second
	} else {
		h.onTime = 4 * time.Second
		h.offTime = 0 * time.Second
	}
	if h.gpioStatus {
		h.toggleTime = h.onTime
	} else {
		h.toggleTime = h.offTime
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 xsrf

import (
	"context"
	"github.com/google/go-cmp/cmp"
	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/safehttptest"
	"golang.org/x/net/xsrftoken"
	"strings"
	"testing"
)

type userIdentifier struct{}

func (userIdentifier) UserID(r *safehttp.IncomingRequest) (string, error) {
	return "1234", nil
}

var (
	formTokenTests = []struct {
		name, userID, actionID, wantBody string
		wantStatus                       safehttp.StatusCode
		wantHeader                       map[string][]string
	}{
		{
			name:       "Valid token",
			userID:     "1234",
			actionID:   "POST /pizza",
			wantStatus: safehttp.StatusOK,
			wantHeader: map[string][]string{},
			wantBody:   "",
		},
		{
			name:       "Invalid actionID in token generation",
			userID:     "1234",
			actionID:   "HEAD /pizza",
			wantStatus: safehttp.StatusForbidden,
			wantHeader: map[string][]string{
				"Content-Type":           {"text/plain; charset=utf-8"},
				"X-Content-Type-Options": {"nosniff"},
			},
			wantBody: "Forbidden\n",
		},
		{
			name:       "Invalid userID in token generation",
			userID:     "5678",
			actionID:   "POST /pizza",
			wantStatus: safehttp.StatusForbidden,
			wantHeader: map[string][]string{
				"Content-Type":           {"text/plain; charset=utf-8"},
				"X-Content-Type-Options": {"nosniff"},
			},
			wantBody: "Forbidden\n",
		},
	}
)

func TestTokenPost(t *testing.T) {
	for _, test := range formTokenTests {
		t.Run(test.name, func(t *testing.T) {
			rec := safehttptest.NewResponseRecorder()
			tok := xsrftoken.Generate("xsrf", test.userID, test.actionID)
			req := safehttptest.NewRequest(safehttp.MethodPost, "https://foo.com/pizza", strings.NewReader(TokenKey+"="+tok))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

			i := Interceptor{AppKey: "xsrf", Identifier: userIdentifier{}}
			i.Before(rec.ResponseWriter, req, nil)

			if got := rec.Status(); got != test.wantStatus {
				t.Errorf("response status: got %v, want %v", got, test.wantStatus)
			}
			if diff := cmp.Diff(test.wantHeader, map[string][]string(rec.Header())); diff != "" {
				t.Errorf("rec.Header() mismatch (-want +got):\n%s", diff)
			}
			if got := rec.Body(); got != test.wantBody {
				t.Errorf("response body: got %q want %q", got, test.wantBody)
			}
		})
	}
}

func TestTokenMultipart(t *testing.T) {
	for _, test := range formTokenTests {
		t.Run(test.name, func(t *testing.T) {
			rec := safehttptest.NewResponseRecorder()
			tok := xsrftoken.Generate("xsrf", test.userID, test.actionID)
			b := "--123\r\n" +
				"Content-Disposition: form-data; name=\"xsrf-token\"\r\n" +
				"\r\n" +
				tok + "\r\n" +
				"--123--\r\n"
			req := safehttptest.NewRequest(safehttp.MethodPost, "https://foo.com/pizza", strings.NewReader(b))
			req.Header.Set("Content-Type", `multipart/form-data; boundary="123"`)

			i := Interceptor{AppKey: "xsrf", Identifier: userIdentifier{}}
			i.Before(rec.ResponseWriter, req, nil)

			if got := rec.Status(); got != test.wantStatus {
				t.Errorf("response status: got %v, want %v", got, test.wantStatus)
			}
			if diff := cmp.Diff(test.wantHeader, map[string][]string(rec.Header())); diff != "" {
				t.Errorf("rw.header mismatch (-want +got):\n%s", diff)
			}
			if got := rec.Body(); got != test.wantBody {
				t.Errorf("response body: got %q want %q", got, test.wantBody)
			}
		})
	}
}

func TestMissingTokenInBody(t *testing.T) {
	tests := []struct {
		name string
		req  *safehttp.IncomingRequest
	}{
		{
			name: "Missing token in POST request with form",
			req: func() *safehttp.IncomingRequest {
				req := safehttptest.NewRequest(safehttp.MethodPost, "/", strings.NewReader("foo=bar"))
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
				return req
			}(),
		},
		{
			name: "Missing token in PATCH request with form",
			req: func() *safehttp.IncomingRequest {
				req := safehttptest.NewRequest(safehttp.MethodPatch, "/", strings.NewReader("foo=bar"))
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
				return req
			}(),
		},
		{
			name: "Missing token in POST request with multipart form",
			req: func() *safehttp.IncomingRequest {
				b := "--123\r\n" +
					"Content-Disposition: form-data; name=\"foo\"\r\n" +
					"\r\n" +
					"bar\r\n" +
					"--123--\r\n"
				req := safehttptest.NewRequest(safehttp.MethodPost, "/", strings.NewReader(b))
				req.Header.Set("Content-Type", `multipart/form-data; boundary="123"`)
				return req
			}(),
		},
		{
			name: "Missing token in PATCH request with multipart form",
			req: func() *safehttp.IncomingRequest {
				b := "--123\r\n" +
					"Content-Disposition: form-data; name=\"foo\"\r\n" +
					"\r\n" +
					"bar\r\n" +
					"--123--\r\n"
				req := safehttptest.NewRequest(safehttp.MethodPatch, "/", strings.NewReader(b))
				req.Header.Set("Content-Type", `multipart/form-data; boundary="123"`)
				return req
			}(),
		},
	}
	for _, test := range tests {
		rec := safehttptest.NewResponseRecorder()

		i := Interceptor{AppKey: "xsrf", Identifier: userIdentifier{}}
		i.Before(rec.ResponseWriter, test.req, nil)

		if want, got := safehttp.StatusUnauthorized, rec.Status(); got != want {
			t.Errorf("response status: got %v, want %v", got, want)
		}
		wantHeaders := map[string][]string{
			"Content-Type":           {"text/plain; charset=utf-8"},
			"X-Content-Type-Options": {"nosniff"},
		}
		if diff := cmp.Diff(wantHeaders, map[string][]string(rec.Header())); diff != "" {
			t.Errorf("rw.header mismatch (-want +got):\n%s", diff)
		}
		if want, got := "Unauthorized\n", rec.Body(); got != want {
			t.Errorf("response body: got %q want %q", got, want)
		}
	}
}

func TestBeforeTokenInRequestContext(t *testing.T) {
	rec := safehttptest.NewResponseRecorder()
	req := safehttptest.NewRequest(safehttp.MethodGet, "https://foo.com/pizza", nil)

	i := Interceptor{AppKey: "xsrf", Identifier: userIdentifier{}}
	i.Before(rec.ResponseWriter, req, nil)

	tok, err := Token(req)
	if tok == "" {
		t.Error(`Token(req): got "", want token`)
	}
	if err != nil {
		t.Errorf("Token(req): got %v, want nil", err)
	}

	if want, got := safehttp.StatusOK, safehttp.StatusCode(rec.Status()); want != got {
		t.Errorf("response status: got %v, want %v", got, want)
	}
	if diff := cmp.Diff(map[string][]string{}, map[string][]string(rec.Header())); diff != "" {
		t.Errorf("rec.Header() mismatch (-want +got):\n%s", diff)
	}
	if want, got := "", rec.Body(); got != want {
		t.Errorf("response body: got %q want %q", got, want)
	}

}

func TestTokenInRequestContext(t *testing.T) {
	req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)
	req.SetContext(context.WithValue(req.Context(), tokenCtxKey{}, "pizza"))

	got, err := Token(req)
	if want := "pizza"; want != got {
		t.Errorf("Token(req): got %v, want %v", got, want)
	}
	if err != nil {
		t.Errorf("Token(req): got %v, want nil", err)
	}
}

func TestMissingTokenInRequestContext(t *testing.T) {
	req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)
	req.SetContext(context.Background())

	got, err := Token(req)
	if want := ""; want != got {
		t.Errorf("Token(req): got %v, want %v", got, want)
	}
	if err == nil {
		t.Error("Token(req): got nil, want error")
	}
}
<commit_msg>Rename AppKey in tests<commit_after>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 xsrf

import (
	"context"
	"github.com/google/go-cmp/cmp"
	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/safehttptest"
	"golang.org/x/net/xsrftoken"
	"strings"
	"testing"
)

type userIdentifier struct{}

func (userIdentifier) UserID(r *safehttp.IncomingRequest) (string, error) {
	return "1234", nil
}

var (
	formTokenTests = []struct {
		name, userID, actionID, wantBody string
		wantStatus                       safehttp.StatusCode
		wantHeader                       map[string][]string
	}{
		{
			name:       "Valid token",
			userID:     "1234",
			actionID:   "POST /pizza",
			wantStatus: safehttp.StatusOK,
			wantHeader: map[string][]string{},
			wantBody:   "",
		},
		{
			name:       "Invalid actionID in token generation",
			userID:     "1234",
			actionID:   "HEAD /pizza",
			wantStatus: safehttp.StatusForbidden,
			wantHeader: map[string][]string{
				"Content-Type":           {"text/plain; charset=utf-8"},
				"X-Content-Type-Options": {"nosniff"},
			},
			wantBody: "Forbidden\n",
		},
		{
			name:       "Invalid userID in token generation",
			userID:     "5678",
			actionID:   "POST /pizza",
			wantStatus: safehttp.StatusForbidden,
			wantHeader: map[string][]string{
				"Content-Type":           {"text/plain; charset=utf-8"},
				"X-Content-Type-Options": {"nosniff"},
			},
			wantBody: "Forbidden\n",
		},
	}
)

func TestTokenPost(t *testing.T) {
	for _, test := range formTokenTests {
		t.Run(test.name, func(t *testing.T) {
			rec := safehttptest.NewResponseRecorder()
			tok := xsrftoken.Generate("testAppKey", test.userID, test.actionID)
			req := safehttptest.NewRequest(safehttp.MethodPost, "https://foo.com/pizza", strings.NewReader(TokenKey+"="+tok))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

			i := Interceptor{AppKey: "testAppKey", Identifier: userIdentifier{}}
			i.Before(rec.ResponseWriter, req, nil)

			if got := rec.Status(); got != test.wantStatus {
				t.Errorf("response status: got %v, want %v", got, test.wantStatus)
			}
			if diff := cmp.Diff(test.wantHeader, map[string][]string(rec.Header())); diff != "" {
				t.Errorf("rec.Header() mismatch (-want +got):\n%s", diff)
			}
			if got := rec.Body(); got != test.wantBody {
				t.Errorf("response body: got %q want %q", got, test.wantBody)
			}
		})
	}
}

func TestTokenMultipart(t *testing.T) {
	for _, test := range formTokenTests {
		t.Run(test.name, func(t *testing.T) {
			rec := safehttptest.NewResponseRecorder()
			tok := xsrftoken.Generate("testAppKey", test.userID, test.actionID)
			b := "--123\r\n" +
				"Content-Disposition: form-data; name=\"xsrf-token\"\r\n" +
				"\r\n" +
				tok + "\r\n" +
				"--123--\r\n"
			req := safehttptest.NewRequest(safehttp.MethodPost, "https://foo.com/pizza", strings.NewReader(b))
			req.Header.Set("Content-Type", `multipart/form-data; boundary="123"`)

			i := Interceptor{AppKey: "testAppKey", Identifier: userIdentifier{}}
			i.Before(rec.ResponseWriter, req, nil)

			if got := rec.Status(); got != test.wantStatus {
				t.Errorf("response status: got %v, want %v", got, test.wantStatus)
			}
			if diff := cmp.Diff(test.wantHeader, map[string][]string(rec.Header())); diff != "" {
				t.Errorf("rw.header mismatch (-want +got):\n%s", diff)
			}
			if got := rec.Body(); got != test.wantBody {
				t.Errorf("response body: got %q want %q", got, test.wantBody)
			}
		})
	}
}

func TestMissingTokenInBody(t *testing.T) {
	tests := []struct {
		name string
		req  *safehttp.IncomingRequest
	}{
		{
			name: "Missing token in POST request with form",
			req: func() *safehttp.IncomingRequest {
				req := safehttptest.NewRequest(safehttp.MethodPost, "/", strings.NewReader("foo=bar"))
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
				return req
			}(),
		},
		{
			name: "Missing token in PATCH request with form",
			req: func() *safehttp.IncomingRequest {
				req := safehttptest.NewRequest(safehttp.MethodPatch, "/", strings.NewReader("foo=bar"))
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
				return req
			}(),
		},
		{
			name: "Missing token in POST request with multipart form",
			req: func() *safehttp.IncomingRequest {
				b := "--123\r\n" +
					"Content-Disposition: form-data; name=\"foo\"\r\n" +
					"\r\n" +
					"bar\r\n" +
					"--123--\r\n"
				req := safehttptest.NewRequest(safehttp.MethodPost, "/", strings.NewReader(b))
				req.Header.Set("Content-Type", `multipart/form-data; boundary="123"`)
				return req
			}(),
		},
		{
			name: "Missing token in PATCH request with multipart form",
			req: func() *safehttp.IncomingRequest {
				b := "--123\r\n" +
					"Content-Disposition: form-data; name=\"foo\"\r\n" +
					"\r\n" +
					"bar\r\n" +
					"--123--\r\n"
				req := safehttptest.NewRequest(safehttp.MethodPatch, "/", strings.NewReader(b))
				req.Header.Set("Content-Type", `multipart/form-data; boundary="123"`)
				return req
			}(),
		},
	}
	for _, test := range tests {
		rec := safehttptest.NewResponseRecorder()

		i := Interceptor{AppKey: "testAppKey", Identifier: userIdentifier{}}
		i.Before(rec.ResponseWriter, test.req, nil)

		if want, got := safehttp.StatusUnauthorized, rec.Status(); got != want {
			t.Errorf("response status: got %v, want %v", got, want)
		}
		wantHeaders := map[string][]string{
			"Content-Type":           {"text/plain; charset=utf-8"},
			"X-Content-Type-Options": {"nosniff"},
		}
		if diff := cmp.Diff(wantHeaders, map[string][]string(rec.Header())); diff != "" {
			t.Errorf("rw.header mismatch (-want +got):\n%s", diff)
		}
		if want, got := "Unauthorized\n", rec.Body(); got != want {
			t.Errorf("response body: got %q want %q", got, want)
		}
	}
}

func TestBeforeTokenInRequestContext(t *testing.T) {
	rec := safehttptest.NewResponseRecorder()
	req := safehttptest.NewRequest(safehttp.MethodGet, "https://foo.com/pizza", nil)

	i := Interceptor{AppKey: "testAppKey", Identifier: userIdentifier{}}
	i.Before(rec.ResponseWriter, req, nil)

	tok, err := Token(req)
	if tok == "" {
		t.Error(`Token(req): got "", want token`)
	}
	if err != nil {
		t.Errorf("Token(req): got %v, want nil", err)
	}

	if want, got := safehttp.StatusOK, safehttp.StatusCode(rec.Status()); want != got {
		t.Errorf("response status: got %v, want %v", got, want)
	}
	if diff := cmp.Diff(map[string][]string{}, map[string][]string(rec.Header())); diff != "" {
		t.Errorf("rec.Header() mismatch (-want +got):\n%s", diff)
	}
	if want, got := "", rec.Body(); got != want {
		t.Errorf("response body: got %q want %q", got, want)
	}

}

func TestTokenInRequestContext(t *testing.T) {
	req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)
	req.SetContext(context.WithValue(req.Context(), tokenCtxKey{}, "pizza"))

	got, err := Token(req)
	if want := "pizza"; want != got {
		t.Errorf("Token(req): got %v, want %v", got, want)
	}
	if err != nil {
		t.Errorf("Token(req): got %v, want nil", err)
	}
}

func TestMissingTokenInRequestContext(t *testing.T) {
	req := safehttptest.NewRequest(safehttp.MethodGet, "/", nil)
	req.SetContext(context.Background())

	got, err := Token(req)
	if want := ""; want != got {
		t.Errorf("Token(req): got %v, want %v", got, want)
	}
	if err == nil {
		t.Error("Token(req): got nil, want error")
	}
}
<|endoftext|>


<commit_before>package metainfo

import (
	"crypto/sha1"
	"io"
	"os"

	"github.com/anacrolix/libtorgo/bencode"
)

// Information specific to a single file inside the MetaInfo structure.
type FileInfo struct {
	Length int64    `bencode:"length"`
	Path   []string `bencode:"path"`
}

// Load a MetaInfo from an io.Reader. Returns a non-nil error in case of
// failure.
func Load(r io.Reader) (*MetaInfo, error) {
	var mi MetaInfo
	d := bencode.NewDecoder(r)
	err := d.Decode(&mi)
	if err != nil {
		return nil, err
	}
	return &mi, nil
}

// Convenience function for loading a MetaInfo from a file.
func LoadFromFile(filename string) (*MetaInfo, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return Load(f)
}

// The info dictionary.
type Info struct {
	PieceLength int64      `bencode:"piece length"`
	Pieces      []byte     `bencode:"pieces"`
	Name        string     `bencode:"name"`
	Length      int64      `bencode:"length,omitempty"`
	Private     bool       `bencode:"private,omitempty"`
	Files       []FileInfo `bencode:"files,omitempty"`
}

// The info dictionary with its hash and raw bytes exposed, as these are
// important to Bittorrent.
type InfoEx struct {
	Info
	Hash  []byte
	Bytes []byte
}

var (
	_ bencode.Marshaler   = InfoEx{}
	_ bencode.Unmarshaler = &InfoEx{}
)

func (this *InfoEx) UnmarshalBencode(data []byte) error {
	this.Bytes = make([]byte, 0, len(data))
	this.Bytes = append(this.Bytes, data...)
	h := sha1.New()
	_, err := h.Write(this.Bytes)
	if err != nil {
		panic(err)
	}
	this.Hash = h.Sum(nil)
	return bencode.Unmarshal(data, &this.Info)
}

func (this InfoEx) MarshalBencode() ([]byte, error) {
	if this.Bytes != nil {
		return this.Bytes, nil
	}
	return bencode.Marshal(&this.Info)
}

type MetaInfo struct {
	Info         InfoEx      `bencode:"info"`
	Announce     string      `bencode:"announce"`
	AnnounceList [][]string  `bencode:"announce-list,omitempty"`
	CreationDate int64       `bencode:"creation date,omitempty"`
	Comment      string      `bencode:"comment,omitempty"`
	CreatedBy    string      `bencode:"created by,omitempty"`
	Encoding     string      `bencode:"encoding,omitempty"`
	URLList      interface{} `bencode:"url-list,omitempty"`
}
<commit_msg>Add UpvertedFiles() to Info to make single-file torrents usable like multi-file torrents.<commit_after>package metainfo

import (
	"crypto/sha1"
	"io"
	"os"

	"github.com/anacrolix/libtorgo/bencode"
)

// Information specific to a single file inside the MetaInfo structure.
type FileInfo struct {
	Length int64    `bencode:"length"`
	Path   []string `bencode:"path"`
}

// Load a MetaInfo from an io.Reader. Returns a non-nil error in case of
// failure.
func Load(r io.Reader) (*MetaInfo, error) {
	var mi MetaInfo
	d := bencode.NewDecoder(r)
	err := d.Decode(&mi)
	if err != nil {
		return nil, err
	}
	return &mi, nil
}

// Convenience function for loading a MetaInfo from a file.
func LoadFromFile(filename string) (*MetaInfo, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return Load(f)
}

// The info dictionary.
type Info struct {
	PieceLength int64      `bencode:"piece length"`
	Pieces      []byte     `bencode:"pieces"`
	Name        string     `bencode:"name"`
	Length      int64      `bencode:"length,omitempty"`
	Private     bool       `bencode:"private,omitempty"`
	Files       []FileInfo `bencode:"files,omitempty"`
}

// The files field, converted up from the old single-file in the parent info
// dict if necessary. This is a helper to avoid having to conditionally handle
// single and multi-file torrent infos.
func (i *Info) UpvertedFiles() []FileInfo {
	if len(i.Files) == 0 {
		return []FileInfo{{
			Length: i.Length,
			Path:   []string{i.Name},
		}}
	}
	return i.Files
}

// The info dictionary with its hash and raw bytes exposed, as these are
// important to Bittorrent.
type InfoEx struct {
	Info
	Hash  []byte
	Bytes []byte
}

var (
	_ bencode.Marshaler   = InfoEx{}
	_ bencode.Unmarshaler = &InfoEx{}
)

func (this *InfoEx) UnmarshalBencode(data []byte) error {
	this.Bytes = make([]byte, 0, len(data))
	this.Bytes = append(this.Bytes, data...)
	h := sha1.New()
	_, err := h.Write(this.Bytes)
	if err != nil {
		panic(err)
	}
	this.Hash = h.Sum(nil)
	return bencode.Unmarshal(data, &this.Info)
}

func (this InfoEx) MarshalBencode() ([]byte, error) {
	if this.Bytes != nil {
		return this.Bytes, nil
	}
	return bencode.Marshal(&this.Info)
}

type MetaInfo struct {
	Info         InfoEx      `bencode:"info"`
	Announce     string      `bencode:"announce"`
	AnnounceList [][]string  `bencode:"announce-list,omitempty"`
	CreationDate int64       `bencode:"creation date,omitempty"`
	Comment      string      `bencode:"comment,omitempty"`
	CreatedBy    string      `bencode:"created by,omitempty"`
	Encoding     string      `bencode:"encoding,omitempty"`
	URLList      interface{} `bencode:"url-list,omitempty"`
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 Ashley Jeffs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package input

import (
	"strings"
	"time"

	"github.com/jeffail/benthos/types"
	"github.com/pebbe/zmq4"
)

//--------------------------------------------------------------------------------------------------

// ZMQ4Config - Configuration for the ZMQ4 input type.
type ZMQ4Config struct {
	Addresses     []string `json:"addresses"`
	SocketType    string   `json:"socket_type"`
	PollTimeoutMS int      `json:"poll_timeout_ms"`
}

// NewZMQ4Config - Creates a new ZMQ4Config with default values.
func NewZMQ4Config() ZMQ4Config {
	return ZMQ4Config{
		Addresses:     []string{"localhost:1234"},
		SocketType:    "PULL",
		PollTimeoutMS: 5000,
	}
}

//--------------------------------------------------------------------------------------------------

// ZMQ4 - An input type that serves ZMQ4 POST requests.
type ZMQ4 struct {
	conf Config

	socket *zmq4.Socket
	poller *zmq4.Poller

	messages  chan types.Message
	responses <-chan types.Response

	newResponsesChan chan (<-chan types.Response)

	closedChan chan struct{}
	closeChan  chan struct{}
}

// NewZMQ4 - Create a new ZMQ4 input type.
func NewZMQ4(conf Config) (*ZMQ4, error) {
	z := ZMQ4{
		conf:             conf,
		messages:         make(chan types.Message),
		responses:        nil,
		newResponsesChan: make(chan (<-chan types.Response)),
		closedChan:       make(chan struct{}),
		closeChan:        make(chan struct{}),
	}

	t, err := getZMQType(conf.ZMQ4.SocketType)
	if nil != err {
		return nil, err
	}

	ctx, err := zmq4.NewContext()
	if nil != err {
		return nil, err
	}

	if z.socket, err = ctx.NewSocket(t); nil != err {
		return nil, err
	}

	for _, address := range conf.ZMQ4.Addresses {
		if strings.Contains(address, "*") {
			err = z.socket.Bind(address)
		} else {
			err = z.socket.Connect(address)
		}
		if err != nil {
			return nil, err
		}
	}

	z.poller = zmq4.NewPoller()
	z.poller.Add(z.socket, zmq4.POLLIN)

	go z.loop()

	return &z, nil
}

//--------------------------------------------------------------------------------------------------

func getZMQType(t string) (zmq4.Type, error) {
	switch t {
	case "REQ":
		return zmq4.REQ, nil
	case "REP":
		return zmq4.REP, nil
	case "DEALER":
		return zmq4.DEALER, nil
	case "ROUTER":
		return zmq4.ROUTER, nil
	case "PUB":
		return zmq4.PUB, nil
	case "SUB":
		return zmq4.SUB, nil
	case "XPUB":
		return zmq4.XPUB, nil
	case "XSUB":
		return zmq4.XSUB, nil
	case "PUSH":
		return zmq4.PUSH, nil
	case "PULL":
		return zmq4.PULL, nil
	case "PAIR":
		return zmq4.PAIR, nil
	case "STREAM":
		return zmq4.STREAM, nil
	}
	return zmq4.PULL, types.ErrInvalidZMQType
}

//--------------------------------------------------------------------------------------------------

// loop - Internal loop brokers incoming messages to output pipe.
func (z *ZMQ4) loop() {
	var bytes [][]byte
	var msgChan chan<- types.Message

	pollTimeout := time.Millisecond * time.Duration(z.conf.ZMQ4.PollTimeoutMS)

	running, responsePending := true, false
	for running {
		// If no bytes then read a message
		if bytes == nil {
			polled, err := z.poller.Poll(pollTimeout)
			if err == nil && len(polled) == 1 {
				bytes, err = z.socket.RecvMessageBytes(0)
				if err != nil {
				}
			}
		}

		// If we have a line to push out
		if bytes != nil && !responsePending {
			msgChan = z.messages
		} else {
			msgChan = nil
		}

		if running {
			select {
			case msgChan <- types.Message{Parts: bytes}:
				responsePending = true
			case err, open := <-z.responses:
				if !open {
					z.responses = nil
				} else if err == nil {
					responsePending = false
					bytes = nil
				}
			case newResChan, open := <-z.newResponsesChan:
				if running = open; open {
					z.responses = newResChan
				}
			case _, running = <-z.closeChan:
			}
		}
	}

	close(z.messages)
	close(z.newResponsesChan)
	close(z.closedChan)
}

// SetResponseChan - Sets the channel used by the input to validate message receipt.
func (z *ZMQ4) SetResponseChan(responses <-chan types.Response) {
	z.newResponsesChan <- responses
}

// ConsumerChan - Returns the messages channel.
func (z *ZMQ4) ConsumerChan() <-chan types.Message {
	return z.messages
}

// CloseAsync - Shuts down the ZMQ4 input and stops processing requests.
func (z *ZMQ4) CloseAsync() {
	close(z.closeChan)
}

// WaitForClose - Blocks until the ZMQ4 input has closed down.
func (z *ZMQ4) WaitForClose(timeout time.Duration) error {
	select {
	case <-z.closedChan:
	case <-time.After(timeout):
		return types.ErrTimeout
	}
	return nil
}

//--------------------------------------------------------------------------------------------------
<commit_msg>Refactor zmq input<commit_after>/*
Copyright (c) 2014 Ashley Jeffs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package input

import (
	"strings"
	"sync/atomic"
	"time"

	"github.com/jeffail/benthos/types"
	"github.com/pebbe/zmq4"
)

//--------------------------------------------------------------------------------------------------

// ZMQ4Config - Configuration for the ZMQ4 input type.
type ZMQ4Config struct {
	Addresses     []string `json:"addresses"`
	SocketType    string   `json:"socket_type"`
	PollTimeoutMS int      `json:"poll_timeout_ms"`
}

// NewZMQ4Config - Creates a new ZMQ4Config with default values.
func NewZMQ4Config() ZMQ4Config {
	return ZMQ4Config{
		Addresses:     []string{"localhost:1234"},
		SocketType:    "PULL",
		PollTimeoutMS: 5000,
	}
}

//--------------------------------------------------------------------------------------------------

// ZMQ4 - An input type that serves ZMQ4 POST requests.
type ZMQ4 struct {
	running int32

	conf Config

	socket *zmq4.Socket

	internalMessages chan [][]byte

	messages  chan types.Message
	responses <-chan types.Response

	newResponsesChan chan (<-chan types.Response)

	closedChan chan struct{}
	closeChan  chan struct{}
}

// NewZMQ4 - Create a new ZMQ4 input type.
func NewZMQ4(conf Config) (*ZMQ4, error) {
	z := ZMQ4{
		running:          1,
		conf:             conf,
		internalMessages: make(chan [][]byte),
		messages:         make(chan types.Message),
		responses:        nil,
		newResponsesChan: make(chan (<-chan types.Response)),
		closedChan:       make(chan struct{}),
		closeChan:        make(chan struct{}),
	}

	t, err := getZMQType(conf.ZMQ4.SocketType)
	if nil != err {
		return nil, err
	}

	ctx, err := zmq4.NewContext()
	if nil != err {
		return nil, err
	}

	if z.socket, err = ctx.NewSocket(t); nil != err {
		return nil, err
	}

	for _, address := range conf.ZMQ4.Addresses {
		if strings.Contains(address, "*") {
			err = z.socket.Bind(address)
		} else {
			err = z.socket.Connect(address)
		}
		if err != nil {
			return nil, err
		}
	}

	go z.readerLoop()
	go z.loop()

	return &z, nil
}

//--------------------------------------------------------------------------------------------------

func getZMQType(t string) (zmq4.Type, error) {
	switch t {
	case "REQ":
		return zmq4.REQ, nil
	case "REP":
		return zmq4.REP, nil
	case "DEALER":
		return zmq4.DEALER, nil
	case "ROUTER":
		return zmq4.ROUTER, nil
	case "PUB":
		return zmq4.PUB, nil
	case "SUB":
		return zmq4.SUB, nil
	case "XPUB":
		return zmq4.XPUB, nil
	case "XSUB":
		return zmq4.XSUB, nil
	case "PUSH":
		return zmq4.PUSH, nil
	case "PULL":
		return zmq4.PULL, nil
	case "PAIR":
		return zmq4.PAIR, nil
	case "STREAM":
		return zmq4.STREAM, nil
	}
	return zmq4.PULL, types.ErrInvalidZMQType
}

//--------------------------------------------------------------------------------------------------

// readerLoop - Internal loop for polling new messages.
func (z *ZMQ4) readerLoop() {
	pollTimeout := time.Millisecond * time.Duration(z.conf.ZMQ4.PollTimeoutMS)
	poller := zmq4.NewPoller()
	poller.Add(z.socket, zmq4.POLLIN)

	for atomic.LoadInt32(&z.running) == 1 {
		// If no bytes then read a message
		polled, err := poller.Poll(pollTimeout)
		if err == nil && len(polled) == 1 {
			if bytes, err := z.socket.RecvMessageBytes(0); err == nil {
				z.internalMessages <- bytes
			} else {
				_ = err
				// TODO: propagate errors, input type should have error channel.
			}
		}
	}
	close(z.internalMessages)
}

// loop - Internal loop brokers incoming messages to output pipe.
func (z *ZMQ4) loop() {
	var bytes [][]byte

	var msgChan chan<- types.Message
	var internalChan <-chan [][]byte

	running, responsePending := true, false
	for running {
		// If we have a line to push out
		if responsePending {
			msgChan = nil
			internalChan = nil
		} else {
			if bytes == nil {
				msgChan = nil
				internalChan = z.internalMessages
			} else {
				msgChan = z.messages
				internalChan = nil
			}
		}

		select {
		case bytes, running = <-internalChan:
		case msgChan <- types.Message{Parts: bytes}:
			responsePending = true
		case err, open := <-z.responses:
			responsePending = false
			if !open {
				z.responses = nil
			} else if err == nil {
				bytes = nil
			}
		case newResChan, open := <-z.newResponsesChan:
			if running = open; open {
				z.responses = newResChan
			}
		case _, running = <-z.closeChan:
		}
	}

	close(z.messages)
	close(z.newResponsesChan)
	close(z.closedChan)
}

// SetResponseChan - Sets the channel used by the input to validate message receipt.
func (z *ZMQ4) SetResponseChan(responses <-chan types.Response) {
	z.newResponsesChan <- responses
}

// ConsumerChan - Returns the messages channel.
func (z *ZMQ4) ConsumerChan() <-chan types.Message {
	return z.messages
}

// CloseAsync - Shuts down the ZMQ4 input and stops processing requests.
func (z *ZMQ4) CloseAsync() {
	atomic.StoreInt32(&z.running, 0)
}

// WaitForClose - Blocks until the ZMQ4 input has closed down.
func (z *ZMQ4) WaitForClose(timeout time.Duration) error {
	select {
	case <-z.closedChan:
	case <-time.After(timeout):
		return types.ErrTimeout
	}
	return nil
}

//--------------------------------------------------------------------------------------------------
<|endoftext|>


<commit_before>// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"context"
	"fmt"
	"net/url"

	"github.com/olivere/elastic/uritemplates"
)

// See the documentation at
// https://www.elastic.co/guide/en/elasticsearch/reference/6.7/ilm-get-lifecycle.html.
type XPackIlmDeleteLifecycleService struct {
	client        *Client
	policy        string
	pretty        bool
	timeout       string
	masterTimeout string
	flatSettings  *bool
	local         *bool
}

// NewXPackIlmDeleteLifecycleService creates a new XPackIlmDeleteLifecycleService.
func NewXPackIlmDeleteLifecycleService(client *Client) *XPackIlmDeleteLifecycleService {
	return &XPackIlmDeleteLifecycleService{
		client: client,
	}
}

// Policy is the name of the index lifecycle policy.
func (s *XPackIlmDeleteLifecycleService) Policy(policy string) *XPackIlmDeleteLifecycleService {
	s.policy = policy
	return s
}

// Timeout is an explicit operation timeout.
func (s *XPackIlmDeleteLifecycleService) Timeout(timeout string) *XPackIlmDeleteLifecycleService {
	s.timeout = timeout
	return s
}

// MasterTimeout specifies the timeout for connection to master.
func (s *XPackIlmDeleteLifecycleService) MasterTimeout(masterTimeout string) *XPackIlmDeleteLifecycleService {
	s.masterTimeout = masterTimeout
	return s
}

// FlatSettings is returns settings in flat format (default: false).
func (s *XPackIlmDeleteLifecycleService) FlatSettings(flatSettings bool) *XPackIlmDeleteLifecycleService {
	s.flatSettings = &flatSettings
	return s
}

// Pretty indicates that the JSON response be indented and human readable.
func (s *XPackIlmDeleteLifecycleService) Pretty(pretty bool) *XPackIlmDeleteLifecycleService {
	s.pretty = pretty
	return s
}

// buildURL builds the URL for the operation.
func (s *XPackIlmDeleteLifecycleService) buildURL() (string, url.Values, error) {
	// Build URL
	var err error
	var path string
	if s.policy != "" {
		path, err = uritemplates.Expand("/_ilm/policy/{policy}", map[string]string{
			"policy": s.policy,
		})
	} else {
		path = "/_template"
	}
	if err != nil {
		return "", url.Values{}, err
	}

	// Add query string parameters
	params := url.Values{}
	if s.pretty {
		params.Set("pretty", "true")
	}
	if s.flatSettings != nil {
		params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
	}
	if s.timeout != "" {
		params.Set("timeout", s.timeout)
	}
	if s.masterTimeout != "" {
		params.Set("master_timeout", s.masterTimeout)
	}
	if s.local != nil {
		params.Set("local", fmt.Sprintf("%v", *s.local))
	}
	return path, params, nil
}

// Validate checks if the operation is valid.
func (s *XPackIlmDeleteLifecycleService) Validate() error {
	var invalid []string
	if s.policy == "" {
		invalid = append(invalid, "Policy")
	}
	if len(invalid) > 0 {
		return fmt.Errorf("missing required fields: %v", invalid)
	}
	return nil
}

// Do executes the operation.
func (s *XPackIlmDeleteLifecycleService) Do(ctx context.Context) (*XPackIlmDeleteLifecycleResponse, error) {
	// Check pre-conditions
	if err := s.Validate(); err != nil {
		return nil, err
	}

	// Delete URL for request
	path, params, err := s.buildURL()
	if err != nil {
		return nil, err
	}

	// Delete HTTP response
	res, err := s.client.PerformRequest(ctx, PerformRequestOptions{
		Method: "DELETE",
		Path:   path,
		Params: params,
	})
	if err != nil {
		return nil, err
	}

	// Return operation response
	ret := new(XPackIlmDeleteLifecycleResponse)
	if err := s.client.decoder.Decode(res.Body, ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// XPackIlmDeleteLifecycleResponse is the response of XPackIlmDeleteLifecycleService.Do.
type XPackIlmDeleteLifecycleResponse struct {
	Acknowledged bool `json:"acknowledged"`
}
<commit_msg>small fix in delete from copy paste error<commit_after>// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
	"context"
	"fmt"
	"net/url"

	"github.com/olivere/elastic/uritemplates"
)

// See the documentation at
// https://www.elastic.co/guide/en/elasticsearch/reference/6.7/ilm-get-lifecycle.html.
type XPackIlmDeleteLifecycleService struct {
	client        *Client
	policy        string
	pretty        bool
	timeout       string
	masterTimeout string
	flatSettings  *bool
	local         *bool
}

// NewXPackIlmDeleteLifecycleService creates a new XPackIlmDeleteLifecycleService.
func NewXPackIlmDeleteLifecycleService(client *Client) *XPackIlmDeleteLifecycleService {
	return &XPackIlmDeleteLifecycleService{
		client: client,
	}
}

// Policy is the name of the index lifecycle policy.
func (s *XPackIlmDeleteLifecycleService) Policy(policy string) *XPackIlmDeleteLifecycleService {
	s.policy = policy
	return s
}

// Timeout is an explicit operation timeout.
func (s *XPackIlmDeleteLifecycleService) Timeout(timeout string) *XPackIlmDeleteLifecycleService {
	s.timeout = timeout
	return s
}

// MasterTimeout specifies the timeout for connection to master.
func (s *XPackIlmDeleteLifecycleService) MasterTimeout(masterTimeout string) *XPackIlmDeleteLifecycleService {
	s.masterTimeout = masterTimeout
	return s
}

// FlatSettings is returns settings in flat format (default: false).
func (s *XPackIlmDeleteLifecycleService) FlatSettings(flatSettings bool) *XPackIlmDeleteLifecycleService {
	s.flatSettings = &flatSettings
	return s
}

// Pretty indicates that the JSON response be indented and human readable.
func (s *XPackIlmDeleteLifecycleService) Pretty(pretty bool) *XPackIlmDeleteLifecycleService {
	s.pretty = pretty
	return s
}

// buildURL builds the URL for the operation.
func (s *XPackIlmDeleteLifecycleService) buildURL() (string, url.Values, error) {
	// Build URL
	var err error
	var path string
	path, err = uritemplates.Expand("/_ilm/policy/{policy}", map[string]string{
		"policy": s.policy,
	})
	if err != nil {
		return "", url.Values{}, err
	}

	// Add query string parameters
	params := url.Values{}
	if s.pretty {
		params.Set("pretty", "true")
	}
	if s.flatSettings != nil {
		params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
	}
	if s.timeout != "" {
		params.Set("timeout", s.timeout)
	}
	if s.masterTimeout != "" {
		params.Set("master_timeout", s.masterTimeout)
	}
	if s.local != nil {
		params.Set("local", fmt.Sprintf("%v", *s.local))
	}
	return path, params, nil
}

// Validate checks if the operation is valid.
func (s *XPackIlmDeleteLifecycleService) Validate() error {
	var invalid []string
	if s.policy == "" {
		invalid = append(invalid, "Policy")
	}
	if len(invalid) > 0 {
		return fmt.Errorf("missing required fields: %v", invalid)
	}
	return nil
}

// Do executes the operation.
func (s *XPackIlmDeleteLifecycleService) Do(ctx context.Context) (*XPackIlmDeleteLifecycleResponse, error) {
	// Check pre-conditions
	if err := s.Validate(); err != nil {
		return nil, err
	}

	// Delete URL for request
	path, params, err := s.buildURL()
	if err != nil {
		return nil, err
	}

	// Delete HTTP response
	res, err := s.client.PerformRequest(ctx, PerformRequestOptions{
		Method: "DELETE",
		Path:   path,
		Params: params,
	})
	if err != nil {
		return nil, err
	}

	// Return operation response
	ret := new(XPackIlmDeleteLifecycleResponse)
	if err := s.client.decoder.Decode(res.Body, ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// XPackIlmDeleteLifecycleResponse is the response of XPackIlmDeleteLifecycleService.Do.
type XPackIlmDeleteLifecycleResponse struct {
	Acknowledged bool `json:"acknowledged"`
}
<|endoftext|>


<commit_before>package cloudwatch

import (
	"testing"
	"time"

	"github.com/Pallinder/go-randomdata"
	"github.com/gliderlabs/logspout/router"
)

const NumMessages = 2000000

func TestCloudWatchAdapter(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode.")
	}

	route := &router.Route{Address: "logspout-cloudwatch"}
	messages := make(chan *router.Message)

	adapter, err := NewAdapter(route)
	if err != nil {
		t.Error(err)
		return
	}

	go adapter.Stream(messages)
	for i := 0; i < NumMessages; i++ {
		messages <- &router.Message{Data: randomdata.Paragraph(), Time: time.Now()}
	}

	close(messages)
}
<commit_msg>Reduce number of messages inserted during integration test.<commit_after>package cloudwatch

import (
	"testing"
	"time"

	"github.com/Pallinder/go-randomdata"
	"github.com/gliderlabs/logspout/router"
)

const NumMessages = 250000

func TestCloudWatchAdapter(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode.")
	}

	route := &router.Route{Address: "logspout-cloudwatch"}
	messages := make(chan *router.Message)

	adapter, err := NewAdapter(route)
	if err != nil {
		t.Error(err)
		return
	}

	go adapter.Stream(messages)
	for i := 0; i < NumMessages; i++ {
		messages <- &router.Message{Data: randomdata.Paragraph(), Time: time.Now()}
	}

	close(messages)
}
<|endoftext|>


<commit_before>package cluster

import (
	"time"

	"github.com/AsynkronIT/gonet"
	"github.com/AsynkronIT/protoactor-go/actor"
	"github.com/AsynkronIT/protoactor-go/log"
	"github.com/AsynkronIT/protoactor-go/remote"
)

var cfg *ClusterConfig

func Start(clusterName, address string, provider ClusterProvider) {
	StartWithConfig(NewClusterConfig(clusterName, address, provider))
}

func StartWithConfig(config *ClusterConfig) {
	cfg = config

	//TODO: make it possible to become a cluster even if remoting is already started
	remote.Start(cfg.Address, cfg.RemotingOption...)

	address := actor.ProcessRegistry.Address
	h, p := gonet.GetAddress(address)
	plog.Info("Starting Proto.Actor cluster", log.String("address", address))
	kinds := remote.GetKnownKinds()

	//for each known kind, spin up a partition-kind actor to handle all requests for that kind
	setupPartition(kinds)
	setupPidCache()
	setupMemberList()

	cfg.ClusterProvider.RegisterMember(cfg.Name, h, p, kinds, cfg.InitialMemberStatusValue, cfg.MemberStatusValueSerializer)
	cfg.ClusterProvider.MonitorMemberStatusChanges()
}

func Shutdown(graceful bool) {
	if graceful {
		cfg.ClusterProvider.Shutdown()
		//This is to wait ownership transfering complete.
		time.Sleep(time.Millisecond * 2000)
		stopMemberList()
		stopPidCache()
		stopPartition()
	}

	remote.Shutdown(graceful)

	address := actor.ProcessRegistry.Address
	plog.Info("Stopped Proto.Actor cluster", log.String("address", address))
}

//Get a PID to a virtual actor
func Get(name string, kind string) (*actor.PID, remote.ResponseStatusCode) {
	//Check Cache
	if pid, ok := pidCache.getCache(name); ok {
		return pid, remote.ResponseStatusCodeOK
	}

	//Get Pid
	address := memberList.getPartitionMember(name, kind)
	if address == "" {
		//No available member found
		return nil, remote.ResponseStatusCodeUNAVAILABLE
	}

	//package the request as a remote.ActorPidRequest
	req := &remote.ActorPidRequest{
		Kind: kind,
		Name: name,
	}

	//ask the DHT partition for this name to give us a PID
	remotePartition := partition.partitionForKind(address, kind)
	f := remotePartition.RequestFuture(req, cfg.TimeoutTime)
	err := f.Wait()
	if err == actor.ErrTimeout {
		plog.Error("PidCache Pid request timeout")
		return nil, remote.ResponseStatusCodeTIMEOUT
	} else if err != nil {
		plog.Error("PidCache Pid request error", log.Error(err))
		return nil, remote.ResponseStatusCodeERROR
	}

	r, _ := f.Result()
	response, ok := r.(*remote.ActorPidResponse)
	if !ok {
		return nil, remote.ResponseStatusCodeERROR
	}

	statusCode := remote.ResponseStatusCode(response.StatusCode)
	switch statusCode {
	case remote.ResponseStatusCodeOK:
		//save cache
		pidCache.addCache(name, response.Pid)
		//tell the original requester that we have a response
		return response.Pid, statusCode
	default:
		//forward to requester
		return response.Pid, statusCode
	}
}

//RemoveCache at PidCache
func RemoveCache(name string) {
	pidCache.removeCacheByName(name)
}
<commit_msg>Simplify cluster GetPid code.<commit_after>package cluster

import (
	"time"

	"github.com/AsynkronIT/gonet"
	"github.com/AsynkronIT/protoactor-go/actor"
	"github.com/AsynkronIT/protoactor-go/log"
	"github.com/AsynkronIT/protoactor-go/remote"
)

var cfg *ClusterConfig

func Start(clusterName, address string, provider ClusterProvider) {
	StartWithConfig(NewClusterConfig(clusterName, address, provider))
}

func StartWithConfig(config *ClusterConfig) {
	cfg = config

	//TODO: make it possible to become a cluster even if remoting is already started
	remote.Start(cfg.Address, cfg.RemotingOption...)

	address := actor.ProcessRegistry.Address
	h, p := gonet.GetAddress(address)
	plog.Info("Starting Proto.Actor cluster", log.String("address", address))
	kinds := remote.GetKnownKinds()

	//for each known kind, spin up a partition-kind actor to handle all requests for that kind
	setupPartition(kinds)
	setupPidCache()
	setupMemberList()

	cfg.ClusterProvider.RegisterMember(cfg.Name, h, p, kinds, cfg.InitialMemberStatusValue, cfg.MemberStatusValueSerializer)
	cfg.ClusterProvider.MonitorMemberStatusChanges()
}

func Shutdown(graceful bool) {
	if graceful {
		cfg.ClusterProvider.Shutdown()
		//This is to wait ownership transfering complete.
		time.Sleep(time.Millisecond * 2000)
		stopMemberList()
		stopPidCache()
		stopPartition()
	}

	remote.Shutdown(graceful)

	address := actor.ProcessRegistry.Address
	plog.Info("Stopped Proto.Actor cluster", log.String("address", address))
}

//Get a PID to a virtual actor
func Get(name string, kind string) (*actor.PID, remote.ResponseStatusCode) {
	//Check Cache
	if pid, ok := pidCache.getCache(name); ok {
		return pid, remote.ResponseStatusCodeOK
	}

	//Get Pid
	address := memberList.getPartitionMember(name, kind)
	if address == "" {
		//No available member found
		return nil, remote.ResponseStatusCodeUNAVAILABLE
	}

	//package the request as a remote.ActorPidRequest
	req := &remote.ActorPidRequest{
		Kind: kind,
		Name: name,
	}

	//ask the DHT partition for this name to give us a PID
	remotePartition := partition.partitionForKind(address, kind)
	r, err := remotePartition.RequestFuture(req, cfg.TimeoutTime).Result()
	if err == actor.ErrTimeout {
		plog.Error("PidCache Pid request timeout")
		return nil, remote.ResponseStatusCodeTIMEOUT
	} else if err != nil {
		plog.Error("PidCache Pid request error", log.Error(err))
		return nil, remote.ResponseStatusCodeERROR
	}

	response, ok := r.(*remote.ActorPidResponse)
	if !ok {
		return nil, remote.ResponseStatusCodeERROR
	}

	statusCode := remote.ResponseStatusCode(response.StatusCode)
	switch statusCode {
	case remote.ResponseStatusCodeOK:
		//save cache
		pidCache.addCache(name, response.Pid)
		//tell the original requester that we have a response
		return response.Pid, statusCode
	default:
		//forward to requester
		return response.Pid, statusCode
	}
}

//RemoveCache at PidCache
func RemoveCache(name string) {
	pidCache.removeCacheByName(name)
}
<|endoftext|>


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

package rescat

import (
	"io"
)

// Fetcher interface to be provided for FS, HTTP etc...
type Fetcher interface {
	Fetch(n string) (b []byte, err error)
}

// Maybe doesn't need to be an interface?
type Provider interface {
	// return the concatinated string as a ??? ready to write to
	// and HTTP Response
	Provide(names []string) (r io.Reader, err error)
	Fetcher
}
<commit_msg>add doc string<commit_after>// Copyright 2013 Reed O'Brien. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rescat

import (
	"io"
)

// Fetcher interface to be provided for FS, HTTP etc...
type Fetcher interface {
	// Fetch the resource n and return it as a byte array.
	Fetch(n string) (b []byte, err error)
}

// Maybe doesn't need to be an interface?
type Provider interface {
	// return the concatinated string as a ??? ready to write to
	// and HTTP Response
	Provide(names []string) (r io.Reader, err error)
	Fetcher
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"io"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/superp00t/etc"
	"github.com/superp00t/etc/yo"
)

type diskStatus struct {
	All  uint64 `json:"all"`
	Used uint64 `json:"used"`
	Free uint64 `json:"free"`
}

type cacher struct {
	Handler http.Handler

	sync.Mutex
}

func hashString(name string) string {
	s := sha256.New()
	s.Write([]byte(name))
	return strings.ToUpper(hex.EncodeToString(s.Sum(nil)))
}

func (c *cacher) Available() uint64 {
	return directory.Concat("c").Free()
}

func (c *cacher) serveContent(rw http.ResponseWriter, r *http.Request, path string) {
	if strings.Contains(r.Header.Get("Accept-Ranges"), "-") {
		// Cannot serve compressed in this fashion
		http.ServeFile(rw, r, path)
		return
	}

	if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
		rw.Header().Set("Content-Encoding", "gzip")

		file, err := etc.FileController(path, true)
		if err != nil {
			return
		}

		tp := http.DetectContentType(file.ReadBytes(512))

		rw.Header().Set("Content-Type", tp)

		file.SeekR(0)

		gz := gzip.NewWriter(rw)
		io.Copy(gz, file)
		gz.Close()
		file.Close()
		return
	}

	http.ServeFile(rw, r, path)
}

func (c *cacher) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	pth := r.URL.Path[1:]
	hash := hashString(pth)
	pCachePath := directory.Concat("c").Concat(hash)
	pSrcPath := directory.Concat("i").GetSub(etc.ParseUnixPath(pth))

	if pCachePath.IsExtant() && time.Since(pCachePath.Time()) < Config.CacheDuration.Duration {
		// cached file exists.
		c.serveContent(rw, r, pCachePath.Render())
		return
	}

	// Backend may be down. serve cached file in its place.
	if !pSrcPath.IsExtant() && pCachePath.IsExtant() {
		c.serveContent(rw, r, pCachePath.Render())
		return
	}

	if pSrcPath.IsExtant() == false {
		http.Error(rw, "file not found", 404)
		return
	}

	cacheDir := directory.Concat("c")

	// delete oldest item in cache if we have not enough space.
	for cacheDir.Free() < pSrcPath.Size() || cacheDir.Size() > Config.MaxCacheBytes {
		yo.Ok("erasing until bytes free are more than", cacheDir.Free())

		lru, err := cacheDir.LRU()
		if err != nil {
			yo.Warn(err)
			break
		}

		cacheDir.Concat(lru).Remove()
	}

	pCachePath.Remove()

	f, err := etc.FileController(pCachePath.Render())
	if err != nil {
		yo.Fatal(err)
	}

	if err = f.Flush(); err != nil {
		yo.Fatal(err)
	}

	s, err := etc.FileController(pSrcPath.Render(), true)
	if err != nil {
		yo.Fatal(err)
	}

	if _, err = io.Copy(f, s); err != nil {
		yo.Fatal(err)
	}

	f.Close()
	s.Close()

	c.serveContent(rw, r, pCachePath.Render())
}
<commit_msg>dont gzip text<commit_after>package main

import (
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"io"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/superp00t/etc"
	"github.com/superp00t/etc/yo"
)

type diskStatus struct {
	All  uint64 `json:"all"`
	Used uint64 `json:"used"`
	Free uint64 `json:"free"`
}

type cacher struct {
	Handler http.Handler

	sync.Mutex
}

func hashString(name string) string {
	s := sha256.New()
	s.Write([]byte(name))
	return strings.ToUpper(hex.EncodeToString(s.Sum(nil)))
}

func (c *cacher) Available() uint64 {
	return directory.Concat("c").Free()
}

func (c *cacher) serveContent(rw http.ResponseWriter, r *http.Request, path string) {
	if strings.Contains(r.Header.Get("Accept-Ranges"), "-") {
		// Cannot serve compressed in this fashion
		http.ServeFile(rw, r, path)
		return
	}

	if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {

		file, err := etc.FileController(path, true)
		if err != nil {
			return
		}

		tp := http.DetectContentType(file.ReadBytes(512))

		if strings.HasPrefix(tp, "text/") {
			http.ServeFile(rw, r, path)
			return
		}

		rw.Header().Set("Content-Encoding", "gzip")
		rw.Header().Set("Content-Type", tp)

		file.SeekR(0)

		gz := gzip.NewWriter(rw)
		io.Copy(gz, file)
		gz.Close()
		file.Close()
		return
	}

	http.ServeFile(rw, r, path)
}

func (c *cacher) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	pth := r.URL.Path[1:]
	hash := hashString(pth)
	pCachePath := directory.Concat("c").Concat(hash)
	pSrcPath := directory.Concat("i").GetSub(etc.ParseUnixPath(pth))

	if pCachePath.IsExtant() && time.Since(pCachePath.Time()) < Config.CacheDuration.Duration {
		// cached file exists.
		c.serveContent(rw, r, pCachePath.Render())
		return
	}

	// Backend may be down. serve cached file in its place.
	if !pSrcPath.IsExtant() && pCachePath.IsExtant() {
		c.serveContent(rw, r, pCachePath.Render())
		return
	}

	if pSrcPath.IsExtant() == false {
		http.Error(rw, "file not found", 404)
		return
	}

	cacheDir := directory.Concat("c")

	// delete oldest item in cache if we have not enough space.
	for cacheDir.Free() < pSrcPath.Size() || cacheDir.Size() > Config.MaxCacheBytes {
		yo.Ok("erasing until bytes free are more than", cacheDir.Free())

		lru, err := cacheDir.LRU()
		if err != nil {
			yo.Warn(err)
			break
		}

		cacheDir.Concat(lru).Remove()
	}

	pCachePath.Remove()

	f, err := etc.FileController(pCachePath.Render())
	if err != nil {
		yo.Fatal(err)
	}

	if err = f.Flush(); err != nil {
		yo.Fatal(err)
	}

	s, err := etc.FileController(pSrcPath.Render(), true)
	if err != nil {
		yo.Fatal(err)
	}

	if _, err = io.Copy(f, s); err != nil {
		yo.Fatal(err)
	}

	f.Close()
	s.Close()

	c.serveContent(rw, r, pCachePath.Render())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"

	cmds "github.com/jbenet/go-ipfs/commands"
	cmdsHttp "github.com/jbenet/go-ipfs/commands/http"
	"github.com/jbenet/go-ipfs/daemon"
)

var Daemon = &cmds.Command{
	Options:     []cmds.Option{},
	Help:        "TODO",
	Subcommands: map[string]*cmds.Command{},
	Run:         daemonFunc,
}

func daemonFunc(req cmds.Request, res cmds.Response) {
	// TODO: spin up a core.IpfsNode

	ctx := req.Context()

	lk, err := daemon.Lock(ctx.ConfigRoot)
	if err != nil {
		res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal)
		return
	}
	defer lk.Close()

	addr, err := ma.NewMultiaddr(ctx.Config.Addresses.API)
	if err != nil {
		res.SetError(err, cmds.ErrNormal)
		return
	}

	_, host, err := manet.DialArgs(addr)
	if err != nil {
		res.SetError(err, cmds.ErrNormal)
		return
	}

	handler := cmdsHttp.Handler{*ctx}
	http.Handle(cmdsHttp.ApiPath+"/", handler)
	err = http.ListenAndServe(host, nil)
	if err != nil {
		res.SetError(err, cmds.ErrNormal)
		return
	}
	// TODO: log to indicate that we are now listening

}
<commit_msg>cmd/ipfs: Log to show API server is listening<commit_after>package main

import (
	"fmt"
	"net/http"

	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"

	cmds "github.com/jbenet/go-ipfs/commands"
	cmdsHttp "github.com/jbenet/go-ipfs/commands/http"
	"github.com/jbenet/go-ipfs/daemon"
)

var Daemon = &cmds.Command{
	Options:     []cmds.Option{},
	Help:        "TODO",
	Subcommands: map[string]*cmds.Command{},
	Run:         daemonFunc,
}

func daemonFunc(req cmds.Request, res cmds.Response) {
	// TODO: spin up a core.IpfsNode

	ctx := req.Context()

	lk, err := daemon.Lock(ctx.ConfigRoot)
	if err != nil {
		res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal)
		return
	}
	defer lk.Close()

	addr, err := ma.NewMultiaddr(ctx.Config.Addresses.API)
	if err != nil {
		res.SetError(err, cmds.ErrNormal)
		return
	}

	_, host, err := manet.DialArgs(addr)
	if err != nil {
		res.SetError(err, cmds.ErrNormal)
		return
	}

	handler := cmdsHttp.Handler{*ctx}
	http.Handle(cmdsHttp.ApiPath+"/", handler)

	fmt.Printf("API server listening on '%s'\n", host)

	err = http.ListenAndServe(host, nil)
	if err != nil {
		res.SetError(err, cmds.ErrNormal)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/umbel/pilosa"
)

// Build holds the build information passed in at compile time.
var Build string

func init() {
	if Build == "" {
		Build = "v0.0.0"
	}

	rand.Seed(time.Now().UTC().UnixNano())
}

const (
	// DefaultDataDir is the default data directory.
	DefaultDataDir = "~/.pilosa"

	// DefaultHost is the default hostname and port to use.
	DefaultHost = "localhost:15000"
)

func main() {
	m := NewMain()
	fmt.Fprintf(m.Stderr, "Pilosa %s\n", Build)

	// Parse command line arguments.
	if err := m.ParseFlags(os.Args[1:]); err != nil {
		fmt.Fprintln(m.Stderr, err)
		os.Exit(2)
	}

	// Execute the program.
	if err := m.Run(); err != nil {
		fmt.Fprintln(m.Stderr, err)
		os.Exit(1)
	}

	// First SIGKILL causes server to shut down gracefully.
	// Second signal causes a hard shutdown.
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt)
	sig := <-c
	fmt.Fprintf(m.Stderr, "Received %s; gracefully shutting down...\n", sig.String())
	go func() { <-c; os.Exit(1) }()

	if err := m.Close(); err != nil {
		fmt.Fprintln(m.Stderr, err)
		os.Exit(1)
	}
}

// Main represents the main program execution.
type Main struct {
	Server *pilosa.Server

	// Configuration options.
	ConfigPath string
	Config     *Config

	// Standard input/output
	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
}

// NewMain returns a new instance of Main.
func NewMain() *Main {
	return &Main{
		Server: pilosa.NewServer(),
		Config: NewConfig(),

		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
}

// Run executes the main program execution.
func (m *Main) Run(args ...string) error {
	// Notify user of config file.
	if m.ConfigPath != "" {
		fmt.Fprintf(m.Stdout, "Using config: %s\n", m.ConfigPath)
	}

	// Setup logging output.
	m.Server.LogOutput = m.Stderr

	// Configure index.
	fmt.Fprintf(m.Stderr, "Using data from: %s\n", m.Config.DataDir)
	m.Server.Index.Path = m.Config.DataDir

	// Build cluster from config file.
	m.Server.Host = m.Config.Host
	m.Server.Cluster = m.Config.PilosaCluster()

	// Initialize server.
	if err := m.Server.Open(); err != nil {
		return err
	}

	fmt.Fprintf(m.Stderr, "Listening as http://%s\n", m.Server.Host)

	return nil
}

// Close shuts down the server.
func (m *Main) Close() error {
	return m.Server.Close()
}

// ParseFlags parses command line flags from args.
func (m *Main) ParseFlags(args []string) error {
	fs := flag.NewFlagSet("pilosa", flag.ContinueOnError)
	fs.SetOutput(m.Stderr)
	fs.StringVar(&m.ConfigPath, "config", "", "config path")
	if err := fs.Parse(args); err != nil {
		return err
	}

	// Load config, if specified.
	if m.ConfigPath != "" {
		if _, err := toml.DecodeFile(m.ConfigPath, &m.Config); err != nil {
			return err
		}
	}

	// Use default data directory if one is not specified.
	if m.Config.DataDir == "" {
		m.Config.DataDir = DefaultDataDir
	}

	// Expand home directory.
	prefix := "~" + string(filepath.Separator)
	if strings.HasPrefix(m.Config.DataDir, prefix) {
		//	u, err := user.Current()
		HomeDir := os.Getenv("HOME")
		/*if err != nil {
			return err
		} else*/if HomeDir == "" {
			return errors.New("data directory not specified and no home dir available")
		}
		m.Config.DataDir = filepath.Join(HomeDir, strings.TrimPrefix(m.Config.DataDir, prefix))
	}

	return nil
}

// Config represents the configuration for the command.
type Config struct {
	DataDir string `toml:"data-dir"`
	Host    string `toml:"host"`

	Cluster struct {
		ReplicaN        int           `toml:"replicas"`
		Nodes           []*ConfigNode `toml:"node"`
		PollingInterval Duration      `toml:"polling-interval"`
	} `toml:"cluster"`

	Plugins struct {
		Path string `toml:"path"`
	} `toml:"plugins"`

	AntiEntropy struct {
		Interval Duration `toml:"interval"`
	} `toml:"anti-entropy"`
}

type ConfigNode struct {
	Host string `toml:"host"`
}

// NewConfig returns an instance of Config with default options.
func NewConfig() *Config {
	c := &Config{
		Host: DefaultHost,
	}
	c.Cluster.ReplicaN = pilosa.DefaultReplicaN
	c.Cluster.PollingInterval = Duration(pilosa.DefaultPollingInterval)
	c.AntiEntropy.Interval = Duration(pilosa.DefaultAntiEntropyInterval)
	return c
}

// PilosaCluster returns a new instance of pilosa.Cluster based on the config.
func (c *Config) PilosaCluster() *pilosa.Cluster {
	cluster := pilosa.NewCluster()
	cluster.ReplicaN = c.Cluster.ReplicaN

	for _, n := range c.Cluster.Nodes {
		cluster.Nodes = append(cluster.Nodes, &pilosa.Node{Host: n.Host})
	}

	return cluster
}

// Duration is a TOML wrapper type for time.Duration.
type Duration time.Duration

// String returns the string representation of the duration.
func (d Duration) String() string { return time.Duration(d).String() }

// UnmarshalText parses a TOML value into a duration value.
func (d *Duration) UnmarshalText(text []byte) error {
	v, err := time.ParseDuration(string(text))
	if err != nil {
		return err
	}

	*d = Duration(v)
	return nil
}
<commit_msg>add CLI profiling<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"runtime/pprof"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/umbel/pilosa"
)

// Build holds the build information passed in at compile time.
var Build string

func init() {
	if Build == "" {
		Build = "v0.0.0"
	}

	rand.Seed(time.Now().UTC().UnixNano())
}

const (
	// DefaultDataDir is the default data directory.
	DefaultDataDir = "~/.pilosa"

	// DefaultHost is the default hostname and port to use.
	DefaultHost = "localhost:15000"
)

func main() {
	m := NewMain()
	fmt.Fprintf(m.Stderr, "Pilosa %s\n", Build)

	// Parse command line arguments.
	if err := m.ParseFlags(os.Args[1:]); err != nil {
		fmt.Fprintln(m.Stderr, err)
		os.Exit(2)
	}

	// Start CPU profiling.
	if m.CPUProfile != "" {
		f, err := os.Create(m.CPUProfile)
		if err != nil {
			fmt.Fprintf(m.Stderr, "create cpu profile: %v", err)
			os.Exit(1)
		}
		defer f.Close()

		fmt.Fprintln(m.Stderr, "Starting cpu profile")
		pprof.StartCPUProfile(f)
		time.AfterFunc(m.CPUTime, func() {
			fmt.Fprintln(m.Stderr, "Stopping cpu profile")
			pprof.StopCPUProfile()
			f.Close()
		})
	}

	// Execute the program.
	if err := m.Run(); err != nil {
		fmt.Fprintln(m.Stderr, err)
		fmt.Fprintln(m.Stderr, "stopping profile")
		os.Exit(1)
	}

	// First SIGKILL causes server to shut down gracefully.
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt)
	sig := <-c
	fmt.Fprintf(m.Stderr, "Received %s; gracefully shutting down...\n", sig.String())

	// Second signal causes a hard shutdown.
	go func() { <-c; os.Exit(1) }()

	if err := m.Close(); err != nil {
		fmt.Fprintln(m.Stderr, err)
		os.Exit(1)
	}
}

// Main represents the main program execution.
type Main struct {
	Server *pilosa.Server

	// Configuration options.
	ConfigPath string
	Config     *Config

	// Profiling options.
	CPUProfile string
	CPUTime    time.Duration

	// Standard input/output
	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
}

// NewMain returns a new instance of Main.
func NewMain() *Main {
	return &Main{
		Server: pilosa.NewServer(),
		Config: NewConfig(),

		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
}

// Run executes the main program execution.
func (m *Main) Run(args ...string) error {
	// Notify user of config file.
	if m.ConfigPath != "" {
		fmt.Fprintf(m.Stdout, "Using config: %s\n", m.ConfigPath)
	}

	// Setup logging output.
	m.Server.LogOutput = m.Stderr

	// Configure index.
	fmt.Fprintf(m.Stderr, "Using data from: %s\n", m.Config.DataDir)
	m.Server.Index.Path = m.Config.DataDir

	// Build cluster from config file.
	m.Server.Host = m.Config.Host
	m.Server.Cluster = m.Config.PilosaCluster()

	// Initialize server.
	if err := m.Server.Open(); err != nil {
		return err
	}

	fmt.Fprintf(m.Stderr, "Listening as http://%s\n", m.Server.Host)

	return nil
}

// Close shuts down the server.
func (m *Main) Close() error {
	return m.Server.Close()
}

// ParseFlags parses command line flags from args.
func (m *Main) ParseFlags(args []string) error {
	fs := flag.NewFlagSet("pilosa", flag.ContinueOnError)
	fs.StringVar(&m.CPUProfile, "cpuprofile", "", "cpu profile")
	fs.DurationVar(&m.CPUTime, "cputime", 30*time.Second, "cpu profile duration")
	fs.StringVar(&m.ConfigPath, "config", "", "config path")
	fs.SetOutput(m.Stderr)
	if err := fs.Parse(args); err != nil {
		return err
	}

	// Load config, if specified.
	if m.ConfigPath != "" {
		if _, err := toml.DecodeFile(m.ConfigPath, &m.Config); err != nil {
			return err
		}
	}

	// Use default data directory if one is not specified.
	if m.Config.DataDir == "" {
		m.Config.DataDir = DefaultDataDir
	}

	// Expand home directory.
	prefix := "~" + string(filepath.Separator)
	if strings.HasPrefix(m.Config.DataDir, prefix) {
		//	u, err := user.Current()
		HomeDir := os.Getenv("HOME")
		/*if err != nil {
			return err
		} else*/if HomeDir == "" {
			return errors.New("data directory not specified and no home dir available")
		}
		m.Config.DataDir = filepath.Join(HomeDir, strings.TrimPrefix(m.Config.DataDir, prefix))
	}

	return nil
}

// Config represents the configuration for the command.
type Config struct {
	DataDir string `toml:"data-dir"`
	Host    string `toml:"host"`

	Cluster struct {
		ReplicaN        int           `toml:"replicas"`
		Nodes           []*ConfigNode `toml:"node"`
		PollingInterval Duration      `toml:"polling-interval"`
	} `toml:"cluster"`

	Plugins struct {
		Path string `toml:"path"`
	} `toml:"plugins"`

	AntiEntropy struct {
		Interval Duration `toml:"interval"`
	} `toml:"anti-entropy"`
}

type ConfigNode struct {
	Host string `toml:"host"`
}

// NewConfig returns an instance of Config with default options.
func NewConfig() *Config {
	c := &Config{
		Host: DefaultHost,
	}
	c.Cluster.ReplicaN = pilosa.DefaultReplicaN
	c.Cluster.PollingInterval = Duration(pilosa.DefaultPollingInterval)
	c.AntiEntropy.Interval = Duration(pilosa.DefaultAntiEntropyInterval)
	return c
}

// PilosaCluster returns a new instance of pilosa.Cluster based on the config.
func (c *Config) PilosaCluster() *pilosa.Cluster {
	cluster := pilosa.NewCluster()
	cluster.ReplicaN = c.Cluster.ReplicaN

	for _, n := range c.Cluster.Nodes {
		cluster.Nodes = append(cluster.Nodes, &pilosa.Node{Host: n.Host})
	}

	return cluster
}

// Duration is a TOML wrapper type for time.Duration.
type Duration time.Duration

// String returns the string representation of the duration.
func (d Duration) String() string { return time.Duration(d).String() }

// UnmarshalText parses a TOML value into a duration value.
func (d *Duration) UnmarshalText(text []byte) error {
	v, err := time.ParseDuration(string(text))
	if err != nil {
		return err
	}

	*d = Duration(v)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/GeertJohan/go.rice"
	"github.com/boltdb/bolt"
	"github.com/cswank/quimby"
	"github.com/cswank/quimby/cmd/quimby/handlers"
	"github.com/cswank/quimby/cmd/quimby/utils"
	"github.com/cswank/rex"
	"github.com/justinas/alice"
	"gopkg.in/alecthomas/kingpin.v2"
)

const (
	version = "0.6.0"
)

var (
	users        = kingpin.Command("users", "User management")
	userAdd      = users.Command("add", "Add a new user.")
	userName     = users.Flag("username", "Username for a new user").String()
	userPW       = users.Flag("password", "Password for a new user").String()
	userPerm     = users.Flag("permission", "Permission (read, write, or admin").String()
	userList     = users.Command("list", "List users.")
	userEdit     = users.Command("edit", "Update a user.")
	cert         = kingpin.Command("cert", "Make an tls cert.")
	domain       = cert.Flag("domain", "The domain for the tls cert.").Required().Short('d').String()
	pth          = cert.Flag("path", "The directory where the cert files will be written").Required().Short('p').String()
	serve        = kingpin.Command("serve", "Start the server.")
	setup        = kingpin.Command("setup", "Set up the the server (keys and init scripts and what not.")
	net          = setup.Flag("net", "network interface").Short('n').Default("eth0").String()
	setupDomain  = setup.Flag("domain", "network interface").Required().Short('d').String()
	command      = kingpin.Command("command", "Send a command.")
	method       = kingpin.Command("method", "Send a method.")
	gadgets      = kingpin.Command("gadgets", "Commands for managing gadgets")
	gadgetAdd    = gadgets.Command("add", "Add a gadget.")
	gadgetName   = gadgets.Flag("name", "Name of the gadget.").String()
	gadgetHost   = gadgets.Flag("host", "ip address of gadget (id http://<ipaddr>:6111)").String()
	gadgetList   = gadgets.Command("list", "List the gadgets.")
	gadgetEdit   = gadgets.Command("edit", "List the gadgets.")
	gadgetDelete = gadgets.Command("delete", "Delete a gadget.")
	token        = kingpin.Command("token", "Generate a jwt token")
	bootstrap    = kingpin.Command("bootstrap", "Set up a bunch of stuff")

	keyPath  = os.Getenv("QUIMBY_TLS_KEY")
	certPath = os.Getenv("QUIMBY_TLS_CERT")
	iface    = os.Getenv("QUIMBY_INTERFACE")
)

func main() {
	kingpin.UsageTemplate(kingpin.CompactUsageTemplate).Version(version).Author("Craig Swank")
	switch kingpin.Parse() {
	case "cert":
		utils.GenerateCert(*domain, *pth)
	case "users add":
		doUser(utils.AddUser)
	case "users list":
		addDB(utils.ListUsers)
	case "users edit":
		addDB(utils.EditUser)
	case "gadgets add":
		doGadget(utils.AddGadget)
	case "gadgets list":
		addDB(utils.ListGadgets)
	case "gadgets edit":
		addDB(utils.EditGadget)
	case "gadgets delete":
		addDB(utils.DeleteGadget)
	case "command":
		addDB(utils.SendCommand)
	case "token":
		utils.GetToken()
	case "bootstrap":
		utils.Bootstrap()
	case "serve":
		addDB(startServer)
	case "setup":
		utils.SetupServer(*setupDomain, *net)
	}
}

type dbNeeder func(*bolt.DB)
type userNeeder func(*quimby.User)
type gadgetNeeder func(*quimby.Gadget)

func getDB() *bolt.DB {
	pth := os.Getenv("QUIMBY_DB")
	if pth == "" {
		log.Fatal("you must specify a db location with QUIMBY_DB")
	}
	db, err := quimby.GetDB(pth)
	if err != nil {
		log.Fatalf("could not open db at %s - %v", pth, err)
	}
	return db
}

func doUser(f userNeeder) {
	db := getDB()
	u := quimby.NewUser(
		*userName,
		quimby.UserDB(db),
		quimby.UserPassword(*userPW),
		quimby.UserPermission(*userPerm),
	)
	f(u)
	defer db.Close()
}

func doGadget(f gadgetNeeder) {
	db := getDB()
	g := &quimby.Gadget{
		DB:   db,
		Name: *gadgetName,
		Host: *gadgetHost,
	}
	f(g)
	db.Close()
}

func addDB(f dbNeeder) {
	db := getDB()
	f(db)
	defer db.Close()
}

func startServer(db *bolt.DB) {
	port := os.Getenv("QUIMBY_PORT")
	if port == "" {
		log.Fatal("you must specify a port with QUIMBY_PORT")
	}

	domain := os.Getenv("QUIMBY_DOMAIN")
	if domain == "" {
		log.Fatal("you must specify a domain with QUIMBY_DOMAIN")
	}

	internalPort := os.Getenv("QUIMBY_INTERNAL_PORT")
	if port == "" {
		log.Fatal("you must specify a port with QUIMBY_INTERNAL_PORT")
	}

	var lg *log.Logger
	if os.Getenv("QUIMBY_NULLLOG") != "" {
		lg = log.New(ioutil.Discard, "quimby ", log.Ltime)
	} else {
		lg = log.New(os.Stdout, "quimby ", log.Ltime)
	}
	clients := quimby.NewClientHolder()
	tfa := quimby.NewTFA(domain)
	start(db, port, internalPort, "/", "/api", lg, clients, tfa)
}

func getMiddleware(perm handlers.ACL, f http.HandlerFunc) http.Handler {
	return alice.New(handlers.Perm(perm)).Then(http.HandlerFunc(f))
}

func start(db *bolt.DB, port, internalPort, root string, iRoot string, lg quimby.Logger, clients *quimby.ClientHolder, tfa quimby.TFAer) {
	quimby.Clients = clients
	quimby.DB = db
	quimby.LG = lg
	handlers.DB = db
	handlers.LG = lg
	handlers.TFA = tfa

	go startInternal(iRoot, db, lg, internalPort)
	go startHomeKit(db, lg)

	r := rex.New("main")
	r.Post("/api/login", http.HandlerFunc(handlers.Login))
	r.Post("/api/logout", http.HandlerFunc(handlers.Logout))
	r.Get("/api/ping", getMiddleware(handlers.Read, handlers.Ping))
	r.Get("/api/currentuser", getMiddleware(handlers.Read, handlers.GetCurrentUser))
	r.Get("/api/users", getMiddleware(handlers.Admin, handlers.GetUsers))
	r.Post("/api/users", getMiddleware(handlers.Admin, handlers.AddUser))
	r.Delete("/api/users/{username}", getMiddleware(handlers.Admin, handlers.DeleteUser))
	r.Post("/api/users/{username}/permission", getMiddleware(handlers.Admin, handlers.UpdateUserPermission))
	r.Post("/api/users/{username}/password", getMiddleware(handlers.Admin, handlers.UpdateUserPassword))
	r.Get("/api/users/{username}", getMiddleware(handlers.Admin, handlers.GetUser))
	r.Get("/api/gadgets", getMiddleware(handlers.Read, handlers.GetGadgets))
	r.Post("/api/gadgets", getMiddleware(handlers.Read, handlers.AddGadget))
	r.Get("/api/gadgets/{id}", getMiddleware(handlers.Read, handlers.GetGadget))
	r.Post("/api/gadgets/{id}", getMiddleware(handlers.Write, handlers.UpdateGadget))
	r.Delete("/api/gadgets/{id}", getMiddleware(handlers.Write, handlers.DeleteGadget))
	r.Post("/api/gadgets/{id}/command", getMiddleware(handlers.Write, handlers.SendCommand))
	r.Post("/api/gadgets/{id}/method", getMiddleware(handlers.Write, handlers.SendMethod))
	r.Get("/api/gadgets/{id}/websocket", getMiddleware(handlers.Write, handlers.Connect))
	r.Get("/api/gadgets/{id}/values", getMiddleware(handlers.Read, handlers.GetUpdates))
	r.Get("/api/gadgets/{id}/status", getMiddleware(handlers.Read, handlers.GetStatus))
	r.Post("/api/gadgets/{id}/notes", getMiddleware(handlers.Write, handlers.AddNote))
	r.Get("/api/gadgets/{id}/notes", getMiddleware(handlers.Read, handlers.GetNotes))
	r.Get("/api/gadgets/{id}/locations/{location}/devices/{device}/status", getMiddleware(handlers.Read, handlers.GetDevice))
	r.Post("/api/gadgets/{id}/locations/{location}/devices/{device}/status", getMiddleware(handlers.Write, handlers.UpdateDevice))
	r.Get("/api/gadgets/{id}/sources/{name}", getMiddleware(handlers.Read, handlers.GetDataPoints))
	r.Get("/api/gadgets/{id}/sources/{name}/csv", getMiddleware(handlers.Read, handlers.GetDataPointsCSV))
	r.Get("/api/beer/{name}", getMiddleware(handlers.Read, handlers.GetRecipe))
	r.Get("/api/admin/clients", getMiddleware(handlers.Admin, handlers.GetClients))

	r.ServeFiles(http.FileServer(rice.MustFindBox("www/dist").HTTPBox()))

	chain := alice.New(handlers.Auth(db, lg, "main"), handlers.FetchGadget(), handlers.Error(lg)).Then(r)

	http.Handle(root, chain)

	addr := fmt.Sprintf("%s:%s", iface, port)
	lg.Printf("listening on %s\n", addr)
	if keyPath == "" {
		lg.Println(http.ListenAndServe(addr, chain))
	} else {
		lg.Println(http.ListenAndServeTLS(fmt.Sprintf("%s:443", iface), certPath, keyPath, chain))
	}
}

func startHomeKit(db *bolt.DB, lg quimby.Logger) {
	key := os.Getenv("QUIMBY_HOMEKIT")
	if key == "" {
		lg.Println("QUIMBY_HOMEKIT not set, not starting homekit")
		return
	}
	hk := quimby.NewHomeKit(key, db)
	hk.Start()
}

//This is the endpoint that the gadgets report to. It is
//served on a separate port so it doesn't have to be exposed
//publicly if the main port is exposed.
func startInternal(iRoot string, db *bolt.DB, lg quimby.Logger, port string) {
	r := rex.New("internal")
	r.Post("/internal/updates", getMiddleware(handlers.Write, handlers.RelayMessage))
	r.Post("/internal/gadgets/{id}/sources/{name}", getMiddleware(handlers.Write, handlers.AddDataPoint))

	chain := alice.New(handlers.Auth(db, lg, "internal"), handlers.FetchGadget()).Then(r)

	http.Handle(iRoot, chain)
	a := fmt.Sprintf(":%s", port)
	lg.Printf("listening on %s", a)
	err := http.ListenAndServe(a, chain)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Bump version to 0.7.0<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/GeertJohan/go.rice"
	"github.com/boltdb/bolt"
	"github.com/cswank/quimby"
	"github.com/cswank/quimby/cmd/quimby/handlers"
	"github.com/cswank/quimby/cmd/quimby/utils"
	"github.com/cswank/rex"
	"github.com/justinas/alice"
	"gopkg.in/alecthomas/kingpin.v2"
)

const (
	version = "0.7.0"
)

var (
	users        = kingpin.Command("users", "User management")
	userAdd      = users.Command("add", "Add a new user.")
	userName     = users.Flag("username", "Username for a new user").String()
	userPW       = users.Flag("password", "Password for a new user").String()
	userPerm     = users.Flag("permission", "Permission (read, write, or admin").String()
	userList     = users.Command("list", "List users.")
	userEdit     = users.Command("edit", "Update a user.")
	cert         = kingpin.Command("cert", "Make an tls cert.")
	domain       = cert.Flag("domain", "The domain for the tls cert.").Required().Short('d').String()
	pth          = cert.Flag("path", "The directory where the cert files will be written").Required().Short('p').String()
	serve        = kingpin.Command("serve", "Start the server.")
	setup        = kingpin.Command("setup", "Set up the the server (keys and init scripts and what not.")
	net          = setup.Flag("net", "network interface").Short('n').Default("eth0").String()
	setupDomain  = setup.Flag("domain", "network interface").Required().Short('d').String()
	command      = kingpin.Command("command", "Send a command.")
	method       = kingpin.Command("method", "Send a method.")
	gadgets      = kingpin.Command("gadgets", "Commands for managing gadgets")
	gadgetAdd    = gadgets.Command("add", "Add a gadget.")
	gadgetName   = gadgets.Flag("name", "Name of the gadget.").String()
	gadgetHost   = gadgets.Flag("host", "ip address of gadget (id http://<ipaddr>:6111)").String()
	gadgetList   = gadgets.Command("list", "List the gadgets.")
	gadgetEdit   = gadgets.Command("edit", "List the gadgets.")
	gadgetDelete = gadgets.Command("delete", "Delete a gadget.")
	token        = kingpin.Command("token", "Generate a jwt token")
	bootstrap    = kingpin.Command("bootstrap", "Set up a bunch of stuff")

	keyPath  = os.Getenv("QUIMBY_TLS_KEY")
	certPath = os.Getenv("QUIMBY_TLS_CERT")
	iface    = os.Getenv("QUIMBY_INTERFACE")
)

func main() {
	kingpin.UsageTemplate(kingpin.CompactUsageTemplate).Version(version).Author("Craig Swank")
	switch kingpin.Parse() {
	case "cert":
		utils.GenerateCert(*domain, *pth)
	case "users add":
		doUser(utils.AddUser)
	case "users list":
		addDB(utils.ListUsers)
	case "users edit":
		addDB(utils.EditUser)
	case "gadgets add":
		doGadget(utils.AddGadget)
	case "gadgets list":
		addDB(utils.ListGadgets)
	case "gadgets edit":
		addDB(utils.EditGadget)
	case "gadgets delete":
		addDB(utils.DeleteGadget)
	case "command":
		addDB(utils.SendCommand)
	case "token":
		utils.GetToken()
	case "bootstrap":
		utils.Bootstrap()
	case "serve":
		addDB(startServer)
	case "setup":
		utils.SetupServer(*setupDomain, *net)
	}
}

type dbNeeder func(*bolt.DB)
type userNeeder func(*quimby.User)
type gadgetNeeder func(*quimby.Gadget)

func getDB() *bolt.DB {
	pth := os.Getenv("QUIMBY_DB")
	if pth == "" {
		log.Fatal("you must specify a db location with QUIMBY_DB")
	}
	db, err := quimby.GetDB(pth)
	if err != nil {
		log.Fatalf("could not open db at %s - %v", pth, err)
	}
	return db
}

func doUser(f userNeeder) {
	db := getDB()
	u := quimby.NewUser(
		*userName,
		quimby.UserDB(db),
		quimby.UserPassword(*userPW),
		quimby.UserPermission(*userPerm),
	)
	f(u)
	defer db.Close()
}

func doGadget(f gadgetNeeder) {
	db := getDB()
	g := &quimby.Gadget{
		DB:   db,
		Name: *gadgetName,
		Host: *gadgetHost,
	}
	f(g)
	db.Close()
}

func addDB(f dbNeeder) {
	db := getDB()
	f(db)
	defer db.Close()
}

func startServer(db *bolt.DB) {
	port := os.Getenv("QUIMBY_PORT")
	if port == "" {
		log.Fatal("you must specify a port with QUIMBY_PORT")
	}

	domain := os.Getenv("QUIMBY_DOMAIN")
	if domain == "" {
		log.Fatal("you must specify a domain with QUIMBY_DOMAIN")
	}

	internalPort := os.Getenv("QUIMBY_INTERNAL_PORT")
	if port == "" {
		log.Fatal("you must specify a port with QUIMBY_INTERNAL_PORT")
	}

	var lg *log.Logger
	if os.Getenv("QUIMBY_NULLLOG") != "" {
		lg = log.New(ioutil.Discard, "quimby ", log.Ltime)
	} else {
		lg = log.New(os.Stdout, "quimby ", log.Ltime)
	}
	clients := quimby.NewClientHolder()
	tfa := quimby.NewTFA(domain)
	start(db, port, internalPort, "/", "/api", lg, clients, tfa)
}

func getMiddleware(perm handlers.ACL, f http.HandlerFunc) http.Handler {
	return alice.New(handlers.Perm(perm)).Then(http.HandlerFunc(f))
}

func start(db *bolt.DB, port, internalPort, root string, iRoot string, lg quimby.Logger, clients *quimby.ClientHolder, tfa quimby.TFAer) {
	quimby.Clients = clients
	quimby.DB = db
	quimby.LG = lg
	handlers.DB = db
	handlers.LG = lg
	handlers.TFA = tfa

	go startInternal(iRoot, db, lg, internalPort)
	go startHomeKit(db, lg)

	r := rex.New("main")
	r.Post("/api/login", http.HandlerFunc(handlers.Login))
	r.Post("/api/logout", http.HandlerFunc(handlers.Logout))
	r.Get("/api/ping", getMiddleware(handlers.Read, handlers.Ping))
	r.Get("/api/currentuser", getMiddleware(handlers.Read, handlers.GetCurrentUser))
	r.Get("/api/users", getMiddleware(handlers.Admin, handlers.GetUsers))
	r.Post("/api/users", getMiddleware(handlers.Admin, handlers.AddUser))
	r.Delete("/api/users/{username}", getMiddleware(handlers.Admin, handlers.DeleteUser))
	r.Post("/api/users/{username}/permission", getMiddleware(handlers.Admin, handlers.UpdateUserPermission))
	r.Post("/api/users/{username}/password", getMiddleware(handlers.Admin, handlers.UpdateUserPassword))
	r.Get("/api/users/{username}", getMiddleware(handlers.Admin, handlers.GetUser))
	r.Get("/api/gadgets", getMiddleware(handlers.Read, handlers.GetGadgets))
	r.Post("/api/gadgets", getMiddleware(handlers.Read, handlers.AddGadget))
	r.Get("/api/gadgets/{id}", getMiddleware(handlers.Read, handlers.GetGadget))
	r.Post("/api/gadgets/{id}", getMiddleware(handlers.Write, handlers.UpdateGadget))
	r.Delete("/api/gadgets/{id}", getMiddleware(handlers.Write, handlers.DeleteGadget))
	r.Post("/api/gadgets/{id}/command", getMiddleware(handlers.Write, handlers.SendCommand))
	r.Post("/api/gadgets/{id}/method", getMiddleware(handlers.Write, handlers.SendMethod))
	r.Get("/api/gadgets/{id}/websocket", getMiddleware(handlers.Write, handlers.Connect))
	r.Get("/api/gadgets/{id}/values", getMiddleware(handlers.Read, handlers.GetUpdates))
	r.Get("/api/gadgets/{id}/status", getMiddleware(handlers.Read, handlers.GetStatus))
	r.Post("/api/gadgets/{id}/notes", getMiddleware(handlers.Write, handlers.AddNote))
	r.Get("/api/gadgets/{id}/notes", getMiddleware(handlers.Read, handlers.GetNotes))
	r.Get("/api/gadgets/{id}/locations/{location}/devices/{device}/status", getMiddleware(handlers.Read, handlers.GetDevice))
	r.Post("/api/gadgets/{id}/locations/{location}/devices/{device}/status", getMiddleware(handlers.Write, handlers.UpdateDevice))
	r.Get("/api/gadgets/{id}/sources/{name}", getMiddleware(handlers.Read, handlers.GetDataPoints))
	r.Get("/api/gadgets/{id}/sources/{name}/csv", getMiddleware(handlers.Read, handlers.GetDataPointsCSV))
	r.Get("/api/beer/{name}", getMiddleware(handlers.Read, handlers.GetRecipe))
	r.Get("/api/admin/clients", getMiddleware(handlers.Admin, handlers.GetClients))

	r.ServeFiles(http.FileServer(rice.MustFindBox("www/dist").HTTPBox()))

	chain := alice.New(handlers.Auth(db, lg, "main"), handlers.FetchGadget(), handlers.Error(lg)).Then(r)

	http.Handle(root, chain)

	addr := fmt.Sprintf("%s:%s", iface, port)
	lg.Printf("listening on %s\n", addr)
	if keyPath == "" {
		lg.Println(http.ListenAndServe(addr, chain))
	} else {
		lg.Println(http.ListenAndServeTLS(fmt.Sprintf("%s:443", iface), certPath, keyPath, chain))
	}
}

func startHomeKit(db *bolt.DB, lg quimby.Logger) {
	key := os.Getenv("QUIMBY_HOMEKIT")
	if key == "" {
		lg.Println("QUIMBY_HOMEKIT not set, not starting homekit")
		return
	}
	hk := quimby.NewHomeKit(key, db)
	hk.Start()
}

//This is the endpoint that the gadgets report to. It is
//served on a separate port so it doesn't have to be exposed
//publicly if the main port is exposed.
func startInternal(iRoot string, db *bolt.DB, lg quimby.Logger, port string) {
	r := rex.New("internal")
	r.Post("/internal/updates", getMiddleware(handlers.Write, handlers.RelayMessage))
	r.Post("/internal/gadgets/{id}/sources/{name}", getMiddleware(handlers.Write, handlers.AddDataPoint))

	chain := alice.New(handlers.Auth(db, lg, "internal"), handlers.FetchGadget()).Then(r)

	http.Handle(iRoot, chain)
	a := fmt.Sprintf(":%s", port)
	lg.Printf("listening on %s", a)
	err := http.ListenAndServe(a, chain)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>