<commit_before>// Copyright 2015 realglobe, 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 main

import (
	"flag"
	"fmt"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type parameters struct {
	// 画面ログ。
	consLv level.Level
	// 追加ログ。
	logType string
	logLv   level.Level
	// ファイルログ。
	logPath string
	logSize int64
	logNum  int
	// fluentd ログ。
	logAddr string
	logTag  string

	// ソケット。
	socType string
	// UNIX ソケット。
	socPath string
	// TCP ソケット。
	socPort int
	// プロトコル。
	protType string

	// URI
	pathOk    string
	pathStart string
	pathSel   string
	pathIdp   string
	// UI 用 HTML を提供する URI。
	pathUi    string
	pathSelUi string
	// UI 用 HTML を置くディレクトリパス。
	uiDir string

	tmplErr string

	// セッション。
	sessLabel    string
	sessLen      int
	sessExpIn    time.Duration
	sessRefDelay time.Duration
	sessDbExpIn  time.Duration
	// チケット。
	ticLen   int
	ticExpIn time.Duration

	// バックエンドの指定。

	// redis
	redTimeout   time.Duration
	redPoolSize  int
	redPoolExpIn time.Duration
	// mongodb
	monTimeout time.Duration

	// web データ DB。
	webDbType  string
	webDbAddr  string
	webDbTag   string
	webDbExpIn time.Duration

	// IdP 情報 DB。
	idpDbType string
	idpDbAddr string
	idpDbTag  string
	idpDbTag2 string

	// TA 情報 DB。
	taDbType string
	taDbAddr string
	taDbTag  string
	taDbTag2 string

	// セッション DB。
	sessDbType string
	sessDbAddr string
	sessDbTag  string

	// その他のオプション。

	// Set-Cookie の Path。
	cookPath string
	// Set-Cookie を Secure にするか。
	cookSec bool

	debug bool
}

func parseParameters(args ...string) (param *parameters, err error) {

	const label = "edo-idp-selector"

	flags := flag.NewFlagSet(label+" parameters", flag.ExitOnError)
	flags.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:")
		fmt.Fprintln(os.Stderr, "  "+args[0]+" [{FLAG}...]")
		fmt.Fprintln(os.Stderr, "FLAG:")
		flags.PrintDefaults()
	}

	param = &parameters{}

	flags.Var(level.Var(&param.consLv, level.INFO), "consLv", "Console log level")
	flags.StringVar(&param.logType, "logType", "", "Extra log: Type")
	flags.Var(level.Var(&param.logLv, level.ALL), "logLv", "Extra log: Level")
	flags.StringVar(&param.logPath, "logPath", filepath.Join(filepath.Dir(os.Args[0]), "log", label+".log"), "Extra log: File path")
	flags.Int64Var(&param.logSize, "logSize", 10*(1<<20) /* 10 MB */, "Extra log: File size limit")
	flags.IntVar(&param.logNum, "logNum", 10, "Extra log: File number limit")
	flags.StringVar(&param.logAddr, "logAddr", "localhost:24224", "Extra log: Fluentd address")
	flags.StringVar(&param.logTag, "logTag", label, "Extra log: Fluentd tag")

	flags.StringVar(&param.socType, "socType", "tcp", "Socket type")
	flags.StringVar(&param.socPath, "socPath", filepath.Join(filepath.Dir(os.Args[0]), "run", label+".soc"), "Unix socket path")
	flags.IntVar(&param.socPort, "socPort", 1603, "TCP socket port")
	flags.StringVar(&param.protType, "protType", "http", "Protocol type")

	flags.StringVar(&param.pathOk, "pathOk", "/ok", "OK URI")
	flags.StringVar(&param.pathStart, "pathStart", "/", "Start URI")
	flags.StringVar(&param.pathSel, "pathSel", "/select", "Account select URI")
	flags.StringVar(&param.pathIdp, "pathIdp", "/api/info/issuer", "ID provider info URI")
	flags.StringVar(&param.pathUi, "pathUi", "/ui", "UI URI")
	flags.StringVar(&param.pathSelUi, "pathSelUi", "/ui/select.html", "Account selection UI URI")
	flags.StringVar(&param.uiDir, "uiDir", "", "UI file directory")

	flags.StringVar(&param.tmplErr, "tmplErr", "", "Error UI template")

	flags.StringVar(&param.sessLabel, "sessLabel", "Idp-Selector", "Session ID label")
	flags.IntVar(&param.sessLen, "sessLen", 30, "Session ID length")
	flags.DurationVar(&param.sessExpIn, "sessExpIn", 7*24*time.Hour, "Session expiration duration")
	flags.DurationVar(&param.sessRefDelay, "sessRefDelay", 24*time.Hour, "Session refresh delay")
	flags.DurationVar(&param.sessDbExpIn, "sessDbExpIn", 14*24*time.Hour, "Session keep duration")
	flags.IntVar(&param.ticLen, "ticLen", 10, "Ticket length")
	flags.DurationVar(&param.ticExpIn, "ticExpIn", 30*time.Minute, "Ticket expiration duration")

	flags.DurationVar(&param.redTimeout, "redTimeout", 30*time.Second, "redis timeout duration")
	flags.IntVar(&param.redPoolSize, "redPoolSize", 10, "redis pool size")
	flags.DurationVar(&param.redPoolExpIn, "redPoolExpIn", time.Minute, "redis connection keep duration")
	flags.DurationVar(&param.monTimeout, "monTimeout", 30*time.Second, "mongodb timeout duration")

	flags.StringVar(&param.webDbType, "webDbType", "redis", "Web data DB type")
	flags.StringVar(&param.webDbAddr, "webDbAddr", "localhost:6379", "Web data DB address")
	flags.StringVar(&param.webDbTag, "webDbTag", "web", "Web data DB tag")
	flags.DurationVar(&param.webDbExpIn, "webDbExpIn", 7*24*time.Hour, "Web data keep duration")

	flags.StringVar(&param.idpDbType, "idpDbType", "mongo", "IdP DB type")
	flags.StringVar(&param.idpDbAddr, "idpDbAddr", "localhost", "IdP DB address")
	flags.StringVar(&param.idpDbTag, "idpDbTag", "edo", "IdP DB tag")
	flags.StringVar(&param.idpDbTag2, "idpDbTag2", "idp", "IdP DB sub tag")

	flags.StringVar(&param.taDbType, "taDbType", "mongo", "TA DB type")
	flags.StringVar(&param.taDbAddr, "taDbAddr", "localhost", "TA DB address")
	flags.StringVar(&param.taDbTag, "taDbTag", "edo", "TA DB tag")
	flags.StringVar(&param.taDbTag2, "taDbTag2", "ta", "TA DB sub tag")

	flags.StringVar(&param.sessDbType, "sessDbType", "redis", "Session DB type")
	flags.StringVar(&param.sessDbAddr, "sessDbAddr", "localhost:6379", "Session DB address")
	flags.StringVar(&param.sessDbTag, "sessDbTag", "session", "Session DB tag")

	flags.StringVar(&param.cookPath, "cookPath", "/", "Path in Set-Cookie")
	flags.BoolVar(&param.cookSec, "cookSec", true, "Secure flag in Set-Cookie")

	flags.BoolVar(&param.debug, "debug", true, "Debug mode")

	var config string
	flags.StringVar(&config, "c", "", "Config file path")

	// 実行引数を読んで、設定ファイルを指定させてから、
	// 設定ファイルを読んで、また実行引数を読む。
	flags.Parse(args[1:])
	if config != "" {
		if buff, err := ioutil.ReadFile(config); err != nil {
			if !os.IsNotExist(err) {
				return nil, erro.Wrap(err)
			}
			log.Warn("Config file " + config + " is not exist")
		} else {
			flags.Parse(strings.Fields(string(buff)))
		}
	}
	flags.Parse(args[1:])

	if l := len(flags.Args()); l > 0 {
		log.Warn("Ignore extra parameters ", flags.Args())
	}

	return param, nil
}

func (param *parameters) LogFilePath() string       { return param.logPath }
func (param *parameters) LogFileLimit() int64       { return param.logSize }
func (param *parameters) LogFileNumber() int        { return param.logNum }
func (param *parameters) LogFluentdAddress() string { return param.logAddr }
func (param *parameters) LogFluentdTag() string     { return param.logTag }

func (param *parameters) SocketType() string   { return param.socType }
func (param *parameters) SocketPort() int      { return param.socPort }
func (param *parameters) SocketPath() string   { return param.socPath }
func (param *parameters) ProtocolType() string { return param.protType }
<commit_msg>favicon 周りが面倒なので、デフォルトで / は使わないようにした<commit_after>// Copyright 2015 realglobe, 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 main

import (
	"flag"
	"fmt"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type parameters struct {
	// 画面ログ。
	consLv level.Level
	// 追加ログ。
	logType string
	logLv   level.Level
	// ファイルログ。
	logPath string
	logSize int64
	logNum  int
	// fluentd ログ。
	logAddr string
	logTag  string

	// ソケット。
	socType string
	// UNIX ソケット。
	socPath string
	// TCP ソケット。
	socPort int
	// プロトコル。
	protType string

	// URI
	pathOk    string
	pathStart string
	pathSel   string
	pathIdp   string
	// UI 用 HTML を提供する URI。
	pathUi    string
	pathSelUi string
	// UI 用 HTML を置くディレクトリパス。
	uiDir string

	tmplErr string

	// セッション。
	sessLabel    string
	sessLen      int
	sessExpIn    time.Duration
	sessRefDelay time.Duration
	sessDbExpIn  time.Duration
	// チケット。
	ticLen   int
	ticExpIn time.Duration

	// バックエンドの指定。

	// redis
	redTimeout   time.Duration
	redPoolSize  int
	redPoolExpIn time.Duration
	// mongodb
	monTimeout time.Duration

	// web データ DB。
	webDbType  string
	webDbAddr  string
	webDbTag   string
	webDbExpIn time.Duration

	// IdP 情報 DB。
	idpDbType string
	idpDbAddr string
	idpDbTag  string
	idpDbTag2 string

	// TA 情報 DB。
	taDbType string
	taDbAddr string
	taDbTag  string
	taDbTag2 string

	// セッション DB。
	sessDbType string
	sessDbAddr string
	sessDbTag  string

	// その他のオプション。

	// Set-Cookie の Path。
	cookPath string
	// Set-Cookie を Secure にするか。
	cookSec bool

	debug bool
}

func parseParameters(args ...string) (param *parameters, err error) {

	const label = "edo-idp-selector"

	flags := flag.NewFlagSet(label+" parameters", flag.ExitOnError)
	flags.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:")
		fmt.Fprintln(os.Stderr, "  "+args[0]+" [{FLAG}...]")
		fmt.Fprintln(os.Stderr, "FLAG:")
		flags.PrintDefaults()
	}

	param = &parameters{}

	flags.Var(level.Var(&param.consLv, level.INFO), "consLv", "Console log level")
	flags.StringVar(&param.logType, "logType", "", "Extra log: Type")
	flags.Var(level.Var(&param.logLv, level.ALL), "logLv", "Extra log: Level")
	flags.StringVar(&param.logPath, "logPath", filepath.Join(filepath.Dir(os.Args[0]), "log", label+".log"), "Extra log: File path")
	flags.Int64Var(&param.logSize, "logSize", 10*(1<<20) /* 10 MB */, "Extra log: File size limit")
	flags.IntVar(&param.logNum, "logNum", 10, "Extra log: File number limit")
	flags.StringVar(&param.logAddr, "logAddr", "localhost:24224", "Extra log: Fluentd address")
	flags.StringVar(&param.logTag, "logTag", label, "Extra log: Fluentd tag")

	flags.StringVar(&param.socType, "socType", "tcp", "Socket type")
	flags.StringVar(&param.socPath, "socPath", filepath.Join(filepath.Dir(os.Args[0]), "run", label+".soc"), "Unix socket path")
	flags.IntVar(&param.socPort, "socPort", 1603, "TCP socket port")
	flags.StringVar(&param.protType, "protType", "http", "Protocol type")

	flags.StringVar(&param.pathOk, "pathOk", "/ok", "OK URI")
	flags.StringVar(&param.pathStart, "pathStart", "/start", "Start URI")
	flags.StringVar(&param.pathSel, "pathSel", "/select", "Account select URI")
	flags.StringVar(&param.pathIdp, "pathIdp", "/api/info/issuer", "ID provider info URI")
	flags.StringVar(&param.pathUi, "pathUi", "/ui", "UI URI")
	flags.StringVar(&param.pathSelUi, "pathSelUi", "/ui/select.html", "Account selection UI URI")
	flags.StringVar(&param.uiDir, "uiDir", "", "UI file directory")

	flags.StringVar(&param.tmplErr, "tmplErr", "", "Error UI template")

	flags.StringVar(&param.sessLabel, "sessLabel", "Idp-Selector", "Session ID label")
	flags.IntVar(&param.sessLen, "sessLen", 30, "Session ID length")
	flags.DurationVar(&param.sessExpIn, "sessExpIn", 7*24*time.Hour, "Session expiration duration")
	flags.DurationVar(&param.sessRefDelay, "sessRefDelay", 24*time.Hour, "Session refresh delay")
	flags.DurationVar(&param.sessDbExpIn, "sessDbExpIn", 14*24*time.Hour, "Session keep duration")
	flags.IntVar(&param.ticLen, "ticLen", 10, "Ticket length")
	flags.DurationVar(&param.ticExpIn, "ticExpIn", 30*time.Minute, "Ticket expiration duration")

	flags.DurationVar(&param.redTimeout, "redTimeout", 30*time.Second, "redis timeout duration")
	flags.IntVar(&param.redPoolSize, "redPoolSize", 10, "redis pool size")
	flags.DurationVar(&param.redPoolExpIn, "redPoolExpIn", time.Minute, "redis connection keep duration")
	flags.DurationVar(&param.monTimeout, "monTimeout", 30*time.Second, "mongodb timeout duration")

	flags.StringVar(&param.webDbType, "webDbType", "redis", "Web data DB type")
	flags.StringVar(&param.webDbAddr, "webDbAddr", "localhost:6379", "Web data DB address")
	flags.StringVar(&param.webDbTag, "webDbTag", "web", "Web data DB tag")
	flags.DurationVar(&param.webDbExpIn, "webDbExpIn", 7*24*time.Hour, "Web data keep duration")

	flags.StringVar(&param.idpDbType, "idpDbType", "mongo", "IdP DB type")
	flags.StringVar(&param.idpDbAddr, "idpDbAddr", "localhost", "IdP DB address")
	flags.StringVar(&param.idpDbTag, "idpDbTag", "edo", "IdP DB tag")
	flags.StringVar(&param.idpDbTag2, "idpDbTag2", "idp", "IdP DB sub tag")

	flags.StringVar(&param.taDbType, "taDbType", "mongo", "TA DB type")
	flags.StringVar(&param.taDbAddr, "taDbAddr", "localhost", "TA DB address")
	flags.StringVar(&param.taDbTag, "taDbTag", "edo", "TA DB tag")
	flags.StringVar(&param.taDbTag2, "taDbTag2", "ta", "TA DB sub tag")

	flags.StringVar(&param.sessDbType, "sessDbType", "redis", "Session DB type")
	flags.StringVar(&param.sessDbAddr, "sessDbAddr", "localhost:6379", "Session DB address")
	flags.StringVar(&param.sessDbTag, "sessDbTag", "session", "Session DB tag")

	flags.StringVar(&param.cookPath, "cookPath", "/", "Path in Set-Cookie")
	flags.BoolVar(&param.cookSec, "cookSec", true, "Secure flag in Set-Cookie")

	flags.BoolVar(&param.debug, "debug", true, "Debug mode")

	var config string
	flags.StringVar(&config, "c", "", "Config file path")

	// 実行引数を読んで、設定ファイルを指定させてから、
	// 設定ファイルを読んで、また実行引数を読む。
	flags.Parse(args[1:])
	if config != "" {
		if buff, err := ioutil.ReadFile(config); err != nil {
			if !os.IsNotExist(err) {
				return nil, erro.Wrap(err)
			}
			log.Warn("Config file " + config + " is not exist")
		} else {
			flags.Parse(strings.Fields(string(buff)))
		}
	}
	flags.Parse(args[1:])

	if l := len(flags.Args()); l > 0 {
		log.Warn("Ignore extra parameters ", flags.Args())
	}

	return param, nil
}

func (param *parameters) LogFilePath() string       { return param.logPath }
func (param *parameters) LogFileLimit() int64       { return param.logSize }
func (param *parameters) LogFileNumber() int        { return param.logNum }
func (param *parameters) LogFluentdAddress() string { return param.logAddr }
func (param *parameters) LogFluentdTag() string     { return param.logTag }

func (param *parameters) SocketType() string   { return param.socType }
func (param *parameters) SocketPort() int      { return param.socPort }
func (param *parameters) SocketPath() string   { return param.socPath }
func (param *parameters) ProtocolType() string { return param.protType }
<|endoftext|>


<commit_before>package record_set

import (
	"fmt"
	"github.com/miekg/dns"
	"log"
	"net"
	"sort"
	"strings"
	"sync"
)

//
// Key/value store used for dns records. keys are unique, but each key has a list of values
type RecordStore interface {
	PutVal(dnsType uint16, key, val string) error        // Put a single key/value type into store.
	GetVal(dnsType uint16, key string) (string, error)   // Gets a single value. Will round robin or randomly select  if multiple values
	GetAll(dnsType uint16, key string) ([]string, error) // Get all key values
	DelKey(dnsType uint16, key string) error             // Deletes key and all values for a
	DelVal(dnsType uint16, key, value string) error      // Deletes a single value from a key. Deletes key ifthere are no more values
	Clear() error                                        // Clear all keys from set
}

type RrIndexes struct {
	sync.Mutex
	indexes map[string]int
}

func (rri *RrIndexes) NextVal(dnsType uint16, key string, vals []string) (val string) {
	vl := len(vals)
	if vl == 0 {
		return
	}
	if vl == 1 {
		val = vals[0]
		return
	}
	sort.Strings(vals)
	rri.Lock()
	defer rri.Unlock()
	kp := fmt.Sprintf("/%d/%s", dnsType, key)
	idx := rri.indexes[kp]
	if idx >= vl {
		idx = 0
	}
	val = vals[idx]
	rri.indexes[kp] = idx + 1
	return
}

func (rri *RrIndexes) Del(dnsType uint16, key string) {
	kp := fmt.Sprintf("/%d/%s", dnsType, key)
	delete(rri.indexes, kp)
}

type RecordSet struct {
	store      RecordStore
	rr_indexes *RrIndexes
}

func Create(store RecordStore) (rs *RecordSet) {
	rs = &RecordSet{store, &RrIndexes{indexes: make(map[string]int)}}
	return
}

func (r *RecordSet) Put(dnsType uint16, host, addr string) {
	log.Printf("Adding/updating  %X %s A %s", dnsType, host, addr)
	err := r.store.PutVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to put primary record: %s", err.Error())
		return
	}
	// For A or AAAA records, put in reverse DNS
	if dnsType == dns.TypeA || dnsType == dns.TypeAAAA {
		raddr, _ := ReverseAddr(addr)
		if raddr != "" {
			log.Printf("Adding %s PTR %s", raddr, host)
			err = r.store.PutVal(dns.TypePTR, raddr+".", host)
			if err != nil {
				log.Printf("Error %s addting PTR record %s => %s", raddr, host)
			}
		}
	}
}

func (r *RecordSet) Del(dnsType uint16, host string) {
	log.Printf("Removing %X  %s", dnsType, host)
	addrs, err := r.store.GetAll(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to fetch address for host %s -- %s", host, err.Error())
	}
	for _, addr := range addrs {
		raddr, _ := ReverseAddr(addr)
		err = r.store.DelKey(dns.TypePTR, raddr+".")
		if err != nil {
			log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
		}
	}
	err = r.store.DelKey(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to remove host key %s (%s)", host, err.Error())
	}
	r.rr_indexes.Del(dnsType, host)
}

func (r *RecordSet) DelAddr(dnsType uint16, host, addr string) {
	log.Printf("Removing %X  %s %s", dnsType, host, addr)
	err := r.store.DelVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to delete  address %s for host %s -- %s", addr, host, err.Error())
		return
	}
	raddr, _ := ReverseAddr(addr)
	err = r.store.DelKey(dns.TypePTR, raddr+".")
	if err != nil {
		log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
	}
	r.rr_indexes.Del(dnsType, host)
}

func (r *RecordSet) Get(dnsType uint16, host string) (addr string) {
	addrs, err := r.store.GetAll(dnsType, host)
	if err != nil {
		log.Printf("Unable to fetch value: %s", err.Error())
		return
	}
	if len(addrs) == 0 { // Try a wildcard
		parts := strings.SplitN(host, ".", 2)
		if len(parts) == 2 {
			wc := "*." + parts[1]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	if len(addrs) == 0 { // Try adouble wildcard
		parts := strings.SplitN(host, ".", 3)
		if len(parts) == 3 {
			wc := "*.*." + parts[2]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	return r.rr_indexes.NextVal(dnsType, host, addrs)
}

// Taken somewhat from stdlib dnsclient.go
func ReverseAddr(addr string) (arpa string, err error) {
	ip := net.ParseIP(addr)
	if ip == nil {
		return "", fmt.Errorf("Unrecognized address: %s", addr)
	}
	if ip.To4() != nil {
		arpa = fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa", ip[15], ip[14], ip[13], ip[12])
		return
	}
	// Must be IPv6
	var parts []string
	for i := len(ip) - 1; i >= 0; i-- {
		v := byte(ip[i])
		str := fmt.Sprintf("%x.%x.", v&0xf, v>>4)
		parts = append(parts, str)
	}
	// Append "ip6.arpa." and return (buf already has the final .)
	parts = append(parts, "ip6.arpa.")
	return strings.Join(parts, ""), nil
}
<commit_msg>oops -- lock has on entry delete<commit_after>package record_set

import (
	"fmt"
	"github.com/miekg/dns"
	"log"
	"net"
	"sort"
	"strings"
	"sync"
)

//
// Key/value store used for dns records. keys are unique, but each key has a list of values
type RecordStore interface {
	PutVal(dnsType uint16, key, val string) error        // Put a single key/value type into store.
	GetVal(dnsType uint16, key string) (string, error)   // Gets a single value. Will round robin or randomly select  if multiple values
	GetAll(dnsType uint16, key string) ([]string, error) // Get all key values
	DelKey(dnsType uint16, key string) error             // Deletes key and all values for a
	DelVal(dnsType uint16, key, value string) error      // Deletes a single value from a key. Deletes key ifthere are no more values
	Clear() error                                        // Clear all keys from set
}

type RrIndexes struct {
	sync.Mutex
	indexes map[string]int
}

func (rri *RrIndexes) NextVal(dnsType uint16, key string, vals []string) (val string) {
	vl := len(vals)
	if vl == 0 {
		return
	}
	if vl == 1 {
		val = vals[0]
		return
	}
	sort.Strings(vals)
	rri.Lock()
	defer rri.Unlock()
	kp := fmt.Sprintf("/%d/%s", dnsType, key)
	idx := rri.indexes[kp]
	if idx >= vl {
		idx = 0
	}
	val = vals[idx]
	rri.indexes[kp] = idx + 1
	return
}

func (rri *RrIndexes) Del(dnsType uint16, key string) {
	kp := fmt.Sprintf("/%d/%s", dnsType, key)
	rri.Lock()
	defer rri.Unlock()
	delete(rri.indexes, kp)
}

type RecordSet struct {
	store      RecordStore
	rr_indexes *RrIndexes
}

func Create(store RecordStore) (rs *RecordSet) {
	rs = &RecordSet{store, &RrIndexes{indexes: make(map[string]int)}}
	return
}

func (r *RecordSet) Put(dnsType uint16, host, addr string) {
	log.Printf("Adding/updating  %X %s A %s", dnsType, host, addr)
	err := r.store.PutVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to put primary record: %s", err.Error())
		return
	}
	// For A or AAAA records, put in reverse DNS
	if dnsType == dns.TypeA || dnsType == dns.TypeAAAA {
		raddr, _ := ReverseAddr(addr)
		if raddr != "" {
			log.Printf("Adding %s PTR %s", raddr, host)
			err = r.store.PutVal(dns.TypePTR, raddr+".", host)
			if err != nil {
				log.Printf("Error %s addting PTR record %s => %s", raddr, host)
			}
		}
	}
}

func (r *RecordSet) Del(dnsType uint16, host string) {
	log.Printf("Removing %X  %s", dnsType, host)
	addrs, err := r.store.GetAll(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to fetch address for host %s -- %s", host, err.Error())
	}
	for _, addr := range addrs {
		raddr, _ := ReverseAddr(addr)
		err = r.store.DelKey(dns.TypePTR, raddr+".")
		if err != nil {
			log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
		}
	}
	err = r.store.DelKey(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to remove host key %s (%s)", host, err.Error())
	}
	r.rr_indexes.Del(dnsType, host)
}

func (r *RecordSet) DelAddr(dnsType uint16, host, addr string) {
	log.Printf("Removing %X  %s %s", dnsType, host, addr)
	err := r.store.DelVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to delete  address %s for host %s -- %s", addr, host, err.Error())
		return
	}
	raddr, _ := ReverseAddr(addr)
	err = r.store.DelKey(dns.TypePTR, raddr+".")
	if err != nil {
		log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
	}
	r.rr_indexes.Del(dnsType, host)
}

func (r *RecordSet) Get(dnsType uint16, host string) (addr string) {
	addrs, err := r.store.GetAll(dnsType, host)
	if err != nil {
		log.Printf("Unable to fetch value: %s", err.Error())
		return
	}
	if len(addrs) == 0 { // Try a wildcard
		parts := strings.SplitN(host, ".", 2)
		if len(parts) == 2 {
			wc := "*." + parts[1]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	if len(addrs) == 0 { // Try adouble wildcard
		parts := strings.SplitN(host, ".", 3)
		if len(parts) == 3 {
			wc := "*.*." + parts[2]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	return r.rr_indexes.NextVal(dnsType, host, addrs)
}

// Taken somewhat from stdlib dnsclient.go
func ReverseAddr(addr string) (arpa string, err error) {
	ip := net.ParseIP(addr)
	if ip == nil {
		return "", fmt.Errorf("Unrecognized address: %s", addr)
	}
	if ip.To4() != nil {
		arpa = fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa", ip[15], ip[14], ip[13], ip[12])
		return
	}
	// Must be IPv6
	var parts []string
	for i := len(ip) - 1; i >= 0; i-- {
		v := byte(ip[i])
		str := fmt.Sprintf("%x.%x.", v&0xf, v>>4)
		parts = append(parts, str)
	}
	// Append "ip6.arpa." and return (buf already has the final .)
	parts = append(parts, "ip6.arpa.")
	return strings.Join(parts, ""), nil
}
<|endoftext|>


<commit_before>package web

import (
	"fmt"
	"log"
	"net/http"
	"sync"
)

// Maximum size of the pool of spare middleware stacks
const mPoolSize = 32

type mLayer struct {
	fn   func(*C, http.Handler) http.Handler
	orig interface{}
}

type mStack struct {
	lock   sync.Mutex
	stack  []mLayer
	pool   chan *cStack
	router internalRouter
}

type internalRouter interface {
	route(*C, http.ResponseWriter, *http.Request)
}

/*
Constructing a middleware stack involves a lot of allocations: at the very least
each layer will have to close over the layer after (inside) it, and perhaps a
context object. Instead of doing this on every request, let's cache fully
assembled middleware stacks (the "c" stands for "cached").

A lot of the complexity here (in particular the "pool" parameter, and the
behavior of release() and invalidate() below) is due to the fact that when the
middleware stack is mutated we need to create a "cache barrier," where no
cStack created before the middleware stack mutation is returned to the active
cache pool (and is therefore eligible for subsequent reuse). The way we do this
is a bit ugly: each cStack maintains a pointer to the pool it originally came
from, and will only return itself to that pool. If the mStack's pool has been
rotated since then (meaning that this cStack is invalid), it will either try
(and likely fail) to insert itself into the stale pool, or it will drop the
cStack on the floor.
*/
type cStack struct {
	C
	m    http.Handler
	pool chan *cStack
}

func (s *cStack) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.C = C{}
	s.m.ServeHTTP(w, r)
}
func (s *cStack) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
	s.C = c
	s.m.ServeHTTP(w, r)
}

func (m *mStack) appendLayer(fn interface{}) {
	ml := mLayer{orig: fn}
	switch fn.(type) {
	case func(http.Handler) http.Handler:
		unwrapped := fn.(func(http.Handler) http.Handler)
		ml.fn = func(c *C, h http.Handler) http.Handler {
			return unwrapped(h)
		}
	case func(*C, http.Handler) http.Handler:
		ml.fn = fn.(func(*C, http.Handler) http.Handler)
	default:
		log.Fatalf(`Unknown middleware type %v. Expected a function `+
			`with signature "func(http.Handler) http.Handler" or `+
			`"func(*web.C, http.Handler) http.Handler".`, fn)
	}
	m.stack = append(m.stack, ml)
}

func (m *mStack) findLayer(l interface{}) int {
	for i, middleware := range m.stack {
		if funcEqual(l, middleware.orig) {
			return i
		}
	}
	return -1
}

func (m *mStack) invalidate() {
	m.pool = make(chan *cStack, mPoolSize)
}

func (m *mStack) newStack() *cStack {
	m.lock.Lock()
	defer m.lock.Unlock()

	cs := cStack{}
	router := m.router

	cs.m = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		router.route(&cs.C, w, r)
	})
	for i := len(m.stack) - 1; i >= 0; i-- {
		cs.m = m.stack[i].fn(&cs.C, cs.m)
	}

	return &cs
}

func (m *mStack) alloc() *cStack {
	// This is a little sloppy: this is only safe if this pointer
	// dereference is atomic. Maybe someday I'll replace it with
	// sync/atomic, but for now I happen to know that on all the
	// architecures I care about it happens to be atomic.
	p := m.pool
	var cs *cStack
	select {
	case cs = <-p:
		// This can happen if we race against an invalidation. It's
		// completely peaceful, so long as we assume we can grab a cStack before
		// our stack blows out.
		if cs == nil {
			return m.alloc()
		}
	default:
		cs = m.newStack()
	}

	cs.pool = p
	return cs
}

func (m *mStack) release(cs *cStack) {
	if cs.pool != m.pool {
		return
	}
	select {
	case cs.pool <- cs:
	default:
	}
}

// Append the given middleware to the middleware stack. See the documentation
// for type Mux for a list of valid middleware types.
//
// No attempt is made to enforce the uniqueness of middlewares.
func (m *mStack) Use(middleware interface{}) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.appendLayer(middleware)
	m.invalidate()
}

// Insert the given middleware immediately before a given existing middleware in
// the stack. See the documentation for type Mux for a list of valid middleware
// types. Returns an error if no middleware has the name given by "before."
//
// No attempt is made to enforce the uniqueness of middlewares. If the insertion
// point is ambiguous, the first (outermost) one is chosen.
func (m *mStack) Insert(middleware, before interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(before)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", before)
	}

	m.appendLayer(middleware)
	inserted := m.stack[len(m.stack)-1]
	copy(m.stack[i+1:], m.stack[i:])
	m.stack[i] = inserted

	m.invalidate()
	return nil
}

// Remove the given middleware from the middleware stack. Returns an error if
// no such middleware can be found.
//
// If the name of the middleware to delete is ambiguous, the first (outermost)
// one is chosen.
func (m *mStack) Abandon(middleware interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(middleware)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", middleware)
	}

	copy(m.stack[i:], m.stack[i+1:])
	m.stack = m.stack[:len(m.stack)-1 : len(m.stack)]

	m.invalidate()
	return nil
}
<commit_msg>Reset cStack's C on release<commit_after>package web

import (
	"fmt"
	"log"
	"net/http"
	"sync"
)

// Maximum size of the pool of spare middleware stacks
const mPoolSize = 32

type mLayer struct {
	fn   func(*C, http.Handler) http.Handler
	orig interface{}
}

type mStack struct {
	lock   sync.Mutex
	stack  []mLayer
	pool   chan *cStack
	router internalRouter
}

type internalRouter interface {
	route(*C, http.ResponseWriter, *http.Request)
}

/*
Constructing a middleware stack involves a lot of allocations: at the very least
each layer will have to close over the layer after (inside) it, and perhaps a
context object. Instead of doing this on every request, let's cache fully
assembled middleware stacks (the "c" stands for "cached").

A lot of the complexity here (in particular the "pool" parameter, and the
behavior of release() and invalidate() below) is due to the fact that when the
middleware stack is mutated we need to create a "cache barrier," where no
cStack created before the middleware stack mutation is returned to the active
cache pool (and is therefore eligible for subsequent reuse). The way we do this
is a bit ugly: each cStack maintains a pointer to the pool it originally came
from, and will only return itself to that pool. If the mStack's pool has been
rotated since then (meaning that this cStack is invalid), it will either try
(and likely fail) to insert itself into the stale pool, or it will drop the
cStack on the floor.
*/
type cStack struct {
	C
	m    http.Handler
	pool chan *cStack
}

func (s *cStack) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.C = C{}
	s.m.ServeHTTP(w, r)
}
func (s *cStack) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
	s.C = c
	s.m.ServeHTTP(w, r)
}

func (m *mStack) appendLayer(fn interface{}) {
	ml := mLayer{orig: fn}
	switch fn.(type) {
	case func(http.Handler) http.Handler:
		unwrapped := fn.(func(http.Handler) http.Handler)
		ml.fn = func(c *C, h http.Handler) http.Handler {
			return unwrapped(h)
		}
	case func(*C, http.Handler) http.Handler:
		ml.fn = fn.(func(*C, http.Handler) http.Handler)
	default:
		log.Fatalf(`Unknown middleware type %v. Expected a function `+
			`with signature "func(http.Handler) http.Handler" or `+
			`"func(*web.C, http.Handler) http.Handler".`, fn)
	}
	m.stack = append(m.stack, ml)
}

func (m *mStack) findLayer(l interface{}) int {
	for i, middleware := range m.stack {
		if funcEqual(l, middleware.orig) {
			return i
		}
	}
	return -1
}

func (m *mStack) invalidate() {
	m.pool = make(chan *cStack, mPoolSize)
}

func (m *mStack) newStack() *cStack {
	m.lock.Lock()
	defer m.lock.Unlock()

	cs := cStack{}
	router := m.router

	cs.m = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		router.route(&cs.C, w, r)
	})
	for i := len(m.stack) - 1; i >= 0; i-- {
		cs.m = m.stack[i].fn(&cs.C, cs.m)
	}

	return &cs
}

func (m *mStack) alloc() *cStack {
	// This is a little sloppy: this is only safe if this pointer
	// dereference is atomic. Maybe someday I'll replace it with
	// sync/atomic, but for now I happen to know that on all the
	// architecures I care about it happens to be atomic.
	p := m.pool
	var cs *cStack
	select {
	case cs = <-p:
		// This can happen if we race against an invalidation. It's
		// completely peaceful, so long as we assume we can grab a cStack before
		// our stack blows out.
		if cs == nil {
			return m.alloc()
		}
	default:
		cs = m.newStack()
	}

	cs.pool = p
	return cs
}

func (m *mStack) release(cs *cStack) {
	cs.C = C{}
	if cs.pool != m.pool {
		return
	}
	select {
	case cs.pool <- cs:
	default:
	}
}

// Append the given middleware to the middleware stack. See the documentation
// for type Mux for a list of valid middleware types.
//
// No attempt is made to enforce the uniqueness of middlewares.
func (m *mStack) Use(middleware interface{}) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.appendLayer(middleware)
	m.invalidate()
}

// Insert the given middleware immediately before a given existing middleware in
// the stack. See the documentation for type Mux for a list of valid middleware
// types. Returns an error if no middleware has the name given by "before."
//
// No attempt is made to enforce the uniqueness of middlewares. If the insertion
// point is ambiguous, the first (outermost) one is chosen.
func (m *mStack) Insert(middleware, before interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(before)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", before)
	}

	m.appendLayer(middleware)
	inserted := m.stack[len(m.stack)-1]
	copy(m.stack[i+1:], m.stack[i:])
	m.stack[i] = inserted

	m.invalidate()
	return nil
}

// Remove the given middleware from the middleware stack. Returns an error if
// no such middleware can be found.
//
// If the name of the middleware to delete is ambiguous, the first (outermost)
// one is chosen.
func (m *mStack) Abandon(middleware interface{}) error {
	m.lock.Lock()
	defer m.lock.Unlock()
	i := m.findLayer(middleware)
	if i < 0 {
		return fmt.Errorf("web: unknown middleware %v", middleware)
	}

	copy(m.stack[i:], m.stack[i+1:])
	m.stack = m.stack[:len(m.stack)-1 : len(m.stack)]

	m.invalidate()
	return nil
}
<|endoftext|>


<commit_before>package backends

import (
	"bytes"
	"fmt"
	"github.com/boltdb/bolt"
)

type AuthBackend interface {
	SetValue(key, value []byte) error
	GetValue(key []byte) ([]byte, error)
	Delete(key []byte) error
}

func NewBoltDBAuthBackend(db *bolt.DB, tokenBucket, userBucket []byte) *BoltAuth {
	return &BoltAuth{
		DS:          db,
		TokenBucket: []byte(tokenBucket),
		UserBucket:  []byte(userBucket),
	}
}

// UserBucketName - default name for BoltDB bucket that stores user info
const UserBucketName = "authbucket"

// TokenBucketName
const TokenBucketName = "tokenbucket"

// BoltCache - container to implement Cache instance with BoltDB backend for storage
type BoltAuth struct {
	DS          *bolt.DB
	TokenBucket []byte
	UserBucket  []byte
}

func (b *BoltAuth) SetValue(key, value []byte) error {
	err := b.DS.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists(b.TokenBucket)
		if err != nil {
			return err
		}
		err = bucket.Put(key, value)
		if err != nil {
			return err
		}
		return nil
	})

	return err
}

func (b *BoltAuth) Delete(key []byte) error {
	return b.Delete(key)
}

func (b *BoltAuth) GetValue(key []byte) (value []byte, err error) {

	err = b.DS.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket(b.TokenBucket)
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", b.TokenBucket)
		}
		// "Byte slices returned from Bolt are only valid during a transaction."
		var buffer bytes.Buffer
		val := bucket.Get(key)

		// If it doesn't exist then it will return nil
		if val == nil {
			return fmt.Errorf("key %q not found \n", key)
		}

		buffer.Write(val)
		value = buffer.Bytes()
		return nil
	})

	return
}
<commit_msg>user struct moved to backends<commit_after>package backends

import (
	"bytes"
	"fmt"
	"github.com/boltdb/bolt"
)

type User struct {
	UUID     string `json:"uuid" form:"-"`
	Username string `json:"username" form:"username"`
	Password string `json:"password" form:"password"`
}

func (u *User) Encode() ([]byte, error) {
	buf := new(bytes.Buffer)
	enc := json.NewEncoder(buf)
	err := enc.Encode(u)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func DecodeUser(user []bytes) (*User, error) {
	var u *User
	buf := bytes.NewBuffer(user)
	dec := json.NewDecoder(buf)
	err := dec.Decode(&u)
	if err != nil {
		return nil, err
	}
	return u, nil
}

type AuthBackend interface {
	SetValue(key, value []byte) error
	GetValue(key []byte) ([]byte, error)
	Delete(key []byte) error
}

func NewBoltDBAuthBackend(db *bolt.DB, tokenBucket, userBucket []byte) *BoltAuth {
	return &BoltAuth{
		DS:          db,
		TokenBucket: []byte(tokenBucket),
		UserBucket:  []byte(userBucket),
	}
}

// UserBucketName - default name for BoltDB bucket that stores user info
const UserBucketName = "authbucket"

// TokenBucketName
const TokenBucketName = "tokenbucket"

// BoltCache - container to implement Cache instance with BoltDB backend for storage
type BoltAuth struct {
	DS          *bolt.DB
	TokenBucket []byte
	UserBucket  []byte
}

func (b *BoltAuth) SetValue(key, value []byte) error {
	err := b.DS.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists(b.TokenBucket)
		if err != nil {
			return err
		}
		err = bucket.Put(key, value)
		if err != nil {
			return err
		}
		return nil
	})

	return err
}

func (b *BoltAuth) Delete(key []byte) error {
	return b.Delete(key)
}

func (b *BoltAuth) GetValue(key []byte) (value []byte, err error) {

	err = b.DS.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket(b.TokenBucket)
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", b.TokenBucket)
		}
		// "Byte slices returned from Bolt are only valid during a transaction."
		var buffer bytes.Buffer
		val := bucket.Get(key)

		// If it doesn't exist then it will return nil
		if val == nil {
			return fmt.Errorf("key %q not found \n", key)
		}

		buffer.Write(val)
		value = buffer.Bytes()
		return nil
	})

	return
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"os"
	"regexp"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccAWSDBSecurityGroup_basic(t *testing.T) {
	var v rds.DBSecurityGroup

	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)
	resourceName := "aws_db_security_group.test"
	rName := fmt.Sprintf("tf-acc-%s", acctest.RandString(5))

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSDBSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSDBSecurityGroupConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSDBSecurityGroupExists(resourceName, &v),
					testAccCheckAWSDBSecurityGroupAttributes(&v),
					resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(`^arn:[^:]+:rds:[^:]+:\d{12}:secgrp:.+`)),
					resource.TestCheckResourceAttr(
						resourceName, "name", rName),
					resource.TestCheckResourceAttr(
						resourceName, "description", "Managed by Terraform"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.3363517775.cidr", "10.0.0.1/24"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
					resource.TestCheckResourceAttr(
						resourceName, "tags.%", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckAWSDBSecurityGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).rdsconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_db_security_group" {
			continue
		}

		// Try to find the Group
		resp, err := conn.DescribeDBSecurityGroups(
			&rds.DescribeDBSecurityGroupsInput{
				DBSecurityGroupName: aws.String(rs.Primary.ID),
			})

		if err == nil {
			if len(resp.DBSecurityGroups) != 0 &&
				*resp.DBSecurityGroups[0].DBSecurityGroupName == rs.Primary.ID {
				return fmt.Errorf("DB Security Group still exists")
			}
		}

		// Verify the error
		newerr, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if newerr.Code() != "DBSecurityGroupNotFound" {
			return err
		}
	}

	return nil
}

func testAccCheckAWSDBSecurityGroupAttributes(group *rds.DBSecurityGroup) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if len(group.IPRanges) == 0 {
			return fmt.Errorf("no cidr: %#v", group.IPRanges)
		}

		if *group.IPRanges[0].CIDRIP != "10.0.0.1/24" {
			return fmt.Errorf("bad cidr: %#v", group.IPRanges)
		}

		statuses := make([]string, 0, len(group.IPRanges))
		for _, ips := range group.IPRanges {
			statuses = append(statuses, *ips.Status)
		}

		if statuses[0] != "authorized" {
			return fmt.Errorf("bad status: %#v", statuses)
		}

		return nil
	}
}

func testAccCheckAWSDBSecurityGroupExists(n string, v *rds.DBSecurityGroup) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No DB Security Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).rdsconn

		opts := rds.DescribeDBSecurityGroupsInput{
			DBSecurityGroupName: aws.String(rs.Primary.ID),
		}

		resp, err := conn.DescribeDBSecurityGroups(&opts)

		if err != nil {
			return err
		}

		if len(resp.DBSecurityGroups) != 1 ||
			*resp.DBSecurityGroups[0].DBSecurityGroupName != rs.Primary.ID {
			return fmt.Errorf("DB Security Group not found")
		}

		*v = *resp.DBSecurityGroups[0]

		return nil
	}
}

func testAccAWSDBSecurityGroupConfig(name string) string {
	return fmt.Sprintf(`
resource "aws_db_security_group" "test" {
  name = "%s"

  ingress {
    cidr = "10.0.0.1/24"
  }

  tags = {
    foo = "test"
  }
}
`, name)
}
<commit_msg>tests/resource/aws_db_security_group: Include EC2-Classic PreCheck<commit_after>package aws

import (
	"fmt"
	"os"
	"regexp"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccAWSDBSecurityGroup_basic(t *testing.T) {
	var v rds.DBSecurityGroup

	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)
	resourceName := "aws_db_security_group.test"
	rName := fmt.Sprintf("tf-acc-%s", acctest.RandString(5))

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccEC2ClassicPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSDBSecurityGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSDBSecurityGroupConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSDBSecurityGroupExists(resourceName, &v),
					testAccCheckAWSDBSecurityGroupAttributes(&v),
					resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(`^arn:[^:]+:rds:[^:]+:\d{12}:secgrp:.+`)),
					resource.TestCheckResourceAttr(
						resourceName, "name", rName),
					resource.TestCheckResourceAttr(
						resourceName, "description", "Managed by Terraform"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.3363517775.cidr", "10.0.0.1/24"),
					resource.TestCheckResourceAttr(
						resourceName, "ingress.#", "1"),
					resource.TestCheckResourceAttr(
						resourceName, "tags.%", "1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckAWSDBSecurityGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).rdsconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_db_security_group" {
			continue
		}

		// Try to find the Group
		resp, err := conn.DescribeDBSecurityGroups(
			&rds.DescribeDBSecurityGroupsInput{
				DBSecurityGroupName: aws.String(rs.Primary.ID),
			})

		if err == nil {
			if len(resp.DBSecurityGroups) != 0 &&
				*resp.DBSecurityGroups[0].DBSecurityGroupName == rs.Primary.ID {
				return fmt.Errorf("DB Security Group still exists")
			}
		}

		// Verify the error
		newerr, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if newerr.Code() != "DBSecurityGroupNotFound" {
			return err
		}
	}

	return nil
}

func testAccCheckAWSDBSecurityGroupAttributes(group *rds.DBSecurityGroup) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if len(group.IPRanges) == 0 {
			return fmt.Errorf("no cidr: %#v", group.IPRanges)
		}

		if *group.IPRanges[0].CIDRIP != "10.0.0.1/24" {
			return fmt.Errorf("bad cidr: %#v", group.IPRanges)
		}

		statuses := make([]string, 0, len(group.IPRanges))
		for _, ips := range group.IPRanges {
			statuses = append(statuses, *ips.Status)
		}

		if statuses[0] != "authorized" {
			return fmt.Errorf("bad status: %#v", statuses)
		}

		return nil
	}
}

func testAccCheckAWSDBSecurityGroupExists(n string, v *rds.DBSecurityGroup) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No DB Security Group ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).rdsconn

		opts := rds.DescribeDBSecurityGroupsInput{
			DBSecurityGroupName: aws.String(rs.Primary.ID),
		}

		resp, err := conn.DescribeDBSecurityGroups(&opts)

		if err != nil {
			return err
		}

		if len(resp.DBSecurityGroups) != 1 ||
			*resp.DBSecurityGroups[0].DBSecurityGroupName != rs.Primary.ID {
			return fmt.Errorf("DB Security Group not found")
		}

		*v = *resp.DBSecurityGroups[0]

		return nil
	}
}

func testAccAWSDBSecurityGroupConfig(name string) string {
	return fmt.Sprintf(`
resource "aws_db_security_group" "test" {
  name = "%s"

  ingress {
    cidr = "10.0.0.1/24"
  }

  tags = {
    foo = "test"
  }
}
`, name)
}
<|endoftext|>


<commit_before>// Description: conf 项目配置项，也用于读取当前目录下的app.conf中的项目
// Author: ZHU HAIHUA
// Since: 2016-02-26 19:19
package conf

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"strings"
	"reflect"
)

type Config struct {
	SERVER struct {
		RUNMODE string `yaml:runmode`
		PORT    string `yaml:"port"`
	}

	DATABASE struct {
		HOST     string `yaml:"host"`
		NAME     string `yaml:"name"`
		USER     string `yaml:"user"`
		PASSWORD string `yaml:"password"`
	}

	EXT map[string]interface{} `yaml:"ext,flow"`
}

// Ext will return the value of the EXT config, the keys is a string
// separated by DOT(.). If you provide a default value, this method
// will return the it while the key cannot be found. otherwise it
// will raise a panic!
func (c *Config) Ext(keys string, defaultVal... interface{}) (interface{}) {
	r, e := c.ExtSep(keys, ".")
	if e != nil {
		if len(defaultVal) > 0 {
			return defaultVal[0]
		} else {
			panic(e)
		}
	} else {
		return r
	}
}

func (c *Config) ExtString(keys string, defaultVal... interface{}) (string) {
	return fmt.Sprintf("%v", c.Ext(keys, defaultVal...))
}

func (c *Config) ExtInt(keys string, defaultVal... interface{}) (int) {
	return c.Ext(keys, defaultVal...).(int)
}

func (c *Config) ExtInt8(keys string, defaultVal... interface{}) (int8) {
	return c.Ext(keys, defaultVal...).(int8)
}

func (c *Config) ExtInt16(keys string, defaultVal... interface{}) (int16) {
	return c.Ext(keys, defaultVal...).(int16)
}

func (c *Config) ExtInt32(keys string, defaultVal... interface{}) (int32) {
	return c.Ext(keys, defaultVal...).(int32)
}

func (c *Config) ExtInt64(keys string, defaultVal... interface{}) (int64) {
	return c.Ext(keys, defaultVal...).(int64)
}

func (c *Config) ExtBool(keys string, defaultVal... interface{}) (bool) {
	return c.Ext(keys, defaultVal...).(bool)
}

func (c *Config) ExtFloat64(keys string, defaultVal... interface{}) (float64) {
	return c.Ext(keys, defaultVal...).(float64)
}

func (c *Config) ExtFloat32(keys string, defaultVal... interface{}) (float32) {
	return c.Ext(keys, defaultVal...).(float32)
}

// Ext will return the value of the EXT config, the keys is separated
// by the given sep string.
func (c *Config) ExtSep(keys, sep string) (interface{}, error) {
	ks := strings.Split(keys, sep);
	var result interface{}
	var isFinal, success bool
	result = c.EXT
	for _, k := range ks {
		result, isFinal, success = find(result, k)
		if !success {
			return "", fmt.Errorf("no such key: %v", k)
		} else if isFinal {
			return result, nil
		}
	}
	return "", fmt.Errorf("not found")
}

func find(v interface{}, key interface{}) (result interface{}, isFinal, success bool) {
	switch m := v.(type) {
	case map[string]interface{}:
		result = m[key.(string)]
		success = true
		isFinal = (reflect.TypeOf(result) != nil && reflect.TypeOf(result).Kind() != reflect.Map)
	case map[interface{}]interface{}:
		result = m[key]
		success = true
		isFinal = (reflect.TypeOf(result) != nil && reflect.TypeOf(result).Kind() != reflect.Map)
	}
	return
}

var (
	Conf = Config{}
)

func LoadConf(path string) {
	c, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return
	}
	yaml.Unmarshal(c, &Conf)
}

func Ext(keys string, defVal... interface{}) interface {}{
	return Conf.Ext(keys, defVal...)
}

// ExtString is a shorcut for (c *Config) ExtString()
func ExtString(keys string, defVal... interface{}) string {
	return Conf.ExtString(keys, defVal...)
}

// ExtBool is a shorcut for (c *Config) ExtBool()
func ExtBool(keys string, defVal... interface{}) bool {
	return Conf.ExtBool(keys, defVal...)
}

// ExtInt is a shorcut for (c *Config) ExtInt()
func ExtInt(keys string, defVal... interface{}) int {
	return Conf.ExtInt(keys, defVal...)
}

// ExtInt8 is a shorcut for (c *Config) ExtInt8()
func ExtInt8(keys string, defVal... interface{}) int8 {
	return Conf.ExtInt8(keys, defVal...)
}

// ExtInt16 is a shorcut for (c *Config) ExtInt16()
func ExtInt16(keys string, defVal... interface{}) int16 {
	return Conf.ExtInt16(keys, defVal...)
}

// ExtInt32 is a shorcut for (c *Config) ExtInt32()
func ExtInt32(keys string, defVal... interface{}) int32 {
	return Conf.ExtInt32(keys, defVal...)
}

// ExtInt64 is a shorcut for (c *Config) ExtInt64()
func ExtInt64(keys string, defVal... interface{}) int64 {
	return Conf.ExtInt64(keys, defVal...)
}

// ExtFloat32 is a shorcut for (c *Config) ExtFloat32()
func ExtFloat32(keys string, defVal... interface{}) float32 {
	return Conf.ExtFloat32(keys, defVal...)
}

// ExtFloat64 is a shorcut for (c *Config) ExtFloat64()
func ExtFloat64(keys string, defVal... interface{}) float64 {
	return Conf.ExtFloat64(keys, defVal...)
}

func init() {
	LoadConf("conf.yml")
}
<commit_msg>fixes: return the result as type map[interface{}]interface{} even it not leaf node of conf<commit_after>// Description: conf 项目配置项，也用于读取当前目录下的app.conf中的项目
// Author: ZHU HAIHUA
// Since: 2016-02-26 19:19
package conf

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"strings"
	"reflect"
)

type Config struct {
	SERVER struct {
		RUNMODE string `yaml:runmode`
		PORT    string `yaml:"port"`
	}

	DATABASE struct {
		HOST     string `yaml:"host"`
		NAME     string `yaml:"name"`
		USER     string `yaml:"user"`
		PASSWORD string `yaml:"password"`
	}

	EXT map[string]interface{} `yaml:"ext,flow"`
}

// Ext will return the value of the EXT config, the keys is a string
// separated by DOT(.). If you provide a default value, this method
// will return the it while the key cannot be found. otherwise it
// will raise a panic!
func (c *Config) Ext(keys string, defaultVal... interface{}) (interface{}) {
	r, e := c.ExtSep(keys, ".")
	if e != nil {
		if len(defaultVal) > 0 {
			return defaultVal[0]
		} else {
			panic(e)
		}
	} else {
		return r
	}
}

func (c *Config) ExtString(keys string, defaultVal... interface{}) (string) {
	return fmt.Sprintf("%v", c.Ext(keys, defaultVal...))
}

func (c *Config) ExtInt(keys string, defaultVal... interface{}) (int) {
	return c.Ext(keys, defaultVal...).(int)
}

func (c *Config) ExtInt8(keys string, defaultVal... interface{}) (int8) {
	return c.Ext(keys, defaultVal...).(int8)
}

func (c *Config) ExtInt16(keys string, defaultVal... interface{}) (int16) {
	return c.Ext(keys, defaultVal...).(int16)
}

func (c *Config) ExtInt32(keys string, defaultVal... interface{}) (int32) {
	return c.Ext(keys, defaultVal...).(int32)
}

func (c *Config) ExtInt64(keys string, defaultVal... interface{}) (int64) {
	return c.Ext(keys, defaultVal...).(int64)
}

func (c *Config) ExtBool(keys string, defaultVal... interface{}) (bool) {
	return c.Ext(keys, defaultVal...).(bool)
}

func (c *Config) ExtFloat64(keys string, defaultVal... interface{}) (float64) {
	return c.Ext(keys, defaultVal...).(float64)
}

func (c *Config) ExtFloat32(keys string, defaultVal... interface{}) (float32) {
	return c.Ext(keys, defaultVal...).(float32)
}

// Ext will return the value of the EXT config, the keys is separated
// by the given sep string.
func (c *Config) ExtSep(keys, sep string) (interface{}, error) {
	ks := strings.Split(keys, sep);
	var result interface{}
	var isFinal, success bool
	result = c.EXT
	for _, k := range ks {
		result, isFinal, success = find(result, k)
		if !success {
			return "", fmt.Errorf("no such key: %v", k)
		} else if isFinal {
			break
		}
	}

	if success {
		return result, nil
	} else {
		return "", fmt.Errorf("not found")
	}
}

func find(v interface{}, key interface{}) (result interface{}, isFinal, success bool) {
	switch m := v.(type) {
	case map[string]interface{}:
		result = m[key.(string)]
		success = true
		isFinal = (reflect.TypeOf(result) != nil && reflect.TypeOf(result).Kind() != reflect.Map)
	case map[interface{}]interface{}:
		result = m[key]
		success = true
		isFinal = (reflect.TypeOf(result) != nil && reflect.TypeOf(result).Kind() != reflect.Map)
	}
	return
}

var (
	Conf = Config{}
)

func LoadConf(path string) {
	c, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return
	}
	yaml.Unmarshal(c, &Conf)
}

func Ext(keys string, defVal... interface{}) interface {}{
	return Conf.Ext(keys, defVal...)
}

// ExtString is a shorcut for (c *Config) ExtString()
func ExtString(keys string, defVal... interface{}) string {
	return Conf.ExtString(keys, defVal...)
}

// ExtBool is a shorcut for (c *Config) ExtBool()
func ExtBool(keys string, defVal... interface{}) bool {
	return Conf.ExtBool(keys, defVal...)
}

// ExtInt is a shorcut for (c *Config) ExtInt()
func ExtInt(keys string, defVal... interface{}) int {
	return Conf.ExtInt(keys, defVal...)
}

// ExtInt8 is a shorcut for (c *Config) ExtInt8()
func ExtInt8(keys string, defVal... interface{}) int8 {
	return Conf.ExtInt8(keys, defVal...)
}

// ExtInt16 is a shorcut for (c *Config) ExtInt16()
func ExtInt16(keys string, defVal... interface{}) int16 {
	return Conf.ExtInt16(keys, defVal...)
}

// ExtInt32 is a shorcut for (c *Config) ExtInt32()
func ExtInt32(keys string, defVal... interface{}) int32 {
	return Conf.ExtInt32(keys, defVal...)
}

// ExtInt64 is a shorcut for (c *Config) ExtInt64()
func ExtInt64(keys string, defVal... interface{}) int64 {
	return Conf.ExtInt64(keys, defVal...)
}

// ExtFloat32 is a shorcut for (c *Config) ExtFloat32()
func ExtFloat32(keys string, defVal... interface{}) float32 {
	return Conf.ExtFloat32(keys, defVal...)
}

// ExtFloat64 is a shorcut for (c *Config) ExtFloat64()
func ExtFloat64(keys string, defVal... interface{}) float64 {
	return Conf.ExtFloat64(keys, defVal...)
}

func init() {
	LoadConf("conf.yml")
}
<|endoftext|>


<commit_before>// Copyright 2017 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/gt/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package conf

import (
	"log"
	"sync"
	// "time"

	"github.com/BurntSushi/toml"
	"github.com/fsnotify/fsnotify"
)

// NewWatcher new fsnotify watcher
func NewWatcher(paths string, v interface{}) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				log.Println("watcher events:", event)
				// if event.Op&fsnotify.Chmod == fsnotify.Chmod {
				// 	log.Println("watcher.Events: ignore CHMOD event:", event)
				// 	continue
				// }

				if event.Op&fsnotify.Write == fsnotify.Write {
					// log.Println("modified file:", event.Name)
					Init(paths, v)
					// log.Println("watch config...", config)
					log.Println("watch config...", v)
				}
			case err := <-watcher.Errors:
				log.Println("watcher.Errors error:", err)
			}
		}
	}()

	err = watcher.Add(paths)
	if err != nil {
		log.Fatal(err)
	}
	<-done
}

var (
	// config     Config
	configLock = new(sync.RWMutex)
)

// Init toml config
func Init(tpath string, v interface{}) {
	configLock.Lock()
	if _, err := toml.DecodeFile(tpath, v); err != nil {
		log.Println("toml.DecodeFile error", err)
		return
	}
	configLock.Unlock()
}
<commit_msg>update conf code<commit_after>// Copyright 2017 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/gt/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package conf

import (
	"log"
	"sync"
	// "time"

	"github.com/BurntSushi/toml"
	"github.com/fsnotify/fsnotify"
)

// NewWatcher new fsnotify watcher
func NewWatcher(paths string, config interface{}) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("fsnotify.NewWatcher(): ", err)
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				log.Println("watcher events: ", event)
				// if event.Op&fsnotify.Chmod == fsnotify.Chmod {
				// 	log.Println("watcher.Events: ignore CHMOD event: ", event)
				// 	continue
				// }

				if event.Op&fsnotify.Write == fsnotify.Write {
					// log.Println("modified file: ", event.Name)
					Init(paths, config)
					// log.Println("watch config... ", config)
					log.Println("watch config... ", config)
				}
			case err := <-watcher.Errors:
				log.Println("watcher.Errors error: ", err)
			}
		}
	}()

	err = watcher.Add(paths)
	if err != nil {
		log.Fatal(err)
	}
	<-done
}

var (
	// config     Config
	confLock = new(sync.RWMutex)
)

// Init toml config
func Init(tpath string, config interface{}) {
	confLock.Lock()
	if _, err := toml.DecodeFile(tpath, config); err != nil {
		log.Println("toml.DecodeFile error ", err)
		return
	}
	confLock.Unlock()
}
<|endoftext|>


<commit_before>// Package websocketproxy is a reverse proxy for WebSocket connections.
package websocketproxy

import (
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/gorilla/websocket"
)

var (
	// DefaultUpgrader specifies the parameters for upgrading an HTTP
	// connection to a WebSocket connection.
	DefaultUpgrader = &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}

	// DefaultDialer is a dialer with all fields set to the default zero values.
	DefaultDialer = websocket.DefaultDialer
)

// WebsocketProxy is an HTTP Handler that takes an incoming WebSocket
// connection and proxies it to another server.
type WebsocketProxy struct {
	// Backend returns the backend URL which the proxy uses to reverse proxy
	// the incoming WebSocket connection. Request is the initial incoming and
	// unmodified request.
	Backend func(*http.Request) *url.URL

	// Upgrader specifies the parameters for upgrading a incoming HTTP
	// connection to a WebSocket connection. If nil, DefaultUpgrader is used.
	Upgrader *websocket.Upgrader

	//  Dialer contains options for connecting to the backend WebSocket server.
	//  If nil, DefaultDialer is used.
	Dialer *websocket.Dialer
}

// ProxyHandler returns a new http.Handler interface that reverse proxies the
// request to the given target.
func ProxyHandler(target *url.URL) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		NewProxy(target).ServeHTTP(rw, req)
	})
}

// NewProxy returns a new Websocket reverse proxy that rewrites the
// URL's to the scheme, host and base path provider in target.
func NewProxy(target *url.URL) *WebsocketProxy {
	backend := func(r *http.Request) *url.URL {
		// Shallow copy
		u := *target
		u.Fragment = r.URL.Fragment
		u.Path = r.URL.Path
		u.RawQuery = r.URL.RawQuery
		return &u
	}
	return &WebsocketProxy{Backend: backend}
}

// ServeHTTP implements the http.Handler that proxies WebSocket connections.
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if w.Backend == nil {
		log.Println("websocketproxy: backend function is not defined")
		http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError)
		return
	}

	backendURL := w.Backend(req)
	if backendURL == nil {
		log.Println("websocketproxy: backend URL is nil")
		http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError)
		return
	}

	dialer := w.Dialer
	if w.Dialer == nil {
		dialer = DefaultDialer
	}

	// Pass headers from the incoming request to the dialer to forward them to
	// the final destinations.
	requestHeader := http.Header{}
	requestHeader.Add("Origin", req.Header.Get("Origin"))
	for _, prot := range req.Header[http.CanonicalHeaderKey("Sec-WebSocket-Protocol")] {
		requestHeader.Add("Sec-WebSocket-Protocol", prot)
	}
	for _, cookie := range req.Header[http.CanonicalHeaderKey("Cookie")] {
		requestHeader.Add("Cookie", cookie)
	}

	// Pass X-Forwarded-For headers too, code below is a part of
	// httputil.ReverseProxy. See http://en.wikipedia.org/wiki/X-Forwarded-For
	// for more information
	// TODO: use RFC7239 http://tools.ietf.org/html/rfc7239
	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		// If we aren't the first proxy retain prior
		// X-Forwarded-For information as a comma+space
		// separated list and fold multiple headers into one.
		if prior, ok := req.Header["X-Forwarded-For"]; ok {
			clientIP = strings.Join(prior, ", ") + ", " + clientIP
		}
		requestHeader.Set("X-Forwarded-For", clientIP)
	}

	// Set the originating protocol of the incoming HTTP request. The SSL might
	// be terminated on our site and because we doing proxy adding this would
	// be helpful for applications on the backend.
	requestHeader.Set("X-Forwarded-Proto", "http")
	if req.TLS != nil {
		requestHeader.Set("X-Forwarded-Proto", "https")
	}

	// Connect to the backend URL, also pass the headers we get from the requst
	// together with the Forwarded headers we prepared above.
	// TODO: support multiplexing on the same backend connection instead of
	// opening a new TCP connection time for each request. This should be
	// optional:
	// http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01
	connBackend, resp, err := dialer.Dial(backendURL.String(), requestHeader)
	if err != nil {
		log.Printf("websocketproxy: couldn't dial to remote backend url %s\n", err)
		return
	}
	defer connBackend.Close()

	upgrader := w.Upgrader
	if w.Upgrader == nil {
		upgrader = DefaultUpgrader
	}

	// Only pass those headers to the upgrader.
	upgradeHeader := http.Header{}
	if hdr := resp.Header.Get("Sec-WebSocket-Protocol"); hdr != "" {
		upgradeHeader.Set("Sec-WebSocket-Protocol", hdr)
	}
	if hdr := resp.Header.Get("Set-Cookie"); hdr != "" {
		upgradeHeader.Set("Set-Cookie", hdr)
	}

	// Now upgrade the existing incoming request to a WebSocket connection.
	// Also pass the header that we gathered from the Dial handshake.
	connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
	if err != nil {
		log.Printf("websocketproxy: couldn't upgrade %s\n", err)
		return
	}
	defer connPub.Close()

	errc := make(chan error, 2)
	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errc <- err
	}

	// Start our proxy now, everything is ready...
	go cp(connBackend.UnderlyingConn(), connPub.UnderlyingConn())
	go cp(connPub.UnderlyingConn(), connBackend.UnderlyingConn())
	<-errc
}
<commit_msg>use the canonical header form<commit_after>// Package websocketproxy is a reverse proxy for WebSocket connections.
package websocketproxy

import (
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/gorilla/websocket"
)

var (
	// DefaultUpgrader specifies the parameters for upgrading an HTTP
	// connection to a WebSocket connection.
	DefaultUpgrader = &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}

	// DefaultDialer is a dialer with all fields set to the default zero values.
	DefaultDialer = websocket.DefaultDialer
)

// WebsocketProxy is an HTTP Handler that takes an incoming WebSocket
// connection and proxies it to another server.
type WebsocketProxy struct {
	// Backend returns the backend URL which the proxy uses to reverse proxy
	// the incoming WebSocket connection. Request is the initial incoming and
	// unmodified request.
	Backend func(*http.Request) *url.URL

	// Upgrader specifies the parameters for upgrading a incoming HTTP
	// connection to a WebSocket connection. If nil, DefaultUpgrader is used.
	Upgrader *websocket.Upgrader

	//  Dialer contains options for connecting to the backend WebSocket server.
	//  If nil, DefaultDialer is used.
	Dialer *websocket.Dialer
}

// ProxyHandler returns a new http.Handler interface that reverse proxies the
// request to the given target.
func ProxyHandler(target *url.URL) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		NewProxy(target).ServeHTTP(rw, req)
	})
}

// NewProxy returns a new Websocket reverse proxy that rewrites the
// URL's to the scheme, host and base path provider in target.
func NewProxy(target *url.URL) *WebsocketProxy {
	backend := func(r *http.Request) *url.URL {
		// Shallow copy
		u := *target
		u.Fragment = r.URL.Fragment
		u.Path = r.URL.Path
		u.RawQuery = r.URL.RawQuery
		return &u
	}
	return &WebsocketProxy{Backend: backend}
}

// ServeHTTP implements the http.Handler that proxies WebSocket connections.
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if w.Backend == nil {
		log.Println("websocketproxy: backend function is not defined")
		http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError)
		return
	}

	backendURL := w.Backend(req)
	if backendURL == nil {
		log.Println("websocketproxy: backend URL is nil")
		http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError)
		return
	}

	dialer := w.Dialer
	if w.Dialer == nil {
		dialer = DefaultDialer
	}

	// Pass headers from the incoming request to the dialer to forward them to
	// the final destinations.
	requestHeader := http.Header{}
	requestHeader.Add("Origin", req.Header.Get("Origin"))
	for _, prot := range req.Header[http.CanonicalHeaderKey("Sec-WebSocket-Protocol")] {
		requestHeader.Add("Sec-WebSocket-Protocol", prot)
	}
	for _, cookie := range req.Header[http.CanonicalHeaderKey("Cookie")] {
		requestHeader.Add("Cookie", cookie)
	}

	// Pass X-Forwarded-For headers too, code below is a part of
	// httputil.ReverseProxy. See http://en.wikipedia.org/wiki/X-Forwarded-For
	// for more information
	// TODO: use RFC7239 http://tools.ietf.org/html/rfc7239
	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		// If we aren't the first proxy retain prior
		// X-Forwarded-For information as a comma+space
		// separated list and fold multiple headers into one.
		if prior, ok := req.Header["X-Forwarded-For"]; ok {
			clientIP = strings.Join(prior, ", ") + ", " + clientIP
		}
		requestHeader.Set("X-Forwarded-For", clientIP)
	}

	// Set the originating protocol of the incoming HTTP request. The SSL might
	// be terminated on our site and because we doing proxy adding this would
	// be helpful for applications on the backend.
	requestHeader.Set("X-Forwarded-Proto", "http")
	if req.TLS != nil {
		requestHeader.Set("X-Forwarded-Proto", "https")
	}

	// Connect to the backend URL, also pass the headers we get from the requst
	// together with the Forwarded headers we prepared above.
	// TODO: support multiplexing on the same backend connection instead of
	// opening a new TCP connection time for each request. This should be
	// optional:
	// http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01
	connBackend, resp, err := dialer.Dial(backendURL.String(), requestHeader)
	if err != nil {
		log.Printf("websocketproxy: couldn't dial to remote backend url %s\n", err)
		return
	}
	defer connBackend.Close()

	upgrader := w.Upgrader
	if w.Upgrader == nil {
		upgrader = DefaultUpgrader
	}

	// Only pass those headers to the upgrader.
	upgradeHeader := http.Header{}
	if hdr := resp.Header.Get("Sec-Websocket-Protocol"); hdr != "" {
		upgradeHeader.Set("Sec-Websocket-Protocol", hdr)
	}
	if hdr := resp.Header.Get("Set-Cookie"); hdr != "" {
		upgradeHeader.Set("Set-Cookie", hdr)
	}

	// Now upgrade the existing incoming request to a WebSocket connection.
	// Also pass the header that we gathered from the Dial handshake.
	connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
	if err != nil {
		log.Printf("websocketproxy: couldn't upgrade %s\n", err)
		return
	}
	defer connPub.Close()

	errc := make(chan error, 2)
	cp := func(dst io.Writer, src io.Reader) {
		_, err := io.Copy(dst, src)
		errc <- err
	}

	// Start our proxy now, everything is ready...
	go cp(connBackend.UnderlyingConn(), connPub.UnderlyingConn())
	go cp(connPub.UnderlyingConn(), connBackend.UnderlyingConn())
	<-errc
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import "context"

// DatabaseCollections provides access to all collections in a single database.
type DatabaseCollections interface {
	// Collection opens a connection to an existing collection within the database.
	// If no collection with given name exists, an NotFoundError is returned.
	Collection(ctx context.Context, name string) (Collection, error)

	// CollectionExists returns true if a collection with given name exists within the database.
	CollectionExists(ctx context.Context, name string) (bool, error)

	// Collections returns a list of all collections in the database.
	Collections(ctx context.Context) ([]Collection, error)

	// CreateCollection creates a new collection with given name and options, and opens a connection to it.
	// If a collection with given name already exists within the database, a DuplicateError is returned.
	CreateCollection(ctx context.Context, name string, options *CreateCollectionOptions) (Collection, error)
}

// CreateCollectionOptions contains options that customize the creating of a collection.
type CreateCollectionOptions struct {
	// The maximal size of a journal or datafile in bytes. The value must be at least 1048576 (1 MiB). (The default is a configuration parameter)
	JournalSize int `json:"journalSize,omitempty"`
	// ReplicationFactor in a cluster (default is 1), this attribute determines how many copies of each shard are kept on different DBServers.
	// The value 1 means that only one copy (no synchronous replication) is kept.
	// A value of k means that k-1 replicas are kept. Any two copies reside on different DBServers.
	// Replication between them is synchronous, that is, every write operation to the "leader" copy will be replicated to all "follower" replicas,
	// before the write operation is reported successful. If a server fails, this is detected automatically
	// and one of the servers holding copies take over, usually without an error being reported.
	ReplicationFactor int `json:"replicationFactor,omitempty"`
	// If true then the data is synchronized to disk before returning from a document create, update, replace or removal operation. (default: false)
	WaitForSync bool `json:"waitForSync,omitempty"`
	// Whether or not the collection will be compacted (default is true)
	DoCompact *bool `json:"doCompact,omitempty"`
	// If true then the collection data is kept in-memory only and not made persistent.
	// Unloading the collection will cause the collection data to be discarded. Stopping or re-starting the server will also
	// cause full loss of data in the collection. Setting this option will make the resulting collection be slightly faster
	// than regular collections because ArangoDB does not enforce any synchronization to disk and does not calculate any
	// CRC checksums for datafiles (as there are no datafiles). This option should therefore be used for cache-type collections only,
	// and not for data that cannot be re-created otherwise. (The default is false)
	IsVolatile bool `json:"isVolatile,omitempty"`
	// In a cluster, this attribute determines which document attributes are used to
	// determine the target shard for documents. Documents are sent to shards based on the values of their shard key attributes.
	// The values of all shard key attributes in a document are hashed, and the hash value is used to determine the target shard.
	// Note: Values of shard key attributes cannot be changed once set. This option is meaningless in a single server setup.
	// The default is []string{"_key"}.
	ShardKeys []string `json:"shardKeys,omitempty"`
	// In a cluster, this value determines the number of shards to create for the collection. In a single server setup, this option is meaningless. (default is 1)
	NumberOfShards int `json:"numberOfShards,omitempty"`
	// If true, create a system collection. In this case collection-name should start with an underscore.
	// End users should normally create non-system collections only. API implementors may be required to create system
	// collections in very special occasions, but normally a regular collection will do. (The default is false)
	IsSystem bool `json:"isSystem,omitempty"`
	// The type of the collection to create. (default is CollectionTypeDocument)
	Type CollectionType `json:"type,omitempty"`
	// The number of buckets into which indexes using a hash table are split. The default is 16 and this number has to be a power
	// of 2 and less than or equal to 1024. For very large collections one should increase this to avoid long pauses when the hash
	// table has to be initially built or resized, since buckets are resized individually and can be initially built in parallel.
	// For example, 64 might be a sensible value for a collection with 100 000 000 documents.
	// Currently, only the edge index respects this value, but other index types might follow in future ArangoDB versions.
	// Changes are applied when the collection is loaded the next time.
	IndexBuckets int `json:"indexBuckets,omitempty"`
	// Specifies how keys in the collection are created.
	KeyOptions *CollectionKeyOptions `json:"keyOptions,omitempty"`
	// This field is used for internal purposes only. DO NOT USE.
	DistributeShardsLike string `json:"distributeShardsLike,omitempty"`
}

// CollectionType is the type of a collection.
type CollectionType int

const (
	// CollectionTypeDocument specifies a document collection
	CollectionTypeDocument = CollectionType(2)
	// CollectionTypeEdge specifies an edges collection
	CollectionTypeEdge = CollectionType(3)
)

// CollectionKeyOptions specifies ways for creating keys of a collection.
type CollectionKeyOptions struct {
	// If set to true, then it is allowed to supply own key values in the _key attribute of a document.
	// If set to false, then the key generator will solely be responsible for generating keys and supplying own
	// key values in the _key attribute of documents is considered an error.
	AllowUserKeys bool `json:"allowUserKeys,omitempty"`
	// Specifies the type of the key generator. The currently available generators are traditional and autoincrement.
	Type KeyGeneratorType `json:"type,omitempty"`
	// increment value for autoincrement key generator. Not used for other key generator types.
	Increment int `json:"increment,omitempty"`
	// Initial offset value for autoincrement key generator. Not used for other key generator types.
	Offset int `json:"offset,omitempty"`
}

// KeyGeneratorType is a type of key generated, used in `CollectionKeyOptions`.
type KeyGeneratorType string

const (
	KeyGeneratorTraditional   = KeyGeneratorType("traditional")
	KeyGeneratorAutoIncrement = KeyGeneratorType("autoincrement")
)
<commit_msg>Added IsSmart, SmartGraphAttribute attributes to CreateCollectionOptions<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import "context"

// DatabaseCollections provides access to all collections in a single database.
type DatabaseCollections interface {
	// Collection opens a connection to an existing collection within the database.
	// If no collection with given name exists, an NotFoundError is returned.
	Collection(ctx context.Context, name string) (Collection, error)

	// CollectionExists returns true if a collection with given name exists within the database.
	CollectionExists(ctx context.Context, name string) (bool, error)

	// Collections returns a list of all collections in the database.
	Collections(ctx context.Context) ([]Collection, error)

	// CreateCollection creates a new collection with given name and options, and opens a connection to it.
	// If a collection with given name already exists within the database, a DuplicateError is returned.
	CreateCollection(ctx context.Context, name string, options *CreateCollectionOptions) (Collection, error)
}

// CreateCollectionOptions contains options that customize the creating of a collection.
type CreateCollectionOptions struct {
	// The maximal size of a journal or datafile in bytes. The value must be at least 1048576 (1 MiB). (The default is a configuration parameter)
	JournalSize int `json:"journalSize,omitempty"`
	// ReplicationFactor in a cluster (default is 1), this attribute determines how many copies of each shard are kept on different DBServers.
	// The value 1 means that only one copy (no synchronous replication) is kept.
	// A value of k means that k-1 replicas are kept. Any two copies reside on different DBServers.
	// Replication between them is synchronous, that is, every write operation to the "leader" copy will be replicated to all "follower" replicas,
	// before the write operation is reported successful. If a server fails, this is detected automatically
	// and one of the servers holding copies take over, usually without an error being reported.
	ReplicationFactor int `json:"replicationFactor,omitempty"`
	// If true then the data is synchronized to disk before returning from a document create, update, replace or removal operation. (default: false)
	WaitForSync bool `json:"waitForSync,omitempty"`
	// Whether or not the collection will be compacted (default is true)
	DoCompact *bool `json:"doCompact,omitempty"`
	// If true then the collection data is kept in-memory only and not made persistent.
	// Unloading the collection will cause the collection data to be discarded. Stopping or re-starting the server will also
	// cause full loss of data in the collection. Setting this option will make the resulting collection be slightly faster
	// than regular collections because ArangoDB does not enforce any synchronization to disk and does not calculate any
	// CRC checksums for datafiles (as there are no datafiles). This option should therefore be used for cache-type collections only,
	// and not for data that cannot be re-created otherwise. (The default is false)
	IsVolatile bool `json:"isVolatile,omitempty"`
	// In a cluster, this attribute determines which document attributes are used to
	// determine the target shard for documents. Documents are sent to shards based on the values of their shard key attributes.
	// The values of all shard key attributes in a document are hashed, and the hash value is used to determine the target shard.
	// Note: Values of shard key attributes cannot be changed once set. This option is meaningless in a single server setup.
	// The default is []string{"_key"}.
	ShardKeys []string `json:"shardKeys,omitempty"`
	// In a cluster, this value determines the number of shards to create for the collection. In a single server setup, this option is meaningless. (default is 1)
	NumberOfShards int `json:"numberOfShards,omitempty"`
	// If true, create a system collection. In this case collection-name should start with an underscore.
	// End users should normally create non-system collections only. API implementors may be required to create system
	// collections in very special occasions, but normally a regular collection will do. (The default is false)
	IsSystem bool `json:"isSystem,omitempty"`
	// The type of the collection to create. (default is CollectionTypeDocument)
	Type CollectionType `json:"type,omitempty"`
	// The number of buckets into which indexes using a hash table are split. The default is 16 and this number has to be a power
	// of 2 and less than or equal to 1024. For very large collections one should increase this to avoid long pauses when the hash
	// table has to be initially built or resized, since buckets are resized individually and can be initially built in parallel.
	// For example, 64 might be a sensible value for a collection with 100 000 000 documents.
	// Currently, only the edge index respects this value, but other index types might follow in future ArangoDB versions.
	// Changes are applied when the collection is loaded the next time.
	IndexBuckets int `json:"indexBuckets,omitempty"`
	// Specifies how keys in the collection are created.
	KeyOptions *CollectionKeyOptions `json:"keyOptions,omitempty"`
	// This field is used for internal purposes only. DO NOT USE.
	DistributeShardsLike string `json:"distributeShardsLike,omitempty"`
	// TODO: Document me
	IsSmart bool `json:"isSmart:omitempty"`
	// TODO: Document me
	SmartGraphAttribute string `json:"smartGraphAttribute,omitempty"`
}

// CollectionType is the type of a collection.
type CollectionType int

const (
	// CollectionTypeDocument specifies a document collection
	CollectionTypeDocument = CollectionType(2)
	// CollectionTypeEdge specifies an edges collection
	CollectionTypeEdge = CollectionType(3)
)

// CollectionKeyOptions specifies ways for creating keys of a collection.
type CollectionKeyOptions struct {
	// If set to true, then it is allowed to supply own key values in the _key attribute of a document.
	// If set to false, then the key generator will solely be responsible for generating keys and supplying own
	// key values in the _key attribute of documents is considered an error.
	AllowUserKeys bool `json:"allowUserKeys,omitempty"`
	// Specifies the type of the key generator. The currently available generators are traditional and autoincrement.
	Type KeyGeneratorType `json:"type,omitempty"`
	// increment value for autoincrement key generator. Not used for other key generator types.
	Increment int `json:"increment,omitempty"`
	// Initial offset value for autoincrement key generator. Not used for other key generator types.
	Offset int `json:"offset,omitempty"`
}

// KeyGeneratorType is a type of key generated, used in `CollectionKeyOptions`.
type KeyGeneratorType string

const (
	KeyGeneratorTraditional   = KeyGeneratorType("traditional")
	KeyGeneratorAutoIncrement = KeyGeneratorType("autoincrement")
)
<|endoftext|>


<commit_before>// Package davelog implements a minimal logging package.
// Inspired by article of Dave Cheney.
// https://dave.cheney.net/2015/11/05/lets-talk-about-logging
package davelog
<commit_msg>davelog: add New<commit_after>// Package davelog implements a minimal logging package.
// Inspired by article of Dave Cheney.
// https://dave.cheney.net/2015/11/05/lets-talk-about-logging
package davelog

import (
	"io"
	"log"
)

// A Log represents an active logger.
type Log struct {
	log     *log.Logger
	verbose bool
}

// New creates logger.
func New(w io.Writer, verbose bool) *Log {
	l := Log{
		log:     log.New(w, "", 0),
		verbose: verbose,
	}
	return &l
}
<|endoftext|>