<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"unsafe"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/version"
)

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(c container, r *http.Request) *devLxdResponse
}

var configGet = devLxdHandler{"/1.0/config", func(c container, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "user.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var configKeyGet = devLxdHandler{"/1.0/config/{key}", func(c container, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ExpandedConfig()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var metadataGet = devLxdHandler{"/1.0/meta-data", func(c container, r *http.Request) *devLxdResponse {
	value := c.ExpandedConfig()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.Name(), c.Name(), value), "raw")
}}

var handlers = []devLxdHandler{
	{"/", func(c container, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	{"/1.0", func(c container, r *http.Request) *devLxdResponse {
		return okResponse(shared.Jmap{"api_version": version.APIVersion}, "json")
	}},
	configGet,
	configKeyGet,
	metadataGet,
	/* TODO: events */
}

func hoistReq(f func(container, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := extractUnderlyingConn(w)
		cred, ok := pidMapper.m[conn]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(cred.pid, d)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		// Access control
		rootUid := int64(0)

		idmapset, err := c.LastIdmapSet()
		if err == nil && idmapset != nil {
			uid, _ := idmapset.ShiftIntoNs(0, 0)
			rootUid = int64(uid)
		}

		if rootUid != cred.uid {
			http.Error(w, "Access denied for non-root user", 401)
			return
		}

		resp := f(c, r)
		if resp.code != http.StatusOK {
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")
			WriteJSON(w, resp.content)
		} else {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func createAndBindDevLxd() (*net.UnixListener, error) {
	sockFile := path.Join(shared.VarPath("devlxd"), "sock")

	/*
	 * If this socket exists, that means a previous lxd died and didn't
	 * clean up after itself. We assume that the LXD is actually dead if we
	 * get this far, since StartDaemon() tries to connect to the actual lxd
	 * socket to make sure that it is actually dead. So, it is safe to
	 * remove it here without any checks.
	 *
	 * Also, it would be nice to SO_REUSEADDR here so we don't have to
	 * delete the socket, but we can't:
	 *   http://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
	 *
	 * Note that this will force clients to reconnect when LXD is restarted.
	 */
	if err := os.Remove(sockFile); err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	unixAddr, err := net.ResolveUnixAddr("unix", sockFile)
	if err != nil {
		return nil, err
	}

	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, err
	}

	if err := os.Chmod(sockFile, 0666); err != nil {
		return nil, err
	}

	return unixl, nil
}

func devLxdServer(d *Daemon) *http.Server {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return &http.Server{
		Handler:   m,
		ConnState: pidMapper.ConnStateHandler,
	}
}

/*
 * Everything below here is the guts of the unix socket bits. Unfortunately,
 * golang's API does not make this easy. What happens is:
 *
 * 1. We install a ConnState listener on the http.Server, which does the
 *    initial unix socket credential exchange. When we get a connection started
 *    event, we use SO_PEERCRED to extract the creds for the socket.
 *
 * 2. We store a map from the connection pointer to the pid for that
 *    connection, so that once the HTTP negotiation occurrs and we get a
 *    ResponseWriter, we know (because we negotiated on the first byte) which
 *    pid the connection belogs to.
 *
 * 3. Regular HTTP negotiation and dispatch occurs via net/http.
 *
 * 4. When rendering the response via ResponseWriter, we match its underlying
 *    connection against what we stored in step (2) to figure out which container
 *    it came from.
 */

/*
 * We keep this in a global so that we can reference it from the server and
 * from our http handlers, since there appears to be no way to pass information
 * around here.
 */
var pidMapper = ConnPidMapper{m: map[*net.UnixConn]*ucred{}}

type ucred struct {
	pid int32
	uid int64
	gid int64
}

type ConnPidMapper struct {
	m map[*net.UnixConn]*ucred
}

func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
	unixConn := conn.(*net.UnixConn)
	switch state {
	case http.StateNew:
		cred, err := getCred(unixConn)
		if err != nil {
			shared.LogDebugf("Error getting ucred for conn %s", err)
		} else {
			m.m[unixConn] = cred
		}
	case http.StateActive:
		return
	case http.StateIdle:
		return
	case http.StateHijacked:
		/*
		 * The "Hijacked" state indicates that the connection has been
		 * taken over from net/http. This is useful for things like
		 * developing websocket libraries, who want to upgrade the
		 * connection to a websocket one, and not use net/http any
		 * more. Whatever the case, we want to forget about it since we
		 * won't see it either.
		 */
		delete(m.m, unixConn)
	case http.StateClosed:
		delete(m.m, unixConn)
	default:
		shared.LogDebugf("Unknown state for connection %s", state)
	}
}

/*
 * I also don't see that golang exports an API to get at the underlying FD, but
 * we need it to get at SO_PEERCRED, so let's grab it.
 */
func extractUnderlyingFd(unixConnPtr *net.UnixConn) int {
	conn := reflect.Indirect(reflect.ValueOf(unixConnPtr))
	netFdPtr := conn.FieldByName("fd")
	netFd := reflect.Indirect(netFdPtr)
	fd := netFd.FieldByName("sysfd")
	return int(fd.Int())
}

func getCred(conn *net.UnixConn) (*ucred, error) {
	fd := extractUnderlyingFd(conn)

	uid, gid, pid, err := getUcred(fd)
	if err != nil {
		return nil, err
	}

	return &ucred{pid, int64(uid), int64(gid)}, nil
}

/*
 * As near as I can tell, there is no nice way of extracting an underlying
 * net.Conn (or in our case, net.UnixConn) from an http.Request or
 * ResponseWriter without hijacking it [1]. Since we want to send and receive
 * unix creds to figure out which container this request came from, we need to
 * do this.
 *
 * [1]: https://groups.google.com/forum/#!topic/golang-nuts/_FWdFXJa6QA
 */
func extractUnderlyingConn(w http.ResponseWriter) *net.UnixConn {
	v := reflect.Indirect(reflect.ValueOf(w))
	connPtr := v.FieldByName("conn")
	conn := reflect.Indirect(connPtr)
	rwc := conn.FieldByName("rwc")

	netConnPtr := (*net.Conn)(unsafe.Pointer(rwc.UnsafeAddr()))
	unixConnPtr := (*netConnPtr).(*net.UnixConn)

	return unixConnPtr
}

var pidNotInContainerErr = fmt.Errorf("pid not in container?")

func findContainerForPid(pid int32, d *Daemon) (container, error) {
	/*
	 * Try and figure out which container a pid is in. There is probably a
	 * better way to do this. Based on rharper's initial performance
	 * metrics, looping over every container and calling newLxdContainer is
	 * expensive, so I wanted to avoid that if possible, so this happens in
	 * a two step process:
	 *
	 * 1. Walk up the process tree until you see something that looks like
	 *    an lxc monitor process and extract its name from there.
	 *
	 * 2. If this fails, it may be that someone did an `lxc exec foo bash`,
	 *    so the process isn't actually a descendant of the container's
	 *    init. In this case we just look through all the containers until
	 *    we find an init with a matching pid namespace. This is probably
	 *    uncommon, so hopefully the slowness won't hurt us.
	 */

	origpid := pid

	for pid > 1 {
		cmdline, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
		if err != nil {
			return nil, err
		}

		if strings.HasPrefix(string(cmdline), "[lxc monitor]") {
			// container names can't have spaces
			parts := strings.Split(string(cmdline), " ")
			name := strings.TrimSuffix(parts[len(parts)-1], "\x00")

			return containerLoadByName(d, name)
		}

		status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
		if err != nil {
			return nil, err
		}

		re := regexp.MustCompile("PPid:\\s*([0-9]*)")
		for _, line := range strings.Split(string(status), "\n") {
			m := re.FindStringSubmatch(line)
			if m != nil && len(m) > 1 {
				result, err := strconv.Atoi(m[1])
				if err != nil {
					return nil, err
				}

				pid = int32(result)
				break
			}
		}
	}

	origPidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", origpid))
	if err != nil {
		return nil, err
	}

	containers, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return nil, err
	}

	for _, container := range containers {
		c, err := containerLoadByName(d, container)
		if err != nil {
			return nil, err
		}

		if !c.IsRunning() {
			continue
		}

		initpid := c.InitPID()
		pidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", initpid))
		if err != nil {
			return nil, err
		}

		if origPidNs == pidNs {
			return c, nil
		}
	}

	return nil, pidNotInContainerErr
}
<commit_msg>Fix extraction of fd from UnixConn with go tip<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"unsafe"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/version"
)

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(c container, r *http.Request) *devLxdResponse
}

var configGet = devLxdHandler{"/1.0/config", func(c container, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "user.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var configKeyGet = devLxdHandler{"/1.0/config/{key}", func(c container, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ExpandedConfig()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var metadataGet = devLxdHandler{"/1.0/meta-data", func(c container, r *http.Request) *devLxdResponse {
	value := c.ExpandedConfig()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.Name(), c.Name(), value), "raw")
}}

var handlers = []devLxdHandler{
	{"/", func(c container, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	{"/1.0", func(c container, r *http.Request) *devLxdResponse {
		return okResponse(shared.Jmap{"api_version": version.APIVersion}, "json")
	}},
	configGet,
	configKeyGet,
	metadataGet,
	/* TODO: events */
}

func hoistReq(f func(container, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := extractUnderlyingConn(w)
		cred, ok := pidMapper.m[conn]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(cred.pid, d)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		// Access control
		rootUid := int64(0)

		idmapset, err := c.LastIdmapSet()
		if err == nil && idmapset != nil {
			uid, _ := idmapset.ShiftIntoNs(0, 0)
			rootUid = int64(uid)
		}

		if rootUid != cred.uid {
			http.Error(w, "Access denied for non-root user", 401)
			return
		}

		resp := f(c, r)
		if resp.code != http.StatusOK {
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")
			WriteJSON(w, resp.content)
		} else {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func createAndBindDevLxd() (*net.UnixListener, error) {
	sockFile := path.Join(shared.VarPath("devlxd"), "sock")

	/*
	 * If this socket exists, that means a previous lxd died and didn't
	 * clean up after itself. We assume that the LXD is actually dead if we
	 * get this far, since StartDaemon() tries to connect to the actual lxd
	 * socket to make sure that it is actually dead. So, it is safe to
	 * remove it here without any checks.
	 *
	 * Also, it would be nice to SO_REUSEADDR here so we don't have to
	 * delete the socket, but we can't:
	 *   http://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
	 *
	 * Note that this will force clients to reconnect when LXD is restarted.
	 */
	if err := os.Remove(sockFile); err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	unixAddr, err := net.ResolveUnixAddr("unix", sockFile)
	if err != nil {
		return nil, err
	}

	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, err
	}

	if err := os.Chmod(sockFile, 0666); err != nil {
		return nil, err
	}

	return unixl, nil
}

func devLxdServer(d *Daemon) *http.Server {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return &http.Server{
		Handler:   m,
		ConnState: pidMapper.ConnStateHandler,
	}
}

/*
 * Everything below here is the guts of the unix socket bits. Unfortunately,
 * golang's API does not make this easy. What happens is:
 *
 * 1. We install a ConnState listener on the http.Server, which does the
 *    initial unix socket credential exchange. When we get a connection started
 *    event, we use SO_PEERCRED to extract the creds for the socket.
 *
 * 2. We store a map from the connection pointer to the pid for that
 *    connection, so that once the HTTP negotiation occurrs and we get a
 *    ResponseWriter, we know (because we negotiated on the first byte) which
 *    pid the connection belogs to.
 *
 * 3. Regular HTTP negotiation and dispatch occurs via net/http.
 *
 * 4. When rendering the response via ResponseWriter, we match its underlying
 *    connection against what we stored in step (2) to figure out which container
 *    it came from.
 */

/*
 * We keep this in a global so that we can reference it from the server and
 * from our http handlers, since there appears to be no way to pass information
 * around here.
 */
var pidMapper = ConnPidMapper{m: map[*net.UnixConn]*ucred{}}

type ucred struct {
	pid int32
	uid int64
	gid int64
}

type ConnPidMapper struct {
	m map[*net.UnixConn]*ucred
}

func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
	unixConn := conn.(*net.UnixConn)
	switch state {
	case http.StateNew:
		cred, err := getCred(unixConn)
		if err != nil {
			shared.LogDebugf("Error getting ucred for conn %s", err)
		} else {
			m.m[unixConn] = cred
		}
	case http.StateActive:
		return
	case http.StateIdle:
		return
	case http.StateHijacked:
		/*
		 * The "Hijacked" state indicates that the connection has been
		 * taken over from net/http. This is useful for things like
		 * developing websocket libraries, who want to upgrade the
		 * connection to a websocket one, and not use net/http any
		 * more. Whatever the case, we want to forget about it since we
		 * won't see it either.
		 */
		delete(m.m, unixConn)
	case http.StateClosed:
		delete(m.m, unixConn)
	default:
		shared.LogDebugf("Unknown state for connection %s", state)
	}
}

/*
 * I also don't see that golang exports an API to get at the underlying FD, but
 * we need it to get at SO_PEERCRED, so let's grab it.
 */
func extractUnderlyingFd(unixConnPtr *net.UnixConn) (int, error) {
	conn := reflect.Indirect(reflect.ValueOf(unixConnPtr))

	netFdPtr := conn.FieldByName("fd")
	if !netFdPtr.IsValid() {
		return -1, fmt.Errorf("Unable to extract fd from net.UnixConn")
	}
	netFd := reflect.Indirect(netFdPtr)

	fd := netFd.FieldByName("sysfd")
	if !fd.IsValid() {
		// Try under the new name
		pfdPtr := netFd.FieldByName("pfd")
		if !pfdPtr.IsValid() {
			return -1, fmt.Errorf("Unable to extract pfd from netFD")
		}
		pfd := reflect.Indirect(pfdPtr)

		fd = pfd.FieldByName("Sysfd")
		if !fd.IsValid() {
			return -1, fmt.Errorf("Unable to extract Sysfd from poll.FD")
		}
	}

	return int(fd.Int()), nil
}

func getCred(conn *net.UnixConn) (*ucred, error) {
	fd, err := extractUnderlyingFd(conn)
	if err != nil {
		return nil, err
	}

	uid, gid, pid, err := getUcred(fd)
	if err != nil {
		return nil, err
	}

	return &ucred{pid, int64(uid), int64(gid)}, nil
}

/*
 * As near as I can tell, there is no nice way of extracting an underlying
 * net.Conn (or in our case, net.UnixConn) from an http.Request or
 * ResponseWriter without hijacking it [1]. Since we want to send and receive
 * unix creds to figure out which container this request came from, we need to
 * do this.
 *
 * [1]: https://groups.google.com/forum/#!topic/golang-nuts/_FWdFXJa6QA
 */
func extractUnderlyingConn(w http.ResponseWriter) *net.UnixConn {
	v := reflect.Indirect(reflect.ValueOf(w))
	connPtr := v.FieldByName("conn")
	conn := reflect.Indirect(connPtr)
	rwc := conn.FieldByName("rwc")

	netConnPtr := (*net.Conn)(unsafe.Pointer(rwc.UnsafeAddr()))
	unixConnPtr := (*netConnPtr).(*net.UnixConn)

	return unixConnPtr
}

var pidNotInContainerErr = fmt.Errorf("pid not in container?")

func findContainerForPid(pid int32, d *Daemon) (container, error) {
	/*
	 * Try and figure out which container a pid is in. There is probably a
	 * better way to do this. Based on rharper's initial performance
	 * metrics, looping over every container and calling newLxdContainer is
	 * expensive, so I wanted to avoid that if possible, so this happens in
	 * a two step process:
	 *
	 * 1. Walk up the process tree until you see something that looks like
	 *    an lxc monitor process and extract its name from there.
	 *
	 * 2. If this fails, it may be that someone did an `lxc exec foo bash`,
	 *    so the process isn't actually a descendant of the container's
	 *    init. In this case we just look through all the containers until
	 *    we find an init with a matching pid namespace. This is probably
	 *    uncommon, so hopefully the slowness won't hurt us.
	 */

	origpid := pid

	for pid > 1 {
		cmdline, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
		if err != nil {
			return nil, err
		}

		if strings.HasPrefix(string(cmdline), "[lxc monitor]") {
			// container names can't have spaces
			parts := strings.Split(string(cmdline), " ")
			name := strings.TrimSuffix(parts[len(parts)-1], "\x00")

			return containerLoadByName(d, name)
		}

		status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
		if err != nil {
			return nil, err
		}

		re := regexp.MustCompile("PPid:\\s*([0-9]*)")
		for _, line := range strings.Split(string(status), "\n") {
			m := re.FindStringSubmatch(line)
			if m != nil && len(m) > 1 {
				result, err := strconv.Atoi(m[1])
				if err != nil {
					return nil, err
				}

				pid = int32(result)
				break
			}
		}
	}

	origPidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", origpid))
	if err != nil {
		return nil, err
	}

	containers, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return nil, err
	}

	for _, container := range containers {
		c, err := containerLoadByName(d, container)
		if err != nil {
			return nil, err
		}

		if !c.IsRunning() {
			continue
		}

		initpid := c.InitPID()
		pidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", initpid))
		if err != nil {
			return nil, err
		}

		if origPidNs == pidNs {
			return c, nil
		}
	}

	return nil, pidNotInContainerErr
}
<|endoftext|>


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

package demo

import (
	"fmt"
	"net/http"
)

var html = `<!DOCTYPE html>

    <title>Shared Contacts Exporter</title>

	%s
	<form action="/contacts" method="post">
	   Domain hosted with Google Apps for Business
	   <input id="app_url" type="url" name="url" placeholder="http://www.example.com" />
	  <input type="submit" value="Set Domain & Export CSV" />
	</form>

	<form enctype="multipart/form-data" action="/import" method="post">
      <input type="file" name="inputfile" />

	   Domain hosted with Google Apps for Business
	   <input id="app_url" type="url" name="url" placeholder="http://www.example.com" />
      <input type="submit" value="Import CSV" />
	</form>

	<form enctype="multipart/form-data" action="/set-action" method="post">
	   Domain hosted with Google Apps for Business
	   <input id="app_url" type="url" name="url" placeholder="http://www.example.com" />
	  <button type="submit" name="what" value="delete">Delete All Contacts</button>
	</form>

`

func init() {
	http.HandleFunc("/", handleHomePage)
}

func handleHomePage(w http.ResponseWriter, r *http.Request) {
	err := r.FormValue("error")
	message := ""

	switch err {
	case "notOnGoogleApps":
		message = `<h4> This URL is not hosted on Google Apps </h4>`
	case "badUrl":
		message = `<h4> An invalid URL was entered </h4>`
	}

	fmt.Fprintf(w, html, message)
}
<commit_msg>Create form Handler<commit_after>// Copyright 2016 Takbok, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package demo

import (
	"fmt"
	"net/http"
)

var html = `<!DOCTYPE html>

    <title>Shared Contacts Exporter</title>

	%s
	<form action="/contacts" method="post">
	   Domain hosted with Google Apps for Business
	   <input id="app_url" type="url" name="url" placeholder="http://www.example.com" />
	  <input type="submit" value="Set Domain & Export CSV" />
	</form>

	<form enctype="multipart/form-data" action="/import" method="post">
      <input type="file" name="inputfile" />

	   Domain hosted with Google Apps for Business
	   <input id="app_url" type="url" name="url" placeholder="http://www.example.com" />
      <input type="submit" value="Import CSV" />
	</form>

	<form enctype="multipart/form-data" action="/set-action" method="post">
	   Domain hosted with Google Apps for Business
	   <input id="app_url" type="url" name="url" placeholder="http://www.example.com" />
	  <button type="submit" name="what" value="delete">Delete All Contacts</button>
	</form>

`

func init() {
	http.HandleFunc("/", handleHomePage)

	http.HandleFunc("/set-action", setAction)
}

func handleHomePage(w http.ResponseWriter, r *http.Request) {
	err := r.FormValue("error")
	message := ""

	switch err {
	case "notOnGoogleApps":
		message = `<h4> This URL is not hosted on Google Apps </h4>`
	case "badUrl":
		message = `<h4> An invalid URL was entered </h4>`
	}

	fmt.Fprintf(w, html, message)
}

func setAction(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Redirect(w, r, "/?error=noDirectAccess", http.StatusTemporaryRedirect)
		return
	}

	url, err := getProperDomainNameFromUrl(r.FormValue("url"))
	if err != nil {
		http.Redirect(w, r, "/?error=badUrl", http.StatusTemporaryRedirect)
		return
	}

	if !isUrlOnGoogleApp(w, r, url) {
		http.Redirect(w, r, "/?error=notOnGoogleApps", http.StatusTemporaryRedirect)
		return
	}

	switch r.FormValue("what") {
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 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.

// This file implements the host side of CGI (being the webserver
// parent process).

// Package cgi implements CGI (Common Gateway Interface) as specified
// in RFC 3875.
//
// Note that using CGI means starting a new process to handle each
// request, which is typically less efficient than using a
// long-running server.  This package is intended primarily for
// compatibility with existing systems.
package cgi

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

var trailingPort = regexp.MustCompile(`:([0-9]+)$`)

var osDefaultInheritEnv = map[string][]string{
	"darwin":  {"DYLD_LIBRARY_PATH"},
	"freebsd": {"LD_LIBRARY_PATH"},
	"hpux":    {"LD_LIBRARY_PATH", "SHLIB_PATH"},
	"irix":    {"LD_LIBRARY_PATH", "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH"},
	"linux":   {"LD_LIBRARY_PATH"},
	"openbsd": {"LD_LIBRARY_PATH"},
	"solaris": {"LD_LIBRARY_PATH", "LD_LIBRARY_PATH_32", "LD_LIBRARY_PATH_64"},
	"windows": {"SystemRoot", "COMSPEC", "PATHEXT", "WINDIR"},
}

// Handler runs an executable in a subprocess with a CGI environment.
type Handler struct {
	Path string // path to the CGI executable
	Root string // root URI prefix of handler or empty for "/"

	// Dir specifies the CGI executable's working directory.
	// If Dir is empty, the base directory of Path is used.
	// If Path has no base directory, the current working
	// directory is used.
	Dir string

	Env        []string    // extra environment variables to set, if any, as "key=value"
	InheritEnv []string    // environment variables to inherit from host, as "key"
	Logger     *log.Logger // optional log for errors or nil to use log.Print
	Args       []string    // optional arguments to pass to child process

	// PathLocationHandler specifies the root http Handler that
	// should handle internal redirects when the CGI process
	// returns a Location header value starting with a "/", as
	// specified in RFC 3875 § 6.3.2. This will likely be
	// http.DefaultServeMux.
	//
	// If nil, a CGI response with a local URI path is instead sent
	// back to the client and not redirected internally.
	PathLocationHandler http.Handler
}

// removeLeadingDuplicates remove leading duplicate in environments.
// It's possible to override environment like following.
//    cgi.Handler{
//      ...
//      Env: []string{"SCRIPT_FILENAME=foo.php"},
//    }
func removeLeadingDuplicates(env []string) (ret []string) {
	n := len(env)
	for i := 0; i < n; i++ {
		e := env[i]
		s := strings.SplitN(e, "=", 2)[0]
		found := false
		for j := i + 1; j < n; j++ {
			if s == strings.SplitN(env[j], "=", 2)[0] {
				found = true
				break
			}
		}
		if !found {
			ret = append(ret, e)
		}
	}
	return
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	root := h.Root
	if root == "" {
		root = "/"
	}

	if len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked" {
		rw.WriteHeader(http.StatusBadRequest)
		rw.Write([]byte("Chunked request bodies are not supported by CGI."))
		return
	}

	pathInfo := req.URL.Path
	if root != "/" && strings.HasPrefix(pathInfo, root) {
		pathInfo = pathInfo[len(root):]
	}

	port := "80"
	if matches := trailingPort.FindStringSubmatch(req.Host); len(matches) != 0 {
		port = matches[1]
	}

	env := []string{
		"SERVER_SOFTWARE=go",
		"SERVER_NAME=" + req.Host,
		"SERVER_PROTOCOL=HTTP/1.1",
		"HTTP_HOST=" + req.Host,
		"GATEWAY_INTERFACE=CGI/1.1",
		"REQUEST_METHOD=" + req.Method,
		"QUERY_STRING=" + req.URL.RawQuery,
		"REQUEST_URI=" + req.URL.RequestURI(),
		"PATH_INFO=" + pathInfo,
		"SCRIPT_NAME=" + root,
		"SCRIPT_FILENAME=" + h.Path,
		"REMOTE_ADDR=" + req.RemoteAddr,
		"REMOTE_HOST=" + req.RemoteAddr,
		"SERVER_PORT=" + port,
	}

	if req.TLS != nil {
		env = append(env, "HTTPS=on")
	}

	for k, v := range req.Header {
		k = strings.Map(upperCaseAndUnderscore, k)
		joinStr := ", "
		if k == "COOKIE" {
			joinStr = "; "
		}
		env = append(env, "HTTP_"+k+"="+strings.Join(v, joinStr))
	}

	if req.ContentLength > 0 {
		env = append(env, fmt.Sprintf("CONTENT_LENGTH=%d", req.ContentLength))
	}
	if ctype := req.Header.Get("Content-Type"); ctype != "" {
		env = append(env, "CONTENT_TYPE="+ctype)
	}

	if h.Env != nil {
		env = append(env, h.Env...)
	}

	envPath := os.Getenv("PATH")
	if envPath == "" {
		envPath = "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin"
	}
	env = append(env, "PATH="+envPath)

	for _, e := range h.InheritEnv {
		if v := os.Getenv(e); v != "" {
			env = append(env, e+"="+v)
		}
	}

	for _, e := range osDefaultInheritEnv[runtime.GOOS] {
		if v := os.Getenv(e); v != "" {
			env = append(env, e+"="+v)
		}
	}

	env = removeLeadingDuplicates(env)

	var cwd, path string
	if h.Dir != "" {
		path = h.Path
		cwd = h.Dir
	} else {
		cwd, path = filepath.Split(h.Path)
	}
	if cwd == "" {
		cwd = "."
	}

	internalError := func(err error) {
		rw.WriteHeader(http.StatusInternalServerError)
		h.printf("CGI error: %v", err)
	}

	cmd := &exec.Cmd{
		Path:   path,
		Args:   append([]string{h.Path}, h.Args...),
		Dir:    cwd,
		Env:    env,
		Stderr: os.Stderr, // for now
	}
	if req.ContentLength != 0 {
		cmd.Stdin = req.Body
	}
	stdoutRead, err := cmd.StdoutPipe()
	if err != nil {
		internalError(err)
		return
	}

	err = cmd.Start()
	if err != nil {
		internalError(err)
		return
	}
	defer cmd.Wait()
	defer stdoutRead.Close()

	linebody, _ := bufio.NewReaderSize(stdoutRead, 1024)
	headers := make(http.Header)
	statusCode := 0
	for {
		line, isPrefix, err := linebody.ReadLine()
		if isPrefix {
			rw.WriteHeader(http.StatusInternalServerError)
			h.printf("cgi: long header line from subprocess.")
			return
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			h.printf("cgi: error reading headers: %v", err)
			return
		}
		if len(line) == 0 {
			break
		}
		parts := strings.SplitN(string(line), ":", 2)
		if len(parts) < 2 {
			h.printf("cgi: bogus header line: %s", string(line))
			continue
		}
		header, val := parts[0], parts[1]
		header = strings.TrimSpace(header)
		val = strings.TrimSpace(val)
		switch {
		case header == "Status":
			if len(val) < 3 {
				h.printf("cgi: bogus status (short): %q", val)
				return
			}
			code, err := strconv.Atoi(val[0:3])
			if err != nil {
				h.printf("cgi: bogus status: %q", val)
				h.printf("cgi: line was %q", line)
				return
			}
			statusCode = code
		default:
			headers.Add(header, val)
		}
	}

	if loc := headers.Get("Location"); loc != "" {
		if strings.HasPrefix(loc, "/") && h.PathLocationHandler != nil {
			h.handleInternalRedirect(rw, req, loc)
			return
		}
		if statusCode == 0 {
			statusCode = http.StatusFound
		}
	}

	if statusCode == 0 {
		statusCode = http.StatusOK
	}

	// Copy headers to rw's headers, after we've decided not to
	// go into handleInternalRedirect, which won't want its rw
	// headers to have been touched.
	for k, vv := range headers {
		for _, v := range vv {
			rw.Header().Add(k, v)
		}
	}

	rw.WriteHeader(statusCode)

	_, err = io.Copy(rw, linebody)
	if err != nil {
		h.printf("cgi: copy error: %v", err)
	}
}

func (h *Handler) printf(format string, v ...interface{}) {
	if h.Logger != nil {
		h.Logger.Printf(format, v...)
	} else {
		log.Printf(format, v...)
	}
}

func (h *Handler) handleInternalRedirect(rw http.ResponseWriter, req *http.Request, path string) {
	url, err := req.URL.Parse(path)
	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		h.printf("cgi: error resolving local URI path %q: %v", path, err)
		return
	}
	// TODO: RFC 3875 isn't clear if only GET is supported, but it
	// suggests so: "Note that any message-body attached to the
	// request (such as for a POST request) may not be available
	// to the resource that is the target of the redirect."  We
	// should do some tests against Apache to see how it handles
	// POST, HEAD, etc. Does the internal redirect get the same
	// method or just GET? What about incoming headers?
	// (e.g. Cookies) Which headers, if any, are copied into the
	// second request?
	newReq := &http.Request{
		Method:     "GET",
		URL:        url,
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
		Host:       url.Host,
		RemoteAddr: req.RemoteAddr,
		TLS:        req.TLS,
	}
	h.PathLocationHandler.ServeHTTP(rw, newReq)
}

func upperCaseAndUnderscore(r rune) rune {
	switch {
	case r >= 'a' && r <= 'z':
		return r - ('a' - 'A')
	case r == '-':
		return '_'
	case r == '=':
		// Maybe not part of the CGI 'spec' but would mess up
		// the environment in any case, as Go represents the
		// environment as a slice of "key=value" strings.
		return '_'
	}
	// TODO: other transformations in spec or practice?
	return r
}
<commit_msg>bufio: drop error return for NewReaderSize and NewWriterSize It complicates the interface unnecessarily. Document this in go1.html. Also update the go/doc Makefile.<commit_after>// Copyright 2011 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.

// This file implements the host side of CGI (being the webserver
// parent process).

// Package cgi implements CGI (Common Gateway Interface) as specified
// in RFC 3875.
//
// Note that using CGI means starting a new process to handle each
// request, which is typically less efficient than using a
// long-running server.  This package is intended primarily for
// compatibility with existing systems.
package cgi

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

var trailingPort = regexp.MustCompile(`:([0-9]+)$`)

var osDefaultInheritEnv = map[string][]string{
	"darwin":  {"DYLD_LIBRARY_PATH"},
	"freebsd": {"LD_LIBRARY_PATH"},
	"hpux":    {"LD_LIBRARY_PATH", "SHLIB_PATH"},
	"irix":    {"LD_LIBRARY_PATH", "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH"},
	"linux":   {"LD_LIBRARY_PATH"},
	"openbsd": {"LD_LIBRARY_PATH"},
	"solaris": {"LD_LIBRARY_PATH", "LD_LIBRARY_PATH_32", "LD_LIBRARY_PATH_64"},
	"windows": {"SystemRoot", "COMSPEC", "PATHEXT", "WINDIR"},
}

// Handler runs an executable in a subprocess with a CGI environment.
type Handler struct {
	Path string // path to the CGI executable
	Root string // root URI prefix of handler or empty for "/"

	// Dir specifies the CGI executable's working directory.
	// If Dir is empty, the base directory of Path is used.
	// If Path has no base directory, the current working
	// directory is used.
	Dir string

	Env        []string    // extra environment variables to set, if any, as "key=value"
	InheritEnv []string    // environment variables to inherit from host, as "key"
	Logger     *log.Logger // optional log for errors or nil to use log.Print
	Args       []string    // optional arguments to pass to child process

	// PathLocationHandler specifies the root http Handler that
	// should handle internal redirects when the CGI process
	// returns a Location header value starting with a "/", as
	// specified in RFC 3875 § 6.3.2. This will likely be
	// http.DefaultServeMux.
	//
	// If nil, a CGI response with a local URI path is instead sent
	// back to the client and not redirected internally.
	PathLocationHandler http.Handler
}

// removeLeadingDuplicates remove leading duplicate in environments.
// It's possible to override environment like following.
//    cgi.Handler{
//      ...
//      Env: []string{"SCRIPT_FILENAME=foo.php"},
//    }
func removeLeadingDuplicates(env []string) (ret []string) {
	n := len(env)
	for i := 0; i < n; i++ {
		e := env[i]
		s := strings.SplitN(e, "=", 2)[0]
		found := false
		for j := i + 1; j < n; j++ {
			if s == strings.SplitN(env[j], "=", 2)[0] {
				found = true
				break
			}
		}
		if !found {
			ret = append(ret, e)
		}
	}
	return
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	root := h.Root
	if root == "" {
		root = "/"
	}

	if len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked" {
		rw.WriteHeader(http.StatusBadRequest)
		rw.Write([]byte("Chunked request bodies are not supported by CGI."))
		return
	}

	pathInfo := req.URL.Path
	if root != "/" && strings.HasPrefix(pathInfo, root) {
		pathInfo = pathInfo[len(root):]
	}

	port := "80"
	if matches := trailingPort.FindStringSubmatch(req.Host); len(matches) != 0 {
		port = matches[1]
	}

	env := []string{
		"SERVER_SOFTWARE=go",
		"SERVER_NAME=" + req.Host,
		"SERVER_PROTOCOL=HTTP/1.1",
		"HTTP_HOST=" + req.Host,
		"GATEWAY_INTERFACE=CGI/1.1",
		"REQUEST_METHOD=" + req.Method,
		"QUERY_STRING=" + req.URL.RawQuery,
		"REQUEST_URI=" + req.URL.RequestURI(),
		"PATH_INFO=" + pathInfo,
		"SCRIPT_NAME=" + root,
		"SCRIPT_FILENAME=" + h.Path,
		"REMOTE_ADDR=" + req.RemoteAddr,
		"REMOTE_HOST=" + req.RemoteAddr,
		"SERVER_PORT=" + port,
	}

	if req.TLS != nil {
		env = append(env, "HTTPS=on")
	}

	for k, v := range req.Header {
		k = strings.Map(upperCaseAndUnderscore, k)
		joinStr := ", "
		if k == "COOKIE" {
			joinStr = "; "
		}
		env = append(env, "HTTP_"+k+"="+strings.Join(v, joinStr))
	}

	if req.ContentLength > 0 {
		env = append(env, fmt.Sprintf("CONTENT_LENGTH=%d", req.ContentLength))
	}
	if ctype := req.Header.Get("Content-Type"); ctype != "" {
		env = append(env, "CONTENT_TYPE="+ctype)
	}

	if h.Env != nil {
		env = append(env, h.Env...)
	}

	envPath := os.Getenv("PATH")
	if envPath == "" {
		envPath = "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin"
	}
	env = append(env, "PATH="+envPath)

	for _, e := range h.InheritEnv {
		if v := os.Getenv(e); v != "" {
			env = append(env, e+"="+v)
		}
	}

	for _, e := range osDefaultInheritEnv[runtime.GOOS] {
		if v := os.Getenv(e); v != "" {
			env = append(env, e+"="+v)
		}
	}

	env = removeLeadingDuplicates(env)

	var cwd, path string
	if h.Dir != "" {
		path = h.Path
		cwd = h.Dir
	} else {
		cwd, path = filepath.Split(h.Path)
	}
	if cwd == "" {
		cwd = "."
	}

	internalError := func(err error) {
		rw.WriteHeader(http.StatusInternalServerError)
		h.printf("CGI error: %v", err)
	}

	cmd := &exec.Cmd{
		Path:   path,
		Args:   append([]string{h.Path}, h.Args...),
		Dir:    cwd,
		Env:    env,
		Stderr: os.Stderr, // for now
	}
	if req.ContentLength != 0 {
		cmd.Stdin = req.Body
	}
	stdoutRead, err := cmd.StdoutPipe()
	if err != nil {
		internalError(err)
		return
	}

	err = cmd.Start()
	if err != nil {
		internalError(err)
		return
	}
	defer cmd.Wait()
	defer stdoutRead.Close()

	linebody := bufio.NewReaderSize(stdoutRead, 1024)
	headers := make(http.Header)
	statusCode := 0
	for {
		line, isPrefix, err := linebody.ReadLine()
		if isPrefix {
			rw.WriteHeader(http.StatusInternalServerError)
			h.printf("cgi: long header line from subprocess.")
			return
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			h.printf("cgi: error reading headers: %v", err)
			return
		}
		if len(line) == 0 {
			break
		}
		parts := strings.SplitN(string(line), ":", 2)
		if len(parts) < 2 {
			h.printf("cgi: bogus header line: %s", string(line))
			continue
		}
		header, val := parts[0], parts[1]
		header = strings.TrimSpace(header)
		val = strings.TrimSpace(val)
		switch {
		case header == "Status":
			if len(val) < 3 {
				h.printf("cgi: bogus status (short): %q", val)
				return
			}
			code, err := strconv.Atoi(val[0:3])
			if err != nil {
				h.printf("cgi: bogus status: %q", val)
				h.printf("cgi: line was %q", line)
				return
			}
			statusCode = code
		default:
			headers.Add(header, val)
		}
	}

	if loc := headers.Get("Location"); loc != "" {
		if strings.HasPrefix(loc, "/") && h.PathLocationHandler != nil {
			h.handleInternalRedirect(rw, req, loc)
			return
		}
		if statusCode == 0 {
			statusCode = http.StatusFound
		}
	}

	if statusCode == 0 {
		statusCode = http.StatusOK
	}

	// Copy headers to rw's headers, after we've decided not to
	// go into handleInternalRedirect, which won't want its rw
	// headers to have been touched.
	for k, vv := range headers {
		for _, v := range vv {
			rw.Header().Add(k, v)
		}
	}

	rw.WriteHeader(statusCode)

	_, err = io.Copy(rw, linebody)
	if err != nil {
		h.printf("cgi: copy error: %v", err)
	}
}

func (h *Handler) printf(format string, v ...interface{}) {
	if h.Logger != nil {
		h.Logger.Printf(format, v...)
	} else {
		log.Printf(format, v...)
	}
}

func (h *Handler) handleInternalRedirect(rw http.ResponseWriter, req *http.Request, path string) {
	url, err := req.URL.Parse(path)
	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		h.printf("cgi: error resolving local URI path %q: %v", path, err)
		return
	}
	// TODO: RFC 3875 isn't clear if only GET is supported, but it
	// suggests so: "Note that any message-body attached to the
	// request (such as for a POST request) may not be available
	// to the resource that is the target of the redirect."  We
	// should do some tests against Apache to see how it handles
	// POST, HEAD, etc. Does the internal redirect get the same
	// method or just GET? What about incoming headers?
	// (e.g. Cookies) Which headers, if any, are copied into the
	// second request?
	newReq := &http.Request{
		Method:     "GET",
		URL:        url,
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
		Host:       url.Host,
		RemoteAddr: req.RemoteAddr,
		TLS:        req.TLS,
	}
	h.PathLocationHandler.ServeHTTP(rw, newReq)
}

func upperCaseAndUnderscore(r rune) rune {
	switch {
	case r >= 'a' && r <= 'z':
		return r - ('a' - 'A')
	case r == '-':
		return '_'
	case r == '=':
		// Maybe not part of the CGI 'spec' but would mess up
		// the environment in any case, as Go represents the
		// environment as a slice of "key=value" strings.
		return '_'
	}
	// TODO: other transformations in spec or practice?
	return r
}
<|endoftext|>


<commit_before>package main

import "net/http"

// Middleware to add HSTS header
func hstsHandler(fn http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
        fn.ServeHTTP(w, r)
    })
}<commit_msg>Fix code<commit_after>package main

import "net/http"

// Middleware to add HSTS header
func hstsHandler(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
        h.ServeHTTP(w, r)
    })
}<|endoftext|>


<commit_before>package integration

import (
	"testing"

	"github.com/cpuguy83/drax"
	"github.com/cpuguy83/drax/api/errors"
	"github.com/docker/distribution/registry/api/errcode"
)

func TestStoreGetPutDelete(t *testing.T) {
	nodes, err := newTestCluster(3, "test")
	if err != nil {
		t.Fatal(err)
	}
	defer cleanupNodes(nodes)

	if len(nodes) != 3 {
		t.Fatalf("expected 3 nodes, got %d", len(nodes))
	}

	// Test key doesn't exist
	for _, n := range nodes {
		_, err := n.KVStore().Get("hello")
		if err == nil {
			t.Fatalf("expected error, got none")
		}

		switch e := err.(type) {
		case errcode.Error:
			if e.Code != errors.StoreKeyNotFound {
				t.Fatalf("expected %q, got %q", errors.StoreKeyNotFound.Error(), err)
			}
		default:
			if err != drax.ErrKeyNotFound {
				t.Fatalf("exepcted %v, got %v", drax.ErrKeyNotFound, err)
			}
		}
	}

	if err := nodes[0].KVStore().Put("hello", []byte("world"), nil); err != nil {
		t.Fatal(err)
	}

	for i, n := range nodes {
		kv, err := n.KVStore().Get("hello")
		if err != nil {
			t.Fatalf("fail on node %d: %v", i+1, err)
		}

		if string(kv.Value) != "world" {
			t.Fatalf("expected world, got %s", kv.Value)
		}
	}

	if err := nodes[0].KVStore().Delete("hello"); err != nil {
		t.Fatal(err)
	}

	// Test key doesn't exist
	for _, n := range nodes {
		_, err := n.KVStore().Get("hello")
		if err == nil {
			t.Fatalf("expected error, got none")
		}

		switch e := err.(type) {
		case errcode.Error:
			if e.Code != errors.StoreKeyNotFound {
				t.Fatalf("expected %q, got %q", errors.StoreKeyNotFound.Error(), err)
			}
		default:
			if err != drax.ErrKeyNotFound {
				t.Fatalf("exepcted %v, got %v", drax.ErrKeyNotFound, err)
			}
		}
	}
}
<commit_msg>add watch integration tests<commit_after>package integration

import (
	"testing"
	"time"

	"github.com/cpuguy83/drax"
	"github.com/cpuguy83/drax/api/errors"
	"github.com/docker/distribution/registry/api/errcode"
)

func TestStoreGetPutDelete(t *testing.T) {
	nodes, err := newTestCluster(3, "test")
	if err != nil {
		t.Fatal(err)
	}
	defer cleanupNodes(nodes)

	if len(nodes) != 3 {
		t.Fatalf("expected 3 nodes, got %d", len(nodes))
	}

	// Test key doesn't exist
	for _, n := range nodes {
		_, err := n.KVStore().Get("hello")
		if err == nil {
			t.Fatalf("expected error, got none")
		}

		switch e := err.(type) {
		case errcode.Error:
			if e.Code != errors.StoreKeyNotFound {
				t.Fatalf("expected %q, got %q", errors.StoreKeyNotFound.Error(), err)
			}
		default:
			if err != drax.ErrKeyNotFound {
				t.Fatalf("exepcted %v, got %v", drax.ErrKeyNotFound, err)
			}
		}
	}

	if err := nodes[0].KVStore().Put("hello", []byte("world"), nil); err != nil {
		t.Fatal(err)
	}

	for i, n := range nodes {
		kv, err := n.KVStore().Get("hello")
		if err != nil {
			t.Fatalf("fail on node %d: %v", i+1, err)
		}

		if string(kv.Value) != "world" {
			t.Fatalf("expected world, got %s", kv.Value)
		}
	}

	if err := nodes[0].KVStore().Delete("hello"); err != nil {
		t.Fatal(err)
	}

	// Test key doesn't exist
	for _, n := range nodes {
		_, err := n.KVStore().Get("hello")
		if err == nil {
			t.Fatalf("expected error, got none")
		}

		switch e := err.(type) {
		case errcode.Error:
			if e.Code != errors.StoreKeyNotFound {
				t.Fatalf("expected %q, got %q", errors.StoreKeyNotFound.Error(), err)
			}
		default:
			if err != drax.ErrKeyNotFound {
				t.Fatalf("exepcted %v, got %v", drax.ErrKeyNotFound, err)
			}
		}
	}
}

func TestStoreWatch(t *testing.T) {
	nodes, err := newTestCluster(3, "test")
	if err != nil {
		t.Fatal(err)
	}
	defer cleanupNodes(nodes)

	if len(nodes) != 3 {
		t.Fatalf("expected 3 nodes, got %d", len(nodes))
	}

	chStop := make(chan struct{})
	chKV, err := nodes[0].KVStore().Watch("hello", chStop)
	if err != nil {
		t.Fatal(err)
	}

	if err := nodes[1].KVStore().Put("hello", []byte("world"), nil); err != nil {
		t.Fatal(err)
	}

	select {
	case kv, open := <-chKV:
		if !open {
			t.Fatal("watch chan should be open")
		}
		if kv.Key != "hello" || string(kv.Value) != "world" {
			t.Fatalf("got wrong k/v pair: %s=%s", kv.Key, string(kv.Value))
		}
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for watch key")
	}

	if err := nodes[2].KVStore().Put("hello", []byte("world2"), nil); err != nil {
		t.Fatal(err)
	}
	select {
	case kv, open := <-chKV:
		if !open {
			t.Fatal("watch chan should be open")
		}
		if kv.Key != "hello" || string(kv.Value) != "world2" {
			t.Fatalf("got wrong k/v pair: %s=%s", kv.Key, string(kv.Value))
		}
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for watch key")
	}

	close(chStop)
	if err := nodes[2].KVStore().Put("hello", []byte("world3"), nil); err != nil {
		t.Fatal(err)
	}

	select {
	case _, open := <-chKV:
		if open {
			t.Fatalf("exepected watch chan to be closeD")
		}
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for watch to close")
	}
}

func TestStoreWatchDelete(t *testing.T) {
	nodes, err := newTestCluster(3, "test")
	if err != nil {
		t.Fatal(err)
	}
	defer cleanupNodes(nodes)

	if len(nodes) != 3 {
		t.Fatalf("expected 3 nodes, got %d", len(nodes))
	}

	chKV, err := nodes[0].KVStore().Watch("hello", nil)
	if err != nil {
		t.Fatal(err)
	}

	if err := nodes[1].KVStore().Put("hello", []byte("world"), nil); err != nil {
		t.Fatal(err)
	}

	select {
	case kv, open := <-chKV:
		if !open {
			t.Fatal("watch chan should be open")
		}
		if kv.Key != "hello" || string(kv.Value) != "world" {
			t.Fatalf("got wrong k/v pair: %s=%s", kv.Key, string(kv.Value))
		}
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for watch key")
	}

	if err := nodes[2].KVStore().Delete("hello"); err != nil {
		t.Fatal(err)
	}

	select {
	case _, open := <-chKV:
		if open {
			t.Fatalf("exepected watch chan to be closeD")
		}
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for watch to close")
	}
}
<|endoftext|>


<commit_before>package golaunch

import (
	"errors"
	"sync"
	"time"

	"log"

	"github.com/currantlabs/gatt"
)

var (
	// Bluetooth device name of the Launch
	pName = "Launch"
	// The main service UUID for the Launch
	serviceID = gatt.MustParseUUID("88f80580-0000-01e6-aace-0002a5d5c51b")
	// Command characteristic (Write)
	cmdCharID = gatt.MustParseUUID("88f80581-0000-01e6-aace-0002a5d5c51b")
	// Touch events characteristic (Notifications)
	touchCharID = gatt.MustParseUUID("88f80582-0000-01e6-aace-0002a5d5c51b")
	// Mode? characteristic (Read/Write)
	modeCharID = gatt.MustParseUUID("88f80583-0000-01e6-aace-0002a5d5c51b")
)

var (
	// ErrConnectionTimeout is the error returned when a connection could
	// not be made in time.
	ErrConnectionTimeout = errors.New("connection timeout")
	// ErrWriteTimeout is the error returned when a write with response did
	// not complete in time.
	ErrWriteTimeout = errors.New("write timeout")
	// ErrInit is the error returned when there was an issue initializing
	// the Launch.
	ErrInit = errors.New("initialization error")
	// ErrUnknownMode is the error returned when someone wants to send a
	// (yet) unknown mode.
	ErrUnknownMode = errors.New("unknown mode")
)

var (
	// Time for the device to get ready before sending data.
	readyTime = time.Second * 2
	// Connection timeout.
	connectionTimeout = time.Second * 10
	// Write timeout on with response writes.
	writeTimeout = time.Second * 1
	// Minimum amount of time between write operations.
	threshold = time.Millisecond * 100
	// Amount of write events to buffer before blocking.
	writeBufferSize = 10
)

const (
	// modeReadValuesAsBytes will cause the Launch to read position/speed
	// values as bytes instead of BCD. Also seems to fix it from having
	// spasms.
	modeReadValuesAsBytes = 0x00
)

// Launch interface represents a device that can move like a Launch.
type Launch interface {
	// Connect will (re)connect to the Launch.
	Connect() error

	// Disconnect will disconnect to the Launch.
	Disconnect()

	// Move moves to position at speed in percent.
	Move(position, speed int)

	// HandleDisconnect registers a function to call when a device disconnects
	HandleDisconnect(f func())
}

// NewLaunch creates and returns a Launch client that can be used to
// communicate.
func NewLaunch() Launch {
	l := &launch{
		mutex:          new(sync.Mutex),
		cmdDiscovered:  make(chan bool, 1),
		modeDiscovered: make(chan bool, 1),
		stopWriting:    make(chan bool, 1),
		wbuffer:        make(chan [2]byte, writeBufferSize),
		limiter:        time.Tick(threshold),
	}
	return l
}

// launch is the structure used to manage the connection to a Launch.
type launch struct {
	mutex *sync.Mutex
	p     gatt.Peripheral

	cmdDiscovered  chan bool
	cmd            *gatt.Characteristic
	modeDiscovered chan bool
	mode           *gatt.Characteristic

	stopWriting chan bool
	wbuffer     chan [2]byte
	limiter     <-chan time.Time

	disconnectFunc func()
}

// Connect initializes configured Bluetooth device and creates a connection to
// a Launch.
func (l *launch) Connect() error {
	// Open Bluetooth device
	l.mutex.Lock()
	d, err := gatt.NewDevice(DefaultClientOptions...)
	if err != nil {
		l.mutex.Unlock()
		return err
	}

	setupComplete := make(chan bool, 1)
	go func() {

		// Register Bluetooth event handlers
		d.Handle(
			gatt.PeripheralDiscovered(l.onPeripheralDiscovered),
			gatt.PeripheralConnected(l.onConnected),
			gatt.PeripheralDisconnected(l.onDisconnected),
		)
		// Start
		d.Init(onStateChanged)

		// Make sure all characteristics are discovered before starting
		for i := 0; i < 2; i++ {
			select {
			case <-l.cmdDiscovered:
			case <-l.modeDiscovered:
			}
		}
		setupComplete <- true
	}()

	select {
	case <-setupComplete:
		l.mutex.Unlock()
		// Give the Launch a little bit of time to get ready
		<-time.After(readyTime)

		// Put the Launch into a mode that it will actually work well
		err := l.writeMode(modeReadValuesAsBytes)
		if err != nil {
			return ErrInit
		}

		// Start the goroutine that will write commands
		go l.writeFromBuffer()

		return nil
	case <-time.After(connectionTimeout):
		l.mutex.Unlock()
		return ErrConnectionTimeout
	}
}

// Disconnect cancels the connection with the Launch.
func (l *launch) Disconnect() {
	if l.p != nil {
		if d := l.p.Device(); d != nil {
			d.CancelConnection(l.p)
		}
	}
}

// writeMode will send specified mode to the Launch. Only support putting
// the Launch in "read values as bytes", so it interprets values on the command
// channel as bytes instead of binary coded decimals.
func (l *launch) writeMode(c byte) error {
	errChan := make(chan error, 1)
	go func() {
		if c == modeReadValuesAsBytes {
			l.mutex.Lock()
			err := l.p.WriteCharacteristic(l.mode, []byte{c}, false)
			l.mutex.Unlock()
			errChan <- err
		}
		errChan <- nil
	}()
	select {
	case err := <-errChan:
		return err
	case <-time.After(connectionTimeout):
		return ErrWriteTimeout
	}
}

// writeFromBuffer sends commands to the Launch that are stored in the write
// buffer. To stop this function send true on the l.stopWriting channel.
func (l *launch) writeFromBuffer() {
	for {
		select {
		case stop := <-l.stopWriting:
			if stop == true {
				return
			}
		case b := <-l.wbuffer:
			// Limit amount of writes to avoid disconnects
			<-l.limiter
			l.mutex.Lock()
			l.p.WriteCharacteristic(l.cmd, b[:], true)
			l.mutex.Unlock()
		}
	}
}

// Move will move to the specified position at the desired speed.
// Position and speed are specified in percent.
func (l *launch) Move(position, speed int) {
	// Make sure we remain in the limits
	if position < 0 {
		position = 0
	} else if position > 99 {
		position = 99
	}

	// Don't go below 20% as very slow speeds seem to crash the Launch
	if speed < 20 {
		speed = 20
	} else if speed > 99 {
		speed = 99
	}

	data := [2]byte{byte(position), byte(speed)}
	l.wbuffer <- data
}

// discoverCharacteristics finds and stores the Launch characteristics for the
// given service.
func (l *launch) discoverCharacteristics(service *gatt.Service) error {
	cs, err := l.p.DiscoverCharacteristics([]gatt.UUID{
		modeCharID,
		cmdCharID,
	}, service)
	if err != nil {
		return err
	}

	for _, c := range cs {
		if c.UUID().Equal(cmdCharID) {
			log.Println("Launch command characteristic found")

			// Store command characteristic
			l.cmd = c
			l.modeDiscovered <- true
		} else if c.UUID().Equal(modeCharID) {
			log.Println("Launch mode characteristic found")

			// Store mode characteristic
			l.mode = c
			l.cmdDiscovered <- true
		}
	}
	return nil
}

// onConnect stores the connected peripheral in *launch state and discovers and
// stores characteristics.
func (l *launch) onConnected(p gatt.Peripheral, err error) {
	log.Printf("Launch connected")
	// Store peripheral
	l.p = p

	services, err := p.DiscoverServices([]gatt.UUID{serviceID})
	if err != nil {
		return
	}

	for _, service := range services {
		if service.UUID().Equal(serviceID) {
			log.Println("Launch service found")
			l.discoverCharacteristics(service)
		}
	}
}

// HandleDisconnect registers a function that is called when the Launch
// disconnects.
func (l *launch) HandleDisconnect(f func()) {
	l.disconnectFunc = f
}

// onDisconnected will clean up stop scanning and connect if it's a Launch.
func (l *launch) onDisconnected(p gatt.Peripheral, err error) {
	log.Println("Launch disconnected")

	select {
	case l.stopWriting <- true:
	default:
	}

	p.Device().Stop()
	<-time.After(time.Second * 2)

	if l.disconnectFunc != nil {
		l.disconnectFunc()
	}
}

// onPeripheralDiscovered will stop scanning and connect if it's a Launch.
func (l *launch) onPeripheralDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
	if a.LocalName == pName {
		// Store peripheral
		l.p = p
		log.Printf("Launch discovered")
		p.Device().StopScanning()
		p.Device().Connect(p)
	}
}

// onStateChange will start scanning for the Launch on power on.
func onStateChanged(d gatt.Device, s gatt.State) {
	switch s {
	case gatt.StatePoweredOn:
		// Scan for Launch
		d.Scan([]gatt.UUID{serviceID}, false)
		return
	default:
		d.StopScanning()
	}
}
<commit_msg>Improve handling disconnects<commit_after>package golaunch

import (
	"errors"
	"sync"
	"time"

	"log"

	"github.com/currantlabs/gatt"
)

var (
	// Bluetooth device name of the Launch
	pName = "Launch"
	// The main service UUID for the Launch
	serviceID = gatt.MustParseUUID("88f80580-0000-01e6-aace-0002a5d5c51b")
	// Command characteristic (Write)
	cmdCharID = gatt.MustParseUUID("88f80581-0000-01e6-aace-0002a5d5c51b")
	// Touch events characteristic (Notifications)
	touchCharID = gatt.MustParseUUID("88f80582-0000-01e6-aace-0002a5d5c51b")
	// Mode? characteristic (Read/Write)
	modeCharID = gatt.MustParseUUID("88f80583-0000-01e6-aace-0002a5d5c51b")
)

var (
	// ErrConnectionTimeout is the error returned when a connection could
	// not be made in time.
	ErrConnectionTimeout = errors.New("connection timeout")
	// ErrWriteTimeout is the error returned when a write with response did
	// not complete in time.
	ErrWriteTimeout = errors.New("write timeout")
	// ErrDisconnected is the error returned when ther is no connection.
	ErrDisconnected = errors.New("disconnected")
	// ErrInit is the error returned when there was an issue initializing
	// the Launch.
	ErrInit = errors.New("initialization error")
	// ErrUnknownMode is the error returned when someone wants to send a
	// (yet) unknown mode.
	ErrUnknownMode = errors.New("unknown mode")
)

var (
	// Time for the device to get ready before sending data.
	readyTime = time.Second * 2
	// Connection timeout.
	connectionTimeout = time.Second * 10
	// Write timeout on with response writes.
	writeTimeout = time.Second * 1
	// Minimum amount of time between write operations.
	threshold = time.Millisecond * 100
	// Amount of write events to buffer before blocking.
	writeBufferSize = 10
)

const (
	// modeReadValuesAsBytes will cause the Launch to read position/speed
	// values as bytes instead of BCD. Also seems to fix it from having
	// spasms.
	modeReadValuesAsBytes = 0x00
)

// Launch interface represents a device that can move like a Launch.
type Launch interface {
	// Connect will (re)connect to the Launch.
	Connect() error

	// Disconnect will disconnect to the Launch.
	Disconnect()

	// Move moves to position at speed in percent.
	Move(position, speed int)

	// HandleDisconnect registers a function to call when a device disconnects
	HandleDisconnect(f func())
}

// NewLaunch creates and returns a Launch client that can be used to
// communicate.
func NewLaunch() Launch {
	l := &launch{
		mutex:          new(sync.Mutex),
		cmdDiscovered:  make(chan bool, 1),
		modeDiscovered: make(chan bool, 1),
		stopWriting:    make(chan bool, 1),
		wbuffer:        make(chan [2]byte, writeBufferSize),
		limiter:        time.Tick(threshold),
	}
	return l
}

// launch is the structure used to manage the connection to a Launch.
type launch struct {
	connected bool
	mutex     *sync.Mutex
	p         gatt.Peripheral

	cmdDiscovered  chan bool
	cmd            *gatt.Characteristic
	modeDiscovered chan bool
	mode           *gatt.Characteristic

	stopWriting chan bool
	wbuffer     chan [2]byte
	limiter     <-chan time.Time

	disconnectFunc func()
}

// Connect initializes configured Bluetooth device and creates a connection to
// a Launch.
func (l *launch) Connect() error {
	if l.connected {
		return nil
	}
	l.mutex.Lock()
	defer l.mutex.Unlock()

	// Open Bluetooth device
	d, err := gatt.NewDevice(DefaultClientOptions...)
	if err != nil {
		return err
	}

	setupComplete := make(chan bool, 1)
	go func() {
		// Register Bluetooth event handlers
		d.Handle(
			gatt.PeripheralDiscovered(l.onPeripheralDiscovered),
			gatt.PeripheralConnected(l.onConnected),
			gatt.PeripheralDisconnected(l.onDisconnected),
		)
		// Start
		d.Init(onStateChanged)

		// Make sure all characteristics are discovered before starting
		for i := 0; i < 2; i++ {
			select {
			case <-l.cmdDiscovered:
			case <-l.modeDiscovered:
			}
		}
		setupComplete <- true
	}()

	select {
	case <-setupComplete:
		l.connected = true

		// Give the Launch a little bit of time to get ready
		<-time.After(readyTime)

		// Put the Launch into a mode that it will actually work well
		err := l.writeMode(modeReadValuesAsBytes)
		if err != nil {
			return ErrInit
		}

		// Start the goroutine that will write commands
		go l.writeFromBuffer()

		return nil
	case <-time.After(connectionTimeout):
		return ErrConnectionTimeout
	}
}

// Disconnect cancels the connection with the Launch.
func (l *launch) Disconnect() {
	if l.connected {
		if d := l.p.Device(); d != nil {
			d.CancelConnection(l.p)
		}
	}
}

// writeMode will send specified mode to the Launch. Only support putting
// the Launch in "read values as bytes", so it interprets values on the command
// channel as bytes instead of binary coded decimals.
func (l *launch) writeMode(c byte) error {
	if !l.connected {
		return ErrDisconnected
	}
	if c == modeReadValuesAsBytes {
		err := l.p.WriteCharacteristic(l.mode, []byte{c}, false)
		return err
	}
	return ErrUnknownMode
}

// writeFromBuffer sends commands to the Launch that are stored in the write
// buffer. To stop this function send true on the l.stopWriting channel.
func (l *launch) writeFromBuffer() {
	for {
		select {
		case stop := <-l.stopWriting:
			if stop == true {
				return
			}
		case b := <-l.wbuffer:
			// Limit amount of writes to avoid disconnects
			<-l.limiter
			l.p.WriteCharacteristic(l.cmd, b[:], true)
		}
	}
}

// Move will move to the specified position at the desired speed.
// Position and speed are specified in percent.
func (l *launch) Move(position, speed int) {
	// Make sure we remain in the limits
	if position < 0 {
		position = 0
	} else if position > 99 {
		position = 99
	}

	// Don't go below 20% as very slow speeds seem to crash the Launch
	if speed < 20 {
		speed = 20
	} else if speed > 99 {
		speed = 99
	}

	data := [2]byte{byte(position), byte(speed)}
	l.wbuffer <- data
}

// discoverCharacteristics finds and stores the Launch characteristics for the
// given service.
func (l *launch) discoverCharacteristics(service *gatt.Service) error {
	cs, err := l.p.DiscoverCharacteristics([]gatt.UUID{
		modeCharID,
		cmdCharID,
	}, service)
	if err != nil {
		return err
	}

	for _, c := range cs {
		if c.UUID().Equal(cmdCharID) {
			log.Println("Launch command characteristic found")

			// Store command characteristic
			l.cmd = c
			l.modeDiscovered <- true
		} else if c.UUID().Equal(modeCharID) {
			log.Println("Launch mode characteristic found")

			// Store mode characteristic
			l.mode = c
			l.cmdDiscovered <- true
		}
	}
	return nil
}

// onConnect stores the connected peripheral in *launch state and discovers and
// stores characteristics.
func (l *launch) onConnected(p gatt.Peripheral, err error) {
	log.Printf("Launch connected")
	// Store peripheral
	l.p = p

	services, err := p.DiscoverServices([]gatt.UUID{serviceID})
	if err != nil {
		return
	}

	for _, service := range services {
		if service.UUID().Equal(serviceID) {
			log.Println("Launch service found")
			l.discoverCharacteristics(service)
		}
	}
}

// HandleDisconnect registers a function that is called when the Launch
// disconnects.
func (l *launch) HandleDisconnect(f func()) {
	l.disconnectFunc = f
}

// onDisconnected will clean up stop scanning and connect if it's a Launch.
func (l *launch) onDisconnected(p gatt.Peripheral, err error) {
	log.Println("Launch disconnected")

	select {
	case l.stopWriting <- true:
	default:
	}

	p.Device().Stop()
	l.connected = false

	if l.disconnectFunc != nil {
		l.disconnectFunc()
	}
}

// onPeripheralDiscovered will stop scanning and connect if it's a Launch.
func (l *launch) onPeripheralDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
	if a.LocalName == pName {
		// Store peripheral
		l.p = p
		log.Printf("Launch discovered")
		p.Device().StopScanning()
		p.Device().Connect(p)
	}
}

// onStateChange will start scanning for the Launch on power on.
func onStateChanged(d gatt.Device, s gatt.State) {
	switch s {
	case gatt.StatePoweredOn:
		// Scan for Launch
		d.Scan([]gatt.UUID{serviceID}, false)
		return
	default:
		d.StopScanning()
	}
}
<|endoftext|>


<commit_before>package megos

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
)

// GetHTTPResponseFromCluster will return a http.Response from one of the Mesos master nodes.
// In a cluster the master nodes can be online or offline.
// With GetHTTPResponseFromCluster you will recieve a response from one of the nodes.
func (c *Client) GetHTTPResponseFromCluster(f func(url.URL) url.URL) (*http.Response, error) {
	for _, instance := range c.Master {
		u := f(*instance)
		resp, err := c.GetHTTPResponse(&u)

		// If there is no error, we hit an instance / master that is online
		if err == nil {
			return resp, nil
		}
	}

	return nil, errors.New("No master online.")
}

// GetHTTPResponseFromLeader will return a http.Response from the determined leader
// of the master nodes.
func (c *Client) GetHTTPResponseFromLeader(f func(Pid) url.URL) (*http.Response, error) {
	u := f(*c.Leader)
	return c.GetHTTPResponse(&u)
}

// GetHTTPResponse will return a http.Response from a URL
func (c *Client) GetHTTPResponse(u *url.URL) (*http.Response, error) {
	resp, err := http.Get(u.String())

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// GetBodyOfHTTPResponse will return the request body of the requested url u.
func (c *Client) GetBodyOfHTTPResponse(u *url.URL) ([]byte, error) {
	resp, err := c.GetHTTPResponse(u)
	if err != nil {
		return []byte{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)

	return body, err
}
<commit_msg>Fixed bug in GetHTTPResponseFromLeader if leader is not selected yet<commit_after>package megos

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
)

// GetHTTPResponseFromCluster will return a http.Response from one of the Mesos master nodes.
// In a cluster the master nodes can be online or offline.
// With GetHTTPResponseFromCluster you will recieve a response from one of the nodes.
func (c *Client) GetHTTPResponseFromCluster(f func(url.URL) url.URL) (*http.Response, error) {
	for _, instance := range c.Master {
		u := f(*instance)
		resp, err := c.GetHTTPResponse(&u)

		// If there is no error, we hit an instance / master that is online
		if err == nil {
			return resp, nil
		}
	}

	return nil, errors.New("No master online.")
}

// GetHTTPResponseFromLeader will return a http.Response from the determined leader
// of the master nodes.
func (c *Client) GetHTTPResponseFromLeader(f func(Pid) url.URL) (*http.Response, error) {
	if c.Leader == nil {
		return nil, errors.New("No leader set.")
	}
	u := f(*c.Leader)
	return c.GetHTTPResponse(&u)
}

// GetHTTPResponse will return a http.Response from a URL
func (c *Client) GetHTTPResponse(u *url.URL) (*http.Response, error) {
	resp, err := http.Get(u.String())

	if err != nil {
		return nil, err
	}

	return resp, nil
}

// GetBodyOfHTTPResponse will return the request body of the requested url u.
func (c *Client) GetBodyOfHTTPResponse(u *url.URL) ([]byte, error) {
	resp, err := c.GetHTTPResponse(u)
	if err != nil {
		return []byte{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)

	return body, err
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package opts

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	goruntime "runtime"
	"strings"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/containers"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/snapshots"
	"github.com/containerd/continuity/fs"
)

// WithNewSnapshot wraps `containerd.WithNewSnapshot` so that if creating the
// snapshot fails we make sure the image is actually unpacked and and retry.
func WithNewSnapshot(id string, i containerd.Image, opts ...snapshots.Opt) containerd.NewContainerOpts {
	f := containerd.WithNewSnapshot(id, i, opts...)
	return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
		if err := f(ctx, client, c); err != nil {
			if !errdefs.IsNotFound(err) {
				return err
			}

			if err := i.Unpack(ctx, c.Snapshotter); err != nil {
				return fmt.Errorf("error unpacking image: %w", err)
			}
			return f(ctx, client, c)
		}
		return nil
	}
}

// WithVolumes copies ownership of volume in rootfs to its corresponding host path.
// It doesn't update runtime spec.
// The passed in map is a host path to container path map for all volumes.
func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts {
	return func(ctx context.Context, client *containerd.Client, c *containers.Container) (err error) {
		if c.Snapshotter == "" {
			return errors.New("no snapshotter set for container")
		}
		if c.SnapshotKey == "" {
			return errors.New("rootfs not created for container")
		}
		snapshotter := client.SnapshotService(c.Snapshotter)
		mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
		if err != nil {
			return err
		}
		// Since only read is needed, append ReadOnly mount option to prevent linux kernel
		// from syncing whole filesystem in umount syscall.
		if len(mounts) == 1 && mounts[0].Type == "overlay" {
			mounts[0].Options = append(mounts[0].Options, "ro")
		}

		root, err := os.MkdirTemp("", "ctd-volume")
		if err != nil {
			return err
		}
		// We change RemoveAll to Remove so that we either leak a temp dir
		// if it fails but not RM snapshot data.
		// refer to https://github.com/containerd/containerd/pull/1868
		// https://github.com/containerd/containerd/pull/1785
		defer os.Remove(root) // nolint: errcheck

		unmounter := func(mountPath string) {
			if uerr := mount.Unmount(mountPath, 0); uerr != nil {
				log.G(ctx).WithError(uerr).Errorf("Failed to unmount snapshot %q", root)
				if err == nil {
					err = uerr
				}
			}
		}

		var mountPaths []string
		if goruntime.GOOS == "windows" {
			for _, m := range mounts {
				// appending the layerID to the root.
				mountPath := filepath.Join(root, filepath.Base(m.Source))
				mountPaths = append(mountPaths, mountPath)
				if err := m.Mount(mountPath); err != nil {
					return err
				}

				defer unmounter(m.Source)
			}
		} else {
			mountPaths = append(mountPaths, root)
			if err := mount.All(mounts, root); err != nil {
				return fmt.Errorf("failed to mount: %w", err)
			}
			defer unmounter(root)
		}

		for host, volume := range volumeMounts {
			// The volume may have been defined with a C: prefix, which we can't use here.
			volume = strings.TrimPrefix(volume, "C:")
			for _, mountPath := range mountPaths {
				src, err := fs.RootPath(mountPath, volume)
				if err != nil {
					return fmt.Errorf("rootpath on mountPath %s, volume %s: %w", mountPath, volume, err)
				}
				if _, err := os.Stat(src); err != nil {
					if os.IsNotExist(err) {
						// Skip copying directory if it does not exist.
						continue
					}
					return fmt.Errorf("stat volume in rootfs: %w", err)
				}
				if err := copyExistingContents(src, host); err != nil {
					return fmt.Errorf("taking runtime copy of volume: %w", err)
				}
			}
		}
		return nil
	}
}

// copyExistingContents copies from the source to the destination and
// ensures the ownership is appropriately set.
func copyExistingContents(source, destination string) error {
	dstList, err := os.ReadDir(destination)
	if err != nil {
		return err
	}
	if len(dstList) != 0 {
		return fmt.Errorf("volume at %q is not initially empty", destination)
	}
	return fs.CopyDir(destination, source, fs.WithXAttrExclude("security.selinux"))
}
<commit_msg>remove duplicate<commit_after>/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package opts

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	goruntime "runtime"
	"strings"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/containers"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/snapshots"
	"github.com/containerd/continuity/fs"
)

// WithNewSnapshot wraps `containerd.WithNewSnapshot` so that if creating the
// snapshot fails we make sure the image is actually unpacked and retry.
func WithNewSnapshot(id string, i containerd.Image, opts ...snapshots.Opt) containerd.NewContainerOpts {
	f := containerd.WithNewSnapshot(id, i, opts...)
	return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
		if err := f(ctx, client, c); err != nil {
			if !errdefs.IsNotFound(err) {
				return err
			}

			if err := i.Unpack(ctx, c.Snapshotter); err != nil {
				return fmt.Errorf("error unpacking image: %w", err)
			}
			return f(ctx, client, c)
		}
		return nil
	}
}

// WithVolumes copies ownership of volume in rootfs to its corresponding host path.
// It doesn't update runtime spec.
// The passed in map is a host path to container path map for all volumes.
func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts {
	return func(ctx context.Context, client *containerd.Client, c *containers.Container) (err error) {
		if c.Snapshotter == "" {
			return errors.New("no snapshotter set for container")
		}
		if c.SnapshotKey == "" {
			return errors.New("rootfs not created for container")
		}
		snapshotter := client.SnapshotService(c.Snapshotter)
		mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey)
		if err != nil {
			return err
		}
		// Since only read is needed, append ReadOnly mount option to prevent linux kernel
		// from syncing whole filesystem in umount syscall.
		if len(mounts) == 1 && mounts[0].Type == "overlay" {
			mounts[0].Options = append(mounts[0].Options, "ro")
		}

		root, err := os.MkdirTemp("", "ctd-volume")
		if err != nil {
			return err
		}
		// We change RemoveAll to Remove so that we either leak a temp dir
		// if it fails but not RM snapshot data.
		// refer to https://github.com/containerd/containerd/pull/1868
		// https://github.com/containerd/containerd/pull/1785
		defer os.Remove(root) // nolint: errcheck

		unmounter := func(mountPath string) {
			if uerr := mount.Unmount(mountPath, 0); uerr != nil {
				log.G(ctx).WithError(uerr).Errorf("Failed to unmount snapshot %q", root)
				if err == nil {
					err = uerr
				}
			}
		}

		var mountPaths []string
		if goruntime.GOOS == "windows" {
			for _, m := range mounts {
				// appending the layerID to the root.
				mountPath := filepath.Join(root, filepath.Base(m.Source))
				mountPaths = append(mountPaths, mountPath)
				if err := m.Mount(mountPath); err != nil {
					return err
				}

				defer unmounter(m.Source)
			}
		} else {
			mountPaths = append(mountPaths, root)
			if err := mount.All(mounts, root); err != nil {
				return fmt.Errorf("failed to mount: %w", err)
			}
			defer unmounter(root)
		}

		for host, volume := range volumeMounts {
			// The volume may have been defined with a C: prefix, which we can't use here.
			volume = strings.TrimPrefix(volume, "C:")
			for _, mountPath := range mountPaths {
				src, err := fs.RootPath(mountPath, volume)
				if err != nil {
					return fmt.Errorf("rootpath on mountPath %s, volume %s: %w", mountPath, volume, err)
				}
				if _, err := os.Stat(src); err != nil {
					if os.IsNotExist(err) {
						// Skip copying directory if it does not exist.
						continue
					}
					return fmt.Errorf("stat volume in rootfs: %w", err)
				}
				if err := copyExistingContents(src, host); err != nil {
					return fmt.Errorf("taking runtime copy of volume: %w", err)
				}
			}
		}
		return nil
	}
}

// copyExistingContents copies from the source to the destination and
// ensures the ownership is appropriately set.
func copyExistingContents(source, destination string) error {
	dstList, err := os.ReadDir(destination)
	if err != nil {
		return err
	}
	if len(dstList) != 0 {
		return fmt.Errorf("volume at %q is not initially empty", destination)
	}
	return fs.CopyDir(destination, source, fs.WithXAttrExclude("security.selinux"))
}
<|endoftext|>