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

// Package prometheus provides bindings to the Prometheus HTTP API:
// http://prometheus.io/docs/querying/api/
package prometheus

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/common/model"
	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

const (
	statusAPIError = 422
	apiPrefix      = "/api/v1"

	epQuery       = "/query"
	epQueryRange  = "/query_range"
	epLabelValues = "/label/:name/values"
	epSeries      = "/series"
)

// ErrorType models the different API error types.
type ErrorType string

// Possible values for ErrorType.
const (
	ErrBadData     ErrorType = "bad_data"
	ErrTimeout               = "timeout"
	ErrCanceled              = "canceled"
	ErrExec                  = "execution"
	ErrBadResponse           = "bad_response"
)

// Error is an error returned by the API.
type Error struct {
	Type ErrorType
	Msg  string
}

func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
}

// CancelableTransport is like net.Transport but provides
// per-request cancelation functionality.
type CancelableTransport interface {
	http.RoundTripper
	CancelRequest(req *http.Request)
}

// DefaultTransport is used if no Transport is set in Config.
var DefaultTransport CancelableTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
}

// Config defines configuration parameters for a new client.
type Config struct {
	// The address of the Prometheus to connect to.
	Address string

	// Transport is used by the Client to drive HTTP requests. If not
	// provided, DefaultTransport will be used.
	Transport CancelableTransport
}

func (cfg *Config) transport() CancelableTransport {
	if cfg.Transport == nil {
		return DefaultTransport
	}
	return cfg.Transport
}

// Client is the interface for an API client.
type Client interface {
	url(ep string, args map[string]string) *url.URL
	do(context.Context, *http.Request) (*http.Response, []byte, error)
}

// New returns a new Client.
//
// It is safe to use the returned Client from multiple goroutines.
func New(cfg Config) (Client, error) {
	u, err := url.Parse(cfg.Address)
	if err != nil {
		return nil, err
	}
	u.Path = strings.TrimRight(u.Path, "/") + apiPrefix

	return &httpClient{
		endpoint:  u,
		transport: cfg.transport(),
	}, nil
}

type httpClient struct {
	endpoint  *url.URL
	transport CancelableTransport
}

func (c *httpClient) url(ep string, args map[string]string) *url.URL {
	p := path.Join(c.endpoint.Path, ep)

	for arg, val := range args {
		arg = ":" + arg
		p = strings.Replace(p, arg, val, -1)
	}

	u := *c.endpoint
	u.Path = p

	return &u
}

func (c *httpClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, err := ctxhttp.Do(ctx, &http.Client{Transport: c.transport}, req)

	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, nil, err
	}

	var body []byte
	done := make(chan struct{})
	go func() {
		body, err = ioutil.ReadAll(resp.Body)
		close(done)
	}()

	select {
	case <-ctx.Done():
		err = resp.Body.Close()
		<-done
		if err == nil {
			err = ctx.Err()
		}
	case <-done:
	}

	return resp, body, err
}

// apiClient wraps a regular client and processes successful API responses.
// Successful also includes responses that errored at the API level.
type apiClient struct {
	Client
}

type apiResponse struct {
	Status    string          `json:"status"`
	Data      json.RawMessage `json:"data"`
	ErrorType ErrorType       `json:"errorType"`
	Error     string          `json:"error"`
}

func (c apiClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, body, err := c.Client.do(ctx, req)
	if err != nil {
		return resp, body, err
	}

	code := resp.StatusCode

	if code/100 != 2 && code != statusAPIError {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  fmt.Sprintf("bad response code %d", resp.StatusCode),
		}
	}

	var result apiResponse

	if err = json.Unmarshal(body, &result); err != nil {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  err.Error(),
		}
	}

	if (code == statusAPIError) != (result.Status == "error") {
		err = &Error{
			Type: ErrBadResponse,
			Msg:  "inconsistent body for response code",
		}
	}

	if code == statusAPIError && result.Status == "error" {
		err = &Error{
			Type: result.ErrorType,
			Msg:  result.Error,
		}
	}

	return resp, []byte(result.Data), err
}

// Range represents a sliced time range.
type Range struct {
	// The boundaries of the time range.
	Start, End time.Time
	// The maximum time between two slices within the boundaries.
	Step time.Duration
}

// queryResult contains result data for a query.
type queryResult struct {
	Type   model.ValueType `json:"resultType"`
	Result interface{}     `json:"result"`

	// The decoded value.
	v model.Value
}

func (qr *queryResult) UnmarshalJSON(b []byte) error {
	v := struct {
		Type   model.ValueType `json:"resultType"`
		Result json.RawMessage `json:"result"`
	}{}

	err := json.Unmarshal(b, &v)
	if err != nil {
		return err
	}

	switch v.Type {
	case model.ValScalar:
		var sv model.Scalar
		err = json.Unmarshal(v.Result, &sv)
		qr.v = &sv

	case model.ValVector:
		var vv model.Vector
		err = json.Unmarshal(v.Result, &vv)
		qr.v = vv

	case model.ValMatrix:
		var mv model.Matrix
		err = json.Unmarshal(v.Result, &mv)
		qr.v = mv

	default:
		err = fmt.Errorf("unexpected value type %q", v.Type)
	}
	return err
}

// QueryAPI provides bindings the Prometheus's query API.
type QueryAPI interface {
	// Query performs a query for the given time.
	Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
	// QueryRange performs a query for the given range.
	QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
	// QueryLabelValues performs a query for the values of the given label.
	LabelValues(ctx context.Context, label string) (model.LabelValues, error)
}

// NewQueryAPI returns a new QueryAPI for the client.
//
// It is safe to use the returned QueryAPI from multiple goroutines.
func NewQueryAPI(c Client) QueryAPI {
	return &httpQueryAPI{client: apiClient{c}}
}

type httpQueryAPI struct {
	client Client
}

func (h *httpQueryAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
	u := h.client.url(epQuery, nil)
	q := u.Query()

	q.Set("query", query)
	q.Set("time", ts.Format(time.RFC3339Nano))

	u.RawQuery = q.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
	u := h.client.url(epQueryRange, nil)
	q := u.Query()

	var (
		start = r.Start.Format(time.RFC3339Nano)
		end   = r.End.Format(time.RFC3339Nano)
		step  = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
	)

	q.Set("query", query)
	q.Set("start", start)
	q.Set("end", end)
	q.Set("step", step)

	u.RawQuery = q.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, error) {
	u := h.client.url(epLabelValues, map[string]string{"name": label})
	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}
	var labelValues model.LabelValues
	err = json.Unmarshal(body, &labelValues)
	return labelValues, err
}
<commit_msg>api: fix godoc comment<commit_after>// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package prometheus provides bindings to the Prometheus HTTP API:
// http://prometheus.io/docs/querying/api/
package prometheus

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/common/model"
	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

const (
	statusAPIError = 422
	apiPrefix      = "/api/v1"

	epQuery       = "/query"
	epQueryRange  = "/query_range"
	epLabelValues = "/label/:name/values"
	epSeries      = "/series"
)

// ErrorType models the different API error types.
type ErrorType string

// Possible values for ErrorType.
const (
	ErrBadData     ErrorType = "bad_data"
	ErrTimeout               = "timeout"
	ErrCanceled              = "canceled"
	ErrExec                  = "execution"
	ErrBadResponse           = "bad_response"
)

// Error is an error returned by the API.
type Error struct {
	Type ErrorType
	Msg  string
}

func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
}

// CancelableTransport is like net.Transport but provides
// per-request cancelation functionality.
type CancelableTransport interface {
	http.RoundTripper
	CancelRequest(req *http.Request)
}

// DefaultTransport is used if no Transport is set in Config.
var DefaultTransport CancelableTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
}

// Config defines configuration parameters for a new client.
type Config struct {
	// The address of the Prometheus to connect to.
	Address string

	// Transport is used by the Client to drive HTTP requests. If not
	// provided, DefaultTransport will be used.
	Transport CancelableTransport
}

func (cfg *Config) transport() CancelableTransport {
	if cfg.Transport == nil {
		return DefaultTransport
	}
	return cfg.Transport
}

// Client is the interface for an API client.
type Client interface {
	url(ep string, args map[string]string) *url.URL
	do(context.Context, *http.Request) (*http.Response, []byte, error)
}

// New returns a new Client.
//
// It is safe to use the returned Client from multiple goroutines.
func New(cfg Config) (Client, error) {
	u, err := url.Parse(cfg.Address)
	if err != nil {
		return nil, err
	}
	u.Path = strings.TrimRight(u.Path, "/") + apiPrefix

	return &httpClient{
		endpoint:  u,
		transport: cfg.transport(),
	}, nil
}

type httpClient struct {
	endpoint  *url.URL
	transport CancelableTransport
}

func (c *httpClient) url(ep string, args map[string]string) *url.URL {
	p := path.Join(c.endpoint.Path, ep)

	for arg, val := range args {
		arg = ":" + arg
		p = strings.Replace(p, arg, val, -1)
	}

	u := *c.endpoint
	u.Path = p

	return &u
}

func (c *httpClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, err := ctxhttp.Do(ctx, &http.Client{Transport: c.transport}, req)

	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, nil, err
	}

	var body []byte
	done := make(chan struct{})
	go func() {
		body, err = ioutil.ReadAll(resp.Body)
		close(done)
	}()

	select {
	case <-ctx.Done():
		err = resp.Body.Close()
		<-done
		if err == nil {
			err = ctx.Err()
		}
	case <-done:
	}

	return resp, body, err
}

// apiClient wraps a regular client and processes successful API responses.
// Successful also includes responses that errored at the API level.
type apiClient struct {
	Client
}

type apiResponse struct {
	Status    string          `json:"status"`
	Data      json.RawMessage `json:"data"`
	ErrorType ErrorType       `json:"errorType"`
	Error     string          `json:"error"`
}

func (c apiClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, body, err := c.Client.do(ctx, req)
	if err != nil {
		return resp, body, err
	}

	code := resp.StatusCode

	if code/100 != 2 && code != statusAPIError {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  fmt.Sprintf("bad response code %d", resp.StatusCode),
		}
	}

	var result apiResponse

	if err = json.Unmarshal(body, &result); err != nil {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  err.Error(),
		}
	}

	if (code == statusAPIError) != (result.Status == "error") {
		err = &Error{
			Type: ErrBadResponse,
			Msg:  "inconsistent body for response code",
		}
	}

	if code == statusAPIError && result.Status == "error" {
		err = &Error{
			Type: result.ErrorType,
			Msg:  result.Error,
		}
	}

	return resp, []byte(result.Data), err
}

// Range represents a sliced time range.
type Range struct {
	// The boundaries of the time range.
	Start, End time.Time
	// The maximum time between two slices within the boundaries.
	Step time.Duration
}

// queryResult contains result data for a query.
type queryResult struct {
	Type   model.ValueType `json:"resultType"`
	Result interface{}     `json:"result"`

	// The decoded value.
	v model.Value
}

func (qr *queryResult) UnmarshalJSON(b []byte) error {
	v := struct {
		Type   model.ValueType `json:"resultType"`
		Result json.RawMessage `json:"result"`
	}{}

	err := json.Unmarshal(b, &v)
	if err != nil {
		return err
	}

	switch v.Type {
	case model.ValScalar:
		var sv model.Scalar
		err = json.Unmarshal(v.Result, &sv)
		qr.v = &sv

	case model.ValVector:
		var vv model.Vector
		err = json.Unmarshal(v.Result, &vv)
		qr.v = vv

	case model.ValMatrix:
		var mv model.Matrix
		err = json.Unmarshal(v.Result, &mv)
		qr.v = mv

	default:
		err = fmt.Errorf("unexpected value type %q", v.Type)
	}
	return err
}

// QueryAPI provides bindings the Prometheus's query API.
type QueryAPI interface {
	// Query performs a query for the given time.
	Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
	// QueryRange performs a query for the given range.
	QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
	// LabelValues performs a query for the values of the given label.
	LabelValues(ctx context.Context, label string) (model.LabelValues, error)
}

// NewQueryAPI returns a new QueryAPI for the client.
//
// It is safe to use the returned QueryAPI from multiple goroutines.
func NewQueryAPI(c Client) QueryAPI {
	return &httpQueryAPI{client: apiClient{c}}
}

type httpQueryAPI struct {
	client Client
}

func (h *httpQueryAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
	u := h.client.url(epQuery, nil)
	q := u.Query()

	q.Set("query", query)
	q.Set("time", ts.Format(time.RFC3339Nano))

	u.RawQuery = q.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
	u := h.client.url(epQueryRange, nil)
	q := u.Query()

	var (
		start = r.Start.Format(time.RFC3339Nano)
		end   = r.End.Format(time.RFC3339Nano)
		step  = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
	)

	q.Set("query", query)
	q.Set("start", start)
	q.Set("end", end)
	q.Set("step", step)

	u.RawQuery = q.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, error) {
	u := h.client.url(epLabelValues, map[string]string{"name": label})
	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}
	var labelValues model.LabelValues
	err = json.Unmarshal(body, &labelValues)
	return labelValues, err
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Version of IronFunctions
var Version = "0.0.67"

func handleVersion(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"version": Version})
}
<commit_msg>functions: 0.0.68 release [skip ci]<commit_after>package server

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Version of IronFunctions
var Version = "0.0.68"

func handleVersion(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"version": Version})
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Version of IronFunctions
var Version = "0.1.18"

func handleVersion(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"version": Version})
}
<commit_msg>functions: 0.1.19 release [skip ci]<commit_after>package server

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Version of IronFunctions
var Version = "0.1.19"

func handleVersion(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"version": Version})
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/sloonz/go-maildir"
	"github.com/sloonz/go-mime-message"
	"github.com/sloonz/go-qprintable"
	"io"
	"log"
	"os"
	"strings"
	"time"
)

var hostname string
var cache Cache

type Attachment struct {
	CID      string `json:"cid"`
	MimeType string `json:"mimeType"`
	Data     []byte `json:"data"`
	Filename string `json:"filename"`
}

type Message struct {
	Id          string       `json:"id"`
	Body        string       `json:"body"`
	Title       string       `json:"title"`
	Author      string       `json:"author"`
	AuthorEmail string       `json:"authorEmail"`
	Date        string       `json:"date"`
	References  []string     `json:"references"`
	Host        string       `json:"host"`
	Attachments []Attachment `json:"attachments"`
}

func isAtomText(s string, allowDot bool) bool {
	if s == "" {
		return false
	}

	pointAllowed := false
	for i := 0; i < len(s); i++ {
		c := s[i]

		// "." is allowed, but not in first position
		// ".." is not allowed
		if c == '.' && pointAllowed && allowDot {
			pointAllowed = false
			continue
		} else {
			pointAllowed = true
		}

		if c >= 'a' && c <= 'z' {
			continue
		}
		if c >= 'A' && c <= 'Z' {
			continue
		}
		if c >= '0' && c <= '9' {
			continue
		}
		if c == '!' || c == '#' || c == '$' || c == '%' || c == '&' ||
			c == '\'' || c == '*' || c == '+' || c == '-' || c == '/' ||
			c == '=' || c == '?' || c == '^' || c == '_' || c == '`' ||
			c == '{' || c == '|' || c == '}' || c == '~' {
			continue
		}

		return false
	}

	return true
}

// allowDot=true is for no-fold-quote ; allowDot=fales is for quoted-string
func encNoFoldQuote(s string, buf *bytes.Buffer, allowDot bool) {
	if isAtomText(s, allowDot) {
		buf.WriteString(s)
	} else {
		// Encode left part as no-fold-quote
		// ASCII 9 (\t), 32 (space), 34 (dquote), 92 (backslash) are escaped with a backslash
		// Non-ASCII and ASCII 0, 10 (\n), 13 (\r) are dropped
		// Other characters are transmitted as-is
		buf.WriteByte('"')
		for i := 0; i < len(s); i++ {
			if s[i] == 0 || s[i] == '\r' || s[i] == '\n' || s[i] > 127 {
				// Drop it
			} else if s[i] == '\t' || s[i] == ' ' || s[i] == '"' || s[i] == '\\' {
				buf.Write([]byte{'\\', s[i]})
			} else {
				buf.WriteByte(s[i])
			}
		}
		buf.WriteByte('"')
	}
}

func encNoFoldLiteral(s string, buf *bytes.Buffer) {
	if isAtomText(s, true) {
		buf.WriteString(s)
	} else {
		// Encode right part as no-fold-literal
		// ASCII 9 (\t), 32 (space), 91 ([), 92 (backslash) and 93 (]) are escaped with a backslash
		// Non-ASCII and ASCII 0, 10 (\n), 13 (\r) are dropped
		// Other characters are transmitted as-is
		buf.WriteByte('[')
		for i := 0; i < len(s); i++ {
			if s[i] == 0 || s[i] == '\r' || s[i] == '\n' || s[i] > 127 {
				// Drop it
			} else if s[i] == '\t' || s[i] == ' ' || s[i] == '[' || s[i] == '\\' || s[i] == ']' {
				buf.Write([]byte{'\\', s[i]})
			} else {
				buf.WriteByte(s[i])
			}
		}
		buf.WriteByte(']')
	}
}

func MessageId(id, host string) string {
	// According to RFC 2822:
	// msg-id          =       [CFWS] "<" id-left "@" id-right ">" [CFWS]
	// id-left         =       dot-atom-text / no-fold-quote
	// id-right        =       dot-atom-text / no-fold-literal
	idBuf := bytes.NewBufferString("<")
	encNoFoldQuote(id, idBuf, true)
	idBuf.WriteByte('@')
	encNoFoldLiteral(host, idBuf)
	idBuf.WriteByte('>')

	return idBuf.String()
}

func (m *Message) Process(md *maildir.Maildir) error {
	var id string
	var mail *message.Message

	if m.Body == "" || m.Title == "" {
		return errors.New("Missing mandatory field")
	}

	if m.Host == "" {
		m.Host = hostname
	}

	if m.AuthorEmail == "" {
		m.AuthorEmail = "noreply@" + m.Host
	}

	if m.Date == "" {
		m.Date = time.Now().UTC().Format(time.RFC1123Z)
	}

	if m.Id != "" {
		id = MessageId(m.Id, m.Host)
		if cache.Getset(m.Id, m.Host, id) {
			return nil
		}
	}

	rootContentType := "text/html; charset=\"UTF-8\""

	bodyPart := message.NewTextMessage(qprintable.UnixTextEncoding, bytes.NewBufferString(m.Body))
	bodyPart.SetHeader("Content-Type", rootContentType)

	if m.Attachments == nil {
		mail = bodyPart
	} else {
		ctBuf := bytes.NewBufferString("")
		encNoFoldQuote(rootContentType, ctBuf, false)
		rootPart := message.NewMultipartMessageParams("related", "",
			map[string]string{"type": ctBuf.String()})

		rootPart.AddPart(bodyPart)
		for _, attachment := range m.Attachments {
			attPart := message.NewBinaryMessage(bytes.NewBuffer(attachment.Data))
			attPart.SetHeader("Content-ID", fmt.Sprintf("<%s>", attachment.CID))
			attPart.SetHeader("Content-Type", attachment.MimeType)
			if attachment.Filename == "" {
				attPart.SetHeader("Content-Disposition", "inline")
			} else {
				fnBuf := bytes.NewBufferString("")
				encNoFoldQuote(attachment.Filename, fnBuf, false)
				attPart.SetHeader("Content-Description", attachment.Filename)
				attPart.SetHeader("Content-Disposition", fmt.Sprintf("inline; filename=%s", fnBuf.String()))
			}
			rootPart.AddPart(attPart)
		}

		mail = &rootPart.Message
	}

	// In a maildir, mails are expected to end with LF line endings. Most softwares are
	// just fine with CRLF line endings, but some (for example Mutt) don’t.
	mail.EOL = "\n"
	mail.SetHeader("Date", m.Date)
	mail.SetHeader("Subject", message.EncodeWord(m.Title))
	mail.SetHeader("From", message.EncodeWord(m.Author)+" <"+m.AuthorEmail+">")
	if id != "" {
		mail.SetHeader("Message-Id", id)
	}
	if len(m.References) > 0 {
		refs := ""
		for _, r := range m.References {
			refs += " " + MessageId(r, m.Host)
		}
		mail.SetHeader("References", refs)
	}

	md.CreateMail(mail)

	return nil
}

func main() {
	var rootDir, folder string
	var err error

	flag.StringVar(&rootDir, "root", os.ExpandEnv("$HOME/Maildir"), "path to maildir")
	flag.StringVar(&folder, "folder", "", "maildir folder name to put email (empty for inbox)")
	flag.StringVar(&cache.path, "cache", os.ExpandEnv("$HOME/.cache/maildir-put.cache"),
		"path to store message-ids to drop duplicate messages")
	flag.BoolVar(&cache.useRedis, "redis", false, "use redis for cache storage")
	flag.StringVar(&cache.redisOptions.Addr, "redis-addr", "127.0.0.1:6379", "redis address")
	flag.Int64Var(&cache.redisOptions.DB, "redis-db", 0, "redis base")
	flag.StringVar(&cache.redisOptions.Password, "redis-password", "", "redis password")

	if flag.Parse(); !flag.Parsed() {
		flag.PrintDefaults()
		os.Exit(1)
	}

	if err = cache.OpenCache(); err != nil {
		log.Fatalf("Can't open cache: %s", err.Error())
	}

	if hostname, err = os.Hostname(); err != nil {
		log.Fatalf("Can't get hostname: %s", err.Error())
	}

	md, err := maildir.New(rootDir, true)
	if err != nil {
		log.Fatalf("Can't open maildir: %s", err.Error())
	}

	for _, subfolder := range strings.Split(folder, "/") {
		if subfolder != "" {
			md, err = md.Child(subfolder, true)
			if err != nil {
				log.Fatalf("Can't open maildir: %s", err.Error())
			}
		}
	}

	dec := json.NewDecoder(os.Stdin)
	for {
		msg := new(Message)
		err = dec.Decode(msg)
		if err == nil {
			err = msg.Process(md)
		}

		if err == io.EOF {
			break
		} else if err != nil {
			log.Printf("Cannot read input message: %s", err.Error())
		}
	}

	if err = cache.Dump(); err != nil {
		log.Printf("warning: can't dump cache: %s", err.Error())
	}
}
<commit_msg>maildir-put: sanitize date<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/sloonz/go-maildir"
	"github.com/sloonz/go-mime-message"
	"github.com/sloonz/go-qprintable"
	"io"
	"log"
	"os"
	"strings"
	"time"
)

var hostname string
var cache Cache

type Attachment struct {
	CID      string `json:"cid"`
	MimeType string `json:"mimeType"`
	Data     []byte `json:"data"`
	Filename string `json:"filename"`
}

type Message struct {
	Id          string       `json:"id"`
	Body        string       `json:"body"`
	Title       string       `json:"title"`
	Author      string       `json:"author"`
	AuthorEmail string       `json:"authorEmail"`
	Date        string       `json:"date"`
	References  []string     `json:"references"`
	Host        string       `json:"host"`
	Attachments []Attachment `json:"attachments"`
}

func isAtomText(s string, allowDot bool) bool {
	if s == "" {
		return false
	}

	pointAllowed := false
	for i := 0; i < len(s); i++ {
		c := s[i]

		// "." is allowed, but not in first position
		// ".." is not allowed
		if c == '.' && pointAllowed && allowDot {
			pointAllowed = false
			continue
		} else {
			pointAllowed = true
		}

		if c >= 'a' && c <= 'z' {
			continue
		}
		if c >= 'A' && c <= 'Z' {
			continue
		}
		if c >= '0' && c <= '9' {
			continue
		}
		if c == '!' || c == '#' || c == '$' || c == '%' || c == '&' ||
			c == '\'' || c == '*' || c == '+' || c == '-' || c == '/' ||
			c == '=' || c == '?' || c == '^' || c == '_' || c == '`' ||
			c == '{' || c == '|' || c == '}' || c == '~' {
			continue
		}

		return false
	}

	return true
}

// allowDot=true is for no-fold-quote ; allowDot=fales is for quoted-string
func encNoFoldQuote(s string, buf *bytes.Buffer, allowDot bool) {
	if isAtomText(s, allowDot) {
		buf.WriteString(s)
	} else {
		// Encode left part as no-fold-quote
		// ASCII 9 (\t), 32 (space), 34 (dquote), 92 (backslash) are escaped with a backslash
		// Non-ASCII and ASCII 0, 10 (\n), 13 (\r) are dropped
		// Other characters are transmitted as-is
		buf.WriteByte('"')
		for i := 0; i < len(s); i++ {
			if s[i] == 0 || s[i] == '\r' || s[i] == '\n' || s[i] > 127 {
				// Drop it
			} else if s[i] == '\t' || s[i] == ' ' || s[i] == '"' || s[i] == '\\' {
				buf.Write([]byte{'\\', s[i]})
			} else {
				buf.WriteByte(s[i])
			}
		}
		buf.WriteByte('"')
	}
}

func encNoFoldLiteral(s string, buf *bytes.Buffer) {
	if isAtomText(s, true) {
		buf.WriteString(s)
	} else {
		// Encode right part as no-fold-literal
		// ASCII 9 (\t), 32 (space), 91 ([), 92 (backslash) and 93 (]) are escaped with a backslash
		// Non-ASCII and ASCII 0, 10 (\n), 13 (\r) are dropped
		// Other characters are transmitted as-is
		buf.WriteByte('[')
		for i := 0; i < len(s); i++ {
			if s[i] == 0 || s[i] == '\r' || s[i] == '\n' || s[i] > 127 {
				// Drop it
			} else if s[i] == '\t' || s[i] == ' ' || s[i] == '[' || s[i] == '\\' || s[i] == ']' {
				buf.Write([]byte{'\\', s[i]})
			} else {
				buf.WriteByte(s[i])
			}
		}
		buf.WriteByte(']')
	}
}

func formatDate(date string) string {
	parsedDate, err := time.Parse(time.RFC3339, date)
	if err != nil {
		return date
	}

	return parsedDate.Format(time.RFC3339)
}

func MessageId(id, host string) string {
	// According to RFC 2822:
	// msg-id          =       [CFWS] "<" id-left "@" id-right ">" [CFWS]
	// id-left         =       dot-atom-text / no-fold-quote
	// id-right        =       dot-atom-text / no-fold-literal
	idBuf := bytes.NewBufferString("<")
	encNoFoldQuote(id, idBuf, true)
	idBuf.WriteByte('@')
	encNoFoldLiteral(host, idBuf)
	idBuf.WriteByte('>')

	return idBuf.String()
}

func (m *Message) Process(md *maildir.Maildir) error {
	var id string
	var mail *message.Message

	if m.Body == "" || m.Title == "" {
		return errors.New("Missing mandatory field")
	}

	if m.Host == "" {
		m.Host = hostname
	}

	if m.AuthorEmail == "" {
		m.AuthorEmail = "noreply@" + m.Host
	}

	if m.Date == "" {
		m.Date = time.Now().UTC().Format(time.RFC1123Z)
	}

	if m.Id != "" {
		id = MessageId(m.Id, m.Host)
		if cache.Getset(m.Id, m.Host, id) {
			return nil
		}
	}

	rootContentType := "text/html; charset=\"UTF-8\""

	bodyPart := message.NewTextMessage(qprintable.UnixTextEncoding, bytes.NewBufferString(m.Body))
	bodyPart.SetHeader("Content-Type", rootContentType)

	if m.Attachments == nil {
		mail = bodyPart
	} else {
		ctBuf := bytes.NewBufferString("")
		encNoFoldQuote(rootContentType, ctBuf, false)
		rootPart := message.NewMultipartMessageParams("related", "",
			map[string]string{"type": ctBuf.String()})

		rootPart.AddPart(bodyPart)
		for _, attachment := range m.Attachments {
			attPart := message.NewBinaryMessage(bytes.NewBuffer(attachment.Data))
			attPart.SetHeader("Content-ID", fmt.Sprintf("<%s>", attachment.CID))
			attPart.SetHeader("Content-Type", attachment.MimeType)
			if attachment.Filename == "" {
				attPart.SetHeader("Content-Disposition", "inline")
			} else {
				fnBuf := bytes.NewBufferString("")
				encNoFoldQuote(attachment.Filename, fnBuf, false)
				attPart.SetHeader("Content-Description", attachment.Filename)
				attPart.SetHeader("Content-Disposition", fmt.Sprintf("inline; filename=%s", fnBuf.String()))
			}
			rootPart.AddPart(attPart)
		}

		mail = &rootPart.Message
	}

	// In a maildir, mails are expected to end with LF line endings. Most softwares are
	// just fine with CRLF line endings, but some (for example Mutt) don’t.
	mail.EOL = "\n"
	mail.SetHeader("Date", formatDate(m.Date))
	mail.SetHeader("Subject", message.EncodeWord(m.Title))
	mail.SetHeader("From", message.EncodeWord(m.Author)+" <"+m.AuthorEmail+">")
	if id != "" {
		mail.SetHeader("Message-Id", id)
	}
	if len(m.References) > 0 {
		refs := ""
		for _, r := range m.References {
			refs += " " + MessageId(r, m.Host)
		}
		mail.SetHeader("References", refs)
	}

	md.CreateMail(mail)

	return nil
}

func main() {
	var rootDir, folder string
	var err error

	flag.StringVar(&rootDir, "root", os.ExpandEnv("$HOME/Maildir"), "path to maildir")
	flag.StringVar(&folder, "folder", "", "maildir folder name to put email (empty for inbox)")
	flag.StringVar(&cache.path, "cache", os.ExpandEnv("$HOME/.cache/maildir-put.cache"),
		"path to store message-ids to drop duplicate messages")
	flag.BoolVar(&cache.useRedis, "redis", false, "use redis for cache storage")
	flag.StringVar(&cache.redisOptions.Addr, "redis-addr", "127.0.0.1:6379", "redis address")
	flag.Int64Var(&cache.redisOptions.DB, "redis-db", 0, "redis base")
	flag.StringVar(&cache.redisOptions.Password, "redis-password", "", "redis password")

	if flag.Parse(); !flag.Parsed() {
		flag.PrintDefaults()
		os.Exit(1)
	}

	if err = cache.OpenCache(); err != nil {
		log.Fatalf("Can't open cache: %s", err.Error())
	}

	if hostname, err = os.Hostname(); err != nil {
		log.Fatalf("Can't get hostname: %s", err.Error())
	}

	md, err := maildir.New(rootDir, true)
	if err != nil {
		log.Fatalf("Can't open maildir: %s", err.Error())
	}

	for _, subfolder := range strings.Split(folder, "/") {
		if subfolder != "" {
			md, err = md.Child(subfolder, true)
			if err != nil {
				log.Fatalf("Can't open maildir: %s", err.Error())
			}
		}
	}

	dec := json.NewDecoder(os.Stdin)
	for {
		msg := new(Message)
		err = dec.Decode(msg)
		if err == nil {
			err = msg.Process(md)
		}

		if err == io.EOF {
			break
		} else if err != nil {
			log.Printf("Cannot read input message: %s", err.Error())
		}
	}

	if err = cache.Dump(); err != nil {
		log.Printf("warning: can't dump cache: %s", err.Error())
	}
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"fmt"
	"runtime"
	"sort"
	"strings"
	"time"
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
	gray    = 37
)

var (
	baseTimestamp time.Time
	isTerminal    bool
)

func init() {
	baseTimestamp = time.Now()
	isTerminal = IsTerminal()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors bool

	// Force disabling colors.
	DisableColors bool

	// Disable timestamp logging. useful when output is redirected to logging
	// system that already adds timestamps.
	DisableTimestamp bool

	// Enable logging the full timestamp when a TTY is attached instead of just
	// the time passed since beginning of execution.
	FullTimestamp bool

	// TimestampFormat to use for display when a full timestamp is printed
	TimestampFormat string

	// The fields are sorted by default for a consistent output. For applications
	// that log extremely frequently and don't use the JSON formatter this may not
	// be desired.
	DisableSorting bool
}

func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
	var keys []string = make([]string, 0, len(entry.Data))
	for k := range entry.Data {
		keys = append(keys, k)
	}

	if !f.DisableSorting {
		sort.Strings(keys)
	}

	b := &bytes.Buffer{}

	prefixFieldClashes(entry.Data)

	isColorTerminal := isTerminal && (runtime.GOOS != "windows")
	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors

	timestampFormat := f.TimestampFormat
	if timestampFormat == "" {
		timestampFormat = DefaultTimestampFormat
	}
	if isColored {
		f.printColored(b, entry, keys, timestampFormat)
	} else {
		if !f.DisableTimestamp {
			f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
		}
		f.appendKeyValue(b, "level", entry.Level.String())
		if entry.Message != "" {
			f.appendKeyValue(b, "msg", entry.Message)
		}
		for _, key := range keys {
			f.appendKeyValue(b, key, entry.Data[key])
		}
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
	var levelColor int
	switch entry.Level {
	case DebugLevel:
		levelColor = gray
	case WarnLevel:
		levelColor = yellow
	case ErrorLevel, FatalLevel, PanicLevel:
		levelColor = red
	default:
		levelColor = blue
	}

	levelText := strings.ToUpper(entry.Level.String())[0:4]

	if !f.FullTimestamp {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
	} else {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
	}
	for _, k := range keys {
		v := entry.Data[k]
		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
	}
}

func needsQuoting(text string) bool {
	for _, ch := range text {
		if !((ch >= 'a' && ch <= 'z') ||
			(ch >= 'A' && ch <= 'Z') ||
			(ch >= '0' && ch <= '9') ||
			ch == '-' || ch == '.') {
			return false
		}
	}
	return true
}

func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {

	b.WriteString(key)
	b.WriteByte('=')

	switch value := value.(type) {
	case string:
		if needsQuoting(value) {
			b.WriteString(value)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	case error:
		errmsg := value.Error()
		if needsQuoting(errmsg) {
			b.WriteString(errmsg)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	default:
		fmt.Fprint(b, value)
	}

	b.WriteByte(' ')
}
<commit_msg>match name to semantic in `needsQuoting`<commit_after>package logrus

import (
	"bytes"
	"fmt"
	"runtime"
	"sort"
	"strings"
	"time"
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
	gray    = 37
)

var (
	baseTimestamp time.Time
	isTerminal    bool
)

func init() {
	baseTimestamp = time.Now()
	isTerminal = IsTerminal()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors bool

	// Force disabling colors.
	DisableColors bool

	// Disable timestamp logging. useful when output is redirected to logging
	// system that already adds timestamps.
	DisableTimestamp bool

	// Enable logging the full timestamp when a TTY is attached instead of just
	// the time passed since beginning of execution.
	FullTimestamp bool

	// TimestampFormat to use for display when a full timestamp is printed
	TimestampFormat string

	// The fields are sorted by default for a consistent output. For applications
	// that log extremely frequently and don't use the JSON formatter this may not
	// be desired.
	DisableSorting bool
}

func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
	var keys []string = make([]string, 0, len(entry.Data))
	for k := range entry.Data {
		keys = append(keys, k)
	}

	if !f.DisableSorting {
		sort.Strings(keys)
	}

	b := &bytes.Buffer{}

	prefixFieldClashes(entry.Data)

	isColorTerminal := isTerminal && (runtime.GOOS != "windows")
	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors

	timestampFormat := f.TimestampFormat
	if timestampFormat == "" {
		timestampFormat = DefaultTimestampFormat
	}
	if isColored {
		f.printColored(b, entry, keys, timestampFormat)
	} else {
		if !f.DisableTimestamp {
			f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
		}
		f.appendKeyValue(b, "level", entry.Level.String())
		if entry.Message != "" {
			f.appendKeyValue(b, "msg", entry.Message)
		}
		for _, key := range keys {
			f.appendKeyValue(b, key, entry.Data[key])
		}
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
	var levelColor int
	switch entry.Level {
	case DebugLevel:
		levelColor = gray
	case WarnLevel:
		levelColor = yellow
	case ErrorLevel, FatalLevel, PanicLevel:
		levelColor = red
	default:
		levelColor = blue
	}

	levelText := strings.ToUpper(entry.Level.String())[0:4]

	if !f.FullTimestamp {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
	} else {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
	}
	for _, k := range keys {
		v := entry.Data[k]
		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
	}
}

func needsQuoting(text string) bool {
	for _, ch := range text {
		if !((ch >= 'a' && ch <= 'z') ||
			(ch >= 'A' && ch <= 'Z') ||
			(ch >= '0' && ch <= '9') ||
			ch == '-' || ch == '.') {
			return true
		}
	}
	return false
}

func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {

	b.WriteString(key)
	b.WriteByte('=')

	switch value := value.(type) {
	case string:
		if !needsQuoting(value) {
			b.WriteString(value)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	case error:
		errmsg := value.Error()
		if !needsQuoting(errmsg) {
			b.WriteString(errmsg)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	default:
		fmt.Fprint(b, value)
	}

	b.WriteByte(' ')
}
<|endoftext|>


<commit_before>package dns

import (
	"context"
	"sync"
	"time"

	"github.com/miekg/dns"
	"v2ray.com/core"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/dice"
	"v2ray.com/core/common/net"
	"v2ray.com/core/transport/internet/udp"
)

const (
	CleanupInterval  = time.Second * 120
	CleanupThreshold = 512
)

var (
	multiQuestionDNS = map[net.Address]bool{
		net.IPAddress([]byte{8, 8, 8, 8}): true,
		net.IPAddress([]byte{8, 8, 4, 4}): true,
		net.IPAddress([]byte{9, 9, 9, 9}): true,
	}
)

type ARecord struct {
	IPs    []net.IP
	Expire time.Time
}

type NameServer interface {
	QueryA(domain string) <-chan *ARecord
}

type PendingRequest struct {
	expire   time.Time
	response chan<- *ARecord
}

type UDPNameServer struct {
	sync.Mutex
	address     net.Destination
	requests    map[uint16]*PendingRequest
	udpServer   *udp.Dispatcher
	nextCleanup time.Time
}

func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPNameServer {
	s := &UDPNameServer{
		address:   address,
		requests:  make(map[uint16]*PendingRequest),
		udpServer: udp.NewDispatcher(dispatcher),
	}
	return s
}

func (s *UDPNameServer) Cleanup() {
	expiredRequests := make([]uint16, 0, 16)
	now := time.Now()
	s.Lock()
	for id, r := range s.requests {
		if r.expire.Before(now) {
			expiredRequests = append(expiredRequests, id)
			close(r.response)
		}
	}
	for _, id := range expiredRequests {
		delete(s.requests, id)
	}
	s.Unlock()
}

func (s *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
	var id uint16
	s.Lock()
	if len(s.requests) > CleanupThreshold && s.nextCleanup.Before(time.Now()) {
		s.nextCleanup = time.Now().Add(CleanupInterval)
		go s.Cleanup()
	}

	for {
		id = dice.RollUint16()
		if _, found := s.requests[id]; found {
			continue
		}
		newError("add pending request id ", id).AtDebug().WriteToLog()
		s.requests[id] = &PendingRequest{
			expire:   time.Now().Add(time.Second * 8),
			response: response,
		}
		break
	}
	s.Unlock()
	return id
}

func (s *UDPNameServer) HandleResponse(payload *buf.Buffer) {
	msg := new(dns.Msg)
	err := msg.Unpack(payload.Bytes())
	if err == dns.ErrTruncated {
		newError("truncated message received. DNS server should still work. If you see anything abnormal, please submit an issue to v2ray-core.").AtWarning().WriteToLog()
	} else if err != nil {
		newError("failed to parse DNS response").Base(err).AtWarning().WriteToLog()
		return
	}
	record := &ARecord{
		IPs: make([]net.IP, 0, 16),
	}
	id := msg.Id
	ttl := uint32(3600) // an hour
	newError("handling response for id ", id, " content: ", msg).AtDebug().WriteToLog()

	s.Lock()
	request, found := s.requests[id]
	if !found {
		s.Unlock()
		return
	}
	delete(s.requests, id)
	s.Unlock()

	for _, rr := range msg.Answer {
		switch rr := rr.(type) {
		case *dns.A:
			record.IPs = append(record.IPs, rr.A)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		case *dns.AAAA:
			record.IPs = append(record.IPs, rr.AAAA)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		}
	}
	record.Expire = time.Now().Add(time.Second * time.Duration(ttl))

	request.response <- record
	close(request.response)
}

func (s *UDPNameServer) buildAMsg(domain string, id uint16) *dns.Msg {
	msg := new(dns.Msg)
	msg.Id = id
	msg.RecursionDesired = true
	msg.Question = []dns.Question{
		{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeA,
			Qclass: dns.ClassINET,
		}}
	if multiQuestionDNS[s.address.Address] {
		msg.Question = append(msg.Question, dns.Question{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeAAAA,
			Qclass: dns.ClassINET,
		})
	}

	return msg
}

func msgToBuffer(msg *dns.Msg) (*buf.Buffer, error) {
	buffer := buf.New()
	if err := buffer.Reset(func(b []byte) (int, error) {
		writtenBuffer, err := msg.PackBuffer(b)
		return len(writtenBuffer), err
	}); err != nil {
		return nil, err
	}
	return buffer, nil
}

func (s *UDPNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)
	id := s.AssignUnusedID(response)

	msg := s.buildAMsg(domain, id)
	b, err := msgToBuffer(msg)
	if err != nil {
		newError("failed to build A query for domain ", domain).Base(err).WriteToLog()
		close(response)
		return response
	}

	ctx, cancel := context.WithCancel(context.Background())
	s.udpServer.Dispatch(ctx, s.address, b, s.HandleResponse)

	go func() {
		for i := 0; i < 2; i++ {
			time.Sleep(time.Second)
			s.Lock()
			_, found := s.requests[id]
			s.Unlock()
			if !found {
				break
			}
			b, _ := msgToBuffer(msg)
			s.udpServer.Dispatch(ctx, s.address, b, s.HandleResponse)
		}
		cancel()
	}()

	return response
}

type LocalNameServer struct {
}

func (*LocalNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)

	go func() {
		defer close(response)

		ips, err := net.LookupIP(domain)
		if err != nil {
			newError("failed to lookup IPs for domain ", domain).Base(err).AtWarning().WriteToLog()
			return
		}

		response <- &ARecord{
			IPs:    ips,
			Expire: time.Now().Add(time.Hour),
		}
	}()

	return response
}
<commit_msg>refactor UDPNameServer clean up task<commit_after>package dns

import (
	"context"
	"sync"
	"time"

	"github.com/miekg/dns"
	"v2ray.com/core"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/dice"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/signal"
	"v2ray.com/core/transport/internet/udp"
)

var (
	multiQuestionDNS = map[net.Address]bool{
		net.IPAddress([]byte{8, 8, 8, 8}): true,
		net.IPAddress([]byte{8, 8, 4, 4}): true,
		net.IPAddress([]byte{9, 9, 9, 9}): true,
	}
)

type ARecord struct {
	IPs    []net.IP
	Expire time.Time
}

type NameServer interface {
	QueryA(domain string) <-chan *ARecord
}

type PendingRequest struct {
	expire   time.Time
	response chan<- *ARecord
}

type UDPNameServer struct {
	sync.Mutex
	address   net.Destination
	requests  map[uint16]*PendingRequest
	udpServer *udp.Dispatcher
	cleanup   *signal.PeriodicTask
}

func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPNameServer {
	s := &UDPNameServer{
		address:   address,
		requests:  make(map[uint16]*PendingRequest),
		udpServer: udp.NewDispatcher(dispatcher),
	}
	s.cleanup = &signal.PeriodicTask{
		Interval: time.Minute,
		Execute:  s.Cleanup,
	}
	s.cleanup.Start()
	return s
}

func (s *UDPNameServer) Cleanup() error {
	now := time.Now()
	s.Lock()
	for id, r := range s.requests {
		if r.expire.Before(now) {
			close(r.response)
			delete(s.requests, id)
		}
	}
	s.Unlock()
	return nil
}

func (s *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
	var id uint16
	s.Lock()

	for {
		id = dice.RollUint16()
		if _, found := s.requests[id]; found {
			time.Sleep(time.Millisecond * 500)
			continue
		}
		newError("add pending request id ", id).AtDebug().WriteToLog()
		s.requests[id] = &PendingRequest{
			expire:   time.Now().Add(time.Second * 8),
			response: response,
		}
		break
	}
	s.Unlock()
	return id
}

func (s *UDPNameServer) HandleResponse(payload *buf.Buffer) {
	msg := new(dns.Msg)
	err := msg.Unpack(payload.Bytes())
	if err == dns.ErrTruncated {
		newError("truncated message received. DNS server should still work. If you see anything abnormal, please submit an issue to v2ray-core.").AtWarning().WriteToLog()
	} else if err != nil {
		newError("failed to parse DNS response").Base(err).AtWarning().WriteToLog()
		return
	}
	record := &ARecord{
		IPs: make([]net.IP, 0, 16),
	}
	id := msg.Id
	ttl := uint32(3600) // an hour
	newError("handling response for id ", id, " content: ", msg).AtDebug().WriteToLog()

	s.Lock()
	request, found := s.requests[id]
	if !found {
		s.Unlock()
		return
	}
	delete(s.requests, id)
	s.Unlock()

	for _, rr := range msg.Answer {
		switch rr := rr.(type) {
		case *dns.A:
			record.IPs = append(record.IPs, rr.A)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		case *dns.AAAA:
			record.IPs = append(record.IPs, rr.AAAA)
			if rr.Hdr.Ttl < ttl {
				ttl = rr.Hdr.Ttl
			}
		}
	}
	record.Expire = time.Now().Add(time.Second * time.Duration(ttl))

	request.response <- record
	close(request.response)
}

func (s *UDPNameServer) buildAMsg(domain string, id uint16) *dns.Msg {
	msg := new(dns.Msg)
	msg.Id = id
	msg.RecursionDesired = true
	msg.Question = []dns.Question{
		{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeA,
			Qclass: dns.ClassINET,
		}}
	if multiQuestionDNS[s.address.Address] {
		msg.Question = append(msg.Question, dns.Question{
			Name:   dns.Fqdn(domain),
			Qtype:  dns.TypeAAAA,
			Qclass: dns.ClassINET,
		})
	}

	return msg
}

func msgToBuffer(msg *dns.Msg) (*buf.Buffer, error) {
	buffer := buf.New()
	if err := buffer.Reset(func(b []byte) (int, error) {
		writtenBuffer, err := msg.PackBuffer(b)
		return len(writtenBuffer), err
	}); err != nil {
		return nil, err
	}
	return buffer, nil
}

func (s *UDPNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)
	id := s.AssignUnusedID(response)

	msg := s.buildAMsg(domain, id)
	b, err := msgToBuffer(msg)
	if err != nil {
		newError("failed to build A query for domain ", domain).Base(err).WriteToLog()
		close(response)
		return response
	}

	ctx, cancel := context.WithCancel(context.Background())
	s.udpServer.Dispatch(ctx, s.address, b, s.HandleResponse)

	go func() {
		for i := 0; i < 2; i++ {
			time.Sleep(time.Second)
			s.Lock()
			_, found := s.requests[id]
			s.Unlock()
			if !found {
				break
			}
			b, _ := msgToBuffer(msg)
			s.udpServer.Dispatch(ctx, s.address, b, s.HandleResponse)
		}
		cancel()
	}()

	return response
}

type LocalNameServer struct {
}

func (*LocalNameServer) QueryA(domain string) <-chan *ARecord {
	response := make(chan *ARecord, 1)

	go func() {
		defer close(response)

		ips, err := net.LookupIP(domain)
		if err != nil {
			newError("failed to lookup IPs for domain ", domain).Base(err).AtWarning().WriteToLog()
			return
		}

		response <- &ARecord{
			IPs:    ips,
			Expire: time.Now().Add(time.Hour),
		}
	}()

	return response
}
<|endoftext|>


<commit_before>/*
	Guides the design of `tok.Token`:
	test whether it's important to yield pointers to the values of interest,
	versus simply putting the values in an `interface{}` slot.

	(It is.)

	Though syntactically irritating to use of pointers to primitives for all tokens,
	this bench demonstrates that doing so avoids a source of allocs,
	and thus has significant performance implications.
*/
package bench

import (
	"testing"
)

// Std:  Benchmark_CopyByValue-8         30000000                43.4 ns/op
// noGC: Benchmark_CopyByValue-8         30000000                34.0 ns/op
// mem:  Benchmark_CopyByValue-8         30000000                44.4 ns/op             8 B/op          1 allocs/op
func Benchmark_CopyByValue(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field int
	}
	type StructB struct {
		field int
	}
	valA := StructA{4}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = valA.field
		valB.field = slot.(int)
	}
	if valB.field != 4 {
		b.Error("final value of valB wrong")
	}
}

// Std:  Benchmark_CopyByRef-8           2000000000               0.59 ns/op
// noGC: Benchmark_CopyByRef-8           2000000000               0.59 ns/op
// mem:  Benchmark_CopyByRef-8           2000000000               0.59 ns/op            0 B/op          0 allocs/op
func Benchmark_CopyByRef(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field int
	}
	type StructB struct {
		field int
	}
	valA := StructA{4}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = &(valA.field)
		valB.field = *(slot.(*int))
	}
	if valB.field != 4 {
		b.Error("final value of valB wrong")
	}
}

// Sanity check: strings are not noticably different:
//
//	Benchmark_CopyByValue-8                 30000000                45.5 ns/op
//	Benchmark_CopyByRef-8                   2000000000               0.59 ns/op
//	Benchmark_CopyByValue_String-8          20000000                72.3 ns/op
//	Benchmark_CopyByRef_String-8            2000000000               0.60 ns/op
func Benchmark_CopyByValue_String(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field string
	}
	type StructB struct {
		field string
	}
	valA := StructA{"alksjdlkjweoihgowihehgioijerg"}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = valA.field
		valB.field = slot.(string)
	}
	if valB.field != valA.field {
		b.Error("final value of valB wrong")
	}
}

func Benchmark_CopyByRef_String(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field string
	}
	type StructB struct {
		field string
	}
	valA := StructA{"alksjdlkjweoihgowihehgioijerg"}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = &(valA.field)
		valB.field = *(slot.(*string))
	}
	if valB.field != valA.field {
		b.Error("final value of valB wrong")
	}
}
<commit_msg>Right, so we're learning(/confirming) things about how go internally keeps track of strings and values...<commit_after>/*
	Guides the design of `tok.Token`:
	test whether it's important to yield pointers to the values of interest,
	versus simply putting the values in an `interface{}` slot.

	(It is.)

	Though syntactically irritating to use of pointers to primitives for all tokens,
	this bench demonstrates that doing so avoids a source of allocs,
	and thus has significant performance implications.
*/
package bench

import (
	"testing"
)

// Std:  Benchmark_CopyByValue-8         30000000                43.4 ns/op
// noGC: Benchmark_CopyByValue-8         30000000                34.0 ns/op
// mem:  Benchmark_CopyByValue-8         30000000                44.4 ns/op             8 B/op          1 allocs/op
func Benchmark_CopyByValue(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field int
	}
	type StructB struct {
		field int
	}
	valA := StructA{4}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = valA.field
		valB.field = slot.(int)
	}
	if valB.field != 4 {
		b.Error("final value of valB wrong")
	}
}

// Std:  Benchmark_CopyByRef-8           2000000000               0.59 ns/op
// noGC: Benchmark_CopyByRef-8           2000000000               0.59 ns/op
// mem:  Benchmark_CopyByRef-8           2000000000               0.59 ns/op            0 B/op          0 allocs/op
func Benchmark_CopyByRef(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field int
	}
	type StructB struct {
		field int
	}
	valA := StructA{4}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = &(valA.field)
		valB.field = *(slot.(*int))
	}
	if valB.field != 4 {
		b.Error("final value of valB wrong")
	}
}

// Sanity check: strings are not noticably different:
//
//	Benchmark_CopyByValue-8                 30000000                45.5 ns/op
//	Benchmark_CopyByRef-8                   2000000000               0.59 ns/op
//	Benchmark_CopyByValue_String-8          20000000                72.3 ns/op
//	Benchmark_CopyByRef_String-8            2000000000               0.60 ns/op
//
// Not commited, but note that there is no sigificant impact from the length of the string.
// Benchmem offers some insight into why:
//
//	Benchmark_CopyByValue_String-8          20000000                73.5 ns/op            16 B/op          1 allocs/op
//
// Evidentally copy-by-value of a string requires a proportionally larger alloc to store the length;
// and furthermore despite being a single alloc, the size in bytes does visibly increase time cost.
func Benchmark_CopyByValue_String(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field string
	}
	type StructB struct {
		field string
	}
	valA := StructA{"alksjdlkjweoihgowihehgioijerg"}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = valA.field
		valB.field = slot.(string)
	}
	if valB.field != valA.field {
		b.Error("final value of valB wrong")
	}
}

func Benchmark_CopyByRef_String(b *testing.B) {
	type Alias interface{}
	var slot Alias
	type StructA struct {
		field string
	}
	type StructB struct {
		field string
	}
	valA := StructA{"alksjdlkjweoihgowihehgioijerg"}
	valB := StructB{}

	for i := 0; i < b.N; i++ {
		slot = &(valA.field)
		valB.field = *(slot.(*string))
	}
	if valB.field != valA.field {
		b.Error("final value of valB wrong")
	}
}
<|endoftext|>


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

// oticonfig.go [created: Thu, 20 Mar 2014]

/*
package oticonfig configuration for the oti command.

Deploying images

You can assemble regional profiles for deploying instances. This allows you to
specify ssh key-pairs and security groups for your instances. you can have
multiple profiles per region as long as they have unique ids.

see C.Ec2.Regions for configuration details.

Finding images

You can launch any images as long as they are available in the desired ec2
region. but oti can find images based on ec2 tags.

see C.Images for configuration details

Packer and oti

oti is made to be used in conjunction with packer or any program that produces
a large number of images in a structured manner. just have packer build images
with the tags specified in C.Images.

// BUG finding images based on tags does not work
*/
package oticonfig

import (
	"github.com/bmatsuo/go-jsontree"
	"github.com/bmatsuo/oti/otitag"
	"github.com/crowdmob/goamz/aws"
	awsec2 "github.com/crowdmob/goamz/ec2"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
)

// the json configuration for oti
type C struct {
	// packer manifest configuration
	Packer Packer `json:",omitempty"`

	// metadata allowing usage of tags on images
	Images Images

	// see func (c *C) AwsKey()
	AwsKeyPath string `json:",omitempty"`

	// default Ec2 deployment configurations
	Ec2 Ec2 `json:",omitempty"`
}

type Packer struct {
	// a directory containing packer manifests.
	// see func (c *C) Packer(string)
	ManifestDir string `json:",omitempty"`
}

type Images struct {
	// tag identifying the service(s) provided by an image
	NameTag string `json:",omitempty"`

	// tag identifying the date an image was created
	BuildDateTag string `json:",omitempty"`

	// tag containing a semantic version for the image
	VersionTag string `json:",omitempty"` // not used
}

type Ec2 struct {
	// see func (c *C) Ec2Tag(otitag.OTITag)
	TagPrefix string `json:",omitempty"`

	// region specific configuration
	Regions []Ec2Region
}

type Ec2Region struct {
	// a unique identifier for the object. required if more than one region
	// profile is defined for the same RegionName.
	Id string `json:",omitempty"`

	// an ec2 canonical region name (e.g. "us-east-1"). required
	RegionName string

	// ec2 key name (recommended). overrideable per instance
	KeyName string `json:",omitempty"`

	// security groups. additional groups can be added per instance.
	SecurityGroups []Ec2SecurityGroup `json:",omitempty"`
}

// security groups with neither Id or Name are ignored.
type Ec2SecurityGroup struct {
	Id   string `json:",omitempty"`
	Name string `json:",omitempty"`
}

// unmarshal json data stored at path into c. any error encountered is returned.
func Read(path string, c *C) error {
	configp, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	err = json.Unmarshal(configp, c)
	if err != nil {
		return err
	}
	return nil
}

// returns name prefixed with c.TagPrefix
func (c *C) Ec2Tag(tag otitag.OTITag) string {
	return c.Ec2.TagPrefix + string(tag)
}

// returns the first region config with RegionName equal to r.Name
func (c *C) Ec2Region(r aws.Region) *Ec2Region {
	for _, cr := range c.Ec2.Regions {
		if cr.RegionName == r.Name {
			return &cr
		}
	}
	return nil
}

// BUG uses the first region config with right name
func (c *C) Ec2KeyName(r aws.Region) string {
	cr := c.Ec2Region(r)
	if cr == nil {
		return ""
	}

	return cr.KeyName
}

// BUG uses the first region config with right name
func (c *C) Ec2SecurityGroups(r aws.Region) []awsec2.SecurityGroup {
	cr := c.Ec2Region(r)
	if cr == nil {
		return nil
	}

	n := len(cr.SecurityGroups)
	if n == 0 {
		return nil
	}

	sgs := make([]awsec2.SecurityGroup, n)
	for i, sg := range cr.SecurityGroups {
		if sg == (Ec2SecurityGroup{}) {
			continue
		}
		sgs[i].Id = sg.Id
		sgs[i].Name = sg.Name
	}

	return sgs
}

// like c.AwsKey() but returns an aws.Auth type
func (c *C) AwsAuth() (aws.Auth, error) {
	key, err := c.AwsKey()
	if err != nil {
		return aws.Auth{}, err
	}
	auth := aws.Auth{
		AccessKey: key.AccessKey,
		SecretKey: key.SecretKey,
	}
	return auth, nil
}

// unmarshal the json data stored in c.AwsKeyPath into a new AwsKey. return
// any error encountered.
func (c *C) AwsKey() (*AwsKey, error) {
	// TODO stat and warn if permissions are not strict

	keyp, err := ioutil.ReadFile(c.AwsKeyPath)
	if err != nil {
		return nil, err
	}

	var k AwsKey
	err = json.Unmarshal(keyp, &k)
	if err != nil {
		return nil, err
	}

	if k.AccessKey == "" {
		return nil, fmt.Errorf("missing AccessKey")
	}

	if k.SecretKey == "" {
		return nil, fmt.Errorf("missing SecretKey")
	}

	return &k, nil
}

// unmarshal a packer file by name. see c.Packers() for details about
// names.
func (c *C) PackerManifest(name string) (*PackerManifest, error) {
	var p PackerManifest
	ppath := filepath.Join(c.Packer.ManifestDir, name+".json")
	pp, err := ioutil.ReadFile(ppath)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(pp, &p)
	if err != nil {
		return nil, err
	}

	return &p, nil
}

// return the name of packer manifests in c.PackerDir. the name of the
// manifest is the file basename (without the ".json" extension).
func (c *C) PackerManifestNames() ([]string, error) {
	ps, err := filepath.Glob(filepath.Join(c.Packer.ManifestDir, "*.json"))
	if err != nil {
		return nil, err
	}
	for i := range ps {
		ps[i] = strings.TrimSuffix(filepath.Base(ps[i]), ".json")
	}
	return ps, nil
}

type AwsKey struct {
	AccessKey string
	SecretKey string
}

type PackerManifest struct {
	Vars         *jsontree.JsonTree
	Builders     []*jsontree.JsonTree
	Provisioners []*jsontree.JsonTree
}
<commit_msg>godoc<commit_after>// Copyright 2014, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// oticonfig.go [created: Thu, 20 Mar 2014]

/*
package oticonfig configuration for the oti command.

Essentials

technically all you really need to get going are aws credentials. if you save
them in aws_credentials.json then oti will find them automatically (use 0400
credentials on the file)

	{ "AccessKey": "xxxxxxxxxxxx", "SecretKey": "yyyyyyyyyyyy", }

see the AwsKey type for details

Configuration file

a configuration file can make repeated use much easier. create a file
containing a json object. by default, oti looks for a file called oti.json.

see the C type for details on the configuration format.

*/
package oticonfig

import (
	"github.com/bmatsuo/go-jsontree"
	"github.com/bmatsuo/oti/otitag"
	"github.com/crowdmob/goamz/aws"
	awsec2 "github.com/crowdmob/goamz/ec2"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
)

// the json configuration for oti
type C struct {
	// packer manifest configuration
	Packer Packer `json:",omitempty"`

	// metadata allowing usage of tags on images
	Images Images

	// see func (c *C) AwsKey()
	AwsKeyPath string `json:",omitempty"`

	// default Ec2 deployment configurations
	Ec2 Ec2 `json:",omitempty"`
}

type Packer struct {
	// a directory containing packer manifests.
	// see func (c *C) Packer(string)
	ManifestDir string `json:",omitempty"`
}

type Images struct {
	// tag identifying the service(s) provided by an image
	NameTag string `json:",omitempty"`

	// tag identifying the date an image was created
	BuildDateTag string `json:",omitempty"`

	// tag containing a semantic version for the image
	VersionTag string `json:",omitempty"` // not used
}

type Ec2 struct {
	// see func (c *C) Ec2Tag(otitag.OTITag)
	TagPrefix string `json:",omitempty"`

	// region specific configuration
	Regions []Ec2Region
}

type Ec2Region struct {
	// a unique identifier for the object. required if more than one region
	// profile is defined for the same RegionName.
	Id string `json:",omitempty"`

	// an ec2 canonical region name (e.g. "us-east-1"). required
	RegionName string

	// ec2 key name (recommended). overrideable per instance
	KeyName string `json:",omitempty"`

	// security groups. additional groups can be added per instance.
	SecurityGroups []Ec2SecurityGroup `json:",omitempty"`
}

// security groups with neither Id or Name are ignored.
type Ec2SecurityGroup struct {
	Id   string `json:",omitempty"`
	Name string `json:",omitempty"`
}

// unmarshal json data stored at path into c. any error encountered is returned.
func Read(path string, c *C) error {
	configp, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	err = json.Unmarshal(configp, c)
	if err != nil {
		return err
	}
	return nil
}

// returns name prefixed with c.TagPrefix
func (c *C) Ec2Tag(tag otitag.OTITag) string {
	return c.Ec2.TagPrefix + string(tag)
}

// returns the first region config with RegionName equal to r.Name
func (c *C) Ec2Region(r aws.Region) *Ec2Region {
	for _, cr := range c.Ec2.Regions {
		if cr.RegionName == r.Name {
			return &cr
		}
	}
	return nil
}

// BUG uses the first region config with right name
func (c *C) Ec2KeyName(r aws.Region) string {
	cr := c.Ec2Region(r)
	if cr == nil {
		return ""
	}

	return cr.KeyName
}

// BUG uses the first region config with right name
func (c *C) Ec2SecurityGroups(r aws.Region) []awsec2.SecurityGroup {
	cr := c.Ec2Region(r)
	if cr == nil {
		return nil
	}

	n := len(cr.SecurityGroups)
	if n == 0 {
		return nil
	}

	sgs := make([]awsec2.SecurityGroup, n)
	for i, sg := range cr.SecurityGroups {
		if sg == (Ec2SecurityGroup{}) {
			continue
		}
		sgs[i].Id = sg.Id
		sgs[i].Name = sg.Name
	}

	return sgs
}

// like c.AwsKey() but returns an aws.Auth type
func (c *C) AwsAuth() (aws.Auth, error) {
	key, err := c.AwsKey()
	if err != nil {
		return aws.Auth{}, err
	}
	auth := aws.Auth{
		AccessKey: key.AccessKey,
		SecretKey: key.SecretKey,
	}
	return auth, nil
}

// unmarshal the json data stored in c.AwsKeyPath into a new AwsKey. return
// any error encountered.
func (c *C) AwsKey() (*AwsKey, error) {
	// TODO stat and warn if permissions are not strict

	keyp, err := ioutil.ReadFile(c.AwsKeyPath)
	if err != nil {
		return nil, err
	}

	var k AwsKey
	err = json.Unmarshal(keyp, &k)
	if err != nil {
		return nil, err
	}

	if k.AccessKey == "" {
		return nil, fmt.Errorf("missing AccessKey")
	}

	if k.SecretKey == "" {
		return nil, fmt.Errorf("missing SecretKey")
	}

	return &k, nil
}

// unmarshal a packer file by name. see c.Packers() for details about
// names.
func (c *C) PackerManifest(name string) (*PackerManifest, error) {
	var p PackerManifest
	ppath := filepath.Join(c.Packer.ManifestDir, name+".json")
	pp, err := ioutil.ReadFile(ppath)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(pp, &p)
	if err != nil {
		return nil, err
	}

	return &p, nil
}

// return the name of packer manifests in c.PackerDir. the name of the
// manifest is the file basename (without the ".json" extension).
func (c *C) PackerManifestNames() ([]string, error) {
	ps, err := filepath.Glob(filepath.Join(c.Packer.ManifestDir, "*.json"))
	if err != nil {
		return nil, err
	}
	for i := range ps {
		ps[i] = strings.TrimSuffix(filepath.Base(ps[i]), ".json")
	}
	return ps, nil
}

type AwsKey struct {
	AccessKey string
	SecretKey string
}

type PackerManifest struct {
	Vars         *jsontree.JsonTree
	Builders     []*jsontree.JsonTree
	Provisioners []*jsontree.JsonTree
}
<|endoftext|>


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

package collector

import (
	"context"
	"reflect"
	"time"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/search"
	"github.com/blevesearch/bleve/size"
)

var reflectStaticSizeTopNCollector int

func init() {
	var coll TopNCollector
	reflectStaticSizeTopNCollector = int(reflect.TypeOf(coll).Size())
}

type collectorStore interface {
	// Add the document, and if the new store size exceeds the provided size
	// the last element is removed and returned.  If the size has not been
	// exceeded, nil is returned.
	AddNotExceedingSize(doc *search.DocumentMatch, size int) *search.DocumentMatch

	Final(skip int, fixup collectorFixup) (search.DocumentMatchCollection, error)
}

// PreAllocSizeSkipCap will cap preallocation to this amount when
// size+skip exceeds this value
var PreAllocSizeSkipCap = 1000

type collectorCompare func(i, j *search.DocumentMatch) int

type collectorFixup func(d *search.DocumentMatch) error

// TopNCollector collects the top N hits, optionally skipping some results
type TopNCollector struct {
	size          int
	skip          int
	total         uint64
	maxScore      float64
	took          time.Duration
	sort          search.SortOrder
	results       search.DocumentMatchCollection
	facetsBuilder *search.FacetsBuilder

	store collectorStore

	needDocIds    bool
	neededFields  []string
	cachedScoring []bool
	cachedDesc    []bool

	lowestMatchOutsideResults *search.DocumentMatch
}

// CheckDoneEvery controls how frequently we check the context deadline
const CheckDoneEvery = uint64(1024)

// NewTopNCollector builds a collector to find the top 'size' hits
// skipping over the first 'skip' hits
// ordering hits by the provided sort order
func NewTopNCollector(size int, skip int, sort search.SortOrder) *TopNCollector {
	hc := &TopNCollector{size: size, skip: skip, sort: sort}

	// pre-allocate space on the store to avoid reslicing
	// unless the size + skip is too large, then cap it
	// everything should still work, just reslices as necessary
	backingSize := size + skip + 1
	if size+skip > PreAllocSizeSkipCap {
		backingSize = PreAllocSizeSkipCap + 1
	}

	if size+skip > 10 {
		hc.store = newStoreHeap(backingSize, func(i, j *search.DocumentMatch) int {
			return hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, i, j)
		})
	} else {
		hc.store = newStoreSlice(backingSize, func(i, j *search.DocumentMatch) int {
			return hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, i, j)
		})
	}

	// these lookups traverse an interface, so do once up-front
	if sort.RequiresDocID() {
		hc.needDocIds = true
	}
	hc.neededFields = sort.RequiredFields()
	hc.cachedScoring = sort.CacheIsScore()
	hc.cachedDesc = sort.CacheDescending()

	return hc
}

func (hc *TopNCollector) Size() int {
	sizeInBytes := reflectStaticSizeTopNCollector + size.SizeOfPtr

	if hc.facetsBuilder != nil {
		sizeInBytes += hc.facetsBuilder.Size()
	}

	for _, entry := range hc.neededFields {
		sizeInBytes += len(entry) + size.SizeOfString
	}

	sizeInBytes += len(hc.cachedScoring) + len(hc.cachedDesc)

	return sizeInBytes
}

// Collect goes to the index to find the matching documents
func (hc *TopNCollector) Collect(ctx context.Context, searcher search.Searcher, reader index.IndexReader) error {
	startTime := time.Now()
	var err error
	var next *search.DocumentMatch

	// pre-allocate enough space in the DocumentMatchPool
	// unless the size + skip is too large, then cap it
	// everything should still work, just allocates DocumentMatches on demand
	backingSize := hc.size + hc.skip + 1
	if hc.size+hc.skip > PreAllocSizeSkipCap {
		backingSize = PreAllocSizeSkipCap + 1
	}
	searchContext := &search.SearchContext{
		DocumentMatchPool: search.NewDocumentMatchPool(backingSize+searcher.DocumentMatchPoolSize(), len(hc.sort)),
	}

	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		next, err = searcher.Next(searchContext)
	}
	for err == nil && next != nil {
		if hc.total%CheckDoneEvery == 0 {
			select {
			case <-ctx.Done():
				return ctx.Err()
			default:
			}
		}

		err = hc.collectSingle(searchContext, reader, next)
		if err != nil {
			break
		}

		next, err = searcher.Next(searchContext)
	}
	// compute search duration
	hc.took = time.Since(startTime)
	if err != nil {
		return err
	}
	// finalize actual results
	err = hc.finalizeResults(reader)
	if err != nil {
		return err
	}
	return nil
}

var sortByScoreOpt = []string{"_score"}

func (hc *TopNCollector) collectSingle(ctx *search.SearchContext, reader index.IndexReader, d *search.DocumentMatch) error {
	var err error

	// visit field terms for features that require it (sort, facets)
	if len(hc.neededFields) > 0 {
		err = hc.visitFieldTerms(reader, d)
		if err != nil {
			return err
		}
	}

	// increment total hits
	hc.total++
	d.HitNumber = hc.total

	// update max score
	if d.Score > hc.maxScore {
		hc.maxScore = d.Score
	}

	// see if we need to load ID (at this early stage, for example to sort on it)
	if hc.needDocIds {
		d.ID, err = reader.ExternalID(d.IndexInternalID)
		if err != nil {
			return err
		}
	}

	// compute this hits sort value
	if len(hc.sort) == 1 && hc.cachedScoring[0] {
		d.Sort = sortByScoreOpt
	} else {
		hc.sort.Value(d)
	}

	// optimization, we track lowest sorting hit already removed from heap
	// with this one comparison, we can avoid all heap operations if
	// this hit would have been added and then immediately removed
	if hc.lowestMatchOutsideResults != nil {
		cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, d, hc.lowestMatchOutsideResults)
		if cmp >= 0 {
			// this hit can't possibly be in the result set, so avoid heap ops
			ctx.DocumentMatchPool.Put(d)
			return nil
		}
	}

	removed := hc.store.AddNotExceedingSize(d, hc.size+hc.skip)
	if removed != nil {
		if hc.lowestMatchOutsideResults == nil {
			hc.lowestMatchOutsideResults = removed
		} else {
			cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, removed, hc.lowestMatchOutsideResults)
			if cmp < 0 {
				tmp := hc.lowestMatchOutsideResults
				hc.lowestMatchOutsideResults = removed
				ctx.DocumentMatchPool.Put(tmp)
			}
		}
	}

	return nil
}

// visitFieldTerms is responsible for visiting the field terms of the
// search hit, and passing visited terms to the sort and facet builder
func (hc *TopNCollector) visitFieldTerms(reader index.IndexReader, d *search.DocumentMatch) error {
	if hc.facetsBuilder != nil {
		hc.facetsBuilder.StartDoc()
	}

	err := reader.DocumentVisitFieldTerms(d.IndexInternalID, hc.neededFields, func(field string, term []byte) {
		if hc.facetsBuilder != nil {
			hc.facetsBuilder.UpdateVisitor(field, term)
		}
		hc.sort.UpdateVisitor(field, term)
	})

	if hc.facetsBuilder != nil {
		hc.facetsBuilder.EndDoc()
	}

	return err
}

// SetFacetsBuilder registers a facet builder for this collector
func (hc *TopNCollector) SetFacetsBuilder(facetsBuilder *search.FacetsBuilder) {
	hc.facetsBuilder = facetsBuilder
	hc.neededFields = append(hc.neededFields, hc.facetsBuilder.RequiredFields()...)
}

// finalizeResults starts with the heap containing the final top size+skip
// it now throws away the results to be skipped
// and does final doc id lookup (if necessary)
func (hc *TopNCollector) finalizeResults(r index.IndexReader) error {
	var err error
	hc.results, err = hc.store.Final(hc.skip, func(doc *search.DocumentMatch) error {
		if doc.ID == "" {
			// look up the id since we need it for lookup
			var err error
			doc.ID, err = r.ExternalID(doc.IndexInternalID)
			if err != nil {
				return err
			}
		}
		return nil
	})

	return err
}

// Results returns the collected hits
func (hc *TopNCollector) Results() search.DocumentMatchCollection {
	return hc.results
}

// Total returns the total number of hits
func (hc *TopNCollector) Total() uint64 {
	return hc.total
}

// MaxScore returns the maximum score seen across all the hits
func (hc *TopNCollector) MaxScore() float64 {
	return hc.maxScore
}

// Took returns the time spent collecting hits
func (hc *TopNCollector) Took() time.Duration {
	return hc.took
}

// FacetResults returns the computed facets results
func (hc *TopNCollector) FacetResults() search.FacetResults {
	if hc.facetsBuilder != nil {
		return hc.facetsBuilder.Results()
	}
	return search.FacetResults{}
}
<commit_msg>optimize to return nil facet results when there's no FacetBuilder<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package collector

import (
	"context"
	"reflect"
	"time"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/search"
	"github.com/blevesearch/bleve/size"
)

var reflectStaticSizeTopNCollector int

func init() {
	var coll TopNCollector
	reflectStaticSizeTopNCollector = int(reflect.TypeOf(coll).Size())
}

type collectorStore interface {
	// Add the document, and if the new store size exceeds the provided size
	// the last element is removed and returned.  If the size has not been
	// exceeded, nil is returned.
	AddNotExceedingSize(doc *search.DocumentMatch, size int) *search.DocumentMatch

	Final(skip int, fixup collectorFixup) (search.DocumentMatchCollection, error)
}

// PreAllocSizeSkipCap will cap preallocation to this amount when
// size+skip exceeds this value
var PreAllocSizeSkipCap = 1000

type collectorCompare func(i, j *search.DocumentMatch) int

type collectorFixup func(d *search.DocumentMatch) error

// TopNCollector collects the top N hits, optionally skipping some results
type TopNCollector struct {
	size          int
	skip          int
	total         uint64
	maxScore      float64
	took          time.Duration
	sort          search.SortOrder
	results       search.DocumentMatchCollection
	facetsBuilder *search.FacetsBuilder

	store collectorStore

	needDocIds    bool
	neededFields  []string
	cachedScoring []bool
	cachedDesc    []bool

	lowestMatchOutsideResults *search.DocumentMatch
}

// CheckDoneEvery controls how frequently we check the context deadline
const CheckDoneEvery = uint64(1024)

// NewTopNCollector builds a collector to find the top 'size' hits
// skipping over the first 'skip' hits
// ordering hits by the provided sort order
func NewTopNCollector(size int, skip int, sort search.SortOrder) *TopNCollector {
	hc := &TopNCollector{size: size, skip: skip, sort: sort}

	// pre-allocate space on the store to avoid reslicing
	// unless the size + skip is too large, then cap it
	// everything should still work, just reslices as necessary
	backingSize := size + skip + 1
	if size+skip > PreAllocSizeSkipCap {
		backingSize = PreAllocSizeSkipCap + 1
	}

	if size+skip > 10 {
		hc.store = newStoreHeap(backingSize, func(i, j *search.DocumentMatch) int {
			return hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, i, j)
		})
	} else {
		hc.store = newStoreSlice(backingSize, func(i, j *search.DocumentMatch) int {
			return hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, i, j)
		})
	}

	// these lookups traverse an interface, so do once up-front
	if sort.RequiresDocID() {
		hc.needDocIds = true
	}
	hc.neededFields = sort.RequiredFields()
	hc.cachedScoring = sort.CacheIsScore()
	hc.cachedDesc = sort.CacheDescending()

	return hc
}

func (hc *TopNCollector) Size() int {
	sizeInBytes := reflectStaticSizeTopNCollector + size.SizeOfPtr

	if hc.facetsBuilder != nil {
		sizeInBytes += hc.facetsBuilder.Size()
	}

	for _, entry := range hc.neededFields {
		sizeInBytes += len(entry) + size.SizeOfString
	}

	sizeInBytes += len(hc.cachedScoring) + len(hc.cachedDesc)

	return sizeInBytes
}

// Collect goes to the index to find the matching documents
func (hc *TopNCollector) Collect(ctx context.Context, searcher search.Searcher, reader index.IndexReader) error {
	startTime := time.Now()
	var err error
	var next *search.DocumentMatch

	// pre-allocate enough space in the DocumentMatchPool
	// unless the size + skip is too large, then cap it
	// everything should still work, just allocates DocumentMatches on demand
	backingSize := hc.size + hc.skip + 1
	if hc.size+hc.skip > PreAllocSizeSkipCap {
		backingSize = PreAllocSizeSkipCap + 1
	}
	searchContext := &search.SearchContext{
		DocumentMatchPool: search.NewDocumentMatchPool(backingSize+searcher.DocumentMatchPoolSize(), len(hc.sort)),
	}

	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		next, err = searcher.Next(searchContext)
	}
	for err == nil && next != nil {
		if hc.total%CheckDoneEvery == 0 {
			select {
			case <-ctx.Done():
				return ctx.Err()
			default:
			}
		}

		err = hc.collectSingle(searchContext, reader, next)
		if err != nil {
			break
		}

		next, err = searcher.Next(searchContext)
	}
	// compute search duration
	hc.took = time.Since(startTime)
	if err != nil {
		return err
	}
	// finalize actual results
	err = hc.finalizeResults(reader)
	if err != nil {
		return err
	}
	return nil
}

var sortByScoreOpt = []string{"_score"}

func (hc *TopNCollector) collectSingle(ctx *search.SearchContext, reader index.IndexReader, d *search.DocumentMatch) error {
	var err error

	// visit field terms for features that require it (sort, facets)
	if len(hc.neededFields) > 0 {
		err = hc.visitFieldTerms(reader, d)
		if err != nil {
			return err
		}
	}

	// increment total hits
	hc.total++
	d.HitNumber = hc.total

	// update max score
	if d.Score > hc.maxScore {
		hc.maxScore = d.Score
	}

	// see if we need to load ID (at this early stage, for example to sort on it)
	if hc.needDocIds {
		d.ID, err = reader.ExternalID(d.IndexInternalID)
		if err != nil {
			return err
		}
	}

	// compute this hits sort value
	if len(hc.sort) == 1 && hc.cachedScoring[0] {
		d.Sort = sortByScoreOpt
	} else {
		hc.sort.Value(d)
	}

	// optimization, we track lowest sorting hit already removed from heap
	// with this one comparison, we can avoid all heap operations if
	// this hit would have been added and then immediately removed
	if hc.lowestMatchOutsideResults != nil {
		cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, d, hc.lowestMatchOutsideResults)
		if cmp >= 0 {
			// this hit can't possibly be in the result set, so avoid heap ops
			ctx.DocumentMatchPool.Put(d)
			return nil
		}
	}

	removed := hc.store.AddNotExceedingSize(d, hc.size+hc.skip)
	if removed != nil {
		if hc.lowestMatchOutsideResults == nil {
			hc.lowestMatchOutsideResults = removed
		} else {
			cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, removed, hc.lowestMatchOutsideResults)
			if cmp < 0 {
				tmp := hc.lowestMatchOutsideResults
				hc.lowestMatchOutsideResults = removed
				ctx.DocumentMatchPool.Put(tmp)
			}
		}
	}

	return nil
}

// visitFieldTerms is responsible for visiting the field terms of the
// search hit, and passing visited terms to the sort and facet builder
func (hc *TopNCollector) visitFieldTerms(reader index.IndexReader, d *search.DocumentMatch) error {
	if hc.facetsBuilder != nil {
		hc.facetsBuilder.StartDoc()
	}

	err := reader.DocumentVisitFieldTerms(d.IndexInternalID, hc.neededFields, func(field string, term []byte) {
		if hc.facetsBuilder != nil {
			hc.facetsBuilder.UpdateVisitor(field, term)
		}
		hc.sort.UpdateVisitor(field, term)
	})

	if hc.facetsBuilder != nil {
		hc.facetsBuilder.EndDoc()
	}

	return err
}

// SetFacetsBuilder registers a facet builder for this collector
func (hc *TopNCollector) SetFacetsBuilder(facetsBuilder *search.FacetsBuilder) {
	hc.facetsBuilder = facetsBuilder
	hc.neededFields = append(hc.neededFields, hc.facetsBuilder.RequiredFields()...)
}

// finalizeResults starts with the heap containing the final top size+skip
// it now throws away the results to be skipped
// and does final doc id lookup (if necessary)
func (hc *TopNCollector) finalizeResults(r index.IndexReader) error {
	var err error
	hc.results, err = hc.store.Final(hc.skip, func(doc *search.DocumentMatch) error {
		if doc.ID == "" {
			// look up the id since we need it for lookup
			var err error
			doc.ID, err = r.ExternalID(doc.IndexInternalID)
			if err != nil {
				return err
			}
		}
		return nil
	})

	return err
}

// Results returns the collected hits
func (hc *TopNCollector) Results() search.DocumentMatchCollection {
	return hc.results
}

// Total returns the total number of hits
func (hc *TopNCollector) Total() uint64 {
	return hc.total
}

// MaxScore returns the maximum score seen across all the hits
func (hc *TopNCollector) MaxScore() float64 {
	return hc.maxScore
}

// Took returns the time spent collecting hits
func (hc *TopNCollector) Took() time.Duration {
	return hc.took
}

// FacetResults returns the computed facets results
func (hc *TopNCollector) FacetResults() search.FacetResults {
	if hc.facetsBuilder != nil {
		return hc.facetsBuilder.Results()
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"

	"github.com/domestic-apps/domestic-api/chores"
	"github.com/domestic-apps/domestic-api/tasks"
)

type secrets struct {
	Uname string `json:"username"`
	Pwd   string `json:"password"`
}

func main() {
	// get mysql username and password from configuration
	file, err := ioutil.ReadFile("./secrets.json")
	if err != nil {
		log.Fatal("File error: %v\n", err)
	}

	var s secrets
	json.Unmarshal(file, &s)

	// Set up Database
	db, err := sql.Open("mysql",
		s.Uname+":"+s.Pwd+"@tcp(localhost:3306)/domestic?parseTime=true")
	if err != nil {
		log.Fatal(err)
	}
	if err = db.Ping(); err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Prepare statements in application handlers.
	choresHandler := chores.InitializeHandler(db)
	tasksHandler := tasks.InitializeHandler(db)
	http.HandleFunc("/chores/", choresHandler.Handle)
	http.HandleFunc("/tasks/", tasksHandler.Handle)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>try out doing a task insert into chores.<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"

	"github.com/domestic-apps/domestic-api/chores"
	"github.com/domestic-apps/domestic-api/tasks"
)

type secrets struct {
	Uname string `json:"username"`
	Pwd   string `json:"password"`
}

func main() {
	// get mysql username and password from configuration
	file, err := ioutil.ReadFile("./secrets.json")
	if err != nil {
		log.Fatal("File error: %v\n", err)
	}

	var s secrets
	json.Unmarshal(file, &s)

	// Set up Database
	db, err := sql.Open("mysql",
		s.Uname+":"+s.Pwd+"@tcp(localhost:3306)/domestic?parseTime=true")
	if err != nil {
		log.Fatal(err)
	}
	if err = db.Ping(); err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Prepare statements in application handlers.
	choresHandler := chores.InitializeHandler(db)
	tasksHandler := tasks.InitializeHandler(db)
	http.HandleFunc("/chores/", choresHandler.Handle)
	http.HandleFunc("/tasks/", tasksHandler.Handle)
	// log.Fatal(http.ListenAndServe(":8080", nil))

	// Let's try doing a database changey thing!
	currentTime := time.Now() // We'll query the chores for things at this time.
	//
	stmt, err := db.Prepare("INSERT INTO chores(chore_id, c_time) SELECT (chore_id, NULL) from chores where ? = true AND ((dwm = 'd') OR (dwm = 'w' AND day = ?) OR (dwm = 'm' AND date = ?))")
	stmt.Exec("morning", currentTime.Weekday, currentTime.day)
}
<|endoftext|>