<commit_before>// Alexandria
//
// Copyright (C) 2015-2016  Colin Benner
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package alexandria

import (
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/blevesearch/bleve"
)

func touch(file string) error {
	now := time.Now()
	return os.Chtimes(file, now, now)
}

// Run index++ to generate a (new) swish++ index file.
func GenerateIndex() error {
	newIndex := false
	// Try to open an existing index or create a new one if none exists.
	index, err := openIndex()
	if err != nil {
		mapping := bleve.NewIndexMapping()
		index, err = bleve.New(Config.AlexandriaDirectory+"bleve", mapping)
		if err != nil {
			return err
		}
		newIndex = true
	}
	defer index.Close()

	files, err := ioutil.ReadDir(Config.KnowledgeDirectory)
	if err != nil {
		return err
	}

	indexUpdateFile := Config.AlexandriaDirectory + "index_updated"
	indexUpdateTime, err := getModTime(indexUpdateFile)
	if err != nil {
		LogError(err)
		return nil
	}
	// Save the time of this indexing operation
	_ = touch(Config.AlexandriaDirectory + "index_updated")

	batch := index.NewBatch()
	for _, file := range files {
		// Check whether the scroll is newer than the index.
		modTime, err := getModTime(Config.KnowledgeDirectory + file.Name())
		if err != nil {
			LogError(err)
			continue
		}
		id := strings.TrimSuffix(file.Name(), ".tex")
		if modTime < indexUpdateTime && !newIndex {
			continue
		}

		// Load and parse the scroll content
		contentBytes, err := ioutil.ReadFile(Config.KnowledgeDirectory + file.Name())
		TryLogError(err)
		content := string(contentBytes)
		metadata := ParseMetadata(content)
		scroll := Scroll{metadata, Id(id), content}

		batch.Index(id, scroll)
	}
	index.Batch(batch)

	return nil
}

func RemoveFromIndex(id Id) error {
	index, err := openIndex()
	if err != nil {
		return err
	}
	defer index.Close()
	return index.Delete(string(id))
}

// Open the bleve index
func openIndex() (bleve.Index, error) {
	return bleve.Open(Config.AlexandriaDirectory + "bleve")
}

// Search the swish index for a given query.
func searchBleve(queryString string) (Results, error) {
	index, err := openIndex()
	if err != nil {
		LogError(err)
		return Results{}, err
	}
	defer index.Close()

	newQueryString := ""
	for _, tmp := range strings.Split(queryString, " ") {
		word := strings.TrimSpace(tmp)
		if word[0] == '-' || word[0] == '+' {
			newQueryString += " " + word
		} else if word[0] == '~' {
			// Remove prefix to make term optional
			newQueryString += " " + word[1:]
		} else {
			newQueryString += " +" + word
		}
	}

	query := bleve.NewQueryStringQuery(newQueryString[1:]) // Remove leading space
	search := bleve.NewSearchRequest(query)
	search.Size = Config.MaxResults
	searchResults, err := index.Search(search)
	if err != nil {
		println("Invalid query string: '" + newQueryString[1:] + "'")
		LogError(err)
		return Results{}, err
	}

	var ids []Id
	for _, match := range searchResults.Hits {
		ids = append(ids, Id(match.ID))
	}

	return Results{ids[:len(searchResults.Hits)], int(searchResults.Total)}, nil
}

// Get a list of scrolls matching the query.
func FindScrolls(query string) (Results, error) {
	results, err := searchBleve(query)
	if err != nil {
		return Results{}, err
	}
	n := ProcessScrolls(results.Ids)
	ids := make([]Id, n)
	i := 0
	for _, id := range results.Ids {
		if _, err := os.Stat(Config.KnowledgeDirectory + string(id) + ".tex"); os.IsNotExist(err) {
			continue
		}
		ids[i] = id
		i += 1
	}
	results.Total = n // The number of hits can be wrong if scrolls have been deleted

	return results, nil
}

func ComputeStatistics() Statistics {
	index, err := openIndex()
	if err != nil {
		LogError(err)
	}
	defer index.Close()

	num, size := getDirSize(Config.KnowledgeDirectory)
	if err == nil {
		tmp, err := index.DocCount()
		if err != nil {
			LogError(err)
		} else {
			num = int(tmp)
		}
	}

	return Stats{num, size}
}
<commit_msg>Use the English analyser<commit_after>// Alexandria
//
// Copyright (C) 2015-2016  Colin Benner
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package alexandria

import (
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/blevesearch/bleve"
)

func touch(file string) error {
	now := time.Now()
	return os.Chtimes(file, now, now)
}

// Run index++ to generate a (new) swish++ index file.
func GenerateIndex() error {
	newIndex := false
	// Try to open an existing index or create a new one if none exists.
	index, err := openIndex()
	if err != nil {
		mapping := bleve.NewIndexMapping()
		mapping.DefaultAnalyzer = "en"
		index, err = bleve.New(Config.AlexandriaDirectory+"bleve", mapping)
		if err != nil {
			return err
		}
		newIndex = true
	}
	defer index.Close()

	files, err := ioutil.ReadDir(Config.KnowledgeDirectory)
	if err != nil {
		return err
	}

	indexUpdateFile := Config.AlexandriaDirectory + "index_updated"
	indexUpdateTime, err := getModTime(indexUpdateFile)
	if err != nil {
		LogError(err)
		return nil
	}
	// Save the time of this indexing operation
	_ = touch(Config.AlexandriaDirectory + "index_updated")

	batch := index.NewBatch()
	for _, file := range files {
		// Check whether the scroll is newer than the index.
		modTime, err := getModTime(Config.KnowledgeDirectory + file.Name())
		if err != nil {
			LogError(err)
			continue
		}
		id := strings.TrimSuffix(file.Name(), ".tex")
		if modTime < indexUpdateTime && !newIndex {
			continue
		}

		// Load and parse the scroll content
		contentBytes, err := ioutil.ReadFile(Config.KnowledgeDirectory + file.Name())
		TryLogError(err)
		content := string(contentBytes)
		metadata := ParseMetadata(content)
		scroll := Scroll{metadata, Id(id), content}

		batch.Index(id, scroll)
	}
	index.Batch(batch)

	return nil
}

func RemoveFromIndex(id Id) error {
	index, err := openIndex()
	if err != nil {
		return err
	}
	defer index.Close()
	return index.Delete(string(id))
}

// Open the bleve index
func openIndex() (bleve.Index, error) {
	return bleve.Open(Config.AlexandriaDirectory + "bleve")
}

// Search the swish index for a given query.
func searchBleve(queryString string) (Results, error) {
	index, err := openIndex()
	if err != nil {
		LogError(err)
		return Results{}, err
	}
	defer index.Close()

	newQueryString := ""
	for _, tmp := range strings.Split(queryString, " ") {
		word := strings.TrimSpace(tmp)
		if word[0] == '-' || word[0] == '+' {
			newQueryString += " " + word
		} else if word[0] == '~' {
			// Remove prefix to make term optional
			newQueryString += " " + word[1:]
		} else {
			newQueryString += " +" + word
		}
	}

	query := bleve.NewQueryStringQuery(newQueryString[1:]) // Remove leading space
	search := bleve.NewSearchRequest(query)
	search.Size = Config.MaxResults
	searchResults, err := index.Search(search)
	if err != nil {
		println("Invalid query string: '" + newQueryString[1:] + "'")
		LogError(err)
		return Results{}, err
	}

	var ids []Id
	for _, match := range searchResults.Hits {
		ids = append(ids, Id(match.ID))
	}

	return Results{ids[:len(searchResults.Hits)], int(searchResults.Total)}, nil
}

// Get a list of scrolls matching the query.
func FindScrolls(query string) (Results, error) {
	results, err := searchBleve(query)
	if err != nil {
		return Results{}, err
	}
	n := ProcessScrolls(results.Ids)
	ids := make([]Id, n)
	i := 0
	for _, id := range results.Ids {
		if _, err := os.Stat(Config.KnowledgeDirectory + string(id) + ".tex"); os.IsNotExist(err) {
			continue
		}
		ids[i] = id
		i += 1
	}
	results.Total = n // The number of hits can be wrong if scrolls have been deleted

	return results, nil
}

func ComputeStatistics() Statistics {
	index, err := openIndex()
	if err != nil {
		LogError(err)
	}
	defer index.Close()

	num, size := getDirSize(Config.KnowledgeDirectory)
	if err == nil {
		tmp, err := index.DocCount()
		if err != nil {
			LogError(err)
		} else {
			num = int(tmp)
		}
	}

	return Stats{num, size}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"

	"github.com/hawx/persona"
	"github.com/hawx/serve"
	"hawx.me/code/mux"

	"hawx.me/code/riviera-admin/actions"
	"hawx.me/code/riviera-admin/views"
)

const HELP = `Usage: riviera-admin [options]

  An admin panel for riviera

    --port <num>        # Port to bind to (default: 8081)
    --socket <path>     # Serve using a unix socket instead
    --riviera <url>     # Url to riviera (default: http://localhost:8080/)

    --audience <host>   # Host and port site is running under (default: http://localhost:8081)
    --user <email>      # User who can access the admin panel
    --secret <str>      # String to use as cookie secret
    --path-prefix <p>   # Path prefix serving on

    --help              # Display help message
`

var (
	port       = flag.String("port", "8081", "")
	socket     = flag.String("socket", "", "")
	riviera    = flag.String("riviera", "http://localhost:8080/", "")
	audience   = flag.String("audience", "http://localhost:8081", "")
	user       = flag.String("user", "", "")
	secret     = flag.String("secret", "some-secret", "")
	pathPrefix = flag.String("path-prefix", "", "")
	help       = flag.Bool("help", false, "")
)

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s", r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

var Login = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	views.Login.Execute(w, struct {
		PathPrefix string
	}{*pathPrefix})
})

type Feed struct {
	FeedUrl         string
	WebsiteUrl      string
	FeedTitle       string
	FeedDescription string
	Status          string
}

var List = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(*riviera + "-/list")
	if err != nil {
		log.Print("list", err)
		w.WriteHeader(500)
		return
	}

	var list []Feed
	err = json.NewDecoder(resp.Body).Decode(&list)
	if err != nil {
		log.Println("list", err)
		w.WriteHeader(500)
		return
	}

	w.Header().Add("Content-Type", "text/html")

	views.Index.Execute(w, struct {
		Url        string
		PathPrefix string
		Feeds      []Feed
	}{*audience, *pathPrefix, list})
})

var Subscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")

	err := actions.Subscribe(*riviera, url)
	if err != nil {
		log.Println("subscribe:", err)
		w.WriteHeader(500)
		return
	}

	if r.FormValue("redirect") == "origin" {
		http.Redirect(w, r, url, 301)
		return
	}

	http.Redirect(w, r, *pathPrefix+"/", 301)
})

var Unsubscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	err := actions.Unsubscribe(*riviera, r.FormValue("url"))
	if err != nil {
		log.Println(err)
		return
	}

	http.Redirect(w, r, *pathPrefix+"/", 301)
})

func main() {
	flag.Parse()

	if *help {
		fmt.Println(HELP)
		return
	}

	store := persona.NewStore(*secret)
	persona := persona.New(store, *audience, []string{*user})

	http.Handle("/", mux.Method{"GET": persona.Switch(List, Login)})
	http.Handle("/subscribe", persona.Protect(mux.Method{"GET": Subscribe}))
	http.Handle("/unsubscribe", persona.Protect(mux.Method{"GET": Unsubscribe}))
	http.Handle("/sign-in", mux.Method{"POST": persona.SignIn})
	http.Handle("/sign-out", mux.Method{"GET": persona.SignOut})

	serve.Serve(*port, *socket, Log(http.DefaultServeMux))
}
<commit_msg>Move to hawx.me/code<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"

	"hawx.me/code/mux"
	"hawx.me/code/persona"
	"hawx.me/code/serve"

	"hawx.me/code/riviera-admin/actions"
	"hawx.me/code/riviera-admin/views"
)

const HELP = `Usage: riviera-admin [options]

  An admin panel for riviera

    --port <num>        # Port to bind to (default: 8081)
    --socket <path>     # Serve using a unix socket instead
    --riviera <url>     # Url to riviera (default: http://localhost:8080/)

    --audience <host>   # Host and port site is running under (default: http://localhost:8081)
    --user <email>      # User who can access the admin panel
    --secret <str>      # String to use as cookie secret
    --path-prefix <p>   # Path prefix serving on

    --help              # Display help message
`

var (
	port       = flag.String("port", "8081", "")
	socket     = flag.String("socket", "", "")
	riviera    = flag.String("riviera", "http://localhost:8080/", "")
	audience   = flag.String("audience", "http://localhost:8081", "")
	user       = flag.String("user", "", "")
	secret     = flag.String("secret", "some-secret", "")
	pathPrefix = flag.String("path-prefix", "", "")
	help       = flag.Bool("help", false, "")
)

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s", r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

var Login = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	views.Login.Execute(w, struct {
		PathPrefix string
	}{*pathPrefix})
})

type Feed struct {
	FeedUrl         string
	WebsiteUrl      string
	FeedTitle       string
	FeedDescription string
	Status          string
}

var List = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(*riviera + "-/list")
	if err != nil {
		log.Print("list", err)
		w.WriteHeader(500)
		return
	}

	var list []Feed
	err = json.NewDecoder(resp.Body).Decode(&list)
	if err != nil {
		log.Println("list", err)
		w.WriteHeader(500)
		return
	}

	w.Header().Add("Content-Type", "text/html")

	views.Index.Execute(w, struct {
		Url        string
		PathPrefix string
		Feeds      []Feed
	}{*audience, *pathPrefix, list})
})

var Subscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")

	err := actions.Subscribe(*riviera, url)
	if err != nil {
		log.Println("subscribe:", err)
		w.WriteHeader(500)
		return
	}

	if r.FormValue("redirect") == "origin" {
		http.Redirect(w, r, url, 301)
		return
	}

	http.Redirect(w, r, *pathPrefix+"/", 301)
})

var Unsubscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	err := actions.Unsubscribe(*riviera, r.FormValue("url"))
	if err != nil {
		log.Println(err)
		return
	}

	http.Redirect(w, r, *pathPrefix+"/", 301)
})

func main() {
	flag.Parse()

	if *help {
		fmt.Println(HELP)
		return
	}

	store := persona.NewStore(*secret)
	persona := persona.New(store, *audience, []string{*user})

	http.Handle("/", mux.Method{"GET": persona.Switch(List, Login)})
	http.Handle("/subscribe", persona.Protect(mux.Method{"GET": Subscribe}))
	http.Handle("/unsubscribe", persona.Protect(mux.Method{"GET": Unsubscribe}))
	http.Handle("/sign-in", mux.Method{"POST": persona.SignIn})
	http.Handle("/sign-out", mux.Method{"GET": persona.SignOut})

	serve.Serve(*port, *socket, Log(http.DefaultServeMux))
}
<|endoftext|>


<commit_before>// Package alien is a lightweight http router from outer space.
package alien

import (
	"errors"
	"net/http"
	"path"
	"strings"
	"sync"
)

var (
	eof              = rune(0)
	errRouteNotFound = errors.New("route not found")
	errBadPattern    = errors.New("bad pattern")
	errUnknownMethod = errors.New("unknown http method")
	headerName       = "_alien"
)

type nodeType int

const (
	nodeRoot nodeType = iota
	nodeParam
	nodeNormal
	nodeCatchAll
	nodeEnd
)

type node struct {
	key      rune
	typ      nodeType
	mu       sync.RWMutex
	value    *route
	children []*node
}

func (n *node) branch(key rune, val *route, typ ...nodeType) *node {
	child := &node{
		key:   key,
		value: val,
	}
	if len(typ) > 0 {
		child.typ = typ[0]
	}
	n.children = append(n.children, child)
	return child
}

func (n *node) findChild(key rune) *node {
	for _, v := range n.children {
		if v.key == key {
			return v
		}
	}
	return nil
}

func (n *node) insert(pattern string, val *route) error {
	n.mu.Lock()
	defer n.mu.Unlock()
	if n.typ != nodeRoot {
		return errors.New("inserting on a non root node")
	}
	var level *node
	var child *node

	if pattern == "" {
		return errors.New("empty pattern is not supported")
	}

	for k, ch := range pattern {
		if k == 0 {
			if ch != '/' {
				return errors.New("path must start with slash ")
			}
			level = n
		}

		child = level.findChild(ch)
		switch level.typ {
		case nodeParam:
			if k < len(pattern) && ch != '/' {
				continue
			}
		}
		if child != nil {
			level = child
			continue
		}
		switch ch {
		case ':':
			level = level.branch(ch, nil, nodeParam)
		case '*':
			level = level.branch(ch, nil, nodeCatchAll)
		default:
			level = level.branch(ch, nil, nodeNormal)
		}
	}
	level.branch(eof, val, nodeEnd)
	return nil
}

func (n *node) find(path string) (*route, error) {
	n.mu.RLock()
	defer n.mu.RUnlock()
	if n.typ != nodeRoot {
		return nil, errors.New("non node search")
	}
	var level *node
	var isParam bool
	for k, ch := range path {
		if k == 0 {
			level = n
		}
		c := level.findChild(ch)
		if isParam {
			if k < len(path) && ch != '/' {
				continue
			}
			isParam = false
		}
		param := level.findChild(':')
		if param != nil {
			level = param
			isParam = true
			continue
		}
		catchAll := level.findChild('*')
		if catchAll != nil {
			level = catchAll
			break
		}
		if c != nil {
			level = c
			continue
		}
		return nil, errRouteNotFound
	}
	if level != nil {
		end := level.findChild(eof)
		if end != nil {
			return end.value, nil
		}
		if slash := level.findChild('/'); slash != nil {
			end = slash.findChild(eof)
			if end != nil {
				return end.value, nil
			}
		}
	}
	return nil, errRouteNotFound
}

type route struct {
	path       string
	middleware []func(http.Handler) http.Handler
	handler    func(http.ResponseWriter, *http.Request)
}

func (r *route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var base http.Handler
	if base == nil {
		base = http.HandlerFunc(r.handler)
	}
	for _, m := range r.middleware {
		base = m(base)
	}
	base.ServeHTTP(w, req)
}

// ParseParams parses params found in mateched from pattern. There are two kinds
// of params, one to capture a segment which starts with : and a nother to
// capture everything( a.k.a catch all) whis starts with *.
//
// For instance
//   pattern:="/hello/:name"
//   matched:="/hello/world"
// Will result into name:world. this function captures the named params and
// theri coreesponding values, returning them in a comma separated  string of a
// key:value nature. please see the tests for more details.
func ParseParams(matched, pattern string) (result string, err error) {
	if strings.Contains(pattern, ":") || strings.Contains(pattern, "*") {
		p1 := strings.Split(matched, "/")
		p2 := strings.Split(pattern, "/")
		s1 := len(p1)
		s2 := len(p2)
		if s1 < s2 {
			err = errBadPattern
			return
		}
		for k, v := range p2 {
			if len(v) > 0 {
				switch v[0] {
				case ':':
					if len(result) == 0 {
						result = v[1:] + ":" + p1[k]
						continue
					}
					result = result + "," + v[1:] + ":" + p1[k]
				case '*':
					name := "catch"
					if k != s2-1 {
						err = errBadPattern
						return
					}
					if len(v) > 1 {
						name = v[1:]
					}
					if len(result) == 0 {
						result = name + ":" + strings.Join(p1[k:], "/")
						return
					}
					result = result + "," + name + ":" + strings.Join(p1[k:], "/")
					return
				}
			}
		}
	}
	return
}

// Params stores route params.
type Params map[string]string

// Load loads params found in src into p.
func (p Params) Load(src string) {
	s := strings.Split(src, ",")
	var vars []string
	for _, v := range s {
		vars = strings.Split(v, ":")
		if len(vars) != 2 {
			continue
		}
		p[vars[0]] = vars[1]
	}
}

// Get returns value associated with key.
func (p Params) Get(key string) string {
	return p[key]
}

// GetParams returrns route params stored in r.
func GetParams(r *http.Request) Params {
	c := r.Header.Get(headerName)
	if c != "" {
		p := make(Params)
		p.Load(c)
		return p
	}
	return nil
}

type router struct {
	get, post, patch, put, head     *node
	connect, options, trace, delete *node
}

func (r *router) addRoute(method, path string, h func(http.ResponseWriter, *http.Request), wares ...func(http.Handler) http.Handler) error {
	newRoute := &route{path: path, handler: h}
	if len(wares) > 0 {
		newRoute.middleware = append(newRoute.middleware, wares...)
	}
	switch method {
	case "GET":
		if r.get == nil {
			r.get = &node{typ: nodeRoot}
		}
		return r.get.insert(path, newRoute)
	case "POST":
		if r.post == nil {
			r.post = &node{typ: nodeRoot}
		}
		return r.post.insert(path, newRoute)
	case "PUT":
		if r.put == nil {
			r.put = &node{typ: nodeRoot}
		}
		return r.put.insert(path, newRoute)
	case "PATCH":
		if r.patch == nil {
			r.patch = &node{typ: nodeRoot}
		}
		return r.patch.insert(path, newRoute)
	case "HEAD":
		if r.head == nil {
			r.head = &node{typ: nodeRoot}
		}
		return r.head.insert(path, newRoute)
	case "CONNECT":
		if r.connect == nil {
			r.connect = &node{typ: nodeRoot}
		}
		return r.connect.insert(path, newRoute)
	case "OPTIONS":
		if r.options == nil {
			r.options = &node{typ: nodeRoot}
		}
		return r.options.insert(path, newRoute)
	case "TRACE":
		if r.trace == nil {
			r.trace = &node{typ: nodeRoot}
		}
		return r.trace.insert(path, newRoute)
	case "DELETE":
		if r.delete == nil {
			r.delete = &node{typ: nodeRoot}
		}
		return r.delete.insert(path, newRoute)
	}
	return errnownMethod
}

func (r *router) find(method, path string) (*route, error) {
	switch method {
	case "GET":
		if r.get != nil {
			return r.get.find(path)
		}
	case "POST":
		if r.post != nil {
			return r.post.find(path)
		}
	case "PUT":
		if r.put != nil {
			return r.put.find(path)
		}
	case "PATCH":
		if r.patch != nil {
			return r.patch.find(path)
		}
	case "HEAD":
		if r.head != nil {
			return r.head.find(path)
		}
	case "CONNECT":
		if r.connect != nil {
			return r.connect.find(path)
		}
	case "OPTIONS":
		if r.options != nil {
			return r.options.find(path)
		}
	case "TRACE":
		if r.trace != nil {
			return r.trace.find(path)
		}
	case "DELETE":
		if r.delete != nil {
			return r.delete.find(path)
		}
	}
	return nil, errRouteNotFound
}

// Mux is a http multiplexer that allows matching of http requests to the
// registered http handlers.
//
// Mux supports named parameters in urls like
//   /hello/:name
// will match
//   /hello/world
// where by inside the request passed to the handler, the param with key name and
// value world will be passed.
//
// Mux supports catch all parameters too
//   /hello/*whatever
// will match
//   /hello/world
//   /hello/world/tanzania
//   /hello/world/afica/tanzania.png
// where by inside the request passed to the handler, the param with key
// whatever will be set and value will be
//   world
//   world/tanzania
//   world/afica/tanzania.png
//
// If you dont specify a name in a catch all route, then the default name "catch"
// will be ussed.
type Mux struct {
	prefix     string
	middleware []func(http.Handler) http.Handler
	notFound   http.Handler
	*router
}

// New returns a new *Mux instance with default handler for mismatched routes.
func New() *Mux {
	m := &Mux{}
	m.router = &router{}
	m.notFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, errRouteNotFound.Error(), http.StatusNotFound)
	})
	return m
}

// AddRoute registers h with pattern and method. If there is a path prefix
// created via the Group method) it will be set.
func (m *Mux) AddRoute(method, pattern string, h func(http.ResponseWriter, *http.Request)) error {
	if m.prefix != "" {
		pattern = path.Join(m.prefix, pattern)
	}
	return m.addRoute(method, pattern, h, m.middleware...)
}

// Get registers h with pattern and method GET.
func (m *Mux) Get(pattern string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("GET", pattern, h)
}

// Put registers h with pattern and method PUT.
func (m *Mux) Put(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PUT", path, h)
}

// Post registers h with pattern and method POST.
func (m *Mux) Post(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("POST", path, h)
}

// Patch registers h with pattern and method PATCH.
func (m *Mux) Patch(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PATCH", path, h)
}

// Head registers h with pattern and method HEAD.
func (m *Mux) Head(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("HEAD", path, h)
}

// Options registers h with pattern and method OPTIONS.
func (m *Mux) Options(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("OPTIONS", path, h)
}

// Connect  registers h with pattern and method CONNECT.
func (m *Mux) Connect(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("CONNECT", path, h)
}

// Trace registers h with pattern and method TRACE.
func (m *Mux) Trace(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("TRACE", path, h)
}

// Delete registers h with pattern and method DELETE.
func (m *Mux) Delete(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("DELETE", path, h)
}

// NotFoundHandler is executed when the request route is not found.
func (m *Mux) NotFoundHandler(h http.Handler) {
	m.notFound = h
}

// ServeHTTP implements http.Handler interface. It muliplexes http requests
// against registered handlers.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p := path.Clean(r.URL.Path)
	h, err := m.find(r.Method, p)
	if err != nil {
		m.notFound.ServeHTTP(w, r)
		return
	}
	params, _ := ParseParams(p, h.path) // check if there is any url params
	if params != "" {
		r.Header.Set(headerName, params)
	}
	h.ServeHTTP(w, r)
}

// Group creates a path prefix group for pattern, all routes registered using
// the returned Mux will only match if the request path starts with pattern. For
// instance .
//   m:=New()
//   home:=m.Group("/home")
//   home.Get("/alone",myHandler)
// will match
//   /home/alone
func (m *Mux) Group(pattern string) *Mux {
	return &Mux{
		prefix:     pattern,
		router:     m.router,
		middleware: m.middleware,
		notFound:   m.notFound,
	}

}

// Use assigns midlewares to the current *Mux. All routes registered by the *Mux
// after this call will have the middlewares assigned to them.
func (m *Mux) Use(middleware ...func(http.Handler) http.Handler) {
	if len(middleware) > 0 {
		m.middleware = append(m.middleware, middleware...)
	}
}
<commit_msg>Revert "Update alien.go"<commit_after>// Package alien is a lightweight http router from outer space.
package alien

import (
	"errors"
	"net/http"
	"path"
	"strings"
	"sync"
)

var (
	eof              = rune(0)
	errRouteNotFound = errors.New("route not found")
	errBadPattern    = errors.New("bad pattern")
	errUnknownMethod = errors.New("unkown http method")
	headerName       = "_alien"
)

type nodeType int

const (
	nodeRoot nodeType = iota
	nodeParam
	nodeNormal
	nodeCatchAll
	nodeEnd
)

type node struct {
	key      rune
	typ      nodeType
	mu       sync.RWMutex
	value    *route
	children []*node
}

func (n *node) branch(key rune, val *route, typ ...nodeType) *node {
	child := &node{
		key:   key,
		value: val,
	}
	if len(typ) > 0 {
		child.typ = typ[0]
	}
	n.children = append(n.children, child)
	return child
}

func (n *node) findChild(key rune) *node {
	for _, v := range n.children {
		if v.key == key {
			return v
		}
	}
	return nil
}

func (n *node) insert(pattern string, val *route) error {
	n.mu.Lock()
	defer n.mu.Unlock()
	if n.typ != nodeRoot {
		return errors.New("inserting on a non root node")
	}
	var level *node
	var child *node

	if pattern == "" {
		return errors.New("empty pattern is not supported")
	}

	for k, ch := range pattern {
		if k == 0 {
			if ch != '/' {
				return errors.New("path must start with slash ")
			}
			level = n
		}

		child = level.findChild(ch)
		switch level.typ {
		case nodeParam:
			if k < len(pattern) && ch != '/' {
				continue
			}
		}
		if child != nil {
			level = child
			continue
		}
		switch ch {
		case ':':
			level = level.branch(ch, nil, nodeParam)
		case '*':
			level = level.branch(ch, nil, nodeCatchAll)
		default:
			level = level.branch(ch, nil, nodeNormal)
		}
	}
	level.branch(eof, val, nodeEnd)
	return nil
}

func (n *node) find(path string) (*route, error) {
	n.mu.RLock()
	defer n.mu.RUnlock()
	if n.typ != nodeRoot {
		return nil, errors.New("non node search")
	}
	var level *node
	var isParam bool
	for k, ch := range path {
		if k == 0 {
			level = n
		}
		c := level.findChild(ch)
		if isParam {
			if k < len(path) && ch != '/' {
				continue
			}
			isParam = false
		}
		param := level.findChild(':')
		if param != nil {
			level = param
			isParam = true
			continue
		}
		catchAll := level.findChild('*')
		if catchAll != nil {
			level = catchAll
			break
		}
		if c != nil {
			level = c
			continue
		}
		return nil, errRouteNotFound
	}
	if level != nil {
		end := level.findChild(eof)
		if end != nil {
			return end.value, nil
		}
		if slash := level.findChild('/'); slash != nil {
			end = slash.findChild(eof)
			if end != nil {
				return end.value, nil
			}
		}
	}
	return nil, errRouteNotFound
}

type route struct {
	path       string
	middleware []func(http.Handler) http.Handler
	handler    func(http.ResponseWriter, *http.Request)
}

func (r *route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var base http.Handler
	if base == nil {
		base = http.HandlerFunc(r.handler)
	}
	for _, m := range r.middleware {
		base = m(base)
	}
	base.ServeHTTP(w, req)
}

// ParseParams parses params found in mateched from pattern. There are two kinds
// of params, one to capture a segment which starts with : and a nother to
// capture everything( a.k.a catch all) whis starts with *.
//
// For instance
//   pattern:="/hello/:name"
//   matched:="/hello/world"
// Will result into name:world. this function captures the named params and
// theri coreesponding values, returning them in a comma separated  string of a
// key:value nature. please see the tests for more details.
func ParseParams(matched, pattern string) (result string, err error) {
	if strings.Contains(pattern, ":") || strings.Contains(pattern, "*") {
		p1 := strings.Split(matched, "/")
		p2 := strings.Split(pattern, "/")
		s1 := len(p1)
		s2 := len(p2)
		if s1 < s2 {
			err = errBadPattern
			return
		}
		for k, v := range p2 {
			if len(v) > 0 {
				switch v[0] {
				case ':':
					if len(result) == 0 {
						result = v[1:] + ":" + p1[k]
						continue
					}
					result = result + "," + v[1:] + ":" + p1[k]
				case '*':
					name := "catch"
					if k != s2-1 {
						err = errBadPattern
						return
					}
					if len(v) > 1 {
						name = v[1:]
					}
					if len(result) == 0 {
						result = name + ":" + strings.Join(p1[k:], "/")
						return
					}
					result = result + "," + name + ":" + strings.Join(p1[k:], "/")
					return
				}
			}
		}
	}
	return
}

// Params stores route params.
type Params map[string]string

// Load loads params found in src into p.
func (p Params) Load(src string) {
	s := strings.Split(src, ",")
	var vars []string
	for _, v := range s {
		vars = strings.Split(v, ":")
		if len(vars) != 2 {
			continue
		}
		p[vars[0]] = vars[1]
	}
}

// Get returns value associated with key.
func (p Params) Get(key string) string {
	return p[key]
}

// GetParams returrns route params stored in r.
func GetParams(r *http.Request) Params {
	c := r.Header.Get(headerName)
	if c != "" {
		p := make(Params)
		p.Load(c)
		return p
	}
	return nil
}

type router struct {
	get, post, patch, put, head     *node
	connect, options, trace, delete *node
}

func (r *router) addRoute(method, path string, h func(http.ResponseWriter, *http.Request), wares ...func(http.Handler) http.Handler) error {
	newRoute := &route{path: path, handler: h}
	if len(wares) > 0 {
		newRoute.middleware = append(newRoute.middleware, wares...)
	}
	switch method {
	case "GET":
		if r.get == nil {
			r.get = &node{typ: nodeRoot}
		}
		return r.get.insert(path, newRoute)
	case "POST":
		if r.post == nil {
			r.post = &node{typ: nodeRoot}
		}
		return r.post.insert(path, newRoute)
	case "PUT":
		if r.put == nil {
			r.put = &node{typ: nodeRoot}
		}
		return r.put.insert(path, newRoute)
	case "PATCH":
		if r.patch == nil {
			r.patch = &node{typ: nodeRoot}
		}
		return r.patch.insert(path, newRoute)
	case "HEAD":
		if r.head == nil {
			r.head = &node{typ: nodeRoot}
		}
		return r.head.insert(path, newRoute)
	case "CONNECT":
		if r.connect == nil {
			r.connect = &node{typ: nodeRoot}
		}
		return r.connect.insert(path, newRoute)
	case "OPTIONS":
		if r.options == nil {
			r.options = &node{typ: nodeRoot}
		}
		return r.options.insert(path, newRoute)
	case "TRACE":
		if r.trace == nil {
			r.trace = &node{typ: nodeRoot}
		}
		return r.trace.insert(path, newRoute)
	case "DELETE":
		if r.delete == nil {
			r.delete = &node{typ: nodeRoot}
		}
		return r.delete.insert(path, newRoute)
	}
	return errUnknownMethod
}

func (r *router) find(method, path string) (*route, error) {
	switch method {
	case "GET":
		if r.get != nil {
			return r.get.find(path)
		}
	case "POST":
		if r.post != nil {
			return r.post.find(path)
		}
	case "PUT":
		if r.put != nil {
			return r.put.find(path)
		}
	case "PATCH":
		if r.patch != nil {
			return r.patch.find(path)
		}
	case "HEAD":
		if r.head != nil {
			return r.head.find(path)
		}
	case "CONNECT":
		if r.connect != nil {
			return r.connect.find(path)
		}
	case "OPTIONS":
		if r.options != nil {
			return r.options.find(path)
		}
	case "TRACE":
		if r.trace != nil {
			return r.trace.find(path)
		}
	case "DELETE":
		if r.delete != nil {
			return r.delete.find(path)
		}
	}
	return nil, errRouteNotFound
}

// Mux is a http multiplexer that allows matching of http requests to the
// registered http handlers.
//
// Mux supports named parameters in urls like
//   /hello/:name
// will match
//   /hello/world
// where by inside the request passed to the handler, the param with key name and
// value world will be passed.
//
// Mux supports catch all parameters too
//   /hello/*whatever
// will match
//   /hello/world
//   /hello/world/tanzania
//   /hello/world/afica/tanzania.png
// where by inside the request passed to the handler, the param with key
// whatever will be set and value will be
//   world
//   world/tanzania
//   world/afica/tanzania.png
//
// If you dont specify a name in a catch all route, then the default name "catch"
// will be ussed.
type Mux struct {
	prefix     string
	middleware []func(http.Handler) http.Handler
	notFound   http.Handler
	*router
}

// New returns a new *Mux instance with default handler for mismatched routes.
func New() *Mux {
	m := &Mux{}
	m.router = &router{}
	m.notFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, errRouteNotFound.Error(), http.StatusNotFound)
	})
	return m
}

// AddRoute registers h with pattern and method. If there is a path prefix
// created via the Group method) it will be set.
func (m *Mux) AddRoute(method, pattern string, h func(http.ResponseWriter, *http.Request)) error {
	if m.prefix != "" {
		pattern = path.Join(m.prefix, pattern)
	}
	return m.addRoute(method, pattern, h, m.middleware...)
}

// Get registers h with pattern and method GET.
func (m *Mux) Get(pattern string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("GET", pattern, h)
}

// Put registers h with pattern and method PUT.
func (m *Mux) Put(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PUT", path, h)
}

// Post registers h with pattern and method POST.
func (m *Mux) Post(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("POST", path, h)
}

// Patch registers h with pattern and method PATCH.
func (m *Mux) Patch(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PATCH", path, h)
}

// Head registers h with pattern and method HEAD.
func (m *Mux) Head(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("HEAD", path, h)
}

// Options registers h with pattern and method OPTIONS.
func (m *Mux) Options(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("OPTIONS", path, h)
}

// Connect  registers h with pattern and method CONNECT.
func (m *Mux) Connect(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("CONNECT", path, h)
}

// Trace registers h with pattern and method TRACE.
func (m *Mux) Trace(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("TRACE", path, h)
}

// Delete registers h with pattern and method DELETE.
func (m *Mux) Delete(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("DELETE", path, h)
}

// NotFoundHandler is executed when the request route is not found.
func (m *Mux) NotFoundHandler(h http.Handler) {
	m.notFound = h
}

// ServeHTTP implements http.Handler interface. It muliplexes http requests
// against registered handlers.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p := path.Clean(r.URL.Path)
	h, err := m.find(r.Method, p)
	if err != nil {
		m.notFound.ServeHTTP(w, r)
		return
	}
	params, _ := ParseParams(p, h.path) // check if there is any url params
	if params != "" {
		r.Header.Set(headerName, params)
	}
	h.ServeHTTP(w, r)
}

// Group creates a path prefix group for pattern, all routes registered using
// the returned Mux will only match if the request path starts with pattern. For
// instance .
//   m:=New()
//   home:=m.Group("/home")
//   home.Get("/alone",myHandler)
// will match
//   /home/alone
func (m *Mux) Group(pattern string) *Mux {
	return &Mux{
		prefix:     pattern,
		router:     m.router,
		middleware: m.middleware,
		notFound:   m.notFound,
	}

}

// Use assigns midlewares to the current *Mux. All routes registered by the *Mux
// after this call will have the middlewares assigned to them.
func (m *Mux) Use(middleware ...func(http.Handler) http.Handler) {
	if len(middleware) > 0 {
		m.middleware = append(m.middleware, middleware...)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"expvar"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"

	"cloud.google.com/go/logging"

	topk "github.com/dgryski/go-topk"
	"golang.org/x/net/publicsuffix"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
)

type originAllower struct {
	m        map[string]struct{}
	ns       *expvar.Map
	hostname string
	gclog    logClient

	mu                 *sync.RWMutex
	topKAllDomains     *topk.Stream
	topKOfflistDomains *topk.Stream
}

type logClient interface {
	Log(logging.Entry)
	Flush()
}

func newOriginAllower(blockedDomains []string, hostname string, gclog logClient, ns *expvar.Map) *originAllower {
	mu := &sync.RWMutex{}
	topKAllDomains := topk.New(100)
	topKOfflistDomains := topk.New(100)
	lifetime := new(expvar.Map).Init()
	ns.Set("lifetime", lifetime)
	lifetime.Set("top_all_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKAllDomains.Keys()
	}))
	lifetime.Set("top_offlist_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKOfflistDomains.Keys()
	}))

	oa := &originAllower{
		m:                  make(map[string]struct{}),
		ns:                 ns,
		hostname:           hostname,
		gclog:              gclog,
		mu:                 mu,
		topKAllDomains:     topKAllDomains,
		topKOfflistDomains: topKOfflistDomains,
	}
	for _, d := range blockedDomains {
		oa.m[d] = struct{}{}
	}
	return oa
}

func (oa *originAllower) Allow(r *http.Request) (string, bool) {
	origin := r.Header.Get("Origin")
	referrer := r.Header.Get("Referer")

	apiKey := r.FormValue("key")
	userAgent := r.Header.Get("User-Agent")

	remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		log.Printf("error splitting %#v as host:port: %s", r.RemoteAddr, err)
		remoteIP = "0.0.0.0"
	}
	entry := &apiLogEntry{
		DetectedDomain:     "",
		DetectedFullDomain: "",
		Allowed:            false,
		APIKey:             apiKey,
		Headers: headers{
			Origin:    origin,
			Referrer:  referrer,
			UserAgent: userAgent,
		},
	}
	defer func() {
		go oa.countRequest(entry, r, remoteIP)
	}()

	if origin == "" && referrer == "" {
		entry.Allowed = true
		return "", true
	}
	if origin != "" {
		domain, fullDomain, ok := oa.checkDomain(origin)
		entry.DetectedDomain = domain
		entry.DetectedFullDomain = fullDomain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}
	if referrer != "" {
		domain, fullDomain, ok := oa.checkDomain(referrer)
		entry.DetectedDomain = domain
		entry.DetectedFullDomain = fullDomain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}

	return "", false
}

// checkDomain checks if the detected domain from the request headers and
// whether domain is allowed to make requests against howsmyssl's API.
func (oa *originAllower) checkDomain(d string) (string, string, bool) {
	domain, fullDomain, err := effectiveDomain(d)
	if err != nil {
		// TODO(jmhodges): replace this len check with false when we use top-k
		return "", "", len(oa.m) == 0
	}
	_, isBlocked := oa.m[domain]
	// TODO(jmhodges): remove this len check when we use top-k
	return domain, fullDomain, !isBlocked || len(oa.m) == 0
}

func (oa *originAllower) countRequest(entry *apiLogEntry, r *http.Request, remoteIP string) {
	oa.gclog.Log(logging.Entry{
		Payload:     entry,
		HTTPRequest: &logging.HTTPRequest{Request: r, RemoteIP: remoteIP},
		Labels: map[string]string{
			"server_hostname": oa.hostname,
			"app":             "howsmyssl",
		},
	})

	if entry.DetectedDomain == "" {
		return
	}

	oa.mu.Lock()
	defer oa.mu.Unlock()
	oa.topKAllDomains.Insert(entry.DetectedDomain, 1)
	if !entry.Allowed {
		oa.topKOfflistDomains.Insert(entry.DetectedDomain, 1)
	}
}

func effectiveDomain(str string) (string, string, error) {
	u, err := url.Parse(str)
	if err != nil {
		return "", "", err
	}
	host := u.Host
	if host == "" {
		return "", "", fmt.Errorf("unparsable domain string %#v", str)
	}
	i := strings.Index(host, ":")
	if i >= 0 {
		host = host[:i]
	}

	if host == "localhost" {
		return "localhost", "localhost", nil
	}
	d, err := publicsuffix.EffectiveTLDPlusOne(host)
	if err != nil {
		return "", "", err
	}
	return d, host, nil
}

func loadOriginsConfig(fp string) *originsConfig {
	f, err := os.Open(fp)
	if err != nil {
		log.Fatalf("unable to open origins config file %#v: %s", fp, err)
	}
	defer f.Close()
	jc := &originsConfig{}
	err = json.NewDecoder(f).Decode(jc)
	if err != nil {
		log.Fatalf("unable to parse origins config file %#v: %s", fp, err)
	}
	for _, a := range jc.BlockedOrigins {
		if strings.HasPrefix(a, "http://") || strings.HasPrefix(a, "https://") {
			log.Fatalf("origins config file (%#v) should have only domains without the leading scheme. For example, %#v should not have the protocol scheme at its beginning.", fp, a)
		}
		if strings.Contains(a, "/") {
			log.Fatalf("origins config file (%#v) should have only domains without a path after it. For example, %#v should not have a trailing path.", fp, a)
		}
	}
	return jc
}

type originsConfig struct {
	// BlockedOrigins are domains that are not to be allowed as referrers to the
	// API. They should not have a scheme or path, but only the domain, as in
	// "example.com".
	BlockedOrigins []string `json:"blocked_origins"`
}

type rejectionReason string

const rejectionConfig = rejectionReason("config")

type apiLogEntry struct {
	DetectedDomain     string          `json:"detected_domain"`
	DetectedFullDomain string          `json:"detected_full_domain"`
	Allowed            bool            `json:"allowed"`
	APIKey             string          `json:"api_key"`
	RejectionReason    rejectionReason `json:"rejection_reason"`
	ReferrerHost       string          `json:referrer_host"`
	Headers            headers         `json:"headers"`
}

type headers struct {
	Origin    string `json:"origin"`
	Referrer  string `json:"referrer"`
	UserAgent string `json:"user_agent"`
}

func loadGoogleServiceAccount(fp string) *googleConfig {
	bs, err := ioutil.ReadFile(fp)
	if err != nil {
		log.Fatalf("unable to read Google service account config %#v: %s", fp, err)
	}
	c := &googleConfig{}
	err = json.Unmarshal(bs, c)
	if err != nil {
		log.Fatalf("unable to parse project ID from Google service account config %#v: %s", fp, err)
	}
	if c.ProjectID == "" {
		log.Fatalf("blank project ID in Google service account config %#v: %s", fp, err)
	}
	jwtConf, err := google.JWTConfigFromJSON(bs, logging.WriteScope)
	if err != nil {
		log.Fatalf("unable to parse Google service account config %#v: %s", fp, err)
	}
	c.conf = jwtConf
	return c
}

type googleConfig struct {
	ProjectID string `json:"project_id"`

	conf *jwt.Config
}

var _ logClient = nullLogClient{}

type nullLogClient struct{}

func (n nullLogClient) Log(e logging.Entry) {
}

func (n nullLogClient) Flush() {
}
<commit_msg>remove accidentally added and unused log field (#183)<commit_after>package main

import (
	"encoding/json"
	"expvar"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"

	"cloud.google.com/go/logging"

	topk "github.com/dgryski/go-topk"
	"golang.org/x/net/publicsuffix"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
)

type originAllower struct {
	m        map[string]struct{}
	ns       *expvar.Map
	hostname string
	gclog    logClient

	mu                 *sync.RWMutex
	topKAllDomains     *topk.Stream
	topKOfflistDomains *topk.Stream
}

type logClient interface {
	Log(logging.Entry)
	Flush()
}

func newOriginAllower(blockedDomains []string, hostname string, gclog logClient, ns *expvar.Map) *originAllower {
	mu := &sync.RWMutex{}
	topKAllDomains := topk.New(100)
	topKOfflistDomains := topk.New(100)
	lifetime := new(expvar.Map).Init()
	ns.Set("lifetime", lifetime)
	lifetime.Set("top_all_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKAllDomains.Keys()
	}))
	lifetime.Set("top_offlist_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKOfflistDomains.Keys()
	}))

	oa := &originAllower{
		m:                  make(map[string]struct{}),
		ns:                 ns,
		hostname:           hostname,
		gclog:              gclog,
		mu:                 mu,
		topKAllDomains:     topKAllDomains,
		topKOfflistDomains: topKOfflistDomains,
	}
	for _, d := range blockedDomains {
		oa.m[d] = struct{}{}
	}
	return oa
}

func (oa *originAllower) Allow(r *http.Request) (string, bool) {
	origin := r.Header.Get("Origin")
	referrer := r.Header.Get("Referer")

	apiKey := r.FormValue("key")
	userAgent := r.Header.Get("User-Agent")

	remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		log.Printf("error splitting %#v as host:port: %s", r.RemoteAddr, err)
		remoteIP = "0.0.0.0"
	}
	entry := &apiLogEntry{
		DetectedDomain:     "",
		DetectedFullDomain: "",
		Allowed:            false,
		APIKey:             apiKey,
		Headers: headers{
			Origin:    origin,
			Referrer:  referrer,
			UserAgent: userAgent,
		},
	}
	defer func() {
		go oa.countRequest(entry, r, remoteIP)
	}()

	if origin == "" && referrer == "" {
		entry.Allowed = true
		return "", true
	}
	if origin != "" {
		domain, fullDomain, ok := oa.checkDomain(origin)
		entry.DetectedDomain = domain
		entry.DetectedFullDomain = fullDomain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}
	if referrer != "" {
		domain, fullDomain, ok := oa.checkDomain(referrer)
		entry.DetectedDomain = domain
		entry.DetectedFullDomain = fullDomain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}

	return "", false
}

// checkDomain checks if the detected domain from the request headers and
// whether domain is allowed to make requests against howsmyssl's API.
func (oa *originAllower) checkDomain(d string) (string, string, bool) {
	domain, fullDomain, err := effectiveDomain(d)
	if err != nil {
		// TODO(jmhodges): replace this len check with false when we use top-k
		return "", "", len(oa.m) == 0
	}
	_, isBlocked := oa.m[domain]
	// TODO(jmhodges): remove this len check when we use top-k
	return domain, fullDomain, !isBlocked || len(oa.m) == 0
}

func (oa *originAllower) countRequest(entry *apiLogEntry, r *http.Request, remoteIP string) {
	oa.gclog.Log(logging.Entry{
		Payload:     entry,
		HTTPRequest: &logging.HTTPRequest{Request: r, RemoteIP: remoteIP},
		Labels: map[string]string{
			"server_hostname": oa.hostname,
			"app":             "howsmyssl",
		},
	})

	if entry.DetectedDomain == "" {
		return
	}

	oa.mu.Lock()
	defer oa.mu.Unlock()
	oa.topKAllDomains.Insert(entry.DetectedDomain, 1)
	if !entry.Allowed {
		oa.topKOfflistDomains.Insert(entry.DetectedDomain, 1)
	}
}

func effectiveDomain(str string) (string, string, error) {
	u, err := url.Parse(str)
	if err != nil {
		return "", "", err
	}
	host := u.Host
	if host == "" {
		return "", "", fmt.Errorf("unparsable domain string %#v", str)
	}
	i := strings.Index(host, ":")
	if i >= 0 {
		host = host[:i]
	}

	if host == "localhost" {
		return "localhost", "localhost", nil
	}
	d, err := publicsuffix.EffectiveTLDPlusOne(host)
	if err != nil {
		return "", "", err
	}
	return d, host, nil
}

func loadOriginsConfig(fp string) *originsConfig {
	f, err := os.Open(fp)
	if err != nil {
		log.Fatalf("unable to open origins config file %#v: %s", fp, err)
	}
	defer f.Close()
	jc := &originsConfig{}
	err = json.NewDecoder(f).Decode(jc)
	if err != nil {
		log.Fatalf("unable to parse origins config file %#v: %s", fp, err)
	}
	for _, a := range jc.BlockedOrigins {
		if strings.HasPrefix(a, "http://") || strings.HasPrefix(a, "https://") {
			log.Fatalf("origins config file (%#v) should have only domains without the leading scheme. For example, %#v should not have the protocol scheme at its beginning.", fp, a)
		}
		if strings.Contains(a, "/") {
			log.Fatalf("origins config file (%#v) should have only domains without a path after it. For example, %#v should not have a trailing path.", fp, a)
		}
	}
	return jc
}

type originsConfig struct {
	// BlockedOrigins are domains that are not to be allowed as referrers to the
	// API. They should not have a scheme or path, but only the domain, as in
	// "example.com".
	BlockedOrigins []string `json:"blocked_origins"`
}

type rejectionReason string

const rejectionConfig = rejectionReason("config")

type apiLogEntry struct {
	DetectedDomain     string          `json:"detected_domain"`
	DetectedFullDomain string          `json:"detected_full_domain"`
	Allowed            bool            `json:"allowed"`
	APIKey             string          `json:"api_key"`
	RejectionReason    rejectionReason `json:"rejection_reason"`
	Headers            headers         `json:"headers"`
}

type headers struct {
	Origin    string `json:"origin"`
	Referrer  string `json:"referrer"`
	UserAgent string `json:"user_agent"`
}

func loadGoogleServiceAccount(fp string) *googleConfig {
	bs, err := ioutil.ReadFile(fp)
	if err != nil {
		log.Fatalf("unable to read Google service account config %#v: %s", fp, err)
	}
	c := &googleConfig{}
	err = json.Unmarshal(bs, c)
	if err != nil {
		log.Fatalf("unable to parse project ID from Google service account config %#v: %s", fp, err)
	}
	if c.ProjectID == "" {
		log.Fatalf("blank project ID in Google service account config %#v: %s", fp, err)
	}
	jwtConf, err := google.JWTConfigFromJSON(bs, logging.WriteScope)
	if err != nil {
		log.Fatalf("unable to parse Google service account config %#v: %s", fp, err)
	}
	c.conf = jwtConf
	return c
}

type googleConfig struct {
	ProjectID string `json:"project_id"`

	conf *jwt.Config
}

var _ logClient = nullLogClient{}

type nullLogClient struct{}

func (n nullLogClient) Log(e logging.Entry) {
}

func (n nullLogClient) Flush() {
}
<|endoftext|>


<commit_before>package pooly

import (
	"errors"
	"sync/atomic"
	"time"
	"unsafe"
)

const (
	defaultConnsNum    = 10
	defaultAttemptsNum = 3
	defaultRetryDelay  = 10 * time.Millisecond
)

var (
	ErrPoolInvalidDriver = errors.New("invalid driver supplied")
	ErrPoolClosed        = errors.New("pool is closed")
	ErrPoolTimeout       = errors.New("operation timed out")
)

// Driver describes the interface responsible of creating/deleting/testing pool connections.
type Driver interface {
	// Dial is a function that establishes a connection with a remote host.
	// It returns the connection created or an error on failure.
	Dial() (*Conn, error)

	// Close closes the given connection.
	Close(*Conn)

	// TestOnBorrow is a function that, given a connection, tests it and returns an error on failure.
	TestOnBorrow(*Conn) error

	// Temporary determines whether the error is temporary or fatal for the connection.
	// On fatal error, the connection will be garbage collected.
	Temporary(error) bool
}

// PoolConfig defines the pool configuration options.
type PoolConfig struct {
	// Connection driver.
	Driver Driver

	// Close connections after remaining idle for this duration.
	// If the value is zero, then idle connections are not closed.
	IdleTimeout time.Duration

	// Defines the duration during which Get operations will try to return a connection from the pool.
	// If the value is zero, then Get should wait forever.
	WaitTimeout time.Duration

	// Maximum number of connections allowed in the pool (10 by default).
	MaxConns int32

	// Maximum number of connection attempts (3 by default).
	MaxAttempts int

	// Time interval between connection attempts (10ms by default).
	RetryDelay time.Duration
}

// Pool maintains a pool of connections. The application calls the Get method to get a connection
// from the pool and the Put method to return the connection to the pool. New can be called to allocate
// more connections in the background.
// When one is done with the pool, Close will cleanup all the connections ressources.
// The pool itself will adapt to the demand by spawning and destroying connections as needed. In order to
// tweak its behavior, settings like IdleTimeout and MaxConns may be used.
type Pool struct {
	*PoolConfig

	connsCount int32
	conns      chan *Conn
	gc         chan *Conn
	inbound    unsafe.Pointer
	closing    int32
	wakeup     chan bool
}

func (p *Pool) setClosing() {
	atomic.StoreInt32(&p.closing, 1)
}

func (p *Pool) isClosing() bool {
	return atomic.LoadInt32(&p.closing) == 1
}

func (p *Pool) inboundChannel() chan *Conn {
	i := atomic.LoadPointer(&p.inbound)
	return *(*chan *Conn)(i)
}

// After that, all inbound connections will be garbage collected.
func (p *Pool) setInboundChannelGC() {
	i := unsafe.Pointer(&p.gc)
	atomic.StorePointer(&p.inbound, i)
}

// Atomically returns the current connections count.
func (p *Pool) fetchConnsCount() int32 {
	for b := false; !b; {
		n := atomic.LoadInt32(&p.connsCount)
		if n > 0 {
			return n
		}
		// Null, set it back to MaxConns in order to prevent newConn from altering it
		b = atomic.CompareAndSwapInt32(&p.connsCount, n, p.MaxConns)
	}
	return 0
}

// Atomically increments the number of connections.
func (p *Pool) incConnsCount() bool {
	for b := false; !b; {
		n := atomic.LoadInt32(&p.connsCount)
		if n == p.MaxConns {
			return false // maximum connections count reached
		}
		b = atomic.CompareAndSwapInt32(&p.connsCount, n, n+1)
	}
	return true
}

// Atomically decrements the number of connections.
func (p *Pool) decConnsCount() {
	atomic.AddInt32(&p.connsCount, -1)
}

// Garbage collects connections.
func (p *Pool) collect() {
	var c *Conn

	for {
		if p.isClosing() {
			if p.fetchConnsCount() == 0 {
				// All connections have been garbage collected
				close(p.conns) // notify Close that we're done
				return
			}
		}

		select {
		case <-p.wakeup:
			continue
		case c = <-p.gc:
		}

		if c != nil && !c.isClosed() {
			// XXX workaround to avoid closing twice a connection
			// Since idle timeouts can occur at any time, we may have duplicates in the queue
			c.setClosed()
			p.Driver.Close(c)
			p.decConnsCount()
		} else if c == nil {
			p.decConnsCount()
		}
	}
}

// NewPool creates a new pool of connections.
func NewPool(c *PoolConfig) (*Pool, error) {
	if c.Driver == nil {
		return nil, ErrPoolInvalidDriver
	}
	if c.MaxConns <= 0 {
		c.MaxConns = defaultConnsNum
	}
	if c.MaxAttempts <= 0 {
		c.MaxAttempts = defaultAttemptsNum
	}
	if c.RetryDelay == 0 {
		c.RetryDelay = defaultRetryDelay
	}

	p := &Pool{
		PoolConfig: c,
		conns:      make(chan *Conn, c.MaxConns),
		gc:         make(chan *Conn, c.MaxConns),
		wakeup:     make(chan bool, 1),
	}
	p.inbound = unsafe.Pointer(&p.conns)
	go p.collect()

	return p, nil
}

func (p *Pool) newConn() {
	if !p.incConnsCount() {
		return
	}
	for i := 0; i < p.MaxAttempts; i++ {
		c, err := p.Driver.Dial()
		if c != nil && (err == nil || p.Driver.Temporary(err)) {
			c.setIdle(p)
			p.inboundChannel() <- c
			return
		}
		time.Sleep(p.RetryDelay)
	}
	p.gc <- nil // connection failed
}

// New attempts to create n new connections in background.
// Note that it does nothing when MaxConns is reached.
func (p *Pool) New(n int) error {
	if p.isClosing() {
		return ErrPoolClosed
	}
	for i := 0; i < n; i++ {
		go p.newConn()
	}
	return nil
}

// Get gets a fully tested connection from the pool
func (p *Pool) Get() (c *Conn, err error) {
	var t <-chan time.Time

	if p.isClosing() {
		err = ErrPoolClosed
		return
	}

	// Try to get a connection right away optimistically
	select {
	case c = <-p.conns:
		goto gotone
	default: // connections are running low, spawn a new one
		if err = p.New(1); err != nil {
			return
		}
	}

	if p.WaitTimeout > 0 {
		t = time.After(p.WaitTimeout)
	}
	select {
	case c = <-p.conns:
		goto gotone
	case <-t:
		err = ErrPoolTimeout
		return
	}

gotone:
	if !c.setActive() {
		// Connection timed out, start over
		return p.Get()
	}
	// Test the connection
	if err := p.Driver.TestOnBorrow(c); err != nil {
		if !p.Driver.Temporary(err) {
			p.gc <- c // garbage collect the connection and start over
			return p.Get()
		}
	}
	return
}

// Put puts a given connection back to the pool depending on its error status.
func (p *Pool) Put(c *Conn, err error) {
	if c != nil && (err == nil || p.Driver.Temporary(err)) {
		c.setIdle(p)
		p.inboundChannel() <- c
		return
	}
	p.gc <- c
}

// Close closes the pool, thus destroying all connections.
// It returns when all spawned connections have been successfully garbage collected.
// After a successful call to Close, the pool can not be used again.
func (p *Pool) Close() error {
	if p.isClosing() {
		return ErrPoolClosed
	}

	p.setInboundChannelGC()
	p.setClosing()
	// XXX wakeup the garbage collector if it happens to be asleep
	// This is necessary when a Close is issued and there are no more connections left to collect
	p.wakeup <- true

	// Garbage collect all the idle connections left
	for c := range p.conns {
		p.gc <- c
	}
	return nil
}
<commit_msg>Prevent bad Put usage<commit_after>package pooly

import (
	"errors"
	"sync/atomic"
	"time"
	"unsafe"
)

const (
	defaultConnsNum    = 10
	defaultAttemptsNum = 3
	defaultRetryDelay  = 10 * time.Millisecond
)

var (
	ErrPoolInvalidDriver = errors.New("invalid driver supplied")
	ErrPoolClosed        = errors.New("pool is closed")
	ErrPoolTimeout       = errors.New("operation timed out")
)

// Driver describes the interface responsible of creating/deleting/testing pool connections.
type Driver interface {
	// Dial is a function that establishes a connection with a remote host.
	// It returns the connection created or an error on failure.
	Dial() (*Conn, error)

	// Close closes the given connection.
	Close(*Conn)

	// TestOnBorrow is a function that, given a connection, tests it and returns an error on failure.
	TestOnBorrow(*Conn) error

	// Temporary determines whether the error is temporary or fatal for the connection.
	// On fatal error, the connection will be garbage collected.
	Temporary(error) bool
}

// PoolConfig defines the pool configuration options.
type PoolConfig struct {
	// Connection driver.
	Driver Driver

	// Close connections after remaining idle for this duration.
	// If the value is zero, then idle connections are not closed.
	IdleTimeout time.Duration

	// Defines the duration during which Get operations will try to return a connection from the pool.
	// If the value is zero, then Get should wait forever.
	WaitTimeout time.Duration

	// Maximum number of connections allowed in the pool (10 by default).
	MaxConns int32

	// Maximum number of connection attempts (3 by default).
	MaxAttempts int

	// Time interval between connection attempts (10ms by default).
	RetryDelay time.Duration
}

// Pool maintains a pool of connections. The application calls the Get method to get a connection
// from the pool and the Put method to return the connection to the pool. New can be called to allocate
// more connections in the background.
// When one is done with the pool, Close will cleanup all the connections ressources.
// The pool itself will adapt to the demand by spawning and destroying connections as needed. In order to
// tweak its behavior, settings like IdleTimeout and MaxConns may be used.
type Pool struct {
	*PoolConfig

	connsCount int32
	conns      chan *Conn
	gc         chan *Conn
	inbound    unsafe.Pointer
	closing    int32
	wakeup     chan bool
}

func (p *Pool) setClosing() {
	atomic.StoreInt32(&p.closing, 1)
}

func (p *Pool) isClosing() bool {
	return atomic.LoadInt32(&p.closing) == 1
}

func (p *Pool) inboundChannel() chan *Conn {
	i := atomic.LoadPointer(&p.inbound)
	return *(*chan *Conn)(i)
}

// After that, all inbound connections will be garbage collected.
func (p *Pool) setInboundChannelGC() {
	i := unsafe.Pointer(&p.gc)
	atomic.StorePointer(&p.inbound, i)
}

// Atomically returns the current connections count.
func (p *Pool) fetchConnsCount() int32 {
	for b := false; !b; {
		n := atomic.LoadInt32(&p.connsCount)
		if n > 0 {
			return n
		}
		// Null, set it back to MaxConns in order to prevent newConn from altering it
		b = atomic.CompareAndSwapInt32(&p.connsCount, n, p.MaxConns)
	}
	return 0
}

// Atomically increments the number of connections.
func (p *Pool) incConnsCount() bool {
	for b := false; !b; {
		n := atomic.LoadInt32(&p.connsCount)
		if n == p.MaxConns {
			return false // maximum connections count reached
		}
		b = atomic.CompareAndSwapInt32(&p.connsCount, n, n+1)
	}
	return true
}

// Atomically decrements the number of connections.
func (p *Pool) decConnsCount() {
	atomic.AddInt32(&p.connsCount, -1)
}

// Garbage collects connections.
func (p *Pool) collect() {
	var c *Conn

	for {
		if p.isClosing() {
			if p.fetchConnsCount() == 0 {
				// All connections have been garbage collected
				close(p.conns) // notify Close that we're done
				return
			}
		}

		select {
		case <-p.wakeup:
			continue
		case c = <-p.gc:
		}

		if c != nil && !c.isClosed() {
			// XXX workaround to avoid closing twice a connection
			// Since idle timeouts can occur at any time, we may have duplicates in the queue
			c.setClosed()
			p.Driver.Close(c)
			p.decConnsCount()
		} else if c == nil {
			p.decConnsCount()
		}
	}
}

// NewPool creates a new pool of connections.
func NewPool(c *PoolConfig) (*Pool, error) {
	if c.Driver == nil {
		return nil, ErrPoolInvalidDriver
	}
	if c.MaxConns <= 0 {
		c.MaxConns = defaultConnsNum
	}
	if c.MaxAttempts <= 0 {
		c.MaxAttempts = defaultAttemptsNum
	}
	if c.RetryDelay == 0 {
		c.RetryDelay = defaultRetryDelay
	}

	p := &Pool{
		PoolConfig: c,
		conns:      make(chan *Conn, c.MaxConns),
		gc:         make(chan *Conn, c.MaxConns),
		wakeup:     make(chan bool, 1),
	}
	p.inbound = unsafe.Pointer(&p.conns)
	go p.collect()

	return p, nil
}

func (p *Pool) newConn() {
	if !p.incConnsCount() {
		return
	}
	for i := 0; i < p.MaxAttempts; i++ {
		c, err := p.Driver.Dial()
		if c != nil && (err == nil || p.Driver.Temporary(err)) {
			c.setIdle(p)
			p.inboundChannel() <- c
			return
		}
		time.Sleep(p.RetryDelay)
	}
	p.gc <- nil // connection failed
}

// New attempts to create n new connections in background.
// Note that it does nothing when MaxConns is reached.
func (p *Pool) New(n int) error {
	if p.isClosing() {
		return ErrPoolClosed
	}
	for i := 0; i < n; i++ {
		go p.newConn()
	}
	return nil
}

// Get gets a fully tested connection from the pool
func (p *Pool) Get() (c *Conn, err error) {
	var t <-chan time.Time

	if p.isClosing() {
		err = ErrPoolClosed
		return
	}

	// Try to get a connection right away optimistically
	select {
	case c = <-p.conns:
		goto gotone
	default: // connections are running low, spawn a new one
		if err = p.New(1); err != nil {
			return
		}
	}

	if p.WaitTimeout > 0 {
		t = time.After(p.WaitTimeout)
	}
	select {
	case c = <-p.conns:
		goto gotone
	case <-t:
		err = ErrPoolTimeout
		return
	}

gotone:
	if !c.setActive() {
		// Connection timed out, start over
		return p.Get()
	}
	// Test the connection
	if err := p.Driver.TestOnBorrow(c); err != nil {
		if !p.Driver.Temporary(err) {
			p.gc <- c // garbage collect the connection and start over
			return p.Get()
		}
	}
	return
}

// Put puts a given connection back to the pool depending on its error status.
func (p *Pool) Put(c *Conn, err error) {
	if c == nil {
		return
	}
	if err != nil && !p.Driver.Temporary(err) {
		p.gc <- c
		return
	}
	c.setIdle(p)
	p.inboundChannel() <- c
}

// Close closes the pool, thus destroying all connections.
// It returns when all spawned connections have been successfully garbage collected.
// After a successful call to Close, the pool can not be used again.
func (p *Pool) Close() error {
	if p.isClosing() {
		return ErrPoolClosed
	}

	p.setInboundChannelGC()
	p.setClosing()
	// XXX wakeup the garbage collector if it happens to be asleep
	// This is necessary when a Close is issued and there are no more connections left to collect
	p.wakeup <- true

	// Garbage collect all the idle connections left
	for c := range p.conns {
		p.gc <- c
	}
	return nil
}
<|endoftext|>


<commit_before>// prop.go
package goprop

import (
	"bufio"
	"errors"
	"io"
	"os"
	"strconv"
	"strings"
)

type Properties map[string]string

var NotExistKeyError error = errors.New("NotExistKeyError")

func NewProp() Properties {
	return make(Properties)
}

// get value as string.
// different to prop[key],the return string value is Unquoted.
func (this *Properties) Get(key string) (string, error) {
	v, ok := (*this)[key]

	if !ok {
		return "", NotExistKeyError
	}

	return v, nil
}

// get value as int
func (this *Properties) Atoi(key string) (int, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.Atoi(v)
}

// get value as bool
func (this *Properties) ParseBool(key string) (bool, error) {
	v, ok := (*this)[key]

	if !ok {
		return false, NotExistKeyError
	}

	return strconv.ParseBool(v)
}

// get value as float
func (this *Properties) ParseFloat(key string) (float64, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.ParseFloat(v, 64)
}

// get value as int
func (this *Properties) ParseInt(key string) (int64, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.ParseInt(v, 10, 64)
}

// get value as uint
func (this *Properties) ParseUint(key string) (uint64, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.ParseUint(v, 10, 64)
}

// load properties from file
func LoadFile(fileName string) (Properties, error) {
	p := make(Properties)
	file, err := os.Open(fileName)

	if err != nil {
		return p, err
	}

	defer file.Close()

	return Load(file)
}

// load properties from reader
func Load(reader io.Reader) (Properties, error) {
	p := make(Properties)

	scanner := bufio.NewScanner(reader)

	for scanner.Scan() {
		kv := strings.SplitN(scanner.Text(), "=", 2)

		if len(kv) == 2 {
			k := strings.TrimSpace(kv[0])
			v := strings.TrimSpace(kv[1])
			p[k] = v
		}
	}

	if scanner.Err() != nil {
		return p, scanner.Err()
	}

	return p, nil
}

// save properties to file
// * file must not exist, because it's dangerous to overwrite
func SaveFile(prop Properties, fileName string) error {
	file, errOpen := os.OpenFile(fileName, os.O_CREATE|os.O_EXCL, os.ModePerm)

	if errOpen != nil {
		return errOpen
	}

	defer file.Close()

	return Save(prop, file)
}

// save properties to writer
func Save(prop Properties, writer io.Writer) error {
	bufWriter := bufio.NewWriter(writer)

	for k, v := range prop {
		_, err := bufWriter.WriteString(k + "=" + v + "\n")

		if err != nil {
			return err
		}
	}

	errFlush := bufWriter.Flush()
	if errFlush != nil {
		return errFlush
	}

	return nil
}
<commit_msg>Update prop.go<commit_after>// prop.go
package goprop

import (
	"bufio"
	"errors"
	"io"
	"os"
	"strconv"
	"strings"
)

type Properties map[string]string

var NotExistKeyError error = errors.New("NotExistKeyError")

func NewProp() Properties {
	return make(Properties)
}

// get value as string.
// the same to prop[key].
func (this *Properties) Get(key string) (string, error) {
	v, ok := (*this)[key]

	if !ok {
		return "", NotExistKeyError
	}

	return v, nil
}

// get value as int
func (this *Properties) Atoi(key string) (int, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.Atoi(v)
}

// get value as bool
func (this *Properties) ParseBool(key string) (bool, error) {
	v, ok := (*this)[key]

	if !ok {
		return false, NotExistKeyError
	}

	return strconv.ParseBool(v)
}

// get value as float
func (this *Properties) ParseFloat(key string) (float64, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.ParseFloat(v, 64)
}

// get value as int
func (this *Properties) ParseInt(key string) (int64, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.ParseInt(v, 10, 64)
}

// get value as uint
func (this *Properties) ParseUint(key string) (uint64, error) {
	v, ok := (*this)[key]

	if !ok {
		return 0, NotExistKeyError
	}

	return strconv.ParseUint(v, 10, 64)
}

// load properties from file
func LoadFile(fileName string) (Properties, error) {
	p := make(Properties)
	file, err := os.Open(fileName)

	if err != nil {
		return p, err
	}

	defer file.Close()

	return Load(file)
}

// load properties from reader
func Load(reader io.Reader) (Properties, error) {
	p := make(Properties)

	scanner := bufio.NewScanner(reader)

	for scanner.Scan() {
		kv := strings.SplitN(scanner.Text(), "=", 2)

		if len(kv) == 2 {
			k := strings.TrimSpace(kv[0])
			v := strings.TrimSpace(kv[1])
			p[k] = v
		}
	}

	if scanner.Err() != nil {
		return p, scanner.Err()
	}

	return p, nil
}

// save properties to file
// * file must not exist, because it's dangerous to overwrite
func SaveFile(prop Properties, fileName string) error {
	file, errOpen := os.OpenFile(fileName, os.O_CREATE|os.O_EXCL, os.ModePerm)

	if errOpen != nil {
		return errOpen
	}

	defer file.Close()

	return Save(prop, file)
}

// save properties to writer
func Save(prop Properties, writer io.Writer) error {
	bufWriter := bufio.NewWriter(writer)

	for k, v := range prop {
		_, err := bufWriter.WriteString(k + "=" + v + "\n")

		if err != nil {
			return err
		}
	}

	errFlush := bufWriter.Flush()
	if errFlush != nil {
		return errFlush
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"time"
)

func main() {
	if len(os.Args) < 3 {
		log.Fatalf("Usage: %s filename cmd...\n", os.Args[0])
	}

	outfile := os.Args[1]
	out, err := os.Create(outfile)
	defer out.Close()
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	subCmd := os.Args[2]
	subArgs := os.Args[3:]

	cmd := exec.Command(subCmd, subArgs...)

	var cmdOut bytes.Buffer
	cmd.Stdout = &cmdOut

	reader := bufio.NewReader(&cmdOut)
	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	h := sha1.New()
	hWriter := bufio.NewWriter(h)
	p := make([]byte, 1024)
	n := 0
	for ; err != io.EOF; n, err = reader.Read(p) {
		// TODO: handle the buffer underwrite case.
		_, herr := hWriter.Write(p[:n])
		if herr != nil {
			log.Printf("writing to sha1 hasher: %v", herr)
		}
		_, oerr := out.Write(p[:n])
		if oerr != nil {
			log.Printf("Error writing to outfile: %v", oerr)
		}
	}

	hWriter.Flush()

	pFile, err := os.OpenFile(".prov", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	defer pFile.Close()
	if err != nil {
		log.Fatalf("Couldn't open .prov: %v", err)
	}

	line := []string{
		fmt.Sprintf("%x", h.Sum(nil)),
		fmt.Sprintf("%v", time.Now()),
		os.Getenv("USER"),
		fmt.Sprintf("%s", subCmd),
	}
	line = append(line, subArgs...)
	w := csv.NewWriter(pFile)

	err = w.Write(line)
	if err != nil {
		log.Fatalf("Couldn't write to .prov: %v", err)
	}
	w.Flush()
}
<commit_msg>cleanup<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"time"
)

func main() {
	if len(os.Args) < 3 {
		log.Fatalf("Usage: %s filename cmd...\n", os.Args[0])
	}

	outfile := os.Args[1]
	out, err := os.Create(outfile)
	defer out.Close()
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	subCmd := os.Args[2]
	subArgs := os.Args[3:]

	cmd := exec.Command(subCmd, subArgs...)

	var cmdOut bytes.Buffer
	cmd.Stdout = &cmdOut

	reader := bufio.NewReader(&cmdOut)
	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	hash := sha1.New()
	hashWriter := bufio.NewWriter(hash)

	inbuf := make([]byte, 8)
	n := 0
	for ; err != io.EOF; n, err = reader.Read(inbuf) {
		// Read stdout from subprocess into buffer inbuf.
		// Then write inbuf to both the hasher and to the output file.
		hashbuf := bytes.NewBuffer(inbuf[:n])
		if _, err := io.Copy(hashWriter, hashbuf); err != nil {
			log.Fatalf(err.Error())
		}
		outbuf := bytes.NewBuffer(inbuf[:n])
		if _, err := io.Copy(out, outbuf); err != nil {
			log.Fatalf(err.Error())
		}
	}

	hashWriter.Flush()

	pFile, err := os.OpenFile(".prov", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	defer pFile.Close()
	if err != nil {
		log.Fatalf("Couldn't open .prov: %v", err)
	}

	line := []string{
		fmt.Sprintf("%x", hash.Sum(nil)),
		fmt.Sprintf("%v", time.Now()),
		os.Getenv("USER"),
		fmt.Sprintf("%s", subCmd),
	}
	line = append(line, subArgs...)
	w := csv.NewWriter(pFile)

	err = w.Write(line)
	if err != nil {
		log.Fatalf("Couldn't write to .prov: %v", err)
	}
	w.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"time"

	quic "github.com/phuslu/quic-go"
	"github.com/phuslu/quic-go/h2quic"
)

func testQuic(ip string, config *GScanConfig, record *ScanRecord) bool {
	addr := net.JoinHostPort(ip, "443")

	start := time.Now()
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return false
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return false
	}
	udpConn.SetDeadline(time.Now().Add(config.Quic.ScanMaxRTT))
	defer udpConn.Close()

	quicCfg := &quic.Config{
		HandshakeTimeout: config.Quic.HandshakeTimeout,
		KeepAlive:        false,
	}

	serverName := config.Quic.ServerName[rand.Intn(len(config.Quic.ServerName))]
	tlsCfg := &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         serverName,
	}
	quicSessn, err := quic.Dial(udpConn, udpAddr, addr, tlsCfg, quicCfg)
	if err != nil {
		return false
	}
	defer quicSessn.Close(nil)

	// lv1 只会验证证书是否存在
	cs := quicSessn.ConnectionState()
	if cs == nil {
		return false
	}
	pcs := cs.PeerCertificates
	if pcs == nil && len(pcs) < 2 {
		return false
	}

	// lv2 验证证书是否正确
	if config.Quic.Level > 1 {
		pkp := pcs[1].RawSubjectPublicKeyInfo
		if !bytes.Equal(g2pkp, pkp) && !bytes.Equal(g3pkp, pkp) { // && !bytes.Equal(g3ecc, pkp[:]) {
			return false
		}
	}

	// lv3 使用 http 访问来验证
	if config.Quic.Level > 2 {
		tr := &h2quic.RoundTripper{DisableCompression: true}
		defer tr.Close()

		tr.DialAddr = func(hostname string, tlsConfig *tls.Config, config *quic.Config) (quic.Session, error) {
			return quicSessn, err
		}
		hclient := &http.Client{
			Transport: tr,
		}
		req, _ := http.NewRequest(http.MethodHead, "https://"+serverName, nil)
		req.Close = true
		resp, err := hclient.Do(req)
		if resp != nil && resp.Body != nil {
			io.Copy(ioutil.Discard, resp.Body)
			resp.Body.Close()
		}
		if err != nil || resp.StatusCode >= 400 {
			return false
		}
	}

	record.RTT = record.RTT + time.Since(start)
	return true
}
<commit_msg>加强QUIC验证<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"strings"
	"time"

	quic "github.com/phuslu/quic-go"
	"github.com/phuslu/quic-go/h2quic"
)

func testQuic(ip string, config *GScanConfig, record *ScanRecord) bool {
	addr := net.JoinHostPort(ip, "443")

	start := time.Now()
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return false
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return false
	}
	udpConn.SetDeadline(time.Now().Add(config.Quic.ScanMaxRTT))
	defer udpConn.Close()

	quicCfg := &quic.Config{
		HandshakeTimeout: config.Quic.HandshakeTimeout,
		KeepAlive:        false,
	}

	serverName := config.Quic.ServerName[rand.Intn(len(config.Quic.ServerName))]
	tlsCfg := &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         serverName,
	}
	quicSessn, err := quic.Dial(udpConn, udpAddr, addr, tlsCfg, quicCfg)
	if err != nil {
		return false
	}
	defer quicSessn.Close(nil)

	// lv1 只会验证证书是否存在
	cs := quicSessn.ConnectionState()
	if cs == nil {
		return false
	}
	pcs := cs.PeerCertificates
	if pcs == nil && len(pcs) < 2 {
		return false
	}

	// lv2 验证证书是否正确
	if config.Quic.Level > 1 {
		pkp := pcs[1].RawSubjectPublicKeyInfo
		if !bytes.Equal(g2pkp, pkp) && !bytes.Equal(g3pkp, pkp) { // && !bytes.Equal(g3ecc, pkp[:]) {
			return false
		}
	}

	// lv3 使用 http 访问来验证
	if config.Quic.Level > 2 {
		tr := &h2quic.RoundTripper{DisableCompression: true}
		defer tr.Close()

		tr.DialAddr = func(hostname string, tlsConfig *tls.Config, config *quic.Config) (quic.Session, error) {
			return quicSessn, err
		}
		hclient := &http.Client{
			Transport: tr,
		}
		req, _ := http.NewRequest(http.MethodHead, "https://"+serverName, nil)
		req.Close = true
		resp, err := hclient.Do(req)
		if resp != nil && resp.Body != nil {
			io.Copy(ioutil.Discard, resp.Body)
			resp.Body.Close()
		}
		if err != nil || resp.StatusCode >= 400 || !strings.Contains(resp.Header.Get("Alt-Svc"), `quic=":443"`) {
			return false
		}
	}

	record.RTT = record.RTT + time.Since(start)
	return true
}
<|endoftext|>


<commit_before>package pgio

import (
	"encoding/binary"
)

func NextByte(buf []byte) ([]byte, byte) {
	b := buf[0]
	return buf[1:], b
}

func NextUint16(buf []byte) ([]byte, uint16) {
	n := binary.BigEndian.Uint16(buf)
	return buf[2:], n
}

func NextUint32(buf []byte) ([]byte, uint32) {
	n := binary.BigEndian.Uint32(buf)
	return buf[4:], n
}

func NextUint64(buf []byte) ([]byte, uint64) {
	n := binary.BigEndian.Uint64(buf)
	return buf[8:], n
}

func NextInt16(buf []byte) ([]byte, int16) {
	buf, n := NextUint16(buf)
	return buf, int16(n)
}

func NextInt32(buf []byte) ([]byte, int32) {
	buf, n := NextUint32(buf)
	return buf, int32(n)
}

func NextInt64(buf []byte) ([]byte, int64) {
	buf, n := NextUint64(buf)
	return buf, int64(n)
}
<commit_msg>Update pgproto3 to enable pgmock<commit_after>package pgio

import (
	"bytes"
	"encoding/binary"
)

func NextByte(buf []byte) ([]byte, byte) {
	b := buf[0]
	return buf[1:], b
}

func NextUint16(buf []byte) ([]byte, uint16) {
	n := binary.BigEndian.Uint16(buf)
	return buf[2:], n
}

func NextUint32(buf []byte) ([]byte, uint32) {
	n := binary.BigEndian.Uint32(buf)
	return buf[4:], n
}

func NextUint64(buf []byte) ([]byte, uint64) {
	n := binary.BigEndian.Uint64(buf)
	return buf[8:], n
}

func NextInt16(buf []byte) ([]byte, int16) {
	buf, n := NextUint16(buf)
	return buf, int16(n)
}

func NextInt32(buf []byte) ([]byte, int32) {
	buf, n := NextUint32(buf)
	return buf, int32(n)
}

func NextInt64(buf []byte) ([]byte, int64) {
	buf, n := NextUint64(buf)
	return buf, int64(n)
}

func NextCString(buf []byte) ([]byte, string, bool) {
	idx := bytes.IndexByte(buf, 0)
	if idx < 0 {
		return buf, "", false
	}
	cstring := string(buf[:idx])
	buf = buf[:idx+1]
	return buf, cstring, true
}
<|endoftext|>


<commit_before>package main

import (
	//	"github.com/klauern/wlsrest/wls"
	"github.com/spf13/cobra"
	//	"fmt"
	"github.com/klauern/remy/cli"
	"github.com/klauern/remy/wls"
	"github.com/spf13/viper"
)

// Config is the base configuration used for all REST requests to the AdminServer
var Config wls.AdminServer

func main() {

	// Base command for the application.
	var WlsRestCmd = &cobra.Command{
		Use:   "remy",
		Short: "Query a WebLogic Server's Rest Management Extention-enabled resources",
		Long:  "Query your underlying server's resources, including Datasources, Applications, Clusters, and Servers by using the WebLogic RESTful Management Extensions API",
	}

	// Request the Servers resource, optionally passing a specific [servername] instance to get that particular Server.
	var serversCmd = &cobra.Command{
		Use:   "servers [Server to query, blank for ALL]",
		Short: "Display Server information",
		Long:  "Show details on all servers under an AdminServer, or specify a specific one",
		Run:   cli.Servers,
	}

	// Request the Clusters resource, optionally passing a specific [clustername] to get a specific Cluster.
	var clustersCmd = &cobra.Command{
		Use:   "clusters [cluster to query, blank for ALL]",
		Short: "Query clusters under AdminServer",
		Long:  "Query the AdminServer for specific clusters, or leave blank for all clusters that this server owns",
		Run:   cli.Clusters,
	}

	// Datasource command, requesting all datasrouces.  Pass a secondary [datasourcename] to get a specific datasource.
	var datasourcesCmd = &cobra.Command{
		Use:   "datasources [datasources to query, blank for ALL]",
		Short: "Query datasources under AdminServer",
		Long:  "Query the AdminServer for specific datasources, or leave blank for all datasources that this server owns",
		Run:   cli.DataSources,
	}

	// Application list command.  Pass an optional [applicationname] to get a specific application instance details.
	var applicationsCmd = &cobra.Command{
		Use:   "applications [application to query, blank for ALL]",
		Short: "Query applications deployed under AdminServer",
		Long:  "Query the AdminServer for specific applications, or leave blank for all applications that this server knows about",
		Run:   cli.Applications,
	}

	// Generate a configuration setting file in your ~/ home or local directory.
	// When determined to be in the ~/home, it will be a ~/.wlsrest.cfg file.
	// When a local file, it will be a wlsrest.cfg file instead.
	var configureCmd = &cobra.Command{
		Use:   "config",
		Short: "Configure the credentials and server to default REST connections to",
		Long:  "Configure what Username, Password, and Admin Server:Port you want to send REST requests to when submitting calls on any of the other commands",
		Run:   cli.Configure,
	}

	// Add option to pass --full-format for all responses.  Single server, application, etc., requests will always return
	// full responses, but group-related queries will return shortened versions
	WlsRestCmd.PersistentFlags().BoolVarP(&cli.FullFormat, cli.FullFormatFlag, "f", false, "Return full format from REST server")

	// Allow specific AdminServer URL to be passed in to override local config files
	WlsRestCmd.PersistentFlags().StringVarP(&Config.AdminURL, cli.AdminURLFlag, "s", "http://localhost:7001", "Url for the Admin Server")

	// Allow the Username property to be overridden locally on the command-line
	WlsRestCmd.PersistentFlags().StringVarP(&Config.Username, cli.UsernameFlag, "u", "weblogic", "Username with privileges to access AdminServer")

	// Allow the Password property to be overridden on the command-line
	WlsRestCmd.PersistentFlags().StringVarP(&Config.Password, cli.PasswordFlag, "p", "welcome1", "Password for the user")

	viper.BindPFlags(WlsRestCmd.PersistentFlags())

	WlsRestCmd.AddCommand(applicationsCmd, configureCmd, clustersCmd, datasourcesCmd, serversCmd)
	WlsRestCmd.Execute()
}
<commit_msg>add version command<commit_after>package main

import (
	//	"github.com/klauern/wlsrest/wls"
	"github.com/spf13/cobra"
	//	"fmt"
	"github.com/klauern/remy/cli"
	"github.com/klauern/remy/wls"
	"github.com/spf13/viper"
	"fmt"
)

// Config is the base configuration used for all REST requests to the AdminServer
var Config wls.AdminServer

const (
	remyVersion string = "0.1"
)

func main() {

	// Base command for the application.
	var WlsRestCmd = &cobra.Command{
		Use:   "remy",
		Short: "Query a WebLogic Server's Rest Management Extention-enabled resources",
		Long:  "Query your underlying server's resources, including Datasources, Applications, Clusters, and Servers by using the WebLogic RESTful Management Extensions API",
	}

	// Request the Servers resource, optionally passing a specific [servername] instance to get that particular Server.
	var serversCmd = &cobra.Command{
		Use:   "servers [Server to query, blank for ALL]",
		Short: "Display Server information",
		Long:  "Show details on all servers under an AdminServer, or specify a specific one",
		Run:   cli.Servers,
	}

	// Request the Clusters resource, optionally passing a specific [clustername] to get a specific Cluster.
	var clustersCmd = &cobra.Command{
		Use:   "clusters [cluster to query, blank for ALL]",
		Short: "Query clusters under AdminServer",
		Long:  "Query the AdminServer for specific clusters, or leave blank for all clusters that this server owns",
		Run:   cli.Clusters,
	}

	// Datasource command, requesting all datasrouces.  Pass a secondary [datasourcename] to get a specific datasource.
	var datasourcesCmd = &cobra.Command{
		Use:   "datasources [datasources to query, blank for ALL]",
		Short: "Query datasources under AdminServer",
		Long:  "Query the AdminServer for specific datasources, or leave blank for all datasources that this server owns",
		Run:   cli.DataSources,
	}

	// Application list command.  Pass an optional [applicationname] to get a specific application instance details.
	var applicationsCmd = &cobra.Command{
		Use:   "applications [application to query, blank for ALL]",
		Short: "Query applications deployed under AdminServer",
		Long:  "Query the AdminServer for specific applications, or leave blank for all applications that this server knows about",
		Run:   cli.Applications,
	}

	// Generate a configuration setting file in your ~/ home or local directory.
	// When determined to be in the ~/home, it will be a ~/.wlsrest.cfg file.
	// When a local file, it will be a wlsrest.cfg file instead.
	var configureCmd = &cobra.Command{
		Use:   "config",
		Short: "Configure the credentials and server to default REST connections to",
		Long:  "Configure what Username, Password, and Admin Server:Port you want to send REST requests to when submitting calls on any of the other commands",
		Run:   cli.Configure,
	}

	// Version command displays the version of the application.
	var versionCmd = &cobra.Command{
		Use: "version",
		Short: "Show the version of this command",
		Long: "Display the version of this command",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("remy version %v\n", remyVersion)
		},
	}

	// Add option to pass --full-format for all responses.  Single server, application, etc., requests will always return
	// full responses, but group-related queries will return shortened versions
	WlsRestCmd.PersistentFlags().BoolVarP(&cli.FullFormat, cli.FullFormatFlag, "f", false, "Return full format from REST server")

	// Allow specific AdminServer URL to be passed in to override local config files
	WlsRestCmd.PersistentFlags().StringVarP(&Config.AdminURL, cli.AdminURLFlag, "s", "http://localhost:7001", "Url for the Admin Server")

	// Allow the Username property to be overridden locally on the command-line
	WlsRestCmd.PersistentFlags().StringVarP(&Config.Username, cli.UsernameFlag, "u", "weblogic", "Username with privileges to access AdminServer")

	// Allow the Password property to be overridden on the command-line
	WlsRestCmd.PersistentFlags().StringVarP(&Config.Password, cli.PasswordFlag, "p", "welcome1", "Password for the user")

	viper.BindPFlags(WlsRestCmd.PersistentFlags())

	WlsRestCmd.AddCommand(applicationsCmd, configureCmd, clustersCmd, datasourcesCmd, serversCmd, versionCmd)
	WlsRestCmd.Execute()
}
<|endoftext|>