<commit_before>package kite

import (
	"errors"
	"fmt"
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/koding/kite/dnode"
	"github.com/koding/kite/kitekey"
)

// runMethod is called when a method is received from remote Kite.
func (c *Client) runMethod(method string, handlerFunc HandlerFunc, args *dnode.Partial) {
	var (
		// The request that will be constructed from incoming dnode message.
		request *Request

		// First value to the response.
		result interface{}

		// Second value to the response.
		kiteErr *Error

		// Will send the response when called.
		callback dnode.Function
	)

	// Send result if "responseCallback" exists in the request.
	defer func() {
		if callback.Caller == nil {
			return
		}

		// Only argument to the callback.
		response := Response{
			Result: result,
			Error:  kiteErr,
		}

		// Call response callback function.
		if err := callback.Call(response); err != nil {
			c.LocalKite.Log.Error(err.Error())
		}
	}()

	// Recover dnode argument errors. The caller can use functions like
	// MustString(), MustSlice()... without the fear of panic.
	defer c.LocalKite.recoverError(&kiteErr)()

	request, callback = c.newRequest(method, args)

	if !c.LocalKite.Config.DisableAuthentication {
		kiteErr = request.authenticate()
		if kiteErr != nil {
			return
		}
	}

	// Call the handler function.
	var err error
	result, err = handlerFunc(request)

	if err != nil {
		panic(err) // This will be recoverd from kite.recoverError() above.
	}
}

// Response is the type of the object that is returned from request handlers
// and the type of only argument that is passed to callback functions.
type Response struct {
	Error  *Error      `json:"error" dnode:"-"`
	Result interface{} `json:"result"`
}

// HandlerFunc is the type of the handlers registered to Kite.
// The returned result must be Marshalable with json package.
type HandlerFunc func(*Request) (result interface{}, err error)

// HandleFunc registers a handler to run when a method call is received from a Kite.
func (k *Kite) HandleFunc(method string, handler HandlerFunc) {
	k.handlers[method] = handler
}

// Request contains information about the incoming request.
type Request struct {
	Method         string
	Args           *dnode.Partial
	LocalKite      *Kite
	Client         *Client
	Username       string
	Authentication *Authentication
}

// runCallback is called when a callback method call is received from remote Kite.
func (c *Client) runCallback(callback func(*dnode.Partial), args *dnode.Partial) {
	kiteErr := new(Error)                      // Not used. For recovering the error.
	defer c.LocalKite.recoverError(&kiteErr)() // Do not panic no matter what.

	// Call the callback function.
	callback(args)
}

// newRequest returns a new *Request from the method and arguments passed.
func (c *Client) newRequest(method string, arguments *dnode.Partial) (request *Request, responseCallback dnode.Function) {
	// Parse dnode method arguments: [options]
	var options callOptions
	arguments.One().MustUnmarshal(&options)

	// Notify the handlers registered with Kite.OnFirstRequest().
	if c.RemoteAddr() != "" {
		c.firstRequestHandlersNotified.Do(func() {
			c.Kite = options.Kite
			c.LocalKite.callOnFirstRequestHandlers(c)
		})
	}

	request = &Request{
		Method:         method,
		Args:           options.WithArgs,
		LocalKite:      c.LocalKite,
		Client:         c,
		Authentication: options.Authentication,
	}

	return request, options.ResponseCallback
}

// authenticate tries to authenticate the user by selecting appropriate
// authenticator function.
func (r *Request) authenticate() *Error {
	// Trust the Kite if we have initiated the connection.
	// RemoteAddr() returns "" if this is an outgoing connection.
	if r.Client.RemoteAddr() == "" {
		return nil
	}

	if r.Authentication == nil {
		return &Error{
			Type:    "authenticationError",
			Message: "No authentication information is provided",
		}
	}

	// Select authenticator function.
	f := r.LocalKite.Authenticators[r.Authentication.Type]
	if f == nil {
		return &Error{
			Type:    "authenticationError",
			Message: fmt.Sprintf("Unknown authentication type: %s", r.Authentication.Type),
		}
	}

	// Call authenticator function. It sets the Request.Username field.
	err := f(r)
	if err != nil {
		return &Error{
			Type:    "authenticationError",
			Message: err.Error(),
		}
	}

	// Fix username of the remote Kite if it is invalid.
	// This prevents a Kite to impersonate someone else's Kite.
	r.Client.Kite.Username = r.Username

	return nil
}

// AuthenticateFromToken is the default Authenticator for Kite.
func (k *Kite) AuthenticateFromToken(r *Request) error {
	token, err := jwt.Parse(r.Authentication.Key, r.LocalKite.RSAKey)
	if err != nil {
		return err
	}

	if !token.Valid {
		return errors.New("Invalid signature in token")
	}

	if audience, ok := token.Claims["aud"].(string); !ok || !strings.HasPrefix(k.Kite().String(), audience) {
		return fmt.Errorf("Invalid audience in token: %s", audience)
	}

	// We don't check for exp and nbf claims here because jwt-go package already checks them.

	if username, ok := token.Claims["sub"].(string); !ok {
		return errors.New("Username is not present in token")
	} else {
		r.Username = username
	}

	return nil
}

// AuthenticateFromKiteKey authenticates user from kite key.
func (k *Kite) AuthenticateFromKiteKey(r *Request) error {
	token, err := jwt.Parse(r.Authentication.Key, kitekey.GetKontrolKey)
	if err != nil {
		return err
	}

	if !token.Valid {
		return errors.New("Invalid signature in token")
	}

	if username, ok := token.Claims["sub"].(string); !ok {
		return errors.New("Username is not present in token")
	} else {
		r.Username = username
	}

	return nil
}
<commit_msg>kite/request: move types to top<commit_after>package kite

import (
	"errors"
	"fmt"
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/koding/kite/dnode"
	"github.com/koding/kite/kitekey"
)

// Request contains information about the incoming request.
type Request struct {
	Method         string
	Args           *dnode.Partial
	LocalKite      *Kite
	Client         *Client
	Username       string
	Authentication *Authentication
}

// Response is the type of the object that is returned from request handlers
// and the type of only argument that is passed to callback functions.
type Response struct {
	Error  *Error      `json:"error" dnode:"-"`
	Result interface{} `json:"result"`
}

// HandlerFunc is the type of the handlers registered to Kite.
// The returned result must be Marshalable with json package.
type HandlerFunc func(*Request) (result interface{}, err error)

// runMethod is called when a method is received from remote Kite.
func (c *Client) runMethod(method string, handlerFunc HandlerFunc, args *dnode.Partial) {
	var (
		// The request that will be constructed from incoming dnode message.
		request *Request

		// First value to the response.
		result interface{}

		// Second value to the response.
		kiteErr *Error

		// Will send the response when called.
		callback dnode.Function
	)

	// Send result if "responseCallback" exists in the request.
	defer func() {
		if callback.Caller == nil {
			return
		}

		// Only argument to the callback.
		response := Response{
			Result: result,
			Error:  kiteErr,
		}

		// Call response callback function.
		if err := callback.Call(response); err != nil {
			c.LocalKite.Log.Error(err.Error())
		}
	}()

	// Recover dnode argument errors. The caller can use functions like
	// MustString(), MustSlice()... without the fear of panic.
	defer c.LocalKite.recoverError(&kiteErr)()

	request, callback = c.newRequest(method, args)

	if !c.LocalKite.Config.DisableAuthentication {
		kiteErr = request.authenticate()
		if kiteErr != nil {
			return
		}
	}

	// Call the handler function.
	var err error
	result, err = handlerFunc(request)

	if err != nil {
		panic(err) // This will be recoverd from kite.recoverError() above.
	}
}

// HandleFunc registers a handler to run when a method call is received from a Kite.
func (k *Kite) HandleFunc(method string, handler HandlerFunc) {
	k.handlers[method] = handler
}

// runCallback is called when a callback method call is received from remote Kite.
func (c *Client) runCallback(callback func(*dnode.Partial), args *dnode.Partial) {
	kiteErr := new(Error)                      // Not used. For recovering the error.
	defer c.LocalKite.recoverError(&kiteErr)() // Do not panic no matter what.

	// Call the callback function.
	callback(args)
}

// newRequest returns a new *Request from the method and arguments passed.
func (c *Client) newRequest(method string, arguments *dnode.Partial) (request *Request, responseCallback dnode.Function) {
	// Parse dnode method arguments: [options]
	var options callOptions
	arguments.One().MustUnmarshal(&options)

	// Notify the handlers registered with Kite.OnFirstRequest().
	if c.RemoteAddr() != "" {
		c.firstRequestHandlersNotified.Do(func() {
			c.Kite = options.Kite
			c.LocalKite.callOnFirstRequestHandlers(c)
		})
	}

	request = &Request{
		Method:         method,
		Args:           options.WithArgs,
		LocalKite:      c.LocalKite,
		Client:         c,
		Authentication: options.Authentication,
	}

	return request, options.ResponseCallback
}

// authenticate tries to authenticate the user by selecting appropriate
// authenticator function.
func (r *Request) authenticate() *Error {
	// Trust the Kite if we have initiated the connection.
	// RemoteAddr() returns "" if this is an outgoing connection.
	if r.Client.RemoteAddr() == "" {
		return nil
	}

	if r.Authentication == nil {
		return &Error{
			Type:    "authenticationError",
			Message: "No authentication information is provided",
		}
	}

	// Select authenticator function.
	f := r.LocalKite.Authenticators[r.Authentication.Type]
	if f == nil {
		return &Error{
			Type:    "authenticationError",
			Message: fmt.Sprintf("Unknown authentication type: %s", r.Authentication.Type),
		}
	}

	// Call authenticator function. It sets the Request.Username field.
	err := f(r)
	if err != nil {
		return &Error{
			Type:    "authenticationError",
			Message: err.Error(),
		}
	}

	// Fix username of the remote Kite if it is invalid.
	// This prevents a Kite to impersonate someone else's Kite.
	r.Client.Kite.Username = r.Username

	return nil
}

// AuthenticateFromToken is the default Authenticator for Kite.
func (k *Kite) AuthenticateFromToken(r *Request) error {
	token, err := jwt.Parse(r.Authentication.Key, r.LocalKite.RSAKey)
	if err != nil {
		return err
	}

	if !token.Valid {
		return errors.New("Invalid signature in token")
	}

	if audience, ok := token.Claims["aud"].(string); !ok || !strings.HasPrefix(k.Kite().String(), audience) {
		return fmt.Errorf("Invalid audience in token: %s", audience)
	}

	// We don't check for exp and nbf claims here because jwt-go package already checks them.

	if username, ok := token.Claims["sub"].(string); !ok {
		return errors.New("Username is not present in token")
	} else {
		r.Username = username
	}

	return nil
}

// AuthenticateFromKiteKey authenticates user from kite key.
func (k *Kite) AuthenticateFromKiteKey(r *Request) error {
	token, err := jwt.Parse(r.Authentication.Key, kitekey.GetKontrolKey)
	if err != nil {
		return err
	}

	if !token.Valid {
		return errors.New("Invalid signature in token")
	}

	if username, ok := token.Claims["sub"].(string); !ok {
		return errors.New("Username is not present in token")
	} else {
		r.Username = username
	}

	return nil
}
<|endoftext|>


<commit_before>package Golf

import (
	"net/http"
	"strings"
)

// A wrapper of http.Request
type Request struct {
	*http.Request
	Params map[string]string
	IP     string
}

func NewRequest(req *http.Request) *Request {
	request := new(Request)
	request.Request = req
	request.IP = strings.Split(req.RemoteAddr, ":")[0]
	return request
}

func (req *Request) GetCookie(key string) string {
	cookie, err := req.Request.Cookie(key)
	if err != nil {
		return ""
	} else {
		return cookie.Value
	}
}
<commit_msg>Add Query and Protocol support<commit_after>package Golf

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

// A wrapper of http.Request
type Request struct {
	*http.Request
	Params map[string]string
	IP     string
}

func NewRequest(req *http.Request) *Request {
	request := new(Request)
	request.Request = req
	request.IP = strings.Split(req.RemoteAddr, ":")[0]
	return request
}

// Query returns query data by the query key.
func (req *Request) Query(key string, index ...int) (string, error) {
	req.ParseForm()
	if val, ok := req.Form[key]; ok {
		if len(index) == 1 {
			return val[index[0]], nil
		} else {
			return val[0], nil
		}
	} else {
		return "", errors.New("Query key not found.")
	}
}

// Cookie returns request cookie item string by a given key.
func (req *Request) Cookie(key string) string {
	cookie, err := req.Request.Cookie(key)
	if err != nil {
		return ""
	} else {
		return cookie.Value
	}
}

// Protocol returns the request protocol string
func (req *Request) Protocol() string {
	return req.Proto
}
<|endoftext|>


<commit_before>package guber

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Request struct {
	client    *Client
	method    string
	baseurl   string
	query     string
	resource  string
	namespace string
	name      string
	body      []byte

	// NOTE this is used distinct from err, because a 404 is not technically an
	// error, except to the end-user who expects a resource to be there.
	// Without this, we don't have a way to determine if an err was a 404 or
	// something lower-level without inspecting the error message.
	found bool

	err      error
	response *http.Response
}

func (r *Request) error(err error) {
	if err != nil && r.err == nil {
		r.err = err
	}
}

func (r *Request) url() string {
	path := ""
	if r.namespace != "" {
		path = fmt.Sprintf("namespaces/%s/", r.namespace)
	}
	path = path + r.resource
	if r.name != "" {
		path = path + "/" + r.name
	}
	if r.query != "" {
		path = path + "?" + r.query
	}
	return r.baseurl + "/" + path
}

func (r *Request) Resource(res Resource) *Request {
	baseurl := fmt.Sprintf("https://%s", r.client.Host)
	if res.DomainName() != "" {
		baseurl = fmt.Sprintf("%s/%s", baseurl, res.DomainName())
	}
	r.baseurl = fmt.Sprintf("%s/%s/%s", baseurl, res.APIGroup(), res.APIVersion())
	r.resource = res.APIName()
	return r
}

func (r *Request) Namespace(namespace string) *Request {
	r.namespace = namespace
	return r
}

func (r *Request) Name(name string) *Request {
	r.name = name
	return r
}

func (r *Request) Entity(e Entity) *Request {
	body, err := json.Marshal(e)
	r.body = body
	r.error(err)
	return r
}

func (r *Request) Query(q *QueryParams) *Request {
	if q == nil {
		return r
	}

	// v, err := query.Values(q)
	// if err != nil {
	// 	panic(err) // TODO should use r.error() here probably
	// }
	// queryStr := v.Encode()

	// TODO  -- we went with this terribly rigid strategy because of how query pkg encodes the = chars
	if ls := q.LabelSelector; ls != "" {
		r.query = "labelSelector=" + ls
	}

	return r
}

func (r *Request) Do() *Request {
	req, err := http.NewRequest(r.method, r.url(), bytes.NewBuffer(r.body))
	if err != nil {
		panic(err) // TODO
	}

	req.SetBasicAuth(r.client.Username, r.client.Password)
	r.error(err)

	resp, err := r.client.http.Do(req)
	r.error(err)

	// TODO
	if resp != nil {
		r.response = resp

		r.readBody()

		if resp.StatusCode == 404 {
			r.found = false
		} else if status := resp.Status; status[:2] != "20" {
			errMsg := fmt.Sprintf("Status: %s, Body: %s", status, string(r.body))
			r.error(errors.New(errMsg))
			r.found = false
		} else {
			r.found = true // NOTE this only really matters for lookups, but we set it true here anyhow
		}
	}
	return r
}

func (r *Request) readBody() {
	if r.response == nil {
		r.error(errors.New("Response is nil"))
		return
	}
	defer r.response.Body.Close()
	body, err := ioutil.ReadAll(r.response.Body)
	r.body = body
	r.error(err)
}

// The exit point for a Request (where error is pooped out)
func (r *Request) Into(e Entity) error {
	if r.body != nil {
		json.Unmarshal(r.body, e)
	}
	return r.err
}
<commit_msg>Use fmt.Errorf for error message on failed request<commit_after>package guber

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Request struct {
	client    *Client
	method    string
	baseurl   string
	query     string
	resource  string
	namespace string
	name      string
	body      []byte

	// NOTE this is used distinct from err, because a 404 is not technically an
	// error, except to the end-user who expects a resource to be there.
	// Without this, we don't have a way to determine if an err was a 404 or
	// something lower-level without inspecting the error message.
	found bool

	err      error
	response *http.Response
}

func (r *Request) error(err error) {
	if err != nil && r.err == nil {
		r.err = err
	}
}

func (r *Request) url() string {
	path := ""
	if r.namespace != "" {
		path = fmt.Sprintf("namespaces/%s/", r.namespace)
	}
	path = path + r.resource
	if r.name != "" {
		path = path + "/" + r.name
	}
	if r.query != "" {
		path = path + "?" + r.query
	}
	return r.baseurl + "/" + path
}

func (r *Request) Resource(res Resource) *Request {
	baseurl := fmt.Sprintf("https://%s", r.client.Host)
	if res.DomainName() != "" {
		baseurl = fmt.Sprintf("%s/%s", baseurl, res.DomainName())
	}
	r.baseurl = fmt.Sprintf("%s/%s/%s", baseurl, res.APIGroup(), res.APIVersion())
	r.resource = res.APIName()
	return r
}

func (r *Request) Namespace(namespace string) *Request {
	r.namespace = namespace
	return r
}

func (r *Request) Name(name string) *Request {
	r.name = name
	return r
}

func (r *Request) Entity(e Entity) *Request {
	body, err := json.Marshal(e)
	r.body = body
	r.error(err)
	return r
}

func (r *Request) Query(q *QueryParams) *Request {
	if q == nil {
		return r
	}

	// v, err := query.Values(q)
	// if err != nil {
	// 	panic(err) // TODO should use r.error() here probably
	// }
	// queryStr := v.Encode()

	// TODO  -- we went with this terribly rigid strategy because of how query pkg encodes the = chars
	if ls := q.LabelSelector; ls != "" {
		r.query = "labelSelector=" + ls
	}

	return r
}

func (r *Request) Do() *Request {
	req, err := http.NewRequest(r.method, r.url(), bytes.NewBuffer(r.body))
	if err != nil {
		panic(err) // TODO
	}

	req.SetBasicAuth(r.client.Username, r.client.Password)
	r.error(err)

	resp, err := r.client.http.Do(req)
	r.error(err)

	// TODO
	if resp != nil {
		r.response = resp

		r.readBody()

		if resp.StatusCode == 404 {
			r.found = false
		} else if status := resp.Status; status[:2] != "20" {
			r.error(fmt.Errorf("Status: %s, Body: %s", status, string(r.body)))
			r.found = false
		} else {
			r.found = true // NOTE this only really matters for lookups, but we set it true here anyhow
		}
	}
	return r
}

func (r *Request) readBody() {
	if r.response == nil {
		r.error(errors.New("Response is nil"))
		return
	}
	defer r.response.Body.Close()
	body, err := ioutil.ReadAll(r.response.Body)
	r.body = body
	r.error(err)
}

// The exit point for a Request (where error is pooped out)
func (r *Request) Into(e Entity) error {
	if r.body != nil {
		json.Unmarshal(r.body, e)
	}
	return r.err
}
<|endoftext|>


<commit_before>package falcore

import (
	"container/list"
	"fmt"
	"hash"
	"hash/crc32"
	"math/rand"
	"net"
	"net/http"
	"reflect"
	"time"
)

// Request wrapper
//
// The request is wrapped so that useful information can be kept
// with the request as it moves through the pipeline.
//
// A pointer is kept to the originating Connection.
//
// There is a unique ID assigned to each request.  This ID is not
// globally unique to keep it shorter for logging purposes.  It is
// possible to have duplicates though very unlikely over the period
// of a day or so.  It is a good idea to log the ID in any custom
// log statements so that individual requests can easily be grepped
// from busy log files.
//
// Falcore collects performance statistics on every stage of the
// pipeline.  The stats for the request are kept in PipelineStageStats.
// This structure will only be complete in the Request passed to the
// pipeline RequestDoneCallback.  Overhead will only be available in
// the RequestDoneCallback and it's the difference between the total
// request time and the sums of the stage times.  It will include things
// like pipeline iteration and the stat collection itself.
//
// See falcore.PipelineStageStat docs for more info.
//
// The Signature is also a cool feature. See the
type Request struct {
	ID                 string
	StartTime          time.Time
	EndTime            time.Time
	HttpRequest        *http.Request
	Connection         net.Conn
	RemoteAddr         *net.TCPAddr
	PipelineStageStats *list.List
	CurrentStage       *PipelineStageStat
	pipelineHash       hash.Hash32
	piplineTot         time.Duration
	Overhead           time.Duration
	Context            map[string]interface{}
}

// Used internally to create and initialize a new request.
func newRequest(request *http.Request, conn net.Conn, startTime time.Time) *Request {
	fReq := new(Request)
	fReq.Context = make(map[string]interface{})
	fReq.HttpRequest = request
	fReq.StartTime = startTime
	fReq.Connection = conn
	if conn != nil {
		fReq.RemoteAddr = conn.RemoteAddr().(*net.TCPAddr)
	}
	// create a semi-unique id to track a connection in the logs
	// ID is the least significant decimal digits of time with some randomization
	// the last 3 zeros of time.Nanoseconds appear to always be zero
	var ut = fReq.StartTime.UnixNano()
	fReq.ID = fmt.Sprintf("%010x", (ut-(ut-(ut%1e12)))+int64(rand.Intn(999)))
	fReq.PipelineStageStats = list.New()
	fReq.pipelineHash = crc32.NewIEEE()
	return fReq
}

// Returns a completed falcore.Request and response after running the single filter stage
// The PipelineStageStats is completed in the returned Request
// The falcore.Request.Connection and falcore.Request.RemoteAddr are nil
func TestWithRequest(request *http.Request, filter RequestFilter, context map[string]interface{}) (*Request, *http.Response) {
	r := newRequest(request, nil, time.Now())
	if context == nil {
		context = make(map[string]interface{})
	}
	r.Context = context
	t := reflect.TypeOf(filter)
	r.startPipelineStage(t.String())
	res := filter.FilterRequest(r)
	r.finishPipelineStage()
	r.finishRequest()
	return r, res
}

// Starts a new pipeline stage and makes it the CurrentStage.
func (fReq *Request) startPipelineStage(name string) {
	fReq.CurrentStage = NewPiplineStage(name)
	fReq.PipelineStageStats.PushBack(fReq.CurrentStage)
}

// Finishes the CurrentStage.
func (fReq *Request) finishPipelineStage() {
	fReq.CurrentStage.EndTime = time.Now()
	fReq.finishCommon()
}

// Appends an already completed PipelineStageStat directly to the list
func (fReq *Request) appendPipelineStage(pss *PipelineStageStat) {
	fReq.PipelineStageStats.PushBack(pss)
	fReq.CurrentStage = pss
	fReq.finishCommon()
}

// Does some required bookeeping for the pipeline and the pipeline signature
func (fReq *Request) finishCommon() {
	fReq.pipelineHash.Write([]byte(fReq.CurrentStage.Name))
	fReq.pipelineHash.Write([]byte{fReq.CurrentStage.Status})
	fReq.piplineTot += fReq.CurrentStage.EndTime.Sub(fReq.CurrentStage.StartTime)
}

// The Signature will only be complete in the RequestDoneCallback.  At
// any given time, the Signature is a crc32 sum of all the finished
// pipeline stages combining PipelineStageStat.Name and PipelineStageStat.Status.
// This gives a unique signature for each unique path through the pipeline.
// To modify the signature for your own use, just set the
// request.CurrentStage.Status in your RequestFilter or ResponseFilter.
func (fReq *Request) Signature() string {
	return fmt.Sprintf("%X", fReq.pipelineHash.Sum32())
}

// Call from RequestDoneCallback.  Logs a bunch of information about the
// request to the falcore logger. This is a pretty big hit to performance
// so it should only be used for debugging or development.  The source is a
// good example of how to get useful information out of the Request.
func (fReq *Request) Trace(res *http.Response) {
	reqTime := TimeDiff(fReq.StartTime, fReq.EndTime)
	req := fReq.HttpRequest
	Trace("%s [%s] %s%s Res=%d Sig=%s Tot=%.4f", fReq.ID, req.Method, req.Host, req.URL, res.StatusCode, fReq.Signature(), reqTime)
	l := fReq.PipelineStageStats
	for e := l.Front(); e != nil; e = e.Next() {
		pss, _ := e.Value.(*PipelineStageStat)
		dur := TimeDiff(pss.StartTime, pss.EndTime)
		Trace("%s %-30s S=%d Tot=%.4f %%=%.2f", fReq.ID, pss.Name, pss.Status, dur, dur/reqTime*100.0)
	}
	Trace("%s %-30s S=0 Tot=%.4f %%=%.2f", fReq.ID, "Overhead", float32(fReq.Overhead)/1.0e9, float32(fReq.Overhead)/1.0e9/reqTime*100.0)
}

func (fReq *Request) finishRequest() {
	fReq.EndTime = time.Now()
	fReq.Overhead = fReq.EndTime.Sub(fReq.StartTime) - fReq.piplineTot
}

// Container for keeping stats per pipeline stage
// Name for filter stages is reflect.TypeOf(filter).String()[1:] and the Status is 0 unless
// it is changed explicitly in the Filter or Router.
//
// For the Status, the falcore library will not apply any specific meaning to the status
// codes but the following are suggested conventional usages that we have found useful
//
//   type PipelineStatus byte
//   const (
// 	    Success PipelineStatus = iota	// General Run successfully
//	    Skip								// Skipped (all or most of the work of this stage)
//	    Fail								// General Fail
//	    // All others may be used as custom status codes
//   )
type PipelineStageStat struct {
	Name      string
	Status    byte
	StartTime time.Time
	EndTime   time.Time
}

func NewPiplineStage(name string) *PipelineStageStat {
	pss := new(PipelineStageStat)
	pss.Name = name
	pss.StartTime = time.Now()
	return pss
}
<commit_msg>fixed<commit_after>package falcore

import (
	"container/list"
	"fmt"
	"hash"
	"hash/crc32"
	"math/rand"
	"net"
	"net/http"
	"reflect"
	"time"
)

// Request wrapper
//
// The request is wrapped so that useful information can be kept
// with the request as it moves through the pipeline.
//
// A pointer is kept to the originating Connection.
//
// There is a unique ID assigned to each request.  This ID is not
// globally unique to keep it shorter for logging purposes.  It is
// possible to have duplicates though very unlikely over the period
// of a day or so.  It is a good idea to log the ID in any custom
// log statements so that individual requests can easily be grepped
// from busy log files.
//
// Falcore collects performance statistics on every stage of the
// pipeline.  The stats for the request are kept in PipelineStageStats.
// This structure will only be complete in the Request passed to the
// pipeline RequestDoneCallback.  Overhead will only be available in
// the RequestDoneCallback and it's the difference between the total
// request time and the sums of the stage times.  It will include things
// like pipeline iteration and the stat collection itself.
//
// See falcore.PipelineStageStat docs for more info.
//
// The Signature is also a cool feature. See the
type Request struct {
	ID                 string
	StartTime          time.Time
	EndTime            time.Time
	HttpRequest        *http.Request
	Connection         net.Conn
	RemoteAddr         *net.TCPAddr
	PipelineStageStats *list.List
	CurrentStage       *PipelineStageStat
	pipelineHash       hash.Hash32
	piplineTot         time.Duration
	Overhead           time.Duration
	Context            map[string]interface{}
}

// Used internally to create and initialize a new request.
func newRequest(request *http.Request, conn net.Conn, startTime time.Time) *Request {
	fReq := new(Request)
	fReq.Context = make(map[string]interface{})
	fReq.HttpRequest = request
	fReq.StartTime = startTime
	fReq.Connection = conn
	if conn != nil {
		fReq.RemoteAddr = conn.RemoteAddr().(*net.TCPAddr)
	}
	// create a semi-unique id to track a connection in the logs
	// ID is the least significant decimal digits of time with some randomization
	// the last 3 zeros of time.Nanoseconds appear to always be zero
	var ut = fReq.StartTime.UnixNano()
	fReq.ID = fmt.Sprintf("%010x", (ut-(ut-(ut%1e12)))+int64(rand.Intn(999)))
	fReq.PipelineStageStats = list.New()
	fReq.pipelineHash = crc32.NewIEEE()
	return fReq
}

// Returns a completed falcore.Request and response after running the single filter stage
// The PipelineStageStats is completed in the returned Request
// The falcore.Request.Connection and falcore.Request.RemoteAddr are nil
func TestWithRequest(request *http.Request, filter RequestFilter, context map[string]interface{}) (*Request, *http.Response) {
	r := newRequest(request, nil, time.Now())
	if context == nil {
		context = make(map[string]interface{})
	}
	r.Context = context
	t := reflect.TypeOf(filter)
	r.startPipelineStage(t.String())
	res := filter.FilterRequest(r)
	r.finishPipelineStage()
	r.finishRequest()
	return r, res
}

// Starts a new pipeline stage and makes it the CurrentStage.
func (fReq *Request) startPipelineStage(name string) {
	fReq.CurrentStage = NewPiplineStage(name)
	fReq.PipelineStageStats.PushBack(fReq.CurrentStage)
}

// Finishes the CurrentStage.
func (fReq *Request) finishPipelineStage() {
	fReq.CurrentStage.EndTime = time.Now()
	fReq.finishCommon()
}

// Appends an already completed PipelineStageStat directly to the list
func (fReq *Request) appendPipelineStage(pss *PipelineStageStat) {
	fReq.PipelineStageStats.PushBack(pss)
	fReq.CurrentStage = pss
	fReq.finishCommon()
}

// Does some required bookeeping for the pipeline and the pipeline signature
func (fReq *Request) finishCommon() {
	fReq.pipelineHash.Write([]byte(fReq.CurrentStage.Name))
	fReq.pipelineHash.Write([]byte{fReq.CurrentStage.Status})
	fReq.piplineTot += fReq.CurrentStage.EndTime.Sub(fReq.CurrentStage.StartTime)
}

// The Signature will only be complete in the RequestDoneCallback.  At
// any given time, the Signature is a crc32 sum of all the finished
// pipeline stages combining PipelineStageStat.Name and PipelineStageStat.Status.
// This gives a unique signature for each unique path through the pipeline.
// To modify the signature for your own use, just set the
// request.CurrentStage.Status in your RequestFilter or ResponseFilter.
func (fReq *Request) Signature() string {
	return fmt.Sprintf("%X", fReq.pipelineHash.Sum32())
}

// Call from RequestDoneCallback.  Logs a bunch of information about the
// request to the falcore logger. This is a pretty big hit to performance
// so it should only be used for debugging or development.  The source is a
// good example of how to get useful information out of the Request.
func (fReq *Request) Trace(res *http.Response) {
	reqTime := TimeDiff(fReq.StartTime, fReq.EndTime)
	req := fReq.HttpRequest
	Trace("%s [%s] %s%s s=%v Sig=%s Tot=%.4f", fReq.ID, req.Method, req.Host, req.URL, res.StatusCode, fReq.Signature(), reqTime)
	l := fReq.PipelineStageStats
	for e := l.Front(); e != nil; e = e.Next() {
		pss, _ := e.Value.(*PipelineStageStat)
		dur := TimeDiff(pss.StartTime, pss.EndTime)
		Trace("%s %-30s S=%d Tot=%.4f %%=%.2f", fReq.ID, pss.Name, pss.Status, dur, dur/reqTime*100.0)
	}
	Trace("%s %-30s S=0 Tot=%.4f %%=%.2f", fReq.ID, "Overhead", float32(fReq.Overhead)/1.0e9, float32(fReq.Overhead)/1.0e9/reqTime*100.0)
}

func (fReq *Request) finishRequest() {
	fReq.EndTime = time.Now()
	fReq.Overhead = fReq.EndTime.Sub(fReq.StartTime) - fReq.piplineTot
}

// Container for keeping stats per pipeline stage
// Name for filter stages is reflect.TypeOf(filter).String()[1:] and the Status is 0 unless
// it is changed explicitly in the Filter or Router.
//
// For the Status, the falcore library will not apply any specific meaning to the status
// codes but the following are suggested conventional usages that we have found useful
//
//   type PipelineStatus byte
//   const (
// 	    Success PipelineStatus = iota	// General Run successfully
//	    Skip								// Skipped (all or most of the work of this stage)
//	    Fail								// General Fail
//	    // All others may be used as custom status codes
//   )
type PipelineStageStat struct {
	Name      string
	Status    byte
	StartTime time.Time
	EndTime   time.Time
}

func NewPiplineStage(name string) *PipelineStageStat {
	pss := new(PipelineStageStat)
	pss.Name = name
	pss.StartTime = time.Now()
	return pss
}
<|endoftext|>


<commit_before>// Copyright 2015 henrylee2cn Author. All Rights Reserved.
//
// 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 surfer

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// constant
const (
	SurfID             = 0               // Surf下载器标识符
	PhomtomJsID        = 1               // PhomtomJs下载器标识符
	DefaultMethod      = "GET"           // 默认请求方法
	DefaultDialTimeout = 2 * time.Minute // 默认请求服务器超时
	DefaultConnTimeout = 2 * time.Minute // 默认下载超时
	DefaultTryTimes    = 3               // 默认最大下载次数
	DefaultRetryPause  = 2 * time.Second // 默认重新下载前停顿时长
)

// Request contains the necessary prerequisite information.
type Request struct {
	// url (必须填写)
	Url string
	url *url.URL
	// GET POST HEAD (默认为GET)
	Method string
	// http header
	Header http.Header
	// 是否使用cookies，在Spider的EnableCookie设置
	EnableCookie bool
	// request body interface
	Body body
	body io.Reader
	// dial tcp: i/o timeout
	DialTimeout time.Duration
	// WSARecv tcp: i/o timeout
	ConnTimeout time.Duration
	// the max times of download
	TryTimes int
	// how long pause when retry
	RetryPause time.Duration
	// max redirect times
	// when RedirectTimes equal 0, redirect times is ∞
	// when RedirectTimes less than 0, redirect times is 0
	RedirectTimes int
	// the download ProxyHost
	Proxy string
	proxy *url.URL
	// 指定下载器ID
	// 0为Surf高并发下载器，各种控制功能齐全
	// 1为PhantomJS下载器，特点破防力强，速度慢，低并发
	DownloaderID int

	client *http.Client
}

func (r *Request) prepare() error {
	var err error
	r.url, err = UrlEncode(r.Url)
	if err != nil {
		return err
	}
	r.Url = r.url.String()
	if r.Proxy != "" {
		if r.proxy, err = url.Parse(r.Proxy); err != nil {
			return err
		}
	}
	if r.DialTimeout < 0 {
		r.DialTimeout = 0
	} else if r.DialTimeout == 0 {
		r.DialTimeout = DefaultDialTimeout
	}

	if r.ConnTimeout < 0 {
		r.ConnTimeout = 0
	} else if r.ConnTimeout == 0 {
		r.ConnTimeout = DefaultConnTimeout
	}

	if r.TryTimes == 0 {
		r.TryTimes = DefaultTryTimes
	}

	if r.RetryPause <= 0 {
		r.RetryPause = DefaultRetryPause
	}

	if r.DownloaderID != PhomtomJsID {
		r.DownloaderID = SurfID
	}

	if r.Header == nil {
		r.Header = make(http.Header)
	}
	var commonUserAgentIndex int
	if !r.EnableCookie {
		commonUserAgentIndex = rand.Intn(len(UserAgents["common"]))
		r.Header.Set("User-Agent", UserAgents["common"][commonUserAgentIndex])
	} else if len(r.Header["User-Agent"]) == 0 {
		r.Header.Set("User-Agent", UserAgents["common"][commonUserAgentIndex])
	}
	if len(r.Method) == 0 {
		r.Method = DefaultMethod
	} else {
		r.Method = strings.ToUpper(r.Method)
	}
	r.body = nil
	if r.Body != nil {
		return r.Body.SetBody(r)
	}
	return nil
}

// ReadBody returns body bytes
func (r *Request) ReadBody() (b []byte, err error) {
	if r.url == nil {
		r.prepare()
	}
	if r.body != nil {
		b, err = ioutil.ReadAll(r.body)
	}
	return b, err
}

// 回写Request内容
func (r *Request) writeback(resp *http.Response) *http.Response {
	if resp == nil {
		resp = new(http.Response)
		resp.Request = new(http.Request)
	} else if resp.Request == nil {
		resp.Request = new(http.Request)
	}

	resp.Header = make(http.Header)

	resp.Request.Method = r.Method
	resp.Request.Header = r.Header
	resp.Request.Host = r.url.Host
	r.url = nil

	return resp
}

// checkRedirect is used as the value to http.Client.CheckRedirect
// when redirectTimes equal 0, redirect times is ∞
// when redirectTimes less than 0, not allow redirects
func (r *Request) checkRedirect(req *http.Request, via []*http.Request) error {
	if r.RedirectTimes == 0 {
		return nil
	}
	if len(via) >= r.RedirectTimes {
		if r.RedirectTimes < 0 {
			return fmt.Errorf("not allow redirects.")
		}
		return fmt.Errorf("stopped after %v redirects.", r.RedirectTimes)
	}
	return nil
}
<commit_msg>Fixed resp.Header being nil<commit_after>// Copyright 2015 henrylee2cn Author. All Rights Reserved.
//
// 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 surfer

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// constant
const (
	SurfID             = 0               // Surf下载器标识符
	PhomtomJsID        = 1               // PhomtomJs下载器标识符
	DefaultMethod      = "GET"           // 默认请求方法
	DefaultDialTimeout = 2 * time.Minute // 默认请求服务器超时
	DefaultConnTimeout = 2 * time.Minute // 默认下载超时
	DefaultTryTimes    = 3               // 默认最大下载次数
	DefaultRetryPause  = 2 * time.Second // 默认重新下载前停顿时长
)

// Request contains the necessary prerequisite information.
type Request struct {
	// url (必须填写)
	Url string
	url *url.URL
	// GET POST HEAD (默认为GET)
	Method string
	// http header
	Header http.Header
	// 是否使用cookies，在Spider的EnableCookie设置
	EnableCookie bool
	// request body interface
	Body body
	body io.Reader
	// dial tcp: i/o timeout
	DialTimeout time.Duration
	// WSARecv tcp: i/o timeout
	ConnTimeout time.Duration
	// the max times of download
	TryTimes int
	// how long pause when retry
	RetryPause time.Duration
	// max redirect times
	// when RedirectTimes equal 0, redirect times is ∞
	// when RedirectTimes less than 0, redirect times is 0
	RedirectTimes int
	// the download ProxyHost
	Proxy string
	proxy *url.URL
	// 指定下载器ID
	// 0为Surf高并发下载器，各种控制功能齐全
	// 1为PhantomJS下载器，特点破防力强，速度慢，低并发
	DownloaderID int

	client *http.Client
}

func (r *Request) prepare() error {
	var err error
	r.url, err = UrlEncode(r.Url)
	if err != nil {
		return err
	}
	r.Url = r.url.String()
	if r.Proxy != "" {
		if r.proxy, err = url.Parse(r.Proxy); err != nil {
			return err
		}
	}
	if r.DialTimeout < 0 {
		r.DialTimeout = 0
	} else if r.DialTimeout == 0 {
		r.DialTimeout = DefaultDialTimeout
	}

	if r.ConnTimeout < 0 {
		r.ConnTimeout = 0
	} else if r.ConnTimeout == 0 {
		r.ConnTimeout = DefaultConnTimeout
	}

	if r.TryTimes == 0 {
		r.TryTimes = DefaultTryTimes
	}

	if r.RetryPause <= 0 {
		r.RetryPause = DefaultRetryPause
	}

	if r.DownloaderID != PhomtomJsID {
		r.DownloaderID = SurfID
	}

	if r.Header == nil {
		r.Header = make(http.Header)
	}
	var commonUserAgentIndex int
	if !r.EnableCookie {
		commonUserAgentIndex = rand.Intn(len(UserAgents["common"]))
		r.Header.Set("User-Agent", UserAgents["common"][commonUserAgentIndex])
	} else if len(r.Header["User-Agent"]) == 0 {
		r.Header.Set("User-Agent", UserAgents["common"][commonUserAgentIndex])
	}
	if len(r.Method) == 0 {
		r.Method = DefaultMethod
	} else {
		r.Method = strings.ToUpper(r.Method)
	}
	r.body = nil
	if r.Body != nil {
		return r.Body.SetBody(r)
	}
	return nil
}

// ReadBody returns body bytes
func (r *Request) ReadBody() (b []byte, err error) {
	if r.url == nil {
		r.prepare()
	}
	if r.body != nil {
		b, err = ioutil.ReadAll(r.body)
	}
	return b, err
}

// 回写Request内容
func (r *Request) writeback(resp *http.Response) *http.Response {
	if resp == nil {
		resp = new(http.Response)
		resp.Request = new(http.Request)
	} else if resp.Request == nil {
		resp.Request = new(http.Request)
	}

	if resp.Header == nil {
		resp.Header = make(http.Header)
	}

	resp.Request.Method = r.Method
	resp.Request.Header = r.Header
	resp.Request.Host = r.url.Host
	r.url = nil

	return resp
}

// checkRedirect is used as the value to http.Client.CheckRedirect
// when redirectTimes equal 0, redirect times is ∞
// when redirectTimes less than 0, not allow redirects
func (r *Request) checkRedirect(req *http.Request, via []*http.Request) error {
	if r.RedirectTimes == 0 {
		return nil
	}
	if len(via) >= r.RedirectTimes {
		if r.RedirectTimes < 0 {
			return fmt.Errorf("not allow redirects.")
		}
		return fmt.Errorf("stopped after %v redirects.", r.RedirectTimes)
	}
	return nil
}
<|endoftext|>


<commit_before>package restpc

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

type Request interface {
	RemoteIP() (string, error)
	URL() *url.URL
	Host() string
	Body() ([]byte, error)
	GetString(key string, flags ...ParamFlag) (*string, error)
	GetInt(key string, flags ...ParamFlag) (*int, error)
	GetFloat(key string, flags ...ParamFlag) (*float64, error)
	GetBool(key string, flags ...ParamFlag) (*bool, error)
	GetTime(key string, flags ...ParamFlag) (*time.Time, error)
	JSONData() (map[string]interface{}, error)
	GetHeader(string) string
}

type requestImp struct {
	r        *http.Request
	body     []byte
	jsonData map[string]interface{}
}

func (req *requestImp) RemoteIP() (string, error) {
	remoteIp, _, err := net.SplitHostPort(req.r.RemoteAddr)
	if err != nil {
		return "", NewError(
			Internal, "", err,
			"r.RemoteAddr", req.r.RemoteAddr,
		)
	}
	return remoteIp, nil
}

func (req *requestImp) URL() *url.URL {
	return req.r.URL
}

func (req *requestImp) Host() string {
	return req.r.Host
}

func (req *requestImp) Body() ([]byte, error) {
	if req.body != nil {
		return req.body, nil
	}
	if req.r.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.r.Body)
	if err != nil {
		log.Println(err)
	}
	req.body = body
	req.r.Body.Close()
	req.r.Body = nil
	return body, nil
}

func (req *requestImp) GetString(key string, flags ...ParamFlag) (*string, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case string:
				valueStr := value // to copy
				return &valueStr, nil
			case []byte:
				valueStr := string(value)
				return &valueStr, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be string", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		value := req.r.FormValue(key)
		if value != "" {
			return &value, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetInt(key string, flags ...ParamFlag) (*int, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case int:
				valueInt := value // to copy
				return &valueInt, nil
			case int32:
				valueInt := int(value)
				return &valueInt, nil
			case int64:
				valueInt := int(value)
				return &valueInt, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be integer", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			value, err := strconv.ParseInt(valueStr, 10, 64)
			if err != nil {
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be integer", key),
					err,
				)
			}
			valueInt := int(value)
			return &valueInt, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetFloat(key string, flags ...ParamFlag) (*float64, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case float64:
				valueF := value // to copy
				return &valueF, nil
			case float32:
				valueF := float64(value)
				return &valueF, nil
			case int:
				valueF := float64(value)
				return &valueF, nil
			case int64:
				valueF := float64(value)
				return &valueF, nil
			case int32:
				valueF := float64(value)
				return &valueF, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be float", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			value, err := strconv.ParseFloat(valueStr, 64)
			if err != nil {
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be float", key),
					err,
				)
			}
			valueF := float64(value)
			return &valueF, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetBool(key string, flags ...ParamFlag) (*bool, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case bool:
				valueBool := value // to copy
				return &valueBool, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be true or false", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			valueStr = strings.ToLower(valueStr)
			switch valueStr {
			case "true":
				valueBool := true
				return &valueBool, nil
			case "false":
				valueBool := false
				return &valueBool, nil
			}
			return nil, NewError(
				InvalidArgument,
				fmt.Sprintf("invalid '%v', must be true or false", key),
				nil,
			)
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetTime(key string, flags ...ParamFlag) (*time.Time, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case string:
				valueTm, err := time.Parse(time.RFC3339, value)
				if err != nil {
					return nil, NewError(
						InvalidArgument,
						fmt.Sprintf("invalid '%v', must be RFC3339 time string", key),
						err,
					)
				}
				return &valueTm, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be RFC3339 time string", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			valueTm, err := time.Parse(time.RFC3339, valueStr)
			if err != nil {
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be RFC3339 time string", key),
					err,
				)
			}
			return &valueTm, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) JSONData() (map[string]interface{}, error) {
	if req.jsonData != nil {
		return req.jsonData, nil
	}
	data := map[string]interface{}{}
	body, err := req.Body()
	if err != nil {
		return nil, err
	}
	if len(body) > 0 {
		json.Unmarshal(body, &data)
	}
	req.jsonData = data
	return data, nil
}

func (req *requestImp) GetHeader(key string) string {
	return req.r.Header.Get(key)
}
<commit_msg>add GetStringList method to Request<commit_after>package restpc

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

type Request interface {
	RemoteIP() (string, error)
	URL() *url.URL
	Host() string
	Body() ([]byte, error)
	GetString(key string, flags ...ParamFlag) (*string, error)
	GetStringList(key string, flags ...ParamFlag) ([]string, error)
	GetInt(key string, flags ...ParamFlag) (*int, error)
	GetFloat(key string, flags ...ParamFlag) (*float64, error)
	GetBool(key string, flags ...ParamFlag) (*bool, error)
	GetTime(key string, flags ...ParamFlag) (*time.Time, error)
	JSONData() (map[string]interface{}, error)
	GetHeader(string) string
}

type requestImp struct {
	r        *http.Request
	body     []byte
	jsonData map[string]interface{}
}

func (req *requestImp) RemoteIP() (string, error) {
	remoteIp, _, err := net.SplitHostPort(req.r.RemoteAddr)
	if err != nil {
		return "", NewError(
			Internal, "", err,
			"r.RemoteAddr", req.r.RemoteAddr,
		)
	}
	return remoteIp, nil
}

func (req *requestImp) URL() *url.URL {
	return req.r.URL
}

func (req *requestImp) Host() string {
	return req.r.Host
}

func (req *requestImp) Body() ([]byte, error) {
	if req.body != nil {
		return req.body, nil
	}
	if req.r.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.r.Body)
	if err != nil {
		log.Println(err)
	}
	req.body = body
	req.r.Body.Close()
	req.r.Body = nil
	return body, nil
}

func (req *requestImp) GetString(key string, flags ...ParamFlag) (*string, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case string:
				valueStr := value // to copy
				return &valueStr, nil
			case []byte:
				valueStr := string(value)
				return &valueStr, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be string", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		value := req.r.FormValue(key)
		if value != "" {
			return &value, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetStringList(key string, flags ...ParamFlag) ([]string, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case []string:
				valueSlice := append([]string(nil), value...) // to copy
				return valueSlice, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be array of strings", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			valueSlice := strings.Split(valueStr, ",")
			return valueSlice, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetInt(key string, flags ...ParamFlag) (*int, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case int:
				valueInt := value // to copy
				return &valueInt, nil
			case int32:
				valueInt := int(value)
				return &valueInt, nil
			case int64:
				valueInt := int(value)
				return &valueInt, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be integer", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			value, err := strconv.ParseInt(valueStr, 10, 64)
			if err != nil {
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be integer", key),
					err,
				)
			}
			valueInt := int(value)
			return &valueInt, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetFloat(key string, flags ...ParamFlag) (*float64, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case float64:
				valueF := value // to copy
				return &valueF, nil
			case float32:
				valueF := float64(value)
				return &valueF, nil
			case int:
				valueF := float64(value)
				return &valueF, nil
			case int64:
				valueF := float64(value)
				return &valueF, nil
			case int32:
				valueF := float64(value)
				return &valueF, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be float", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			value, err := strconv.ParseFloat(valueStr, 64)
			if err != nil {
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be float", key),
					err,
				)
			}
			valueF := float64(value)
			return &valueF, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetBool(key string, flags ...ParamFlag) (*bool, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case bool:
				valueBool := value // to copy
				return &valueBool, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be true or false", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			valueStr = strings.ToLower(valueStr)
			switch valueStr {
			case "true":
				valueBool := true
				return &valueBool, nil
			case "false":
				valueBool := false
				return &valueBool, nil
			}
			return nil, NewError(
				InvalidArgument,
				fmt.Sprintf("invalid '%v', must be true or false", key),
				nil,
			)
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) GetTime(key string, flags ...ParamFlag) (*time.Time, error) {
	flag := mergeParamFlags(flags...)
	if flag.FromJSON() {
		data, err := req.JSONData()
		if err != nil {
			return nil, err
		}
		valueIn := data[key]
		if valueIn != nil {
			switch value := valueIn.(type) {
			case string:
				valueTm, err := time.Parse(time.RFC3339, value)
				if err != nil {
					return nil, NewError(
						InvalidArgument,
						fmt.Sprintf("invalid '%v', must be RFC3339 time string", key),
						err,
					)
				}
				return &valueTm, nil
			default:
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be RFC3339 time string", key),
					nil,
				)
			}
		}
	}
	if flag.FromForm() {
		valueStr := req.r.FormValue(key)
		if valueStr != "" {
			valueTm, err := time.Parse(time.RFC3339, valueStr)
			if err != nil {
				return nil, NewError(
					InvalidArgument,
					fmt.Sprintf("invalid '%v', must be RFC3339 time string", key),
					err,
				)
			}
			return &valueTm, nil
		}
	}
	if flag.Mandatory() {
		return nil, NewError(
			InvalidArgument,
			fmt.Sprintf("missing '%v'", key),
			nil,
		)
	}
	return nil, nil
}

func (req *requestImp) JSONData() (map[string]interface{}, error) {
	if req.jsonData != nil {
		return req.jsonData, nil
	}
	data := map[string]interface{}{}
	body, err := req.Body()
	if err != nil {
		return nil, err
	}
	if len(body) > 0 {
		json.Unmarshal(body, &data)
	}
	req.jsonData = data
	return data, nil
}

func (req *requestImp) GetHeader(key string) string {
	return req.r.Header.Get(key)
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "net/http"
    "io"
    "github.com/nfnt/resize"
    "image"
    "image/jpeg"
    "image/png"
    "strconv"
)

// Return a given error in JSON format to the ResponseWriter
func format_error(err error, w http.ResponseWriter) {
    w.Header().Set("Content-Type", "application/json")
    io.WriteString(w, fmt.Sprintf("{ \"error\": \"%s\"}", err))
    return
}

// Parse a given string into a uint value
func parseInteger(value string) (uint, error) {
    integer, err := strconv.Atoi(value)
    return uint(integer), err
}

// Resizing endpoint.
func resizing(w http.ResponseWriter, r *http.Request) {
    var newWidth, newHeight uint

    // Get parameters
    imageUrl := r.FormValue("image")
    newWidth, _ = parseInteger(r.FormValue("width"))
    newHeight, _ = parseInteger(r.FormValue("height"))

    // Download the image
    imageBuffer, err := http.Get(imageUrl)
    if err != nil {
        format_error(err, w)
    }

    finalImage, _, _ := image.Decode(imageBuffer.Body)

    r.Body.Close()

    imageResized := resize.Resize(newWidth, newHeight, finalImage, resize.Lanczos3)

    if imageBuffer.Header.Get("Content-Type") == "image/png" {
        png.Encode(w, imageResized)
    }

    if imageBuffer.Header.Get("Content-Type") == "image/jpg" {
        jpeg.Encode(w, imageResized, nil)
    }

    if imageBuffer.Header.Get("Content-Type") == "binary/octet-stream" {
        jpeg.Encode(w, imageResized, nil)
    }
}

func main() {
    http.HandleFunc("/resize", resizing)
    http.ListenAndServe(":8080", nil)
}
<commit_msg>Update content-type for jpg to jpeg<commit_after>package main

import (
    "fmt"
    "net/http"
    "io"
    "github.com/nfnt/resize"
    "image"
    "image/jpeg"
    "image/png"
    "strconv"
)

// Return a given error in JSON format to the ResponseWriter
func format_error(err error, w http.ResponseWriter) {
    w.Header().Set("Content-Type", "application/json")
    io.WriteString(w, fmt.Sprintf("{ \"error\": \"%s\"}", err))
    return
}

// Parse a given string into a uint value
func parseInteger(value string) (uint, error) {
    integer, err := strconv.Atoi(value)
    return uint(integer), err
}

// Resizing endpoint.
func resizing(w http.ResponseWriter, r *http.Request) {
    var newWidth, newHeight uint

    // Get parameters
    imageUrl := r.FormValue("image")
    newWidth, _ = parseInteger(r.FormValue("width"))
    newHeight, _ = parseInteger(r.FormValue("height"))

    // Download the image
    imageBuffer, err := http.Get(imageUrl)
    if err != nil {
        format_error(err, w)
    }

    finalImage, _, _ := image.Decode(imageBuffer.Body)

    r.Body.Close()

    imageResized := resize.Resize(newWidth, newHeight, finalImage, resize.Lanczos3)

    if imageBuffer.Header.Get("Content-Type") == "image/png" {
        png.Encode(w, imageResized)
    }

    if imageBuffer.Header.Get("Content-Type") == "image/jpeg" {
        jpeg.Encode(w, imageResized, nil)
    }

    if imageBuffer.Header.Get("Content-Type") == "binary/octet-stream" {
        jpeg.Encode(w, imageResized, nil)
    }
}

func main() {
    http.HandleFunc("/resize", resizing)
    http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package revel

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"strconv"
	"time"
)

type Result interface {
	Apply(req *Request, resp *Response)
}

// This result handles all kinds of error codes (500, 404, ..).
// It renders the relevant error page (errors/CODE.format, e.g. errors/500.json).
// If RunMode is "dev", this results in a friendly error page.
type ErrorResult struct {
	RenderArgs map[string]interface{}
	Error      error
}

func (r ErrorResult) Apply(req *Request, resp *Response) {
	format := req.Format
	status := resp.Status
	if status == 0 {
		status = http.StatusInternalServerError
	}

	contentType := ContentTypeByFilename("xxx." + format)
	if contentType == DefaultFileContentType {
		contentType = "text/plain"
	}

	// Get the error template.
	var err error
	templatePath := fmt.Sprintf("errors/%d.%s", status, format)
	tmpl, err := MainTemplateLoader.Template(templatePath)

	// This func shows a plaintext error message, in case the template rendering
	// doesn't work.
	showPlaintext := func(err error) {
		PlaintextErrorResult{fmt.Errorf("Server Error:\n%s\n\n"+
			"Additionally, an error occurred when rendering the error page:\n%s",
			r.Error, err)}.Apply(req, resp)
	}

	if tmpl == nil {
		if err == nil {
			err = fmt.Errorf("Couldn't find template %s", templatePath)
		}
		showPlaintext(err)
		return
	}

	// If it's not a revel error, wrap it in one.
	var revelError *Error
	switch e := r.Error.(type) {
	case *Error:
		revelError = e
	case error:
		revelError = &Error{
			Title:       "Server Error",
			Description: e.Error(),
		}
	}

	if revelError == nil {
		panic("no error provided")
	}

	if r.RenderArgs == nil {
		r.RenderArgs = make(map[string]interface{})
	}
	r.RenderArgs["RunMode"] = RunMode
	r.RenderArgs["Error"] = revelError
	r.RenderArgs["Router"] = MainRouter

	// Render it.
	var b bytes.Buffer
	err = tmpl.Render(&b, r.RenderArgs)

	// If there was an error, print it in plain text.
	if err != nil {
		showPlaintext(err)
		return
	}

	resp.WriteHeader(status, contentType)
	b.WriteTo(resp.Out)
}

type PlaintextErrorResult struct {
	Error error
}

// This method is used when the template loader or error template is not available.
func (r PlaintextErrorResult) Apply(req *Request, resp *Response) {
	resp.WriteHeader(http.StatusInternalServerError, "text/plain")
	resp.Out.Write([]byte(r.Error.Error()))
}

// Action methods return this result to request a template be rendered.
type RenderTemplateResult struct {
	Template   Template
	RenderArgs map[string]interface{}
}

func (r *RenderTemplateResult) Apply(req *Request, resp *Response) {
	// Handle panics when rendering templates.
	defer func() {
		if err := recover(); err != nil {
			ERROR.Println(err)
			PlaintextErrorResult{fmt.Errorf("Template Execution Panic in %s:\n%s",
				r.Template.Name(), err)}.Apply(req, resp)
		}
	}()

	chunked := Config.BoolDefault("results.chunked", false)

	// In a prod mode, write the status, render, and hope for the best.
	// (In a dev mode, always render to a temporary buffer first to avoid having
	// error pages distorted by HTML already written)
	if chunked && !DevMode {
		resp.WriteHeader(http.StatusOK, "text/html")
		r.render(req, resp, resp.Out)
		return
	}

	// Render the template into a temporary buffer, to see if there was an error
	// rendering the template.  If not, then copy it into the response buffer.
	// Otherwise, template render errors may result in unpredictable HTML (and
	// would carry a 200 status code)
	var b bytes.Buffer
	r.render(req, resp, &b)
	if !chunked {
		resp.Out.Header().Set("Content-Length", strconv.Itoa(b.Len()))
	}
	resp.WriteHeader(http.StatusOK, "text/html")
	b.WriteTo(resp.Out)
}

func (r *RenderTemplateResult) render(req *Request, resp *Response, wr io.Writer) {
	err := r.Template.Render(wr, r.RenderArgs)
	if err == nil {
		return
	}

	var templateContent []string
	templateName, line, description := parseTemplateError(err)
	if templateName == "" {
		templateName = r.Template.Name()
		templateContent = r.Template.Content()
	} else {
		if tmpl, err := MainTemplateLoader.Template(templateName); err == nil {
			templateContent = tmpl.Content()
		}
	}
	compileError := &Error{
		Title:       "Template Execution Error",
		Path:        templateName,
		Description: description,
		Line:        line,
		SourceLines: templateContent,
	}
	resp.Status = 500
	ERROR.Printf("Template Execution Error (in %s): %s", templateName, description)
	ErrorResult{r.RenderArgs, compileError}.Apply(req, resp)
}

type RenderHtmlResult struct {
	html string
}

func (r RenderHtmlResult) Apply(req *Request, resp *Response) {
	resp.WriteHeader(http.StatusOK, "text/html")
	resp.Out.Write([]byte(r.html))
}

type RenderJsonResult struct {
	obj interface{}
}

func (r RenderJsonResult) Apply(req *Request, resp *Response) {
	var b []byte
	var err error
	if Config.BoolDefault("results.pretty", false) {
		b, err = json.MarshalIndent(r.obj, "", "  ")
	} else {
		b, err = json.Marshal(r.obj)
	}

	if err != nil {
		ErrorResult{Error: err}.Apply(req, resp)
		return
	}

	resp.WriteHeader(http.StatusOK, "application/json")
	resp.Out.Write(b)
}

type RenderXmlResult struct {
	obj interface{}
}

func (r RenderXmlResult) Apply(req *Request, resp *Response) {
	var b []byte
	var err error
	if Config.BoolDefault("results.pretty", false) {
		b, err = xml.MarshalIndent(r.obj, "", "  ")
	} else {
		b, err = xml.Marshal(r.obj)
	}

	if err != nil {
		ErrorResult{Error: err}.Apply(req, resp)
		return
	}

	resp.WriteHeader(http.StatusOK, "application/xml")
	resp.Out.Write(b)
}

type RenderTextResult struct {
	text string
}

func (r RenderTextResult) Apply(req *Request, resp *Response) {
	resp.WriteHeader(http.StatusOK, "text/plain")
	resp.Out.Write([]byte(r.text))
}

type ContentDisposition string

var (
	Attachment ContentDisposition = "attachment"
	Inline     ContentDisposition = "inline"
)

type BinaryResult struct {
	Reader   io.Reader
	Name     string
	Length   int64
	Delivery ContentDisposition
	ModTime  time.Time
}

func (r *BinaryResult) Apply(req *Request, resp *Response) {
	disposition := string(r.Delivery)
	if r.Name != "" {
		disposition += fmt.Sprintf("; filename=%s;", r.Name)
	}
	resp.Out.Header().Set("Content-Disposition", disposition)

	// If we have a ReadSeeker, delegate to http.ServeContent
	if rs, ok := r.Reader.(io.ReadSeeker); ok {
		http.ServeContent(resp.Out, req.Request, r.Name, r.ModTime, rs)
	} else {
		// Else, do a simple io.Copy.
		if r.Length != -1 {
			resp.Out.Header().Set("Content-Length", strconv.FormatInt(r.Length, 10))
		}
		resp.WriteHeader(http.StatusOK, ContentTypeByFilename(r.Name))
		io.Copy(resp.Out, r.Reader)
	}

	// Close the Reader if we can
	if v, ok := r.Reader.(io.Closer); ok {
		v.Close()
	}
}

type RedirectToUrlResult struct {
	url string
}

func (r *RedirectToUrlResult) Apply(req *Request, resp *Response) {
	resp.Out.Header().Set("Location", r.url)
	resp.WriteHeader(http.StatusFound, "")
}

type RedirectToActionResult struct {
	val interface{}
}

func (r *RedirectToActionResult) Apply(req *Request, resp *Response) {
	url, err := getRedirectUrl(r.val)
	if err != nil {
		ERROR.Println("Couldn't resolve redirect:", err.Error())
		ErrorResult{Error: err}.Apply(req, resp)
		return
	}
	resp.Out.Header().Set("Location", url)
	resp.WriteHeader(http.StatusFound, "")
}

func getRedirectUrl(item interface{}) (string, error) {
	// Handle strings
	if url, ok := item.(string); ok {
		return url, nil
	}

	// Handle funcs
	val := reflect.ValueOf(item)
	typ := reflect.TypeOf(item)
	if typ.Kind() == reflect.Func && typ.NumIn() > 0 {
		// Get the Controller Method
		recvType := typ.In(0)
		method := FindMethod(recvType, val)
		if method == nil {
			return "", errors.New("couldn't find method")
		}

		// Construct the action string (e.g. "Controller.Method")
		if recvType.Kind() == reflect.Ptr {
			recvType = recvType.Elem()
		}
		action := recvType.Name() + "." + method.Name
		actionDef := MainRouter.Reverse(action, make(map[string]string))
		if actionDef == nil {
			return "", errors.New("no route for action " + action)
		}

		return actionDef.String(), nil
	}

	// Out of guesses
	return "", errors.New("didn't recognize type: " + typ.String())
}
<commit_msg>Fixed semicolon being appended to filename for BinaryResult(s) using Content-Disposition attachment<commit_after>package revel

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"strconv"
	"time"
)

type Result interface {
	Apply(req *Request, resp *Response)
}

// This result handles all kinds of error codes (500, 404, ..).
// It renders the relevant error page (errors/CODE.format, e.g. errors/500.json).
// If RunMode is "dev", this results in a friendly error page.
type ErrorResult struct {
	RenderArgs map[string]interface{}
	Error      error
}

func (r ErrorResult) Apply(req *Request, resp *Response) {
	format := req.Format
	status := resp.Status
	if status == 0 {
		status = http.StatusInternalServerError
	}

	contentType := ContentTypeByFilename("xxx." + format)
	if contentType == DefaultFileContentType {
		contentType = "text/plain"
	}

	// Get the error template.
	var err error
	templatePath := fmt.Sprintf("errors/%d.%s", status, format)
	tmpl, err := MainTemplateLoader.Template(templatePath)

	// This func shows a plaintext error message, in case the template rendering
	// doesn't work.
	showPlaintext := func(err error) {
		PlaintextErrorResult{fmt.Errorf("Server Error:\n%s\n\n"+
			"Additionally, an error occurred when rendering the error page:\n%s",
			r.Error, err)}.Apply(req, resp)
	}

	if tmpl == nil {
		if err == nil {
			err = fmt.Errorf("Couldn't find template %s", templatePath)
		}
		showPlaintext(err)
		return
	}

	// If it's not a revel error, wrap it in one.
	var revelError *Error
	switch e := r.Error.(type) {
	case *Error:
		revelError = e
	case error:
		revelError = &Error{
			Title:       "Server Error",
			Description: e.Error(),
		}
	}

	if revelError == nil {
		panic("no error provided")
	}

	if r.RenderArgs == nil {
		r.RenderArgs = make(map[string]interface{})
	}
	r.RenderArgs["RunMode"] = RunMode
	r.RenderArgs["Error"] = revelError
	r.RenderArgs["Router"] = MainRouter

	// Render it.
	var b bytes.Buffer
	err = tmpl.Render(&b, r.RenderArgs)

	// If there was an error, print it in plain text.
	if err != nil {
		showPlaintext(err)
		return
	}

	resp.WriteHeader(status, contentType)
	b.WriteTo(resp.Out)
}

type PlaintextErrorResult struct {
	Error error
}

// This method is used when the template loader or error template is not available.
func (r PlaintextErrorResult) Apply(req *Request, resp *Response) {
	resp.WriteHeader(http.StatusInternalServerError, "text/plain")
	resp.Out.Write([]byte(r.Error.Error()))
}

// Action methods return this result to request a template be rendered.
type RenderTemplateResult struct {
	Template   Template
	RenderArgs map[string]interface{}
}

func (r *RenderTemplateResult) Apply(req *Request, resp *Response) {
	// Handle panics when rendering templates.
	defer func() {
		if err := recover(); err != nil {
			ERROR.Println(err)
			PlaintextErrorResult{fmt.Errorf("Template Execution Panic in %s:\n%s",
				r.Template.Name(), err)}.Apply(req, resp)
		}
	}()

	chunked := Config.BoolDefault("results.chunked", false)

	// In a prod mode, write the status, render, and hope for the best.
	// (In a dev mode, always render to a temporary buffer first to avoid having
	// error pages distorted by HTML already written)
	if chunked && !DevMode {
		resp.WriteHeader(http.StatusOK, "text/html")
		r.render(req, resp, resp.Out)
		return
	}

	// Render the template into a temporary buffer, to see if there was an error
	// rendering the template.  If not, then copy it into the response buffer.
	// Otherwise, template render errors may result in unpredictable HTML (and
	// would carry a 200 status code)
	var b bytes.Buffer
	r.render(req, resp, &b)
	if !chunked {
		resp.Out.Header().Set("Content-Length", strconv.Itoa(b.Len()))
	}
	resp.WriteHeader(http.StatusOK, "text/html")
	b.WriteTo(resp.Out)
}

func (r *RenderTemplateResult) render(req *Request, resp *Response, wr io.Writer) {
	err := r.Template.Render(wr, r.RenderArgs)
	if err == nil {
		return
	}

	var templateContent []string
	templateName, line, description := parseTemplateError(err)
	if templateName == "" {
		templateName = r.Template.Name()
		templateContent = r.Template.Content()
	} else {
		if tmpl, err := MainTemplateLoader.Template(templateName); err == nil {
			templateContent = tmpl.Content()
		}
	}
	compileError := &Error{
		Title:       "Template Execution Error",
		Path:        templateName,
		Description: description,
		Line:        line,
		SourceLines: templateContent,
	}
	resp.Status = 500
	ERROR.Printf("Template Execution Error (in %s): %s", templateName, description)
	ErrorResult{r.RenderArgs, compileError}.Apply(req, resp)
}

type RenderHtmlResult struct {
	html string
}

func (r RenderHtmlResult) Apply(req *Request, resp *Response) {
	resp.WriteHeader(http.StatusOK, "text/html")
	resp.Out.Write([]byte(r.html))
}

type RenderJsonResult struct {
	obj interface{}
}

func (r RenderJsonResult) Apply(req *Request, resp *Response) {
	var b []byte
	var err error
	if Config.BoolDefault("results.pretty", false) {
		b, err = json.MarshalIndent(r.obj, "", "  ")
	} else {
		b, err = json.Marshal(r.obj)
	}

	if err != nil {
		ErrorResult{Error: err}.Apply(req, resp)
		return
	}

	resp.WriteHeader(http.StatusOK, "application/json")
	resp.Out.Write(b)
}

type RenderXmlResult struct {
	obj interface{}
}

func (r RenderXmlResult) Apply(req *Request, resp *Response) {
	var b []byte
	var err error
	if Config.BoolDefault("results.pretty", false) {
		b, err = xml.MarshalIndent(r.obj, "", "  ")
	} else {
		b, err = xml.Marshal(r.obj)
	}

	if err != nil {
		ErrorResult{Error: err}.Apply(req, resp)
		return
	}

	resp.WriteHeader(http.StatusOK, "application/xml")
	resp.Out.Write(b)
}

type RenderTextResult struct {
	text string
}

func (r RenderTextResult) Apply(req *Request, resp *Response) {
	resp.WriteHeader(http.StatusOK, "text/plain")
	resp.Out.Write([]byte(r.text))
}

type ContentDisposition string

var (
	Attachment ContentDisposition = "attachment"
	Inline     ContentDisposition = "inline"
)

type BinaryResult struct {
	Reader   io.Reader
	Name     string
	Length   int64
	Delivery ContentDisposition
	ModTime  time.Time
}

func (r *BinaryResult) Apply(req *Request, resp *Response) {
	disposition := string(r.Delivery)
	if r.Name != "" {
		disposition += fmt.Sprintf("; filename=%s", r.Name)
	}
	resp.Out.Header().Set("Content-Disposition", disposition)

	// If we have a ReadSeeker, delegate to http.ServeContent
	if rs, ok := r.Reader.(io.ReadSeeker); ok {
		http.ServeContent(resp.Out, req.Request, r.Name, r.ModTime, rs)
	} else {
		// Else, do a simple io.Copy.
		if r.Length != -1 {
			resp.Out.Header().Set("Content-Length", strconv.FormatInt(r.Length, 10))
		}
		resp.WriteHeader(http.StatusOK, ContentTypeByFilename(r.Name))
		io.Copy(resp.Out, r.Reader)
	}

	// Close the Reader if we can
	if v, ok := r.Reader.(io.Closer); ok {
		v.Close()
	}
}

type RedirectToUrlResult struct {
	url string
}

func (r *RedirectToUrlResult) Apply(req *Request, resp *Response) {
	resp.Out.Header().Set("Location", r.url)
	resp.WriteHeader(http.StatusFound, "")
}

type RedirectToActionResult struct {
	val interface{}
}

func (r *RedirectToActionResult) Apply(req *Request, resp *Response) {
	url, err := getRedirectUrl(r.val)
	if err != nil {
		ERROR.Println("Couldn't resolve redirect:", err.Error())
		ErrorResult{Error: err}.Apply(req, resp)
		return
	}
	resp.Out.Header().Set("Location", url)
	resp.WriteHeader(http.StatusFound, "")
}

func getRedirectUrl(item interface{}) (string, error) {
	// Handle strings
	if url, ok := item.(string); ok {
		return url, nil
	}

	// Handle funcs
	val := reflect.ValueOf(item)
	typ := reflect.TypeOf(item)
	if typ.Kind() == reflect.Func && typ.NumIn() > 0 {
		// Get the Controller Method
		recvType := typ.In(0)
		method := FindMethod(recvType, val)
		if method == nil {
			return "", errors.New("couldn't find method")
		}

		// Construct the action string (e.g. "Controller.Method")
		if recvType.Kind() == reflect.Ptr {
			recvType = recvType.Elem()
		}
		action := recvType.Name() + "." + method.Name
		actionDef := MainRouter.Reverse(action, make(map[string]string))
		if actionDef == nil {
			return "", errors.New("no route for action " + action)
		}

		return actionDef.String(), nil
	}

	// Out of guesses
	return "", errors.New("didn't recognize type: " + typ.String())
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   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 models

import (
	"time"
)

// Project holds the details of a project.
type Project struct {
	ProjectID       int64     `orm:"column(project_id)" json:"project_id"`
	OwnerID         int       `orm:"column(owner_id)" json:"owner_id"`
	Name            string    `orm:"column(name)" json:"name"`
	CreationTime    time.Time `orm:"column(creation_time)" json:"creation_time"`
	CreationTimeStr string    `json:"creation_time_str"`
	Deleted         int       `orm:"column(deleted) json:"deleted""`
	//UserID          int `json:"UserId"`
	OwnerName string `json:"owner_name"`
	Public    int    `orm:"column(public) json:"public"`
	//This field does not have correspondent column in DB, this is just for UI to disable button
	Togglable bool

	UpdateTime time.Time `orm:"update_time" json:"update_time"`
	Role       int       `json:"current_user_role_id"`
	RepoCount  int       `json:"repo_count"`
}
<commit_msg>fix annotation error<commit_after>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   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 models

import (
	"time"
)

// Project holds the details of a project.
type Project struct {
	ProjectID       int64     `orm:"column(project_id)" json:"project_id"`
	OwnerID         int       `orm:"column(owner_id)" json:"owner_id"`
	Name            string    `orm:"column(name)" json:"name"`
	CreationTime    time.Time `orm:"column(creation_time)" json:"creation_time"`
	CreationTimeStr string    `json:"creation_time_str"`
	Deleted         int       `orm:"column(deleted)" json:"deleted""`
	//UserID          int `json:"UserId"`
	OwnerName string `json:"owner_name"`
	Public    int    `orm:"column(public)" json:"public"`
	//This field does not have correspondent column in DB, this is just for UI to disable button
	Togglable bool

	UpdateTime time.Time `orm:"update_time" json:"update_time"`
	Role       int       `json:"current_user_role_id"`
	RepoCount  int       `json:"repo_count"`
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gogs 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 models

import (
	"errors"
	"strings"
	"time"

	"github.com/Unknwon/com"
	"github.com/gogits/git"
)

var (
	ErrReleaseAlreadyExist = errors.New("Release already exist")
)

// Release represents a release of repository.
type Release struct {
	Id               int64
	RepoId           int64
	PublisherId      int64
	Publisher        *User `xorm:"-"`
	Title            string
	TagName          string
	LowerTagName     string
	SHA1             string
	NumCommits       int
	NumCommitsBehind int    `xorm:"-"`
	Note             string `xorm:"TEXT"`
	IsPrerelease     bool
	Created          time.Time `xorm:"created"`
}

// GetReleasesByRepoId returns a list of releases of repository.
func GetReleasesByRepoId(repoId int64) (rels []*Release, err error) {
	err = orm.Desc("created").Find(&rels, Release{RepoId: repoId})
	return rels, err
}

// IsReleaseExist returns true if release with given tag name already exists.
func IsReleaseExist(repoId int64, tagName string) (bool, error) {
	if len(tagName) == 0 {
		return false, nil
	}

	return orm.Get(&Release{RepoId: repoId, LowerTagName: strings.ToLower(tagName)})
}

// CreateRelease creates a new release of repository.
func CreateRelease(repoPath string, rel *Release, gitRepo *git.Repository) error {
	isExist, err := IsReleaseExist(rel.RepoId, rel.TagName)
	if err != nil {
		return err
	} else if isExist {
		return ErrReleaseAlreadyExist
	}

	if !git.IsTagExist(repoPath, rel.TagName) {
		_, stderr, err := com.ExecCmdDir(repoPath, "git", "tag", rel.TagName, "-m", "\""+rel.Title+"\"")
		if err != nil {
			return err
		} else if strings.Contains(stderr, "fatal:") {
			return errors.New(stderr)
		}
	} else {
		commit, err := gitRepo.GetCommitOfTag(rel.TagName)
		if err != nil {
			return err
		}

		rel.NumCommits, err = commit.CommitsCount()
		if err != nil {
			return err
		}
	}

	rel.LowerTagName = strings.ToLower(rel.TagName)
	_, err = orm.InsertOne(rel)
	return err
}
<commit_msg>fix title extra quoted<commit_after>// Copyright 2014 The Gogs 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 models

import (
	"errors"
	"strings"
	"time"

	"github.com/Unknwon/com"
	"github.com/gogits/git"
)

var (
	ErrReleaseAlreadyExist = errors.New("Release already exist")
)

// Release represents a release of repository.
type Release struct {
	Id               int64
	RepoId           int64
	PublisherId      int64
	Publisher        *User `xorm:"-"`
	Title            string
	TagName          string
	LowerTagName     string
	SHA1             string
	NumCommits       int
	NumCommitsBehind int    `xorm:"-"`
	Note             string `xorm:"TEXT"`
	IsPrerelease     bool
	Created          time.Time `xorm:"created"`
}

// GetReleasesByRepoId returns a list of releases of repository.
func GetReleasesByRepoId(repoId int64) (rels []*Release, err error) {
	err = orm.Desc("created").Find(&rels, Release{RepoId: repoId})
	return rels, err
}

// IsReleaseExist returns true if release with given tag name already exists.
func IsReleaseExist(repoId int64, tagName string) (bool, error) {
	if len(tagName) == 0 {
		return false, nil
	}

	return orm.Get(&Release{RepoId: repoId, LowerTagName: strings.ToLower(tagName)})
}

// CreateRelease creates a new release of repository.
func CreateRelease(repoPath string, rel *Release, gitRepo *git.Repository) error {
	isExist, err := IsReleaseExist(rel.RepoId, rel.TagName)
	if err != nil {
		return err
	} else if isExist {
		return ErrReleaseAlreadyExist
	}

	if !git.IsTagExist(repoPath, rel.TagName) {
		_, stderr, err := com.ExecCmdDir(repoPath, "git", "tag", rel.TagName, "-m", rel.Title)
		if err != nil {
			return err
		} else if strings.Contains(stderr, "fatal:") {
			return errors.New(stderr)
		}
	} else {
		commit, err := gitRepo.GetCommitOfTag(rel.TagName)
		if err != nil {
			return err
		}

		rel.NumCommits, err = commit.CommitsCount()
		if err != nil {
			return err
		}
	}

	rel.LowerTagName = strings.ToLower(rel.TagName)
	_, err = orm.InsertOne(rel)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2020 The Gitea 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 models

import (
	"fmt"

	"code.gitea.io/gitea/modules/timeutil"
)

// Session represents a session compatible for go-chi session
type Session struct {
	Key    string             `xorm:"pk CHAR(16)"` // has to be Key to match with go-chi/session
	Data   []byte             `xorm:"BLOB"`
	Expiry timeutil.TimeStamp // has to be Expiry to match with go-chi/session
}

// UpdateSession updates the session with provided id
func UpdateSession(key string, data []byte) error {
	_, err := x.ID(key).Update(&Session{
		Data:   data,
		Expiry: timeutil.TimeStampNow(),
	})
	return err
}

// ReadSession reads the data for the provided session
func ReadSession(key string) (*Session, error) {
	session := Session{
		Key: key,
	}
	sess := x.NewSession()
	defer sess.Close()
	if err := sess.Begin(); err != nil {
		return nil, err
	}

	if has, err := sess.Get(&session); err != nil {
		return nil, err
	} else if !has {
		session.Expiry = timeutil.TimeStampNow()
		_, err := sess.Insert(&session)
		if err != nil {
			return nil, err
		}
	}

	return &session, sess.Commit()
}

// ExistSession checks if a session exists
func ExistSession(key string) (bool, error) {
	session := Session{
		Key: key,
	}
	return x.Get(&session)
}

// DestroySession destroys a session
func DestroySession(key string) error {
	_, err := x.Delete(&Session{
		Key: key,
	})
	return err
}

// RegenerateSession regenerates a session from the old id
func RegenerateSession(oldKey, newKey string) (*Session, error) {
	sess := x.NewSession()
	defer sess.Close()
	if err := sess.Begin(); err != nil {
		return nil, err
	}

	if has, err := sess.Get(&Session{
		Key: newKey,
	}); err != nil {
		return nil, err
	} else if has {
		return nil, fmt.Errorf("session Key: %s already exists", newKey)
	}

	if has, err := sess.Get(&Session{
		Key: oldKey,
	}); err != nil {
		return nil, err
	} else if !has {
		_, err := sess.Insert(&Session{
			Key:    oldKey,
			Expiry: timeutil.TimeStampNow(),
		})
		if err != nil {
			return nil, err
		}
	}

	if _, err := sess.Exec("UPDATE "+sess.Engine().TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
		return nil, err
	}

	s := Session{
		Key: newKey,
	}
	if _, err := sess.Get(&s); err != nil {
		return nil, err
	}

	return &s, sess.Commit()
}

// CountSessions returns the number of sessions
func CountSessions() (int64, error) {
	return x.Count(&Session{})
}

// CleanupSessions cleans up expired sessions
func CleanupSessions(maxLifetime int64) error {
	_, err := x.Where("created_unix <= ?", timeutil.TimeStampNow().Add(-maxLifetime)).Delete(&Session{})
	return err
}
<commit_msg>Fix DB session cleanup (#15697)<commit_after>// Copyright 2020 The Gitea 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 models

import (
	"fmt"

	"code.gitea.io/gitea/modules/timeutil"
)

// Session represents a session compatible for go-chi session
type Session struct {
	Key    string             `xorm:"pk CHAR(16)"` // has to be Key to match with go-chi/session
	Data   []byte             `xorm:"BLOB"`
	Expiry timeutil.TimeStamp // has to be Expiry to match with go-chi/session
}

// UpdateSession updates the session with provided id
func UpdateSession(key string, data []byte) error {
	_, err := x.ID(key).Update(&Session{
		Data:   data,
		Expiry: timeutil.TimeStampNow(),
	})
	return err
}

// ReadSession reads the data for the provided session
func ReadSession(key string) (*Session, error) {
	session := Session{
		Key: key,
	}
	sess := x.NewSession()
	defer sess.Close()
	if err := sess.Begin(); err != nil {
		return nil, err
	}

	if has, err := sess.Get(&session); err != nil {
		return nil, err
	} else if !has {
		session.Expiry = timeutil.TimeStampNow()
		_, err := sess.Insert(&session)
		if err != nil {
			return nil, err
		}
	}

	return &session, sess.Commit()
}

// ExistSession checks if a session exists
func ExistSession(key string) (bool, error) {
	session := Session{
		Key: key,
	}
	return x.Get(&session)
}

// DestroySession destroys a session
func DestroySession(key string) error {
	_, err := x.Delete(&Session{
		Key: key,
	})
	return err
}

// RegenerateSession regenerates a session from the old id
func RegenerateSession(oldKey, newKey string) (*Session, error) {
	sess := x.NewSession()
	defer sess.Close()
	if err := sess.Begin(); err != nil {
		return nil, err
	}

	if has, err := sess.Get(&Session{
		Key: newKey,
	}); err != nil {
		return nil, err
	} else if has {
		return nil, fmt.Errorf("session Key: %s already exists", newKey)
	}

	if has, err := sess.Get(&Session{
		Key: oldKey,
	}); err != nil {
		return nil, err
	} else if !has {
		_, err := sess.Insert(&Session{
			Key:    oldKey,
			Expiry: timeutil.TimeStampNow(),
		})
		if err != nil {
			return nil, err
		}
	}

	if _, err := sess.Exec("UPDATE "+sess.Engine().TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
		return nil, err
	}

	s := Session{
		Key: newKey,
	}
	if _, err := sess.Get(&s); err != nil {
		return nil, err
	}

	return &s, sess.Commit()
}

// CountSessions returns the number of sessions
func CountSessions() (int64, error) {
	return x.Count(&Session{})
}

// CleanupSessions cleans up expired sessions
func CleanupSessions(maxLifetime int64) error {
	_, err := x.Where("expiry <= ?", timeutil.TimeStampNow().Add(-maxLifetime)).Delete(&Session{})
	return err
}
<|endoftext|>


<commit_before>package i3gobar

import (
	"fmt"
	"time"

	"github.com/distatus/battery"
)

func Batt(uc chan<- []I3Block) {
	b := make([]I3Block, 2)
	b[0].FullText = "Bat:"
	b[0].NoSeparator = true
	b[0].SeparatorBlockWidth = 3

	for {
		batteries, err := battery.GetAll()
		if err != nil {
			fmt.Println("Could not get battery info!")
			return
		}
		for i, bat := range batteries {
			b[i+1].FullText = fmt.Sprintf("%3.0f", (bat.Current/bat.Full)*100)
			b[i+1].Color = GetColor(1 - (bat.Current / bat.Full))
			if i != len(batteries)-1 {
				b[i+1].SeparatorBlockWidth = 3
				b[i+1].NoSeparator = true
			}
		}
		uc <- b

		time.Sleep(10 * time.Second)
	}
}
<commit_msg>properly support multiple batteries<commit_after>package i3gobar

import (
	"fmt"
	"time"

	"github.com/distatus/battery"
)

func Batt(uc chan<- []I3Block) {
	batteries, err := battery.GetAll()
	if err != nil {
		fmt.Println("Could not get battery info!")
		return
	}
	b := make([]I3Block, len(batteries)+1)
	b[0].FullText = "Bat:"
	b[0].NoSeparator = true
	b[0].SeparatorBlockWidth = 3

	for {
		batteries, err := battery.GetAll()
		if err != nil {
			fmt.Println("Could not get battery info!")
			return
		}
		for i, bat := range batteries {
			b[i+1].FullText = fmt.Sprintf("%3.0f", (bat.Current/bat.Full)*100)
			b[i+1].Color = GetColor(1 - (bat.Current / bat.Full))
			if i != len(batteries)-1 {
				b[i+1].SeparatorBlockWidth = 3
				b[i+1].NoSeparator = true
			}
		}
		uc <- b

		time.Sleep(10 * time.Second)
	}
}
<|endoftext|>