<commit_before>package hdf5

// #include "hdf5.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"errors"
	"fmt"

	"runtime"
	"unsafe"
)

type Dataspace struct {
	Identifier
}

type SpaceClass C.H5S_class_t

const (
	S_NO_CLASS SpaceClass = -1 // error
	S_SCALAR   SpaceClass = 0  // scalar variable
	S_SIMPLE   SpaceClass = 1  // simple data space
	S_NULL     SpaceClass = 2  // null data space
)

func newDataspace(id C.hid_t) *Dataspace {
	ds := &Dataspace{Identifier{id}}
	runtime.SetFinalizer(ds, (*Dataspace).finalizer)
	return ds
}

// CreateDataspace creates a new dataspace of a specified type.
func CreateDataspace(class SpaceClass) (*Dataspace, error) {
	hid := C.H5Screate(C.H5S_class_t(class))
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	ds := newDataspace(hid)
	return ds, nil
}

func (s *Dataspace) finalizer() {
	err := s.Close()
	if err != nil {
		panic(fmt.Sprintf("error closing dspace: %s", err))
	}
}

// Copy creates an exact copy of a dataspace.
func (s *Dataspace) Copy() (*Dataspace, error) {
	hid := C.H5Scopy(s.id)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	o := newDataspace(hid)
	return o, err
}

// Close releases and terminates access to a dataspace.
func (s *Dataspace) Close() error {
	err := C.H5Sclose(s.id)
	return h5err(err)
}

func (s *Dataspace) Id() int {
	return int(s.id)
}

// CreateSimpleDataspace creates a new simple dataspace and opens it for access.
func CreateSimpleDataspace(dims, maxDims []uint) (*Dataspace, error) {
	var c_dims, c_maxdims *C.hsize_t

	rank := C.int(0)
	if dims != nil {
		rank = C.int(len(dims))
		c_dims = (*C.hsize_t)(unsafe.Pointer(&dims[0]))

	}
	if maxDims != nil {
		rank = C.int(len(maxDims))
		c_maxdims = (*C.hsize_t)(unsafe.Pointer(&maxDims[0]))

	}
	if len(dims) != len(maxDims) && (dims != nil && maxDims != nil) {
		return nil, errors.New("lengths of dims and maxDims do not match")
	}

	hid := C.H5Screate_simple(rank, c_dims, c_maxdims)
	if hid < 0 {
		return nil, fmt.Errorf("failed to create dataspace")
	}
	return newDataspace(hid), nil
}

// IsSimple returns whether a dataspace is a simple dataspace.
func (s *Dataspace) IsSimple() bool {
	return int(C.H5Sis_simple(s.id)) > 0
}

// SetOffset sets the offset of a simple dataspace.
func (s *Dataspace) SetOffset(offset []uint) error {
	rank := len(offset)
	if rank == 0 {
		err := C.H5Soffset_simple(s.id, nil)
		return h5err(err)
	}
	if rank != s.SimpleExtentNDims() {
		err := errors.New("size of offset does not match extent")
		return err
	}

	c_offset := (*C.hssize_t)(unsafe.Pointer(&offset[0]))
	err := C.H5Soffset_simple(s.id, c_offset)
	return h5err(err)
}

// SelectHyperslab creates a subset of the data space.
func (s *Dataspace) SelectHyperslab(offset, stride, count, block []uint) error {
	rank := len(offset)
	if rank == 0 {
		err := C.H5Soffset_simple(s.id, nil)
		return h5err(err)
	}
	if rank != s.SimpleExtentNDims() {
		err := errors.New("size of offset does not match extent")
		return err
	}

	c_offset := (*C.hsize_t)(unsafe.Pointer(&offset[0]))
	c_count := (*C.hsize_t)(unsafe.Pointer(&count[0]))
	var c_stride, c_block *C.hsize_t
	if stride != nil {
		c_stride = (*C.hsize_t)(unsafe.Pointer(&stride[0]))
	}
	if block != nil {
		c_block = (*C.hsize_t)(unsafe.Pointer(&block[0]))
	}
	err := C.H5Sselect_hyperslab(s.id, C.H5S_SELECT_SET, c_offset, c_stride, c_count, c_block)
	return h5err(err)
}

// SimpleExtentDims returns dataspace dimension size and maximum size.
func (s *Dataspace) SimpleExtentDims() (dims, maxdims []uint, err error) {
	rank := s.SimpleExtentNDims()
	dims = make([]uint, rank)
	maxdims = make([]uint, rank)

	c_dims := (*C.hsize_t)(unsafe.Pointer(&dims[0]))
	c_maxdims := (*C.hsize_t)(unsafe.Pointer(&maxdims[0]))
	rc := C.H5Sget_simple_extent_dims(s.id, c_dims, c_maxdims)
	err = h5err(C.herr_t(rc))
	return
}

// SimpleExtentNDims returns the dimensionality of a dataspace.
func (s *Dataspace) SimpleExtentNDims() int {
	return int(C.H5Sget_simple_extent_ndims(s.id))
}

// SimpleExtentNPoints returns the number of elements in a dataspace.
func (s *Dataspace) SimpleExtentNPoints() int {
	return int(C.H5Sget_simple_extent_npoints(s.id))
}

// SimpleExtentType returns the current class of a dataspace.
func (s *Dataspace) SimpleExtentType() SpaceClass {
	return SpaceClass(C.H5Sget_simple_extent_type(s.id))
}
<commit_msg>Fix Close method of Dataspace<commit_after>package hdf5

// #include "hdf5.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"errors"
	"fmt"

	"runtime"
	"unsafe"
)

type Dataspace struct {
	Identifier
}

type SpaceClass C.H5S_class_t

const (
	S_NO_CLASS SpaceClass = -1 // error
	S_SCALAR   SpaceClass = 0  // scalar variable
	S_SIMPLE   SpaceClass = 1  // simple data space
	S_NULL     SpaceClass = 2  // null data space
)

func newDataspace(id C.hid_t) *Dataspace {
	ds := &Dataspace{Identifier{id}}
	runtime.SetFinalizer(ds, (*Dataspace).finalizer)
	return ds
}

// CreateDataspace creates a new dataspace of a specified type.
func CreateDataspace(class SpaceClass) (*Dataspace, error) {
	hid := C.H5Screate(C.H5S_class_t(class))
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	ds := newDataspace(hid)
	return ds, nil
}

func (s *Dataspace) finalizer() {
	err := s.Close()
	if err != nil {
		panic(fmt.Sprintf("error closing dspace: %s", err))
	}
}

// Copy creates an exact copy of a dataspace.
func (s *Dataspace) Copy() (*Dataspace, error) {
	hid := C.H5Scopy(s.id)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	o := newDataspace(hid)
	return o, err
}

// Close releases and terminates access to a dataspace.
func (s *Dataspace) Close() error {
	if s.id > 0 {
		err := h5err(C.H5Sclose(s.id))
		s.id = 0
		return err
	}
	return nil
}

func (s *Dataspace) Id() int {
	return int(s.id)
}

// CreateSimpleDataspace creates a new simple dataspace and opens it for access.
func CreateSimpleDataspace(dims, maxDims []uint) (*Dataspace, error) {
	var c_dims, c_maxdims *C.hsize_t

	rank := C.int(0)
	if dims != nil {
		rank = C.int(len(dims))
		c_dims = (*C.hsize_t)(unsafe.Pointer(&dims[0]))

	}
	if maxDims != nil {
		rank = C.int(len(maxDims))
		c_maxdims = (*C.hsize_t)(unsafe.Pointer(&maxDims[0]))

	}
	if len(dims) != len(maxDims) && (dims != nil && maxDims != nil) {
		return nil, errors.New("lengths of dims and maxDims do not match")
	}

	hid := C.H5Screate_simple(rank, c_dims, c_maxdims)
	if hid < 0 {
		return nil, fmt.Errorf("failed to create dataspace")
	}
	return newDataspace(hid), nil
}

// IsSimple returns whether a dataspace is a simple dataspace.
func (s *Dataspace) IsSimple() bool {
	return int(C.H5Sis_simple(s.id)) > 0
}

// SetOffset sets the offset of a simple dataspace.
func (s *Dataspace) SetOffset(offset []uint) error {
	rank := len(offset)
	if rank == 0 {
		err := C.H5Soffset_simple(s.id, nil)
		return h5err(err)
	}
	if rank != s.SimpleExtentNDims() {
		err := errors.New("size of offset does not match extent")
		return err
	}

	c_offset := (*C.hssize_t)(unsafe.Pointer(&offset[0]))
	err := C.H5Soffset_simple(s.id, c_offset)
	return h5err(err)
}

// SelectHyperslab creates a subset of the data space.
func (s *Dataspace) SelectHyperslab(offset, stride, count, block []uint) error {
	rank := len(offset)
	if rank == 0 {
		err := C.H5Soffset_simple(s.id, nil)
		return h5err(err)
	}
	if rank != s.SimpleExtentNDims() {
		err := errors.New("size of offset does not match extent")
		return err
	}

	c_offset := (*C.hsize_t)(unsafe.Pointer(&offset[0]))
	c_count := (*C.hsize_t)(unsafe.Pointer(&count[0]))
	var c_stride, c_block *C.hsize_t
	if stride != nil {
		c_stride = (*C.hsize_t)(unsafe.Pointer(&stride[0]))
	}
	if block != nil {
		c_block = (*C.hsize_t)(unsafe.Pointer(&block[0]))
	}
	err := C.H5Sselect_hyperslab(s.id, C.H5S_SELECT_SET, c_offset, c_stride, c_count, c_block)
	return h5err(err)
}

// SimpleExtentDims returns dataspace dimension size and maximum size.
func (s *Dataspace) SimpleExtentDims() (dims, maxdims []uint, err error) {
	rank := s.SimpleExtentNDims()
	dims = make([]uint, rank)
	maxdims = make([]uint, rank)

	c_dims := (*C.hsize_t)(unsafe.Pointer(&dims[0]))
	c_maxdims := (*C.hsize_t)(unsafe.Pointer(&maxdims[0]))
	rc := C.H5Sget_simple_extent_dims(s.id, c_dims, c_maxdims)
	err = h5err(C.herr_t(rc))
	return
}

// SimpleExtentNDims returns the dimensionality of a dataspace.
func (s *Dataspace) SimpleExtentNDims() int {
	return int(C.H5Sget_simple_extent_ndims(s.id))
}

// SimpleExtentNPoints returns the number of elements in a dataspace.
func (s *Dataspace) SimpleExtentNPoints() int {
	return int(C.H5Sget_simple_extent_npoints(s.id))
}

// SimpleExtentType returns the current class of a dataspace.
func (s *Dataspace) SimpleExtentType() SpaceClass {
	return SpaceClass(C.H5Sget_simple_extent_type(s.id))
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    // Setup route handlers
    http.HandleFunc("/", index)
    http.HandleFunc("/login", login)
    http.HandleFunc("/home", home)

    // Start the server
    log.Println("Listening on port 5000")
    http.ListenAndServe(":5000", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
    path := r.URL.Path

    if len(path) >= 7 {
        path = r.URL.Path[0:7]
    }

    if path == "/public" {
        public(w, r)
    } else {
        fmt.Fprintf(w, "welcome my han.go")
    }
}

func public(w http.ResponseWriter, r *http.Request) {
    file := r.URL.Path[1:]
    http.ServeFile(w, r, file)
}

func login(w http.ResponseWriter, r *http.Request) {
    if r.Method == "GET" {
        file := "pages/login.html"
        http.ServeFile(w, r, file)

    } else if r.Method == "POST" {
        r.ParseForm()

        username := r.PostForm["username"][0]
        password := r.PostForm["password"][0]

        if username == "maiah" && password == "abc123" {
            http.Redirect(w, r, "/home", http.StatusFound)
        } else {
            fmt.Fprintf(w, "login now, %s", username)
        }
    }
}

func home(w http.ResponseWriter, r *http.Request) {
    file := "pages/home.html"
    http.ServeFile(w, r, file)
}
<commit_msg>Simple handling of successful auth<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	// Setup route handlers
	http.HandleFunc("/", index)
	http.HandleFunc("/login", login)
	http.HandleFunc("/home", home)

	// Start the server
	log.Println("Listening on port 5000")
	http.ListenAndServe(":5000", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path

	if len(path) >= 7 {
		path = r.URL.Path[0:7]
	}

	if path == "/public" {
		public(w, r)
	} else {
		fmt.Fprintf(w, "welcome my han.go")
	}
}

func public(w http.ResponseWriter, r *http.Request) {
	file := r.URL.Path[1:]
	http.ServeFile(w, r, file)
}

func login(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		//file := "pages/login.html"
		//http.ServeFile(w, r, file)

		fmt.Fprintf(w, createLoginForm(), "")

	} else if r.Method == "POST" {
		r.ParseForm()

		username := r.PostForm["username"][0]
		password := r.PostForm["password"][0]

		if username == "gohan" && password == "abc123" {
			http.Redirect(w, r, "/home", http.StatusFound)
		} else {
			fmt.Fprintf(w, createLoginForm(), "Invalid username/passowrd")
		}
	}
}

func home(w http.ResponseWriter, r *http.Request) {
	file := "pages/home.html"
	http.ServeFile(w, r, file)
}

func createLoginForm() string {
	return `<!DOCTYPE html>
            <html>
            <head>
                <title>Login</title>
            </head>
            <body>
                <p>%s</p>
                <form method="POST" action="/login">
                    <input type="text" name="username" placeholder="Username" />
                    <input type="password" name="password" placeholder="Password" />
                    <input type="submit" value="Login" />
                <form>
            </body>
            </html>`
}
<|endoftext|>


<commit_before>package web

import (
	"fmt"
	"mime"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"

	"github.com/gorilla/mux"
	"github.com/zenoss/glog"
	"github.com/zenoss/go-json-rest"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/domain/service"
	"github.com/zenoss/serviced/domain/servicestate"
	"github.com/zenoss/serviced/proxy"
	"github.com/zenoss/serviced/rpc/master"
)

// ServiceConfig is the ui/rest handler for control center
type ServiceConfig struct {
	bindPort    string
	agentPort   string
	zookeepers  []string
	stats       bool
	hostaliases []string
}

// NewServiceConfig creates a new ServiceConfig
func NewServiceConfig(bindPort string, agentPort string, zookeepers []string, stats bool, hostaliases []string) *ServiceConfig {
	cfg := ServiceConfig{bindPort, agentPort, zookeepers, stats, []string{}}
	if len(cfg.agentPort) == 0 {
		cfg.agentPort = "127.0.0.1:4979"
	}
	if len(cfg.zookeepers) == 0 {
		cfg.zookeepers = []string{"127.0.0.1:2181"}
	}
	return &cfg
}

// Serve handles control plane web UI requests and virtual host requests for zenoss web based services.
// The UI server actually listens on port 7878, the uihandler defined here just reverse proxies to it.
// Virutal host routing to zenoss web based services is done by the vhosthandler function.
func (sc *ServiceConfig) Serve() {
	client, err := sc.getClient()
	if err != nil {
		glog.Errorf("Unable to get control plane client: %v", err)
		return
	}

	// Reverse proxy to the web UI server.
	uihandler := func(w http.ResponseWriter, r *http.Request) {
		uiURL, err := url.Parse("http://127.0.0.1:7878")
		if err != nil {
			glog.Errorf("Can't parse UI URL: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui := httputil.NewSingleHostReverseProxy(uiURL)
		if ui == nil {
			glog.Errorf("Can't proxy UI request: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui.ServeHTTP(w, r)
	}

	// Lookup the appropriate virtual host and forward the request to it.
	// TODO: when zookeeper registration is integrated we can be more event
	// driven and only refresh the vhost map when service states change.
	vhosthandler := func(w http.ResponseWriter, r *http.Request) {
		glog.V(1).Infof("vhosthandler handling: %v", r)

		var empty interface{}
		services := []*dao.RunningService{}
		client.GetRunningServices(&empty, &services)

		vhosts := make(map[string][]*servicestate.ServiceState, 0)

		for _, s := range services {
			var svc service.Service

			if err := client.GetService(s.ServiceID, &svc); err != nil {
				glog.Errorf("Can't get service: %s (%v)", s.Id, err)
			}

			vheps := svc.GetServiceVHosts()

			for _, vhep := range vheps {
				for _, vh := range vhep.VHosts {
					svcstates := []*servicestate.ServiceState{}
					if err := client.GetServiceStates(s.ServiceID, &svcstates); err != nil {
						http.Error(w, fmt.Sprintf("can't retrieve service states for %s (%v)", s.ServiceID, err), http.StatusInternalServerError)
						return
					}

					for _, ss := range svcstates {
						vhosts[vh] = append(vhosts[vh], ss)
					}
				}
			}

		}

		glog.V(1).Infof("vhosthandler VHost map: %v", vhosts)

		muxvars := mux.Vars(r)
		svcstates, ok := vhosts[muxvars["subdomain"]]
		if !ok {
			http.Error(w, fmt.Sprintf("service associated with vhost %v is not running", muxvars["subdomain"]), http.StatusNotFound)
			return
		}

		// TODO: implement a more intelligent strategy than "always pick the first one" when more
		// than one service state is mapped to a given virtual host
		for _, svcep := range svcstates[0].Endpoints {
			for _, vh := range svcep.VHosts {
				if vh == muxvars["subdomain"] {
					rpurl := url.URL{Scheme: "http", Host: fmt.Sprintf("%s:%d", svcstates[0].PrivateIP, svcep.PortNumber)}

					glog.V(1).Infof("vhosthandler reverse proxy to: %v", rpurl)

					rp := httputil.NewSingleHostReverseProxy(&rpurl)
					rp.ServeHTTP(w, r)
					return
				}
			}
		}

		http.Error(w, fmt.Sprintf("unrecognized endpoint: %s", muxvars["subdomain"]), http.StatusNotImplemented)
	}

	r := mux.NewRouter()

	if hnm, err := os.Hostname(); err == nil {
		sc.hostaliases = append(sc.hostaliases, hnm)
	}

	cmd := exec.Command("hostname", "--fqdn")
	if hnm, err := cmd.CombinedOutput(); err == nil {
		sc.hostaliases = append(sc.hostaliases, string(hnm[:len(hnm)-1]))
	}

	for _, ha := range sc.hostaliases {
		glog.V(1).Infof("Use vhosthandler for: %s", fmt.Sprintf("{subdomain}.%s", ha))
		r.HandleFunc("/{path:.*}", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
		r.HandleFunc("/", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
	}

	r.HandleFunc("/{path:.*}", uihandler)

	http.Handle("/", r)

	// FIXME: bubble up these errors to the caller
	certfile, err := proxy.TempCertFile()
	if err != nil {
		glog.Fatalf("Could not prepare cert.pem file: %s", err)
	}
	keyfile, err := proxy.TempKeyFile()
	if err != nil {
		glog.Fatalf("Could not prepare key.pem file: %s", err)
	}
	err = http.ListenAndServeTLS(sc.bindPort, certfile, keyfile, nil)
	if err != nil {
		glog.Fatalf("could not setup webserver: %s", err)
	}
}

// ServeUI is a blocking call that runs the UI hander on port :7878
func (sc *ServiceConfig) ServeUI() {
	mime.AddExtensionType(".json", "application/json")
	mime.AddExtensionType(".woff", "application/font-woff")

	handler := rest.ResourceHandler{
		EnableRelaxedContentType: true,
	}

	routes := sc.getRoutes()
	handler.SetRoutes(routes...)

	// FIXME: bubble up these errors to the caller
	if err := http.ListenAndServe(":7878", &handler); err != nil {
		glog.Fatalf("could not setup internal web server: %s", err)
	}
}

var methods = []string{"GET", "POST", "PUT", "DELETE"}

func routeToInternalServiceProxy(path string, target string, routes []rest.Route) []rest.Route {
	targetURL, err := url.Parse(target)
	if err != nil {
		glog.Errorf("Unable to parse proxy target URL: %s", target)
		return routes
	}
	// Wrap the normal http.Handler in a rest.handlerFunc
	handlerFunc := func(w *rest.ResponseWriter, r *rest.Request) {
		proxy := serviced.NewReverseProxy(path, targetURL)
		proxy.ServeHTTP(w.ResponseWriter, r.Request)
	}
	// Add on a glob to match subpaths
	andsubpath := path + "*x"
	for _, method := range methods {
		routes = append(routes, rest.Route{method, path, handlerFunc})
		routes = append(routes, rest.Route{method, andsubpath, handlerFunc})
	}
	return routes
}

func (sc *ServiceConfig) unAuthorizedClient(realfunc handlerClientFunc) handlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		client, err := sc.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			restServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (sc *ServiceConfig) authorizedClient(realfunc handlerClientFunc) handlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		if !loginOK(r) {
			restUnauthorized(w)
			return
		}
		client, err := sc.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			restServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (sc *ServiceConfig) isCollectingStats() handlerFunc {
	if sc.stats {
		return func(w *rest.ResponseWriter, r *rest.Request) {
			w.WriteHeader(http.StatusOK)
		}
	}
	return func(w *rest.ResponseWriter, r *rest.Request) {
		w.WriteHeader(http.StatusNotImplemented)
	}
}

func (sc *ServiceConfig) getClient() (c *serviced.ControlClient, err error) {
	// setup the client
	c, err = serviced.NewControlClient(sc.agentPort)
	if err != nil {
		glog.Fatalf("Could not create a control plane client: %v", err)
	}
	return c, err
}

func (sc *ServiceConfig) newRequestHandler(check checkFunc, realfunc ctxhandlerFunc) handlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		if !check(w, r) {
			return
		}
		reqCtx := newRequestContext(sc)
		defer reqCtx.end()
		realfunc(w, r, reqCtx)
	}
}

func (sc *ServiceConfig) checkAuth(realfunc ctxhandlerFunc) handlerFunc {
	check := func(w *rest.ResponseWriter, r *rest.Request) bool {
		if !loginOK(r) {
			restUnauthorized(w)
			return false
		}
		return true
	}
	return sc.newRequestHandler(check, realfunc)
}

func (sc *ServiceConfig) noAuth(realfunc ctxhandlerFunc) handlerFunc {
	check := func(w *rest.ResponseWriter, r *rest.Request) bool {
		return true
	}
	return sc.newRequestHandler(check, realfunc)
}

type requestContext struct {
	sc     *ServiceConfig
	master *master.Client
}

func newRequestContext(sc *ServiceConfig) *requestContext {
	return &requestContext{sc: sc}
}

func (ctx *requestContext) getMasterClient() (*master.Client, error) {
	if ctx.master == nil {
		c, err := master.NewClient(ctx.sc.agentPort)
		if err != nil {
			glog.Errorf("Could not create a control plane client to %v: %v", ctx.sc.agentPort, err)
			return nil, err
		}
		ctx.master = c
	}
	return ctx.master, nil
}

func (ctx *requestContext) end() error {
	if ctx.master != nil {
		return ctx.master.Close()
	}
	return nil
}

type ctxhandlerFunc func(w *rest.ResponseWriter, r *rest.Request, ctx *requestContext)
type checkFunc func(w *rest.ResponseWriter, r *rest.Request) bool

type getRoutes func(sc *ServiceConfig) []rest.Route
<commit_msg>checkpoint<commit_after>package web

import (
	"fmt"
	"mime"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"time"

	vhost "github.com/daniel-garcia/go-vhost"
	"github.com/gorilla/mux"
	"github.com/zenoss/glog"
	"github.com/zenoss/go-json-rest"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/domain/service"
	"github.com/zenoss/serviced/domain/servicestate"
	"github.com/zenoss/serviced/proxy"
	"github.com/zenoss/serviced/rpc/master"
)

// ServiceConfig is the ui/rest handler for control center
type ServiceConfig struct {
	bindPort    string
	agentPort   string
	zookeepers  []string
	stats       bool
	hostaliases []string
}

// NewServiceConfig creates a new ServiceConfig
func NewServiceConfig(bindPort string, agentPort string, zookeepers []string, stats bool, hostaliases []string) *ServiceConfig {
	cfg := ServiceConfig{bindPort, agentPort, zookeepers, stats, []string{}}
	if len(cfg.agentPort) == 0 {
		cfg.agentPort = "127.0.0.1:4979"
	}
	if len(cfg.zookeepers) == 0 {
		cfg.zookeepers = []string{"127.0.0.1:2181"}
	}
	return &cfg
}

// Serve handles control plane web UI requests and virtual host requests for zenoss web based services.
// The UI server actually listens on port 7878, the uihandler defined here just reverse proxies to it.
// Virutal host routing to zenoss web based services is done by the vhosthandler function.
func (sc *ServiceConfig) Serve() {
	client, err := sc.getClient()
	if err != nil {
		glog.Errorf("Unable to get control plane client: %v", err)
		return
	}

	// Reverse proxy to the web UI server.
	uihandler := func(w http.ResponseWriter, r *http.Request) {
		uiURL, err := url.Parse("http://127.0.0.1:7878")
		if err != nil {
			glog.Errorf("Can't parse UI URL: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui := httputil.NewSingleHostReverseProxy(uiURL)
		if ui == nil {
			glog.Errorf("Can't proxy UI request: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui.ServeHTTP(w, r)
	}

	// Lookup the appropriate virtual host and forward the request to it.
	// TODO: when zookeeper registration is integrated we can be more event
	// driven and only refresh the vhost map when service states change.
	vhosthandler := func(w http.ResponseWriter, r *http.Request) {
		glog.V(1).Infof("vhosthandler handling: %v", r)

		var empty interface{}
		services := []*dao.RunningService{}
		client.GetRunningServices(&empty, &services)

		vhosts := make(map[string][]*servicestate.ServiceState, 0)

		for _, s := range services {
			var svc service.Service

			if err := client.GetService(s.ServiceID, &svc); err != nil {
				glog.Errorf("Can't get service: %s (%v)", s.Id, err)
			}

			vheps := svc.GetServiceVHosts()

			for _, vhep := range vheps {
				for _, vh := range vhep.VHosts {
					svcstates := []*servicestate.ServiceState{}
					if err := client.GetServiceStates(s.ServiceID, &svcstates); err != nil {
						http.Error(w, fmt.Sprintf("can't retrieve service states for %s (%v)", s.ServiceID, err), http.StatusInternalServerError)
						return
					}

					for _, ss := range svcstates {
						vhosts[vh] = append(vhosts[vh], ss)
					}
				}
			}

		}

		glog.V(1).Infof("vhosthandler VHost map: %v", vhosts)

		muxvars := mux.Vars(r)
		svcstates, ok := vhosts[muxvars["subdomain"]]
		if !ok {
			http.Error(w, fmt.Sprintf("service associated with vhost %v is not running", muxvars["subdomain"]), http.StatusNotFound)
			return
		}

		// TODO: implement a more intelligent strategy than "always pick the first one" when more
		// than one service state is mapped to a given virtual host
		for _, svcep := range svcstates[0].Endpoints {
			for _, vh := range svcep.VHosts {
				if vh == muxvars["subdomain"] {
					rpurl := url.URL{Scheme: "http", Host: fmt.Sprintf("%s:%d", svcstates[0].PrivateIP, svcep.PortNumber)}

					glog.V(1).Infof("vhosthandler reverse proxy to: %v", rpurl)

					rp := httputil.NewSingleHostReverseProxy(&rpurl)
					rp.ServeHTTP(w, r)
					return
				}
			}
		}

		http.Error(w, fmt.Sprintf("unrecognized endpoint: %s", muxvars["subdomain"]), http.StatusNotImplemented)
	}

	r := mux.NewRouter()

	if hnm, err := os.Hostname(); err == nil {
		sc.hostaliases = append(sc.hostaliases, hnm)
	}

	cmd := exec.Command("hostname", "--fqdn")
	if hnm, err := cmd.CombinedOutput(); err == nil {
		sc.hostaliases = append(sc.hostaliases, string(hnm[:len(hnm)-1]))
	}

	for _, ha := range sc.hostaliases {
		glog.V(1).Infof("Use vhosthandler for: %s", fmt.Sprintf("{subdomain}.%s", ha))
		r.HandleFunc("/{path:.*}", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
		r.HandleFunc("/", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
	}

	r.HandleFunc("/{path:.*}", uihandler)

	http.Handle("/", r)

	// FIXME: bubble up these errors to the caller
	certfile, err := proxy.TempCertFile()
	if err != nil {
		glog.Fatalf("Could not prepare cert.pem file: %s", err)
	}
	keyfile, err := proxy.TempKeyFile()
	if err != nil {
		glog.Fatalf("Could not prepare key.pem file: %s", err)
	}
	err = http.ListenAndServeTLS(sc.bindPort, certfile, keyfile, nil)
	if err != nil {
		glog.Fatalf("could not setup webserver: %s", err)
	}
}

const muxTimeout = time.Second * 5

type defaultListenerT struct {
	httpMuxer *vhost.HTTPMuxer
}

func (m *defaultListenerT) Accept() (net.Conn, error) {
	conn, err := m.httpMuxer.NextError()
	switch err.(type) {
	case vhost.NotFound:
		return conn, nil
	}
	return conn, err
}

func (m *defaultListenerT) Close() error {
	return m.Close()
}

func (m *defaultListenerT) Addr() net.Addr {
	return &net.TCPAddr{}
}

// ServeUI is a blocking call that runs the UI hander on port :7878
func (sc *ServiceConfig) ServeUI() {

	l, err := net.Listen("tcp", ":7878")
	if err != nil {
		glog.Fatalf("could not listen on :7878, %s", err)
	}

	mux, err := vhost.NewHTTPMuxer(l, muxTimeout)
	if err != nil {
		glog.Fatalf("could not mux: %s", err)
	}
	defaultListener := &defaultListenerT{mux}

	mime.AddExtensionType(".json", "application/json")
	mime.AddExtensionType(".woff", "application/font-woff")

	defaultHandler := rest.ResourceHandler{
		EnableRelaxedContentType: true,
	}

	routes := sc.getRoutes()
	defaultHandler.SetRoutes(routes...)
	server := http.Server{
		Handler: &defaultHandler,
	}
	server.Serve(defaultListener)
}

var methods = []string{"GET", "POST", "PUT", "DELETE"}

func routeToInternalServiceProxy(path string, target string, routes []rest.Route) []rest.Route {
	targetURL, err := url.Parse(target)
	if err != nil {
		glog.Errorf("Unable to parse proxy target URL: %s", target)
		return routes
	}
	// Wrap the normal http.Handler in a rest.handlerFunc
	handlerFunc := func(w *rest.ResponseWriter, r *rest.Request) {
		proxy := serviced.NewReverseProxy(path, targetURL)
		proxy.ServeHTTP(w.ResponseWriter, r.Request)
	}
	// Add on a glob to match subpaths
	andsubpath := path + "*x"
	for _, method := range methods {
		routes = append(routes, rest.Route{method, path, handlerFunc})
		routes = append(routes, rest.Route{method, andsubpath, handlerFunc})
	}
	return routes
}

func (sc *ServiceConfig) unAuthorizedClient(realfunc handlerClientFunc) handlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		client, err := sc.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			restServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (sc *ServiceConfig) authorizedClient(realfunc handlerClientFunc) handlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		if !loginOK(r) {
			restUnauthorized(w)
			return
		}
		client, err := sc.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			restServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (sc *ServiceConfig) isCollectingStats() handlerFunc {
	if sc.stats {
		return func(w *rest.ResponseWriter, r *rest.Request) {
			w.WriteHeader(http.StatusOK)
		}
	}
	return func(w *rest.ResponseWriter, r *rest.Request) {
		w.WriteHeader(http.StatusNotImplemented)
	}
}

func (sc *ServiceConfig) getClient() (c *serviced.ControlClient, err error) {
	// setup the client
	c, err = serviced.NewControlClient(sc.agentPort)
	if err != nil {
		glog.Fatalf("Could not create a control plane client: %v", err)
	}
	return c, err
}

func (sc *ServiceConfig) newRequestHandler(check checkFunc, realfunc ctxhandlerFunc) handlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		if !check(w, r) {
			return
		}
		reqCtx := newRequestContext(sc)
		defer reqCtx.end()
		realfunc(w, r, reqCtx)
	}
}

func (sc *ServiceConfig) checkAuth(realfunc ctxhandlerFunc) handlerFunc {
	check := func(w *rest.ResponseWriter, r *rest.Request) bool {
		if !loginOK(r) {
			restUnauthorized(w)
			return false
		}
		return true
	}
	return sc.newRequestHandler(check, realfunc)
}

func (sc *ServiceConfig) noAuth(realfunc ctxhandlerFunc) handlerFunc {
	check := func(w *rest.ResponseWriter, r *rest.Request) bool {
		return true
	}
	return sc.newRequestHandler(check, realfunc)
}

type requestContext struct {
	sc     *ServiceConfig
	master *master.Client
}

func newRequestContext(sc *ServiceConfig) *requestContext {
	return &requestContext{sc: sc}
}

func (ctx *requestContext) getMasterClient() (*master.Client, error) {
	if ctx.master == nil {
		c, err := master.NewClient(ctx.sc.agentPort)
		if err != nil {
			glog.Errorf("Could not create a control plane client to %v: %v", ctx.sc.agentPort, err)
			return nil, err
		}
		ctx.master = c
	}
	return ctx.master, nil
}

func (ctx *requestContext) end() error {
	if ctx.master != nil {
		return ctx.master.Close()
	}
	return nil
}

type ctxhandlerFunc func(w *rest.ResponseWriter, r *rest.Request, ctx *requestContext)
type checkFunc func(w *rest.ResponseWriter, r *rest.Request) bool

type getRoutes func(sc *ServiceConfig) []rest.Route
<|endoftext|>


<commit_before>package hsm

import "container/list"

type HSMType uint32

const (
    HSMTypeStd = iota
)

type HSM interface {
    Type() HSMType

    Init()
    Dispatch(event Event)

    GetState() State
    IsIn(stateID string) bool

    QInit(targetStateID string)
    QTran(targetStateID string)
}

type StdHSM struct {
    MyType      HSMType
    SourceState State
    State       State
    StateTable  map[string]State
}

// initial must set top as parent
func NewStdHSM(myType HSMType, top, initial State) *StdHSM {
    AssertEqual(TopStateID, top.ID())
    AssertEqual(InitialStateID, initial.ID())
    hsm := &StdHSM{
        MyType:      myType,
        SourceState: initial,
        State:       top,
        StateTable:  make(map[string]State),
    }
    hsm.StateTable[top.ID()] = top
    // setup state table
    hsm.setupStateTable()
    return hsm
}

func (self *StdHSM) Type() HSMType {
    return self.MyType
}

func (self *StdHSM) setupStateTable() {
    for traverse_queue := self.State.Children(); len(traverse_queue) != 0; {
        state := traverse_queue[0]
        traverse_queue = traverse_queue[1:]
        _, ok := self.StateTable[state.ID()]
        AssertFalse(ok)
        self.StateTable[state.ID()] = state
        children := state.Children()
        for _, state := range children {
            traverse_queue = append(traverse_queue, state)
        }
    }
}

func (self *StdHSM) Init() {
    self.Init2(self, StdEvents[EventInit])
}

func (self *StdHSM) Init2(hsm HSM, event Event) {
    // health check
    AssertNotEqual(nil, self.State)
    AssertNotEqual(nil, self.SourceState)
    // check HSM is not executed yet
    AssertEqual(self.StateTable[TopStateID], self.State)
    AssertEqual(self.StateTable[InitialStateID], self.SourceState)
    // save State in a temporary
    s := self.State
    // top-most initial transition
    Trigger(hsm, self.SourceState, event)
    // initial transition must go *one* level deep
    AssertEqual(s, Trigger(hsm, self.State, StdEvents[EventEmpty]))
    // update the termporary
    s = self.State
    // enter the state
    Trigger(hsm, s, StdEvents[EventEntry])
    for Trigger(hsm, s, StdEvents[EventInit]) == nil { // init handled?
        // initial transition must go *one* level deep
        AssertEqual(s, Trigger(hsm, self.State, StdEvents[EventEmpty]))
        s = self.State
        // enter the substate
        Trigger(hsm, s, StdEvents[EventEntry])
    }
    // we are in well-initialized state now
}

func (self *StdHSM) Dispatch(event Event) {
    self.Dispatch2(self, event)
}

func (self *StdHSM) Dispatch2(hsm HSM, event Event) {
    // Use `SourceState' to record the state which handle the event indeed(which
    // could be super, super-super, ... state).
    // `State' would stay unchange pointing at the current(most concrete) state.
    for self.SourceState = self.State; self.SourceState != nil; {
        self.SourceState = Trigger(hsm, self.SourceState, event)
    }
}

func (self *StdHSM) GetState() State {
    return self.State
}
func (self *StdHSM) IsIn(stateID string) bool {
    state := self.StateTable[stateID]
    return self.isIn(state)
}

func (self *StdHSM) isIn(state State) bool {
    // nagivate from current state up to all super state and
    // try to find specified `state'
    for s := self.State; s != nil; s = Trigger(self, self.State, StdEvents[EventEmpty]) {
        if s == state {
            // a match is found
            return true
        }
    }
    // no match found
    return false
}

func (self *StdHSM) QInit(targetStateID string) {
    target := self.LookupState(targetStateID)
    self.qinit(target)
}

func (self *StdHSM) qinit(state State) {
    self.State = state
}

func (self *StdHSM) QTran(targetStateID string) {
    target := self.LookupState(targetStateID)
    self.QTran2(self, target)
}

func (self *StdHSM) LookupState(targetStateID string) State {
    AssertNotEqual(TopStateID, targetStateID)
    target, ok := self.StateTable[targetStateID]
    AssertTrue(ok)
    return target
}

func (self *StdHSM) QTran2(hsm HSM, target State) {
    var p, q, s State
    for s := self.State; s != self.SourceState; {
        // we are about to dereference `s'
        AssertNotEqual(nil, s)
        t := Trigger(hsm, s, StdEvents[EventExit])
        if t != nil {
            s = t
        } else {
            s = Trigger(hsm, s, StdEvents[EventEmpty])
        }
    }

    stateChain := list.New()
    stateChain.Init()

    stateChain.PushBack(nil)
    stateChain.PushBack(target)

    // (a) check `SourceState' == `target' (transition to self)
    if self.SourceState == target {
        Trigger(hsm, self.SourceState, StdEvents[EventExit])
        goto inLCA
    }
    // (b) check `SourceState' == `target.Super()'
    p = Trigger(hsm, target, StdEvents[EventEmpty])
    if self.SourceState == p {
        goto inLCA
    }
    // (c) check `SourceState.Super()' == `target.Super()' (most common)
    q = Trigger(hsm, self.SourceState, StdEvents[EventEmpty])
    if q == p {
        Trigger(hsm, self.SourceState, StdEvents[EventExit])
        goto inLCA
    }
    // (d) check `SourceState.Super()' == `target'
    if q == target {
        Trigger(hsm, self.SourceState, StdEvents[EventExit])
        stateChain.Remove(stateChain.Back())
        goto inLCA
    }
    // (e) check rest of `SourceState' == `target.Super().Super()...'  hierarchy
    stateChain.PushBack(p)
    s = Trigger(hsm, p, StdEvents[EventEmpty])
    for s != nil {
        if self.SourceState == s {
            goto inLCA
        }
        stateChain.PushBack(s)
        s = Trigger(hsm, s, StdEvents[EventEmpty])
    }
    // exit source state
    Trigger(hsm, self.SourceState, StdEvents[EventExit])
    // (f) check rest of `SourceState.Super()' == `target.Super().Super()...'
    for lca := stateChain.Back(); lca != nil; lca = lca.Prev() {
        if q == lca.Value {
            // do not enter the LCA
            stateChain.Remove(stateChain.Back())
            goto inLCA
        }
    }
    // (g) check each `SourceState.Super().Super()...' for target...
    for s = q; s != nil; s = Trigger(hsm, s, StdEvents[EventEmpty]) {
        for lca := stateChain.Back(); lca != nil; lca = lca.Prev() {
            if s == lca.Value {
                stateChain = ListTruncate(stateChain, lca)
                goto inLCA
            }
        }
        Trigger(hsm, s, StdEvents[EventExit])
    }
    // malformed HSM
    AssertTrue(false)
inLCA: // now we are in the LCA of `SourceState' and `target'
    // retrace the entry path in reverse order
    for e := stateChain.Back(); e != nil && e.Value != nil; {
        s, ok := e.Value.(State)
        AssertEqual(true, ok)
        Trigger(hsm, s, StdEvents[EventEntry]) // enter `s' state
        stateChain.Remove(stateChain.Back())
        e = stateChain.Back()
    }
    // update current state
    self.State = target
    for Trigger(hsm, target, StdEvents[EventInit]) == nil {
        // initial transition must go *one* level deep
        AssertEqual(s, Trigger(hsm, self.State, StdEvents[EventEmpty]))
        target = self.State
        Trigger(hsm, target, StdEvents[EventEntry])
    }
}
<commit_msg>add user hsm type<commit_after>package hsm

import "container/list"

type HSMType uint32

const (
    HSMTypeStd = iota
    HSMTypeUser
)

type HSM interface {
    Type() HSMType

    Init()
    Dispatch(event Event)

    GetState() State
    IsIn(stateID string) bool

    QInit(targetStateID string)
    QTran(targetStateID string)
}

type StdHSM struct {
    MyType      HSMType
    SourceState State
    State       State
    StateTable  map[string]State
}

// initial must set top as parent
func NewStdHSM(myType HSMType, top, initial State) *StdHSM {
    AssertEqual(TopStateID, top.ID())
    AssertEqual(InitialStateID, initial.ID())
    hsm := &StdHSM{
        MyType:      myType,
        SourceState: initial,
        State:       top,
        StateTable:  make(map[string]State),
    }
    hsm.StateTable[top.ID()] = top
    // setup state table
    hsm.setupStateTable()
    return hsm
}

func (self *StdHSM) Type() HSMType {
    return self.MyType
}

func (self *StdHSM) setupStateTable() {
    for traverse_queue := self.State.Children(); len(traverse_queue) != 0; {
        state := traverse_queue[0]
        traverse_queue = traverse_queue[1:]
        _, ok := self.StateTable[state.ID()]
        AssertFalse(ok)
        self.StateTable[state.ID()] = state
        children := state.Children()
        for _, state := range children {
            traverse_queue = append(traverse_queue, state)
        }
    }
}

func (self *StdHSM) Init() {
    self.Init2(self, StdEvents[EventInit])
}

func (self *StdHSM) Init2(hsm HSM, event Event) {
    // health check
    AssertNotEqual(nil, self.State)
    AssertNotEqual(nil, self.SourceState)
    // check HSM is not executed yet
    AssertEqual(self.StateTable[TopStateID], self.State)
    AssertEqual(self.StateTable[InitialStateID], self.SourceState)
    // save State in a temporary
    s := self.State
    // top-most initial transition
    Trigger(hsm, self.SourceState, event)
    // initial transition must go *one* level deep
    AssertEqual(s, Trigger(hsm, self.State, StdEvents[EventEmpty]))
    // update the termporary
    s = self.State
    // enter the state
    Trigger(hsm, s, StdEvents[EventEntry])
    for Trigger(hsm, s, StdEvents[EventInit]) == nil { // init handled?
        // initial transition must go *one* level deep
        AssertEqual(s, Trigger(hsm, self.State, StdEvents[EventEmpty]))
        s = self.State
        // enter the substate
        Trigger(hsm, s, StdEvents[EventEntry])
    }
    // we are in well-initialized state now
}

func (self *StdHSM) Dispatch(event Event) {
    self.Dispatch2(self, event)
}

func (self *StdHSM) Dispatch2(hsm HSM, event Event) {
    // Use `SourceState' to record the state which handle the event indeed(which
    // could be super, super-super, ... state).
    // `State' would stay unchange pointing at the current(most concrete) state.
    for self.SourceState = self.State; self.SourceState != nil; {
        self.SourceState = Trigger(hsm, self.SourceState, event)
    }
}

func (self *StdHSM) GetState() State {
    return self.State
}
func (self *StdHSM) IsIn(stateID string) bool {
    state := self.StateTable[stateID]
    return self.isIn(state)
}

func (self *StdHSM) isIn(state State) bool {
    // nagivate from current state up to all super state and
    // try to find specified `state'
    for s := self.State; s != nil; s = Trigger(self, self.State, StdEvents[EventEmpty]) {
        if s == state {
            // a match is found
            return true
        }
    }
    // no match found
    return false
}

func (self *StdHSM) QInit(targetStateID string) {
    target := self.LookupState(targetStateID)
    self.qinit(target)
}

func (self *StdHSM) qinit(state State) {
    self.State = state
}

func (self *StdHSM) QTran(targetStateID string) {
    target := self.LookupState(targetStateID)
    self.QTran2(self, target)
}

func (self *StdHSM) LookupState(targetStateID string) State {
    AssertNotEqual(TopStateID, targetStateID)
    target, ok := self.StateTable[targetStateID]
    AssertTrue(ok)
    return target
}

func (self *StdHSM) QTran2(hsm HSM, target State) {
    var p, q, s State
    for s := self.State; s != self.SourceState; {
        // we are about to dereference `s'
        AssertNotEqual(nil, s)
        t := Trigger(hsm, s, StdEvents[EventExit])
        if t != nil {
            s = t
        } else {
            s = Trigger(hsm, s, StdEvents[EventEmpty])
        }
    }

    stateChain := list.New()
    stateChain.Init()

    stateChain.PushBack(nil)
    stateChain.PushBack(target)

    // (a) check `SourceState' == `target' (transition to self)
    if self.SourceState == target {
        Trigger(hsm, self.SourceState, StdEvents[EventExit])
        goto inLCA
    }
    // (b) check `SourceState' == `target.Super()'
    p = Trigger(hsm, target, StdEvents[EventEmpty])
    if self.SourceState == p {
        goto inLCA
    }
    // (c) check `SourceState.Super()' == `target.Super()' (most common)
    q = Trigger(hsm, self.SourceState, StdEvents[EventEmpty])
    if q == p {
        Trigger(hsm, self.SourceState, StdEvents[EventExit])
        goto inLCA
    }
    // (d) check `SourceState.Super()' == `target'
    if q == target {
        Trigger(hsm, self.SourceState, StdEvents[EventExit])
        stateChain.Remove(stateChain.Back())
        goto inLCA
    }
    // (e) check rest of `SourceState' == `target.Super().Super()...'  hierarchy
    stateChain.PushBack(p)
    s = Trigger(hsm, p, StdEvents[EventEmpty])
    for s != nil {
        if self.SourceState == s {
            goto inLCA
        }
        stateChain.PushBack(s)
        s = Trigger(hsm, s, StdEvents[EventEmpty])
    }
    // exit source state
    Trigger(hsm, self.SourceState, StdEvents[EventExit])
    // (f) check rest of `SourceState.Super()' == `target.Super().Super()...'
    for lca := stateChain.Back(); lca != nil; lca = lca.Prev() {
        if q == lca.Value {
            // do not enter the LCA
            stateChain.Remove(stateChain.Back())
            goto inLCA
        }
    }
    // (g) check each `SourceState.Super().Super()...' for target...
    for s = q; s != nil; s = Trigger(hsm, s, StdEvents[EventEmpty]) {
        for lca := stateChain.Back(); lca != nil; lca = lca.Prev() {
            if s == lca.Value {
                stateChain = ListTruncate(stateChain, lca)
                goto inLCA
            }
        }
        Trigger(hsm, s, StdEvents[EventExit])
    }
    // malformed HSM
    AssertTrue(false)
inLCA: // now we are in the LCA of `SourceState' and `target'
    // retrace the entry path in reverse order
    for e := stateChain.Back(); e != nil && e.Value != nil; {
        s, ok := e.Value.(State)
        AssertEqual(true, ok)
        Trigger(hsm, s, StdEvents[EventEntry]) // enter `s' state
        stateChain.Remove(stateChain.Back())
        e = stateChain.Back()
    }
    // update current state
    self.State = target
    for Trigger(hsm, target, StdEvents[EventInit]) == nil {
        // initial transition must go *one* level deep
        AssertEqual(s, Trigger(hsm, self.State, StdEvents[EventEmpty]))
        target = self.State
        Trigger(hsm, target, StdEvents[EventEntry])
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 Matthew Baird
// 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 elastigo

import (
	"fmt"
	hostpool "github.com/bitly/go-hostpool"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	Version         = "0.0.2"
	DefaultProtocol = "http"
	DefaultDomain   = "localhost"
	DefaultPort     = "9200"
	// A decay duration of zero results in the default behaviour
	DefaultDecayDuration = 0
)

type Conn struct {
	// Maintain these for backwards compatibility
	Protocol       string
	Domain         string
	ClusterDomains []string
	Port           string
	Username       string
	Password       string
	Hosts          []string
	hp             hostpool.HostPool
	once           sync.Once

	// To compute the weighting scores, we perform a weighted average of recent response times,
	// over the course of `DecayDuration`. DecayDuration may be set to 0 to use the default
	// value of 5 minutes. The EpsilonValueCalculator uses this to calculate a score
	// from the weighted average response time.
	DecayDuration time.Duration
}

func NewConn() *Conn {
	return &Conn{
		// Maintain these for backwards compatibility
		Protocol:       DefaultProtocol,
		Domain:         DefaultDomain,
		ClusterDomains: []string{DefaultDomain},
		Port:           DefaultPort,
		DecayDuration:  time.Duration(DefaultDecayDuration * time.Second),
	}
}

func (c *Conn) SetHosts(newhosts []string) {

	// Store the new host list
	c.Hosts = newhosts

	// Reinitialise the host pool Pretty naive as this will nuke the current
	// hostpool, and therefore reset any scoring
	c.initializeHostPool()
}

// Set up the host pool to be used
func (c *Conn) initializeHostPool() {

	// If no hosts are set, fallback to defaults
	if len(c.Hosts) == 0 {
		c.Hosts = append(c.Hosts, fmt.Sprintf("%s:%s", c.Domain, c.Port))
	}

	// Epsilon Greedy is an algorithm that allows HostPool not only to
	// track failure state, but also to learn about "better" options in
	// terms of speed, and to pick from available hosts based on how well
	// they perform. This gives a weighted request rate to better
	// performing hosts, while still distributing requests to all hosts
	// (proportionate to their performance).  The interface is the same as
	// the standard HostPool, but be sure to mark the HostResponse
	// immediately after executing the request to the host, as that will
	// stop the implicitly running request timer.
	//
	// A good overview of Epsilon Greedy is here http://stevehanov.ca/blog/index.php?id=132
	c.hp = hostpool.NewEpsilonGreedy(
		c.Hosts, c.DecayDuration, &hostpool.LinearEpsilonValueCalculator{})
}

func (c *Conn) NewRequest(method, path, query string) (*Request, error) {
	// Setup the hostpool on our first run
	c.once.Do(c.initializeHostPool)

	// Get a host from the host pool
	hr := c.hp.Get()

	// Get the final host and port
	host, portNum := splitHostnamePartsFromHost(hr.Host(), c.Port)

	// Build request
	var uri string
	// If query parameters are provided, the add them to the URL,
	// otherwise, leave them out
	if len(query) > 0 {
		uri = fmt.Sprintf("%s://%s:%s%s?%s", c.Protocol, host, portNum, path, query)
	} else {
		uri = fmt.Sprintf("%s://%s:%s%s", c.Protocol, host, portNum, path)
	}
	req, err := http.NewRequest(method, uri, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", "elasticSearch/"+Version+" ("+runtime.GOOS+"-"+runtime.GOARCH+")")

	if c.Username != "" || c.Password != "" {
		req.SetBasicAuth(c.Username, c.Password)
	}

	newRequest := &Request{
		Request:      req,
		hostResponse: hr,
	}
	return newRequest, nil
}

// Split apart the hostname on colon
// Return the host and a default port if there is no separator
func splitHostnamePartsFromHost(fullHost string, defaultPortNum string) (string, string) {

	h := strings.Split(fullHost, ":")

	if len(h) == 2 {
		return h[0], h[1]
	}

	return h[0], defaultPortNum
}
<commit_msg>making 'port' configurable<commit_after>// Copyright 2013 Matthew Baird
// 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 elastigo

import (
	"fmt"
	hostpool "github.com/bitly/go-hostpool"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	Version         = "0.0.2"
	DefaultProtocol = "http"
	DefaultDomain   = "localhost"
	DefaultPort     = "9200"
	// A decay duration of zero results in the default behaviour
	DefaultDecayDuration = 0
)

type Conn struct {
	// Maintain these for backwards compatibility
	Protocol       string
	Domain         string
	ClusterDomains []string
	Port           string
	Username       string
	Password       string
	Hosts          []string
	hp             hostpool.HostPool
	once           sync.Once

	// To compute the weighting scores, we perform a weighted average of recent response times,
	// over the course of `DecayDuration`. DecayDuration may be set to 0 to use the default
	// value of 5 minutes. The EpsilonValueCalculator uses this to calculate a score
	// from the weighted average response time.
	DecayDuration time.Duration
}

func NewConn() *Conn {
	return &Conn{
		// Maintain these for backwards compatibility
		Protocol:       DefaultProtocol,
		Domain:         DefaultDomain,
		ClusterDomains: []string{DefaultDomain},
		Port:           DefaultPort,
		DecayDuration:  time.Duration(DefaultDecayDuration * time.Second),
	}
}

func (c *Conn) SetPort(port string) {
    c.Port = port
}

func (c *Conn) SetHosts(newhosts []string) {

	// Store the new host list
	c.Hosts = newhosts

	// Reinitialise the host pool Pretty naive as this will nuke the current
	// hostpool, and therefore reset any scoring
	c.initializeHostPool()
}

// Set up the host pool to be used
func (c *Conn) initializeHostPool() {

	// If no hosts are set, fallback to defaults
	if len(c.Hosts) == 0 {
		c.Hosts = append(c.Hosts, fmt.Sprintf("%s:%s", c.Domain, c.Port))
	}

	// Epsilon Greedy is an algorithm that allows HostPool not only to
	// track failure state, but also to learn about "better" options in
	// terms of speed, and to pick from available hosts based on how well
	// they perform. This gives a weighted request rate to better
	// performing hosts, while still distributing requests to all hosts
	// (proportionate to their performance).  The interface is the same as
	// the standard HostPool, but be sure to mark the HostResponse
	// immediately after executing the request to the host, as that will
	// stop the implicitly running request timer.
	//
	// A good overview of Epsilon Greedy is here http://stevehanov.ca/blog/index.php?id=132
	c.hp = hostpool.NewEpsilonGreedy(
		c.Hosts, c.DecayDuration, &hostpool.LinearEpsilonValueCalculator{})
}

func (c *Conn) NewRequest(method, path, query string) (*Request, error) {
	// Setup the hostpool on our first run
	c.once.Do(c.initializeHostPool)

	// Get a host from the host pool
	hr := c.hp.Get()

	// Get the final host and port
	host, portNum := splitHostnamePartsFromHost(hr.Host(), c.Port)

	// Build request
	var uri string
	// If query parameters are provided, the add them to the URL,
	// otherwise, leave them out
	if len(query) > 0 {
		uri = fmt.Sprintf("%s://%s:%s%s?%s", c.Protocol, host, portNum, path, query)
	} else {
		uri = fmt.Sprintf("%s://%s:%s%s", c.Protocol, host, portNum, path)
	}
	req, err := http.NewRequest(method, uri, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", "elasticSearch/"+Version+" ("+runtime.GOOS+"-"+runtime.GOARCH+")")

	if c.Username != "" || c.Password != "" {
		req.SetBasicAuth(c.Username, c.Password)
	}

	newRequest := &Request{
		Request:      req,
		hostResponse: hr,
	}
	return newRequest, nil
}

// Split apart the hostname on colon
// Return the host and a default port if there is no separator
func splitHostnamePartsFromHost(fullHost string, defaultPortNum string) (string, string) {

	h := strings.Split(fullHost, ":")

	if len(h) == 2 {
		return h[0], h[1]
	}

	return h[0], defaultPortNum
}
<|endoftext|>


<commit_before>package hub

type Subject interface {
	Publish(message interface{})
	Subscribe() Subscriber
}

type Subscriber interface {
	Receive() <-chan interface{}
	Close()
}

type Stopper interface {
	Stop() <- chan bool
}

type PubSub interface {
	Stopper
	Subject(key string) Subject
}

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

type EmptyStopper struct {

}

func (s *EmptyStopper) Stop() <- chan bool {
	c := make(chan bool)
	close(c)
	return c
}

<commit_msg>Add comments.<commit_after>package hub

// Subject a pub/sub subject.
type Subject interface {
	Publish(message interface{})
	Subscribe() Subscriber
}

// Subscriber a subscriber to a subject.
// Calling Close() is necessary
// or it will lead to memory leak.
type Subscriber interface {
	Receive() <-chan interface{}
	Close()
}

// Stopper provides Stop() interface for PubSub
// to cleanup and flush suspending messages.
type Stopper interface {
	Stop() <-chan bool
}

// PubSub holds subjects,
// Publishers and Subscribers can communicate
// with each other via the same subject.
type PubSub interface {
	Stopper
	Subject(key string) Subject
}

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

// EmptyStopper provides an empty implementation of Stopper.
type EmptyStopper struct {
}

// Stop does nothing.
func (s *EmptyStopper) Stop() <-chan bool {
	c := make(chan bool)
	close(c)
	return c
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/Preetam/onecontactlink/internal-api/client"
	"github.com/Preetam/onecontactlink/schema"

	"github.com/VividCortex/siesta"
)

func serveGetRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
		return
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	templ.ExecuteTemplate(w, "request", map[string]string{
		"Name": user.Name,
	})
}

func servePostRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	recaptchaResponse := params.String("g-recaptcha-response", "", "reCAPTCHA response")
	nameStr := params.String("name", "", "name")
	emailStr := params.String("email", "", "email")
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
	}

	if *recaptchaResponse == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Bad CAPTCHA",
		})
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	if *nameStr == "" || *emailStr == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "You must provide a name and email address",
		})
		return
	}

	// verify CAPTCHA
	resp, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", url.Values{
		"secret":   []string{RecaptchaSecret},
		"response": []string{*recaptchaResponse},
	})
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	recaptchaAPIResponse := struct {
		Success bool `json:"success"`
	}{}

	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&recaptchaAPIResponse)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	if !recaptchaAPIResponse.Success {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Couldn't verify CAPTCHA. Please try again.",
		})
		return
	}

	toUser := user.ID
	fromUser := 0

	email, err := internalAPIClient.GetEmail(*emailStr)
	if err != nil {
		if err == client.ErrNotFound {
			// Email doesn't exist. Create a new user with the email.
			user, err := internalAPIClient.CreateUser(schema.NewUser(*nameStr, *emailStr))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				templ.ExecuteTemplate(w, "request", map[string]string{
					"Name":    user.Name,
					"Warning": "Something went wrong. Please try again.",
				})
				return
			}
			fromUser = user.ID
		} else {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name":    user.Name,
				"Warning": "Something went wrong. Please try again.",
			})
			return
		}
	} else {
		fromUser = email.User
	}

	requestID, err := internalAPIClient.CreateRequest(fromUser, toUser)
	if err != nil {
		if err == client.ErrConflict {
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name": user.Name,
				"Info": "Looks like you already made this request. If " + user.Name +
					" has already approved your request, we'll send you their latest contact info.",
			})
			// Try to send another request email. This is idempotent.
			internalAPIClient.SendRequestEmail(requestID)
			internalAPIClient.SendContactInfoEmail(requestID)
			return
		}
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	err = internalAPIClient.SendRequestEmail(requestID)
	if err != nil {
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Request sent!",
	})
}

func serveManageRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	actionStr := params.String("action", "", "action")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}

	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	if *actionStr != "approve" && *actionStr != "reject" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Invalid action.",
		})
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract request ID
	requestID := int(linkToken.Data["request"].(float64))
	err = internalAPIClient.ManageRequest(requestID, *actionStr)
	if err != nil {
		if serverErr, ok := err.(client.ServerError); ok {
			if int(serverErr) == http.StatusConflict {
				templ.ExecuteTemplate(w, "invalid", map[string]string{
					"Warning": "Oops, you already responded to this request. We can't change" +
						" anything right now.",
				})
				return
			}
		}

		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		log.Println(err)
		return

	}

	if *actionStr == "approve" {
		err = internalAPIClient.SendContactInfoEmail(requestID)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "invalid", map[string]string{
				"Error": "Something went wrong. Please try again.",
			})
			log.Println(err)
			return
		}

		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Approved! We'll send them an email with your contact information.",
		})
	} else {
		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Rejected. That email won't be able to send you any more requests.",
		})
	}
}

func serveAuth(c siesta.Context, w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	err := params.Parse(r.Form)
	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}
	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract user ID
	userID := int(linkToken.Data["user"].(float64))

	// get user information
	user, err := internalAPIClient.GetUser(userID)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	linkToken.Data["name"] = user.Name

	// Update the expiration and set a cookie
	linkToken.Expires = int(time.Now().Unix() + 86400)

	token, err := tokenCodec.EncodeToken(linkToken)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:     "ocl",
		Value:    token,
		Path:     "/",
		HttpOnly: true,
		Secure:   !DevMode,
	})

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Logged in!",
	})
}
<commit_msg>set cookie domain<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/Preetam/onecontactlink/internal-api/client"
	"github.com/Preetam/onecontactlink/schema"

	"github.com/VividCortex/siesta"
)

func serveGetRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
		return
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	templ.ExecuteTemplate(w, "request", map[string]string{
		"Name": user.Name,
	})
}

func servePostRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	recaptchaResponse := params.String("g-recaptcha-response", "", "reCAPTCHA response")
	nameStr := params.String("name", "", "name")
	emailStr := params.String("email", "", "email")
	linkStr := params.String("link", "", "link code")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid OneContactLink",
		})
		return
	}

	if err != nil || !strings.Contains(*linkStr, "-") {
		invalidLink()
	}
	parts := strings.Split(*linkStr, "-")
	if len(parts) != 2 {
		invalidLink()
	}

	if *recaptchaResponse == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Bad CAPTCHA",
		})
		return
	}

	// get request link
	requestLink, err := internalAPIClient.GetRequestLinkByCode(parts[1])
	if err != nil {
		invalidLink()
		return
	}

	// get user
	user, err := internalAPIClient.GetUser(requestLink.User)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Something went wrong",
		})
		return
	}

	if user.Code != parts[0] {
		invalidLink()
		return
	}

	if *nameStr == "" || *emailStr == "" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "You must provide a name and email address",
		})
		return
	}

	// verify CAPTCHA
	resp, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", url.Values{
		"secret":   []string{RecaptchaSecret},
		"response": []string{*recaptchaResponse},
	})
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	recaptchaAPIResponse := struct {
		Success bool `json:"success"`
	}{}

	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&recaptchaAPIResponse)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	if !recaptchaAPIResponse.Success {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Error": "Couldn't verify CAPTCHA. Please try again.",
		})
		return
	}

	toUser := user.ID
	fromUser := 0

	email, err := internalAPIClient.GetEmail(*emailStr)
	if err != nil {
		if err == client.ErrNotFound {
			// Email doesn't exist. Create a new user with the email.
			user, err := internalAPIClient.CreateUser(schema.NewUser(*nameStr, *emailStr))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				templ.ExecuteTemplate(w, "request", map[string]string{
					"Name":    user.Name,
					"Warning": "Something went wrong. Please try again.",
				})
				return
			}
			fromUser = user.ID
		} else {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name":    user.Name,
				"Warning": "Something went wrong. Please try again.",
			})
			return
		}
	} else {
		fromUser = email.User
	}

	requestID, err := internalAPIClient.CreateRequest(fromUser, toUser)
	if err != nil {
		if err == client.ErrConflict {
			templ.ExecuteTemplate(w, "request", map[string]string{
				"Name": user.Name,
				"Info": "Looks like you already made this request. If " + user.Name +
					" has already approved your request, we'll send you their latest contact info.",
			})
			// Try to send another request email. This is idempotent.
			internalAPIClient.SendRequestEmail(requestID)
			internalAPIClient.SendContactInfoEmail(requestID)
			return
		}
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}
	err = internalAPIClient.SendRequestEmail(requestID)
	if err != nil {
		templ.ExecuteTemplate(w, "request", map[string]string{
			"Name":    user.Name,
			"Warning": "Something went wrong. Please try again.",
		})
		return
	}

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Request sent!",
	})
}

func serveManageRequest(w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	actionStr := params.String("action", "", "action")
	err := params.Parse(r.Form)

	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}

	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	if *actionStr != "approve" && *actionStr != "reject" {
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Invalid action.",
		})
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract request ID
	requestID := int(linkToken.Data["request"].(float64))
	err = internalAPIClient.ManageRequest(requestID, *actionStr)
	if err != nil {
		if serverErr, ok := err.(client.ServerError); ok {
			if int(serverErr) == http.StatusConflict {
				templ.ExecuteTemplate(w, "invalid", map[string]string{
					"Warning": "Oops, you already responded to this request. We can't change" +
						" anything right now.",
				})
				return
			}
		}

		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		log.Println(err)
		return

	}

	if *actionStr == "approve" {
		err = internalAPIClient.SendContactInfoEmail(requestID)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			templ.ExecuteTemplate(w, "invalid", map[string]string{
				"Error": "Something went wrong. Please try again.",
			})
			log.Println(err)
			return
		}

		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Approved! We'll send them an email with your contact information.",
		})
	} else {
		templ.ExecuteTemplate(w, "success", map[string]string{
			"Success": "Rejected. That email won't be able to send you any more requests.",
		})
	}
}

func serveAuth(c siesta.Context, w http.ResponseWriter, r *http.Request) {
	params := &siesta.Params{}
	linkStr := params.String("link", "", "link token")
	err := params.Parse(r.Form)
	invalidLink := func() {
		w.WriteHeader(http.StatusNotFound)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Not a valid link",
		})
		return
	}
	if err != nil {
		invalidLink()
		return
	}

	linkToken, err := tokenCodec.DecodeToken(*linkStr)
	if err != nil {
		invalidLink()
		return
	}

	// Check if token expired
	if linkToken.Expires <= int(time.Now().Unix()) {
		// Token expired.
		w.WriteHeader(http.StatusBadRequest)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "This link has expired.",
		})
		return
	}

	// extract user ID
	userID := int(linkToken.Data["user"].(float64))

	// get user information
	user, err := internalAPIClient.GetUser(userID)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	linkToken.Data["name"] = user.Name

	// Update the expiration and set a cookie
	linkToken.Expires = int(time.Now().Unix() + 86400)

	token, err := tokenCodec.EncodeToken(linkToken)
	if err != nil {
		// Token expired.
		w.WriteHeader(http.StatusInternalServerError)
		templ.ExecuteTemplate(w, "invalid", map[string]string{
			"Error": "Something went wrong. Please try again.",
		})
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:     "ocl",
		Value:    token,
		Domain:   ".onecontact.link",
		Path:     "/",
		HttpOnly: true,
		Secure:   !DevMode,
	})

	templ.ExecuteTemplate(w, "success", map[string]string{
		"Success": "Logged in!",
	})
}
<|endoftext|>


<commit_before><commit_msg>Add AK/SK support for OTC CCE driver<commit_after><|endoftext|>