<commit_before>package main

import (
	"code.google.com/p/gorilla/mux"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strings"
)

var (
	httpPort     = flag.Int("http", 80, "Port to bind HTTP server to")
	httpsPort    = flag.Int("https", 443, "Port to bind HTTP server to")
	domainSuffix = flag.String("domain", "", "Domain-suffix for apps")
	help         = flag.Bool("h", false, "Show this help")
)

func main() {
	flag.Parse()

	if *help || *domainSuffix == "" {
		flag.PrintDefaults()
		return
	}

	httpAddr := fmt.Sprintf("0.0.0.0:%d", *httpPort)
	httpsAddr := fmt.Sprintf("0.0.0.0:%d", *httpsPort)

	httpr := mux.NewRouter()
	httpr.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, fmt.Sprintf("https://%s:%d", r.Host, *httpsPort), http.StatusMovedPermanently)
	})
	log.Printf("Binding HTTP to %s", httpAddr)
	go http.ListenAndServe(httpAddr, httpr)

	httpsr := mux.NewRouter()
	httpsr.PathPrefix("/").HandlerFunc(handler)
	log.Printf("Binding HTTPS to %s", httpsAddr)
	e := http.ListenAndServeTLS(httpsAddr, "cert.pem", "key.pem", httpsr)
	if e != nil {
		log.Fatalf("Could not start webserver: %s", e)
	}
}

var (
	backends = map[string]string{}
)

func handler(w http.ResponseWriter, r *http.Request) {
	if !strings.HasSuffix(r.Host, *domainSuffix) {
		http.Error(w, "Invalid domain", 404)
		return
	}
	// Strip domain suffix + dot
	appname := r.Host[0 : len(r.Host)-len(*domainSuffix)-1]
	_, ok := backends[appname]
	if !ok {
		http.Error(w, "Unknown app name", 404)
		return
	}
	fmt.Fprintf(w, "Bla")
}
<commit_msg>Add error handling for HTTP server<commit_after>package main

import (
	"code.google.com/p/gorilla/mux"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strings"
)

var (
	httpPort     = flag.Int("http", 80, "Port to bind HTTP server to")
	httpsPort    = flag.Int("https", 443, "Port to bind HTTP server to")
	domainSuffix = flag.String("domain", "", "Domain-suffix for apps")
	help         = flag.Bool("h", false, "Show this help")
)

func main() {
	flag.Parse()

	if *help || *domainSuffix == "" {
		flag.PrintDefaults()
		return
	}

	httpAddr := fmt.Sprintf("0.0.0.0:%d", *httpPort)
	httpsAddr := fmt.Sprintf("0.0.0.0:%d", *httpsPort)

	httpr := mux.NewRouter()
	httpr.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, fmt.Sprintf("https://%s:%d", r.Host, *httpsPort), http.StatusMovedPermanently)
	})
	log.Printf("Binding HTTP to %s", httpAddr)
	go func() {
		e := http.ListenAndServe(httpAddr, httpr)
		if e != nil {
			log.Fatalf("Could not bind HTTP server: %s", e)
		}
	}()

	httpsr := mux.NewRouter()
	httpsr.PathPrefix("/").HandlerFunc(appHandler)
	log.Printf("Binding HTTPS to %s", httpsAddr)
	e := http.ListenAndServeTLS(httpsAddr, "cert.pem", "key.pem", httpsr)
	if e != nil {
		log.Fatalf("Could not bind HTTPS server: %s", e)
	}
}

var (
	backends = map[string]string{}
)

func appHandler(w http.ResponseWriter, r *http.Request) {
	if !strings.HasSuffix(r.Host, *domainSuffix) {
		http.Error(w, "Invalid domain", 404)
		return
	}
	// Strip domain suffix + dot
	appname := r.Host[0 : len(r.Host)-len(*domainSuffix)-1]
	_, ok := backends[appname]
	if !ok {
		http.Error(w, "Unknown app name", 404)
		return
	}
	fmt.Fprintf(w, "Bla")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"github.com/streadway/amqp"
	"io"
	"koding/fujin/proxyconfig"
	"koding/tools/config"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

func init() {
	log.SetPrefix("fujin ")
}

type IncomingMessage struct {
	ProxyResponse *proxyconfig.ProxyResponse
}

var proxy proxyconfig.Proxy // this will be only updated whenever we receive a msg from kontrold
var proxyDB *proxyconfig.ProxyConfiguration
var amqpStream *AmqpStream
var start chan bool
var first bool = true

func main() {
	log.Printf("fujin proxy started ")
	start = make(chan bool)

	// open kontrol-daemon database connection
	var err error
	proxyDB, err = proxyconfig.Connect()
	if err != nil {
		log.Fatalf("proxyconfig mongodb connect: %s", err)
	}

	// register fujin instance to kontrol-daemon
	amqpStream = setupAmqp()
	log.Printf("register fujin to kontrold with uuid '%s'", amqpStream.uuid)
	amqpStream.Publish(buildProxyCmd("addProxy", amqpStream.uuid))
	log.Println("register command is send. waiting for response from kontrold...")
	go handleInput(amqpStream.input, amqpStream.uuid)

	select {
	case <-time.After(time.Second * 15):
		log.Fatalf("ERROR: no repsonse received from kontrold, aborting process.")
		os.Exit(1)
	case <-start: // wait until we got message from kontrold or exit via above chan
	}

	reverseProxy := NewSingleHostReverseProxy()
	http.Handle("/", reverseProxy)

	// start one with goroutine in order not to block the other one
	go func() {
		err = http.ListenAndServeTLS(":"+config.HttpsPort, "cert.pem", "key.pem", nil)
		if err != nil {
			log.Println("https mode is disabled. please add cert.pem and key.pem files.")
		} else {
			log.Printf("https mode is enabled. serving at :%s ...", config.HttpsPort)
		}
	}()

	log.Printf("normal mode is enabled. serving at :%s ...", config.HttpPort)
	http.ListenAndServe(":"+config.HttpPort, nil)
}

func handleInput(input <-chan amqp.Delivery, uuid string) {
	for {
		select {
		case d := <-input:
			// log.Printf("got %dB message data: [%v] %s",
			// 	len(d.Body),
			// 	d.DeliveryTag,
			// 	d.Body)

			var msg IncomingMessage

			err := json.Unmarshal(d.Body, &msg)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			if msg.ProxyResponse != nil {
				if msg.ProxyResponse.Action == "updateProxy" {
					log.Println("update action received from kontrold. updating proxy route table")
					var err error
					proxy, err = proxyDB.GetProxy(uuid)
					if err != nil {
						log.Println(err)
					}

					if first {
						start <- true
						first = false
						log.Println("routing tables updated. ready to start servers.")
					}

				}

			} else {
				log.Println("incoming message is in wrong format")
			}
		}
	}
}

func parseKey(host string) (string, string) {
	counts := strings.Count(host, "-")
	if counts == 0 {
		return "wrongService", "wrongKey"
	}

	partsFirst := strings.Split(host, ".")
	firstSub := partsFirst[0]

	partsSecond := strings.Split(firstSub, "-")
	name := partsSecond[0]
	key := partsSecond[1]

	return name, key
}

func targetUrl(numberOfDeaths int, name, key string) *url.URL {
	var target *url.URL
	var err error
	host := targetHost(name, key)

	keyRoutingTable := proxy.Services[name]
	v := len(keyRoutingTable.Keys[key])
	if v == numberOfDeaths {
		log.Println("All given servers are death. Fallback to localhost:8000")
		target, err = url.Parse("http://proxy.in.koding.com")
		if err != nil {
			log.Fatal(err)
		}
		return target
	}

	target, err = url.Parse("http://" + host)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("got / request. using proxy to %s (key: %s)", target.Host, key)

	return target
}

func targetHost(name, key string) string {
	var hostname string

	keyRoutingTable := proxy.Services[name]

	v := len(keyRoutingTable.Keys)
	if v == 0 {
		hostname = "proxy.in.koding.com"
		log.Println("no keys are added, using default url ", hostname)
	} else {
		// use round-robin algorithm for each hostname
		for i, value := range keyRoutingTable.Keys[key] {
			currentIndex := value.CurrentIndex
			if currentIndex == i {
				hostname = value.Host
				for k, _ := range keyRoutingTable.Keys[key] {
					if len(keyRoutingTable.Keys[key])-1 == currentIndex {
						keyRoutingTable.Keys[key][k].CurrentIndex = 0 // reached end
					} else {
						keyRoutingTable.Keys[key][k].CurrentIndex = currentIndex + 1
					}
				}
				break
			}
		}
	}

	return hostname
}

/*************************************************
*
*  util functions
*
*  - arslan
*************************************************/

func buildProxyCmd(action, uuid string) []byte {
	var req proxyconfig.ProxyMessage
	req.Action = action
	req.Uuid = uuid

	data, err := json.Marshal(req)
	if err != nil {
		log.Println("json marshall error", err)
	}

	return data
}

// not used currently, find a more reliable way
func checkServer(host string) error {
	log.Println("Checking server")
	remoteAddr, err := net.ResolveTCPAddr("tcp", host)
	if err != nil {
		return err
	}

	remoteConn, err := net.DialTCP("tcp", nil, remoteAddr)
	if err != nil {
		return err
	}

	remoteConn.Close()
	return nil
}

/*************************************************
*
*  modified version of go's reverseProxy source code
*  has support for dynamic url and websockets
*
*  - arslan
*************************************************/

// onExitFlushLoop is a callback set by tests to detect the state of the
// flushLoop() goroutine.
var onExitFlushLoop func()

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper

	// FlushInterval specifies the flush interval
	// to flush to the client while copying the
	// response body.
	// If zero, no periodic flushing is done.
	FlushInterval time.Duration
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
func NewSingleHostReverseProxy() *ReverseProxy {
	director := func(req *http.Request) {
		log.Println("HOST:", req.RequestURI, req.Host)
		var deaths int
		name, key := parseKey(req.Host)
		target := targetUrl(deaths, name, key)
		targetQuery := target.RawQuery

		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &ReverseProxy{Director: director}
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te", // canonicalized version of "TE"
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",
}

func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	name, _ := os.Hostname()
	log.Println("PUBLIC HOSTNAME", req.Host)
	log.Println("LOCAL HOSTNAME", name)
	if name == req.Host {
		io.WriteString(rw, "hello, world!\n")
		return
	}

	conn_hdr := ""
	conn_hdrs := req.Header["Connection"]
	log.Printf("Connection headers: %v", conn_hdrs)
	if len(conn_hdrs) > 0 {
		conn_hdr = conn_hdrs[0]
	}

	upgrade_websocket := false
	if strings.ToLower(conn_hdr) == "upgrade" {
		log.Printf("got Connection: Upgrade")
		upgrade_hdrs := req.Header["Upgrade"]
		log.Printf("Upgrade headers: %v", upgrade_hdrs)
		if len(upgrade_hdrs) > 0 {
			upgrade_websocket = (strings.ToLower(upgrade_hdrs[0]) == "websocket")
		}
	}

	// https://groups.google.com/d/msg/golang-nuts/KBx9pDlvFOc/edt4iad96nwJ
	if upgrade_websocket {
		rConn, err := net.Dial("tcp", req.URL.Host)
		if err != nil {
			http.Error(rw, "Error contacting backend server.", http.StatusInternalServerError)
			log.Printf("Error dialing websocket backend %s: %v", req.URL.Host, err)
			return
		}

		hj, ok := rw.(http.Hijacker)
		if !ok {
			http.Error(rw, "Not a hijacker?", http.StatusInternalServerError)
			return
		}

		conn, _, err := hj.Hijack()
		if err != nil {
			log.Printf("Hijack error: %v", err)
			return
		}
		defer conn.Close()
		defer rConn.Close()

		err = req.Write(rConn)
		if err != nil {
			log.Printf("Error copying request to target: %v", err)
			return
		}

		go p.copyResponse(rConn, conn)
		p.copyResponse(conn, rConn)

	} else {
		transport := p.Transport
		if transport == nil {
			transport = http.DefaultTransport
		}

		outreq := new(http.Request)
		*outreq = *req // includes shallow copies of maps, but okay

		p.Director(outreq)
		outreq.Proto = "HTTP/1.1"
		outreq.ProtoMajor = 1
		outreq.ProtoMinor = 1
		outreq.Close = false

		// Remove hop-by-hop headers to the backend.  Especially
		// important is "Connection" because we want a persistent
		// connection, regardless of what the client sent to us.  This
		// is modifying the same underlying map from req (shallow
		// copied above) so we only copy it if necessary.
		copiedHeaders := false
		for _, h := range hopHeaders {
			if outreq.Header.Get(h) != "" {
				if !copiedHeaders {
					outreq.Header = make(http.Header)
					copyHeader(outreq.Header, req.Header)
					copiedHeaders = true
				}
				outreq.Header.Del(h)
			}
		}

		if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
			// If we aren't the first proxy retain prior
			// X-Forwarded-For information as a comma+space
			// separated list and fold multiple headers into one.
			if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
			outreq.Header.Set("X-Forwarded-For", clientIP)
		}

		res, err := transport.RoundTrip(outreq)
		if err != nil {
			log.Printf("http: proxy error: %v", err)
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
		defer res.Body.Close()

		copyHeader(rw.Header(), res.Header)

		rw.WriteHeader(res.StatusCode)
		p.copyResponse(rw, res.Body)
	}

}

func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
	if p.FlushInterval != 0 {
		if wf, ok := dst.(writeFlusher); ok {
			mlw := &maxLatencyWriter{
				dst:     wf,
				latency: p.FlushInterval,
				done:    make(chan bool),
			}
			go mlw.flushLoop()
			defer mlw.stop()
			dst = mlw
		}
	}

	io.Copy(dst, src)
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration

	lk   sync.Mutex // protects Write + Flush
	done chan bool
}

func (m *maxLatencyWriter) Write(p []byte) (int, error) {
	m.lk.Lock()
	defer m.lk.Unlock()
	return m.dst.Write(p)
}

func (m *maxLatencyWriter) flushLoop() {
	t := time.NewTicker(m.latency)
	defer t.Stop()
	for {
		select {
		case <-m.done:
			if onExitFlushLoop != nil {
				onExitFlushLoop()
			}
			return
		case <-t.C:
			m.lk.Lock()
			m.dst.Flush()
			m.lk.Unlock()
		}
	}
}

func (m *maxLatencyWriter) stop() { m.done <- true }

func localIP() (net.IP, error) {
	tt, err := net.Interfaces()
	if err != nil {
		return nil, err
	}
	for _, t := range tt {
		aa, err := t.Addrs()
		if err != nil {
			return nil, err
		}
		for _, a := range aa {
			ipnet, ok := a.(*net.IPNet)
			if !ok {
				continue
			}
			v4 := ipnet.IP.To4()
			if v4 == nil || v4[0] == 127 { // loopback address
				continue
			}
			return v4, nil
		}
	}
	return nil, errors.New("cannot find local IP address")
}
<commit_msg>Some refactor changes to handling fallback domain<commit_after>package main

import (
	"encoding/json"
	"errors"
	"github.com/streadway/amqp"
	"io"
	"koding/fujin/proxyconfig"
	"koding/tools/config"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

func init() {
	log.SetPrefix("fujin ")
}

type IncomingMessage struct {
	ProxyResponse *proxyconfig.ProxyResponse
}

var proxy proxyconfig.Proxy // this will be only updated whenever we receive a msg from kontrold
var proxyDB *proxyconfig.ProxyConfiguration
var amqpStream *AmqpStream
var start chan bool
var first bool = true

func main() {
	log.Printf("fujin proxy started ")
	start = make(chan bool)

	// open kontrol-daemon database connection
	var err error
	proxyDB, err = proxyconfig.Connect()
	if err != nil {
		log.Fatalf("proxyconfig mongodb connect: %s", err)
	}

	// register fujin instance to kontrol-daemon
	amqpStream = setupAmqp()
	log.Printf("register fujin to kontrold with uuid '%s'", amqpStream.uuid)
	amqpStream.Publish(buildProxyCmd("addProxy", amqpStream.uuid))
	log.Println("register command is send. waiting for response from kontrold...")
	go handleInput(amqpStream.input, amqpStream.uuid)

	select {
	case <-time.After(time.Second * 15):
		log.Fatalf("ERROR: no repsonse received from kontrold, aborting process.")
		os.Exit(1)
	case <-start: // wait until we got message from kontrold or exit via above chan
	}

	reverseProxy := NewSingleHostReverseProxy()
	http.Handle("/", reverseProxy)

	// start one with goroutine in order not to block the other one
	go func() {
		err = http.ListenAndServeTLS(":"+config.HttpsPort, "cert.pem", "key.pem", nil)
		if err != nil {
			log.Println("https mode is disabled. please add cert.pem and key.pem files.")
		} else {
			log.Printf("https mode is enabled. serving at :%s ...", config.HttpsPort)
		}
	}()

	log.Printf("normal mode is enabled. serving at :%s ...", config.HttpPort)
	http.ListenAndServe(":"+config.HttpPort, nil)
}

func handleInput(input <-chan amqp.Delivery, uuid string) {
	for {
		select {
		case d := <-input:
			// log.Printf("got %dB message data: [%v] %s",
			// 	len(d.Body),
			// 	d.DeliveryTag,
			// 	d.Body)

			var msg IncomingMessage

			err := json.Unmarshal(d.Body, &msg)
			if err != nil {
				log.Print("bad json incoming msg: ", err)
			}

			if msg.ProxyResponse != nil {
				if msg.ProxyResponse.Action == "updateProxy" {
					log.Println("update action received from kontrold. updating proxy route table")
					var err error
					proxy, err = proxyDB.GetProxy(uuid)
					if err != nil {
						log.Println(err)
					}

					if first {
						start <- true
						first = false
						log.Println("routing tables updated. ready to start servers.")
					}

				}

			} else {
				log.Println("incoming message is in wrong format")
			}
		}
	}
}

func parseKey(host string) (string, string) {
	counts := strings.Count(host, "-")
	if counts == 0 {
		return "wrongService", "wrongKey"
	}

	partsFirst := strings.Split(host, ".")
	firstSub := partsFirst[0]

	partsSecond := strings.Split(firstSub, "-")
	name := partsSecond[0]
	key := partsSecond[1]

	return name, key
}

func targetUrl(numberOfDeaths int, name, key string) *url.URL {
	var target *url.URL
	var err error
	host := targetHost(name, key)

	target, err = url.Parse("http://" + host)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("got / request. using proxy to %s (key: %s)", target.Host, key)

	return target
}

func targetHost(name, key string) string {
	var hostname string

	keyRoutingTable := proxy.Services[name]
	v := len(keyRoutingTable.Keys)
	if v == 0 {
		hostname = "proxy.in.koding.com"
		log.Println("no keys are added, using default url ", hostname)
	} else {
		// use round-robin algorithm for each hostname
		for i, value := range keyRoutingTable.Keys[key] {
			currentIndex := value.CurrentIndex
			if currentIndex == i {
				hostname = value.Host
				for k, _ := range keyRoutingTable.Keys[key] {
					if len(keyRoutingTable.Keys[key])-1 == currentIndex {
						keyRoutingTable.Keys[key][k].CurrentIndex = 0 // reached end
					} else {
						keyRoutingTable.Keys[key][k].CurrentIndex = currentIndex + 1
					}
				}
				break
			}
		}
	}

	return hostname
}

/*************************************************
*
*  util functions
*
*  - arslan
*************************************************/

func buildProxyCmd(action, uuid string) []byte {
	var req proxyconfig.ProxyMessage
	req.Action = action
	req.Uuid = uuid

	data, err := json.Marshal(req)
	if err != nil {
		log.Println("json marshall error", err)
	}

	return data
}

// not used currently, find a more reliable way
func checkServer(host string) error {
	log.Println("Checking server")
	remoteAddr, err := net.ResolveTCPAddr("tcp", host)
	if err != nil {
		return err
	}

	remoteConn, err := net.DialTCP("tcp", nil, remoteAddr)
	if err != nil {
		return err
	}

	remoteConn.Close()
	return nil
}

/*************************************************
*
*  modified version of go's reverseProxy source code
*  has support for dynamic url and websockets
*
*  - arslan
*************************************************/

// onExitFlushLoop is a callback set by tests to detect the state of the
// flushLoop() goroutine.
var onExitFlushLoop func()

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper

	// FlushInterval specifies the flush interval
	// to flush to the client while copying the
	// response body.
	// If zero, no periodic flushing is done.
	FlushInterval time.Duration
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
func NewSingleHostReverseProxy() *ReverseProxy {
	director := func(req *http.Request) {
		log.Println("HOST:", req.RequestURI, req.Host)
		var deaths int
		name, key := parseKey(req.Host)
		target := targetUrl(deaths, name, key)
		targetQuery := target.RawQuery

		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &ReverseProxy{Director: director}
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te", // canonicalized version of "TE"
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",
}

func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	conn_hdr := ""
	conn_hdrs := req.Header["Connection"]
	log.Printf("Connection headers: %v", conn_hdrs)
	if len(conn_hdrs) > 0 {
		conn_hdr = conn_hdrs[0]
	}

	upgrade_websocket := false
	if strings.ToLower(conn_hdr) == "upgrade" {
		log.Printf("got Connection: Upgrade")
		upgrade_hdrs := req.Header["Upgrade"]
		log.Printf("Upgrade headers: %v", upgrade_hdrs)
		if len(upgrade_hdrs) > 0 {
			upgrade_websocket = (strings.ToLower(upgrade_hdrs[0]) == "websocket")
		}
	}

	// https://groups.google.com/d/msg/golang-nuts/KBx9pDlvFOc/edt4iad96nwJ
	if upgrade_websocket {
		rConn, err := net.Dial("tcp", req.URL.Host)
		if err != nil {
			http.Error(rw, "Error contacting backend server.", http.StatusInternalServerError)
			log.Printf("Error dialing websocket backend %s: %v", req.URL.Host, err)
			return
		}

		hj, ok := rw.(http.Hijacker)
		if !ok {
			http.Error(rw, "Not a hijacker?", http.StatusInternalServerError)
			return
		}

		conn, _, err := hj.Hijack()
		if err != nil {
			log.Printf("Hijack error: %v", err)
			return
		}
		defer conn.Close()
		defer rConn.Close()

		err = req.Write(rConn)
		if err != nil {
			log.Printf("Error copying request to target: %v", err)
			return
		}

		go p.copyResponse(rConn, conn)
		p.copyResponse(conn, rConn)

	} else {
		transport := p.Transport
		if transport == nil {
			transport = http.DefaultTransport
		}

		outreq := new(http.Request)
		*outreq = *req // includes shallow copies of maps, but okay

		p.Director(outreq)
		outreq.Proto = "HTTP/1.1"
		outreq.ProtoMajor = 1
		outreq.ProtoMinor = 1
		outreq.Close = false

		name, _ := os.Hostname()
		log.Println("LOCAL HOSTNAME", name)
		log.Println("REMOTE HOSTANME", outreq.URL)
		if name == outreq.URL.Host {
			io.WriteString(rw, "hello, world!\n")
			return
		}

		// Remove hop-by-hop headers to the backend.  Especially
		// important is "Connection" because we want a persistent
		// connection, regardless of what the client sent to us.  This
		// is modifying the same underlying map from req (shallow
		// copied above) so we only copy it if necessary.
		copiedHeaders := false
		for _, h := range hopHeaders {
			if outreq.Header.Get(h) != "" {
				if !copiedHeaders {
					outreq.Header = make(http.Header)
					copyHeader(outreq.Header, req.Header)
					copiedHeaders = true
				}
				outreq.Header.Del(h)
			}
		}

		if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
			// If we aren't the first proxy retain prior
			// X-Forwarded-For information as a comma+space
			// separated list and fold multiple headers into one.
			if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
			outreq.Header.Set("X-Forwarded-For", clientIP)
		}

		res, err := transport.RoundTrip(outreq)
		if err != nil {
			log.Printf("http: proxy error: %v", err)
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
		defer res.Body.Close()

		copyHeader(rw.Header(), res.Header)

		rw.WriteHeader(res.StatusCode)
		p.copyResponse(rw, res.Body)
	}

}

func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
	if p.FlushInterval != 0 {
		if wf, ok := dst.(writeFlusher); ok {
			mlw := &maxLatencyWriter{
				dst:     wf,
				latency: p.FlushInterval,
				done:    make(chan bool),
			}
			go mlw.flushLoop()
			defer mlw.stop()
			dst = mlw
		}
	}

	io.Copy(dst, src)
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration

	lk   sync.Mutex // protects Write + Flush
	done chan bool
}

func (m *maxLatencyWriter) Write(p []byte) (int, error) {
	m.lk.Lock()
	defer m.lk.Unlock()
	return m.dst.Write(p)
}

func (m *maxLatencyWriter) flushLoop() {
	t := time.NewTicker(m.latency)
	defer t.Stop()
	for {
		select {
		case <-m.done:
			if onExitFlushLoop != nil {
				onExitFlushLoop()
			}
			return
		case <-t.C:
			m.lk.Lock()
			m.dst.Flush()
			m.lk.Unlock()
		}
	}
}

func (m *maxLatencyWriter) stop() { m.done <- true }

func localIP() (net.IP, error) {
	tt, err := net.Interfaces()
	if err != nil {
		return nil, err
	}
	for _, t := range tt {
		aa, err := t.Addrs()
		if err != nil {
			return nil, err
		}
		for _, a := range aa {
			ipnet, ok := a.(*net.IPNet)
			if !ok {
				continue
			}
			v4 := ipnet.IP.To4()
			if v4 == nil || v4[0] == 127 { // loopback address
				continue
			}
			return v4, nil
		}
	}
	return nil, errors.New("cannot find local IP address")
}
<|endoftext|>


<commit_before>package cribbage

import (
	"fmt"
	"github.com/fatih/color"
)

const (
	SPADE   = "♠"
	HEART   = "♥"
	DIAMOND = "♦"
	CLUB    = "♣"
)

func RenderCard(card *Card) string {
	if card == nil {
		return "<nil-card>"
	}
	var printer func(...interface{}) string
	if isUnknown(card) {
		printer = color.New(color.BgBlue, color.FgWhite).SprintFunc()
	} else if isRed(card) {
		printer = color.New(color.BgWhite, color.FgRed).SprintFunc()
	} else {
		printer = color.New(color.BgWhite, color.FgBlack).SprintFunc()
	}
	return printer(renderRank(card), renderSuit(card))
}

func RenderHand(hand *Hand) string {
	if hand == nil {
		return "<nil-hand>"
	}
	out := ""
	for i, card := range hand.cards {
		if card == nil {
			card = &Card{}
		}
		out += fmt.Sprintf("%d:", i) + RenderCard(card)
		if i < len(hand.cards) {
			out += " "
		}
	}
	return out
}

func RenderSeq(seq *Sequence) string {
	if seq == nil {
		return "<nil-seq>"
	}
	out := ""
	for i := 0; i < seq.Size(); i++ {
		player, card := seq.Get(i)
		out += fmt.Sprintf("p%d:", player) + RenderCard(card) + " "
	}
	return out
}

func isRed(card *Card) bool {
	return card.Suit == "Hearts" || card.Suit == "Diamonds"
}

func isUnknown(card *Card) bool {
	return card.Suit == "" || card.Suit == ""
}

func renderSuit(card *Card) string {
	switch card.Suit {
	case "Hearts":
		return HEART
	case "Spades":
		return SPADE
	case "Clubs":
		return CLUB
	case "Diamonds":
		return DIAMOND
	default:
		return "▹"
	}
}

func renderRank(card *Card) string {
	switch card.Rank {
	case "Two":
		return "2"
	case "Three":
		return "3"
	case "Four":
		return "4"
	case "Five":
		return "5"
	case "Six":
		return "6"
	case "Seven":
		return "7"
	case "Eight":
		return "8"
	case "Nine":
		return "9"
	case "Ten":
		return "10"
	case "Jack":
		return "J"
	case "Queen":
		return "Q"
	case "King":
		return "K"
	case "Ace":
		return "A"
	default:
		return "◃"
	}
}
<commit_msg>Display total of current card sequence<commit_after>package cribbage

import (
	"fmt"
	"github.com/fatih/color"
)

const (
	SPADE   = "♠"
	HEART   = "♥"
	DIAMOND = "♦"
	CLUB    = "♣"
)

func RenderCard(card *Card) string {
	if card == nil {
		return "<nil-card>"
	}
	var printer func(...interface{}) string
	if isUnknown(card) {
		printer = color.New(color.BgBlue, color.FgWhite).SprintFunc()
	} else if isRed(card) {
		printer = color.New(color.BgWhite, color.FgRed).SprintFunc()
	} else {
		printer = color.New(color.BgWhite, color.FgBlack).SprintFunc()
	}
	return printer(renderRank(card), renderSuit(card))
}

func RenderHand(hand *Hand) string {
	if hand == nil {
		return "<nil-hand>"
	}
	out := ""
	for i, card := range hand.cards {
		if card == nil {
			card = &Card{}
		}
		out += fmt.Sprintf("%d:", i) + RenderCard(card)
		if i < len(hand.cards) {
			out += " "
		}
	}
	return out
}

func RenderSeq(seq *Sequence) string {
	if seq == nil {
		return "<nil-seq>"
	}
	out := ""
	for i := 0; i < seq.Size(); i++ {
		player, card := seq.Get(i)
		out += fmt.Sprintf("p%d:", player) + RenderCard(card) + " "
	}
	return out + fmt.Sprintf("total: %d", seq.Total())
}

func isRed(card *Card) bool {
	return card.Suit == "Hearts" || card.Suit == "Diamonds"
}

func isUnknown(card *Card) bool {
	return card.Suit == "" || card.Suit == ""
}

func renderSuit(card *Card) string {
	switch card.Suit {
	case "Hearts":
		return HEART
	case "Spades":
		return SPADE
	case "Clubs":
		return CLUB
	case "Diamonds":
		return DIAMOND
	default:
		return "▹"
	}
}

func renderRank(card *Card) string {
	switch card.Rank {
	case "Two":
		return "2"
	case "Three":
		return "3"
	case "Four":
		return "4"
	case "Five":
		return "5"
	case "Six":
		return "6"
	case "Seven":
		return "7"
	case "Eight":
		return "8"
	case "Nine":
		return "9"
	case "Ten":
		return "10"
	case "Jack":
		return "J"
	case "Queen":
		return "Q"
	case "King":
		return "K"
	case "Ace":
		return "A"
	default:
		return "◃"
	}
}
<|endoftext|>


<commit_before>// Package goawk is an implementation of AWK written in Go.
//
// You can use the command-line "goawk" command or run AWK from your
// Go programs using the "interp" package. The command-line program
// has the same interface as regular awk:
//
//     goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
//
// The -F flag specifies the field separator (the default is to split
// on whitespace). The -v flag allows you to set a variable to a
// given value (multiple -v flags allowed). The -f flag allows you to
// read AWK source from a file instead of the 'prog' command-line
// argument. The rest of the arguments are input filenames (default
// is to read from stdin).
//
// A simple example (prints the sum of the numbers in the file's
// second column):
//
//     $ echo 'foo 12
//     > bar 34
//     > baz 56' >file.txt
//     $ goawk '{ sum += $2 } END { print sum }' file.txt
//     102
//
// To use GoAWK in your Go programs, see README.md or the "interp"
// docs.
//
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"unicode/utf8"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/lexer"
	"github.com/benhoyt/goawk/parser"
)

const (
	version    = "v1.12.0"
	copyright  = "GoAWK " + version + " - Copyright (c) 2021 Ben Hoyt"
	shortUsage = "usage: goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]"
	longUsage  = `Standard AWK arguments:
  -F separator
        field separator (default " ")
  -v assignment
        name=value variable assignment (multiple allowed)
  -f progfile
        load AWK source from progfile (multiple allowed)

Additional GoAWK arguments:
  -b    use byte indexes for index(), length(), match(), and substr()
  -cpuprofile file
        write CPU profile to file
  -d    debug mode (print parsed AST to stderr)
  -dt   show variable types debug info
  -h    show this usage message
  -version
        show GoAWK version and exit
`
)

func main() {
	// Parse command line arguments manually rather than using the
	// "flag" package, so we can support flags with no space between
	// flag and argument, like '-F:' (allowed by POSIX)
	var progFiles []string
	var vars []string
	fieldSep := " "
	cpuprofile := ""
	debug := false
	debugTypes := false
	memprofile := ""
	useBytes := false

	var i int
	for i = 1; i < len(os.Args); i++ {
		// Stop on explicit end of args or first arg not prefixed with "-"
		arg := os.Args[i]
		if arg == "--" {
			i++
			break
		}
		if arg == "-" || !strings.HasPrefix(arg, "-") {
			break
		}

		switch arg {
		case "-F":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -F")
			}
			i++
			fieldSep = os.Args[i]
		case "-f":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -f")
			}
			i++
			progFiles = append(progFiles, os.Args[i])
		case "-v":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -v")
			}
			i++
			vars = append(vars, os.Args[i])
		case "-b":
			useBytes = true
		case "-cpuprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -cpuprofile")
			}
			i++
			cpuprofile = os.Args[i]
		case "-d":
			debug = true
		case "-dt":
			debugTypes = true
		case "-h", "--help":
			fmt.Printf("%s\n\n%s\n\n%s", copyright, shortUsage, longUsage)
			os.Exit(0)
		case "-memprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -memprofile")
			}
			i++
			memprofile = os.Args[i]
		case "-version", "--version":
			fmt.Println(version)
			os.Exit(0)
		default:
			switch {
			case strings.HasPrefix(arg, "-F"):
				fieldSep = arg[2:]
			case strings.HasPrefix(arg, "-f"):
				progFiles = append(progFiles, arg[2:])
			case strings.HasPrefix(arg, "-v"):
				vars = append(vars, arg[2:])
			case strings.HasPrefix(arg, "-cpuprofile="):
				cpuprofile = arg[12:]
			case strings.HasPrefix(arg, "-memprofile="):
				memprofile = arg[12:]
			default:
				errorExitf("flag provided but not defined: %s", arg)
			}
		}
	}

	// Any remaining args are program and input files
	args := os.Args[i:]

	var src []byte
	var stdinBytes []byte // used if there's a parse error
	if len(progFiles) > 0 {
		// Read source: the concatenation of all source files specified
		buf := &bytes.Buffer{}
		progFiles = expandWildcardsOnWindows(progFiles)
		for _, progFile := range progFiles {
			if progFile == "-" {
				b, err := ioutil.ReadAll(os.Stdin)
				if err != nil {
					errorExit(err)
				}
				stdinBytes = b
				_, _ = buf.Write(b)
			} else {
				f, err := os.Open(progFile)
				if err != nil {
					errorExit(err)
				}
				_, err = buf.ReadFrom(f)
				if err != nil {
					_ = f.Close()
					errorExit(err)
				}
				_ = f.Close()
			}
			// Append newline to file in case it doesn't end with one
			_ = buf.WriteByte('\n')
		}
		src = buf.Bytes()
	} else {
		if len(args) < 1 {
			errorExitf(shortUsage)
		}
		src = []byte(args[0])
		args = args[1:]
	}

	// Parse source code and setup interpreter
	parserConfig := &parser.ParserConfig{
		DebugTypes:  debugTypes,
		DebugWriter: os.Stderr,
	}
	prog, err := parser.ParseProgram(src, parserConfig)
	if err != nil {
		if err, ok := err.(*parser.ParseError); ok {
			name, line := errorFileLine(progFiles, stdinBytes, err.Position.Line)
			fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n",
				name, line, err.Position.Column, err.Message)
			showSourceLine(src, err.Position)
			os.Exit(1)
		}
		errorExitf("%s", err)
	}
	if debug {
		fmt.Fprintln(os.Stderr, prog)
	}
	config := &interp.Config{
		Argv0: filepath.Base(os.Args[0]),
		Args:  expandWildcardsOnWindows(args),
		Bytes: useBytes,
		Vars:  []string{"FS", fieldSep},
	}
	for _, v := range vars {
		parts := strings.SplitN(v, "=", 2)
		if len(parts) != 2 {
			errorExitf("-v flag must be in format name=value")
		}
		config.Vars = append(config.Vars, parts[0], parts[1])
	}

	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			errorExitf("could not create CPU profile: %v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			errorExitf("could not start CPU profile: %v", err)
		}
	}

	// Run the program!
	status, err := interp.ExecProgram(prog, config)
	if err != nil {
		errorExit(err)
	}

	if cpuprofile != "" {
		pprof.StopCPUProfile()
	}
	if memprofile != "" {
		f, err := os.Create(memprofile)
		if err != nil {
			errorExitf("could not create memory profile: %v", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			errorExitf("could not write memory profile: %v", err)
		}
		_ = f.Close()
	}

	os.Exit(status)
}

// Show source line and position of error, for example:
//
// BEGIN { x*; }
//           ^
func showSourceLine(src []byte, pos lexer.Position) {
	lines := bytes.Split(src, []byte{'\n'})
	srcLine := string(lines[pos.Line-1])
	numTabs := strings.Count(srcLine[:pos.Column-1], "\t")
	runeColumn := utf8.RuneCountInString(srcLine[:pos.Column-1])
	fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", "    ", -1))
	fmt.Fprintln(os.Stderr, strings.Repeat(" ", runeColumn)+strings.Repeat("   ", numTabs)+"^")
}

// Determine which filename and line number to display for the overall
// error line number.
func errorFileLine(progFiles []string, stdinBytes []byte, errorLine int) (string, int) {
	if len(progFiles) == 0 {
		return "<cmdline>", errorLine
	}
	startLine := 1
	for _, progFile := range progFiles {
		var content []byte
		if progFile == "-" {
			progFile = "<stdin>"
			content = stdinBytes
		} else {
			b, err := ioutil.ReadFile(progFile)
			if err != nil {
				return "<unknown>", errorLine
			}
			content = b
		}
		content = append(content, '\n')

		numLines := bytes.Count(content, []byte{'\n'})
		if errorLine >= startLine && errorLine < startLine+numLines {
			return progFile, errorLine - startLine + 1
		}
		startLine += numLines
	}
	return "<unknown>", errorLine
}

func errorExit(err error) {
	pathErr, ok := err.(*os.PathError)
	if ok && os.IsNotExist(err) {
		errorExitf("file %q not found", pathErr.Path)
	}
	errorExitf("%s", err)
}

func errorExitf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

func expandWildcardsOnWindows(args []string) []string {
	if runtime.GOOS != "windows" {
		return args
	}
	return expandWildcards(args)
}

// Originally from https://github.com/mattn/getwild (compatible LICENSE).
func expandWildcards(args []string) []string {
	result := make([]string, 0, len(args))
	for _, arg := range args {
		matches, err := filepath.Glob(arg)
		if err == nil && len(matches) > 0 {
			result = append(result, matches...)
		} else {
			result = append(result, arg)
		}
	}
	return result
}
<commit_msg>Bump GoAWK version for release<commit_after>// Package goawk is an implementation of AWK written in Go.
//
// You can use the command-line "goawk" command or run AWK from your
// Go programs using the "interp" package. The command-line program
// has the same interface as regular awk:
//
//     goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
//
// The -F flag specifies the field separator (the default is to split
// on whitespace). The -v flag allows you to set a variable to a
// given value (multiple -v flags allowed). The -f flag allows you to
// read AWK source from a file instead of the 'prog' command-line
// argument. The rest of the arguments are input filenames (default
// is to read from stdin).
//
// A simple example (prints the sum of the numbers in the file's
// second column):
//
//     $ echo 'foo 12
//     > bar 34
//     > baz 56' >file.txt
//     $ goawk '{ sum += $2 } END { print sum }' file.txt
//     102
//
// To use GoAWK in your Go programs, see README.md or the "interp"
// docs.
//
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"unicode/utf8"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/lexer"
	"github.com/benhoyt/goawk/parser"
)

const (
	version    = "v1.13.0"
	copyright  = "GoAWK " + version + " - Copyright (c) 2021 Ben Hoyt"
	shortUsage = "usage: goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]"
	longUsage  = `Standard AWK arguments:
  -F separator
        field separator (default " ")
  -v assignment
        name=value variable assignment (multiple allowed)
  -f progfile
        load AWK source from progfile (multiple allowed)

Additional GoAWK arguments:
  -b    use byte indexes for index(), length(), match(), and substr()
  -cpuprofile file
        write CPU profile to file
  -d    debug mode (print parsed AST to stderr)
  -dt   show variable types debug info
  -h    show this usage message
  -version
        show GoAWK version and exit
`
)

func main() {
	// Parse command line arguments manually rather than using the
	// "flag" package, so we can support flags with no space between
	// flag and argument, like '-F:' (allowed by POSIX)
	var progFiles []string
	var vars []string
	fieldSep := " "
	cpuprofile := ""
	debug := false
	debugTypes := false
	memprofile := ""
	useBytes := false

	var i int
	for i = 1; i < len(os.Args); i++ {
		// Stop on explicit end of args or first arg not prefixed with "-"
		arg := os.Args[i]
		if arg == "--" {
			i++
			break
		}
		if arg == "-" || !strings.HasPrefix(arg, "-") {
			break
		}

		switch arg {
		case "-F":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -F")
			}
			i++
			fieldSep = os.Args[i]
		case "-f":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -f")
			}
			i++
			progFiles = append(progFiles, os.Args[i])
		case "-v":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -v")
			}
			i++
			vars = append(vars, os.Args[i])
		case "-b":
			useBytes = true
		case "-cpuprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -cpuprofile")
			}
			i++
			cpuprofile = os.Args[i]
		case "-d":
			debug = true
		case "-dt":
			debugTypes = true
		case "-h", "--help":
			fmt.Printf("%s\n\n%s\n\n%s", copyright, shortUsage, longUsage)
			os.Exit(0)
		case "-memprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -memprofile")
			}
			i++
			memprofile = os.Args[i]
		case "-version", "--version":
			fmt.Println(version)
			os.Exit(0)
		default:
			switch {
			case strings.HasPrefix(arg, "-F"):
				fieldSep = arg[2:]
			case strings.HasPrefix(arg, "-f"):
				progFiles = append(progFiles, arg[2:])
			case strings.HasPrefix(arg, "-v"):
				vars = append(vars, arg[2:])
			case strings.HasPrefix(arg, "-cpuprofile="):
				cpuprofile = arg[12:]
			case strings.HasPrefix(arg, "-memprofile="):
				memprofile = arg[12:]
			default:
				errorExitf("flag provided but not defined: %s", arg)
			}
		}
	}

	// Any remaining args are program and input files
	args := os.Args[i:]

	var src []byte
	var stdinBytes []byte // used if there's a parse error
	if len(progFiles) > 0 {
		// Read source: the concatenation of all source files specified
		buf := &bytes.Buffer{}
		progFiles = expandWildcardsOnWindows(progFiles)
		for _, progFile := range progFiles {
			if progFile == "-" {
				b, err := ioutil.ReadAll(os.Stdin)
				if err != nil {
					errorExit(err)
				}
				stdinBytes = b
				_, _ = buf.Write(b)
			} else {
				f, err := os.Open(progFile)
				if err != nil {
					errorExit(err)
				}
				_, err = buf.ReadFrom(f)
				if err != nil {
					_ = f.Close()
					errorExit(err)
				}
				_ = f.Close()
			}
			// Append newline to file in case it doesn't end with one
			_ = buf.WriteByte('\n')
		}
		src = buf.Bytes()
	} else {
		if len(args) < 1 {
			errorExitf(shortUsage)
		}
		src = []byte(args[0])
		args = args[1:]
	}

	// Parse source code and setup interpreter
	parserConfig := &parser.ParserConfig{
		DebugTypes:  debugTypes,
		DebugWriter: os.Stderr,
	}
	prog, err := parser.ParseProgram(src, parserConfig)
	if err != nil {
		if err, ok := err.(*parser.ParseError); ok {
			name, line := errorFileLine(progFiles, stdinBytes, err.Position.Line)
			fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n",
				name, line, err.Position.Column, err.Message)
			showSourceLine(src, err.Position)
			os.Exit(1)
		}
		errorExitf("%s", err)
	}
	if debug {
		fmt.Fprintln(os.Stderr, prog)
	}
	config := &interp.Config{
		Argv0: filepath.Base(os.Args[0]),
		Args:  expandWildcardsOnWindows(args),
		Bytes: useBytes,
		Vars:  []string{"FS", fieldSep},
	}
	for _, v := range vars {
		parts := strings.SplitN(v, "=", 2)
		if len(parts) != 2 {
			errorExitf("-v flag must be in format name=value")
		}
		config.Vars = append(config.Vars, parts[0], parts[1])
	}

	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			errorExitf("could not create CPU profile: %v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			errorExitf("could not start CPU profile: %v", err)
		}
	}

	// Run the program!
	status, err := interp.ExecProgram(prog, config)
	if err != nil {
		errorExit(err)
	}

	if cpuprofile != "" {
		pprof.StopCPUProfile()
	}
	if memprofile != "" {
		f, err := os.Create(memprofile)
		if err != nil {
			errorExitf("could not create memory profile: %v", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			errorExitf("could not write memory profile: %v", err)
		}
		_ = f.Close()
	}

	os.Exit(status)
}

// Show source line and position of error, for example:
//
// BEGIN { x*; }
//           ^
func showSourceLine(src []byte, pos lexer.Position) {
	lines := bytes.Split(src, []byte{'\n'})
	srcLine := string(lines[pos.Line-1])
	numTabs := strings.Count(srcLine[:pos.Column-1], "\t")
	runeColumn := utf8.RuneCountInString(srcLine[:pos.Column-1])
	fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", "    ", -1))
	fmt.Fprintln(os.Stderr, strings.Repeat(" ", runeColumn)+strings.Repeat("   ", numTabs)+"^")
}

// Determine which filename and line number to display for the overall
// error line number.
func errorFileLine(progFiles []string, stdinBytes []byte, errorLine int) (string, int) {
	if len(progFiles) == 0 {
		return "<cmdline>", errorLine
	}
	startLine := 1
	for _, progFile := range progFiles {
		var content []byte
		if progFile == "-" {
			progFile = "<stdin>"
			content = stdinBytes
		} else {
			b, err := ioutil.ReadFile(progFile)
			if err != nil {
				return "<unknown>", errorLine
			}
			content = b
		}
		content = append(content, '\n')

		numLines := bytes.Count(content, []byte{'\n'})
		if errorLine >= startLine && errorLine < startLine+numLines {
			return progFile, errorLine - startLine + 1
		}
		startLine += numLines
	}
	return "<unknown>", errorLine
}

func errorExit(err error) {
	pathErr, ok := err.(*os.PathError)
	if ok && os.IsNotExist(err) {
		errorExitf("file %q not found", pathErr.Path)
	}
	errorExitf("%s", err)
}

func errorExitf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

func expandWildcardsOnWindows(args []string) []string {
	if runtime.GOOS != "windows" {
		return args
	}
	return expandWildcards(args)
}

// Originally from https://github.com/mattn/getwild (compatible LICENSE).
func expandWildcards(args []string) []string {
	result := make([]string, 0, len(args))
	for _, arg := range args {
		matches, err := filepath.Glob(arg)
		if err == nil && len(matches) > 0 {
			result = append(result, matches...)
		} else {
			result = append(result, arg)
		}
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os/exec"
	"sync"
	"time"

	"github.com/gobs/args"
	"github.com/gobs/httpclient"
	"golang.org/x/net/websocket"
)

func decode(resp *httpclient.HttpResponse, v interface{}) error {
	err := json.NewDecoder(resp.Body).Decode(v)
	resp.Close()

	return err
}

//
// DevTools version info
//
type Version struct {
	Browser         string `json:"Browser"`
	ProtocolVersion string `json:"Protocol-Version"`
	UserAgent       string `json:"User-Agent"`
	V8Version       string `json:"V8-Version"`
	WebKitVersion   string `json:"WebKit-Version"`
}

func (v *Version) String() string {
	return fmt.Sprintf("Browser: %v\n"+
		"Protocol Version: %v\n"+
		"User Agent: %v\n"+
		"V8 Version: %v\n"+
		"WebKit Version: %v\n",
		v.Browser,
		v.ProtocolVersion,
		v.UserAgent,
		v.V8Version,
		v.WebKitVersion)
}

//
// Chrome open tab or page
//
type Tab struct {
	Id          string `json:"id"`
	Type        string `json:"type"`
	Description string `json:"description"`
	Title       string `json:"title"`
	Url         string `json:"url"`
	WsUrl       string `json:"webSocketDebuggerUrl"`
	DevUrl      string `json:"devtoolsFrontendUrl"`
}

func (t Tab) String() string {
	return fmt.Sprintf("Id: %v\n"+
		"Type: %v\n"+
		"Description: %v\n"+
		"Title: %v\n"+
		"Url: %v\n"+
		"WebSocket Url: %v\n"+
		"Devtools Url: %v\n",
		t.Id,
		t.Type,
		t.Description,
		t.Title,
		t.Url,
		t.WsUrl,
		t.DevUrl)
}

//
// RemoteDebugger
//
type RemoteDebugger struct {
	http   *httpclient.HttpClient
	ws     *websocket.Conn
	reqid  int
	closed bool

	responses map[int]chan json.RawMessage
	r_lock    sync.Mutex
}

//
// Connect to the remote debugger and return `RemoteDebugger` object
//
func Connect(port string) (*RemoteDebugger, error) {
	remote := &RemoteDebugger{
		http:      httpclient.NewHttpClient("http://" + port),
		responses: map[int]chan json.RawMessage{},
	}

	// check http connection
	tabs, err := remote.TabList("")
	if err != nil {
		return nil, err
	}

	var wsUrl string

	for _, tab := range tabs {
		if tab.WsUrl != "" {
			wsUrl = tab.WsUrl
			break
		}
	}

	// check websocket connection
	if remote.ws, err = websocket.Dial(wsUrl, "", "http://localhost"); err != nil {
		return nil, err
	}

	go remote.readMessages()
	return remote, nil
}

func (remote *RemoteDebugger) Close() error {
	remote.closed = true
	return remote.ws.Close()
}

type wsParams map[string]interface{}

type wsMessage struct {
	Id     int             `json:"id"`
	Result json.RawMessage `json:"result"`

	Method string          `json:"Method"`
	Params json.RawMessage `json:"Params"`
}

func (remote *RemoteDebugger) sendRequest(method string, params wsParams) (json.RawMessage, error) {
	remote.r_lock.Lock()
	reqid := remote.reqid
	remote.responses[reqid] = make(chan json.RawMessage, 1)
	remote.reqid++
	remote.r_lock.Unlock()

	command := map[string]interface{}{
		"id":     reqid,
		"method": method,
		"params": params,
	}

	bytes, err := json.Marshal(command)
	if err != nil {
		return nil, err
	}

	log.Println("send", string(bytes))

	_, err = remote.ws.Write(bytes)
	if err != nil {
		return nil, err
	}

	res := <-remote.responses[reqid]
	remote.r_lock.Lock()
	remote.responses[reqid] = nil
	remote.r_lock.Unlock()

	return res, nil
}

func (remote *RemoteDebugger) readMessages() {
	buf := make([]byte, 4096)
	var bytes []byte

	for !remote.closed {
		if n, err := remote.ws.Read(buf); err != nil {
			log.Println("read error", err)
			if err == io.EOF {
				break
			}
		} else {
			if n > 0 {
				bytes = append(bytes, buf[:n]...)

				// hack to check end of message
				if bytes[0] == '{' && bytes[len(bytes)-1] != '}' {
					continue
				}
			}

			var message wsMessage

			//
			// unmarshall message
			//
			if err := json.Unmarshal(bytes, &message); err != nil {
				log.Println("error unmarshaling", string(bytes), len(bytes), err)
			} else if message.Method != "" {
				//
				// should be an event notification
				//
				log.Println("EVENT", message.Method, string(message.Params))
			} else {
				//
				// should be a method reply
				//
				remote.r_lock.Lock()
				ch := remote.responses[message.Id]
				remote.r_lock.Unlock()

				if ch != nil {
					ch <- message.Result
				}
			}

			bytes = nil
		}
	}
}

//
// Return various version info (protocol, browser, etc.)
//
func (remote *RemoteDebugger) Version() (*Version, error) {
	resp, err := remote.http.Get("/json/version", nil, nil)
	if err != nil {
		return nil, err
	}

	var version Version

	if err = decode(resp, &version); err != nil {
		return nil, err
	}

	return &version, nil
}

//
// Return the list of open tabs/page
//
// If filter is not empty only tabs of the specified type are returned (i.e. "page")
//
func (remote *RemoteDebugger) TabList(filter string) ([]*Tab, error) {
	resp, err := remote.http.Get("/json/list", nil, nil)
	if err != nil {
		return nil, err
	}

	var tabs []*Tab

	if err = decode(resp, &tabs); err != nil {
		return nil, err
	}

	if filter == "" {
		return tabs, nil
	}

	var filtered []*Tab

	for _, t := range tabs {
		if t.Type == filter {
			filtered = append(filtered, t)
		}
	}

	return filtered, nil
}

//
// Activate specified tab
//
func (remote *RemoteDebugger) ActivateTab(tab *Tab) error {
	resp, err := remote.http.Get("/json/activate/"+tab.Id, nil, nil)
	resp.Close()
	return err
}

//
// Close specified tab
//
func (remote *RemoteDebugger) CloseTab(tab *Tab) error {
	resp, err := remote.http.Get("/json/close/"+tab.Id, nil, nil)
	resp.Close()
	return err
}

//
// Create a new tab
//
func (remote *RemoteDebugger) NewTab(url string) (*Tab, error) {
	params := map[string]interface{}{}
	if url != "" {
		params["url"] = url
	}
	resp, err := remote.http.Get("/json/new", params, nil)
	if err != nil {
		return nil, err
	}

	var tab Tab
	if err = decode(resp, &tab); err != nil {
		return nil, err
	}

	return &tab, nil
}

func (remote *RemoteDebugger) getDomains() error {
	res, err := remote.sendRequest("Schema.getDomains", nil)
	if res != nil {
		log.Println(" ", string(res))
	}

	return err
}

func (remote *RemoteDebugger) Navigate(url string) error {
	res, err := remote.sendRequest("Page.navigate", wsParams{
		"url": url,
	})

	if res != nil {
		log.Println(" ", string(res))
	}

	return err
}

func (remote *RemoteDebugger) events(domain string, enable bool) error {
	method := domain

	if enable {
		method += ".enable"
	} else {
		method += ".disable"
	}

	res, err := remote.sendRequest(method, nil)
	if res != nil {
		log.Println(" ", string(res))
	}

	return err
}

func (remote *RemoteDebugger) DOMEvents(enable bool) error {
	return remote.events("DOM", enable)
}

func (remote *RemoteDebugger) PageEvents(enable bool) error {
	return remote.events("Page", enable)
}

func (remote *RemoteDebugger) NetworkEvents(enable bool) error {
	return remote.events("Network", enable)
}

func (remote *RemoteDebugger) RuntimeEvents(enable bool) error {
	return remote.events("Runtime", enable)
}

func runCommand(commandString string) error {
	parts := args.GetArgs(commandString)
	cmd := exec.Command(parts[0], parts[1:]...)
	err := cmd.Start()
	if err == nil {
		time.Sleep(time.Second) // give the app some time to start
	} else {
		log.Println("command start", err)
	}

	return err
}

func main() {
	cmd := flag.String("cmd", "open /Applications/Google\\ Chrome.app --args --remote-debugging-port=9222 --disable-extensions --headless about:blank", "command to execute to start the browser")
	port := flag.String("port", "localhost:9222", "Chrome remote debugger port")
	filter := flag.String("filter", "page", "filter tab list")
	page := flag.String("page", "http://httpbin.org", "page to load")
	flag.Parse()

	if *cmd != "" {
		runCommand(*cmd)
	}

	remote, err := Connect(*port)
	if err != nil {
		log.Fatal("connect", err)
	}

	defer remote.Close()

	fmt.Println()
	fmt.Println("Version:")
	fmt.Println(remote.Version())

	fmt.Println()
	tabs, err := remote.TabList(*filter)
	if err != nil {
		log.Fatal("cannot get list of tabs: ", err)
	}

	fmt.Println(tabs)

	fmt.Println()
	fmt.Println(remote.getDomains())

	remote.PageEvents(true)
	remote.DOMEvents(true)
	remote.RuntimeEvents(true)
	remote.NetworkEvents(true)

	l := len(tabs)
	if l > 0 {
		remote.ActivateTab(tabs[l-1])

		fmt.Println()
		fmt.Println(remote.Navigate(*page))
	}

	time.Sleep(60 * time.Second)
}
<commit_msg>Added user callbacks<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os/exec"
	"sync"
	"time"

	"github.com/gobs/args"
	"github.com/gobs/httpclient"
	"golang.org/x/net/websocket"
)

func decode(resp *httpclient.HttpResponse, v interface{}) error {
	err := json.NewDecoder(resp.Body).Decode(v)
	resp.Close()

	return err
}

//
// DevTools version info
//
type Version struct {
	Browser         string `json:"Browser"`
	ProtocolVersion string `json:"Protocol-Version"`
	UserAgent       string `json:"User-Agent"`
	V8Version       string `json:"V8-Version"`
	WebKitVersion   string `json:"WebKit-Version"`
}

func (v *Version) String() string {
	return fmt.Sprintf("Browser: %v\n"+
		"Protocol Version: %v\n"+
		"User Agent: %v\n"+
		"V8 Version: %v\n"+
		"WebKit Version: %v\n",
		v.Browser,
		v.ProtocolVersion,
		v.UserAgent,
		v.V8Version,
		v.WebKitVersion)
}

//
// Chrome open tab or page
//
type Tab struct {
	Id          string `json:"id"`
	Type        string `json:"type"`
	Description string `json:"description"`
	Title       string `json:"title"`
	Url         string `json:"url"`
	WsUrl       string `json:"webSocketDebuggerUrl"`
	DevUrl      string `json:"devtoolsFrontendUrl"`
}

func (t Tab) String() string {
	return fmt.Sprintf("Id: %v\n"+
		"Type: %v\n"+
		"Description: %v\n"+
		"Title: %v\n"+
		"Url: %v\n"+
		"WebSocket Url: %v\n"+
		"Devtools Url: %v\n",
		t.Id,
		t.Type,
		t.Description,
		t.Title,
		t.Url,
		t.WsUrl,
		t.DevUrl)
}

//
// RemoteDebugger
//
type RemoteDebugger struct {
	http    *httpclient.HttpClient
	ws      *websocket.Conn
	reqid   int
	closed  bool
	verbose bool

	sync.Mutex

	responses map[int]chan json.RawMessage
	callbacks map[string]EventCallback
}

type Params map[string]interface{}
type EventCallback func(params Params)

//
// Connect to the remote debugger and return `RemoteDebugger` object
//
func Connect(port string) (*RemoteDebugger, error) {
	remote := &RemoteDebugger{
		http:      httpclient.NewHttpClient("http://" + port),
		responses: map[int]chan json.RawMessage{},
		callbacks: map[string]EventCallback{},
	}

	// check http connection
	tabs, err := remote.TabList("")
	if err != nil {
		return nil, err
	}

	var wsUrl string

	for _, tab := range tabs {
		if tab.WsUrl != "" {
			wsUrl = tab.WsUrl
			break
		}
	}

	// check websocket connection
	if remote.ws, err = websocket.Dial(wsUrl, "", "http://localhost"); err != nil {
		return nil, err
	}

	go remote.readMessages()
	return remote, nil
}

func (remote *RemoteDebugger) Close() error {
	remote.closed = true
	return remote.ws.Close()
}

type wsMessage struct {
	Id     int             `json:"id"`
	Result json.RawMessage `json:"result"`

	Method string          `json:"Method"`
	Params json.RawMessage `json:"Params"`
}

func (remote *RemoteDebugger) sendRequest(method string, params Params) (json.RawMessage, error) {
	remote.Lock()
	reqid := remote.reqid
	remote.responses[reqid] = make(chan json.RawMessage, 1)
	remote.reqid++
	remote.Unlock()

	command := map[string]interface{}{
		"id":     reqid,
		"method": method,
		"params": params,
	}

	bytes, err := json.Marshal(command)
	if err != nil {
		return nil, err
	}

	if remote.verbose {
		log.Println("send", string(bytes))
	}

	_, err = remote.ws.Write(bytes)
	if err != nil {
		return nil, err
	}

	res := <-remote.responses[reqid]
	remote.Lock()
	remote.responses[reqid] = nil
	remote.Unlock()

	return res, nil
}

func (remote *RemoteDebugger) readMessages() {
	buf := make([]byte, 4096)
	var bytes []byte

	for !remote.closed {
		if n, err := remote.ws.Read(buf); err != nil {
			log.Println("read error", err)
			if err == io.EOF {
				break
			}
		} else {
			if n > 0 {
				bytes = append(bytes, buf[:n]...)

				// hack to check end of message
				if bytes[0] == '{' && bytes[len(bytes)-1] != '}' {
					continue
				}
			}

			var message wsMessage

			//
			// unmarshall message
			//
			if err := json.Unmarshal(bytes, &message); err != nil {
				log.Println("error unmarshaling", string(bytes), len(bytes), err)
			} else if message.Method != "" {
				//
				// should be an event notification
				//
				remote.Lock()
				cb := remote.callbacks[message.Method]
				remote.Unlock()

				if cb != nil {
					var params Params
					if err := json.Unmarshal(message.Params, &params); err != nil {
						log.Println("error unmarshaling", string(message.Params), len(message.Params), err)
					} else {
						cb(params)
					}
				} else if remote.verbose {
					log.Println("EVENT", message.Method, string(message.Params))
				}
			} else {
				//
				// should be a method reply
				//
				remote.Lock()
				ch := remote.responses[message.Id]
				remote.Unlock()

				if ch != nil {
					ch <- message.Result
				}
			}

			bytes = nil
		}
	}
}

//
// Return various version info (protocol, browser, etc.)
//
func (remote *RemoteDebugger) Version() (*Version, error) {
	resp, err := remote.http.Get("/json/version", nil, nil)
	if err != nil {
		return nil, err
	}

	var version Version

	if err = decode(resp, &version); err != nil {
		return nil, err
	}

	return &version, nil
}

//
// Return the list of open tabs/page
//
// If filter is not empty only tabs of the specified type are returned (i.e. "page")
//
func (remote *RemoteDebugger) TabList(filter string) ([]*Tab, error) {
	resp, err := remote.http.Get("/json/list", nil, nil)
	if err != nil {
		return nil, err
	}

	var tabs []*Tab

	if err = decode(resp, &tabs); err != nil {
		return nil, err
	}

	if filter == "" {
		return tabs, nil
	}

	var filtered []*Tab

	for _, t := range tabs {
		if t.Type == filter {
			filtered = append(filtered, t)
		}
	}

	return filtered, nil
}

//
// Activate specified tab
//
func (remote *RemoteDebugger) ActivateTab(tab *Tab) error {
	resp, err := remote.http.Get("/json/activate/"+tab.Id, nil, nil)
	resp.Close()
	return err
}

//
// Close specified tab
//
func (remote *RemoteDebugger) CloseTab(tab *Tab) error {
	resp, err := remote.http.Get("/json/close/"+tab.Id, nil, nil)
	resp.Close()
	return err
}

//
// Create a new tab
//
func (remote *RemoteDebugger) NewTab(url string) (*Tab, error) {
	params := Params{}
	if url != "" {
		params["url"] = url
	}
	resp, err := remote.http.Get("/json/new", params, nil)
	if err != nil {
		return nil, err
	}

	var tab Tab
	if err = decode(resp, &tab); err != nil {
		return nil, err
	}

	return &tab, nil
}

func (remote *RemoteDebugger) getDomains() error {
	res, err := remote.sendRequest("Schema.getDomains", nil)
	if res != nil {
		log.Println(" ", string(res))
	}

	return err
}

func (remote *RemoteDebugger) Navigate(url string) error {
	res, err := remote.sendRequest("Page.navigate", Params{
		"url": url,
	})

	if res != nil {
		log.Println(" ", string(res))
	}

	return err
}

func (remote *RemoteDebugger) CallbackEvent(method string, cb EventCallback) {
	remote.Lock()
	remote.callbacks[method] = cb
	remote.Unlock()
}

func (remote *RemoteDebugger) events(domain string, enable bool) error {
	method := domain

	if enable {
		method += ".enable"
	} else {
		method += ".disable"
	}

	res, err := remote.sendRequest(method, nil)
	if res != nil {
		log.Println(" ", string(res))
	}

	return err
}

func (remote *RemoteDebugger) DOMEvents(enable bool) error {
	return remote.events("DOM", enable)
}

func (remote *RemoteDebugger) PageEvents(enable bool) error {
	return remote.events("Page", enable)
}

func (remote *RemoteDebugger) NetworkEvents(enable bool) error {
	return remote.events("Network", enable)
}

func (remote *RemoteDebugger) RuntimeEvents(enable bool) error {
	return remote.events("Runtime", enable)
}

func runCommand(commandString string) error {
	parts := args.GetArgs(commandString)
	cmd := exec.Command(parts[0], parts[1:]...)
	err := cmd.Start()
	if err == nil {
		time.Sleep(time.Second) // give the app some time to start
	} else {
		log.Println("command start", err)
	}

	return err
}

func main() {
	cmd := flag.String("cmd", "open /Applications/Google\\ Chrome.app --args --remote-debugging-port=9222 --disable-extensions --headless about:blank", "command to execute to start the browser")
	port := flag.String("port", "localhost:9222", "Chrome remote debugger port")
	filter := flag.String("filter", "page", "filter tab list")
	page := flag.String("page", "http://httpbin.org", "page to load")
	flag.Parse()

	if *cmd != "" {
		runCommand(*cmd)
	}

	remote, err := Connect(*port)
	if err != nil {
		log.Fatal("connect", err)
	}

	defer remote.Close()

	fmt.Println()
	fmt.Println("Version:")
	fmt.Println(remote.Version())

	fmt.Println()
	tabs, err := remote.TabList(*filter)
	if err != nil {
		log.Fatal("cannot get list of tabs: ", err)
	}

	fmt.Println(tabs)

	fmt.Println()
	fmt.Println(remote.getDomains())

	remote.PageEvents(true)
	remote.DOMEvents(true)
	remote.RuntimeEvents(true)
	remote.NetworkEvents(true)

	remote.CallbackEvent("Network.responseReceived", func(params Params) {
		log.Println("responseReceived",
			params["response"].(map[string]interface{})["url"])
	})

	remote.CallbackEvent("Network.requestWillBeSent", func(params Params) {
		log.Println("requestWillBeSent",
			params["documentURL"],
			params["request"].(map[string]interface{})["url"])
	})

	l := len(tabs)
	if l > 0 {
		remote.ActivateTab(tabs[l-1])

		fmt.Println()
		fmt.Println(remote.Navigate(*page))
	}

	time.Sleep(60 * time.Second)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
)

type toc struct {
	header []int
	title  []string
}

func writeHeader() string {
	header := "**Table of Contents**"
	return header
}

func removeSpecialCharacters(str string) string {
	r, _ := regexp.Compile("([a-zA-Z]+)")
	cleanedStr := r.FindAllString(str, -1)
	return strings.Join(cleanedStr, " ")
}

func main() {
	var startsWith bool
	var line string
	// var headerCount string
	// var content string

	file, err := os.Open("example.md")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
		line = scanner.Text()
		startsWith = strings.HasPrefix(line, "#")
		if startsWith == true {
			fmt.Println("TRUE")
			headerCount := strings.Count(line, "#")
			fmt.Println(strings.Count(line, "#"))
			content := line[headerCount:]
			fmt.Println(content)
			cleanedContent := removeSpecialCharacters(content)
			fmt.Println(cleanedContent)
		}
		// toc := toc{header: headerCount, title: content}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

}
<commit_msg>append elements to struct<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
)

type Toc struct {
	header []int
	title  []string
}

func writeHeader() string {
	header := "**Table of Contents**"
	return header
}

func removeSpecialCharacters(str string) string {
	r, _ := regexp.Compile("([a-zA-Z]+)")
	cleanedStr := r.FindAllString(str, -1)
	return strings.Join(cleanedStr, " ")
}

func main() {
	var startsWith bool
	var line string
	var doctoc = new(Toc)

	file, err := os.Open("example.md")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
		line = scanner.Text()
		startsWith = strings.HasPrefix(line, "#")
		if startsWith == true {
			fmt.Println("TRUE")
			headerCount := strings.Count(line, "#")
			fmt.Println(strings.Count(line, "#"))
			content := line[headerCount:]
			fmt.Println(content)
			cleanedContent := removeSpecialCharacters(content)
			fmt.Println(cleanedContent)
			doctoc.header = append(doctoc.header, headerCount)
			doctoc.title = append(doctoc.title, cleanedContent)
		}
	}

	fmt.Println(doctoc)

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha512"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"strings"
)

// during alpha, we use basic auth for alpha access
// basic auth is chosen because it does not interfere with any other techniques used by this project
// the alphaUsers map holds all alpha users and their sha512 hashed passwords.
// the password is to be salted with the alphaSalt (e.g. <password>+alphaSalt)
// feel free to add yourself or other project members to the map
var alphaSalt = "che9ohlu0xie9yaW6aeg"
var alphaUsers = map[string]string{
	"GeertJohan": "8e764667137fc73b16a3a3cd43a6a9314c8c2214215306f563068bd91b7f5de06da19815b10e3b6719c7f9cd4499b26e13738fadba0b244c4055f1d7af0100b8",
	"Argure":     "cc5be974f66c51babfc1e800847303adf9cb092342dce874483347ce04e9a122a409a9876080f56e3322a2176b490fc72c5e7b87e9fde45b718d09d26d9006dc",
	"nihlaeth":   "efa115c51855f495b31beb31904acc67b223e0d0466cb4f2f42ad7d8b07c43ef25e7ba66ed6b48f97c40ca625d163dede7f2700507602f583874a3da7eabb665",
	"frank88":    "fc647f062eabc707c9bb266ff2a15efab2006e2dee267e2d753da2c5f7e4ce26bc70da916402323012769f5f716accb03420c7952acdd961cf81bedf4365a411",
	"renee":      "1eb07335daaba6d7b86ec7de45e32532eb80e6edfdad5e6a759e57db86d68007c92655873e5bd4bc70fa1b48a16c7173af500c39617e3c027f280dd9ceae1e47",
	"younes":     "996a9b02da5088409c96d609fe5db9e5129efbb335beda06c35e4980814b59a98d4241a4fddf45a2eb93465f520d5ba2cdfe54ed75955bcb43775a88a46db2f2",
	"guido":      "cf283559a5731f10206c8e395bc6d38d1ecf2c5f6e0cc748af9e0793a190a1b2cb1f27baf39b3707961d11cbc9f881297bc307de5f1c493806d1af1e5c4afe88",
}

func alphaCheckBasicAuth(r *http.Request) bool {
	// retrieve auth header
	authHeader := r.Header.Get("Authorization")
	if len(authHeader) < 7 {
		// no or invalid auth given
		return false
	}
	if authHeader[:6] != "Basic " {
		// invalid auth type
		return false
	}

	// decode auth data
	authData, err := base64.StdEncoding.DecodeString(authHeader[6:])
	if err != nil {
		return false
	}
	authDataSlice := strings.SplitN(string(authData), ":", 2)
	if len(authDataSlice) != 2 {
		return false
	}
	givenUsername := authDataSlice[0]
	givenPassword := authDataSlice[1]

	// retrieved hashed password from alphaUsers map
	correctPasswordHashed, userExists := alphaUsers[givenUsername]
	if !userExists {
		// user does not exist
		return false
	}

	// hash retrieved password, format as hex string
	passwordHasher := sha512.New()
	io.WriteString(passwordHasher, givenPassword)
	io.WriteString(passwordHasher, alphaSalt)
	givenPasswordHashed := fmt.Sprintf("%x", passwordHasher.Sum(nil))
	// password matches?
	if givenPasswordHashed == correctPasswordHashed {
		// yay! correct auth!
		return true
	}

	return false
}
<commit_msg>Added alpha account.<commit_after>package main

import (
	"crypto/sha512"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"strings"
)

// during alpha, we use basic auth for alpha access
// basic auth is chosen because it does not interfere with any other techniques used by this project
// the alphaUsers map holds all alpha users and their sha512 hashed passwords.
// the password is to be salted with the alphaSalt (e.g. <password>+alphaSalt)
// feel free to add yourself or other project members to the map
var alphaSalt = "che9ohlu0xie9yaW6aeg"
var alphaUsers = map[string]string{
	"GeertJohan": "8e764667137fc73b16a3a3cd43a6a9314c8c2214215306f563068bd91b7f5de06da19815b10e3b6719c7f9cd4499b26e13738fadba0b244c4055f1d7af0100b8",
	"Argure":     "cc5be974f66c51babfc1e800847303adf9cb092342dce874483347ce04e9a122a409a9876080f56e3322a2176b490fc72c5e7b87e9fde45b718d09d26d9006dc",
	"nihlaeth":   "efa115c51855f495b31beb31904acc67b223e0d0466cb4f2f42ad7d8b07c43ef25e7ba66ed6b48f97c40ca625d163dede7f2700507602f583874a3da7eabb665",
	"frank88":    "fc647f062eabc707c9bb266ff2a15efab2006e2dee267e2d753da2c5f7e4ce26bc70da916402323012769f5f716accb03420c7952acdd961cf81bedf4365a411",
	"renee":      "1eb07335daaba6d7b86ec7de45e32532eb80e6edfdad5e6a759e57db86d68007c92655873e5bd4bc70fa1b48a16c7173af500c39617e3c027f280dd9ceae1e47",
	"younes":     "996a9b02da5088409c96d609fe5db9e5129efbb335beda06c35e4980814b59a98d4241a4fddf45a2eb93465f520d5ba2cdfe54ed75955bcb43775a88a46db2f2",
	"guido":      "cf283559a5731f10206c8e395bc6d38d1ecf2c5f6e0cc748af9e0793a190a1b2cb1f27baf39b3707961d11cbc9f881297bc307de5f1c493806d1af1e5c4afe88",
	"alpha":      "e15bf77af7123dbf2be7d0206ff4007f9bc3d1f29539d7b795cda4a230cd7807db55c4c38f4e98feb3d59cfbda97fe668e53d0edc8de0473d4fa33e8c76dd58c",
}

func alphaCheckBasicAuth(r *http.Request) bool {
	// retrieve auth header
	authHeader := r.Header.Get("Authorization")
	if len(authHeader) < 7 {
		// no or invalid auth given
		return false
	}
	if authHeader[:6] != "Basic " {
		// invalid auth type
		return false
	}

	// decode auth data
	authData, err := base64.StdEncoding.DecodeString(authHeader[6:])
	if err != nil {
		return false
	}
	authDataSlice := strings.SplitN(string(authData), ":", 2)
	if len(authDataSlice) != 2 {
		return false
	}
	givenUsername := authDataSlice[0]
	givenPassword := authDataSlice[1]

	// retrieved hashed password from alphaUsers map
	correctPasswordHashed, userExists := alphaUsers[givenUsername]
	if !userExists {
		// user does not exist
		return false
	}

	// hash retrieved password, format as hex string
	passwordHasher := sha512.New()
	io.WriteString(passwordHasher, givenPassword)
	io.WriteString(passwordHasher, alphaSalt)
	givenPasswordHashed := fmt.Sprintf("%x", passwordHasher.Sum(nil))
	// password matches?
	if givenPasswordHashed == correctPasswordHashed {
		// yay! correct auth!
		return true
	}

	return false
}
<|endoftext|>


<commit_before>package builders

import (
	"github.com/docker/docker/api/types/swarm"
)

// Service creates a service with default values.
// Any number of service builder functions can be passed to augment it.
// Currently, only ServiceName is implemented
func Service(builders ...func(*swarm.Service)) *swarm.Service {
	service := &swarm.Service{
		ID: "serviceID",
		Spec: swarm.ServiceSpec{
			Annotations: swarm.Annotations{
				Name: "defaultServiceName",
			},
			EndpointSpec: &swarm.EndpointSpec{},
		},
	}

	for _, builder := range builders {
		builder(service)
	}

	return service
}

// ServiceID sets the service ID
func ServiceID(ID string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.ID = ID
	}
}

// ServiceName sets the service name
func ServiceName(name string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Annotations.Name = name
	}
}

// ServiceLabels sets the service's labels
func ServiceLabels(labels map[string]string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Annotations.Labels = labels
	}
}

// GlobalService sets the service to use "global" mode
func GlobalService() func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Mode = swarm.ServiceMode{Global: &swarm.GlobalService{}}
	}
}

// ReplicatedService sets the service to use "replicated" mode with the specified number of replicas
func ReplicatedService(replicas uint64) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Mode = swarm.ServiceMode{Replicated: &swarm.ReplicatedService{Replicas: &replicas}}
		if service.ServiceStatus == nil {
			service.ServiceStatus = &swarm.ServiceStatus{}
		}
		service.ServiceStatus.DesiredTasks = replicas
	}
}

// ServiceStatus sets the services' ServiceStatus (API v1.41 and above)
func ServiceStatus(desired, running uint64) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.ServiceStatus = &swarm.ServiceStatus{
			RunningTasks: running,
			DesiredTasks: desired,
		}
	}
}

// ServiceImage sets the service's image
func ServiceImage(image string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.TaskTemplate = swarm.TaskSpec{ContainerSpec: &swarm.ContainerSpec{Image: image}}
	}
}

// ServicePort sets the service's port
func ServicePort(port swarm.PortConfig) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.EndpointSpec.Ports = append(service.Spec.EndpointSpec.Ports, port)

		assignedPort := port
		if assignedPort.PublishedPort == 0 {
			assignedPort.PublishedPort = 30000
		}
		service.Endpoint.Ports = append(service.Endpoint.Ports, assignedPort)
	}
}
<commit_msg>internal/test/builders: don't set EndpointSpec by default<commit_after>package builders

import (
	"github.com/docker/docker/api/types/swarm"
)

// Service creates a service with default values.
// Any number of service builder functions can be passed to augment it.
func Service(builders ...func(*swarm.Service)) *swarm.Service {
	service := &swarm.Service{}
	defaults := []func(*swarm.Service){ServiceID("serviceID"), ServiceName("defaultServiceName")}

	for _, opt := range append(defaults, builders...) {
		opt(service)
	}

	return service
}

// ServiceID sets the service ID
func ServiceID(ID string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.ID = ID
	}
}

// ServiceName sets the service name
func ServiceName(name string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Annotations.Name = name
	}
}

// ServiceLabels sets the service's labels
func ServiceLabels(labels map[string]string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Annotations.Labels = labels
	}
}

// GlobalService sets the service to use "global" mode
func GlobalService() func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Mode = swarm.ServiceMode{Global: &swarm.GlobalService{}}
	}
}

// ReplicatedService sets the service to use "replicated" mode with the specified number of replicas
func ReplicatedService(replicas uint64) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.Spec.Mode = swarm.ServiceMode{Replicated: &swarm.ReplicatedService{Replicas: &replicas}}
		if service.ServiceStatus == nil {
			service.ServiceStatus = &swarm.ServiceStatus{}
		}
		service.ServiceStatus.DesiredTasks = replicas
	}
}

// ServiceStatus sets the services' ServiceStatus (API v1.41 and above)
func ServiceStatus(desired, running uint64) func(*swarm.Service) {
	return func(service *swarm.Service) {
		service.ServiceStatus = &swarm.ServiceStatus{
			RunningTasks: running,
			DesiredTasks: desired,
		}
	}
}

// ServiceImage sets the service's image
func ServiceImage(image string) func(*swarm.Service) {
	return func(service *swarm.Service) {
		if service.Spec.TaskTemplate.ContainerSpec == nil {
			service.Spec.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
		}
		service.Spec.TaskTemplate.ContainerSpec.Image = image
	}
}

// ServicePort sets the service's port
func ServicePort(port swarm.PortConfig) func(*swarm.Service) {
	return func(service *swarm.Service) {
		if service.Spec.EndpointSpec == nil {
			service.Spec.EndpointSpec = &swarm.EndpointSpec{}
		}
		service.Spec.EndpointSpec.Ports = append(service.Spec.EndpointSpec.Ports, port)

		assignedPort := port
		if assignedPort.PublishedPort == 0 {
			assignedPort.PublishedPort = 30000
		}
		service.Endpoint.Ports = append(service.Endpoint.Ports, assignedPort)
	}
}
<|endoftext|>


<commit_before>package catalog

import (
	"net/http"

	"github.com/gorilla/websocket"
)

type RancherBaseClientImpl struct {
	Opts    *ClientOpts
	Schemas *Schemas
	Types   map[string]Schema
	customHeaders map[string]string
}

type RancherBaseClient interface {
	Websocket(string, map[string][]string) (*websocket.Conn, *http.Response, error)
	List(string, *ListOpts, interface{}) error
	Post(string, interface{}, interface{}) error
	GetLink(Resource, string, interface{}) error
	Create(string, interface{}, interface{}) error
	Update(string, *Resource, interface{}, interface{}) error
	ById(string, string, interface{}) error
	Delete(*Resource) error
	Reload(*Resource, interface{}) error
	Action(string, string, *Resource, interface{}, interface{}) error
	GetOpts() *ClientOpts
	GetSchemas() *Schemas
	GetTypes() map[string]Schema
	SetCustomHeaders(headers map[string]string)

	doGet(string, *ListOpts, interface{}) error
	doList(string, *ListOpts, interface{}) error
	doNext(string, interface{}) error
	doModify(string, string, interface{}, interface{}) error
	doCreate(string, interface{}, interface{}) error
	doUpdate(string, *Resource, interface{}, interface{}) error
	doById(string, string, interface{}) error
	doResourceDelete(string, *Resource) error
	doAction(string, string, *Resource, interface{}, interface{}) error
}
<commit_msg>Fix gofmt error in catalog client<commit_after>package catalog

import (
	"net/http"

	"github.com/gorilla/websocket"
)

type RancherBaseClientImpl struct {
	Opts          *ClientOpts
	Schemas       *Schemas
	Types         map[string]Schema
	customHeaders map[string]string
}

type RancherBaseClient interface {
	Websocket(string, map[string][]string) (*websocket.Conn, *http.Response, error)
	List(string, *ListOpts, interface{}) error
	Post(string, interface{}, interface{}) error
	GetLink(Resource, string, interface{}) error
	Create(string, interface{}, interface{}) error
	Update(string, *Resource, interface{}, interface{}) error
	ById(string, string, interface{}) error
	Delete(*Resource) error
	Reload(*Resource, interface{}) error
	Action(string, string, *Resource, interface{}, interface{}) error
	GetOpts() *ClientOpts
	GetSchemas() *Schemas
	GetTypes() map[string]Schema
	SetCustomHeaders(headers map[string]string)

	doGet(string, *ListOpts, interface{}) error
	doList(string, *ListOpts, interface{}) error
	doNext(string, interface{}) error
	doModify(string, string, interface{}, interface{}) error
	doCreate(string, interface{}, interface{}) error
	doUpdate(string, *Resource, interface{}, interface{}) error
	doById(string, string, interface{}) error
	doResourceDelete(string, *Resource) error
	doAction(string, string, *Resource, interface{}, interface{}) error
}
<|endoftext|>


<commit_before>package controllers

import (
	"database/sql"
	"fmt"
	"strconv"
	"time"

	"github.com/kayac/alphawing/app/models"
	"github.com/kayac/alphawing/app/routes"
	"github.com/revel/revel"
)

type LimitedTimeController struct {
	AlphaWingController
	Bundle *models.Bundle
}

func (c *LimitedTimeController) GetDownloadPlist(bundleId int) revel.Result {
	bundle := c.Bundle

	t, err := models.NewLimitedTimeTokenInfoByKey(Conf.Secret)
	if err != nil {
		panic(err)
	}
	v := t.UrlValues()

	ipaUrl, err := c.UriFor(fmt.Sprintf("bundle/%d/download_ipa", bundle.Id))
	if err != nil {
		panic(err)
	}
	ipaUrl.RawQuery = v.Encode()

	r, err := bundle.PlistReader(Dbm, ipaUrl)
	if err != nil {
		panic(err)
	}

	c.Response.ContentType = "application/x-plist"
	return c.RenderBinary(r, models.PlistFileName, revel.Attachment, time.Now())
}

func (c *LimitedTimeController) GetDownloadIpa(bundleId int) revel.Result {
	resp, file, err := c.GoogleService.DownloadFile(c.Bundle.FileId)
	if err != nil {
		panic(err)
	}

	modtime, err := time.Parse(time.RFC3339, file.ModifiedDate)
	if err != nil {
		panic(err)
	}

	err = c.createAudit(models.ResourceBundle, bundleId, models.ActionDownload)
	if err != nil {
		panic(err)
	}

	c.Response.ContentType = "application/octet-stream"
	return c.RenderBinary(resp.Body, file.OriginalFilename, revel.Attachment, modtime)
}

func (c *LimitedTimeController) CheckValidLimitedTimeToken() revel.Result {
	bundle := c.Bundle
	if c.Bundle == nil {
		return c.NotFound("NotFound")
	}

	token := c.Params.Get(models.TokenKey)
	seed := c.Params.Get(models.SeedKey)
	limit := c.Params.Get(models.LimitKey)

	c.Validation.Required(token).Message(models.TokenKey + " is required")
	c.Validation.Required(seed).Message(models.SeedKey + " is required")
	c.Validation.Required(limit).Message(models.LimitKey + " is required")
	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(routes.BundleControllerWithValidation.GetDownloadBundle(bundle.Id))
	}

	tokenInfo, err := models.NewLimitedTimeTokenInfo(token, seed, limit)
	if err != nil {
		panic(err)
	}

	ok, err := tokenInfo.IsValid(Conf.Secret)
	if err != nil {
		panic(err)
	}
	if !ok {
		return c.Forbidden("Forbidden")
	}

	return nil
}

func (c *LimitedTimeController) CheckNotFound() revel.Result {
	bundleIdStr := c.Params.Get("bundleId")

	c.Validation.Required(bundleIdStr).Message("BundleId is required.")
	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(routes.AlphaWingController.Index())
	}

	bundleId, err := strconv.Atoi(bundleIdStr)
	if err != nil {
		c.Flash.Error("BundleId is invalid.")
		return c.Redirect(routes.AlphaWingController.Index())
	}

	bundle, err := models.GetBundle(Dbm, bundleId)
	if err != nil {
		if err == sql.ErrNoRows {
			return c.NotFound("NotFound")
		}
		panic(err)
	}
	c.Bundle = bundle

	return nil
}
<commit_msg>c.Params.Get() -> c.Params.Query.Get()<commit_after>package controllers

import (
	"database/sql"
	"fmt"
	"strconv"
	"time"

	"github.com/kayac/alphawing/app/models"
	"github.com/kayac/alphawing/app/routes"
	"github.com/revel/revel"
)

type LimitedTimeController struct {
	AlphaWingController
	Bundle *models.Bundle
}

func (c *LimitedTimeController) GetDownloadPlist(bundleId int) revel.Result {
	bundle := c.Bundle

	t, err := models.NewLimitedTimeTokenInfoByKey(Conf.Secret)
	if err != nil {
		panic(err)
	}
	v := t.UrlValues()

	ipaUrl, err := c.UriFor(fmt.Sprintf("bundle/%d/download_ipa", bundle.Id))
	if err != nil {
		panic(err)
	}
	ipaUrl.RawQuery = v.Encode()

	r, err := bundle.PlistReader(Dbm, ipaUrl)
	if err != nil {
		panic(err)
	}

	c.Response.ContentType = "application/x-plist"
	return c.RenderBinary(r, models.PlistFileName, revel.Attachment, time.Now())
}

func (c *LimitedTimeController) GetDownloadIpa(bundleId int) revel.Result {
	resp, file, err := c.GoogleService.DownloadFile(c.Bundle.FileId)
	if err != nil {
		panic(err)
	}

	modtime, err := time.Parse(time.RFC3339, file.ModifiedDate)
	if err != nil {
		panic(err)
	}

	err = c.createAudit(models.ResourceBundle, bundleId, models.ActionDownload)
	if err != nil {
		panic(err)
	}

	c.Response.ContentType = "application/octet-stream"
	return c.RenderBinary(resp.Body, file.OriginalFilename, revel.Attachment, modtime)
}

func (c *LimitedTimeController) CheckValidLimitedTimeToken() revel.Result {
	bundle := c.Bundle
	if c.Bundle == nil {
		return c.NotFound("NotFound")
	}

	token := c.Params.Query.Get(models.TokenKey)
	seed := c.Params.Query.Get(models.SeedKey)
	limit := c.Params.Query.Get(models.LimitKey)

	c.Validation.Required(token).Message(models.TokenKey + " is required")
	c.Validation.Required(seed).Message(models.SeedKey + " is required")
	c.Validation.Required(limit).Message(models.LimitKey + " is required")
	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(routes.BundleControllerWithValidation.GetDownloadBundle(bundle.Id))
	}

	tokenInfo, err := models.NewLimitedTimeTokenInfo(token, seed, limit)
	if err != nil {
		panic(err)
	}

	ok, err := tokenInfo.IsValid(Conf.Secret)
	if err != nil {
		panic(err)
	}
	if !ok {
		return c.Forbidden("Forbidden")
	}

	return nil
}

func (c *LimitedTimeController) CheckNotFound() revel.Result {
	bundleIdStr := c.Params.Get("bundleId")

	c.Validation.Required(bundleIdStr).Message("BundleId is required.")
	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(routes.AlphaWingController.Index())
	}

	bundleId, err := strconv.Atoi(bundleIdStr)
	if err != nil {
		c.Flash.Error("BundleId is invalid.")
		return c.Redirect(routes.AlphaWingController.Index())
	}

	bundle, err := models.GetBundle(Dbm, bundleId)
	if err != nil {
		if err == sql.ErrNoRows {
			return c.NotFound("NotFound")
		}
		panic(err)
	}
	c.Bundle = bundle

	return nil
}
<|endoftext|>


<commit_before>package chem

import (
	"fmt"
	"strings"
)

type Filter interface {
	binds() []interface{}
	toBooleanExpression() string
}

type IntegerFilter struct {
	col   Column
	value int
}

func (f IntegerFilter) toBooleanExpression() string {
	return fmt.Sprintf("%v == ?", f.col.toColumnExpression())
}

func (f IntegerFilter) binds() (out []interface{}) {
	out = append(out, f.value)
	return
}

type BooleanOperatorFilter struct {
	operator string
	filters  []Filter
}

func (f BooleanOperatorFilter) toBooleanExpression() string {
	expressionList := make([]string, len(f.filters))
	for i, filter := range f.filters {
		expressionList[i] = filter.toBooleanExpression()
	}
	expression := strings.Join(
		expressionList,
		fmt.Sprintf(" %v ", f.operator),
	)
	if expression == "" {
		return expression
	}
	// wrap the expression to make sure precedence is what user expects
	return fmt.Sprintf("( %v )", expression)
}

func (f BooleanOperatorFilter) binds() []interface{} {
	out := make([]interface{}, 0, len(f.filters))
	for _, filter := range f.filters {
		out = append(out, filter.binds()...)
	}
	return out
}

func And(filters ...Filter) Filter {
	return BooleanOperatorFilter{
		operator: "AND",
		filters:  filters,
	}
}
<commit_msg>create ValueFilter to be a more general version of IntegerFilter<commit_after>package chem

import (
	"fmt"
	"strings"
)

type Filter interface {
	binds() []interface{}
	toBooleanExpression() string
}

type IntegerFilter struct {
	col   Column
	value int
}

func (f IntegerFilter) toBooleanExpression() string {
	return fmt.Sprintf("%v == ?", f.col.toColumnExpression())
}

func (f IntegerFilter) binds() (out []interface{}) {
	out = append(out, f.value)
	return
}

type ValueFilter struct {
	column   Column
	operator string
	value    interface{}
}

func (f ValueFilter) toBooleanExpression() string {
	return fmt.Sprintf("%v %v ?", f.column.toColumnExpression(), f.operator)
}

func (f ValueFilter) binds() []interface{} {
	return []interface{}{f.value}
}

type BooleanOperatorFilter struct {
	operator string
	filters  []Filter
}

func (f BooleanOperatorFilter) toBooleanExpression() string {
	expressionList := make([]string, len(f.filters))
	for i, filter := range f.filters {
		expressionList[i] = filter.toBooleanExpression()
	}
	expression := strings.Join(
		expressionList,
		fmt.Sprintf(" %v ", f.operator),
	)
	if expression == "" {
		return expression
	}
	// wrap the expression to make sure precedence is what user expects
	return fmt.Sprintf("( %v )", expression)
}

func (f BooleanOperatorFilter) binds() []interface{} {
	out := make([]interface{}, 0, len(f.filters))
	for _, filter := range f.filters {
		out = append(out, filter.binds()...)
	}
	return out
}

func And(filters ...Filter) Filter {
	return BooleanOperatorFilter{
		operator: "AND",
		filters:  filters,
	}
}
<|endoftext|>


<commit_before>// Package git contains various commands that shell out to git
package git

import (
	"errors"
	"fmt"
	"os/exec"
	"strings"

	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
)

func LsRemote(remote, remoteRef string) (string, error) {
	if remote == "" {
		return "", errors.New("remote required")
	}
	if remoteRef == "" {
		return simpleExec("git", "ls-remote", remote)

	}
	return simpleExec("git", "ls-remote", remote, remoteRef)
}

func ResolveRef(ref string) (string, error) {
	return simpleExec("git", "rev-parse", ref)
}

func CurrentRef() (string, error) {
	return ResolveRef("HEAD")
}

func CurrentBranch() (string, error) {
	return simpleExec("git", "rev-parse", "--abbrev-ref", "HEAD")
}

func CurrentRemoteRef() (string, error) {
	remote, err := CurrentRemote()
	if err != nil {
		return "", err
	}

	return ResolveRef(remote)
}

func CurrentRemote() (string, error) {
	branch, err := CurrentBranch()
	if err != nil {
		return "", err
	}

	if branch == "HEAD" {
		return "", errors.New("not on a branch")
	}

	remote := Config.Find(fmt.Sprintf("branch.%s.remote", branch))
	if remote == "" {
		return "", errors.New("remote not found")
	}

	return remote + "/" + branch, nil
}

func UpdateIndex(file string) error {
	_, err := simpleExec("git", "update-index", "-q", "--refresh", file)
	return err
}

type gitConfig struct {
}

var Config = &gitConfig{}

// Find returns the git config value for the key
func (c *gitConfig) Find(val string) string {
	output, _ := simpleExec("git", "config", val)
	return output
}

// SetGlobal sets the git config value for the key in the global config
func (c *gitConfig) SetGlobal(key, val string) {
	simpleExec("git", "config", "--global", "--add", key, val)
}

// UnsetGlobal removes the git config value for the key from the global config
func (c *gitConfig) UnsetGlobal(key string) {
	simpleExec("git", "config", "--global", "--unset", key)
}

// List lists all of the git config values
func (c *gitConfig) List() (string, error) {
	return simpleExec("git", "config", "-l")
}

// ListFromFile lists all of the git config values in the given config file
func (c *gitConfig) ListFromFile(f string) (string, error) {
	return simpleExec("git", "config", "-l", "-f", f)
}

// Version returns the git version
func (c *gitConfig) Version() (string, error) {
	return simpleExec("git", "version")
}

// simpleExec is a small wrapper around os/exec.Command.
func simpleExec(name string, arg ...string) (string, error) {
	tracerx.Printf("run_command: '%s' %s", name, strings.Join(arg, " "))
	cmd := exec.Command(name, arg...)

	output, err := cmd.Output()
	if _, ok := err.(*exec.ExitError); ok {
		return "", nil
	}
	if err != nil {
		return fmt.Sprintf("Error running %s %s", name, arg), err
	}

	return strings.Trim(string(output), " \n"), nil
}
<commit_msg>ラララララ ラー ララララー<commit_after>// Package git contains various commands that shell out to git
package git

import (
	"errors"
	"fmt"
	"os/exec"
	"strings"

	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
)

func LsRemote(remote, remoteRef string) (string, error) {
	if remote == "" {
		return "", errors.New("remote required")
	}
	if remoteRef == "" {
		return simpleExec("git", "ls-remote", remote)

	}
	return simpleExec("git", "ls-remote", remote, remoteRef)
}

func ResolveRef(ref string) (string, error) {
	return simpleExec("git", "rev-parse", ref)
}

func CurrentRef() (string, error) {
	return ResolveRef("HEAD")
}

func CurrentBranch() (string, error) {
	return simpleExec("git", "rev-parse", "--abbrev-ref", "HEAD")
}

func CurrentRemoteRef() (string, error) {
	remote, err := CurrentRemote()
	if err != nil {
		return "", err
	}

	return ResolveRef(remote)
}

func CurrentRemote() (string, error) {
	branch, err := CurrentBranch()
	if err != nil {
		return "", err
	}

	if branch == "HEAD" {
		return "", errors.New("not on a branch")
	}

	remote := Config.Find(fmt.Sprintf("branch.%s.remote", branch))
	if remote == "" {
		return "", errors.New("remote not found")
	}

	return remote + "/" + branch, nil
}

func UpdateIndex(file string) error {
	_, err := simpleExec("git", "update-index", "-q", "--refresh", file)
	return err
}

type gitConfig struct {
}

var Config = &gitConfig{}

// Find returns the git config value for the key
func (c *gitConfig) Find(val string) string {
	output, _ := simpleExec("git", "config", val)
	return output
}

// SetGlobal sets the git config value for the key in the global config
func (c *gitConfig) SetGlobal(key, val string) {
	simpleExec("git", "config", "--global", "--add", key, val)
}

// UnsetGlobal removes the git config value for the key from the global config
func (c *gitConfig) UnsetGlobal(key string) {
	simpleExec("git", "config", "--global", "--unset", key)
}

// List lists all of the git config values
func (c *gitConfig) List() (string, error) {
	return simpleExec("git", "config", "-l")
}

// ListFromFile lists all of the git config values in the given config file
func (c *gitConfig) ListFromFile(f string) (string, error) {
	return simpleExec("git", "config", "-l", "-f", f)
}

// Version returns the git version
func (c *gitConfig) Version() (string, error) {
	return simpleExec("git", "version")
}

// simpleExec is a small wrapper around os/exec.Command.
func simpleExec(name string, args ...string) (string, error) {
	tracerx.Printf("run_command: '%s' %s", name, strings.Join(args, " "))
	cmd := exec.Command(name, args...)

	output, err := cmd.Output()
	if _, ok := err.(*exec.ExitError); ok {
		return "", nil
	}
	if err != nil {
		return fmt.Sprintf("Error running %s %s", name, args), err
	}

	return strings.Trim(string(output), " \n"), nil
}
<|endoftext|>