<commit_before>package net

import (
	"sync"

	handshake "github.com/jbenet/go-ipfs/net/handshake"
	pb "github.com/jbenet/go-ipfs/net/handshake/pb"

	ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)

// IDService is a structure that implements ProtocolIdentify.
// It is a trivial service that gives the other peer some
// useful information about the local peer. A sort of hello.
//
// The IDService sends:
//  * Our IPFS Protocol Version
//  * Our IPFS Agent Version
//  * Our public Listen Addresses
type IDService struct {
	Network Network

	// connections undergoing identification
	// for wait purposes
	currid map[Conn]chan struct{}
	currmu sync.RWMutex
}

func NewIDService(n Network) *IDService {
	s := &IDService{
		Network: n,
		currid:  make(map[Conn]chan struct{}),
	}
	n.SetHandler(ProtocolIdentify, s.RequestHandler)
	return s
}

func (ids *IDService) IdentifyConn(c Conn) {
	ids.currmu.Lock()
	if _, found := ids.currid[c]; found {
		ids.currmu.Unlock()
		log.Debugf("IdentifyConn called twice on: %s", c)
		return // already identifying it.
	}
	ids.currid[c] = make(chan struct{})
	ids.currmu.Unlock()

	s, err := c.NewStreamWithProtocol(ProtocolIdentify)
	if err != nil {
		log.Error("network: unable to open initial stream for %s", ProtocolIdentify)
		log.Event(ids.Network.CtxGroup().Context(), "IdentifyOpenFailed", c.RemotePeer())
	}

	// ok give the response to our handler.
	ids.ResponseHandler(s)

	ids.currmu.Lock()
	ch, found := ids.currid[c]
	delete(ids.currid, c)
	ids.currmu.Unlock()

	if !found {
		log.Errorf("IdentifyConn failed to find channel (programmer error) for %s", c)
		return
	}

	close(ch) // release everyone waiting.
}

func (ids *IDService) RequestHandler(s Stream) {
	defer s.Close()
	c := s.Conn()

	w := ggio.NewDelimitedWriter(s)
	mes := pb.Handshake3{}
	ids.populateMessage(&mes, s.Conn())
	w.WriteMsg(&mes)

	log.Debugf("%s sent message to %s %s", ProtocolIdentify,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) ResponseHandler(s Stream) {
	defer s.Close()
	c := s.Conn()

	r := ggio.NewDelimitedReader(s, 2048)
	mes := pb.Handshake3{}
	if err := r.ReadMsg(&mes); err != nil {
		log.Errorf("%s error receiving message from %s %s", ProtocolIdentify,
			c.RemotePeer(), c.RemoteMultiaddr())
		return
	}
	ids.consumeMessage(&mes, c)

	log.Debugf("%s received message from %s %s", ProtocolIdentify,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) populateMessage(mes *pb.Handshake3, c Conn) {

	// set protocols this node is currently handling
	protos := ids.Network.Protocols()
	mes.Protocols = make([]string, len(protos))
	for i, p := range protos {
		mes.Protocols[i] = string(p)
	}

	// observed address so other side is informed of their
	// "public" address, at least in relation to us.
	mes.ObservedAddr = c.RemoteMultiaddr().Bytes()

	// set listen addrs
	laddrs, err := ids.Network.InterfaceListenAddresses()
	if err != nil {
		log.Error(err)
	} else {
		mes.ListenAddrs = make([][]byte, len(laddrs))
		for i, addr := range laddrs {
			mes.ListenAddrs[i] = addr.Bytes()
		}
		log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs)
	}

	// set protocol versions
	mes.H1 = handshake.NewHandshake1("", "")
}

func (ids *IDService) consumeMessage(mes *pb.Handshake3, c Conn) {
	p := c.RemotePeer()

	// mes.Protocols
	// mes.ObservedAddr

	// mes.ListenAddrs
	laddrs := mes.GetListenAddrs()
	lmaddrs := make([]ma.Multiaddr, 0, len(laddrs))
	for _, addr := range laddrs {
		maddr, err := ma.NewMultiaddrBytes(addr)
		if err != nil {
			log.Errorf("%s failed to parse multiaddr from %s %s", ProtocolIdentify, p,
				c.RemoteMultiaddr())
			continue
		}
		lmaddrs = append(lmaddrs, maddr)
	}

	// update our peerstore with the addresses.
	ids.Network.Peerstore().AddAddresses(p, lmaddrs)
	log.Debugf("%s received listen addrs for %s: %s", c.LocalPeer(), c.RemotePeer(), lmaddrs)

	// get protocol versions
	pv := *mes.H1.ProtocolVersion
	av := *mes.H1.AgentVersion
	ids.Network.Peerstore().Put(p, "ProtocolVersion", pv)
	ids.Network.Peerstore().Put(p, "AgentVersion", av)
}

// IdentifyWait returns a channel which will be closed once
// "ProtocolIdentify" (handshake3) finishes on given conn.
// This happens async so the connection can start to be used
// even if handshake3 knowledge is not necesary.
// Users **MUST** call IdentifyWait _after_ IdentifyConn
func (ids *IDService) IdentifyWait(c Conn) <-chan struct{} {
	ids.currmu.Lock()
	ch, found := ids.currid[c]
	ids.currmu.Unlock()
	if found {
		return ch
	}

	// if not found, it means we are already done identifying it, or
	// haven't even started. either way, return a new channel closed.
	ch = make(chan struct{})
	close(ch)
	return ch
}
<commit_msg>net/id: when dup id, wait on it.<commit_after>package net

import (
	"sync"

	handshake "github.com/jbenet/go-ipfs/net/handshake"
	pb "github.com/jbenet/go-ipfs/net/handshake/pb"

	ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)

// IDService is a structure that implements ProtocolIdentify.
// It is a trivial service that gives the other peer some
// useful information about the local peer. A sort of hello.
//
// The IDService sends:
//  * Our IPFS Protocol Version
//  * Our IPFS Agent Version
//  * Our public Listen Addresses
type IDService struct {
	Network Network

	// connections undergoing identification
	// for wait purposes
	currid map[Conn]chan struct{}
	currmu sync.RWMutex
}

func NewIDService(n Network) *IDService {
	s := &IDService{
		Network: n,
		currid:  make(map[Conn]chan struct{}),
	}
	n.SetHandler(ProtocolIdentify, s.RequestHandler)
	return s
}

func (ids *IDService) IdentifyConn(c Conn) {
	ids.currmu.Lock()
	if wait, found := ids.currid[c]; found {
		ids.currmu.Unlock()
		log.Debugf("IdentifyConn called twice on: %s", c)
		<-wait // already identifying it. wait for it.
		return
	}
	ids.currid[c] = make(chan struct{})
	ids.currmu.Unlock()

	s, err := c.NewStreamWithProtocol(ProtocolIdentify)
	if err != nil {
		log.Error("network: unable to open initial stream for %s", ProtocolIdentify)
		log.Event(ids.Network.CtxGroup().Context(), "IdentifyOpenFailed", c.RemotePeer())
	}

	// ok give the response to our handler.
	ids.ResponseHandler(s)

	ids.currmu.Lock()
	ch, found := ids.currid[c]
	delete(ids.currid, c)
	ids.currmu.Unlock()

	if !found {
		log.Errorf("IdentifyConn failed to find channel (programmer error) for %s", c)
		return
	}

	close(ch) // release everyone waiting.
}

func (ids *IDService) RequestHandler(s Stream) {
	defer s.Close()
	c := s.Conn()

	w := ggio.NewDelimitedWriter(s)
	mes := pb.Handshake3{}
	ids.populateMessage(&mes, s.Conn())
	w.WriteMsg(&mes)

	log.Debugf("%s sent message to %s %s", ProtocolIdentify,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) ResponseHandler(s Stream) {
	defer s.Close()
	c := s.Conn()

	r := ggio.NewDelimitedReader(s, 2048)
	mes := pb.Handshake3{}
	if err := r.ReadMsg(&mes); err != nil {
		log.Errorf("%s error receiving message from %s %s", ProtocolIdentify,
			c.RemotePeer(), c.RemoteMultiaddr())
		return
	}
	ids.consumeMessage(&mes, c)

	log.Debugf("%s received message from %s %s", ProtocolIdentify,
		c.RemotePeer(), c.RemoteMultiaddr())
}

func (ids *IDService) populateMessage(mes *pb.Handshake3, c Conn) {

	// set protocols this node is currently handling
	protos := ids.Network.Protocols()
	mes.Protocols = make([]string, len(protos))
	for i, p := range protos {
		mes.Protocols[i] = string(p)
	}

	// observed address so other side is informed of their
	// "public" address, at least in relation to us.
	mes.ObservedAddr = c.RemoteMultiaddr().Bytes()

	// set listen addrs
	laddrs, err := ids.Network.InterfaceListenAddresses()
	if err != nil {
		log.Error(err)
	} else {
		mes.ListenAddrs = make([][]byte, len(laddrs))
		for i, addr := range laddrs {
			mes.ListenAddrs[i] = addr.Bytes()
		}
		log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs)
	}

	// set protocol versions
	mes.H1 = handshake.NewHandshake1("", "")
}

func (ids *IDService) consumeMessage(mes *pb.Handshake3, c Conn) {
	p := c.RemotePeer()

	// mes.Protocols
	// mes.ObservedAddr

	// mes.ListenAddrs
	laddrs := mes.GetListenAddrs()
	lmaddrs := make([]ma.Multiaddr, 0, len(laddrs))
	for _, addr := range laddrs {
		maddr, err := ma.NewMultiaddrBytes(addr)
		if err != nil {
			log.Errorf("%s failed to parse multiaddr from %s %s", ProtocolIdentify, p,
				c.RemoteMultiaddr())
			continue
		}
		lmaddrs = append(lmaddrs, maddr)
	}

	// update our peerstore with the addresses.
	ids.Network.Peerstore().AddAddresses(p, lmaddrs)
	log.Debugf("%s received listen addrs for %s: %s", c.LocalPeer(), c.RemotePeer(), lmaddrs)

	// get protocol versions
	pv := *mes.H1.ProtocolVersion
	av := *mes.H1.AgentVersion
	ids.Network.Peerstore().Put(p, "ProtocolVersion", pv)
	ids.Network.Peerstore().Put(p, "AgentVersion", av)
}

// IdentifyWait returns a channel which will be closed once
// "ProtocolIdentify" (handshake3) finishes on given conn.
// This happens async so the connection can start to be used
// even if handshake3 knowledge is not necesary.
// Users **MUST** call IdentifyWait _after_ IdentifyConn
func (ids *IDService) IdentifyWait(c Conn) <-chan struct{} {
	ids.currmu.Lock()
	ch, found := ids.currid[c]
	ids.currmu.Unlock()
	if found {
		return ch
	}

	// if not found, it means we are already done identifying it, or
	// haven't even started. either way, return a new channel closed.
	ch = make(chan struct{})
	close(ch)
	return ch
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"text/tabwriter"
	"github.com/rackspace/gophercloud/openstack/compute/servers"
	"github.com/rackspace/gophercloud/openstack/identity"
	"github.com/rackspace/gophercloud/openstack/utils"
)

func main() {
	ao, err := utils.AuthOptions()
	if err != nil {
		panic(err)
	}

	a, err := identity.Authenticate(ao)
	if err != nil {
		panic(err)
	}

	sc, err := identity.GetServiceCatalog(a)
	if err != nil {
		panic(err)
	}

	eps, err := findAllComputeEndpoints(sc)
	if err != nil {
		panic(err)
	}

	n := 0
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 2, 8, 2, ' ', 0)
	fmt.Fprintln(w, "ID\tName\tRegion\tIPv4\tIPv6\t")
	for _, ep := range eps {
		client := servers.NewClient(ep.PublicURL, a, ao)

		listResults, err := servers.List(client)
		if err != nil {
			panic(err)
		}

		svrs, err := servers.GetServers(listResults)
		if err != nil {
			panic(err)
		}

		n = n + len(svrs)

		for _, s := range svrs {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t\n", s.Id, s.Name, ep.Region, s.AccessIPv4, s.AccessIPv6)
		}
	}
	w.Flush()
	fmt.Printf("--------\n%d servers listed.\n", n)
}

func findAllComputeEndpoints(sc *identity.ServiceCatalog) ([]identity.Endpoint, error) {
	ces, err := sc.CatalogEntries()
	if err != nil {
		return nil, err
	}

	for _, ce := range ces {
		if ce.Type == "compute" {
			return ce.Endpoints, nil
		}
	}

	return nil, fmt.Errorf("Compute endpoint not found.")
}

<commit_msg>Add support for region name<commit_after>package main

import (
	"fmt"
	"os"
	"text/tabwriter"
	"github.com/rackspace/gophercloud/openstack/compute/servers"
	"github.com/rackspace/gophercloud/openstack/identity"
	"github.com/rackspace/gophercloud/openstack/utils"
)

func main() {
	ao, err := utils.AuthOptions()
	if err != nil {
		panic(err)
	}

	a, err := identity.Authenticate(ao)
	if err != nil {
		panic(err)
	}

	sc, err := identity.GetServiceCatalog(a)
	if err != nil {
		panic(err)
	}

	eps, err := findAllComputeEndpoints(sc)
	if err != nil {
		panic(err)
	}

	region := os.Getenv("OS_REGION_NAME")

	n := 0
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 2, 8, 2, ' ', 0)
	fmt.Fprintln(w, "ID\tName\tRegion\tIPv4\tIPv6\t")
	for _, ep := range eps {
		if (region != "") && (region != ep.Region) {
			continue
		}

		client := servers.NewClient(ep.PublicURL, a, ao)

		listResults, err := servers.List(client)
		if err != nil {
			panic(err)
		}

		svrs, err := servers.GetServers(listResults)
		if err != nil {
			panic(err)
		}

		n = n + len(svrs)

		for _, s := range svrs {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t\n", s.Id, s.Name, ep.Region, s.AccessIPv4, s.AccessIPv6)
		}
	}
	w.Flush()
	fmt.Printf("--------\n%d servers listed.\n", n)
}

func findAllComputeEndpoints(sc *identity.ServiceCatalog) ([]identity.Endpoint, error) {
	ces, err := sc.CatalogEntries()
	if err != nil {
		return nil, err
	}

	for _, ce := range ces {
		if ce.Type == "compute" {
			return ce.Endpoints, nil
		}
	}

	return nil, fmt.Errorf("Compute endpoint not found.")
}

<|endoftext|>


<commit_before>// +build windows

package pluginaction_test

import (
	"io/ioutil"
	"os"

	. "code.cloudfoundry.org/cli/actor/pluginaction"
	"code.cloudfoundry.org/cli/actor/pluginaction/pluginactionfakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("install actions", func() {
	var (
		actor      *Actor
		fakeConfig *pluginactionfakes.FakeConfig
		tempDir    string
	)

	BeforeEach(func() {
		fakeConfig = new(pluginactionfakes.FakeConfig)
		var err error
		tempPluginDir, err = ioutil.TempDir("", "")
		Expect(err).ToNot(HaveOccurred())
		actor = NewActor(fakeConfig, nil)
	})

	AfterEach(func() {
		err := os.RemoveAll(tempPluginDir)
		Expect(err).ToNot(HaveOccurred())
	})

	Describe("CreateExecutableCopy", func() {
		Context("when the file exists", func() {
			var pluginPath string

			BeforeEach(func() {
				tempFile, err := ioutil.TempFile("", "")
				Expect(err).ToNot(HaveOccurred())

				_, err = tempFile.WriteString("cthulhu")
				Expect(err).ToNot(HaveOccurred())
				err = tempFile.Close()
				Expect(err).ToNot(HaveOccurred())

				pluginPath = tempFile.Name()
			})

			AfterEach(func() {
				err := os.Remove(pluginPath)
				Expect(err).ToNot(HaveOccurred())
			})

			It("adds .exe to the end of the filename", func() {
				copyPath, err := actor.CreateExecutableCopy(pluginPath, tempPluginDir)
				Expect(err).ToNot(HaveOccurred())

				Expect(copyPath).To(HaveSuffix(".exe"))
			})
		})
	})
})
<commit_msg>still fixing windows units<commit_after>// +build windows

package pluginaction_test

import (
	"io/ioutil"
	"os"

	. "code.cloudfoundry.org/cli/actor/pluginaction"
	"code.cloudfoundry.org/cli/actor/pluginaction/pluginactionfakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("install actions", func() {
	var (
		actor         *Actor
		fakeConfig    *pluginactionfakes.FakeConfig
		tempPluginDir string
	)

	BeforeEach(func() {
		fakeConfig = new(pluginactionfakes.FakeConfig)
		var err error
		tempPluginDir, err = ioutil.TempDir("", "")
		Expect(err).ToNot(HaveOccurred())
		actor = NewActor(fakeConfig, nil)
	})

	AfterEach(func() {
		err := os.RemoveAll(tempPluginDir)
		Expect(err).ToNot(HaveOccurred())
	})

	Describe("CreateExecutableCopy", func() {
		Context("when the file exists", func() {
			var pluginPath string

			BeforeEach(func() {
				tempFile, err := ioutil.TempFile("", "")
				Expect(err).ToNot(HaveOccurred())

				_, err = tempFile.WriteString("cthulhu")
				Expect(err).ToNot(HaveOccurred())
				err = tempFile.Close()
				Expect(err).ToNot(HaveOccurred())

				pluginPath = tempFile.Name()
			})

			AfterEach(func() {
				err := os.Remove(pluginPath)
				Expect(err).ToNot(HaveOccurred())
			})

			It("adds .exe to the end of the filename", func() {
				copyPath, err := actor.CreateExecutableCopy(pluginPath, tempPluginDir)
				Expect(err).ToNot(HaveOccurred())

				Expect(copyPath).To(HaveSuffix(".exe"))
			})
		})
	})
})
<|endoftext|>


<commit_before>package client

import (
	"github.com/dotcloud/docker/future"
	"github.com/dotcloud/docker/rcli"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

// Run docker in "simple mode": run a single command and return.
func SimpleMode(args []string) error {
	var oldState *State
	var err error
	if IsTerminal(0) && os.Getenv("NORAW") == "" {
		oldState, err = MakeRaw(0)
		if err != nil {
			return err
		}
		defer Restore(0, oldState)
	}
	// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
	// CloseWrite(), which we need to cleanly signal that stdin is closed without
	// closing the connection.
	// See http://code.google.com/p/go/issues/detail?id=3345
	conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...)
	if err != nil {
		return err
	}
	receive_stdout := future.Go(func() error {
		_, err := io.Copy(os.Stdout, conn)
		return err
	})
	send_stdin := future.Go(func() error {
		_, err := io.Copy(conn, os.Stdin)
		if err := conn.CloseWrite(); err != nil {
			log.Printf("Couldn't send EOF: " + err.Error())
		}
		return err
	})
	if err := <-receive_stdout; err != nil {
		return err
	}
	if oldState != nil {
		Restore(0, oldState)
	}
	if !IsTerminal(0) {
		if err := <-send_stdin; err != nil {
			return err
		}
	}
	return nil
}

// Run docker in "interactive mode": run a bash-compatible shell capable of running docker commands.
func InteractiveMode(scripts ...string) error {
	// Determine path of current docker binary
	dockerPath, err := exec.LookPath(os.Args[0])
	if err != nil {
		return err
	}
	dockerPath, err = filepath.Abs(dockerPath)
	if err != nil {
		return err
	}

	// Create a temp directory
	tmp, err := ioutil.TempDir("", "docker-shell")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmp)

	// For each command, create an alias in temp directory
	// FIXME: generate this list dynamically with introspection of some sort
	// It might make sense to merge docker and dockerd to keep that introspection
	// within a single binary.
	for _, cmd := range []string{
		"help",
		"run",
		"ps",
		"pull",
		"put",
		"rm",
		"kill",
		"wait",
		"stop",
		"start",
		"restart",
		"logs",
		"diff",
		"commit",
		"attach",
		"info",
		"tar",
		"web",
		"images",
		"docker",
	} {
		if err := os.Symlink(dockerPath, path.Join(tmp, cmd)); err != nil {
			return err
		}
	}

	// Run $SHELL with PATH set to temp directory
	rcfile, err := ioutil.TempFile("", "docker-shell-rc")
	if err != nil {
		return err
	}
	io.WriteString(rcfile, "enable -n help\n")
	os.Setenv("PATH", tmp+":"+os.Getenv("PATH"))
	os.Setenv("PS1", "\\h docker> ")
	shell := exec.Command("/bin/bash", append([]string{"--rcfile", rcfile.Name()}, scripts...)...)
	shell.Stdin = os.Stdin
	shell.Stdout = os.Stdout
	shell.Stderr = os.Stderr
	if err := shell.Run(); err != nil {
		return err
	}
	return nil
}
<commit_msg>Automatically remove the rcfile generated by docker -i from /tmp<commit_after>package client

import (
	"github.com/dotcloud/docker/future"
	"github.com/dotcloud/docker/rcli"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

// Run docker in "simple mode": run a single command and return.
func SimpleMode(args []string) error {
	var oldState *State
	var err error
	if IsTerminal(0) && os.Getenv("NORAW") == "" {
		oldState, err = MakeRaw(0)
		if err != nil {
			return err
		}
		defer Restore(0, oldState)
	}
	// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
	// CloseWrite(), which we need to cleanly signal that stdin is closed without
	// closing the connection.
	// See http://code.google.com/p/go/issues/detail?id=3345
	conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...)
	if err != nil {
		return err
	}
	receive_stdout := future.Go(func() error {
		_, err := io.Copy(os.Stdout, conn)
		return err
	})
	send_stdin := future.Go(func() error {
		_, err := io.Copy(conn, os.Stdin)
		if err := conn.CloseWrite(); err != nil {
			log.Printf("Couldn't send EOF: " + err.Error())
		}
		return err
	})
	if err := <-receive_stdout; err != nil {
		return err
	}
	if oldState != nil {
		Restore(0, oldState)
	}
	if !IsTerminal(0) {
		if err := <-send_stdin; err != nil {
			return err
		}
	}
	return nil
}

// Run docker in "interactive mode": run a bash-compatible shell capable of running docker commands.
func InteractiveMode(scripts ...string) error {
	// Determine path of current docker binary
	dockerPath, err := exec.LookPath(os.Args[0])
	if err != nil {
		return err
	}
	dockerPath, err = filepath.Abs(dockerPath)
	if err != nil {
		return err
	}

	// Create a temp directory
	tmp, err := ioutil.TempDir("", "docker-shell")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmp)

	// For each command, create an alias in temp directory
	// FIXME: generate this list dynamically with introspection of some sort
	// It might make sense to merge docker and dockerd to keep that introspection
	// within a single binary.
	for _, cmd := range []string{
		"help",
		"run",
		"ps",
		"pull",
		"put",
		"rm",
		"kill",
		"wait",
		"stop",
		"start",
		"restart",
		"logs",
		"diff",
		"commit",
		"attach",
		"info",
		"tar",
		"web",
		"images",
		"docker",
	} {
		if err := os.Symlink(dockerPath, path.Join(tmp, cmd)); err != nil {
			return err
		}
	}

	// Run $SHELL with PATH set to temp directory
	rcfile, err := ioutil.TempFile("", "docker-shell-rc")
	if err != nil {
		return err
	}
	defer os.Remove(rcfile.Name())
	io.WriteString(rcfile, "enable -n help\n")
	os.Setenv("PATH", tmp+":"+os.Getenv("PATH"))
	os.Setenv("PS1", "\\h docker> ")
	shell := exec.Command("/bin/bash", append([]string{"--rcfile", rcfile.Name()}, scripts...)...)
	shell.Stdin = os.Stdin
	shell.Stdout = os.Stdout
	shell.Stderr = os.Stderr
	if err := shell.Run(); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"fmt"
	"io"
	"log"
	"time"

	"github.com/pivotal-golang/s3cli/config"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
)

// S3Blobstore encapsulates interactions with an S3 compatible blobstore
type S3Blobstore struct {
	s3Client    *s3.S3
	s3cliConfig *config.S3Cli
}

var errorInvalidCredentialsSourceValue = errors.New("the client operates in read only mode. Change 'credentials_source' parameter value ")
var oneTB = int64(1000 * 1024 * 1024 * 1024)

// New returns a BlobstoreClient if the configuration file backing configFile is valid
func New(s3Client *s3.S3, s3cliConfig *config.S3Cli) (S3Blobstore, error) {
	return S3Blobstore{s3Client: s3Client, s3cliConfig: s3cliConfig}, nil
}

// Get fetches a blob from an S3 compatible blobstore
// Destination will be overwritten if exists
func (client *S3Blobstore) Get(src string, dest io.WriterAt) error {
	downloader := s3manager.NewDownloaderWithClient(client.s3Client)

	_, err := downloader.Download(dest, &s3.GetObjectInput{
		Bucket: aws.String(client.s3cliConfig.BucketName),
		Key:    aws.String(src),
	})

	if err != nil {
		return err
	}

	return nil
}

// Put uploads a blob to an S3 compatible blobstore
func (client *S3Blobstore) Put(src io.ReadSeeker, dest string) error {
	cfg := client.s3cliConfig
	if cfg.CredentialsSource == config.NoneCredentialsSource {
		return errorInvalidCredentialsSourceValue
	}

	uploader := s3manager.NewUploaderWithClient(client.s3Client, func(u *s3manager.Uploader) {
		u.LeavePartsOnError = false

		if !cfg.MultipartUpload {
			// disable multipart uploads by way of large PartSize configuration
			u.PartSize = oneTB
		}
	})
	uploadInput := &s3manager.UploadInput{
		Body:   src,
		Bucket: aws.String(cfg.BucketName),
		Key:    aws.String(dest),
	}
	if cfg.ServerSideEncryption != "" {
		uploadInput.ServerSideEncryption = aws.String(cfg.ServerSideEncryption)
	}
	if cfg.SSEKMSKeyID != "" {
		uploadInput.SSEKMSKeyId = aws.String(cfg.SSEKMSKeyID)
	}

	retry := 0
	maxRetries := 3
	for {
		putResult, err := uploader.Upload(uploadInput)
		if err != nil {
			if _, ok := err.(s3manager.MultiUploadFailure); ok {
				if retry == maxRetries {
					log.Println("Upload retry limit exceeded:", err.Error())
					return fmt.Errorf("upload retry limit exceeded: %s", err.Error())
				}
				retry++
				time.Sleep(time.Second * time.Duration(retry))
				continue
			}
			log.Println("Upload failed:", err.Error())
			return fmt.Errorf("upload failure: %s", err.Error())
		}

		log.Println("Successfully uploaded file to", putResult.Location)
		return nil
	}
}

// Delete removes a blob from an S3 compatible blobstore. If the object does
// not exist, Delete does not return an error.
func (client *S3Blobstore) Delete(dest string) error {
	if client.s3cliConfig.CredentialsSource == config.NoneCredentialsSource {
		return errorInvalidCredentialsSourceValue
	}

	deleteParams := &s3.DeleteObjectInput{
		Bucket: aws.String(client.s3cliConfig.BucketName),
		Key:    aws.String(dest),
	}

	_, err := client.s3Client.DeleteObject(deleteParams)

	if err == nil {
		return nil
	}

	if reqErr, ok := err.(awserr.RequestFailure); ok {
		if reqErr.StatusCode() == 404 {
			return nil
		}
	}
	return err
}

// Exists checks if blob exists in an S3 compatible blobstore
func (client *S3Blobstore) Exists(dest string) (bool, error) {

	existsParams := &s3.HeadObjectInput{
		Bucket: aws.String(client.s3cliConfig.BucketName),
		Key:    aws.String(dest),
	}

	_, err := client.s3Client.HeadObject(existsParams)

	if err == nil {
		log.Printf("File '%s' exists in bucket '%s'\n", dest, client.s3cliConfig.BucketName)
		return true, nil
	}

	if reqErr, ok := err.(awserr.RequestFailure); ok {
		if reqErr.StatusCode() == 404 {
			log.Printf("File '%s' does not exist in bucket '%s'\n", dest, client.s3cliConfig.BucketName)
			return false, nil
		}
	}
	return false, err
}
<commit_msg>Time out when network partition occurs<commit_after>package client

import (
	"errors"
	"fmt"
	"io"
	"log"
	"time"

	"github.com/pivotal-golang/s3cli/config"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
)

// S3Blobstore encapsulates interactions with an S3 compatible blobstore
type S3Blobstore struct {
	s3Client    *s3.S3
	s3cliConfig *config.S3Cli
}

var errorInvalidCredentialsSourceValue = errors.New("the client operates in read only mode. Change 'credentials_source' parameter value ")
var oneTB = int64(1000 * 1024 * 1024 * 1024)

// New returns a BlobstoreClient if the configuration file backing configFile is valid
func New(s3Client *s3.S3, s3cliConfig *config.S3Cli) (S3Blobstore, error) {
	return S3Blobstore{s3Client: s3Client, s3cliConfig: s3cliConfig}, nil
}

// Get fetches a blob from an S3 compatible blobstore
// Destination will be overwritten if exists
func (client *S3Blobstore) Get(src string, dest io.WriterAt) error {
	setConcurrency := func(d *s3manager.Downloader) {
		d.Concurrency = 105 // creates 525MB buffer, to avoid hanging on download w/ broken network
	}
	downloader := s3manager.NewDownloaderWithClient(client.s3Client, setConcurrency)

	_, err := downloader.Download(dest, &s3.GetObjectInput{
		Bucket: aws.String(client.s3cliConfig.BucketName),
		Key:    aws.String(src),
	})

	if err != nil {
		return err
	}

	return nil
}

// Put uploads a blob to an S3 compatible blobstore
func (client *S3Blobstore) Put(src io.ReadSeeker, dest string) error {
	cfg := client.s3cliConfig
	if cfg.CredentialsSource == config.NoneCredentialsSource {
		return errorInvalidCredentialsSourceValue
	}

	uploader := s3manager.NewUploaderWithClient(client.s3Client, func(u *s3manager.Uploader) {
		u.LeavePartsOnError = false

		if !cfg.MultipartUpload {
			// disable multipart uploads by way of large PartSize configuration
			u.PartSize = oneTB
		}
	})
	uploadInput := &s3manager.UploadInput{
		Body:   src,
		Bucket: aws.String(cfg.BucketName),
		Key:    aws.String(dest),
	}
	if cfg.ServerSideEncryption != "" {
		uploadInput.ServerSideEncryption = aws.String(cfg.ServerSideEncryption)
	}
	if cfg.SSEKMSKeyID != "" {
		uploadInput.SSEKMSKeyId = aws.String(cfg.SSEKMSKeyID)
	}

	retry := 0
	maxRetries := 3
	for {
		putResult, err := uploader.Upload(uploadInput)
		if err != nil {
			if _, ok := err.(s3manager.MultiUploadFailure); ok {
				if retry == maxRetries {
					log.Println("Upload retry limit exceeded:", err.Error())
					return fmt.Errorf("upload retry limit exceeded: %s", err.Error())
				}
				retry++
				time.Sleep(time.Second * time.Duration(retry))
				continue
			}
			log.Println("Upload failed:", err.Error())
			return fmt.Errorf("upload failure: %s", err.Error())
		}

		log.Println("Successfully uploaded file to", putResult.Location)
		return nil
	}
}

// Delete removes a blob from an S3 compatible blobstore. If the object does
// not exist, Delete does not return an error.
func (client *S3Blobstore) Delete(dest string) error {
	if client.s3cliConfig.CredentialsSource == config.NoneCredentialsSource {
		return errorInvalidCredentialsSourceValue
	}

	deleteParams := &s3.DeleteObjectInput{
		Bucket: aws.String(client.s3cliConfig.BucketName),
		Key:    aws.String(dest),
	}

	_, err := client.s3Client.DeleteObject(deleteParams)

	if err == nil {
		return nil
	}

	if reqErr, ok := err.(awserr.RequestFailure); ok {
		if reqErr.StatusCode() == 404 {
			return nil
		}
	}
	return err
}

// Exists checks if blob exists in an S3 compatible blobstore
func (client *S3Blobstore) Exists(dest string) (bool, error) {

	existsParams := &s3.HeadObjectInput{
		Bucket: aws.String(client.s3cliConfig.BucketName),
		Key:    aws.String(dest),
	}

	_, err := client.s3Client.HeadObject(existsParams)

	if err == nil {
		log.Printf("File '%s' exists in bucket '%s'\n", dest, client.s3cliConfig.BucketName)
		return true, nil
	}

	if reqErr, ok := err.(awserr.RequestFailure); ok {
		if reqErr.StatusCode() == 404 {
			log.Printf("File '%s' does not exist in bucket '%s'\n", dest, client.s3cliConfig.BucketName)
			return false, nil
		}
	}
	return false, err
}
<|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.

// Helper functions to make constructing templates and sets easier.

package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Functions and methods to parse a single template.

// Must is a helper that wraps a call to a function returning (*Template, os.Error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
//	var t = template.Must(template.Parse("text"))
func Must(t *Template, err os.Error) *Template {
	if err != nil {
		panic(err)
	}
	return t
}

// ParseFile creates a new Template and parses the template definition from
// the named file.  The template name is the base name of the file.
func ParseFile(filename string) (*Template, os.Error) {
	t := New(filepath.Base(filename))
	return t.ParseFile(filename)
}

// parseFileInSet creates a new Template and parses the template
// definition from the named file. The template name is the base name
// of the file. It also adds the template to the set. Function bindings are
// checked against those in the set.
func parseFileInSet(filename string, set *Set) (*Template, os.Error) {
	t := New(filepath.Base(filename))
	return t.parseFileInSet(filename, set)
}

// ParseFile reads the template definition from a file and parses it to
// construct an internal representation of the template for execution.
func (t *Template) ParseFile(filename string) (*Template, os.Error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return t, err
	}
	return t.Parse(string(b))
}

// parseFileInSet is the same as ParseFile except that function bindings
// are checked against those in the set and the template is added
// to the set.
func (t *Template) parseFileInSet(filename string, set *Set) (*Template, os.Error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return t, err
	}
	return t.ParseInSet(string(b), set)
}

// Functions and methods to parse a set.

// SetMust is a helper that wraps a call to a function returning (*Set, os.Error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
//	var s = template.SetMust(template.ParseSetFile("file"))
func SetMust(s *Set, err os.Error) *Set {
	if err != nil {
		panic(err)
	}
	return s
}

// ParseFile parses the named files into a set of named templates.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseFile(filenames ...string) (*Set, os.Error) {
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return s, err
		}
		_, err = s.Parse(string(b))
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseSetFile creates a new Set and parses the set definition from the
// named files. Each file must be individually parseable.
func ParseSetFile(filenames ...string) (*Set, os.Error) {
	s := new(Set)
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return s, err
		}
		_, err = s.Parse(string(b))
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseFiles parses the set definition from the files identified by the
// pattern.  The pattern is processed by filepath.Glob and must match at
// least one file.
func (s *Set) ParseFiles(pattern string) (*Set, os.Error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return s, err
	}
	if len(filenames) == 0 {
		return s, fmt.Errorf("pattern matches no files: %#q", pattern)
	}
	return s.ParseFile(filenames...)
}

// ParseSetFiles creates a new Set and parses the set definition from the
// files identified by the pattern. The pattern is processed by filepath.Glob
// and must match at least one file.
func ParseSetFiles(pattern string) (*Set, os.Error) {
	set, err := new(Set).ParseFiles(pattern)
	if err != nil {
		return set, err
	}
	return set, nil
}

// Functions and methods to parse stand-alone template files into a set.

// ParseTemplateFile parses the named template files and adds
// them to the set. Each template will named the base name of
// its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFile(filenames ...string) (*Set, os.Error) {
	for _, filename := range filenames {
		_, err := parseFileInSet(filename, s)
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseTemplateFiles parses the template files matched by the
// patern and adds them to the set. Each template will named
// the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFiles(pattern string) (*Set, os.Error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return s, err
	}
	for _, filename := range filenames {
		_, err := parseFileInSet(filename, s)
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseTemplateFile creates a set by parsing the named files,
// each of which defines a single template. Each template will
// named the base name of its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFile(filenames ...string) (*Set, os.Error) {
	set := new(Set)
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return set, err
		}
		if err := set.add(t); err != nil {
			return set, err
		}
	}
	return set, nil
}

// ParseTemplateFiles creates a set by parsing the files matched
// by the pattern, each of which defines a single template. Each
// template will named the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFiles(pattern string) (*Set, os.Error) {
	set := new(Set)
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return set, err
	}
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return set, err
		}
		if err := set.add(t); err != nil {
			return set, err
		}
	}
	return set, nil
}
<commit_msg>exp/template: ensure that a valid Set is returned even on error.<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.

// Helper functions to make constructing templates and sets easier.

package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Functions and methods to parse a single template.

// Must is a helper that wraps a call to a function returning (*Template, os.Error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
//	var t = template.Must(template.Parse("text"))
func Must(t *Template, err os.Error) *Template {
	if err != nil {
		panic(err)
	}
	return t
}

// ParseFile creates a new Template and parses the template definition from
// the named file.  The template name is the base name of the file.
func ParseFile(filename string) (*Template, os.Error) {
	t := New(filepath.Base(filename))
	return t.ParseFile(filename)
}

// parseFileInSet creates a new Template and parses the template
// definition from the named file. The template name is the base name
// of the file. It also adds the template to the set. Function bindings are
// checked against those in the set.
func parseFileInSet(filename string, set *Set) (*Template, os.Error) {
	t := New(filepath.Base(filename))
	return t.parseFileInSet(filename, set)
}

// ParseFile reads the template definition from a file and parses it to
// construct an internal representation of the template for execution.
func (t *Template) ParseFile(filename string) (*Template, os.Error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return t, err
	}
	return t.Parse(string(b))
}

// parseFileInSet is the same as ParseFile except that function bindings
// are checked against those in the set and the template is added
// to the set.
func (t *Template) parseFileInSet(filename string, set *Set) (*Template, os.Error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return t, err
	}
	return t.ParseInSet(string(b), set)
}

// Functions and methods to parse a set.

// SetMust is a helper that wraps a call to a function returning (*Set, os.Error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
//	var s = template.SetMust(template.ParseSetFile("file"))
func SetMust(s *Set, err os.Error) *Set {
	if err != nil {
		panic(err)
	}
	return s
}

// ParseFile parses the named files into a set of named templates.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseFile(filenames ...string) (*Set, os.Error) {
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return s, err
		}
		_, err = s.Parse(string(b))
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseSetFile creates a new Set and parses the set definition from the
// named files. Each file must be individually parseable.
func ParseSetFile(filenames ...string) (*Set, os.Error) {
	s := new(Set)
	s.init()
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return s, err
		}
		_, err = s.Parse(string(b))
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseFiles parses the set definition from the files identified by the
// pattern.  The pattern is processed by filepath.Glob and must match at
// least one file.
func (s *Set) ParseFiles(pattern string) (*Set, os.Error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return s, err
	}
	if len(filenames) == 0 {
		return s, fmt.Errorf("pattern matches no files: %#q", pattern)
	}
	return s.ParseFile(filenames...)
}

// ParseSetFiles creates a new Set and parses the set definition from the
// files identified by the pattern. The pattern is processed by filepath.Glob
// and must match at least one file.
func ParseSetFiles(pattern string) (*Set, os.Error) {
	set, err := new(Set).ParseFiles(pattern)
	if err != nil {
		return set, err
	}
	return set, nil
}

// Functions and methods to parse stand-alone template files into a set.

// ParseTemplateFile parses the named template files and adds
// them to the set. Each template will named the base name of
// its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFile(filenames ...string) (*Set, os.Error) {
	for _, filename := range filenames {
		_, err := parseFileInSet(filename, s)
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseTemplateFiles parses the template files matched by the
// patern and adds them to the set. Each template will named
// the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFiles(pattern string) (*Set, os.Error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return s, err
	}
	for _, filename := range filenames {
		_, err := parseFileInSet(filename, s)
		if err != nil {
			return s, err
		}
	}
	return s, nil
}

// ParseTemplateFile creates a set by parsing the named files,
// each of which defines a single template. Each template will
// named the base name of its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFile(filenames ...string) (*Set, os.Error) {
	set := new(Set)
	set.init()
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return set, err
		}
		if err := set.add(t); err != nil {
			return set, err
		}
	}
	return set, nil
}

// ParseTemplateFiles creates a set by parsing the files matched
// by the pattern, each of which defines a single template. Each
// template will named the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFiles(pattern string) (*Set, os.Error) {
	set := new(Set)
	set.init()
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return set, err
	}
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return set, err
		}
		if err := set.add(t); err != nil {
			return set, err
		}
	}
	return set, nil
}
<|endoftext|>


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

package walk

import (
	"fmt"
	"os"
	"unsafe"
)

import (
	. "walk/winapi"
	. "walk/winapi/gdi32"
	. "walk/winapi/kernel32"
	. "walk/winapi/user32"
)

type CloseReason int

const (
	CloseReasonUnknown CloseReason = iota
	CloseReasonUser
)

type TopLevelWindow struct {
	ContainerBase
	owner             RootWidget
	closingPublisher  CloseEventPublisher
	closeReason       CloseReason
	prevFocusHWnd     HWND
	isInRestoreState  bool
	startingPublisher EventPublisher
}

func (tlw *TopLevelWindow) LayoutFlags() LayoutFlags {
	return ShrinkableHorz | ShrinkableVert | GrowableHorz | GrowableVert | GreedyHorz | GreedyVert
}

func (tlw *TopLevelWindow) SizeHint() Size {
	return tlw.dialogBaseUnitsToPixels(Size{252, 218})
}

func (tlw *TopLevelWindow) Title() string {
	return widgetText(tlw.hWnd)
}

func (tlw *TopLevelWindow) SetTitle(value string) os.Error {
	return setWidgetText(tlw.hWnd, value)
}

func (tlw *TopLevelWindow) Run() int {
	tlw.startingPublisher.Publish()

	var msg MSG

	for tlw.hWnd != 0 {
		switch GetMessage(&msg, 0, 0, 0) {
		case 0:
			return int(msg.WParam)

		case -1:
			return -1
		}

		if !IsDialogMessage(tlw.hWnd, &msg) {
			TranslateMessage(&msg)
			DispatchMessage(&msg)
		}
	}

	return 0
}

func (tlw *TopLevelWindow) Starting() *Event {
	return tlw.startingPublisher.Event()
}

func (tlw *TopLevelWindow) Owner() RootWidget {
	return tlw.owner
}

func (tlw *TopLevelWindow) SetOwner(value RootWidget) os.Error {
	tlw.owner = value

	var ownerHWnd HWND
	if value != nil {
		ownerHWnd = value.BaseWidget().hWnd
	}

	SetLastError(0)
	if 0 == SetWindowLong(tlw.hWnd, GWL_HWNDPARENT, int(ownerHWnd)) && GetLastError() != 0 {
		return lastError("SetWindowLong")
	}

	return nil
}

func (tlw *TopLevelWindow) Hide() {
	tlw.SetVisible(false)
}

func (tlw *TopLevelWindow) Show() {
	tlw.SetVisible(true)
}

func (tlw *TopLevelWindow) close() os.Error {
	tlw.Dispose()

	return nil
}

func (tlw *TopLevelWindow) Close() os.Error {
	SendMessage(tlw.hWnd, WM_CLOSE, 0, 0)

	return nil
}

func (tlw *TopLevelWindow) SaveState() os.Error {
	var wp WINDOWPLACEMENT

	wp.Length = uint(unsafe.Sizeof(wp))

	if !GetWindowPlacement(tlw.hWnd, &wp) {
		return lastError("GetWindowPlacement")
	}

	state := fmt.Sprint(
		wp.Flags, wp.ShowCmd,
		wp.PtMinPosition.X, wp.PtMinPosition.Y,
		wp.PtMaxPosition.X, wp.PtMaxPosition.Y,
		wp.RcNormalPosition.Left, wp.RcNormalPosition.Top,
		wp.RcNormalPosition.Right, wp.RcNormalPosition.Bottom)

	if err := tlw.putState(state); err != nil {
		return err
	}

	return tlw.ContainerBase.SaveState()
}

func (tlw *TopLevelWindow) RestoreState() os.Error {
	if tlw.isInRestoreState {
		return nil
	}
	tlw.isInRestoreState = true
	defer func() {
		tlw.isInRestoreState = false
	}()

	state, err := tlw.getState()
	if err != nil {
		return err
	}
	if state == "" {
		return nil
	}

	var wp WINDOWPLACEMENT

	if _, err := fmt.Sscan(state,
		&wp.Flags, &wp.ShowCmd,
		&wp.PtMinPosition.X, &wp.PtMinPosition.Y,
		&wp.PtMaxPosition.X, &wp.PtMaxPosition.Y,
		&wp.RcNormalPosition.Left, &wp.RcNormalPosition.Top,
		&wp.RcNormalPosition.Right, &wp.RcNormalPosition.Bottom); err != nil {
		return err
	}

	wp.Length = uint(unsafe.Sizeof(wp))

	if !SetWindowPlacement(tlw.hWnd, &wp) {
		return lastError("SetWindowPlacement")
	}

	if err := tlw.ContainerBase.RestoreState(); err != nil {
		return err
	}

	return nil
}

func (tlw *TopLevelWindow) Closing() *CloseEvent {
	return tlw.closingPublisher.Event()
}

func (tlw *TopLevelWindow) wndProc(hwnd HWND, msg uint, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_ACTIVATE:
		switch LOWORD(uint(wParam)) {
		case WA_ACTIVE, WA_CLICKACTIVE:
			if tlw.prevFocusHWnd != 0 {
				SetFocus(tlw.prevFocusHWnd)
			}

		case WA_INACTIVE:
			tlw.prevFocusHWnd = GetFocus()
		}
		return 0

	case WM_CLOSE:
		tlw.closeReason = CloseReasonUnknown
		var canceled bool
		tlw.closingPublisher.Publish(&canceled, tlw.closeReason)
		if !canceled {
			if tlw.owner != nil {
				tlw.owner.SetEnabled(true)
				if !SetWindowPos(tlw.owner.BaseWidget().hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW) {
					lastError("SetWindowPos")
				}
			}

			tlw.close()
		}
		return 0

	case WM_GETMINMAXINFO:
		mmi := (*MINMAXINFO)(unsafe.Pointer(lParam))
		var min Size
		if tlw.layout != nil {
			min = tlw.sizeFromClientSize(tlw.layout.MinSize())
		}
		mmi.PtMinTrackSize = POINT{
			maxi(min.Width, tlw.minSize.Width),
			maxi(min.Height, tlw.minSize.Height),
		}
		return 0

	case WM_SYSCOMMAND:
		if wParam == SC_CLOSE {
			tlw.closeReason = CloseReasonUser
		}
	}

	return tlw.ContainerBase.wndProc(hwnd, msg, wParam, lParam)
}
<commit_msg>Add a Synchronize function that allows to run a closure within the main thread.<commit_after>// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import (
	"fmt"
	"os"
	"unsafe"
	"sync"
)

import (
	. "walk/winapi"
	. "walk/winapi/gdi32"
	. "walk/winapi/kernel32"
	. "walk/winapi/user32"
)

type CloseReason int

const (
	CloseReasonUnknown CloseReason = iota
	CloseReasonUser
)

var syncFuncs struct {
	m     sync.Mutex
	funcs []func()
}

func Synchronize(f func()) {
	syncFuncs.m.Lock()
	defer syncFuncs.m.Unlock()
	syncFuncs.funcs = append(syncFuncs.funcs, f)
}

func runSynchronized() {
	// Clear the list of callbacks first to avoid deadlock
	// if a callback itself calls Synchronize()...
	syncFuncs.m.Lock()
	funcs := syncFuncs.funcs
	syncFuncs.funcs = nil
	syncFuncs.m.Unlock()
	for _, f := range funcs {
		f()
	}
}

type TopLevelWindow struct {
	ContainerBase
	owner             RootWidget
	closingPublisher  CloseEventPublisher
	closeReason       CloseReason
	prevFocusHWnd     HWND
	isInRestoreState  bool
	startingPublisher EventPublisher
}

func (tlw *TopLevelWindow) LayoutFlags() LayoutFlags {
	return ShrinkableHorz | ShrinkableVert | GrowableHorz | GrowableVert | GreedyHorz | GreedyVert
}

func (tlw *TopLevelWindow) SizeHint() Size {
	return tlw.dialogBaseUnitsToPixels(Size{252, 218})
}

func (tlw *TopLevelWindow) Title() string {
	return widgetText(tlw.hWnd)
}

func (tlw *TopLevelWindow) SetTitle(value string) os.Error {
	return setWidgetText(tlw.hWnd, value)
}

func (tlw *TopLevelWindow) Run() int {
	tlw.startingPublisher.Publish()

	var msg MSG

	for tlw.hWnd != 0 {
		runSynchronized()
		switch GetMessage(&msg, 0, 0, 0) {
		case 0:
			return int(msg.WParam)

		case -1:
			return -1
		}

		if !IsDialogMessage(tlw.hWnd, &msg) {
			TranslateMessage(&msg)
			DispatchMessage(&msg)
		}
	}

	return 0
}

func (tlw *TopLevelWindow) Starting() *Event {
	return tlw.startingPublisher.Event()
}

func (tlw *TopLevelWindow) Owner() RootWidget {
	return tlw.owner
}

func (tlw *TopLevelWindow) SetOwner(value RootWidget) os.Error {
	tlw.owner = value

	var ownerHWnd HWND
	if value != nil {
		ownerHWnd = value.BaseWidget().hWnd
	}

	SetLastError(0)
	if 0 == SetWindowLong(tlw.hWnd, GWL_HWNDPARENT, int(ownerHWnd)) && GetLastError() != 0 {
		return lastError("SetWindowLong")
	}

	return nil
}

func (tlw *TopLevelWindow) Hide() {
	tlw.SetVisible(false)
}

func (tlw *TopLevelWindow) Show() {
	tlw.SetVisible(true)
}

func (tlw *TopLevelWindow) close() os.Error {
	tlw.Dispose()

	return nil
}

func (tlw *TopLevelWindow) Close() os.Error {
	SendMessage(tlw.hWnd, WM_CLOSE, 0, 0)

	return nil
}

func (tlw *TopLevelWindow) SaveState() os.Error {
	var wp WINDOWPLACEMENT

	wp.Length = uint(unsafe.Sizeof(wp))

	if !GetWindowPlacement(tlw.hWnd, &wp) {
		return lastError("GetWindowPlacement")
	}

	state := fmt.Sprint(
		wp.Flags, wp.ShowCmd,
		wp.PtMinPosition.X, wp.PtMinPosition.Y,
		wp.PtMaxPosition.X, wp.PtMaxPosition.Y,
		wp.RcNormalPosition.Left, wp.RcNormalPosition.Top,
		wp.RcNormalPosition.Right, wp.RcNormalPosition.Bottom)

	if err := tlw.putState(state); err != nil {
		return err
	}

	return tlw.ContainerBase.SaveState()
}

func (tlw *TopLevelWindow) RestoreState() os.Error {
	if tlw.isInRestoreState {
		return nil
	}
	tlw.isInRestoreState = true
	defer func() {
		tlw.isInRestoreState = false
	}()

	state, err := tlw.getState()
	if err != nil {
		return err
	}
	if state == "" {
		return nil
	}

	var wp WINDOWPLACEMENT

	if _, err := fmt.Sscan(state,
		&wp.Flags, &wp.ShowCmd,
		&wp.PtMinPosition.X, &wp.PtMinPosition.Y,
		&wp.PtMaxPosition.X, &wp.PtMaxPosition.Y,
		&wp.RcNormalPosition.Left, &wp.RcNormalPosition.Top,
		&wp.RcNormalPosition.Right, &wp.RcNormalPosition.Bottom); err != nil {
		return err
	}

	wp.Length = uint(unsafe.Sizeof(wp))

	if !SetWindowPlacement(tlw.hWnd, &wp) {
		return lastError("SetWindowPlacement")
	}

	if err := tlw.ContainerBase.RestoreState(); err != nil {
		return err
	}

	return nil
}

func (tlw *TopLevelWindow) Closing() *CloseEvent {
	return tlw.closingPublisher.Event()
}

func (tlw *TopLevelWindow) wndProc(hwnd HWND, msg uint, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_ACTIVATE:
		switch LOWORD(uint(wParam)) {
		case WA_ACTIVE, WA_CLICKACTIVE:
			if tlw.prevFocusHWnd != 0 {
				SetFocus(tlw.prevFocusHWnd)
			}

		case WA_INACTIVE:
			tlw.prevFocusHWnd = GetFocus()
		}
		return 0

	case WM_CLOSE:
		tlw.closeReason = CloseReasonUnknown
		var canceled bool
		tlw.closingPublisher.Publish(&canceled, tlw.closeReason)
		if !canceled {
			if tlw.owner != nil {
				tlw.owner.SetEnabled(true)
				if !SetWindowPos(tlw.owner.BaseWidget().hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW) {
					lastError("SetWindowPos")
				}
			}

			tlw.close()
		}
		return 0

	case WM_GETMINMAXINFO:
		mmi := (*MINMAXINFO)(unsafe.Pointer(lParam))
		var min Size
		if tlw.layout != nil {
			min = tlw.sizeFromClientSize(tlw.layout.MinSize())
		}
		mmi.PtMinTrackSize = POINT{
			maxi(min.Width, tlw.minSize.Width),
			maxi(min.Height, tlw.minSize.Height),
		}
		return 0

	case WM_SYSCOMMAND:
		if wParam == SC_CLOSE {
			tlw.closeReason = CloseReasonUser
		}
	}

	return tlw.ContainerBase.wndProc(hwnd, msg, wParam, lParam)
}
<|endoftext|>


<commit_before>package transport

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/gammazero/nexus/logger"
	"github.com/gammazero/nexus/transport/serialize"
	"github.com/gammazero/nexus/wamp"
	"github.com/gorilla/websocket"
)

// WebsocketPeer implements the Peer interface, connecting the Send and Recv
// methods to a websocket.
type websocketPeer struct {
	conn        *websocket.Conn
	serializer  serialize.Serializer
	payloadType int

	// Used to signal the websocket is closed.
	closed chan struct{}

	// Channels communicate with router.
	rd chan wamp.Message
	wr chan wamp.Message

	// Stop send handler without closing wr channel.
	stopSend chan struct{}

	log logger.Logger
}

const (
	// WAMP uses the following WebSocket subprotocol identifiers for unbatched
	// modes:
	jsonWebsocketProtocol    = "wamp.2.json"
	msgpackWebsocketProtocol = "wamp.2.msgpack"

	defaultOutQueueSize = 16
	ctrlTimeout         = 5 * time.Second
)

type DialFunc func(network, addr string) (net.Conn, error)

// ConnectWebsockerPeer creates a new websockerPeer with the specified config,
// and connects it to the websocket server at the specified URL.
//
// queueSize is the maximum number of messages that can be queue to be written
// to the websocker.  Once the queue has reached this limit, the WAMP router
// will drop messages in order to not block.  A value of < 1 uses the default
// size.
func ConnectWebsocketPeer(url string, serialization serialize.Serialization, tlsConfig *tls.Config, dial DialFunc, outQueueSize int, logger logger.Logger) (wamp.Peer, error) {
	var (
		protocol    string
		payloadType int
		serializer  serialize.Serializer
	)

	switch serialization {
	case serialize.JSON:
		protocol = jsonWebsocketProtocol
		payloadType = websocket.TextMessage
		serializer = &serialize.JSONSerializer{}
	case serialize.MSGPACK:
		protocol = msgpackWebsocketProtocol
		payloadType = websocket.BinaryMessage
		serializer = &serialize.MessagePackSerializer{}
	default:
		return nil, fmt.Errorf("unsupported serialization: %v", serialization)
	}

	dialer := websocket.Dialer{
		Subprotocols:    []string{protocol},
		TLSClientConfig: tlsConfig,
		Proxy:           http.ProxyFromEnvironment,
		NetDial:         dial,
	}
	conn, _, err := dialer.Dial(url, nil)
	if err != nil {
		return nil, err
	}
	return NewWebsocketPeer(conn, serializer, payloadType, outQueueSize, logger), nil
}

// NewWebsockerPeer creates a websocket peer from an existing websocket
// connection.  This is used for for hanndling clients connecting to the WAMP
// service.
func NewWebsocketPeer(conn *websocket.Conn, serializer serialize.Serializer, payloadType int, outQueueSize int, logger logger.Logger) wamp.Peer {
	if outQueueSize < 1 {
		outQueueSize = defaultOutQueueSize
	}
	w := &websocketPeer{
		conn:        conn,
		serializer:  serializer,
		payloadType: payloadType,
		closed:      make(chan struct{}),
		stopSend:    make(chan struct{}),

		// Messages read from the websocket can be handled immediately, since
		// they have traveled over the websocket and the read channel does not
		// need to be more than size 1.
		rd: make(chan wamp.Message, 1),

		// The channel for messages being written to the websocket sould be
		// large enough to prevent blocking while waiting for a slow websocket
		// to send messages.  For this reason it may be necessary for these
		// messages to be put into an outbound queue that can grow.
		wr: make(chan wamp.Message, outQueueSize),

		log: logger,
	}
	// Sending to and receiving from websocket is handled concurrently.
	go w.recvHandler()
	go w.sendHandler()

	return w
}

func (w *websocketPeer) Recv() <-chan wamp.Message { return w.rd }

func (w *websocketPeer) Send(msg wamp.Message) {
	select {
	case w.wr <- msg:
	default:
		w.log.Println("WARNING: client blocked router.  Dropped:",
			msg.MessageType())
	}
}

func (w *websocketPeer) Close() {
	closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure,
		"goodbye")
	err := w.conn.WriteControl(websocket.CloseMessage, closeMsg,
		time.Now().Add(ctrlTimeout))
	if err != nil {
		w.log.Println("Error sending close message:", err)
	}
	close(w.closed)
	if err = w.conn.Close(); err != nil {
		w.log.Println("Error closing connection:", err)
	}
}

// sendHandler pulls messages from the write channel, and pushes them to the
// websocket.
func (w *websocketPeer) sendHandler() {
	for {
		select {
		case msg, open := <-w.wr:
			if !open {
				return
			}

			b, err := w.serializer.Serialize(msg.(wamp.Message))
			if err != nil {
				w.log.Print(err)
			}

			if err = w.conn.WriteMessage(w.payloadType, b); err != nil {
				w.log.Print(err)
			}
		case <-w.stopSend:
			return
		}
	}
}

// recvHandler pulls messages from the websocket and pushes them to the read
// channel.
func (w *websocketPeer) recvHandler() {
	for {
		msgType, b, err := w.conn.ReadMessage()
		if err != nil {
			select {
			case <-w.closed:
				w.log.Print("Peer connection closed")
			default:
				w.log.Println("Cannot read from peer:", err)
				w.conn.Close()
			}
			break
		}

		if msgType == websocket.CloseMessage {
			w.conn.Close()
			break
		}

		msg, err := w.serializer.Deserialize(b)
		if err != nil {
			// TODO: something more than merely logging?
			w.log.Println("Cannot deserialize peer message:", err)
			continue
		}
		// It is OK for the router to block a client since routing should be
		// very quick compared to the time to transfer a message over
		// websocket, and a blocked client will not block other clients.
		w.rd <- msg
	}
	// Close read channel, cause router to remove session if not already.
	close(w.rd)
	// Stop sendHandler, without closing write channel.
	close(w.stopSend)
}
<commit_msg>cleanup websocket close<commit_after>package transport

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/gammazero/nexus/logger"
	"github.com/gammazero/nexus/transport/serialize"
	"github.com/gammazero/nexus/wamp"
	"github.com/gorilla/websocket"
)

// WebsocketPeer implements the Peer interface, connecting the Send and Recv
// methods to a websocket.
type websocketPeer struct {
	conn        *websocket.Conn
	serializer  serialize.Serializer
	payloadType int

	// Used to signal the websocket is closed explicitly.
	closed chan struct{}

	// Channels communicate with router.
	rd chan wamp.Message
	wr chan wamp.Message

	wsWriterDone chan struct{}

	log logger.Logger
}

const (
	// WAMP uses the following WebSocket subprotocol identifiers for unbatched
	// modes:
	jsonWebsocketProtocol    = "wamp.2.json"
	msgpackWebsocketProtocol = "wamp.2.msgpack"

	defaultOutQueueSize = 16
	ctrlTimeout         = 5 * time.Second
)

type DialFunc func(network, addr string) (net.Conn, error)

// ConnectWebsockerPeer creates a new websockerPeer with the specified config,
// and connects it to the websocket server at the specified URL.
//
// queueSize is the maximum number of messages that can be queue to be written
// to the websocker.  Once the queue has reached this limit, the WAMP router
// will drop messages in order to not block.  A value of < 1 uses the default
// size.
func ConnectWebsocketPeer(url string, serialization serialize.Serialization, tlsConfig *tls.Config, dial DialFunc, outQueueSize int, logger logger.Logger) (wamp.Peer, error) {
	var (
		protocol    string
		payloadType int
		serializer  serialize.Serializer
	)

	switch serialization {
	case serialize.JSON:
		protocol = jsonWebsocketProtocol
		payloadType = websocket.TextMessage
		serializer = &serialize.JSONSerializer{}
	case serialize.MSGPACK:
		protocol = msgpackWebsocketProtocol
		payloadType = websocket.BinaryMessage
		serializer = &serialize.MessagePackSerializer{}
	default:
		return nil, fmt.Errorf("unsupported serialization: %v", serialization)
	}

	dialer := websocket.Dialer{
		Subprotocols:    []string{protocol},
		TLSClientConfig: tlsConfig,
		Proxy:           http.ProxyFromEnvironment,
		NetDial:         dial,
	}
	conn, _, err := dialer.Dial(url, nil)
	if err != nil {
		return nil, err
	}
	return NewWebsocketPeer(conn, serializer, payloadType, outQueueSize, logger), nil
}

// NewWebsockerPeer creates a websocket peer from an existing websocket
// connection.  This is used for for hanndling clients connecting to the WAMP
// service.
func NewWebsocketPeer(conn *websocket.Conn, serializer serialize.Serializer, payloadType int, outQueueSize int, logger logger.Logger) wamp.Peer {
	if outQueueSize < 1 {
		outQueueSize = defaultOutQueueSize
	}
	w := &websocketPeer{
		conn:         conn,
		serializer:   serializer,
		payloadType:  payloadType,
		closed:       make(chan struct{}),
		wsWriterDone: make(chan struct{}),

		// Messages read from the websocket can be handled immediately, since
		// they have traveled over the websocket and the read channel does not
		// need to be more than size 1.
		rd: make(chan wamp.Message, 1),

		// The channel for messages being written to the websocket sould be
		// large enough to prevent blocking while waiting for a slow websocket
		// to send messages.  For this reason it may be necessary for these
		// messages to be put into an outbound queue that can grow.
		wr: make(chan wamp.Message, outQueueSize),

		log: logger,
	}
	// Sending to and receiving from websocket is handled concurrently.
	go w.recvHandler()
	go w.sendHandler()

	return w
}

func (w *websocketPeer) Recv() <-chan wamp.Message { return w.rd }

func (w *websocketPeer) Send(msg wamp.Message) {
	select {
	case w.wr <- msg:
	default:
		w.log.Println("WARNING: client blocked router.  Dropped:",
			msg.MessageType())
	}
}

// Close closes the websocker peer.  This closes the local send channel, and
// sends a close control message to the websocket to tell the other side to
// close.
//
// *** Do not call Send after calling Close. ***
func (w *websocketPeer) Close() {
	// Tell sendHandler to exit, allowing it to finish sending any queued
	// messages.  Do not close wr channel in case there are incoming messages
	// during close.
	w.wr <- nil
	<-w.wsWriterDone

	closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure,
		"goodbye")

	// Tell recvHandler to close.
	close(w.closed)

	// Ignore errors since websocket may have been closed by other side first
	// in response to a goodbye message.
	w.conn.WriteControl(websocket.CloseMessage, closeMsg,
		time.Now().Add(ctrlTimeout))
	w.conn.Close()
}

// sendHandler pulls messages from the write channel, and pushes them to the
// websocket.
func (w *websocketPeer) sendHandler() {
	defer close(w.wsWriterDone)
	for msg := range w.wr {
		if msg == nil {
			return
		}
		b, err := w.serializer.Serialize(msg.(wamp.Message))
		if err != nil {
			w.log.Print(err)
		}

		if err = w.conn.WriteMessage(w.payloadType, b); err != nil {
			w.log.Print(err)
		}
	}
}

// recvHandler pulls messages from the websocket and pushes them to the read
// channel.
func (w *websocketPeer) recvHandler() {
	for {
		msgType, b, err := w.conn.ReadMessage()
		if err != nil {
			select {
			case <-w.closed:
				// Peer was closed explicitly. sendHandler should have already
				// been told to exit.
			default:
				// Peer received control message to close.  Cause sendHandler
				// to exit without closing the write channel (in case writes
				// still happening) and allow it to finish sending any queued
				// messages.
				w.wr <- nil
				<-w.wsWriterDone

				// Close websocket connection.
				w.conn.Close()
			}
			if !strings.HasPrefix(err.Error(), "websocket: close 1000") {
				w.log.Print(err)
			}
			break
		}

		if msgType == websocket.CloseMessage {
			w.conn.Close()
			break
		}

		msg, err := w.serializer.Deserialize(b)
		if err != nil {
			// TODO: something more than merely logging?
			w.log.Println("Cannot deserialize peer message:", err)
			continue
		}
		// It is OK for the router to block a client since routing should be
		// very quick compared to the time to transfer a message over
		// websocket, and a blocked client will not block other clients.
		w.rd <- msg
	}
	// Close read channel, cause router to remove session if not already.
	close(w.rd)
}
<|endoftext|>


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

package http

import (
	"log"
	"net"
	"strconv"
	"strings"
	"time"
)

// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an
// HTTP response or the Cookie header of an HTTP request.
//
// See https://tools.ietf.org/html/rfc6265 for details.
type Cookie struct {
	Name  string
	Value string

	Path       string    // optional
	Domain     string    // optional
	Expires    time.Time // optional
	RawExpires string    // for reading cookies only

	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
	// MaxAge>0 means Max-Age attribute present and given in seconds
	MaxAge   int
	Secure   bool
	HttpOnly bool
	SameSite SameSite
	Raw      string
	Unparsed []string // Raw text of unparsed attribute-value pairs
}

// SameSite allows a server define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests. The main
// goal is mitigate the risk of cross-origin information leakage, and provides
// some protection against cross-site request forgery attacks.
//
// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.
type SameSite int

const (
	SameSiteDefaultMode SameSite = iota + 1
	SameSiteLaxMode
	SameSiteStrictMode
)

// readSetCookies parses all "Set-Cookie" values from
// the header h and returns the successfully parsed Cookies.
func readSetCookies(h Header) []*Cookie {
	cookieCount := len(h["Set-Cookie"])
	if cookieCount == 0 {
		return []*Cookie{}
	}
	cookies := make([]*Cookie, 0, cookieCount)
	for _, line := range h["Set-Cookie"] {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		parts[0] = strings.TrimSpace(parts[0])
		j := strings.Index(parts[0], "=")
		if j < 0 {
			continue
		}
		name, value := parts[0][:j], parts[0][j+1:]
		if !isCookieNameValid(name) {
			continue
		}
		value, ok := parseCookieValue(value, true)
		if !ok {
			continue
		}
		c := &Cookie{
			Name:  name,
			Value: value,
			Raw:   line,
		}
		for i := 1; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}

			attr, val := parts[i], ""
			if j := strings.Index(attr, "="); j >= 0 {
				attr, val = attr[:j], attr[j+1:]
			}
			lowerAttr := strings.ToLower(attr)
			val, ok = parseCookieValue(val, false)
			if !ok {
				c.Unparsed = append(c.Unparsed, parts[i])
				continue
			}
			switch lowerAttr {
			case "samesite":
				lowerVal := strings.ToLower(val)
				switch lowerVal {
				case "lax":
					c.SameSite = SameSiteLaxMode
				case "strict":
					c.SameSite = SameSiteStrictMode
				default:
					c.SameSite = SameSiteDefaultMode
				}
				continue
			case "secure":
				c.Secure = true
				continue
			case "httponly":
				c.HttpOnly = true
				continue
			case "domain":
				c.Domain = val
				continue
			case "max-age":
				secs, err := strconv.Atoi(val)
				if err != nil || secs != 0 && val[0] == '0' {
					break
				}
				if secs <= 0 {
					secs = -1
				}
				c.MaxAge = secs
				continue
			case "expires":
				c.RawExpires = val
				exptime, err := time.Parse(time.RFC1123, val)
				if err != nil {
					exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val)
					if err != nil {
						c.Expires = time.Time{}
						break
					}
				}
				c.Expires = exptime.UTC()
				continue
			case "path":
				c.Path = val
				continue
			}
			c.Unparsed = append(c.Unparsed, parts[i])
		}
		cookies = append(cookies, c)
	}
	return cookies
}

// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func SetCookie(w ResponseWriter, cookie *Cookie) {
	if v := cookie.String(); v != "" {
		w.Header().Add("Set-Cookie", v)
	}
}

// String returns the serialization of the cookie for use in a Cookie
// header (if only Name and Value are set) or a Set-Cookie response
// header (if other fields are set).
// If c is nil or c.Name is invalid, the empty string is returned.
func (c *Cookie) String() string {
	if c == nil || !isCookieNameValid(c.Name) {
		return ""
	}
	var b strings.Builder
	b.WriteString(sanitizeCookieName(c.Name))
	b.WriteRune('=')
	b.WriteString(sanitizeCookieValue(c.Value))

	if len(c.Path) > 0 {
		b.WriteString("; Path=")
		b.WriteString(sanitizeCookiePath(c.Path))
	}
	if len(c.Domain) > 0 {
		if validCookieDomain(c.Domain) {
			// A c.Domain containing illegal characters is not
			// sanitized but simply dropped which turns the cookie
			// into a host-only cookie. A leading dot is okay
			// but won't be sent.
			d := c.Domain
			if d[0] == '.' {
				d = d[1:]
			}
			b.WriteString("; Domain=")
			b.WriteString(d)
		} else {
			log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain)
		}
	}
	var buf [len(TimeFormat)]byte
	if validCookieExpires(c.Expires) {
		b.WriteString("; Expires=")
		b.Write(c.Expires.UTC().AppendFormat(buf[:0], TimeFormat))
	}
	if c.MaxAge > 0 {
		b.WriteString("; Max-Age=")
		b.Write(strconv.AppendInt(buf[:0], int64(c.MaxAge), 10))
	} else if c.MaxAge < 0 {
		b.WriteString("; Max-Age=0")
	}
	if c.HttpOnly {
		b.WriteString("; HttpOnly")
	}
	if c.Secure {
		b.WriteString("; Secure")
	}
	switch c.SameSite {
	case SameSiteDefaultMode:
		b.WriteString("; SameSite")
	case SameSiteLaxMode:
		b.WriteString("; SameSite=Lax")
	case SameSiteStrictMode:
		b.WriteString("; SameSite=Strict")
	}
	return b.String()
}

// readCookies parses all "Cookie" values from the header h and
// returns the successfully parsed Cookies.
//
// if filter isn't empty, only cookies of that name are returned
func readCookies(h Header, filter string) []*Cookie {
	lines, ok := h["Cookie"]
	if !ok {
		return []*Cookie{}
	}

	cookies := []*Cookie{}
	for _, line := range lines {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		// Per-line attributes
		for i := 0; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}
			name, val := parts[i], ""
			if j := strings.Index(name, "="); j >= 0 {
				name, val = name[:j], name[j+1:]
			}
			if !isCookieNameValid(name) {
				continue
			}
			if filter != "" && filter != name {
				continue
			}
			val, ok := parseCookieValue(val, true)
			if !ok {
				continue
			}
			cookies = append(cookies, &Cookie{Name: name, Value: val})
		}
	}
	return cookies
}

// validCookieDomain returns whether v is a valid cookie domain-value.
func validCookieDomain(v string) bool {
	if isCookieDomainName(v) {
		return true
	}
	if net.ParseIP(v) != nil && !strings.Contains(v, ":") {
		return true
	}
	return false
}

// validCookieExpires returns whether v is a valid cookie expires-value.
func validCookieExpires(t time.Time) bool {
	// IETF RFC 6265 Section 5.1.1.5, the year must not be less than 1601
	return t.Year() >= 1601
}

// isCookieDomainName returns whether s is a valid domain name or a valid
// domain name with a leading dot '.'.  It is almost a direct copy of
// package net's isDomainName.
func isCookieDomainName(s string) bool {
	if len(s) == 0 {
		return false
	}
	if len(s) > 255 {
		return false
	}

	if s[0] == '.' {
		// A cookie a domain attribute may start with a leading dot.
		s = s[1:]
	}
	last := byte('.')
	ok := false // Ok once we've seen a letter.
	partlen := 0
	for i := 0; i < len(s); i++ {
		c := s[i]
		switch {
		default:
			return false
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
			// No '_' allowed here (in contrast to package net).
			ok = true
			partlen++
		case '0' <= c && c <= '9':
			// fine
			partlen++
		case c == '-':
			// Byte before dash cannot be dot.
			if last == '.' {
				return false
			}
			partlen++
		case c == '.':
			// Byte before dot cannot be dot, dash.
			if last == '.' || last == '-' {
				return false
			}
			if partlen > 63 || partlen == 0 {
				return false
			}
			partlen = 0
		}
		last = c
	}
	if last == '-' || partlen > 63 {
		return false
	}

	return ok
}

var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")

func sanitizeCookieName(n string) string {
	return cookieNameSanitizer.Replace(n)
}

// https://tools.ietf.org/html/rfc6265#section-4.1.1
// cookie-value      = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
// cookie-octet      = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
//           ; US-ASCII characters excluding CTLs,
//           ; whitespace DQUOTE, comma, semicolon,
//           ; and backslash
// We loosen this as spaces and commas are common in cookie values
// but we produce a quoted cookie-value in when value starts or ends
// with a comma or space.
// See https://golang.org/issue/7243 for the discussion.
func sanitizeCookieValue(v string) string {
	v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
	if len(v) == 0 {
		return v
	}
	if strings.IndexByte(v, ' ') >= 0 || strings.IndexByte(v, ',') >= 0 {
		return `"` + v + `"`
	}
	return v
}

func validCookieValueByte(b byte) bool {
	return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
}

// path-av           = "Path=" path-value
// path-value        = <any CHAR except CTLs or ";">
func sanitizeCookiePath(v string) string {
	return sanitizeOrWarn("Cookie.Path", validCookiePathByte, v)
}

func validCookiePathByte(b byte) bool {
	return 0x20 <= b && b < 0x7f && b != ';'
}

func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string {
	ok := true
	for i := 0; i < len(v); i++ {
		if valid(v[i]) {
			continue
		}
		log.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName)
		ok = false
		break
	}
	if ok {
		return v
	}
	buf := make([]byte, 0, len(v))
	for i := 0; i < len(v); i++ {
		if b := v[i]; valid(b) {
			buf = append(buf, b)
		}
	}
	return string(buf)
}

func parseCookieValue(raw string, allowDoubleQuote bool) (string, bool) {
	// Strip the quotes, if present.
	if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
		raw = raw[1 : len(raw)-1]
	}
	for i := 0; i < len(raw); i++ {
		if !validCookieValueByte(raw[i]) {
			return "", false
		}
	}
	return raw, true
}

func isCookieNameValid(raw string) bool {
	if raw == "" {
		return false
	}
	return strings.IndexFunc(raw, isNotToken) < 0
}
<commit_msg>net/http: add missing words to SameSite doc/comments<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"log"
	"net"
	"strconv"
	"strings"
	"time"
)

// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an
// HTTP response or the Cookie header of an HTTP request.
//
// See https://tools.ietf.org/html/rfc6265 for details.
type Cookie struct {
	Name  string
	Value string

	Path       string    // optional
	Domain     string    // optional
	Expires    time.Time // optional
	RawExpires string    // for reading cookies only

	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
	// MaxAge>0 means Max-Age attribute present and given in seconds
	MaxAge   int
	Secure   bool
	HttpOnly bool
	SameSite SameSite
	Raw      string
	Unparsed []string // Raw text of unparsed attribute-value pairs
}

// SameSite allows a server to define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests. The main
// goal is to mitigate the risk of cross-origin information leakage, and provides
// some protection against cross-site request forgery attacks.
//
// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.
type SameSite int

const (
	SameSiteDefaultMode SameSite = iota + 1
	SameSiteLaxMode
	SameSiteStrictMode
)

// readSetCookies parses all "Set-Cookie" values from
// the header h and returns the successfully parsed Cookies.
func readSetCookies(h Header) []*Cookie {
	cookieCount := len(h["Set-Cookie"])
	if cookieCount == 0 {
		return []*Cookie{}
	}
	cookies := make([]*Cookie, 0, cookieCount)
	for _, line := range h["Set-Cookie"] {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		parts[0] = strings.TrimSpace(parts[0])
		j := strings.Index(parts[0], "=")
		if j < 0 {
			continue
		}
		name, value := parts[0][:j], parts[0][j+1:]
		if !isCookieNameValid(name) {
			continue
		}
		value, ok := parseCookieValue(value, true)
		if !ok {
			continue
		}
		c := &Cookie{
			Name:  name,
			Value: value,
			Raw:   line,
		}
		for i := 1; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}

			attr, val := parts[i], ""
			if j := strings.Index(attr, "="); j >= 0 {
				attr, val = attr[:j], attr[j+1:]
			}
			lowerAttr := strings.ToLower(attr)
			val, ok = parseCookieValue(val, false)
			if !ok {
				c.Unparsed = append(c.Unparsed, parts[i])
				continue
			}
			switch lowerAttr {
			case "samesite":
				lowerVal := strings.ToLower(val)
				switch lowerVal {
				case "lax":
					c.SameSite = SameSiteLaxMode
				case "strict":
					c.SameSite = SameSiteStrictMode
				default:
					c.SameSite = SameSiteDefaultMode
				}
				continue
			case "secure":
				c.Secure = true
				continue
			case "httponly":
				c.HttpOnly = true
				continue
			case "domain":
				c.Domain = val
				continue
			case "max-age":
				secs, err := strconv.Atoi(val)
				if err != nil || secs != 0 && val[0] == '0' {
					break
				}
				if secs <= 0 {
					secs = -1
				}
				c.MaxAge = secs
				continue
			case "expires":
				c.RawExpires = val
				exptime, err := time.Parse(time.RFC1123, val)
				if err != nil {
					exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val)
					if err != nil {
						c.Expires = time.Time{}
						break
					}
				}
				c.Expires = exptime.UTC()
				continue
			case "path":
				c.Path = val
				continue
			}
			c.Unparsed = append(c.Unparsed, parts[i])
		}
		cookies = append(cookies, c)
	}
	return cookies
}

// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func SetCookie(w ResponseWriter, cookie *Cookie) {
	if v := cookie.String(); v != "" {
		w.Header().Add("Set-Cookie", v)
	}
}

// String returns the serialization of the cookie for use in a Cookie
// header (if only Name and Value are set) or a Set-Cookie response
// header (if other fields are set).
// If c is nil or c.Name is invalid, the empty string is returned.
func (c *Cookie) String() string {
	if c == nil || !isCookieNameValid(c.Name) {
		return ""
	}
	var b strings.Builder
	b.WriteString(sanitizeCookieName(c.Name))
	b.WriteRune('=')
	b.WriteString(sanitizeCookieValue(c.Value))

	if len(c.Path) > 0 {
		b.WriteString("; Path=")
		b.WriteString(sanitizeCookiePath(c.Path))
	}
	if len(c.Domain) > 0 {
		if validCookieDomain(c.Domain) {
			// A c.Domain containing illegal characters is not
			// sanitized but simply dropped which turns the cookie
			// into a host-only cookie. A leading dot is okay
			// but won't be sent.
			d := c.Domain
			if d[0] == '.' {
				d = d[1:]
			}
			b.WriteString("; Domain=")
			b.WriteString(d)
		} else {
			log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain)
		}
	}
	var buf [len(TimeFormat)]byte
	if validCookieExpires(c.Expires) {
		b.WriteString("; Expires=")
		b.Write(c.Expires.UTC().AppendFormat(buf[:0], TimeFormat))
	}
	if c.MaxAge > 0 {
		b.WriteString("; Max-Age=")
		b.Write(strconv.AppendInt(buf[:0], int64(c.MaxAge), 10))
	} else if c.MaxAge < 0 {
		b.WriteString("; Max-Age=0")
	}
	if c.HttpOnly {
		b.WriteString("; HttpOnly")
	}
	if c.Secure {
		b.WriteString("; Secure")
	}
	switch c.SameSite {
	case SameSiteDefaultMode:
		b.WriteString("; SameSite")
	case SameSiteLaxMode:
		b.WriteString("; SameSite=Lax")
	case SameSiteStrictMode:
		b.WriteString("; SameSite=Strict")
	}
	return b.String()
}

// readCookies parses all "Cookie" values from the header h and
// returns the successfully parsed Cookies.
//
// if filter isn't empty, only cookies of that name are returned
func readCookies(h Header, filter string) []*Cookie {
	lines, ok := h["Cookie"]
	if !ok {
		return []*Cookie{}
	}

	cookies := []*Cookie{}
	for _, line := range lines {
		parts := strings.Split(strings.TrimSpace(line), ";")
		if len(parts) == 1 && parts[0] == "" {
			continue
		}
		// Per-line attributes
		for i := 0; i < len(parts); i++ {
			parts[i] = strings.TrimSpace(parts[i])
			if len(parts[i]) == 0 {
				continue
			}
			name, val := parts[i], ""
			if j := strings.Index(name, "="); j >= 0 {
				name, val = name[:j], name[j+1:]
			}
			if !isCookieNameValid(name) {
				continue
			}
			if filter != "" && filter != name {
				continue
			}
			val, ok := parseCookieValue(val, true)
			if !ok {
				continue
			}
			cookies = append(cookies, &Cookie{Name: name, Value: val})
		}
	}
	return cookies
}

// validCookieDomain returns whether v is a valid cookie domain-value.
func validCookieDomain(v string) bool {
	if isCookieDomainName(v) {
		return true
	}
	if net.ParseIP(v) != nil && !strings.Contains(v, ":") {
		return true
	}
	return false
}

// validCookieExpires returns whether v is a valid cookie expires-value.
func validCookieExpires(t time.Time) bool {
	// IETF RFC 6265 Section 5.1.1.5, the year must not be less than 1601
	return t.Year() >= 1601
}

// isCookieDomainName returns whether s is a valid domain name or a valid
// domain name with a leading dot '.'.  It is almost a direct copy of
// package net's isDomainName.
func isCookieDomainName(s string) bool {
	if len(s) == 0 {
		return false
	}
	if len(s) > 255 {
		return false
	}

	if s[0] == '.' {
		// A cookie a domain attribute may start with a leading dot.
		s = s[1:]
	}
	last := byte('.')
	ok := false // Ok once we've seen a letter.
	partlen := 0
	for i := 0; i < len(s); i++ {
		c := s[i]
		switch {
		default:
			return false
		case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
			// No '_' allowed here (in contrast to package net).
			ok = true
			partlen++
		case '0' <= c && c <= '9':
			// fine
			partlen++
		case c == '-':
			// Byte before dash cannot be dot.
			if last == '.' {
				return false
			}
			partlen++
		case c == '.':
			// Byte before dot cannot be dot, dash.
			if last == '.' || last == '-' {
				return false
			}
			if partlen > 63 || partlen == 0 {
				return false
			}
			partlen = 0
		}
		last = c
	}
	if last == '-' || partlen > 63 {
		return false
	}

	return ok
}

var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")

func sanitizeCookieName(n string) string {
	return cookieNameSanitizer.Replace(n)
}

// https://tools.ietf.org/html/rfc6265#section-4.1.1
// cookie-value      = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
// cookie-octet      = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
//           ; US-ASCII characters excluding CTLs,
//           ; whitespace DQUOTE, comma, semicolon,
//           ; and backslash
// We loosen this as spaces and commas are common in cookie values
// but we produce a quoted cookie-value in when value starts or ends
// with a comma or space.
// See https://golang.org/issue/7243 for the discussion.
func sanitizeCookieValue(v string) string {
	v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
	if len(v) == 0 {
		return v
	}
	if strings.IndexByte(v, ' ') >= 0 || strings.IndexByte(v, ',') >= 0 {
		return `"` + v + `"`
	}
	return v
}

func validCookieValueByte(b byte) bool {
	return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
}

// path-av           = "Path=" path-value
// path-value        = <any CHAR except CTLs or ";">
func sanitizeCookiePath(v string) string {
	return sanitizeOrWarn("Cookie.Path", validCookiePathByte, v)
}

func validCookiePathByte(b byte) bool {
	return 0x20 <= b && b < 0x7f && b != ';'
}

func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string {
	ok := true
	for i := 0; i < len(v); i++ {
		if valid(v[i]) {
			continue
		}
		log.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName)
		ok = false
		break
	}
	if ok {
		return v
	}
	buf := make([]byte, 0, len(v))
	for i := 0; i < len(v); i++ {
		if b := v[i]; valid(b) {
			buf = append(buf, b)
		}
	}
	return string(buf)
}

func parseCookieValue(raw string, allowDoubleQuote bool) (string, bool) {
	// Strip the quotes, if present.
	if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
		raw = raw[1 : len(raw)-1]
	}
	for i := 0; i < len(raw); i++ {
		if !validCookieValueByte(raw[i]) {
			return "", false
		}
	}
	return raw, true
}

func isCookieNameValid(raw string) bool {
	if raw == "" {
		return false
	}
	return strings.IndexFunc(raw, isNotToken) < 0
}
<|endoftext|>