<commit_before>package main

import (
	"fmt"
	"strings"
	"time"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

const MSG_BUFFER int = 50
const MAX_MSG_LENGTH int = 512

const HELP_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> Available commands:
   /about           - About this chat
   /exit            - Exit the chat
   /help            - Show this help text
   /list            - List the users that are currently connected
   /beep            - Enable BEL notifications on mention.
   /me $ACTION      - Show yourself doing an action
   /nick $NAME      - Rename yourself to a new name
   /whois $NAME     - Display information about another connected user
   /msg $NAME $MESSAGE
` + RESET

const OP_HELP_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> Available operator commands:
   /ban $NAME       - Banish a user from the chat
   /kick $NAME      - Kick em' out.
   /op $NAME        - Promote a user to server operator
   /silence $NAME   - Revoke a user's ability to speak
`

const ABOUT_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> ssh-chat is made by @shazow.

   It is a custom ssh server built in Go to serve a chat experience
   instead of a shell.

   Source: https://github.com/shazow/ssh-chat

   For more, visit shazow.net or follow at twitter.com/shazow
` + RESET

const REQUIRED_WAIT time.Duration = time.Second / 2

type Client struct {
	Server        *Server
	Conn          *ssh.ServerConn
	Msg           chan string
	Name          string
	Color         string
	Op            bool
	ready         chan struct{}
	term          *terminal.Terminal
	termWidth     int
	termHeight    int
	silencedUntil time.Time
	lastTX        time.Time
	beepMe        bool
}

func NewClient(server *Server, conn *ssh.ServerConn) *Client {
	return &Client{
		Server: server,
		Conn:   conn,
		Name:   conn.User(),
		Color:  RandomColor256(),
		Msg:    make(chan string, MSG_BUFFER),
		ready:  make(chan struct{}, 1),
		lastTX: time.Now(),
	}
}

func (c *Client) ColoredName() string {
	return ColorString(c.Color, c.Name)
}

func (c *Client) SysMsg(msg string, args ...interface{}) {
	c.Msg <- ContinuousFormat(SYSTEM_MESSAGE_FORMAT, "-> "+fmt.Sprintf(msg, args...))
}

func (c *Client) Write(msg string) {
	c.term.Write([]byte(msg + "\r\n"))
}

func (c *Client) WriteLines(msg []string) {
	for _, line := range msg {
		c.Write(line)
	}
}

func (c *Client) Send(msg string) {
	if len(msg) > MAX_MSG_LENGTH {
		return
	}
	select {
	case c.Msg <- msg:
	default:
		logger.Errorf("Msg buffer full, dropping: %s (%s)", c.Name, c.Conn.RemoteAddr())
		c.Conn.Close()
	}
}

func (c *Client) SendLines(msg []string) {
	for _, line := range msg {
		c.Send(line)
	}
}

func (c *Client) IsSilenced() bool {
	return c.silencedUntil.After(time.Now())
}

func (c *Client) Silence(d time.Duration) {
	c.silencedUntil = time.Now().Add(d)
}

func (c *Client) Resize(width int, height int) error {
	err := c.term.SetSize(width, height)
	if err != nil {
		logger.Errorf("Resize failed: %dx%d", width, height)
		return err
	}
	c.termWidth, c.termHeight = width, height
	return nil
}

func (c *Client) Rename(name string) {
	c.Name = name
	c.term.SetPrompt(fmt.Sprintf("[%s] ", c.ColoredName()))
}

func (c *Client) Fingerprint() string {
	return c.Conn.Permissions.Extensions["fingerprint"]
}

func (c *Client) handleShell(channel ssh.Channel) {
	defer channel.Close()

	// FIXME: This shouldn't live here, need to restructure the call chaining.
	c.Server.Add(c)
	go func() {
		// Block until done, then remove.
		c.Conn.Wait()
		c.Server.Remove(c)
	}()

	go func() {
		for msg := range c.Msg {
			c.Write(msg)
		}
	}()

	for {
		line, err := c.term.ReadLine()
		if err != nil {
			break
		}

		parts := strings.SplitN(line, " ", 3)
		isCmd := strings.HasPrefix(parts[0], "/")

		if isCmd {
			// TODO: Factor this out.
			switch parts[0] {
			case "/test-colors": // Shh, this command is a secret!
				c.Write(ColorString("32", "Lorem ipsum dolor sit amet,"))
				c.Write("consectetur " + ColorString("31;1", "adipiscing") + " elit.")
			case "/exit":
				channel.Close()
			case "/help":
				c.WriteLines(strings.Split(HELP_TEXT, "\n"))
				if c.Server.IsOp(c) {
					c.WriteLines(strings.Split(OP_HELP_TEXT, "\n"))
				}
			case "/about":
				c.WriteLines(strings.Split(ABOUT_TEXT, "\n"))
			case "/uptime":
				c.Write(c.Server.Uptime())
			case "/beep":
				c.beepMe = !c.beepMe
				if c.beepMe {
					c.SysMsg("I'll beep you good.")
				} else {
					c.SysMsg("No more beeps. :(")
				}
			case "/me":
				me := strings.TrimLeft(line, "/me")
				if me == "" {
					me = " is at a loss for words."
				}
				msg := fmt.Sprintf("** %s%s", c.ColoredName(), me)
				if c.IsSilenced() || len(msg) > 1000 {
					c.SysMsg("Message rejected.")
				} else {
					c.Server.Broadcast(msg, nil)
				}
			case "/nick":
				if len(parts) == 2 {
					c.Server.Rename(c, parts[1])
				} else {
					c.SysMsg("Missing $NAME from: /nick $NAME")
				}
			case "/whois":
				if len(parts) == 2 {
					client := c.Server.Who(parts[1])
					if client != nil {
						version := RE_STRIP_TEXT.ReplaceAllString(string(client.Conn.ClientVersion()), "")
						if len(version) > 100 {
							version = "Evil Jerk with a superlong string"
						}
						c.SysMsg("%s is %s via %s", client.ColoredName(), client.Fingerprint(), version)
					} else {
						c.SysMsg("No such name: %s", parts[1])
					}
				} else {
					c.SysMsg("Missing $NAME from: /whois $NAME")
				}
			case "/list":
				names := c.Server.List(nil)
				c.SysMsg("%d connected: %s", len(names), strings.Join(names, ", "))
			case "/ban":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) != 2 {
					c.SysMsg("Missing $NAME from: /ban $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.SysMsg("Banned by %s.", c.ColoredName())
						c.Server.Ban(fingerprint, nil)
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.ColoredName()), nil)
					}
				}
			case "/op":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) != 2 {
					c.SysMsg("Missing $NAME from: /op $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.SysMsg("Made op by %s.", c.ColoredName())
						c.Server.Op(fingerprint)
					}
				}
			case "/kick":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) != 2 {
					c.SysMsg("Missing $NAME from: /kick $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						client.SysMsg("Kicked by %s.", c.ColoredName())
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was kicked by %s", parts[1], c.ColoredName()), nil)
					}
				}
			case "/silence":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) < 2 {
					c.SysMsg("Missing $NAME from: /silence $NAME")
				} else {
					duration := time.Duration(5) * time.Minute
					if len(parts) >= 3 {
						parsedDuration, err := time.ParseDuration(parts[2])
						if err == nil {
							duration = parsedDuration
						}
					}
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						client.Silence(duration)
						client.SysMsg("Silenced for %s by %s.", duration, c.ColoredName())
					}
				}
			case "/msg": /* Send a PM */
				/* Make sure we have a recipient and a message */
				if len(parts) < 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /msg $NAME $MESSAGE")
					break
				} else if len(parts) < 3 {
					c.Msg <- fmt.Sprintf("-> Missing $MESSAGE from: /msg $NAME $MESSAGE")
					break
				}
				/* Ask the server to send the message */
				if err := c.Server.Privmsg(parts[1], parts[2], c); nil != err {
					c.Msg <- fmt.Sprintf("Unable to send message to %v: %v", parts[1], err)
				}

			default:
				c.SysMsg("Invalid command: %s", line)
			}
			continue
		}

		msg := fmt.Sprintf("%s: %s", c.ColoredName(), line)
		/* Rate limit */
		if time.Now().Sub(c.lastTX) < REQUIRED_WAIT {
			c.Msg <- fmt.Sprintf("-> Rate limiting in effect.")
			continue
		}
		if c.IsSilenced() || len(msg) > 1000 || len(msg) == 0 {
			c.SysMsg("Message rejected.")
			continue
		}
		c.Server.Broadcast(msg, c)
		c.lastTX = time.Now()
	}

}

func (c *Client) handleChannels(channels <-chan ssh.NewChannel) {
	prompt := fmt.Sprintf("[%s] ", c.ColoredName())

	hasShell := false

	for ch := range channels {
		if t := ch.ChannelType(); t != "session" {
			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		channel, requests, err := ch.Accept()
		if err != nil {
			logger.Errorf("Could not accept channel: %v", err)
			continue
		}
		defer channel.Close()

		c.term = terminal.NewTerminal(channel, prompt)
		c.term.AutoCompleteCallback = c.Server.AutoCompleteFunction

		for req := range requests {
			var width, height int
			var ok bool

			switch req.Type {
			case "shell":
				if c.term != nil && !hasShell {
					go c.handleShell(channel)
					ok = true
					hasShell = true
				}
			case "pty-req":
				width, height, ok = parsePtyRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			case "window-change":
				width, height, ok = parseWinchRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			}

			if req.WantReply {
				req.Reply(ok, nil)
			}
		}
	}
}
<commit_msg>missing reset.<commit_after>package main

import (
	"fmt"
	"strings"
	"time"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

const MSG_BUFFER int = 50
const MAX_MSG_LENGTH int = 512

const HELP_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> Available commands:
   /about           - About this chat
   /exit            - Exit the chat
   /help            - Show this help text
   /list            - List the users that are currently connected
   /beep            - Enable BEL notifications on mention.
   /me $ACTION      - Show yourself doing an action
   /nick $NAME      - Rename yourself to a new name
   /whois $NAME     - Display information about another connected user
   /msg $NAME $MESSAGE
` + RESET

const OP_HELP_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> Available operator commands:
   /ban $NAME       - Banish a user from the chat
   /kick $NAME      - Kick em' out.
   /op $NAME        - Promote a user to server operator
   /silence $NAME   - Revoke a user's ability to speak
` + RESET

const ABOUT_TEXT string = SYSTEM_MESSAGE_FORMAT + `-> ssh-chat is made by @shazow.

   It is a custom ssh server built in Go to serve a chat experience
   instead of a shell.

   Source: https://github.com/shazow/ssh-chat

   For more, visit shazow.net or follow at twitter.com/shazow
` + RESET

const REQUIRED_WAIT time.Duration = time.Second / 2

type Client struct {
	Server        *Server
	Conn          *ssh.ServerConn
	Msg           chan string
	Name          string
	Color         string
	Op            bool
	ready         chan struct{}
	term          *terminal.Terminal
	termWidth     int
	termHeight    int
	silencedUntil time.Time
	lastTX        time.Time
	beepMe        bool
}

func NewClient(server *Server, conn *ssh.ServerConn) *Client {
	return &Client{
		Server: server,
		Conn:   conn,
		Name:   conn.User(),
		Color:  RandomColor256(),
		Msg:    make(chan string, MSG_BUFFER),
		ready:  make(chan struct{}, 1),
		lastTX: time.Now(),
	}
}

func (c *Client) ColoredName() string {
	return ColorString(c.Color, c.Name)
}

func (c *Client) SysMsg(msg string, args ...interface{}) {
	c.Msg <- ContinuousFormat(SYSTEM_MESSAGE_FORMAT, "-> "+fmt.Sprintf(msg, args...))
}

func (c *Client) Write(msg string) {
	c.term.Write([]byte(msg + "\r\n"))
}

func (c *Client) WriteLines(msg []string) {
	for _, line := range msg {
		c.Write(line)
	}
}

func (c *Client) Send(msg string) {
	if len(msg) > MAX_MSG_LENGTH {
		return
	}
	select {
	case c.Msg <- msg:
	default:
		logger.Errorf("Msg buffer full, dropping: %s (%s)", c.Name, c.Conn.RemoteAddr())
		c.Conn.Close()
	}
}

func (c *Client) SendLines(msg []string) {
	for _, line := range msg {
		c.Send(line)
	}
}

func (c *Client) IsSilenced() bool {
	return c.silencedUntil.After(time.Now())
}

func (c *Client) Silence(d time.Duration) {
	c.silencedUntil = time.Now().Add(d)
}

func (c *Client) Resize(width int, height int) error {
	err := c.term.SetSize(width, height)
	if err != nil {
		logger.Errorf("Resize failed: %dx%d", width, height)
		return err
	}
	c.termWidth, c.termHeight = width, height
	return nil
}

func (c *Client) Rename(name string) {
	c.Name = name
	c.term.SetPrompt(fmt.Sprintf("[%s] ", c.ColoredName()))
}

func (c *Client) Fingerprint() string {
	return c.Conn.Permissions.Extensions["fingerprint"]
}

func (c *Client) handleShell(channel ssh.Channel) {
	defer channel.Close()

	// FIXME: This shouldn't live here, need to restructure the call chaining.
	c.Server.Add(c)
	go func() {
		// Block until done, then remove.
		c.Conn.Wait()
		c.Server.Remove(c)
	}()

	go func() {
		for msg := range c.Msg {
			c.Write(msg)
		}
	}()

	for {
		line, err := c.term.ReadLine()
		if err != nil {
			break
		}

		parts := strings.SplitN(line, " ", 3)
		isCmd := strings.HasPrefix(parts[0], "/")

		if isCmd {
			// TODO: Factor this out.
			switch parts[0] {
			case "/test-colors": // Shh, this command is a secret!
				c.Write(ColorString("32", "Lorem ipsum dolor sit amet,"))
				c.Write("consectetur " + ColorString("31;1", "adipiscing") + " elit.")
			case "/exit":
				channel.Close()
			case "/help":
				c.WriteLines(strings.Split(HELP_TEXT, "\n"))
				if c.Server.IsOp(c) {
					c.WriteLines(strings.Split(OP_HELP_TEXT, "\n"))
				}
			case "/about":
				c.WriteLines(strings.Split(ABOUT_TEXT, "\n"))
			case "/uptime":
				c.Write(c.Server.Uptime())
			case "/beep":
				c.beepMe = !c.beepMe
				if c.beepMe {
					c.SysMsg("I'll beep you good.")
				} else {
					c.SysMsg("No more beeps. :(")
				}
			case "/me":
				me := strings.TrimLeft(line, "/me")
				if me == "" {
					me = " is at a loss for words."
				}
				msg := fmt.Sprintf("** %s%s", c.ColoredName(), me)
				if c.IsSilenced() || len(msg) > 1000 {
					c.SysMsg("Message rejected.")
				} else {
					c.Server.Broadcast(msg, nil)
				}
			case "/nick":
				if len(parts) == 2 {
					c.Server.Rename(c, parts[1])
				} else {
					c.SysMsg("Missing $NAME from: /nick $NAME")
				}
			case "/whois":
				if len(parts) == 2 {
					client := c.Server.Who(parts[1])
					if client != nil {
						version := RE_STRIP_TEXT.ReplaceAllString(string(client.Conn.ClientVersion()), "")
						if len(version) > 100 {
							version = "Evil Jerk with a superlong string"
						}
						c.SysMsg("%s is %s via %s", client.ColoredName(), client.Fingerprint(), version)
					} else {
						c.SysMsg("No such name: %s", parts[1])
					}
				} else {
					c.SysMsg("Missing $NAME from: /whois $NAME")
				}
			case "/list":
				names := c.Server.List(nil)
				c.SysMsg("%d connected: %s", len(names), strings.Join(names, ", "))
			case "/ban":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) != 2 {
					c.SysMsg("Missing $NAME from: /ban $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.SysMsg("Banned by %s.", c.ColoredName())
						c.Server.Ban(fingerprint, nil)
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.ColoredName()), nil)
					}
				}
			case "/op":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) != 2 {
					c.SysMsg("Missing $NAME from: /op $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.SysMsg("Made op by %s.", c.ColoredName())
						c.Server.Op(fingerprint)
					}
				}
			case "/kick":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) != 2 {
					c.SysMsg("Missing $NAME from: /kick $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						client.SysMsg("Kicked by %s.", c.ColoredName())
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was kicked by %s", parts[1], c.ColoredName()), nil)
					}
				}
			case "/silence":
				if !c.Server.IsOp(c) {
					c.SysMsg("You're not an admin.")
				} else if len(parts) < 2 {
					c.SysMsg("Missing $NAME from: /silence $NAME")
				} else {
					duration := time.Duration(5) * time.Minute
					if len(parts) >= 3 {
						parsedDuration, err := time.ParseDuration(parts[2])
						if err == nil {
							duration = parsedDuration
						}
					}
					client := c.Server.Who(parts[1])
					if client == nil {
						c.SysMsg("No such name: %s", parts[1])
					} else {
						client.Silence(duration)
						client.SysMsg("Silenced for %s by %s.", duration, c.ColoredName())
					}
				}
			case "/msg": /* Send a PM */
				/* Make sure we have a recipient and a message */
				if len(parts) < 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /msg $NAME $MESSAGE")
					break
				} else if len(parts) < 3 {
					c.Msg <- fmt.Sprintf("-> Missing $MESSAGE from: /msg $NAME $MESSAGE")
					break
				}
				/* Ask the server to send the message */
				if err := c.Server.Privmsg(parts[1], parts[2], c); nil != err {
					c.Msg <- fmt.Sprintf("Unable to send message to %v: %v", parts[1], err)
				}

			default:
				c.SysMsg("Invalid command: %s", line)
			}
			continue
		}

		msg := fmt.Sprintf("%s: %s", c.ColoredName(), line)
		/* Rate limit */
		if time.Now().Sub(c.lastTX) < REQUIRED_WAIT {
			c.Msg <- fmt.Sprintf("-> Rate limiting in effect.")
			continue
		}
		if c.IsSilenced() || len(msg) > 1000 || len(msg) == 0 {
			c.SysMsg("Message rejected.")
			continue
		}
		c.Server.Broadcast(msg, c)
		c.lastTX = time.Now()
	}

}

func (c *Client) handleChannels(channels <-chan ssh.NewChannel) {
	prompt := fmt.Sprintf("[%s] ", c.ColoredName())

	hasShell := false

	for ch := range channels {
		if t := ch.ChannelType(); t != "session" {
			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		channel, requests, err := ch.Accept()
		if err != nil {
			logger.Errorf("Could not accept channel: %v", err)
			continue
		}
		defer channel.Close()

		c.term = terminal.NewTerminal(channel, prompt)
		c.term.AutoCompleteCallback = c.Server.AutoCompleteFunction

		for req := range requests {
			var width, height int
			var ok bool

			switch req.Type {
			case "shell":
				if c.term != nil && !hasShell {
					go c.handleShell(channel)
					ok = true
					hasShell = true
				}
			case "pty-req":
				width, height, ok = parsePtyRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			case "window-change":
				width, height, ok = parseWinchRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			}

			if req.WantReply {
				req.Reply(ok, nil)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/coreos/go-etcd/etcd"
	"github.com/peterbourgon/mergemap"
	"gopkg.in/BlueDragonX/go-settings.v0"
	"strings"
	"time"
)

const (
	retrySeed   = 500
	retryMax    = 30000
	retryFactor = 1.5
)

var DefaultEtcdURIs []string = []string{"http://172.17.42.1:4001/"}

// Configuration server client interface.
type Client interface {
	// Wait for the server to become available. The wait can be stopped by
	// sending a value to `stop` or closing it. Return true if the server came
	// online or false if the wait was canceled.
	Wait(stop chan bool) bool

	// Recursively retrieve the values for a group of `keys`. The values are
	// merged into a map of values structured as a tree.
	Get(keys []string) (map[string]interface{}, error)

	// Recursively watch each prefix in `prefixes` for changes. Send the name
	// of the changed prefix to the `changes` channel. Stop watching and exit
	// when `stop` receives `true`. Wait for the server to become available if
	// it isn't. Each failed watch attempt will be followed by an increasingly
	// longer period of sleep.
	Watch(prefixes []string, changes chan string, stop chan bool)
}

// Return the base key name for a key path.
func getKeyName(path string) string {
	parts := strings.Split(path, "/")
	unclean := parts[len(parts)-1]
	return strings.Replace(unclean, "-", "_", -1)
}

// Return a value containing node contents.
func getNodeValue(node *etcd.Node) interface{} {
	if node.Dir {
		mapping := make(map[string]interface{})
		for _, child := range node.Nodes {
			name := getKeyName(child.Key)
			mapping[name] = getNodeValue(child)
		}
		return mapping
	} else {
		return node.Value
	}
}

// Convert the node tree to a map.
func getNodeMap(node *etcd.Node) map[string]interface{} {
	path := strings.Replace(strings.Trim(node.Key, "/"), "-", "_", -1)
	parts := strings.Split(path, "/")
	base := parts[len(parts)-1]
	dir := parts[:len(parts)-1]
	mapping := map[string]interface{}{
		base: getNodeValue(node),
	}

	for i := len(dir) - 1; i >= 0; i-- {
		mapping = map[string]interface{}{
			parts[i]: mapping,
		}
	}
	return mapping
}

// An etcd client implementation.
type EtcdClient struct {
	client *etcd.Client
}

// Create a new etcd client.
func NewEtcdClient(config *settings.Settings) (*EtcdClient, error) {
	uris := config.StringArrayDflt("uris", []string{})
	if len(uris) == 0 {
		uris = DefaultEtcdURIs
	}
	for n, uri := range uris {
		uris[n] = strings.TrimRight(uri, "/")
	}

	tlsKey := config.StringDflt("tls-key", "")
	tlsCert := config.StringDflt("tls-cert", "")
	tlsCaCert := config.StringDflt("tls-ca-cert", "")

	var err error
	var etcdClient *etcd.Client
	if tlsKey != "" && tlsCert != "" && tlsCaCert != "" {
		if etcdClient, err = etcd.NewTLSClient(uris, tlsCert, tlsKey, tlsCaCert); err != nil {
			return nil, err
		}
	} else {
		etcdClient = etcd.NewClient(uris)
	}

	return &EtcdClient{
		client: etcdClient,
	}, nil
}

// Wait for the server to become available. The wait can be stopped by sending
// a value to `stop` or closing it. Return true if the server came online or
// false if the wait was canceled.
func (c *EtcdClient) Wait(stop chan bool) bool {
	var retryTime int64 = retrySeed
	for {
		if _, err := c.client.Get("/", false, false); err == nil {
			logger.Debug("connected to etcd")
			break
		} else {
			logger.Infof("waiting %.1f seconds for etcd", float64(retryTime)/1000.0)
			logger.Debugf("error was: %s", err)

			select {
			case <-time.After(time.Duration(retryTime) * time.Millisecond):
			case <-stop:
				return false
			}

			if retryTime < retryMax {
				retryTime = int64(float64(retryTime) * float64(retryFactor))
			} else {
				retryTime = retryMax
			}
		}
	}
	return true
}

// Get a single key and convert it to a map. Returns an empty map if the is not
// found. Returns an error on failure.
func (c *EtcdClient) getOne(key string) (map[string]interface{}, error) {
	if response, err := c.client.Get(key, false, true); err == nil {
		item := getNodeMap(response.Node)
		return item, nil
	} else if etcdErr, ok := err.(*etcd.EtcdError); ok && etcdErr.ErrorCode == 100 {
		return make(map[string]interface{}), nil
	} else {
		return nil, err
	}
}

// Get a group of keys rooted and merge them into a single map.
func (c *EtcdClient) Get(keys []string) (map[string]interface{}, error) {
	var err error
	mapping := make(map[string]interface{})
	for _, key := range keys {
		var keyMapping map[string]interface{}
		if keyMapping, err = c.getOne(key); err == nil {
			mapping = mergemap.Merge(mapping, keyMapping)
		} else {
			break
		}
	}
	return mapping, err
}

// Watch a single prefix for changes.
func (c *EtcdClient) watchOne(prefix string, changes chan string, stop chan bool) {
	prefix = strings.Trim(prefix, "/")
	var waitIndex uint64 = 0
	var retryTime int64 = retrySeed
	logger.Debugf("watching %s for changes", prefix)

Loop:
	for {
		var err error
		var response *etcd.Response
		if response, err = c.client.Watch(prefix, waitIndex, true, nil, stop); err == nil {
			waitIndex = response.EtcdIndex + 1
			retryTime = retrySeed
			logger.Debugf("prefix %s changed, index was %d, action was %s", prefix, response.EtcdIndex, response.Action)
			changes <- prefix
		} else if _, ok := err.(*json.SyntaxError); ok {
			// This is caused by the connection timing out thus cutting the
			// stream the JSON encoder is readong from. I would expect this to
			// be common on HTTP long-pulls especially if a proxy is involved.
			// On the other hand I am unsure whether changes to the go-etcd
			// client library or the etcd server itself would mitigate this
			// issue when directly connected. Either way we will retry the
			// watch using the same index so as not to miss any changes.
			logger.Debugf("watch timed out, retrying immediately")
		} else if err == etcd.ErrWatchStoppedByUser {
			err = nil
			break
		} else {
			logger.Errorf("watch on %s failed, retrying in %.1f seconds", prefix, float64(retryTime)/1000)
			logger.Debugf("error was: %s", err)

			select {
			case <-time.After(time.Duration(retryTime) * time.Millisecond):
			case <-stop:
				break Loop
			}

			if retryTime < retryMax {
				retryTime = int64(float64(retryTime) * float64(retryFactor))
			} else {
				retryTime = retryMax
			}
		}
	}
}

// Recursively watch each prefix in `prefixes` for changes. Send the name of
// changed prefix to the `changes` channel. Stop watching and exit when `stop`
// receives `true`. Wait for the server to become available if it isn't. Each
// failed attempt will be followed by an increasingly longer period of sleep.
func (c *EtcdClient) Watch(prefixes []string, changes chan string, stop chan bool) {
	defer close(changes)
	type syncStore struct {
		stop chan bool
		join chan bool
	}

	syncs := make([]syncStore, len(prefixes))
	for n, prefix := range prefixes {
		syncs[n] = syncStore{
			make(chan bool),
			make(chan bool),
		}
		go func(prefix string, sync syncStore) {
			c.watchOne(prefix, changes, sync.stop)
			close(sync.join)
		}(prefix, syncs[n])
	}

	<-stop
	for _, sync := range syncs {
		sync.stop <- true
	}
	for _, sync := range syncs {
		select {
		case <-sync.join:
		case <-time.After(200 * time.Millisecond):
		}
	}
}
<commit_msg>Improve timeout error message in watch.<commit_after>package main

import (
	"encoding/json"
	"github.com/coreos/go-etcd/etcd"
	"github.com/peterbourgon/mergemap"
	"gopkg.in/BlueDragonX/go-settings.v0"
	"strings"
	"time"
)

const (
	retrySeed   = 500
	retryMax    = 30000
	retryFactor = 1.5
)

var DefaultEtcdURIs []string = []string{"http://172.17.42.1:4001/"}

// Configuration server client interface.
type Client interface {
	// Wait for the server to become available. The wait can be stopped by
	// sending a value to `stop` or closing it. Return true if the server came
	// online or false if the wait was canceled.
	Wait(stop chan bool) bool

	// Recursively retrieve the values for a group of `keys`. The values are
	// merged into a map of values structured as a tree.
	Get(keys []string) (map[string]interface{}, error)

	// Recursively watch each prefix in `prefixes` for changes. Send the name
	// of the changed prefix to the `changes` channel. Stop watching and exit
	// when `stop` receives `true`. Wait for the server to become available if
	// it isn't. Each failed watch attempt will be followed by an increasingly
	// longer period of sleep.
	Watch(prefixes []string, changes chan string, stop chan bool)
}

// Return the base key name for a key path.
func getKeyName(path string) string {
	parts := strings.Split(path, "/")
	unclean := parts[len(parts)-1]
	return strings.Replace(unclean, "-", "_", -1)
}

// Return a value containing node contents.
func getNodeValue(node *etcd.Node) interface{} {
	if node.Dir {
		mapping := make(map[string]interface{})
		for _, child := range node.Nodes {
			name := getKeyName(child.Key)
			mapping[name] = getNodeValue(child)
		}
		return mapping
	} else {
		return node.Value
	}
}

// Convert the node tree to a map.
func getNodeMap(node *etcd.Node) map[string]interface{} {
	path := strings.Replace(strings.Trim(node.Key, "/"), "-", "_", -1)
	parts := strings.Split(path, "/")
	base := parts[len(parts)-1]
	dir := parts[:len(parts)-1]
	mapping := map[string]interface{}{
		base: getNodeValue(node),
	}

	for i := len(dir) - 1; i >= 0; i-- {
		mapping = map[string]interface{}{
			parts[i]: mapping,
		}
	}
	return mapping
}

// An etcd client implementation.
type EtcdClient struct {
	client *etcd.Client
}

// Create a new etcd client.
func NewEtcdClient(config *settings.Settings) (*EtcdClient, error) {
	uris := config.StringArrayDflt("uris", []string{})
	if len(uris) == 0 {
		uris = DefaultEtcdURIs
	}
	for n, uri := range uris {
		uris[n] = strings.TrimRight(uri, "/")
	}

	tlsKey := config.StringDflt("tls-key", "")
	tlsCert := config.StringDflt("tls-cert", "")
	tlsCaCert := config.StringDflt("tls-ca-cert", "")

	var err error
	var etcdClient *etcd.Client
	if tlsKey != "" && tlsCert != "" && tlsCaCert != "" {
		if etcdClient, err = etcd.NewTLSClient(uris, tlsCert, tlsKey, tlsCaCert); err != nil {
			return nil, err
		}
	} else {
		etcdClient = etcd.NewClient(uris)
	}

	return &EtcdClient{
		client: etcdClient,
	}, nil
}

// Wait for the server to become available. The wait can be stopped by sending
// a value to `stop` or closing it. Return true if the server came online or
// false if the wait was canceled.
func (c *EtcdClient) Wait(stop chan bool) bool {
	var retryTime int64 = retrySeed
	for {
		if _, err := c.client.Get("/", false, false); err == nil {
			logger.Debug("connected to etcd")
			break
		} else {
			logger.Infof("waiting %.1f seconds for etcd", float64(retryTime)/1000.0)
			logger.Debugf("error was: %s", err)

			select {
			case <-time.After(time.Duration(retryTime) * time.Millisecond):
			case <-stop:
				return false
			}

			if retryTime < retryMax {
				retryTime = int64(float64(retryTime) * float64(retryFactor))
			} else {
				retryTime = retryMax
			}
		}
	}
	return true
}

// Get a single key and convert it to a map. Returns an empty map if the is not
// found. Returns an error on failure.
func (c *EtcdClient) getOne(key string) (map[string]interface{}, error) {
	if response, err := c.client.Get(key, false, true); err == nil {
		item := getNodeMap(response.Node)
		return item, nil
	} else if etcdErr, ok := err.(*etcd.EtcdError); ok && etcdErr.ErrorCode == 100 {
		return make(map[string]interface{}), nil
	} else {
		return nil, err
	}
}

// Get a group of keys rooted and merge them into a single map.
func (c *EtcdClient) Get(keys []string) (map[string]interface{}, error) {
	var err error
	mapping := make(map[string]interface{})
	for _, key := range keys {
		var keyMapping map[string]interface{}
		if keyMapping, err = c.getOne(key); err == nil {
			mapping = mergemap.Merge(mapping, keyMapping)
		} else {
			break
		}
	}
	return mapping, err
}

// Watch a single prefix for changes.
func (c *EtcdClient) watchOne(prefix string, changes chan string, stop chan bool) {
	prefix = strings.Trim(prefix, "/")
	var waitIndex uint64 = 0
	var retryTime int64 = retrySeed
	logger.Debugf("watching %s for changes", prefix)

Loop:
	for {
		var err error
		var response *etcd.Response
		if response, err = c.client.Watch(prefix, waitIndex, true, nil, stop); err == nil {
			waitIndex = response.EtcdIndex + 1
			retryTime = retrySeed
			logger.Debugf("prefix %s changed, index was %d, action was %s", prefix, response.EtcdIndex, response.Action)
			changes <- prefix
		} else if _, ok := err.(*json.SyntaxError); ok {
			// This is caused by the connection timing out thus cutting the
			// stream the JSON encoder is readong from. I would expect this to
			// be common on HTTP long-pulls especially if a proxy is involved.
			// On the other hand I am unsure whether changes to the go-etcd
			// client library or the etcd server itself would mitigate this
			// issue when directly connected. Either way we will retry the
			// watch using the same index so as not to miss any changes.
			logger.Debugf("watch on %s timed out, retrying immediately")
		} else if err == etcd.ErrWatchStoppedByUser {
			err = nil
			break
		} else {
			logger.Errorf("watch on %s failed, retrying in %.1f seconds", prefix, float64(retryTime)/1000)
			logger.Debugf("error was: %s", err)

			select {
			case <-time.After(time.Duration(retryTime) * time.Millisecond):
			case <-stop:
				break Loop
			}

			if retryTime < retryMax {
				retryTime = int64(float64(retryTime) * float64(retryFactor))
			} else {
				retryTime = retryMax
			}
		}
	}
}

// Recursively watch each prefix in `prefixes` for changes. Send the name of
// changed prefix to the `changes` channel. Stop watching and exit when `stop`
// receives `true`. Wait for the server to become available if it isn't. Each
// failed attempt will be followed by an increasingly longer period of sleep.
func (c *EtcdClient) Watch(prefixes []string, changes chan string, stop chan bool) {
	defer close(changes)
	type syncStore struct {
		stop chan bool
		join chan bool
	}

	syncs := make([]syncStore, len(prefixes))
	for n, prefix := range prefixes {
		syncs[n] = syncStore{
			make(chan bool),
			make(chan bool),
		}
		go func(prefix string, sync syncStore) {
			c.watchOne(prefix, changes, sync.stop)
			close(sync.join)
		}(prefix, syncs[n])
	}

	<-stop
	for _, sync := range syncs {
		sync.stop <- true
	}
	for _, sync := range syncs {
		select {
		case <-sync.join:
		case <-time.After(200 * time.Millisecond):
		}
	}
}
<|endoftext|>


<commit_before>package tent

import (
	"bytes"
	"encoding/json"
	"io"
	"mime"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/tent/hawk-go"
	"github.com/tent/http-link-go"
)

type Client struct {
	Credentials *hawk.Credentials

	Servers []MetaPostServer
}

const PostMediaType = "application/vnd.tent.post.v0+json"

func (client *Client) CreatePost(post *Post) error {
	data, err := json.Marshal(post)
	if err != nil {
		return err
	}
	var method, uri string
	if post.ID == "" || post.Version.ID == "" {
		method, uri = "POST", client.Servers[0].URLs.NewPost
	} else {
		method = "PUT"
		uri, err = client.Servers[0].URLs.PostURL(post.Entity, post.ID, post.Version.ID)
		if err != nil {
			return err
		}
	}
	req, err := newRequest(method, uri, nil, bytes.NewReader(data))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", mime.FormatMediaType(PostMediaType, map[string]string{"type": post.Type}))
	if len(post.Links) > 0 {
		req.Header.Set("Link", link.Format(post.Links))
		post.Links = nil
	}
	res, err := HTTP.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return newBadResponseError(ErrBadStatusCode, res)
	}

	if linkHeader := res.Header.Get("Link"); linkHeader != "" {
		links, err := link.Parse(linkHeader)
		if err != nil {
			return err
		}
		post.Links = links
	}

	if ok := timeoutRead(res.Body, func() {
		err = json.NewDecoder(res.Body).Decode(post)
	}); !ok {
		return newBadResponseError(ErrReadTimeout, res)
	}

	return err
}

func (client *Client) Request(req func(*MetaPostServer) error) error {
	for i, server := range client.Servers {
		err := req(&server)
		if err != nil && i < len(client.Servers)-1 {
			continue
		}
		return err
	}
	panic("not reached")
}

func (client *Client) SignRequest(req *http.Request) {
	auth := hawk.NewRequestAuth(req, client.Credentials, 0)
	req.Header.Set("Authorization", auth.RequestHeader())
}

func (client *Client) newRequest(method, url string, header http.Header, body io.Reader) (*http.Request, error) {
	req, err := newRequest(method, url, header, body)
	if err != nil {
		return nil, err
	}
	client.SignRequest(req)
	return req, nil
}

func (client *Client) requestJSON(method string, url func(server *MetaPostServer) (string, error), header http.Header, body []byte, data interface{}) error {
	return client.Request(func(server *MetaPostServer) error {
		uri, err := url(server)
		if err != nil {
			return err
		}
		var bodyReader io.Reader
		if body != nil {
			bodyReader = bytes.NewReader(body)
		}
		req, err := client.newRequest(method, uri, header, bodyReader)
		if err != nil {
			return err
		}
		res, err := HTTP.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		if res.StatusCode != 200 {
			return newBadResponseError(ErrBadStatusCode, res)
		}
		if ok := timeoutRead(res.Body, func() {
			err = json.NewDecoder(res.Body).Decode(data)
		}); !ok {
			return newBadResponseError(ErrReadTimeout, res)
		}
		return err
	})
}

var timeout = 10 * time.Second
var HTTP = newHTTPClient(timeout)
var UserAgent = "tent-go/1"

func SetTimeout(t time.Duration) {
	timeout = t
	HTTP = newHTTPClient(t)
}

func newHTTPClient(timeout time.Duration) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			ResponseHeaderTimeout: timeout,
			Dial: (&net.Dialer{Timeout: timeout}).Dial,
		},
	}
}

func timeoutRead(body io.Closer, read func()) (ok bool) {
	done := make(chan struct{})
	go func() {
		read()
		done <- struct{}{}
	}()
	select {
	case <-done:
		return true
	case <-time.After(timeout):
		body.Close()
		return false
	}
}

func newRequest(method, url string, header http.Header, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}
	if req.URL.Path != "" {
		// url.Parse unescapes the Path, which can screw with some of the signing stuff
		req.URL.Opaque = strings.SplitN(url, ":", 2)[1]
		req.URL.Path = ""
		req.URL.RawQuery = ""
	}
	if header != nil {
		req.Header = header
	}
	req.Header.Set("User-Agent", UserAgent)
	return req, nil
}

type BadResponseErrorType int

const (
	ErrBadStatusCode BadResponseErrorType = iota
	ErrBadContentType
	ErrReadTimeout
)

type BadResponseError struct {
	Type      BadResponseErrorType
	Response  *http.Response
	TentError *TentError
}

type TentError struct {
	Error  string            `json:"error"`
	Fields map[string]string `json:"fields"`
}

const MediaTypeError = "application/vnd.tent.error.v0+json"

func newBadResponseError(typ BadResponseErrorType, res *http.Response) *BadResponseError {
	err := &BadResponseError{Type: typ, Response: res}

	// try to decode an error message from the body
	if typ == ErrBadStatusCode {
		mediaType, _, _ := mime.ParseMediaType(res.Header.Get("Content-Type"))
		if mediaType == MediaTypeError {
			tentError := &TentError{}
			json.NewDecoder(res.Body).Decode(tentError)
			err.TentError = tentError
		}
	}
	return err
}

func (e *BadResponseError) Error() string {
	switch e.Type {
	case ErrBadContentType:
		return "tent: incorrect Content-Type received: " + strconv.Quote(e.Response.Header.Get("Content-Type"))
	case ErrReadTimeout:
		return "tent: timeout reading response body of " + e.Response.Request.Method + " " + e.Response.Request.URL.String()
	default:
		return "tent: unexpected " + strconv.Itoa(e.Response.StatusCode) + " performing " + e.Response.Request.Method + " " + e.Response.Request.URL.String()
	}
}
<commit_msg>Add panic if credentials are nil<commit_after>package tent

import (
	"bytes"
	"encoding/json"
	"io"
	"mime"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/tent/hawk-go"
	"github.com/tent/http-link-go"
)

type Client struct {
	Credentials *hawk.Credentials

	Servers []MetaPostServer
}

const PostMediaType = "application/vnd.tent.post.v0+json"

func (client *Client) CreatePost(post *Post) error {
	data, err := json.Marshal(post)
	if err != nil {
		return err
	}
	var method, uri string
	if post.ID == "" || post.Version.ID == "" {
		method, uri = "POST", client.Servers[0].URLs.NewPost
	} else {
		method = "PUT"
		uri, err = client.Servers[0].URLs.PostURL(post.Entity, post.ID, post.Version.ID)
		if err != nil {
			return err
		}
	}
	req, err := newRequest(method, uri, nil, bytes.NewReader(data))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", mime.FormatMediaType(PostMediaType, map[string]string{"type": post.Type}))
	if len(post.Links) > 0 {
		req.Header.Set("Link", link.Format(post.Links))
		post.Links = nil
	}
	res, err := HTTP.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return newBadResponseError(ErrBadStatusCode, res)
	}

	if linkHeader := res.Header.Get("Link"); linkHeader != "" {
		links, err := link.Parse(linkHeader)
		if err != nil {
			return err
		}
		post.Links = links
	}

	if ok := timeoutRead(res.Body, func() {
		err = json.NewDecoder(res.Body).Decode(post)
	}); !ok {
		return newBadResponseError(ErrReadTimeout, res)
	}

	return err
}

func (client *Client) Request(req func(*MetaPostServer) error) error {
	for i, server := range client.Servers {
		err := req(&server)
		if err != nil && i < len(client.Servers)-1 {
			continue
		}
		return err
	}
	panic("not reached")
}

func (client *Client) SignRequest(req *http.Request) {
	if client.Credentials == nil {
		panic("tent: missing credentials")
	}
	auth := hawk.NewRequestAuth(req, client.Credentials, 0)
	req.Header.Set("Authorization", auth.RequestHeader())
}

func (client *Client) newRequest(method, url string, header http.Header, body io.Reader) (*http.Request, error) {
	req, err := newRequest(method, url, header, body)
	if err != nil {
		return nil, err
	}
	client.SignRequest(req)
	return req, nil
}

func (client *Client) requestJSON(method string, url func(server *MetaPostServer) (string, error), header http.Header, body []byte, data interface{}) error {
	return client.Request(func(server *MetaPostServer) error {
		uri, err := url(server)
		if err != nil {
			return err
		}
		var bodyReader io.Reader
		if body != nil {
			bodyReader = bytes.NewReader(body)
		}
		req, err := client.newRequest(method, uri, header, bodyReader)
		if err != nil {
			return err
		}
		res, err := HTTP.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		if res.StatusCode != 200 {
			return newBadResponseError(ErrBadStatusCode, res)
		}
		if ok := timeoutRead(res.Body, func() {
			err = json.NewDecoder(res.Body).Decode(data)
		}); !ok {
			return newBadResponseError(ErrReadTimeout, res)
		}
		return err
	})
}

var timeout = 10 * time.Second
var HTTP = newHTTPClient(timeout)
var UserAgent = "tent-go/1"

func SetTimeout(t time.Duration) {
	timeout = t
	HTTP = newHTTPClient(t)
}

func newHTTPClient(timeout time.Duration) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			ResponseHeaderTimeout: timeout,
			Dial: (&net.Dialer{Timeout: timeout}).Dial,
		},
	}
}

func timeoutRead(body io.Closer, read func()) (ok bool) {
	done := make(chan struct{})
	go func() {
		read()
		done <- struct{}{}
	}()
	select {
	case <-done:
		return true
	case <-time.After(timeout):
		body.Close()
		return false
	}
}

func newRequest(method, url string, header http.Header, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}
	if req.URL.Path != "" {
		// url.Parse unescapes the Path, which can screw with some of the signing stuff
		req.URL.Opaque = strings.SplitN(url, ":", 2)[1]
		req.URL.Path = ""
		req.URL.RawQuery = ""
	}
	if header != nil {
		req.Header = header
	}
	req.Header.Set("User-Agent", UserAgent)
	return req, nil
}

type BadResponseErrorType int

const (
	ErrBadStatusCode BadResponseErrorType = iota
	ErrBadContentType
	ErrReadTimeout
)

type BadResponseError struct {
	Type      BadResponseErrorType
	Response  *http.Response
	TentError *TentError
}

type TentError struct {
	Error  string            `json:"error"`
	Fields map[string]string `json:"fields"`
}

const MediaTypeError = "application/vnd.tent.error.v0+json"

func newBadResponseError(typ BadResponseErrorType, res *http.Response) *BadResponseError {
	err := &BadResponseError{Type: typ, Response: res}

	// try to decode an error message from the body
	if typ == ErrBadStatusCode {
		mediaType, _, _ := mime.ParseMediaType(res.Header.Get("Content-Type"))
		if mediaType == MediaTypeError {
			tentError := &TentError{}
			json.NewDecoder(res.Body).Decode(tentError)
			err.TentError = tentError
		}
	}
	return err
}

func (e *BadResponseError) Error() string {
	switch e.Type {
	case ErrBadContentType:
		return "tent: incorrect Content-Type received: " + strconv.Quote(e.Response.Header.Get("Content-Type"))
	case ErrReadTimeout:
		return "tent: timeout reading response body of " + e.Response.Request.Method + " " + e.Response.Request.URL.String()
	default:
		return "tent: unexpected " + strconv.Itoa(e.Response.StatusCode) + " performing " + e.Response.Request.Method + " " + e.Response.Request.URL.String()
	}
}
<|endoftext|>


<commit_before>package goquic

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/oleiade/lane"
)

type Conn struct {
	addr       *net.UDPAddr
	sock       *net.UDPConn
	quicClient *QuicClient
	readChan   chan udpData
	buffer     bytes.Buffer
	header     http.Header
}

type Stream struct {
	conn             *Conn
	quicClientStream *QuicClientStream
	pendingReads     *lane.Queue
	buf              bytes.Buffer
	header           http.Header
	headerParsed     bool
	// True writeFinished means that this stream is half-closed on our side
	writeFinished bool
	// True when connection is closed fully
	closed bool
}

type errorString struct {
	s string
}

type udpData struct {
	n    int
	addr *net.UDPAddr
	buf  []byte
}

func (e *errorString) Error() string {
	return e.s
}

func (c *Conn) Close() (err error) {
	return c.sock.Close()
}

func (c *Conn) SetDeadline(t time.Time) (err error) {
	// TODO(hodduc) not supported yet
	return &errorString{"Not Supported"}
}

func (c *Conn) SetReadDeadline(t time.Time) (err error) {
	// TODO(hodduc) not supported yet
	return &errorString{"Not Supported"}
}

func (c *Conn) SetWriteDeadline(t time.Time) (err error) {
	// TODO(hodduc) not supported yet
	return &errorString{"Not Supported"}
}

func (c *Conn) Socket() *net.UDPConn {
	return c.sock
}

func (c *Conn) processEvents() {
	c.processEventsWithDeadline(time.Time{})
}

func (c *Conn) processEventsWithDeadline(deadline time.Time) {
	//c.sock.SetDeadline(time.Now().Add(60 * time.Second)) // TIMEOUT = 60 sec

	localAddr, ok := c.sock.LocalAddr().(*net.UDPAddr)
	if !ok {
		panic("Cannot convert localAddr")
	}

	var timeoutCh <-chan time.Time
	if !deadline.IsZero() {
		timeoutCh = time.After(-time.Since(deadline))
	} else {
		timeoutCh = make(chan time.Time, 1)
	}

	fmt.Println("Select! ##################################")
	select {
	case result, ok := <-c.readChan:
		fmt.Println("Read! ##################################", result.n)
		if result.n == 0 {
			break
		}
		if !ok {
			break
		}
		c.quicClient.ProcessPacket(localAddr, result.addr, result.buf[:result.n])
		fmt.Println("Read Done! ##################################", result.n)
	case alarm, ok := <-c.quicClient.taskRunner.AlarmChan:
		fmt.Println("Alarm! ##############################")
		if !ok {
			break
		}
		alarm.OnAlarm()
	case <-timeoutCh:
		fmt.Println("Timeout! ##############################")
		// Break when past deadline
	}
}

func (c *Conn) waitForEvents() bool {
	c.processEvents()
	return c.quicClient.session.NumActiveRequests() != 0
}

func (c *Conn) Connect() bool {
	qc := c.quicClient
	qc.StartConnect()
	for qc.EncryptionBeingEstablished() {
		// Busy loop waiting for connection to be established
		// TODO(serialx): Maybe we can add some tiny deadlines instead of time.Now to decrease busy waiting?
		c.waitForEvents()
	}
	// TODO(hodduc) when to return false? does waitForEvents fails?
	// TODO(serialx): Ask hodduc what the comment above means
	return true
}

func (c *Conn) CreateStream() *Stream {
	quicClientStream := c.quicClient.CreateReliableQuicStream()
	stream := &Stream{
		conn:             c,
		quicClientStream: quicClientStream,
		pendingReads:     lane.NewQueue(),
	}
	quicClientStream.UserStream.(*ClientStreamImpl).stream = stream
	return stream
}

func (s *Stream) WriteHeader(header http.Header, isBodyEmpty bool) {
	s.quicClientStream.WriteHeader(header, isBodyEmpty)
	if isBodyEmpty {
		s.writeFinished = true
	}
}

func (s *Stream) Write(buf []byte) (int, error) {
	if s.writeFinished {
		return 0, errors.New("Write already finished")
	}
	s.quicClientStream.WriteOrBufferData(buf, false)
	return len(buf), nil
}

func (s *Stream) FinWrite() error {
	if s.writeFinished {
		return errors.New("Write already finished")
	}
	s.quicClientStream.WriteOrBufferData(nil, true)
	s.writeFinished = true
	return nil
}

func (s *Stream) onFinRead() {
	// XXX(serialx): This does not seem to be called at all?
}

func (s *Stream) onClose() {
	s.closed = true
	fmt.Println("*********************************************")
	fmt.Println("*********************************************")
	fmt.Println("**************  ON CLOSE     ****************")
	fmt.Println("*********************************************")
	fmt.Println("*********************************************")
}

func min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

func (s *Stream) ReadHeader() (http.Header, error) {
	if !s.headerParsed {
		// Read until header parsing is successful
		for {
			fmt.Println("$$$$$$$$$$$$$$$$$$$$$$$$$$ ReadHeader")
			for s.pendingReads.Empty() {
				s.conn.waitForEvents()
			}

			_, err := s.buf.Write(s.pendingReads.Dequeue().([]byte))
			if err != nil {
				return nil, err
			}

			headerBuf := bytes.NewBuffer(s.buf.Bytes()) // Create a temporary buf just in case for parsing failure
			header, err := ParseHeaders(headerBuf)
			if err == nil { // If parsing successful
				// XXX(serialx): Is it correct to assume headers are in proper packet frame boundary?
				//               What if theres some parts of body left in headerBuf?
				s.header = header
				s.headerParsed = true
				break
			}
		}
	}

	return s.header, nil
}

func (s *Stream) Read(buf []byte) (int, error) {
	fmt.Println("$$$$$$$$$$$$$$$$$$$$$$$$ TRY READ", s.headerParsed)

	s.conn.processEventsWithDeadline(time.Now()) // Process any pending events

	// We made sure we've processed all events. So pendingReads.Empty() means that it is really empty
	if s.closed && s.pendingReads.Empty() {
		return 0, io.EOF
	}

	if !s.headerParsed {
		s.ReadHeader()
	}

	// Wait for body
	for s.pendingReads.Empty() {
		s.conn.waitForEvents()
	}

	buffer := s.pendingReads.Dequeue().([]byte)
	fmt.Println("$$$$$$$$$$$$$$$$$$$$$$$$ READ", buffer)
	return copy(buf, buffer), nil // XXX(serialx): Must do buffering to respect io.Reader specs
}

func Dial(network, address string) (c *Conn, err error) {
	i := strings.LastIndex(network, ":")
	if i > 0 { // has colon
		return nil, &errorString{"Not supported yet"} // TODO
	}

	ra, err := net.ResolveUDPAddr(network, address)
	if err != nil {
		return nil, err
	}

	return dialQuic(network, net.Addr(ra).(*net.UDPAddr))
}

type ClientSessionImpl struct {
	conn *Conn
}

type ClientStreamImpl struct {
	conn   *Conn
	stream *Stream
}

func (c *ClientSessionImpl) CreateIncomingDataStream(stream_id uint32) DataStreamProcessor {
	// NOT SUPPORTED
	return nil
}

func (c *ClientSessionImpl) CreateOutgoingDataStream() DataStreamProcessor {
	return &ClientStreamImpl{
		conn: c.conn,
	}
}

func (cs *ClientStreamImpl) ProcessData(writer QuicStream, buffer []byte) int {
	//cs.conn.buffer.Write(buffer)
	fmt.Println("ProcessData #########################################################", buffer)
	cs.stream.pendingReads.Enqueue(buffer)
	return len(buffer)
}

func (cs *ClientStreamImpl) OnFinRead(writer QuicStream) {
	cs.stream.onFinRead()
}

func (cs *ClientStreamImpl) OnClose(writer QuicStream) {
	cs.stream.onClose()
}

func dialQuic(network string, addr *net.UDPAddr) (*Conn, error) {
	switch network {
	case "udp", "udp4", "udp6":
	default:
		return nil, &errorString{"Unknown network"}
	}
	if addr == nil {
		return nil, &errorString{"Missing address"}
	}

	fmt.Println("Connect to ", network, " - ", addr)
	conn_udp, err := net.DialUDP(network, nil, addr)
	if err != nil {
		return nil, err
	}

	quic_conn := &Conn{
		addr: addr,
		sock: conn_udp,
	}

	createQuicClientSessionImpl := func() DataStreamCreator {
		return &ClientSessionImpl{conn: quic_conn}
	}

	taskRunner := &TaskRunner{AlarmChan: make(chan *GoQuicAlarm)}
	quicClient, err := CreateQuicClient(addr, quic_conn, createQuicClientSessionImpl, taskRunner)
	if err != nil {
		return nil, err
	}
	quic_conn.quicClient = quicClient

	quic_conn.readChan = make(chan udpData)

	go func() {
		buf := make([]byte, 65535)
		for {
			fmt.Println("another reading start ***********************")
			n, peer_addr, err := quic_conn.sock.ReadFromUDP(buf)
			fmt.Println("another reading ***********************")
			if err == nil {
				buf_new := make([]byte, n)
				copy(buf_new, buf) // XXX(hodduc) buffer copy?
				quic_conn.readChan <- udpData{n: n, addr: peer_addr, buf: buf_new}
				fmt.Println("********************************************", n)
				//			} else if err.(net.Error).Timeout() {
				//				continue
			} else {
				panic(err)
			}
		}
	}()

	if quic_conn.Connect() == false {
		return nil, &errorString{"Cannot connect"}
	}

	return quic_conn, nil
}
<commit_msg>Remove debug prints<commit_after>package goquic

import (
	"bytes"
	"errors"
	"io"
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/oleiade/lane"
)

type Conn struct {
	addr       *net.UDPAddr
	sock       *net.UDPConn
	quicClient *QuicClient
	readChan   chan udpData
	buffer     bytes.Buffer
	header     http.Header
}

type Stream struct {
	conn             *Conn
	quicClientStream *QuicClientStream
	pendingReads     *lane.Queue
	buf              bytes.Buffer
	header           http.Header
	headerParsed     bool
	// True writeFinished means that this stream is half-closed on our side
	writeFinished bool
	// True when connection is closed fully
	closed bool
}

type errorString struct {
	s string
}

type udpData struct {
	n    int
	addr *net.UDPAddr
	buf  []byte
}

func (e *errorString) Error() string {
	return e.s
}

func (c *Conn) Close() (err error) {
	return c.sock.Close()
}

func (c *Conn) SetDeadline(t time.Time) (err error) {
	// TODO(hodduc) not supported yet
	return &errorString{"Not Supported"}
}

func (c *Conn) SetReadDeadline(t time.Time) (err error) {
	// TODO(hodduc) not supported yet
	return &errorString{"Not Supported"}
}

func (c *Conn) SetWriteDeadline(t time.Time) (err error) {
	// TODO(hodduc) not supported yet
	return &errorString{"Not Supported"}
}

func (c *Conn) Socket() *net.UDPConn {
	return c.sock
}

func (c *Conn) processEvents() {
	c.processEventsWithDeadline(time.Time{})
}

func (c *Conn) processEventsWithDeadline(deadline time.Time) {
	//c.sock.SetDeadline(time.Now().Add(60 * time.Second)) // TIMEOUT = 60 sec

	localAddr, ok := c.sock.LocalAddr().(*net.UDPAddr)
	if !ok {
		panic("Cannot convert localAddr")
	}

	var timeoutCh <-chan time.Time
	if !deadline.IsZero() {
		timeoutCh = time.After(-time.Since(deadline))
	} else {
		timeoutCh = make(chan time.Time, 1)
	}

	select {
	case result, ok := <-c.readChan:
		if result.n == 0 {
			break
		}
		if !ok {
			break
		}
		c.quicClient.ProcessPacket(localAddr, result.addr, result.buf[:result.n])
	case alarm, ok := <-c.quicClient.taskRunner.AlarmChan:
		if !ok {
			break
		}
		alarm.OnAlarm()
	case <-timeoutCh:
		// Break when past deadline
	}
}

func (c *Conn) waitForEvents() bool {
	c.processEvents()
	return c.quicClient.session.NumActiveRequests() != 0
}

func (c *Conn) Connect() bool {
	qc := c.quicClient
	qc.StartConnect()
	for qc.EncryptionBeingEstablished() {
		// Busy loop waiting for connection to be established
		// TODO(serialx): Maybe we can add some tiny deadlines instead of time.Now to decrease busy waiting?
		c.waitForEvents()
	}
	// TODO(hodduc) when to return false? does waitForEvents fails?
	// TODO(serialx): Ask hodduc what the comment above means
	return true
}

func (c *Conn) CreateStream() *Stream {
	quicClientStream := c.quicClient.CreateReliableQuicStream()
	stream := &Stream{
		conn:             c,
		quicClientStream: quicClientStream,
		pendingReads:     lane.NewQueue(),
	}
	quicClientStream.UserStream.(*ClientStreamImpl).stream = stream
	return stream
}

func (s *Stream) WriteHeader(header http.Header, isBodyEmpty bool) {
	s.quicClientStream.WriteHeader(header, isBodyEmpty)
	if isBodyEmpty {
		s.writeFinished = true
	}
}

func (s *Stream) Write(buf []byte) (int, error) {
	if s.writeFinished {
		return 0, errors.New("Write already finished")
	}
	s.quicClientStream.WriteOrBufferData(buf, false)
	return len(buf), nil
}

func (s *Stream) FinWrite() error {
	if s.writeFinished {
		return errors.New("Write already finished")
	}
	s.quicClientStream.WriteOrBufferData(nil, true)
	s.writeFinished = true
	return nil
}

func (s *Stream) onFinRead() {
	// XXX(serialx): This does not seem to be called at all?
}

func (s *Stream) onClose() {
	s.closed = true
}

func min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

func (s *Stream) ReadHeader() (http.Header, error) {
	if !s.headerParsed {
		// Read until header parsing is successful
		for {
			for s.pendingReads.Empty() {
				s.conn.waitForEvents()
			}

			_, err := s.buf.Write(s.pendingReads.Dequeue().([]byte))
			if err != nil {
				return nil, err
			}

			headerBuf := bytes.NewBuffer(s.buf.Bytes()) // Create a temporary buf just in case for parsing failure
			header, err := ParseHeaders(headerBuf)
			if err == nil { // If parsing successful
				// XXX(serialx): Is it correct to assume headers are in proper packet frame boundary?
				//               What if theres some parts of body left in headerBuf?
				s.header = header
				s.headerParsed = true
				break
			}
		}
	}

	return s.header, nil
}

func (s *Stream) Read(buf []byte) (int, error) {
	s.conn.processEventsWithDeadline(time.Now()) // Process any pending events

	// We made sure we've processed all events. So pendingReads.Empty() means that it is really empty
	if s.closed && s.pendingReads.Empty() {
		return 0, io.EOF
	}

	if !s.headerParsed {
		s.ReadHeader()
	}

	// Wait for body
	for s.pendingReads.Empty() {
		s.conn.waitForEvents()
	}

	buffer := s.pendingReads.Dequeue().([]byte)
	return copy(buf, buffer), nil // XXX(serialx): Must do buffering to respect io.Reader specs
}

func Dial(network, address string) (c *Conn, err error) {
	i := strings.LastIndex(network, ":")
	if i > 0 { // has colon
		return nil, &errorString{"Not supported yet"} // TODO
	}

	ra, err := net.ResolveUDPAddr(network, address)
	if err != nil {
		return nil, err
	}

	return dialQuic(network, net.Addr(ra).(*net.UDPAddr))
}

type ClientSessionImpl struct {
	conn *Conn
}

type ClientStreamImpl struct {
	conn   *Conn
	stream *Stream
}

func (c *ClientSessionImpl) CreateIncomingDataStream(stream_id uint32) DataStreamProcessor {
	// NOT SUPPORTED
	return nil
}

func (c *ClientSessionImpl) CreateOutgoingDataStream() DataStreamProcessor {
	return &ClientStreamImpl{
		conn: c.conn,
	}
}

func (cs *ClientStreamImpl) ProcessData(writer QuicStream, buffer []byte) int {
	//cs.conn.buffer.Write(buffer)
	cs.stream.pendingReads.Enqueue(buffer)
	return len(buffer)
}

func (cs *ClientStreamImpl) OnFinRead(writer QuicStream) {
	cs.stream.onFinRead()
}

func (cs *ClientStreamImpl) OnClose(writer QuicStream) {
	cs.stream.onClose()
}

func dialQuic(network string, addr *net.UDPAddr) (*Conn, error) {
	switch network {
	case "udp", "udp4", "udp6":
	default:
		return nil, &errorString{"Unknown network"}
	}
	if addr == nil {
		return nil, &errorString{"Missing address"}
	}

	conn_udp, err := net.DialUDP(network, nil, addr)
	if err != nil {
		return nil, err
	}

	quic_conn := &Conn{
		addr: addr,
		sock: conn_udp,
	}

	createQuicClientSessionImpl := func() DataStreamCreator {
		return &ClientSessionImpl{conn: quic_conn}
	}

	taskRunner := &TaskRunner{AlarmChan: make(chan *GoQuicAlarm)}
	quicClient, err := CreateQuicClient(addr, quic_conn, createQuicClientSessionImpl, taskRunner)
	if err != nil {
		return nil, err
	}
	quic_conn.quicClient = quicClient

	quic_conn.readChan = make(chan udpData)

	go func() {
		buf := make([]byte, 65535)
		for {
			n, peer_addr, err := quic_conn.sock.ReadFromUDP(buf)
			if err == nil {
				buf_new := make([]byte, n)
				copy(buf_new, buf) // XXX(hodduc) buffer copy?
				quic_conn.readChan <- udpData{n: n, addr: peer_addr, buf: buf_new}
				//			} else if err.(net.Error).Timeout() {
				//				continue
			} else {
				panic(err)
			}
		}
	}()

	if quic_conn.Connect() == false {
		return nil, &errorString{"Cannot connect"}
	}

	return quic_conn, nil
}
<|endoftext|>


<commit_before>package gowebdav

import (
	"bytes"
	"encoding/base64"
	"encoding/xml"
	"io"
	"net/http"
	"net/url"
	"os"
	pathpkg "path"
	"strings"
	"time"
)

type Client struct {
	root    string
	headers http.Header
	c       *http.Client
}

func NewClient(uri string, user string, pw string) *Client {
	c := &Client{uri, make(http.Header), &http.Client{}}

	if len(user) > 0 && len(pw) > 0 {
		a := user + ":" + pw
		auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(a))
		c.headers.Add("Authorization", auth)
	}

	c.root = FixSlash(c.root)

	return c
}

func (c *Client) SetHeader(key, value string) {
	c.headers.Add(key, value)
}

func (c *Client) Connect() error {
	rs, err := c.options("/")
	if err == nil {
		rs.Body.Close()

		if rs.StatusCode != 200 || (rs.Header.Get("Dav") == "" && rs.Header.Get("DAV") == "") {
			return newPathError("Connect", c.root, rs.StatusCode)
		}

		_, err = c.ReadDir("/")
	}
	return err
}

type props struct {
	Status   string   `xml:"DAV: status"`
	Name     string   `xml:"DAV: prop>displayname,omitempty"`
	Type     xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"`
	Size     string   `xml:"DAV: prop>getcontentlength,omitempty"`
	Modified string   `xml:"DAV: prop>getlastmodified,omitempty"`
}
type response struct {
	Href  string  `xml:"DAV: href"`
	Props []props `xml:"DAV: propstat"`
}

func getProps(r *response, status string) *props {
	for _, prop := range r.Props {
		if strings.Index(prop.Status, status) != -1 {
			return &prop
		}
	}
	return nil
}

func (c *Client) ReadDir(path string) ([]os.FileInfo, error) {
	path = FixSlashes(path)
	files := make([]os.FileInfo, 0)
	skipSelf := true
	parse := func(resp interface{}) error {
		r := resp.(*response)

		if skipSelf {
			skipSelf = false
			if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" {
				r.Props = nil
				return nil
			}
			return newPathError("ReadDir", path, 405)
		}

		if p := getProps(r, "200"); p != nil {
			f := new(File)
			if ps, err := url.QueryUnescape(r.Href); err == nil {
				f.name = pathpkg.Base(ps)
			} else {
				f.name = p.Name
			}
			f.path = path + f.name
			f.modified = parseModified(&p.Modified)

			if p.Type.Local == "collection" {
				f.path += "/"
				f.size = 0
				f.isdir = true
			} else {
				f.size = parseInt64(&p.Size)
				f.isdir = false
			}

			files = append(files, *f)
		}

		r.Props = nil
		return nil
	}

	err := c.propfind(path, false,
		`<d:propfind xmlns:d='DAV:'>
			<d:prop>
				<d:displayname/>
				<d:resourcetype/>
				<d:getcontentlength/>
				<d:getlastmodified/>
			</d:prop>
		</d:propfind>`,
		&response{},
		parse)

	if err != nil {
		if _, ok := err.(*os.PathError); !ok {
			err = &os.PathError{"ReadDir", path, err}
		}
	}
	return files, err
}

func (c *Client) Stat(path string) (os.FileInfo, error) {
	var f *File = nil
	parse := func(resp interface{}) error {
		r := resp.(*response)
		if p := getProps(r, "200"); p != nil && f == nil {
			f = new(File)
			f.name = p.Name
			f.path = path

			if p.Type.Local == "collection" {
				if !strings.HasSuffix(f.path, "/") {
					f.path += "/"
				}
				f.size = 0
				f.modified = time.Unix(0, 0)
				f.isdir = true
			} else {
				f.size = parseInt64(&p.Size)
				f.modified = parseModified(&p.Modified)
				f.isdir = false
			}
		}

		r.Props = nil
		return nil
	}

	err := c.propfind(path, true,
		`<d:propfind xmlns:d='DAV:'>
			<d:prop>
				<d:displayname/>
				<d:resourcetype/>
				<d:getcontentlength/>
				<d:getlastmodified/>
			</d:prop>
		</d:propfind>`,
		&response{},
		parse)

	if err != nil {
		if _, ok := err.(*os.PathError); !ok {
			err = &os.PathError{"ReadDir", path, err}
		}
	}
	return f, err
}

func (c *Client) Remove(path string) error {
	return c.RemoveAll(path)
}

func (c *Client) RemoveAll(path string) error {
	rs, err := c.req("DELETE", path, nil, nil)
	if err != nil {
		return newPathError("Remove", path, 400)
	}
	rs.Body.Close()

	if rs.StatusCode == 200 || rs.StatusCode == 404 {
		return nil
	} else {
		return newPathError("Remove", path, rs.StatusCode)
	}
}

func (c *Client) Mkdir(path string, _ os.FileMode) error {
	path = FixSlashes(path)
	status := c.mkcol(path)
	if status == 201 {
		return nil
	} else {
		return newPathError("Mkdir", path, status)
	}
}

func (c *Client) MkdirAll(path string, _ os.FileMode) error {
	path = FixSlashes(path)
	status := c.mkcol(path)
	if status == 201 {
		return nil
	} else if status == 409 {
		paths := strings.Split(path, "/")
		sub := "/"
		for _, e := range paths {
			if e == "" {
				continue
			}
			sub += e + "/"
			status = c.mkcol(sub)
			if status != 201 {
				return newPathError("MkdirAll", sub, status)
			}
		}
		return nil
	}

	return newPathError("MkdirAll", path, status)
}

func (c *Client) Rename(oldpath string, newpath string, overwrite bool) error {
	return c.copymove("MOVE", oldpath, newpath, overwrite)
}

func (c *Client) Copy(oldpath string, newpath string, overwrite bool) error {
	return c.copymove("COPY", oldpath, newpath, overwrite)
}

func (c *Client) Read(path string) ([]byte, error) {
	if stream, err := c.ReadStream(path); err == nil {
		defer stream.Close()
		buf := new(bytes.Buffer)
		buf.ReadFrom(stream)
		return buf.Bytes(), nil
	} else {
		return nil, err
	}
}

func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
	rs, err := c.req("GET", path, nil, nil)
	if err != nil {
		return nil, newPathErrorErr("ReadStream", path, err)
	}
	return rs.Body, nil
}

func (c *Client) Write(path string, data []byte, _ os.FileMode) error {
	s := c.put(path, bytes.NewReader(data))
	switch s {

	case 200, 201:
		return nil

	case 409:
		if idx := strings.LastIndex(path, "/"); idx == -1 {
			// faulty root
			return newPathError("Write", path, 500)
		} else {
			if err := c.MkdirAll(path[0:idx+1], 0755); err == nil {
				s = c.put(path, bytes.NewReader(data))
				if s == 200 || s == 201 {
					return nil
				}
			}
		}
	}
	return newPathError("Write", path, s)
}

func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) error {
	// TODO check if parent collection exists
	s := c.put(path, stream)
	switch s {
	case 200, 201:
		return nil

	default:
		return newPathError("WriteStream", path, s)
	}
}
<commit_msg>fix delete for yandex<commit_after>package gowebdav

import (
	"bytes"
	"encoding/base64"
	"encoding/xml"
	"io"
	"net/http"
	"net/url"
	"os"
	pathpkg "path"
	"strings"
	"time"
)

type Client struct {
	root    string
	headers http.Header
	c       *http.Client
}

func NewClient(uri string, user string, pw string) *Client {
	c := &Client{uri, make(http.Header), &http.Client{}}

	if len(user) > 0 && len(pw) > 0 {
		a := user + ":" + pw
		auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(a))
		c.headers.Add("Authorization", auth)
	}

	c.root = FixSlash(c.root)

	return c
}

func (c *Client) SetHeader(key, value string) {
	c.headers.Add(key, value)
}

func (c *Client) Connect() error {
	rs, err := c.options("/")
	if err == nil {
		rs.Body.Close()

		if rs.StatusCode != 200 || (rs.Header.Get("Dav") == "" && rs.Header.Get("DAV") == "") {
			return newPathError("Connect", c.root, rs.StatusCode)
		}

		_, err = c.ReadDir("/")
	}
	return err
}

type props struct {
	Status   string   `xml:"DAV: status"`
	Name     string   `xml:"DAV: prop>displayname,omitempty"`
	Type     xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"`
	Size     string   `xml:"DAV: prop>getcontentlength,omitempty"`
	Modified string   `xml:"DAV: prop>getlastmodified,omitempty"`
}
type response struct {
	Href  string  `xml:"DAV: href"`
	Props []props `xml:"DAV: propstat"`
}

func getProps(r *response, status string) *props {
	for _, prop := range r.Props {
		if strings.Index(prop.Status, status) != -1 {
			return &prop
		}
	}
	return nil
}

func (c *Client) ReadDir(path string) ([]os.FileInfo, error) {
	path = FixSlashes(path)
	files := make([]os.FileInfo, 0)
	skipSelf := true
	parse := func(resp interface{}) error {
		r := resp.(*response)

		if skipSelf {
			skipSelf = false
			if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" {
				r.Props = nil
				return nil
			}
			return newPathError("ReadDir", path, 405)
		}

		if p := getProps(r, "200"); p != nil {
			f := new(File)
			if ps, err := url.QueryUnescape(r.Href); err == nil {
				f.name = pathpkg.Base(ps)
			} else {
				f.name = p.Name
			}
			f.path = path + f.name
			f.modified = parseModified(&p.Modified)

			if p.Type.Local == "collection" {
				f.path += "/"
				f.size = 0
				f.isdir = true
			} else {
				f.size = parseInt64(&p.Size)
				f.isdir = false
			}

			files = append(files, *f)
		}

		r.Props = nil
		return nil
	}

	err := c.propfind(path, false,
		`<d:propfind xmlns:d='DAV:'>
			<d:prop>
				<d:displayname/>
				<d:resourcetype/>
				<d:getcontentlength/>
				<d:getlastmodified/>
			</d:prop>
		</d:propfind>`,
		&response{},
		parse)

	if err != nil {
		if _, ok := err.(*os.PathError); !ok {
			err = &os.PathError{"ReadDir", path, err}
		}
	}
	return files, err
}

func (c *Client) Stat(path string) (os.FileInfo, error) {
	var f *File = nil
	parse := func(resp interface{}) error {
		r := resp.(*response)
		if p := getProps(r, "200"); p != nil && f == nil {
			f = new(File)
			f.name = p.Name
			f.path = path

			if p.Type.Local == "collection" {
				if !strings.HasSuffix(f.path, "/") {
					f.path += "/"
				}
				f.size = 0
				f.modified = time.Unix(0, 0)
				f.isdir = true
			} else {
				f.size = parseInt64(&p.Size)
				f.modified = parseModified(&p.Modified)
				f.isdir = false
			}
		}

		r.Props = nil
		return nil
	}

	err := c.propfind(path, true,
		`<d:propfind xmlns:d='DAV:'>
			<d:prop>
				<d:displayname/>
				<d:resourcetype/>
				<d:getcontentlength/>
				<d:getlastmodified/>
			</d:prop>
		</d:propfind>`,
		&response{},
		parse)

	if err != nil {
		if _, ok := err.(*os.PathError); !ok {
			err = &os.PathError{"ReadDir", path, err}
		}
	}
	return f, err
}

func (c *Client) Remove(path string) error {
	return c.RemoveAll(path)
}

func (c *Client) RemoveAll(path string) error {
	rs, err := c.req("DELETE", path, nil, nil)
	if err != nil {
		return newPathError("Remove", path, 400)
	}
	rs.Body.Close()

	if rs.StatusCode == 200 || rs.StatusCode == 204 || rs.StatusCode == 404  {
		return nil
	} else {
		return newPathError("Remove", path, rs.StatusCode)
	}
}

func (c *Client) Mkdir(path string, _ os.FileMode) error {
	path = FixSlashes(path)
	status := c.mkcol(path)
	if status == 201 {
		return nil
	} else {
		return newPathError("Mkdir", path, status)
	}
}

func (c *Client) MkdirAll(path string, _ os.FileMode) error {
	path = FixSlashes(path)
	status := c.mkcol(path)
	if status == 201 {
		return nil
	} else if status == 409 {
		paths := strings.Split(path, "/")
		sub := "/"
		for _, e := range paths {
			if e == "" {
				continue
			}
			sub += e + "/"
			status = c.mkcol(sub)
			if status != 201 {
				return newPathError("MkdirAll", sub, status)
			}
		}
		return nil
	}

	return newPathError("MkdirAll", path, status)
}

func (c *Client) Rename(oldpath string, newpath string, overwrite bool) error {
	return c.copymove("MOVE", oldpath, newpath, overwrite)
}

func (c *Client) Copy(oldpath string, newpath string, overwrite bool) error {
	return c.copymove("COPY", oldpath, newpath, overwrite)
}

func (c *Client) Read(path string) ([]byte, error) {
	if stream, err := c.ReadStream(path); err == nil {
		defer stream.Close()
		buf := new(bytes.Buffer)
		buf.ReadFrom(stream)
		return buf.Bytes(), nil
	} else {
		return nil, err
	}
}

func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
	rs, err := c.req("GET", path, nil, nil)
	if err != nil {
		return nil, newPathErrorErr("ReadStream", path, err)
	}
	return rs.Body, nil
}

func (c *Client) Write(path string, data []byte, _ os.FileMode) error {
	s := c.put(path, bytes.NewReader(data))
	switch s {

	case 200, 201:
		return nil

	case 409:
		if idx := strings.LastIndex(path, "/"); idx == -1 {
			// faulty root
			return newPathError("Write", path, 500)
		} else {
			if err := c.MkdirAll(path[0:idx+1], 0755); err == nil {
				s = c.put(path, bytes.NewReader(data))
				if s == 200 || s == 201 {
					return nil
				}
			}
		}
	}
	return newPathError("Write", path, s)
}

func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) error {
	// TODO check if parent collection exists
	s := c.put(path, stream)
	switch s {
	case 200, 201:
		return nil

	default:
		return newPathError("WriteStream", path, s)
	}
}
<|endoftext|>


<commit_before>package model

// Model holds an abstracted data model representing the translation
// of various types of Kubernetes config to Cilium config.
type Model struct {
	HTTP []HTTPListener `json:"http,omitempty"`
}

// HTTPListener holds configuration for any listener that terminates and proxies HTTP
// including HTTP and HTTPS.
// Each holds the configuration info for one distinct HTTP listener, by
//   - Hostname
//   - TLS
//   - Address
//   - Port
type HTTPListener struct {
	// Name of the HTTPListener
	Name string
	// Sources is a slice of fully qualified resources this HTTPListener is sourced
	// from.
	Sources []FullyQualifiedResource
	// IPAddress that the listener should listen on.
	// The string must be parseable as an IP address.
	Address string
	// Port on which the service can be expected to be accessed by clients.
	Port uint32
	// Hostname that the listener should match.
	// Wildcards are supported in prefix or suffix forms, or the special wildcard `*`.
	// An empty list means that the Listener should match all hostnames.
	Hostname string
	// TLS Certifcate information. If omitted, then the listener is a cleartext HTTP listener.
	TLS *TLSSecret
	// Routes associated with HTTP traffic to the service.
	// An empty list means that traffic will not be routed.
	Routes []HTTPRoute
}

// FullyQualifiedResource stores the full details of a Kubernetes resource, including
// the Group, Version, and Kind.
// Namespace must be set to the empty string for cluster-scoped resources.
type FullyQualifiedResource struct {
	Name      string
	Namespace string
	Group     string
	Version   string
	Kind      string
}

// TLSSecret holds a reference to a secret containing a TLS keypair.
type TLSSecret struct {
	Name      string
	Namespace string
}

// HTTPRoute holds all the details needed to route HTTP traffic to a backend.
type HTTPRoute struct {
	Name string
	// PathMatch specifies that the HTTPRoute should match a path.
	PathMatch StringMatch
	Backends  []Backend
}

// StringMatch describes various types of string matching.
// Only one field may be set.
// If no fields are set, all paths should match (no path match criteria should
// be generated for Envoy.)
type StringMatch struct {
	Prefix string
	Exact  string
	Regex  string
}

// Backend holds a Kubernetes Service that points to a backend for traffic.
type Backend struct {
	// Name of the Service.
	Name string
	// Namespace of the Service.
	Namespace string
	// Port contains the details of the port on the Service to connect to
	// If unset, the same port as the top-level Listener will be used.
	Port *BackendPort
}

// BackendPort holds the details of what port on the Service to connect to.
// Only one of Port or Name can be set.
type BackendPort struct {
	// Port holds the numeric port to connect to.
	Port uint32
	// Name holds a string which will be used to connect to the port with a
	// matching spec.ports[].name in the target Service.
	Name string
}
<commit_msg>ingress/model: Add json tags for all fields<commit_after>package model

// Model holds an abstracted data model representing the translation
// of various types of Kubernetes config to Cilium config.
type Model struct {
	HTTP []HTTPListener `json:"http,omitempty"`
}

// HTTPListener holds configuration for any listener that terminates and proxies HTTP
// including HTTP and HTTPS.
// Each holds the configuration info for one distinct HTTP listener, by
//   - Hostname
//   - TLS
//   - Address
//   - Port
type HTTPListener struct {
	// Name of the HTTPListener
	Name string `json:"name,omitempty"`
	// Sources is a slice of fully qualified resources this HTTPListener is sourced
	// from.
	Sources []FullyQualifiedResource `json:"sources,omitempty"`
	// IPAddress that the listener should listen on.
	// The string must be parseable as an IP address.
	Address string `json:"address,omitempty"`
	// Port on which the service can be expected to be accessed by clients.
	Port uint32 `json:"port,omitempty"`
	// Hostname that the listener should match.
	// Wildcards are supported in prefix or suffix forms, or the special wildcard `*`.
	// An empty list means that the Listener should match all hostnames.
	Hostname string `json:"hostname,omitempty"`
	// TLS Certificate information. If omitted, then the listener is a cleartext HTTP listener.
	TLS *TLSSecret `json:"tls,omitempty"`
	// Routes associated with HTTP traffic to the service.
	// An empty list means that traffic will not be routed.
	Routes []HTTPRoute `json:"routes,omitempty"`
}

// FullyQualifiedResource stores the full details of a Kubernetes resource, including
// the Group, Version, and Kind.
// Namespace must be set to the empty string for cluster-scoped resources.
type FullyQualifiedResource struct {
	Name      string `json:"name,omitempty"`
	Namespace string `json:"namespace,omitempty"`
	Group     string `json:"group,omitempty"`
	Version   string `json:"version,omitempty"`
	Kind      string `json:"kind,omitempty"`
}

// TLSSecret holds a reference to a secret containing a TLS keypair.
type TLSSecret struct {
	Name      string `json:"name,omitempty"`
	Namespace string `json:"namespace,omitempty"`
}

// HTTPRoute holds all the details needed to route HTTP traffic to a backend.
type HTTPRoute struct {
	Name string `json:"name,omitempty"`
	// PathMatch specifies that the HTTPRoute should match a path.
	PathMatch StringMatch `json:"path_match"`
	Backends  []Backend   `json:"backends,omitempty"`
}

// StringMatch describes various types of string matching.
// Only one field may be set.
// If no fields are set, all paths should match (no path match criteria should
// be generated for Envoy.)
type StringMatch struct {
	Prefix string `json:"prefix,omitempty"`
	Exact  string `json:"exact,omitempty"`
	Regex  string `json:"regex,omitempty"`
}

// Backend holds a Kubernetes Service that points to a backend for traffic.
type Backend struct {
	// Name of the Service.
	Name string `json:"name,omitempty"`
	// Namespace of the Service.
	Namespace string `json:"namespace,omitempty"`
	// Port contains the details of the port on the Service to connect to
	// If unset, the same port as the top-level Listener will be used.
	Port *BackendPort `json:"port,omitempty"`
}

// BackendPort holds the details of what port on the Service to connect to.
// Only one of Port or Name can be set.
type BackendPort struct {
	// Port holds the numeric port to connect to.
	Port uint32 `json:"port,omitempty"`
	// Name holds a string which will be used to connect to the port with a
	// matching spec.ports[].name in the target Service.
	Name string `json:"name,omitempty"`
}
<|endoftext|>


<commit_before>package godoauth

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/docker/libtrust"
	"gopkg.in/yaml.v2"
)

type Config struct {
	Version string     `yaml:"version,omitempty"`
	Log     Log        `yaml:"log,omitempty"`
	Storage Storage    `yaml:"storage,omitempty"`
	HTTP    ServerConf `yaml:"http"`
	Token   Token      `yaml:"token"`
}

type Log struct {
	Level string `yaml:"level,omitempty"`
	File  string `yaml:"file,omitempty"`
}

type Storage struct {
	Vault Vault `yaml:"vault"`
}

type Vault struct {
	Host      string        `yaml:"host"`
	Port      int           `yaml:"port"`
	AuthToken string        `yaml:"auth_token"`
	Proto     string        `yaml:"proto"`
	Timeout   time.Duration `yaml:"timeout,omitempty"`
}

func (v Vault) HostURL() string {
	return fmt.Sprintf("%s://%s:%d", v.Proto, v.Host, v.Port)
}

type Duration time.Duration

func (d *Duration) UnmarshalText(b []byte) error {
	v, err := time.ParseDuration(string(b))
	if err != nil {
		return err
	}
	*d = Duration(v)
	return nil
}

type ServerConf struct {
	Addr    string        `yaml:"addr"`
	Timeout time.Duration `yaml:"timeout"`
	TLS     ServerTLS     `yaml:"tls"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

type ServerTLS struct {
	Certificate string `yaml:"certificate,omitempty"`
	Key         string `yaml:"key,omitempty"`
}

type Token struct {
	Issuer      string `yaml:"issuer"`
	Expiration  int64  `yaml:"expiration"`
	Certificate string `yaml:"certificate"`
	Key         string `yaml:"key"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

func (c *Config) LoadFromFile(path string) error {
	fp, err := os.Open(path)
	if err != nil {
		return err
	}
	return c.Parse(fp)
}

func (c *Config) Parse(rd io.Reader) error {
	in, err := ioutil.ReadAll(rd)
	if err != nil {
		return err
	}

	err = yaml.Unmarshal(in, c)
	if err != nil {
		return err
	}

	if c.Token.Certificate == "" || c.Token.Key == "" {
		return fmt.Errorf("Missing Certificate or Key for the Token definition")
	}

	_, err = url.Parse(c.Storage.Vault.Proto + "://" + c.Storage.Vault.Host + ":" + strconv.Itoa(c.Storage.Vault.Port))
	if err != nil {
		return err
	}

	if c.Storage.Vault.Timeout <= 0 {
		c.Storage.Vault.Timeout = time.Duration(3 * time.Second)
	}

	if c.HTTP.Timeout <= 0 {
		c.HTTP.Timeout = time.Duration(5 * time.Second)
	}

	return nil
}

func (c *Config) LoadCerts() error {
	var err error

	c.Token.publicKey, c.Token.privateKey, err = c.loadCerts(c.Token.Certificate, c.Token.Key)
	if err != nil {
		return err
	}
	// Sign something dummy to find out which algorithm is used.
	_, sigAlg, err := c.Token.privateKey.Sign(strings.NewReader("whoami"), 0)
	if err != nil {
		return fmt.Errorf("failed to sign: %s", err)
	}
	// check if the library supports this sign algorithm
	if alg := jwt.GetSigningMethod(sigAlg); alg == nil {
		return fmt.Errorf("signing algorithm not supported: %s", sigAlg)
	}
	return nil
}

func (c *Config) loadCerts(certFile, keyFile string) (pk libtrust.PublicKey, prk libtrust.PrivateKey, err error) {
	certificate, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return
	}
	x509Cert, err := x509.ParseCertificate(certificate.Certificate[0])
	if err != nil {
		return
	}
	pk, err = libtrust.FromCryptoPublicKey(x509Cert.PublicKey)
	if err != nil {
		return
	}
	prk, err = libtrust.FromCryptoPrivateKey(certificate.PrivateKey)
	return
}
<commit_msg>Use Vault.HostURL in config validation<commit_after>package godoauth

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/docker/libtrust"
	"gopkg.in/yaml.v2"
)

type Config struct {
	Version string     `yaml:"version,omitempty"`
	Log     Log        `yaml:"log,omitempty"`
	Storage Storage    `yaml:"storage,omitempty"`
	HTTP    ServerConf `yaml:"http"`
	Token   Token      `yaml:"token"`
}

type Log struct {
	Level string `yaml:"level,omitempty"`
	File  string `yaml:"file,omitempty"`
}

type Storage struct {
	Vault Vault `yaml:"vault"`
}

type Vault struct {
	Host      string        `yaml:"host"`
	Port      int           `yaml:"port"`
	AuthToken string        `yaml:"auth_token"`
	Proto     string        `yaml:"proto"`
	Timeout   time.Duration `yaml:"timeout,omitempty"`
}

func (v Vault) HostURL() string {
	return fmt.Sprintf("%s://%s:%d", v.Proto, v.Host, v.Port)
}

type Duration time.Duration

func (d *Duration) UnmarshalText(b []byte) error {
	v, err := time.ParseDuration(string(b))
	if err != nil {
		return err
	}
	*d = Duration(v)
	return nil
}

type ServerConf struct {
	Addr    string        `yaml:"addr"`
	Timeout time.Duration `yaml:"timeout"`
	TLS     ServerTLS     `yaml:"tls"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

type ServerTLS struct {
	Certificate string `yaml:"certificate,omitempty"`
	Key         string `yaml:"key,omitempty"`
}

type Token struct {
	Issuer      string `yaml:"issuer"`
	Expiration  int64  `yaml:"expiration"`
	Certificate string `yaml:"certificate"`
	Key         string `yaml:"key"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

func (c *Config) LoadFromFile(path string) error {
	fp, err := os.Open(path)
	if err != nil {
		return err
	}
	return c.Parse(fp)
}

func (c *Config) Parse(rd io.Reader) error {
	in, err := ioutil.ReadAll(rd)
	if err != nil {
		return err
	}

	err = yaml.Unmarshal(in, c)
	if err != nil {
		return err
	}

	if c.Token.Certificate == "" || c.Token.Key == "" {
		return fmt.Errorf("Missing Certificate or Key for the Token definition")
	}

	_, err = url.Parse(c.Storage.Vault.HostURL())
	if err != nil {
		return err
	}

	if c.Storage.Vault.Timeout <= 0 {
		c.Storage.Vault.Timeout = time.Duration(3 * time.Second)
	}

	if c.HTTP.Timeout <= 0 {
		c.HTTP.Timeout = time.Duration(5 * time.Second)
	}

	return nil
}

func (c *Config) LoadCerts() error {
	var err error

	c.Token.publicKey, c.Token.privateKey, err = c.loadCerts(c.Token.Certificate, c.Token.Key)
	if err != nil {
		return err
	}
	// Sign something dummy to find out which algorithm is used.
	_, sigAlg, err := c.Token.privateKey.Sign(strings.NewReader("whoami"), 0)
	if err != nil {
		return fmt.Errorf("failed to sign: %s", err)
	}
	// check if the library supports this sign algorithm
	if alg := jwt.GetSigningMethod(sigAlg); alg == nil {
		return fmt.Errorf("signing algorithm not supported: %s", sigAlg)
	}
	return nil
}

func (c *Config) loadCerts(certFile, keyFile string) (pk libtrust.PublicKey, prk libtrust.PrivateKey, err error) {
	certificate, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return
	}
	x509Cert, err := x509.ParseCertificate(certificate.Certificate[0])
	if err != nil {
		return
	}
	pk, err = libtrust.FromCryptoPublicKey(x509Cert.PublicKey)
	if err != nil {
		return
	}
	prk, err = libtrust.FromCryptoPrivateKey(certificate.PrivateKey)
	return
}
<|endoftext|>


<commit_before>// Package router provides a simple router to match URL's
// with Controllers/Services/etc. Support for parameters
// and options.
package router

// Route struct contains all the information for the route
// including the Controller and the Method to call
type Route struct {
	Pattern    string
	Controller string
	Method     string
}

// Router struct stores all the routes configured using
// a simple map of the url to the respective Route struct
// TODO improve mapping
type Router struct {
	Routes map[string]*Route
}

// Router Handler method, receives a url and return the
// associated route
func (router *Router) Handle(url string) *Route {

	// @TODO pattern matching
	return router.Routes[url]
}
<commit_msg>change Route Controller property to Action<commit_after>// Package router provides a simple router to match URL's
// with Controllers/Services/etc. Support for parameters
// and options.
package router

// Route struct contains all the information for the route
// including the Controller and the Method to call
type Route struct {
	Pattern string
	Action  string
	Method  string
}

// Router struct stores all the routes configured using
// a simple map of the url to the respective Route struct
// TODO improve mapping
type Router struct {
	Routes map[string]*Route
}

// Router Handler method, receives a url and return the
// associated route
func (router *Router) Handle(url string) *Route {

	// @TODO pattern matching
	return router.Routes[url]
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"libvsw"
	//"log"
	"os"
	"time"
)

func sample_pinp(vsw *libvsw.Vsw, mainSrc, subSrc int) {
	vsw.PinpGeometry(0, 0, 65536/4, 65536/4, 0, 0, 65536, 65536)
	vsw.PinpBorder(0x00ff00ff, 4)
	vsw.SetSubMode(libvsw.SUB_MODE_PINP)
	vsw.CutUs(mainSrc, subSrc)
	rate := 1000
	for {
		for i := 1; i < 4; i++ {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*i, 0, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
		for i := 1; i < 4; i++ {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*3, 65536/4*i, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
		for i := 2; i >= 0; i-- {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*i, 65536/4*3, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
		for i := 2; i >= 0; i-- {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*0, 65536/4*i, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
	}
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: %s IP_address_of_livewedge\n", os.Args[0])
		os.Exit(1)
	}
	vsw, err := libvsw.NewVsw(os.Args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open LiveWedge: %s\n", err)
		os.Exit(1)
	}
	defer vsw.Close()

	sample_pinp(vsw, 1, 4)
}
<commit_msg>sample_pinp: Use MixSub to show sub screen slowly<commit_after>package main

import (
	"fmt"
	"libvsw"
	//"log"
	"os"
	"time"
)

func sample_pinp(vsw *libvsw.Vsw, mainSrc, subSrc int) {
	vsw.Cut(mainSrc)
	vsw.CutSub(0) // Erase sub screen
	vsw.PinpGeometry(0, 0, 65536/4, 65536/4, 0, 0, 65536, 65536)
	vsw.PinpBorder(0x00ff00ff, 4)
	vsw.SetSubMode(libvsw.SUB_MODE_PINP)
	rate := 1000
	vsw.MixSub(rate, subSrc) // Show sub screen slowly
	time.Sleep(time.Duration(rate) * time.Millisecond)
	for {
		for i := 1; i < 4; i++ {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*i, 0, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
		for i := 1; i < 4; i++ {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*3, 65536/4*i, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
		for i := 2; i >= 0; i-- {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*i, 65536/4*3, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
		for i := 2; i >= 0; i-- {
			time.Sleep(time.Duration(rate) * time.Millisecond)
			vsw.PinpGeometry(65536/4*0, 65536/4*i, 65536/4, 65536/4, 0, 0, 65536, 65536)
		}
	}
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: %s IP_address_of_livewedge\n", os.Args[0])
		os.Exit(1)
	}
	vsw, err := libvsw.NewVsw(os.Args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open LiveWedge: %s\n", err)
		os.Exit(1)
	}
	defer vsw.Close()

	sample_pinp(vsw, 1, 4)
}
<|endoftext|>