<commit_before>package main

import (
	_ "embed"
	"fmt"
	"github.com/gorilla/mux"
	. "github.com/mickael-kerjean/filestash/server/common"
	. "github.com/mickael-kerjean/filestash/server/ctrl"
	. "github.com/mickael-kerjean/filestash/server/middleware"
	_ "github.com/mickael-kerjean/filestash/server/plugin"
	"net/http"
	"net/http/pprof"
	"os"
	"runtime"
	"runtime/debug"
	"strconv"
)

//go:embed plugin/index.go
var EmbedPluginList []byte

func main() {
	app := App{}
	Init(&app)
}

func Init(a *App) {
	var middlewares []Middleware
	r := mux.NewRouter()

	// API for Session
	session := r.PathPrefix("/api/session").Subrouter()
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart}
	session.HandleFunc("", NewMiddlewareChain(SessionGet, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser}
	session.HandleFunc("", NewMiddlewareChain(SessionAuthenticate, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax}
	session.HandleFunc("", NewMiddlewareChain(SessionLogout, middlewares, *a)).Methods("DELETE")
	middlewares = []Middleware{ApiHeaders, SecureHeaders}
	session.HandleFunc("/auth/{service}", NewMiddlewareChain(SessionOAuthBackend, middlewares, *a)).Methods("GET")
	session.HandleFunc("/auth/", NewMiddlewareChain(SessionAuthMiddleware, middlewares, *a)).Methods("GET", "POST")

	// API for admin
	middlewares = []Middleware{ApiHeaders, SecureAjax}
	admin := r.PathPrefix("/admin/api").Subrouter()
	admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionGet, middlewares, *a)).Methods("GET")
	admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionAuthenticate, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{ApiHeaders, AdminOnly, SecureAjax}
	admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigHandler, middlewares, *a)).Methods("GET")
	admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigUpdateHandler, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{IndexHeaders, AdminOnly}
	admin.HandleFunc("/logs", NewMiddlewareChain(FetchLogHandler, middlewares, *a)).Methods("GET")

	// API for File management
	files := r.PathPrefix("/api/files").Subrouter()
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly}
	files.HandleFunc("/cat", NewMiddlewareChain(FileCat, middlewares, *a)).Methods("GET", "HEAD")
	files.HandleFunc("/zip", NewMiddlewareChain(FileDownloader, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly}
	files.HandleFunc("/cat", NewMiddlewareChain(FileAccess, middlewares, *a)).Methods("OPTIONS")
	files.HandleFunc("/cat", NewMiddlewareChain(FileSave, middlewares, *a)).Methods("POST")
	files.HandleFunc("/ls", NewMiddlewareChain(FileLs, middlewares, *a)).Methods("GET")
	files.HandleFunc("/mv", NewMiddlewareChain(FileMv, middlewares, *a)).Methods("GET")
	files.HandleFunc("/rm", NewMiddlewareChain(FileRm, middlewares, *a)).Methods("GET")
	files.HandleFunc("/mkdir", NewMiddlewareChain(FileMkdir, middlewares, *a)).Methods("GET")
	files.HandleFunc("/touch", NewMiddlewareChain(FileTouch, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SessionStart, LoggedInOnly}
	files.HandleFunc("/search", NewMiddlewareChain(FileSearch, middlewares, *a)).Methods("GET")

	// API for exporter
	middlewares = []Middleware{ApiHeaders, SecureHeaders, RedirectSharedLoginIfNeeded, SessionStart, LoggedInOnly}
	r.PathPrefix("/api/export/{share}/{mtype0}/{mtype1}").Handler(NewMiddlewareChain(FileExport, middlewares, *a))

	// API for Shared link
	share := r.PathPrefix("/api/share").Subrouter()
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly}
	share.HandleFunc("", NewMiddlewareChain(ShareList, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser}
	share.HandleFunc("/{share}/proof", NewMiddlewareChain(ShareVerifyProof, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, CanManageShare}
	share.HandleFunc("/{share}", NewMiddlewareChain(ShareDelete, middlewares, *a)).Methods("DELETE")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser, CanManageShare}
	share.HandleFunc("/{share}", NewMiddlewareChain(ShareUpsert, middlewares, *a)).Methods("POST")

	// Webdav server / Shared Link
	middlewares = []Middleware{IndexHeaders, SecureHeaders}
	r.HandleFunc("/s/{share}", NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, *a)).Methods("GET")
	middlewares = []Middleware{WebdavBlacklist, SessionStart}
	r.PathPrefix("/s/{share}").Handler(NewMiddlewareChain(WebdavHandler, middlewares, *a))

	// Application Resources
	middlewares = []Middleware{ApiHeaders}
	r.HandleFunc("/api/config", NewMiddlewareChain(PublicConfigHandler, middlewares, *a)).Methods("GET")
	r.HandleFunc("/api/backend", NewMiddlewareChain(AdminBackend, middlewares, *a)).Methods("GET")
	r.HandleFunc("/api/middlewares/authentication", NewMiddlewareChain(AdminAuthenticationMiddleware, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{StaticHeaders}
	r.PathPrefix("/assets").Handler(http.HandlerFunc(NewMiddlewareChain(StaticHandler(FILE_ASSETS), middlewares, *a))).Methods("GET")
	r.HandleFunc("/favicon.ico", NewMiddlewareChain(StaticHandler(FILE_ASSETS+"/assets/logo/"), middlewares, *a)).Methods("GET")
	r.HandleFunc("/sw_cache.js", NewMiddlewareChain(StaticHandler(FILE_ASSETS+"/assets/worker/"), middlewares, *a)).Methods("GET")

	// Other endpoints
	middlewares = []Middleware{ApiHeaders}
	r.HandleFunc("/report", NewMiddlewareChain(ReportHandler, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{IndexHeaders}
	r.HandleFunc("/about", NewMiddlewareChain(AboutHandler, middlewares, *a)).Methods("GET")
	r.HandleFunc("/robots.txt", func(res http.ResponseWriter, req *http.Request) {
		res.Write([]byte(""))
	})
	r.HandleFunc("/.well-known/security.txt", NewMiddlewareChain(WellKnownSecurityHandler, []Middleware{}, *a)).Methods("GET")
	r.HandleFunc("/healthz", NewMiddlewareChain(HealthHandler, []Middleware{}, *a)).Methods("GET")
	r.HandleFunc("/custom.css", NewMiddlewareChain(CustomCssHandler, []Middleware{}, *a)).Methods("GET")

	if os.Getenv("DEBUG") == "true" {
		initDebugRoutes(r)
	}
	initPluginsRoutes(r, a)

	r.PathPrefix("/admin").Handler(http.HandlerFunc(NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, *a))).Methods("GET")
	r.PathPrefix("/").Handler(http.HandlerFunc(NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, *a))).Methods("GET", "POST")

	// Routes are served via plugins to avoid getting stuck with plain HTTP. The idea is to
	// support many more protocols in the future: HTTPS, HTTP2, TOR or whatever that sounds
	// fancy I don't know much when this got written: IPFS, solid, ...
	Log.Info("Filestash %s starting", APP_VERSION)
	for _, obj := range Hooks.Get.Starter() {
		go obj(r)
	}
	if len(Hooks.Get.Starter()) == 0 {
		Log.Warning("No starter plugin available")
		return
	}

	go func() {
		InitPluginList(EmbedPluginList)
	}()
	select {}
}

func initDebugRoutes(r *mux.Router) {
	r.HandleFunc("/debug/pprof/", pprof.Index)
	r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	r.HandleFunc("/debug/pprof/profile", pprof.Profile)
	r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	r.HandleFunc("/debug/pprof/trace", pprof.Trace)
	r.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
	r.Handle("/debug/pprof/heap", pprof.Handler("heap"))
	r.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
	r.Handle("/debug/pprof/block", pprof.Handler("block"))
	r.Handle("/debug/pprof/allocs", pprof.Handler("allocs"))
	r.Handle("/debug/pprof/mutex", pprof.Handler("mutex"))
	r.HandleFunc("/debug/free", func(w http.ResponseWriter, r *http.Request) {
		debug.FreeOSMemory()
		w.Write([]byte("DONE"))
	})
	bToMb := func(b uint64) string {
		return strconv.Itoa(int(b / 1024 / 1024))
	}
	r.HandleFunc("/debug/memory", func(w http.ResponseWriter, r *http.Request) {
		var m runtime.MemStats
		runtime.ReadMemStats(&m)
		w.Write([]byte("
"))
		w.Write([]byte("Alloc      = " + bToMb(m.Alloc) + "MiB
"))
		w.Write([]byte("TotalAlloc = " + bToMb(m.TotalAlloc) + "MiB
"))
		w.Write([]byte("Sys        = " + bToMb(m.Sys) + "MiB
"))
		w.Write([]byte("NumGC      = " + strconv.Itoa(int(m.NumGC))))
		w.Write([]byte("
"))
	})
}

func initPluginsRoutes(r *mux.Router, a *App) {
	// Endpoints hanle by plugins
	for _, obj := range Hooks.Get.HttpEndpoint() {
		obj(r, a)
	}
	// frontoffice overrides: it is the mean by which plugin can interact with the frontoffice
	for _, obj := range Hooks.Get.FrontendOverrides() {
		r.HandleFunc(obj, func(res http.ResponseWriter, req *http.Request) {
			res.Header().Set("Content-Type", GetMimeType(req.URL.String()))
			res.Write([]byte(fmt.Sprintf("/* Default '%s' */", obj)))
		})
	}
	// map which file can be open with what application
	r.HandleFunc("/overrides/xdg-open.js", func(res http.ResponseWriter, req *http.Request) {
		res.Header().Set("Content-Type", GetMimeType(req.URL.String()))
		res.Write([]byte(`window.overrides["xdg-open"] = function(mime){`))
		openers := Hooks.Get.XDGOpen()
		for i := 0; i < len(openers); i++ {
			res.Write([]byte(openers[i]))
		}
		res.Write([]byte(`return null;}`))
	})
}
<commit_msg>fix (starter): make it possible for starter plugin to complete<commit_after>package main

import (
	_ "embed"
	"fmt"
	"github.com/gorilla/mux"
	. "github.com/mickael-kerjean/filestash/server/common"
	. "github.com/mickael-kerjean/filestash/server/ctrl"
	. "github.com/mickael-kerjean/filestash/server/middleware"
	_ "github.com/mickael-kerjean/filestash/server/plugin"
	"net/http"
	"net/http/pprof"
	"os"
	"runtime"
	"runtime/debug"
	"strconv"
	"sync"
)

//go:embed plugin/index.go
var EmbedPluginList []byte

func main() {
	app := App{}
	Init(&app)
}

func Init(a *App) {
	var middlewares []Middleware
	r := mux.NewRouter()

	// API for Session
	session := r.PathPrefix("/api/session").Subrouter()
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart}
	session.HandleFunc("", NewMiddlewareChain(SessionGet, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser}
	session.HandleFunc("", NewMiddlewareChain(SessionAuthenticate, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax}
	session.HandleFunc("", NewMiddlewareChain(SessionLogout, middlewares, *a)).Methods("DELETE")
	middlewares = []Middleware{ApiHeaders, SecureHeaders}
	session.HandleFunc("/auth/{service}", NewMiddlewareChain(SessionOAuthBackend, middlewares, *a)).Methods("GET")
	session.HandleFunc("/auth/", NewMiddlewareChain(SessionAuthMiddleware, middlewares, *a)).Methods("GET", "POST")

	// API for admin
	middlewares = []Middleware{ApiHeaders, SecureAjax}
	admin := r.PathPrefix("/admin/api").Subrouter()
	admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionGet, middlewares, *a)).Methods("GET")
	admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionAuthenticate, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{ApiHeaders, AdminOnly, SecureAjax}
	admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigHandler, middlewares, *a)).Methods("GET")
	admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigUpdateHandler, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{IndexHeaders, AdminOnly}
	admin.HandleFunc("/logs", NewMiddlewareChain(FetchLogHandler, middlewares, *a)).Methods("GET")

	// API for File management
	files := r.PathPrefix("/api/files").Subrouter()
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly}
	files.HandleFunc("/cat", NewMiddlewareChain(FileCat, middlewares, *a)).Methods("GET", "HEAD")
	files.HandleFunc("/zip", NewMiddlewareChain(FileDownloader, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly}
	files.HandleFunc("/cat", NewMiddlewareChain(FileAccess, middlewares, *a)).Methods("OPTIONS")
	files.HandleFunc("/cat", NewMiddlewareChain(FileSave, middlewares, *a)).Methods("POST")
	files.HandleFunc("/ls", NewMiddlewareChain(FileLs, middlewares, *a)).Methods("GET")
	files.HandleFunc("/mv", NewMiddlewareChain(FileMv, middlewares, *a)).Methods("GET")
	files.HandleFunc("/rm", NewMiddlewareChain(FileRm, middlewares, *a)).Methods("GET")
	files.HandleFunc("/mkdir", NewMiddlewareChain(FileMkdir, middlewares, *a)).Methods("GET")
	files.HandleFunc("/touch", NewMiddlewareChain(FileTouch, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SessionStart, LoggedInOnly}
	files.HandleFunc("/search", NewMiddlewareChain(FileSearch, middlewares, *a)).Methods("GET")

	// API for exporter
	middlewares = []Middleware{ApiHeaders, SecureHeaders, RedirectSharedLoginIfNeeded, SessionStart, LoggedInOnly}
	r.PathPrefix("/api/export/{share}/{mtype0}/{mtype1}").Handler(NewMiddlewareChain(FileExport, middlewares, *a))

	// API for Shared link
	share := r.PathPrefix("/api/share").Subrouter()
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly}
	share.HandleFunc("", NewMiddlewareChain(ShareList, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser}
	share.HandleFunc("/{share}/proof", NewMiddlewareChain(ShareVerifyProof, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, CanManageShare}
	share.HandleFunc("/{share}", NewMiddlewareChain(ShareDelete, middlewares, *a)).Methods("DELETE")
	middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser, CanManageShare}
	share.HandleFunc("/{share}", NewMiddlewareChain(ShareUpsert, middlewares, *a)).Methods("POST")

	// Webdav server / Shared Link
	middlewares = []Middleware{IndexHeaders, SecureHeaders}
	r.HandleFunc("/s/{share}", NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, *a)).Methods("GET")
	middlewares = []Middleware{WebdavBlacklist, SessionStart}
	r.PathPrefix("/s/{share}").Handler(NewMiddlewareChain(WebdavHandler, middlewares, *a))

	// Application Resources
	middlewares = []Middleware{ApiHeaders}
	r.HandleFunc("/api/config", NewMiddlewareChain(PublicConfigHandler, middlewares, *a)).Methods("GET")
	r.HandleFunc("/api/backend", NewMiddlewareChain(AdminBackend, middlewares, *a)).Methods("GET")
	r.HandleFunc("/api/middlewares/authentication", NewMiddlewareChain(AdminAuthenticationMiddleware, middlewares, *a)).Methods("GET")
	middlewares = []Middleware{StaticHeaders}
	r.PathPrefix("/assets").Handler(http.HandlerFunc(NewMiddlewareChain(StaticHandler(FILE_ASSETS), middlewares, *a))).Methods("GET")
	r.HandleFunc("/favicon.ico", NewMiddlewareChain(StaticHandler(FILE_ASSETS+"/assets/logo/"), middlewares, *a)).Methods("GET")
	r.HandleFunc("/sw_cache.js", NewMiddlewareChain(StaticHandler(FILE_ASSETS+"/assets/worker/"), middlewares, *a)).Methods("GET")

	// Other endpoints
	middlewares = []Middleware{ApiHeaders}
	r.HandleFunc("/report", NewMiddlewareChain(ReportHandler, middlewares, *a)).Methods("POST")
	middlewares = []Middleware{IndexHeaders}
	r.HandleFunc("/about", NewMiddlewareChain(AboutHandler, middlewares, *a)).Methods("GET")
	r.HandleFunc("/robots.txt", func(res http.ResponseWriter, req *http.Request) {
		res.Write([]byte(""))
	})
	r.HandleFunc("/.well-known/security.txt", NewMiddlewareChain(WellKnownSecurityHandler, []Middleware{}, *a)).Methods("GET")
	r.HandleFunc("/healthz", NewMiddlewareChain(HealthHandler, []Middleware{}, *a)).Methods("GET")
	r.HandleFunc("/custom.css", NewMiddlewareChain(CustomCssHandler, []Middleware{}, *a)).Methods("GET")

	if os.Getenv("DEBUG") == "true" {
		initDebugRoutes(r)
	}
	initPluginsRoutes(r, a)

	r.PathPrefix("/admin").Handler(http.HandlerFunc(NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, *a))).Methods("GET")
	r.PathPrefix("/").Handler(http.HandlerFunc(NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, *a))).Methods("GET", "POST")

	// Routes are served via plugins to avoid getting stuck with plain HTTP. The idea is to
	// support many more protocols in the future: HTTPS, HTTP2, TOR or whatever that sounds
	// fancy I don't know much when this got written: IPFS, solid, ...
	Log.Info("Filestash %s starting", APP_VERSION)
	if len(Hooks.Get.Starter()) == 0 {
		Log.Warning("No starter plugin available")
		os.Exit(1)
		return
	}
	var wg sync.WaitGroup
	for _, obj := range Hooks.Get.Starter() {
		wg.Add(1)
		go func() {
			obj(r)
			wg.Done()
		}()
	}
	go func() {
		InitPluginList(EmbedPluginList)
	}()
	wg.Wait()
}

func initDebugRoutes(r *mux.Router) {
	r.HandleFunc("/debug/pprof/", pprof.Index)
	r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	r.HandleFunc("/debug/pprof/profile", pprof.Profile)
	r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	r.HandleFunc("/debug/pprof/trace", pprof.Trace)
	r.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
	r.Handle("/debug/pprof/heap", pprof.Handler("heap"))
	r.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
	r.Handle("/debug/pprof/block", pprof.Handler("block"))
	r.Handle("/debug/pprof/allocs", pprof.Handler("allocs"))
	r.Handle("/debug/pprof/mutex", pprof.Handler("mutex"))
	r.HandleFunc("/debug/free", func(w http.ResponseWriter, r *http.Request) {
		debug.FreeOSMemory()
		w.Write([]byte("DONE"))
	})
	bToMb := func(b uint64) string {
		return strconv.Itoa(int(b / 1024 / 1024))
	}
	r.HandleFunc("/debug/memory", func(w http.ResponseWriter, r *http.Request) {
		var m runtime.MemStats
		runtime.ReadMemStats(&m)
		w.Write([]byte("
"))
		w.Write([]byte("Alloc      = " + bToMb(m.Alloc) + "MiB
"))
		w.Write([]byte("TotalAlloc = " + bToMb(m.TotalAlloc) + "MiB
"))
		w.Write([]byte("Sys        = " + bToMb(m.Sys) + "MiB
"))
		w.Write([]byte("NumGC      = " + strconv.Itoa(int(m.NumGC))))
		w.Write([]byte("
"))
	})
}

func initPluginsRoutes(r *mux.Router, a *App) {
	// Endpoints hanle by plugins
	for _, obj := range Hooks.Get.HttpEndpoint() {
		obj(r, a)
	}
	// frontoffice overrides: it is the mean by which plugin can interact with the frontoffice
	for _, obj := range Hooks.Get.FrontendOverrides() {
		r.HandleFunc(obj, func(res http.ResponseWriter, req *http.Request) {
			res.Header().Set("Content-Type", GetMimeType(req.URL.String()))
			res.Write([]byte(fmt.Sprintf("/* Default '%s' */", obj)))
		})
	}
	// map which file can be open with what application
	r.HandleFunc("/overrides/xdg-open.js", func(res http.ResponseWriter, req *http.Request) {
		res.Header().Set("Content-Type", GetMimeType(req.URL.String()))
		res.Write([]byte(`window.overrides["xdg-open"] = function(mime){`))
		openers := Hooks.Get.XDGOpen()
		for i := 0; i < len(openers); i++ {
			res.Write([]byte(openers[i]))
		}
		res.Write([]byte(`return null;}`))
	})
}
<|endoftext|>


<commit_before>package imageserver

import (
	"testing"
)

func TestServerGetSourceErrorMissingSource(t *testing.T) {
	server := &Server{}
	parameters := make(Parameters)
	_, err := server.getSource(parameters)
	if err == nil {
		t.Fatal("No error")
	}
}
<commit_msg>test test test<commit_after>package imageserver

import (
	"errors"
	"testing"
)

type size struct {
	width  int
	height int
}

type providerSize struct{}

func (provider *providerSize) Get(source interface{}, parameters Parameters) (*Image, error) {
	size, ok := source.(size)
	if !ok {
		return nil, errors.New("Source is not a size")
	}
	return CreateImage(size.width, size.height), nil
}

func TestServerGetSource(t *testing.T) {
	server := &Server{
		Provider: new(providerSize),
	}
	_, err := server.getSource(Parameters{
		"source": size{
			width:  500,
			height: 400,
		},
	})
	if err != nil {
		t.Fatal(err)
	}
}

func TestServerGetSourceErrorMissingSource(t *testing.T) {
	server := &Server{}
	parameters := make(Parameters)
	_, err := server.getSource(parameters)
	if err == nil {
		t.Fatal("No error")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"testing"
	"time"
)

const (
	ANY_DB_DRIVER     = ""
	MULTI_DOMAIN_MODE = ""
	TEST_DEPLOYED_ON  = "localhost:9999"
	TEST_DB           = "goslow_test"
)

var DATA_SOURCE = map[string]string{
	ANY_DB_DRIVER: "", // value doesn't matter
	"sqlite3":     DEFAULT_CONFIG.dataSource,
	"postgres":    "postgres://localhost/" + TEST_DB,
}

type TestCase struct {
	driver                 string
	dataSource             string
	createDefaultEndpoints bool
	adminPathPrefix        string
}

type TestCases []*TestCase

type ServerTest func(*testing.T, *httptest.Server, *TestCase)

var (
	multiDomainTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
	}

	endpointCreationTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
		NewTestCase(ANY_DB_DRIVER, "/goslow"),
		NewTestCase(ANY_DB_DRIVER, "/goslow/"),
		NewTestCase(ANY_DB_DRIVER, "/te"),
		NewTestCase(ANY_DB_DRIVER, "/te/"),
		NewTestCase(ANY_DB_DRIVER, "/composite/path"),
	}
)

func NewTestCase(driver string, adminPathPrefix string) *TestCase {
	dataSource, knownDriver := DATA_SOURCE[driver]
	if !knownDriver {
		log.Fatalf("unknown driver: <%s>", driver)
	}
	createDefaultEndpoints := (adminPathPrefix == MULTI_DOMAIN_MODE)
	return &TestCase{
		driver:                 driver,
		dataSource:             dataSource,
		createDefaultEndpoints: createDefaultEndpoints,
		adminPathPrefix:        adminPathPrefix,
	}
}

func TestZeroSite(t *testing.T) {
	runAll(t, zeroSiteServerTest, multiDomainTestCases)
}

func runAll(t *testing.T, serverTest ServerTest, testCases TestCases) {
	for _, testCase := range runnable(expandToConcreteDriver(testCases)) {
		run(t, serverTest, testCase)
	}
}

func runnable(testCases TestCases) TestCases {
	runnableTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.runnable() {
			runnableTestCases = append(runnableTestCases, testCase)
		}
	}
	return runnableTestCases
}

func expandToConcreteDriver(testCases TestCases) TestCases {
	concreteTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.driver == ANY_DB_DRIVER {
			concreteTestCases = append(concreteTestCases, NewTestCase("sqlite3", testCase.adminPathPrefix))
			concreteTestCases = append(concreteTestCases, NewTestCase("postgres", testCase.adminPathPrefix))
		} else {
			concreteTestCases = append(concreteTestCases, testCase)
		}
	}
	return concreteTestCases
}

func (testCase *TestCase) runnable() bool {
	return !testCase.skippable()
}

func (testCase *TestCase) skippable() bool {
	return testCase.driver == "postgres" && testing.Short()
}

func run(t *testing.T, serverTest ServerTest, testCase *TestCase) {
	if testCase.driver == "postgres" {
		createDb(TEST_DB)
		defer dropDb(TEST_DB)
	}
	goSlowServer := newGoSlowServer(testCase)
	server := httptest.NewServer(goSlowServer)
	defer server.Close()
	defer goSlowServer.storage.db.Close() // so we can drop database
	serverTest(t, server, testCase)
}

func zeroSiteServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondWith(t, DEFAULT_RESPONSE,
		createGET(server.URL, "/", makeFullDomain("0")))
}

func shouldRespondWith(t *testing.T, expectedResponse []byte, request *http.Request) {
	response := do(request)
	bytesShouldBeEqual(t, expectedResponse, read(response))
}

func TestTooLargeDelay(t *testing.T) {
	runAll(t, checkTooLargeDelay, multiDomainTestCases)
}

func checkTooLargeDelay(t *testing.T, server *httptest.Server, testCase *TestCase) {
	prefix := testCase.adminPathPrefix
	domain := newDomain(server, join(prefix, "/booya"), []byte("haha"))
	site := getSite(domain)
	resp := createEndpoint(server, &Endpoint{Site: site, Delay: time.Duration(1000) * time.Second})
	shouldHaveStatusCode(t, http.StatusBadRequest, resp)
	resp = GET(server.URL, "/", domain)
	shouldHaveStatusCode(t, http.StatusNotFound, resp)
}

func TestRedefineBuiltinSites(t *testing.T) {
	runAll(t, checkRedefineBuiltinSites, multiDomainTestCases)
}

func checkRedefineBuiltinSites(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, site := range []string{"0", "99", "500", "create"} {
		resp := createEndpoint(server, &Endpoint{Site: site, Path: "/test", Response: []byte("hop"), Method: "GET"})
		shouldHaveStatusCode(t, http.StatusForbidden, resp)
	}
}

func TestRedefineNonExistentSite(t *testing.T) {
	runAll(t, checkRedefineNonExistentSite, multiDomainTestCases)
}

func checkRedefineNonExistentSite(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, site := range []string{"", "ha", "admin-500"} {
		resp := createEndpoint(server, &Endpoint{Site: site})
		shouldHaveStatusCode(t, http.StatusNotFound, resp)
	}
}

func TestDelay(t *testing.T) {
	runAll(t, checkDelay, multiDomainTestCases)
}

func checkDelay(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("0")),
		0, 0.1) // seconds
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("1")),
		1, 1.1) // seconds
}

func TestStatus(t *testing.T) {
	runAll(t, checkStatus, multiDomainTestCases)
}

func checkStatus(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, statusCode := range []int{200, 404, 500} {
		resp := GET(server.URL, "/", makeFullDomain(strconv.Itoa(statusCode)))
		shouldHaveStatusCode(t, statusCode, resp)
	}
}

func TestEndpointCreation(t *testing.T) {
	runAll(t, checkEndpointCreationTestCase, endpointCreationTestCases)
}

func checkEndpointCreationTestCase(t *testing.T, server *httptest.Server, testCase *TestCase) {
	prefix := testCase.adminPathPrefix
	isInSingleSiteMode := prefix != ""
	domain, site := TEST_DEPLOYED_ON, ""
	root_response := []byte("haha")
	test_response := []byte("hop")
	test_post_response := []byte("for POST")
	empty_payload := []byte("")

	if isInSingleSiteMode {
		resp := createEndpoint(server, &Endpoint{Path: join(prefix, "/"), Response: root_response})
		shouldHaveStatusCode(t, http.StatusOK, resp)
	} else {
		domain = newDomain(server, join(prefix, "/"), root_response)
		site = getSite(domain)
	}

	bytesShouldBeEqual(t, read(GET(server.URL, "/", domain)), root_response)

	// testing GET endpoint
	resp := createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_response, Method: "GET"})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that GET /test endpoint works
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that GET /test doesn't affect POST
	resp = POST(server.URL, "/test", domain, []byte(""))
	intShouldBeEqual(t, 404, resp.StatusCode)

	// testing POST endpoint
	resp = createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_post_response, Method: "POST",
		Delay: time.Duration(100) * time.Millisecond})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that POST endpoint doesn't affect GET
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that POST /test endpoint works
	bytesShouldBeEqual(t, read(POST(server.URL, "/test", domain, empty_payload)), test_post_response)
	shouldRespondIn(t, createPOST(server.URL, "/test", domain, empty_payload), 0.1, 0.15)
}

func newGoSlowServer(testCase *TestCase) *Server {
	config := DEFAULT_CONFIG // copies DEFAULT_CONFIG
	config.deployedOn = TEST_DEPLOYED_ON
	config.createDefaultEndpoints = testCase.createDefaultEndpoints
	config.adminPathPrefix = testCase.adminPathPrefix
	config.driver = testCase.driver
	config.dataSource = testCase.dataSource
	return NewServer(&config)
}

func GET(url, path, host string) *http.Response {
	req := createGET(url, path, host)
	return do(req)
}

func do(req *http.Request) *http.Response {
	resp, err := new(http.Client).Do(req)
	if err != nil {
		log.Fatal(err)
	}
	return resp
}

func createGET(url, path, host string) *http.Request {
	return createRequest("GET", url, path, host, nil)
}

func createRequest(method, url, path, host string, body io.Reader) *http.Request {
	req, err := http.NewRequest(method, url+path, body)
	if err != nil {
		log.Fatal(err)
	}
	req.Host = host
	return req
}

func read(resp *http.Response) []byte {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	return body
}

func makeFullDomain(site string) string {
	return fmt.Sprintf("%s.%s", site, TEST_DEPLOYED_ON)
}

func bytesShouldBeEqual(t *testing.T, expected, actual []byte) {
	if !bytes.Equal(expected, actual) {
		t.Fatalf("<<%v>> != <<%v>>", string(expected), string(actual))
	}
}

func intShouldBeEqual(t *testing.T, expected, actual int) {
	if expected != actual {
		t.Fatalf("<<%v>> != <<%v>>", expected, actual)
	}
}

func shouldRespondIn(t *testing.T, req *http.Request, minSeconds, maxSeconds float64) {
	start := time.Now()
	resp := do(req)
	read(resp)
	duration := time.Since(start)
	minDuration := toDuration(minSeconds)
	maxDuration := toDuration(maxSeconds)
	if duration < minDuration || duration > maxDuration {
		t.Fatalf("%s%s answered in %v. Not in the interval [%v; %v]",
			req.Host, req.URL.Path, duration, minDuration, maxDuration)
	}
}

func shouldHaveStatusCode(t *testing.T, statusCode int, resp *http.Response) {
	intShouldBeEqual(t, statusCode, resp.StatusCode)
}

func toDuration(seconds float64) time.Duration {
	return time.Duration(seconds*1000) * time.Millisecond
}

func newDomain(server *httptest.Server, path string, response []byte) string {
	resp := POST(server.URL, fmt.Sprintf("%s?output=short&method=GET", path),
		makeFullDomain("create"), response)
	return string(read(resp))
}

func POST(url, path, host string, payload []byte) *http.Response {
	req := createPOST(url, path, host, payload)
	return do(req)
}

func createPOST(url, path, host string, payload []byte) *http.Request {
	return createRequest("POST", url, path, host, bytes.NewReader(payload))
}

func getSite(domain string) string {
	return strings.Split(domain, ".")[0]
}

func createEndpoint(server *httptest.Server, endpoint *Endpoint) *http.Response {
	req := createPOST(server.URL, endpoint.Path, makeFullDomain("admin-"+endpoint.Site),
		endpoint.Response)
	req.URL.RawQuery = getQueryString(endpoint)
	return do(req)
}

func getQueryString(endpoint *Endpoint) string {
	params := url.Values{}
	params.Set("method", endpoint.Method)
	params.Set("delay", fmt.Sprintf("%f", endpoint.Delay.Seconds()))
	return params.Encode()
}

// Wrapper around path.Join. Preserves trailing slash.
func join(elem ...string) string {
	lastElem := elem[len(elem)-1]
	shouldEndWithSlash := strings.HasSuffix(lastElem, "/")
	joined := path.Join(elem...)
	if shouldEndWithSlash && !strings.HasSuffix(joined, "/") {
		joined += "/"
	}
	return joined
}

func createDb(name string) {
	cmd := exec.Command("createdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("createdb error: %s", err)
	}
}

func dropDb(name string) {
	cmd := exec.Command("dropdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("dropdb error: %s", err)
	}
}
<commit_msg>Rename newDomain to createDomain<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"testing"
	"time"
)

const (
	ANY_DB_DRIVER     = ""
	MULTI_DOMAIN_MODE = ""
	TEST_DEPLOYED_ON  = "localhost:9999"
	TEST_DB           = "goslow_test"
)

var DATA_SOURCE = map[string]string{
	ANY_DB_DRIVER: "", // value doesn't matter
	"sqlite3":     DEFAULT_CONFIG.dataSource,
	"postgres":    "postgres://localhost/" + TEST_DB,
}

type TestCase struct {
	driver                 string
	dataSource             string
	createDefaultEndpoints bool
	adminPathPrefix        string
}

type TestCases []*TestCase

type ServerTest func(*testing.T, *httptest.Server, *TestCase)

var (
	multiDomainTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
	}

	endpointCreationTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
		NewTestCase(ANY_DB_DRIVER, "/goslow"),
		NewTestCase(ANY_DB_DRIVER, "/goslow/"),
		NewTestCase(ANY_DB_DRIVER, "/te"),
		NewTestCase(ANY_DB_DRIVER, "/te/"),
		NewTestCase(ANY_DB_DRIVER, "/composite/path"),
	}
)

func NewTestCase(driver string, adminPathPrefix string) *TestCase {
	dataSource, knownDriver := DATA_SOURCE[driver]
	if !knownDriver {
		log.Fatalf("unknown driver: <%s>", driver)
	}
	createDefaultEndpoints := (adminPathPrefix == MULTI_DOMAIN_MODE)
	return &TestCase{
		driver:                 driver,
		dataSource:             dataSource,
		createDefaultEndpoints: createDefaultEndpoints,
		adminPathPrefix:        adminPathPrefix,
	}
}

func TestZeroSite(t *testing.T) {
	runAll(t, zeroSiteServerTest, multiDomainTestCases)
}

func runAll(t *testing.T, serverTest ServerTest, testCases TestCases) {
	for _, testCase := range runnable(expandToConcreteDriver(testCases)) {
		run(t, serverTest, testCase)
	}
}

func runnable(testCases TestCases) TestCases {
	runnableTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.runnable() {
			runnableTestCases = append(runnableTestCases, testCase)
		}
	}
	return runnableTestCases
}

func expandToConcreteDriver(testCases TestCases) TestCases {
	concreteTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.driver == ANY_DB_DRIVER {
			concreteTestCases = append(concreteTestCases, NewTestCase("sqlite3", testCase.adminPathPrefix))
			concreteTestCases = append(concreteTestCases, NewTestCase("postgres", testCase.adminPathPrefix))
		} else {
			concreteTestCases = append(concreteTestCases, testCase)
		}
	}
	return concreteTestCases
}

func (testCase *TestCase) runnable() bool {
	return !testCase.skippable()
}

func (testCase *TestCase) skippable() bool {
	return testCase.driver == "postgres" && testing.Short()
}

func run(t *testing.T, serverTest ServerTest, testCase *TestCase) {
	if testCase.driver == "postgres" {
		createDb(TEST_DB)
		defer dropDb(TEST_DB)
	}
	goSlowServer := newGoSlowServer(testCase)
	server := httptest.NewServer(goSlowServer)
	defer server.Close()
	defer goSlowServer.storage.db.Close() // so we can drop database
	serverTest(t, server, testCase)
}

func zeroSiteServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondWith(t, DEFAULT_RESPONSE,
		createGET(server.URL, "/", makeFullDomain("0")))
}

func shouldRespondWith(t *testing.T, expectedResponse []byte, request *http.Request) {
	response := do(request)
	bytesShouldBeEqual(t, expectedResponse, read(response))
}

func TestTooLargeDelay(t *testing.T) {
	runAll(t, tooLargeDelayServerTest, multiDomainTestCases)
}

func tooLargeDelayServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	domain := createDomain(server, &Endpoint{Path: testCase.endpointCreationPath("/booya"), Response: []byte("haha")})
	site := getSite(domain)
	resp := createEndpoint(server, &Endpoint{Site: site, Delay: time.Duration(1000) * time.Second})
	shouldHaveStatusCode(t, http.StatusBadRequest, resp)
	resp = GET(server.URL, "/", domain)
	shouldHaveStatusCode(t, http.StatusNotFound, resp)
}

func (testCase *TestCase) endpointCreationPath(desiredPath string) string {
	return join(testCase.adminPathPrefix, desiredPath)
}

func TestRedefineBuiltinSites(t *testing.T) {
	runAll(t, checkRedefineBuiltinSites, multiDomainTestCases)
}

func checkRedefineBuiltinSites(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, site := range []string{"0", "99", "500", "create"} {
		resp := createEndpoint(server, &Endpoint{Site: site, Path: "/test", Response: []byte("hop"), Method: "GET"})
		shouldHaveStatusCode(t, http.StatusForbidden, resp)
	}
}

func TestRedefineNonExistentSite(t *testing.T) {
	runAll(t, checkRedefineNonExistentSite, multiDomainTestCases)
}

func checkRedefineNonExistentSite(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, site := range []string{"", "ha", "admin-500"} {
		resp := createEndpoint(server, &Endpoint{Site: site})
		shouldHaveStatusCode(t, http.StatusNotFound, resp)
	}
}

func TestDelay(t *testing.T) {
	runAll(t, checkDelay, multiDomainTestCases)
}

func checkDelay(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("0")),
		0, 0.1) // seconds
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("1")),
		1, 1.1) // seconds
}

func TestStatus(t *testing.T) {
	runAll(t, checkStatus, multiDomainTestCases)
}

func checkStatus(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, statusCode := range []int{200, 404, 500} {
		resp := GET(server.URL, "/", makeFullDomain(strconv.Itoa(statusCode)))
		shouldHaveStatusCode(t, statusCode, resp)
	}
}

func TestEndpointCreation(t *testing.T) {
	runAll(t, checkEndpointCreationTestCase, endpointCreationTestCases)
}

func checkEndpointCreationTestCase(t *testing.T, server *httptest.Server, testCase *TestCase) {
	prefix := testCase.adminPathPrefix
	isInSingleSiteMode := prefix != ""
	domain, site := TEST_DEPLOYED_ON, ""
	root_response := []byte("haha")
	test_response := []byte("hop")
	test_post_response := []byte("for POST")
	empty_payload := []byte("")

	if isInSingleSiteMode {
		resp := createEndpoint(server, &Endpoint{Path: join(prefix, "/"), Response: root_response})
		shouldHaveStatusCode(t, http.StatusOK, resp)
	} else {
		domain = createDomain(server, &Endpoint{Path: join(prefix, "/"), Response: root_response})
		site = getSite(domain)
	}

	bytesShouldBeEqual(t, read(GET(server.URL, "/", domain)), root_response)

	// testing GET endpoint
	resp := createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_response, Method: "GET"})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that GET /test endpoint works
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that GET /test doesn't affect POST
	resp = POST(server.URL, "/test", domain, []byte(""))
	intShouldBeEqual(t, 404, resp.StatusCode)

	// testing POST endpoint
	resp = createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_post_response, Method: "POST",
		Delay: time.Duration(100) * time.Millisecond})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that POST endpoint doesn't affect GET
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that POST /test endpoint works
	bytesShouldBeEqual(t, read(POST(server.URL, "/test", domain, empty_payload)), test_post_response)
	shouldRespondIn(t, createPOST(server.URL, "/test", domain, empty_payload), 0.1, 0.15)
}

func newGoSlowServer(testCase *TestCase) *Server {
	config := DEFAULT_CONFIG // copies DEFAULT_CONFIG
	config.deployedOn = TEST_DEPLOYED_ON
	config.createDefaultEndpoints = testCase.createDefaultEndpoints
	config.adminPathPrefix = testCase.adminPathPrefix
	config.driver = testCase.driver
	config.dataSource = testCase.dataSource
	return NewServer(&config)
}

func GET(url, path, host string) *http.Response {
	req := createGET(url, path, host)
	return do(req)
}

func do(req *http.Request) *http.Response {
	resp, err := new(http.Client).Do(req)
	if err != nil {
		log.Fatal(err)
	}
	return resp
}

func createGET(url, path, host string) *http.Request {
	return createRequest("GET", url, path, host, nil)
}

func createRequest(method, url, path, host string, body io.Reader) *http.Request {
	req, err := http.NewRequest(method, url+path, body)
	if err != nil {
		log.Fatal(err)
	}
	req.Host = host
	return req
}

func read(resp *http.Response) []byte {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	return body
}

func makeFullDomain(site string) string {
	return fmt.Sprintf("%s.%s", site, TEST_DEPLOYED_ON)
}

func bytesShouldBeEqual(t *testing.T, expected, actual []byte) {
	if !bytes.Equal(expected, actual) {
		t.Fatalf("<<%v>> != <<%v>>", string(expected), string(actual))
	}
}

func intShouldBeEqual(t *testing.T, expected, actual int) {
	if expected != actual {
		t.Fatalf("<<%v>> != <<%v>>", expected, actual)
	}
}

func shouldRespondIn(t *testing.T, req *http.Request, minSeconds, maxSeconds float64) {
	start := time.Now()
	resp := do(req)
	read(resp)
	duration := time.Since(start)
	minDuration := toDuration(minSeconds)
	maxDuration := toDuration(maxSeconds)
	if duration < minDuration || duration > maxDuration {
		t.Fatalf("%s%s answered in %v. Not in the interval [%v; %v]",
			req.Host, req.URL.Path, duration, minDuration, maxDuration)
	}
}

func shouldHaveStatusCode(t *testing.T, statusCode int, resp *http.Response) {
	intShouldBeEqual(t, statusCode, resp.StatusCode)
}

func toDuration(seconds float64) time.Duration {
	return time.Duration(seconds*1000) * time.Millisecond
}

func createDomain(server *httptest.Server, endpoint *Endpoint) string {
	resp := POST(server.URL, fmt.Sprintf("%s?output=short&method=GET", endpoint.Path),
		makeFullDomain("create"), endpoint.Response)
	return string(read(resp))
}

func POST(url, path, host string, payload []byte) *http.Response {
	req := createPOST(url, path, host, payload)
	return do(req)
}

func createPOST(url, path, host string, payload []byte) *http.Request {
	return createRequest("POST", url, path, host, bytes.NewReader(payload))
}

func getSite(domain string) string {
	return strings.Split(domain, ".")[0]
}

func createEndpoint(server *httptest.Server, endpoint *Endpoint) *http.Response {
	req := createPOST(server.URL, endpoint.Path, makeFullDomain("admin-"+endpoint.Site),
		endpoint.Response)
	req.URL.RawQuery = getQueryString(endpoint)
	return do(req)
}

func getQueryString(endpoint *Endpoint) string {
	params := url.Values{}
	params.Set("method", endpoint.Method)
	params.Set("delay", fmt.Sprintf("%f", endpoint.Delay.Seconds()))
	return params.Encode()
}

// Wrapper around path.Join. Preserves trailing slash.
func join(elem ...string) string {
	lastElem := elem[len(elem)-1]
	shouldEndWithSlash := strings.HasSuffix(lastElem, "/")
	joined := path.Join(elem...)
	if shouldEndWithSlash && !strings.HasSuffix(joined, "/") {
		joined += "/"
	}
	return joined
}

func createDb(name string) {
	cmd := exec.Command("createdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("createdb error: %s", err)
	}
}

func dropDb(name string) {
	cmd := exec.Command("dropdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("dropdb error: %s", err)
	}
}
<|endoftext|>


<commit_before>package servo

import (
	"hwio"
)

const (
	// default servo period, in milliseconds
	DEFAULT_SERVO_PERIOD = 20

	// defaults for servo duty, in microseconds
	DEFAULT_DUTY_MIN = 1000
	DEFAULT_DUTY_MAX = 2000
)

type Servo struct {
	PWM     hwio.PWMModule
	Pin     hwio.Pin
	minDuty int // min duty in microseconds
	maxDuty int // max duty in microseconds
}

// Create a new servo and initialise it.
func New(pwm hwio.PWMModule, pin interface{}) (*Servo, error) {
	var p hwio.Pin
	var e error

	switch pt := pin.(type) {
	case hwio.Pin:
		p = pt
	case string:
		p, e = hwio.GetPin(pt)
		if e != nil {
			return nil, e
		}
	}

	result := &Servo{PWM: pwm, Pin: p}

	// enable the servo
	pwm.EnablePin(p, true)

	result.SetPeriod(DEFAULT_SERVO_PERIOD)
	result.SetRange(DEFAULT_DUTY_MIN, DEFAULT_DUTY_MAX)

	return result, nil
}

// helper function to set the period of each cycle. Servos generally want this to be fixed, typically at 20ms.
// This just sets the underling PWM period, so if you need less than 1 ms you can set that directly on the PWM.
func (servo *Servo) SetPeriod(milliseconds int) {
	servo.PWM.SetPeriod(servo.Pin, int64(milliseconds*1000000))
}

// Set the servo to the specified angle, typically 0-180. This sets the duty cycle proportionally between min and max,
// which are defaulted to 1000-2000 microseconds range.
func (servo *Servo) Write(angle int) {
	servo.WriteMicroseconds(hwio.Map(angle, 0, 180, servo.minDuty, servo.maxDuty))
}

// Like the Arduino Servo.writeMicroseconds function. This is really setting the PWM duty directly, so it is possible
// to write values too small or too large for the servo to track.
func (servo *Servo) WriteMicroseconds(ms int) {
	// just pass to the underlying PWM pin.
	servo.PWM.SetDuty(servo.Pin, int64(ms*1000))
}

// Set the minimum and maximum number of microseconds for the servo. Write maps 0-180 to these values.
func (servo *Servo) SetRange(min int, max int) {
	servo.minDuty = min
	servo.maxDuty = max
}
<commit_msg>improve servo error handling<commit_after>package servo

import (
	"hwio"
)

const (
	// default servo period, in milliseconds
	DEFAULT_SERVO_PERIOD = 20

	// defaults for servo duty, in microseconds
	DEFAULT_DUTY_MIN = 1000
	DEFAULT_DUTY_MAX = 2000
)

type Servo struct {
	PWM     hwio.PWMModule
	Pin     hwio.Pin
	minDuty int // min duty in microseconds
	maxDuty int // max duty in microseconds
}

// Create a new servo and initialise it.
func New(pwm hwio.PWMModule, pin interface{}) (*Servo, error) {
	var p hwio.Pin
	var e error

	switch pt := pin.(type) {
	case hwio.Pin:
		p = pt
	case string:
		p, e = hwio.GetPin(pt)
		if e != nil {
			return nil, e
		}
	}

	result := &Servo{PWM: pwm, Pin: p}

	// enable the servo
	e = pwm.EnablePin(p, true)
	if e != nil {
		return nil, e
	}

	e = result.SetPeriod(DEFAULT_SERVO_PERIOD)
	if e != nil {
		return nil, e
	}

	result.SetRange(DEFAULT_DUTY_MIN, DEFAULT_DUTY_MAX)

	return result, nil
}

// helper function to set the period of each cycle. Servos generally want this to be fixed, typically at 20ms.
// This just sets the underling PWM period, so if you need less than 1 ms you can set that directly on the PWM.
func (servo *Servo) SetPeriod(milliseconds int) error {
	return servo.PWM.SetPeriod(servo.Pin, int64(milliseconds*1000000))
}

// Set the servo to the specified angle, typically 0-180. This sets the duty cycle proportionally between min and max,
// which are defaulted to 1000-2000 microseconds range.
func (servo *Servo) Write(angle int) {
	servo.WriteMicroseconds(hwio.Map(angle, 0, 180, servo.minDuty, servo.maxDuty))
}

// Like the Arduino Servo.writeMicroseconds function. This is really setting the PWM duty directly, so it is possible
// to write values too small or too large for the servo to track.
func (servo *Servo) WriteMicroseconds(ms int) {
	// just pass to the underlying PWM pin.
	servo.PWM.SetDuty(servo.Pin, int64(ms*1000))
}

// Set the minimum and maximum number of microseconds for the servo. Write maps 0-180 to these values.
func (servo *Servo) SetRange(min int, max int) {
	servo.minDuty = min
	servo.maxDuty = max
}
<|endoftext|>


<commit_before>// http://golang.org/src/pkg/crypto/tls/generate_cert.go
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package shared

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net"
	"os"
	"os/user"
	"path"
	"time"
)

// CertInfo is the representation of a Certificate in the API.
type CertInfo struct {
	Certificate string `json:"certificate"`
	Fingerprint string `json:"fingerprint"`
	Type        string `json:"type"`
}

/*
 * Generate a list of names for which the certificate will be valid.
 * This will include the hostname and ip address
 */
func mynames() ([]string, error) {
	h, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	ret := []string{h}

	ifs, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	for _, iface := range ifs {
		if IsLoopback(&iface) {
			continue
		}

		addrs, err := iface.Addrs()
		if err != nil {
			return nil, err
		}

		for _, addr := range addrs {
			ret = append(ret, addr.String())
		}
	}

	return ret, nil
}

func FindOrGenCert(certf string, keyf string, certtype bool) error {
	if PathExists(certf) && PathExists(keyf) {
		return nil
	}

	/* If neither stat succeeded, then this is our first run and we
	 * need to generate cert and privkey */
	err := GenCert(certf, keyf, certtype)
	if err != nil {
		return err
	}

	return nil
}

// GenCert will create and populate a certificate file and a key file
func GenCert(certf string, keyf string, certtype bool) error {
	/* Create the basenames if needed */
	dir := path.Dir(certf)
	err := os.MkdirAll(dir, 0750)
	if err != nil {
		return err
	}
	dir = path.Dir(keyf)
	err = os.MkdirAll(dir, 0750)
	if err != nil {
		return err
	}

	certBytes, keyBytes, err := GenerateMemCert(certtype)
	if err != nil {
		return err
	}

	certOut, err := os.Create(certf)
	if err != nil {
		log.Fatalf("failed to open %s for writing: %s", certf, err)
		return err
	}
	certOut.Write(certBytes)
	certOut.Close()

	keyOut, err := os.OpenFile(keyf, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Printf("failed to open %s for writing: %s", keyf, err)
		return err
	}
	keyOut.Write(keyBytes)
	keyOut.Close()
	return nil
}

// GenerateMemCert creates client or server certificate and key pair,
// returning them as byte arrays in memory.
func GenerateMemCert(client bool) ([]byte, []byte, error) {
	privk, err := rsa.GenerateKey(rand.Reader, 4096)
	if err != nil {
		log.Fatalf("failed to generate key")
		return nil, nil, err
	}

	hosts, err := mynames()
	if err != nil {
		log.Fatalf("Failed to get my hostname")
		return nil, nil, err
	}

	validFrom := time.Now()
	validTo := validFrom.Add(10 * 365 * 24 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
		return nil, nil, err
	}

	userEntry, err := user.Current()
	var username string
	if err == nil {
		username = userEntry.Username
		if username == "" {
			username = "UNKNOWN"
		}
	} else {
		username = "UNKNOWN"
	}

	hostname, err := os.Hostname()
	if err != nil {
		hostname = "UNKNOWN"
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"linuxcontainers.org"},
			CommonName:   fmt.Sprintf("%s@%s", username, hostname),
		},
		NotBefore: validFrom,
		NotAfter:  validTo,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		BasicConstraintsValid: true,
	}

	if client {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
	} else {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	}

	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privk.PublicKey, privk)
	if err != nil {
		log.Fatalf("Failed to create certificate: %s", err)
		return nil, nil, err
	}

	cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privk)})
	return cert, key, nil
}

func ReadCert(fpath string) (*x509.Certificate, error) {
	cf, err := ioutil.ReadFile(fpath)
	if err != nil {
		return nil, err
	}

	certBlock, _ := pem.Decode(cf)
	if certBlock == nil {
		return nil, fmt.Errorf("Invalid certificate file")
	}

	return x509.ParseCertificate(certBlock.Bytes)
}
<commit_msg>shared/cert: be more thorough when parsing ip addr<commit_after>// http://golang.org/src/pkg/crypto/tls/generate_cert.go
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package shared

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net"
	"os"
	"os/user"
	"path"
	"time"
)

// CertInfo is the representation of a Certificate in the API.
type CertInfo struct {
	Certificate string `json:"certificate"`
	Fingerprint string `json:"fingerprint"`
	Type        string `json:"type"`
}

/*
 * Generate a list of names for which the certificate will be valid.
 * This will include the hostname and ip address
 */
func mynames() ([]string, error) {
	h, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	ret := []string{h}

	ifs, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	for _, iface := range ifs {
		if IsLoopback(&iface) {
			continue
		}

		addrs, err := iface.Addrs()
		if err != nil {
			return nil, err
		}

		for _, addr := range addrs {
			ret = append(ret, addr.String())
		}
	}

	return ret, nil
}

func FindOrGenCert(certf string, keyf string, certtype bool) error {
	if PathExists(certf) && PathExists(keyf) {
		return nil
	}

	/* If neither stat succeeded, then this is our first run and we
	 * need to generate cert and privkey */
	err := GenCert(certf, keyf, certtype)
	if err != nil {
		return err
	}

	return nil
}

// GenCert will create and populate a certificate file and a key file
func GenCert(certf string, keyf string, certtype bool) error {
	/* Create the basenames if needed */
	dir := path.Dir(certf)
	err := os.MkdirAll(dir, 0750)
	if err != nil {
		return err
	}
	dir = path.Dir(keyf)
	err = os.MkdirAll(dir, 0750)
	if err != nil {
		return err
	}

	certBytes, keyBytes, err := GenerateMemCert(certtype)
	if err != nil {
		return err
	}

	certOut, err := os.Create(certf)
	if err != nil {
		log.Fatalf("failed to open %s for writing: %s", certf, err)
		return err
	}
	certOut.Write(certBytes)
	certOut.Close()

	keyOut, err := os.OpenFile(keyf, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Printf("failed to open %s for writing: %s", keyf, err)
		return err
	}
	keyOut.Write(keyBytes)
	keyOut.Close()
	return nil
}

// GenerateMemCert creates client or server certificate and key pair,
// returning them as byte arrays in memory.
func GenerateMemCert(client bool) ([]byte, []byte, error) {
	privk, err := rsa.GenerateKey(rand.Reader, 4096)
	if err != nil {
		log.Fatalf("failed to generate key")
		return nil, nil, err
	}

	hosts, err := mynames()
	if err != nil {
		log.Fatalf("Failed to get my hostname")
		return nil, nil, err
	}

	validFrom := time.Now()
	validTo := validFrom.Add(10 * 365 * 24 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
		return nil, nil, err
	}

	userEntry, err := user.Current()
	var username string
	if err == nil {
		username = userEntry.Username
		if username == "" {
			username = "UNKNOWN"
		}
	} else {
		username = "UNKNOWN"
	}

	hostname, err := os.Hostname()
	if err != nil {
		hostname = "UNKNOWN"
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"linuxcontainers.org"},
			CommonName:   fmt.Sprintf("%s@%s", username, hostname),
		},
		NotBefore: validFrom,
		NotAfter:  validTo,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		BasicConstraintsValid: true,
	}

	if client {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
	} else {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	}

	for _, h := range hosts {
		if ip, _, err := net.ParseCIDR(h); err == nil {
			if !ip.IsLinkLocalUnicast() && !ip.IsLinkLocalMulticast() {
				template.IPAddresses = append(template.IPAddresses, ip)
			}
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privk.PublicKey, privk)
	if err != nil {
		log.Fatalf("Failed to create certificate: %s", err)
		return nil, nil, err
	}

	cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privk)})
	return cert, key, nil
}

func ReadCert(fpath string) (*x509.Certificate, error) {
	cf, err := ioutil.ReadFile(fpath)
	if err != nil {
		return nil, err
	}

	certBlock, _ := pem.Decode(cf)
	if certBlock == nil {
		return nil, fmt.Errorf("Invalid certificate file")
	}

	return x509.ParseCertificate(certBlock.Bytes)
}
<|endoftext|>


<commit_before>/* This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>.

@author: UnHa Kim <unha.kim.ghts@gmail.com> */

package shared

import (
	"bytes"
	"fmt"
)

func init() {
	f메모()
}

func f메모() {
	버퍼 := new(bytes.Buffer)
	버퍼.WriteString("에러가 발생했는 데 원인도 모르겠고 막막하다면?\n")
	버퍼.WriteString("1. 문제를 잘게 쪼갠다.\n")
	버퍼.WriteString("2. 문제를 단순화 시킨다. 예:) 테스트 데이터를 1개로 한정.\n")
	버퍼.WriteString("3. 내부 실행과정을 추적한다.\n")
	버퍼.WriteString("디버깅에 이보다 달리 더 좋은 방법은 없다.\n\n")
	버퍼.WriteString("\n")

	버퍼.WriteString("테스트 실행 시 가끔씩 zmq소켓 초기화 에러 발생함.\n")
	버퍼.WriteString("해당 포트가 사용 중이라고 하는 데, 이것을 깨끗하게 정리할 방법은 없는가?\n")
	버퍼.WriteString("\n")

	버퍼.WriteString("NH tx OpenAPI(혹은 WMCA SDK)는 Win32 dll 임.\n")
	버퍼.WriteString("64비트 윈도우에서 사용하기 위해서는 Win32 중계 프로그램이 필요함.\n")
	버퍼.WriteString("MsgPack, ZeroMQ를 사용하면 간단히 작성할 수 있을 듯 함.\n")
	버퍼.WriteString("wmcaConnect(), wmcaDisconnect(), wmcaQuery()등 대부분의 함수는 REQ-REP 소켓.\n")
	버퍼.WriteString("wmcaAttach()로 수신하는 실시간 데이터는 PUB-SUB 소켓으로 전달해 주면 됨.\n")
	버퍼.WriteString("문제는 내가 C++ 문외한이라는 것.\n")
	버퍼.WriteString("\n")

	문자열 := 버퍼.String() + "\n\n"

	fmt.Println(문자열)
}
<commit_msg>NH tx OpenAPI 중계 호스트 구상 수정.<commit_after>/* This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>.

@author: UnHa Kim <unha.kim.ghts@gmail.com> */

package shared

import (
	"bytes"
	"fmt"
)

func init() {
	f메모()
}

func f메모() {
	버퍼 := new(bytes.Buffer)
	버퍼.WriteString("에러가 발생했는 데 원인도 모르겠고 막막하다면?\n")
	버퍼.WriteString("1. 문제를 잘게 쪼갠다.\n")
	버퍼.WriteString("2. 문제를 단순화 시킨다. 예:) 테스트 데이터를 1개로 한정.\n")
	버퍼.WriteString("3. 내부 실행과정을 추적한다.\n")
	버퍼.WriteString("디버깅에 이보다 달리 더 좋은 방법은 없다.\n\n")
	버퍼.WriteString("\n")

	버퍼.WriteString("테스트 실행 시 가끔씩 zmq소켓 초기화 에러 발생함.\n")
	버퍼.WriteString("해당 포트가 사용 중이라고 하는 데, 이것을 깨끗하게 정리할 방법은 없는가?\n")
	버퍼.WriteString("\n")

	버퍼.WriteString("NH tx OpenAPI(혹은 WMCA SDK)는 Win32 dll 임.\n")
	버퍼.WriteString("64비트 윈도우에서 사용하기 위해서는 Win32 중계 프로그램이 필요함.\n")
	버퍼.WriteString("MsgPack, ZeroMQ를 사용하면 간단히 작성할 수 있을 듯 함.\n")
	버퍼.WriteString("wmcaConnect(), wmcaDisconnect(), wmcaQuery()등 대부분의 함수는 REQ-REP 소켓.\n")
	버퍼.WriteString("wmcaAttach()로 수신하는 실시간 데이터는 PUB-SUB 소켓으로 전달해 주면 됨.\n")
	버퍼.WriteString("문제는 내가 C++ 문외한이라는 것.\n")
	버퍼.WriteString("잘 알지도 못하는 복잡한 C++을 다시 공부하는 것은 어려우니,\n")
	버퍼.WriteString("Go언어나 파이썬 같은 가독성이 좋고 접근성이 좋은 언어로,\n")
	버퍼.WriteString("바로 DLL을 호출한 후 수신한 데이터를 MsgPack, ZeroMQ를 통해서 중계하도록 하자.\n")
	버퍼.WriteString("Go언어는 go.sys, 파이썬은 ctype으로 직접 DLL을 호출할 수 있다.\n")
	버퍼.WriteString("github.com/golang/sys 참조할 것.\n")

	문자열 := 버퍼.String() + "\n\n"

	fmt.Println(문자열)
}
<|endoftext|>


<commit_before>package parse // import "github.com/tdewolff/parse"

import (
	"errors"
	"io"
)

// minBuf and maxBuf are the initial and maximal internal buffer size.
var MinBuf = 1024
var MaxBuf = 4096

// ErrBufferExceeded is returned when the internal buffer exceeds 4096 bytes, a string or comment must thus be smaller than 4kB!
var ErrBufferExceeded = errors.New("max buffer exceeded")

////////////////////////////////////////////////////////////////

// ShiftBuffer is a buffered reader that allows peeking forward and shifting, taking an io.Reader.
type ShiftBuffer struct {
	r       io.Reader
	readErr error

	buf []byte
	pos int
	n   int

	minBuf, maxBuf int
}

// NewShiftBufferReader returns a new ShiftBuffer.
func NewShiftBuffer(r io.Reader) *ShiftBuffer {
	// If reader has the bytes in memory already, use that instead!
	if fr, ok := r.(interface {
		Bytes() []byte
	}); ok {
		return &ShiftBuffer{
			readErr: io.EOF,
			buf:     fr.Bytes(),
		}
	}
	return &ShiftBuffer{
		r:   r,
		buf: make([]byte, 0, MinBuf),
	}
}

// Err returns the error.
func (z ShiftBuffer) Err() error {
	if z.readErr == io.EOF {
		if z.pos+z.n >= len(z.buf) {
			return io.EOF
		}
	} else if z.readErr != nil {
		return z.readErr
	}
	return nil
}

// Move advances the 0 position of read.
func (z *ShiftBuffer) Move(n int) {
	z.n += n
}

// MoveTo sets the 0 position of read.
func (z *ShiftBuffer) MoveTo(n int) {
	z.n = n
}

// Pos returns the 0 position of read.
func (z ShiftBuffer) Pos() int {
	return z.n
}

// Len returns the length of the buffer.
func (z ShiftBuffer) Len() int {
	return len(z.buf)
}

// Peek returns the ith byte and possible does a reallocation
func (z *ShiftBuffer) Peek(i int) byte {
	if z.pos+z.n+i >= len(z.buf) {
		if z.readErr != nil {
			return 0
		}

		// reallocate a new buffer (possibly larger)
		c := cap(z.buf)
		d := z.n + i
		var buf1 []byte
		if 2*d > c {
			if 2*c > MaxBuf {
				z.readErr = ErrBufferExceeded
				return 0
			}
			buf1 = make([]byte, d, 2*c)
		} else {
			buf1 = z.buf[:d]
		}
		copy(buf1, z.buf[z.pos:z.pos+d])

		// Read in to fill the buffer till capacity
		var n int
		n, z.readErr = z.r.Read(buf1[d:cap(buf1)])
		z.pos, z.buf = 0, buf1[:d+n]
		if n == 0 {
			return 0
		}
	}
	return z.buf[z.pos+z.n+i]
}

// Buffered returns the bytes of the current selection.
func (z ShiftBuffer) Buffered() []byte {
	return z.buf[z.pos : z.pos+z.n]
}

// Shift returns the bytes of the current selection and advances the position.
func (z *ShiftBuffer) Shift() []byte {
	b := z.buf[z.pos : z.pos+z.n]
	z.pos += z.n
	z.n = 0
	return b
}
<commit_msg>Refactor Err()<commit_after>package parse // import "github.com/tdewolff/parse"

import (
	"errors"
	"io"
)

// minBuf and maxBuf are the initial and maximal internal buffer size.
var MinBuf = 1024
var MaxBuf = 4096

// ErrBufferExceeded is returned when the internal buffer exceeds 4096 bytes, a string or comment must thus be smaller than 4kB!
var ErrBufferExceeded = errors.New("max buffer exceeded")

////////////////////////////////////////////////////////////////

// ShiftBuffer is a buffered reader that allows peeking forward and shifting, taking an io.Reader.
type ShiftBuffer struct {
	r       io.Reader
	readErr error

	buf []byte
	pos int
	n   int

	minBuf, maxBuf int
}

// NewShiftBufferReader returns a new ShiftBuffer.
func NewShiftBuffer(r io.Reader) *ShiftBuffer {
	// If reader has the bytes in memory already, use that instead!
	if fr, ok := r.(interface {
		Bytes() []byte
	}); ok {
		return &ShiftBuffer{
			readErr: io.EOF,
			buf:     fr.Bytes(),
		}
	}
	return &ShiftBuffer{
		r:   r,
		buf: make([]byte, 0, MinBuf),
	}
}

// Err returns the error.
func (z ShiftBuffer) Err() error {
	if z.readErr == io.EOF && z.pos+z.n < len(z.buf) {
		return nil
	}
	return z.readErr
}

// Move advances the 0 position of read.
func (z *ShiftBuffer) Move(n int) {
	z.n += n
}

// MoveTo sets the 0 position of read.
func (z *ShiftBuffer) MoveTo(n int) {
	z.n = n
}

// Pos returns the 0 position of read.
func (z ShiftBuffer) Pos() int {
	return z.n
}

// Len returns the length of the buffer.
func (z ShiftBuffer) Len() int {
	return len(z.buf)
}

// Peek returns the ith byte and possible does a reallocation
func (z *ShiftBuffer) Peek(i int) byte {
	if z.pos+z.n+i >= len(z.buf) {
		if z.readErr != nil {
			return 0
		}

		// reallocate a new buffer (possibly larger)
		c := cap(z.buf)
		d := z.n + i
		var buf1 []byte
		if 2*d > c {
			if 2*c > MaxBuf {
				z.readErr = ErrBufferExceeded
				return 0
			}
			buf1 = make([]byte, d, 2*c)
		} else {
			buf1 = z.buf[:d]
		}
		copy(buf1, z.buf[z.pos:z.pos+d])

		// Read in to fill the buffer till capacity
		var n int
		n, z.readErr = z.r.Read(buf1[d:cap(buf1)])
		z.pos, z.buf = 0, buf1[:d+n]
		if n == 0 {
			return 0
		}
	}
	return z.buf[z.pos+z.n+i]
}

// Buffered returns the bytes of the current selection.
func (z ShiftBuffer) Buffered() []byte {
	return z.buf[z.pos : z.pos+z.n]
}

// Shift returns the bytes of the current selection and advances the position.
func (z *ShiftBuffer) Shift() []byte {
	b := z.buf[z.pos : z.pos+z.n]
	z.pos += z.n
	z.n = 0
	return b
}
<|endoftext|>


<commit_before>package shim

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"

	"cloud.google.com/go/pubsub"
)

type PubsubHandlerFunc func(message pubsub.Message) error

type PubsubResult struct {
	Error error `json:"error"`
}

func HandlePubSubMessage(h PubsubHandlerFunc) {
	stdin, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		log.Fatal(err)
	}

	var message pubsub.Message
	err = json.Unmarshal(stdin, &message)
	if err != nil {
		log.Fatal(err)
	}

	result := PubsubResult{
		Error: h(message),
	}
	out, err := json.Marshal(&result)
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(out)
}
<commit_msg>Change PubSub error to string<commit_after>package shim

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"

	"cloud.google.com/go/pubsub"
)

type PubsubHandlerFunc func(message pubsub.Message) error

type PubsubResult struct {
	Error string `json:"error,omitempty"`
}

func HandlePubSubMessage(h PubsubHandlerFunc) {
	stdin, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		log.Fatal(err)
	}

	var message pubsub.Message
	err = json.Unmarshal(stdin, &message)
	if err != nil {
		log.Fatal(err)
	}

	errMessage := ""
	err = h(message)
	if err != nil {
		errMessage = err.Error()
	}

	result := PubsubResult{
		Error: errMessage,
	}
	out, err := json.Marshal(&result)
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(out)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/consensus"
	"github.com/NebulousLabs/Sia/modules/explorer"
	"github.com/NebulousLabs/Sia/modules/gateway"
	"github.com/NebulousLabs/Sia/modules/host"
	"github.com/NebulousLabs/Sia/modules/miner"
	"github.com/NebulousLabs/Sia/modules/renter"
	"github.com/NebulousLabs/Sia/modules/transactionpool"
	"github.com/NebulousLabs/Sia/modules/wallet"
	"github.com/NebulousLabs/Sia/profile"

	"github.com/bgentry/speakeasy"
	"github.com/spf13/cobra"
)

// verifyAPISecurity checks that the security values are consistent with a
// sane, secure system.
func verifyAPISecurity(config Config) error {
	// Make sure that only the loopback address is allowed unless the
	// --disable-api-security flag has been used.
	if !config.Siad.AllowAPIBind {
		addr := modules.NetAddress(config.Siad.APIaddr)
		if !addr.IsLoopback() {
			if addr.Host() == "" {
				return fmt.Errorf("a blank host will listen on all interfaces, did you mean localhost:%v?\nyou must pass --disable-api-security to bind Siad to a non-localhost address", addr.Port())
			}
			return errors.New("you must pass --disable-api-security to bind Siad to a non-localhost address")
		}
		return nil
	}

	// If the --disable-api-security flag is used, enforce that
	// --authenticate-api must also be used.
	if config.Siad.AllowAPIBind && !config.Siad.AuthenticateAPI {
		return errors.New("cannot use --disable-api-security without setting an api password")
	}
	return nil
}

// processNetAddr adds a ':' to a bare integer, so that it is a proper port
// number.
func processNetAddr(addr string) string {
	_, err := strconv.Atoi(addr)
	if err == nil {
		return ":" + addr
	}
	return addr
}

// processModules makes the modules string lowercase to make checking if a
// module in the string easier, and returns an error if the string contains an
// invalid module character.
func processModules(modules string) (string, error) {
	modules = strings.ToLower(modules)
	validModules := "cghmrtwe"
	invalidModules := modules
	for _, m := range validModules {
		invalidModules = strings.Replace(invalidModules, string(m), "", 1)
	}
	if len(invalidModules) > 0 {
		return "", errors.New("Unable to parse --modules flag, unrecognized or duplicate modules: " + invalidModules)
	}
	return modules, nil
}

// processConfig checks the configuration values and performs cleanup on
// incorrect-but-allowed values.
func processConfig(config Config) (Config, error) {
	var err1 error
	config.Siad.APIaddr = processNetAddr(config.Siad.APIaddr)
	config.Siad.RPCaddr = processNetAddr(config.Siad.RPCaddr)
	config.Siad.HostAddr = processNetAddr(config.Siad.HostAddr)
	config.Siad.Modules, err1 = processModules(config.Siad.Modules)
	err2 := verifyAPISecurity(config)
	err := build.JoinErrors([]error{err1, err2}, ", and ")
	if err != nil {
		return Config{}, err
	}
	return config, nil
}

// startDaemon uses the config parameters to initialize Sia modules and start
// siad.
func startDaemon(config Config) (err error) {
	// Prompt user for API password.
	if config.Siad.AuthenticateAPI {
		config.APIPassword, err = speakeasy.Ask("Enter API password: ")
		if err != nil {
			return err
		}
		if config.APIPassword == "" {
			return errors.New("password cannot be blank")
		}
	}

	// Process the config variables after they are parsed by cobra.
	config, err = processConfig(config)
	if err != nil {
		return err
	}

	// Print a startup message.
	fmt.Println("Loading...")
	loadStart := time.Now()

	// Create the server and start serving daemon routes immediately.
	fmt.Printf("(0/%d) Loading siad...\n", len(config.Siad.Modules))
	srv, err := NewServer(config.Siad.APIaddr, config.Siad.RequiredUserAgent, config.APIPassword)
	if err != nil {
		return err
	}

	servErrs := make(chan error)
	go func() {
		servErrs <- srv.Serve()
	}()

	// Initialize the Sia modules
	i := 0
	var g modules.Gateway
	if strings.Contains(config.Siad.Modules, "g") {
		i++
		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(config.Siad.Modules))
		g, err = gateway.New(config.Siad.RPCaddr, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.GatewayDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing gateway...")
			err := g.Close()
			if err != nil {
				fmt.Println("Error during gateway shutdown:", err)
			}
		}()
	}
	var cs modules.ConsensusSet
	if strings.Contains(config.Siad.Modules, "c") {
		i++
		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(config.Siad.Modules))
		cs, err = consensus.New(g, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing consensus set...")
			err := cs.Close()
			if err != nil {
				fmt.Println("Error during consensus set shutdown:", err)
			}
		}()
	}
	var e modules.Explorer
	if strings.Contains(config.Siad.Modules, "e") {
		i++
		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(config.Siad.Modules))
		e, err = explorer.New(cs, filepath.Join(config.Siad.SiaDir, modules.ExplorerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing explorer...")
			err := e.Close()
			if err != nil {
				fmt.Println("Error during explorer shutdown:", err)
			}
		}()
	}
	var tpool modules.TransactionPool
	if strings.Contains(config.Siad.Modules, "t") {
		i++
		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(config.Siad.Modules))
		tpool, err = transactionpool.New(cs, g, filepath.Join(config.Siad.SiaDir, modules.TransactionPoolDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing transaction pool...")
			err := tpool.Close()
			if err != nil {
				fmt.Println("Error during transaction pool shutdown:", err)
			}
		}()
	}
	var w modules.Wallet
	if strings.Contains(config.Siad.Modules, "w") {
		i++
		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(config.Siad.Modules))
		w, err = wallet.New(cs, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing wallet...")
			err := w.Close()
			if err != nil {
				fmt.Println("Error during wallet shutdown:", err)
			}
		}()
	}
	var m modules.Miner
	if strings.Contains(config.Siad.Modules, "m") {
		i++
		fmt.Printf("(%d/%d) Loading miner...\n", i, len(config.Siad.Modules))
		m, err = miner.New(cs, tpool, w, filepath.Join(config.Siad.SiaDir, modules.MinerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing miner...")
			err := m.Close()
			if err != nil {
				fmt.Println("Error during miner shutdown:", err)
			}
		}()
	}
	var h modules.Host
	if strings.Contains(config.Siad.Modules, "h") {
		i++
		fmt.Printf("(%d/%d) Loading host...\n", i, len(config.Siad.Modules))
		h, err = host.New(cs, tpool, w, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing host...")
			err := h.Close()
			if err != nil {
				fmt.Println("Error during host shutdown:", err)
			}
		}()
	}
	var r modules.Renter
	if strings.Contains(config.Siad.Modules, "r") {
		i++
		fmt.Printf("(%d/%d) Loading renter...\n", i, len(config.Siad.Modules))
		r, err = renter.New(cs, w, tpool, filepath.Join(config.Siad.SiaDir, modules.RenterDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing renter...")
			err := r.Close()
			if err != nil {
				fmt.Println("Error during renter shutdown:", err)
			}
		}()
	}

	// Create the Sia API
	a := api.New(
		config.Siad.RequiredUserAgent,
		config.APIPassword,
		cs,
		e,
		g,
		h,
		m,
		r,
		tpool,
		w,
	)

	// connect the API to the server
	srv.mux.Handle("/", a)

	// stop the server if a kill signal is caught
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill)
	go func() {
		<-sigChan
		fmt.Println("\rCaught stop signal, quitting...")
		srv.Close()
	}()

	// Print a 'startup complete' message.
	startupTime := time.Since(loadStart)
	fmt.Println("Finished loading in", startupTime.Seconds(), "seconds")

	err = <-servErrs
	if err != nil {
		build.Critical(err)
	}

	return nil
}

// startDaemonCmd is a passthrough function for startDaemon.
func startDaemonCmd(cmd *cobra.Command, _ []string) {
	// Create the profiling directory if profiling is enabled.
	if globalConfig.Siad.Profile {
		go profile.StartContinuousProfile(globalConfig.Siad.ProfileDir)
	}

	// Start siad. startDaemon will only return when it is shutting down.
	err := startDaemon(globalConfig)
	if err != nil {
		die(err)
	}
}
<commit_msg>enable profiling during DEBUG<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/consensus"
	"github.com/NebulousLabs/Sia/modules/explorer"
	"github.com/NebulousLabs/Sia/modules/gateway"
	"github.com/NebulousLabs/Sia/modules/host"
	"github.com/NebulousLabs/Sia/modules/miner"
	"github.com/NebulousLabs/Sia/modules/renter"
	"github.com/NebulousLabs/Sia/modules/transactionpool"
	"github.com/NebulousLabs/Sia/modules/wallet"
	"github.com/NebulousLabs/Sia/profile"

	"github.com/bgentry/speakeasy"
	"github.com/spf13/cobra"
)

// verifyAPISecurity checks that the security values are consistent with a
// sane, secure system.
func verifyAPISecurity(config Config) error {
	// Make sure that only the loopback address is allowed unless the
	// --disable-api-security flag has been used.
	if !config.Siad.AllowAPIBind {
		addr := modules.NetAddress(config.Siad.APIaddr)
		if !addr.IsLoopback() {
			if addr.Host() == "" {
				return fmt.Errorf("a blank host will listen on all interfaces, did you mean localhost:%v?\nyou must pass --disable-api-security to bind Siad to a non-localhost address", addr.Port())
			}
			return errors.New("you must pass --disable-api-security to bind Siad to a non-localhost address")
		}
		return nil
	}

	// If the --disable-api-security flag is used, enforce that
	// --authenticate-api must also be used.
	if config.Siad.AllowAPIBind && !config.Siad.AuthenticateAPI {
		return errors.New("cannot use --disable-api-security without setting an api password")
	}
	return nil
}

// processNetAddr adds a ':' to a bare integer, so that it is a proper port
// number.
func processNetAddr(addr string) string {
	_, err := strconv.Atoi(addr)
	if err == nil {
		return ":" + addr
	}
	return addr
}

// processModules makes the modules string lowercase to make checking if a
// module in the string easier, and returns an error if the string contains an
// invalid module character.
func processModules(modules string) (string, error) {
	modules = strings.ToLower(modules)
	validModules := "cghmrtwe"
	invalidModules := modules
	for _, m := range validModules {
		invalidModules = strings.Replace(invalidModules, string(m), "", 1)
	}
	if len(invalidModules) > 0 {
		return "", errors.New("Unable to parse --modules flag, unrecognized or duplicate modules: " + invalidModules)
	}
	return modules, nil
}

// processConfig checks the configuration values and performs cleanup on
// incorrect-but-allowed values.
func processConfig(config Config) (Config, error) {
	var err1 error
	config.Siad.APIaddr = processNetAddr(config.Siad.APIaddr)
	config.Siad.RPCaddr = processNetAddr(config.Siad.RPCaddr)
	config.Siad.HostAddr = processNetAddr(config.Siad.HostAddr)
	config.Siad.Modules, err1 = processModules(config.Siad.Modules)
	err2 := verifyAPISecurity(config)
	err := build.JoinErrors([]error{err1, err2}, ", and ")
	if err != nil {
		return Config{}, err
	}
	return config, nil
}

// startDaemon uses the config parameters to initialize Sia modules and start
// siad.
func startDaemon(config Config) (err error) {
	// Prompt user for API password.
	if config.Siad.AuthenticateAPI {
		config.APIPassword, err = speakeasy.Ask("Enter API password: ")
		if err != nil {
			return err
		}
		if config.APIPassword == "" {
			return errors.New("password cannot be blank")
		}
	}

	// Process the config variables after they are parsed by cobra.
	config, err = processConfig(config)
	if err != nil {
		return err
	}

	// Print a startup message.
	fmt.Println("Loading...")
	loadStart := time.Now()

	// Create the server and start serving daemon routes immediately.
	fmt.Printf("(0/%d) Loading siad...\n", len(config.Siad.Modules))
	srv, err := NewServer(config.Siad.APIaddr, config.Siad.RequiredUserAgent, config.APIPassword)
	if err != nil {
		return err
	}

	servErrs := make(chan error)
	go func() {
		servErrs <- srv.Serve()
	}()

	// Initialize the Sia modules
	i := 0
	var g modules.Gateway
	if strings.Contains(config.Siad.Modules, "g") {
		i++
		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(config.Siad.Modules))
		g, err = gateway.New(config.Siad.RPCaddr, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.GatewayDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing gateway...")
			err := g.Close()
			if err != nil {
				fmt.Println("Error during gateway shutdown:", err)
			}
		}()
	}
	var cs modules.ConsensusSet
	if strings.Contains(config.Siad.Modules, "c") {
		i++
		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(config.Siad.Modules))
		cs, err = consensus.New(g, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing consensus set...")
			err := cs.Close()
			if err != nil {
				fmt.Println("Error during consensus set shutdown:", err)
			}
		}()
	}
	var e modules.Explorer
	if strings.Contains(config.Siad.Modules, "e") {
		i++
		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(config.Siad.Modules))
		e, err = explorer.New(cs, filepath.Join(config.Siad.SiaDir, modules.ExplorerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing explorer...")
			err := e.Close()
			if err != nil {
				fmt.Println("Error during explorer shutdown:", err)
			}
		}()
	}
	var tpool modules.TransactionPool
	if strings.Contains(config.Siad.Modules, "t") {
		i++
		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(config.Siad.Modules))
		tpool, err = transactionpool.New(cs, g, filepath.Join(config.Siad.SiaDir, modules.TransactionPoolDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing transaction pool...")
			err := tpool.Close()
			if err != nil {
				fmt.Println("Error during transaction pool shutdown:", err)
			}
		}()
	}
	var w modules.Wallet
	if strings.Contains(config.Siad.Modules, "w") {
		i++
		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(config.Siad.Modules))
		w, err = wallet.New(cs, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing wallet...")
			err := w.Close()
			if err != nil {
				fmt.Println("Error during wallet shutdown:", err)
			}
		}()
	}
	var m modules.Miner
	if strings.Contains(config.Siad.Modules, "m") {
		i++
		fmt.Printf("(%d/%d) Loading miner...\n", i, len(config.Siad.Modules))
		m, err = miner.New(cs, tpool, w, filepath.Join(config.Siad.SiaDir, modules.MinerDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing miner...")
			err := m.Close()
			if err != nil {
				fmt.Println("Error during miner shutdown:", err)
			}
		}()
	}
	var h modules.Host
	if strings.Contains(config.Siad.Modules, "h") {
		i++
		fmt.Printf("(%d/%d) Loading host...\n", i, len(config.Siad.Modules))
		h, err = host.New(cs, tpool, w, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing host...")
			err := h.Close()
			if err != nil {
				fmt.Println("Error during host shutdown:", err)
			}
		}()
	}
	var r modules.Renter
	if strings.Contains(config.Siad.Modules, "r") {
		i++
		fmt.Printf("(%d/%d) Loading renter...\n", i, len(config.Siad.Modules))
		r, err = renter.New(cs, w, tpool, filepath.Join(config.Siad.SiaDir, modules.RenterDir))
		if err != nil {
			return err
		}
		defer func() {
			fmt.Println("Closing renter...")
			err := r.Close()
			if err != nil {
				fmt.Println("Error during renter shutdown:", err)
			}
		}()
	}

	// Create the Sia API
	a := api.New(
		config.Siad.RequiredUserAgent,
		config.APIPassword,
		cs,
		e,
		g,
		h,
		m,
		r,
		tpool,
		w,
	)

	// connect the API to the server
	srv.mux.Handle("/", a)

	// stop the server if a kill signal is caught
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, os.Kill)
	go func() {
		<-sigChan
		fmt.Println("\rCaught stop signal, quitting...")
		srv.Close()
	}()

	// Print a 'startup complete' message.
	startupTime := time.Since(loadStart)
	fmt.Println("Finished loading in", startupTime.Seconds(), "seconds")

	err = <-servErrs
	if err != nil {
		build.Critical(err)
	}

	return nil
}

// startDaemonCmd is a passthrough function for startDaemon.
func startDaemonCmd(cmd *cobra.Command, _ []string) {
	// Create the profiling directory if profiling is enabled.
	if globalConfig.Siad.Profile || build.DEBUG {
		go profile.StartContinuousProfile(globalConfig.Siad.ProfileDir)
	}

	// Start siad. startDaemon will only return when it is shutting down.
	err := startDaemon(globalConfig)
	if err != nil {
		die(err)
	}
}
<|endoftext|>