<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 oglemock

import (
	"errors"
	"fmt"
	"math"
	"reflect"
)

// PartialExpecation is a function that should be called exactly once with
// expected arguments or matchers in order to set up an expected method call.
// See Controller.ExpectMethodCall below. It returns an expectation that can be
// further modified (e.g. by calling WillOnce).
//
// If the arguments are of the wrong type, the function reports a fatal error
// and returns nil.
type PartialExpecation func(...interface{}) Expectation

// Controller represents an object that implements the central logic of
// oglemock: recording and verifying expectations, responding to mock method
// calls, and so on.
type Controller interface {
	// ExpectCall expresses an expectation that the method of the given name
	// should be called on the supplied mock object. It returns a function that
	// should be called with the expected arguments, matchers for the arguments,
	// or a mix of both.
	//
	// fileName and lineNumber should indicate the line on which the expectation
	// was made, if known.
	//
	// For example:
	//
	//     mockWriter := [...]
	//     controller.ExpectCall(mockWriter, "Write")(ElementsAre(0x1))
	//         .WillOnce(Return(1, nil))
	//
	// If the mock object doesn't have a method of the supplied name, the
	// function reports a fatal error and returns nil.
	ExpectCall(
		o MockObject,
		methodName string,
		fileName string,
		lineNumber int) PartialExpecation

	// Finish causes the controller to check for any unsatisfied expectations,
	// and report them as errors if they exist.
	//
	// The controller may panic if any of its methods (including this one) are
	// called after Finish is called.
	Finish()

	// HandleMethodCall looks for a registered expectation matching the call of
	// the given method on mock object o, invokes the appropriate action (if
	// any), and returns the values returned by that action (if any).
	//
	// If the action returns nothing, the controller returns zero values. If
	// there is no matching expectation, the controller reports an error and
	// returns zero values.
	//
	// If the mock object doesn't have a method of the supplied name, the
	// arguments are of the wrong type, or the action returns the wrong types,
	// the function reports a fatal error.
	//
	// HandleMethodCall is exported for the sake of mock implementations, and
	// should not be used directly.
	HandleMethodCall(
		o MockObject,
		methodName string,
		fileName string,
		lineNumber int,
		args []interface{}) []interface{}
}

// methodMap represents a map from method name to set of expectations for that
// method.
type methodMap map[string][]*InternalExpectation

// objectMap represents a map from mock object ID to a methodMap for that object.
type objectMap map[uintptr]methodMap

// NewController sets up a fresh controller, without any expectations set, and
// configures the controller to use the supplied error reporter.
func NewController(reporter ErrorReporter) Controller {
	return &controllerImpl{reporter, objectMap{}}
}

type controllerImpl struct {
	reporter ErrorReporter
	expectationsByObject objectMap
}

// getExpectations returns the list of registered expectations for the named
// method of the supplied object, or an empty slice if none have been
// registered. When this method returns, it is guaranteed that
// c.expectationsByObject has an entry for the object.
func (c *controllerImpl) getExpectations(
	o MockObject,
	methodName string) []*InternalExpectation {
	id := o.Oglemock_Id()

	// Look up the mock object.
	expectationsByMethod, ok := c.expectationsByObject[id]
	if !ok {
		expectationsByMethod = methodMap{}
		c.expectationsByObject[id] = expectationsByMethod
	}

	result, ok := expectationsByMethod[methodName]
	if !ok {
		return []*InternalExpectation{}
	}

	return result
}

// addExpectation adds an expectation to the list registered for the named
// method of the supplied mock object.
func (c *controllerImpl) addExpectation(
	o MockObject,
	methodName string,
	exp *InternalExpectation) {
	// Get the existing list.
	existing := c.getExpectations(o, methodName)

	// Store a modified list.
	id := o.Oglemock_Id()
	c.expectationsByObject[id][methodName] = append(existing, exp)
}

func (c *controllerImpl) ExpectCall(
	o MockObject,
	methodName string,
	fileName string,
	lineNumber int) PartialExpecation {
	// Find the signature for the requested method.
	oType := reflect.TypeOf(o)
	method, ok := oType.MethodByName(methodName)
	if !ok {
		c.reporter.ReportFatalError(
			fileName,
			lineNumber,
			errors.New("Unknown method: " + methodName))
		return nil
	}

	partialAlreadyCalled := false
	return func(args ...interface{}) Expectation {
		// This function should only be called once.
		if partialAlreadyCalled {
			panic("Partial expectation called more than once.")
		}

		partialAlreadyCalled = true

		// Make sure that the number of args is legal. Keep in mind that the
		// method's type has an extra receiver arg.
		if len(args) != method.Type.NumIn() - 1 {
			panic(
				fmt.Sprintf(
					"Expectation for %s given wrong number of arguments: " +
						"expected %d, got %d.",
					methodName,
					method.Type.NumIn() - 1,
					len(args)))
		}

		// Create an expectation and insert it into the controller's map.
		exp := InternalNewExpectation(method.Type, args, fileName, lineNumber)
		c.addExpectation(o, methodName, exp)

		// Return the expectation to the user.
		return exp
	}
}

func (c *controllerImpl) Finish() {
	// Check whether the minimum cardinality for each registered expectation has
	// been satisfied.
	for _, expectationsByMethod := range c.expectationsByObject {
		for methodName, expectations := range expectationsByMethod {
			for _, exp := range expectations {
				minCardinality, _ := computeCardinality(exp)
				if exp.NumMatches < minCardinality {
					c.reporter.ReportError(
						exp.FileName,
						exp.LineNumber,
						errors.New(
							fmt.Sprintf(
								"Unsatisfied expectation; expected %s to be called " +
								"at least %d times; called %d times.",
								methodName,
								minCardinality,
								exp.NumMatches)))
				}
			}
		}
	}
}

// expectationMatches checks the matchers for the expectation against the
// supplied arguments.
func expectationMatches(exp *InternalExpectation, args []interface{}) bool {
	matchers := exp.ArgMatchers
	if len(args) != len(matchers) {
		panic(
			fmt.Sprintf(
				"Wrong number of arguments: expected %d; got %d",
				len(matchers),
				len(args)))
	}

	// Check each matcher.
	for i, matcher := range matchers {
		res, _ := matcher.Matches(args[i])
		if !res {
			return false
		}
	}

	return true
}

// chooseExpectation returns the expectation that matches the supplied
// arguments. If there is more than one such expectation, the one furthest
// along in the list for the method is returned. If there is no such
// expectation, nil is returned.
func (c *controllerImpl) chooseExpectation(
	o MockObject,
	methodName string,
	args []interface{}) *InternalExpectation {
	// Do we have any expectations for this method?
	expectations := c.getExpectations(o, methodName)
	if len(expectations) == 0 {
		return nil
	}

	for i := len(expectations) - 1; i >= 0; i-- {
		if (expectationMatches(expectations[i], args)) {
			return expectations[i]
		}
	}

	return nil
}

// makeZeroReturnValues creates a []interface{} containing appropriate zero
// values for returning from the supplied method type.
func makeZeroReturnValues(method reflect.Method) []interface{} {
	methodType := method.Type
	result := make([]interface{}, methodType.NumOut())

	for i, _ := range result {
		outType := methodType.Out(i)
		zeroVal := reflect.Zero(outType)
		result[i] = zeroVal.Interface()
	}

	return result
}

// computeCardinality decides on the [min, max] range of the number of expected
// matches for the supplied expectations, according to the rules documented in
// expectation.go.
func computeCardinality(exp *InternalExpectation) (min, max uint) {
	// Explicit cardinality.
	if exp.ExpectedNumMatches >= 0 {
		min = uint(exp.ExpectedNumMatches)
		max = min
		return
	}

	// Implicit count based on one-time actions.
	if len(exp.OneTimeActions) != 0 {
		min = uint(len(exp.OneTimeActions))
		max = min

		// If there is a fallback action, this is only a lower bound.
		if exp.FallbackAction != nil {
			max = math.MaxUint32
		}

		return
	}

	// Implicit lack of restriction based on a fallback action being configured.
	if exp.FallbackAction != nil {
		min = 0
		max = math.MaxUint32
		return
	}

	// Implicit cardinality of one.
	min = 1
	max = 1
	return
}

// chooseAction returns the action that should be invoked for the i'th match to
// the supplied expectation (counting from zero). If the implicit "return zero
// values" action should be used, it returns nil.
func chooseAction(i uint, exp *InternalExpectation) Action {
	// Exhaust one-time actions first.
	if i < uint(len(exp.OneTimeActions)) {
		return exp.OneTimeActions[i]
	}

	// Fallback action (or nil if none is configured).
	return exp.FallbackAction
}

func (c *controllerImpl) HandleMethodCall(
	o MockObject,
	methodName string,
	fileName string,
	lineNumber int,
	args []interface{}) []interface{} {
	// Find the signature for the requested method.
	oType := reflect.TypeOf(o)
	method, ok := oType.MethodByName(methodName)
	if !ok {
		panic("Unknown method: " + methodName)
	}

	// Find an expectation matching this call.
	expectation := c.chooseExpectation(o, methodName, args)
	if expectation == nil {
		c.reporter.ReportError(
			fileName,
			lineNumber,
			errors.New(
				fmt.Sprintf("Unexpected call to %s with args: %v", methodName, args)))

		return makeZeroReturnValues(method)
	}

	// Increase the number of matches recorded, and check whether we're over the
	// number expected.
	expectation.NumMatches++
	_, maxCardinality := computeCardinality(expectation)
	if expectation.NumMatches > maxCardinality {
		c.reporter.ReportError(
			expectation.FileName,
			expectation.LineNumber,
			errors.New(
				fmt.Sprintf(
					"Unexpected call to %s: " +
						"expected to be called at most %d times; called %d times.",
					methodName,
					maxCardinality,
					expectation.NumMatches)))

		return makeZeroReturnValues(method)
	}

	// Choose an action to invoke. If there is none, just return zero values.
	action := chooseAction(expectation.NumMatches - 1, expectation)
	if action == nil {
		return makeZeroReturnValues(method)
	}

	// Let the action take over.
	return action.Invoke(args)
}
<commit_msg>Fixed two more controller panics.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 oglemock

import (
	"errors"
	"fmt"
	"math"
	"reflect"
)

// PartialExpecation is a function that should be called exactly once with
// expected arguments or matchers in order to set up an expected method call.
// See Controller.ExpectMethodCall below. It returns an expectation that can be
// further modified (e.g. by calling WillOnce).
//
// If the arguments are of the wrong type, the function reports a fatal error
// and returns nil.
type PartialExpecation func(...interface{}) Expectation

// Controller represents an object that implements the central logic of
// oglemock: recording and verifying expectations, responding to mock method
// calls, and so on.
type Controller interface {
	// ExpectCall expresses an expectation that the method of the given name
	// should be called on the supplied mock object. It returns a function that
	// should be called with the expected arguments, matchers for the arguments,
	// or a mix of both.
	//
	// fileName and lineNumber should indicate the line on which the expectation
	// was made, if known.
	//
	// For example:
	//
	//     mockWriter := [...]
	//     controller.ExpectCall(mockWriter, "Write")(ElementsAre(0x1))
	//         .WillOnce(Return(1, nil))
	//
	// If the mock object doesn't have a method of the supplied name, the
	// function reports a fatal error and returns nil.
	ExpectCall(
		o MockObject,
		methodName string,
		fileName string,
		lineNumber int) PartialExpecation

	// Finish causes the controller to check for any unsatisfied expectations,
	// and report them as errors if they exist.
	//
	// The controller may panic if any of its methods (including this one) are
	// called after Finish is called.
	Finish()

	// HandleMethodCall looks for a registered expectation matching the call of
	// the given method on mock object o, invokes the appropriate action (if
	// any), and returns the values returned by that action (if any).
	//
	// If the action returns nothing, the controller returns zero values. If
	// there is no matching expectation, the controller reports an error and
	// returns zero values.
	//
	// If the mock object doesn't have a method of the supplied name, the
	// arguments are of the wrong type, or the action returns the wrong types,
	// the function reports a fatal error.
	//
	// HandleMethodCall is exported for the sake of mock implementations, and
	// should not be used directly.
	HandleMethodCall(
		o MockObject,
		methodName string,
		fileName string,
		lineNumber int,
		args []interface{}) []interface{}
}

// methodMap represents a map from method name to set of expectations for that
// method.
type methodMap map[string][]*InternalExpectation

// objectMap represents a map from mock object ID to a methodMap for that object.
type objectMap map[uintptr]methodMap

// NewController sets up a fresh controller, without any expectations set, and
// configures the controller to use the supplied error reporter.
func NewController(reporter ErrorReporter) Controller {
	return &controllerImpl{reporter, objectMap{}}
}

type controllerImpl struct {
	reporter ErrorReporter
	expectationsByObject objectMap
}

// getExpectations returns the list of registered expectations for the named
// method of the supplied object, or an empty slice if none have been
// registered. When this method returns, it is guaranteed that
// c.expectationsByObject has an entry for the object.
func (c *controllerImpl) getExpectations(
	o MockObject,
	methodName string) []*InternalExpectation {
	id := o.Oglemock_Id()

	// Look up the mock object.
	expectationsByMethod, ok := c.expectationsByObject[id]
	if !ok {
		expectationsByMethod = methodMap{}
		c.expectationsByObject[id] = expectationsByMethod
	}

	result, ok := expectationsByMethod[methodName]
	if !ok {
		return []*InternalExpectation{}
	}

	return result
}

// addExpectation adds an expectation to the list registered for the named
// method of the supplied mock object.
func (c *controllerImpl) addExpectation(
	o MockObject,
	methodName string,
	exp *InternalExpectation) {
	// Get the existing list.
	existing := c.getExpectations(o, methodName)

	// Store a modified list.
	id := o.Oglemock_Id()
	c.expectationsByObject[id][methodName] = append(existing, exp)
}

func (c *controllerImpl) ExpectCall(
	o MockObject,
	methodName string,
	fileName string,
	lineNumber int) PartialExpecation {
	// Find the signature for the requested method.
	oType := reflect.TypeOf(o)
	method, ok := oType.MethodByName(methodName)
	if !ok {
		c.reporter.ReportFatalError(
			fileName,
			lineNumber,
			errors.New("Unknown method: " + methodName))
		return nil
	}

	partialAlreadyCalled := false
	return func(args ...interface{}) Expectation {
		// This function should only be called once.
		if partialAlreadyCalled {
			c.reporter.ReportFatalError(
				fileName,
				lineNumber,
				errors.New("Partial expectation called more than once."))
			return nil
		}

		partialAlreadyCalled = true

		// Make sure that the number of args is legal. Keep in mind that the
		// method's type has an extra receiver arg.
		if len(args) != method.Type.NumIn() - 1 {
			c.reporter.ReportFatalError(
				fileName,
				lineNumber,
				errors.New(
					fmt.Sprintf(
						"Expectation for %s given wrong number of arguments: " +
						"expected %d, got %d.",
						methodName,
						method.Type.NumIn() - 1,
						len(args))))
			return nil
		}

		// Create an expectation and insert it into the controller's map.
		exp := InternalNewExpectation(method.Type, args, fileName, lineNumber)
		c.addExpectation(o, methodName, exp)

		// Return the expectation to the user.
		return exp
	}
}

func (c *controllerImpl) Finish() {
	// Check whether the minimum cardinality for each registered expectation has
	// been satisfied.
	for _, expectationsByMethod := range c.expectationsByObject {
		for methodName, expectations := range expectationsByMethod {
			for _, exp := range expectations {
				minCardinality, _ := computeCardinality(exp)
				if exp.NumMatches < minCardinality {
					c.reporter.ReportError(
						exp.FileName,
						exp.LineNumber,
						errors.New(
							fmt.Sprintf(
								"Unsatisfied expectation; expected %s to be called " +
								"at least %d times; called %d times.",
								methodName,
								minCardinality,
								exp.NumMatches)))
				}
			}
		}
	}
}

// expectationMatches checks the matchers for the expectation against the
// supplied arguments.
func expectationMatches(exp *InternalExpectation, args []interface{}) bool {
	matchers := exp.ArgMatchers
	if len(args) != len(matchers) {
		panic(
			fmt.Sprintf(
				"Wrong number of arguments: expected %d; got %d",
				len(matchers),
				len(args)))
	}

	// Check each matcher.
	for i, matcher := range matchers {
		res, _ := matcher.Matches(args[i])
		if !res {
			return false
		}
	}

	return true
}

// chooseExpectation returns the expectation that matches the supplied
// arguments. If there is more than one such expectation, the one furthest
// along in the list for the method is returned. If there is no such
// expectation, nil is returned.
func (c *controllerImpl) chooseExpectation(
	o MockObject,
	methodName string,
	args []interface{}) *InternalExpectation {
	// Do we have any expectations for this method?
	expectations := c.getExpectations(o, methodName)
	if len(expectations) == 0 {
		return nil
	}

	for i := len(expectations) - 1; i >= 0; i-- {
		if (expectationMatches(expectations[i], args)) {
			return expectations[i]
		}
	}

	return nil
}

// makeZeroReturnValues creates a []interface{} containing appropriate zero
// values for returning from the supplied method type.
func makeZeroReturnValues(method reflect.Method) []interface{} {
	methodType := method.Type
	result := make([]interface{}, methodType.NumOut())

	for i, _ := range result {
		outType := methodType.Out(i)
		zeroVal := reflect.Zero(outType)
		result[i] = zeroVal.Interface()
	}

	return result
}

// computeCardinality decides on the [min, max] range of the number of expected
// matches for the supplied expectations, according to the rules documented in
// expectation.go.
func computeCardinality(exp *InternalExpectation) (min, max uint) {
	// Explicit cardinality.
	if exp.ExpectedNumMatches >= 0 {
		min = uint(exp.ExpectedNumMatches)
		max = min
		return
	}

	// Implicit count based on one-time actions.
	if len(exp.OneTimeActions) != 0 {
		min = uint(len(exp.OneTimeActions))
		max = min

		// If there is a fallback action, this is only a lower bound.
		if exp.FallbackAction != nil {
			max = math.MaxUint32
		}

		return
	}

	// Implicit lack of restriction based on a fallback action being configured.
	if exp.FallbackAction != nil {
		min = 0
		max = math.MaxUint32
		return
	}

	// Implicit cardinality of one.
	min = 1
	max = 1
	return
}

// chooseAction returns the action that should be invoked for the i'th match to
// the supplied expectation (counting from zero). If the implicit "return zero
// values" action should be used, it returns nil.
func chooseAction(i uint, exp *InternalExpectation) Action {
	// Exhaust one-time actions first.
	if i < uint(len(exp.OneTimeActions)) {
		return exp.OneTimeActions[i]
	}

	// Fallback action (or nil if none is configured).
	return exp.FallbackAction
}

func (c *controllerImpl) HandleMethodCall(
	o MockObject,
	methodName string,
	fileName string,
	lineNumber int,
	args []interface{}) []interface{} {
	// Find the signature for the requested method.
	oType := reflect.TypeOf(o)
	method, ok := oType.MethodByName(methodName)
	if !ok {
		panic("Unknown method: " + methodName)
	}

	// Find an expectation matching this call.
	expectation := c.chooseExpectation(o, methodName, args)
	if expectation == nil {
		c.reporter.ReportError(
			fileName,
			lineNumber,
			errors.New(
				fmt.Sprintf("Unexpected call to %s with args: %v", methodName, args)))

		return makeZeroReturnValues(method)
	}

	// Increase the number of matches recorded, and check whether we're over the
	// number expected.
	expectation.NumMatches++
	_, maxCardinality := computeCardinality(expectation)
	if expectation.NumMatches > maxCardinality {
		c.reporter.ReportError(
			expectation.FileName,
			expectation.LineNumber,
			errors.New(
				fmt.Sprintf(
					"Unexpected call to %s: " +
						"expected to be called at most %d times; called %d times.",
					methodName,
					maxCardinality,
					expectation.NumMatches)))

		return makeZeroReturnValues(method)
	}

	// Choose an action to invoke. If there is none, just return zero values.
	action := chooseAction(expectation.NumMatches - 1, expectation)
	if action == nil {
		return makeZeroReturnValues(method)
	}

	// Let the action take over.
	return action.Invoke(args)
}
<|endoftext|>


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

package jsonrpc2

import (
	"encoding/json"
	"io"
	"math/rand"
	"bytes"
	"net/http"
)

// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------

// clientRequest represents a JSON-RPC request sent by a client.
type clientRequest struct {
	// JSON-RPC protocol.
	Version string `json:"jsonrpc"`

	// A String containing the name of the method to be invoked.
	Method string `json:"method"`

	// Object to pass as request parameter to the method.
	Params interface{} `json:"params"`

	// The request id. This can be of any type. It is used to match the
	// response with the request that it is replying to.
	Id uint64 `json:"id"`
}

// clientResponse represents a JSON-RPC response returned to a client.
type clientResponse struct {
	Version string           `json:"jsonrpc"`
	Result  *json.RawMessage `json:"result"`
	Error   *json.RawMessage `json:"error"`
}

// EncodeClientRequest encodes parameters for a JSON-RPC client request.
func EncodeClientRequest(method string, args interface{}) ([]byte, error) {
	c := &clientRequest{
		Version: "2.0",
		Method:  method,
		Params:  args,
		Id:      uint64(rand.Int63()),
	}
	return json.Marshal(c)
}

// DecodeClientResponse decodes the response body of a client request into
// the interface reply.
func DecodeClientResponse(r io.Reader, reply interface{}) error {
	var c clientResponse
	if err := json.NewDecoder(r).Decode(&c); err != nil {
		return err
	}
	if c.Error != nil {
		jsonErr := &Error{}
		if err := json.Unmarshal(*c.Error, jsonErr); err != nil {
			return &Error{
				Code:    E_SERVER,
				Message: string(*c.Error),
			}
		}
		return jsonErr
	}

	if c.Result == nil {
		return ErrNullResult
	}

	return json.Unmarshal(*c.Result, reply)
}

func Call(url string, method string, request interface{}, reply interface{}) error {
	jsonReqBuf, err := EncodeClientRequest(method, request)
	if err != nil {
		return err
	}

	jsonReqBufR := bytes.NewReader(jsonReqBuf)
	rsp, err := http.Post(url, `application/json`, jsonReqBufR)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()

	return DecodeClientResponse(rsp.Body, reply)
}
<commit_msg>jsonrpc2 Call改为两个返回值，分别表示rpc协议的错误、程序错误<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package jsonrpc2

import (
	"encoding/json"
	"io"
	"math/rand"
	"bytes"
	"net/http"
)

// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------

// clientRequest represents a JSON-RPC request sent by a client.
type clientRequest struct {
	// JSON-RPC protocol.
	Version string `json:"jsonrpc"`

	// A String containing the name of the method to be invoked.
	Method string `json:"method"`

	// Object to pass as request parameter to the method.
	Params interface{} `json:"params"`

	// The request id. This can be of any type. It is used to match the
	// response with the request that it is replying to.
	Id uint64 `json:"id"`
}

// clientResponse represents a JSON-RPC response returned to a client.
type clientResponse struct {
	Id      interface{}      `json:"id"`
	Version string           `json:"jsonrpc"`
	Result  *json.RawMessage `json:"result"`
	Error   *json.RawMessage `json:"error"`
}

// EncodeClientRequest encodes parameters for a JSON-RPC client request.
func EncodeClientRequest(method string, args interface{}) ([]byte, error) {
	c := &clientRequest{
		Version: "2.0",
		Method:  method,
		Params:  args,
		Id:      uint64(rand.Int63()),
	}
	return json.Marshal(c)
}

// DecodeClientResponse decodes the response body of a client request into
// the interface reply.
func DecodeClientResponse(r io.Reader, reply interface{}) (replyError *Error, err error) {
	var c clientResponse
	if err := json.NewDecoder(r).Decode(&c); err != nil {
		return nil, err
	}

	// Error
	if c.Error != nil {
		jsonErr := &Error{}
		if err := json.Unmarshal(*c.Error, jsonErr); err != nil {
			return nil, &Error{
				Code:    E_SERVER,
				Message: string(*c.Error),
			}
		}
		return jsonErr, nil
	}

	// Result
	if c.Result == nil {
		return nil, ErrNullResult
	}
	return nil, json.Unmarshal(*c.Result, reply)
}

func Call(url string, method string, request interface{}, reply interface{}) (replyError *Error, err error) {
	jsonReqBuf, err := EncodeClientRequest(method, request)
	if err != nil {
		return
	}

	jsonReqBufR := bytes.NewReader(jsonReqBuf)
	rsp, err := http.Post(url, `application/json`, jsonReqBufR)
	if err != nil {
		return
	}
	defer rsp.Body.Close()

	return DecodeClientResponse(rsp.Body, reply)
}<|endoftext|>


<commit_before>package wincred

import (
	"encoding/binary"
	"reflect"
	"syscall"
	"time"
	"unicode/utf16"
	"unsafe"
)

var nullPointer = unsafe.Pointer(uintptr(0))

// Create a Go string using a pointer to a zero-terminated UTF 16 encoded string.
// See github.com/AllenDang/w32
func utf16PtrToString(wstr *uint16) string {
	if wstr != nil {
		buf := make([]uint16, 0, 256)
		for ptr := uintptr(unsafe.Pointer(wstr)); ; ptr += 2 {
			rune := *(*uint16)(unsafe.Pointer(ptr))
			if rune == 0 {
				return string(utf16.Decode(buf))
			}
			buf = append(buf, rune)
		}
	}

	return ""
}

// Create a byte array from a given UTF 16 char array
func utf16ToByte(wstr []uint16) (result []byte) {
	result = make([]byte, len(wstr)*2)
	for i, _ := range wstr {
		binary.LittleEndian.PutUint16(result[(i*2):(i*2)+2], wstr[i])
	}
	return
}

// Convert the given CREDENTIAL struct to a more usable structure
func nativeToCredential(cred *nativeCREDENTIAL) (result *Credential) {
	if unsafe.Pointer(cred) == nullPointer {
		return nil
	}
	result = new(Credential)
	result.Comment = utf16PtrToString(cred.Comment)
	result.TargetName = utf16PtrToString(cred.TargetName)
	result.TargetAlias = utf16PtrToString(cred.TargetAlias)
	result.UserName = utf16PtrToString(cred.UserName)
	result.LastWritten = time.Unix(0, cred.LastWritten.Nanoseconds())
	result.Persist = CredentialPersistence(cred.Persist)
	result.CredentialBlob = goBytes(unsafe.Pointer(cred.CredentialBlob), cred.CredentialBlobSize)
	result.Attributes = make([]CredentialAttribute, cred.AttributeCount)
	attrSliceHeader := reflect.SliceHeader{
		Data: cred.Attributes,
		Len:  int(cred.AttributeCount),
		Cap:  int(cred.AttributeCount),
	}
	attrSlice := *(*[]nativeCREDENTIAL_ATTRIBUTE)(unsafe.Pointer(&attrSliceHeader))
	for i, attr := range attrSlice {
		resultAttr := &result.Attributes[i]
		resultAttr.Keyword = utf16PtrToString(attr.Keyword)
		resultAttr.Value = goBytes(unsafe.Pointer(attr.Value), attr.ValueSize)
	}
	return result
}

func goBytes(src unsafe.Pointer, len uint32) []byte {
	if src == nullPointer {
		return []byte{}
	}
	slice := (*[1 << 30]byte)(src)[0:len]
	rv := make([]byte, len)
	copy(rv, slice)
	return rv[:]
}

// Convert the given Credential object back to a CREDENTIAL struct, which can be used for calling the
// Windows APIs
func nativeFromCredential(cred *Credential) (result *nativeCREDENTIAL) {
	if cred == nil {
		return nil
	}
	result = new(nativeCREDENTIAL)
	result.Flags = 0
	result.Type = 0
	result.TargetName, _ = syscall.UTF16PtrFromString(cred.TargetName)
	result.Comment, _ = syscall.UTF16PtrFromString(cred.Comment)
	result.LastWritten = syscall.NsecToFiletime(cred.LastWritten.UnixNano())
	result.CredentialBlobSize = uint32(len(cred.CredentialBlob))
	if len(cred.CredentialBlob) > 0 {
		result.CredentialBlob = uintptr(unsafe.Pointer(&cred.CredentialBlob[0]))
	} else {
		result.CredentialBlob = 0
	}
	result.Persist = uint32(cred.Persist)
	result.AttributeCount = uint32(len(cred.Attributes))
	attributes := make([]nativeCREDENTIAL_ATTRIBUTE, len(cred.Attributes))
	if len(attributes) > 0 {
		result.Attributes = uintptr(unsafe.Pointer(&attributes[0]))
	} else {
		result.Attributes = 0
	}
	for i, _ := range cred.Attributes {
		inAttr := &cred.Attributes[i]
		outAttr := &attributes[i]
		outAttr.Keyword, _ = syscall.UTF16PtrFromString(inAttr.Keyword)
		outAttr.Flags = 0
		outAttr.ValueSize = uint32(len(inAttr.Value))
		if len(inAttr.Value) > 0 {
			outAttr.Value = uintptr(unsafe.Pointer(&inAttr.Value[0]))
		} else {
			outAttr.Value = 0
		}
	}
	result.TargetAlias, _ = syscall.UTF16PtrFromString(cred.TargetAlias)
	result.UserName, _ = syscall.UTF16PtrFromString(cred.UserName)

	return
}
<commit_msg>Moved `goBytes` function<commit_after>package wincred

import (
	"encoding/binary"
	"reflect"
	"syscall"
	"time"
	"unicode/utf16"
	"unsafe"
)

var nullPointer = unsafe.Pointer(uintptr(0))

// Create a Go string using a pointer to a zero-terminated UTF 16 encoded string.
// See github.com/AllenDang/w32
func utf16PtrToString(wstr *uint16) string {
	if wstr != nil {
		buf := make([]uint16, 0, 256)
		for ptr := uintptr(unsafe.Pointer(wstr)); ; ptr += 2 {
			rune := *(*uint16)(unsafe.Pointer(ptr))
			if rune == 0 {
				return string(utf16.Decode(buf))
			}
			buf = append(buf, rune)
		}
	}

	return ""
}

// Create a byte array from a given UTF 16 char array
func utf16ToByte(wstr []uint16) (result []byte) {
	result = make([]byte, len(wstr)*2)
	for i, _ := range wstr {
		binary.LittleEndian.PutUint16(result[(i*2):(i*2)+2], wstr[i])
	}
	return
}

// Copies the given C byte array to a Go byte array (see `C.GoBytes`)
func goBytes(src unsafe.Pointer, len uint32) []byte {
	if src == nullPointer {
		return []byte{}
	}
	slice := (*[1 << 30]byte)(src)[0:len]
	rv := make([]byte, len)
	copy(rv, slice)
	return rv[:]
}

// Convert the given CREDENTIAL struct to a more usable structure
func nativeToCredential(cred *nativeCREDENTIAL) (result *Credential) {
	if unsafe.Pointer(cred) == nullPointer {
		return nil
	}
	result = new(Credential)
	result.Comment = utf16PtrToString(cred.Comment)
	result.TargetName = utf16PtrToString(cred.TargetName)
	result.TargetAlias = utf16PtrToString(cred.TargetAlias)
	result.UserName = utf16PtrToString(cred.UserName)
	result.LastWritten = time.Unix(0, cred.LastWritten.Nanoseconds())
	result.Persist = CredentialPersistence(cred.Persist)
	result.CredentialBlob = goBytes(unsafe.Pointer(cred.CredentialBlob), cred.CredentialBlobSize)
	result.Attributes = make([]CredentialAttribute, cred.AttributeCount)
	attrSliceHeader := reflect.SliceHeader{
		Data: cred.Attributes,
		Len:  int(cred.AttributeCount),
		Cap:  int(cred.AttributeCount),
	}
	attrSlice := *(*[]nativeCREDENTIAL_ATTRIBUTE)(unsafe.Pointer(&attrSliceHeader))
	for i, attr := range attrSlice {
		resultAttr := &result.Attributes[i]
		resultAttr.Keyword = utf16PtrToString(attr.Keyword)
		resultAttr.Value = goBytes(unsafe.Pointer(attr.Value), attr.ValueSize)
	}
	return result
}

// Convert the given Credential object back to a CREDENTIAL struct, which can be used for calling the
// Windows APIs
func nativeFromCredential(cred *Credential) (result *nativeCREDENTIAL) {
	if cred == nil {
		return nil
	}
	result = new(nativeCREDENTIAL)
	result.Flags = 0
	result.Type = 0
	result.TargetName, _ = syscall.UTF16PtrFromString(cred.TargetName)
	result.Comment, _ = syscall.UTF16PtrFromString(cred.Comment)
	result.LastWritten = syscall.NsecToFiletime(cred.LastWritten.UnixNano())
	result.CredentialBlobSize = uint32(len(cred.CredentialBlob))
	if len(cred.CredentialBlob) > 0 {
		result.CredentialBlob = uintptr(unsafe.Pointer(&cred.CredentialBlob[0]))
	} else {
		result.CredentialBlob = 0
	}
	result.Persist = uint32(cred.Persist)
	result.AttributeCount = uint32(len(cred.Attributes))
	attributes := make([]nativeCREDENTIAL_ATTRIBUTE, len(cred.Attributes))
	if len(attributes) > 0 {
		result.Attributes = uintptr(unsafe.Pointer(&attributes[0]))
	} else {
		result.Attributes = 0
	}
	for i, _ := range cred.Attributes {
		inAttr := &cred.Attributes[i]
		outAttr := &attributes[i]
		outAttr.Keyword, _ = syscall.UTF16PtrFromString(inAttr.Keyword)
		outAttr.Flags = 0
		outAttr.ValueSize = uint32(len(inAttr.Value))
		if len(inAttr.Value) > 0 {
			outAttr.Value = uintptr(unsafe.Pointer(&inAttr.Value[0]))
		} else {
			outAttr.Value = 0
		}
	}
	result.TargetAlias, _ = syscall.UTF16PtrFromString(cred.TargetAlias)
	result.UserName, _ = syscall.UTF16PtrFromString(cred.UserName)

	return
}
<|endoftext|>


<commit_before>package auth

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/registry/client"
	"github.com/docker/distribution/registry/client/transport"
)

// ErrNoBasicAuthCredentials is returned if a request can't be authorized with
// basic auth due to lack of credentials.
var ErrNoBasicAuthCredentials = errors.New("no basic auth credentials")

// AuthenticationHandler is an interface for authorizing a request from
// params from a "WWW-Authenicate" header for a single scheme.
type AuthenticationHandler interface {
	// Scheme returns the scheme as expected from the "WWW-Authenicate" header.
	Scheme() string

	// AuthorizeRequest adds the authorization header to a request (if needed)
	// using the parameters from "WWW-Authenticate" method. The parameters
	// values depend on the scheme.
	AuthorizeRequest(req *http.Request, params map[string]string) error
}

// CredentialStore is an interface for getting credentials for
// a given URL
type CredentialStore interface {
	// Basic returns basic auth for the given URL
	Basic(*url.URL) (string, string)

	// RefreshToken returns a refresh token for the
	// given URL and service
	RefreshToken(*url.URL, string) string

	// SetRefreshToken sets the refresh token if none
	// is provided for the given url and service
	SetRefreshToken(realm *url.URL, service, token string)
}

// NewAuthorizer creates an authorizer which can handle multiple authentication
// schemes. The handlers are tried in order, the higher priority authentication
// methods should be first. The challengeMap holds a list of challenges for
// a given root API endpoint (for example "https://registry-1.docker.io/v2/").
func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler) transport.RequestModifier {
	return &endpointAuthorizer{
		challenges: manager,
		handlers:   handlers,
	}
}

type endpointAuthorizer struct {
	challenges ChallengeManager
	handlers   []AuthenticationHandler
	transport  http.RoundTripper
}

func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error {
	v2Root := strings.Index(req.URL.Path, "/v2/")
	if v2Root == -1 {
		return nil
	}

	ping := url.URL{
		Host:   req.URL.Host,
		Scheme: req.URL.Scheme,
		Path:   req.URL.Path[:v2Root+4],
	}

	pingEndpoint := ping.String()

	challenges, err := ea.challenges.GetChallenges(pingEndpoint)
	if err != nil {
		return err
	}

	if len(challenges) > 0 {
		for _, handler := range ea.handlers {
			for _, challenge := range challenges {
				if challenge.Scheme != handler.Scheme() {
					continue
				}
				if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// This is the minimum duration a token can last (in seconds).
// A token must not live less than 60 seconds because older versions
// of the Docker client didn't read their expiration from the token
// response and assumed 60 seconds.  So to remain compatible with
// those implementations, a token must live at least this long.
const minimumTokenLifetimeSeconds = 60

// Private interface for time used by this package to enable tests to provide their own implementation.
type clock interface {
	Now() time.Time
}

type tokenHandler struct {
	header    http.Header
	creds     CredentialStore
	transport http.RoundTripper
	clock     clock

	forceOAuth bool
	clientID   string
	scopes     []Scope

	tokenLock       sync.Mutex
	tokenCache      string
	tokenExpiration time.Time
}

// Scope is a type which is serializable to a string
// using the allow scope grammar.
type Scope interface {
	String() string
}

// RepositoryScope represents a token scope for access
// to a repository.
type RepositoryScope struct {
	Repository string
	Actions    []string
}

// String returns the string representation of the repository
// using the scope grammar
func (rs RepositoryScope) String() string {
	return fmt.Sprintf("repository:%s:%s", rs.Repository, strings.Join(rs.Actions, ","))
}

// TokenHandlerOptions is used to configure a new token handler
type TokenHandlerOptions struct {
	Transport   http.RoundTripper
	Credentials CredentialStore

	ForceOAuth bool
	ClientID   string
	Scopes     []Scope
}

// An implementation of clock for providing real time data.
type realClock struct{}

// Now implements clock
func (realClock) Now() time.Time { return time.Now() }

// NewTokenHandler creates a new AuthenicationHandler which supports
// fetching tokens from a remote token server.
func NewTokenHandler(transport http.RoundTripper, creds CredentialStore, scope string, actions ...string) AuthenticationHandler {
	// Create options...
	return NewTokenHandlerWithOptions(TokenHandlerOptions{
		Transport:   transport,
		Credentials: creds,
		Scopes: []Scope{
			RepositoryScope{
				Repository: scope,
				Actions:    actions,
			},
		},
	})
}

// NewTokenHandlerWithOptions creates a new token handler using the provided
// options structure.
func NewTokenHandlerWithOptions(options TokenHandlerOptions) AuthenticationHandler {
	handler := &tokenHandler{
		transport:  options.Transport,
		creds:      options.Credentials,
		forceOAuth: options.ForceOAuth,
		clientID:   options.ClientID,
		scopes:     options.Scopes,
		clock:      realClock{},
	}

	return handler
}

func (th *tokenHandler) client() *http.Client {
	return &http.Client{
		Transport: th.transport,
		Timeout:   15 * time.Second,
	}
}

func (th *tokenHandler) Scheme() string {
	return "bearer"
}

func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	var additionalScopes []string
	if fromParam := req.URL.Query().Get("from"); fromParam != "" {
		additionalScopes = append(additionalScopes, RepositoryScope{
			Repository: fromParam,
			Actions:    []string{"pull"},
		}.String())
	}
	if err := th.refreshToken(params, additionalScopes...); err != nil {
		return err
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.tokenCache))

	return nil
}

func (th *tokenHandler) refreshToken(params map[string]string, additionalScopes ...string) error {
	th.tokenLock.Lock()
	defer th.tokenLock.Unlock()
	scopes := make([]string, 0, len(th.scopes)+len(additionalScopes))
	for _, scope := range th.scopes {
		scopes = append(scopes, scope.String())
	}
	var addedScopes bool
	for _, scope := range additionalScopes {
		scopes = append(scopes, scope)
		addedScopes = true
	}

	now := th.clock.Now()
	if now.After(th.tokenExpiration) || addedScopes {
		token, expiration, err := th.fetchToken(params, scopes)
		if err != nil {
			return err
		}

		// do not update cache for added scope tokens
		if !addedScopes {
			th.tokenCache = token
			th.tokenExpiration = expiration
		}
	}

	return nil
}

type postTokenResponse struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	Scope        string    `json:"scope"`
}

func (th *tokenHandler) fetchTokenWithOAuth(realm *url.URL, refreshToken, service string, scopes []string) (token string, expiration time.Time, err error) {
	form := url.Values{}
	form.Set("scope", strings.Join(scopes, " "))
	form.Set("service", service)

	clientID := th.clientID
	if clientID == "" {
		// Use default client, this is a required field
		clientID = "registry-client"
	}
	form.Set("client_id", clientID)

	if refreshToken != "" {
		form.Set("grant_type", "refresh_token")
		form.Set("refresh_token", refreshToken)
	} else if th.creds != nil {
		form.Set("grant_type", "password")
		username, password := th.creds.Basic(realm)
		form.Set("username", username)
		form.Set("password", password)

		// attempt to get a refresh token
		form.Set("access_type", "offline")
	} else {
		// refuse to do oauth without a grant type
		return "", time.Time{}, fmt.Errorf("no supported grant type")
	}

	resp, err := th.client().PostForm(realm.String(), form)
	if err != nil {
		return "", time.Time{}, err
	}
	defer resp.Body.Close()

	if !client.SuccessStatus(resp.StatusCode) {
		err := client.HandleErrorResponse(resp)
		return "", time.Time{}, err
	}

	decoder := json.NewDecoder(resp.Body)

	var tr postTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", time.Time{}, fmt.Errorf("unable to decode token response: %s", err)
	}

	if tr.RefreshToken != "" && tr.RefreshToken != refreshToken {
		th.creds.SetRefreshToken(realm, service, tr.RefreshToken)
	}

	if tr.ExpiresIn < minimumTokenLifetimeSeconds {
		// The default/minimum lifetime.
		tr.ExpiresIn = minimumTokenLifetimeSeconds
		logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
	}

	if tr.IssuedAt.IsZero() {
		// issued_at is optional in the token response.
		tr.IssuedAt = th.clock.Now().UTC()
	}

	return tr.AccessToken, tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second), nil
}

type getTokenResponse struct {
	Token        string    `json:"token"`
	AccessToken  string    `json:"access_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	RefreshToken string    `json:"refresh_token"`
}

func (th *tokenHandler) fetchTokenWithBasicAuth(realm *url.URL, service string, scopes []string) (token string, expiration time.Time, err error) {

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

	reqParams := req.URL.Query()

	if service != "" {
		reqParams.Add("service", service)
	}

	for _, scope := range scopes {
		reqParams.Add("scope", scope)
	}

	if th.creds != nil {
		username, password := th.creds.Basic(realm)
		if username != "" && password != "" {
			reqParams.Add("account", username)
			req.SetBasicAuth(username, password)
		}
	}

	req.URL.RawQuery = reqParams.Encode()

	resp, err := th.client().Do(req)
	if err != nil {
		return "", time.Time{}, err
	}
	defer resp.Body.Close()

	if !client.SuccessStatus(resp.StatusCode) {
		err := client.HandleErrorResponse(resp)
		return "", time.Time{}, err
	}

	decoder := json.NewDecoder(resp.Body)

	var tr getTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", time.Time{}, fmt.Errorf("unable to decode token response: %s", err)
	}

	if tr.RefreshToken != "" && th.creds != nil {
		th.creds.SetRefreshToken(realm, service, tr.RefreshToken)
	}

	// `access_token` is equivalent to `token` and if both are specified
	// the choice is undefined.  Canonicalize `access_token` by sticking
	// things in `token`.
	if tr.AccessToken != "" {
		tr.Token = tr.AccessToken
	}

	if tr.Token == "" {
		return "", time.Time{}, errors.New("authorization server did not include a token in the response")
	}

	if tr.ExpiresIn < minimumTokenLifetimeSeconds {
		// The default/minimum lifetime.
		tr.ExpiresIn = minimumTokenLifetimeSeconds
		logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
	}

	if tr.IssuedAt.IsZero() {
		// issued_at is optional in the token response.
		tr.IssuedAt = th.clock.Now().UTC()
	}

	return tr.Token, tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second), nil
}

func (th *tokenHandler) fetchToken(params map[string]string, scopes []string) (token string, expiration time.Time, err error) {
	realm, ok := params["realm"]
	if !ok {
		return "", time.Time{}, errors.New("no realm specified for token auth challenge")
	}

	// TODO(dmcgowan): Handle empty scheme and relative realm
	realmURL, err := url.Parse(realm)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("invalid token auth challenge realm: %s", err)
	}

	service := params["service"]

	var refreshToken string

	if th.creds != nil {
		refreshToken = th.creds.RefreshToken(realmURL, service)
	}

	if refreshToken != "" || th.forceOAuth {
		return th.fetchTokenWithOAuth(realmURL, refreshToken, service, scopes)
	}

	return th.fetchTokenWithBasicAuth(realmURL, service, scopes)
}

type basicHandler struct {
	creds CredentialStore
}

// NewBasicHandler creaters a new authentiation handler which adds
// basic authentication credentials to a request.
func NewBasicHandler(creds CredentialStore) AuthenticationHandler {
	return &basicHandler{
		creds: creds,
	}
}

func (*basicHandler) Scheme() string {
	return "basic"
}

func (bh *basicHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	if bh.creds != nil {
		username, password := bh.creds.Basic(req.URL)
		if username != "" && password != "" {
			req.SetBasicAuth(username, password)
			return nil
		}
	}
	return ErrNoBasicAuthCredentials
}
<commit_msg>Add offline token option<commit_after>package auth

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/registry/client"
	"github.com/docker/distribution/registry/client/transport"
)

// ErrNoBasicAuthCredentials is returned if a request can't be authorized with
// basic auth due to lack of credentials.
var ErrNoBasicAuthCredentials = errors.New("no basic auth credentials")

// AuthenticationHandler is an interface for authorizing a request from
// params from a "WWW-Authenicate" header for a single scheme.
type AuthenticationHandler interface {
	// Scheme returns the scheme as expected from the "WWW-Authenicate" header.
	Scheme() string

	// AuthorizeRequest adds the authorization header to a request (if needed)
	// using the parameters from "WWW-Authenticate" method. The parameters
	// values depend on the scheme.
	AuthorizeRequest(req *http.Request, params map[string]string) error
}

// CredentialStore is an interface for getting credentials for
// a given URL
type CredentialStore interface {
	// Basic returns basic auth for the given URL
	Basic(*url.URL) (string, string)

	// RefreshToken returns a refresh token for the
	// given URL and service
	RefreshToken(*url.URL, string) string

	// SetRefreshToken sets the refresh token if none
	// is provided for the given url and service
	SetRefreshToken(realm *url.URL, service, token string)
}

// NewAuthorizer creates an authorizer which can handle multiple authentication
// schemes. The handlers are tried in order, the higher priority authentication
// methods should be first. The challengeMap holds a list of challenges for
// a given root API endpoint (for example "https://registry-1.docker.io/v2/").
func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler) transport.RequestModifier {
	return &endpointAuthorizer{
		challenges: manager,
		handlers:   handlers,
	}
}

type endpointAuthorizer struct {
	challenges ChallengeManager
	handlers   []AuthenticationHandler
	transport  http.RoundTripper
}

func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error {
	v2Root := strings.Index(req.URL.Path, "/v2/")
	if v2Root == -1 {
		return nil
	}

	ping := url.URL{
		Host:   req.URL.Host,
		Scheme: req.URL.Scheme,
		Path:   req.URL.Path[:v2Root+4],
	}

	pingEndpoint := ping.String()

	challenges, err := ea.challenges.GetChallenges(pingEndpoint)
	if err != nil {
		return err
	}

	if len(challenges) > 0 {
		for _, handler := range ea.handlers {
			for _, challenge := range challenges {
				if challenge.Scheme != handler.Scheme() {
					continue
				}
				if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// This is the minimum duration a token can last (in seconds).
// A token must not live less than 60 seconds because older versions
// of the Docker client didn't read their expiration from the token
// response and assumed 60 seconds.  So to remain compatible with
// those implementations, a token must live at least this long.
const minimumTokenLifetimeSeconds = 60

// Private interface for time used by this package to enable tests to provide their own implementation.
type clock interface {
	Now() time.Time
}

type tokenHandler struct {
	header    http.Header
	creds     CredentialStore
	transport http.RoundTripper
	clock     clock

	offlineAccess bool
	forceOAuth    bool
	clientID      string
	scopes        []Scope

	tokenLock       sync.Mutex
	tokenCache      string
	tokenExpiration time.Time
}

// Scope is a type which is serializable to a string
// using the allow scope grammar.
type Scope interface {
	String() string
}

// RepositoryScope represents a token scope for access
// to a repository.
type RepositoryScope struct {
	Repository string
	Actions    []string
}

// String returns the string representation of the repository
// using the scope grammar
func (rs RepositoryScope) String() string {
	return fmt.Sprintf("repository:%s:%s", rs.Repository, strings.Join(rs.Actions, ","))
}

// TokenHandlerOptions is used to configure a new token handler
type TokenHandlerOptions struct {
	Transport   http.RoundTripper
	Credentials CredentialStore

	OfflineAccess bool
	ForceOAuth    bool
	ClientID      string
	Scopes        []Scope
}

// An implementation of clock for providing real time data.
type realClock struct{}

// Now implements clock
func (realClock) Now() time.Time { return time.Now() }

// NewTokenHandler creates a new AuthenicationHandler which supports
// fetching tokens from a remote token server.
func NewTokenHandler(transport http.RoundTripper, creds CredentialStore, scope string, actions ...string) AuthenticationHandler {
	// Create options...
	return NewTokenHandlerWithOptions(TokenHandlerOptions{
		Transport:   transport,
		Credentials: creds,
		Scopes: []Scope{
			RepositoryScope{
				Repository: scope,
				Actions:    actions,
			},
		},
	})
}

// NewTokenHandlerWithOptions creates a new token handler using the provided
// options structure.
func NewTokenHandlerWithOptions(options TokenHandlerOptions) AuthenticationHandler {
	handler := &tokenHandler{
		transport:     options.Transport,
		creds:         options.Credentials,
		offlineAccess: options.OfflineAccess,
		forceOAuth:    options.ForceOAuth,
		clientID:      options.ClientID,
		scopes:        options.Scopes,
		clock:         realClock{},
	}

	return handler
}

func (th *tokenHandler) client() *http.Client {
	return &http.Client{
		Transport: th.transport,
		Timeout:   15 * time.Second,
	}
}

func (th *tokenHandler) Scheme() string {
	return "bearer"
}

func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	var additionalScopes []string
	if fromParam := req.URL.Query().Get("from"); fromParam != "" {
		additionalScopes = append(additionalScopes, RepositoryScope{
			Repository: fromParam,
			Actions:    []string{"pull"},
		}.String())
	}
	if err := th.refreshToken(params, additionalScopes...); err != nil {
		return err
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.tokenCache))

	return nil
}

func (th *tokenHandler) refreshToken(params map[string]string, additionalScopes ...string) error {
	th.tokenLock.Lock()
	defer th.tokenLock.Unlock()
	scopes := make([]string, 0, len(th.scopes)+len(additionalScopes))
	for _, scope := range th.scopes {
		scopes = append(scopes, scope.String())
	}
	var addedScopes bool
	for _, scope := range additionalScopes {
		scopes = append(scopes, scope)
		addedScopes = true
	}

	now := th.clock.Now()
	if now.After(th.tokenExpiration) || addedScopes {
		token, expiration, err := th.fetchToken(params, scopes)
		if err != nil {
			return err
		}

		// do not update cache for added scope tokens
		if !addedScopes {
			th.tokenCache = token
			th.tokenExpiration = expiration
		}
	}

	return nil
}

type postTokenResponse struct {
	AccessToken  string    `json:"access_token"`
	RefreshToken string    `json:"refresh_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	Scope        string    `json:"scope"`
}

func (th *tokenHandler) fetchTokenWithOAuth(realm *url.URL, refreshToken, service string, scopes []string) (token string, expiration time.Time, err error) {
	form := url.Values{}
	form.Set("scope", strings.Join(scopes, " "))
	form.Set("service", service)

	clientID := th.clientID
	if clientID == "" {
		// Use default client, this is a required field
		clientID = "registry-client"
	}
	form.Set("client_id", clientID)

	if refreshToken != "" {
		form.Set("grant_type", "refresh_token")
		form.Set("refresh_token", refreshToken)
	} else if th.creds != nil {
		form.Set("grant_type", "password")
		username, password := th.creds.Basic(realm)
		form.Set("username", username)
		form.Set("password", password)

		// attempt to get a refresh token
		form.Set("access_type", "offline")
	} else {
		// refuse to do oauth without a grant type
		return "", time.Time{}, fmt.Errorf("no supported grant type")
	}

	resp, err := th.client().PostForm(realm.String(), form)
	if err != nil {
		return "", time.Time{}, err
	}
	defer resp.Body.Close()

	if !client.SuccessStatus(resp.StatusCode) {
		err := client.HandleErrorResponse(resp)
		return "", time.Time{}, err
	}

	decoder := json.NewDecoder(resp.Body)

	var tr postTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", time.Time{}, fmt.Errorf("unable to decode token response: %s", err)
	}

	if tr.RefreshToken != "" && tr.RefreshToken != refreshToken {
		th.creds.SetRefreshToken(realm, service, tr.RefreshToken)
	}

	if tr.ExpiresIn < minimumTokenLifetimeSeconds {
		// The default/minimum lifetime.
		tr.ExpiresIn = minimumTokenLifetimeSeconds
		logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
	}

	if tr.IssuedAt.IsZero() {
		// issued_at is optional in the token response.
		tr.IssuedAt = th.clock.Now().UTC()
	}

	return tr.AccessToken, tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second), nil
}

type getTokenResponse struct {
	Token        string    `json:"token"`
	AccessToken  string    `json:"access_token"`
	ExpiresIn    int       `json:"expires_in"`
	IssuedAt     time.Time `json:"issued_at"`
	RefreshToken string    `json:"refresh_token"`
}

func (th *tokenHandler) fetchTokenWithBasicAuth(realm *url.URL, service string, scopes []string) (token string, expiration time.Time, err error) {

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

	reqParams := req.URL.Query()

	if service != "" {
		reqParams.Add("service", service)
	}

	for _, scope := range scopes {
		reqParams.Add("scope", scope)
	}

	if th.offlineAccess {
		reqParams.Add("offline_token", "true")
	}

	if th.creds != nil {
		username, password := th.creds.Basic(realm)
		if username != "" && password != "" {
			reqParams.Add("account", username)
			req.SetBasicAuth(username, password)
		}
	}

	req.URL.RawQuery = reqParams.Encode()

	resp, err := th.client().Do(req)
	if err != nil {
		return "", time.Time{}, err
	}
	defer resp.Body.Close()

	if !client.SuccessStatus(resp.StatusCode) {
		err := client.HandleErrorResponse(resp)
		return "", time.Time{}, err
	}

	decoder := json.NewDecoder(resp.Body)

	var tr getTokenResponse
	if err = decoder.Decode(&tr); err != nil {
		return "", time.Time{}, fmt.Errorf("unable to decode token response: %s", err)
	}

	if tr.RefreshToken != "" && th.creds != nil {
		th.creds.SetRefreshToken(realm, service, tr.RefreshToken)
	}

	// `access_token` is equivalent to `token` and if both are specified
	// the choice is undefined.  Canonicalize `access_token` by sticking
	// things in `token`.
	if tr.AccessToken != "" {
		tr.Token = tr.AccessToken
	}

	if tr.Token == "" {
		return "", time.Time{}, errors.New("authorization server did not include a token in the response")
	}

	if tr.ExpiresIn < minimumTokenLifetimeSeconds {
		// The default/minimum lifetime.
		tr.ExpiresIn = minimumTokenLifetimeSeconds
		logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
	}

	if tr.IssuedAt.IsZero() {
		// issued_at is optional in the token response.
		tr.IssuedAt = th.clock.Now().UTC()
	}

	return tr.Token, tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second), nil
}

func (th *tokenHandler) fetchToken(params map[string]string, scopes []string) (token string, expiration time.Time, err error) {
	realm, ok := params["realm"]
	if !ok {
		return "", time.Time{}, errors.New("no realm specified for token auth challenge")
	}

	// TODO(dmcgowan): Handle empty scheme and relative realm
	realmURL, err := url.Parse(realm)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("invalid token auth challenge realm: %s", err)
	}

	service := params["service"]

	var refreshToken string

	if th.creds != nil {
		refreshToken = th.creds.RefreshToken(realmURL, service)
	}

	if refreshToken != "" || th.forceOAuth {
		return th.fetchTokenWithOAuth(realmURL, refreshToken, service, scopes)
	}

	return th.fetchTokenWithBasicAuth(realmURL, service, scopes)
}

type basicHandler struct {
	creds CredentialStore
}

// NewBasicHandler creaters a new authentiation handler which adds
// basic authentication credentials to a request.
func NewBasicHandler(creds CredentialStore) AuthenticationHandler {
	return &basicHandler{
		creds: creds,
	}
}

func (*basicHandler) Scheme() string {
	return "basic"
}

func (bh *basicHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	if bh.creds != nil {
		username, password := bh.creds.Basic(req.URL)
		if username != "" && password != "" {
			req.SetBasicAuth(username, password)
			return nil
		}
	}
	return ErrNoBasicAuthCredentials
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package policy

import (
	"time"

	"github.com/m3db/m3x/time"
)

// DefaultPolicyVersion is the version for the default policy
const (
	DefaultPolicyVersion = 0
)

// DefaultVersionedPolicies are the default versioned policies
var (
	DefaultVersionedPolicies = VersionedPolicies{
		Version: DefaultPolicyVersion,
		Policies: []Policy{
			{
				Resolution: Resolution{Window: 10 * time.Second, Precision: xtime.Second},
				Retention:  Retention(2 * 24 * time.Hour),
			},
			{
				Resolution: Resolution{Window: time.Minute, Precision: xtime.Minute},
				Retention:  Retention(30 * 24 * time.Hour),
			},
		},
	}
)
<commit_msg>Add an init policy version<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package policy

import (
	"time"

	"github.com/m3db/m3x/time"
)

const (
	// InitPolicyVersion is the version of an uninitialized policy
	InitPolicyVersion = -1

	// DefaultPolicyVersion is the version for the default policy
	DefaultPolicyVersion = 0
)

// DefaultVersionedPolicies are the default versioned policies
var (
	DefaultVersionedPolicies = VersionedPolicies{
		Version: DefaultPolicyVersion,
		Policies: []Policy{
			{
				Resolution: Resolution{Window: 10 * time.Second, Precision: xtime.Second},
				Retention:  Retention(2 * 24 * time.Hour),
			},
			{
				Resolution: Resolution{Window: time.Minute, Precision: xtime.Minute},
				Retention:  Retention(30 * 24 * time.Hour),
			},
		},
	}
)
<|endoftext|>


<commit_before>package validations

import (
	"reflect"
	"time"

	"github.com/juju/errgo"
)

type TimeFunc func(s time.Time) (time.Time, error)

func Time(field *time.Time, validations []TimeFunc) Func {
	return func() error {
		privField := time.Time{}

		for _, validation := range validations {
			if newField, err := validation(privField); err != nil {
				return errgo.Mask(err)
			} else {
				privField = newField
			}
		}

		reflect.ValueOf(field).Elem().Set(reflect.ValueOf(privField))

		return nil
	}
}

func ParseTime(source, format string) TimeFunc {
	return func(value time.Time) (time.Time, error) {
		parsed, err := time.Parse(format, source)
		if err != nil {
			return time.Time{}, errgo.Mask(err)
		}

		return parsed, nil
	}
}

func ParseTimeUnix(seconds int64) TimeFunc {
	return func(value time.Time) (time.Time, error) {
		return time.Unix(seconds, 0), nil
	}
}

func NotBefore(otherTime time.Time) TimeFunc {
	return func(value time.Time) (time.Time, error) {
		if value.Before(otherTime) {
			return time.Time{}, errgo.Newf("not before")
		}

		return value, nil
	}
}
<commit_msg>NotAfter filter in time.<commit_after>package validations

import (
	"reflect"
	"time"

	"github.com/juju/errgo"
)

type TimeFunc func(s time.Time) (time.Time, error)

func Time(field *time.Time, validations []TimeFunc) Func {
	return func() error {
		privField := time.Time{}

		for _, validation := range validations {
			if newField, err := validation(privField); err != nil {
				return errgo.Mask(err)
			} else {
				privField = newField
			}
		}

		reflect.ValueOf(field).Elem().Set(reflect.ValueOf(privField))

		return nil
	}
}

func ParseTime(source, format string) TimeFunc {
	return func(value time.Time) (time.Time, error) {
		parsed, err := time.Parse(format, source)
		if err != nil {
			return time.Time{}, errgo.Mask(err)
		}

		return parsed, nil
	}
}

func ParseTimeUnix(seconds int64) TimeFunc {
	return func(value time.Time) (time.Time, error) {
		return time.Unix(seconds, 0), nil
	}
}

func NotBefore(otherTime time.Time) TimeFunc {
	return func(value time.Time) (time.Time, error) {
		if value.Before(otherTime) {
			return time.Time{}, errgo.Newf("not before")
		}

		return value, nil
	}
}

func NotAfter(otherTime time.Time) TimeFunc {
	return func(value time.Time) (time.Time, error) {
		if value.After(otherTime) {
			return time.Time{}, errgo.Newf("not after")
		}

		return value, nil
	}
}
<|endoftext|>


<commit_before>
package com

import (
	"time"
	"strings"
	"fmt"
	"strconv"
)

// format unix time int to string
func Date(ti int64, format string) string {
	t := time.Unix(int64(ti), 0)
	return DateTime(t, format)
}

// format unix time string to string
func DateUnixStr(ts string, format string) string {
	i, _ := strconv.ParseInt(ts, 10, 64)
	return Date(i, format)
}

// format time.Time to string
func DateTime(t time.Time, format string) string {
	res := strings.Replace(format, "MM", t.Format("01"), -1)
	res = strings.Replace(res, "M", t.Format("1"), -1)
	res = strings.Replace(res, "DD", t.Format("02"), -1)
	res = strings.Replace(res, "D", t.Format("2"), -1)
	res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
	res = strings.Replace(res, "YY", t.Format("06"), -1)
	res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
	res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
	res = strings.Replace(res, "hh", t.Format("03"), -1)
	res = strings.Replace(res, "h", t.Format("3"), -1)
	res = strings.Replace(res, "mm", t.Format("04"), -1)
	res = strings.Replace(res, "m", t.Format("4"), -1)
	res = strings.Replace(res, "ss", t.Format("05"), -1)
	res = strings.Replace(res, "s", t.Format("5"), -1)
	return res
}

// get now unix timestamp int64
func Now() int64 {
	return time.Now().Unix()
}

<commit_msg>rename time funcs Date, DateT, DateS<commit_after>package com

import (
	"time"
	"strings"
	"fmt"
	"strconv"
)

// format unix time int to string
func Date(ti int64, format string) string {
	t := time.Unix(int64(ti), 0)
	return DateT(t, format)
}

// format unix time string to string
func DateS(ts string, format string) string {
	i, _ := strconv.ParseInt(ts, 10, 64)
	return DateT(i, format)
}

// format time.Time to string
func DateT(t time.Time, format string) string {
	res := strings.Replace(format, "MM", t.Format("01"), -1)
	res = strings.Replace(res, "M", t.Format("1"), -1)
	res = strings.Replace(res, "DD", t.Format("02"), -1)
	res = strings.Replace(res, "D", t.Format("2"), -1)
	res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
	res = strings.Replace(res, "YY", t.Format("06"), -1)
	res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
	res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
	res = strings.Replace(res, "hh", t.Format("03"), -1)
	res = strings.Replace(res, "h", t.Format("3"), -1)
	res = strings.Replace(res, "mm", t.Format("04"), -1)
	res = strings.Replace(res, "m", t.Format("4"), -1)
	res = strings.Replace(res, "ss", t.Format("05"), -1)
	res = strings.Replace(res, "s", t.Format("5"), -1)
	return res
}

// get now unix timestamp int64
func Now() int64 {
	return time.Now().Unix()
}

<|endoftext|>


<commit_before>// Copyright 2013 com 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 com

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

// Format unix time int64 to string
func Date(ti int64, format string) string {
	t := time.Unix(int64(ti), 0)
	return DateT(t, format)
}

// Format unix time string to string
func DateS(ts string, format string) string {
	i, _ := strconv.ParseInt(ts, 10, 64)
	return Date(i, format)
}

// Format time.Time struct to string
// MM - month - 01
// M - month - 1, single bit
// DD - day - 02
// D - day 2
// YYYY - year - 2006
// YY - year - 06
// HH - 24 hours - 03
// H - 24 hours - 3
// hh - 12 hours - 03
// h - 12 hours - 3
// mm - minute - 04
// m - minute - 4
// ss - second - 05
// s - second = 5
func DateT(t time.Time, format string) string {
	res := strings.Replace(format, "MM", t.Format("01"), -1)
	res = strings.Replace(res, "M", t.Format("1"), -1)
	res = strings.Replace(res, "DD", t.Format("02"), -1)
	res = strings.Replace(res, "D", t.Format("2"), -1)
	res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
	res = strings.Replace(res, "YY", t.Format("06"), -1)
	res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
	res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
	res = strings.Replace(res, "hh", t.Format("03"), -1)
	res = strings.Replace(res, "h", t.Format("3"), -1)
	res = strings.Replace(res, "mm", t.Format("04"), -1)
	res = strings.Replace(res, "m", t.Format("4"), -1)
	res = strings.Replace(res, "ss", t.Format("05"), -1)
	res = strings.Replace(res, "s", t.Format("5"), -1)
	return res
}

// DateFormat pattern rules.
var datePatterns = []string{
	// year
	"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
	"y", "06", //A two digit representation of a year Examples: 99 or 03
	// month
	"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
	"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
	"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
	"F", "January", // A full textual representation of a month, such as January or March January through December
	// day
	"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
	"j", "2", // Day of the month without leading zeros 1 to 31
	// week
	"D", "Mon", // A textual representation of a day, three letters Mon through Sun
	"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
	// time
	"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
	"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
	"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
	"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
	"a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
	"A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
	"i", "04", // Minutes with leading zeros 00 to 59
	"s", "05", // Seconds, with leading zeros 00 through 59
	// time zone
	"T", "MST",
	"P", "-07:00",
	"O", "-0700",
	// RFC 2822
	"r", time.RFC1123Z,
}
var DateFormatReplacer = strings.NewReplacer(datePatterns...)

// Parse Date use PHP time format.
func DateParse(dateString, format string) (time.Time, error) {
	return time.ParseInLocation(ConvDateFormat(format), dateString, time.Local)
}

// Convert PHP time format.
func ConvDateFormat(format string) string {
	format = DateFormatReplacer.Replace(format)
	return format
}

//将时间戳格式化为日期字符窜
func DateFormat(format string, timestamp interface{}) (t string) { // timestamp
	switch format {
	case "Y-m-d H:i:s", "":
		format = "2006-01-02 15:04:05"
	case "Y-m-d H:i":
		format = "2006-01-02 15:04"
	case "y-m-d H:i":
		format = "06-01-02 15:04"
	case "m-d H:i":
		format = "01-02 15:04"
	case "Y-m-d":
		format = "2006-01-02"
	case "y-m-d":
		format = "06-01-02"
	case "m-d":
		format = "01-02"
	default:
		format = ConvDateFormat(format)
	}
	sd := Int64(timestamp)
	t = time.Unix(sd, 0).Format(format)
	return
}

//日期字符窜转为时间戳数字
func StrToTime(str string, args ...string) (unixtime int) {
	layout := "2006-01-02 15:04:05"
	if len(args) > 0 {
		layout = args[0]
	}
	t, err := time.Parse(layout, str)
	if err == nil {
		unixtime = int(t.Unix())
	} else {
		fmt.Println(err, str)
	}
	return
}

//格式化字节。 FormatByte(字节整数，保留小数位数)
func FormatByte(args ...interface{}) string {
	sizes := [...]string{"YB", "ZB", "EB", "PB", "TB", "GB", "MB", "KB", "B"}
	var (
		total     int     = len(sizes)
		size      float64 = 0
		precision int     = 0
	)
	ln := len(args)
	if ln > 0 {
		switch args[0].(type) {
		case float64:
			size = args[0].(float64)
		case float32:
			size = float64(args[0].(float32))
		case int64:
			size = float64(args[0].(int64))
		case int32:
			size = float64(args[0].(int32))
		case int:
			size = float64(args[0].(int))
		case uint64:
			size = float64(args[0].(uint64))
		case uint32:
			size = float64(args[0].(uint32))
		case uint:
			size = float64(args[0].(uint))
		case string:
			i, _ := strconv.Atoi(args[0].(string))
			size = float64(i)
		default:
			fmt.Printf("FormatByte error: first param (%#v) invalid.\n", args[0])
		}
	}
	if ln > 1 {
		switch args[1].(type) {
		case int:
			precision = args[1].(int)
		case int64:
			precision = int(args[1].(int64))
		case int32:
			precision = int(args[1].(int32))
		case uint:
			precision = int(args[1].(uint))
		case uint64:
			precision = int(args[1].(uint64))
		case uint32:
			precision = int(args[1].(uint32))
		default:
			fmt.Printf("FormatByte error: second param (%#v) invalid.\n", args[1])
		}
	}
	for total--; total > 0 && size > 1024.0; total-- {
		size /= 1024.0
	}
	return fmt.Sprintf("%.*f%s", precision, size, sizes[total])
}

//格式化耗时
func DateFormatShort(timestamp interface{}) string {
	now := time.Now()
	year := now.Year()
	month := now.Month()
	day := now.Day()
	cTime := StrToTime(fmt.Sprintf(`%d-%.2d-%.2d 00:00:00`, year, month, day)) //月、日始终保持两位
	timestamp2 := Int(timestamp)
	if cTime < timestamp2 {
		return DateFormat("15:04", timestamp)
	}
	cTime = StrToTime(fmt.Sprintf(`%d-01-01 00:00:00`, year))
	if cTime < timestamp2 {
		return DateFormat("01-02", timestamp)
	}
	return DateFormat("06-01-02", timestamp)
}

//格式化耗时
func FormatPastTime(timestamp interface{}, args ...string) string {
	duration := time.Now().Sub(time.Unix(Int64(timestamp), 0))
	if u := uint64(duration); u >= uint64(time.Hour)*24 {
		format := "Y-m-d H:i:s"
		if len(args) > 0 {
			format = args[0]
		}
		return DateFormat(format, timestamp)
	}
	return FriendlyTime(duration)
}

//对人类友好的经历时间格式
func FriendlyTime(d time.Duration, args ...string) (r string) {
	format := `Y-m-d H:i:s`
	shortt := ``
	switch len(args) {
	case 2:
		shortt = args[1]
		fallthrough
	case 1:
		if args[0] != `` {
			format = args[0]
		}
	}
	u := uint64(d)
	if u < uint64(time.Second) {
		switch {
		case u == 0:
			r = `0s`
		case u < uint64(time.Microsecond):
			r = fmt.Sprintf("%.2f%s", float64(u), `ns`) //纳秒
		case u < uint64(time.Millisecond):
			r = fmt.Sprintf("%.2f%s", float64(u)/1000, `us`) //微秒
		default:
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000, `ms`) //毫秒
		}
		r += shortt
	} else {
		switch {
		case u < uint64(time.Minute):
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000, `s`) + shortt //秒
		case u < uint64(time.Hour):
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000/60, `m`) + shortt //分钟
		case u < uint64(time.Hour)*24:
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000/60/60, `h`) + shortt //小时
		default:
			r = DateFormat(format, u)
		}
	}
	return
}

var StartTime time.Time = time.Now()

//总运行时长
func TotalRunTime() string {
	return FriendlyTime(time.Now().Sub(StartTime))
}
<commit_msg>update<commit_after>// Copyright 2013 com 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 com

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

//Date Format unix time int64 to string
func Date(ti int64, format string) string {
	t := time.Unix(int64(ti), 0)
	return DateT(t, format)
}

//DateS Format unix time string to string
func DateS(ts string, format string) string {
	i, _ := strconv.ParseInt(ts, 10, 64)
	return Date(i, format)
}

//DateT Format time.Time struct to string
// MM - month - 01
// M - month - 1, single bit
// DD - day - 02
// D - day 2
// YYYY - year - 2006
// YY - year - 06
// HH - 24 hours - 03
// H - 24 hours - 3
// hh - 12 hours - 03
// h - 12 hours - 3
// mm - minute - 04
// m - minute - 4
// ss - second - 05
// s - second = 5
func DateT(t time.Time, format string) string {
	res := strings.Replace(format, "MM", t.Format("01"), -1)
	res = strings.Replace(res, "M", t.Format("1"), -1)
	res = strings.Replace(res, "DD", t.Format("02"), -1)
	res = strings.Replace(res, "D", t.Format("2"), -1)
	res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
	res = strings.Replace(res, "YY", t.Format("06"), -1)
	res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
	res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
	res = strings.Replace(res, "hh", t.Format("03"), -1)
	res = strings.Replace(res, "h", t.Format("3"), -1)
	res = strings.Replace(res, "mm", t.Format("04"), -1)
	res = strings.Replace(res, "m", t.Format("4"), -1)
	res = strings.Replace(res, "ss", t.Format("05"), -1)
	res = strings.Replace(res, "s", t.Format("5"), -1)
	return res
}

// DateFormat pattern rules.
var datePatterns = []string{
	// year
	"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
	"y", "06", //A two digit representation of a year Examples: 99 or 03
	// month
	"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
	"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
	"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
	"F", "January", // A full textual representation of a month, such as January or March January through December
	// day
	"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
	"j", "2", // Day of the month without leading zeros 1 to 31
	// week
	"D", "Mon", // A textual representation of a day, three letters Mon through Sun
	"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
	// time
	"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
	"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
	"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
	"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
	"a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
	"A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
	"i", "04", // Minutes with leading zeros 00 to 59
	"s", "05", // Seconds, with leading zeros 00 through 59
	// time zone
	"T", "MST",
	"P", "-07:00",
	"O", "-0700",
	// RFC 2822
	"r", time.RFC1123Z,
}
var DateFormatReplacer = strings.NewReplacer(datePatterns...)

//DateParse Parse Date use PHP time format.
func DateParse(dateString, format string) (time.Time, error) {
	return time.ParseInLocation(ConvDateFormat(format), dateString, time.Local)
}

//ConvDateFormat Convert PHP time format.
func ConvDateFormat(format string) string {
	format = DateFormatReplacer.Replace(format)
	return format
}

//DateFormat 将时间戳格式化为日期字符窜
func DateFormat(format string, timestamp interface{}) (t string) { // timestamp
	switch format {
	case "Y-m-d H:i:s", "":
		format = "2006-01-02 15:04:05"
	case "Y-m-d H:i":
		format = "2006-01-02 15:04"
	case "y-m-d H:i":
		format = "06-01-02 15:04"
	case "m-d H:i":
		format = "01-02 15:04"
	case "Y-m-d":
		format = "2006-01-02"
	case "y-m-d":
		format = "06-01-02"
	case "m-d":
		format = "01-02"
	default:
		format = ConvDateFormat(format)
	}
	sd := Int64(timestamp)
	t = time.Unix(sd, 0).Format(format)
	return
}

//StrToTime 日期字符窜转为时间戳数字
func StrToTime(str string, args ...string) (unixtime int) {
	layout := "2006-01-02 15:04:05"
	if len(args) > 0 {
		layout = args[0]
	}
	t, err := time.Parse(layout, str)
	if err == nil {
		unixtime = int(t.Unix())
	} else {
		fmt.Println(err, str)
	}
	return
}

//FormatByte 格式化字节。 FormatByte(字节整数，保留小数位数)
func FormatByte(args ...interface{}) string {
	sizes := [...]string{"YB", "ZB", "EB", "PB", "TB", "GB", "MB", "KB", "B"}
	var (
		total     = len(sizes)
		size      float64
		precision int
	)
	ln := len(args)
	if ln > 0 {
		switch v := args[0].(type) {
		case float64:
			size = v
		case float32:
			size = float64(v)
		case int64:
			size = float64(v)
		case int32:
			size = float64(v)
		case int:
			size = float64(v)
		case uint64:
			size = float64(v)
		case uint32:
			size = float64(v)
		case uint:
			size = float64(v)
		case string:
			i, _ := strconv.Atoi(v)
			size = float64(i)
		default:
			fmt.Printf("FormatByte error: first param (%#v) invalid.\n", args[0])
		}
	}
	if ln > 1 {
		switch v := args[1].(type) {
		case int:
			precision = v
		case int64:
			precision = int(v)
		case int32:
			precision = int(v)
		case uint:
			precision = int(v)
		case uint64:
			precision = int(v)
		case uint32:
			precision = int(v)
		default:
			fmt.Printf("FormatByte error: second param (%#v) invalid.\n", args[1])
		}
	}
	for total--; total > 0 && size > 1024.0; total-- {
		size /= 1024.0
	}
	return fmt.Sprintf("%.*f%s", precision, size, sizes[total])
}

//DateFormatShort 格式化耗时
func DateFormatShort(timestamp interface{}) string {
	now := time.Now()
	year := now.Year()
	month := now.Month()
	day := now.Day()
	cTime := StrToTime(fmt.Sprintf(`%d-%.2d-%.2d 00:00:00`, year, month, day)) //月、日始终保持两位
	timestamp2 := Int(timestamp)
	if cTime < timestamp2 {
		return DateFormat("15:04", timestamp)
	}
	cTime = StrToTime(fmt.Sprintf(`%d-01-01 00:00:00`, year))
	if cTime < timestamp2 {
		return DateFormat("01-02", timestamp)
	}
	return DateFormat("06-01-02", timestamp)
}

//FormatPastTime 格式化耗时
func FormatPastTime(timestamp interface{}, args ...string) string {
	duration := time.Now().Sub(time.Unix(Int64(timestamp), 0))
	if u := uint64(duration); u >= uint64(time.Hour)*24 {
		format := "Y-m-d H:i:s"
		if len(args) > 0 {
			format = args[0]
		}
		return DateFormat(format, timestamp)
	}
	return FriendlyTime(duration)
}

//FriendlyTime 对人类友好的经历时间格式
func FriendlyTime(d time.Duration, args ...string) (r string) {
	var suffix string
	switch len(args) {
	case 1:
		suffix = args[0]
	}
	u := uint64(d)
	if u < uint64(time.Second) {
		switch {
		case u == 0:
			r = `0s`
		case u < uint64(time.Microsecond):
			r = fmt.Sprintf("%.2f%s", float64(u), `ns`) //纳秒
		case u < uint64(time.Millisecond):
			r = fmt.Sprintf("%.2f%s", float64(u)/1000, `us`) //微秒
		default:
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000, `ms`) //毫秒
		}
	} else {
		switch {
		case u < uint64(time.Minute):
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000, `s`) //秒
		case u < uint64(time.Hour):
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000/60, `m`) //分钟
		case u < uint64(time.Hour)*24:
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000/60/60, `h`) //小时
		case u < uint64(time.Hour)*24*7:
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000/60/60/24, `d`) //天
		default:
			r = fmt.Sprintf("%.2f%s", float64(u)/1000/1000/1000/60/60/24/7, `w`) //周
		}
	}
	r += suffix
	return
}

//StartTime 开始时间
var StartTime = time.Now()

//TotalRunTime 总运行时长
func TotalRunTime() string {
	return FriendlyTime(time.Now().Sub(StartTime))
}
<|endoftext|>


<commit_before>package dns

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/x509"
	"encoding/hex"
	"io"
	"net"
	"strconv"
)

// TLSA support functions

// CertificateToDANE converts a certificate to a hex string as used in the TLSA record.
func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) string {
	switch matchingType {
	case 0:
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			return hex.EncodeToString(cert.RawSubjectPublicKeyInfo)
		}
	case 1:
		h := sha256.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	case 2:
		h := sha512.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	}
	return ""
}

// Sign creates a TLSA record from a SSL certificate.
func (r *RR_TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) error {
	r.Hdr.Rrtype = TypeTLSA
	r.Usage = uint8(usage)
	r.Selector = uint8(selector)
	r.MatchingType = uint8(matchingType)

	r.Certificate = CertificateToDANE(r.Selector, r.MatchingType, cert)
	return nil
}

// Verify verifies a TLSA record against a SSL certificate. If it is OK
// a nil error is returned.
func (r *RR_TLSA) Verify(cert *x509.Certificate) error {
	if r.Certificate == CertificateToDANE(r.Selector, r.MatchingType, cert) {
		return nil
	}
	return ErrSig	// ErrSig, really?
}

// TLSAName returns the ownername of a TLSA resource record as per the
// rules specified in RFC 6698, Section 3. When an erros occurs the
// empty string is returned.
func TLSAName(name, service, network string) string {
	if !IsFqdn(name) {
		return ""
	}
	p, e := net.LookupPort(network, service)
	if e != nil {
		return ""
	}
	return "_" + strconv.Itoa(p) + "_" + network + "." + name
}
<commit_msg>documentation<commit_after>package dns

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/x509"
	"encoding/hex"
	"io"
	"net"
	"strconv"
)

// CertificateToDANE converts a certificate to a hex string as used in the TLSA record.
func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) string {
	switch matchingType {
	case 0:
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			return hex.EncodeToString(cert.RawSubjectPublicKeyInfo)
		}
	case 1:
		h := sha256.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	case 2:
		h := sha512.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	}
	return ""
}

// Sign creates a TLSA record from an SSL certificate.
func (r *RR_TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) error {
	r.Hdr.Rrtype = TypeTLSA
	r.Usage = uint8(usage)
	r.Selector = uint8(selector)
	r.MatchingType = uint8(matchingType)

	r.Certificate = CertificateToDANE(r.Selector, r.MatchingType, cert)
	return nil
}

// Verify verifies a TLSA record against an SSL certificate. If it is OK
// a nil error is returned.
func (r *RR_TLSA) Verify(cert *x509.Certificate) error {
	if r.Certificate == CertificateToDANE(r.Selector, r.MatchingType, cert) {
		return nil
	}
	return ErrSig	// ErrSig, really?
}

// TLSAName returns the ownername of a TLSA resource record as per the
// rules specified in RFC 6698, Section 3. When an error occurs the
// empty string is returned.
func TLSAName(name, service, network string) string {
	if !IsFqdn(name) {
		return ""
	}
	p, e := net.LookupPort(network, service)
	if e != nil {
		return ""
	}
	return "_" + strconv.Itoa(p) + "_" + network + "." + name
}
<|endoftext|>