<commit_before>package lapack

import (
	"testing"

	"github.com/ready-steady/support/assert"
	"github.com/ready-steady/support/fixture"
)

func TestDSYEV(t *testing.T) {
	n := 5

	A := []float64{
		0.162182308193243,
		0.601981941401637,
		0.450541598502498,
		0.825816977489547,
		0.106652770180584,
		0.601981941401637,
		0.262971284540144,
		0.083821377996933,
		0.538342435260057,
		0.961898080855054,
		0.450541598502498,
		0.083821377996933,
		0.228976968716819,
		0.996134716626885,
		0.004634224134067,
		0.825816977489547,
		0.538342435260057,
		0.996134716626885,
		0.078175528753184,
		0.774910464711502,
		0.106652770180584,
		0.961898080855054,
		0.004634224134067,
		0.774910464711502,
		0.817303220653433,
	}

	w := make([]float64, n)
	work := []float64{0}
	lwork := -1
	info := 0

	DSYEV('V', 'U', n, A, n, w, work, lwork, &info)

	lwork = int(work[0])
	work = make([]float64, lwork)

	DSYEV('V', 'U', n, A, n, w, work, lwork, &info)

	assert.Equal(info, 0, t)

	eigenVectors := []float64{
		-0.350512137830478,
		+0.116468084895727,
		-0.435005782872646,
		+0.750503447417042,
		-0.333303121372602,
		+0.462361750400701,
		-0.693041256027589,
		-0.409079614137348,
		+0.219801690292016,
		+0.300427221556423,
		-0.638529696902280,
		-0.450088584675982,
		+0.439292414730346,
		+0.201686466663592,
		+0.395025107611391,
		-0.330279135877552,
		+0.306798908225146,
		-0.590447291483772,
		-0.272545278020086,
		+0.611458248553625,
		+0.382829428829298,
		+0.457628341015560,
		+0.319089342511548,
		+0.522946873930437,
		+0.518388356797788,
	}

	eigenValues := []float64{
		-1.145487871954612,
		-0.676875725405419,
		-0.050275996742486,
		+0.892450858666551,
		+2.529798046292787,
	}

	assert.AlmostEqual(A, eigenVectors, t)
	assert.AlmostEqual(w, eigenValues, t)
}

func BenchmarkDSYEV(b *testing.B) {
	n := 1000
	A := fixture.MakeSymMatrix(uint32(n))
	w := make([]float64, n)
	lwork := 4 * n
	work := make([]float64, lwork)
	info := 0

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		DSYEV('V', 'U', n, A, n, w, work, lwork, &info)
		if info != 0 {
			b.Fatalf("got %v instead of 0", info)
		}
	}
}
<commit_msg>Updated the usage of support/fixture<commit_after>package lapack

import (
	"testing"

	"github.com/ready-steady/support/assert"
	"github.com/ready-steady/support/fixture"
)

func TestDSYEV(t *testing.T) {
	n := 5

	A := []float64{
		0.162182308193243,
		0.601981941401637,
		0.450541598502498,
		0.825816977489547,
		0.106652770180584,
		0.601981941401637,
		0.262971284540144,
		0.083821377996933,
		0.538342435260057,
		0.961898080855054,
		0.450541598502498,
		0.083821377996933,
		0.228976968716819,
		0.996134716626885,
		0.004634224134067,
		0.825816977489547,
		0.538342435260057,
		0.996134716626885,
		0.078175528753184,
		0.774910464711502,
		0.106652770180584,
		0.961898080855054,
		0.004634224134067,
		0.774910464711502,
		0.817303220653433,
	}

	w := make([]float64, n)
	work := []float64{0}
	lwork := -1
	info := 0

	DSYEV('V', 'U', n, A, n, w, work, lwork, &info)

	lwork = int(work[0])
	work = make([]float64, lwork)

	DSYEV('V', 'U', n, A, n, w, work, lwork, &info)

	assert.Equal(info, 0, t)

	eigenVectors := []float64{
		-0.350512137830478,
		+0.116468084895727,
		-0.435005782872646,
		+0.750503447417042,
		-0.333303121372602,
		+0.462361750400701,
		-0.693041256027589,
		-0.409079614137348,
		+0.219801690292016,
		+0.300427221556423,
		-0.638529696902280,
		-0.450088584675982,
		+0.439292414730346,
		+0.201686466663592,
		+0.395025107611391,
		-0.330279135877552,
		+0.306798908225146,
		-0.590447291483772,
		-0.272545278020086,
		+0.611458248553625,
		+0.382829428829298,
		+0.457628341015560,
		+0.319089342511548,
		+0.522946873930437,
		+0.518388356797788,
	}

	eigenValues := []float64{
		-1.145487871954612,
		-0.676875725405419,
		-0.050275996742486,
		+0.892450858666551,
		+2.529798046292787,
	}

	assert.AlmostEqual(A, eigenVectors, t)
	assert.AlmostEqual(w, eigenValues, t)
}

func BenchmarkDSYEV(b *testing.B) {
	n := 1000
	A := fixture.MakeSymmetricMatrix(uint(n))
	w := make([]float64, n)
	lwork := 4 * n
	work := make([]float64, lwork)
	info := 0

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		DSYEV('V', 'U', n, A, n, w, work, lwork, &info)
		if info != 0 {
			b.Fatalf("got %v instead of 0", info)
		}
	}
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"flag"
	"fmt"

	"github.com/mschoch/gouchstore"
)

var startId = flag.String("startId", "", "the document ID to scan from")
var endId = flag.String("endId", "", "the document ID to scan to")
var startSeq = flag.Int("startSeq", -1, "the sequence number to scan from")
var endSeq = flag.Int("endSeq", -1, "the sequence number to scan to")

func allDocumentsCallback(g *gouchstore.Gouchstore, docInfo *gouchstore.DocumentInfo, userContext interface{}) {
	bytes, err := json.MarshalIndent(docInfo, "", "  ")
	if err != nil {
		fmt.Println(err)
	} else {
		userContext.(map[string]int)["count"]++
		fmt.Println(string(bytes))
	}
}

func main() {

	flag.Parse()

	if flag.NArg() < 1 {
		fmt.Println("Must specify path to a gouchstore compatible file")
		return
	}
	db, err := gouchstore.Open(flag.Args()[0], gouchstore.OPEN_RDONLY)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer db.Close()

	context := map[string]int{"count": 0}

	// sequence mode
	if *startSeq != -1 || *endSeq != -1 {
		if *startSeq < 0 {
			*startSeq = 0
		}
		db.ChangesSince(uint64(*startSeq), uint64(*endSeq), allDocumentsCallback, context)
		if err != nil {
			fmt.Println(err)
			return
		}
	} else { // id mode
		err = db.AllDocuments(*startId, *endId, allDocumentsCallback, context)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	fmt.Printf("Listed %d documents\n", context["count"])
}
<commit_msg>update to correct method signature<commit_after>//  Copyright (c) 2014 Marty Schoch
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package main

import (
	"encoding/json"
	"flag"
	"fmt"

	"github.com/mschoch/gouchstore"
)

var startId = flag.String("startId", "", "the document ID to scan from")
var endId = flag.String("endId", "", "the document ID to scan to")
var startSeq = flag.Int("startSeq", -1, "the sequence number to scan from")
var endSeq = flag.Int("endSeq", -1, "the sequence number to scan to")

func allDocumentsCallback(g *gouchstore.Gouchstore, docInfo *gouchstore.DocumentInfo, userContext interface{}) error {
	bytes, err := json.MarshalIndent(docInfo, "", "  ")
	if err != nil {
		fmt.Println(err)
	} else {
		userContext.(map[string]int)["count"]++
		fmt.Println(string(bytes))
	}
	return nil
}

func main() {

	flag.Parse()

	if flag.NArg() < 1 {
		fmt.Println("Must specify path to a gouchstore compatible file")
		return
	}
	db, err := gouchstore.Open(flag.Args()[0], gouchstore.OPEN_RDONLY)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer db.Close()

	context := map[string]int{"count": 0}

	// sequence mode
	if *startSeq != -1 || *endSeq != -1 {
		if *startSeq < 0 {
			*startSeq = 0
		}
		db.ChangesSince(uint64(*startSeq), uint64(*endSeq), allDocumentsCallback, context)
		if err != nil {
			fmt.Println(err)
			return
		}
	} else { // id mode
		err = db.AllDocuments(*startId, *endId, allDocumentsCallback, context)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	fmt.Printf("Listed %d documents\n", context["count"])
}
<|endoftext|>


<commit_before>// This file was taken from:
// https://github.com/OpenBazaar/openbazaar-go/blob/master/core/ulimit_non_unix.go

// +build darwin linux netbsd openbsd

package ulimit

const (
	// DefaultFDLimit is the default recommended number of FDs to allocate.
	DefaultFDLimit uint64 = 1 << 15 // 32k
)
<commit_msg>fixed ulimit constant error on non-darwin OSs<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package ulimit

const (
	// DefaultFDLimit is the default recommended number of FDs to allocate.
	DefaultFDLimit uint64 = 1 << 15 // 32k
)
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
)

type Gateway struct {
	Client        *docker.Client
	DefaultDomain string
	SkipNoDomain  bool
	Destinations  map[string]DestinationMap
	*sync.Mutex
}

func NewGateway(client *docker.Client) *Gateway {
	return &Gateway{
		Client:        client,
		DefaultDomain: os.Getenv("GW_DOMAIN"),
		SkipNoDomain:  os.Getenv("GW_SKIP_NO_DOMAIN") != "",
		Destinations:  map[string]DestinationMap{},
		Mutex:         &sync.Mutex{},
	}
}

func (gw *Gateway) fetchDomain(c *docker.Container) string {
	for _, v := range c.Config.Env {
		chunks := strings.Split(v, "=")

		if chunks[0] == "DOMAIN" && len(chunks) > 1 {
			return chunks[1]
		}
	}

	if gw.SkipNoDomain {
		return ""
	}

	return fmt.Sprintf("%s.%s", c.ID[0:12], gw.DefaultDomain)
}

func (gw *Gateway) notFound(w http.ResponseWriter, r *http.Request) {
	routes := []string{}

	for host, _ := range gw.Destinations {
		routes = append(routes, fmt.Sprintf("- http://%s", host))
	}

	msg := []string{
		"Cant find any routes for this host!\n",
		"Check available URLs:",
		strings.Join(routes, "\n"),
	}

	http.Error(w, strings.Join(msg, "\n"), http.StatusBadGateway)
}

func (gw *Gateway) Load() error {
	containers, err := gw.Client.ListContainers(docker.ListContainersOptions{})
	if err != nil {
		return err
	}

	for _, c := range containers {
		container, err := gw.Client.InspectContainer(c.ID)
		if err == nil {
			gw.Add(container)
		}
	}

	return nil
}

func (gw *Gateway) Add(container *docker.Container) error {
	log.Println("Adding container:", container.ID)

	key := gw.fetchDomain(container)

	if key == "" {
		log.Println("Skipped adding container", container.ID)
		return nil
	}

	if gw.Destinations[key][container.ID] != nil {
		return fmt.Errorf("Destination alreaady exists!")
	}

	if len(container.Config.ExposedPorts) == 0 {
		log.Printf("Container %s does not have any exposed ports\n", container.ID)
		return nil
	}

	dest, err := NewDestination(container)
	if err != nil {
		return err
	}

	gw.Lock()
	defer gw.Unlock()

	if gw.Destinations[key] == nil {
		gw.Destinations[key] = DestinationMap{}
	}

	gw.Destinations[key][container.ID] = dest
	return nil
}

func (gw *Gateway) Remove(container *docker.Container) error {
	log.Println("Removing container:", container.ID)
	key := gw.fetchDomain(container)

	if len(gw.Destinations[key]) == 0 {
		return nil
	}

	gw.Lock()
	defer gw.Unlock()

	delete(gw.Destinations[key], container.ID)
	return nil
}

func (gw *Gateway) RemoveByContainerId(containerId string) {
	gw.Lock()
	defer gw.Unlock()

	for host, dstMap := range gw.Destinations {
		for id := range dstMap {
			// Remove matching container from the route table
			if id == containerId {
				delete(dstMap, id)
			}
		}

		// Remove host from the routing table if there are no destination
		if len(dstMap) == 0 {
			delete(gw.Destinations, host)
		}
	}
}

func (gw *Gateway) Flush() {
	gw.Lock()
	defer gw.Unlock()

	for k := range gw.Destinations {
		delete(gw.Destinations, k)
	}
}

func (gw *Gateway) Find(reqHost string) *Destination {
	host := strings.ToLower(strings.Split(reqHost, ":")[0])

	if len(gw.Destinations[host]) == 0 {
		return nil
	}

	list := []*Destination{}
	for _, dst := range gw.Destinations[host] {
		list = append(list, dst)
	}

	return list[rand.Intn(len(list))]
}

func (gw *Gateway) Handle(w http.ResponseWriter, r *http.Request) {
	destination := gw.Find(r.Host)

	log.Printf("Request method=%s host=%s path=%s -> %s\n", r.Method, r.Host, r.RequestURI, destination)

	if destination == nil {
		gw.notFound(w, r)
		return
	}

	destination.proxy.ServeHTTP(w, r)
}

func (gw *Gateway) RenderDestinations(w http.ResponseWriter, r *http.Request) {
	result := map[string][]string{}

	for k, dstMap := range gw.Destinations {
		for _, dst := range dstMap {
			result[k] = append(result[k], dst.String())
		}
	}

	data, _ := json.Marshal(result)
	fmt.Fprintf(w, "%s", data)
}

func (gw *Gateway) RenderLogs(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		gw.notFound(w, r)
		return
	}

	// Determine how many lines of logs we need to fetch
	lines := r.URL.Query().Get("lines")
	if lines == "" {
		lines = "3000"
	}

	container, err := gw.Client.InspectContainer(dest.containerId)
	if err != nil {
		http.Error(w, "Unable to inspect container:"+dest.containerId, http.StatusBadRequest)
		return
	}

	buff := bytes.NewBuffer([]byte{})

	err = gw.Client.Logs(docker.LogsOptions{
		Container:    dest.containerId,
		Stdout:       true,
		Stderr:       true,
		OutputStream: buff,
		ErrorStream:  buff,
		RawTerminal:  container.Config.Tty,
		Tail:         lines,
	})

	if err != nil {
		fmt.Fprintln(w, "Error while fetching logs:", err)
		return
	}

	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", buff.String())
}

func (gw *Gateway) RenderEnvironment(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		gw.notFound(w, r)
		return
	}

	container, err := gw.Client.InspectContainer(dest.containerId)
	if err != nil {
		fmt.Fprintln(w, "Error while inspecting container:", err)
		return
	}

	env := strings.Join(container.Config.Env, "\n")
	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", env)
}

func (gw *Gateway) RenderReset(w http.ResponseWriter, r *http.Request) {
	gw.Flush()

	err := gw.Load()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	http.Redirect(w, r, "/_routes", 301)
}

func (gw *Gateway) RenderHelp(w http.ResponseWriter, r *http.Request) {
	help := strings.TrimSpace(`
List of all available system endpoints;

/_help   - Show this message
/_routes - List all available routes
/_reset  - Flush all existing routes and load new ones
/_logs   - Print container logs (for specified host)
/_env    - Print container environment variables (for specified host)

To get logs or environment variable for a container:

http://my-container.domain.com/_logs
http://my-container.domain.com/_logs?lines=100
http://my-container.domain.com/_env
`)

	fmt.Fprintf(w, help)
}

func (gw *Gateway) Start(bind string) error {
	log.Printf("Starting gateway server on http://%s\n", bind)

	http.HandleFunc("/_help", gw.RenderHelp)
	http.HandleFunc("/_routes", gw.RenderDestinations)
	http.HandleFunc("/_destinations", gw.RenderDestinations)
	http.HandleFunc("/_logs", gw.RenderLogs)
	http.HandleFunc("/_env", gw.RenderEnvironment)
	http.HandleFunc("/_reset", gw.RenderReset)
	http.HandleFunc("/", gw.Handle)

	return http.ListenAndServe(bind, nil)
}
<commit_msg>Print available routes only if there are any<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
)

type Gateway struct {
	Client        *docker.Client
	DefaultDomain string
	SkipNoDomain  bool
	Destinations  map[string]DestinationMap
	*sync.Mutex
}

func NewGateway(client *docker.Client) *Gateway {
	return &Gateway{
		Client:        client,
		DefaultDomain: os.Getenv("GW_DOMAIN"),
		SkipNoDomain:  os.Getenv("GW_SKIP_NO_DOMAIN") != "",
		Destinations:  map[string]DestinationMap{},
		Mutex:         &sync.Mutex{},
	}
}

func (gw *Gateway) fetchDomain(c *docker.Container) string {
	for _, v := range c.Config.Env {
		chunks := strings.Split(v, "=")

		if chunks[0] == "DOMAIN" && len(chunks) > 1 {
			return chunks[1]
		}
	}

	if gw.SkipNoDomain {
		return ""
	}

	return fmt.Sprintf("%s.%s", c.ID[0:12], gw.DefaultDomain)
}

func (gw *Gateway) notFound(w http.ResponseWriter, r *http.Request) {
	routes := []string{}

	for host, _ := range gw.Destinations {
		routes = append(routes, fmt.Sprintf("- http://%s", host))
	}

	msg := "Cant find any routes for this host!\n"

	if len(gw.Destinations) > 0 {
		msg += "\nCheck available URLs:\n"
		msg += strings.Join(routes, "\n")
	}

	http.Error(w, msg, http.StatusBadGateway)
}

func (gw *Gateway) Load() error {
	containers, err := gw.Client.ListContainers(docker.ListContainersOptions{})
	if err != nil {
		return err
	}

	for _, c := range containers {
		container, err := gw.Client.InspectContainer(c.ID)
		if err == nil {
			gw.Add(container)
		}
	}

	return nil
}

func (gw *Gateway) Add(container *docker.Container) error {
	log.Println("Adding container:", container.ID)

	key := gw.fetchDomain(container)

	if key == "" {
		log.Println("Skipped adding container", container.ID)
		return nil
	}

	if gw.Destinations[key][container.ID] != nil {
		return fmt.Errorf("Destination alreaady exists!")
	}

	if len(container.Config.ExposedPorts) == 0 {
		log.Printf("Container %s does not have any exposed ports\n", container.ID)
		return nil
	}

	dest, err := NewDestination(container)
	if err != nil {
		return err
	}

	gw.Lock()
	defer gw.Unlock()

	if gw.Destinations[key] == nil {
		gw.Destinations[key] = DestinationMap{}
	}

	gw.Destinations[key][container.ID] = dest
	return nil
}

func (gw *Gateway) Remove(container *docker.Container) error {
	log.Println("Removing container:", container.ID)
	key := gw.fetchDomain(container)

	if len(gw.Destinations[key]) == 0 {
		return nil
	}

	gw.Lock()
	defer gw.Unlock()

	delete(gw.Destinations[key], container.ID)
	return nil
}

func (gw *Gateway) RemoveByContainerId(containerId string) {
	gw.Lock()
	defer gw.Unlock()

	for host, dstMap := range gw.Destinations {
		for id := range dstMap {
			// Remove matching container from the route table
			if id == containerId {
				delete(dstMap, id)
			}
		}

		// Remove host from the routing table if there are no destination
		if len(dstMap) == 0 {
			delete(gw.Destinations, host)
		}
	}
}

func (gw *Gateway) Flush() {
	gw.Lock()
	defer gw.Unlock()

	for k := range gw.Destinations {
		delete(gw.Destinations, k)
	}
}

func (gw *Gateway) Find(reqHost string) *Destination {
	host := strings.ToLower(strings.Split(reqHost, ":")[0])

	if len(gw.Destinations[host]) == 0 {
		return nil
	}

	list := []*Destination{}
	for _, dst := range gw.Destinations[host] {
		list = append(list, dst)
	}

	return list[rand.Intn(len(list))]
}

func (gw *Gateway) Handle(w http.ResponseWriter, r *http.Request) {
	destination := gw.Find(r.Host)

	log.Printf("Request method=%s host=%s path=%s -> %s\n", r.Method, r.Host, r.RequestURI, destination)

	if destination == nil {
		gw.notFound(w, r)
		return
	}

	destination.proxy.ServeHTTP(w, r)
}

func (gw *Gateway) RenderDestinations(w http.ResponseWriter, r *http.Request) {
	result := map[string][]string{}

	for k, dstMap := range gw.Destinations {
		for _, dst := range dstMap {
			result[k] = append(result[k], dst.String())
		}
	}

	data, _ := json.Marshal(result)
	fmt.Fprintf(w, "%s", data)
}

func (gw *Gateway) RenderLogs(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		gw.notFound(w, r)
		return
	}

	// Determine how many lines of logs we need to fetch
	lines := r.URL.Query().Get("lines")
	if lines == "" {
		lines = "3000"
	}

	container, err := gw.Client.InspectContainer(dest.containerId)
	if err != nil {
		http.Error(w, "Unable to inspect container:"+dest.containerId, http.StatusBadRequest)
		return
	}

	buff := bytes.NewBuffer([]byte{})

	err = gw.Client.Logs(docker.LogsOptions{
		Container:    dest.containerId,
		Stdout:       true,
		Stderr:       true,
		OutputStream: buff,
		ErrorStream:  buff,
		RawTerminal:  container.Config.Tty,
		Tail:         lines,
	})

	if err != nil {
		fmt.Fprintln(w, "Error while fetching logs:", err)
		return
	}

	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", buff.String())
}

func (gw *Gateway) RenderEnvironment(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		gw.notFound(w, r)
		return
	}

	container, err := gw.Client.InspectContainer(dest.containerId)
	if err != nil {
		fmt.Fprintln(w, "Error while inspecting container:", err)
		return
	}

	env := strings.Join(container.Config.Env, "\n")
	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", env)
}

func (gw *Gateway) RenderReset(w http.ResponseWriter, r *http.Request) {
	gw.Flush()

	err := gw.Load()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	http.Redirect(w, r, "/_routes", 301)
}

func (gw *Gateway) RenderHelp(w http.ResponseWriter, r *http.Request) {
	help := strings.TrimSpace(`
List of all available system endpoints;

/_help   - Show this message
/_routes - List all available routes
/_reset  - Flush all existing routes and load new ones
/_logs   - Print container logs (for specified host)
/_env    - Print container environment variables (for specified host)

To get logs or environment variable for a container:

http://my-container.domain.com/_logs
http://my-container.domain.com/_logs?lines=100
http://my-container.domain.com/_env
`)

	fmt.Fprintf(w, help)
}

func (gw *Gateway) Start(bind string) error {
	log.Printf("Starting gateway server on http://%s\n", bind)

	http.HandleFunc("/_help", gw.RenderHelp)
	http.HandleFunc("/_routes", gw.RenderDestinations)
	http.HandleFunc("/_destinations", gw.RenderDestinations)
	http.HandleFunc("/_logs", gw.RenderLogs)
	http.HandleFunc("/_env", gw.RenderEnvironment)
	http.HandleFunc("/_reset", gw.RenderReset)
	http.HandleFunc("/", gw.Handle)

	return http.ListenAndServe(bind, nil)
}
<|endoftext|>


<commit_before>package validator

import (
	"errors"
	"reflect"
	"strconv"

	"github.com/elsonwu/i18n"
	"github.com/elsonwu/is"
)

type String struct {
	CoreValidate
}

func (self *String) Filter(f reflect.StructField, fv reflect.Value) bool {
	return f.Type.Kind() == reflect.String
}

func (self *String) Validate(f reflect.StructField, fv reflect.Value) []error {
	if "required" == f.Tag.Get("required") && "" == fv.String() {
		return []error{errors.New(i18n.T("%s cannot be blank", FieldName(f)))}
	}

	min := f.Tag.Get("min")
	if "" != min {
		min2, err := strconv.Atoi(min)
		if err != nil {
			return []error{err}
		}

		if min2 > fv.Len() {
			return []error{errors.New(i18n.T("%s is too short", FieldName(f)))}
		}
	}

	max := f.Tag.Get("max")
	if "" != max {
		max2, err := strconv.Atoi(max)
		if err != nil {
			return []error{err}
		}

		if max2 < fv.Len() {
			return []error{errors.New(i18n.T("%s is too long", FieldName(f)))}
		}
	}

	isv := f.Tag.Get("is")
	if "" != isv {
		switch isv {
		case "email":
			if !is.Email(fv.String()) {
				return []error{errors.New(i18n.T("%s is not a valid email", FieldName(f)))}
			}
		case "url":
			if !is.Url(fv.String()) {
				return []error{errors.New(i18n.T("%s is not a valid url", FieldName(f)))}
			}
		}
	}

	return nil
}
<commit_msg>support "omitempty" for is:email and url<commit_after>package validator

import (
	"errors"
	"reflect"
	"strconv"
	"strings"

	"github.com/elsonwu/i18n"
	"github.com/elsonwu/is"
)

type String struct {
	CoreValidate
}

func (self *String) Filter(f reflect.StructField, fv reflect.Value) bool {
	return f.Type.Kind() == reflect.String
}

func (self *String) Validate(f reflect.StructField, fv reflect.Value) []error {
	if "required" == f.Tag.Get("required") && "" == fv.String() {
		return []error{errors.New(i18n.T("%s cannot be blank", FieldName(f)))}
	}

	min := f.Tag.Get("min")
	if "" != min {
		min2, err := strconv.Atoi(min)
		if err != nil {
			return []error{err}
		}

		if min2 > fv.Len() {
			return []error{errors.New(i18n.T("%s is too short", FieldName(f)))}
		}
	}

	max := f.Tag.Get("max")
	if "" != max {
		max2, err := strconv.Atoi(max)
		if err != nil {
			return []error{err}
		}

		if max2 < fv.Len() {
			return []error{errors.New(i18n.T("%s is too long", FieldName(f)))}
		}
	}

	isv := f.Tag.Get("is")
	if "" != isv {
		tags := strings.Split(isv, ",")

		if 1 < len(tags) {
			if "omitempty" == tags[1] && fv.String() == "" {
				goto skip
			}
		}

		switch tags[0] {
		case "email":
			if !is.Email(fv.String()) {
				return []error{errors.New(i18n.T("%s is not a valid email", FieldName(f)))}
			}
		case "url":
			if !is.Url(fv.String()) {
				return []error{errors.New(i18n.T("%s is not a valid url", FieldName(f)))}
			}
		}
	skip:
	}

	return nil
}
<|endoftext|>


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

// TODO(sadovsky): Once the storage engine layer is made public, implement a
// Syncbase-based storage engine so that we can run all the storage engine tests
// against Syncbase itself.

package nosql_test

import (
	"testing"

	wire "v.io/syncbase/v23/services/syncbase/nosql"
	"v.io/syncbase/v23/syncbase"
	"v.io/syncbase/v23/syncbase/nosql"
	tu "v.io/syncbase/v23/syncbase/testutil"
	"v.io/v23/naming"
	"v.io/v23/verror"
	_ "v.io/x/ref/runtime/factories/generic"
)

// Tests various Name and FullName methods.
func TestName(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")

	b, err := d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}

	if d.Name() != "d" {
		t.Errorf("Wrong name: %q", d.Name())
	}
	if d.FullName() != naming.Join(sName, "a", "d") {
		t.Errorf("Wrong full name: %q", d.FullName())
	}
	if b.Name() == d.Name() {
		t.Errorf("Names should not match: %q", b.Name())
	}
	if b.FullName() == d.FullName() {
		t.Errorf("Full names should not match: %q", b.FullName())
	}
}

// Tests basic functionality of BatchDatabase.
func TestBatchBasics(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	tb := tu.CreateTable(t, ctx, d, "tb")

	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{}, []interface{}{})

	var b1, b2 nosql.BatchDatabase
	var b1tb, b2tb nosql.Table
	var err error

	// Test that the effects of a transaction are not visible until commit.
	b1, err = d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb = b1.Table("tb")

	if err := b1tb.Put(ctx, "fooKey", "fooValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}

	// Check that foo is not yet visible.
	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{}, []interface{}{})

	if err := b1.Commit(ctx); err != nil {
		t.Fatalf("b1.Commit() failed: %v", err)
	}

	// Check that foo is now visible.
	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"fooKey"}, []interface{}{"fooValue"})

	// Test that concurrent transactions are isolated.
	if b1, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	if b2, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb, b2tb = b1.Table("tb"), b2.Table("tb")

	if err := b1tb.Put(ctx, "barKey", "barValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}
	if err := b1tb.Put(ctx, "bazKey", "bazValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}

	var got string
	if err := b2tb.Get(ctx, "barKey", &got); verror.ErrorID(err) != verror.ErrNoExist.ID {
		t.Fatalf("Get() should have failed: %v", err)
	}
	if err := b2tb.Put(ctx, "rabKey", "rabValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}

	if err := b1.Commit(ctx); err != nil {
		t.Fatalf("b1.Commit() failed: %v", err)
	}
	if err := b2.Commit(ctx); err == nil {
		t.Fatalf("b2.Commit() should have failed: %v", err)
	}

	// Check that foo, bar, and baz (but not rab) are now visible.
	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"barKey", "bazKey", "fooKey"}, []interface{}{"barValue", "bazValue", "fooValue"})
}

// Tests enforcement of BatchOptions.ReadOnly.
func TestReadOnlyBatch(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	tb := tu.CreateTable(t, ctx, d, "tb")

	if err := tb.Put(ctx, "fooKey", "fooValue"); err != nil {
		t.Fatalf("tb.Put() failed: %v", err)
	}

	b1, err := d.BeginBatch(ctx, wire.BatchOptions{ReadOnly: true})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb := b1.Table("tb")

	if err := b1tb.Put(ctx, "barKey", "barValue"); verror.ErrorID(err) != wire.ErrReadOnlyBatch.ID {
		t.Fatalf("Put() should have failed: %v", err)
	}
	if err := b1tb.Delete(ctx, nosql.Prefix("fooKey")); verror.ErrorID(err) != wire.ErrReadOnlyBatch.ID {
		t.Fatalf("Table.Delete() should have failed: %v", err)
	}
	if err := b1tb.Row("fooKey").Delete(ctx); verror.ErrorID(err) != wire.ErrReadOnlyBatch.ID {
		t.Fatalf("Row.Delete() should have failed: %v", err)
	}
}

// Tests that all ops fail after attempted commit or abort.
func TestOpAfterFinalize(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	tb := tu.CreateTable(t, ctx, d, "tb")

	// TODO(sadovsky): Add some sort of "op after finalize" error type and check
	// for it specifically below.
	checkOpsFail := func(b nosql.BatchDatabase) {
		btb := b.Table("tb")
		var got string
		if err := btb.Get(ctx, "fooKey", &got); err == nil {
			tu.Fatal(t, "Get() should have failed")
		}
		it := btb.Scan(ctx, nosql.Prefix(""))
		it.Advance()
		if it.Err() == nil {
			tu.Fatal(t, "Scan() should have failed")
		}
		if err := btb.Put(ctx, "barKey", "barValue"); err == nil {
			tu.Fatal(t, "Put() should have failed")
		}
		if err := btb.Delete(ctx, nosql.Prefix("fooKey")); err == nil {
			tu.Fatal(t, "Table.Delete() should have failed: %v", err)
		}
		if err := btb.Row("fooKey").Delete(ctx); err == nil {
			tu.Fatal(t, "Row.Delete() should have failed: %v", err)
		}
		if err := b.Commit(ctx); err == nil {
			tu.Fatal(t, "Commit() should have failed")
		}
	}

	// Commit a transaction, check that subsequent ops fail.
	b1, err := d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb := b1.Table("tb")

	if err := b1tb.Put(ctx, "fooKey", "fooValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}
	if err := b1.Commit(ctx); err != nil {
		t.Fatalf("b1.Commit() failed: %v", err)
	}
	checkOpsFail(b1)

	// Create a transaction with a conflict, check that the commit fails, then
	// check that subsequent ops fail.
	if b1, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb = b1.Table("tb")

	// Conflicts with future b1tb.Get().
	if err := tb.Put(ctx, "fooKey", "v2"); err != nil {
		t.Fatalf("tb.Put() failed: %v", err)
	}

	var got string
	if err := b1tb.Get(ctx, "fooKey", &got); err != nil {
		t.Fatalf("Get() failed: %v", err)
	}
	want := "fooValue"
	if got != want {
		t.Fatalf("Get() returned unexpected value: got %q, want %q", got, want)
	}
	if err := b1.Commit(ctx); err == nil {
		t.Fatalf("b1.Commit() should have failed: %v", err)
	}
	checkOpsFail(b1)

	// Create a transaction and immediately abort it, then check that subsequent
	// ops fail.
	if b1, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb = b1.Table("tb")
	b1.Abort(ctx)
	checkOpsFail(b1)
}

// Tests that batch methods called on non-batch return ErrNotBoundToBatch and
// that non-batch methods called on batch return ErrBoundToBatch.
func TestDisallowedMethods(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	b, err := d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}

	// Batch methods on non-batch.
	dc := wire.DatabaseClient(d.FullName())
	if err := dc.Commit(ctx); verror.ErrorID(err) != wire.ErrNotBoundToBatch.ID {
		t.Fatalf("dc.Commit() should have failed: %v", err)
	}
	if err := dc.Abort(ctx); verror.ErrorID(err) != wire.ErrNotBoundToBatch.ID {
		t.Fatalf("dc.Abort() should have failed: %v", err)
	}

	// Non-batch methods on batch.
	bc := wire.DatabaseClient(b.FullName())
	if err := bc.Create(ctx, nil); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.Create() should have failed: %v", err)
	}
	if err := bc.Delete(ctx); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.Delete() should have failed: %v", err)
	}
	if _, err := bc.BeginBatch(ctx, wire.BatchOptions{}); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.BeginBatch() should have failed: %v", err)
	}
	if _, _, err := bc.GetPermissions(ctx); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.GetPermissions() should have failed: %v", err)
	}
	if err := bc.SetPermissions(ctx, nil, ""); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.SetPermissions() should have failed: %v", err)
	}
	// TODO(sadovsky): Test all other SyncGroupManager methods.
	if _, err := bc.GetSyncGroupNames(ctx); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.GetSyncGroupNames() should have failed: %v", err)
	}
}
<commit_msg>syncbase: BeginBatch() bugfix and restartability TODOs<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// TODO(sadovsky): Once the storage engine layer is made public, implement a
// Syncbase-based storage engine so that we can run all the storage engine tests
// against Syncbase itself.

package nosql_test

import (
	"testing"

	wire "v.io/syncbase/v23/services/syncbase/nosql"
	"v.io/syncbase/v23/syncbase"
	"v.io/syncbase/v23/syncbase/nosql"
	tu "v.io/syncbase/v23/syncbase/testutil"
	"v.io/v23/naming"
	"v.io/v23/verror"
	_ "v.io/x/ref/runtime/factories/generic"
)

// Tests various Name and FullName methods.
func TestName(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")

	b, err := d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}

	if d.Name() != "d" {
		t.Errorf("Wrong name: %q", d.Name())
	}
	if d.FullName() != naming.Join(sName, "a", "d") {
		t.Errorf("Wrong full name: %q", d.FullName())
	}
	if b.Name() == d.Name() {
		t.Errorf("Names should not match: %q", b.Name())
	}
	if b.FullName() == d.FullName() {
		t.Errorf("Full names should not match: %q", b.FullName())
	}
}

// Tests basic functionality of BatchDatabase.
func TestBatchBasics(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	tb := tu.CreateTable(t, ctx, d, "tb")

	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{}, []interface{}{})

	var b1, b2 nosql.BatchDatabase
	var b1tb, b2tb nosql.Table
	var err error

	// Test that the effects of a transaction are not visible until commit.
	b1, err = d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb = b1.Table("tb")

	if err := b1tb.Put(ctx, "fooKey", "fooValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}

	// Check that foo is not yet visible.
	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{}, []interface{}{})

	if err := b1.Commit(ctx); err != nil {
		t.Fatalf("b1.Commit() failed: %v", err)
	}

	// Check that foo is now visible.
	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"fooKey"}, []interface{}{"fooValue"})

	// Test that concurrent transactions are isolated.
	if b1, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	if b2, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb, b2tb = b1.Table("tb"), b2.Table("tb")

	if err := b1tb.Put(ctx, "barKey", "barValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}
	if err := b1tb.Put(ctx, "bazKey", "bazValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}

	var got string
	if err := b2tb.Get(ctx, "barKey", &got); verror.ErrorID(err) != verror.ErrNoExist.ID {
		t.Fatalf("Get() should have failed: %v", err)
	}
	if err := b2tb.Put(ctx, "rabKey", "rabValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}

	if err := b1.Commit(ctx); err != nil {
		t.Fatalf("b1.Commit() failed: %v", err)
	}
	if err := b2.Commit(ctx); err == nil {
		t.Fatalf("b2.Commit() should have failed: %v", err)
	}

	// Check that foo, bar, and baz (but not rab) are now visible.
	tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"barKey", "bazKey", "fooKey"}, []interface{}{"barValue", "bazValue", "fooValue"})
}

// Tests enforcement of BatchOptions.ReadOnly.
func TestReadOnlyBatch(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	tb := tu.CreateTable(t, ctx, d, "tb")

	if err := tb.Put(ctx, "fooKey", "fooValue"); err != nil {
		t.Fatalf("tb.Put() failed: %v", err)
	}

	b1, err := d.BeginBatch(ctx, wire.BatchOptions{ReadOnly: true})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb := b1.Table("tb")

	if err := b1tb.Put(ctx, "barKey", "barValue"); verror.ErrorID(err) != wire.ErrReadOnlyBatch.ID {
		t.Fatalf("Put() should have failed: %v", err)
	}
	if err := b1tb.Delete(ctx, nosql.Prefix("fooKey")); verror.ErrorID(err) != wire.ErrReadOnlyBatch.ID {
		t.Fatalf("Table.Delete() should have failed: %v", err)
	}
	if err := b1tb.Row("fooKey").Delete(ctx); verror.ErrorID(err) != wire.ErrReadOnlyBatch.ID {
		t.Fatalf("Row.Delete() should have failed: %v", err)
	}
}

// Tests that all ops fail after attempted commit or abort.
func TestOpAfterFinalize(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	tb := tu.CreateTable(t, ctx, d, "tb")

	// TODO(sadovsky): Add some sort of "op after finalize" error type and check
	// for it specifically below.
	checkOpsFail := func(b nosql.BatchDatabase) {
		btb := b.Table("tb")
		var got string
		if err := btb.Get(ctx, "fooKey", &got); err == nil {
			tu.Fatal(t, "Get() should have failed")
		}
		it := btb.Scan(ctx, nosql.Prefix(""))
		it.Advance()
		if it.Err() == nil {
			tu.Fatal(t, "Scan() should have failed")
		}
		if err := btb.Put(ctx, "barKey", "barValue"); err == nil {
			tu.Fatal(t, "Put() should have failed")
		}
		if err := btb.Delete(ctx, nosql.Prefix("fooKey")); err == nil {
			tu.Fatal(t, "Table.Delete() should have failed: %v", err)
		}
		if err := btb.Row("fooKey").Delete(ctx); err == nil {
			tu.Fatal(t, "Row.Delete() should have failed: %v", err)
		}
		if err := b.Commit(ctx); err == nil {
			tu.Fatal(t, "Commit() should have failed")
		}
	}

	// Commit a transaction, check that subsequent ops fail.
	b1, err := d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb := b1.Table("tb")

	if err := b1tb.Put(ctx, "fooKey", "fooValue"); err != nil {
		t.Fatalf("Put() failed: %v", err)
	}
	if err := b1.Commit(ctx); err != nil {
		t.Fatalf("b1.Commit() failed: %v", err)
	}
	checkOpsFail(b1)

	// Create a transaction with a conflict, check that the commit fails, then
	// check that subsequent ops fail.
	if b1, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb = b1.Table("tb")

	// Conflicts with future b1tb.Get().
	if err := tb.Put(ctx, "fooKey", "v2"); err != nil {
		t.Fatalf("tb.Put() failed: %v", err)
	}

	var got string
	if err := b1tb.Get(ctx, "fooKey", &got); err != nil {
		t.Fatalf("Get() failed: %v", err)
	}
	want := "fooValue"
	if got != want {
		t.Fatalf("Get() returned unexpected value: got %q, want %q", got, want)
	}
	if err := b1.Commit(ctx); err == nil {
		t.Fatalf("b1.Commit() should have failed: %v", err)
	}
	checkOpsFail(b1)

	// Create a transaction and immediately abort it, then check that subsequent
	// ops fail.
	if b1, err = d.BeginBatch(ctx, wire.BatchOptions{}); err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}
	b1tb = b1.Table("tb")
	b1.Abort(ctx)
	checkOpsFail(b1)
}

// Tests that batch methods called on non-batch return ErrNotBoundToBatch and
// that non-batch methods called on batch return ErrBoundToBatch.
func TestDisallowedMethods(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := tu.CreateNoSQLDatabase(t, ctx, a, "d")
	b, err := d.BeginBatch(ctx, wire.BatchOptions{})
	if err != nil {
		t.Fatalf("d.BeginBatch() failed: %v", err)
	}

	// Batch methods on non-batch.
	dc := wire.DatabaseClient(d.FullName())
	if err := dc.Commit(ctx); verror.ErrorID(err) != wire.ErrNotBoundToBatch.ID {
		t.Fatalf("dc.Commit() should have failed: %v", err)
	}
	if err := dc.Abort(ctx); verror.ErrorID(err) != wire.ErrNotBoundToBatch.ID {
		t.Fatalf("dc.Abort() should have failed: %v", err)
	}

	// Non-batch methods on batch.
	bc := wire.DatabaseClient(b.FullName())
	// For bc.Create(), we check that err is not nil instead of checking for
	// ErrBoundToBatch specifically since in practice bc.Create() will return
	// either ErrExist or "invalid name" depending on whether the database and
	// batch exist.
	if err := bc.Create(ctx, nil); err == nil {
		t.Fatalf("bc.Create() should have failed: %v", err)
	}
	if err := bc.Delete(ctx); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.Delete() should have failed: %v", err)
	}
	if _, err := bc.BeginBatch(ctx, wire.BatchOptions{}); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.BeginBatch() should have failed: %v", err)
	}
	if _, _, err := bc.GetPermissions(ctx); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.GetPermissions() should have failed: %v", err)
	}
	if err := bc.SetPermissions(ctx, nil, ""); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.SetPermissions() should have failed: %v", err)
	}
	// TODO(sadovsky): Test all other SyncGroupManager methods.
	if _, err := bc.GetSyncGroupNames(ctx); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("bc.GetSyncGroupNames() should have failed: %v", err)
	}

	// Test that Table.{Create,Delete} fail with ErrBoundToBatch.
	tc := wire.TableClient(naming.Join(b.FullName(), "tb"))
	if err := tc.Create(ctx, nil); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("tc.Create() should have failed: %v", err)
	}
	if err := tc.Delete(ctx); verror.ErrorID(err) != wire.ErrBoundToBatch.ID {
		t.Fatalf("tc.Delete() should have failed: %v", err)
	}
}

// Tests that d.BeginBatch() fails gracefully if the database does not exist.
func TestBeginBatchWithNonexistentDatabase(t *testing.T) {
	ctx, sName, cleanup := tu.SetupOrDie(nil)
	defer cleanup()
	a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a")
	d := a.NoSQLDatabase("d")
	if _, err := d.BeginBatch(ctx, wire.BatchOptions{}); verror.ErrorID(err) != verror.ErrNoExist.ID {
		t.Fatalf("d.BeginBatch() should have failed: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"C"
	"os"
	"bytes"
	"github.com/s3git/s3git-go"
)

//export s3git_init_repository
func s3git_init_repository(path *C.char) int {

	_, err := s3git.InitRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	return 0
}

//export s3git_open_repository
func s3git_open_repository(path *C.char) int {

	_, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	return 0
}

//export s3git_clone
func s3git_clone(url, path *C.char) int {

	_, err := s3git.Clone(C.GoString(url), C.GoString(path))
	if err != nil {
		return -1
	}

	return 0
}

//export s3git_add
func s3git_add(path, filename *C.char) *C.char {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return C.CString("")
	}

	file, err := os.Open(C.GoString(filename))
	if err != nil {
		return C.CString("")
	}

	key, _, err := repo.Add(file)
	if err != nil {
		return C.CString("")
	}

	return C.CString(key)
}

//export s3git_commit
func s3git_commit(path, message *C.char) int {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	repo.Commit(C.GoString(message))

	return 0
}

//export s3git_get
func s3git_get(path, hash *C.char) *C.char {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return C.CString("")
	}

	r, err := repo.Get(C.GoString(hash))
	if err != nil {
		return C.CString("")
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(r)
	s := buf.String()

	return C.CString(s)
}

//export s3git_push
func s3git_push(path *C.char) int {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	repo.Push(false, func(total int64) {})

	return 0
}

////export s3git_list
//func s3git_list(path, hash *C.char) *C.char {
//
//	repo, err := s3git.OpenRepository(C.GoString(path))
//	if err != nil {
//		return C.CString("")
//	}
//
//	list, _ := repo.List(C.GoString(hash))
//
//	for l := range list {
//
//	}
//
//	repo.Push(false, func(total int64) {})
//
//	return 0
//}

func main() {}
<commit_msg>Add license<commit_after>/*
 * Copyright 2016 Frank Wessels <fwessels@xs4all.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"C"
	"os"
	"bytes"
	"github.com/s3git/s3git-go"
)

//export s3git_init_repository
func s3git_init_repository(path *C.char) int {

	_, err := s3git.InitRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	return 0
}

//export s3git_open_repository
func s3git_open_repository(path *C.char) int {

	_, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	return 0
}

//export s3git_clone
func s3git_clone(url, path *C.char) int {

	_, err := s3git.Clone(C.GoString(url), C.GoString(path))
	if err != nil {
		return -1
	}

	return 0
}

//export s3git_add
func s3git_add(path, filename *C.char) *C.char {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return C.CString("")
	}

	file, err := os.Open(C.GoString(filename))
	if err != nil {
		return C.CString("")
	}

	key, _, err := repo.Add(file)
	if err != nil {
		return C.CString("")
	}

	return C.CString(key)
}

//export s3git_commit
func s3git_commit(path, message *C.char) int {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	repo.Commit(C.GoString(message))

	return 0
}

//export s3git_get
func s3git_get(path, hash *C.char) *C.char {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return C.CString("")
	}

	r, err := repo.Get(C.GoString(hash))
	if err != nil {
		return C.CString("")
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(r)
	s := buf.String()

	return C.CString(s)
}

//export s3git_push
func s3git_push(path *C.char) int {

	repo, err := s3git.OpenRepository(C.GoString(path))
	if err != nil {
		return -1
	}

	repo.Push(false, func(total int64) {})

	return 0
}

////export s3git_list
//func s3git_list(path, hash *C.char) *C.char {
//
//	repo, err := s3git.OpenRepository(C.GoString(path))
//	if err != nil {
//		return C.CString("")
//	}
//
//	list, _ := repo.List(C.GoString(hash))
//
//	for l := range list {
//
//	}
//
//	repo.Push(false, func(total int64) {})
//
//	return 0
//}

func main() {}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/http"
	"strings"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"

	"github.com/julienschmidt/httprouter"
)

// Error is a type that is encoded as JSON and returned in an API response in
// the event of an error. Only the Message field is required. More fields may
// be added to this struct in the future for better error reporting.
type Error struct {
	// Message describes the error in English. Typically it is set to
	// `err.Error()`. This field is required.
	Message string `json:"message"`

	// TODO: add a Param field with the (omitempty option in the json tag)
	// to indicate that the error was caused by an invalid, missing, or
	// incorrect parameter. This is not trivial as the API does not
	// currently do parameter validation itself. For example, the
	// /gateway/connect endpoint relies on the gateway.Connect method to
	// validate the netaddress. However, this prevents the API from knowing
	// whether an error returned by gateway.Connect is because of a
	// connection error or an invalid netaddress parameter. Validating
	// parameters in the API is not sufficient, as a parameter's value may
	// be valid or invalid depending on the current state of a module.
}

// Error implements the error interface for the Error type. It returns only the
// Message field.
func (err Error) Error() string {
	return err.Message
}

// HttpGET is a utility function for making http get requests to sia with a
// whitelisted user-agent. A non-2xx response does not return an error.
func HttpGET(url string) (resp *http.Response, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	return http.DefaultClient.Do(req)
}

// HttpGETAuthenticated is a utility function for making authenticated http get
// requests to sia with a whitelisted user-agent and the supplied password. A
// non-2xx response does not return an error.
func HttpGETAuthenticated(url string, password string) (resp *http.Response, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	req.SetBasicAuth("", password)
	return http.DefaultClient.Do(req)
}

// HttpPOST is a utility function for making post requests to sia with a
// whitelisted user-agent. A non-2xx response does not return an error.
func HttpPOST(url string, data string) (resp *http.Response, err error) {
	req, err := http.NewRequest("POST", url, strings.NewReader(data))
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return http.DefaultClient.Do(req)
}

// HttpPOSTAuthenticated is a utility function for making authenticated http
// post requests to sia with a whitelisted user-agent and the supplied
// password. A non-2xx response does not return an error.
func HttpPOSTAuthenticated(url string, data string, password string) (resp *http.Response, err error) {
	req, err := http.NewRequest("POST", url, strings.NewReader(data))
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("", password)
	return http.DefaultClient.Do(req)
}

// RequireUserAgent is middleware that requires all requests to set a
// UserAgent that contains the specified string.
func RequireUserAgent(h http.Handler, ua string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if !strings.Contains(req.UserAgent(), ua) {
			WriteError(w, Error{"Browser access disabled due to security vulnerability. Use Sia-UI or siac."}, http.StatusBadRequest)
			return
		}
		h.ServeHTTP(w, req)
	})
}

// RequirePassword is middleware that requires a request to authenticate with a
// password using HTTP basic auth. Usernames are ignored. Empty passwords
// indicate no authentication is required.
func RequirePassword(h httprouter.Handle, password string) httprouter.Handle {
	// An empty password is equivalent to no password.
	if password == "" {
		return h
	}
	return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
		_, pass, ok := req.BasicAuth()
		if !ok || pass != password {
			w.Header().Set("WWW-Authenticate", "Basic realm=\"SiaAPI\"")
			WriteError(w, Error{"API authentication failed."}, http.StatusUnauthorized)
			return
		}
		h(w, req, ps)
	}
}

// API encapsulates a collection of modules and implements a http.Handler
// to access their methods.
type API struct {
	cs       modules.ConsensusSet
	explorer modules.Explorer
	gateway  modules.Gateway
	host     modules.Host
	miner    modules.Miner
	renter   modules.Renter
	tpool    modules.TransactionPool
	wallet   modules.Wallet

	router http.Handler
}

// api.ServeHTTP implements the http.Handler interface.
func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	api.router.ServeHTTP(w, r)
}

// New creates a new Sia API from the provided modules.  The API will require
// authentication using HTTP basic auth for certain endpoints of the supplied
// password is not the empty string.  Usernames are ignored for authentication.
func New(requiredUserAgent string, requiredPassword string, cs modules.ConsensusSet, e modules.Explorer, g modules.Gateway, h modules.Host, m modules.Miner, r modules.Renter, tp modules.TransactionPool, w modules.Wallet) *API {
	api := &API{
		cs:       cs,
		explorer: e,
		gateway:  g,
		host:     h,
		miner:    m,
		renter:   r,
		tpool:    tp,
		wallet:   w,
	}

	// Register API handlers
	router := httprouter.New()
	router.NotFound = http.HandlerFunc(UnrecognizedCallHandler)
	router.RedirectTrailingSlash = false

	// Consensus API Calls
	if api.cs != nil {
		router.GET("/consensus", api.consensusHandler)
		router.POST("/consensus/validate/transactionset", api.consensusValidateTransactionsetHandler)
	}

	// Explorer API Calls
	if api.explorer != nil {
		router.GET("/explorer", api.explorerHandler)
		router.GET("/explorer/blocks/:height", api.explorerBlocksHandler)
		router.GET("/explorer/hashes/:hash", api.explorerHashHandler)
	}

	// Gateway API Calls
	if api.gateway != nil {
		router.GET("/gateway", api.gatewayHandler)
		router.POST("/gateway/connect/:netaddress", RequirePassword(api.gatewayConnectHandler, requiredPassword))
		router.POST("/gateway/disconnect/:netaddress", RequirePassword(api.gatewayDisconnectHandler, requiredPassword))
	}

	// Host API Calls
	if api.host != nil {
		// Calls directly pertaining to the host.
		router.GET("/host", api.hostHandlerGET)                                                   // Get the host status.
		router.POST("/host", RequirePassword(api.hostHandlerPOST, requiredPassword))              // Change the settings of the host.
		router.POST("/host/announce", RequirePassword(api.hostAnnounceHandler, requiredPassword)) // Announce the host to the network.
		router.GET("/host/estimatescore", api.hostEstimateScoreGET)

		// Calls pertaining to the storage manager that the host uses.
		router.GET("/host/storage", api.storageHandler)
		router.POST("/host/storage/folders/add", RequirePassword(api.storageFoldersAddHandler, requiredPassword))
		router.POST("/host/storage/folders/remove", RequirePassword(api.storageFoldersRemoveHandler, requiredPassword))
		router.POST("/host/storage/folders/resize", RequirePassword(api.storageFoldersResizeHandler, requiredPassword))
		router.POST("/host/storage/sectors/delete/:merkleroot", RequirePassword(api.storageSectorsDeleteHandler, requiredPassword))
	}

	// Miner API Calls
	if api.miner != nil {
		router.GET("/miner", api.minerHandler)
		router.GET("/miner/header", RequirePassword(api.minerHeaderHandlerGET, requiredPassword))
		router.POST("/miner/header", RequirePassword(api.minerHeaderHandlerPOST, requiredPassword))
		router.GET("/miner/start", RequirePassword(api.minerStartHandler, requiredPassword))
		router.GET("/miner/stop", RequirePassword(api.minerStopHandler, requiredPassword))
	}

	// Renter API Calls
	if api.renter != nil {
		router.GET("/renter", api.renterHandlerGET)
		router.POST("/renter", RequirePassword(api.renterHandlerPOST, requiredPassword))
		router.GET("/renter/contracts", api.renterContractsHandler)
		router.GET("/renter/downloads", api.renterDownloadsHandler)
		router.GET("/renter/files", api.renterFilesHandler)
		router.GET("/renter/prices", api.renterPricesHandler)

		// TODO: re-enable these routes once the new .sia format has been
		// standardized and implemented.
		// router.POST("/renter/load", RequirePassword(api.renterLoadHandler, requiredPassword))
		// router.POST("/renter/loadascii", RequirePassword(api.renterLoadAsciiHandler, requiredPassword))
		// router.GET("/renter/share", RequirePassword(api.renterShareHandler, requiredPassword))
		// router.GET("/renter/shareascii", RequirePassword(api.renterShareAsciiHandler, requiredPassword))

		router.POST("/renter/delete/*siapath", RequirePassword(api.renterDeleteHandler, requiredPassword))
		router.GET("/renter/download/*siapath", RequirePassword(api.renterDownloadHandler, requiredPassword))
		router.GET("/renter/downloadasync/*siapath", RequirePassword(api.renterDownloadAsyncHandler, requiredPassword))
		router.POST("/renter/rename/*siapath", RequirePassword(api.renterRenameHandler, requiredPassword))
		router.POST("/renter/upload/*siapath", RequirePassword(api.renterUploadHandler, requiredPassword))

		// HostDB endpoints.
		router.GET("/hostdb/active", api.hostdbActiveHandler)
		router.GET("/hostdb/all", api.hostdbAllHandler)
		router.GET("/hostdb/hosts/:pubkey", api.hostdbHostsHandler)
	}

	// Transaction pool API Calls
	if api.tpool != nil {
		router.GET("/tpool/fee", api.tpoolFeeHandlerGET)
		router.GET("/tpool/raw/:id", api.tpoolRawHandlerGET)
		router.POST("/tpool/raw", api.tpoolRawHandlerPOST)

		// TODO: re-enable this route once the transaction pool API has been finalized
		//router.GET("/transactionpool/transactions", api.transactionpoolTransactionsHandler)
	}

	// Wallet API Calls
	if api.wallet != nil {
		router.GET("/wallet", api.walletHandler)
		router.POST("/wallet/033x", RequirePassword(api.wallet033xHandler, requiredPassword))
		router.GET("/wallet/address", RequirePassword(api.walletAddressHandler, requiredPassword))
		router.GET("/wallet/addresses", api.walletAddressesHandler)
		router.GET("/wallet/backup", RequirePassword(api.walletBackupHandler, requiredPassword))
		router.POST("/wallet/init", RequirePassword(api.walletInitHandler, requiredPassword))
		router.POST("/wallet/init/seed", RequirePassword(api.walletInitSeedHandler, requiredPassword))
		router.POST("/wallet/lock", RequirePassword(api.walletLockHandler, requiredPassword))
		router.POST("/wallet/seed", RequirePassword(api.walletSeedHandler, requiredPassword))
		router.GET("/wallet/seeds", RequirePassword(api.walletSeedsHandler, requiredPassword))
		router.POST("/wallet/siacoins", RequirePassword(api.walletSiacoinsHandler, requiredPassword))
		router.POST("/wallet/siafunds", RequirePassword(api.walletSiafundsHandler, requiredPassword))
		router.POST("/wallet/siagkey", RequirePassword(api.walletSiagkeyHandler, requiredPassword))
		router.POST("/wallet/sweep/seed", RequirePassword(api.walletSweepSeedHandler, requiredPassword))
		router.GET("/wallet/transaction/:id", api.walletTransactionHandler)
		router.GET("/wallet/transactions", api.walletTransactionsHandler)
		router.GET("/wallet/transactions/:addr", api.walletTransactionsAddrHandler)
		router.GET("/wallet/verify/address/:addr", api.walletVerifyAddressHandler)
		router.POST("/wallet/unlock", RequirePassword(api.walletUnlockHandler, requiredPassword))
		router.POST("/wallet/changepassword", RequirePassword(api.walletChangePasswordHandler, requiredPassword))
	}

	// Apply UserAgent middleware and return the API
	api.router = RequireUserAgent(router, requiredUserAgent)
	return api
}

// UnrecognizedCallHandler handles calls to unknown pages (404).
func UnrecognizedCallHandler(w http.ResponseWriter, req *http.Request) {
	WriteError(w, Error{"404 - Refer to API.md"}, http.StatusNotFound)
}

// WriteError an error to the API caller.
func WriteError(w http.ResponseWriter, err Error, code int) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(code)
	encodingErr := json.NewEncoder(w).Encode(err)
	if _, isJsonErr := encodingErr.(*json.SyntaxError); isJsonErr {
		// Marshalling should only fail in the event of a developer error.
		// Specifically, only non-marshallable types should cause an error here.
		build.Critical("failed to encode API error response:", encodingErr)
	}
}

// WriteJSON writes the object to the ResponseWriter. If the encoding fails, an
// error is written instead. The Content-Type of the response header is set
// accordingly.
func WriteJSON(w http.ResponseWriter, obj interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	err := json.NewEncoder(w).Encode(obj)
	if _, isJsonErr := err.(*json.SyntaxError); isJsonErr {
		// Marshalling should only fail in the event of a developer error.
		// Specifically, only non-marshallable types should cause an error here.
		build.Critical("failed to encode API response:", err)
	}
}

// WriteSuccess writes the HTTP header with status 204 No Content to the
// ResponseWriter. WriteSuccess should only be used to indicate that the
// requested action succeeded AND there is no data to return.
func WriteSuccess(w http.ResponseWriter) {
	w.WriteHeader(http.StatusNoContent)
}
<commit_msg>add cleanCloseHandler: return from http handler immediately if remote closes<commit_after>package api

import (
	"encoding/json"
	"net/http"
	"strings"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"

	"github.com/julienschmidt/httprouter"
)

// Error is a type that is encoded as JSON and returned in an API response in
// the event of an error. Only the Message field is required. More fields may
// be added to this struct in the future for better error reporting.
type Error struct {
	// Message describes the error in English. Typically it is set to
	// `err.Error()`. This field is required.
	Message string `json:"message"`

	// TODO: add a Param field with the (omitempty option in the json tag)
	// to indicate that the error was caused by an invalid, missing, or
	// incorrect parameter. This is not trivial as the API does not
	// currently do parameter validation itself. For example, the
	// /gateway/connect endpoint relies on the gateway.Connect method to
	// validate the netaddress. However, this prevents the API from knowing
	// whether an error returned by gateway.Connect is because of a
	// connection error or an invalid netaddress parameter. Validating
	// parameters in the API is not sufficient, as a parameter's value may
	// be valid or invalid depending on the current state of a module.
}

// Error implements the error interface for the Error type. It returns only the
// Message field.
func (err Error) Error() string {
	return err.Message
}

// HttpGET is a utility function for making http get requests to sia with a
// whitelisted user-agent. A non-2xx response does not return an error.
func HttpGET(url string) (resp *http.Response, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	return http.DefaultClient.Do(req)
}

// HttpGETAuthenticated is a utility function for making authenticated http get
// requests to sia with a whitelisted user-agent and the supplied password. A
// non-2xx response does not return an error.
func HttpGETAuthenticated(url string, password string) (resp *http.Response, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	req.SetBasicAuth("", password)
	return http.DefaultClient.Do(req)
}

// HttpPOST is a utility function for making post requests to sia with a
// whitelisted user-agent. A non-2xx response does not return an error.
func HttpPOST(url string, data string) (resp *http.Response, err error) {
	req, err := http.NewRequest("POST", url, strings.NewReader(data))
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return http.DefaultClient.Do(req)
}

// HttpPOSTAuthenticated is a utility function for making authenticated http
// post requests to sia with a whitelisted user-agent and the supplied
// password. A non-2xx response does not return an error.
func HttpPOSTAuthenticated(url string, data string, password string) (resp *http.Response, err error) {
	req, err := http.NewRequest("POST", url, strings.NewReader(data))
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "Sia-Agent")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("", password)
	return http.DefaultClient.Do(req)
}

// RequireUserAgent is middleware that requires all requests to set a
// UserAgent that contains the specified string.
func RequireUserAgent(h http.Handler, ua string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if !strings.Contains(req.UserAgent(), ua) {
			WriteError(w, Error{"Browser access disabled due to security vulnerability. Use Sia-UI or siac."}, http.StatusBadRequest)
			return
		}
		h.ServeHTTP(w, req)
	})
}

// RequirePassword is middleware that requires a request to authenticate with a
// password using HTTP basic auth. Usernames are ignored. Empty passwords
// indicate no authentication is required.
func RequirePassword(h httprouter.Handle, password string) httprouter.Handle {
	// An empty password is equivalent to no password.
	if password == "" {
		return h
	}
	return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
		_, pass, ok := req.BasicAuth()
		if !ok || pass != password {
			w.Header().Set("WWW-Authenticate", "Basic realm=\"SiaAPI\"")
			WriteError(w, Error{"API authentication failed."}, http.StatusUnauthorized)
			return
		}
		h(w, req, ps)
	}
}

// cleanCloseHandler wraps the entire API, ensuring that underlying conns are
// not leaked if the rmeote end closes the connection before the underlying
// handler finishes.
func cleanCloseHandler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		done := make(chan struct{})
		go func(w http.ResponseWriter, r *http.Request) {
			defer close(done)
			next.ServeHTTP(w, r)
		}(w, r)
		select {
		case <-done:
		case <-r.Context().Done():
		}
	})
}

// API encapsulates a collection of modules and implements a http.Handler
// to access their methods.
type API struct {
	cs       modules.ConsensusSet
	explorer modules.Explorer
	gateway  modules.Gateway
	host     modules.Host
	miner    modules.Miner
	renter   modules.Renter
	tpool    modules.TransactionPool
	wallet   modules.Wallet

	router http.Handler
}

// api.ServeHTTP implements the http.Handler interface.
func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	api.router.ServeHTTP(w, r)
}

// New creates a new Sia API from the provided modules.  The API will require
// authentication using HTTP basic auth for certain endpoints of the supplied
// password is not the empty string.  Usernames are ignored for authentication.
func New(requiredUserAgent string, requiredPassword string, cs modules.ConsensusSet, e modules.Explorer, g modules.Gateway, h modules.Host, m modules.Miner, r modules.Renter, tp modules.TransactionPool, w modules.Wallet) *API {
	api := &API{
		cs:       cs,
		explorer: e,
		gateway:  g,
		host:     h,
		miner:    m,
		renter:   r,
		tpool:    tp,
		wallet:   w,
	}

	// Register API handlers
	router := httprouter.New()
	router.NotFound = http.HandlerFunc(UnrecognizedCallHandler)
	router.RedirectTrailingSlash = false

	// Consensus API Calls
	if api.cs != nil {
		router.GET("/consensus", api.consensusHandler)
		router.POST("/consensus/validate/transactionset", api.consensusValidateTransactionsetHandler)
	}

	// Explorer API Calls
	if api.explorer != nil {
		router.GET("/explorer", api.explorerHandler)
		router.GET("/explorer/blocks/:height", api.explorerBlocksHandler)
		router.GET("/explorer/hashes/:hash", api.explorerHashHandler)
	}

	// Gateway API Calls
	if api.gateway != nil {
		router.GET("/gateway", api.gatewayHandler)
		router.POST("/gateway/connect/:netaddress", RequirePassword(api.gatewayConnectHandler, requiredPassword))
		router.POST("/gateway/disconnect/:netaddress", RequirePassword(api.gatewayDisconnectHandler, requiredPassword))
	}

	// Host API Calls
	if api.host != nil {
		// Calls directly pertaining to the host.
		router.GET("/host", api.hostHandlerGET)                                                   // Get the host status.
		router.POST("/host", RequirePassword(api.hostHandlerPOST, requiredPassword))              // Change the settings of the host.
		router.POST("/host/announce", RequirePassword(api.hostAnnounceHandler, requiredPassword)) // Announce the host to the network.
		router.GET("/host/estimatescore", api.hostEstimateScoreGET)

		// Calls pertaining to the storage manager that the host uses.
		router.GET("/host/storage", api.storageHandler)
		router.POST("/host/storage/folders/add", RequirePassword(api.storageFoldersAddHandler, requiredPassword))
		router.POST("/host/storage/folders/remove", RequirePassword(api.storageFoldersRemoveHandler, requiredPassword))
		router.POST("/host/storage/folders/resize", RequirePassword(api.storageFoldersResizeHandler, requiredPassword))
		router.POST("/host/storage/sectors/delete/:merkleroot", RequirePassword(api.storageSectorsDeleteHandler, requiredPassword))
	}

	// Miner API Calls
	if api.miner != nil {
		router.GET("/miner", api.minerHandler)
		router.GET("/miner/header", RequirePassword(api.minerHeaderHandlerGET, requiredPassword))
		router.POST("/miner/header", RequirePassword(api.minerHeaderHandlerPOST, requiredPassword))
		router.GET("/miner/start", RequirePassword(api.minerStartHandler, requiredPassword))
		router.GET("/miner/stop", RequirePassword(api.minerStopHandler, requiredPassword))
	}

	// Renter API Calls
	if api.renter != nil {
		router.GET("/renter", api.renterHandlerGET)
		router.POST("/renter", RequirePassword(api.renterHandlerPOST, requiredPassword))
		router.GET("/renter/contracts", api.renterContractsHandler)
		router.GET("/renter/downloads", api.renterDownloadsHandler)
		router.GET("/renter/files", api.renterFilesHandler)
		router.GET("/renter/prices", api.renterPricesHandler)

		// TODO: re-enable these routes once the new .sia format has been
		// standardized and implemented.
		// router.POST("/renter/load", RequirePassword(api.renterLoadHandler, requiredPassword))
		// router.POST("/renter/loadascii", RequirePassword(api.renterLoadAsciiHandler, requiredPassword))
		// router.GET("/renter/share", RequirePassword(api.renterShareHandler, requiredPassword))
		// router.GET("/renter/shareascii", RequirePassword(api.renterShareAsciiHandler, requiredPassword))

		router.POST("/renter/delete/*siapath", RequirePassword(api.renterDeleteHandler, requiredPassword))
		router.GET("/renter/download/*siapath", RequirePassword(api.renterDownloadHandler, requiredPassword))
		router.GET("/renter/downloadasync/*siapath", RequirePassword(api.renterDownloadAsyncHandler, requiredPassword))
		router.POST("/renter/rename/*siapath", RequirePassword(api.renterRenameHandler, requiredPassword))
		router.POST("/renter/upload/*siapath", RequirePassword(api.renterUploadHandler, requiredPassword))

		// HostDB endpoints.
		router.GET("/hostdb/active", api.hostdbActiveHandler)
		router.GET("/hostdb/all", api.hostdbAllHandler)
		router.GET("/hostdb/hosts/:pubkey", api.hostdbHostsHandler)
	}

	// Transaction pool API Calls
	if api.tpool != nil {
		router.GET("/tpool/fee", api.tpoolFeeHandlerGET)
		router.GET("/tpool/raw/:id", api.tpoolRawHandlerGET)
		router.POST("/tpool/raw", api.tpoolRawHandlerPOST)

		// TODO: re-enable this route once the transaction pool API has been finalized
		//router.GET("/transactionpool/transactions", api.transactionpoolTransactionsHandler)
	}

	// Wallet API Calls
	if api.wallet != nil {
		router.GET("/wallet", api.walletHandler)
		router.POST("/wallet/033x", RequirePassword(api.wallet033xHandler, requiredPassword))
		router.GET("/wallet/address", RequirePassword(api.walletAddressHandler, requiredPassword))
		router.GET("/wallet/addresses", api.walletAddressesHandler)
		router.GET("/wallet/backup", RequirePassword(api.walletBackupHandler, requiredPassword))
		router.POST("/wallet/init", RequirePassword(api.walletInitHandler, requiredPassword))
		router.POST("/wallet/init/seed", RequirePassword(api.walletInitSeedHandler, requiredPassword))
		router.POST("/wallet/lock", RequirePassword(api.walletLockHandler, requiredPassword))
		router.POST("/wallet/seed", RequirePassword(api.walletSeedHandler, requiredPassword))
		router.GET("/wallet/seeds", RequirePassword(api.walletSeedsHandler, requiredPassword))
		router.POST("/wallet/siacoins", RequirePassword(api.walletSiacoinsHandler, requiredPassword))
		router.POST("/wallet/siafunds", RequirePassword(api.walletSiafundsHandler, requiredPassword))
		router.POST("/wallet/siagkey", RequirePassword(api.walletSiagkeyHandler, requiredPassword))
		router.POST("/wallet/sweep/seed", RequirePassword(api.walletSweepSeedHandler, requiredPassword))
		router.GET("/wallet/transaction/:id", api.walletTransactionHandler)
		router.GET("/wallet/transactions", api.walletTransactionsHandler)
		router.GET("/wallet/transactions/:addr", api.walletTransactionsAddrHandler)
		router.GET("/wallet/verify/address/:addr", api.walletVerifyAddressHandler)
		router.POST("/wallet/unlock", RequirePassword(api.walletUnlockHandler, requiredPassword))
		router.POST("/wallet/changepassword", RequirePassword(api.walletChangePasswordHandler, requiredPassword))
	}

	// Apply UserAgent middleware and return the API
	api.router = cleanCloseHandler(RequireUserAgent(router, requiredUserAgent))
	return api
}

// UnrecognizedCallHandler handles calls to unknown pages (404).
func UnrecognizedCallHandler(w http.ResponseWriter, req *http.Request) {
	WriteError(w, Error{"404 - Refer to API.md"}, http.StatusNotFound)
}

// WriteError an error to the API caller.
func WriteError(w http.ResponseWriter, err Error, code int) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(code)
	encodingErr := json.NewEncoder(w).Encode(err)
	if _, isJsonErr := encodingErr.(*json.SyntaxError); isJsonErr {
		// Marshalling should only fail in the event of a developer error.
		// Specifically, only non-marshallable types should cause an error here.
		build.Critical("failed to encode API error response:", encodingErr)
	}
}

// WriteJSON writes the object to the ResponseWriter. If the encoding fails, an
// error is written instead. The Content-Type of the response header is set
// accordingly.
func WriteJSON(w http.ResponseWriter, obj interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	err := json.NewEncoder(w).Encode(obj)
	if _, isJsonErr := err.(*json.SyntaxError); isJsonErr {
		// Marshalling should only fail in the event of a developer error.
		// Specifically, only non-marshallable types should cause an error here.
		build.Critical("failed to encode API response:", err)
	}
}

// WriteSuccess writes the HTTP header with status 204 No Content to the
// ResponseWriter. WriteSuccess should only be used to indicate that the
// requested action succeeded AND there is no data to return.
func WriteSuccess(w http.ResponseWriter) {
	w.WriteHeader(http.StatusNoContent)
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/stretchr/graceful"
)

const (
	apiTimeout = 5 * time.Second
)

// handleHTTPRequest is a wrapper function that handles all incoming calls to
// the API.
func handleHTTPRequest(mux *http.ServeMux, url string, handler http.HandlerFunc) {
	mux.HandleFunc(url, func(w http.ResponseWriter, req *http.Request) {
		handler(w, req)
	})
}

// initAPI determines which functions handle each API call.
func (srv *Server) initAPI(addr string) {
	mux := http.NewServeMux()

	// 404 Calls
	handleHTTPRequest(mux, "/", srv.unrecognizedCallHandler)

	// Consensus API Calls
	handleHTTPRequest(mux, "/consensus/status", srv.consensusStatusHandler)
	handleHTTPRequest(mux, "/consensus/synchronize", srv.consensusSynchronizeHandler)

	// Daemon API Calls
	handleHTTPRequest(mux, "/daemon/stop", srv.daemonStopHandler)
	handleHTTPRequest(mux, "/daemon/updates/apply", srv.daemonUpdatesApplyHandler)
	handleHTTPRequest(mux, "/daemon/updates/check", srv.daemonUpdatesCheckHandler)
	handleHTTPRequest(mux, "/daemon/update/apply", srv.daemonUpdatesApplyHandler) // DEPRECATED
	handleHTTPRequest(mux, "/daemon/update/check", srv.daemonUpdatesCheckHandler) // DEPRECATED

	// Debugging API Calls
	handleHTTPRequest(mux, "/debug/constants", srv.debugConstantsHandler)
	handleHTTPRequest(mux, "/debug/mutextest", srv.mutexTestHandler)

	// Gateway API Calls
	handleHTTPRequest(mux, "/gateway/status", srv.gatewayStatusHandler)
	handleHTTPRequest(mux, "/gateway/peers/add", srv.gatewayPeersAddHandler)
	handleHTTPRequest(mux, "/gateway/peers/remove", srv.gatewayPeersRemoveHandler)
	handleHTTPRequest(mux, "/gateway/peer/add", srv.gatewayPeersAddHandler)         // DEPRECATED
	handleHTTPRequest(mux, "/gateway/peer/remove", srv.gatewayPeersRemoveHandler)   // DEPRECATED
	handleHTTPRequest(mux, "/gateway/synchronize", srv.consensusSynchronizeHandler) // DEPRECATED

	// Host API Calls
	handleHTTPRequest(mux, "/host/announce", srv.hostAnnounceHandler)
	handleHTTPRequest(mux, "/host/configure", srv.hostConfigureHandler)
	handleHTTPRequest(mux, "/host/status", srv.hostStatusHandler)
	handleHTTPRequest(mux, "/host/config", srv.hostConfigureHandler) // DEPRECATED

	// HostDB API Calls
	handleHTTPRequest(mux, "/hostdb/hosts/active", srv.hostdbHostsActiveHandler)
	handleHTTPRequest(mux, "/hostdb/hosts/all", srv.hostdbHostsAllHandler)
	handleHTTPRequest(mux, "/hostdb/host/active", srv.hostdbHostsActiveHandler) // DEPRECATED
	handleHTTPRequest(mux, "/hostdb/host/all", srv.hostdbHostsAllHandler)       // DEPRECATED

	// Miner API Calls
	handleHTTPRequest(mux, "/miner/start", srv.minerStartHandler)
	handleHTTPRequest(mux, "/miner/status", srv.minerStatusHandler)
	handleHTTPRequest(mux, "/miner/stop", srv.minerStopHandler)

	// Renter API Calls
	handleHTTPRequest(mux, "/renter/downloadqueue", srv.renterDownloadqueueHandler)
	handleHTTPRequest(mux, "/renter/files/delete", srv.renterFilesDeleteHandler)
	handleHTTPRequest(mux, "/renter/files/download", srv.renterFilesDownloadHandler)
	handleHTTPRequest(mux, "/renter/files/list", srv.renterFilesListHandler)
	handleHTTPRequest(mux, "/renter/files/load", srv.renterFilesLoadHandler)
	handleHTTPRequest(mux, "/renter/files/loadascii", srv.renterFilesLoadAsciiHandler)
	handleHTTPRequest(mux, "/renter/files/rename", srv.renterFilesRenameHandler)
	handleHTTPRequest(mux, "/renter/files/share", srv.renterFilesShareHandler)
	handleHTTPRequest(mux, "/renter/files/shareascii", srv.renterFilesShareAsciiHandler)
	handleHTTPRequest(mux, "/renter/files/upload", srv.renterFilesUploadHandler)
	handleHTTPRequest(mux, "/renter/files", srv.renterFilesListHandler)        // DEPRECATED
	handleHTTPRequest(mux, "/renter/status", srv.renterStatusHandler)          // DEPRECATED
	handleHTTPRequest(mux, "/renter/download", srv.renterFilesDownloadHandler) // DEPRECATED
	handleHTTPRequest(mux, "/renter/upload", srv.renterFilesUploadHandler)     // DEPRECATED

	// TransactionPool API Calls
	handleHTTPRequest(mux, "/transactionpool/transactions", srv.transactionpoolTransactionsHandler)

	// Wallet API Calls
	handleHTTPRequest(mux, "/wallet/address", srv.walletAddressHandler)
	handleHTTPRequest(mux, "/wallet/send", srv.walletSendHandler)
	handleHTTPRequest(mux, "/wallet/status", srv.walletStatusHandler)

	// create graceful HTTP server
	srv.apiServer = &graceful.Server{
		Timeout: apiTimeout,
		Server:  &http.Server{Addr: addr, Handler: mux},
	}
}

// unrecognizedCallHandler handles calls to unknown pages (404).
func (srv *Server) unrecognizedCallHandler(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "404 - Refer to API.md")
}

// Serve listens for and handles API calls. It a blocking function.
func (srv *Server) Serve() error {
	// graceful will run until it catches a signal.
	// It can also be stopped manually by stopHandler.
	err := srv.apiServer.ListenAndServe()
	// despite its name, graceful still propogates this benign error
	if err != nil && strings.HasSuffix(err.Error(), "use of closed network connection") {
		err = nil
	}
	fmt.Println("\rCaught stop signal, quitting.")
	return err
}

// writeError an error to the API caller.
func writeError(w http.ResponseWriter, msg string, err int) {
	http.Error(w, msg, err)
}

// writeJSON writes the object to the ResponseWriter. If the encoding fails, an
// error is written instead.
func writeJSON(w http.ResponseWriter, obj interface{}) {
	if json.NewEncoder(w).Encode(obj) != nil {
		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
	}
}

// writeSuccess writes the success json object ({"Success":true}) to the
// ResponseWriter
func writeSuccess(w http.ResponseWriter) {
	writeJSON(w, struct{ Success bool }{true})
}
<commit_msg>remove deprecated API calls<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/stretchr/graceful"
)

const (
	apiTimeout = 5 * time.Second
)

// handleHTTPRequest is a wrapper function that handles all incoming calls to
// the API.
func handleHTTPRequest(mux *http.ServeMux, url string, handler http.HandlerFunc) {
	mux.HandleFunc(url, func(w http.ResponseWriter, req *http.Request) {
		handler(w, req)
	})
}

// initAPI determines which functions handle each API call.
func (srv *Server) initAPI(addr string) {
	mux := http.NewServeMux()

	// 404 Calls
	handleHTTPRequest(mux, "/", srv.unrecognizedCallHandler)

	// Consensus API Calls
	handleHTTPRequest(mux, "/consensus/status", srv.consensusStatusHandler)
	handleHTTPRequest(mux, "/consensus/synchronize", srv.consensusSynchronizeHandler)

	// Daemon API Calls
	handleHTTPRequest(mux, "/daemon/stop", srv.daemonStopHandler)
	handleHTTPRequest(mux, "/daemon/updates/apply", srv.daemonUpdatesApplyHandler)
	handleHTTPRequest(mux, "/daemon/updates/check", srv.daemonUpdatesCheckHandler)

	// Debugging API Calls
	handleHTTPRequest(mux, "/debug/constants", srv.debugConstantsHandler)
	handleHTTPRequest(mux, "/debug/mutextest", srv.mutexTestHandler)

	// Gateway API Calls
	handleHTTPRequest(mux, "/gateway/status", srv.gatewayStatusHandler)
	handleHTTPRequest(mux, "/gateway/peers/add", srv.gatewayPeersAddHandler)
	handleHTTPRequest(mux, "/gateway/peers/remove", srv.gatewayPeersRemoveHandler)

	// Host API Calls
	handleHTTPRequest(mux, "/host/announce", srv.hostAnnounceHandler)
	handleHTTPRequest(mux, "/host/configure", srv.hostConfigureHandler)
	handleHTTPRequest(mux, "/host/status", srv.hostStatusHandler)

	// HostDB API Calls
	handleHTTPRequest(mux, "/hostdb/hosts/active", srv.hostdbHostsActiveHandler)
	handleHTTPRequest(mux, "/hostdb/hosts/all", srv.hostdbHostsAllHandler)

	// Miner API Calls
	handleHTTPRequest(mux, "/miner/start", srv.minerStartHandler)
	handleHTTPRequest(mux, "/miner/status", srv.minerStatusHandler)
	handleHTTPRequest(mux, "/miner/stop", srv.minerStopHandler)

	// Renter API Calls
	handleHTTPRequest(mux, "/renter/downloadqueue", srv.renterDownloadqueueHandler)
	handleHTTPRequest(mux, "/renter/files/delete", srv.renterFilesDeleteHandler)
	handleHTTPRequest(mux, "/renter/files/download", srv.renterFilesDownloadHandler)
	handleHTTPRequest(mux, "/renter/files/list", srv.renterFilesListHandler)
	handleHTTPRequest(mux, "/renter/files/load", srv.renterFilesLoadHandler)
	handleHTTPRequest(mux, "/renter/files/loadascii", srv.renterFilesLoadAsciiHandler)
	handleHTTPRequest(mux, "/renter/files/rename", srv.renterFilesRenameHandler)
	handleHTTPRequest(mux, "/renter/files/share", srv.renterFilesShareHandler)
	handleHTTPRequest(mux, "/renter/files/shareascii", srv.renterFilesShareAsciiHandler)
	handleHTTPRequest(mux, "/renter/files/upload", srv.renterFilesUploadHandler)

	// TransactionPool API Calls
	handleHTTPRequest(mux, "/transactionpool/transactions", srv.transactionpoolTransactionsHandler)

	// Wallet API Calls
	handleHTTPRequest(mux, "/wallet/address", srv.walletAddressHandler)
	handleHTTPRequest(mux, "/wallet/send", srv.walletSendHandler)
	handleHTTPRequest(mux, "/wallet/status", srv.walletStatusHandler)

	// create graceful HTTP server
	srv.apiServer = &graceful.Server{
		Timeout: apiTimeout,
		Server:  &http.Server{Addr: addr, Handler: mux},
	}
}

// unrecognizedCallHandler handles calls to unknown pages (404).
func (srv *Server) unrecognizedCallHandler(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "404 - Refer to API.md")
}

// Serve listens for and handles API calls. It a blocking function.
func (srv *Server) Serve() error {
	// graceful will run until it catches a signal.
	// It can also be stopped manually by stopHandler.
	err := srv.apiServer.ListenAndServe()
	// despite its name, graceful still propogates this benign error
	if err != nil && strings.HasSuffix(err.Error(), "use of closed network connection") {
		err = nil
	}
	fmt.Println("\rCaught stop signal, quitting.")
	return err
}

// writeError an error to the API caller.
func writeError(w http.ResponseWriter, msg string, err int) {
	http.Error(w, msg, err)
}

// writeJSON writes the object to the ResponseWriter. If the encoding fails, an
// error is written instead.
func writeJSON(w http.ResponseWriter, obj interface{}) {
	if json.NewEncoder(w).Encode(obj) != nil {
		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
	}
}

// writeSuccess writes the success json object ({"Success":true}) to the
// ResponseWriter
func writeSuccess(w http.ResponseWriter) {
	writeJSON(w, struct{ Success bool }{true})
}
<|endoftext|>


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

package readerdriver

import (
	"io"
	"runtime"
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten/v2/audio/internal/oboe"
)

func IsAvailable() bool {
	return true
}

type context struct {
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
}

func NewContext(sampleRate int, channelNum int, bitDepthInBytes int) (Context, chan struct{}, error) {
	ready := make(chan struct{})
	close(ready)

	c := &context{
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
	}
	return c, ready, nil
}

func (c *context) NewPlayer(src io.Reader) Player {
	p := &player{
		context: c,
		src:     src,
		cond:    sync.NewCond(&sync.Mutex{}),
		volume:  1,
	}
	runtime.SetFinalizer(p, (*player).Close)
	return p
}

func (c *context) Suspend() error {
	return oboe.Suspend()
}

func (c *context) Resume() error {
	return oboe.Resume()
}

type player struct {
	context *context
	p       *oboe.Player
	src     io.Reader
	err     error
	cond    *sync.Cond
	closed  bool
	volume  float64
	eof     bool
}

func (p *player) Pause() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.err != nil {
		return
	}
	if p.closed {
		return
	}
	if p.p == nil {
		return
	}
	if err := p.p.Pause(); err != nil {
		p.setErrorImpl(err)
		return
	}
	p.cond.Signal()
}

func (p *player) Play() {
	// Call Play asynchronously since Oboe's Play might take long.
	ch := make(chan struct{})
	go func() {
		p.cond.L.Lock()
		defer p.cond.L.Unlock()
		close(ch)
		p.playImpl()
	}()

	// Wait until the mutex is locked in the above goroutine.
	<-ch
}

func (p *player) playImpl() {
	if p.err != nil {
		return
	}
	if p.p != nil && p.p.IsPlaying() {
		return
	}
	defer p.cond.Signal()
	var runLoop bool
	if p.p == nil {
		p.p = oboe.NewPlayer(p.context.sampleRate, p.context.channelNum, p.context.bitDepthInBytes, p.volume, func() {
			p.cond.Signal()
		})
		runLoop = true
	}

	buf := make([]byte, p.context.maxBufferSize())
	for p.p.UnplayedBufferSize() < p.context.maxBufferSize() {
		n, err := p.src.Read(buf)
		if err != nil && err != io.EOF {
			p.setErrorImpl(err)
			return
		}
		p.p.AppendBuffer(buf[:n])
		if err == io.EOF {
			break
		}
	}

	if err := p.p.Play(); err != nil {
		p.setErrorImpl(err)
		return
	}
	if runLoop {
		go p.loop()
	}
}

func (p *player) IsPlaying() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	if p.p == nil {
		return false
	}
	return p.p.IsPlaying()
}

func (p *player) Reset() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.resetImpl()
}

func (p *player) resetImpl() {
	if p.err != nil {
		return
	}
	if p.closed {
		return
	}
	if p.p == nil {
		return
	}
	if err := p.p.Close(); err != nil {
		p.setErrorImpl(err)
		return
	}
	p.p = nil
	p.eof = false
	p.cond.Signal()
}

func (p *player) Volume() float64 {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.volume
}

func (p *player) SetVolume(volume float64) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.volume = volume
	if p.p == nil {
		return
	}
	p.p.SetVolume(volume)
}

func (p *player) UnplayedBufferSize() int {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.unplayedBufferSizeImpl()
}

func (p *player) unplayedBufferSizeImpl() int {
	if p.p == nil {
		return 0
	}
	return p.p.UnplayedBufferSize()
}

func (p *player) Err() error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.err
}

func (p *player) Close() error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.closeImpl()
}

func (p *player) closeImpl() error {
	defer p.cond.Signal()

	runtime.SetFinalizer(p, nil)
	p.closed = true
	if p.p == nil {
		return p.err
	}
	if err := p.p.Close(); err != nil && p.err == nil {
		// Do not call setErrorImpl, or this can cause infinite recursive.
		p.err = err
		return p.err
	}
	p.p = nil
	return p.err
}

func (p *player) setError(err error) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.setErrorImpl(err)
}

func (p *player) setErrorImpl(err error) {
	p.err = err
	p.closeImpl()
}

func (p *player) shouldWait() bool {
	if p.closed {
		return false
	}
	if p.p == nil {
		return false
	}

	// Wait when the player is paused.
	if !p.p.IsPlaying() {
		return true
	}

	// When the source reaches EOF, wait until all the data is consumed.
	if p.eof {
		return p.p.UnplayedBufferSize() > 0
	}

	return p.p.UnplayedBufferSize() >= p.context.maxBufferSize()
}

func (p *player) wait() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	for p.shouldWait() {
		p.cond.Wait()
	}
	return p.p != nil && p.p.IsPlaying()
}

func (p *player) write(buf []byte) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.closed {
		return
	}
	if p.p == nil {
		return
	}
	p.p.AppendBuffer(buf)
}

func (p *player) loop() {
	buf := make([]byte, 4096)
	for {
		if !p.wait() {
			return
		}

		n, err := p.src.Read(buf)
		if err != nil && err != io.EOF {
			p.setError(err)
			return
		}
		p.write(buf[:n])

		p.cond.L.Lock()
		if err == io.EOF {
			p.eof = true
		}

		// Now p.resetImpl() doesn't close the stream gracefully. Then buffer size check is necessary here.
		if p.eof && p.unplayedBufferSizeImpl() == 0 {
			// Even when the unplayed buffer size is 0,
			// the audio data in the hardware might not be played yet (#1632).
			// Just wait for a while.
			time.Sleep(100 * time.Millisecond)
			p.resetImpl()
			p.cond.L.Unlock()
			return
		}
		p.cond.L.Unlock()
	}
}
<commit_msg>audio/internal/readerdriver: Mark EOF at the first reading<commit_after>// Copyright 2021 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package readerdriver

import (
	"io"
	"runtime"
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten/v2/audio/internal/oboe"
)

func IsAvailable() bool {
	return true
}

type context struct {
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
}

func NewContext(sampleRate int, channelNum int, bitDepthInBytes int) (Context, chan struct{}, error) {
	ready := make(chan struct{})
	close(ready)

	c := &context{
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
	}
	return c, ready, nil
}

func (c *context) NewPlayer(src io.Reader) Player {
	p := &player{
		context: c,
		src:     src,
		cond:    sync.NewCond(&sync.Mutex{}),
		volume:  1,
	}
	runtime.SetFinalizer(p, (*player).Close)
	return p
}

func (c *context) Suspend() error {
	return oboe.Suspend()
}

func (c *context) Resume() error {
	return oboe.Resume()
}

type player struct {
	context *context
	p       *oboe.Player
	src     io.Reader
	err     error
	cond    *sync.Cond
	closed  bool
	volume  float64
	eof     bool
}

func (p *player) Pause() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.err != nil {
		return
	}
	if p.closed {
		return
	}
	if p.p == nil {
		return
	}
	if err := p.p.Pause(); err != nil {
		p.setErrorImpl(err)
		return
	}
	p.cond.Signal()
}

func (p *player) Play() {
	// Call Play asynchronously since Oboe's Play might take long.
	ch := make(chan struct{})
	go func() {
		p.cond.L.Lock()
		defer p.cond.L.Unlock()
		close(ch)
		p.playImpl()
	}()

	// Wait until the mutex is locked in the above goroutine.
	<-ch
}

func (p *player) playImpl() {
	if p.err != nil {
		return
	}
	if p.p != nil && p.p.IsPlaying() {
		return
	}
	defer p.cond.Signal()
	var runLoop bool
	if p.p == nil {
		p.p = oboe.NewPlayer(p.context.sampleRate, p.context.channelNum, p.context.bitDepthInBytes, p.volume, func() {
			p.cond.Signal()
		})
		runLoop = true
	}

	buf := make([]byte, p.context.maxBufferSize())
	for p.p.UnplayedBufferSize() < p.context.maxBufferSize() {
		n, err := p.src.Read(buf)
		if err != nil && err != io.EOF {
			p.setErrorImpl(err)
			return
		}
		p.p.AppendBuffer(buf[:n])
		if err == io.EOF {
			p.eof = true
			break
		}
	}

	if err := p.p.Play(); err != nil {
		p.setErrorImpl(err)
		return
	}
	if runLoop {
		go p.loop()
	}
}

func (p *player) IsPlaying() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	if p.p == nil {
		return false
	}
	return p.p.IsPlaying()
}

func (p *player) Reset() {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.resetImpl()
}

func (p *player) resetImpl() {
	if p.err != nil {
		return
	}
	if p.closed {
		return
	}
	if p.p == nil {
		return
	}
	if err := p.p.Close(); err != nil {
		p.setErrorImpl(err)
		return
	}
	p.p = nil
	p.eof = false
	p.cond.Signal()
}

func (p *player) Volume() float64 {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.volume
}

func (p *player) SetVolume(volume float64) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.volume = volume
	if p.p == nil {
		return
	}
	p.p.SetVolume(volume)
}

func (p *player) UnplayedBufferSize() int {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.unplayedBufferSizeImpl()
}

func (p *player) unplayedBufferSizeImpl() int {
	if p.p == nil {
		return 0
	}
	return p.p.UnplayedBufferSize()
}

func (p *player) Err() error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.err
}

func (p *player) Close() error {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	return p.closeImpl()
}

func (p *player) closeImpl() error {
	defer p.cond.Signal()

	runtime.SetFinalizer(p, nil)
	p.closed = true
	if p.p == nil {
		return p.err
	}
	if err := p.p.Close(); err != nil && p.err == nil {
		// Do not call setErrorImpl, or this can cause infinite recursive.
		p.err = err
		return p.err
	}
	p.p = nil
	return p.err
}

func (p *player) setError(err error) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()
	p.setErrorImpl(err)
}

func (p *player) setErrorImpl(err error) {
	p.err = err
	p.closeImpl()
}

func (p *player) shouldWait() bool {
	if p.closed {
		return false
	}
	if p.p == nil {
		return false
	}

	// Wait when the player is paused.
	if !p.p.IsPlaying() {
		return true
	}

	// When the source reaches EOF, wait until all the data is consumed.
	if p.eof {
		return p.p.UnplayedBufferSize() > 0
	}

	return p.p.UnplayedBufferSize() >= p.context.maxBufferSize()
}

func (p *player) wait() bool {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	for p.shouldWait() {
		p.cond.Wait()
	}
	return p.p != nil && p.p.IsPlaying()
}

func (p *player) write(buf []byte) {
	p.cond.L.Lock()
	defer p.cond.L.Unlock()

	if p.closed {
		return
	}
	if p.p == nil {
		return
	}
	p.p.AppendBuffer(buf)
}

func (p *player) loop() {
	buf := make([]byte, 4096)
	for {
		if !p.wait() {
			return
		}

		n, err := p.src.Read(buf)
		if err != nil && err != io.EOF {
			p.setError(err)
			return
		}
		p.write(buf[:n])

		p.cond.L.Lock()
		if err == io.EOF {
			p.eof = true
		}

		// Now p.resetImpl() doesn't close the stream gracefully. Then buffer size check is necessary here.
		if p.eof && p.unplayedBufferSizeImpl() == 0 {
			// Even when the unplayed buffer size is 0,
			// the audio data in the hardware might not be played yet (#1632).
			// Just wait for a while.
			time.Sleep(100 * time.Millisecond)
			p.resetImpl()
			p.cond.L.Unlock()
			return
		}
		p.cond.L.Unlock()
	}
}
<|endoftext|>