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

// Package mobileapi is intended for use with `gomobile`.
package mobileapi

import (
	"fmt"
	"log"
	"os"
	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/google/note-maps/kv"
	"github.com/google/note-maps/kv/badger"
	"github.com/google/note-maps/store"
	"github.com/google/note-maps/store/pb"
)

func SetPath(p string) {
	m.Lock()
	defer m.Unlock()
	if path == "" {
		path = p
	}
}

func Close() {
	m.Lock()
	defer m.Unlock()
	if err := db.Close(); err != nil {
		log.Println("could not close database:", err)
	}
	db = nil
}

func Query(method string, bs []byte) ([]byte, error) {
	txn, err := newQueryTxn()
	if err != nil {
		return nil, err
	}
	defer txn.Discard()
	switch method {
	case "GetTopicMaps":
		var request pb.GetTopicMapsRequest
		if err := proto.Unmarshal(bs, &request); err != nil {
			return nil, err
		}
		q := store.Query(txn)
		if response, err := q.GetTopicMaps(&request); err != nil {
			return nil, err
		} else {
			return proto.Marshal(response)
		}
	default:
		return nil, fmt.Errorf("unrecognized query: %#v", method)
	}
}

func Command(method string, bs []byte) ([]byte, error) {
	txn, err := newCommandTxn()
	if err != nil {
		return nil, err
	}
	defer txn.Discard()
	switch method {
	default:
		return nil, fmt.Errorf("unrecognized command: %#v", method)
	}
}

const (
	permissions = 0700
)

var (
	db   *badger.DB
	m    sync.Mutex
	path string
)

func getDB() (*badger.DB, error) {
	m.Lock()
	defer m.Unlock()
	if db == nil {
		if path == "" {
			return nil, fmt.Errorf("incomplete initialization: path is empty")
		}
		err := os.MkdirAll(path, permissions)
		if err != nil {
			return nil, err
		}
		options := badger.DefaultOptions(path)
		db, err = badger.Open(options)
		if err != nil {
			return nil, err
		}
	}
	return db, nil
}

func newQueryTxn() (kv.TxnDiscarder, error) {
	db, err := getDB()
	if err != nil {
		return nil, err
	}
	return db.NewTxn(false), nil
}

func newCommandTxn() (kv.TxnCommitDiscarder, error) {
	db, err := getDB()
	if err != nil {
		return nil, err
	}
	return db.NewTxn(false), nil
}
<commit_msg>Reduce maximum value log size for mobile<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package mobileapi is intended for use with `gomobile`.
package mobileapi

import (
	"fmt"
	"log"
	"os"
	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/google/note-maps/kv"
	"github.com/google/note-maps/kv/badger"
	"github.com/google/note-maps/store"
	"github.com/google/note-maps/store/pb"
)

func SetPath(p string) {
	m.Lock()
	defer m.Unlock()
	if path == "" {
		path = p
	}
}

func Close() {
	m.Lock()
	defer m.Unlock()
	if err := db.Close(); err != nil {
		log.Println("could not close database:", err)
	}
	db = nil
}

func Query(method string, bs []byte) ([]byte, error) {
	txn, err := newQueryTxn()
	if err != nil {
		return nil, err
	}
	defer txn.Discard()
	switch method {
	case "GetTopicMaps":
		var request pb.GetTopicMapsRequest
		if err := proto.Unmarshal(bs, &request); err != nil {
			return nil, err
		}
		q := store.Query(txn)
		if response, err := q.GetTopicMaps(&request); err != nil {
			return nil, err
		} else {
			return proto.Marshal(response)
		}
	default:
		return nil, fmt.Errorf("unrecognized query: %#v", method)
	}
}

func Command(method string, bs []byte) ([]byte, error) {
	txn, err := newCommandTxn()
	if err != nil {
		return nil, err
	}
	defer txn.Discard()
	switch method {
	default:
		return nil, fmt.Errorf("unrecognized command: %#v", method)
	}
}

const (
	permissions = 0700
)

var (
	db   *badger.DB
	m    sync.Mutex
	path string
)

func getDB() (*badger.DB, error) {
	m.Lock()
	defer m.Unlock()

	if db == nil {
		if path == "" {
			return nil, fmt.Errorf("incomplete initialization: path is empty")
		}
		err := os.MkdirAll(path, permissions)
		if err != nil {
			return nil, err
		}

		options := badger.DefaultOptions(path)

		// Default options leads to a failure on Android, "Map log file.
		// Path=.../000000.vlog. Error=cannot allocate memory"
		//
		// A fix suggested in https://github.com/ipfs/ipfs-cluster/issues/771 is to
		// decrease the ValueLogFileSize.
		options = options.WithValueLogFileSize(1 << 24)

		db, err = badger.Open(options)
		if err != nil {
			return nil, err
		}
	}

	return db, nil
}

func newQueryTxn() (kv.TxnDiscarder, error) {
	db, err := getDB()
	if err != nil {
		return nil, err
	}
	return db.NewTxn(false), nil
}

func newCommandTxn() (kv.TxnCommitDiscarder, error) {
	db, err := getDB()
	if err != nil {
		return nil, err
	}
	return db.NewTxn(false), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/flynn/go-crypto-ssh"
	"github.com/flynn/go-shlex"
)

const PrereceiveHook = `#!/bin/bash
set -eo pipefail; while read oldrev newrev refname; do
[[ $refname = "refs/heads/master" ]] && git archive $newrev | {{RECEIVER}} "$RECEIVE_USER" "$RECEIVE_REPO" "$RECEIVE_KEYNAME" "$RECEIVE_FINGERPRINT" | sed -$([[ $(uname) == "Darwin" ]] && echo l || echo u) "s/^/"$'\e[1G'"/"
done
`

var port *string = flag.String("p", "22", "port to listen on")
var repoPath *string = flag.String("r", "/tmp/repos", "path to repo cache")
var keyPath *string = flag.String("k", "/tmp/keys", "path to named keys")
var noAuth *bool = flag.Bool("n", false, "no client authentication")

var receiver string
var privateKey string
var keyNames = make(map[string]string)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage:  %v [options] <privatekey> <receiver>\n\n", os.Args[0])
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	if len(os.Args) < 2 {
		flag.Usage()
		return
	}
	privateKey = flag.Arg(0)
	receiver = flag.Arg(1)

	var config *ssh.ServerConfig
	if *noAuth {
		config = &ssh.ServerConfig{
			NoClientAuth: true,
		}
	} else {
		config = &ssh.ServerConfig{
			PublicKeyCallback: func(conn *ssh.ServerConn, user, algo string, pubkey []byte) bool {
				clientkey, _, ok := ssh.ParsePublicKey(pubkey)
				if !ok {
					return false
				}
				os.MkdirAll(*keyPath, 0755)
				files, err := ioutil.ReadDir(*keyPath)
				if err != nil {
					panic(err)
				}
				for _, file := range files {
					if !file.IsDir() {
						data, err := ioutil.ReadFile(*keyPath + "/" + file.Name())
						if err != nil {
							panic(err)
						}
						filekey, _, _, _, ok := ssh.ParseAuthorizedKey(data)
						if !ok {
							continue
						}
						if bytes.Equal(clientkey.Marshal(), filekey.Marshal()) {
							keyNames[publicKeyFingerprint(clientkey)] = file.Name()
							return true
						}
					}
				}
				return false
			},
		}
	}

	pemBytes, err := ioutil.ReadFile(privateKey)
	if err != nil {
		log.Fatal("Failed to load private key:", err)
	}
	if err = config.SetRSAPrivateKey(pemBytes); err != nil {
		log.Fatal("Failed to parse private key:", err)
	}

	listener, err := ssh.Listen("tcp", "0.0.0.0:"+*port, config)
	if err != nil {
		log.Fatal("failed to listen for connection")
	}
	for {
		// SSH connections just house multiplexed connections
		conn, err := listener.Accept()
		if err != nil {
			log.Println("failed to accept incoming connection")
			continue
		}
		if err := conn.Handshake(); err != nil {
			log.Println("failed to handshake")
			continue
		}
		go handleConnection(conn)
	}
}

func handleConnection(conn *ssh.ServerConn) {
	defer conn.Close()
	for {
		// Accept reads from the connection, demultiplexes packets
		// to their corresponding channels and returns when a new
		// channel request is seen. Some goroutine must always be
		// calling Accept; otherwise no messages will be forwarded
		// to the channels.
		ch, err := conn.Accept()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Println("hanndleConnection Accept:", err)
			break
		}
		if ch.ChannelType() != "session" {
			ch.Reject(ssh.UnknownChannelType, "unknown channel type")
			break
		}
		go handleChannel(conn, ch)
	}
}

func handleChannel(conn *ssh.ServerConn, ch ssh.Channel) {
	err := ch.Accept()
	if err != nil {
		panic(err)
	}
	defer ch.Close()
	for {
		req, err := ch.ReadRequest()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Println("handleChannel read request err:", err)
			continue
		}
		switch req.Request {
		case "exec":
			if req.WantReply {
				ch.AckRequest(true)
			}
			cmdline := string(req.Payload[4:])
			cmdargs, err := shlex.Split(cmdline)
			if err != nil {
				panic(err)
			}
			if strings.HasPrefix(cmdargs[1], "/") {
				cmdargs[1] = cmdargs[1][1:]
			}
			ensureCacheRepo(cmdargs[1])
			var keyname, fingerprint string
			if *noAuth {
				fingerprint = ""
				keyname = ""
			} else {
				fingerprint = publicKeyFingerprint(conn.PublicKey)
				keyname = keyNames[fingerprint]
			}
			cmd := exec.Command(cmdargs[0], cmdargs[1:]...)
			cmd.Dir = *repoPath
			cmd.Env = []string{
				"RECEIVE_USER=" + conn.User,
				"RECEIVE_REPO=" + cmdargs[1],
				"RECEIVE_KEYNAME=" + keyname,
				"RECEIVE_FINGERPRINT=" + fingerprint,
			}
			errCh := attachCmd(cmd, ch, ch.Stderr(), ch)
			err = cmd.Start()
			if err != nil {
				panic(err)
			}
			exitCh := exitStatusCh(cmd)
			if err = <-errCh; err != nil {
				panic(err)
			}
			ch.Exit(<-exitCh)
		case "env":
			if req.WantReply {
				ch.AckRequest(true)
			}
		}
	}
}

func attachCmd(cmd *exec.Cmd, stdout, stderr io.Writer, stdin io.Reader) chan error {
	errCh := make(chan error)

	stdinIn, err := cmd.StdinPipe()
	if err != nil {
		panic(err)
	}
	stdoutOut, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}
	stderrOut, err := cmd.StderrPipe()
	if err != nil {
		panic(err)
	}

	go func() {
		_, e := io.Copy(stdinIn, stdin)
		errCh <- e
	}()
	go func() {
		_, e := io.Copy(stdout, stdoutOut)
		errCh <- e
	}()
	go func() {
		_, e := io.Copy(stderr, stderrOut)
		errCh <- e
	}()

	return errCh
}

func exitStatusCh(cmd *exec.Cmd) chan uint {
	exitCh := make(chan uint)
	go func() {
		err := cmd.Wait()
		if err != nil {
			if exiterr, ok := err.(*exec.ExitError); ok {
				// There is no plattform independent way to retrieve
				// the exit code, but the following will work on Unix
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					exitCh <- uint(status.ExitStatus())
				}
			} else {
				panic(err)
			}
			return
		}
		exitCh <- uint(0)
	}()
	return exitCh
}

func ensureCacheRepo(path string) {
	cachePath := *repoPath + "/" + path
	if _, err := os.Stat(cachePath); os.IsNotExist(err) {
		os.MkdirAll(cachePath, 0755)
		cmd := exec.Command("git", "init", "--bare")
		cmd.Dir = cachePath
		err = cmd.Run()
		if err != nil {
			panic(err)
		}
	}
	receiver, err := filepath.Abs(receiver)
	if err != nil {
		panic(err)
	}
	ioutil.WriteFile(
		cachePath+"/hooks/pre-receive",
		[]byte(strings.Replace(PrereceiveHook, "{{RECEIVER}}", receiver, 1)),
		0755)
}

func publicKeyFingerprint(key ssh.PublicKey) string {
	var values []string
	h := md5.New()
	h.Write(ssh.MarshalPublicKey(key))
	for _, value := range h.Sum(nil) {
		values = append(values, fmt.Sprintf("%x", value))
	}
	return strings.Join(values, ":")
}
<commit_msg>Move PublicKeyCallback to separate function<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/flynn/go-crypto-ssh"
	"github.com/flynn/go-shlex"
)

const PrereceiveHook = `#!/bin/bash
set -eo pipefail; while read oldrev newrev refname; do
[[ $refname = "refs/heads/master" ]] && git archive $newrev | {{RECEIVER}} "$RECEIVE_USER" "$RECEIVE_REPO" "$RECEIVE_KEYNAME" "$RECEIVE_FINGERPRINT" | sed -$([[ $(uname) == "Darwin" ]] && echo l || echo u) "s/^/"$'\e[1G'"/"
done
`

var port *string = flag.String("p", "22", "port to listen on")
var repoPath *string = flag.String("r", "/tmp/repos", "path to repo cache")
var keyPath *string = flag.String("k", "/tmp/keys", "path to named keys")
var noAuth *bool = flag.Bool("n", false, "no client authentication")

var receiver string
var privateKey string
var keyNames = make(map[string]string)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage:  %v [options] <privatekey> <receiver>\n\n", os.Args[0])
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	if len(os.Args) < 2 {
		flag.Usage()
		return
	}
	privateKey = flag.Arg(0)
	receiver = flag.Arg(1)

	var config *ssh.ServerConfig
	if *noAuth {
		config = &ssh.ServerConfig{NoClientAuth: true}
	} else {
		config = &ssh.ServerConfig{PublicKeyCallback: keyCallback}
	}

	pemBytes, err := ioutil.ReadFile(privateKey)
	if err != nil {
		log.Fatal("Failed to load private key:", err)
	}
	if err = config.SetRSAPrivateKey(pemBytes); err != nil {
		log.Fatal("Failed to parse private key:", err)
	}

	listener, err := ssh.Listen("tcp", "0.0.0.0:"+*port, config)
	if err != nil {
		log.Fatal("failed to listen for connection")
	}
	for {
		// SSH connections just house multiplexed connections
		conn, err := listener.Accept()
		if err != nil {
			log.Println("failed to accept incoming connection")
			continue
		}
		if err := conn.Handshake(); err != nil {
			log.Println("failed to handshake")
			continue
		}
		go handleConnection(conn)
	}
}

func keyCallback(conn *ssh.ServerConn, user, algo string, pubkey []byte) bool {
	clientkey, _, ok := ssh.ParsePublicKey(pubkey)
	if !ok {
		return false
	}
	os.MkdirAll(*keyPath, 0755)
	files, err := ioutil.ReadDir(*keyPath)
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		if !file.IsDir() {
			data, err := ioutil.ReadFile(*keyPath + "/" + file.Name())
			if err != nil {
				panic(err)
			}
			filekey, _, _, _, ok := ssh.ParseAuthorizedKey(data)
			if !ok {
				continue
			}
			if bytes.Equal(clientkey.Marshal(), filekey.Marshal()) {
				keyNames[publicKeyFingerprint(clientkey)] = file.Name()
				return true
			}
		}
	}
	return false
}

func handleConnection(conn *ssh.ServerConn) {
	defer conn.Close()
	for {
		// Accept reads from the connection, demultiplexes packets
		// to their corresponding channels and returns when a new
		// channel request is seen. Some goroutine must always be
		// calling Accept; otherwise no messages will be forwarded
		// to the channels.
		ch, err := conn.Accept()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Println("hanndleConnection Accept:", err)
			break
		}
		if ch.ChannelType() != "session" {
			ch.Reject(ssh.UnknownChannelType, "unknown channel type")
			break
		}
		go handleChannel(conn, ch)
	}
}

func handleChannel(conn *ssh.ServerConn, ch ssh.Channel) {
	err := ch.Accept()
	if err != nil {
		panic(err)
	}
	defer ch.Close()
	for {
		req, err := ch.ReadRequest()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Println("handleChannel read request err:", err)
			continue
		}
		switch req.Request {
		case "exec":
			if req.WantReply {
				ch.AckRequest(true)
			}
			cmdline := string(req.Payload[4:])
			cmdargs, err := shlex.Split(cmdline)
			if err != nil {
				panic(err)
			}
			if strings.HasPrefix(cmdargs[1], "/") {
				cmdargs[1] = cmdargs[1][1:]
			}
			ensureCacheRepo(cmdargs[1])
			var keyname, fingerprint string
			if *noAuth {
				fingerprint = ""
				keyname = ""
			} else {
				fingerprint = publicKeyFingerprint(conn.PublicKey)
				keyname = keyNames[fingerprint]
			}
			cmd := exec.Command(cmdargs[0], cmdargs[1:]...)
			cmd.Dir = *repoPath
			cmd.Env = []string{
				"RECEIVE_USER=" + conn.User,
				"RECEIVE_REPO=" + cmdargs[1],
				"RECEIVE_KEYNAME=" + keyname,
				"RECEIVE_FINGERPRINT=" + fingerprint,
			}
			errCh := attachCmd(cmd, ch, ch.Stderr(), ch)
			err = cmd.Start()
			if err != nil {
				panic(err)
			}
			exitCh := exitStatusCh(cmd)
			if err = <-errCh; err != nil {
				panic(err)
			}
			ch.Exit(<-exitCh)
		case "env":
			if req.WantReply {
				ch.AckRequest(true)
			}
		}
	}
}

func attachCmd(cmd *exec.Cmd, stdout, stderr io.Writer, stdin io.Reader) chan error {
	errCh := make(chan error)

	stdinIn, err := cmd.StdinPipe()
	if err != nil {
		panic(err)
	}
	stdoutOut, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}
	stderrOut, err := cmd.StderrPipe()
	if err != nil {
		panic(err)
	}

	go func() {
		_, e := io.Copy(stdinIn, stdin)
		errCh <- e
	}()
	go func() {
		_, e := io.Copy(stdout, stdoutOut)
		errCh <- e
	}()
	go func() {
		_, e := io.Copy(stderr, stderrOut)
		errCh <- e
	}()

	return errCh
}

func exitStatusCh(cmd *exec.Cmd) chan uint {
	exitCh := make(chan uint)
	go func() {
		err := cmd.Wait()
		if err != nil {
			if exiterr, ok := err.(*exec.ExitError); ok {
				// There is no plattform independent way to retrieve
				// the exit code, but the following will work on Unix
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					exitCh <- uint(status.ExitStatus())
				}
			} else {
				panic(err)
			}
			return
		}
		exitCh <- uint(0)
	}()
	return exitCh
}

func ensureCacheRepo(path string) {
	cachePath := *repoPath + "/" + path
	if _, err := os.Stat(cachePath); os.IsNotExist(err) {
		os.MkdirAll(cachePath, 0755)
		cmd := exec.Command("git", "init", "--bare")
		cmd.Dir = cachePath
		err = cmd.Run()
		if err != nil {
			panic(err)
		}
	}
	receiver, err := filepath.Abs(receiver)
	if err != nil {
		panic(err)
	}
	ioutil.WriteFile(
		cachePath+"/hooks/pre-receive",
		[]byte(strings.Replace(PrereceiveHook, "{{RECEIVER}}", receiver, 1)),
		0755)
}

func publicKeyFingerprint(key ssh.PublicKey) string {
	var values []string
	h := md5.New()
	h.Write(ssh.MarshalPublicKey(key))
	for _, value := range h.Sum(nil) {
		values = append(values, fmt.Sprintf("%x", value))
	}
	return strings.Join(values, ":")
}
<|endoftext|>


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

// Package proxy provides a WebDriver protocol that forwards requests
// to a WebDriver server provided by an environment.Env.
package proxy

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/bazelbuild/rules_webtesting/go/httphelper"
	"github.com/bazelbuild/rules_webtesting/go/launcher/diagnostics"
	"github.com/bazelbuild/rules_webtesting/go/launcher/environment"
	"github.com/bazelbuild/rules_webtesting/go/launcher/errors"
	"github.com/bazelbuild/rules_webtesting/go/launcher/healthreporter"
	"github.com/bazelbuild/rules_webtesting/go/metadata"
	"github.com/bazelbuild/rules_webtesting/go/portpicker"
)

const (
	compName = "WebDriver proxy"
	timeout  = 1 * time.Second
)

var handlerProviders = map[string]HTTPHandlerProvider{}

// A HTTPHandlerProvider is a function that provides a HTTPHandler.
type HTTPHandlerProvider func(*Proxy) (HTTPHandler, error)

// A HTTPHandler implements http.Handler plus a Shutdown method.
type HTTPHandler interface {
	http.Handler

	// Shutdown is called when the proxy is in the process of shutting down.
	Shutdown(context.Context) error
}

// AddHTTPHandlerProvider adds a HTTPHandlerProvider used to create handlers for
// specified routes in any Proxy structs creates by New.
func AddHTTPHandlerProvider(route string, provider HTTPHandlerProvider) {
	handlerProviders[route] = provider
}

type certs struct {
	certFile string
	keyFile  string
}

// Proxy starts a WebDriver protocol proxy.
type Proxy struct {
	Diagnostics  diagnostics.Diagnostics
	Env          environment.Env
	Metadata     *metadata.Metadata
	HTTPAddress  string
	HTTPSAddress string
	handlers     []HTTPHandler
	httpSrv      *http.Server
	httpsSrv     *http.Server
	httpPort     int
	httpsPort    int
	certs        *certs
}

// New creates a new Proxy object.
func New(env environment.Env, m *metadata.Metadata, d diagnostics.Diagnostics) (*Proxy, error) {
	fqdn, err := httphelper.FQDN()
	if err != nil {
		return nil, errors.New(compName, err)
	}

	httpPort, err := portpicker.PickUnusedPort()
	if err != nil {
		return nil, errors.New(compName, err)
	}

	httpAddress := net.JoinHostPort(fqdn, strconv.Itoa(httpPort))

	httpsPort := -1
	httpsAddress := ""
	certs := newCerts(m)
	if certs != nil {
		httpsPort, err = portpicker.PickUnusedPort()
		if err != nil {
			return nil, errors.New(compName, err)
		}
		httpsAddress = net.JoinHostPort(fqdn, strconv.Itoa(httpsPort))
	}

	p := &Proxy{
		Diagnostics:  d,
		Env:          env,
		Metadata:     m,
		HTTPAddress:  httpAddress,
		HTTPSAddress: httpsAddress,
		httpPort:     httpPort,
		httpsPort:    httpsPort,
		certs:        certs,
	}

	mux := http.NewServeMux()

	for route, provider := range handlerProviders {
		h, err := provider(p)
		if err != nil {
			return nil, err
		}
		p.handlers = append(p.handlers, h)
		mux.Handle(route, h)
	}

	p.httpSrv = &http.Server{
		Addr:    ":" + strconv.Itoa(p.httpPort),
		Handler: mux,
	}

	if p.certs != nil {
		p.httpsSrv = &http.Server{
			Addr:    ":" + strconv.Itoa(p.httpsPort),
			Handler: mux,
		}
	}

	return p, nil
}

func newCerts(m *metadata.Metadata) *certs {
	certFile, err := m.GetFilePath("PROXY_TLS_CERT")
	if err != nil {
		return nil
	}
	keyFile, err := m.GetFilePath("PROXY_TLS_KEY")
	if err != nil {
		return nil
	}

	return &certs{certFile, keyFile}
}

// Name returns the name used in error messages.
func (*Proxy) Name() string {
	return compName
}

// Start configures the proxy with handlers, starts its listen loop, and waits for it to respond to a health check.
func (p *Proxy) Start(ctx context.Context) error {
	start := time.Now()
	defer func() {
		if err := p.Diagnostics.Timing(p.Name(), "start", "", start, time.Now()); err != nil {
			log.Print(err)
		}
	}()

	go func() {
		log.Printf("launching HTTP server at: %v", p.HTTPAddress)
		err := errors.New(p.Name(), p.httpSrv.ListenAndServe())
		p.Diagnostics.Severe(err)
	}()

	if p.httpsSrv != nil {
		go func() {
			log.Printf("launching HTTPS server at: %v", p.HTTPSAddress)
			err := errors.New(p.Name(), p.httpsSrv.ListenAndServeTLS(p.certs.certFile, p.certs.keyFile))
			p.Diagnostics.Severe(err)
		}()
	}

	healthyCtx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	return healthreporter.WaitForHealthy(healthyCtx, p)
}

// Healthy returns nil if the proxy is able to receive requests.
func (p *Proxy) Healthy(ctx context.Context) error {
	if err := p.httpHealthy(ctx); err != nil {
		return err
	}
	if err := p.httpsHealthy(ctx); err != nil {
		return err
	}
	return nil
}

func (p *Proxy) httpHealthy(ctx context.Context) error {
	url := fmt.Sprintf("http://%s/healthz", p.HTTPAddress)
	resp, err := httphelper.Get(ctx, url)
	if err != nil {
		return errors.New(p.Name(), fmt.Errorf("error getting %s: %v", url, err))
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(p.Name(), fmt.Errorf("request to %s returned status %v", url, resp.StatusCode))
	}
	return nil
}

func (p *Proxy) httpsHealthy(ctx context.Context) error {
	if p.HTTPSAddress == "" {
		return nil
	}
	url := fmt.Sprintf("https://%s/healthz", p.HTTPSAddress)
	resp, err := httphelper.Get(ctx, url)
	if err != nil {
		return errors.New(p.Name(), fmt.Errorf("error getting %s: %v", url, err))
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(p.Name(), fmt.Errorf("request to %s returned status %v", url, resp.StatusCode))
	}
	return nil
}

// Shutdown calls Shutdown on all handlers, then shuts the HTTP server down.
func (p *Proxy) Shutdown(ctx context.Context) error {
	for _, handler := range p.handlers {
		if err := handler.Shutdown(ctx); err != nil {
			p.Diagnostics.Warning(err)
		}
	}
	// TODO(DrMarcII) figure out why Shutdown doesn't work.
	return nil
}
<commit_msg>Increase the WD proxy timeout. (#154)<commit_after>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package proxy provides a WebDriver protocol that forwards requests
// to a WebDriver server provided by an environment.Env.
package proxy

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/bazelbuild/rules_webtesting/go/httphelper"
	"github.com/bazelbuild/rules_webtesting/go/launcher/diagnostics"
	"github.com/bazelbuild/rules_webtesting/go/launcher/environment"
	"github.com/bazelbuild/rules_webtesting/go/launcher/errors"
	"github.com/bazelbuild/rules_webtesting/go/launcher/healthreporter"
	"github.com/bazelbuild/rules_webtesting/go/metadata"
	"github.com/bazelbuild/rules_webtesting/go/portpicker"
)

const (
	compName = "WebDriver proxy"
	timeout  = 3 * time.Second
)

var handlerProviders = map[string]HTTPHandlerProvider{}

// A HTTPHandlerProvider is a function that provides a HTTPHandler.
type HTTPHandlerProvider func(*Proxy) (HTTPHandler, error)

// A HTTPHandler implements http.Handler plus a Shutdown method.
type HTTPHandler interface {
	http.Handler

	// Shutdown is called when the proxy is in the process of shutting down.
	Shutdown(context.Context) error
}

// AddHTTPHandlerProvider adds a HTTPHandlerProvider used to create handlers for
// specified routes in any Proxy structs creates by New.
func AddHTTPHandlerProvider(route string, provider HTTPHandlerProvider) {
	handlerProviders[route] = provider
}

type certs struct {
	certFile string
	keyFile  string
}

// Proxy starts a WebDriver protocol proxy.
type Proxy struct {
	Diagnostics  diagnostics.Diagnostics
	Env          environment.Env
	Metadata     *metadata.Metadata
	HTTPAddress  string
	HTTPSAddress string
	handlers     []HTTPHandler
	httpSrv      *http.Server
	httpsSrv     *http.Server
	httpPort     int
	httpsPort    int
	certs        *certs
}

// New creates a new Proxy object.
func New(env environment.Env, m *metadata.Metadata, d diagnostics.Diagnostics) (*Proxy, error) {
	fqdn, err := httphelper.FQDN()
	if err != nil {
		return nil, errors.New(compName, err)
	}

	httpPort, err := portpicker.PickUnusedPort()
	if err != nil {
		return nil, errors.New(compName, err)
	}

	httpAddress := net.JoinHostPort(fqdn, strconv.Itoa(httpPort))

	httpsPort := -1
	httpsAddress := ""
	certs := newCerts(m)
	if certs != nil {
		httpsPort, err = portpicker.PickUnusedPort()
		if err != nil {
			return nil, errors.New(compName, err)
		}
		httpsAddress = net.JoinHostPort(fqdn, strconv.Itoa(httpsPort))
	}

	p := &Proxy{
		Diagnostics:  d,
		Env:          env,
		Metadata:     m,
		HTTPAddress:  httpAddress,
		HTTPSAddress: httpsAddress,
		httpPort:     httpPort,
		httpsPort:    httpsPort,
		certs:        certs,
	}

	mux := http.NewServeMux()

	for route, provider := range handlerProviders {
		h, err := provider(p)
		if err != nil {
			return nil, err
		}
		p.handlers = append(p.handlers, h)
		mux.Handle(route, h)
	}

	p.httpSrv = &http.Server{
		Addr:    ":" + strconv.Itoa(p.httpPort),
		Handler: mux,
	}

	if p.certs != nil {
		p.httpsSrv = &http.Server{
			Addr:    ":" + strconv.Itoa(p.httpsPort),
			Handler: mux,
		}
	}

	return p, nil
}

func newCerts(m *metadata.Metadata) *certs {
	certFile, err := m.GetFilePath("PROXY_TLS_CERT")
	if err != nil {
		return nil
	}
	keyFile, err := m.GetFilePath("PROXY_TLS_KEY")
	if err != nil {
		return nil
	}

	return &certs{certFile, keyFile}
}

// Name returns the name used in error messages.
func (*Proxy) Name() string {
	return compName
}

// Start configures the proxy with handlers, starts its listen loop, and waits for it to respond to a health check.
func (p *Proxy) Start(ctx context.Context) error {
	start := time.Now()
	defer func() {
		if err := p.Diagnostics.Timing(p.Name(), "start", "", start, time.Now()); err != nil {
			log.Print(err)
		}
	}()

	go func() {
		log.Printf("launching HTTP server at: %v", p.HTTPAddress)
		err := errors.New(p.Name(), p.httpSrv.ListenAndServe())
		p.Diagnostics.Severe(err)
	}()

	if p.httpsSrv != nil {
		go func() {
			log.Printf("launching HTTPS server at: %v", p.HTTPSAddress)
			err := errors.New(p.Name(), p.httpsSrv.ListenAndServeTLS(p.certs.certFile, p.certs.keyFile))
			p.Diagnostics.Severe(err)
		}()
	}

	healthyCtx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	return healthreporter.WaitForHealthy(healthyCtx, p)
}

// Healthy returns nil if the proxy is able to receive requests.
func (p *Proxy) Healthy(ctx context.Context) error {
	if err := p.httpHealthy(ctx); err != nil {
		return err
	}
	if err := p.httpsHealthy(ctx); err != nil {
		return err
	}
	return nil
}

func (p *Proxy) httpHealthy(ctx context.Context) error {
	url := fmt.Sprintf("http://%s/healthz", p.HTTPAddress)
	resp, err := httphelper.Get(ctx, url)
	if err != nil {
		return errors.New(p.Name(), fmt.Errorf("error getting %s: %v", url, err))
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(p.Name(), fmt.Errorf("request to %s returned status %v", url, resp.StatusCode))
	}
	return nil
}

func (p *Proxy) httpsHealthy(ctx context.Context) error {
	if p.HTTPSAddress == "" {
		return nil
	}
	url := fmt.Sprintf("https://%s/healthz", p.HTTPSAddress)
	resp, err := httphelper.Get(ctx, url)
	if err != nil {
		return errors.New(p.Name(), fmt.Errorf("error getting %s: %v", url, err))
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(p.Name(), fmt.Errorf("request to %s returned status %v", url, resp.StatusCode))
	}
	return nil
}

// Shutdown calls Shutdown on all handlers, then shuts the HTTP server down.
func (p *Proxy) Shutdown(ctx context.Context) error {
	for _, handler := range p.handlers {
		if err := handler.Shutdown(ctx); err != nil {
			p.Diagnostics.Warning(err)
		}
	}
	// TODO(DrMarcII) figure out why Shutdown doesn't work.
	return nil
}
<|endoftext|>


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

package howto

import (
	"bytes"
	"strings"
	"testing"

	"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
)

func TestCommuteSearch(t *testing.T) {
	tc := testutil.SystemTest(t)
	buf := &bytes.Buffer{}
	companyID := strings.SplitAfter(testCompany.Name, "companies/")[1]
	if err := commuteSearch(buf, tc.ProjectID, companyID); err != nil {
		t.Fatalf("commuteSearch: %v", err)
	}
	want := "Mountain View"
	if got := buf.String(); !strings.Contains(got, want) {
		t.Fatalf("commuteSearch got %q, want to contain %q", got, want)
	}
}
<commit_msg>jobs/v4: add retry to TestCommuteSearch (#917)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package howto

import (
	"bytes"
	"strings"
	"testing"
	"time"

	"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
)

func TestCommuteSearch(t *testing.T) {
	tc := testutil.SystemTest(t)
	companyID := strings.SplitAfter(testCompany.Name, "companies/")[1]
	testutil.Retry(t, 20, 2*time.Second, func(r *testutil.R) {
		buf := &bytes.Buffer{}
		if err := commuteSearch(buf, tc.ProjectID, companyID); err != nil {
			t.Fatalf("commuteSearch: %v", err)
		}
		want := "Mountain View"
		if got := buf.String(); !strings.Contains(got, want) {
			t.Fatalf("commuteSearch got %q, want to contain %q", got, want)
		}
	})
}
<|endoftext|>


<commit_before>package db

import (
	"bytes"
	"database/sql"
	"github.com/OpenBazaar/spvwallet"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/wire"
	"sync"
	"time"
)

type TxnsDB struct {
	db   *sql.DB
	lock sync.RWMutex
}

func (t *TxnsDB) Put(txn *wire.MsgTx, value, height int, timestamp time.Time, watchOnly bool) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	tx, err := t.db.Begin()
	if err != nil {
		return err
	}
	stmt, err := tx.Prepare("insert or replace into txns(txid, value, height, timestamp, watchOnly, tx) values(?,?,?,?,?,?)")
	defer stmt.Close()
	if err != nil {
		tx.Rollback()
		return err
	}
	watchOnlyInt := 0
	if watchOnly {
		watchOnlyInt = 1
	}
	var buf bytes.Buffer
	txn.Serialize(&buf)
	_, err = stmt.Exec(txn.TxHash().String(), value, height, int(timestamp.Unix()), watchOnlyInt, buf.Bytes())
	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func (t *TxnsDB) Get(txid chainhash.Hash) (*wire.MsgTx, spvwallet.Txn, error) {
	t.lock.Lock()
	defer t.lock.Unlock()
	var txn spvwallet.Txn
	stmt, err := t.db.Prepare("select tx, value, height, timestamp, watchOnly from txns where txid=?")
	if err != nil {
		return nil, txn, err
	}
	defer stmt.Close()
	var ret []byte
	var height int
	var timestamp int
	var value int
	var watchOnlyInt int
	err = stmt.QueryRow(txid.String()).Scan(&ret, &value, &height, &timestamp, &watchOnlyInt)
	if err != nil {
		return nil, txn, err
	}
	watchOnly := false
	if watchOnlyInt > 0 {
		watchOnly = true
	}
	r := bytes.NewReader(ret)
	msgTx := wire.NewMsgTx(1)
	msgTx.BtcDecode(r, 1)
	txn = spvwallet.Txn{
		Txid:      msgTx.TxHash().String(),
		Value:     int64(value),
		Height:    int32(height),
		Timestamp: time.Unix(int64(timestamp), 0),
		WatchOnly: watchOnly,
	}
	return msgTx, txn, nil
}

func (t *TxnsDB) GetAll(includeWatchOnly bool) ([]spvwallet.Txn, error) {
	t.lock.Lock()
	defer t.lock.Unlock()
	var ret []spvwallet.Txn
	stm := "select tx, value, height, timestamp, watchOnly from txns"
	rows, err := t.db.Query(stm)
	defer rows.Close()
	if err != nil {
		return ret, err
	}
	for rows.Next() {
		var tx []byte
		var value int
		var height int
		var timestamp int
		var watchOnlyInt int
		if err := rows.Scan(&tx, &value, &height, &timestamp, &watchOnlyInt); err != nil {
			continue
		}
		r := bytes.NewReader(tx)
		msgTx := wire.NewMsgTx(1)
		msgTx.BtcDecode(r, 1)

		watchOnly := false
		if watchOnlyInt > 0 {
			if !includeWatchOnly {
				continue
			}
			watchOnly = true
		}

		txn := spvwallet.Txn{msgTx.TxHash().String(), int64(value), int32(height), time.Unix(int64(timestamp), 0), watchOnly, tx}
		ret = append(ret, txn)
	}
	return ret, nil
}

func (t *TxnsDB) Delete(txid *chainhash.Hash) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	_, err := t.db.Exec("delete from txns where txid=?", txid.String())
	if err != nil {
		return err
	}
	return nil
}

func (t *TxnsDB) UpdateHeight(txid chainhash.Hash, height int) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	tx, err := t.db.Begin()
	if err != nil {
		return err
	}
	stmt, err := tx.Prepare("update txns set height=? where txid=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(height, txid.String())
	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}
<commit_msg>Fix bugs in txns db<commit_after>package db

import (
	"bytes"
	"database/sql"
	"github.com/OpenBazaar/spvwallet"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/wire"
	"sync"
	"time"
)

type TxnsDB struct {
	db   *sql.DB
	lock sync.RWMutex
}

func (t *TxnsDB) Put(txn *wire.MsgTx, value, height int, timestamp time.Time, watchOnly bool) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	tx, err := t.db.Begin()
	if err != nil {
		return err
	}
	stmt, err := tx.Prepare("insert or replace into txns(txid, value, height, timestamp, watchOnly, tx) values(?,?,?,?,?,?)")
	if err != nil {
		tx.Rollback()
		return err
	}
	defer stmt.Close()
	watchOnlyInt := 0
	if watchOnly {
		watchOnlyInt = 1
	}
	var buf bytes.Buffer
	txn.Serialize(&buf)
	_, err = stmt.Exec(txn.TxHash().String(), value, height, int(timestamp.Unix()), watchOnlyInt, buf.Bytes())
	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func (t *TxnsDB) Get(txid chainhash.Hash) (*wire.MsgTx, spvwallet.Txn, error) {
	t.lock.Lock()
	defer t.lock.Unlock()
	var txn spvwallet.Txn
	stmt, err := t.db.Prepare("select tx, value, height, timestamp, watchOnly from txns where txid=?")
	if err != nil {
		return nil, txn, err
	}
	defer stmt.Close()
	var ret []byte
	var height int
	var timestamp int
	var value int
	var watchOnlyInt int
	err = stmt.QueryRow(txid.String()).Scan(&ret, &value, &height, &timestamp, &watchOnlyInt)
	if err != nil {
		return nil, txn, err
	}
	watchOnly := false
	if watchOnlyInt > 0 {
		watchOnly = true
	}
	r := bytes.NewReader(ret)
	msgTx := wire.NewMsgTx(1)
	msgTx.BtcDecode(r, 1)
	txn = spvwallet.Txn{
		Txid:      msgTx.TxHash().String(),
		Value:     int64(value),
		Height:    int32(height),
		Timestamp: time.Unix(int64(timestamp), 0),
		WatchOnly: watchOnly,
	}
	return msgTx, txn, nil
}

func (t *TxnsDB) GetAll(includeWatchOnly bool) ([]spvwallet.Txn, error) {
	t.lock.Lock()
	defer t.lock.Unlock()
	var ret []spvwallet.Txn
	stm := "select tx, value, height, timestamp, watchOnly from txns"
	rows, err := t.db.Query(stm)
	if err != nil {
		return ret, err
	}
	defer rows.Close()
	for rows.Next() {
		var tx []byte
		var value int
		var height int
		var timestamp int
		var watchOnlyInt int
		if err := rows.Scan(&tx, &value, &height, &timestamp, &watchOnlyInt); err != nil {
			continue
		}
		r := bytes.NewReader(tx)
		msgTx := wire.NewMsgTx(1)
		msgTx.BtcDecode(r, 1)

		watchOnly := false
		if watchOnlyInt > 0 {
			if !includeWatchOnly {
				continue
			}
			watchOnly = true
		}

		txn := spvwallet.Txn{msgTx.TxHash().String(), int64(value), int32(height), time.Unix(int64(timestamp), 0), watchOnly, tx}
		ret = append(ret, txn)
	}
	return ret, nil
}

func (t *TxnsDB) Delete(txid *chainhash.Hash) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	_, err := t.db.Exec("delete from txns where txid=?", txid.String())
	if err != nil {
		return err
	}
	return nil
}

func (t *TxnsDB) UpdateHeight(txid chainhash.Hash, height int) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	tx, err := t.db.Begin()
	if err != nil {
		return err
	}
	stmt, err := tx.Prepare("update txns set height=? where txid=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(height, txid.String())
	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}
<|endoftext|>


<commit_before>package observer

// ObservableInterface - interface or abstract class defining the operations
// for attaching and de-attaching observers to the client.
// In the GOF book this class/interface is known as Subject.
type ObservableInterface interface {
	Attach(observer *Observer)
	Detach(observer *Observer)
	Notify(value ...interface{})
}

// Observable - concrete Observable class implements ObservableInterface.
// It maintains the state of the object and when a change in the
// state occurs it notifies the attached Observers.
type Observable []Observer

// Attach - adds new observers to observed object
func (observers *Observable) Attach(o Observer) {
	*observers = append(*observers, o)
}

// Detach - removes given observer from observed object
func (observers *Observable) Detach(o Observer) {
	newObservable := []Observer{}
	for _, observer := range *observers {
		if observer != o {
			newObservable = append(newObservable, observer)
		}
	}
	*observers = newObservable
}

// Notify - notifies all observers listening on object
func (observers *Observable) Notify(values ...interface{}) {
	for _, observer := range *observers {
		observer.Update(values)
	}
}
<commit_msg>Fix Observable detaching<commit_after>package observer

// ObservableInterface - interface or abstract class defining the operations
// for attaching and de-attaching observers to the client.
// In the GOF book this class/interface is known as Subject.
type ObservableInterface interface {
	Attach(observer *Observer)
	Detach(observer *Observer)
	Notify(value ...interface{})
}

// Observable - concrete Observable class implements ObservableInterface.
// It maintains the state of the object and when a change in the
// state occurs it notifies the attached Observers.
type Observable []Observer

// Attach - adds new observers to observed object
func (observers *Observable) Attach(o Observer) {
	*observers = append(*observers, o)
}

// Detach - removes given observer from observed object
func (observers *Observable) Detach(o Observer) {
	newObservable := Observable{}
	for _, observer := range *observers {
		if observer != o {
			newObservable.Attach(observer)
		}
	}
	*observers = newObservable
}

// Notify - notifies all observers listening on object
func (observers *Observable) Notify(values ...interface{}) {
	for _, observer := range *observers {
		observer.Update(values)
	}
}
<|endoftext|>


<commit_before>// Package ginoauth2 implements an OAuth2 based authorization
// middleware for the Gin https://github.com/gin-gonic/gin
// webframework.
//
// Example:
//     package main
//     import (
//     	"flag"
//     	"time"
//     	"github.com/gin-gonic/gin"
//     	"github.com/golang/glog"
//     	"github.com/zalando/gin-glog"
//     	"github.com/zalando/gin-oauth2"
//     	"golang.org/x/oauth2"
//     )
//
//     var OAuth2Endpoint = oauth2.Endpoint{
//     	AuthURL:  "https://token.oauth2.corp.com/access_token",
//     	TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//     }
//
//     func UidCheck(tc *TokenContainer, ctx *gin.Context) bool {
//      uid := tc.Scopes["uid"].(string)
//      if uid != "sszuecs" {
//       return false
//      }
//      ctx.Set("uid", uid)
//      return true
//     }
//
//     func main() {
//     	flag.Parse()
//     	router := gin.New()
//     	router.Use(ginglog.Logger(3 * time.Second))
//     	router.Use(gin.Recovery())
//
//     	ginoauth2.VarianceTimer = 300 * time.Millisecond // defaults to 30s
//
//     	public := router.Group("/api")
//     	public.GET("/", func(c *gin.Context) {
//     		c.JSON(200, gin.H{"message": "Hello to public world"})
//     	})
//
//     	private := router.Group("/api/private")
//     	private.Use(ginoauth2.Auth(UidCheck, OAuth2Endpoint))
//     	private.GET("/", func(c *gin.Context) {
//     		c.JSON(200, gin.H{"message": "Hello from private"})
//     	})
//
//     	glog.Info("bootstrapped application")
//     	router.Run(":8081")
//
package ginoauth2

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"golang.org/x/oauth2"
)

// VarianceTimer controls the max runtime of Auth() and AuthChain() middleware
var VarianceTimer time.Duration = 30000 * time.Millisecond

// AuthInfoURL is the URL to get information of your token
var AuthInfoURL string

// Transport to use for client http connections to AuthInfoURL
var Transport = http.Transport{}

// TokenContainer stores all relevant token information
type TokenContainer struct {
	Token     *oauth2.Token
	Scopes    map[string]interface{} // LDAP record vom Benutzer (cn, ..
	GrantType string                 // password, ??
	Realm     string                 // services, employees
}

// AccessCheckFunction is a function that checks if a given token grants
// access.
type AccessCheckFunction func(tc *TokenContainer, ctx *gin.Context) bool

func extractToken(r *http.Request) (*oauth2.Token, error) {
	hdr := r.Header.Get("Authorization")
	if hdr == "" {
		return nil, errors.New("No authorization header")
	}

	th := strings.Split(hdr, " ")
	if len(th) != 2 {
		return nil, errors.New("Incomplete authorization header")
	}

	return &oauth2.Token{AccessToken: th[1], TokenType: th[0]}, nil
}

func RequestAuthInfo(t *oauth2.Token) ([]byte, error) {
	var uv = make(url.Values)
	// uv.Set("realm", o.Realm)
	uv.Set("access_token", t.AccessToken)
	infoURL := AuthInfoURL + "?" + uv.Encode()
	client := &http.Client{Transport: &Transport}
	req, err := http.NewRequest("GET", infoURL, nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func ParseTokenContainer(t *oauth2.Token, data map[string]interface{}) (*TokenContainer, error) {
	tdata := make(map[string]interface{})

	ttype := data["token_type"].(string)
	gtype := data["grant_type"].(string)

	realm := data["realm"].(string)
	exp := data["expires_in"].(float64)
	tok := data["access_token"].(string)
	if ttype != t.TokenType {
		return nil, errors.New("Token type mismatch")
	}
	if tok != t.AccessToken {
		return nil, errors.New("Mismatch between verify request and answer")
	}

	scopes := data["scope"].([]interface{})
	for _, scope := range scopes {
		sscope := scope.(string)
		sval, ok := data[sscope]
		if ok {
			tdata[sscope] = sval
		}
	}

	return &TokenContainer{
		Token: &oauth2.Token{
			AccessToken: tok,
			TokenType:   ttype,
			Expiry:      time.Now().Add(time.Duration(exp) * time.Second),
		},
		Scopes:    tdata,
		Realm:     realm,
		GrantType: gtype,
	}, nil
}

func GetTokenContainer(token *oauth2.Token) (*TokenContainer, error) {
	body, err := RequestAuthInfo(token)
	if err != nil {
		glog.Errorf("[Gin-OAuth] RequestAuthInfo failed caused by: %s", err)
		return nil, err
	}
	// extract AuthInfo
	var data map[string]interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		glog.Errorf("[Gin-OAuth] JSON.Unmarshal failed caused by: %s", err)
		return nil, err
	}
	if _, ok := data["error_description"]; ok {
		var s string
		s = data["error_description"].(string)
		glog.Errorf("[Gin-OAuth] RequestAuthInfo returned an error: %s", s)
		return nil, errors.New(s)
	}
	return ParseTokenContainer(token, data)
}

func getTokenContainer(ctx *gin.Context) (*TokenContainer, bool) {
	var oauthToken *oauth2.Token
	var tc *TokenContainer
	var err error

	if oauthToken, err = extractToken(ctx.Request); err != nil {
		glog.Errorf("[Gin-OAuth] Can not extract oauth2.Token, caused by: %s", err)
		return nil, false
	}
	if !oauthToken.Valid() {
		glog.Infof("[Gin-OAuth] Invalid Token - nil or expired")
		return nil, false
	}

	if tc, err = GetTokenContainer(oauthToken); err != nil {
		glog.Errorf("[Gin-OAuth] Can not extract TokenContainer, caused by: %s", err)
		return nil, false
	}

	return tc, true
}

//
// TokenContainer
//
// Validates that the AccessToken within TokenContainer is not expired and not empty.
func (t *TokenContainer) Valid() bool {
	if t.Token == nil {
		return false
	}
	return t.Token.Valid()
}

// Router middleware that can be used to get an authenticated and authorized service for the whole router group.
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//	private := router.Group("")
//	private.Use(ginoauth2.Auth(ginoatuh2.UidCheck, ginoauth2.endpoints))
//	private.GET("/api/private", func(c *gin.Context) {
//		c.JSON(200, gin.H{"message": "Hello from private"})
//	})
//
func Auth(accessCheckFunction AccessCheckFunction, endpoints oauth2.Endpoint) gin.HandlerFunc {
	return AuthChain(endpoints, accessCheckFunction)
}

// AuthChain is a router middleware that can be used to get an authenticated
// and authorized service for the whole router group. Similar to Auth, but
// takes a chain of AccessCheckFunctions and only fails if all of them fails.
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//	    private := router.Group("")
//      checkChain := []AccessCheckFunction{
//          ginoauth2.UidCheck,
//          ginoauth2.GroupCheck,
//      }
//      private.Use(ginoauth2.AuthChain(checkChain, ginoauth2.endpoints))
//      private.GET("/api/private", func(c *gin.Context) {
//          c.JSON(200, gin.H{"message": "Hello from private"})
//      })
//
func AuthChain(endpoints oauth2.Endpoint, accessCheckFunctions ...AccessCheckFunction) gin.HandlerFunc {
	// init
	AuthInfoURL = endpoints.TokenURL
	// middleware
	return func(ctx *gin.Context) {
		t := time.Now()
		varianceControl := make(chan bool, 1)
		var tokenContainer *TokenContainer

		go func() {
			ok := false
			tokenContainer, ok = getTokenContainer(ctx)
			if !ok {
				// set LOCATION header to auth endpoint such that the user can easily get a new access-token
				ctx.Writer.Header().Set("Location", endpoints.AuthURL)
				ctx.AbortWithError(http.StatusUnauthorized, errors.New("No token in context"))
				varianceControl <- false
				return
			}

			if !tokenContainer.Valid() {
				// set LOCATION header to auth endpoint such that the user can easily get a new access-token
				ctx.Writer.Header().Set("Location", endpoints.AuthURL)
				ctx.AbortWithError(http.StatusUnauthorized, errors.New("Invalid Token"))
				varianceControl <- false
				return
			}

			for i, fn := range accessCheckFunctions {
				if fn(tokenContainer, ctx) {
					varianceControl <- true
					break
				}

				if len(accessCheckFunctions)-1 == i {
					ctx.AbortWithError(http.StatusForbidden, errors.New("Access to the Resource is fobidden"))
					varianceControl <- false
					return
				}
			}
		}()

		select {
		case ok := <-varianceControl:
			if !ok {
				glog.V(2).Infof("[Gin-OAuth] %12v %s access not allowed", time.Since(t), ctx.Request.URL.Path)
				return
			}
		case <-time.After(VarianceTimer):
			ctx.AbortWithError(http.StatusGatewayTimeout, errors.New("Authorization check overtime"))
			glog.V(2).Infof("[Gin-OAuth] %12v %s overtime", time.Since(t), ctx.Request.URL.Path)
			return
		}

		// access allowed
		ctx.Writer.Header().Set("Bearer", tokenContainer.Token.AccessToken)

		glog.V(2).Infof("[Gin-OAuth] %12v %s access allowed", time.Since(t), ctx.Request.URL.Path)
	}
}

// RequestLogger is a middleware that logs all the request and prints
// relevant information.  This can be used for logging all the
// requests that contain important information and are authorized.
// The assumption is that the request to log has a content and an Id
// identifiying who made the request uIdKey string to use as key to
// get the uid from the context contentKey string to use as key to get
// the content to be logged from the context.
//
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//      router.Use(ginoauth2.RequestLogger([]string{"uid"}, "data"))
//
func RequestLogger(keys []string, contentKey string) gin.HandlerFunc {
	return func(c *gin.Context) {
		request := c.Request
		c.Next()
		err := c.Errors
		if request.Method != "GET" && err == nil {
			data, e := c.Get(contentKey)
			if e != false { //key is non existent
				values := make([]string, 0)
				for _, key := range keys {
					val, keyPresent := c.Get(key)
					if keyPresent {
						values = append(values, val.(string))
					}
				}
				glog.Infof("[Gin-OAuth] Request: %+v for %s", data, strings.Join(values, "-"))
			}
		}
	}
}

// vim: ts=4 sw=4 noexpandtab nolist syn=go
<commit_msg>Remove Bearer header from response<commit_after>// Package ginoauth2 implements an OAuth2 based authorization
// middleware for the Gin https://github.com/gin-gonic/gin
// webframework.
//
// Example:
//     package main
//     import (
//     	"flag"
//     	"time"
//     	"github.com/gin-gonic/gin"
//     	"github.com/golang/glog"
//     	"github.com/zalando/gin-glog"
//     	"github.com/zalando/gin-oauth2"
//     	"golang.org/x/oauth2"
//     )
//
//     var OAuth2Endpoint = oauth2.Endpoint{
//     	AuthURL:  "https://token.oauth2.corp.com/access_token",
//     	TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//     }
//
//     func UidCheck(tc *TokenContainer, ctx *gin.Context) bool {
//      uid := tc.Scopes["uid"].(string)
//      if uid != "sszuecs" {
//       return false
//      }
//      ctx.Set("uid", uid)
//      return true
//     }
//
//     func main() {
//     	flag.Parse()
//     	router := gin.New()
//     	router.Use(ginglog.Logger(3 * time.Second))
//     	router.Use(gin.Recovery())
//
//     	ginoauth2.VarianceTimer = 300 * time.Millisecond // defaults to 30s
//
//     	public := router.Group("/api")
//     	public.GET("/", func(c *gin.Context) {
//     		c.JSON(200, gin.H{"message": "Hello to public world"})
//     	})
//
//     	private := router.Group("/api/private")
//     	private.Use(ginoauth2.Auth(UidCheck, OAuth2Endpoint))
//     	private.GET("/", func(c *gin.Context) {
//     		c.JSON(200, gin.H{"message": "Hello from private"})
//     	})
//
//     	glog.Info("bootstrapped application")
//     	router.Run(":8081")
//
package ginoauth2

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"golang.org/x/oauth2"
)

// VarianceTimer controls the max runtime of Auth() and AuthChain() middleware
var VarianceTimer time.Duration = 30000 * time.Millisecond

// AuthInfoURL is the URL to get information of your token
var AuthInfoURL string

// Transport to use for client http connections to AuthInfoURL
var Transport = http.Transport{}

// TokenContainer stores all relevant token information
type TokenContainer struct {
	Token     *oauth2.Token
	Scopes    map[string]interface{} // LDAP record vom Benutzer (cn, ..
	GrantType string                 // password, ??
	Realm     string                 // services, employees
}

// AccessCheckFunction is a function that checks if a given token grants
// access.
type AccessCheckFunction func(tc *TokenContainer, ctx *gin.Context) bool

func extractToken(r *http.Request) (*oauth2.Token, error) {
	hdr := r.Header.Get("Authorization")
	if hdr == "" {
		return nil, errors.New("No authorization header")
	}

	th := strings.Split(hdr, " ")
	if len(th) != 2 {
		return nil, errors.New("Incomplete authorization header")
	}

	return &oauth2.Token{AccessToken: th[1], TokenType: th[0]}, nil
}

func RequestAuthInfo(t *oauth2.Token) ([]byte, error) {
	var uv = make(url.Values)
	// uv.Set("realm", o.Realm)
	uv.Set("access_token", t.AccessToken)
	infoURL := AuthInfoURL + "?" + uv.Encode()
	client := &http.Client{Transport: &Transport}
	req, err := http.NewRequest("GET", infoURL, nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func ParseTokenContainer(t *oauth2.Token, data map[string]interface{}) (*TokenContainer, error) {
	tdata := make(map[string]interface{})

	ttype := data["token_type"].(string)
	gtype := data["grant_type"].(string)

	realm := data["realm"].(string)
	exp := data["expires_in"].(float64)
	tok := data["access_token"].(string)
	if ttype != t.TokenType {
		return nil, errors.New("Token type mismatch")
	}
	if tok != t.AccessToken {
		return nil, errors.New("Mismatch between verify request and answer")
	}

	scopes := data["scope"].([]interface{})
	for _, scope := range scopes {
		sscope := scope.(string)
		sval, ok := data[sscope]
		if ok {
			tdata[sscope] = sval
		}
	}

	return &TokenContainer{
		Token: &oauth2.Token{
			AccessToken: tok,
			TokenType:   ttype,
			Expiry:      time.Now().Add(time.Duration(exp) * time.Second),
		},
		Scopes:    tdata,
		Realm:     realm,
		GrantType: gtype,
	}, nil
}

func GetTokenContainer(token *oauth2.Token) (*TokenContainer, error) {
	body, err := RequestAuthInfo(token)
	if err != nil {
		glog.Errorf("[Gin-OAuth] RequestAuthInfo failed caused by: %s", err)
		return nil, err
	}
	// extract AuthInfo
	var data map[string]interface{}
	err = json.Unmarshal(body, &data)
	if err != nil {
		glog.Errorf("[Gin-OAuth] JSON.Unmarshal failed caused by: %s", err)
		return nil, err
	}
	if _, ok := data["error_description"]; ok {
		var s string
		s = data["error_description"].(string)
		glog.Errorf("[Gin-OAuth] RequestAuthInfo returned an error: %s", s)
		return nil, errors.New(s)
	}
	return ParseTokenContainer(token, data)
}

func getTokenContainer(ctx *gin.Context) (*TokenContainer, bool) {
	var oauthToken *oauth2.Token
	var tc *TokenContainer
	var err error

	if oauthToken, err = extractToken(ctx.Request); err != nil {
		glog.Errorf("[Gin-OAuth] Can not extract oauth2.Token, caused by: %s", err)
		return nil, false
	}
	if !oauthToken.Valid() {
		glog.Infof("[Gin-OAuth] Invalid Token - nil or expired")
		return nil, false
	}

	if tc, err = GetTokenContainer(oauthToken); err != nil {
		glog.Errorf("[Gin-OAuth] Can not extract TokenContainer, caused by: %s", err)
		return nil, false
	}

	return tc, true
}

//
// TokenContainer
//
// Validates that the AccessToken within TokenContainer is not expired and not empty.
func (t *TokenContainer) Valid() bool {
	if t.Token == nil {
		return false
	}
	return t.Token.Valid()
}

// Router middleware that can be used to get an authenticated and authorized service for the whole router group.
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//	private := router.Group("")
//	private.Use(ginoauth2.Auth(ginoatuh2.UidCheck, ginoauth2.endpoints))
//	private.GET("/api/private", func(c *gin.Context) {
//		c.JSON(200, gin.H{"message": "Hello from private"})
//	})
//
func Auth(accessCheckFunction AccessCheckFunction, endpoints oauth2.Endpoint) gin.HandlerFunc {
	return AuthChain(endpoints, accessCheckFunction)
}

// AuthChain is a router middleware that can be used to get an authenticated
// and authorized service for the whole router group. Similar to Auth, but
// takes a chain of AccessCheckFunctions and only fails if all of them fails.
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//	    private := router.Group("")
//      checkChain := []AccessCheckFunction{
//          ginoauth2.UidCheck,
//          ginoauth2.GroupCheck,
//      }
//      private.Use(ginoauth2.AuthChain(checkChain, ginoauth2.endpoints))
//      private.GET("/api/private", func(c *gin.Context) {
//          c.JSON(200, gin.H{"message": "Hello from private"})
//      })
//
func AuthChain(endpoints oauth2.Endpoint, accessCheckFunctions ...AccessCheckFunction) gin.HandlerFunc {
	// init
	AuthInfoURL = endpoints.TokenURL
	// middleware
	return func(ctx *gin.Context) {
		t := time.Now()
		varianceControl := make(chan bool, 1)
		var tokenContainer *TokenContainer

		go func() {
			ok := false
			tokenContainer, ok = getTokenContainer(ctx)
			if !ok {
				// set LOCATION header to auth endpoint such that the user can easily get a new access-token
				ctx.Writer.Header().Set("Location", endpoints.AuthURL)
				ctx.AbortWithError(http.StatusUnauthorized, errors.New("No token in context"))
				varianceControl <- false
				return
			}

			if !tokenContainer.Valid() {
				// set LOCATION header to auth endpoint such that the user can easily get a new access-token
				ctx.Writer.Header().Set("Location", endpoints.AuthURL)
				ctx.AbortWithError(http.StatusUnauthorized, errors.New("Invalid Token"))
				varianceControl <- false
				return
			}

			for i, fn := range accessCheckFunctions {
				if fn(tokenContainer, ctx) {
					varianceControl <- true
					break
				}

				if len(accessCheckFunctions)-1 == i {
					ctx.AbortWithError(http.StatusForbidden, errors.New("Access to the Resource is fobidden"))
					varianceControl <- false
					return
				}
			}
		}()

		select {
		case ok := <-varianceControl:
			if !ok {
				glog.V(2).Infof("[Gin-OAuth] %12v %s access not allowed", time.Since(t), ctx.Request.URL.Path)
				return
			}
		case <-time.After(VarianceTimer):
			ctx.AbortWithError(http.StatusGatewayTimeout, errors.New("Authorization check overtime"))
			glog.V(2).Infof("[Gin-OAuth] %12v %s overtime", time.Since(t), ctx.Request.URL.Path)
			return
		}

		glog.V(2).Infof("[Gin-OAuth] %12v %s access allowed", time.Since(t), ctx.Request.URL.Path)
	}
}

// RequestLogger is a middleware that logs all the request and prints
// relevant information.  This can be used for logging all the
// requests that contain important information and are authorized.
// The assumption is that the request to log has a content and an Id
// identifiying who made the request uIdKey string to use as key to
// get the uid from the context contentKey string to use as key to get
// the content to be logged from the context.
//
// Example:
//
//      var endpoints oauth2.Endpoint = oauth2.Endpoint{
//	        AuthURL:  "https://token.oauth2.corp.com/access_token",
//	        TokenURL: "https://oauth2.corp.com/corp/oauth2/tokeninfo",
//      }
//      var acl []ginoauth2.AccessTuple = []ginoauth2.AccessTuple{{"employee", 1070, "sszuecs"}, {"employee", 1114, "njuettner"}}
//      router := gin.Default()
//      router.Use(ginoauth2.RequestLogger([]string{"uid"}, "data"))
//
func RequestLogger(keys []string, contentKey string) gin.HandlerFunc {
	return func(c *gin.Context) {
		request := c.Request
		c.Next()
		err := c.Errors
		if request.Method != "GET" && err == nil {
			data, e := c.Get(contentKey)
			if e != false { //key is non existent
				values := make([]string, 0)
				for _, key := range keys {
					val, keyPresent := c.Get(key)
					if keyPresent {
						values = append(values, val.(string))
					}
				}
				glog.Infof("[Gin-OAuth] Request: %+v for %s", data, strings.Join(values, "-"))
			}
		}
	}
}

// vim: ts=4 sw=4 noexpandtab nolist syn=go
<|endoftext|>


<commit_before>package allocations

import (
	"fmt"
	"strings"

	consul "github.com/hashicorp/consul/api"
	nomad "github.com/hashicorp/nomad/api"
	consul_helper "github.com/jippi/hashi-ui/backend/consul/helper"
	"github.com/jippi/hashi-ui/backend/structs"
)

const (
	fetchedHealth = "NOMAD_FETCHED_ALLOCATION_HEALTH"
	UnwatchHealth = "NOMAD_UNWATCH_ALLOCATION_HEALTH"
	WatchHealth   = "NOMAD_WATCH_ALLOCATION_HEALTH"
)

type allocationHealthResponse struct {
	ID      string
	Checks  map[string]string
	Count   map[string]int
	Total   int
	Healthy *bool
}

type health struct {
	action       structs.Action
	nomad        *nomad.Client
	consul       *consul.Client
	consulQuery  *consul.QueryOptions
	clientID     string
	allocationID string
	clientName   string
}

func NewHealth(action structs.Action, nomad *nomad.Client, consul *consul.Client, consulQuery *consul.QueryOptions) *health {
	return &health{
		action:      action,
		nomad:       nomad,
		consul:      consul,
		consulQuery: consulQuery,
	}
}

func (w *health) Do() (*structs.Response, error) {
	// find the client name (we assume it matches the consul name)
	if w.clientName == "" {
		info, _, err := w.nomad.Nodes().Info(w.clientID, nil)
		if err != nil {
			return structs.NewErrorResponse(err)
		}
		w.clientName = info.Name
	}

	// lookup health for the consul client
	checks, meta, err := w.consul.Health().Node(w.clientName, w.consulQuery)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	if !consul_helper.QueryChanged(w.consulQuery, meta) {
		return nil, nil
	}

	result := make(map[string]string)
	status := make(map[string]int)
	var healthy *bool
	total := 0

	for _, check := range checks {
		// nomad managed service ids got the following format
		// "_nomad-executor-${ALLOC_ID}-*",
		if !strings.Contains(check.ServiceID, "_nomad-executor-"+w.allocationID) {
			continue
		}

		total = total + 1

		result[check.Name] = check.Status

		if _, ok := status[check.Status]; !ok {
			status[check.Status] = 1
		} else {
			status[check.Status] = status[check.Status] + 1
		}

		if healthy != nil && *healthy == false {
			continue
		}

		if check.Status == "passing" {
			healthy = boolToPtr(true)
		} else {
			healthy = boolToPtr(false)
		}
	}

	response := &allocationHealthResponse{
		ID:      w.allocationID,
		Checks:  result,
		Count:   status,
		Total:   total,
		Healthy: healthy,
	}

	return structs.NewResponseWithIndex(fetchedHealth, response, meta.LastIndex)
}

func (w *health) Key() string {
	w.parse()

	return fmt.Sprintf("/allocation/%s/health?client=%s", w.allocationID, w.clientID)
}

func (w *health) IsMutable() bool {
	return false
}

func (w *health) BackendType() string {
	return "nomad"
}

func (w *health) parse() {
	params := w.action.Payload.(map[string]interface{})
	w.allocationID = params["id"].(string)
	w.clientID = params["client"].(string)
}

func boolToPtr(b bool) *bool {
	return &b
}
<commit_msg>Fixes #353<commit_after>package allocations

import (
	"fmt"
	"strings"

	consul "github.com/hashicorp/consul/api"
	nomad "github.com/hashicorp/nomad/api"
	consul_helper "github.com/jippi/hashi-ui/backend/consul/helper"
	"github.com/jippi/hashi-ui/backend/structs"
)

const (
	fetchedHealth = "NOMAD_FETCHED_ALLOCATION_HEALTH"
	UnwatchHealth = "NOMAD_UNWATCH_ALLOCATION_HEALTH"
	WatchHealth   = "NOMAD_WATCH_ALLOCATION_HEALTH"
)

type allocationHealthResponse struct {
	ID      string
	Checks  map[string]string
	Count   map[string]int
	Total   int
	Healthy *bool
}

type health struct {
	action       structs.Action
	nomad        *nomad.Client
	consul       *consul.Client
	consulQuery  *consul.QueryOptions
	clientID     string
	allocationID string
	clientName   string
}

func NewHealth(action structs.Action, nomad *nomad.Client, consul *consul.Client, consulQuery *consul.QueryOptions) *health {
	return &health{
		action:      action,
		nomad:       nomad,
		consul:      consul,
		consulQuery: consulQuery,
	}
}

func (w *health) Do() (*structs.Response, error) {
	// find the client name (we assume it matches the consul name)
	if w.clientName == "" {
		node, _, err := w.nomad.Nodes().Info(w.clientID, nil)
		if err != nil {
			return structs.NewErrorResponse(err)
		}
		w.clientName = node.Name

		// make sure to query the right Consul DC
		datacenter, ok := node.Attributes["consul.datacenter"]
		if !ok {
			return structs.NewErrorResponse("node is not linked to Consul")
		}

		w.consulQuery.Datacenter = datacenter
	}

	// lookup health for the consul client
	checks, meta, err := w.consul.Health().Node(w.clientName, w.consulQuery)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	if !consul_helper.QueryChanged(w.consulQuery, meta) {
		return nil, nil
	}

	result := make(map[string]string)
	status := make(map[string]int)
	var healthy *bool
	total := 0

	for _, check := range checks {
		// nomad managed service ids got the following format
		// "_nomad-executor-${ALLOC_ID}-*",
		if !strings.Contains(check.ServiceID, "_nomad-executor-"+w.allocationID) {
			continue
		}

		total = total + 1

		result[check.Name] = check.Status

		if _, ok := status[check.Status]; !ok {
			status[check.Status] = 1
		} else {
			status[check.Status] = status[check.Status] + 1
		}

		if healthy != nil && *healthy == false {
			continue
		}

		if check.Status == "passing" {
			healthy = boolToPtr(true)
		} else {
			healthy = boolToPtr(false)
		}
	}

	response := &allocationHealthResponse{
		ID:      w.allocationID,
		Checks:  result,
		Count:   status,
		Total:   total,
		Healthy: healthy,
	}

	return structs.NewResponseWithIndex(fetchedHealth, response, meta.LastIndex)
}

func (w *health) Key() string {
	w.parse()

	return fmt.Sprintf("/allocation/%s/health?client=%s", w.allocationID, w.clientID)
}

func (w *health) IsMutable() bool {
	return false
}

func (w *health) BackendType() string {
	return "nomad"
}

func (w *health) parse() {
	params := w.action.Payload.(map[string]interface{})
	w.allocationID = params["id"].(string)
	w.clientID = params["client"].(string)
}

func boolToPtr(b bool) *bool {
	return &b
}
<|endoftext|>


<commit_before>package rest

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/flavioribeiro/gonfig"
)

// HTTPError is a helper to return errors on handlers
func HTTPError(w http.ResponseWriter, httpErr int, msg string, err error) {
	w.WriteHeader(httpErr)
	fmt.Fprintf(w, `{"error": "%s: %s"}`, msg, err.Error())
}

// JSONHandler adds json headers
func JSONHandler(actual http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		actual.ServeHTTP(w, r)
	})
}

// GetLogOutput returns the output we want to use
// for http requests log
func GetLogOutput() io.Writer {
	var logOutput io.Writer
	currentDir, _ := os.Getwd()
	cfg, _ := gonfig.FromJsonFile(currentDir + "/config.json")
	logfile, _ := cfg.GetString("LOGFILE", "")
	if logfile == "" {
		logOutput = ioutil.Discard
	} else {
		logOutput = os.Stdout
	}

	return logOutput
}
<commit_msg>rest/helpers: write http logs to file set on config.json (close #31)<commit_after>package rest

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/flavioribeiro/gonfig"
)

// HTTPError is a helper to return errors on handlers
func HTTPError(w http.ResponseWriter, httpErr int, msg string, err error) {
	w.WriteHeader(httpErr)
	fmt.Fprintf(w, `{"error": "%s: %s"}`, msg, err.Error())
}

// JSONHandler adds json headers
func JSONHandler(actual http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		actual.ServeHTTP(w, r)
	})
}

// GetLogOutput returns the output we want to use
// for http requests log
func GetLogOutput() io.Writer {
	var logOutput io.Writer
	currentDir, _ := os.Getwd()
	cfg, _ := gonfig.FromJsonFile(currentDir + "/config.json")
	logfile, _ := cfg.GetString("LOGFILE", "")
	if logfile == "" {
		logOutput = ioutil.Discard
	} else {
		fmt.Println("Logging requests on", logfile)
		f, err := os.Create(logfile)
		if err != nil {
			panic(err)
		}

		logOutput = f
	}

	return logOutput
}
<|endoftext|>


<commit_before>package rest

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"github.com/rs/rest-layer/resource"
	"github.com/rs/rest-layer/schema/query"
)

// RouteMatch represent a REST request's matched resource with the method to
// apply and its parameters.
type RouteMatch struct {
	// Method is the HTTP method used on the resource.
	Method string
	// ResourcePath is the list of intermediate resources followed by the
	// targeted resource. Each intermediate resource much match all the previous
	// resource components of this path and newly created resources will have
	// their corresponding fields filled with resource path information
	// (resource.field => resource.value).
	ResourcePath ResourcePath
	// Params is the list of client provided parameters (thru query-string or alias).
	Params url.Values
}

type key int

const (
	routeKey key = iota
	indexKey
)

var routePool = sync.Pool{
	New: func() interface{} {
		return &RouteMatch{
			ResourcePath: make(ResourcePath, 0, 2),
		}
	},
}

var errResourceNotFound = &Error{http.StatusNotFound, "Resource Not Found", nil}

func contextWithRoute(ctx context.Context, route *RouteMatch) context.Context {
	return context.WithValue(ctx, routeKey, route)
}

func contextWithIndex(ctx context.Context, index resource.Index) context.Context {
	return context.WithValue(ctx, indexKey, index)
}

// RouteFromContext extracts the matched route from the given net/context.
func RouteFromContext(ctx context.Context) (*RouteMatch, bool) {
	route, ok := ctx.Value(routeKey).(*RouteMatch)
	return route, ok
}

// IndexFromContext extracts the router from the given net/context.
func IndexFromContext(ctx context.Context) (resource.Index, bool) {
	index, ok := ctx.Value(indexKey).(resource.Index)
	return index, ok
}

// FindRoute returns the REST route for the given request.
func FindRoute(index resource.Index, req *http.Request) (*RouteMatch, error) {
	route := routePool.Get().(*RouteMatch)
	route.Method = req.Method
	if req.URL.RawQuery != "" {
		route.Params = req.URL.Query()
	}
	err := findRoute(req.URL.Path, index, route)
	if err != nil {
		route.Release()
		route = nil
	}
	return route, err
}

// findRoute recursively route a (sub)resource request.
func findRoute(path string, index resource.Index, route *RouteMatch) error {
	// Extract the first component of the path.
	var name string
	name, path = nextPathComponent(path)

	resourcePath := name
	if prefix := route.ResourcePath.Path(); prefix != "" {
		resourcePath = prefix + "." + name
	}

	if rsrc, found := index.GetResource(resourcePath, nil); found {
		// First component must match a resource.
		if len(path) >= 1 {
			// If there are some components left, the path targets an item or an alias.

			// Shift the item id from the path components.
			var id string
			id, path = nextPathComponent(path)

			// Handle sub-resources (/resource1/id1/resource2/id2).
			if len(path) >= 1 {
				subPathComp, _ := nextPathComponent(path)
				subResourcePath := resourcePath + "." + subPathComp
				if subResource, found := index.GetResource(subResourcePath, nil); found {
					// Append the intermediate resource path.
					if err := route.ResourcePath.append(rsrc, subResource.ParentField(), id, name); err != nil {
						return err
					}
					// Recurse to match the sub-path.
					if err := findRoute(path, index, route); err != nil {
						return err
					}
				} else {
					route.ResourcePath.clear()
					return errResourceNotFound
				}
				return nil
			}

			// Handle aliases (/resource/alias or /resource1/id1/resource2/alias).
			if alias, found := rsrc.GetAlias(id); found {
				// Apply aliases query to the request.
				for key, values := range alias {
					for _, value := range values {
						route.Params.Add(key, value)
					}
				}
			} else {
				// Set the id route field.
				return route.ResourcePath.append(rsrc, "id", id, name)
			}
		}
		// Set the collection resource.
		return route.ResourcePath.append(rsrc, "", nil, name)
	}
	route.ResourcePath.clear()
	return errResourceNotFound
}

// nextPathComponent returns the next path component and the remaining path
//
// Input: /comp1/comp2/comp3
// Output: comp1, comp2/comp3
func nextPathComponent(path string) (string, string) {
	// Remove leading slash if any
	for len(path) > 0 && path[0] == '/' {
		path = path[1:]
	}

	comp := path
	if i := strings.IndexByte(path, '/'); i != -1 {
		comp = path[:i]
		path = path[i+1:]
	} else {
		path = path[0:0]
	}
	return comp, path
}

// Resource returns the last resource path's resource.
func (r *RouteMatch) Resource() *resource.Resource {
	l := len(r.ResourcePath)
	if l == 0 {
		return nil
	}
	return (r.ResourcePath)[l-1].Resource
}

// ResourceID returns the last resource path's resource id value if any.
//
// If this method returns a non nil value, it means the route is an item request,
// otherwise it's a collection request.
func (r *RouteMatch) ResourceID() interface{} {
	l := len(r.ResourcePath)
	if l == 0 {
		return nil
	}
	return (r.ResourcePath)[l-1].Value
}

// Query builds a query object from the matched route
func (r *RouteMatch) Query() (*query.Query, *Error) {
	q := &query.Query{}
	// Append route fields to the query
	for _, rp := range r.ResourcePath {
		if rp.Value != nil {
			q.Predicate = append(q.Predicate, query.Equal{Field: rp.Field, Value: rp.Value})
		}
	}
	rsc := r.Resource()
	if rsc == nil {
		return nil, &Error{500, "missing resource", nil}
	}
	// Parse query string params.
	switch r.Method {
	case "HEAD":
		// request empty response
		q.Window = &query.Window{Offset: 0, Limit: 0}
	case "GET":
		limit := -1
		if l, found, err := getUintParam(r.Params, "limit"); found {
			if err != nil {
				return nil, &Error{422, err.Error(), nil}
			}
			limit = l
		} else if l := rsc.Conf().PaginationDefaultLimit; l > 0 {
			limit = l
		}
		skip := 0
		if s, found, err := getUintParam(r.Params, "skip"); found {
			if err != nil {
				return nil, &Error{422, err.Error(), nil}
			}
			skip = s
		}
		page := 1
		if p, found, err := getUintParam(r.Params, "page"); found {
			if err != nil {
				return nil, &Error{422, err.Error(), nil}
			}
			page = p
		}
		if page > 1 && limit <= 0 {
			return nil, &Error{422, "Cannot use `page' parameter with no `limit' parameter on a resource with no default pagination size", nil}
		}
		q.Window = query.Page(page, limit, skip)
	}
	if sort := r.Params.Get("sort"); sort != "" {
		s, err := query.ParseSort(sort)
		if err == nil {
			err = s.Validate(rsc.Validator())
		}
		if err != nil {
			return nil, &Error{422, fmt.Sprintf("Invalid `sort` parameter: %s", err), nil}
		}
		q.Sort = s
	}
	if filters, found := r.Params["filter"]; found {
		// If several filter parameters are present, merge them using $and
		for _, filter := range filters {
			p, err := query.ParsePredicate(filter)
			if err == nil {
				err = p.Validate(rsc.Validator())
			}
			if err != nil {
				return nil, &Error{422, fmt.Sprintf("Invalid `filter` parameter: %s", err), nil}
			}
			q.Predicate = append(q.Predicate, p...)
		}
	}
	if fields := r.Params.Get("fields"); fields != "" {
		p, err := query.ParseProjection(fields)
		if err == nil {
			err = p.Validate(rsc.Validator())
		}
		if err != nil {
			return nil, &Error{422, fmt.Sprintf("Invalid `fields` parameter: %s", err), nil}
		}
		q.Projection = p
	}
	return q, nil
}

// Release releases the route so it can be reused.
func (r *RouteMatch) Release() {
	r.Params = nil
	r.Method = ""
	r.ResourcePath.clear()
	routePool.Put(r)
}
<commit_msg>routing: HEAD must behave just like GET, except returning the body (#161)<commit_after>package rest

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"github.com/rs/rest-layer/resource"
	"github.com/rs/rest-layer/schema/query"
)

// RouteMatch represent a REST request's matched resource with the method to
// apply and its parameters.
type RouteMatch struct {
	// Method is the HTTP method used on the resource.
	Method string
	// ResourcePath is the list of intermediate resources followed by the
	// targeted resource. Each intermediate resource much match all the previous
	// resource components of this path and newly created resources will have
	// their corresponding fields filled with resource path information
	// (resource.field => resource.value).
	ResourcePath ResourcePath
	// Params is the list of client provided parameters (thru query-string or alias).
	Params url.Values
}

type key int

const (
	routeKey key = iota
	indexKey
)

var routePool = sync.Pool{
	New: func() interface{} {
		return &RouteMatch{
			ResourcePath: make(ResourcePath, 0, 2),
		}
	},
}

var errResourceNotFound = &Error{http.StatusNotFound, "Resource Not Found", nil}

func contextWithRoute(ctx context.Context, route *RouteMatch) context.Context {
	return context.WithValue(ctx, routeKey, route)
}

func contextWithIndex(ctx context.Context, index resource.Index) context.Context {
	return context.WithValue(ctx, indexKey, index)
}

// RouteFromContext extracts the matched route from the given net/context.
func RouteFromContext(ctx context.Context) (*RouteMatch, bool) {
	route, ok := ctx.Value(routeKey).(*RouteMatch)
	return route, ok
}

// IndexFromContext extracts the router from the given net/context.
func IndexFromContext(ctx context.Context) (resource.Index, bool) {
	index, ok := ctx.Value(indexKey).(resource.Index)
	return index, ok
}

// FindRoute returns the REST route for the given request.
func FindRoute(index resource.Index, req *http.Request) (*RouteMatch, error) {
	route := routePool.Get().(*RouteMatch)
	route.Method = req.Method
	if req.URL.RawQuery != "" {
		route.Params = req.URL.Query()
	}
	err := findRoute(req.URL.Path, index, route)
	if err != nil {
		route.Release()
		route = nil
	}
	return route, err
}

// findRoute recursively route a (sub)resource request.
func findRoute(path string, index resource.Index, route *RouteMatch) error {
	// Extract the first component of the path.
	var name string
	name, path = nextPathComponent(path)

	resourcePath := name
	if prefix := route.ResourcePath.Path(); prefix != "" {
		resourcePath = prefix + "." + name
	}

	if rsrc, found := index.GetResource(resourcePath, nil); found {
		// First component must match a resource.
		if len(path) >= 1 {
			// If there are some components left, the path targets an item or an alias.

			// Shift the item id from the path components.
			var id string
			id, path = nextPathComponent(path)

			// Handle sub-resources (/resource1/id1/resource2/id2).
			if len(path) >= 1 {
				subPathComp, _ := nextPathComponent(path)
				subResourcePath := resourcePath + "." + subPathComp
				if subResource, found := index.GetResource(subResourcePath, nil); found {
					// Append the intermediate resource path.
					if err := route.ResourcePath.append(rsrc, subResource.ParentField(), id, name); err != nil {
						return err
					}
					// Recurse to match the sub-path.
					if err := findRoute(path, index, route); err != nil {
						return err
					}
				} else {
					route.ResourcePath.clear()
					return errResourceNotFound
				}
				return nil
			}

			// Handle aliases (/resource/alias or /resource1/id1/resource2/alias).
			if alias, found := rsrc.GetAlias(id); found {
				// Apply aliases query to the request.
				for key, values := range alias {
					for _, value := range values {
						route.Params.Add(key, value)
					}
				}
			} else {
				// Set the id route field.
				return route.ResourcePath.append(rsrc, "id", id, name)
			}
		}
		// Set the collection resource.
		return route.ResourcePath.append(rsrc, "", nil, name)
	}
	route.ResourcePath.clear()
	return errResourceNotFound
}

// nextPathComponent returns the next path component and the remaining path
//
// Input: /comp1/comp2/comp3
// Output: comp1, comp2/comp3
func nextPathComponent(path string) (string, string) {
	// Remove leading slash if any
	for len(path) > 0 && path[0] == '/' {
		path = path[1:]
	}

	comp := path
	if i := strings.IndexByte(path, '/'); i != -1 {
		comp = path[:i]
		path = path[i+1:]
	} else {
		path = path[0:0]
	}
	return comp, path
}

// Resource returns the last resource path's resource.
func (r *RouteMatch) Resource() *resource.Resource {
	l := len(r.ResourcePath)
	if l == 0 {
		return nil
	}
	return (r.ResourcePath)[l-1].Resource
}

// ResourceID returns the last resource path's resource id value if any.
//
// If this method returns a non nil value, it means the route is an item request,
// otherwise it's a collection request.
func (r *RouteMatch) ResourceID() interface{} {
	l := len(r.ResourcePath)
	if l == 0 {
		return nil
	}
	return (r.ResourcePath)[l-1].Value
}

// Query builds a query object from the matched route
func (r *RouteMatch) Query() (*query.Query, *Error) {
	q := &query.Query{}
	// Append route fields to the query
	for _, rp := range r.ResourcePath {
		if rp.Value != nil {
			q.Predicate = append(q.Predicate, query.Equal{Field: rp.Field, Value: rp.Value})
		}
	}
	rsc := r.Resource()
	if rsc == nil {
		return nil, &Error{500, "missing resource", nil}
	}
	// Parse query string params.
	switch r.Method {
	case "HEAD", "GET":
		limit := -1
		if l, found, err := getUintParam(r.Params, "limit"); found {
			if err != nil {
				return nil, &Error{422, err.Error(), nil}
			}
			limit = l
		} else if l := rsc.Conf().PaginationDefaultLimit; l > 0 {
			limit = l
		}
		skip := 0
		if s, found, err := getUintParam(r.Params, "skip"); found {
			if err != nil {
				return nil, &Error{422, err.Error(), nil}
			}
			skip = s
		}
		page := 1
		if p, found, err := getUintParam(r.Params, "page"); found {
			if err != nil {
				return nil, &Error{422, err.Error(), nil}
			}
			page = p
		}
		if page > 1 && limit <= 0 {
			return nil, &Error{422, "Cannot use `page' parameter with no `limit' parameter on a resource with no default pagination size", nil}
		}
		q.Window = query.Page(page, limit, skip)
	}
	if sort := r.Params.Get("sort"); sort != "" {
		s, err := query.ParseSort(sort)
		if err == nil {
			err = s.Validate(rsc.Validator())
		}
		if err != nil {
			return nil, &Error{422, fmt.Sprintf("Invalid `sort` parameter: %s", err), nil}
		}
		q.Sort = s
	}
	if filters, found := r.Params["filter"]; found {
		// If several filter parameters are present, merge them using $and
		for _, filter := range filters {
			p, err := query.ParsePredicate(filter)
			if err == nil {
				err = p.Validate(rsc.Validator())
			}
			if err != nil {
				return nil, &Error{422, fmt.Sprintf("Invalid `filter` parameter: %s", err), nil}
			}
			q.Predicate = append(q.Predicate, p...)
		}
	}
	if fields := r.Params.Get("fields"); fields != "" {
		p, err := query.ParseProjection(fields)
		if err == nil {
			err = p.Validate(rsc.Validator())
		}
		if err != nil {
			return nil, &Error{422, fmt.Sprintf("Invalid `fields` parameter: %s", err), nil}
		}
		q.Projection = p
	}
	return q, nil
}

// Release releases the route so it can be reused.
func (r *RouteMatch) Release() {
	r.Params = nil
	r.Method = ""
	r.ResourcePath.clear()
	routePool.Put(r)
}
<|endoftext|>


<commit_before>package janus

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	statsd "gopkg.in/alexcesaro/statsd.v2"
)

type transport struct {
	http.RoundTripper
	context      *gin.Context
	statsdClient *statsd.Client
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	timing := t.statsdClient.NewTiming()
	resp, err = t.RoundTripper.RoundTrip(req)
	timing.Send(getStatsdMetricName(req))

	if resp.StatusCode >= 400 {
		t.statsdClient.Increment("error_request")
	} else {
		t.statsdClient.Increment("success_request")

		//This is useful for the middlewares
		var bodyBytes []byte

		if resp.Body != nil {
			defer resp.Body.Close()
			bodyBytes, _ = ioutil.ReadAll(resp.Body)
		}

		// Restore the io.ReadCloser to its original state
		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

		// Use the content
		log.WithFields(log.Fields{
			"req":  req,
			"resp": resp,
		}).Info("Setting body")

		t.context.Set("body", bodyBytes)
	}

	return resp, err
}

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 routes
// 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.
// NewSingleHostReverseProxy does not rewrite the Host header.
// To rewrite Host headers, use ReverseProxy directly with a custom
// Director policy.
func NewSingleHostReverseProxy(proxy Proxy, transport http.RoundTripper) *httputil.ReverseProxy {
	target, _ := url.Parse(proxy.TargetURL)
	targetQuery := target.RawQuery

	director := func(req *http.Request) {
		listenPath := strings.Replace(proxy.ListenPath, "/*randomName", "", -1)
		path := singleJoiningSlash(target.Path, listenPath)

		if proxy.StripListenPath {
			log.Debugf("Stripping: %s", req.URL.Path)
			hasSuffix := strings.HasSuffix(target.Path, "/")
			path = strings.Replace(path, listenPath, "", 1)
			if hasSuffix {
				path += "/"
			}
		}

		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = path
		// This is very important to avoid problems with ssl verification for the HOST header
		req.Host = target.Host

		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &httputil.ReverseProxy{Director: director, Transport: transport}
}

// Returns metric name for StatsD in "<request method>.<request path>" format
func getStatsdMetricName(req *http.Request) string {
	return fmt.Sprintf(
		"%s.%s",
		strings.ToLower(req.Method),
		strings.Replace(
			// Double underscores
			strings.Replace(req.URL.Path, "_", "__", -1),
			// and replace dots with single underscore
			".",
			"_",
			-1))
}
<commit_msg>Better handling of slashes<commit_after>package janus

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	statsd "gopkg.in/alexcesaro/statsd.v2"
)

type transport struct {
	http.RoundTripper
	context      *gin.Context
	statsdClient *statsd.Client
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	timing := t.statsdClient.NewTiming()
	resp, err = t.RoundTripper.RoundTrip(req)
	timing.Send(getStatsdMetricName(req))

	if resp.StatusCode >= 400 {
		t.statsdClient.Increment("error_request")
	} else {
		t.statsdClient.Increment("success_request")

		//This is useful for the middlewares
		var bodyBytes []byte

		if resp.Body != nil {
			defer resp.Body.Close()
			bodyBytes, _ = ioutil.ReadAll(resp.Body)
		}

		// Restore the io.ReadCloser to its original state
		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

		// Use the content
		log.WithFields(log.Fields{
			"req":  req,
			"resp": resp,
		}).Info("Setting body")

		t.context.Set("body", bodyBytes)
	}

	return resp, err
}

// NewSingleHostReverseProxy returns a new ReverseProxy that routes
// 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.
// NewSingleHostReverseProxy does not rewrite the Host header.
// To rewrite Host headers, use ReverseProxy directly with a custom
// Director policy.
func NewSingleHostReverseProxy(proxy Proxy, transport http.RoundTripper) *httputil.ReverseProxy {
	target, _ := url.Parse(proxy.TargetURL)
	targetQuery := target.RawQuery

	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		path := target.Path

		if proxy.StripListenPath {
			path = singleJoiningSlash(target.Path, req.URL.Path)

			listenPath := extractListenPath(proxy.ListenPath)
			log.Debug("Stripping: ", listenPath)
			path = strings.Replace(path, listenPath, "", 1)

			log.Debug("Upstream Path is: ", path)

			if strings.HasSuffix(target.Path, "/") {
				// path = path + "/"
			} else {
				path = path[:len(path)-1]
			}
		}

		req.URL.Path = path

		if !proxy.PreserveHostHeader {
			// This is very important to avoid problems with ssl verification for the HOST header
			req.Host = target.Host
		}
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &httputil.ReverseProxy{Director: director, Transport: transport}
}

func extractListenPath(listenPath string) string {
	var reg = regexp.MustCompile(`(\/\*.+)`)
	return reg.ReplaceAllString(listenPath, "")
}

func cleanSlashes(a string) string {
	endSlash := strings.HasSuffix(a, "//")
	startSlash := strings.HasPrefix(a, "//")

	if startSlash {
		a = "/" + strings.TrimPrefix(a, "//")
	}

	if endSlash {
		a = strings.TrimSuffix(a, "//") + "/"
	}

	return a
}

func singleJoiningSlash(a, b string) string {
	a = cleanSlashes(a)
	b = cleanSlashes(b)

	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")

	switch {
	case aslash && bslash:
		log.Debug(a + b)
		return a + b[1:]
	case !aslash && !bslash:
		if len(b) > 0 {
			log.Debug(a + b)
			return a + "/" + b
		}

		log.Debug(a + b)
		return a
	}

	log.Debug(a + b)
	return a + b
}

// Returns metric name for StatsD in "<request method>.<request path>" format
func getStatsdMetricName(req *http.Request) string {
	return fmt.Sprintf(
		"%s.%s",
		strings.ToLower(req.Method),
		strings.Replace(
			// Double underscores
			strings.Replace(req.URL.Path, "_", "__", -1),
			// and replace dots with single underscore
			".",
			"_",
			-1))
}
<|endoftext|>


<commit_before>package dir

import (
	"io/ioutil"
	"log"
	"os"
)

func Ls(dir string) []string {
	files, err := ioutil.ReadDir(dir)
	var filenames []string

	if err != nil {
		log.Fatalf("%v", err)
		os.Exit(1)
	}

	for _, f := range files {
		filenames = append(filenames, f.Name())
	}

	return filenames
}

func CreateDir(dirname string) {
	err := os.Mkdir(dirname, os.ModePerm)

	if err != nil {
		log.Fatalf("%v", err)
		os.Exit(1)
	}
}
<commit_msg>Add HasFile function to dir<commit_after>package dir

import (
	"io/ioutil"
	"log"
	"os"
)

func Ls(dir string) []string {
	files, err := ioutil.ReadDir(dir)
	var filenames []string

	if err != nil {
		log.Fatalf("%v", err)
		os.Exit(1)
	}

	for _, f := range files {
		filenames = append(filenames, f.Name())
	}

	return filenames
}

func CreateDir(dirname string) {
	err := os.Mkdir(dirname, os.ModePerm)

	if err != nil {
		log.Fatalf("%v", err)
		os.Exit(1)
	}
}

func HasFile(dirname, filename string) bool {
	files := Ls(dirname)

	for _, file := range files {
		if file == filename {
			return true
		}
	}

	return false
}
<|endoftext|>