<commit_before>package fury

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	CONNECT = "CONNECT"
	DELETE  = "DELETE"
	GET     = "GET"
	HEAD    = "HEAD"
	OPTIONS = "OPTIONS"
	PATCH   = "PATCH"
	POST    = "POST"
	PUT     = "PUT"
	TRACE   = "TRACE"
)

const (
	HEADER_CONTENT_TYPE = "Content-Type"
)

const (
	CONTENT_TYPE_JSON = "application/json"
	CONTENT_TYPE_XML  = "application/xml"
)

type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
type CleanupFunc func(fury *Fury)

type Valid interface {
	OK() error
}

func Validate(v interface{}) error {
	obj, ok := v.(Valid)
	if !ok {
		return nil
	}

	if err := obj.OK(); err != nil {
		return err
	}
	return nil
}

func LoadJsonAndValidate(data []byte, v interface{}) error {
	err := json.Unmarshal(data, v)
	if err != nil {
		return err
	}
	return Validate(v)
}

type Renderer interface {
	Render(code int, name string, data interface{})
	Html(code int, html string)
	String(code int, s string)
	Json(code int, data interface{})
	Xml(code int, data interface{})
	SetContentType(name string)
	WriteWithCode(code int, data []byte)
}

type Meta struct {
	writer  http.ResponseWriter
	request *http.Request
	path    string
	query   url.Values
	fury    *Fury
}

func (m *Meta) App() *Fury {
	return m.fury
}

func (m *Meta) RequestHeaders() http.Header {
	return m.request.Header
}

func (m *Meta) Request() *http.Request {
	return m.request
}

func (meta *Meta) SetContentType(name string) {
	meta.writer.Header().Add(HEADER_CONTENT_TYPE, name)
}

func (meta *Meta) WriteWithCode(code int, data []byte) {
	meta.writer.WriteHeader(code)
	meta.writer.Write(data)
}

func (meta *Meta) Render(code int, name string, data interface{}) {
	return
}

func (meta *Meta) Html(code int, html string) {
	return
}

func (meta *Meta) String(code int, s string) {
	meta.WriteWithCode(code, []byte(s))
}

func (meta *Meta) Json(code int, data interface{}) {
	content, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		http.Error(meta.writer, err.Error(), http.StatusInternalServerError)
		return
	}

	meta.SetContentType(CONTENT_TYPE_JSON)
	meta.WriteWithCode(code, content)
}

func (meta *Meta) Xml(code int, data interface{}) {
	content, err := xml.MarshalIndent(data, "", "  ")
	if err != nil {
		http.Error(meta.writer, err.Error(), http.StatusInternalServerError)
		return
	}

	meta.SetContentType(CONTENT_TYPE_XML)
	meta.WriteWithCode(code, content)
}

type ConnectMixin interface {
	Connect(meta *Meta)
}

type DeleteMixin interface {
	Delete(meta *Meta)
}

type GetMixin interface {
	Get(meta *Meta)
}

type HeadMixin interface {
	Head(meta *Meta)
}

type OptionsMixin interface {
	Options(meta *Meta)
}

type PatchMixin interface {
	Patch(meta *Meta)
}

type PostMixin interface {
	Post(meta *Meta)
}

type PutMixin interface {
	Put(meta *Meta)
}

type TraceMixin interface {
	Trace(meta *Meta)
}

type Fury struct {
	port             int
	host             string
	cleanupFunctions []CleanupFunc
	middleware       []MiddlewareFunc
	registry         map[string]interface{}
}

func (fury *Fury) FromRegistry(key string) interface{} {
	return fury.registry[key]
}

func (fury *Fury) ToRegistry(key string, value interface{}) *Fury {
	fury.registry[key] = value
	return fury
}

func (fury *Fury) Route(path string, resource interface{}) *Fury {
	handler := fury.requestHandler(resource)
	for _, middleware := range fury.middleware {
		handler = middleware(handler)
	}
	http.HandleFunc(path, handler)
	return fury
}

func (fury *Fury) RegisterCleanup(fun ...CleanupFunc) *Fury {
	fury.cleanupFunctions = append(fury.cleanupFunctions, fun...)
	return fury
}

func (fury *Fury) UseMiddleware(middleware ...MiddlewareFunc) *Fury {
	fury.middleware = append(fury.middleware, middleware...)
	return fury
}

func (fury *Fury) Start() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	address := fmt.Sprintf("%s:%d", fury.host, fury.port)

	server := &http.Server{
		Addr:           address,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	listener, err := net.Listen("tcp", server.Addr)
	if err != nil {
		Logger().Error(err)
		os.Exit(3)
	}

	go func() {
		<-sigs
		Logger().Infof("STOPPING FURY at %s", address)
		listener.Close()

		for _, cleanupFun := range fury.cleanupFunctions {
			cleanupFun(fury)
		}
	}()

	Logger().Infof("STARTING FURY at %s", address)
	server.Serve(listener)
}

func (fury *Fury) Abort(rw http.ResponseWriter, statusCode int) {
	rw.WriteHeader(statusCode)
}

func (fury *Fury) requestHandler(resource interface{}) (finalHandler http.HandlerFunc) {
	finalHandler = func(rw http.ResponseWriter, request *http.Request) {

		if request.ParseForm() != nil {
			rw.WriteHeader(http.StatusBadRequest)
			return
		}

		var handler func(m *Meta)

		switch request.Method {
		case CONNECT:
			if resource, ok := resource.(ConnectMixin); ok {
				handler = resource.Connect
			}
		case DELETE:
			if resource, ok := resource.(DeleteMixin); ok {
				handler = resource.Delete
			}
		case GET:
			if resource, ok := resource.(GetMixin); ok {
				handler = resource.Get
			}
		case HEAD:
			if resource, ok := resource.(HeadMixin); ok {
				handler = resource.Head
			}
		case OPTIONS:
			if resource, ok := resource.(OptionsMixin); ok {
				handler = resource.Options
			}
		case POST:
			if resource, ok := resource.(PostMixin); ok {
				handler = resource.Post
			}
		case PUT:
			if resource, ok := resource.(PutMixin); ok {
				handler = resource.Put
			}
		case PATCH:
			if resource, ok := resource.(PatchMixin); ok {
				handler = resource.Patch
			}
		case TRACE:
			if resource, ok := resource.(TraceMixin); ok {
				handler = resource.Trace
			}
		}

		if handler == nil {
			rw.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		var meta = &Meta{writer: rw, request: request, query: request.Form, fury: fury}
		handler(meta)
	}
	return
}

func New(host string, port int) *Fury {
	return &Fury{
		host:     host,
		port:     port,
		registry: make(map[string]interface{}),
	}
}
<commit_msg>Small changes to LoadAndValidateJson<commit_after>package fury

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"syscall"
	"time"
	"io/ioutil"
)

const (
	CONNECT = "CONNECT"
	DELETE  = "DELETE"
	GET     = "GET"
	HEAD    = "HEAD"
	OPTIONS = "OPTIONS"
	PATCH   = "PATCH"
	POST    = "POST"
	PUT     = "PUT"
	TRACE   = "TRACE"
)

const (
	HEADER_CONTENT_TYPE = "Content-Type"
)

const (
	CONTENT_TYPE_JSON = "application/json"
	CONTENT_TYPE_XML  = "application/xml"
)

type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
type CleanupFunc func(fury *Fury)

type Valid interface {
	OK() error
}

func Validate(v interface{}) error {
	obj, ok := v.(Valid)
	if !ok {
		return nil
	}

	if err := obj.OK(); err != nil {
		return err
	}
	return nil
}

func LoadAndValidateJson(meta *Meta, v interface{}) error {
	data, err := ioutil.ReadAll(meta.Request().Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(data, v)
	if err != nil {
		return err
	}

	return Validate(v)
}

type Renderer interface {
	Render(code int, name string, data interface{})
	Html(code int, html string)
	String(code int, s string)
	Json(code int, data interface{})
	Xml(code int, data interface{})
	SetContentType(name string)
	WriteWithCode(code int, data []byte)
}

type Meta struct {
	writer  http.ResponseWriter
	request *http.Request
	path    string
	query   url.Values
	fury    *Fury
}

func (m *Meta) App() *Fury {
	return m.fury
}

func (m *Meta) RequestHeaders() http.Header {
	return m.request.Header
}

func (m *Meta) Request() *http.Request {
	return m.request
}

func (meta *Meta) SetContentType(name string) {
	meta.writer.Header().Add(HEADER_CONTENT_TYPE, name)
}

func (meta *Meta) WriteWithCode(code int, data []byte) {
	meta.writer.WriteHeader(code)
	meta.writer.Write(data)
}

func (meta *Meta) Render(code int, name string, data interface{}) {
	return
}

func (meta *Meta) Html(code int, html string) {
	return
}

func (meta *Meta) String(code int, s string) {
	meta.WriteWithCode(code, []byte(s))
}

func (meta *Meta) Json(code int, data interface{}) {
	content, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		http.Error(meta.writer, err.Error(), http.StatusInternalServerError)
		return
	}

	meta.SetContentType(CONTENT_TYPE_JSON)
	meta.WriteWithCode(code, content)
}

func (meta *Meta) Xml(code int, data interface{}) {
	content, err := xml.MarshalIndent(data, "", "  ")
	if err != nil {
		http.Error(meta.writer, err.Error(), http.StatusInternalServerError)
		return
	}

	meta.SetContentType(CONTENT_TYPE_XML)
	meta.WriteWithCode(code, content)
}

type ConnectMixin interface {
	Connect(meta *Meta)
}

type DeleteMixin interface {
	Delete(meta *Meta)
}

type GetMixin interface {
	Get(meta *Meta)
}

type HeadMixin interface {
	Head(meta *Meta)
}

type OptionsMixin interface {
	Options(meta *Meta)
}

type PatchMixin interface {
	Patch(meta *Meta)
}

type PostMixin interface {
	Post(meta *Meta)
}

type PutMixin interface {
	Put(meta *Meta)
}

type TraceMixin interface {
	Trace(meta *Meta)
}

type Fury struct {
	port             int
	host             string
	cleanupFunctions []CleanupFunc
	middleware       []MiddlewareFunc
	registry         map[string]interface{}
}

func (fury *Fury) FromRegistry(key string) interface{} {
	return fury.registry[key]
}

func (fury *Fury) ToRegistry(key string, value interface{}) *Fury {
	fury.registry[key] = value
	return fury
}

func (fury *Fury) Route(path string, resource interface{}) *Fury {
	handler := fury.requestHandler(resource)
	for _, middleware := range fury.middleware {
		handler = middleware(handler)
	}
	http.HandleFunc(path, handler)
	return fury
}

func (fury *Fury) RegisterCleanup(fun ...CleanupFunc) *Fury {
	fury.cleanupFunctions = append(fury.cleanupFunctions, fun...)
	return fury
}

func (fury *Fury) UseMiddleware(middleware ...MiddlewareFunc) *Fury {
	fury.middleware = append(fury.middleware, middleware...)
	return fury
}

func (fury *Fury) Start() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	address := fmt.Sprintf("%s:%d", fury.host, fury.port)

	server := &http.Server{
		Addr:           address,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	listener, err := net.Listen("tcp", server.Addr)
	if err != nil {
		Logger().Error(err)
		os.Exit(3)
	}

	go func() {
		<-sigs
		Logger().Infof("STOPPING FURY at %s", address)
		listener.Close()

		for _, cleanupFun := range fury.cleanupFunctions {
			cleanupFun(fury)
		}
	}()

	Logger().Infof("STARTING FURY at %s", address)
	server.Serve(listener)
}

func (fury *Fury) Abort(rw http.ResponseWriter, statusCode int) {
	rw.WriteHeader(statusCode)
}

func (fury *Fury) requestHandler(resource interface{}) (finalHandler http.HandlerFunc) {
	finalHandler = func(rw http.ResponseWriter, request *http.Request) {

		if request.ParseForm() != nil {
			rw.WriteHeader(http.StatusBadRequest)
			return
		}

		var handler func(m *Meta)

		switch request.Method {
		case CONNECT:
			if resource, ok := resource.(ConnectMixin); ok {
				handler = resource.Connect
			}
		case DELETE:
			if resource, ok := resource.(DeleteMixin); ok {
				handler = resource.Delete
			}
		case GET:
			if resource, ok := resource.(GetMixin); ok {
				handler = resource.Get
			}
		case HEAD:
			if resource, ok := resource.(HeadMixin); ok {
				handler = resource.Head
			}
		case OPTIONS:
			if resource, ok := resource.(OptionsMixin); ok {
				handler = resource.Options
			}
		case POST:
			if resource, ok := resource.(PostMixin); ok {
				handler = resource.Post
			}
		case PUT:
			if resource, ok := resource.(PutMixin); ok {
				handler = resource.Put
			}
		case PATCH:
			if resource, ok := resource.(PatchMixin); ok {
				handler = resource.Patch
			}
		case TRACE:
			if resource, ok := resource.(TraceMixin); ok {
				handler = resource.Trace
			}
		}

		if handler == nil {
			rw.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		var meta = &Meta{writer: rw, request: request, query: request.Form, fury: fury}
		handler(meta)
	}
	return
}

func New(host string, port int) *Fury {
	return &Fury{
		host:     host,
		port:     port,
		registry: make(map[string]interface{}),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

type GaleWarning struct {
	Number int
	Date   time.Time
}

// Bulletin spécial: Avis de Grand frais à Coup de vent numéro 36
var (
	reWarning = regexp.MustCompile(`^\s*Bulletin spécial:.*?(\d+)`)
)

// extractWarningNumber returns the gale warning number in supplied weatcher
// forecast. It returns zero if there is none.
func extractWarningNumber(path string) (int, error) {
	fp, err := os.Open(path)
	if err != nil {
		return 0, err
	}
	defer fp.Close()

	scanner := bufio.NewScanner(fp)
	for scanner.Scan() {
		m := reWarning.FindSubmatch(scanner.Bytes())
		if m != nil {
			n, err := strconv.ParseInt(string(m[1]), 10, 32)
			if err != nil {
				return 0, err
			}
			return int(n), nil
		}
	}
	return 0, scanner.Err()
}

var (
	rePath = regexp.MustCompile(`^.*(\d{4}_\d{2}_\d{2}T_?\d{2}_\d{2}_\d{2})\.txt$`)
)

type sortedWarnings []GaleWarning

func (s sortedWarnings) Len() int {
	return len(s)
}

func (s sortedWarnings) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortedWarnings) Less(i, j int) bool {
	return s[i].Date.Before(s[j].Date)
}

// extractWarningNumbers returns the sequence of gale warnings extracted from
// weather forecasts in supplied directory.
func extractWarningNumbers(dir string) ([]GaleWarning, error) {

	warnings := []GaleWarning{}
	err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
		if err != nil || !fi.Mode().IsRegular() {
			return err
		}
		m := rePath.FindStringSubmatch(path)
		if m == nil {
			return nil
		}
		date := strings.Replace(m[1], "T_", "T", -1)
		d, err := time.Parse("2006_01_02T15_04_05", date)
		if err != nil {
			return err
		}
		n, err := extractWarningNumber(path)
		if err != nil {
			return err
		}
		warnings = append(warnings, GaleWarning{
			Number: n,
			Date:   d,
		})
		return nil
	})
	sort.Sort(sortedWarnings(warnings))
	// Fill intermediary reports without warnings with previous warning number
	num := 1
	for i, w := range warnings {
		if w.Number != 0 {
			num = w.Number
		} else {
			w := w
			w.Number = num
			warnings[i] = w
		}
	}
	return warnings, err
}

func serveGaleWarnings(galeDir string, template []byte, w http.ResponseWriter,
	req *http.Request) error {

	warnings, err := extractWarningNumbers(galeDir)
	if err != nil {
		return err
	}
	// Add virtual beginning of year and current day points
	now := time.Now()
	jan1 := time.Date(now.Year(), time.January, 1, 0, 0, 0, 0, time.UTC)
	if len(warnings) == 0 || jan1.Before(warnings[0].Date) {
		warnings = append([]GaleWarning{GaleWarning{
			Number: 0,
			Date:   jan1,
		}}, warnings...)
	}
	warnings = append(warnings, GaleWarning{
		Number: warnings[len(warnings)-1].Number,
		Date:   now,
	})

	baseDate := time.Date(2016, time.January, 1, 0, 0, 0, 0, time.UTC)

	type warningOffset struct {
		X       float64 `json:"x"`
		Y       float64 `json:"y"`
		Date    string  `json:"date"`
		YearDay int     `json:"yearday"`
	}
	offsets := []warningOffset{}
	refs := []warningOffset{}
	for _, w := range warnings {
		deltaDays := w.Date.Sub(baseDate).Hours() / 24.
		offset := warningOffset{
			X:       deltaDays,
			Y:       float64(w.Number),
			Date:    w.Date.Format("2006-01-02 15:04:05"),
			YearDay: w.Date.YearDay(),
		}
		offsets = append(offsets, offset)
		offset.Y = float64(offset.YearDay)
		refs = append(refs, offset)
	}

	dataVar, err := json.Marshal(&offsets)
	if err != nil {
		return err
	}
	refVar, err := json.Marshal(&refs)
	if err != nil {
		return err
	}
	page := bytes.Replace(template, []byte("$DATA"), dataVar, -1)
	page = bytes.Replace(page, []byte("$REF"), refVar, -1)
	w.Header().Set("Content-Type", "text/html")
	_, err = w.Write(page)
	return err
}

func handleGaleWarnings(galeDir string, template []byte, w http.ResponseWriter,
	req *http.Request) {

	err := serveGaleWarnings(galeDir, template, w, req)
	if err != nil {
		log.Printf("error: %s\n", err)
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("error: %s", err)))
	}
}

var (
	galeCmd = app.Command("gale", "display gale warning number vs day in the year")
	galeDir = galeCmd.Arg("forecastdir", "directory container weather forecasts").
		Required().String()
	galePrefix = galeCmd.Flag("prefix", "public URL prefix").String()
	galeHttp   = galeCmd.Flag("http", "HTTP host:port").Default(":5000").String()
)

func galeFn() error {
	prefix := *galePrefix
	addr := *galeHttp
	template, err := ioutil.ReadFile("scripts/main.html")
	if err != nil {
		return err
	}
	http.HandleFunc(prefix+"/", func(w http.ResponseWriter, req *http.Request) {
		handleGaleWarnings(*galeDir, template, w, req)
	})
	http.Handle(prefix+"/scripts/", http.StripPrefix(prefix+"/scripts/",
		http.FileServer(http.Dir("scripts"))))
	fmt.Printf("serving on %s\n", addr)
	return http.ListenAndServe(addr, nil)
}
<commit_msg>gale: update gale warning regexp for new forecast format<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

type GaleWarning struct {
	Number int
	Date   time.Time
}

// Bulletin spécial: Avis de Grand frais à Coup de vent numéro 36
var (
	reWarning = regexp.MustCompile(`^\s*(?:Bulletin spécial:|BMS\s+côte\s+numéro).*?(\d+)`)
)

// extractWarningNumber returns the gale warning number in supplied weatcher
// forecast. It returns zero if there is none.
func extractWarningNumber(path string) (int, error) {
	fp, err := os.Open(path)
	if err != nil {
		return 0, err
	}
	defer fp.Close()

	scanner := bufio.NewScanner(fp)
	for scanner.Scan() {
		m := reWarning.FindSubmatch(scanner.Bytes())
		if m != nil {
			n, err := strconv.ParseInt(string(m[1]), 10, 32)
			if err != nil {
				return 0, err
			}
			return int(n), nil
		}
	}
	return 0, scanner.Err()
}

var (
	rePath = regexp.MustCompile(`^.*(\d{4}_\d{2}_\d{2}T_?\d{2}_\d{2}_\d{2})\.txt$`)
)

type sortedWarnings []GaleWarning

func (s sortedWarnings) Len() int {
	return len(s)
}

func (s sortedWarnings) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortedWarnings) Less(i, j int) bool {
	return s[i].Date.Before(s[j].Date)
}

// extractWarningNumbers returns the sequence of gale warnings extracted from
// weather forecasts in supplied directory.
func extractWarningNumbers(dir string) ([]GaleWarning, error) {

	warnings := []GaleWarning{}
	err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
		if err != nil || !fi.Mode().IsRegular() {
			return err
		}
		m := rePath.FindStringSubmatch(path)
		if m == nil {
			return nil
		}
		date := strings.Replace(m[1], "T_", "T", -1)
		d, err := time.Parse("2006_01_02T15_04_05", date)
		if err != nil {
			return err
		}
		n, err := extractWarningNumber(path)
		if err != nil {
			return err
		}
		warnings = append(warnings, GaleWarning{
			Number: n,
			Date:   d,
		})
		return nil
	})
	sort.Sort(sortedWarnings(warnings))
	// Fill intermediary reports without warnings with previous warning number
	num := 1
	for i, w := range warnings {
		if w.Number != 0 {
			num = w.Number
		} else {
			w := w
			w.Number = num
			warnings[i] = w
		}
	}
	return warnings, err
}

func serveGaleWarnings(galeDir string, template []byte, w http.ResponseWriter,
	req *http.Request) error {

	warnings, err := extractWarningNumbers(galeDir)
	if err != nil {
		return err
	}
	// Add virtual beginning of year and current day points
	now := time.Now()
	jan1 := time.Date(now.Year(), time.January, 1, 0, 0, 0, 0, time.UTC)
	if len(warnings) == 0 || jan1.Before(warnings[0].Date) {
		warnings = append([]GaleWarning{GaleWarning{
			Number: 0,
			Date:   jan1,
		}}, warnings...)
	}
	warnings = append(warnings, GaleWarning{
		Number: warnings[len(warnings)-1].Number,
		Date:   now,
	})

	baseDate := time.Date(2016, time.January, 1, 0, 0, 0, 0, time.UTC)

	type warningOffset struct {
		X       float64 `json:"x"`
		Y       float64 `json:"y"`
		Date    string  `json:"date"`
		YearDay int     `json:"yearday"`
	}
	offsets := []warningOffset{}
	refs := []warningOffset{}
	for _, w := range warnings {
		deltaDays := w.Date.Sub(baseDate).Hours() / 24.
		offset := warningOffset{
			X:       deltaDays,
			Y:       float64(w.Number),
			Date:    w.Date.Format("2006-01-02 15:04:05"),
			YearDay: w.Date.YearDay(),
		}
		offsets = append(offsets, offset)
		offset.Y = float64(offset.YearDay)
		refs = append(refs, offset)
	}

	dataVar, err := json.Marshal(&offsets)
	if err != nil {
		return err
	}
	refVar, err := json.Marshal(&refs)
	if err != nil {
		return err
	}
	page := bytes.Replace(template, []byte("$DATA"), dataVar, -1)
	page = bytes.Replace(page, []byte("$REF"), refVar, -1)
	w.Header().Set("Content-Type", "text/html")
	_, err = w.Write(page)
	return err
}

func handleGaleWarnings(galeDir string, template []byte, w http.ResponseWriter,
	req *http.Request) {

	err := serveGaleWarnings(galeDir, template, w, req)
	if err != nil {
		log.Printf("error: %s\n", err)
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("error: %s", err)))
	}
}

var (
	galeCmd = app.Command("gale", "display gale warning number vs day in the year")
	galeDir = galeCmd.Arg("forecastdir", "directory container weather forecasts").
		Required().String()
	galePrefix = galeCmd.Flag("prefix", "public URL prefix").String()
	galeHttp   = galeCmd.Flag("http", "HTTP host:port").Default(":5000").String()
)

func galeFn() error {
	prefix := *galePrefix
	addr := *galeHttp
	template, err := ioutil.ReadFile("scripts/main.html")
	if err != nil {
		return err
	}
	http.HandleFunc(prefix+"/", func(w http.ResponseWriter, req *http.Request) {
		handleGaleWarnings(*galeDir, template, w, req)
	})
	http.Handle(prefix+"/scripts/", http.StripPrefix(prefix+"/scripts/",
		http.FileServer(http.Dir("scripts"))))
	fmt.Printf("serving on %s\n", addr)
	return http.ListenAndServe(addr, nil)
}
<|endoftext|>


<commit_before>package ghch

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/Songmu/gitsemvers"
	"github.com/octokit/go-octokit/octokit"
	"github.com/pkg/errors"
	"github.com/tcnksm/go-gitconfig"
)

const version = "0.0.1"

type ghch struct {
	repoPath string
	gitPath  string
	remote   string
	verbose  bool
	token    string
	client   *octokit.Client
}

func (gh *ghch) initialize() *ghch {
	var auth octokit.AuthMethod
	gh.setToken()
	if gh.token != "" {
		auth = octokit.TokenAuth{AccessToken: gh.token}
	}
	gh.client = octokit.NewClient(auth)
	return gh
}

func (gh *ghch) setToken() {
	if gh.token != "" {
		return
	}
	if gh.token = os.Getenv("GITHUB_TOKEN"); gh.token != "" {
		return
	}
	gh.token, _ = gitconfig.GithubToken()
	return
}

func (gh *ghch) gitProg() string {
	if gh.gitPath != "" {
		return gh.gitPath
	}
	return "git"
}

func (gh *ghch) cmd(argv ...string) (string, error) {
	arg := []string{"-C", gh.repoPath}
	arg = append(arg, argv...)
	cmd := exec.Command(gh.gitProg(), arg...)
	cmd.Env = append(os.Environ(), "LANG=C")

	var b bytes.Buffer
	cmd.Stdout = &b
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	return b.String(), err
}

var verReg = regexp.MustCompile(`^v?[0-9]+(?:\.[0-9]+){0,2}$`)

func (gh *ghch) versions() []string {
	sv := gitsemvers.Semvers{
		RepoPath: gh.repoPath,
		GitPath:  gh.gitPath,
	}
	return sv.VersionStrings()
}

func (gh *ghch) getRemote() string {
	if gh.remote != "" {
		return gh.remote
	}
	return "origin"
}

var repoURLReg = regexp.MustCompile(`([^/:]+)/([^/]+?)(?:\.git)?$`)

func (gh *ghch) ownerAndRepo() (owner, repo string) {
	out, _ := gh.cmd("remote", "-v")
	remotes := strings.Split(out, "\n")
	for _, r := range remotes {
		fields := strings.Fields(r)
		if len(fields) > 1 && fields[0] == gh.getRemote() {
			if matches := repoURLReg.FindStringSubmatch(fields[1]); len(matches) > 2 {
				return matches[1], matches[2]
			}
		}
	}
	return
}

func (gh *ghch) mergedPRs(from, to string) (prs []*octokit.PullRequest) {
	owner, repo := gh.ownerAndRepo()
	nums := gh.mergedPRNums(from, to)

	var wg sync.WaitGroup
	prCh := make(chan *octokit.PullRequest)
	finish := make(chan struct{})

	go func() {
		for {
			pr, ok := <-prCh
			if !ok {
				finish <- struct{}{}
				return
			}
			prs = append(prs, pr)
		}
	}()

	for _, num := range nums {
		wg.Add(1)
		go func(num int) {
			defer wg.Done()
			url, _ := octokit.PullRequestsURL.Expand(octokit.M{"owner": owner, "repo": repo, "number": num})
			pr, r := gh.client.PullRequests(url).One()
			if r.HasError() {
				log.Print(r.Err)
				return
			}
			if !gh.verbose {
				pr = reducePR(pr)
			}
			prCh <- pr
		}(num)
	}
	wg.Wait()
	close(prCh)
	<-finish

	return
}

func (gh *ghch) getLatestSemverTag() string {
	vers := gh.versions()
	if len(vers) < 1 {
		return ""
	}
	return vers[0]
}

var prMergeReg = regexp.MustCompile(`^[a-f0-9]{7} Merge pull request #([0-9]+) from`)

func (gh *ghch) mergedPRNums(from, to string) (nums []int) {
	if from == "" {
		from, _ = gh.cmd("rev-list", "--max-parents=0", "HEAD")
		from = strings.TrimSpace(from)
	}

	revisionRange := fmt.Sprintf("%s..%s", from, to)
	out, err := gh.cmd("log", revisionRange, "--merges", "--oneline")
	if err != nil {
		return
	}
	return parseMergedPRNums(out)
}

func parseMergedPRNums(out string) (nums []int) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		if matches := prMergeReg.FindStringSubmatch(line); len(matches) > 1 {
			i, _ := strconv.Atoi(matches[1])
			nums = append(nums, i)
		}
	}
	return
}

func (gh *ghch) getChangedAt(rev string) (time.Time, error) {
	if rev == "" {
		rev = "HEAD"
	}
	out, err := gh.cmd("show", "-s", rev+"^{commit}", `--format=%ct`)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. `git show` failed")
	}
	out = strings.TrimSpace(out)
	i, err := strconv.ParseInt(out, 10, 64)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. ParseInt failed")
	}
	return time.Unix(i, 0), nil
}
<commit_msg>add baseURL to support GitHub Enterprise<commit_after>package ghch

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/Songmu/gitsemvers"
	"github.com/octokit/go-octokit/octokit"
	"github.com/pkg/errors"
	"github.com/tcnksm/go-gitconfig"
)

const version = "0.0.1"

type ghch struct {
	repoPath string
	gitPath  string
	remote   string
	verbose  bool
	token    string
	baseURL  string
	client   *octokit.Client
}

func (gh *ghch) initialize() *ghch {
	var auth octokit.AuthMethod
	gh.setToken()
	if gh.token != "" {
		auth = octokit.TokenAuth{AccessToken: gh.token}
	}

	gh.setBaseURL()

	if gh.baseURL != "" {
		gh.client = octokit.NewClientWith(gh.baseURL, "Octokit Go", auth, nil)
		return gh
	}

	gh.client = octokit.NewClient(auth)
	return gh
}

func (gh *ghch) setToken() {
	if gh.token != "" {
		return
	}
	if gh.token = os.Getenv("GITHUB_TOKEN"); gh.token != "" {
		return
	}
	gh.token, _ = gitconfig.GithubToken()
	return
}

func (gh *ghch) setBaseURL() {
	if gh.baseURL != "" {
		return
	}
	if gh.baseURL = os.Getenv("GITHUB_API"); gh.baseURL != "" {
		return
	}

	return
}

func (gh *ghch) gitProg() string {
	if gh.gitPath != "" {
		return gh.gitPath
	}
	return "git"
}

func (gh *ghch) cmd(argv ...string) (string, error) {
	arg := []string{"-C", gh.repoPath}
	arg = append(arg, argv...)
	cmd := exec.Command(gh.gitProg(), arg...)
	cmd.Env = append(os.Environ(), "LANG=C")

	var b bytes.Buffer
	cmd.Stdout = &b
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	return b.String(), err
}

var verReg = regexp.MustCompile(`^v?[0-9]+(?:\.[0-9]+){0,2}$`)

func (gh *ghch) versions() []string {
	sv := gitsemvers.Semvers{
		RepoPath: gh.repoPath,
		GitPath:  gh.gitPath,
	}
	return sv.VersionStrings()
}

func (gh *ghch) getRemote() string {
	if gh.remote != "" {
		return gh.remote
	}
	return "origin"
}

var repoURLReg = regexp.MustCompile(`([^/:]+)/([^/]+?)(?:\.git)?$`)

func (gh *ghch) ownerAndRepo() (owner, repo string) {
	out, _ := gh.cmd("remote", "-v")
	remotes := strings.Split(out, "\n")
	for _, r := range remotes {
		fields := strings.Fields(r)
		if len(fields) > 1 && fields[0] == gh.getRemote() {
			if matches := repoURLReg.FindStringSubmatch(fields[1]); len(matches) > 2 {
				return matches[1], matches[2]
			}
		}
	}
	return
}

func (gh *ghch) mergedPRs(from, to string) (prs []*octokit.PullRequest) {
	owner, repo := gh.ownerAndRepo()
	nums := gh.mergedPRNums(from, to)

	var wg sync.WaitGroup
	prCh := make(chan *octokit.PullRequest)
	finish := make(chan struct{})

	go func() {
		for {
			pr, ok := <-prCh
			if !ok {
				finish <- struct{}{}
				return
			}
			prs = append(prs, pr)
		}
	}()

	for _, num := range nums {
		wg.Add(1)
		go func(num int) {
			defer wg.Done()
			url, _ := octokit.PullRequestsURL.Expand(octokit.M{"owner": owner, "repo": repo, "number": num})
			pr, r := gh.client.PullRequests(url).One()
			if r.HasError() {
				log.Print(r.Err)
				return
			}
			if !gh.verbose {
				pr = reducePR(pr)
			}
			prCh <- pr
		}(num)
	}
	wg.Wait()
	close(prCh)
	<-finish

	return
}

func (gh *ghch) getLatestSemverTag() string {
	vers := gh.versions()
	if len(vers) < 1 {
		return ""
	}
	return vers[0]
}

var prMergeReg = regexp.MustCompile(`^[a-f0-9]{7} Merge pull request #([0-9]+) from`)

func (gh *ghch) mergedPRNums(from, to string) (nums []int) {
	if from == "" {
		from, _ = gh.cmd("rev-list", "--max-parents=0", "HEAD")
		from = strings.TrimSpace(from)
	}

	revisionRange := fmt.Sprintf("%s..%s", from, to)
	out, err := gh.cmd("log", revisionRange, "--merges", "--oneline")
	if err != nil {
		return
	}
	return parseMergedPRNums(out)
}

func parseMergedPRNums(out string) (nums []int) {
	lines := strings.Split(out, "\n")
	for _, line := range lines {
		if matches := prMergeReg.FindStringSubmatch(line); len(matches) > 1 {
			i, _ := strconv.Atoi(matches[1])
			nums = append(nums, i)
		}
	}
	return
}

func (gh *ghch) getChangedAt(rev string) (time.Time, error) {
	if rev == "" {
		rev = "HEAD"
	}
	out, err := gh.cmd("show", "-s", rev+"^{commit}", `--format=%ct`)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. `git show` failed")
	}
	out = strings.TrimSpace(out)
	i, err := strconv.ParseInt(out, 10, 64)
	if err != nil {
		return time.Time{}, errors.Wrap(err, "failed to get changed at from git revision. ParseInt failed")
	}
	return time.Unix(i, 0), nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/igb"
	"github.com/goby-lang/goby/vm"
	"github.com/pkg/profile"
)

const Version string = vm.Version

func main() {
	profileOptionPtr := flag.Bool("p", false, "Profile program execution")
	versionOptionPtr := flag.Bool("v", false, "Show current Goby version")
	interactiveOptionPtr := flag.Bool("i", false, "Run interactive goby")

	flag.Parse()

	if *interactiveOptionPtr {
		igb.StartIgb(Version)
		os.Exit(0)
	}

	if *profileOptionPtr {
		defer profile.Start().Stop()
	}

	if *versionOptionPtr {
		fmt.Println(Version)
		os.Exit(0)
	}

	fp := flag.Arg(0)

	if fp == "" {
		flag.Usage()
		os.Exit(0)
	}

	args := flag.Args()[1:]

	dir, _, fileExt := extractFileInfo(fp)
	file, ok := readFile(fp)

	if !ok {
		return
	}

	switch fileExt {
	case "gb", "rb":
		instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

		if err != nil {
			fmt.Println(err.Error())
			return
		}

		v, err := vm.New(dir, args)

		if err != nil {
			fmt.Println(err.Error())
			return
		}

		fp, err := filepath.Abs(fp)

		if err != nil {
			fmt.Println(err.Error())
			return
		}

		v.ExecInstructions(instructionSets, fp)
	default:
		fmt.Printf("Unknown file extension: %s", fileExt)
	}
}

func extractFileInfo(fp string) (dir, filename, fileExt string) {
	dir, filename = filepath.Split(fp)
	dir, _ = filepath.Abs(dir)
	fileExt = filepath.Ext(fp)
	splited := strings.Split(filename, ".")
	filename, fileExt = splited[0], splited[1]
	return
}

func readFile(filepath string) (file []byte, ok bool) {
	file, err := ioutil.ReadFile(filepath)

	if err != nil {
		fmt.Println(err.Error())
		return []byte{}, false
	}

	return file, true
}
<commit_msg>it is panic to run 'goby xxxx'<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/igb"
	"github.com/goby-lang/goby/vm"
	"github.com/pkg/profile"
)

const Version string = vm.Version

func main() {
	profileOptionPtr := flag.Bool("p", false, "Profile program execution")
	versionOptionPtr := flag.Bool("v", false, "Show current Goby version")
	interactiveOptionPtr := flag.Bool("i", false, "Run interactive goby")

	flag.Parse()

	if *interactiveOptionPtr {
		igb.StartIgb(Version)
		os.Exit(0)
	}

	if *profileOptionPtr {
		defer profile.Start().Stop()
	}

	if *versionOptionPtr {
		fmt.Println(Version)
		os.Exit(0)
	}

	fp := flag.Arg(0)

	if fp == "" || !strings.Contains(fp, ".") {
		flag.Usage()
		os.Exit(0)
	}

	args := flag.Args()[1:]

	dir, _, fileExt := extractFileInfo(fp)
	file, ok := readFile(fp)

	if !ok {
		return
	}

	switch fileExt {
	case "gb", "rb":
		instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

		if err != nil {
			fmt.Println(err.Error())
			return
		}

		v, err := vm.New(dir, args)

		if err != nil {
			fmt.Println(err.Error())
			return
		}

		fp, err := filepath.Abs(fp)

		if err != nil {
			fmt.Println(err.Error())
			return
		}

		v.ExecInstructions(instructionSets, fp)
	default:
		fmt.Printf("Unknown file extension: %s", fileExt)
	}
}

func extractFileInfo(fp string) (dir, filename, fileExt string) {
	dir, filename = filepath.Split(fp)
	dir, _ = filepath.Abs(dir)
	fileExt = filepath.Ext(fp)
	splited := strings.Split(filename, ".")
	filename, fileExt = splited[0], splited[1]
	return
}

func readFile(filepath string) (file []byte, ok bool) {
	file, err := ioutil.ReadFile(filepath)

	if err != nil {
		fmt.Println(err.Error())
		return []byte{}, false
	}

	return file, true
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"

	"golang.org/x/crypto/ssh/terminal"
)

const VERSION = "0.0.1"

type Editor struct {
	Reader *bufio.Reader

	ScreenRows int
	ScreenCols int
}

func NewEditor() *Editor {
	editor := &Editor{}
	editor.Reader = bufio.NewReader(os.Stdin)
	w, h, err := terminal.GetSize(0)
	if err != nil {
		fmt.Print(err)
	}
	editor.ScreenRows = h
	editor.ScreenCols = w
	return editor
}

const CTRL_Q = 0x11

func (e *Editor) ReadKey() rune {
	rune, _, err := e.Reader.ReadRune()

	if err != nil {
		fmt.Print(err)
	}

	return rune
}

func (e *Editor) ProcessKeyPress() error {
	key := e.ReadKey()

	// print out the unicode value i.e. A -> 65, a -> 97
	fmt.Print(key)
	if key == CTRL_Q {
		e.Exit()
		return errors.New("quit")
	}
	return nil
}

func (e *Editor) RefreshScreen() {
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[?25l")...) // hide cursor
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	buffer = e.DrawRows(buffer)
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	buffer = append(buffer, []byte("\x1b[?25h")...) // show cursor
	os.Stdout.Write(buffer)
}

func (e *Editor) Exit() {
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[2J")...)   // clear screen
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	os.Stdout.Write(buffer)
}

func (e *Editor) DrawRows(buffer []byte) []byte {
	for y := 1; y <= e.ScreenRows; y++ {
		if y == e.ScreenRows/3 {
			welcome := fmt.Sprintf("goed editor -- version %s", VERSION)
			padding := (e.ScreenCols - len(welcome)) / 2
			buffer = append(buffer, []byte("~")...)
			for i := 1; i <= padding; i++ {
				buffer = append(buffer, []byte(" ")...)
			}
			buffer = append(buffer, []byte(welcome)...)
		} else {
			buffer = append(buffer, []byte("~")...)
		}
		buffer = append(buffer, []byte("\x1b[K")...)
		if y < e.ScreenRows {
			buffer = append(buffer, []byte("\r\n")...)
		}
	}
	return buffer
}

func main() {

	// put the terminal into raw mode
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err)
	}
	// restore terminal however we exit
	defer terminal.Restore(0, oldState)

	e := NewEditor()
	e.RefreshScreen()
	// input loop
	for {
		err = e.ProcessKeyPress()
		if err != nil {
			break
		}
	}
}
<commit_msg>Some special key handling.<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"time"

	"./terminal"
)

const VERSION = "0.0.1"

const (
	CTRL_Q      = 0x11
	ARROW_LEFT  = 1000
	ARROW_RIGHT = 1001
	ARROW_UP    = 1002
	ARROW_DOWN  = 1003
	PAGE_UP     = 1004
	PAGE_DOWN   = 1005
	DELETE      = 1006
)

type Editor struct {
	Reader *bufio.Reader

	ScreenRows int
	ScreenCols int

	CursorRow int
	CursorCol int

	Message string
}

func NewEditor() *Editor {
	editor := &Editor{}
	editor.Reader = bufio.NewReader(os.Stdin)
	w, h, err := terminal.GetSize(0)
	if err != nil {
		fmt.Print(err)
	}
	editor.ScreenRows = h
	editor.ScreenCols = w
	return editor
}

func (e *Editor) ReadKey() int {
	var err error
	b := make([]byte, 10)
	n := 0
	for n == 0 {
		n, _ = os.Stdin.Read(b)
		if n == 0 {
			time.Sleep(time.Microsecond)
		}
	}
	e.Message = fmt.Sprintf(" code=%02x", b[0:n])
	if err != nil {
		fmt.Print(err)
	}
	switch b[0] {
	case 0x1b:
		switch b[1] {
		case 0x5b:
			switch b[2] {
			case 'A':
				return ARROW_UP
			case 'B':
				return ARROW_DOWN
			case 'C':
				return ARROW_RIGHT
			case 'D':
				return ARROW_LEFT
			case 0x33:
				switch b[3] {
				case 0x7e:
					return DELETE
				}
			}
		}
	}
	return int(b[0])
}

func (e *Editor) ProcessKeyPress() error {
	key := e.ReadKey()
	e.Message += fmt.Sprintf(" key=%d", key)

	switch key {
	case CTRL_Q:
		e.Exit()
		return errors.New("quit")
	case ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT:
		e.MoveCursor(key)
	}
	return nil
}

func (e *Editor) RefreshScreen() {
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[?25l")...) // hide cursor
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	buffer = e.DrawRows(buffer)

	buffer = append(buffer, []byte(fmt.Sprintf("\x1b[%d;%dH", e.CursorRow+1, e.CursorCol+1))...)

	buffer = append(buffer, []byte("\x1b[?25h")...) // show cursor
	os.Stdout.Write(buffer)
}

func (e *Editor) Exit() {
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[2J")...)   // clear screen
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	os.Stdout.Write(buffer)
}

func (e *Editor) DrawRows(buffer []byte) []byte {
	for y := 1; y <= e.ScreenRows; y++ {
		if y == e.ScreenRows/3 {
			welcome := fmt.Sprintf("goed editor -- version %s", VERSION)
			padding := (e.ScreenCols - len(welcome)) / 2
			buffer = append(buffer, []byte("~")...)
			for i := 1; i <= padding; i++ {
				buffer = append(buffer, []byte(" ")...)
			}
			buffer = append(buffer, []byte(welcome)...)
		} else {
			buffer = append(buffer, []byte("~")...)
		}
		buffer = append(buffer, []byte("\x1b[K")...)
		if y < e.ScreenRows {
			buffer = append(buffer, []byte("\r\n")...)
		} else {
			buffer = append(buffer, []byte(e.Message)...)
		}
	}
	return buffer
}

func (e *Editor) MoveCursor(key int) {
	switch key {
	case ARROW_LEFT:
		if e.CursorCol > 0 {
			e.CursorCol--
		}
	case ARROW_RIGHT:
		if e.CursorCol < e.ScreenCols-1 {
			e.CursorCol++
		}
	case ARROW_UP:
		if e.CursorRow > 0 {
			e.CursorRow--
		}
	case ARROW_DOWN:
		if e.CursorRow < e.ScreenRows-1 {
			e.CursorRow++
		}
	}
}

func main() {

	// put the terminal into raw mode
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err)
	}
	// restore terminal however we exit
	defer terminal.Restore(0, oldState)

	e := NewEditor()
	// input loop
	for {
		e.RefreshScreen()
		err = e.ProcessKeyPress()
		if err != nil {
			break
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http2

import (
	"net/http"
	"sync"
)

var (
	commonBuildOnce   sync.Once
	commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case
	commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case
)

func buildCommonHeaderMapsOnce() {
	commonBuildOnce.Do(buildCommonHeaderMaps)
}

func buildCommonHeaderMaps() {
	common := []string{
		"accept",
		"accept-charset",
		"accept-encoding",
		"accept-language",
		"accept-ranges",
		"age",
		"access-control-allow-origin",
		"allow",
		"authorization",
		"cache-control",
		"content-disposition",
		"content-encoding",
		"content-language",
		"content-length",
		"content-location",
		"content-range",
		"content-type",
		"cookie",
		"date",
		"etag",
		"expect",
		"expires",
		"from",
		"host",
		"if-match",
		"if-modified-since",
		"if-none-match",
		"if-unmodified-since",
		"last-modified",
		"link",
		"location",
		"max-forwards",
		"proxy-authenticate",
		"proxy-authorization",
		"range",
		"referer",
		"refresh",
		"retry-after",
		"server",
		"set-cookie",
		"strict-transport-security",
		"trailer",
		"transfer-encoding",
		"user-agent",
		"vary",
		"via",
		"www-authenticate",
	}
	commonLowerHeader = make(map[string]string, len(common))
	commonCanonHeader = make(map[string]string, len(common))
	for _, v := range common {
		chk := http.CanonicalHeaderKey(v)
		commonLowerHeader[chk] = v
		commonCanonHeader[v] = chk
	}
}

func lowerHeader(v string) (lower string, ascii bool) {
	buildCommonHeaderMapsOnce()
	if s, ok := commonLowerHeader[v]; ok {
		return s, true
	}
	return asciiToLower(v)
}
<commit_msg>http2: add a few other common headers to the shared headermap cache<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http2

import (
	"net/http"
	"sync"
)

var (
	commonBuildOnce   sync.Once
	commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case
	commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case
)

func buildCommonHeaderMapsOnce() {
	commonBuildOnce.Do(buildCommonHeaderMaps)
}

func buildCommonHeaderMaps() {
	common := []string{
		"accept",
		"accept-charset",
		"accept-encoding",
		"accept-language",
		"accept-ranges",
		"age",
		"access-control-allow-credentials",
		"access-control-allow-headers",
		"access-control-allow-methods",
		"access-control-allow-origin",
		"access-control-expose-headers",
		"access-control-max-age",
		"access-control-request-headers",
		"access-control-request-method",
		"allow",
		"authorization",
		"cache-control",
		"content-disposition",
		"content-encoding",
		"content-language",
		"content-length",
		"content-location",
		"content-range",
		"content-type",
		"cookie",
		"date",
		"etag",
		"expect",
		"expires",
		"from",
		"host",
		"if-match",
		"if-modified-since",
		"if-none-match",
		"if-unmodified-since",
		"last-modified",
		"link",
		"location",
		"max-forwards",
		"origin",
		"proxy-authenticate",
		"proxy-authorization",
		"range",
		"referer",
		"refresh",
		"retry-after",
		"server",
		"set-cookie",
		"strict-transport-security",
		"trailer",
		"transfer-encoding",
		"user-agent",
		"vary",
		"via",
		"www-authenticate",
		"x-forwarded-for",
		"x-forwarded-proto",
	}
	commonLowerHeader = make(map[string]string, len(common))
	commonCanonHeader = make(map[string]string, len(common))
	for _, v := range common {
		chk := http.CanonicalHeaderKey(v)
		commonLowerHeader[chk] = v
		commonCanonHeader[v] = chk
	}
}

func lowerHeader(v string) (lower string, ascii bool) {
	buildCommonHeaderMapsOnce()
	if s, ok := commonLowerHeader[v]; ok {
		return s, true
	}
	return asciiToLower(v)
}
<|endoftext|>


<commit_before>package gois

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/jinzhu/now"
)

// Record holds the information returned by the whois server
type Record struct {
	Domain        string
	TrimmedDomain string
	CreatedOn     time.Time
	Registered    bool
}

func longestTLDSuffix(domain string) string {
	longestTld := ""
	for tld := range TLDWhoisServers {
		if strings.HasSuffix(domain, "."+tld) && utf8.RuneCountInString(tld) > utf8.RuneCountInString(longestTld) {
			longestTld = tld
		}
	}
	return longestTld
}

func trimSubdomains(domain, tld string) (trimmedDomain string) {
	noTld := strings.TrimSuffix(domain, "."+tld)
	parts := strings.Split(noTld, ".")
	trimmedDomain = fmt.Sprintf("%s.%s", parts[len(parts)-1], tld)
	return trimmedDomain
}

// Whois returns the public whois information for a domain
func Whois(domain string) (record *Record, err error) {
	tld := longestTLDSuffix(domain)
	server := TLDWhoisServers[tld]

	trimmedDomain := trimSubdomains(domain, tld)
	requestDomain := trimmedDomain
	if server == "whois.verisign-grs.com" {
		requestDomain = "=" + trimmedDomain
	} else if server == "whois.denic.de" {
		requestDomain = "-T dn,ace " + trimmedDomain
	}

	response, err := QueryWhoisServer(requestDomain, server)
	if err != nil {
		return
	}

	record, err = parse(response)
	if err == nil {
		record.Domain = domain
		record.TrimmedDomain = trimmedDomain
	}

	return
}

// QueryWhoisServer queries a particular whois server for information about a domain
func QueryWhoisServer(domain, server string) (response string, err error) {
	conn, err := net.Dial("tcp", server+":43")
	if err != nil {
		return
	}
	defer conn.Close()

	fmt.Fprintf(conn, "%s\r\n", domain)
	if buf, err := ioutil.ReadAll(conn); err == nil {
		response = string(buf)
	}

	return
}

func parse(response string) (record *Record, err error) {
	for _, line := range strings.Split(response, "\n") {
		line = strings.TrimSpace(line)
		if line == "" || !strings.Contains(line, ":") {
			continue
		}
		parts := strings.SplitN(line, ":", 2)
		key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
		if strings.ToLower(key) == "creation date" || strings.ToLower(key) == "changed" || strings.ToLower(key) == "domain create date" {
			if parsedDate, parseErr := now.Parse(value); parseErr != nil {
				err = parseErr
			} else {
				record = &Record{CreatedOn: parsedDate, Registered: true}
			}
			return
		}
	}
	return nil, errors.New("Unable to parse whois record")
}

func init() {
	now.TimeFormats = append(now.TimeFormats, "02-Jan-2006")
	now.TimeFormats = append(now.TimeFormats, "02-Jan-2006 15:04:05 MST")
	now.TimeFormats = append(now.TimeFormats, "2006-01-02T15:04:05.0Z")
	now.TimeFormats = append(now.TimeFormats, "2006-01-02T15:04:05Z")
	now.TimeFormats = append(now.TimeFormats, "2006-01-02T15:04:05-07:00")
}
<commit_msg>fix for .ru domains<commit_after>package gois

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/jinzhu/now"
)

// Record holds the information returned by the whois server
type Record struct {
	Domain        string
	TrimmedDomain string
	CreatedOn     time.Time
	Registered    bool
}

func longestTLDSuffix(domain string) string {
	longestTld := ""
	for tld := range TLDWhoisServers {
		if strings.HasSuffix(domain, "."+tld) && utf8.RuneCountInString(tld) > utf8.RuneCountInString(longestTld) {
			longestTld = tld
		}
	}
	return longestTld
}

func trimSubdomains(domain, tld string) (trimmedDomain string) {
	noTld := strings.TrimSuffix(domain, "."+tld)
	parts := strings.Split(noTld, ".")
	trimmedDomain = fmt.Sprintf("%s.%s", parts[len(parts)-1], tld)
	return trimmedDomain
}

// Whois returns the public whois information for a domain
func Whois(domain string) (record *Record, err error) {
	tld := longestTLDSuffix(domain)
	server := TLDWhoisServers[tld]

	trimmedDomain := trimSubdomains(domain, tld)
	requestDomain := trimmedDomain
	if server == "whois.verisign-grs.com" {
		requestDomain = "=" + trimmedDomain
	} else if server == "whois.denic.de" {
		requestDomain = "-T dn,ace " + trimmedDomain
	}

	response, err := QueryWhoisServer(requestDomain, server)
	if err != nil {
		return
	}

	record, err = parse(response)
	if err == nil {
		record.Domain = domain
		record.TrimmedDomain = trimmedDomain
	}

	return
}

// QueryWhoisServer queries a particular whois server for information about a domain
func QueryWhoisServer(domain, server string) (response string, err error) {
	conn, err := net.Dial("tcp", server+":43")
	if err != nil {
		return
	}
	defer conn.Close()

	fmt.Fprintf(conn, "%s\r\n", domain)
	if buf, err := ioutil.ReadAll(conn); err == nil {
		response = string(buf)
	}

	return
}

func parse(response string) (record *Record, err error) {
	for _, line := range strings.Split(response, "\n") {
		line = strings.TrimSpace(line)
		if line == "" || !strings.Contains(line, ":") {
			continue
		}
		parts := strings.SplitN(line, ":", 2)
		key, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
		if strings.ToLower(key) == "created" || strings.ToLower(key) == "creation date" || strings.ToLower(key) == "changed" || strings.ToLower(key) == "domain create date" {
			if parsedDate, parseErr := now.Parse(value); parseErr != nil {
				err = parseErr
			} else {
				record = &Record{CreatedOn: parsedDate, Registered: true}
			}
			return
		}
	}
	return nil, errors.New("Unable to parse whois record")
}

func init() {
	now.TimeFormats = append(now.TimeFormats, "02-Jan-2006")
	now.TimeFormats = append(now.TimeFormats, "02-Jan-2006 15:04:05 MST")
	now.TimeFormats = append(now.TimeFormats, "2006-01-02T15:04:05.0Z")
	now.TimeFormats = append(now.TimeFormats, "2006-01-02T15:04:05Z")
	now.TimeFormats = append(now.TimeFormats, "2006-01-02T15:04:05-07:00")
	now.TimeFormats = append(now.TimeFormats, "2006.01.02")
}
<|endoftext|>


<commit_before>package httpmux

import (
	"fmt"
	"net/http"
	"path"
	"regexp"
	"strings"

	"golang.org/x/net/context"
)

var pathParamContextKey = &struct{}{}

type Mux struct {
	handlers []handler
}

func New() *Mux {
	return new(Mux)
}

type handler struct {
	path        *regexp.Regexp
	userHandler http.Handler
}

func (me *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	matches := me.matchingHandlers(r)
	switch len(matches) {
	case 0:
		http.NotFound(w, r)
		return
	case 1:
		m := matches[0]
		r = r.WithContext(context.WithValue(r.Context(), pathParamContextKey, &PathParams{m}))
		m.handler.userHandler.ServeHTTP(w, r)
	default:
		panic("multiple handlers match: " + strings.Join(func() (ret []string) {
			for _, m := range matches {
				ret = append(ret, m.handler.path.String())
			}
			return
		}(), ", "))
	}
}

type match struct {
	handler    handler
	submatches []string
}

func (me *Mux) matchingHandlers(r *http.Request) (ret []match) {
	for _, h := range me.handlers {
		subs := h.path.FindStringSubmatch(r.URL.Path)
		if subs == nil {
			continue
		}
		ret = append(ret, match{h, subs})
	}
	return
}

func (me *Mux) Handle(path string, h http.Handler) {
	if !strings.HasSuffix(path, "$") {
		path += "$"
	}
	re, err := regexp.Compile("^" + path)
	if err != nil {
		panic(err)
	}
	me.handlers = append(me.handlers, handler{re, h})
}

func (me *Mux) HandleFunc(path string, hf func(http.ResponseWriter, *http.Request)) {
	me.Handle(path, http.HandlerFunc(hf))
}

func Path(parts ...string) string {
	return path.Join(parts...)
}

type PathParams struct {
	match match
}

func (me *PathParams) ByName(name string) string {
	for i, sn := range me.match.handler.path.SubexpNames()[1:] {
		if sn == name {
			return me.match.submatches[i+1]
		}
	}
	return ""
}

func RequestPathParams(r *http.Request) *PathParams {
	ctx := r.Context()
	return ctx.Value(pathParamContextKey).(*PathParams)
}

func PathRegexpParam(name string, re string) string {
	return fmt.Sprintf("(?P<%s>%s)", name, re)
}

func Param(name string) string {
	return fmt.Sprintf("(?P<%s>[^/]+)", name)
}

func RestParam(name string) string {
	return fmt.Sprintf("(?P<%s>.*)$", name)
}

func NonEmptyRestParam(name string) string {
	return fmt.Sprintf("(?P<%s>.+)$", name)
}
<commit_msg>httpmux: Serve first matching pattern<commit_after>package httpmux

import (
	"fmt"
	"net/http"
	"path"
	"regexp"
	"strings"

	"golang.org/x/net/context"
)

var pathParamContextKey = &struct{}{}

type Mux struct {
	handlers []handler
}

func New() *Mux {
	return new(Mux)
}

type handler struct {
	path        *regexp.Regexp
	userHandler http.Handler
}

func (me *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	matches := me.matchingHandlers(r)
	if len(matches) == 0 {
		http.NotFound(w, r)
		return
	}
	m := matches[0]
	r = r.WithContext(context.WithValue(r.Context(), pathParamContextKey, &PathParams{m}))
	m.handler.userHandler.ServeHTTP(w, r)
}

type match struct {
	handler    handler
	submatches []string
}

func (me *Mux) matchingHandlers(r *http.Request) (ret []match) {
	for _, h := range me.handlers {
		subs := h.path.FindStringSubmatch(r.URL.Path)
		if subs == nil {
			continue
		}
		ret = append(ret, match{h, subs})
	}
	return
}

func (me *Mux) Handle(path string, h http.Handler) {
	if !strings.HasSuffix(path, "$") {
		path += "$"
	}
	re, err := regexp.Compile("^" + path)
	if err != nil {
		panic(err)
	}
	me.handlers = append(me.handlers, handler{re, h})
}

func (me *Mux) HandleFunc(path string, hf func(http.ResponseWriter, *http.Request)) {
	me.Handle(path, http.HandlerFunc(hf))
}

func Path(parts ...string) string {
	return path.Join(parts...)
}

type PathParams struct {
	match match
}

func (me *PathParams) ByName(name string) string {
	for i, sn := range me.match.handler.path.SubexpNames()[1:] {
		if sn == name {
			return me.match.submatches[i+1]
		}
	}
	return ""
}

func RequestPathParams(r *http.Request) *PathParams {
	ctx := r.Context()
	return ctx.Value(pathParamContextKey).(*PathParams)
}

func PathRegexpParam(name string, re string) string {
	return fmt.Sprintf("(?P<%s>%s)", name, re)
}

func Param(name string) string {
	return fmt.Sprintf("(?P<%s>[^/]+)", name)
}

func RestParam(name string) string {
	return fmt.Sprintf("(?P<%s>.*)$", name)
}

func NonEmptyRestParam(name string) string {
	return fmt.Sprintf("(?P<%s>.+)$", name)
}
<|endoftext|>


<commit_before>package httpseverywhere

import (
	"net/url"
	"regexp"
	"strings"
	"sync/atomic"

	"golang.org/x/net/publicsuffix"

	"github.com/getlantern/golog"
)

var (
	log = golog.LoggerFor("httpseverywhere")
)

// Rewrite exports the rewrite method for users of this library.
var Rewrite = newAsync()

// rewrite changes an HTTP URL to rewrite.
type rewrite func(url string) (string, bool)

type https struct {
	// This is a map of root host names to Targets -- map[string]*Targets
	hostsToTargets atomic.Value
}

// A rule maps the regular expression to match and the string to change it to.
// It also stores the compiled regular expression for efficiency.
type rule struct {
	from *regexp.Regexp
	From string
	To   string
}

// An exclusion just contains the compiled regular expression exclusion pattern.
type exclusion struct {
	Pattern string
	pattern *regexp.Regexp
}

// Rules is a struct containing rules and exclusions for a given rule set. This
// is public so that we can encode and decode it from GOB format.
type Rules struct {
	Rules      []*rule
	Exclusions []*exclusion
}

// Targets contains the target hosts for the given base domain.
type Targets struct {
	wildcardPrefix []*regexp.Regexp
	wildcardSuffix []*regexp.Regexp

	// We use maps here to filter duplicates.
	WildcardPrefix map[string]bool
	WildcardSuffix map[string]bool
	Plain          map[string]bool

	Rules *Rules
}

// new creates a new rewrite instance from embedded GOB data with asynchronous
// loading of the rule sets to allow the caller to about around a 2 second
// delay.
func newAsync() rewrite {
	h := &https{}

	h.hostsToTargets.Store(make(map[string]*Targets))
	go func() {
		d := newDeserializer()
		temp := d.newHostsToTargets()
		h.hostsToTargets.Store(temp)
	}()

	return h.rewrite
}

// newSync creates a new rewrite instance from embedded GOB data.
func newSync() rewrite {
	h := &https{}
	d := newDeserializer()
	h.hostsToTargets.Store(d.newHostsToTargets())
	return h.rewrite
}

func (h *https) rewrite(urlStr string) (string, bool) {
	if strings.HasPrefix(urlStr, "https") {
		return urlStr, false
	}
	if !strings.HasPrefix(urlStr, "http://") {
		return urlStr, false
	}
	url, root, err := extractURLAndRoot(urlStr)
	if err != nil {
		return urlStr, false
	}
	if len(root) == 0 {
		log.Error("Root is the empty string!")
		return urlStr, false
	}
	if targets, ok := h.hostsToTargets.Load().(map[string]*Targets)[root]; ok {
		return targets.rewrite(urlStr, url.Host)
	}
	return urlStr, false
}

func extractURLAndRoot(originalURL string) (*url.URL, string, error) {
	// Just normalize it as a url with the http protocol
	var urlStr string
	if !strings.HasPrefix(originalURL, "http://") {
		urlStr = "http://" + originalURL
	} else {
		urlStr = originalURL
	}
	url, err := url.Parse(urlStr)
	if err != nil {
		log.Errorf("Could not parse URL %v with error %v", urlStr, err)
		return nil, urlStr, err
	}

	tld, _ := publicsuffix.PublicSuffix(url.Host)

	// Because some TLDs such as "co.uk" include "."s, we strip the TLD prior
	// to stripping subdomains.
	noTLD := strings.TrimSuffix(url.Host, "."+tld)
	root := stripSubdomains(noTLD)
	return url, root, nil
}

func stripSubdomains(host string) string {
	host = strings.TrimSpace(host)
	parts := strings.Split(host, ".")
	return parts[len(parts)-1]
}

func (t *Targets) rewrite(url, domain string) (string, bool) {
	// We basically want to apply the associated set of rules if any of the
	// targets match the url.
	log.Debugf("Attempting to rewrite url %v and domain %v", url, domain)
	for k := range t.Plain {
		if domain == k {
			return t.Rules.rewrite(url)
		}
	}
	if r, done := t.matchTargets(url, t.wildcardPrefix); done {
		return r, done
	}
	return t.matchTargets(url, t.wildcardSuffix)
}

func (t *Targets) matchTargets(url string, targets []*regexp.Regexp) (string, bool) {
	for _, pre := range targets {
		if pre.MatchString(url) {
			r, done := t.Rules.rewrite(url)
			if done {
				return r, done
			}
		}
	}
	return url, false
}

// rewrite converts the given URL to HTTPS if there is an associated rule for
// it.
func (r *Rules) rewrite(url string) (string, bool) {
	for _, exclude := range r.Exclusions {
		if exclude.pattern.MatchString(url) {
			return url, false
		}
	}
	for _, rule := range r.Rules {
		if rule.from.MatchString(url) {
			return rule.from.ReplaceAllString(url, rule.To), true
		}
	}
	return url, false
}
<commit_msg>strip port<commit_after>package httpseverywhere

import (
	"net"
	"net/url"
	"regexp"
	"strings"
	"sync/atomic"

	"golang.org/x/net/publicsuffix"

	"github.com/getlantern/golog"
)

var (
	log = golog.LoggerFor("httpseverywhere")
)

// Rewrite exports the rewrite method for users of this library.
var Rewrite = newAsync()

// rewrite changes an HTTP URL to rewrite.
type rewrite func(url string) (string, bool)

type https struct {
	// This is a map of root host names to Targets -- map[string]*Targets
	hostsToTargets atomic.Value
}

// A rule maps the regular expression to match and the string to change it to.
// It also stores the compiled regular expression for efficiency.
type rule struct {
	from *regexp.Regexp
	From string
	To   string
}

// An exclusion just contains the compiled regular expression exclusion pattern.
type exclusion struct {
	Pattern string
	pattern *regexp.Regexp
}

// Rules is a struct containing rules and exclusions for a given rule set. This
// is public so that we can encode and decode it from GOB format.
type Rules struct {
	Rules      []*rule
	Exclusions []*exclusion
}

// Targets contains the target hosts for the given base domain.
type Targets struct {
	wildcardPrefix []*regexp.Regexp
	wildcardSuffix []*regexp.Regexp

	// We use maps here to filter duplicates.
	WildcardPrefix map[string]bool
	WildcardSuffix map[string]bool
	Plain          map[string]bool

	Rules *Rules
}

// new creates a new rewrite instance from embedded GOB data with asynchronous
// loading of the rule sets to allow the caller to about around a 2 second
// delay.
func newAsync() rewrite {
	h := &https{}

	h.hostsToTargets.Store(make(map[string]*Targets))
	go func() {
		d := newDeserializer()
		temp := d.newHostsToTargets()
		h.hostsToTargets.Store(temp)
	}()

	return h.rewrite
}

// newSync creates a new rewrite instance from embedded GOB data.
func newSync() rewrite {
	h := &https{}
	d := newDeserializer()
	h.hostsToTargets.Store(d.newHostsToTargets())
	return h.rewrite
}

func (h *https) rewrite(urlStr string) (string, bool) {
	if strings.HasPrefix(urlStr, "https") {
		return urlStr, false
	}
	if !strings.HasPrefix(urlStr, "http") {
		return urlStr, false
	}
	url, root, err := extractURLAndRoot(urlStr)
	if err != nil {
		return urlStr, false
	}
	if len(root) == 0 {
		log.Error("Root is the empty string!")
		return urlStr, false
	}
	if targets, ok := h.hostsToTargets.Load().(map[string]*Targets)[root]; ok {
		return targets.rewrite(urlStr, url.Host)
	}
	return urlStr, false
}

func extractURLAndRoot(originalURL string) (*url.URL, string, error) {
	// Just normalize it as a url with the http protocol
	var urlStr string
	if !strings.HasPrefix(originalURL, "http://") {
		urlStr = "http://" + originalURL
	} else {
		urlStr = originalURL
	}
	url, err := url.Parse(urlStr)
	if err != nil {
		log.Errorf("Could not parse URL %v with error %v", urlStr, err)
		return nil, urlStr, err
	}

	host := withoutPort(url.Host)

	tld, _ := publicsuffix.PublicSuffix(host)

	// Because some TLDs such as "co.uk" include "."s, we strip the TLD prior
	// to stripping subdomains.
	noTLD := strings.TrimSuffix(host, "."+tld)
	root := stripSubdomains(noTLD)
	return url, root, nil
}

func withoutPort(hostport string) string {
	host, _, err := net.SplitHostPort(hostport)
	if err != nil {
		return hostport
	}
	return host
}

func stripSubdomains(host string) string {
	host = strings.TrimSpace(host)
	parts := strings.Split(host, ".")
	return parts[len(parts)-1]
}

func (t *Targets) rewrite(url, domain string) (string, bool) {
	// We basically want to apply the associated set of rules if any of the
	// targets match the url.
	log.Debugf("Attempting to rewrite url %v and domain %v", url, domain)
	for k := range t.Plain {
		if domain == k {
			return t.Rules.rewrite(url)
		}
	}
	if r, done := t.matchTargets(url, t.wildcardPrefix); done {
		return r, done
	}
	return t.matchTargets(url, t.wildcardSuffix)
}

func (t *Targets) matchTargets(url string, targets []*regexp.Regexp) (string, bool) {
	for _, pre := range targets {
		if pre.MatchString(url) {
			r, done := t.Rules.rewrite(url)
			if done {
				return r, done
			}
		}
	}
	return url, false
}

// rewrite converts the given URL to HTTPS if there is an associated rule for
// it.
func (r *Rules) rewrite(url string) (string, bool) {
	for _, exclude := range r.Exclusions {
		if exclude.pattern.MatchString(url) {
			return url, false
		}
	}
	for _, rule := range r.Rules {
		if rule.from.MatchString(url) {
			return rule.from.ReplaceAllString(url, rule.To), true
		}
	}
	return url, false
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 JT Olds
// See LICENSE for copying information

package whgoth // import "gopkg.in/go-webhelp/whgoth.v1"

import (
	"crypto/rand"
	"encoding/hex"
	"log"
	"net/http"
	"net/url"

	"github.com/markbates/goth"
	"github.com/spacemonkeygo/errors"
	"golang.org/x/net/context"
	"gopkg.in/webhelp.v1/whcompat"
	"gopkg.in/webhelp.v1/wherr"
	"gopkg.in/webhelp.v1/whmux"
	"gopkg.in/webhelp.v1/whredir"
	"gopkg.in/webhelp.v1/whroute"
	"gopkg.in/webhelp.v1/whsess"
)

type AuthProvider struct {
	goth.Provider
	baseURL          string
	sessionNamespace string

	whmux.Dir
}

func newAuthProvider(p goth.Provider, baseURL, sessionNamespace string) (
	a *AuthProvider) {
	a = &AuthProvider{
		Provider:         p,
		baseURL:          baseURL,
		sessionNamespace: sessionNamespace,
	}
	a.Dir = whmux.Dir{
		"login":    whmux.Exact(http.HandlerFunc(a.login)),
		"callback": whmux.Exact(http.HandlerFunc(a.callback)),
	}
	return a
}

func (a *AuthProvider) login(w http.ResponseWriter, r *http.Request) {
	ctx := whcompat.Context(r)

	u, err := a.User(ctx)
	if err == nil && u != nil {
		whredir.Redirect(w, r, safeRedirect(r.FormValue("redirect_to"), "/"))
		return
	}

	state := newState()
	sess, err := a.Provider.BeginAuth(ctx, state)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}
	for key := range session.Values {
		delete(session.Values, key)
	}
	session.Values["state"] = state
	session.Values["logged_in"] = false
	session.Values["redirect_to"] = safeRedirect(r.FormValue("redirect_to"), "/")
	session.Values["auth"] = sess.Marshal()
	err = session.Save(w)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	whredir.Redirect(w, r, url)
}

func (a *AuthProvider) Logout(ctx context.Context, w http.ResponseWriter) (
	err error) {
	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		return err
	}
	return session.Clear(w)
}

func (a *AuthProvider) callback(w http.ResponseWriter, r *http.Request) {
	ctx := whcompat.Context(r)
	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	if loggedIn, _ := session.Values["logged_in"].(bool); loggedIn {
		wherr.Handle(w, r, wherr.BadRequest.New("already logged in"))
		return
	}

	auth, ok := session.Values["auth"].(string)
	if !ok {
		wherr.Handle(w, r, wherr.InternalServerError.New(
			"no existing session found"))
		return
	}

	sess, err := a.Provider.UnmarshalSession(auth)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	authToken, err := sess.Authorize(ctx, a.Provider, r.URL.Query())
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	redirectTo, _ := session.Values["redirect_to"].(string)
	session.Values["auth_token"] = authToken
	session.Values["logged_in"] = true
	session.Values["redirect_to"] = nil
	session.Values["auth"] = sess.Marshal()
	err = session.Save(w)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	whredir.Redirect(w, r, safeRedirect(redirectTo, "/"))
}

func (a *AuthProvider) User(ctx context.Context) (*goth.User, error) {
	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		return nil, err
	}

	if loggedIn, _ := session.Values["logged_in"].(bool); !loggedIn {
		return nil, nil
	}

	auth, ok := session.Values["auth"].(string)
	if !ok {
		return nil, nil
	}

	sess, err := a.Provider.UnmarshalSession(auth)
	if err != nil {
		log.Printf("failed to unmarshal session: %v", err)
		return nil, nil
	}

	u, err := a.Provider.FetchUser(ctx, sess)
	if err != nil {
		log.Printf("failed to fetch user: %v", err)
		return nil, nil
	}

	if u.UserID == "" || u.Provider == "" {
		log.Printf("user had no user id or provider set")
		return nil, nil
	}
	return &u, nil
}

func (a *AuthProvider) LoginURL(redirectTo string) string {
	return a.baseURL + "/login?" + url.Values{
		"redirect_to": {redirectTo}}.Encode()
}

type AuthProviders struct {
	baseURL   string
	providers []*AuthProvider

	whmux.Dir
}

func NewAuthProviders(baseURL, sessionNamespace string,
	providers ...goth.Provider) (a *AuthProviders) {
	p := make([]*AuthProvider, 0, len(providers))
	pMux := make(whmux.Dir, len(providers))
	for _, provider := range providers {
		w := newAuthProvider(provider,
			baseURL+"/provider/"+provider.Name(),
			sessionNamespace+"."+provider.Name())
		p = append(p, w)
		pMux[provider.Name()] = w
	}
	a = &AuthProviders{
		baseURL:   baseURL,
		providers: p,
	}
	a.Dir = whmux.Dir{
		"logout":   whmux.Exact(http.HandlerFunc(a.logout)),
		"provider": pMux,
	}
	return a
}

func (a *AuthProviders) Logout(ctx context.Context, w http.ResponseWriter) (
	err error) {
	var errs errors.ErrorGroup
	for _, provider := range a.providers {
		errs.Add(provider.Logout(ctx, w))
	}
	return errs.Finalize()
}

func (a *AuthProviders) logout(w http.ResponseWriter, r *http.Request) {
	err := a.Logout(whcompat.Context(r), w)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}
	whredir.Redirect(w, r, safeRedirect(r.FormValue("redirect_to"), "/"))
}

func safeRedirect(redirectTo, def string) string {
	u, err := url.Parse(redirectTo)
	if err != nil {
		return def
	}
	if u.Path == "" {
		return def
	}
	return u.Path
}

func (a *AuthProviders) Providers() []*AuthProvider {
	return a.providers
}

func (a *AuthProviders) User(ctx context.Context) (*goth.User, error) {
	for _, provider := range a.providers {
		u, err := provider.User(ctx)
		if err != nil || u != nil {
			return u, err
		}
	}
	return nil, nil
}

func (a *AuthProviders) RequireUser(
	authorizedHandler, unauthorizedHandler http.Handler) http.Handler {
	return whroute.HandlerFunc(authorizedHandler,
		func(w http.ResponseWriter, r *http.Request) {
			u, err := a.User(whcompat.Context(r))
			if err != nil {
				wherr.Handle(w, r, err)
				return
			}
			if u == nil {
				unauthorizedHandler.ServeHTTP(w, r)
			} else {
				authorizedHandler.ServeHTTP(w, r)
			}
		})
}

func (a *AuthProviders) LogoutURL(redirectTo string) string {
	return a.baseURL + "/logout?" + url.Values{
		"redirect_to": {redirectTo}}.Encode()
}

func newState() string {
	var p [16]byte
	_, err := rand.Read(p[:])
	if err != nil {
		panic(err)
	}
	return hex.EncodeToString(p[:])
}
<commit_msg>pull request update (https://github.com/markbates/goth/pull/113)<commit_after>// Copyright (C) 2016 JT Olds
// See LICENSE for copying information

package whgoth // import "gopkg.in/go-webhelp/whgoth.v1"

import (
	"crypto/rand"
	"encoding/hex"
	"log"
	"net/http"
	"net/url"

	"github.com/markbates/goth"
	"github.com/spacemonkeygo/errors"
	"golang.org/x/net/context"
	"gopkg.in/webhelp.v1/whcompat"
	"gopkg.in/webhelp.v1/wherr"
	"gopkg.in/webhelp.v1/whmux"
	"gopkg.in/webhelp.v1/whredir"
	"gopkg.in/webhelp.v1/whroute"
	"gopkg.in/webhelp.v1/whsess"
)

type AuthProvider struct {
	goth.Provider
	baseURL          string
	sessionNamespace string

	whmux.Dir
}

func newAuthProvider(p goth.Provider, baseURL, sessionNamespace string) (
	a *AuthProvider) {
	a = &AuthProvider{
		Provider:         p,
		baseURL:          baseURL,
		sessionNamespace: sessionNamespace,
	}
	a.Dir = whmux.Dir{
		"login":    whmux.Exact(http.HandlerFunc(a.login)),
		"callback": whmux.Exact(http.HandlerFunc(a.callback)),
	}
	return a
}

func (a *AuthProvider) login(w http.ResponseWriter, r *http.Request) {
	ctx := whcompat.Context(r)

	u, err := a.User(ctx)
	if err == nil && u != nil {
		whredir.Redirect(w, r, safeRedirect(r.FormValue("redirect_to"), "/"))
		return
	}

	state := newState()
	sess, err := a.Provider.BeginAuthCtx(ctx, state)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}
	for key := range session.Values {
		delete(session.Values, key)
	}
	session.Values["state"] = state
	session.Values["logged_in"] = false
	session.Values["redirect_to"] = safeRedirect(r.FormValue("redirect_to"), "/")
	session.Values["auth"] = sess.Marshal()
	err = session.Save(w)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	whredir.Redirect(w, r, url)
}

func (a *AuthProvider) Logout(ctx context.Context, w http.ResponseWriter) (
	err error) {
	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		return err
	}
	return session.Clear(w)
}

func (a *AuthProvider) callback(w http.ResponseWriter, r *http.Request) {
	ctx := whcompat.Context(r)
	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	if loggedIn, _ := session.Values["logged_in"].(bool); loggedIn {
		wherr.Handle(w, r, wherr.BadRequest.New("already logged in"))
		return
	}

	auth, ok := session.Values["auth"].(string)
	if !ok {
		wherr.Handle(w, r, wherr.InternalServerError.New(
			"no existing session found"))
		return
	}

	sess, err := a.Provider.UnmarshalSession(auth)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	authToken, err := sess.AuthorizeCtx(ctx, a.Provider, r.URL.Query())
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	redirectTo, _ := session.Values["redirect_to"].(string)
	session.Values["auth_token"] = authToken
	session.Values["logged_in"] = true
	session.Values["redirect_to"] = nil
	session.Values["auth"] = sess.Marshal()
	err = session.Save(w)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}

	whredir.Redirect(w, r, safeRedirect(redirectTo, "/"))
}

func (a *AuthProvider) User(ctx context.Context) (*goth.User, error) {
	session, err := whsess.Load(ctx, a.sessionNamespace)
	if err != nil {
		return nil, err
	}

	if loggedIn, _ := session.Values["logged_in"].(bool); !loggedIn {
		return nil, nil
	}

	auth, ok := session.Values["auth"].(string)
	if !ok {
		return nil, nil
	}

	sess, err := a.Provider.UnmarshalSession(auth)
	if err != nil {
		log.Printf("failed to unmarshal session: %v", err)
		return nil, nil
	}

	u, err := a.Provider.FetchUserCtx(ctx, sess)
	if err != nil {
		log.Printf("failed to fetch user: %v", err)
		return nil, nil
	}

	if u.UserID == "" || u.Provider == "" {
		log.Printf("user had no user id or provider set")
		return nil, nil
	}
	return &u, nil
}

func (a *AuthProvider) LoginURL(redirectTo string) string {
	return a.baseURL + "/login?" + url.Values{
		"redirect_to": {redirectTo}}.Encode()
}

type AuthProviders struct {
	baseURL   string
	providers []*AuthProvider

	whmux.Dir
}

func NewAuthProviders(baseURL, sessionNamespace string,
	providers ...goth.Provider) (a *AuthProviders) {
	p := make([]*AuthProvider, 0, len(providers))
	pMux := make(whmux.Dir, len(providers))
	for _, provider := range providers {
		w := newAuthProvider(provider,
			baseURL+"/provider/"+provider.Name(),
			sessionNamespace+"."+provider.Name())
		p = append(p, w)
		pMux[provider.Name()] = w
	}
	a = &AuthProviders{
		baseURL:   baseURL,
		providers: p,
	}
	a.Dir = whmux.Dir{
		"logout":   whmux.Exact(http.HandlerFunc(a.logout)),
		"provider": pMux,
	}
	return a
}

func (a *AuthProviders) Logout(ctx context.Context, w http.ResponseWriter) (
	err error) {
	var errs errors.ErrorGroup
	for _, provider := range a.providers {
		errs.Add(provider.Logout(ctx, w))
	}
	return errs.Finalize()
}

func (a *AuthProviders) logout(w http.ResponseWriter, r *http.Request) {
	err := a.Logout(whcompat.Context(r), w)
	if err != nil {
		wherr.Handle(w, r, err)
		return
	}
	whredir.Redirect(w, r, safeRedirect(r.FormValue("redirect_to"), "/"))
}

func safeRedirect(redirectTo, def string) string {
	u, err := url.Parse(redirectTo)
	if err != nil {
		return def
	}
	if u.Path == "" {
		return def
	}
	return u.Path
}

func (a *AuthProviders) Providers() []*AuthProvider {
	return a.providers
}

func (a *AuthProviders) User(ctx context.Context) (*goth.User, error) {
	for _, provider := range a.providers {
		u, err := provider.User(ctx)
		if err != nil || u != nil {
			return u, err
		}
	}
	return nil, nil
}

func (a *AuthProviders) RequireUser(
	authorizedHandler, unauthorizedHandler http.Handler) http.Handler {
	return whroute.HandlerFunc(authorizedHandler,
		func(w http.ResponseWriter, r *http.Request) {
			u, err := a.User(whcompat.Context(r))
			if err != nil {
				wherr.Handle(w, r, err)
				return
			}
			if u == nil {
				unauthorizedHandler.ServeHTTP(w, r)
			} else {
				authorizedHandler.ServeHTTP(w, r)
			}
		})
}

func (a *AuthProviders) LogoutURL(redirectTo string) string {
	return a.baseURL + "/logout?" + url.Values{
		"redirect_to": {redirectTo}}.Encode()
}

func newState() string {
	var p [16]byte
	_, err := rand.Read(p[:])
	if err != nil {
		panic(err)
	}
	return hex.EncodeToString(p[:])
}
<|endoftext|>


<commit_before>package gpio

type GPIO interface {
	MakeOutput() error
	MakeInput() error
	WriteValue(int) error
	ReadValue() (int, error)
	Close() error
}
<commit_msg>Add Interrupt to GPIO<commit_after>package gpio

import "time"

type InterruptEvent struct {
	value int
	timestamp time.Time
}

type GPIO interface {
	MakeOutput() error
	MakeInput() error
	WriteValue(int) error
	ReadValue() (int, error)
	Interrupt(string, chan InterruptEvent) error
	Close() error
}
<|endoftext|>


<commit_before>package listener

import (
	"bytes"
	"net/http"
	"testing"

	"github.com/cep21/gohelpers/workarounds"
	"github.com/signalfuse/signalfxproxy/config"
	"github.com/signalfuse/signalfxproxy/core"
	"github.com/stretchr/testify/assert"
)

const testCollectdBody = `[
    {
        "dsnames": [
            "shortterm",
            "midterm",
            "longterm"
        ],
        "dstypes": [
            "gauge",
            "gauge",
            "gauge"
        ],
        "host": "i-b13d1e5f",
        "interval": 10.0,
        "plugin": "load",
        "plugin_instance": "",
        "time": 1415062577.4960001,
        "type": "load",
        "type_instance": "",
        "values": [
            0.37,
            0.60999999999999999,
            0.76000000000000001
        ]
    },
    {
        "dsnames": [
            "value"
        ],
        "dstypes": [
            "gauge"
        ],
        "host": "i-b13d1e5f",
        "interval": 10.0,
        "plugin": "memory",
        "plugin_instance": "",
        "time": 1415062577.4960001,
        "type": "memory",
        "type_instance": "used",
        "values": [
            1524310000.0
        ]
    },
    {
        "dsnames": [
            "value"
        ],
        "dstypes": [
            "derive"
        ],
        "host": "i-b13d1e5f",
        "interval": 10.0,
        "plugin": "df",
        "plugin_instance": "dev",
        "time": 1415062577.4949999,
        "type": "df_complex",
        "type_instance": "free",
        "values": [
            1962600000.0
        ]
    }
]`

func TestInvalidListen(t *testing.T) {
	listenFrom := &config.ListenFrom{
		ListenAddr: workarounds.GolangDoesnotAllowPointerToStringLiteral("127.0.0.1:999999"),
	}
	sendTo := &basicDatapointStreamingAPI{}
	_, err := CollectdListenerLoader(sendTo, listenFrom)
	assert.Error(t, err)
}

func TestCollectdListenerInvalidJSONEngine(t *testing.T) {
	sendTo := &basicDatapointStreamingAPI{
		channel: make(chan core.Datapoint),
	}
	listenFrom := &config.ListenFrom{
		JSONEngine: workarounds.GolangDoesnotAllowPointerToStringLiteral("unknown"),
	}
	_, err := CollectdListenerLoader(sendTo, listenFrom)
	assert.Error(t, err)
}

func TestCollectDListener(t *testing.T) {
	jsonBody := testCollectdBody

	sendTo := &basicDatapointStreamingAPI{
		channel: make(chan core.Datapoint),
	}
	listenFrom := &config.ListenFrom{}
	collectdListener, err := CollectdListenerLoader(sendTo, listenFrom)
	defer collectdListener.Close()
	assert.Nil(t, err)
	assert.NotNil(t, collectdListener)

	req, _ := http.NewRequest("POST", "http://127.0.0.1:8081/post-collectd", bytes.NewBuffer([]byte(jsonBody)))
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	go func() {
		dp := <-sendTo.channel
		assert.Equal(t, "load.shortterm", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "load.midterm", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "load.longterm", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "memory.used", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "df_complex.free", dp.Metric(), "Metric not named correctly")
	}()
	resp, err := client.Do(req)
	assert.Nil(t, err)
	assert.Equal(t, resp.StatusCode, 200, "Request should work")

	assert.Equal(t, 4, len(collectdListener.GetStats()), "Request should work")

	req, _ = http.NewRequest("POST", "http://127.0.0.1:8081/post-collectd", bytes.NewBuffer([]byte(`invalidjson`)))
	req.Header.Set("Content-Type", "application/json")
	resp, err = client.Do(req)
	assert.Nil(t, err)
	assert.Equal(t, resp.StatusCode, 400, "Request should work")

	req, _ = http.NewRequest("POST", "http://127.0.0.1:8081/post-collectd", bytes.NewBuffer([]byte(jsonBody)))
	req.Header.Set("Content-Type", "application/plaintext")
	resp, err = client.Do(req)
	assert.Nil(t, err)
	assert.Equal(t, resp.StatusCode, 400, "Request should work (Plaintext not supported)")

}
<commit_msg>collectd benchmark<commit_after>package listener

import (
	"bytes"
	"net/http"
	"testing"

	"net/http/httptest"
	"strings"

	"github.com/cep21/gohelpers/workarounds"
	"github.com/signalfuse/signalfxproxy/config"
	"github.com/signalfuse/signalfxproxy/core"
	"github.com/signalfuse/signalfxproxy/jsonengines"
	"github.com/stretchr/testify/assert"
)

const testCollectdBody = `[
    {
        "dsnames": [
            "shortterm",
            "midterm",
            "longterm"
        ],
        "dstypes": [
            "gauge",
            "gauge",
            "gauge"
        ],
        "host": "i-b13d1e5f",
        "interval": 10.0,
        "plugin": "load",
        "plugin_instance": "",
        "time": 1415062577.4960001,
        "type": "load",
        "type_instance": "",
        "values": [
            0.37,
            0.60999999999999999,
            0.76000000000000001
        ]
    },
    {
        "dsnames": [
            "value"
        ],
        "dstypes": [
            "gauge"
        ],
        "host": "i-b13d1e5f",
        "interval": 10.0,
        "plugin": "memory",
        "plugin_instance": "",
        "time": 1415062577.4960001,
        "type": "memory",
        "type_instance": "used",
        "values": [
            1524310000.0
        ]
    },
    {
        "dsnames": [
            "value"
        ],
        "dstypes": [
            "derive"
        ],
        "host": "i-b13d1e5f",
        "interval": 10.0,
        "plugin": "df",
        "plugin_instance": "dev",
        "time": 1415062577.4949999,
        "type": "df_complex",
        "type_instance": "free",
        "values": [
            1962600000.0
        ]
    }
]`

func TestInvalidListen(t *testing.T) {
	listenFrom := &config.ListenFrom{
		ListenAddr: workarounds.GolangDoesnotAllowPointerToStringLiteral("127.0.0.1:999999"),
	}
	sendTo := &basicDatapointStreamingAPI{}
	_, err := CollectdListenerLoader(sendTo, listenFrom)
	assert.Error(t, err)
}

func TestCollectdListenerInvalidJSONEngine(t *testing.T) {
	sendTo := &basicDatapointStreamingAPI{
		channel: make(chan core.Datapoint),
	}
	listenFrom := &config.ListenFrom{
		JSONEngine: workarounds.GolangDoesnotAllowPointerToStringLiteral("unknown"),
	}
	_, err := CollectdListenerLoader(sendTo, listenFrom)
	assert.Error(t, err)
}

func TestCollectDListener(t *testing.T) {
	jsonBody := testCollectdBody

	sendTo := &basicDatapointStreamingAPI{
		channel: make(chan core.Datapoint),
	}
	listenFrom := &config.ListenFrom{}
	collectdListener, err := CollectdListenerLoader(sendTo, listenFrom)
	defer collectdListener.Close()
	assert.Nil(t, err)
	assert.NotNil(t, collectdListener)

	req, _ := http.NewRequest("POST", "http://127.0.0.1:8081/post-collectd", bytes.NewBuffer([]byte(jsonBody)))
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	go func() {
		dp := <-sendTo.channel
		assert.Equal(t, "load.shortterm", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "load.midterm", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "load.longterm", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "memory.used", dp.Metric(), "Metric not named correctly")

		dp = <-sendTo.channel
		assert.Equal(t, "df_complex.free", dp.Metric(), "Metric not named correctly")
	}()
	resp, err := client.Do(req)
	assert.Nil(t, err)
	assert.Equal(t, resp.StatusCode, 200, "Request should work")

	assert.Equal(t, 4, len(collectdListener.GetStats()), "Request should work")

	req, _ = http.NewRequest("POST", "http://127.0.0.1:8081/post-collectd", bytes.NewBuffer([]byte(`invalidjson`)))
	req.Header.Set("Content-Type", "application/json")
	resp, err = client.Do(req)
	assert.Nil(t, err)
	assert.Equal(t, resp.StatusCode, 400, "Request should work")

	req, _ = http.NewRequest("POST", "http://127.0.0.1:8081/post-collectd", bytes.NewBuffer([]byte(jsonBody)))
	req.Header.Set("Content-Type", "application/plaintext")
	resp, err = client.Do(req)
	assert.Nil(t, err)
	assert.Equal(t, resp.StatusCode, 400, "Request should work (Plaintext not supported)")

}

func BenchmarkCollectdListener(b *testing.B) {
	bytes := int64(0)

	jsonEngine, _ := jsonengines.Load("")

	for i := 0; i < b.N; i++ {
		sendTo := &basicDatapointStreamingAPI{
			channel: make(chan core.Datapoint, 6),
		}

		listener := &collectdListenerServer{
			datapointStreamingAPI: sendTo,
			decodingEngine:        jsonEngine,
		}
		writter := httptest.NewRecorder()
		body := strings.NewReader(testCollectdBody)
		req, err := http.NewRequest("GET", "http://example.com/collectd", body)
		req.Header.Add("Content-type", "application/json")
		bytes += int64(len(testCollectdBody))
		listener.handleCollectd(writter, req)
		assert.NoError(b, err)
		assert.Equal(b, 5, len(sendTo.channel))

	}
	b.SetBytes(bytes)
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"

	"github.com/fvbock/endless"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
)

/*
HTTPListener is a structure which provides an HTTP listener to service
requests. This structure offers methods to add routes and middlewares. Typical
usage would first call NewHTTPListener(), add routes, then call
StartHTTPListener.
*/
type HTTPListener struct {
	Address string
	Port    int

	Router                 *mux.Router
	BaseMiddlewareHandlers alice.Chain
}

/*
NewHTTPListener creates a new instance of the HTTPListener
*/
func NewHTTPListener(address string, port int) *HTTPListener {
	return &HTTPListener{
		Address: address,
		Port:    port,
		Router:  mux.NewRouter(),
	}
}

/*
AddMiddleware adds a new middleware handler to the request chain.
*/
func (listener *HTTPListener) AddMiddleware(middlewareHandler alice.Constructor) *HTTPListener {
	listener.BaseMiddlewareHandlers = listener.BaseMiddlewareHandlers.Append(middlewareHandler)
	return listener
}

/*
AddRoute adds a HTTP handler route to the HTTP listener.
*/
func (listener *HTTPListener) AddRoute(path string, handlerFunc http.HandlerFunc, methods ...string) *HTTPListener {
	listener.Router.Handle(path, listener.BaseMiddlewareHandlers.ThenFunc(handlerFunc)).Methods(methods...)
	return listener
}

/*
AddRouteWithMiddleware adds a HTTP handler route that goes through an additional
middleware handler, to the HTTP listener. This is particularly useful
for setting up routes that require authentication.
*/
func (listener *HTTPListener) AddRouteWithMiddleware(path string, handlerFunc http.HandlerFunc, middlewareHandler alice.Constructor, methods ...string) *HTTPListener {
	listener.Router.Handle(
		path,
		listener.BaseMiddlewareHandlers.Append(middlewareHandler).ThenFunc(handlerFunc),
	).Methods(methods...)

	return listener
}

/*
AddStaticRoute adds a HTTP handler route for static assets. Note that this does not work
with tools that embed assets in your executable. To do that you would need to setup
your own static file handler manually.
*/
func (listener *HTTPListener) AddStaticRoute(pathPrefix string, directory string) *HTTPListener {
	fileServer := http.FileServer(http.Dir(directory))
	listener.Router.PathPrefix(pathPrefix).Handler(http.StripPrefix(pathPrefix, fileServer))

	return listener
}

/*
Start starts the HTTP listener and servicing requests.
*/
func (listener *HTTPListener) Start() error {
	httpServer := endless.NewServer(fmt.Sprintf("%s:%d", listener.Address, listener.Port), alice.New().Then(listener.Router))
	return httpServer.ListenAndServe()
}

/*
StartTLS starts the HTTP listener with SSL and services requests
*/
func (listener *HTTPListener) StartTLS(certificateFile, keyFile string) error {
	httpServer := endless.NewServer(fmt.Sprintf("%s:%d", listener.Address, listener.Port), alice.New().Then(listener.Router))
	return httpServer.ListenAndServeTLS(certificateFile, keyFile)
}
<commit_msg>Removed endless. Had trouble on Windows<commit_after>package server

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/justinas/alice"
)

/*
HTTPListener is a structure which provides an HTTP listener to service
requests. This structure offers methods to add routes and middlewares. Typical
usage would first call NewHTTPListener(), add routes, then call
StartHTTPListener.
*/
type HTTPListener struct {
	Address string
	Port    int

	Router                 *mux.Router
	BaseMiddlewareHandlers alice.Chain
}

/*
NewHTTPListener creates a new instance of the HTTPListener
*/
func NewHTTPListener(address string, port int) *HTTPListener {
	return &HTTPListener{
		Address: address,
		Port:    port,
		Router:  mux.NewRouter(),
	}
}

/*
AddMiddleware adds a new middleware handler to the request chain.
*/
func (listener *HTTPListener) AddMiddleware(middlewareHandler alice.Constructor) *HTTPListener {
	listener.BaseMiddlewareHandlers = listener.BaseMiddlewareHandlers.Append(middlewareHandler)
	return listener
}

/*
AddRoute adds a HTTP handler route to the HTTP listener.
*/
func (listener *HTTPListener) AddRoute(path string, handlerFunc http.HandlerFunc, methods ...string) *HTTPListener {
	listener.Router.Handle(path, listener.BaseMiddlewareHandlers.ThenFunc(handlerFunc)).Methods(methods...)
	return listener
}

/*
AddRouteWithMiddleware adds a HTTP handler route that goes through an additional
middleware handler, to the HTTP listener. This is particularly useful
for setting up routes that require authentication.
*/
func (listener *HTTPListener) AddRouteWithMiddleware(path string, handlerFunc http.HandlerFunc, middlewareHandler alice.Constructor, methods ...string) *HTTPListener {
	listener.Router.Handle(
		path,
		listener.BaseMiddlewareHandlers.Append(middlewareHandler).ThenFunc(handlerFunc),
	).Methods(methods...)

	return listener
}

/*
AddStaticRoute adds a HTTP handler route for static assets. Note that this does not work
with tools that embed assets in your executable. To do that you would need to setup
your own static file handler manually.
*/
func (listener *HTTPListener) AddStaticRoute(pathPrefix string, directory string) *HTTPListener {
	fileServer := http.FileServer(http.Dir(directory))
	listener.Router.PathPrefix(pathPrefix).Handler(http.StripPrefix(pathPrefix, fileServer))

	return listener
}

/*
Start starts the HTTP listener and servicing requests.
*/
func (listener *HTTPListener) Start() error {
	httpServer := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", listener.Address, listener.Port),
		Handler: alice.New().Then(listener.Router),
	}

	return httpServer.ListenAndServe()
}

/*
StartTLS starts the HTTP listener with SSL and services requests
*/
func (listener *HTTPListener) StartTLS(certificateFile, keyFile string) error {
	httpServer := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", listener.Address, listener.Port),
		Handler: alice.New().Then(listener.Router),
	}

	return httpServer.ListenAndServeTLS(certificateFile, keyFile)
}
<|endoftext|>


<commit_before>package dashboard_graph

import (
	"fmt"
	"sort"
	"strings"

	h "github.com/Cepave/open-falcon-backend/modules/f2e-api/app/helper"
	m "github.com/Cepave/open-falcon-backend/modules/f2e-api/app/model/dashboard"
	"github.com/gin-gonic/gin"
)

type APIGraphCreateReqDataWithNewScreenInputs struct {
	ScreenName string   `json:"screen_name" form:"screen_name" binding:"required"`
	Title      string   `json:"title" form:"title" binding:"required"`
	Endpoints  []string `json:"endpoints" form:"endpoints" binding:"required"`
	Counters   []string `json:"counters" form:"counters" binding:"required"`
	TimeSpan   int64    `json:"timespan" form:"timespan"`
	GraphType  string   `json:"graph_type" form:"graph_type" binding:"required"`
	Method     string   `json:"method" form:"method"`
	Position   int64    `json:"position" form:"position"`
	FalconTags string   `json:"falcon_tags" form:"falcon_tags"`
}

func (mine APIGraphCreateReqDataWithNewScreenInputs) Check() (err error) {
	sc := m.DashboardScreen{Name: mine.ScreenName}
	// check screen_id
	if sc.ExistName() {
		err = fmt.Errorf("screen name:%v already existing", mine.ScreenName)
		return
	}

	if mine.TimeSpan%60 != 0 {
		err = fmt.Errorf("value of timespan is not vaild: %v", mine.TimeSpan)
		return
	}

	if mine.GraphType != "h" && mine.GraphType != "k" && mine.GraphType != "a" {
		err = fmt.Errorf("value of graph_type only accpet 'k' or 'h' or 'a', you typed: %v", mine.GraphType)
		return
	}
	return
}

func GraphCreateReqDataWithNewScreen(c *gin.Context) {
	inputs := APIGraphCreateReqDataWithNewScreenInputs{}
	// set default value
	inputs.TimeSpan = 3600
	inputs.GraphType = "h"
	if err := c.Bind(&inputs); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}

	if err := inputs.Check(); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}

	dt := db.Dashboard.Begin()
	sc := m.DashboardScreen{Name: inputs.ScreenName}
	dt = dt.Save(&sc)
	if dt.Error != nil {
		h.JSONR(c, badstatus, dt.Error)
		dt.Rollback()
		return
	}

	es := inputs.Endpoints
	cs := inputs.Counters
	sort.Strings(es)
	sort.Strings(cs)
	esString := strings.Join(es, TMP_GRAPH_FILED_DELIMITER)
	csString := strings.Join(cs, TMP_GRAPH_FILED_DELIMITER)
	user, _ := h.GetUser(c)

	d := m.DashboardGraph{
		Title:     inputs.Title,
		Hosts:     esString,
		Counters:  csString,
		ScreenId:  sc.ID,
		TimeSpan:  inputs.TimeSpan,
		GraphType: inputs.GraphType,
		Method:    inputs.Method,
		Position:  inputs.Position,
		Creator:   user.Name,
	}
	dt = dt.Save(&d)
	if dt.Error != nil {
		dt.Rollback()
		h.JSONR(c, badstatus, dt.Error)
		return
	}

	var lid []int
	dt = dt.Table(d.TableName()).Raw("select LAST_INSERT_ID() as id").Pluck("id", &lid)
	if dt.Error != nil {
		dt.Rollback()
		h.JSONR(c, badstatus, dt.Error)
		return
	}
	dt.Commit()
	aid := lid[0]

	h.JSONR(c, map[string]interface{}{"id": aid, "screen_id": d.ScreenId, "screen_name": inputs.ScreenName})
}
<commit_msg>fix bug of missing add creator info on create graph with new screen<commit_after>package dashboard_graph

import (
	"fmt"
	"sort"
	"strings"

	h "github.com/Cepave/open-falcon-backend/modules/f2e-api/app/helper"
	m "github.com/Cepave/open-falcon-backend/modules/f2e-api/app/model/dashboard"
	"github.com/gin-gonic/gin"
)

type APIGraphCreateReqDataWithNewScreenInputs struct {
	ScreenName string   `json:"screen_name" form:"screen_name" binding:"required"`
	Title      string   `json:"title" form:"title" binding:"required"`
	Endpoints  []string `json:"endpoints" form:"endpoints" binding:"required"`
	Counters   []string `json:"counters" form:"counters" binding:"required"`
	TimeSpan   int64    `json:"timespan" form:"timespan"`
	GraphType  string   `json:"graph_type" form:"graph_type" binding:"required"`
	Method     string   `json:"method" form:"method"`
	Position   int64    `json:"position" form:"position"`
	FalconTags string   `json:"falcon_tags" form:"falcon_tags"`
}

func (mine APIGraphCreateReqDataWithNewScreenInputs) Check() (err error) {
	sc := m.DashboardScreen{Name: mine.ScreenName}
	// check screen_id
	if sc.ExistName() {
		err = fmt.Errorf("screen name:%v already existing", mine.ScreenName)
		return
	}

	if mine.TimeSpan%60 != 0 {
		err = fmt.Errorf("value of timespan is not vaild: %v", mine.TimeSpan)
		return
	}

	if mine.GraphType != "h" && mine.GraphType != "k" && mine.GraphType != "a" {
		err = fmt.Errorf("value of graph_type only accpet 'k' or 'h' or 'a', you typed: %v", mine.GraphType)
		return
	}
	return
}

func GraphCreateReqDataWithNewScreen(c *gin.Context) {
	inputs := APIGraphCreateReqDataWithNewScreenInputs{}
	// set default value
	inputs.TimeSpan = 3600
	inputs.GraphType = "h"
	if err := c.Bind(&inputs); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}

	if err := inputs.Check(); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}

	user, err := h.GetUser(c)
	if err != nil {
		h.JSONR(c, badstatus, err)
		return
	}
	dt := db.Dashboard.Begin()
	sc := m.DashboardScreen{Name: inputs.ScreenName, Creator: user.Name}
	dt = dt.Save(&sc)
	if dt.Error != nil {
		h.JSONR(c, badstatus, dt.Error)
		dt.Rollback()
		return
	}

	es := inputs.Endpoints
	cs := inputs.Counters
	sort.Strings(es)
	sort.Strings(cs)
	esString := strings.Join(es, TMP_GRAPH_FILED_DELIMITER)
	csString := strings.Join(cs, TMP_GRAPH_FILED_DELIMITER)

	d := m.DashboardGraph{
		Title:     inputs.Title,
		Hosts:     esString,
		Counters:  csString,
		ScreenId:  sc.ID,
		TimeSpan:  inputs.TimeSpan,
		GraphType: inputs.GraphType,
		Method:    inputs.Method,
		Position:  inputs.Position,
		Creator:   user.Name,
	}
	dt = dt.Save(&d)
	if dt.Error != nil {
		dt.Rollback()
		h.JSONR(c, badstatus, dt.Error)
		return
	}

	var lid []int
	dt = dt.Table(d.TableName()).Raw("select LAST_INSERT_ID() as id").Pluck("id", &lid)
	if dt.Error != nil {
		dt.Rollback()
		h.JSONR(c, badstatus, dt.Error)
		return
	}
	dt.Commit()
	aid := lid[0]

	h.JSONR(c, map[string]interface{}{"id": aid, "screen_id": d.ScreenId, "screen_name": inputs.ScreenName})
}
<|endoftext|>