<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math/rand"
	"os"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/transport"
)

// CallOption configures a Call before it starts or extracts information from
// a Call after it completes.
type CallOption interface {
	// before is called before the call is sent to any server.  If before
	// returns a non-nil error, the RPC fails with that error.
	before(*callInfo) error

	// after is called after the call has completed.  after cannot return an
	// error, so any failures should be reported via output parameters.
	after(*callInfo)
}

type beforeCall func(c *callInfo) error

func (o beforeCall) before(c *callInfo) error { return o(c) }
func (o beforeCall) after(c *callInfo)        {}

type afterCall func(c *callInfo)

func (o afterCall) before(c *callInfo) error { return nil }
func (o afterCall) after(c *callInfo)        { o(c) }

// Header returns a CallOptions that retrieves the header metadata
// for a unary RPC.
func Header(md *metadata.MD) CallOption {
	return afterCall(func(c *callInfo) {
		*md = c.headerMD
	})
}

// Trailer returns a CallOptions that retrieves the trailer metadata
// for a unary RPC.
func Trailer(md *metadata.MD) CallOption {
	return afterCall(func(c *callInfo) {
		*md = c.trailerMD
	})
}

// The format of the payload: compressed or not?
type payloadFormat uint8

const (
	compressionNone payloadFormat = iota // no compression
	compressionFlate
	// More formats
)

// parser reads complelete gRPC messages from the underlying reader.
type parser struct {
	s io.Reader
}

// msgFixedHeader defines the header of a gRPC message (go/grpc-wirefmt).
type msgFixedHeader struct {
	T      payloadFormat
	Length uint32
}

// recvMsg is to read a complete gRPC message from the stream. It is blocking if
// the message has not been complete yet. It returns the message and its type,
// EOF is returned with nil msg and 0 pf if the entire stream is done. Other
// non-nil error is returned if something is wrong on reading.
func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
	var hdr msgFixedHeader
	if err := binary.Read(p.s, binary.BigEndian, &hdr); err != nil {
		return 0, nil, err
	}
	if hdr.Length == 0 {
		return hdr.T, nil, nil
	}
	msg = make([]byte, int(hdr.Length))
	if _, err := io.ReadFull(p.s, msg); err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		return 0, nil, err
	}
	return hdr.T, msg, nil
}

// encode serializes msg and prepends the message header. If msg is nil, it
// generates the message header of 0 message length.
func encode(msg proto.Message, pf payloadFormat) ([]byte, error) {
	var buf bytes.Buffer
	// Write message fixed header.
	if err := buf.WriteByte(uint8(pf)); err != nil {
		return nil, err
	}
	var b []byte
	var length uint32
	if msg != nil {
		var err error
		// TODO(zhaoq): optimize to reduce memory alloc and copying.
		b, err = proto.Marshal(msg)
		if err != nil {
			return nil, err
		}
		length = uint32(len(b))
	}
	var szHdr [4]byte
	binary.BigEndian.PutUint32(szHdr[:], length)
	if _, err := buf.Write(szHdr[:]); err != nil {
		return nil, err
	}
	if _, err := buf.Write(b); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func recvProto(p *parser, m proto.Message) error {
	pf, d, err := p.recvMsg()
	if err != nil {
		return err
	}
	switch pf {
	case compressionNone:
		if err := proto.Unmarshal(d, m); err != nil {
			return Errorf(codes.Internal, "grpc: %v", err)
		}
	default:
		return Errorf(codes.Internal, "gprc: compression is not supported yet.")
	}
	return nil
}

// rpcError defines the status from an RPC.
type rpcError struct {
	code codes.Code
	desc string
}

func (e rpcError) Error() string {
	return fmt.Sprintf("rpc error: code = %d desc = %q", e.code, e.desc)
}

// Code returns the error code for err if it was produced by the rpc system.
// Otherwise, it returns codes.Unknown.
func Code(err error) codes.Code {
	if e, ok := err.(rpcError); ok {
		return e.code
	}
	return codes.Unknown
}

// Errorf returns an error containing an error code and a description;
// CodeOf extracts the Code.
// Errorf returns nil if c is OK.
func Errorf(c codes.Code, format string, a ...interface{}) error {
	if c == codes.OK {
		return nil
	}
	return rpcError{
		code: c,
		desc: fmt.Sprintf(format, a...),
	}
}

// toRPCErr converts a transport error into a rpcError if possible.
func toRPCErr(err error) error {
	switch e := err.(type) {
	case transport.StreamError:
		return rpcError{
			code: e.Code,
			desc: e.Desc,
		}
	case transport.ConnectionError:
		return rpcError{
			code: codes.Internal,
			desc: e.Desc,
		}
	}
	return Errorf(codes.Unknown, "grpc: failed to convert %v to rpcErr", err)
}

// convertCode converts a standard Go error into its canonical code. Note that
// this is only used to translate the error returned by the server applications.
func convertCode(err error) codes.Code {
	switch err {
	case nil:
		return codes.OK
	case io.EOF:
		return codes.OutOfRange
	case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
		return codes.FailedPrecondition
	case os.ErrInvalid:
		return codes.InvalidArgument
	case context.Canceled:
		return codes.Canceled
	case context.DeadlineExceeded:
		return codes.DeadlineExceeded
	}
	switch {
	case os.IsExist(err):
		return codes.AlreadyExists
	case os.IsNotExist(err):
		return codes.NotFound
	case os.IsPermission(err):
		return codes.PermissionDenied
	}
	return codes.Unknown
}

const (
	// how long to wait after the first failure before retrying
	baseDelay = 1.0 * time.Second
	// upper bound on backoff delay
	maxDelay      = 120 * time.Second
	backoffFactor = 2.0 // backoff increases by this factor on each retry
	backoffRange  = 0.4 // backoff is randomized downwards by this factor
)

// backoff returns a value in [0, maxDelay] that increases exponentially with
// retries, starting from baseDelay.
func backoff(retries int) time.Duration {
	backoff, max := float64(baseDelay), float64(maxDelay)
	for backoff < max && retries > 0 {
		backoff = backoff * backoffFactor
		retries--
	}
	if backoff > max {
		backoff = max
	}

	// Randomize backoff delays so that if a cluster of requests start at
	// the same time, they won't operate in lockstep.  We just subtract up
	// to 40% so that we obey maxDelay.
	backoff -= backoff * backoffRange * rand.Float64()
	if backoff < 0 {
		return 0
	}
	return time.Duration(backoff)
}
<commit_msg>remove err checking because bytes.Buffer.Write and bytes.Buffer.WriteByte always return nil<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math/rand"
	"os"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/transport"
)

// CallOption configures a Call before it starts or extracts information from
// a Call after it completes.
type CallOption interface {
	// before is called before the call is sent to any server.  If before
	// returns a non-nil error, the RPC fails with that error.
	before(*callInfo) error

	// after is called after the call has completed.  after cannot return an
	// error, so any failures should be reported via output parameters.
	after(*callInfo)
}

type beforeCall func(c *callInfo) error

func (o beforeCall) before(c *callInfo) error { return o(c) }
func (o beforeCall) after(c *callInfo)        {}

type afterCall func(c *callInfo)

func (o afterCall) before(c *callInfo) error { return nil }
func (o afterCall) after(c *callInfo)        { o(c) }

// Header returns a CallOptions that retrieves the header metadata
// for a unary RPC.
func Header(md *metadata.MD) CallOption {
	return afterCall(func(c *callInfo) {
		*md = c.headerMD
	})
}

// Trailer returns a CallOptions that retrieves the trailer metadata
// for a unary RPC.
func Trailer(md *metadata.MD) CallOption {
	return afterCall(func(c *callInfo) {
		*md = c.trailerMD
	})
}

// The format of the payload: compressed or not?
type payloadFormat uint8

const (
	compressionNone payloadFormat = iota // no compression
	compressionFlate
	// More formats
)

// parser reads complelete gRPC messages from the underlying reader.
type parser struct {
	s io.Reader
}

// msgFixedHeader defines the header of a gRPC message (go/grpc-wirefmt).
type msgFixedHeader struct {
	T      payloadFormat
	Length uint32
}

// recvMsg is to read a complete gRPC message from the stream. It is blocking if
// the message has not been complete yet. It returns the message and its type,
// EOF is returned with nil msg and 0 pf if the entire stream is done. Other
// non-nil error is returned if something is wrong on reading.
func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
	var hdr msgFixedHeader
	if err := binary.Read(p.s, binary.BigEndian, &hdr); err != nil {
		return 0, nil, err
	}
	if hdr.Length == 0 {
		return hdr.T, nil, nil
	}
	msg = make([]byte, int(hdr.Length))
	if _, err := io.ReadFull(p.s, msg); err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		return 0, nil, err
	}
	return hdr.T, msg, nil
}

// encode serializes msg and prepends the message header. If msg is nil, it
// generates the message header of 0 message length.
func encode(msg proto.Message, pf payloadFormat) ([]byte, error) {
	var buf bytes.Buffer
	// Write message fixed header.
	buf.WriteByte(uint8(pf))
	var b []byte
	var length uint32
	if msg != nil {
		var err error
		// TODO(zhaoq): optimize to reduce memory alloc and copying.
		b, err = proto.Marshal(msg)
		if err != nil {
			return nil, err
		}
		length = uint32(len(b))
	}
	var szHdr [4]byte
	binary.BigEndian.PutUint32(szHdr[:], length)
	buf.Write(szHdr[:])
	buf.Write(b)
	return buf.Bytes(), nil
}

func recvProto(p *parser, m proto.Message) error {
	pf, d, err := p.recvMsg()
	if err != nil {
		return err
	}
	switch pf {
	case compressionNone:
		if err := proto.Unmarshal(d, m); err != nil {
			return Errorf(codes.Internal, "grpc: %v", err)
		}
	default:
		return Errorf(codes.Internal, "gprc: compression is not supported yet.")
	}
	return nil
}

// rpcError defines the status from an RPC.
type rpcError struct {
	code codes.Code
	desc string
}

func (e rpcError) Error() string {
	return fmt.Sprintf("rpc error: code = %d desc = %q", e.code, e.desc)
}

// Code returns the error code for err if it was produced by the rpc system.
// Otherwise, it returns codes.Unknown.
func Code(err error) codes.Code {
	if e, ok := err.(rpcError); ok {
		return e.code
	}
	return codes.Unknown
}

// Errorf returns an error containing an error code and a description;
// CodeOf extracts the Code.
// Errorf returns nil if c is OK.
func Errorf(c codes.Code, format string, a ...interface{}) error {
	if c == codes.OK {
		return nil
	}
	return rpcError{
		code: c,
		desc: fmt.Sprintf(format, a...),
	}
}

// toRPCErr converts a transport error into a rpcError if possible.
func toRPCErr(err error) error {
	switch e := err.(type) {
	case transport.StreamError:
		return rpcError{
			code: e.Code,
			desc: e.Desc,
		}
	case transport.ConnectionError:
		return rpcError{
			code: codes.Internal,
			desc: e.Desc,
		}
	}
	return Errorf(codes.Unknown, "grpc: failed to convert %v to rpcErr", err)
}

// convertCode converts a standard Go error into its canonical code. Note that
// this is only used to translate the error returned by the server applications.
func convertCode(err error) codes.Code {
	switch err {
	case nil:
		return codes.OK
	case io.EOF:
		return codes.OutOfRange
	case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
		return codes.FailedPrecondition
	case os.ErrInvalid:
		return codes.InvalidArgument
	case context.Canceled:
		return codes.Canceled
	case context.DeadlineExceeded:
		return codes.DeadlineExceeded
	}
	switch {
	case os.IsExist(err):
		return codes.AlreadyExists
	case os.IsNotExist(err):
		return codes.NotFound
	case os.IsPermission(err):
		return codes.PermissionDenied
	}
	return codes.Unknown
}

const (
	// how long to wait after the first failure before retrying
	baseDelay = 1.0 * time.Second
	// upper bound on backoff delay
	maxDelay      = 120 * time.Second
	backoffFactor = 2.0 // backoff increases by this factor on each retry
	backoffRange  = 0.4 // backoff is randomized downwards by this factor
)

// backoff returns a value in [0, maxDelay] that increases exponentially with
// retries, starting from baseDelay.
func backoff(retries int) time.Duration {
	backoff, max := float64(baseDelay), float64(maxDelay)
	for backoff < max && retries > 0 {
		backoff = backoff * backoffFactor
		retries--
	}
	if backoff > max {
		backoff = max
	}

	// Randomize backoff delays so that if a cluster of requests start at
	// the same time, they won't operate in lockstep.  We just subtract up
	// to 40% so that we obey maxDelay.
	backoff -= backoff * backoffRange * rand.Float64()
	if backoff < 0 {
		return 0
	}
	return time.Duration(backoff)
}
<|endoftext|>


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

package xhttp

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"gopkg.in/goyy/goyy.v0/util/files"
	"gopkg.in/goyy/goyy.v0/util/strings"
	"gopkg.in/goyy/goyy.v0/util/templates"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
)

var (
	r = &renderer{}
)

type renderer struct {
	t *template.Template
}

func (me *renderer) HTML(w http.ResponseWriter, status int, name string, v interface{}) error {
	if Conf.Template.Reloaded || me.t == nil {
		err := me.compile(Conf.Template)
		if err != nil {
			return err
		}
	}
	me.writeHeader(w, status, "text/html")
	return me.t.ExecuteTemplate(w, name, v)
}

func (me *renderer) JSON(w http.ResponseWriter, status int, v interface{}) error {
	me.writeHeader(w, status, "application/json")
	return json.NewEncoder(w).Encode(v)
}

func (me *renderer) JSONP(w http.ResponseWriter, status int, callback string, v interface{}) error {
	result, err := json.Marshal(v)
	if err != nil {
		return err
	}

	// JSON marshaled fine, write out the result.
	me.writeHeader(w, status, "application/json")
	w.Write([]byte(callback + "("))
	w.Write(result)
	w.Write([]byte(");"))

	return nil
}

func (me *renderer) XML(w http.ResponseWriter, status int, v interface{}) error {
	me.writeHeader(w, status, "application/xml")
	return xml.NewEncoder(w).Encode(v)
}

func (me *renderer) Text(w http.ResponseWriter, status int, format string, values ...interface{}) (err error) {
	me.writeHeader(w, status, "text/plain")
	if len(values) > 0 {
		_, err = w.Write([]byte(fmt.Sprintf(format, values...)))
	} else {
		_, err = w.Write([]byte(format))
	}
	return
}

// Error writes the given HTTP status to the current ResponseWriter
func (me *renderer) Error(w http.ResponseWriter, status int) error {
	me.writeHeader(w, status, "text/html")
	return nil
}

func (me *renderer) Redirect(w http.ResponseWriter, req *http.Request, location string, status ...int) error {
	code := http.StatusFound
	if len(status) == 1 {
		code = status[0]
	}
	http.Redirect(w, req, location, code)
	return nil
}

func (me *renderer) writeHeader(w http.ResponseWriter, status int, contentType string) {
	contentType = contentType + "; charset=utf-8"
	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
}

func (me *renderer) compile(options *templateOptions) error {
	dir := options.Dir
	me.t = template.New(dir)
	me.t.Delims(options.Delims.Left, options.Delims.Right)
	// parse an initial template in case we don't have any
	me.t = template.Must(me.t.Parse("xhttp"))

	// add our funcmaps
	me.t.Funcs(templates.Html.FuncMap)
	for _, funcs := range options.Funcs {
		me.t.Funcs(funcs)
	}

	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		r, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		} else {
			r = strings.Replace(r, "\\", "/", -1)
		}

		ext := files.Extension(r)

		for _, extension := range options.Extensions {
			if ext == extension {
				buf, err := ioutil.ReadFile(path)
				if err != nil {
					panic(err)
				}

				name := (r[0 : len(r)-len(ext)-1])
				me.t = me.t.New(filepath.ToSlash(name))

				// Bomb out if parse fails. We don't want any silent server starts.
				me.t = template.Must(me.t.Parse(string(buf)))
				break
			}
		}
		return nil
	})
	return nil
}

func (me *renderer) execute(name string, binding interface{}) (*bytes.Buffer, error) {
	buf := new(bytes.Buffer)
	return buf, me.t.ExecuteTemplate(buf, name, binding)
}
<commit_msg>Add error log output<commit_after>// Copyright 2014 The goyy Authors.  All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package xhttp

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"gopkg.in/goyy/goyy.v0/util/files"
	"gopkg.in/goyy/goyy.v0/util/strings"
	"gopkg.in/goyy/goyy.v0/util/templates"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
)

var (
	r = &renderer{}
)

type renderer struct {
	t *template.Template
}

func (me *renderer) HTML(w http.ResponseWriter, status int, name string, v interface{}) error {
	if Conf.Template.Reloaded || me.t == nil {
		err := me.compile(Conf.Template)
		if err != nil {
			return err
		}
	}
	me.writeHeader(w, status, "text/html")
	return me.t.ExecuteTemplate(w, name, v)
}

func (me *renderer) JSON(w http.ResponseWriter, status int, v interface{}) error {
	me.writeHeader(w, status, "application/json")
	return json.NewEncoder(w).Encode(v)
}

func (me *renderer) JSONP(w http.ResponseWriter, status int, callback string, v interface{}) error {
	result, err := json.Marshal(v)
	if err != nil {
		logger.Error(err.Error())
		return err
	}

	// JSON marshaled fine, write out the result.
	me.writeHeader(w, status, "application/json")
	w.Write([]byte(callback + "("))
	w.Write(result)
	w.Write([]byte(");"))

	return nil
}

func (me *renderer) XML(w http.ResponseWriter, status int, v interface{}) error {
	me.writeHeader(w, status, "application/xml")
	return xml.NewEncoder(w).Encode(v)
}

func (me *renderer) Text(w http.ResponseWriter, status int, format string, values ...interface{}) (err error) {
	me.writeHeader(w, status, "text/plain")
	if len(values) > 0 {
		_, err = w.Write([]byte(fmt.Sprintf(format, values...)))
	} else {
		_, err = w.Write([]byte(format))
	}
	return
}

// Error writes the given HTTP status to the current ResponseWriter
func (me *renderer) Error(w http.ResponseWriter, status int) error {
	me.writeHeader(w, status, "text/html")
	return nil
}

func (me *renderer) Redirect(w http.ResponseWriter, req *http.Request, location string, status ...int) error {
	code := http.StatusFound
	if len(status) == 1 {
		code = status[0]
	}
	http.Redirect(w, req, location, code)
	return nil
}

func (me *renderer) writeHeader(w http.ResponseWriter, status int, contentType string) {
	contentType = contentType + "; charset=utf-8"
	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
}

func (me *renderer) compile(options *templateOptions) error {
	dir := options.Dir
	me.t = template.New(dir)
	me.t.Delims(options.Delims.Left, options.Delims.Right)
	// parse an initial template in case we don't have any
	me.t = template.Must(me.t.Parse("xhttp"))

	// add our funcmaps
	me.t.Funcs(templates.Html.FuncMap)
	for _, funcs := range options.Funcs {
		me.t.Funcs(funcs)
	}

	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		r, err := filepath.Rel(dir, path)
		if err != nil {
			logger.Error(err.Error())
			return err
		} else {
			r = strings.Replace(r, "\\", "/", -1)
		}

		ext := files.Extension(r)

		for _, extension := range options.Extensions {
			if ext == extension {
				buf, err := ioutil.ReadFile(path)
				if err != nil {
					logger.Error(err.Error())
					return err
				}

				name := (r[0 : len(r)-len(ext)-1])
				me.t = me.t.New(filepath.ToSlash(name))

				// Bomb out if parse fails. We don't want any silent server starts.
				me.t = template.Must(me.t.Parse(string(buf)))
				break
			}
		}
		return nil
	})
	return nil
}

func (me *renderer) execute(name string, binding interface{}) (*bytes.Buffer, error) {
	buf := new(bytes.Buffer)
	return buf, me.t.ExecuteTemplate(buf, name, binding)
}
<|endoftext|>


<commit_before>package main

// Split the buffer by '\n' (0x0A) characters, return an byte[][] of
// indicating each metric, and byte[] of the remaining parts of the buffer
func ParseBuffer(buffer []byte) ([][]byte, []byte) {
	metrics := make([][]byte, 0)
	metricBuffer := make([]byte, 0xff)

	var metricSize uint32 =  0

	for _, b := range buffer {
		if b == '\n' {

			newMetric := make([]byte, metricSize)
			copy(newMetric, metricBuffer[:metricSize])

			metrics = append(metrics, newMetric)
			metricSize = 0;
		} else {
			metricBuffer[metricSize] = b;
			metricSize++;
		}
	}

	return metrics, metricBuffer[:metricSize]
}
<commit_msg>More efficient implementation (13 allocs/op down to 5 allocs/op)<commit_after>package main

// Split the buffer by '\n' (0x0A) characters, return an byte[][] of
// indicating each metric, and byte[] of the remaining parts of the buffer
func ParseBuffer(buffer []byte) ([][]byte, []byte) {
	metrics := make([][]byte, 0)

	var metricBufferCapacity uint32 = 0xff
	metricBuffer := make([]byte, metricBufferCapacity)

	var metricSize uint32 =  0
	var metricBufferUsage uint32 = 0

	for _, b := range buffer {
		if b == '\n' {

			metrics = append(metrics, metricBuffer[metricBufferUsage - metricSize:metricBufferUsage])
			metricSize = 0;
		} else {

			if metricBufferUsage == metricBufferCapacity {
				newMetricBufferCapacity := (metricBufferCapacity + 1) * 2
				newBuffer := make([]byte, metricBufferCapacity, newMetricBufferCapacity)
				copy(newBuffer, metricBuffer)
				metricBuffer = newBuffer
				metricBufferCapacity = newMetricBufferCapacity
			}

			metricBuffer[metricBufferUsage] = b
			metricSize++
			metricBufferUsage++
		}
	}

	return metrics, metricBuffer[:metricSize]
}
<|endoftext|>


<commit_before>package whisper

import (
	"crypto/ecdsa"
	"errors"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/crypto/ecies"
	"github.com/ethereum/go-ethereum/event/filter"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/p2p"
	"gopkg.in/fatih/set.v0"
)

const (
	statusMsg    = 0x0
	envelopesMsg = 0x01
)

type MessageEvent struct {
	To      *ecdsa.PrivateKey
	From    *ecdsa.PublicKey
	Message *Message
}

const DefaultTtl = 50 * time.Second

var wlogger = logger.NewLogger("SHH")

type Whisper struct {
	protocol p2p.Protocol
	filters  *filter.Filters

	mmu      sync.RWMutex
	messages map[common.Hash]*Envelope
	expiry   map[uint32]*set.SetNonTS

	quit chan struct{}

	keys map[string]*ecdsa.PrivateKey
}

func New() *Whisper {
	whisper := &Whisper{
		messages: make(map[common.Hash]*Envelope),
		filters:  filter.New(),
		expiry:   make(map[uint32]*set.SetNonTS),
		quit:     make(chan struct{}),
		keys:     make(map[string]*ecdsa.PrivateKey),
	}
	whisper.filters.Start()

	// p2p whisper sub protocol handler
	whisper.protocol = p2p.Protocol{
		Name:    "shh",
		Version: 2,
		Length:  2,
		Run:     whisper.msgHandler,
	}

	return whisper
}

func (self *Whisper) Start() {
	wlogger.Infoln("Whisper started")
	go self.update()
}

func (self *Whisper) Stop() {
	close(self.quit)
}

func (self *Whisper) Send(envelope *Envelope) error {
	return self.add(envelope)
}

func (self *Whisper) NewIdentity() *ecdsa.PrivateKey {
	key, err := crypto.GenerateKey()
	if err != nil {
		panic(err)
	}

	self.keys[string(crypto.FromECDSAPub(&key.PublicKey))] = key

	return key
}

func (self *Whisper) HasIdentity(key *ecdsa.PublicKey) bool {
	return self.keys[string(crypto.FromECDSAPub(key))] != nil
}

func (self *Whisper) GetIdentity(key *ecdsa.PublicKey) *ecdsa.PrivateKey {
	return self.keys[string(crypto.FromECDSAPub(key))]
}

// func (self *Whisper) RemoveIdentity(key *ecdsa.PublicKey) bool {
// 	k := string(crypto.FromECDSAPub(key))
// 	if _, ok := self.keys[k]; ok {
// 		delete(self.keys, k)
// 		return true
// 	}
// 	return false
// }

func (self *Whisper) Watch(opts Filter) int {
	return self.filters.Install(filter.Generic{
		Str1: string(crypto.FromECDSAPub(opts.To)),
		Str2: string(crypto.FromECDSAPub(opts.From)),
		Data: bytesToMap(opts.Topics),
		Fn: func(data interface{}) {
			opts.Fn(data.(*Message))
		},
	})
}

func (self *Whisper) Unwatch(id int) {
	self.filters.Uninstall(id)
}

func (self *Whisper) Messages(id int) (messages []*Message) {
	filter := self.filters.Get(id)
	if filter != nil {
		for _, e := range self.messages {
			if msg, key := self.open(e); msg != nil {
				f := createFilter(msg, e.Topics, key)
				if self.filters.Match(filter, f) {
					messages = append(messages, msg)
				}
			}
		}
	}

	return
}

// Main handler for passing whisper messages to whisper peer objects
func (self *Whisper) msgHandler(peer *p2p.Peer, ws p2p.MsgReadWriter) error {
	wpeer := NewPeer(self, peer, ws)
	// initialise whisper peer (handshake/status)
	if err := wpeer.init(); err != nil {
		return err
	}
	// kick of the main handler for broadcasting/managing envelopes
	go wpeer.start()
	defer wpeer.stop()

	// Main *read* loop. Writing is done by the peer it self.
	for {
		msg, err := ws.ReadMsg()
		if err != nil {
			return err
		}

		var envelopes []*Envelope
		if err := msg.Decode(&envelopes); err != nil {
			peer.Infoln(err)
			continue
		}

		for _, envelope := range envelopes {
			if err := self.add(envelope); err != nil {
				// TODO Punish peer here. Invalid envelope.
				peer.Infoln(err)
			}
			wpeer.addKnown(envelope)
		}
	}
}

// takes care of adding envelopes to the messages pool. At this moment no sanity checks are being performed.
func (self *Whisper) add(envelope *Envelope) error {
	if !envelope.valid() {
		return errors.New("invalid pow provided for envelope")
	}

	self.mmu.Lock()
	defer self.mmu.Unlock()

	hash := envelope.Hash()
	self.messages[hash] = envelope
	if self.expiry[envelope.Expiry] == nil {
		self.expiry[envelope.Expiry] = set.NewNonTS()
	}

	if !self.expiry[envelope.Expiry].Has(hash) {
		self.expiry[envelope.Expiry].Add(hash)
		go self.postEvent(envelope)
	}

	wlogger.DebugDetailf("added whisper envelope %x\n", envelope)

	return nil
}

func (self *Whisper) update() {
	expire := time.NewTicker(800 * time.Millisecond)
out:
	for {
		select {
		case <-expire.C:
			self.expire()
		case <-self.quit:
			break out
		}
	}
}

func (self *Whisper) expire() {
	self.mmu.Lock()
	defer self.mmu.Unlock()

	now := uint32(time.Now().Unix())
	for then, hashSet := range self.expiry {
		if then > now {
			continue
		}

		hashSet.Each(func(v interface{}) bool {
			delete(self.messages, v.(common.Hash))
			return true
		})
		self.expiry[then].Clear()
	}
}

func (self *Whisper) envelopes() (envelopes []*Envelope) {
	self.mmu.RLock()
	defer self.mmu.RUnlock()

	envelopes = make([]*Envelope, len(self.messages))
	i := 0
	for _, envelope := range self.messages {
		envelopes[i] = envelope
		i++
	}

	return
}

func (self *Whisper) postEvent(envelope *Envelope) {
	if message, key := self.open(envelope); message != nil {
		self.filters.Notify(createFilter(message, envelope.Topics, key), message)
	}
}

func (self *Whisper) open(envelope *Envelope) (*Message, *ecdsa.PrivateKey) {
	for _, key := range self.keys {
		if message, err := envelope.Open(key); err == nil || (err != nil && err == ecies.ErrInvalidPublicKey) {
			message.To = &key.PublicKey

			return message, key
		}
	}

	return nil, nil
}

func (self *Whisper) Protocol() p2p.Protocol {
	return self.protocol
}

func createFilter(message *Message, topics [][]byte, key *ecdsa.PrivateKey) filter.Filter {
	return filter.Generic{
		Str1: string(crypto.FromECDSAPub(&key.PublicKey)), Str2: string(crypto.FromECDSAPub(message.Recover())),
		Data: bytesToMap(topics),
	}
}
<commit_msg>Move version to const and expose via Version()<commit_after>package whisper

import (
	"crypto/ecdsa"
	"errors"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/crypto/ecies"
	"github.com/ethereum/go-ethereum/event/filter"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/p2p"
	"gopkg.in/fatih/set.v0"
)

const (
	statusMsg      = 0x0
	envelopesMsg   = 0x01
	whisperVersion = 0x02
)

type MessageEvent struct {
	To      *ecdsa.PrivateKey
	From    *ecdsa.PublicKey
	Message *Message
}

const DefaultTtl = 50 * time.Second

var wlogger = logger.NewLogger("SHH")

type Whisper struct {
	protocol p2p.Protocol
	filters  *filter.Filters

	mmu      sync.RWMutex
	messages map[common.Hash]*Envelope
	expiry   map[uint32]*set.SetNonTS

	quit chan struct{}

	keys map[string]*ecdsa.PrivateKey
}

func New() *Whisper {
	whisper := &Whisper{
		messages: make(map[common.Hash]*Envelope),
		filters:  filter.New(),
		expiry:   make(map[uint32]*set.SetNonTS),
		quit:     make(chan struct{}),
		keys:     make(map[string]*ecdsa.PrivateKey),
	}
	whisper.filters.Start()

	// p2p whisper sub protocol handler
	whisper.protocol = p2p.Protocol{
		Name:    "shh",
		Version: uint(whisperVersion),
		Length:  2,
		Run:     whisper.msgHandler,
	}

	return whisper
}

func (self *Whisper) Version() uint {
	return self.protocol.Version
}

func (self *Whisper) Start() {
	wlogger.Infoln("Whisper started")
	go self.update()
}

func (self *Whisper) Stop() {
	close(self.quit)
}

func (self *Whisper) Send(envelope *Envelope) error {
	return self.add(envelope)
}

func (self *Whisper) NewIdentity() *ecdsa.PrivateKey {
	key, err := crypto.GenerateKey()
	if err != nil {
		panic(err)
	}

	self.keys[string(crypto.FromECDSAPub(&key.PublicKey))] = key

	return key
}

func (self *Whisper) HasIdentity(key *ecdsa.PublicKey) bool {
	return self.keys[string(crypto.FromECDSAPub(key))] != nil
}

func (self *Whisper) GetIdentity(key *ecdsa.PublicKey) *ecdsa.PrivateKey {
	return self.keys[string(crypto.FromECDSAPub(key))]
}

// func (self *Whisper) RemoveIdentity(key *ecdsa.PublicKey) bool {
// 	k := string(crypto.FromECDSAPub(key))
// 	if _, ok := self.keys[k]; ok {
// 		delete(self.keys, k)
// 		return true
// 	}
// 	return false
// }

func (self *Whisper) Watch(opts Filter) int {
	return self.filters.Install(filter.Generic{
		Str1: string(crypto.FromECDSAPub(opts.To)),
		Str2: string(crypto.FromECDSAPub(opts.From)),
		Data: bytesToMap(opts.Topics),
		Fn: func(data interface{}) {
			opts.Fn(data.(*Message))
		},
	})
}

func (self *Whisper) Unwatch(id int) {
	self.filters.Uninstall(id)
}

func (self *Whisper) Messages(id int) (messages []*Message) {
	filter := self.filters.Get(id)
	if filter != nil {
		for _, e := range self.messages {
			if msg, key := self.open(e); msg != nil {
				f := createFilter(msg, e.Topics, key)
				if self.filters.Match(filter, f) {
					messages = append(messages, msg)
				}
			}
		}
	}

	return
}

// Main handler for passing whisper messages to whisper peer objects
func (self *Whisper) msgHandler(peer *p2p.Peer, ws p2p.MsgReadWriter) error {
	wpeer := NewPeer(self, peer, ws)
	// initialise whisper peer (handshake/status)
	if err := wpeer.init(); err != nil {
		return err
	}
	// kick of the main handler for broadcasting/managing envelopes
	go wpeer.start()
	defer wpeer.stop()

	// Main *read* loop. Writing is done by the peer it self.
	for {
		msg, err := ws.ReadMsg()
		if err != nil {
			return err
		}

		var envelopes []*Envelope
		if err := msg.Decode(&envelopes); err != nil {
			peer.Infoln(err)
			continue
		}

		for _, envelope := range envelopes {
			if err := self.add(envelope); err != nil {
				// TODO Punish peer here. Invalid envelope.
				peer.Infoln(err)
			}
			wpeer.addKnown(envelope)
		}
	}
}

// takes care of adding envelopes to the messages pool. At this moment no sanity checks are being performed.
func (self *Whisper) add(envelope *Envelope) error {
	if !envelope.valid() {
		return errors.New("invalid pow provided for envelope")
	}

	self.mmu.Lock()
	defer self.mmu.Unlock()

	hash := envelope.Hash()
	self.messages[hash] = envelope
	if self.expiry[envelope.Expiry] == nil {
		self.expiry[envelope.Expiry] = set.NewNonTS()
	}

	if !self.expiry[envelope.Expiry].Has(hash) {
		self.expiry[envelope.Expiry].Add(hash)
		go self.postEvent(envelope)
	}

	wlogger.DebugDetailf("added whisper envelope %x\n", envelope)

	return nil
}

func (self *Whisper) update() {
	expire := time.NewTicker(800 * time.Millisecond)
out:
	for {
		select {
		case <-expire.C:
			self.expire()
		case <-self.quit:
			break out
		}
	}
}

func (self *Whisper) expire() {
	self.mmu.Lock()
	defer self.mmu.Unlock()

	now := uint32(time.Now().Unix())
	for then, hashSet := range self.expiry {
		if then > now {
			continue
		}

		hashSet.Each(func(v interface{}) bool {
			delete(self.messages, v.(common.Hash))
			return true
		})
		self.expiry[then].Clear()
	}
}

func (self *Whisper) envelopes() (envelopes []*Envelope) {
	self.mmu.RLock()
	defer self.mmu.RUnlock()

	envelopes = make([]*Envelope, len(self.messages))
	i := 0
	for _, envelope := range self.messages {
		envelopes[i] = envelope
		i++
	}

	return
}

func (self *Whisper) postEvent(envelope *Envelope) {
	if message, key := self.open(envelope); message != nil {
		self.filters.Notify(createFilter(message, envelope.Topics, key), message)
	}
}

func (self *Whisper) open(envelope *Envelope) (*Message, *ecdsa.PrivateKey) {
	for _, key := range self.keys {
		if message, err := envelope.Open(key); err == nil || (err != nil && err == ecies.ErrInvalidPublicKey) {
			message.To = &key.PublicKey

			return message, key
		}
	}

	return nil, nil
}

func (self *Whisper) Protocol() p2p.Protocol {
	return self.protocol
}

func createFilter(message *Message, topics [][]byte, key *ecdsa.PrivateKey) filter.Filter {
	return filter.Generic{
		Str1: string(crypto.FromECDSAPub(&key.PublicKey)), Str2: string(crypto.FromECDSAPub(message.Recover())),
		Data: bytesToMap(topics),
	}
}
<|endoftext|>


<commit_before>package formbuilder

import (
	"os"

	ncommon "github.com/admpub/nging/application/library/common"

	"github.com/coscms/forms"
	"github.com/coscms/forms/common"
	"github.com/coscms/forms/config"
	"github.com/coscms/forms/fields"

	"github.com/webx-top/com"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/formfilter"
	"github.com/webx-top/echo/middleware/render/driver"
	"github.com/webx-top/validation"
)

// New 表单
//@param m: dbschema
func New(c echo.Context, m interface{}, jsonFile string, options ...Option) *forms.Forms {
	form := forms.New()
	form.Style = common.BOOTSTRAP
	for _, option := range options {
		if option == nil {
			continue
		}
		option(c, form)
	}
	form.SetLabelFunc(func(txt string) string {
		return c.T(txt)
	})
	var cfg *config.Config
	renderer := c.Renderer().(driver.Driver)
	jsonFile += `.form.json`
	jsonFile = renderer.TmplPath(c, jsonFile)
	if len(jsonFile) == 0 {
		return nil
	}
	b, err := renderer.RawContent(jsonFile)
	if err != nil {
		if os.IsNotExist(err) && renderer.Manager() != nil {
			form.SetModel(m)
			cfg = form.ToConfig()
			var jsonb []byte
			jsonb, err = form.ToJSONBlob(cfg)
			if err == nil {
				err = renderer.Manager().SetTemplate(jsonFile, jsonb)
				if err == nil {
					c.Logger().Infof(c.T(`生成表单配置文件“%v”成功。`), jsonFile)
				}
			}
		}
	} else {
		cfg, err = forms.Unmarshal(b, jsonFile)
	}
	if err != nil {
		c.Logger().Error(err)
	}
	if cfg == nil {
		cfg = form.NewConfig()
	}
	form.Init(cfg, m)
	if c.IsPost() {
		opts := []formfilter.Options{formfilter.Include(cfg.GetNames()...)}
		if customs, ok := c.Internal().Get(`formfilter.Options`).([]formfilter.Options); ok {
			opts = append(opts, customs...)
		}
		err = c.MustBind(m, formfilter.Build(opts...))
		if err == nil {
			form.ValidFromConfig()
			valid := form.Validate()
			if valid.HasError() {
				err = valid.Errors[0]
			}
		}
		if err != nil {
			if vErr, ok := err.(*validation.ValidationError); ok {
				c.Data().SetInfo(vErr.Message, 0).SetZone(vErr.Field)
			} else {
				c.Data().SetError(err)
			}
		}
	}
	setNextURLField := func() {
		if len(cfg.Action) == 0 {
			form.SetParam(`action`, c.Request().URI())
		}
		nextURL := c.Form(ncommon.DefaultReturnToURLVarName)
		if len(nextURL) == 0 {
			nextURL = c.Referer()
		}
		form.Elements(fields.HiddenField(ncommon.DefaultReturnToURLVarName).SetValue(nextURL))
	}
	csrfToken, ok := c.Get(`csrf`).(string)
	if ok {
		form.AddBeforeRender(func() {
			form.Elements(fields.HiddenField(`csrf`).SetValue(csrfToken))
			setNextURLField()
		})
	} else {
		form.AddBeforeRender(setNextURLField)
	}
	wrap := forms.NewForms(form)
	c.Set(`forms`, wrap)
	// 手动调用:
	// wrap.ParseFromConfig()
	return wrap
}

// NewModel 表单
//@param m: dbschema
func NewModel(c echo.Context, m interface{}, cfg *config.Config, options ...Option) *forms.Forms {
	form := forms.New()
	for _, option := range options {
		if option == nil {
			continue
		}
		option(c, form)
	}
	form.SetLabelFunc(func(txt string) string {
		return c.T(txt)
	})
	if cfg == nil {
		cfg = form.NewConfig()
	}
	form.Init(cfg, m)
	if c.IsPost() {
		opts := []formfilter.Options{formfilter.Include(cfg.GetNames()...)}
		if customs, ok := c.Internal().Get(`formfilter.Options`).([]formfilter.Options); ok {
			opts = append(opts, customs...)
		}
		err := c.MustBind(m, formfilter.Build(opts...))
		if err == nil {
			validFields, _ := c.Internal().Get(`formbuilder.validFields`).([]string)
			err = form.Valid(validFields...)
		}
		if err != nil {
			if vErr, ok := err.(*validation.ValidationError); ok {
				c.Data().SetInfo(vErr.Message, 0).SetZone(vErr.Field)
			} else {
				c.Data().SetError(err)
			}
		}
	}
	setNextURLField := func() {
		if len(cfg.Action) == 0 {
			form.SetParam(`action`, c.Request().URI())
		}
		nextURL := c.Form(ncommon.DefaultReturnToURLVarName)
		if len(nextURL) == 0 {
			nextURL = c.Referer()
		}
		form.Elements(fields.HiddenField(ncommon.DefaultReturnToURLVarName).SetValue(nextURL))
	}
	csrfToken, ok := c.Get(`csrf`).(string)
	if ok {
		form.AddBeforeRender(func() {
			form.Elements(fields.HiddenField(`csrf`).SetValue(csrfToken))
			setNextURLField()
		})
	} else {
		form.AddBeforeRender(setNextURLField)
	}
	form.AddClass("form-horizontal").SetParam("role", "form")
	wrap := forms.NewForms(form)
	c.Set(`forms`, wrap)
	// 手动调用:
	// wrap.ParseFromConfig()
	return wrap
}

// NewConfig 表单配置
func NewConfig(theme, tmpl, method, action string) *config.Config {
	cfg := forms.NewConfig()
	cfg.Theme = theme
	cfg.Template = tmpl
	cfg.Method = method
	cfg.Action = action
	return cfg
}

// NewSnippet 表单片段
func NewSnippet(theme ...string) *forms.Form {
	cfg := forms.NewConfig()
	if len(theme) > 0 {
		cfg.Theme = theme[0]
	}
	cfg.Template = common.TmplDir(cfg.Theme) + `/allfields.html`
	form := forms.NewWithConfig(cfg)
	return form
}

func ClearCache() {
	common.ClearCachedConfig()
	common.ClearCachedTemplate()
}

func DelCachedConfig(file string) bool {
	return common.DelCachedConfig(file)
}

func AddChoiceByKV(field fields.FieldInterface, kvData *echo.KVData, checkedKeys ...string) fields.FieldInterface {
	for _, kv := range kvData.Slice() {
		var checked bool
		if kv.H != nil {
			checked = kv.H.Bool(`checked`) || kv.H.Bool(`selected`)
		}
		if len(checkedKeys) > 0 {
			checked = com.InSlice(kv.K, checkedKeys)
		}
		field.AddChoice(kv.K, kv.V, checked)
	}
	return field
}

func SetChoiceByKV(field fields.FieldInterface, kvData *echo.KVData, checkedKeys ...string) fields.FieldInterface {
	choices := []fields.InputChoice{}
	for _, kv := range kvData.Slice() {
		var checked bool
		if kv.H != nil {
			checked = kv.H.Bool(`checked`) || kv.H.Bool(`selected`)
		}
		if len(checkedKeys) > 0 {
			checked = com.InSlice(kv.K, checkedKeys)
		}
		choices = append(choices, fields.InputChoice{
			ID:      kv.K,
			Val:     kv.V,
			Checked: checked,
		})
	}

	field.SetChoices(choices)
	return field
}
<commit_msg>update<commit_after>package formbuilder

import (
	"os"

	ncommon "github.com/admpub/nging/application/library/common"

	"github.com/coscms/forms"
	"github.com/coscms/forms/common"
	"github.com/coscms/forms/config"
	"github.com/coscms/forms/fields"

	"github.com/webx-top/com"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/formfilter"
	"github.com/webx-top/echo/middleware/render/driver"
	"github.com/webx-top/validation"
)

// New 表单
//@param m: dbschema
func New(c echo.Context, m interface{}, jsonFile string, options ...Option) (*forms.Forms, error) {
	form := forms.New()
	form.Style = common.BOOTSTRAP
	for _, option := range options {
		if option == nil {
			continue
		}
		option(c, form)
	}
	form.SetLabelFunc(func(txt string) string {
		return c.T(txt)
	})
	var cfg *config.Config
	renderer := c.Renderer().(driver.Driver)
	jsonFile += `.form.json`
	jsonFile = renderer.TmplPath(c, jsonFile)
	if len(jsonFile) == 0 {
		return nil
	}
	b, err := renderer.RawContent(jsonFile)
	if err != nil {
		if os.IsNotExist(err) && renderer.Manager() != nil {
			form.SetModel(m)
			cfg = form.ToConfig()
			var jsonb []byte
			jsonb, err = form.ToJSONBlob(cfg)
			if err == nil {
				err = renderer.Manager().SetTemplate(jsonFile, jsonb)
				if err == nil {
					c.Logger().Infof(c.T(`生成表单配置文件“%v”成功。`), jsonFile)
				}
			}
		}
	} else {
		cfg, err = forms.Unmarshal(b, jsonFile)
	}
	if err != nil {
		return nil, err
	}
	if cfg == nil {
		cfg = form.NewConfig()
	}
	form.Init(cfg, m)
	if c.IsPost() {
		opts := []formfilter.Options{formfilter.Include(cfg.GetNames()...)}
		if customs, ok := c.Internal().Get(`formfilter.Options`).([]formfilter.Options); ok {
			opts = append(opts, customs...)
		}
		err = c.MustBind(m, formfilter.Build(opts...))
		if err == nil {
			form.ValidFromConfig()
			valid := form.Validate()
			if valid.HasError() {
				err = valid.Errors[0]
			}
		}
		if err != nil {
			if vErr, ok := err.(*validation.ValidationError); ok {
				c.Data().SetInfo(vErr.Message, 0).SetZone(vErr.Field)
			} else {
				c.Data().SetError(err)
			}
		}
	}
	setNextURLField := func() {
		if len(cfg.Action) == 0 {
			form.SetParam(`action`, c.Request().URI())
		}
		nextURL := c.Form(ncommon.DefaultReturnToURLVarName)
		if len(nextURL) == 0 {
			nextURL = c.Referer()
		}
		form.Elements(fields.HiddenField(ncommon.DefaultReturnToURLVarName).SetValue(nextURL))
	}
	csrfToken, ok := c.Get(`csrf`).(string)
	if ok {
		form.AddBeforeRender(func() {
			form.Elements(fields.HiddenField(`csrf`).SetValue(csrfToken))
			setNextURLField()
		})
	} else {
		form.AddBeforeRender(setNextURLField)
	}
	wrap := forms.NewForms(form)
	c.Set(`forms`, wrap)
	// 手动调用:
	// wrap.ParseFromConfig()
	return wrap, nil
}

// NewModel 表单
//@param m: dbschema
func NewModel(c echo.Context, m interface{}, cfg *config.Config, options ...Option) *forms.Forms {
	form := forms.New()
	for _, option := range options {
		if option == nil {
			continue
		}
		option(c, form)
	}
	form.SetLabelFunc(func(txt string) string {
		return c.T(txt)
	})
	if cfg == nil {
		cfg = form.NewConfig()
	}
	form.Init(cfg, m)
	if c.IsPost() {
		opts := []formfilter.Options{formfilter.Include(cfg.GetNames()...)}
		if customs, ok := c.Internal().Get(`formfilter.Options`).([]formfilter.Options); ok {
			opts = append(opts, customs...)
		}
		err := c.MustBind(m, formfilter.Build(opts...))
		if err == nil {
			validFields, _ := c.Internal().Get(`formbuilder.validFields`).([]string)
			err = form.Valid(validFields...)
		}
		if err != nil {
			if vErr, ok := err.(*validation.ValidationError); ok {
				c.Data().SetInfo(vErr.Message, 0).SetZone(vErr.Field)
			} else {
				c.Data().SetError(err)
			}
		}
	}
	setNextURLField := func() {
		if len(cfg.Action) == 0 {
			form.SetParam(`action`, c.Request().URI())
		}
		nextURL := c.Form(ncommon.DefaultReturnToURLVarName)
		if len(nextURL) == 0 {
			nextURL = c.Referer()
		}
		form.Elements(fields.HiddenField(ncommon.DefaultReturnToURLVarName).SetValue(nextURL))
	}
	csrfToken, ok := c.Get(`csrf`).(string)
	if ok {
		form.AddBeforeRender(func() {
			form.Elements(fields.HiddenField(`csrf`).SetValue(csrfToken))
			setNextURLField()
		})
	} else {
		form.AddBeforeRender(setNextURLField)
	}
	form.AddClass("form-horizontal").SetParam("role", "form")
	wrap := forms.NewForms(form)
	c.Set(`forms`, wrap)
	// 手动调用:
	// wrap.ParseFromConfig()
	return wrap
}

// NewConfig 表单配置
func NewConfig(theme, tmpl, method, action string) *config.Config {
	cfg := forms.NewConfig()
	cfg.Theme = theme
	cfg.Template = tmpl
	cfg.Method = method
	cfg.Action = action
	return cfg
}

// NewSnippet 表单片段
func NewSnippet(theme ...string) *forms.Form {
	cfg := forms.NewConfig()
	if len(theme) > 0 {
		cfg.Theme = theme[0]
	}
	cfg.Template = common.TmplDir(cfg.Theme) + `/allfields.html`
	form := forms.NewWithConfig(cfg)
	return form
}

func ClearCache() {
	common.ClearCachedConfig()
	common.ClearCachedTemplate()
}

func DelCachedConfig(file string) bool {
	return common.DelCachedConfig(file)
}

func AddChoiceByKV(field fields.FieldInterface, kvData *echo.KVData, checkedKeys ...string) fields.FieldInterface {
	for _, kv := range kvData.Slice() {
		var checked bool
		if kv.H != nil {
			checked = kv.H.Bool(`checked`) || kv.H.Bool(`selected`)
		}
		if len(checkedKeys) > 0 {
			checked = com.InSlice(kv.K, checkedKeys)
		}
		field.AddChoice(kv.K, kv.V, checked)
	}
	return field
}

func SetChoiceByKV(field fields.FieldInterface, kvData *echo.KVData, checkedKeys ...string) fields.FieldInterface {
	choices := []fields.InputChoice{}
	for _, kv := range kvData.Slice() {
		var checked bool
		if kv.H != nil {
			checked = kv.H.Bool(`checked`) || kv.H.Bool(`selected`)
		}
		if len(checkedKeys) > 0 {
			checked = com.InSlice(kv.K, checkedKeys)
		}
		choices = append(choices, fields.InputChoice{
			ID:      kv.K,
			Val:     kv.V,
			Checked: checked,
		})
	}

	field.SetChoices(choices)
	return field
}
<|endoftext|>


<commit_before>// +build linux

/*
Copyright 2020 The Kubernetes 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 systemd

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/godbus/dbus/v5"
	"k8s.io/klog/v2"
)

const (
	logindService   = "org.freedesktop.login1"
	logindObject    = dbus.ObjectPath("/org/freedesktop/login1")
	logindInterface = "org.freedesktop.login1.Manager"
)

type dBusConnector interface {
	Object(dest string, path dbus.ObjectPath) dbus.BusObject
	AddMatchSignal(options ...dbus.MatchOption) error
	Signal(ch chan<- *dbus.Signal)
}

// DBusCon has functions that can be used to interact with systemd and logind over dbus.
type DBusCon struct {
	SystemBus dBusConnector
}

// InhibitLock is a lock obtained after creating an systemd inhibitor by calling InhibitShutdown().
type InhibitLock uint32

// CurrentInhibitDelay returns the current delay inhibitor timeout value as configured in logind.conf(5).
// see https://www.freedesktop.org/software/systemd/man/logind.conf.html for more details.
func (bus *DBusCon) CurrentInhibitDelay() (time.Duration, error) {
	obj := bus.SystemBus.Object(logindService, logindObject)
	res, err := obj.GetProperty(logindInterface + ".InhibitDelayMaxUSec")
	if err != nil {
		return 0, fmt.Errorf("failed reading InhibitDelayMaxUSec property from logind: %v", err)
	}

	delay, ok := res.Value().(uint64)
	if !ok {
		return 0, fmt.Errorf("InhibitDelayMaxUSec from logind is not a uint64 as expected")
	}

	// InhibitDelayMaxUSec is in microseconds
	duration := time.Duration(delay) * time.Microsecond
	return duration, nil
}

// InhibitShutdown creates an systemd inhibitor by calling logind's Inhibt() and returns the inhibitor lock
// see https://www.freedesktop.org/wiki/Software/systemd/inhibit/ for more details.
func (bus *DBusCon) InhibitShutdown() (InhibitLock, error) {
	obj := bus.SystemBus.Object(logindService, logindObject)
	what := "shutdown"
	who := "kubelet"
	why := "Kubelet needs time to handle node shutdown"
	mode := "delay"

	call := obj.Call("org.freedesktop.login1.Manager.Inhibit", 0, what, who, why, mode)
	if call.Err != nil {
		return InhibitLock(0), fmt.Errorf("failed creating systemd inhibitor: %v", call.Err)
	}

	var fd uint32
	err := call.Store(&fd)
	if err != nil {
		return InhibitLock(0), fmt.Errorf("failed storing inhibit lock file descriptor: %v", err)
	}

	return InhibitLock(fd), nil
}

// ReleaseInhibitLock will release the underlying inhibit lock which will cause the shutdown to start.
func (bus *DBusCon) ReleaseInhibitLock(lock InhibitLock) error {
	err := syscall.Close(int(lock))

	if err != nil {
		return fmt.Errorf("unable to close systemd inhibitor lock: %v", err)
	}

	return nil
}

// ReloadLogindConf uses dbus to send a SIGHUP to the systemd-logind service causing logind to reload it's configuration.
func (bus *DBusCon) ReloadLogindConf() error {
	systemdService := "org.freedesktop.systemd1"
	systemdObject := "/org/freedesktop/systemd1"
	systemdInterface := "org.freedesktop.systemd1.Manager"

	obj := bus.SystemBus.Object(systemdService, dbus.ObjectPath(systemdObject))
	unit := "systemd-logind.service"
	who := "all"
	var signal int32 = 1 // SIGHUP

	call := obj.Call(systemdInterface+".KillUnit", 0, unit, who, signal)
	if call.Err != nil {
		return fmt.Errorf("unable to reload logind conf: %v", call.Err)
	}

	return nil
}

// MonitorShutdown detects the a node shutdown by watching for "PrepareForShutdown" logind events.
// see https://www.freedesktop.org/wiki/Software/systemd/inhibit/ for more details.
func (bus *DBusCon) MonitorShutdown() (<-chan bool, error) {
	err := bus.SystemBus.AddMatchSignal(dbus.WithMatchInterface(logindInterface), dbus.WithMatchMember("PrepareForShutdown"), dbus.WithMatchObjectPath("/org/freedesktop/login1"))

	if err != nil {
		return nil, err
	}

	busChan := make(chan *dbus.Signal, 1)
	bus.SystemBus.Signal(busChan)

	shutdownChan := make(chan bool, 1)

	go func() {
		for {
			select {
			case event := <-busChan:
				if event == nil || len(event.Body) == 0 {
					klog.Errorf("Failed obtaining shutdown event, PrepareForShutdown event was empty")
				}
				shutdownActive, ok := event.Body[0].(bool)
				if !ok {
					klog.Errorf("Failed obtaining shutdown event, PrepareForShutdown event was not bool type as expected")
					return
				}
				shutdownChan <- shutdownActive
			}
		}
	}()

	return shutdownChan, nil
}

const (
	logindConfigDirectory = "/etc/systemd/logind.conf.d/"
	kubeletLogindConf     = "99-kubelet.conf"
)

// OverrideInhibitDelay writes a config file to logind overriding InhibitDelayMaxSec to the value desired.
func (bus *DBusCon) OverrideInhibitDelay(inhibitDelayMax time.Duration) error {
	err := os.MkdirAll(logindConfigDirectory, 0755)
	if err != nil {
		return fmt.Errorf("failed creating %v directory: %v", logindConfigDirectory, err)
	}

	// This attempts to set the `InhibitDelayMaxUSec` dbus property of logind which is MaxInhibitDelay measured in microseconds.
	// The corresponding logind config file property is named `InhibitDelayMaxSec` and is measured in seconds which is set via logind.conf config.
	// Refer to https://www.freedesktop.org/software/systemd/man/logind.conf.html for more details.

	inhibitOverride := fmt.Sprintf(`# Kubelet logind override
[Login]
InhibitDelayMaxSec=%.0f
`, inhibitDelayMax.Seconds())

	logindOverridePath := filepath.Join(logindConfigDirectory, kubeletLogindConf)
	if err := ioutil.WriteFile(logindOverridePath, []byte(inhibitOverride), 0755); err != nil {
		return fmt.Errorf("failed writing logind shutdown inhibit override file %v: %v", logindOverridePath, err)
	}

	return nil
}
<commit_msg>remove executable permission bits<commit_after>// +build linux

/*
Copyright 2020 The Kubernetes 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 systemd

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/godbus/dbus/v5"
	"k8s.io/klog/v2"
)

const (
	logindService   = "org.freedesktop.login1"
	logindObject    = dbus.ObjectPath("/org/freedesktop/login1")
	logindInterface = "org.freedesktop.login1.Manager"
)

type dBusConnector interface {
	Object(dest string, path dbus.ObjectPath) dbus.BusObject
	AddMatchSignal(options ...dbus.MatchOption) error
	Signal(ch chan<- *dbus.Signal)
}

// DBusCon has functions that can be used to interact with systemd and logind over dbus.
type DBusCon struct {
	SystemBus dBusConnector
}

// InhibitLock is a lock obtained after creating an systemd inhibitor by calling InhibitShutdown().
type InhibitLock uint32

// CurrentInhibitDelay returns the current delay inhibitor timeout value as configured in logind.conf(5).
// see https://www.freedesktop.org/software/systemd/man/logind.conf.html for more details.
func (bus *DBusCon) CurrentInhibitDelay() (time.Duration, error) {
	obj := bus.SystemBus.Object(logindService, logindObject)
	res, err := obj.GetProperty(logindInterface + ".InhibitDelayMaxUSec")
	if err != nil {
		return 0, fmt.Errorf("failed reading InhibitDelayMaxUSec property from logind: %v", err)
	}

	delay, ok := res.Value().(uint64)
	if !ok {
		return 0, fmt.Errorf("InhibitDelayMaxUSec from logind is not a uint64 as expected")
	}

	// InhibitDelayMaxUSec is in microseconds
	duration := time.Duration(delay) * time.Microsecond
	return duration, nil
}

// InhibitShutdown creates an systemd inhibitor by calling logind's Inhibt() and returns the inhibitor lock
// see https://www.freedesktop.org/wiki/Software/systemd/inhibit/ for more details.
func (bus *DBusCon) InhibitShutdown() (InhibitLock, error) {
	obj := bus.SystemBus.Object(logindService, logindObject)
	what := "shutdown"
	who := "kubelet"
	why := "Kubelet needs time to handle node shutdown"
	mode := "delay"

	call := obj.Call("org.freedesktop.login1.Manager.Inhibit", 0, what, who, why, mode)
	if call.Err != nil {
		return InhibitLock(0), fmt.Errorf("failed creating systemd inhibitor: %v", call.Err)
	}

	var fd uint32
	err := call.Store(&fd)
	if err != nil {
		return InhibitLock(0), fmt.Errorf("failed storing inhibit lock file descriptor: %v", err)
	}

	return InhibitLock(fd), nil
}

// ReleaseInhibitLock will release the underlying inhibit lock which will cause the shutdown to start.
func (bus *DBusCon) ReleaseInhibitLock(lock InhibitLock) error {
	err := syscall.Close(int(lock))

	if err != nil {
		return fmt.Errorf("unable to close systemd inhibitor lock: %v", err)
	}

	return nil
}

// ReloadLogindConf uses dbus to send a SIGHUP to the systemd-logind service causing logind to reload it's configuration.
func (bus *DBusCon) ReloadLogindConf() error {
	systemdService := "org.freedesktop.systemd1"
	systemdObject := "/org/freedesktop/systemd1"
	systemdInterface := "org.freedesktop.systemd1.Manager"

	obj := bus.SystemBus.Object(systemdService, dbus.ObjectPath(systemdObject))
	unit := "systemd-logind.service"
	who := "all"
	var signal int32 = 1 // SIGHUP

	call := obj.Call(systemdInterface+".KillUnit", 0, unit, who, signal)
	if call.Err != nil {
		return fmt.Errorf("unable to reload logind conf: %v", call.Err)
	}

	return nil
}

// MonitorShutdown detects the a node shutdown by watching for "PrepareForShutdown" logind events.
// see https://www.freedesktop.org/wiki/Software/systemd/inhibit/ for more details.
func (bus *DBusCon) MonitorShutdown() (<-chan bool, error) {
	err := bus.SystemBus.AddMatchSignal(dbus.WithMatchInterface(logindInterface), dbus.WithMatchMember("PrepareForShutdown"), dbus.WithMatchObjectPath("/org/freedesktop/login1"))

	if err != nil {
		return nil, err
	}

	busChan := make(chan *dbus.Signal, 1)
	bus.SystemBus.Signal(busChan)

	shutdownChan := make(chan bool, 1)

	go func() {
		for {
			select {
			case event := <-busChan:
				if event == nil || len(event.Body) == 0 {
					klog.Errorf("Failed obtaining shutdown event, PrepareForShutdown event was empty")
				}
				shutdownActive, ok := event.Body[0].(bool)
				if !ok {
					klog.Errorf("Failed obtaining shutdown event, PrepareForShutdown event was not bool type as expected")
					return
				}
				shutdownChan <- shutdownActive
			}
		}
	}()

	return shutdownChan, nil
}

const (
	logindConfigDirectory = "/etc/systemd/logind.conf.d/"
	kubeletLogindConf     = "99-kubelet.conf"
)

// OverrideInhibitDelay writes a config file to logind overriding InhibitDelayMaxSec to the value desired.
func (bus *DBusCon) OverrideInhibitDelay(inhibitDelayMax time.Duration) error {
	err := os.MkdirAll(logindConfigDirectory, 0755)
	if err != nil {
		return fmt.Errorf("failed creating %v directory: %v", logindConfigDirectory, err)
	}

	// This attempts to set the `InhibitDelayMaxUSec` dbus property of logind which is MaxInhibitDelay measured in microseconds.
	// The corresponding logind config file property is named `InhibitDelayMaxSec` and is measured in seconds which is set via logind.conf config.
	// Refer to https://www.freedesktop.org/software/systemd/man/logind.conf.html for more details.

	inhibitOverride := fmt.Sprintf(`# Kubelet logind override
[Login]
InhibitDelayMaxSec=%.0f
`, inhibitDelayMax.Seconds())

	logindOverridePath := filepath.Join(logindConfigDirectory, kubeletLogindConf)
	if err := ioutil.WriteFile(logindOverridePath, []byte(inhibitOverride), 0644); err != nil {
		return fmt.Errorf("failed writing logind shutdown inhibit override file %v: %v", logindOverridePath, err)
	}

	return nil
}
<|endoftext|>


<commit_before>package plek

import (
	"fmt"
	"net/url"
	"os"
	"strings"
)

const devDomain = "dev.gov.uk"

// An EnvVarMissing is returned when a required environment variable is missing
type EnvVarMissing struct {
	// The environment variable this relates to
	EnvVar string
}

func (e *EnvVarMissing) Error() string {
	return "Expected " + e.EnvVar + " to be set. Perhaps you should run your task through govuk_setenv <appname>?"
}

// An EnvVarURLInvalid is returned when an environment variable does not
// contain a valid URL.
type EnvVarURLInvalid struct {
	// The environment variable this relates to
	EnvVar string
	// The error returned when parsing the URL.
	Err error
}

func (e *EnvVarURLInvalid) Error() string {
	return e.EnvVar + " " + e.Err.Error()
}

var httpDomains = map[string]bool{
	devDomain: true,
}

// Find returns the base URL for the given service name in the default parent
// domain. The domain is taken from the GOVUK_APP_DOMAIN environment variable.
// If this is unset, "dev.gov.uk" is used.
//
// The URLs for an individual service can be overridden by setting a
// corresponding PLEK_SERVICE_FOO_URI environment variable.  For example, to
// override the "foo-api" service url, set PLEK_SERVICE_FOO_API_URI to the base
// URL of the service.
func Find(hostname string) string {
	overrideURL := serviceURLFromEnvOverride(hostname)
	if overrideURL != "" {
		return overrideURL
	}

	appDomain := os.Getenv("GOVUK_APP_DOMAIN")
	if appDomain == "" {
		if devDomainFromEnv := os.Getenv("DEV_DOMAIN"); devDomainFromEnv != "" {
			appDomain = devDomainFromEnv
		} else {
			appDomain = devDomain
		}
	}

	return Plek{parentDomain: appDomain}.Find(hostname)
}

// Plek builds service URLs for a given parent domain.
type Plek struct {
	parentDomain string
}

// New builds a new Plek instance for a given parent domain.
func New(parentDomain string) Plek {
	return Plek{parentDomain: parentDomain}
}

// Find returns the base URL for the given service name.
func (p Plek) Find(serviceName string) string {
	u := &url.URL{Scheme: "https", Host: serviceName + "." + p.parentDomain}
	if httpDomains[p.parentDomain] {
		u.Scheme = "http"
	}
	return u.String()
}

// WebsiteRoot returns the public website base URL.  This is taken from the
// GOVUK_WEBSITE_ROOT environment variable.  If this is unset, an EnvVarMissing
// error will be returned.
func WebsiteRoot() (string, error) {
	return readEnvVarURL("GOVUK_WEBSITE_ROOT")
}

// AssetRoot returns the public assets base URL. This is taken from the
// GOVUK_ASSET_ROOT environment variable.  If this is unset, an EnvVarMissing
// error will be returned.
func AssetRoot() (string, error) {
	return readEnvVarURL("GOVUK_ASSET_ROOT")
}

func readEnvVarURL(envVar string) (string, error) {
	urlString := os.Getenv(envVar)
	if urlString == "" {
		return "", &EnvVarMissing{EnvVar: envVar}
	}
	return urlString, nil
}

func serviceURLFromEnvOverride(serviceName string) string {
	varName := fmt.Sprintf(
		"PLEK_SERVICE_%s_URI",
		strings.ToUpper(strings.Replace(serviceName, "-", "_", -1)),
	)
	urlString, err := readEnvVarURL(varName)
	if err != nil {
		// it has to be EnvVarMissing
		return ""
	}
	return urlString
}
<commit_msg>Rename variable name for consistency.<commit_after>package plek

import (
	"fmt"
	"net/url"
	"os"
	"strings"
)

const devDomain = "dev.gov.uk"

// An EnvVarMissing is returned when a required environment variable is missing
type EnvVarMissing struct {
	// The environment variable this relates to
	EnvVar string
}

func (e *EnvVarMissing) Error() string {
	return "Expected " + e.EnvVar + " to be set. Perhaps you should run your task through govuk_setenv <appname>?"
}

// An EnvVarURLInvalid is returned when an environment variable does not
// contain a valid URL.
type EnvVarURLInvalid struct {
	// The environment variable this relates to
	EnvVar string
	// The error returned when parsing the URL.
	Err error
}

func (e *EnvVarURLInvalid) Error() string {
	return e.EnvVar + " " + e.Err.Error()
}

var httpDomains = map[string]bool{
	devDomain: true,
}

// Find returns the base URL for the given service name in the default parent
// domain. The domain is taken from the GOVUK_APP_DOMAIN environment variable.
// If this is unset, "dev.gov.uk" is used.
//
// The URLs for an individual service can be overridden by setting a
// corresponding PLEK_SERVICE_FOO_URI environment variable.  For example, to
// override the "foo-api" service url, set PLEK_SERVICE_FOO_API_URI to the base
// URL of the service.
func Find(serviceName string) string {
	overrideURL := serviceURLFromEnvOverride(serviceName)
	if overrideURL != "" {
		return overrideURL
	}

	appDomain := os.Getenv("GOVUK_APP_DOMAIN")
	if appDomain == "" {
		if devDomainFromEnv := os.Getenv("DEV_DOMAIN"); devDomainFromEnv != "" {
			appDomain = devDomainFromEnv
		} else {
			appDomain = devDomain
		}
	}

	return Plek{parentDomain: appDomain}.Find(serviceName)
}

// Plek builds service URLs for a given parent domain.
type Plek struct {
	parentDomain string
}

// New builds a new Plek instance for a given parent domain.
func New(parentDomain string) Plek {
	return Plek{parentDomain: parentDomain}
}

// Find returns the base URL for the given service name.
func (p Plek) Find(serviceName string) string {
	u := &url.URL{Scheme: "https", Host: serviceName + "." + p.parentDomain}
	if httpDomains[p.parentDomain] {
		u.Scheme = "http"
	}
	return u.String()
}

// WebsiteRoot returns the public website base URL.  This is taken from the
// GOVUK_WEBSITE_ROOT environment variable.  If this is unset, an EnvVarMissing
// error will be returned.
func WebsiteRoot() (string, error) {
	return readEnvVarURL("GOVUK_WEBSITE_ROOT")
}

// AssetRoot returns the public assets base URL. This is taken from the
// GOVUK_ASSET_ROOT environment variable.  If this is unset, an EnvVarMissing
// error will be returned.
func AssetRoot() (string, error) {
	return readEnvVarURL("GOVUK_ASSET_ROOT")
}

func readEnvVarURL(envVar string) (string, error) {
	urlString := os.Getenv(envVar)
	if urlString == "" {
		return "", &EnvVarMissing{EnvVar: envVar}
	}
	return urlString, nil
}

func serviceURLFromEnvOverride(serviceName string) string {
	varName := fmt.Sprintf(
		"PLEK_SERVICE_%s_URI",
		strings.ToUpper(strings.Replace(serviceName, "-", "_", -1)),
	)
	urlString, err := readEnvVarURL(varName)
	if err != nil {
		// it has to be EnvVarMissing
		return ""
	}
	return urlString
}
<|endoftext|>


<commit_before>// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// 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 perform

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/matrix-org/gomatrix"
	"github.com/matrix-org/gomatrixserverlib"
	"github.com/matrix-org/util"
	"github.com/sirupsen/logrus"

	fsAPI "github.com/matrix-org/dendrite/federationapi/api"
	"github.com/matrix-org/dendrite/roomserver/api"
	"github.com/matrix-org/dendrite/roomserver/internal/helpers"
	"github.com/matrix-org/dendrite/roomserver/internal/input"
	"github.com/matrix-org/dendrite/roomserver/storage"
	"github.com/matrix-org/dendrite/setup/config"
	userapi "github.com/matrix-org/dendrite/userapi/api"
)

type Leaver struct {
	Cfg     *config.RoomServer
	DB      storage.Database
	FSAPI   fsAPI.FederationInternalAPI
	UserAPI userapi.UserInternalAPI
	Inputer *input.Inputer
}

// WriteOutputEvents implements OutputRoomEventWriter
func (r *Leaver) PerformLeave(
	ctx context.Context,
	req *api.PerformLeaveRequest,
	res *api.PerformLeaveResponse,
) ([]api.OutputEvent, error) {
	_, domain, err := gomatrixserverlib.SplitID('@', req.UserID)
	if err != nil {
		return nil, fmt.Errorf("supplied user ID %q in incorrect format", req.UserID)
	}
	if domain != r.Cfg.Matrix.ServerName {
		return nil, fmt.Errorf("user %q does not belong to this homeserver", req.UserID)
	}
	logger := logrus.WithContext(ctx).WithFields(logrus.Fields{
		"room_id": req.RoomID,
		"user_id": req.UserID,
	})
	logger.Info("User requested to leave join")
	if strings.HasPrefix(req.RoomID, "!") {
		output, err := r.performLeaveRoomByID(context.Background(), req, res)
		if err != nil {
			logger.WithError(err).Error("Failed to leave room")
		} else {
			logger.Info("User left room successfully")
		}
		return output, err
	}
	return nil, fmt.Errorf("room ID %q is invalid", req.RoomID)
}

func (r *Leaver) performLeaveRoomByID(
	ctx context.Context,
	req *api.PerformLeaveRequest,
	res *api.PerformLeaveResponse, // nolint:unparam
) ([]api.OutputEvent, error) {
	// If there's an invite outstanding for the room then respond to
	// that.
	isInvitePending, senderUser, eventID, err := helpers.IsInvitePending(ctx, r.DB, req.RoomID, req.UserID)
	if err == nil && isInvitePending {
		var host gomatrixserverlib.ServerName
		_, host, err = gomatrixserverlib.SplitID('@', senderUser)
		if err != nil {
			return nil, fmt.Errorf("sender %q is invalid", senderUser)
		}
		if host != r.Cfg.Matrix.ServerName {
			return r.performFederatedRejectInvite(ctx, req, res, senderUser, eventID)
		}
		// check that this is not a "server notice room"
		accData := &userapi.QueryAccountDataResponse{}
		if err = r.UserAPI.QueryAccountData(ctx, &userapi.QueryAccountDataRequest{
			UserID:   req.UserID,
			RoomID:   req.RoomID,
			DataType: "m.tag",
		}, accData); err != nil {
			return nil, fmt.Errorf("unable to query account data: %w", err)
		}

		if roomData, ok := accData.RoomAccountData[req.RoomID]; ok {
			tagData, ok := roomData["m.tag"]
			if ok {
				tags := gomatrix.TagContent{}
				if err = json.Unmarshal(tagData, &tags); err != nil {
					return nil, fmt.Errorf("unable to unmarshal tag content")
				}
				if _, ok = tags.Tags["m.server_notice"]; ok {
					// mimic the returned values from Synapse
					res.Message = "You cannot reject this invite"
					res.Code = 403
					return nil, fmt.Errorf("You cannot reject this invite")
				}
			}
		}
	}

	// There's no invite pending, so first of all we want to find out
	// if the room exists and if the user is actually in it.
	latestReq := api.QueryLatestEventsAndStateRequest{
		RoomID: req.RoomID,
		StateToFetch: []gomatrixserverlib.StateKeyTuple{
			{
				EventType: gomatrixserverlib.MRoomMember,
				StateKey:  req.UserID,
			},
		},
	}
	latestRes := api.QueryLatestEventsAndStateResponse{}
	if err = helpers.QueryLatestEventsAndState(ctx, r.DB, &latestReq, &latestRes); err != nil {
		return nil, err
	}
	if !latestRes.RoomExists {
		return nil, fmt.Errorf("room %q does not exist", req.RoomID)
	}

	// Now let's see if the user is in the room.
	if len(latestRes.StateEvents) == 0 {
		return nil, fmt.Errorf("user %q is not a member of room %q", req.UserID, req.RoomID)
	}
	membership, err := latestRes.StateEvents[0].Membership()
	if err != nil {
		return nil, fmt.Errorf("error getting membership: %w", err)
	}
	if membership != gomatrixserverlib.Join && membership != gomatrixserverlib.Invite {
		return nil, fmt.Errorf("user %q is not joined to the room (membership is %q)", req.UserID, membership)
	}

	// Prepare the template for the leave event.
	userID := req.UserID
	eb := gomatrixserverlib.EventBuilder{
		Type:     gomatrixserverlib.MRoomMember,
		Sender:   userID,
		StateKey: &userID,
		RoomID:   req.RoomID,
		Redacts:  "",
	}
	if err = eb.SetContent(map[string]interface{}{"membership": "leave"}); err != nil {
		return nil, fmt.Errorf("eb.SetContent: %w", err)
	}
	if err = eb.SetUnsigned(struct{}{}); err != nil {
		return nil, fmt.Errorf("eb.SetUnsigned: %w", err)
	}

	// We know that the user is in the room at this point so let's build
	// a leave event.
	// TODO: Check what happens if the room exists on the server
	// but everyone has since left. I suspect it does the wrong thing.
	event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, &eb)
	if err != nil {
		return nil, fmt.Errorf("eventutil.BuildEvent: %w", err)
	}

	// Give our leave event to the roomserver input stream. The
	// roomserver will process the membership change and notify
	// downstream automatically.
	inputReq := api.InputRoomEventsRequest{
		InputRoomEvents: []api.InputRoomEvent{
			{
				Kind:         api.KindNew,
				Event:        event.Headered(buildRes.RoomVersion),
				Origin:       event.Origin(),
				SendAsServer: string(r.Cfg.Matrix.ServerName),
			},
		},
	}
	inputRes := api.InputRoomEventsResponse{}
	r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes)
	if err = inputRes.Err(); err != nil {
		return nil, fmt.Errorf("r.InputRoomEvents: %w", err)
	}

	return nil, nil
}

func (r *Leaver) performFederatedRejectInvite(
	ctx context.Context,
	req *api.PerformLeaveRequest,
	res *api.PerformLeaveResponse, // nolint:unparam
	senderUser, eventID string,
) ([]api.OutputEvent, error) {
	_, domain, err := gomatrixserverlib.SplitID('@', senderUser)
	if err != nil {
		return nil, fmt.Errorf("user ID %q invalid: %w", senderUser, err)
	}

	// Ask the federation sender to perform a federated leave for us.
	leaveReq := fsAPI.PerformLeaveRequest{
		RoomID:      req.RoomID,
		UserID:      req.UserID,
		ServerNames: []gomatrixserverlib.ServerName{domain},
	}
	leaveRes := fsAPI.PerformLeaveResponse{}
	if err := r.FSAPI.PerformLeave(ctx, &leaveReq, &leaveRes); err != nil {
		// failures in PerformLeave should NEVER stop us from telling other components like the
		// sync API that the invite was withdrawn. Otherwise we can end up with stuck invites.
		util.GetLogger(ctx).WithError(err).Errorf("failed to PerformLeave, still retiring invite event")
	}

	// Withdraw the invite, so that the sync API etc are
	// notified that we rejected it.
	return []api.OutputEvent{
		{
			Type: api.OutputTypeRetireInviteEvent,
			RetireInviteEvent: &api.OutputRetireInviteEvent{
				EventID:      eventID,
				Membership:   "leave",
				TargetUserID: req.UserID,
			},
		},
	}, nil
}
<commit_msg>Update database when rejecting federated invite (#2300)<commit_after>// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// 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 perform

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/matrix-org/gomatrix"
	"github.com/matrix-org/gomatrixserverlib"
	"github.com/matrix-org/util"
	"github.com/sirupsen/logrus"

	fsAPI "github.com/matrix-org/dendrite/federationapi/api"
	"github.com/matrix-org/dendrite/roomserver/api"
	"github.com/matrix-org/dendrite/roomserver/internal/helpers"
	"github.com/matrix-org/dendrite/roomserver/internal/input"
	"github.com/matrix-org/dendrite/roomserver/storage"
	"github.com/matrix-org/dendrite/setup/config"
	userapi "github.com/matrix-org/dendrite/userapi/api"
)

type Leaver struct {
	Cfg     *config.RoomServer
	DB      storage.Database
	FSAPI   fsAPI.FederationInternalAPI
	UserAPI userapi.UserInternalAPI
	Inputer *input.Inputer
}

// WriteOutputEvents implements OutputRoomEventWriter
func (r *Leaver) PerformLeave(
	ctx context.Context,
	req *api.PerformLeaveRequest,
	res *api.PerformLeaveResponse,
) ([]api.OutputEvent, error) {
	_, domain, err := gomatrixserverlib.SplitID('@', req.UserID)
	if err != nil {
		return nil, fmt.Errorf("supplied user ID %q in incorrect format", req.UserID)
	}
	if domain != r.Cfg.Matrix.ServerName {
		return nil, fmt.Errorf("user %q does not belong to this homeserver", req.UserID)
	}
	logger := logrus.WithContext(ctx).WithFields(logrus.Fields{
		"room_id": req.RoomID,
		"user_id": req.UserID,
	})
	logger.Info("User requested to leave join")
	if strings.HasPrefix(req.RoomID, "!") {
		output, err := r.performLeaveRoomByID(context.Background(), req, res)
		if err != nil {
			logger.WithError(err).Error("Failed to leave room")
		} else {
			logger.Info("User left room successfully")
		}
		return output, err
	}
	return nil, fmt.Errorf("room ID %q is invalid", req.RoomID)
}

func (r *Leaver) performLeaveRoomByID(
	ctx context.Context,
	req *api.PerformLeaveRequest,
	res *api.PerformLeaveResponse, // nolint:unparam
) ([]api.OutputEvent, error) {
	// If there's an invite outstanding for the room then respond to
	// that.
	isInvitePending, senderUser, eventID, err := helpers.IsInvitePending(ctx, r.DB, req.RoomID, req.UserID)
	if err == nil && isInvitePending {
		var host gomatrixserverlib.ServerName
		_, host, err = gomatrixserverlib.SplitID('@', senderUser)
		if err != nil {
			return nil, fmt.Errorf("sender %q is invalid", senderUser)
		}
		if host != r.Cfg.Matrix.ServerName {
			return r.performFederatedRejectInvite(ctx, req, res, senderUser, eventID)
		}
		// check that this is not a "server notice room"
		accData := &userapi.QueryAccountDataResponse{}
		if err = r.UserAPI.QueryAccountData(ctx, &userapi.QueryAccountDataRequest{
			UserID:   req.UserID,
			RoomID:   req.RoomID,
			DataType: "m.tag",
		}, accData); err != nil {
			return nil, fmt.Errorf("unable to query account data: %w", err)
		}

		if roomData, ok := accData.RoomAccountData[req.RoomID]; ok {
			tagData, ok := roomData["m.tag"]
			if ok {
				tags := gomatrix.TagContent{}
				if err = json.Unmarshal(tagData, &tags); err != nil {
					return nil, fmt.Errorf("unable to unmarshal tag content")
				}
				if _, ok = tags.Tags["m.server_notice"]; ok {
					// mimic the returned values from Synapse
					res.Message = "You cannot reject this invite"
					res.Code = 403
					return nil, fmt.Errorf("You cannot reject this invite")
				}
			}
		}
	}

	// There's no invite pending, so first of all we want to find out
	// if the room exists and if the user is actually in it.
	latestReq := api.QueryLatestEventsAndStateRequest{
		RoomID: req.RoomID,
		StateToFetch: []gomatrixserverlib.StateKeyTuple{
			{
				EventType: gomatrixserverlib.MRoomMember,
				StateKey:  req.UserID,
			},
		},
	}
	latestRes := api.QueryLatestEventsAndStateResponse{}
	if err = helpers.QueryLatestEventsAndState(ctx, r.DB, &latestReq, &latestRes); err != nil {
		return nil, err
	}
	if !latestRes.RoomExists {
		return nil, fmt.Errorf("room %q does not exist", req.RoomID)
	}

	// Now let's see if the user is in the room.
	if len(latestRes.StateEvents) == 0 {
		return nil, fmt.Errorf("user %q is not a member of room %q", req.UserID, req.RoomID)
	}
	membership, err := latestRes.StateEvents[0].Membership()
	if err != nil {
		return nil, fmt.Errorf("error getting membership: %w", err)
	}
	if membership != gomatrixserverlib.Join && membership != gomatrixserverlib.Invite {
		return nil, fmt.Errorf("user %q is not joined to the room (membership is %q)", req.UserID, membership)
	}

	// Prepare the template for the leave event.
	userID := req.UserID
	eb := gomatrixserverlib.EventBuilder{
		Type:     gomatrixserverlib.MRoomMember,
		Sender:   userID,
		StateKey: &userID,
		RoomID:   req.RoomID,
		Redacts:  "",
	}
	if err = eb.SetContent(map[string]interface{}{"membership": "leave"}); err != nil {
		return nil, fmt.Errorf("eb.SetContent: %w", err)
	}
	if err = eb.SetUnsigned(struct{}{}); err != nil {
		return nil, fmt.Errorf("eb.SetUnsigned: %w", err)
	}

	// We know that the user is in the room at this point so let's build
	// a leave event.
	// TODO: Check what happens if the room exists on the server
	// but everyone has since left. I suspect it does the wrong thing.
	event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, &eb)
	if err != nil {
		return nil, fmt.Errorf("eventutil.BuildEvent: %w", err)
	}

	// Give our leave event to the roomserver input stream. The
	// roomserver will process the membership change and notify
	// downstream automatically.
	inputReq := api.InputRoomEventsRequest{
		InputRoomEvents: []api.InputRoomEvent{
			{
				Kind:         api.KindNew,
				Event:        event.Headered(buildRes.RoomVersion),
				Origin:       event.Origin(),
				SendAsServer: string(r.Cfg.Matrix.ServerName),
			},
		},
	}
	inputRes := api.InputRoomEventsResponse{}
	r.Inputer.InputRoomEvents(ctx, &inputReq, &inputRes)
	if err = inputRes.Err(); err != nil {
		return nil, fmt.Errorf("r.InputRoomEvents: %w", err)
	}

	return nil, nil
}

func (r *Leaver) performFederatedRejectInvite(
	ctx context.Context,
	req *api.PerformLeaveRequest,
	res *api.PerformLeaveResponse, // nolint:unparam
	senderUser, eventID string,
) ([]api.OutputEvent, error) {
	_, domain, err := gomatrixserverlib.SplitID('@', senderUser)
	if err != nil {
		return nil, fmt.Errorf("user ID %q invalid: %w", senderUser, err)
	}

	// Ask the federation sender to perform a federated leave for us.
	leaveReq := fsAPI.PerformLeaveRequest{
		RoomID:      req.RoomID,
		UserID:      req.UserID,
		ServerNames: []gomatrixserverlib.ServerName{domain},
	}
	leaveRes := fsAPI.PerformLeaveResponse{}
	if err = r.FSAPI.PerformLeave(ctx, &leaveReq, &leaveRes); err != nil {
		// failures in PerformLeave should NEVER stop us from telling other components like the
		// sync API that the invite was withdrawn. Otherwise we can end up with stuck invites.
		util.GetLogger(ctx).WithError(err).Errorf("failed to PerformLeave, still retiring invite event")
	}

	info, err := r.DB.RoomInfo(ctx, req.RoomID)
	if err != nil {
		util.GetLogger(ctx).WithError(err).Errorf("failed to get RoomInfo, still retiring invite event")
	}

	updater, err := r.DB.MembershipUpdater(ctx, req.RoomID, req.UserID, true, info.RoomVersion)
	if err != nil {
		util.GetLogger(ctx).WithError(err).Errorf("failed to get MembershipUpdater, still retiring invite event")
	}
	if updater != nil {
		if _, err = updater.SetToLeave(req.UserID, eventID); err != nil {
			util.GetLogger(ctx).WithError(err).Errorf("failed to set membership to leave, still retiring invite event")
			if err = updater.Rollback(); err != nil {
				util.GetLogger(ctx).WithError(err).Errorf("failed to rollback membership leave, still retiring invite event")
			}
		} else {
			if err = updater.Commit(); err != nil {
				util.GetLogger(ctx).WithError(err).Errorf("failed to commit membership update, still retiring invite event")
			}
		}
	}

	// Withdraw the invite, so that the sync API etc are
	// notified that we rejected it.
	return []api.OutputEvent{
		{
			Type: api.OutputTypeRetireInviteEvent,
			RetireInviteEvent: &api.OutputRetireInviteEvent{
				EventID:      eventID,
				Membership:   "leave",
				TargetUserID: req.UserID,
			},
		},
	}, nil
}
<|endoftext|>


<commit_before>package meli

import (
	"context"
	"io/ioutil"
	"strings"
	"testing"
)

func TestPullDockerImage(t *testing.T) {
	tt := []struct {
		dc          *DockerContainer
		expectedErr error
	}{
		{&DockerContainer{ComposeService: ComposeService{Image: "busybox"}, LogMedium: ioutil.Discard}, nil},
	}
	var ctx = context.Background()
	cli := &mockDockerClient{}
	for _, v := range tt {
		err := PullDockerImage(ctx, cli, v.dc)
		if err != nil {
			t.Errorf("\nCalled PullDockerImage(%#+v) \ngot %s \nwanted %#+v", v.dc, err, v.expectedErr)
		}
	}
}

func TestBuildDockerImage(t *testing.T) {
	tt := []struct {
		dc          *DockerContainer
		expected    string
		expectedErr error
	}{
		{
			&DockerContainer{
				ServiceName:       "myservicename",
				DockerComposeFile: "docker-compose.yml",
				ComposeService: ComposeService{
					Build: Buildstruct{Dockerfile: "testdata/Dockerfile"}},
				LogMedium: ioutil.Discard},
			"meli_myservicename",
			nil},
		{
			&DockerContainer{
				ServiceName:       "myservicename",
				DockerComposeFile: "docker-compose.yml",
				ComposeService: ComposeService{
					Build: Buildstruct{Dockerfile: "testdata/Dockerfile"}},
				LogMedium: ioutil.Discard,
				Rebuild:   true,
			},
			"meli_myservicename",
			nil},
	}

	var ctx = context.Background()
	cli := &mockDockerClient{}
	LoadAuth()
	for _, v := range tt {
		actual, err := BuildDockerImage(ctx, cli, v.dc)
		if err != nil {
			t.Errorf("\nCalled BuildDockerImage(%#+v) \ngot %s \nwanted %#+v", v.dc, err, v.expectedErr)
		}
		if actual != v.expected {
			t.Errorf("\nCalled BuildDockerImage(%#+v) \ngot %s \nwanted %#+v", v.dc, actual, v.expected)
		}
	}
}

func BenchmarkPullDockerImage(b *testing.B) {
	var ctx = context.Background()
	cli := &mockDockerClient{}
	dc := &DockerContainer{ComposeService: ComposeService{Image: "busybox"}, LogMedium: ioutil.Discard}
	LoadAuth()
	for n := 0; n < b.N; n++ {
		_ = PullDockerImage(ctx, cli, dc)
	}
}

func BenchmarkBuildDockerImage(b *testing.B) {
	var ctx = context.Background()
	cli := &mockDockerClient{}
	dc := &DockerContainer{
		ServiceName:       "myservicename",
		DockerComposeFile: "docker-compose.yml",
		ComposeService: ComposeService{
			Build: Buildstruct{Dockerfile: "testdata/Dockerfile"}},
		LogMedium: ioutil.Discard,
		Rebuild:   true,
	}
	LoadAuth()
	for n := 0; n < b.N; n++ {
		_, _ = BuildDockerImage(ctx, cli, dc)
	}
}

func BenchmarkPoolReadFrom(b *testing.B) {
	r := strings.NewReader("hello")
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, _ = poolReadFrom(r)
	}
}
<commit_msg>reset benchmark timer<commit_after>package meli

import (
	"context"
	"io/ioutil"
	"strings"
	"testing"
)

func TestPullDockerImage(t *testing.T) {
	tt := []struct {
		dc          *DockerContainer
		expectedErr error
	}{
		{&DockerContainer{ComposeService: ComposeService{Image: "busybox"}, LogMedium: ioutil.Discard}, nil},
	}
	var ctx = context.Background()
	cli := &mockDockerClient{}
	for _, v := range tt {
		err := PullDockerImage(ctx, cli, v.dc)
		if err != nil {
			t.Errorf("\nCalled PullDockerImage(%#+v) \ngot %s \nwanted %#+v", v.dc, err, v.expectedErr)
		}
	}
}

func TestBuildDockerImage(t *testing.T) {
	tt := []struct {
		dc          *DockerContainer
		expected    string
		expectedErr error
	}{
		{
			&DockerContainer{
				ServiceName:       "myservicename",
				DockerComposeFile: "docker-compose.yml",
				ComposeService: ComposeService{
					Build: Buildstruct{Dockerfile: "testdata/Dockerfile"}},
				LogMedium: ioutil.Discard},
			"meli_myservicename",
			nil},
		{
			&DockerContainer{
				ServiceName:       "myservicename",
				DockerComposeFile: "docker-compose.yml",
				ComposeService: ComposeService{
					Build: Buildstruct{Dockerfile: "testdata/Dockerfile"}},
				LogMedium: ioutil.Discard,
				Rebuild:   true,
			},
			"meli_myservicename",
			nil},
	}

	var ctx = context.Background()
	cli := &mockDockerClient{}
	LoadAuth()
	for _, v := range tt {
		actual, err := BuildDockerImage(ctx, cli, v.dc)
		if err != nil {
			t.Errorf("\nCalled BuildDockerImage(%#+v) \ngot %s \nwanted %#+v", v.dc, err, v.expectedErr)
		}
		if actual != v.expected {
			t.Errorf("\nCalled BuildDockerImage(%#+v) \ngot %s \nwanted %#+v", v.dc, actual, v.expected)
		}
	}
}

func BenchmarkPullDockerImage(b *testing.B) {
	var ctx = context.Background()
	cli := &mockDockerClient{}
	dc := &DockerContainer{ComposeService: ComposeService{Image: "busybox"}, LogMedium: ioutil.Discard}
	LoadAuth()
	for n := 0; n < b.N; n++ {
		_ = PullDockerImage(ctx, cli, dc)
	}
}

func BenchmarkBuildDockerImage(b *testing.B) {
	var ctx = context.Background()
	cli := &mockDockerClient{}
	dc := &DockerContainer{
		ServiceName:       "myservicename",
		DockerComposeFile: "docker-compose.yml",
		ComposeService: ComposeService{
			Build: Buildstruct{Dockerfile: "testdata/Dockerfile"}},
		LogMedium: ioutil.Discard,
		Rebuild:   true,
	}
	LoadAuth()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, _ = BuildDockerImage(ctx, cli, dc)
	}
}

func BenchmarkPoolReadFrom(b *testing.B) {
	r := strings.NewReader("hello")
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		_, _ = poolReadFrom(r)
	}
}
<|endoftext|>


<commit_before>/*
Package godnsbl lets you perform RBL (Real-time Blackhole List - https://en.wikipedia.org/wiki/DNSBL)
lookups using Golang

JSON annotations on the types are provided as a convenience.
*/
package godnsbl

import (
	"fmt"
	"net"
	"strings"
)

/*
Blacklists is the list of blackhole lists to check against
*/
var Blacklists = []string{
	"aspews.ext.sorbs.net",
	"b.barracudacentral.org",
	"bl.deadbeef.com",
	"bl.emailbasura.org",
	"bl.spamcannibal.org",
	"bl.spamcop.net",
	"blackholes.five-ten-sg.com",
	"blacklist.woody.ch",
	"bogons.cymru.com",
	"cbl.abuseat.org",
	"cdl.anti-spam.org.cn",
	"combined.abuse.ch",
	"combined.rbl.msrbl.net",
	"db.wpbl.info",
	"dnsbl-1.uceprotect.net",
	"dnsbl-2.uceprotect.net",
	"dnsbl-3.uceprotect.net",
	"dnsbl.cyberlogic.net",
	"dnsbl.dronebl.org",
	"dnsbl.inps.de",
	"dnsbl.njabl.org",
	"dnsbl.sorbs.net",
	"drone.abuse.ch",
	"duinv.aupads.org",
	"dul.dnsbl.sorbs.net",
	"dul.ru",
	"dyna.spamrats.com",
	"dynip.rothen.com",
	"http.dnsbl.sorbs.net",
	"images.rbl.msrbl.net",
	"ips.backscatterer.org",
	"ix.dnsbl.manitu.net",
	"korea.services.net",
	"misc.dnsbl.sorbs.net",
	"noptr.spamrats.com",
	"ohps.dnsbl.net.au",
	"omrs.dnsbl.net.au",
	"orvedb.aupads.org",
	"osps.dnsbl.net.au",
	"osrs.dnsbl.net.au",
	"owfs.dnsbl.net.au",
	"owps.dnsbl.net.au",
	"pbl.spamhaus.org",
	"phishing.rbl.msrbl.net",
	"probes.dnsbl.net.au",
	"proxy.bl.gweep.ca",
	"proxy.block.transip.nl",
	"psbl.surriel.com",
	"rdts.dnsbl.net.au",
	"relays.bl.gweep.ca",
	"relays.bl.kundenserver.de",
	"relays.nether.net",
	"residential.block.transip.nl",
	"ricn.dnsbl.net.au",
	"rmst.dnsbl.net.au",
	"sbl.spamhaus.org",
	"short.rbl.jp",
	"smtp.dnsbl.sorbs.net",
	"socks.dnsbl.sorbs.net",
	"spam.abuse.ch",
	"spam.dnsbl.sorbs.net",
	"spam.rbl.msrbl.net",
	"spam.spamrats.com",
	"spamlist.or.kr",
	"spamrbl.imp.ch",
	"t3direct.dnsbl.net.au",
	"tor.dnsbl.sectoor.de",
	"torserver.tor.dnsbl.sectoor.de",
	"ubl.lashback.com",
	"ubl.unsubscore.com",
	"virbl.bit.nl",
	"virus.rbl.jp",
	"virus.rbl.msrbl.net",
	"web.dnsbl.sorbs.net",
	"wormrbl.imp.ch",
	"xbl.spamhaus.org",
	"zen.spamhaus.org",
	"zombie.dnsbl.sorbs.net"}

/*
RBLResults holds the results of the lookup.
*/
type RBLResults struct {
	// List is the RBL that was searched
	List string `json:"list"`
	// Host is the host or IP that was passed (i.e. smtp.gmail.com)
	Host string `json:"host"`
	// Results is a slice of Results - one per IP address searched
	Results []Result `json:"results"`
}

/*
Result holds the individual IP lookup results for each RBL search
*/
type Result struct {
	// Address is the IP address that was searched
	Address string `json:"address"`
	// Listed indicates whether or not the IP was on the RBL
	Listed bool `json:"listed"`
	// RBL lists sometimes add extra information as a TXT record
	// if any info is present, it will be stored here.
	Text string `json:"text"`
	// Error represents any error that was encountered (DNS timeout, host not
	// found, etc.) if any
	Error bool `json:"error"`
	// ErrorType is the type of error encountered if any
	ErrorType error `json:"error_type"`
}

/*
Reverse the octets of a given IPv4 address
64.233.171.108 becomes 108.171.233.64
*/
func Reverse(ip net.IP) string {
	if ip.To4() != nil {
		splitAddress := strings.Split(ip.String(), ".")

		for i, j := 0, len(splitAddress)-1; i < len(splitAddress)/2; i, j = i+1, j-1 {
			splitAddress[i], splitAddress[j] = splitAddress[j], splitAddress[i]
		}

		return strings.Join(splitAddress, ".")
	}
	return ""
}

func query(rbl string, host string, r *Result) {
	r.Listed = false

	lookup := fmt.Sprintf("%s.%s", host, rbl)

	res, err := net.LookupHost(lookup)
	if len(res) > 0 {
		r.Listed = true
		txt, _ := net.LookupTXT(lookup)
		if len(txt) > 0 {
			r.Text = txt[0]
		}
	}
	if err != nil {
		r.Error = true
		r.ErrorType = err
	}

	return
}

/*
Lookup performs the search and returns the RBLResults
*/
func Lookup(rblList string, targetHost string) (r RBLResults) {
	r.List = rblList
	r.Host = targetHost

	if ip, err := net.LookupIP(targetHost); err == nil {
		for _, addr := range ip {
			if addr.To4() != nil {
				res := Result{}
				res.Address = addr.String()

				addr := Reverse(addr)

				query(rblList, addr, &res)

				r.Results = append(r.Results, res)
			}
		}
	} else {
		r.Results = append(r.Results, Result{})
	}
	return
}
<commit_msg>Updated Reverse() to be more idiomatic<commit_after>/*
Package godnsbl lets you perform RBL (Real-time Blackhole List - https://en.wikipedia.org/wiki/DNSBL)
lookups using Golang

JSON annotations on the types are provided as a convenience.
*/
package godnsbl

import (
	"fmt"
	"net"
	"strings"
)

/*
Blacklists is the list of blackhole lists to check against
*/
var Blacklists = []string{
	"aspews.ext.sorbs.net",
	"b.barracudacentral.org",
	"bl.deadbeef.com",
	"bl.emailbasura.org",
	"bl.spamcannibal.org",
	"bl.spamcop.net",
	"blackholes.five-ten-sg.com",
	"blacklist.woody.ch",
	"bogons.cymru.com",
	"cbl.abuseat.org",
	"cdl.anti-spam.org.cn",
	"combined.abuse.ch",
	"combined.rbl.msrbl.net",
	"db.wpbl.info",
	"dnsbl-1.uceprotect.net",
	"dnsbl-2.uceprotect.net",
	"dnsbl-3.uceprotect.net",
	"dnsbl.cyberlogic.net",
	"dnsbl.dronebl.org",
	"dnsbl.inps.de",
	"dnsbl.njabl.org",
	"dnsbl.sorbs.net",
	"drone.abuse.ch",
	"duinv.aupads.org",
	"dul.dnsbl.sorbs.net",
	"dul.ru",
	"dyna.spamrats.com",
	"dynip.rothen.com",
	"http.dnsbl.sorbs.net",
	"images.rbl.msrbl.net",
	"ips.backscatterer.org",
	"ix.dnsbl.manitu.net",
	"korea.services.net",
	"misc.dnsbl.sorbs.net",
	"noptr.spamrats.com",
	"ohps.dnsbl.net.au",
	"omrs.dnsbl.net.au",
	"orvedb.aupads.org",
	"osps.dnsbl.net.au",
	"osrs.dnsbl.net.au",
	"owfs.dnsbl.net.au",
	"owps.dnsbl.net.au",
	"pbl.spamhaus.org",
	"phishing.rbl.msrbl.net",
	"probes.dnsbl.net.au",
	"proxy.bl.gweep.ca",
	"proxy.block.transip.nl",
	"psbl.surriel.com",
	"rdts.dnsbl.net.au",
	"relays.bl.gweep.ca",
	"relays.bl.kundenserver.de",
	"relays.nether.net",
	"residential.block.transip.nl",
	"ricn.dnsbl.net.au",
	"rmst.dnsbl.net.au",
	"sbl.spamhaus.org",
	"short.rbl.jp",
	"smtp.dnsbl.sorbs.net",
	"socks.dnsbl.sorbs.net",
	"spam.abuse.ch",
	"spam.dnsbl.sorbs.net",
	"spam.rbl.msrbl.net",
	"spam.spamrats.com",
	"spamlist.or.kr",
	"spamrbl.imp.ch",
	"t3direct.dnsbl.net.au",
	"tor.dnsbl.sectoor.de",
	"torserver.tor.dnsbl.sectoor.de",
	"ubl.lashback.com",
	"ubl.unsubscore.com",
	"virbl.bit.nl",
	"virus.rbl.jp",
	"virus.rbl.msrbl.net",
	"web.dnsbl.sorbs.net",
	"wormrbl.imp.ch",
	"xbl.spamhaus.org",
	"zen.spamhaus.org",
	"zombie.dnsbl.sorbs.net"}

/*
RBLResults holds the results of the lookup.
*/
type RBLResults struct {
	// List is the RBL that was searched
	List string `json:"list"`
	// Host is the host or IP that was passed (i.e. smtp.gmail.com)
	Host string `json:"host"`
	// Results is a slice of Results - one per IP address searched
	Results []Result `json:"results"`
}

/*
Result holds the individual IP lookup results for each RBL search
*/
type Result struct {
	// Address is the IP address that was searched
	Address string `json:"address"`
	// Listed indicates whether or not the IP was on the RBL
	Listed bool `json:"listed"`
	// RBL lists sometimes add extra information as a TXT record
	// if any info is present, it will be stored here.
	Text string `json:"text"`
	// Error represents any error that was encountered (DNS timeout, host not
	// found, etc.) if any
	Error bool `json:"error"`
	// ErrorType is the type of error encountered if any
	ErrorType error `json:"error_type"`
}

/*
Reverse the octets of a given IPv4 address
64.233.171.108 becomes 108.171.233.64
*/
func Reverse(ip net.IP) string {
	if ip.To4() == nil {
		return ""
	}

	splitAddress := strings.Split(ip.String(), ".")

	for i, j := 0, len(splitAddress)-1; i < len(splitAddress)/2; i, j = i+1, j-1 {
		splitAddress[i], splitAddress[j] = splitAddress[j], splitAddress[i]
	}

	return strings.Join(splitAddress, ".")
}

func query(rbl string, host string, r *Result) {
	r.Listed = false

	lookup := fmt.Sprintf("%s.%s", host, rbl)

	res, err := net.LookupHost(lookup)
	if len(res) > 0 {
		r.Listed = true
		txt, _ := net.LookupTXT(lookup)
		if len(txt) > 0 {
			r.Text = txt[0]
		}
	}
	if err != nil {
		r.Error = true
		r.ErrorType = err
	}

	return
}

/*
Lookup performs the search and returns the RBLResults
*/
func Lookup(rblList string, targetHost string) (r RBLResults) {
	r.List = rblList
	r.Host = targetHost

	if ip, err := net.LookupIP(targetHost); err == nil {
		for _, addr := range ip {
			if addr.To4() != nil {
				res := Result{}
				res.Address = addr.String()

				addr := Reverse(addr)

				query(rblList, addr, &res)

				r.Results = append(r.Results, res)
			}
		}
	} else {
		r.Results = append(r.Results, Result{})
	}
	return
}
<|endoftext|>