<commit_before>package core

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"pilosa/config"
	"pilosa/db"
	"reflect"
	"runtime"
	"strconv"

	notify "github.com/bitly/go-notify"
	"github.com/davecgh/go-spew/spew"
	"github.com/gorilla/websocket"
	"tux21b.org/v1/gocql/uuid"
)

type WebService struct {
	service *Service
}

func NewWebService(service *Service) *WebService {
	return &WebService{service}
}

func (self *WebService) Run() {
	port_string := strconv.Itoa(config.GetInt("port_http"))
	log.Printf("Serving HTTP on port %s...\n", port_string)
	mux := http.NewServeMux()
	mux.HandleFunc("/message", self.HandleMessage)
	mux.HandleFunc("/query", self.HandleQuery)
	mux.HandleFunc("/stats", self.HandleStats)
	mux.HandleFunc("/info", self.HandleInfo)
	mux.HandleFunc("/processes", self.HandleProcesses)
	mux.HandleFunc("/listen/ws", self.HandleListenWS)
	mux.HandleFunc("/listen/stream", self.HandleListenStream)
	mux.HandleFunc("/listen", self.HandleListen)
	mux.HandleFunc("/test", self.HandleTest)
	mux.HandleFunc("/version", self.HandleVersion)
	mux.HandleFunc("/ping", self.HandlePing)
	mux.HandleFunc("/batch", self.HandleBatch)
	s := &http.Server{
		Addr:    ":" + port_string,
		Handler: mux,
	}
	s.ListenAndServe()
}

func (self *WebService) HandleMessage(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
		return
	}
	var message db.Message
	decoder := json.NewDecoder(r.Body)
	if decoder.Decode(&message) != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}
	//service.Inbox <- &message
}

func (self *WebService) HandleBatch(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
		return
	}
	/*   	values.Set("db", database)
	values.Set("id", id)
	values.Set("frame", fragment_type)
	values.Set("slice", fmt.Sprintf("%d", slice))
	values.Set("bitmap", compressed_string)
	*/

	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	database_name := r.Form.Get("db")
	if database_name == "" {
		http.Error(w, "Provide a database (db)", http.StatusNotFound)
		return
	}

	bitmap_id_string := r.Form.Get("id")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a bitmap id (id)", http.StatusNotFound)
		return
	}
	bitmap_id, _ := strconv.ParseUint(bitmap_id_string, 10, 64)

	slice_string := r.Form.Get("slice")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a slice (slice)", http.StatusNotFound)
		return
	}
	slice, _ := strconv.ParseInt(slice_string, 10, 32)

	frame := r.Form.Get("frame")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a frame (frame)", http.StatusNotFound)
		return
	}

	compressed_bitmap := r.Form.Get("bitmap")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a compressed base64 bitmap (bitmap)", http.StatusNotFound)
		return
	}

	results := self.service.Batch(database_name, frame, compressed_bitmap, bitmap_id, int(slice))

	encoder := json.NewEncoder(w)
	err = encoder.Encode(results)
	if err != nil {
		log.Println("Error Batch results")
		log.Println(spew.Sdump(r.Form))
		err = encoder.Encode("Bad Batch Request")
	}

}

func (self *WebService) HandleQuery(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
		return
	}
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	database_name := r.Form.Get("db")
	if database_name == "" {
		database_name = config.GetString("default_db")
	}
	if database_name == "" {
		http.Error(w, "Provide a database (db)", http.StatusNotFound)
		return
	}
	pql := r.Form.Get("pql")
	if pql == "" {
		http.Error(w, "Provide a valid query string (pql)", http.StatusNotFound)
		return
	}

	results := self.service.Executor.RunPQL(database_name, pql)

	encoder := json.NewEncoder(w)
	err = encoder.Encode(results)
	if err != nil {
		log.Fatal("Error encoding stats")
	}

}

func (self *WebService) HandleStats(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	encoder := json.NewEncoder(w)
	//stats := service.GetStats()
	//	stats := ""
	m := &runtime.MemStats{}
	runtime.ReadMemStats(m)
	stats := map[string]interface{}{"num_goroutines": runtime.NumGoroutine(), "Memory Aquired": m.Sys, "Memory Used": m.Alloc}

	err := encoder.Encode(stats)
	if err != nil {
		log.Fatal("Error encoding stats")
	}
}

func (self *WebService) HandleInfo(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	spew.Fdump(w, self.service.Cluster)
}

func (self *WebService) HandleVersion(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}

	fmt.Fprintf(w, "Pilosa v."+self.service.version+"\n")
}

func (self *WebService) HandleTest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	spew.Dump("TEST!")

	msg := new(db.Message)
	msg.Data = "mystring"
	self.service.Transport.Push(msg)

	msg2 := new(db.Message)
	msg2.Data = 789
	self.service.Transport.Push(msg2)
}

func (self *WebService) HandleProcesses(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	encoder := json.NewEncoder(w)
	processes := self.service.ProcessMap.GetMetadata()
	err := encoder.Encode(processes)
	if err != nil {
		log.Fatal("Error encoding stats")
	}
}

func (self *WebService) HandlePing(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	process_string := r.Form.Get("process")
	process_id, err := uuid.ParseUUID(process_string)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	_, err = self.service.ProcessMap.GetProcess(&process_id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	duration, err := self.service.Ping(&process_id)
	if err != nil {
		spew.Fdump(w, err)
		return
	}
	encoder := json.NewEncoder(w)
	encoder.Encode(map[string]float64{"duration": duration.Seconds()})
}

func (self *WebService) HandleListen(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`
	<title>Pilosa - streaming client</title>

	`))
}

func (self *WebService) streamer(writer func(map[string]interface{}) error) {
	inbox := make(chan interface{}, 10)
	outbox := make(chan interface{}, 10)
	notify.Start("inbox", inbox)
	notify.Start("outbox", outbox)
	var obj interface{}
	var data interface{}
	var inmessage *db.Message
	var outmessage *db.Envelope
	var host string
	for {
		select {
		case obj = <-outbox:
			outmessage = obj.(*db.Envelope)
			data = outmessage.Message.Data
			host = outmessage.Host.String()
		case obj = <-inbox:
			inmessage = obj.(*db.Message)
			data = inmessage.Data
			host = ""
		}

		typ := reflect.TypeOf(data)

		err := writer(map[string]interface{}{
			"type": typ.String(),
			"dump": spew.Sdump(data),
			"host": host,
		})
		if err != nil {
			log.Println("stopping")
			notify.Stop("inbox", inbox)
			notify.Stop("outbox", outbox)
			drain(inbox)
			drain(outbox)
			return
		}
	}
}

func (self *WebService) HandleListenWS(w http.ResponseWriter, r *http.Request) {
	defer func() {
		err := recover()
		spew.Dump(err)
	}()
	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		log.Println(err)
		return
	}
	self.streamer(func(data map[string]interface{}) error {
		return ws.WriteJSON(data)
	})
}

func drain(ch chan interface{}) {
	for {
		switch {
		case <-ch:
		default:
			return
		}
	}
}

func (self *WebService) HandleListenStream(w http.ResponseWriter, r *http.Request) {
	defer func() {
		err := recover()
		spew.Dump(err)
	}()
	writer := json.NewEncoder(w)
	flusher := w.(http.Flusher)
	self.streamer(func(data map[string]interface{}) error {
		err := writer.Encode(data)
		flusher.Flush()
		return err
	})
}
<commit_msg>Add status screen<commit_after>package core

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"pilosa/config"
	"pilosa/db"
	"reflect"
	"runtime"
	"strconv"

	notify "github.com/bitly/go-notify"
	"github.com/davecgh/go-spew/spew"
	"github.com/gorilla/websocket"
	"tux21b.org/v1/gocql/uuid"
)

type WebService struct {
	service *Service
}

func NewWebService(service *Service) *WebService {
	return &WebService{service}
}

func (self *WebService) Run() {
	port_string := strconv.Itoa(config.GetInt("port_http"))
	log.Printf("Serving HTTP on port %s...\n", port_string)
	mux := http.NewServeMux()
	mux.HandleFunc("/message", self.HandleMessage)
	mux.HandleFunc("/query", self.HandleQuery)
	mux.HandleFunc("/stats", self.HandleStats)
	mux.HandleFunc("/status", self.HandleStatus)
	mux.HandleFunc("/info", self.HandleInfo)
	mux.HandleFunc("/processes", self.HandleProcesses)
	mux.HandleFunc("/listen/ws", self.HandleListenWS)
	mux.HandleFunc("/listen/stream", self.HandleListenStream)
	mux.HandleFunc("/listen", self.HandleListen)
	mux.HandleFunc("/test", self.HandleTest)
	mux.HandleFunc("/version", self.HandleVersion)
	mux.HandleFunc("/ping", self.HandlePing)
	mux.HandleFunc("/batch", self.HandleBatch)
	s := &http.Server{
		Addr:    ":" + port_string,
		Handler: mux,
	}
	s.ListenAndServe()
}

func (self *WebService) HandleMessage(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
		return
	}
	var message db.Message
	decoder := json.NewDecoder(r.Body)
	if decoder.Decode(&message) != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}
	//service.Inbox <- &message
}

func (self *WebService) HandleBatch(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
		return
	}
	/*   	values.Set("db", database)
	values.Set("id", id)
	values.Set("frame", fragment_type)
	values.Set("slice", fmt.Sprintf("%d", slice))
	values.Set("bitmap", compressed_string)
	*/

	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	database_name := r.Form.Get("db")
	if database_name == "" {
		http.Error(w, "Provide a database (db)", http.StatusNotFound)
		return
	}

	bitmap_id_string := r.Form.Get("id")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a bitmap id (id)", http.StatusNotFound)
		return
	}
	bitmap_id, _ := strconv.ParseUint(bitmap_id_string, 10, 64)

	slice_string := r.Form.Get("slice")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a slice (slice)", http.StatusNotFound)
		return
	}
	slice, _ := strconv.ParseInt(slice_string, 10, 32)

	frame := r.Form.Get("frame")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a frame (frame)", http.StatusNotFound)
		return
	}

	compressed_bitmap := r.Form.Get("bitmap")
	if bitmap_id_string == "" {
		http.Error(w, "Provide a compressed base64 bitmap (bitmap)", http.StatusNotFound)
		return
	}

	results := self.service.Batch(database_name, frame, compressed_bitmap, bitmap_id, int(slice))

	encoder := json.NewEncoder(w)
	err = encoder.Encode(results)
	if err != nil {
		log.Println("Error Batch results")
		log.Println(spew.Sdump(r.Form))
		err = encoder.Encode("Bad Batch Request")
	}

}

func (self *WebService) HandleQuery(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
		return
	}
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	database_name := r.Form.Get("db")
	if database_name == "" {
		database_name = config.GetString("default_db")
	}
	if database_name == "" {
		http.Error(w, "Provide a database (db)", http.StatusNotFound)
		return
	}
	pql := r.Form.Get("pql")
	if pql == "" {
		http.Error(w, "Provide a valid query string (pql)", http.StatusNotFound)
		return
	}

	results := self.service.Executor.RunPQL(database_name, pql)

	encoder := json.NewEncoder(w)
	err = encoder.Encode(results)
	if err != nil {
		log.Fatal("Error encoding stats")
	}

}

func (self *WebService) HandleStats(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	encoder := json.NewEncoder(w)
	//stats := service.GetStats()
	//	stats := ""
	m := &runtime.MemStats{}
	runtime.ReadMemStats(m)
	stats := map[string]interface{}{"num_goroutines": runtime.NumGoroutine(), "Memory Aquired": m.Sys, "Memory Used": m.Alloc}

	err := encoder.Encode(stats)
	if err != nil {
		log.Fatal("Error encoding stats")
	}
}

func (self *WebService) HandleInfo(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	spew.Fdump(w, self.service.Cluster)
}

func (self *WebService) HandleVersion(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}

	fmt.Fprintf(w, "Pilosa v."+self.service.version+"\n")
}

func (self *WebService) HandleTest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	spew.Dump("TEST!")

	msg := new(db.Message)
	msg.Data = "mystring"
	self.service.Transport.Push(msg)

	msg2 := new(db.Message)
	msg2.Data = 789
	self.service.Transport.Push(msg2)
}

func (self *WebService) HandleProcesses(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	encoder := json.NewEncoder(w)
	processes := self.service.ProcessMap.GetMetadata()
	err := encoder.Encode(processes)
	if err != nil {
		log.Fatal("Error encoding stats")
	}
}

func (self *WebService) HandlePing(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "Only GET allowed", http.StatusMethodNotAllowed)
		return
	}
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	process_string := r.Form.Get("process")
	process_id, err := uuid.ParseUUID(process_string)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	_, err = self.service.ProcessMap.GetProcess(&process_id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	duration, err := self.service.Ping(&process_id)
	if err != nil {
		spew.Fdump(w, err)
		return
	}
	encoder := json.NewEncoder(w)
	encoder.Encode(map[string]float64{"duration": duration.Seconds()})
}

func (self *WebService) HandleListen(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`
	<title>Pilosa - streaming client</title>

	`))
}

func (self *WebService) streamer(writer func(map[string]interface{}) error) {
	inbox := make(chan interface{}, 10)
	outbox := make(chan interface{}, 10)
	notify.Start("inbox", inbox)
	notify.Start("outbox", outbox)
	var obj interface{}
	var data interface{}
	var inmessage *db.Message
	var outmessage *db.Envelope
	var host string
	for {
		select {
		case obj = <-outbox:
			outmessage = obj.(*db.Envelope)
			data = outmessage.Message.Data
			host = outmessage.Host.String()
		case obj = <-inbox:
			inmessage = obj.(*db.Message)
			data = inmessage.Data
			host = ""
		}

		typ := reflect.TypeOf(data)

		err := writer(map[string]interface{}{
			"type": typ.String(),
			"dump": spew.Sdump(data),
			"host": host,
		})
		if err != nil {
			log.Println("stopping")
			notify.Stop("inbox", inbox)
			notify.Stop("outbox", outbox)
			drain(inbox)
			drain(outbox)
			return
		}
	}
}

func (self *WebService) HandleListenWS(w http.ResponseWriter, r *http.Request) {
	defer func() {
		err := recover()
		spew.Dump(err)
	}()
	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(w, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		log.Println(err)
		return
	}
	self.streamer(func(data map[string]interface{}) error {
		return ws.WriteJSON(data)
	})
}

func drain(ch chan interface{}) {
	for {
		switch {
		case <-ch:
		default:
			return
		}
	}
}

func (self *WebService) HandleListenStream(w http.ResponseWriter, r *http.Request) {
	defer func() {
		err := recover()
		spew.Dump(err)
	}()
	writer := json.NewEncoder(w)
	flusher := w.(http.Flusher)
	self.streamer(func(data map[string]interface{}) error {
		err := writer.Encode(data)
		flusher.Flush()
		return err
	})
}

func (self *WebService) HandleStatus(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`
	<title>Pilosa - status</title>

	`))
}
<|endoftext|>


<commit_before>package authentication

import (
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"github.com/ONSdigital/go-launch-a-survey/settings"
	"github.com/ONSdigital/go-launch-a-survey/surveys"
	"github.com/satori/go.uuid"
	"gopkg.in/square/go-jose.v2"
	"gopkg.in/square/go-jose.v2/json"
	"gopkg.in/square/go-jose.v2/jwt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"bytes"
	"encoding/base64"
	"log"
	"math/rand"
	"path"
	"strings"
)

// KeyLoadError describes an error that can occur during key loading
type KeyLoadError struct {
	// Op is the operation which caused the error, such as
	// "read", "parse" or "cast".
	Op string

	// Err is a description of the error that occurred during the operation.
	Err string
}

func (e *KeyLoadError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return e.Op + ": " + e.Err
}

// PublicKeyResult is a wrapper for the public key and the kid that identifies it
type PublicKeyResult struct {
	key *rsa.PublicKey
	kid string
}

// PrivateKeyResult is a wrapper for the private key and the kid that identifies it
type PrivateKeyResult struct {
	key *rsa.PrivateKey
	kid string
}

func loadEncryptionKey() (*PublicKeyResult, *KeyLoadError) {
	encryptionKeyPath := settings.Get("JWT_ENCRYPTION_KEY_PATH")

	keyData, err := ioutil.ReadFile(encryptionKeyPath)
	if err != nil {
		return nil, &KeyLoadError{Op: "read", Err: "Failed to read encryption key from file: " + encryptionKeyPath}
	}

	block, _ := pem.Decode(keyData)
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, &KeyLoadError{Op: "parse", Err: "Failed to parse encryption key PEM"}
	}

	kid := fmt.Sprintf("%x", sha1.Sum(keyData))

	publicKey, ok := pub.(*rsa.PublicKey)
	if !ok {
		return nil, &KeyLoadError{Op: "cast", Err: "Failed to cast key to rsa.PublicKey"}
	}

	return &PublicKeyResult{publicKey, kid}, nil
}

func loadSigningKey() (*PrivateKeyResult, *KeyLoadError) {
	signingKeyPath := settings.Get("JWT_SIGNING_KEY_PATH")
	keyData, err := ioutil.ReadFile(signingKeyPath)
	if err != nil {
		return nil, &KeyLoadError{Op: "read", Err: "Failed to read signing key from file: " + signingKeyPath}
	}

	block, _ := pem.Decode(keyData)
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, &KeyLoadError{Op: "parse", Err: "Failed to parse signing key from PEM"}
	}

	PublicKey, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
	if err != nil {
		return nil, &KeyLoadError{Op: "marshal", Err: "Failed to marshal public key"}
	}

	pubBytes := pem.EncodeToMemory(&pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: PublicKey,
	})
	kid := fmt.Sprintf("%x", sha1.Sum(pubBytes))

	return &PrivateKeyResult{privateKey, kid}, nil
}

// EqClaims is a representation of the set of values needed when generating a valid token
type EqClaims struct {
	jwt.Claims
	UserID                string       `json:"user_id"`
	EqID                  string       `json:"eq_id"`
	PeriodID              string       `json:"period_id"`
	PeriodStr             string       `json:"period_str"`
	CollectionExerciseSid string       `json:"collection_exercise_sid"`
	RuRef                 string       `json:"ru_ref"`
	RuName                string       `json:"ru_name"`
	RefPStartDate         string       `json:"ref_p_start_date"`         // iso_8601_date
	RefPEndDate           string       `json:"ref_p_end_date,omitempty"` // iso_8601_date
	FormType              string       `json:"form_type"`
	SurveyURL             string       `json:"survey_url,omitempty"`
	ReturnBy              string       `json:"return_by"`
	TradAs                string       `json:"trad_as,omitempty"`
	EmploymentDate        string       `json:"employment_date,omitempty"` // iso_8601_date
	RegionCode            string       `json:"region_code,omitempty"`
	LanguageCode          string       `json:"language_code,omitempty"`
	VariantFlags          variantFlags `json:"variant_flags,omitempty"`
	TxID                  string       `json:"tx_id,omitempty"`
	Roles                 []string     `json:"roles,omitempty"`
	CaseID                string       `json:"case_id,omitempty"`
	CaseRef               string       `json:"case_ref,omitempty"`
	AccountServiceURL     string       `json:"account_service_url,omitempty"`
}

type variantFlags struct {
	SexualIdentity bool `json:"sexual_identity,omitempty"`
}

// QuestionnaireSchema is a minimal representation of a questionnaire schema used for extracting the eq_id and form_type
type QuestionnaireSchema struct {
	EqID     string `json:"eq_id"`
	FormType string `json:"form_type"`
}

// Generates a random string of a defined length
func randomStringGen() (rs string) {
	size := 6 //change the length of the generated random string
	rb := make([]byte, size)
	_, err := rand.Read(rb)

	if err != nil {
		log.Println(err)
	}

	randomString := base64.URLEncoding.EncodeToString(rb)

	return randomString
}

func getStringOrDefault(key string, values map[string][]string, defaultValue string) string {
	var value string
	if keyValues, ok := values[key]; ok {
		value = keyValues[0]
	} else {
		value = defaultValue
	}
	return value
}

func generateClaims(claimValues map[string][]string) (claims EqClaims) {
	userID := getStringOrDefault("user_id", claimValues, "UNKNOWN")
	periodID := getStringOrDefault("period_id", claimValues, "201605")
	periodStr := getStringOrDefault("period_str", claimValues, "May 2017")
	collexSID := getStringOrDefault("collection_exercise_sid", claimValues, randomStringGen())
	ruRef := getStringOrDefault("ru_ref", claimValues, "12346789012A")
	ruName := getStringOrDefault("ru_name", claimValues, "ESSENTIAL ENTERPRISE LTD.")
	refPStartDate := getStringOrDefault("ref_p_start_date", claimValues, "2016-05-01")
	refPEndDate := getStringOrDefault("ref_p_end_date", claimValues, "2016-05-31")
	returnBy := getStringOrDefault("return_by", claimValues, "2016-06-12")
	tradAs := getStringOrDefault("trad_as", claimValues, "ESSENTIAL ENTERPRISE LTD.")
	employmentDate := getStringOrDefault("employmentDate", claimValues, "2016-06-10")
	regionCode := getStringOrDefault("region_code", claimValues, "GB-ENG")
	languageCode := getStringOrDefault("language_code", claimValues, "en")
	caseRef := getStringOrDefault("case_ref", claimValues, "1000000000000001")
	accountURL := getStringOrDefault("account_url", claimValues, "")

	var sexualIdentity bool
	if sexualIdentityValues, ok := claimValues["sexual_identity"]; ok {
		sexualIdentity = sexualIdentityValues[0] == "true"
	} else {
		sexualIdentity = true
	}

	var roles []string
	if rolesValues, ok := claimValues["roles"]; ok {
		roles = rolesValues
	} else {
		roles = []string{"dumper"}
	}

	claims = EqClaims{
		UserID:                userID,
		PeriodID:              periodID,
		PeriodStr:             periodStr,
		CollectionExerciseSid: collexSID,
		RuRef:          ruRef,
		RuName:         ruName,
		RefPStartDate:  refPStartDate,
		RefPEndDate:    refPEndDate,
		ReturnBy:       returnBy,
		TradAs:         tradAs,
		EmploymentDate: employmentDate,
		RegionCode:     regionCode,
		LanguageCode:   languageCode,
		TxID:           uuid.NewV4().String(),
		VariantFlags: variantFlags{
			SexualIdentity: sexualIdentity,
		},
		Roles:             roles,
		CaseID:            uuid.NewV4().String(),
		CaseRef:           caseRef,
		AccountServiceURL: accountURL,
	}

	return claims
}

// GenerateJwtClaims creates a jwtClaim needed to generate a token
func GenerateJwtClaims() (jwtClaims jwt.Claims) {
	issued := time.Now()
	expires := issued.Add(time.Minute * 10) // TODO: Support custom exp: r.PostForm.Get("exp")

	jwtClaims = jwt.Claims{
		IssuedAt: jwt.NewNumericDate(issued),
		Expiry:   jwt.NewNumericDate(expires),
		ID:       uuid.NewV4().String(),
	}

	return jwtClaims
}

func launcherSchemaFromURL(url string) (launcherSchema surveys.LauncherSchema, error string) {
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}

	if resp.StatusCode != 200 {
		return launcherSchema, fmt.Sprintf("Failed to load Schema from %s", url)
	}

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	validationError := validateSchema(responseBody)
	if validationError != "" {
		return launcherSchema, validationError
	}

	var schema QuestionnaireSchema
	if err := json.Unmarshal(responseBody, &schema); err != nil {
		panic(err)
	}

	cacheBust := ""
	if !strings.Contains(url, "?") {
		cacheBust = "?bust=" + time.Now().Format("20060102150405")
	}

	launcherSchema = surveys.LauncherSchema{
		EqID:     schema.EqID,
		FormType: schema.FormType,
		URL:      url + cacheBust,
	}

	return launcherSchema, ""
}

func validateSchema(payload []byte) (error string) {
	if settings.Get("SCHEMA_VALIDATOR_URL") == "" {
		return ""
	}

	validateURL, _ := url.Parse(settings.Get("SCHEMA_VALIDATOR_URL"))
	validateURL.Path = path.Join(validateURL.Path, "validate")

	resp, err := http.Post(validateURL.String(), "application/json", bytes.NewBuffer(payload))
	if err != nil {
		return err.Error()
	}

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err.Error()
	}

	if resp.StatusCode != 200 {
		return string(responseBody)
	}

	return ""
}

func addSchemaToClaims(claims *EqClaims, LauncherSchema surveys.LauncherSchema) {
	claims.EqID = LauncherSchema.EqID
	claims.FormType = LauncherSchema.FormType
	claims.SurveyURL = LauncherSchema.URL
}

// TokenError describes an error that can occur during JWT generation
type TokenError struct {
	// Err is a description of the error that occurred.
	Desc string

	// From is optionally the original error from which this one was caused.
	From error
}

func (e *TokenError) Error() string {
	if e == nil {
		return "<nil>"
	}
	err := e.Desc
	if e.From != nil {
		err += " (" + e.From.Error() + ")"
	}
	return err
}

// generateTokenFromClaims creates a token though encryption using the private and public keys
func generateTokenFromClaims(cl EqClaims) (string, *TokenError) {
	privateKeyResult, keyErr := loadSigningKey()
	if keyErr != nil {
		return "", &TokenError{Desc: "Error loading signing key", From: keyErr}
	}

	publicKeyResult, keyErr := loadEncryptionKey()
	if keyErr != nil {
		return "", &TokenError{Desc: "Error loading encryption key", From: keyErr}
	}

	opts := jose.SignerOptions{}
	opts.WithType("JWT")
	opts.WithHeader("kid", privateKeyResult.kid)

	signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: privateKeyResult.key}, &opts)
	if err != nil {
		return "", &TokenError{Desc: "Error creating JWT signer", From: err}
	}

	encryptor, err := jose.NewEncrypter(
		jose.A256GCM,
		jose.Recipient{Algorithm: jose.RSA_OAEP, Key: publicKeyResult.key, KeyID: publicKeyResult.kid},
		(&jose.EncrypterOptions{}).WithType("JWT").WithContentType("JWT"))

	if err != nil {
		return "", &TokenError{Desc: "Error creating JWT signer", From: err}
	}

	token, err := jwt.SignedAndEncrypted(signer, encryptor).Claims(cl).CompactSerialize()

	if err != nil {
		return "", &TokenError{Desc: "Error signing and encrypting JWT", From: err}
	}

	log.Println("Created signed/encrypted JWT:", token)

	return token, nil
}

// GenerateTokenFromDefaults coverts a set of DEFAULT values into a JWT
func GenerateTokenFromDefaults(surveyURL string, accountURL string, urlValues url.Values) (token string, error string) {
	claims := EqClaims{}
	urlValues["account_url"] = []string{accountURL}
	claims = generateClaims(urlValues)

	jwtClaims := GenerateJwtClaims()
	claims.Claims = jwtClaims
	launcherSchema, validationError := launcherSchemaFromURL(surveyURL)
	if validationError != "" {
		return "", validationError
	}
	addSchemaToClaims(&claims, launcherSchema)

	token, tokenError := generateTokenFromClaims(claims)
	if tokenError != nil {
		return token, fmt.Sprintf("GenerateTokenFromDefaults failed err: %v", tokenError)
	}

	return token, ""
}

// GenerateTokenFromPost coverts a set of POST values into a JWT
func GenerateTokenFromPost(postValues url.Values) (string, string) {
	log.Println("POST received: ", postValues)

	claims := EqClaims{}
	claims = generateClaims(postValues)

	jwtClaims := GenerateJwtClaims()
	claims.Claims = jwtClaims

	schema := postValues.Get("schema")
	launcherSchema := surveys.FindSurveyByName(schema)
	addSchemaToClaims(&claims, launcherSchema)

	token, tokenError := generateTokenFromClaims(claims)
	if tokenError != nil {
		return token, fmt.Sprintf("GenerateTokenFromPost failed err: %v", tokenError)
	}

	return token, ""
}
<commit_msg>Generate collection IDs more randomly using crypto/rand<commit_after>package authentication

import (
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"github.com/ONSdigital/go-launch-a-survey/settings"
	"github.com/ONSdigital/go-launch-a-survey/surveys"
	"github.com/satori/go.uuid"
	"gopkg.in/square/go-jose.v2"
	"gopkg.in/square/go-jose.v2/json"
	"gopkg.in/square/go-jose.v2/jwt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"bytes"
	"encoding/base64"
	"log"
	"crypto/rand"
	"path"
	"strings"
)

// KeyLoadError describes an error that can occur during key loading
type KeyLoadError struct {
	// Op is the operation which caused the error, such as
	// "read", "parse" or "cast".
	Op string

	// Err is a description of the error that occurred during the operation.
	Err string
}

func (e *KeyLoadError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return e.Op + ": " + e.Err
}

// PublicKeyResult is a wrapper for the public key and the kid that identifies it
type PublicKeyResult struct {
	key *rsa.PublicKey
	kid string
}

// PrivateKeyResult is a wrapper for the private key and the kid that identifies it
type PrivateKeyResult struct {
	key *rsa.PrivateKey
	kid string
}

func loadEncryptionKey() (*PublicKeyResult, *KeyLoadError) {
	encryptionKeyPath := settings.Get("JWT_ENCRYPTION_KEY_PATH")

	keyData, err := ioutil.ReadFile(encryptionKeyPath)
	if err != nil {
		return nil, &KeyLoadError{Op: "read", Err: "Failed to read encryption key from file: " + encryptionKeyPath}
	}

	block, _ := pem.Decode(keyData)
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, &KeyLoadError{Op: "parse", Err: "Failed to parse encryption key PEM"}
	}

	kid := fmt.Sprintf("%x", sha1.Sum(keyData))

	publicKey, ok := pub.(*rsa.PublicKey)
	if !ok {
		return nil, &KeyLoadError{Op: "cast", Err: "Failed to cast key to rsa.PublicKey"}
	}

	return &PublicKeyResult{publicKey, kid}, nil
}

func loadSigningKey() (*PrivateKeyResult, *KeyLoadError) {
	signingKeyPath := settings.Get("JWT_SIGNING_KEY_PATH")
	keyData, err := ioutil.ReadFile(signingKeyPath)
	if err != nil {
		return nil, &KeyLoadError{Op: "read", Err: "Failed to read signing key from file: " + signingKeyPath}
	}

	block, _ := pem.Decode(keyData)
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, &KeyLoadError{Op: "parse", Err: "Failed to parse signing key from PEM"}
	}

	PublicKey, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
	if err != nil {
		return nil, &KeyLoadError{Op: "marshal", Err: "Failed to marshal public key"}
	}

	pubBytes := pem.EncodeToMemory(&pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: PublicKey,
	})
	kid := fmt.Sprintf("%x", sha1.Sum(pubBytes))

	return &PrivateKeyResult{privateKey, kid}, nil
}

// EqClaims is a representation of the set of values needed when generating a valid token
type EqClaims struct {
	jwt.Claims
	UserID                string       `json:"user_id"`
	EqID                  string       `json:"eq_id"`
	PeriodID              string       `json:"period_id"`
	PeriodStr             string       `json:"period_str"`
	CollectionExerciseSid string       `json:"collection_exercise_sid"`
	RuRef                 string       `json:"ru_ref"`
	RuName                string       `json:"ru_name"`
	RefPStartDate         string       `json:"ref_p_start_date"`         // iso_8601_date
	RefPEndDate           string       `json:"ref_p_end_date,omitempty"` // iso_8601_date
	FormType              string       `json:"form_type"`
	SurveyURL             string       `json:"survey_url,omitempty"`
	ReturnBy              string       `json:"return_by"`
	TradAs                string       `json:"trad_as,omitempty"`
	EmploymentDate        string       `json:"employment_date,omitempty"` // iso_8601_date
	RegionCode            string       `json:"region_code,omitempty"`
	LanguageCode          string       `json:"language_code,omitempty"`
	VariantFlags          variantFlags `json:"variant_flags,omitempty"`
	TxID                  string       `json:"tx_id,omitempty"`
	Roles                 []string     `json:"roles,omitempty"`
	CaseID                string       `json:"case_id,omitempty"`
	CaseRef               string       `json:"case_ref,omitempty"`
	AccountServiceURL     string       `json:"account_service_url,omitempty"`
}

type variantFlags struct {
	SexualIdentity bool `json:"sexual_identity,omitempty"`
}

// QuestionnaireSchema is a minimal representation of a questionnaire schema used for extracting the eq_id and form_type
type QuestionnaireSchema struct {
	EqID     string `json:"eq_id"`
	FormType string `json:"form_type"`
}

// Generates a random string of a defined length
func randomStringGen() (rs string) {
	size := 6 //change the length of the generated random string
	rb := make([]byte, size)
	_, err := rand.Read(rb)

	if err != nil {
		log.Println(err)
	}

	randomString := base64.URLEncoding.EncodeToString(rb)

	return randomString
}

func getStringOrDefault(key string, values map[string][]string, defaultValue string) string {
	var value string
	if keyValues, ok := values[key]; ok {
		value = keyValues[0]
	} else {
		value = defaultValue
	}
	return value
}

func generateClaims(claimValues map[string][]string) (claims EqClaims) {
	userID := getStringOrDefault("user_id", claimValues, "UNKNOWN")
	periodID := getStringOrDefault("period_id", claimValues, "201605")
	periodStr := getStringOrDefault("period_str", claimValues, "May 2017")
	collexSID := getStringOrDefault("collection_exercise_sid", claimValues, randomStringGen())
	ruRef := getStringOrDefault("ru_ref", claimValues, "12346789012A")
	ruName := getStringOrDefault("ru_name", claimValues, "ESSENTIAL ENTERPRISE LTD.")
	refPStartDate := getStringOrDefault("ref_p_start_date", claimValues, "2016-05-01")
	refPEndDate := getStringOrDefault("ref_p_end_date", claimValues, "2016-05-31")
	returnBy := getStringOrDefault("return_by", claimValues, "2016-06-12")
	tradAs := getStringOrDefault("trad_as", claimValues, "ESSENTIAL ENTERPRISE LTD.")
	employmentDate := getStringOrDefault("employmentDate", claimValues, "2016-06-10")
	regionCode := getStringOrDefault("region_code", claimValues, "GB-ENG")
	languageCode := getStringOrDefault("language_code", claimValues, "en")
	caseRef := getStringOrDefault("case_ref", claimValues, "1000000000000001")
	accountURL := getStringOrDefault("account_url", claimValues, "")

	var sexualIdentity bool
	if sexualIdentityValues, ok := claimValues["sexual_identity"]; ok {
		sexualIdentity = sexualIdentityValues[0] == "true"
	} else {
		sexualIdentity = true
	}

	var roles []string
	if rolesValues, ok := claimValues["roles"]; ok {
		roles = rolesValues
	} else {
		roles = []string{"dumper"}
	}

	claims = EqClaims{
		UserID:                userID,
		PeriodID:              periodID,
		PeriodStr:             periodStr,
		CollectionExerciseSid: collexSID,
		RuRef:          ruRef,
		RuName:         ruName,
		RefPStartDate:  refPStartDate,
		RefPEndDate:    refPEndDate,
		ReturnBy:       returnBy,
		TradAs:         tradAs,
		EmploymentDate: employmentDate,
		RegionCode:     regionCode,
		LanguageCode:   languageCode,
		TxID:           uuid.NewV4().String(),
		VariantFlags: variantFlags{
			SexualIdentity: sexualIdentity,
		},
		Roles:             roles,
		CaseID:            uuid.NewV4().String(),
		CaseRef:           caseRef,
		AccountServiceURL: accountURL,
	}

	return claims
}

// GenerateJwtClaims creates a jwtClaim needed to generate a token
func GenerateJwtClaims() (jwtClaims jwt.Claims) {
	issued := time.Now()
	expires := issued.Add(time.Minute * 10) // TODO: Support custom exp: r.PostForm.Get("exp")

	jwtClaims = jwt.Claims{
		IssuedAt: jwt.NewNumericDate(issued),
		Expiry:   jwt.NewNumericDate(expires),
		ID:       uuid.NewV4().String(),
	}

	return jwtClaims
}

func launcherSchemaFromURL(url string) (launcherSchema surveys.LauncherSchema, error string) {
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}

	if resp.StatusCode != 200 {
		return launcherSchema, fmt.Sprintf("Failed to load Schema from %s", url)
	}

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	validationError := validateSchema(responseBody)
	if validationError != "" {
		return launcherSchema, validationError
	}

	var schema QuestionnaireSchema
	if err := json.Unmarshal(responseBody, &schema); err != nil {
		panic(err)
	}

	cacheBust := ""
	if !strings.Contains(url, "?") {
		cacheBust = "?bust=" + time.Now().Format("20060102150405")
	}

	launcherSchema = surveys.LauncherSchema{
		EqID:     schema.EqID,
		FormType: schema.FormType,
		URL:      url + cacheBust,
	}

	return launcherSchema, ""
}

func validateSchema(payload []byte) (error string) {
	if settings.Get("SCHEMA_VALIDATOR_URL") == "" {
		return ""
	}

	validateURL, _ := url.Parse(settings.Get("SCHEMA_VALIDATOR_URL"))
	validateURL.Path = path.Join(validateURL.Path, "validate")

	resp, err := http.Post(validateURL.String(), "application/json", bytes.NewBuffer(payload))
	if err != nil {
		return err.Error()
	}

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err.Error()
	}

	if resp.StatusCode != 200 {
		return string(responseBody)
	}

	return ""
}

func addSchemaToClaims(claims *EqClaims, LauncherSchema surveys.LauncherSchema) {
	claims.EqID = LauncherSchema.EqID
	claims.FormType = LauncherSchema.FormType
	claims.SurveyURL = LauncherSchema.URL
}

// TokenError describes an error that can occur during JWT generation
type TokenError struct {
	// Err is a description of the error that occurred.
	Desc string

	// From is optionally the original error from which this one was caused.
	From error
}

func (e *TokenError) Error() string {
	if e == nil {
		return "<nil>"
	}
	err := e.Desc
	if e.From != nil {
		err += " (" + e.From.Error() + ")"
	}
	return err
}

// generateTokenFromClaims creates a token though encryption using the private and public keys
func generateTokenFromClaims(cl EqClaims) (string, *TokenError) {
	privateKeyResult, keyErr := loadSigningKey()
	if keyErr != nil {
		return "", &TokenError{Desc: "Error loading signing key", From: keyErr}
	}

	publicKeyResult, keyErr := loadEncryptionKey()
	if keyErr != nil {
		return "", &TokenError{Desc: "Error loading encryption key", From: keyErr}
	}

	opts := jose.SignerOptions{}
	opts.WithType("JWT")
	opts.WithHeader("kid", privateKeyResult.kid)

	signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: privateKeyResult.key}, &opts)
	if err != nil {
		return "", &TokenError{Desc: "Error creating JWT signer", From: err}
	}

	encryptor, err := jose.NewEncrypter(
		jose.A256GCM,
		jose.Recipient{Algorithm: jose.RSA_OAEP, Key: publicKeyResult.key, KeyID: publicKeyResult.kid},
		(&jose.EncrypterOptions{}).WithType("JWT").WithContentType("JWT"))

	if err != nil {
		return "", &TokenError{Desc: "Error creating JWT signer", From: err}
	}

	token, err := jwt.SignedAndEncrypted(signer, encryptor).Claims(cl).CompactSerialize()

	if err != nil {
		return "", &TokenError{Desc: "Error signing and encrypting JWT", From: err}
	}

	log.Println("Created signed/encrypted JWT:", token)

	return token, nil
}

// GenerateTokenFromDefaults coverts a set of DEFAULT values into a JWT
func GenerateTokenFromDefaults(surveyURL string, accountURL string, urlValues url.Values) (token string, error string) {
	claims := EqClaims{}
	urlValues["account_url"] = []string{accountURL}
	claims = generateClaims(urlValues)

	jwtClaims := GenerateJwtClaims()
	claims.Claims = jwtClaims
	launcherSchema, validationError := launcherSchemaFromURL(surveyURL)
	if validationError != "" {
		return "", validationError
	}
	addSchemaToClaims(&claims, launcherSchema)

	token, tokenError := generateTokenFromClaims(claims)
	if tokenError != nil {
		return token, fmt.Sprintf("GenerateTokenFromDefaults failed err: %v", tokenError)
	}

	return token, ""
}

// GenerateTokenFromPost coverts a set of POST values into a JWT
func GenerateTokenFromPost(postValues url.Values) (string, string) {
	log.Println("POST received: ", postValues)

	claims := EqClaims{}
	claims = generateClaims(postValues)

	jwtClaims := GenerateJwtClaims()
	claims.Claims = jwtClaims

	schema := postValues.Get("schema")
	launcherSchema := surveys.FindSurveyByName(schema)
	addSchemaToClaims(&claims, launcherSchema)

	token, tokenError := generateTokenFromClaims(claims)
	if tokenError != nil {
		return token, fmt.Sprintf("GenerateTokenFromPost failed err: %v", tokenError)
	}

	return token, ""
}
<|endoftext|>


<commit_before>package proxy

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
)

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

func formatRequest(r *http.Request) string {
	var request []string
	request = append(request, fmt.Sprintf("Host: %v", r.Host))

	for name, headers := range r.Header {
		for _, h := range headers {
			request = append(request, fmt.Sprintf("%v: %v", name, h))
		}
	}

	return strings.Join(request, "\n")
}

func NewErgoProxy(config *Config) *httputil.ReverseProxy {
	director := func(req *http.Request) {
		t := time.Now()
		fmt.Printf(
			"[%s] %v %v %v \n",
			t.Format("2006-01-02 15:04:05"),
			req.Method,
			req.URL,
			req.Proto,
		)

		if config.Verbose {
			fmt.Println(formatRequest(req))
		}

		service := config.GetService(req.URL.Host)
		if service != nil {
			target, _ := url.Parse(service.Url)
			targetQuery := target.RawQuery

			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
			req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)

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

			if _, ok := req.Header["User-Agent"]; !ok {
				req.Header.Set("User-Agent", "")
			}
		}
	}

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

func ServeProxy(config *Config) error {
	http.HandleFunc("/proxy.pac", func(w http.ResponseWriter, r *http.Request) {
		content := `
		function FindProxyForURL (url, host) {
			if (dnsDomainIs(host, '` + config.Domain + `')) {
				return 'PROXY 127.0.0.1:` + config.Port + `';
			}

			return 'DIRECT';
		}
		`
		w.Header().Set("Content-Type", "application/x-ns-proxy-autoconfig")
		w.Write([]byte(content))
	})

	http.Handle("/", NewErgoProxy(config))

	return http.ListenAndServe(":"+config.Port, nil)
}
<commit_msg>feature: Add endpoint for listing services<commit_after>package proxy

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
)

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

func formatRequest(r *http.Request) string {
	var request []string
	request = append(request, fmt.Sprintf("Host: %v", r.Host))

	for name, headers := range r.Header {
		for _, h := range headers {
			request = append(request, fmt.Sprintf("%v: %v", name, h))
		}
	}

	return strings.Join(request, "\n")
}

func NewErgoProxy(config *Config) *httputil.ReverseProxy {
	director := func(req *http.Request) {
		t := time.Now()
		fmt.Printf(
			"[%s] %v %v %v \n",
			t.Format("2006-01-02 15:04:05"),
			req.Method,
			req.URL,
			req.Proto,
		)

		if config.Verbose {
			fmt.Println(formatRequest(req))
		}

		service := config.GetService(req.URL.Host)
		if service != nil {
			target, _ := url.Parse(service.Url)
			targetQuery := target.RawQuery

			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
			req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)

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

			if _, ok := req.Header["User-Agent"]; !ok {
				req.Header.Set("User-Agent", "")
			}
		}
	}

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

func ServeProxy(config *Config) error {
	http.HandleFunc("/proxy.pac", func(w http.ResponseWriter, r *http.Request) {
		content := `
		function FindProxyForURL (url, host) {
			if (dnsDomainIs(host, '` + config.Domain + `')) {
				return 'PROXY 127.0.0.1:` + config.Port + `';
			}

			return 'DIRECT';
		}
		`
		w.Header().Set("Content-Type", "application/x-ns-proxy-autoconfig")
		w.Write([]byte(content))
	})

	http.HandleFunc("/_ergo/list", func(w http.ResponseWriter, r *http.Request) {
		for _, s := range config.Services {
			localUrl := `http://` + s.Name + config.Domain
			fmt.Fprintf(w, "- %s -> %s \n", localUrl, s.Url)
		}
	})

	http.Handle("/", NewErgoProxy(config))

	return http.ListenAndServe(":"+config.Port, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/jcgay/chtignore/Godeps/_workspace/src/github.com/codegangsta/cli"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"unicode"
)

var VERSION = "unknown-snapshot"

var logger = log.New(os.Stderr, "", 0)

func main() {
	app(os.Args, os.Stdout)
}

func app(args []string, output io.Writer) {
	app := cli.NewApp()
	app.Name = "chtignore"
	app.Usage = "print .gitignore templates in standard output"
	app.ArgsUsage = "template"
	app.Action = printTemplates
	app.Version = VERSION
	app.Commands = []cli.Command{
		{
			Name:   "list",
			Usage:  "list available templates",
			Action: list,
		},
	}
	app.Writer = output
	app.Run(args)
}

func printTemplates(c *cli.Context) {
	args := c.Args()
	output := c.App.Writer

	length := len(args)
	if length == 0 {
		cli.ShowAppHelp(c)
	}

	contents := make(chan string, length)
	for _, candidate := range args {
		go func(candidate string) {
			candidate = upperFirstChar(candidate)
			content := tryGetTemplate(candidate)

			if content != "" {
				contents <- fmt.Sprintf("# %s\n%s", candidate, content)
			} else {
				contents <- ""
			}
		}(candidate)
	}

	for i := 0; i < length; i++ {
		if content := <-contents; content != "" {
			fmt.Fprintln(output, content)
		}
	}
}

func list(c *cli.Context) {
	fmt.Fprintln(c.App.Writer, listTemplates())
}

func tryGetTemplate(template string) string {
	resp := get(fmt.Sprintf("https://raw.githubusercontent.com/github/gitignore/master/%s.gitignore", template))
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNotFound {
		resp = get(fmt.Sprintf("https://raw.githubusercontent.com/github/gitignore/master/Global/%s.gitignore", template))
	}

	if resp.StatusCode != http.StatusOK {
		logger.Fatal("Cannot find a template for: ", template)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Fatal(err)
	}

	return string(body)
}

func get(url string) (resp *http.Response) {
	resp, err := http.Get(url)
	if err != nil {
		logger.Fatal(err)
	}

	return resp
}

func upperFirstChar(str string) string {
	a := []rune(str)
	firstChar := a[0]

	if unicode.IsUpper(firstChar) {
		return str
	}
	a[0] = unicode.ToUpper(firstChar)
	return string(a)
}

func listTemplates() string {
	templates := make([]string, 0)
	templates = getAndAppend(templates, "https://api.github.com/repos/github/gitignore/contents/")
	templates = getAndAppend(templates, "https://api.github.com/repos/github/gitignore/contents/Global")
	sort.Strings(templates)
	return strings.Join(templates, ", ")
}

func getAndAppend(templates []string, url string) []string {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		logger.Fatal(err)
	}
	req.Header.Add("Accept", "application/vnd.github.v3+json")

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

	if resp.StatusCode != http.StatusOK {
		logger.Fatal("Cannot list templates: %s", resp.StatusCode)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Fatal(err)
	}

	result := make([]GitIgnoreTemplate, 0)
	if err := json.Unmarshal(body, &result); err != nil {
		logger.Fatal(err)
	}

	for _, template := range result {
		if template.Name != "" && strings.Contains(template.Name, ".gitignore") {
			templates = append(templates, strings.Replace(template.Name, ".gitignore", "", 1))
		}
	}

	return templates
}

type GitIgnoreTemplate struct {
	Name string `json:"name"`
}
<commit_msg>Fix misconstructed logger.Fatal call<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/jcgay/chtignore/Godeps/_workspace/src/github.com/codegangsta/cli"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"unicode"
)

var VERSION = "unknown-snapshot"

var logger = log.New(os.Stderr, "", 0)

func main() {
	app(os.Args, os.Stdout)
}

func app(args []string, output io.Writer) {
	app := cli.NewApp()
	app.Name = "chtignore"
	app.Usage = "print .gitignore templates in standard output"
	app.ArgsUsage = "template"
	app.Action = printTemplates
	app.Version = VERSION
	app.Commands = []cli.Command{
		{
			Name:   "list",
			Usage:  "list available templates",
			Action: list,
		},
	}
	app.Writer = output
	app.Run(args)
}

func printTemplates(c *cli.Context) {
	args := c.Args()
	output := c.App.Writer

	length := len(args)
	if length == 0 {
		cli.ShowAppHelp(c)
	}

	contents := make(chan string, length)
	for _, candidate := range args {
		go func(candidate string) {
			candidate = upperFirstChar(candidate)
			content := tryGetTemplate(candidate)

			if content != "" {
				contents <- fmt.Sprintf("# %s\n%s", candidate, content)
			} else {
				contents <- ""
			}
		}(candidate)
	}

	for i := 0; i < length; i++ {
		if content := <-contents; content != "" {
			fmt.Fprintln(output, content)
		}
	}
}

func list(c *cli.Context) {
	fmt.Fprintln(c.App.Writer, listTemplates())
}

func tryGetTemplate(template string) string {
	resp := get(fmt.Sprintf("https://raw.githubusercontent.com/github/gitignore/master/%s.gitignore", template))
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNotFound {
		resp = get(fmt.Sprintf("https://raw.githubusercontent.com/github/gitignore/master/Global/%s.gitignore", template))
	}

	if resp.StatusCode != http.StatusOK {
		logger.Fatal("Cannot find a template for: ", template)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Fatal(err)
	}

	return string(body)
}

func get(url string) (resp *http.Response) {
	resp, err := http.Get(url)
	if err != nil {
		logger.Fatal(err)
	}

	return resp
}

func upperFirstChar(str string) string {
	a := []rune(str)
	firstChar := a[0]

	if unicode.IsUpper(firstChar) {
		return str
	}
	a[0] = unicode.ToUpper(firstChar)
	return string(a)
}

func listTemplates() string {
	templates := make([]string, 0)
	templates = getAndAppend(templates, "https://api.github.com/repos/github/gitignore/contents/")
	templates = getAndAppend(templates, "https://api.github.com/repos/github/gitignore/contents/Global")
	sort.Strings(templates)
	return strings.Join(templates, ", ")
}

func getAndAppend(templates []string, url string) []string {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		logger.Fatal(err)
	}
	req.Header.Add("Accept", "application/vnd.github.v3+json")

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

	if resp.StatusCode != http.StatusOK {
		logger.Fatal("Cannot list templates: ", resp.StatusCode)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Fatal(err)
	}

	result := make([]GitIgnoreTemplate, 0)
	if err := json.Unmarshal(body, &result); err != nil {
		logger.Fatal(err)
	}

	for _, template := range result {
		if template.Name != "" && strings.Contains(template.Name, ".gitignore") {
			templates = append(templates, strings.Replace(template.Name, ".gitignore", "", 1))
		}
	}

	return templates
}

type GitIgnoreTemplate struct {
	Name string `json:"name"`
}
<|endoftext|>


<commit_before>package sitemap

import (
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
	"time"
)

// Index is a structure of <sitemapindex>
type Index struct {
	XMLName xml.Name `xml:"sitemapindex"`
	Sitemap []Parts  `xml:"sitemap"`
}

// Parts is a structure of <sitemap> in <sitemapindex>
type Parts struct {
	Loc     string `xml:"loc"`
	LastMod string `xml:"lastmod"`
}

// Sitemap is a structure of <sitemap>
type Sitemap struct {
	XMLName xml.Name `xml:"urlset"`
	URL     []URL    `xml:"url"`
}

// URL is a structure of <url> in <sitemap>
type URL struct {
	Loc        string  `xml:"loc"`
	LastMod    string  `xml:"lastmod"`
	ChangeFreq string  `xml:"changefreq"`
	Priority   float32 `xml:"priority"`
}

var fetch = func(url string) ([]byte, error) {
	var body []byte

	res, err := http.Get(url)
	if err != nil {
		return body, err
	}
	defer res.Body.Close()

	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return body, err
	}

	return body, err
}

// Get sitemap data from URL
func GetSitemap(url string) (Sitemap, error) {
	var index Index
	var sitemap Sitemap

	data, err := fetch(url)
	if err != nil {
		return sitemap, err
	}

	indexErr := xml.Unmarshal(data, &index)
	sitemapErr := xml.Unmarshal(data, &sitemap)

	if indexErr != nil && sitemapErr != nil {
		err = errors.New("URL is not a sitemap or sitemapindex")
		return Sitemap{}, err
	}

	if indexErr == nil {
		sitemap, err = index.get(data)
		if err != nil {
			return sitemap, err
		}
	}

	return sitemap, err
}

// Get Sitemap data from sitemapindex file
func (s *Index) get(data []byte) (Sitemap, error) {
	var index Index
	var sitemap Sitemap

	err := xml.Unmarshal(data, &index)
	if err != nil {
		return Sitemap{}, err
	}

	for _, s := range index.Sitemap {
		time.Sleep(time.Second) // TODO: sleep time will be option.
		data, err := fetch(s.Loc)
		if err != nil {
			return sitemap, err
		}
		xml.Unmarshal(data, &sitemap)
	}

	return sitemap, err
}
<commit_msg>parts structure is not export<commit_after>package sitemap

import (
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
	"time"
)

// Index is a structure of <sitemapindex>
type Index struct {
	XMLName xml.Name `xml:"sitemapindex"`
	Sitemap []parts  `xml:"sitemap"`
}

// Parts is a structure of <sitemap> in <sitemapindex>
type parts struct {
	Loc     string `xml:"loc"`
	LastMod string `xml:"lastmod"`
}

// Sitemap is a structure of <sitemap>
type Sitemap struct {
	XMLName xml.Name `xml:"urlset"`
	URL     []URL    `xml:"url"`
}

// URL is a structure of <url> in <sitemap>
type URL struct {
	Loc        string  `xml:"loc"`
	LastMod    string  `xml:"lastmod"`
	ChangeFreq string  `xml:"changefreq"`
	Priority   float32 `xml:"priority"`
}

var fetch = func(url string) ([]byte, error) {
	var body []byte

	res, err := http.Get(url)
	if err != nil {
		return body, err
	}
	defer res.Body.Close()

	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return body, err
	}

	return body, err
}

// Get sitemap data from URL
func GetSitemap(url string) (Sitemap, error) {
	var index Index
	var sitemap Sitemap

	data, err := fetch(url)
	if err != nil {
		return sitemap, err
	}

	indexErr := xml.Unmarshal(data, &index)
	sitemapErr := xml.Unmarshal(data, &sitemap)

	if indexErr != nil && sitemapErr != nil {
		err = errors.New("URL is not a sitemap or sitemapindex")
		return Sitemap{}, err
	}

	if indexErr == nil {
		sitemap, err = index.get(data)
		if err != nil {
			return sitemap, err
		}
	}

	return sitemap, err
}

// Get Sitemap data from sitemapindex file
func (s *Index) get(data []byte) (Sitemap, error) {
	var index Index
	var sitemap Sitemap

	err := xml.Unmarshal(data, &index)
	if err != nil {
		return Sitemap{}, err
	}

	for _, s := range index.Sitemap {
		time.Sleep(time.Second) // TODO: sleep time will be option.
		data, err := fetch(s.Loc)
		if err != nil {
			return sitemap, err
		}
		xml.Unmarshal(data, &sitemap)
	}

	return sitemap, err
}
<|endoftext|>


<commit_before>package special

import (
	"testing"

	"github.com/ready-steady/assert"
	"github.com/ready-steady/probability"
	"github.com/ready-steady/probability/generator"
	"github.com/ready-steady/probability/uniform"
)

func TestIncBeta(t *testing.T) {
	p, q := 2.0, 3.0
	logBeta := LogBeta(p, q)

	points := []float64{
		0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
		0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00,
	}

	values := []float64{
		0.000000000000000e+00,
		1.401875000000000e-02,
		5.230000000000002e-02,
		1.095187500000000e-01,
		1.807999999999999e-01,
		2.617187500000001e-01,
		3.483000000000000e-01,
		4.370187500000001e-01,
		5.248000000000003e-01,
		6.090187500000001e-01,
		6.875000000000000e-01,
		7.585187500000001e-01,
		8.208000000000000e-01,
		8.735187499999999e-01,
		9.163000000000000e-01,
		9.492187500000000e-01,
		9.728000000000000e-01,
		9.880187500000001e-01,
		9.963000000000000e-01,
		9.995187500000000e-01,
		1.000000000000000e+00,
	}

	actual := make([]float64, len(points))

	for i := range points {
		actual[i] = IncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 1e-15, t)

	p, q = 0.1, 0.2
	logBeta = LogBeta(p, q)

	values = []float64{
		0.000000000000000e+00,
		5.095391215346399e-01,
		5.482400859052436e-01,
		5.732625733722232e-01,
		5.925346573554778e-01,
		6.086596697678208e-01,
		6.228433547203172e-01,
		6.357578563479236e-01,
		6.478288604374864e-01,
		6.593557133297501e-01,
		6.705707961028990e-01,
		6.816739425887479e-01,
		6.928567823206671e-01,
		7.043251807250750e-01,
		7.163269829958610e-01,
		7.291961263917867e-01,
		7.434379555965913e-01,
		7.599272566076309e-01,
		7.804880320024465e-01,
		8.104335200313719e-01,
		1.000000000000000e+00,
	}

	for i := range points {
		actual[i] = IncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 2e-15, t)
}

func TestInvIncBeta(t *testing.T) {
	p, q := 1.0, 2.0
	logBeta := LogBeta(p, q)

	points := []float64{
		0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
		0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00,
	}

	values := []float64{
		0.000000000000000e+00,
		0.025320565519104e+00,
		0.051316701949486e+00,
		0.078045554270711e+00,
		0.105572809000084e+00,
		0.133974596215561e+00,
		0.163339973465924e+00,
		0.193774225170145e+00,
		0.225403330758517e+00,
		0.258380151290432e+00,
		0.292893218813452e+00,
		0.329179606750063e+00,
		0.367544467966324e+00,
		0.408392021690038e+00,
		0.452277442494834e+00,
		0.500000000000000e+00,
		0.552786404500042e+00,
		0.612701665379257e+00,
		0.683772233983162e+00,
		0.776393202250021e+00,
		1.000000000000000e+00,
	}

	actual := make([]float64, len(points))

	for i := range points {
		actual[i] = InvIncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 2e-15, t)

	p, q = 0.2, 0.3
	logBeta = LogBeta(p, q)

	values = []float64{
		0.000000000000000e+00,
		2.793072851850660e-06,
		8.937381711316164e-05,
		6.784491773826951e-04,
		2.855345858289119e-03,
		8.684107512129325e-03,
		2.144658503798324e-02,
		4.568556852983932e-02,
		8.683942933344659e-02,
		1.502095712585510e-01,
		2.391350361479824e-01,
		3.527066234122371e-01,
		4.840600731467657e-01,
		6.206841200371190e-01,
		7.474718280552188e-01,
		8.514539745840592e-01,
		9.257428898178934e-01,
		9.707021084050310e-01,
		9.923134416335146e-01,
		9.992341305241808e-01,
		1.000000000000000e+00,
	}

	for i := range points {
		actual[i] = InvIncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 1e-14, t)
}

func BenchmarkIncBeta(b *testing.B) {
	p, q := 0.5, 1.5
	logBeta := LogBeta(p, q)
	points := probability.Sample(uniform.New(0, 1), generator.New(0), 1000)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, x := range points {
			IncBeta(x, p, q, logBeta)
		}
	}
}

func BenchmarkInvIncBeta(b *testing.B) {
	p, q := 0.5, 1.5
	logBeta := LogBeta(p, q)
	points := probability.Sample(uniform.New(0, 1), generator.New(0), 1000)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, α := range points {
			InvIncBeta(α, p, q, logBeta)
		}
	}
}
<commit_msg>Do not depend on probability<commit_after>package special

import (
	"math/rand"
	"testing"

	"github.com/ready-steady/assert"
)

func TestIncBeta(t *testing.T) {
	p, q := 2.0, 3.0
	logBeta := LogBeta(p, q)

	points := []float64{
		0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
		0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00,
	}

	values := []float64{
		0.000000000000000e+00,
		1.401875000000000e-02,
		5.230000000000002e-02,
		1.095187500000000e-01,
		1.807999999999999e-01,
		2.617187500000001e-01,
		3.483000000000000e-01,
		4.370187500000001e-01,
		5.248000000000003e-01,
		6.090187500000001e-01,
		6.875000000000000e-01,
		7.585187500000001e-01,
		8.208000000000000e-01,
		8.735187499999999e-01,
		9.163000000000000e-01,
		9.492187500000000e-01,
		9.728000000000000e-01,
		9.880187500000001e-01,
		9.963000000000000e-01,
		9.995187500000000e-01,
		1.000000000000000e+00,
	}

	actual := make([]float64, len(points))

	for i := range points {
		actual[i] = IncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 1e-15, t)

	p, q = 0.1, 0.2
	logBeta = LogBeta(p, q)

	values = []float64{
		0.000000000000000e+00,
		5.095391215346399e-01,
		5.482400859052436e-01,
		5.732625733722232e-01,
		5.925346573554778e-01,
		6.086596697678208e-01,
		6.228433547203172e-01,
		6.357578563479236e-01,
		6.478288604374864e-01,
		6.593557133297501e-01,
		6.705707961028990e-01,
		6.816739425887479e-01,
		6.928567823206671e-01,
		7.043251807250750e-01,
		7.163269829958610e-01,
		7.291961263917867e-01,
		7.434379555965913e-01,
		7.599272566076309e-01,
		7.804880320024465e-01,
		8.104335200313719e-01,
		1.000000000000000e+00,
	}

	for i := range points {
		actual[i] = IncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 2e-15, t)
}

func TestInvIncBeta(t *testing.T) {
	p, q := 1.0, 2.0
	logBeta := LogBeta(p, q)

	points := []float64{
		0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
		0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00,
	}

	values := []float64{
		0.000000000000000e+00,
		0.025320565519104e+00,
		0.051316701949486e+00,
		0.078045554270711e+00,
		0.105572809000084e+00,
		0.133974596215561e+00,
		0.163339973465924e+00,
		0.193774225170145e+00,
		0.225403330758517e+00,
		0.258380151290432e+00,
		0.292893218813452e+00,
		0.329179606750063e+00,
		0.367544467966324e+00,
		0.408392021690038e+00,
		0.452277442494834e+00,
		0.500000000000000e+00,
		0.552786404500042e+00,
		0.612701665379257e+00,
		0.683772233983162e+00,
		0.776393202250021e+00,
		1.000000000000000e+00,
	}

	actual := make([]float64, len(points))

	for i := range points {
		actual[i] = InvIncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 2e-15, t)

	p, q = 0.2, 0.3
	logBeta = LogBeta(p, q)

	values = []float64{
		0.000000000000000e+00,
		2.793072851850660e-06,
		8.937381711316164e-05,
		6.784491773826951e-04,
		2.855345858289119e-03,
		8.684107512129325e-03,
		2.144658503798324e-02,
		4.568556852983932e-02,
		8.683942933344659e-02,
		1.502095712585510e-01,
		2.391350361479824e-01,
		3.527066234122371e-01,
		4.840600731467657e-01,
		6.206841200371190e-01,
		7.474718280552188e-01,
		8.514539745840592e-01,
		9.257428898178934e-01,
		9.707021084050310e-01,
		9.923134416335146e-01,
		9.992341305241808e-01,
		1.000000000000000e+00,
	}

	for i := range points {
		actual[i] = InvIncBeta(points[i], p, q, logBeta)
	}
	assert.EqualWithin(actual, values, 1e-14, t)
}

func BenchmarkIncBeta(b *testing.B) {
	p, q := 0.5, 1.5
	logBeta := LogBeta(p, q)
	points := generate(1000)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, x := range points {
			IncBeta(x, p, q, logBeta)
		}
	}
}

func BenchmarkInvIncBeta(b *testing.B) {
	p, q := 0.5, 1.5
	logBeta := LogBeta(p, q)
	points := generate(1000)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, α := range points {
			InvIncBeta(α, p, q, logBeta)
		}
	}
}

func generate(count uint) []float64 {
	rand.Seed(0)

	points := make([]float64, count)
	for i := range points {
		points[i] = rand.Float64()
	}

	return points
}
<|endoftext|>


<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Package opt provides sets of options used by LevelDB.
package opt

import (
	"sync"

	"leveldb/cache"
	"leveldb/comparer"
	"leveldb/filter"
)

type OptionsFlag uint

const (
	// If set, the database will be created if it is missing.
	OFCreateIfMissing OptionsFlag = 1 << iota

	// If set, an error is raised if the database already exists.
	OFErrorIfExist

	// If set, the implementation will do aggressive checking of the
	// data it is processing and will stop early if it detects any
	// errors.  This may have unforeseen ramifications: for example, a
	// corruption of one DB entry may cause a large number of entries to
	// become unreadable or for the entire DB to become unopenable.
	OFParanoidCheck
)

// Database compression type
type Compression uint

func (c Compression) String() string {
	switch c {
	case DefaultCompression:
		return "default"
	case NoCompression:
		return "none"
	case SnappyCompression:
		return "snappy"
	}
	return "unknown"
}

const (
	DefaultCompression Compression = iota
	NoCompression
	SnappyCompression
	nCompression
)

// Options represent sets of LevelDB options.
type Options struct {
	// Comparer used to define the order of keys in the table.
	// Default: a comparer that uses lexicographic byte-wise ordering
	//
	// REQUIRES: The client must ensure that the comparer supplied
	// here has the same name and orders keys *exactly* the same as the
	// comparer provided to previous open calls on the same DB.
	Comparer comparer.Comparer

	// Specify the database flag.
	Flag OptionsFlag

	// Amount of data to build up in memory (backed by an unsorted journal
	// on disk) before converting to a sorted on-disk file.
	//
	// Larger values increase performance, especially during bulk loads.
	// Up to two write buffers may be held in memory at the same time,
	// so you may wish to adjust this parameter to control memory usage.
	// Also, a larger write buffer will result in a longer recovery time
	// the next time the database is opened.
	//
	// Default: 4MB
	WriteBuffer int

	// Number of open files that can be used by the DB.  You may need to
	// increase this if your database has a large working set (budget
	// one open file per 2MB of working set).
	//
	// Default: 1000
	MaxOpenFiles int

	// Control over blocks (user data is stored in a set of blocks, and
	// a block is the unit of reading from disk).

	// If non-NULL, use the specified cache for blocks.
	// If NULL, leveldb will automatically create and use an 8MB internal cache.
	// Default: NULL
	BlockCache cache.Cache

	// Approximate size of user data packed per block.  Note that the
	// block size specified here corresponds to uncompressed data.  The
	// actual size of the unit read from disk may be smaller if
	// compression is enabled.  This parameter can be changed dynamically.
	//
	// Default: 4K
	BlockSize int

	// Number of keys between restart points for delta encoding of keys.
	// This parameter can be changed dynamically.  Most clients should
	// leave this parameter alone.
	//
	// Default: 16
	BlockRestartInterval int

	// Compress blocks using the specified compression algorithm.  This
	// parameter can be changed dynamically.
	//
	// Default: kSnappyCompression, which gives lightweight but fast
	// compression.
	//
	// Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz:
	//    ~200-500MB/s compression
	//    ~400-800MB/s decompression
	// Note that these speeds are significantly faster than most
	// persistent storage speeds, and therefore it is typically never
	// worth switching to kNoCompression.  Even if the input data is
	// incompressible, the kSnappyCompression implementation will
	// efficiently detect that and will switch to uncompressed mode.
	CompressionType Compression

	// If non-NULL, use the specified filter policy to reduce disk reads.
	// Many applications will benefit from passing the result of
	// NewBloomFilter() here.
	//
	// Default: NULL
	Filter filter.Filter

	mu sync.Mutex
}

// OptionsGetter wraps methods used to get sanitized options.
type OptionsGetter interface {
	GetComparer() comparer.Comparer
	HasFlag(flag OptionsFlag) bool
	GetWriteBuffer() int
	GetMaxOpenFiles() int
	GetBlockCache() cache.Cache
	GetBlockSize() int
	GetBlockRestartInterval() int
	GetCompressionType() Compression
	GetFilter() filter.Filter
}

func (o *Options) GetComparer() comparer.Comparer {
	if o == nil || o.Comparer == nil {
		return comparer.DefaultComparer
	}
	return o.Comparer
}

func (o *Options) HasFlag(flag OptionsFlag) bool {
	if o == nil {
		return false
	}
	return (o.Flag & flag) != 0
}

func (o *Options) GetWriteBuffer() int {
	if o == nil || o.WriteBuffer <= 0 {
		return 4 << 20
	}
	return o.WriteBuffer
}

func (o *Options) GetMaxOpenFiles() int {
	if o == nil || o.MaxOpenFiles <= 0 {
		return 1000
	}
	return o.MaxOpenFiles
}

func (o *Options) GetBlockCache() cache.Cache {
	o.mu.Lock()
	defer o.mu.Unlock()
	if o == nil {
		return nil
	}
	if o.BlockCache == nil {
		o.BlockCache = cache.NewLRUCache(8 << 20)
	}
	return o.BlockCache
}

func (o *Options) GetBlockSize() int {
	if o == nil || o.BlockSize <= 0 {
		return 4096
	}
	return o.BlockSize
}

func (o *Options) GetBlockRestartInterval() int {
	if o == nil || o.BlockRestartInterval <= 0 {
		return 16
	}
	return o.BlockRestartInterval
}

func (o *Options) GetCompressionType() Compression {
	if o == nil || o.CompressionType <= DefaultCompression || o.CompressionType >= nCompression {
		return SnappyCompression
	}
	return o.CompressionType
}

func (o *Options) GetFilter() filter.Filter {
	if o == nil {
		return nil
	}
	return o.Filter
}

type ReadOptionsFlag uint

const (
	// If true, all data read from underlying storage will be
	// verified against corresponding checksums.
	RFVerifyChecksums ReadOptionsFlag = 1 << iota

	// Should the data read for this iteration be cached in memory?
	// If set iteration chaching will be disabled.
	// Callers may wish to set this flag for bulk scans.
	RFDontFillCache
)

// ReadOptions represent sets of options used by LevelDB during read
// operations.
type ReadOptions struct {
	// Specify the read flag.
	Flag ReadOptionsFlag
}

type ReadOptionsGetter interface {
	HasFlag(flag ReadOptionsFlag) bool
}

func (o *ReadOptions) HasFlag(flag ReadOptionsFlag) bool {
	if o == nil {
		return false
	}
	return (o.Flag & flag) != 0
}

type WriteOptionsFlag uint

const (
	// If set, the write will be flushed from the operating system
	// buffer cache (by calling WritableFile::Sync()) before the write
	// is considered complete.  If this flag is true, writes will be
	// slower.
	//
	// If this flag is false, and the machine crashes, some recent
	// writes may be lost.  Note that if it is just the process that
	// crashes (i.e., the machine does not reboot), no writes will be
	// lost even if sync==false.
	//
	// In other words, a DB write with sync==false has similar
	// crash semantics as the "write()" system call.  A DB write
	// with sync==true has similar crash semantics to a "write()"
	// system call followed by "fsync()".
	WFSync WriteOptionsFlag = 1 << iota
)

// WriteOptions represent sets of options used by LevelDB during write
// operations.
type WriteOptions struct {
	// Specify the write flag.
	Flag WriteOptionsFlag
}

type WriteOptionsGetter interface {
	HasFlag(flag WriteOptionsFlag) bool
}

func (o *WriteOptions) HasFlag(flag WriteOptionsFlag) bool {
	if o == nil {
		return false
	}
	return (o.Flag & flag) != 0
}
<commit_msg>opt: introduce OptionsSetter<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Package opt provides sets of options used by LevelDB.
package opt

import (
	"errors"
	"sync"

	"leveldb/cache"
	"leveldb/comparer"
	"leveldb/filter"
)

var (
	ErrInvalid    = errors.New("invalid value")
	ErrNotSet     = errors.New("not set")
	ErrNotAllowed = errors.New("not allowed")
)

const (
	DefaultWriteBuffer          = 4 << 20
	DefaultMaxOpenFiles         = 1000
	DefaultBlockCacheSize       = 8 << 20
	DefaultBlockSize            = 4096
	DefaultBlockRestartInterval = 16
	DefaultCompressionType      = SnappyCompression
)

type OptionsFlag uint

const (
	// If set, the database will be created if it is missing.
	OFCreateIfMissing OptionsFlag = 1 << iota

	// If set, an error is raised if the database already exists.
	OFErrorIfExist

	// If set, the implementation will do aggressive checking of the
	// data it is processing and will stop early if it detects any
	// errors.  This may have unforeseen ramifications: for example, a
	// corruption of one DB entry may cause a large number of entries to
	// become unreadable or for the entire DB to become unopenable.
	OFParanoidCheck
)

// Database compression type
type Compression uint

func (c Compression) String() string {
	switch c {
	case DefaultCompression:
		return "default"
	case NoCompression:
		return "none"
	case SnappyCompression:
		return "snappy"
	}
	return "unknown"
}

const (
	DefaultCompression Compression = iota
	NoCompression
	SnappyCompression
	nCompression
)

// Options represent sets of LevelDB options.
type Options struct {
	// Comparer used to define the order of keys in the table.
	// Default: a comparer that uses lexicographic byte-wise ordering
	//
	// REQUIRES: The client must ensure that the comparer supplied
	// here has the same name and orders keys *exactly* the same as the
	// comparer provided to previous open calls on the same DB.
	Comparer comparer.Comparer

	// Specify the database flag.
	Flag OptionsFlag

	// Amount of data to build up in memory (backed by an unsorted journal
	// on disk) before converting to a sorted on-disk file.
	//
	// Larger values increase performance, especially during bulk loads.
	// Up to two write buffers may be held in memory at the same time,
	// so you may wish to adjust this parameter to control memory usage.
	// Also, a larger write buffer will result in a longer recovery time
	// the next time the database is opened.
	//
	// Default: 4MB
	WriteBuffer int

	// Number of open files that can be used by the DB.  You may need to
	// increase this if your database has a large working set (budget
	// one open file per 2MB of working set).
	//
	// Default: 1000
	MaxOpenFiles int

	// Control over blocks (user data is stored in a set of blocks, and
	// a block is the unit of reading from disk).

	// If non-NULL, use the specified cache for blocks.
	// If NULL, leveldb will automatically create and use an 8MB internal cache.
	// Default: NULL
	BlockCache cache.Cache

	// Approximate size of user data packed per block.  Note that the
	// block size specified here corresponds to uncompressed data.  The
	// actual size of the unit read from disk may be smaller if
	// compression is enabled.  This parameter can be changed dynamically.
	//
	// Default: 4K
	BlockSize int

	// Number of keys between restart points for delta encoding of keys.
	// This parameter can be changed dynamically.  Most clients should
	// leave this parameter alone.
	//
	// Default: 16
	BlockRestartInterval int

	// Compress blocks using the specified compression algorithm.  This
	// parameter can be changed dynamically.
	//
	// Default: kSnappyCompression, which gives lightweight but fast
	// compression.
	//
	// Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz:
	//    ~200-500MB/s compression
	//    ~400-800MB/s decompression
	// Note that these speeds are significantly faster than most
	// persistent storage speeds, and therefore it is typically never
	// worth switching to kNoCompression.  Even if the input data is
	// incompressible, the kSnappyCompression implementation will
	// efficiently detect that and will switch to uncompressed mode.
	CompressionType Compression

	// If non-NULL, use the specified filter policy to reduce disk reads.
	// Many applications will benefit from passing the result of
	// NewBloomFilter() here.
	//
	// Default: NULL
	Filter filter.Filter

	mu sync.RWMutex
}

// OptionsGetter wraps methods used to get sanitized options.
type OptionsGetter interface {
	GetComparer() comparer.Comparer
	HasFlag(flag OptionsFlag) bool
	GetWriteBuffer() int
	GetMaxOpenFiles() int
	GetBlockCache() cache.Cache
	GetBlockSize() int
	GetBlockRestartInterval() int
	GetCompressionType() Compression
	GetFilter() filter.Filter
}

// OptionsSetter wraps methods used to set options.
type OptionsSetter interface {
	SetComparer(cmp comparer.Comparer) error
	SetFlag(flag OptionsFlag) error
	ClearFlag(flag OptionsFlag) error
	SetWriteBuffer(size int) error
	SetMaxOpenFiles(max int) error
	SetBlockCache(cache cache.Cache) error
	SetBlockCacheCapacity(capacity int) error
	SetBlockSize(size int) error
	SetBlockRestartInterval(interval int) error
	SetCompressionType(compression Compression) error
	SetFilter(p filter.Filter) error
}

// Getter

func (o *Options) GetComparer() comparer.Comparer {
	if o == nil {
		return comparer.DefaultComparer
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	if o.Comparer == nil {
		return comparer.DefaultComparer
	}
	return o.Comparer
}

func (o *Options) HasFlag(flag OptionsFlag) bool {
	if o == nil {
		return false
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	return (o.Flag & flag) != 0
}

func (o *Options) GetWriteBuffer() int {
	if o == nil {
		return DefaultWriteBuffer
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	if o.WriteBuffer <= 0 {
		return DefaultWriteBuffer
	}
	return o.WriteBuffer
}

func (o *Options) GetMaxOpenFiles() int {
	if o == nil {
		return DefaultMaxOpenFiles
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	if o.MaxOpenFiles <= 0 {
		return DefaultMaxOpenFiles
	}
	return o.MaxOpenFiles
}

func (o *Options) GetBlockCache() cache.Cache {
	if o == nil {
		return nil
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	return o.BlockCache
}

func (o *Options) GetBlockSize() int {
	if o == nil {
		return DefaultBlockSize
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	if o.BlockSize <= 0 {
		return DefaultBlockSize
	}
	return o.BlockSize
}

func (o *Options) GetBlockRestartInterval() int {
	if o == nil {
		return DefaultBlockRestartInterval
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	if o.BlockRestartInterval <= 0 {
		return DefaultBlockRestartInterval
	}
	return o.BlockRestartInterval
}

func (o *Options) GetCompressionType() Compression {
	if o == nil {
		return DefaultCompressionType
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	if o.CompressionType <= DefaultCompression || o.CompressionType >= nCompression {
		return DefaultCompressionType
	}
	return o.CompressionType
}

func (o *Options) GetFilter() filter.Filter {
	if o == nil {
		return nil
	}
	o.mu.RLock()
	defer o.mu.RUnlock()
	return o.Filter
}

// Setter

func (o *Options) SetComparer(cmp comparer.Comparer) error {
	if o == nil {
		return ErrNotSet
	}
	if cmp == nil {
		return ErrInvalid
	}
	o.mu.Lock()
	o.Comparer = cmp
	o.mu.Unlock()
	return nil
}

func (o *Options) SetFlag(flag OptionsFlag) error {
	if o == nil {
		return ErrNotSet
	}
	o.mu.Lock()
	o.Flag |= flag
	o.mu.Unlock()
	return nil
}

func (o *Options) ClearFlag(flag OptionsFlag) error {
	if o == nil {
		return ErrNotSet
	}
	o.mu.Lock()
	o.Flag &= ^flag
	o.mu.Unlock()
	return nil
}

func (o *Options) SetWriteBuffer(size int) error {
	if o == nil {
		return ErrNotSet
	}
	if size <= 0 {
		return ErrInvalid
	}
	o.mu.Lock()
	o.WriteBuffer = size
	o.mu.Unlock()
	return nil
}

func (o *Options) SetMaxOpenFiles(max int) error {
	if o == nil {
		return ErrNotSet
	}
	if max <= 0 {
		return ErrInvalid
	}
	o.mu.Lock()
	o.MaxOpenFiles = max
	o.mu.Unlock()
	return nil
}

func (o *Options) SetBlockCache(cache cache.Cache) error {
	if o == nil {
		return ErrNotSet
	}
	o.mu.Lock()
	o.BlockCache = cache
	o.mu.Unlock()
	return nil
}

func (o *Options) SetBlockCacheCapacity(capacity int) error {
	if o == nil {
		return ErrNotSet
	}
	o.mu.Lock()
	if o.BlockCache == nil {
		return ErrNotSet
	}
	o.BlockCache.SetCapacity(capacity)
	o.mu.Unlock()
	return nil
}

func (o *Options) SetBlockSize(size int) error {
	if o == nil {
		return ErrNotSet
	}
	if size <= 0 {
		return ErrInvalid
	}
	o.mu.Lock()
	o.BlockSize = size
	o.mu.Unlock()
	return nil
}

func (o *Options) SetBlockRestartInterval(interval int) error {
	if o == nil {
		return ErrNotSet
	}
	if interval <= 0 {
		return ErrInvalid
	}
	o.mu.Lock()
	o.BlockRestartInterval = interval
	o.mu.Unlock()
	return nil
}

func (o *Options) SetCompressionType(compression Compression) error {
	if o == nil {
		return ErrNotSet
	}
	if o.CompressionType >= nCompression {
		return ErrInvalid
	}
	o.mu.Lock()
	o.CompressionType = compression
	o.mu.Unlock()
	return nil
}

func (o *Options) SetFilter(p filter.Filter) error {
	if o == nil {
		return ErrNotSet
	}
	o.mu.Lock()
	o.Filter = p
	o.mu.Unlock()
	return nil
}

type ReadOptionsFlag uint

const (
	// If true, all data read from underlying storage will be
	// verified against corresponding checksums.
	RFVerifyChecksums ReadOptionsFlag = 1 << iota

	// Should the data read for this iteration be cached in memory?
	// If set iteration chaching will be disabled.
	// Callers may wish to set this flag for bulk scans.
	RFDontFillCache
)

// ReadOptions represent sets of options used by LevelDB during read
// operations.
type ReadOptions struct {
	// Specify the read flag.
	Flag ReadOptionsFlag
}

type ReadOptionsGetter interface {
	HasFlag(flag ReadOptionsFlag) bool
}

func (o *ReadOptions) HasFlag(flag ReadOptionsFlag) bool {
	if o == nil {
		return false
	}
	return (o.Flag & flag) != 0
}

type WriteOptionsFlag uint

const (
	// If set, the write will be flushed from the operating system
	// buffer cache (by calling WritableFile::Sync()) before the write
	// is considered complete.  If this flag is true, writes will be
	// slower.
	//
	// If this flag is false, and the machine crashes, some recent
	// writes may be lost.  Note that if it is just the process that
	// crashes (i.e., the machine does not reboot), no writes will be
	// lost even if sync==false.
	//
	// In other words, a DB write with sync==false has similar
	// crash semantics as the "write()" system call.  A DB write
	// with sync==true has similar crash semantics to a "write()"
	// system call followed by "fsync()".
	WFSync WriteOptionsFlag = 1 << iota
)

// WriteOptions represent sets of options used by LevelDB during write
// operations.
type WriteOptions struct {
	// Specify the write flag.
	Flag WriteOptionsFlag
}

type WriteOptionsGetter interface {
	HasFlag(flag WriteOptionsFlag) bool
}

func (o *WriteOptions) HasFlag(flag WriteOptionsFlag) bool {
	if o == nil {
		return false
	}
	return (o.Flag & flag) != 0
}
<|endoftext|>


<commit_before>package qtype

// used in CONNECTION_CLOSE frame
type TransportError uint16

const (
	NoError TransportError = iota
	InternalError
	ServerBusy
	FlowControlError
	StreamIDError
	StreamStateError
	FinalOffsetError
	FrameFormatError
	TransportParameterError
	VersionNegotiationError
	ProtocolViolation
	UnsolicitedPathResponse

	// not defined the value in spec 11
	FlowControlReceivedTooMuchData

	FrameError = 0x100 // 0x1XX, the XX will be frame type
)

func (e TransportError) Error() string {
	if e >= FrameError {
		// TODO: solve import cycle by separating file of FrameType implementation
		return "FrameError: " // + quiclatest.FrameType(uint16(e)&0xff).String()
	}
	return []string{
		"NoError",
		"InternalError",
		"ServerBusy",
		"FlowControlError",
		"StreamIDError",
		"StreamStateError",
		"FinalOffsetError",
		"FrameFormatError",
		"TransportParameterError",
		"VersionNegotiationError",
		"ProtocolViolation",
		"UnsolicitedPathResponse",
	}[e]
}

// only 0 is reserved for STOPPING
type ApplicationError uint16

const (
	Stopping ApplicationError = iota
)

func (e ApplicationError) Error() string {
	return []string{
		"Stopping",
	}[e]
}
<commit_msg>add self defined application error<commit_after>package qtype

import "math"

// used in CONNECTION_CLOSE frame
type TransportError uint16

const (
	NoError TransportError = iota
	InternalError
	ServerBusy
	FlowControlError
	StreamIDError
	StreamStateError
	FinalOffsetError
	FrameFormatError
	TransportParameterError
	VersionNegotiationError
	ProtocolViolation
	UnsolicitedPathResponse

	// not defined the value in spec 11
	FlowControlReceivedTooMuchData

	FrameError = 0x100 // 0x1XX, the XX will be frame type
)

func (e TransportError) Error() string {
	if e >= FrameError {
		// TODO: solve import cycle by separating file of FrameType implementation
		return "FrameError: " // + quiclatest.FrameType(uint16(e)&0xff).String()
	}
	return []string{
		"NoError",
		"InternalError",
		"ServerBusy",
		"FlowControlError",
		"StreamIDError",
		"StreamStateError",
		"FinalOffsetError",
		"FrameFormatError",
		"TransportParameterError",
		"VersionNegotiationError",
		"ProtocolViolation",
		"UnsolicitedPathResponse",
	}[e]
}

// only 0 is reserved for STOPPING
type ApplicationError uint16

const (
	Stopping             ApplicationError = iota
	ApplicationErrorTest ApplicationError = math.MaxUint16
)

func (e ApplicationError) Error() string {
	if e == ApplicationErrorTest {
		return "Application Error Test"
	}
	return []string{
		"Stopping",
	}[e]
}
<|endoftext|>