<commit_before>package sudoku

import (
	"fmt"
	"math/rand"
)

type SolveDirections []*SolveStep

const (
	NAKED_SINGLE = iota
	HIDDEN_SINGLE_IN_ROW
	HIDDEN_SINGLE_IN_COL
	HIDDEN_SINGLE_IN_BLOCK
)

type SolveStep struct {
	TargetCells  CellList
	PointerCells CellList
	Nums         []int
	Technique    SolveTechnique
}

type SolveTechnique interface {
	Name() string
	Description(*SolveStep) string
	Find(*Grid) *SolveStep
	IsFill() bool
}

type fillSolveTechnique struct {
}

type cullSolveTechnique struct {
}

var fillTechniques []SolveTechnique
var cullTechniques []SolveTechnique

func init() {
	//TODO: init techniques with enough space
	fillTechniques = append(fillTechniques, nakedSingleTechnique{})
	fillTechniques = append(fillTechniques, hiddenSingleInRow{})
	fillTechniques = append(fillTechniques, hiddenSingleInCol{})
	fillTechniques = append(fillTechniques, hiddenSingleInBlock{})
	cullTechniques = append(cullTechniques, pointingPair{})
}

type nakedSingleTechnique struct {
	*fillSolveTechnique
}

type hiddenSingleInRow struct {
	*fillSolveTechnique
}

type hiddenSingleInCol struct {
	*fillSolveTechnique
}

type hiddenSingleInBlock struct {
	*fillSolveTechnique
}

type pointingPair struct {
	*cullSolveTechnique
}

func (self *fillSolveTechnique) IsFill() bool {
	return true
}

func (self *cullSolveTechnique) IsFill() bool {
	return false
}

func newFillSolveStep(cell *Cell, num int, technique SolveTechnique) *SolveStep {
	//TODO: why do these need to be pulled out separately?
	cellArr := [...]*Cell{cell}
	numArr := [...]int{num}
	return &SolveStep{cellArr[:], nil, numArr[:], technique}
}

func (self *SolveStep) Apply(grid *Grid) {
	//TODO: also handle non isFill items.
	if self.Technique.IsFill() {
		if len(self.TargetCells) == 0 || len(self.Nums) == 0 {
			return
		}
		cell := self.TargetCells[0].InGrid(grid)
		cell.SetNumber(self.Nums[0])
	}
}

func (self nakedSingleTechnique) Name() string {
	return "Only Legal Number"
}

func (self nakedSingleTechnique) Description(step *SolveStep) string {
	if len(step.Nums) == 0 {
		return ""
	}
	num := step.Nums[0]
	return fmt.Sprintf("%d is the only remaining valid number for that cell", num)
}

func (self nakedSingleTechnique) Find(grid *Grid) *SolveStep {
	//This will be a random item
	obj := grid.queue.NewGetter().GetSmallerThan(2)
	if obj == nil {
		//There weren't any cells with one option.
		return nil
	}
	cell := obj.(*Cell)
	return newFillSolveStep(cell, cell.implicitNumber(), self)
}

func (self hiddenSingleInRow) Name() string {
	return "Necessary In Row"
}

func (self hiddenSingleInRow) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.Nums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.Nums[0]
	return fmt.Sprintf("%d is required in the %d row, and %d is the only column it fits", num, cell.Row+1, cell.Col+1)
}

func (self hiddenSingleInRow) Find(grid *Grid) *SolveStep {
	getter := func(index int) []*Cell {
		return grid.Row(index)
	}
	return necessaryInCollection(grid, self, getter)
}

func (self hiddenSingleInCol) Name() string {
	return "Necessary In Col"
}

func (self hiddenSingleInCol) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.Nums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.Nums[0]
	return fmt.Sprintf("%d is required in the %d column, and %d is the only row it fits", num, cell.Row+1, cell.Col+1)
}

func (self hiddenSingleInCol) Find(grid *Grid) *SolveStep {
	getter := func(index int) []*Cell {
		return grid.Col(index)
	}
	return necessaryInCollection(grid, self, getter)
}

func (self hiddenSingleInBlock) Name() string {
	return "Necessary In Block"
}

func (self hiddenSingleInBlock) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.Nums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.Nums[0]
	return fmt.Sprintf("%d is required in the %d block, and %d, %d is the only cell it fits", num, cell.Block+1, cell.Row+1, cell.Col+1)
}

func (self hiddenSingleInBlock) Find(grid *Grid) *SolveStep {
	getter := func(index int) []*Cell {
		return grid.Block(index)
	}
	return necessaryInCollection(grid, self, getter)
}

func necessaryInCollection(grid *Grid, technique SolveTechnique, collectionGetter func(index int) []*Cell) *SolveStep {
	//This will be a random item
	indexes := rand.Perm(DIM)

	for _, i := range indexes {
		seenInCollection := make([]int, DIM)
		collection := collectionGetter(i)
		for _, cell := range collection {
			for _, possibility := range cell.Possibilities() {
				seenInCollection[possibility-1]++
			}
		}
		seenIndexes := rand.Perm(DIM)
		for _, index := range seenIndexes {
			seen := seenInCollection[index]
			if seen == 1 {
				//Okay, we know our target number. Which cell was it?
				for _, cell := range collection {
					if cell.Possible(index + 1) {
						//Found it!
						return newFillSolveStep(cell, index+1, technique)
					}
				}
			}
		}
	}
	//Nope.
	return nil
}

func (self pointingPair) Name() string {
	return "Pointing pair"
}

func (self pointingPair) Description(step *SolveStep) string {
	//TODO: implement this
	return ""
}

func (self pointingPair) Find(grid *Grid) *SolveStep {
	//Within each block, for each number, see if all items that allow it are aligned in a row or column.
	//TODO: randomize order of blocks.
	//TODO: should we create a FilterByLegal method on lists of cells?
	//TODO: finish this!
	for i := 0; i < DIM; i++ {
		block := grid.Block(i)
		//TODO: randomize order of numbers to test for.
		for num := 0; num < DIM; num++ {
			cells := block.FilterByPossible(num + 1)
			//cellList is now a list of all cells that have that number.
			if len(cells) == 0 || len(cells) > BLOCK_DIM {
				//Meh, not a match.
				continue
			}
			//Okay, it's possible it's a match. Are all rows the same?
			if cells.SameRow() {
				//Yup!
				return &SolveStep{grid.Row(cells.Row()).RemoveCells(cells), cells, []int{num + 1}, self}
			}
			//Okay, are all cols?
			if cells.SameCol() {
				//Yup!
				return &SolveStep{grid.Col(cells.Col()).RemoveCells(cells), cells, []int{num + 1}, self}
			}
		}
	}
	return nil
}

func (self *Grid) HumanSolve() SolveDirections {
	var results []*SolveStep
	for !self.Solved() {
		//TODO: try the techniques in parallel
		//TODO: pick the technique based on a weighting of how common a human is to pick each one.
		//TODO: provide hints to the techniques of where to look based on the last filled cell
		//TODO: if no fill techniques work, use a culltechnique.
		techniqueOrder := rand.Perm(len(fillTechniques))
		for _, index := range techniqueOrder {
			technique := fillTechniques[index]
			step := technique.Find(self)
			if step != nil {
				results = append(results, step)
				step.Apply(self)
				break
			}
		}
	}
	if !self.Solved() {
		//We couldn't solve the puzzle.
		return nil
	}
	return results
}
<commit_msg>solveStep.Apply() should work for cull steps, too (not tested).<commit_after>package sudoku

import (
	"fmt"
	"math/rand"
)

type SolveDirections []*SolveStep

const (
	NAKED_SINGLE = iota
	HIDDEN_SINGLE_IN_ROW
	HIDDEN_SINGLE_IN_COL
	HIDDEN_SINGLE_IN_BLOCK
)

type SolveStep struct {
	TargetCells  CellList
	PointerCells CellList
	Nums         []int
	Technique    SolveTechnique
}

type SolveTechnique interface {
	Name() string
	Description(*SolveStep) string
	Find(*Grid) *SolveStep
	IsFill() bool
}

type fillSolveTechnique struct {
}

type cullSolveTechnique struct {
}

var fillTechniques []SolveTechnique
var cullTechniques []SolveTechnique

func init() {
	//TODO: init techniques with enough space
	fillTechniques = append(fillTechniques, nakedSingleTechnique{})
	fillTechniques = append(fillTechniques, hiddenSingleInRow{})
	fillTechniques = append(fillTechniques, hiddenSingleInCol{})
	fillTechniques = append(fillTechniques, hiddenSingleInBlock{})
	cullTechniques = append(cullTechniques, pointingPair{})
}

type nakedSingleTechnique struct {
	*fillSolveTechnique
}

type hiddenSingleInRow struct {
	*fillSolveTechnique
}

type hiddenSingleInCol struct {
	*fillSolveTechnique
}

type hiddenSingleInBlock struct {
	*fillSolveTechnique
}

type pointingPair struct {
	*cullSolveTechnique
}

func (self *fillSolveTechnique) IsFill() bool {
	return true
}

func (self *cullSolveTechnique) IsFill() bool {
	return false
}

func newFillSolveStep(cell *Cell, num int, technique SolveTechnique) *SolveStep {
	//TODO: why do these need to be pulled out separately?
	cellArr := [...]*Cell{cell}
	numArr := [...]int{num}
	return &SolveStep{cellArr[:], nil, numArr[:], technique}
}

func (self *SolveStep) Apply(grid *Grid) {
	if self.Technique.IsFill() {
		if len(self.TargetCells) == 0 || len(self.Nums) == 0 {
			return
		}
		cell := self.TargetCells[0].InGrid(grid)
		cell.SetNumber(self.Nums[0])
	} else {
		for _, cell := range self.TargetCells {
			gridCell := cell.InGrid(grid)
			for _, exclude := range self.Nums {
				gridCell.setExcluded(exclude, true)
			}
		}
	}
}

func (self nakedSingleTechnique) Name() string {
	return "Only Legal Number"
}

func (self nakedSingleTechnique) Description(step *SolveStep) string {
	if len(step.Nums) == 0 {
		return ""
	}
	num := step.Nums[0]
	return fmt.Sprintf("%d is the only remaining valid number for that cell", num)
}

func (self nakedSingleTechnique) Find(grid *Grid) *SolveStep {
	//This will be a random item
	obj := grid.queue.NewGetter().GetSmallerThan(2)
	if obj == nil {
		//There weren't any cells with one option.
		return nil
	}
	cell := obj.(*Cell)
	return newFillSolveStep(cell, cell.implicitNumber(), self)
}

func (self hiddenSingleInRow) Name() string {
	return "Necessary In Row"
}

func (self hiddenSingleInRow) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.Nums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.Nums[0]
	return fmt.Sprintf("%d is required in the %d row, and %d is the only column it fits", num, cell.Row+1, cell.Col+1)
}

func (self hiddenSingleInRow) Find(grid *Grid) *SolveStep {
	getter := func(index int) []*Cell {
		return grid.Row(index)
	}
	return necessaryInCollection(grid, self, getter)
}

func (self hiddenSingleInCol) Name() string {
	return "Necessary In Col"
}

func (self hiddenSingleInCol) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.Nums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.Nums[0]
	return fmt.Sprintf("%d is required in the %d column, and %d is the only row it fits", num, cell.Row+1, cell.Col+1)
}

func (self hiddenSingleInCol) Find(grid *Grid) *SolveStep {
	getter := func(index int) []*Cell {
		return grid.Col(index)
	}
	return necessaryInCollection(grid, self, getter)
}

func (self hiddenSingleInBlock) Name() string {
	return "Necessary In Block"
}

func (self hiddenSingleInBlock) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.Nums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.Nums[0]
	return fmt.Sprintf("%d is required in the %d block, and %d, %d is the only cell it fits", num, cell.Block+1, cell.Row+1, cell.Col+1)
}

func (self hiddenSingleInBlock) Find(grid *Grid) *SolveStep {
	getter := func(index int) []*Cell {
		return grid.Block(index)
	}
	return necessaryInCollection(grid, self, getter)
}

func necessaryInCollection(grid *Grid, technique SolveTechnique, collectionGetter func(index int) []*Cell) *SolveStep {
	//This will be a random item
	indexes := rand.Perm(DIM)

	for _, i := range indexes {
		seenInCollection := make([]int, DIM)
		collection := collectionGetter(i)
		for _, cell := range collection {
			for _, possibility := range cell.Possibilities() {
				seenInCollection[possibility-1]++
			}
		}
		seenIndexes := rand.Perm(DIM)
		for _, index := range seenIndexes {
			seen := seenInCollection[index]
			if seen == 1 {
				//Okay, we know our target number. Which cell was it?
				for _, cell := range collection {
					if cell.Possible(index + 1) {
						//Found it!
						return newFillSolveStep(cell, index+1, technique)
					}
				}
			}
		}
	}
	//Nope.
	return nil
}

func (self pointingPair) Name() string {
	return "Pointing pair"
}

func (self pointingPair) Description(step *SolveStep) string {
	//TODO: implement this
	return ""
}

func (self pointingPair) Find(grid *Grid) *SolveStep {
	//Within each block, for each number, see if all items that allow it are aligned in a row or column.
	//TODO: randomize order of blocks.
	//TODO: should we create a FilterByLegal method on lists of cells?
	//TODO: finish this!
	for i := 0; i < DIM; i++ {
		block := grid.Block(i)
		//TODO: randomize order of numbers to test for.
		for num := 0; num < DIM; num++ {
			cells := block.FilterByPossible(num + 1)
			//cellList is now a list of all cells that have that number.
			if len(cells) == 0 || len(cells) > BLOCK_DIM {
				//Meh, not a match.
				continue
			}
			//Okay, it's possible it's a match. Are all rows the same?
			if cells.SameRow() {
				//Yup!
				return &SolveStep{grid.Row(cells.Row()).RemoveCells(cells), cells, []int{num + 1}, self}
			}
			//Okay, are all cols?
			if cells.SameCol() {
				//Yup!
				return &SolveStep{grid.Col(cells.Col()).RemoveCells(cells), cells, []int{num + 1}, self}
			}
		}
	}
	return nil
}

func (self *Grid) HumanSolve() SolveDirections {
	var results []*SolveStep
	for !self.Solved() {
		//TODO: try the techniques in parallel
		//TODO: pick the technique based on a weighting of how common a human is to pick each one.
		//TODO: provide hints to the techniques of where to look based on the last filled cell
		//TODO: if no fill techniques work, use a culltechnique.
		techniqueOrder := rand.Perm(len(fillTechniques))
		for _, index := range techniqueOrder {
			technique := fillTechniques[index]
			step := technique.Find(self)
			if step != nil {
				results = append(results, step)
				step.Apply(self)
				break
			}
		}
	}
	if !self.Solved() {
		//We couldn't solve the puzzle.
		return nil
	}
	return results
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2017 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cloud

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"time"

	"github.com/pkg/errors"
)

const (
	// RequestTimeout is the default cloud request timeout
	RequestTimeout = 20 * time.Second
	// RetryInterval is the default cloud request retry interval
	RetryInterval = 500 * time.Millisecond
	// MaxRetries specifies max retry attempts
	MaxRetries = 3

	k6IdempotencyKeyHeader = "k6-Idempotency-Key"
)

// Client handles communication with Load Impact cloud API.
type Client struct {
	client  *http.Client
	token   string
	baseURL string
	version string

	retries       int
	retryInterval time.Duration
}

func NewClient(token, host, version string) *Client {
	c := &Client{
		client:        &http.Client{Timeout: RequestTimeout},
		token:         token,
		baseURL:       fmt.Sprintf("%s/v1", host),
		version:       version,
		retries:       MaxRetries,
		retryInterval: RetryInterval,
	}
	return c
}

func (c *Client) NewRequest(method, url string, data interface{}) (*http.Request, error) {
	var buf io.Reader

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

		buf = bytes.NewBuffer(b)
	}

	req, err := http.NewRequest(method, url, buf)
	if err != nil {
		return nil, err
	}

	c.prepareHeaders(req)

	return req, nil
}

func (c *Client) Do(req *http.Request, v interface{}) error {
	var originalBody []byte
	var err error

	if req.Body != nil {
		originalBody, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return err
		}

		if cerr := req.Body.Close(); cerr != nil {
			err = cerr
		}
	}

	for i := 1; i <= c.retries; i++ {
		if len(originalBody) > 0 {
			req.Body = ioutil.NopCloser(bytes.NewBuffer(originalBody))
		}

		retry, err := c.do(req, v, i)

		if retry {
			time.Sleep(c.retryInterval)
			continue
		}

		return err
	}

	return err
}

func (c *Client) prepareHeaders(req *http.Request) {
	if req.Header.Get("Content-Type") == "" {
		req.Header.Set("Content-Type", "application/json")
	}

	if c.token != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Token %s", c.token))
	}

	if shouldAddIdempotencyKey(req) {
		req.Header.Set(k6IdempotencyKeyHeader, randomStrHex())
	}

	req.Header.Set("User-Agent", "k6cloud/"+c.version)
}

func (c *Client) do(req *http.Request, v interface{}, attempt int) (retry bool, err error) {
	resp, err := c.client.Do(req)

	defer func() {
		if resp != nil {
			if cerr := resp.Body.Close(); cerr != nil && err == nil {
				err = cerr
			}
		}
	}()

	if shouldRetry(resp, err, attempt, c.retries) {
		return true, err
	}

	if err != nil {
		return false, err
	}

	if err = checkResponse(resp); err != nil {
		return false, err
	}

	if v != nil {
		if err = json.NewDecoder(resp.Body).Decode(v); err == io.EOF {
			err = nil // Ignore EOF from empty body
		}
	}

	return false, err
}

func checkResponse(r *http.Response) error {
	if r == nil {
		return ErrUnknown
	}

	if c := r.StatusCode; c >= 200 && c <= 299 {
		return nil
	}

	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	var payload struct {
		Error ErrorResponse `json:"error"`
	}
	if err := json.Unmarshal(data, &payload); err != nil {
		if r.StatusCode == http.StatusUnauthorized {
			return ErrNotAuthenticated
		}
		if r.StatusCode == http.StatusForbidden {
			return ErrNotAuthorized
		}
		return errors.Errorf(
			"Unexpected HTTP error from %s: %d %s",
			r.Request.URL,
			r.StatusCode,
			http.StatusText(r.StatusCode),
		)
	}
	payload.Error.Response = r
	return payload.Error
}

func shouldRetry(resp *http.Response, err error, attempt, maxAttempts int) bool {
	if attempt >= maxAttempts {
		return false
	}

	if resp == nil || err != nil {
		return true
	}

	if resp.StatusCode >= 500 || resp.StatusCode == 429 {
		return true
	}

	return false
}

func shouldAddIdempotencyKey(req *http.Request) bool {
	switch req.Method {
	case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
		return false
	default:
		return req.Header.Get(k6IdempotencyKeyHeader) == ""
	}
}

// randomStrHex returns a hex string which can be used
// for session token id or idempotency key.
//nolint:gosec
func randomStrHex() string {
	// 16 hex characters
	b := make([]byte, 8)
	_, _ = rand.Read(b)
	return hex.EncodeToString(b)
}

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}
<commit_msg>stats/cloud: move prepareHeaders inside Do<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2017 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cloud

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"time"

	"github.com/pkg/errors"
)

const (
	// RequestTimeout is the default cloud request timeout
	RequestTimeout = 20 * time.Second
	// RetryInterval is the default cloud request retry interval
	RetryInterval = 500 * time.Millisecond
	// MaxRetries specifies max retry attempts
	MaxRetries = 3

	k6IdempotencyKeyHeader = "k6-Idempotency-Key"
)

// Client handles communication with Load Impact cloud API.
type Client struct {
	client  *http.Client
	token   string
	baseURL string
	version string

	retries       int
	retryInterval time.Duration
}

func NewClient(token, host, version string) *Client {
	c := &Client{
		client:        &http.Client{Timeout: RequestTimeout},
		token:         token,
		baseURL:       fmt.Sprintf("%s/v1", host),
		version:       version,
		retries:       MaxRetries,
		retryInterval: RetryInterval,
	}
	return c
}

func (c *Client) NewRequest(method, url string, data interface{}) (*http.Request, error) {
	var buf io.Reader

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

		buf = bytes.NewBuffer(b)
	}

	req, err := http.NewRequest(method, url, buf)
	if err != nil {
		return nil, err
	}

	return req, nil
}

func (c *Client) Do(req *http.Request, v interface{}) error {
	var originalBody []byte
	var err error

	if req.Body != nil {
		originalBody, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return err
		}

		if cerr := req.Body.Close(); cerr != nil {
			err = cerr
		}
	}

	// TODO(cuonglm): finding away to move this back to NewRequest
	c.prepareHeaders(req)

	for i := 1; i <= c.retries; i++ {
		if len(originalBody) > 0 {
			req.Body = ioutil.NopCloser(bytes.NewBuffer(originalBody))
		}

		retry, err := c.do(req, v, i)

		if retry {
			time.Sleep(c.retryInterval)
			continue
		}

		return err
	}

	return err
}

func (c *Client) prepareHeaders(req *http.Request) {
	if req.Header.Get("Content-Type") == "" {
		req.Header.Set("Content-Type", "application/json")
	}

	if c.token != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Token %s", c.token))
	}

	if shouldAddIdempotencyKey(req) {
		req.Header.Set(k6IdempotencyKeyHeader, randomStrHex())
	}

	req.Header.Set("User-Agent", "k6cloud/"+c.version)
}

func (c *Client) do(req *http.Request, v interface{}, attempt int) (retry bool, err error) {
	resp, err := c.client.Do(req)

	defer func() {
		if resp != nil {
			if cerr := resp.Body.Close(); cerr != nil && err == nil {
				err = cerr
			}
		}
	}()

	if shouldRetry(resp, err, attempt, c.retries) {
		return true, err
	}

	if err != nil {
		return false, err
	}

	if err = checkResponse(resp); err != nil {
		return false, err
	}

	if v != nil {
		if err = json.NewDecoder(resp.Body).Decode(v); err == io.EOF {
			err = nil // Ignore EOF from empty body
		}
	}

	return false, err
}

func checkResponse(r *http.Response) error {
	if r == nil {
		return ErrUnknown
	}

	if c := r.StatusCode; c >= 200 && c <= 299 {
		return nil
	}

	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	var payload struct {
		Error ErrorResponse `json:"error"`
	}
	if err := json.Unmarshal(data, &payload); err != nil {
		if r.StatusCode == http.StatusUnauthorized {
			return ErrNotAuthenticated
		}
		if r.StatusCode == http.StatusForbidden {
			return ErrNotAuthorized
		}
		return errors.Errorf(
			"Unexpected HTTP error from %s: %d %s",
			r.Request.URL,
			r.StatusCode,
			http.StatusText(r.StatusCode),
		)
	}
	payload.Error.Response = r
	return payload.Error
}

func shouldRetry(resp *http.Response, err error, attempt, maxAttempts int) bool {
	if attempt >= maxAttempts {
		return false
	}

	if resp == nil || err != nil {
		return true
	}

	if resp.StatusCode >= 500 || resp.StatusCode == 429 {
		return true
	}

	return false
}

func shouldAddIdempotencyKey(req *http.Request) bool {
	switch req.Method {
	case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
		return false
	default:
		return req.Header.Get(k6IdempotencyKeyHeader) == ""
	}
}

// randomStrHex returns a hex string which can be used
// for session token id or idempotency key.
//nolint:gosec
func randomStrHex() string {
	// 16 hex characters
	b := make([]byte, 8)
	_, _ = rand.Read(b)
	return hex.EncodeToString(b)
}

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}
<|endoftext|>


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

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file implements the symbol table.

package golisp

import (
	"container/list"
	"errors"
	"fmt"
	"strings"
	"sync"
	"sync/atomic"
)

const (
	READ_LOCK = iota
	WRITE_LOCK
)

type SymbolTableFrame struct {
	Name         string
	Parent       *SymbolTableFrame
	Previous     *SymbolTableFrame
	Frame        *FrameMap
	Bindings     map[string]*Binding
	CurrentCode  *list.List
	IsRestricted bool
}

type symbolsTable struct {
	Symbols map[string]*Data
	Mutex   sync.RWMutex
}

type environmentsTable struct {
	Environments map[string]*SymbolTableFrame
	Mutex        sync.RWMutex
}

var Global *SymbolTableFrame
var TopLevelEnvironments environmentsTable = environmentsTable{make(map[string]*SymbolTableFrame, 5), sync.RWMutex{}}

var internedSymbols symbolsTable = symbolsTable{make(map[string]*Data, 256), sync.RWMutex{}}

func Intern(name string) (sym *Data) {
	// Naked symbols do not need to be added to the symbol table
	if strings.HasSuffix(name, ":") {
		return SymbolWithName(name)
	}

	internedSymbols.Mutex.RLock()
	lock := READ_LOCK
	defer func() {
		if lock == READ_LOCK {
			internedSymbols.Mutex.RUnlock()
		} else {
			internedSymbols.Mutex.Unlock()
		}
	}()

	sym = internedSymbols.Symbols[name]
	if sym == nil {
		internedSymbols.Mutex.RUnlock()
		internedSymbols.Mutex.Lock()
		lock = WRITE_LOCK
		sym = SymbolWithName(name)
		internedSymbols.Symbols[name] = sym
	}
	return
}

func (self *SymbolTableFrame) Depth() int {
	if self.Previous == nil {
		return 1
	} else {
		return 1 + self.Previous.Depth()
	}
}

func (self *SymbolTableFrame) CurrentCodeString() string {
	if self.CurrentCode.Len() > 0 {
		return self.CurrentCode.Front().Value.(string)
	} else {
		return "Unknown code"
	}
}

func (self *SymbolTableFrame) InternalDump(frameNumber int) {
	fmt.Printf("Frame %d: %s\n", frameNumber, self.CurrentCodeString())
	for _, b := range self.Bindings {
		if b.Val == nil || TypeOf(b.Val) != PrimitiveType {
			b.Dump()
		}
	}
	fmt.Printf("\n")
	if self.Previous != nil {
		self.Previous.InternalDump(frameNumber + 1)
	}
}

func (self *SymbolTableFrame) Dump() {
	println()
	self.InternalDump(0)
}

func (self *SymbolTableFrame) DumpSingleFrame(frameNumber int) {
	if frameNumber == 0 {
		fmt.Printf("%s\n", self.CurrentCodeString())
		for _, b := range self.Bindings {
			if b.Val == nil || TypeOf(b.Val) != PrimitiveType {
				b.Dump()
			}
		}
		fmt.Printf("\n")
	} else if self.Previous != nil {
		self.Previous.DumpSingleFrame(frameNumber - 1)
	} else {
		fmt.Printf("Invalid frame selected.\n")
	}
}

func (self *SymbolTableFrame) InternalDumpHeaders(frameNumber int) {
	fmt.Printf("Frame %d: %s\n", frameNumber, self.CurrentCodeString())
	if self.Previous != nil {
		self.Previous.InternalDumpHeaders(frameNumber + 1)
	}
}

func (self *SymbolTableFrame) DumpHeaders() {
	println()
	self.InternalDumpHeaders(0)
}

func (self *SymbolTableFrame) DumpHeader() {
	fmt.Printf("%s\n", self.CurrentCodeString())
}

func NewSymbolTableFrameBelow(p *SymbolTableFrame, name string) *SymbolTableFrame {
	var f *FrameMap = nil
	if p != nil {
		f = p.Frame
	}
	restricted := p != nil && p.IsRestricted
	env := &SymbolTableFrame{Name: name, Parent: p, Bindings: make(map[string]*Binding), Frame: f, CurrentCode: list.New(), IsRestricted: restricted}
	if p == nil || p == Global {
		TopLevelEnvironments.Mutex.Lock()
		defer TopLevelEnvironments.Mutex.Unlock()

		TopLevelEnvironments.Environments[name] = env
	}
	return env
}

func NewSymbolTableFrameBelowWithFrame(p *SymbolTableFrame, f *FrameMap, name string) *SymbolTableFrame {
	if f == nil {
		f = p.Frame
	}
	restricted := p != nil && p.IsRestricted
	env := &SymbolTableFrame{Name: name, Parent: p, Bindings: make(map[string]*Binding, 10), Frame: f, CurrentCode: list.New(), IsRestricted: restricted}
	if p == nil || p == Global {
		TopLevelEnvironments.Mutex.Lock()
		defer TopLevelEnvironments.Mutex.Unlock()

		TopLevelEnvironments.Environments[name] = env
	}
	return env
}

func (self *SymbolTableFrame) HasFrame() bool {
	return self.Frame != nil
}

func (self *SymbolTableFrame) BindingNamed(name string) (b *Binding, present bool) {
	b, present = self.Bindings[name]
	return
}

func (self *SymbolTableFrame) SetBindingAt(name string, b *Binding) {
	self.Bindings[name] = b
}

func (self *SymbolTableFrame) DeleteBinding(name string) {
	delete(self.Bindings, name)
}

func (self *SymbolTableFrame) findSymbol(name string) (symbol *Data, found bool) {
	binding, found := self.BindingNamed(name)
	if found {
		return binding.Sym, true
	} else if self.Parent != nil {
		return self.Parent.findSymbol(name)
	} else {
		return nil, false
	}
}

func (self *SymbolTableFrame) FindBindingFor(symbol *Data) (binding *Binding, found bool) {
	name := StringValue(symbol)
	binding, found = self.BindingNamed(name)
	if found {
		return
	} else if self.Parent != nil {
		return self.Parent.FindBindingFor(symbol)
	} else {
		return nil, false
	}
}

func (self *SymbolTableFrame) Intern(name string) (sym *Data) {
	sym = Intern(name)
	self.BindTo(sym, nil)
	return
}

func (self *SymbolTableFrame) BindTo(symbol *Data, value *Data) *Data {
	binding, found := self.FindBindingFor(symbol)
	if found {
		binding.Val = value
	} else {
		binding := BindingWithSymbolAndValue(symbol, value)
		self.SetBindingAt(StringValue(symbol), binding)
	}
	return value
}

func (self *SymbolTableFrame) SetTo(symbol *Data, value *Data) (result *Data, err error) {
	localBinding, found := self.findBindingInLocalFrameFor(symbol)
	if found {
		localBinding.Val = value
		return value, nil
	}

	naked := StringValue(NakedSymbolFrom(symbol))
	if self.HasFrame() && self.Frame.HasSlot(naked) {
		self.Frame.Set(naked, value)
		return value, nil
	}

	binding, found := self.FindBindingFor(symbol)
	if found {
		binding.Val = value
		return value, nil
	}

	return nil, errors.New(fmt.Sprintf("%s is undefined", StringValue(symbol)))
}

func (self *SymbolTableFrame) findBindingInLocalFrameFor(symbol *Data) (b *Binding, found bool) {
	return self.BindingNamed(StringValue(symbol))
}

func (self *SymbolTableFrame) BindLocallyTo(symbol *Data, value *Data) *Data {
	binding, found := self.findBindingInLocalFrameFor(symbol)
	if found {
		binding.Val = value
	} else {
		binding := BindingWithSymbolAndValue(symbol, value)
		self.SetBindingAt(StringValue(symbol), binding)
	}
	return value
}

func (self *SymbolTableFrame) ValueOfWithFunctionSlotCheck(symbol *Data, needFunction bool) *Data {
	localBinding, found := self.findBindingInLocalFrameFor(symbol)
	if found {
		if FunctionP(localBinding.Val) {
			atomic.StoreInt32(&FunctionValue(localBinding.Val).SlotFunction, 1)
		}
		return localBinding.Val
	}

	if self.HasFrame() {
		f := self.Frame
		naked := StringValue(NakedSymbolFrom(symbol))
		if f.HasSlot(naked) {
			slotValue := f.Get(naked)
			if !needFunction {
				return slotValue
			}
			if FunctionP(slotValue) {
				atomic.StoreInt32(&FunctionValue(slotValue).SlotFunction, 1)
				return slotValue
			}
		}
	}

	binding, found := self.FindBindingFor(symbol)
	if found {
		if FunctionP(binding.Val) {
			atomic.StoreInt32(&FunctionValue(binding.Val).SlotFunction, 0)
		}
		return binding.Val
	} else {
		return EmptyCons()
	}
}

func (self *SymbolTableFrame) ValueOf(symbol *Data) *Data {
	return self.ValueOfWithFunctionSlotCheck(symbol, false)
}
<commit_msg>Revert "don't add naked symbols to the internal symbol map, they don't need to"<commit_after>// Copyright 2014 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file implements the symbol table.

package golisp

import (
	"container/list"
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
)

const (
	READ_LOCK = iota
	WRITE_LOCK
)

type SymbolTableFrame struct {
	Name         string
	Parent       *SymbolTableFrame
	Previous     *SymbolTableFrame
	Frame        *FrameMap
	Bindings     map[string]*Binding
	CurrentCode  *list.List
	IsRestricted bool
}

type symbolsTable struct {
	Symbols map[string]*Data
	Mutex   sync.RWMutex
}

type environmentsTable struct {
	Environments map[string]*SymbolTableFrame
	Mutex        sync.RWMutex
}

var Global *SymbolTableFrame
var TopLevelEnvironments environmentsTable = environmentsTable{make(map[string]*SymbolTableFrame, 5), sync.RWMutex{}}

var internedSymbols symbolsTable = symbolsTable{make(map[string]*Data, 256), sync.RWMutex{}}

func Intern(name string) (sym *Data) {
	internedSymbols.Mutex.RLock()
	lock := READ_LOCK
	defer func() {
		if lock == READ_LOCK {
			internedSymbols.Mutex.RUnlock()
		} else {
			internedSymbols.Mutex.Unlock()
		}
	}()

	sym = internedSymbols.Symbols[name]
	if sym == nil {
		internedSymbols.Mutex.RUnlock()
		internedSymbols.Mutex.Lock()
		lock = WRITE_LOCK
		sym = SymbolWithName(name)
		internedSymbols.Symbols[name] = sym
	}
	return
}

func (self *SymbolTableFrame) Depth() int {
	if self.Previous == nil {
		return 1
	} else {
		return 1 + self.Previous.Depth()
	}
}

func (self *SymbolTableFrame) CurrentCodeString() string {
	if self.CurrentCode.Len() > 0 {
		return self.CurrentCode.Front().Value.(string)
	} else {
		return "Unknown code"
	}
}

func (self *SymbolTableFrame) InternalDump(frameNumber int) {
	fmt.Printf("Frame %d: %s\n", frameNumber, self.CurrentCodeString())
	for _, b := range self.Bindings {
		if b.Val == nil || TypeOf(b.Val) != PrimitiveType {
			b.Dump()
		}
	}
	fmt.Printf("\n")
	if self.Previous != nil {
		self.Previous.InternalDump(frameNumber + 1)
	}
}

func (self *SymbolTableFrame) Dump() {
	println()
	self.InternalDump(0)
}

func (self *SymbolTableFrame) DumpSingleFrame(frameNumber int) {
	if frameNumber == 0 {
		fmt.Printf("%s\n", self.CurrentCodeString())
		for _, b := range self.Bindings {
			if b.Val == nil || TypeOf(b.Val) != PrimitiveType {
				b.Dump()
			}
		}
		fmt.Printf("\n")
	} else if self.Previous != nil {
		self.Previous.DumpSingleFrame(frameNumber - 1)
	} else {
		fmt.Printf("Invalid frame selected.\n")
	}
}

func (self *SymbolTableFrame) InternalDumpHeaders(frameNumber int) {
	fmt.Printf("Frame %d: %s\n", frameNumber, self.CurrentCodeString())
	if self.Previous != nil {
		self.Previous.InternalDumpHeaders(frameNumber + 1)
	}
}

func (self *SymbolTableFrame) DumpHeaders() {
	println()
	self.InternalDumpHeaders(0)
}

func (self *SymbolTableFrame) DumpHeader() {
	fmt.Printf("%s\n", self.CurrentCodeString())
}

func NewSymbolTableFrameBelow(p *SymbolTableFrame, name string) *SymbolTableFrame {
	var f *FrameMap = nil
	if p != nil {
		f = p.Frame
	}
	restricted := p != nil && p.IsRestricted
	env := &SymbolTableFrame{Name: name, Parent: p, Bindings: make(map[string]*Binding), Frame: f, CurrentCode: list.New(), IsRestricted: restricted}
	if p == nil || p == Global {
		TopLevelEnvironments.Mutex.Lock()
		defer TopLevelEnvironments.Mutex.Unlock()

		TopLevelEnvironments.Environments[name] = env
	}
	return env
}

func NewSymbolTableFrameBelowWithFrame(p *SymbolTableFrame, f *FrameMap, name string) *SymbolTableFrame {
	if f == nil {
		f = p.Frame
	}
	restricted := p != nil && p.IsRestricted
	env := &SymbolTableFrame{Name: name, Parent: p, Bindings: make(map[string]*Binding, 10), Frame: f, CurrentCode: list.New(), IsRestricted: restricted}
	if p == nil || p == Global {
		TopLevelEnvironments.Mutex.Lock()
		defer TopLevelEnvironments.Mutex.Unlock()

		TopLevelEnvironments.Environments[name] = env
	}
	return env
}

func (self *SymbolTableFrame) HasFrame() bool {
	return self.Frame != nil
}

func (self *SymbolTableFrame) BindingNamed(name string) (b *Binding, present bool) {
	b, present = self.Bindings[name]
	return
}

func (self *SymbolTableFrame) SetBindingAt(name string, b *Binding) {
	self.Bindings[name] = b
}

func (self *SymbolTableFrame) DeleteBinding(name string) {
	delete(self.Bindings, name)
}

func (self *SymbolTableFrame) findSymbol(name string) (symbol *Data, found bool) {
	binding, found := self.BindingNamed(name)
	if found {
		return binding.Sym, true
	} else if self.Parent != nil {
		return self.Parent.findSymbol(name)
	} else {
		return nil, false
	}
}

func (self *SymbolTableFrame) FindBindingFor(symbol *Data) (binding *Binding, found bool) {
	name := StringValue(symbol)
	binding, found = self.BindingNamed(name)
	if found {
		return
	} else if self.Parent != nil {
		return self.Parent.FindBindingFor(symbol)
	} else {
		return nil, false
	}
}

func (self *SymbolTableFrame) Intern(name string) (sym *Data) {
	sym = Intern(name)
	self.BindTo(sym, nil)
	return
}

func (self *SymbolTableFrame) BindTo(symbol *Data, value *Data) *Data {
	binding, found := self.FindBindingFor(symbol)
	if found {
		binding.Val = value
	} else {
		binding := BindingWithSymbolAndValue(symbol, value)
		self.SetBindingAt(StringValue(symbol), binding)
	}
	return value
}

func (self *SymbolTableFrame) SetTo(symbol *Data, value *Data) (result *Data, err error) {
	localBinding, found := self.findBindingInLocalFrameFor(symbol)
	if found {
		localBinding.Val = value
		return value, nil
	}

	naked := StringValue(NakedSymbolFrom(symbol))
	if self.HasFrame() && self.Frame.HasSlot(naked) {
		self.Frame.Set(naked, value)
		return value, nil
	}

	binding, found := self.FindBindingFor(symbol)
	if found {
		binding.Val = value
		return value, nil
	}

	return nil, errors.New(fmt.Sprintf("%s is undefined", StringValue(symbol)))
}

func (self *SymbolTableFrame) findBindingInLocalFrameFor(symbol *Data) (b *Binding, found bool) {
	return self.BindingNamed(StringValue(symbol))
}

func (self *SymbolTableFrame) BindLocallyTo(symbol *Data, value *Data) *Data {
	binding, found := self.findBindingInLocalFrameFor(symbol)
	if found {
		binding.Val = value
	} else {
		binding := BindingWithSymbolAndValue(symbol, value)
		self.SetBindingAt(StringValue(symbol), binding)
	}
	return value
}

func (self *SymbolTableFrame) ValueOfWithFunctionSlotCheck(symbol *Data, needFunction bool) *Data {
	localBinding, found := self.findBindingInLocalFrameFor(symbol)
	if found {
		if FunctionP(localBinding.Val) {
			atomic.StoreInt32(&FunctionValue(localBinding.Val).SlotFunction, 1)
		}
		return localBinding.Val
	}

	if self.HasFrame() {
		f := self.Frame
		naked := StringValue(NakedSymbolFrom(symbol))
		if f.HasSlot(naked) {
			slotValue := f.Get(naked)
			if !needFunction {
				return slotValue
			}
			if FunctionP(slotValue) {
				atomic.StoreInt32(&FunctionValue(slotValue).SlotFunction, 1)
				return slotValue
			}
		}
	}

	binding, found := self.FindBindingFor(symbol)
	if found {
		if FunctionP(binding.Val) {
			atomic.StoreInt32(&FunctionValue(binding.Val).SlotFunction, 0)
		}
		return binding.Val
	} else {
		return EmptyCons()
	}
}

func (self *SymbolTableFrame) ValueOf(symbol *Data) *Data {
	return self.ValueOfWithFunctionSlotCheck(symbol, false)
}
<|endoftext|>


<commit_before>package requestHandler

import (
	"encoding/json"
	"fmt"
	"github.com/zwirec/TGChatScanner/models"
	"log"
	"net/http"
	"strconv"
)

type UserJSON struct {
	Err   string       `json:"error,omitempty"`
	Model *models.User `json:"entity,omitempty"`
}

type ImagesJSON struct {
	Err    string         `json:"error"`
	Images []models.Image `json:"images"`
}

type ChatsJSON struct {
	Err   string        `json:"error"`
	Chats []models.Chat `json:"chats"`
}

type TagsJSON struct {
	Err  string       `json:"error"`
	Tags []models.Tag `json:"tags"`
}

var user_key = "user"

func getImages(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	values := req.URL.Query()
	img := models.Image{}

	imgs, err := img.GetImgByParams(appContext.Db, values)

	if err != nil {
		response := ImagesJSON{Err: "server error",
			Images: nil}
		responseJSON, err := json.Marshal(response)

		if err == nil {
			writeResponse(w, string(responseJSON), http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		} else {
			writeResponse(w, nil, http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			err_l.Println(err)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
	}

	response := ImagesJSON{Err: "",
		Images: imgs}
	responseJSON, err := json.Marshal(response)

	if err == nil {
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	} else {
		err_l.Println(err)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}
	return
}

func getChatTags(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	values := req.URL.Query()

	chat_id, err := strconv.ParseInt(values["chat_id"][0], 10, 64)

	if err != nil {
		response := TagsJSON{Err: "invalid chat_id",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)
		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	}

	chat := models.Chat{TGID: chat_id}

	tags, err := chat.GetTags(appContext.Db)

	if err != nil {
		err_l.Println(err)
		response := TagsJSON{Err: "system error",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)

		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	} else {
		response := TagsJSON{Err: "",
			Tags: tags}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			err_l.Println(err)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}

}

func getUserTags(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	values := req.URL.Query()

	user_id, err := strconv.ParseInt(values["user_id"][0], 10, 32)

	if err != nil {
		response := TagsJSON{Err: "invalid chat_id",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)
		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	}

	user := models.User{TGID: int(user_id)}

	tags, err := user.GetTags(appContext.Db)

	if err != nil {
		err_l.Println(err)
		response := TagsJSON{Err: "system error",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)

		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	} else {
		response := TagsJSON{Err: "",
			Tags: tags}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			err_l.Println(err)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}

}

func getChats(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	user := req.Context().Value(user_key).(*models.User)

	if err := user.GetUsersChats(appContext.Db); err != nil {
		err_l.Println(err)
		response := ChatsJSON{Err: "system error",
			Chats: nil}
		responseJSON, _ := json.Marshal(response)
		writeResponse(w, string(responseJSON), http.StatusInternalServerError)

		logHttpRequest(acc_l, req, http.StatusInternalServerError)
		return
	} else {
		response := ChatsJSON{Err: "",
			Chats: user.Chats}
		responseJSON, _ := json.Marshal(response)
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}
}

func getTags(w http.ResponseWriter, req *http.Request) {
	//TODO
	fmt.Fprint(w, "tags.get")
	return
}

func removeSubs(w http.ResponseWriter, req *http.Request) {
	//TODO
	fmt.Fprint(w, "subs.remove")
	return
}

func validateLoginParams(values map[string]interface{}) (ok bool) {
	if values["username"] == nil || values["password"] == nil {
		return false
	}
	return true
}

func writeResponse(w http.ResponseWriter, data interface{}, status int) error {
	w.WriteHeader(status)
	if data != nil {
		_, err := fmt.Fprint(w, data)
		if err != nil {
			return err
		}
	}
	return nil
}

func logHttpRequest(l *log.Logger, req *http.Request, code int) {
	l.Printf(`%s "%s %s %s %d"`, req.RemoteAddr, req.Method, req.URL.Path, req.Proto, code)
}
<commit_msg>update<commit_after>package requestHandler

import (
	"encoding/json"
	"fmt"
	"github.com/zwirec/TGChatScanner/models"
	"log"
	"net/http"
	"strconv"
)

type UserJSON struct {
	Err   string       `json:"error,omitempty"`
	Model *models.User `json:"entity,omitempty"`
}

type ImagesJSON struct {
	Err    string         `json:"error"`
	Images []models.Image `json:"images"`
}

type ChatsJSON struct {
	Err   string        `json:"error"`
	Chats []models.Chat `json:"chats"`
}

type TagsJSON struct {
	Err  string       `json:"error"`
	Tags []models.Tag `json:"tags"`
}

var user_key = "user"

func getImages(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	values := req.URL.Query()
	img := models.Image{}

	imgs, err := img.GetImgByParams(appContext.Db, values)

	if err != nil {
		response := ImagesJSON{Err: "server error",
			Images: nil}
		responseJSON, err := json.Marshal(response)

		if err == nil {
			writeResponse(w, string(responseJSON), http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		} else {
			writeResponse(w, nil, http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			err_l.Println(err)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
	}

	response := ImagesJSON{Err: "",
		Images: imgs}
	responseJSON, err := json.Marshal(response)

	if err == nil {
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	} else {
		err_l.Println(err)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}
	return
}

func getChatTags(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	values := req.URL.Query()

	chatid, ok := values["chat_id"]

	if !ok {
		response := TagsJSON{Err: "invalid chat_id",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)
		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	}

	chat_id, err := strconv.ParseInt(chatid[0], 10, 64)

	if err != nil {
		response := TagsJSON{Err: "invalid chat_id",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)
		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	}

	chat := models.Chat{TGID: chat_id}

	tags, err := chat.GetTags(appContext.Db)

	if err != nil {
		err_l.Println(err)
		response := TagsJSON{Err: "system error",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)
		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	} else {
		response := TagsJSON{Err: "",
			Tags: tags}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			err_l.Println(err)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}

}

func getUserTags(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	user := req.Context().Value(user_key).(*models.User)

	tags, err := user.GetTags(appContext.Db)

	if err != nil {
		err_l.Println(err)
		response := TagsJSON{Err: "system error",
			Tags: nil}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			err_l.Println(err)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusBadRequest)

		logHttpRequest(acc_l, req, http.StatusBadRequest)
		return
	} else {
		response := TagsJSON{Err: "",
			Tags: tags}
		responseJSON, err := json.Marshal(response)
		if err != nil {
			writeResponse(w, nil, http.StatusInternalServerError)
			logHttpRequest(acc_l, req, http.StatusInternalServerError)
			err_l.Println(err)
			return
		}
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}

}

func getChats(w http.ResponseWriter, req *http.Request) {
	err_l := appContext.SysLogger
	acc_l := appContext.AccessLogger

	user := req.Context().Value(user_key).(*models.User)

	if err := user.GetUsersChats(appContext.Db); err != nil {
		err_l.Println(err)
		response := ChatsJSON{Err: "system error",
			Chats: nil}
		responseJSON, _ := json.Marshal(response)
		writeResponse(w, string(responseJSON), http.StatusInternalServerError)

		logHttpRequest(acc_l, req, http.StatusInternalServerError)
		return
	} else {
		response := ChatsJSON{Err: "",
			Chats: user.Chats}
		responseJSON, _ := json.Marshal(response)
		writeResponse(w, string(responseJSON), http.StatusOK)
		logHttpRequest(acc_l, req, http.StatusOK)
		return
	}
}

func getTags(w http.ResponseWriter, req *http.Request) {
	//TODO
	fmt.Fprint(w, "tags.get")
	return
}

func removeSubs(w http.ResponseWriter, req *http.Request) {
	//TODO
	fmt.Fprint(w, "subs.remove")
	return
}

func validateLoginParams(values map[string]interface{}) (ok bool) {
	if values["username"] == nil || values["password"] == nil {
		return false
	}
	return true
}

func writeResponse(w http.ResponseWriter, data interface{}, status int) error {
	w.WriteHeader(status)
	if data != nil {
		_, err := fmt.Fprint(w, data)
		if err != nil {
			return err
		}
	}
	return nil
}

func logHttpRequest(l *log.Logger, req *http.Request, code int) {
	l.Printf(`%s "%s %s %s %d"`, req.RemoteAddr, req.Method, req.URL.Path, req.Proto, code)
}
<|endoftext|>


<commit_before>package lfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/github/git-lfs/vendor/_nuts/github.com/cheggaaa/pb"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
	contentaddressable "github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/go-contentaddressable"
)

func PointerSmudgeToFile(filename string, ptr *Pointer, download bool, cb CopyCallback) error {
	os.MkdirAll(filepath.Dir(filename), 0755)
	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Could not create working directory file: %v", err)
	}
	defer file.Close()
	if err := PointerSmudge(file, ptr, filename, download, cb); err != nil {
		if IsDownloadDeclinedError(err) {
			// write placeholder data instead
			file.Seek(0, os.SEEK_SET)
			ptr.Encode(file)
			return err
		} else {
			return fmt.Errorf("Could not write working directory file: %v", err)
		}
	}
	return nil
}

func PointerSmudge(writer io.Writer, ptr *Pointer, workingfile string, download bool, cb CopyCallback) error {
	mediafile, err := LocalMediaPath(ptr.Oid)
	if err != nil {
		return err
	}

	stat, statErr := os.Stat(mediafile)
	if statErr == nil && stat != nil {
		fileSize := stat.Size()
		if fileSize == 0 || fileSize != ptr.Size {
			tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
			os.RemoveAll(mediafile)
			stat = nil
		}
	}

	if statErr != nil || stat == nil {
		if download {
			err = downloadFile(writer, ptr, workingfile, mediafile, cb)
		} else {
			return newDownloadDeclinedError(nil)
		}
	} else {
		err = readLocalFile(writer, ptr, mediafile, workingfile, cb)
	}

	if err != nil {
		return newSmudgeError(err, ptr.Oid, mediafile)
	}

	return nil
}

// PointerSmudgeObject uses a Pointer and objectResource to download the object to the
// media directory. It does not write the file to the working directory.
func PointerSmudgeObject(ptr *Pointer, obj *objectResource, cb CopyCallback) error {
	mediafile, err := LocalMediaPath(obj.Oid)
	if err != nil {
		return err
	}

	stat, statErr := os.Stat(mediafile)
	if statErr == nil && stat != nil {
		fileSize := stat.Size()
		if fileSize == 0 || fileSize != obj.Size {
			tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
			os.RemoveAll(mediafile)
			stat = nil
		}
	}

	if statErr != nil || stat == nil {
		err := downloadObject(ptr, obj, mediafile, cb)

		if err != nil {
			return newSmudgeError(err, obj.Oid, mediafile)
		}
	}

	return nil
}

func downloadObject(ptr *Pointer, obj *objectResource, mediafile string, cb CopyCallback) error {
	reader, size, err := DownloadObject(obj)
	if reader != nil {
		defer reader.Close()
	}

	// TODO this can be unified with the same code in downloadFile
	if err != nil {
		// err.Errorf("Error downloading %s.", mediafile) ERRTODO
		return err
	}

	if ptr.Size == 0 {
		ptr.Size = size
	}

	mediaFile, err := contentaddressable.NewFile(mediafile)
	if err != nil {
		return Errorf(err, "Error opening media file buffer.")
	}

	_, err = CopyWithCallback(mediaFile, reader, ptr.Size, cb)
	if err == nil {
		err = mediaFile.Accept()
	}
	mediaFile.Close()

	if err != nil {
		return Errorf(err, "Error buffering media file.")
	}

	return nil
}

func downloadFile(writer io.Writer, ptr *Pointer, workingfile, mediafile string, cb CopyCallback) error {
	fmt.Fprintf(os.Stderr, "Downloading %s (%s)\n", workingfile, pb.FormatBytes(ptr.Size))
	reader, size, err := Download(filepath.Base(mediafile))
	if reader != nil {
		defer reader.Close()
	}

	if err != nil {
		//err.Errorf("Error downloading %s.", mediafile) ERRTODO
		return err
	}

	if ptr.Size == 0 {
		ptr.Size = size
	}

	mediaFile, err := contentaddressable.NewFile(mediafile)
	if err != nil {
		return Errorf(err, "Error opening media file buffer.")
	}

	_, err = CopyWithCallback(mediaFile, reader, ptr.Size, cb)
	if err == nil {
		err = mediaFile.Accept()
	}
	mediaFile.Close()

	if err != nil {
		return Errorf(err, "Error buffering media file.")
	}

	return readLocalFile(writer, ptr, mediafile, workingfile, nil)
}

func readLocalFile(writer io.Writer, ptr *Pointer, mediafile string, workingfile string, cb CopyCallback) error {
	reader, err := os.Open(mediafile)
	if err != nil {
		return Errorf(err, "Error opening media file.")
	}
	defer reader.Close()

	if ptr.Size == 0 {
		if stat, _ := os.Stat(mediafile); stat != nil {
			ptr.Size = stat.Size()
		}
	}

	if len(ptr.Extensions) > 0 {
		registeredExts := Config.Extensions()
		extensions := make(map[string]Extension)
		for _, ptrExt := range ptr.Extensions {
			ext, ok := registeredExts[ptrExt.Name]
			if !ok {
				err := fmt.Errorf("Extension '%s' is not configured.", ptrExt.Name)
				return Error(err)
			}
			ext.Priority = ptrExt.Priority
			extensions[ext.Name] = ext
		}
		exts, err := SortExtensions(extensions)
		if err != nil {
			return Error(err)
		}

		// pipe extensions in reverse order
		var extsR []Extension
		for i, _ := range exts {
			ext := exts[len(exts)-1-i]
			extsR = append(extsR, ext)
		}

		request := &pipeRequest{"smudge", reader, workingfile, extsR}

		response, err := pipeExtensions(request)
		if err != nil {
			return Error(err)
		}

		actualExts := make(map[string]*pipeExtResult)
		for _, result := range response.results {
			actualExts[result.name] = result
		}

		// verify name, order, and oids
		oid := response.results[0].oidIn
		if ptr.Oid != oid {
			err = fmt.Errorf("Actual oid %s during smudge does not match expected %s", oid, ptr.Oid)
			return Error(err)
		}

		for _, expected := range ptr.Extensions {
			actual := actualExts[expected.Name]
			if actual.name != expected.Name {
				err = fmt.Errorf("Actual extension name '%s' does not match expected '%s'", actual.name, expected.Name)
				return Error(err)
			}
			if actual.oidOut != expected.Oid {
				err = fmt.Errorf("Actual oid %s for extension '%s' does not match expected %s", actual.oidOut, expected.Name, expected.Oid)
				return Error(err)
			}
		}

		// setup reader
		reader, err = os.Open(response.file.Name())
		if err != nil {
			return Errorf(err, "Error opening smudged file.")
		}
		defer reader.Close()
	}

	_, err = CopyWithCallback(writer, reader, ptr.Size, cb)
	if err != nil {
		return Errorf(err, "Error reading from media file.")
	}

	return nil
}
<commit_msg>ンンンンン ンンンン<commit_after>package lfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/github/git-lfs/vendor/_nuts/github.com/cheggaaa/pb"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
	contentaddressable "github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/go-contentaddressable"
)

func PointerSmudgeToFile(filename string, ptr *Pointer, download bool, cb CopyCallback) error {
	os.MkdirAll(filepath.Dir(filename), 0755)
	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Could not create working directory file: %v", err)
	}
	defer file.Close()
	if err := PointerSmudge(file, ptr, filename, download, cb); err != nil {
		if IsDownloadDeclinedError(err) {
			// write placeholder data instead
			file.Seek(0, os.SEEK_SET)
			ptr.Encode(file)
			return err
		} else {
			return fmt.Errorf("Could not write working directory file: %v", err)
		}
	}
	return nil
}

func PointerSmudge(writer io.Writer, ptr *Pointer, workingfile string, download bool, cb CopyCallback) error {
	mediafile, err := LocalMediaPath(ptr.Oid)
	if err != nil {
		return err
	}

	stat, statErr := os.Stat(mediafile)
	if statErr == nil && stat != nil {
		fileSize := stat.Size()
		if fileSize == 0 || fileSize != ptr.Size {
			tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
			os.RemoveAll(mediafile)
			stat = nil
		}
	}

	if statErr != nil || stat == nil {
		if download {
			err = downloadFile(writer, ptr, workingfile, mediafile, cb)
		} else {
			return newDownloadDeclinedError(nil)
		}
	} else {
		err = readLocalFile(writer, ptr, mediafile, workingfile, cb)
	}

	if err != nil {
		return newSmudgeError(err, ptr.Oid, mediafile)
	}

	return nil
}

// PointerSmudgeObject uses a Pointer and objectResource to download the object to the
// media directory. It does not write the file to the working directory.
func PointerSmudgeObject(ptr *Pointer, obj *objectResource, cb CopyCallback) error {
	mediafile, err := LocalMediaPath(obj.Oid)
	if err != nil {
		return err
	}

	stat, statErr := os.Stat(mediafile)
	if statErr == nil && stat != nil {
		fileSize := stat.Size()
		if fileSize == 0 || fileSize != obj.Size {
			tracerx.Printf("Removing %s, size %d is invalid", mediafile, fileSize)
			os.RemoveAll(mediafile)
			stat = nil
		}
	}

	if statErr != nil || stat == nil {
		err := downloadObject(ptr, obj, mediafile, cb)

		if err != nil {
			return newSmudgeError(err, obj.Oid, mediafile)
		}
	}

	return nil
}

func downloadObject(ptr *Pointer, obj *objectResource, mediafile string, cb CopyCallback) error {
	reader, size, err := DownloadObject(obj)
	if reader != nil {
		defer reader.Close()
	}

	// TODO this can be unified with the same code in downloadFile
	if err != nil {
		return Errorf(err, "Error downloading %s", mediafile)
	}

	if ptr.Size == 0 {
		ptr.Size = size
	}

	mediaFile, err := contentaddressable.NewFile(mediafile)
	if err != nil {
		return Errorf(err, "Error opening media file buffer.")
	}

	_, err = CopyWithCallback(mediaFile, reader, ptr.Size, cb)
	if err == nil {
		err = mediaFile.Accept()
	}
	mediaFile.Close()

	if err != nil {
		return Errorf(err, "Error buffering media file.")
	}

	return nil
}

func downloadFile(writer io.Writer, ptr *Pointer, workingfile, mediafile string, cb CopyCallback) error {
	fmt.Fprintf(os.Stderr, "Downloading %s (%s)\n", workingfile, pb.FormatBytes(ptr.Size))
	reader, size, err := Download(filepath.Base(mediafile))
	if reader != nil {
		defer reader.Close()
	}

	if err != nil {
		return Errorf(err, "Error downloading %s.", mediafile)
	}

	if ptr.Size == 0 {
		ptr.Size = size
	}

	mediaFile, err := contentaddressable.NewFile(mediafile)
	if err != nil {
		return Errorf(err, "Error opening media file buffer.")
	}

	_, err = CopyWithCallback(mediaFile, reader, ptr.Size, cb)
	if err == nil {
		err = mediaFile.Accept()
	}
	mediaFile.Close()

	if err != nil {
		return Errorf(err, "Error buffering media file.")
	}

	return readLocalFile(writer, ptr, mediafile, workingfile, nil)
}

func readLocalFile(writer io.Writer, ptr *Pointer, mediafile string, workingfile string, cb CopyCallback) error {
	reader, err := os.Open(mediafile)
	if err != nil {
		return Errorf(err, "Error opening media file.")
	}
	defer reader.Close()

	if ptr.Size == 0 {
		if stat, _ := os.Stat(mediafile); stat != nil {
			ptr.Size = stat.Size()
		}
	}

	if len(ptr.Extensions) > 0 {
		registeredExts := Config.Extensions()
		extensions := make(map[string]Extension)
		for _, ptrExt := range ptr.Extensions {
			ext, ok := registeredExts[ptrExt.Name]
			if !ok {
				err := fmt.Errorf("Extension '%s' is not configured.", ptrExt.Name)
				return Error(err)
			}
			ext.Priority = ptrExt.Priority
			extensions[ext.Name] = ext
		}
		exts, err := SortExtensions(extensions)
		if err != nil {
			return Error(err)
		}

		// pipe extensions in reverse order
		var extsR []Extension
		for i, _ := range exts {
			ext := exts[len(exts)-1-i]
			extsR = append(extsR, ext)
		}

		request := &pipeRequest{"smudge", reader, workingfile, extsR}

		response, err := pipeExtensions(request)
		if err != nil {
			return Error(err)
		}

		actualExts := make(map[string]*pipeExtResult)
		for _, result := range response.results {
			actualExts[result.name] = result
		}

		// verify name, order, and oids
		oid := response.results[0].oidIn
		if ptr.Oid != oid {
			err = fmt.Errorf("Actual oid %s during smudge does not match expected %s", oid, ptr.Oid)
			return Error(err)
		}

		for _, expected := range ptr.Extensions {
			actual := actualExts[expected.Name]
			if actual.name != expected.Name {
				err = fmt.Errorf("Actual extension name '%s' does not match expected '%s'", actual.name, expected.Name)
				return Error(err)
			}
			if actual.oidOut != expected.Oid {
				err = fmt.Errorf("Actual oid %s for extension '%s' does not match expected %s", actual.oidOut, expected.Name, expected.Oid)
				return Error(err)
			}
		}

		// setup reader
		reader, err = os.Open(response.file.Name())
		if err != nil {
			return Errorf(err, "Error opening smudged file.")
		}
		defer reader.Close()
	}

	_, err = CopyWithCallback(writer, reader, ptr.Size, cb)
	if err != nil {
		return Errorf(err, "Error reading from media file.")
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017-present Kirill Danshin and Gramework contributors
//
// 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
//

package gramework

import (
	"errors"
	"reflect"
	"strings"

	"github.com/valyala/fasthttp"
)

type reqHandlerDefault interface {
	Handler(*Context)
}

type reqHandlerWithError interface {
	Handler(*Context) error
}

type reqHandlerWithEfaceError interface {
	Handler(*Context) (interface{}, error)
}

type reqHandlerWithEface interface {
	Handler(*Context) interface{}
}

type reqHandlerNoCtx interface {
	Handler()
}

type reqHandlerWithErrorNoCtx interface {
	Handler() error
}

type reqHandlerWithEfaceErrorNoCtx interface {
	Handler() (interface{}, error)
}

type reqHandlerWithEfaceNoCtx interface {
	Handler() interface{}
}

func (r *Router) determineHandler(handler interface{}) func(*Context) {
	// copy handler, we don't want to mutate our arguments
	rawHandler := handler

	// prepare handler in case if it one of our supported interfaces
	switch h := handler.(type) {
	case reqHandlerDefault:
		rawHandler = h.Handler
	case reqHandlerWithError:
		rawHandler = h.Handler
	case reqHandlerWithEfaceError:
		rawHandler = h.Handler
	case reqHandlerWithEface:
		rawHandler = h.Handler
	case reqHandlerNoCtx:
		rawHandler = h.Handler
	case reqHandlerWithErrorNoCtx:
		rawHandler = h.Handler
	case reqHandlerWithEfaceErrorNoCtx:
		rawHandler = h.Handler
	case reqHandlerWithEfaceNoCtx:
		rawHandler = h.Handler
	}

	// finally, process the handler
	switch h := rawHandler.(type) {
	case HTML:
		return r.getHTMLServer(h)
	case JSON:
		return r.getJSONServer(h)
	case func(*Context):
		return h
	case RequestHandler:
		return h
	case func(*Context) error:
		return r.getErrorHandler(h)
	case func(*fasthttp.RequestCtx):
		return r.getGrameHandler(h)
	case func(*fasthttp.RequestCtx) error:
		return r.getGrameErrorHandler(h)
	case func() interface{}:
		return r.getEfaceEncoder(h)
	case func() (interface{}, error):
		return r.getEfaceErrEncoder(h)
	case func(*Context) interface{}:
		return r.getEfaceCtxEncoder(h)
	case func(*Context) (interface{}, error):
		return r.getEfaceCtxErrEncoder(h)
	case string:
		return r.getStringServer(h)
	case []byte:
		return r.getBytesServer(h)
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		return r.getFmtDHandler(h)
	case float32, float64:
		return r.getFmtFHandler(h)
	case func():
		return r.getGrameDumbHandler(h)
	case func() error:
		return r.getGrameDumbErrorHandler(h)
	case func() string:
		return r.getEFuncStrHandler(h)
	case func() map[string]interface{}:
		return r.getHandlerEncoder(h)
	case func(*Context) map[string]interface{}:
		return r.getCtxHandlerEncoder(h)
	case func() (map[string]interface{}, error):
		return r.getHandlerEncoderErr(h)
	case func(*Context) (map[string]interface{}, error):
		return r.getCtxHandlerEncoderErr(h)
	default:
		rv := reflect.ValueOf(h)
		if rv.Kind() == reflect.Func {
			handler, err := r.getCachedReflectHandler(h)
			if err != nil {
				r.app.internalLog.WithError(err).Fatal("Unsupported reflect handler signature")
			}

			return handler
		}
		r.app.internalLog.Warnf("Unknown handler type: %T, serving fmt.Sprintf(%%v)", h)
		return r.getFmtVHandler(h)
	}
}

type reflectDecodedBodyRecv struct {
	idx int
	t   reflect.Type
}

func (r *Router) getCachedReflectHandler(h interface{}) (func(*Context), error) {
	funcT := reflect.TypeOf(h)
	if funcT.IsVariadic() {
		return nil, errors.New("could not process variadic reflect handler")
	}

	results := funcT.NumOut()
	if results > 2 {
		return nil, errors.New("reflect handler output should be one of (any), (any, error), (error) or ()")
	}

	params := funcT.NumIn()
	decodedBodyRecv := []reflectDecodedBodyRecv{}
	ctxRecv := -1

	checkForErrorAt := -1
	encodeDataAt := -1

	for i := 0; i < params; i++ {
		p := funcT.In(i)
		if strings.Contains(p.String(), "*gramework.Context") {
			ctxRecv = i
			continue
		}
		decodedBodyRecv = append(decodedBodyRecv, reflectDecodedBodyRecv{
			idx: i,
			t:   p,
		})
	}

	for i := 0; i < results; i++ {
		r := funcT.Out(i)
		println(r.String())

		if r.String() == "error" {
			if i == 0 && results > 1 {
				return nil, errors.New("reflect handler output should be one of (any), (any, error), (error) or ()")
			}

			checkForErrorAt = i
			continue
		}

		if encodeDataAt >= 0 {
			return nil, errors.New("reflect handler output should be one of (any), (any, error), (error) or ()")
		}

		encodeDataAt = i
	}

	funcV := reflect.ValueOf(h)

	handler := r.getEfaceCtxErrEncoder(func(ctx *Context) (v interface{}, err error) {
		callParams := make([]reflect.Value, params)
		if len(decodedBodyRecv) > 0 {
			unsupportedBodyType := true
			for i := range decodedBodyRecv {
				decoded := reflect.New(decodedBodyRecv[i].t).Interface()
				if jsonErr := ctx.UnJSON(decoded); jsonErr == nil {
					unsupportedBodyType = false
					decodedV := reflect.ValueOf(decoded)

					callParams[decodedBodyRecv[i].idx] = decodedV.Elem()
				} else {
					callParams[decodedBodyRecv[i].idx] = reflect.Zero(decodedBodyRecv[i].t)
				}
			}

			if unsupportedBodyType {
				ctx.SetStatusCode(500)
				ctx.Logger.Error("unsupported body type")
				return
			}
		}
		if ctxRecv >= 0 {
			callParams[ctxRecv] = reflect.ValueOf(ctx)
		}

		res := funcV.Call(callParams)
		if checkForErrorAt >= 0 && !res[checkForErrorAt].IsNil() {
			resErr, ok := res[checkForErrorAt].Interface().(error)
			if ok {
				err = resErr
			} else {
				err = errUnknown
			}
		}

		if encodeDataAt >= 0 {
			v = res[encodeDataAt].Interface()
		}
		return
	})

	return handler, nil
}

var errUnknown = errors.New("Unknown Server Error")
<commit_msg>fix determineHandler bug for reflect handlers<commit_after>// Copyright 2017-present Kirill Danshin and Gramework contributors
//
// 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
//

package gramework

import (
	"errors"
	"reflect"
	"strings"

	"github.com/valyala/fasthttp"
)

type reqHandlerDefault interface {
	Handler(*Context)
}

type reqHandlerWithError interface {
	Handler(*Context) error
}

type reqHandlerWithEfaceError interface {
	Handler(*Context) (interface{}, error)
}

type reqHandlerWithEface interface {
	Handler(*Context) interface{}
}

type reqHandlerNoCtx interface {
	Handler()
}

type reqHandlerWithErrorNoCtx interface {
	Handler() error
}

type reqHandlerWithEfaceErrorNoCtx interface {
	Handler() (interface{}, error)
}

type reqHandlerWithEfaceNoCtx interface {
	Handler() interface{}
}

func (r *Router) determineHandler(handler interface{}) func(*Context) {
	// copy handler, we don't want to mutate our arguments
	rawHandler := handler

	// prepare handler in case if it one of our supported interfaces
	switch h := handler.(type) {
	case reqHandlerDefault:
		rawHandler = h.Handler
	case reqHandlerWithError:
		rawHandler = h.Handler
	case reqHandlerWithEfaceError:
		rawHandler = h.Handler
	case reqHandlerWithEface:
		rawHandler = h.Handler
	case reqHandlerNoCtx:
		rawHandler = h.Handler
	case reqHandlerWithErrorNoCtx:
		rawHandler = h.Handler
	case reqHandlerWithEfaceErrorNoCtx:
		rawHandler = h.Handler
	case reqHandlerWithEfaceNoCtx:
		rawHandler = h.Handler
	}

	// finally, process the handler
	switch h := rawHandler.(type) {
	case HTML:
		return r.getHTMLServer(h)
	case JSON:
		return r.getJSONServer(h)
	case func(*Context):
		return h
	case RequestHandler:
		return h
	case func(*Context) error:
		return r.getErrorHandler(h)
	case func(*fasthttp.RequestCtx):
		return r.getGrameHandler(h)
	case func(*fasthttp.RequestCtx) error:
		return r.getGrameErrorHandler(h)
	case func() interface{}:
		return r.getEfaceEncoder(h)
	case func() (interface{}, error):
		return r.getEfaceErrEncoder(h)
	case func(*Context) interface{}:
		return r.getEfaceCtxEncoder(h)
	case func(*Context) (interface{}, error):
		return r.getEfaceCtxErrEncoder(h)
	case string:
		return r.getStringServer(h)
	case []byte:
		return r.getBytesServer(h)
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		return r.getFmtDHandler(h)
	case float32, float64:
		return r.getFmtFHandler(h)
	case func():
		return r.getGrameDumbHandler(h)
	case func() error:
		return r.getGrameDumbErrorHandler(h)
	case func() string:
		return r.getEFuncStrHandler(h)
	case func() map[string]interface{}:
		return r.getHandlerEncoder(h)
	case func(*Context) map[string]interface{}:
		return r.getCtxHandlerEncoder(h)
	case func() (map[string]interface{}, error):
		return r.getHandlerEncoderErr(h)
	case func(*Context) (map[string]interface{}, error):
		return r.getCtxHandlerEncoderErr(h)
	default:
		rv := reflect.ValueOf(h)
		if rv.Kind() == reflect.Func {
			handler, err := r.getCachedReflectHandler(h)
			if err != nil {
				r.app.internalLog.WithError(err).Fatal("Unsupported reflect handler signature")
			}

			return handler
		}
		r.app.internalLog.Warnf("Unknown handler type: %T, serving fmt.Sprintf(%%v)", h)
		return r.getFmtVHandler(h)
	}
}

type reflectDecodedBodyRecv struct {
	idx int
	t   reflect.Type
}

func (r *Router) getCachedReflectHandler(h interface{}) (func(*Context), error) {
	funcT := reflect.TypeOf(h)
	if funcT.IsVariadic() {
		return nil, errors.New("could not process variadic reflect handler")
	}

	results := funcT.NumOut()
	if results > 2 {
		return nil, errors.New("reflect handler output should be one of (any), (any, error), (error) or ()")
	}

	params := funcT.NumIn()
	decodedBodyRecv := []reflectDecodedBodyRecv{}
	ctxRecv := -1

	checkForErrorAt := -1
	encodeDataAt := -1

	for i := 0; i < params; i++ {
		p := funcT.In(i)
		if strings.Contains(p.String(), "*gramework.Context") {
			ctxRecv = i
			continue
		}
		decodedBodyRecv = append(decodedBodyRecv, reflectDecodedBodyRecv{
			idx: i,
			t:   p,
		})
	}

	for i := 0; i < results; i++ {
		r := funcT.Out(i)
		println(r.String())

		if r.String() == "error" {
			if i == 0 && results > 1 {
				return nil, errors.New("reflect handler output should be one of (any), (any, error), (error) or ()")
			}

			checkForErrorAt = i
			continue
		}

		if encodeDataAt >= 0 {
			return nil, errors.New("reflect handler output should be one of (any), (any, error), (error) or ()")
		}

		encodeDataAt = i
	}

	funcV := reflect.ValueOf(h)

	handler := func(ctx *Context) {
		callParams := make([]reflect.Value, params)
		if len(decodedBodyRecv) > 0 {
			unsupportedBodyType := true
			for i := range decodedBodyRecv {
				decoded := reflect.New(decodedBodyRecv[i].t).Interface()
				if jsonErr := ctx.UnJSON(decoded); jsonErr == nil {
					unsupportedBodyType = false
					decodedV := reflect.ValueOf(decoded)

					callParams[decodedBodyRecv[i].idx] = decodedV.Elem()
				} else {
					callParams[decodedBodyRecv[i].idx] = reflect.Zero(decodedBodyRecv[i].t)
				}
			}

			if unsupportedBodyType {
				ctx.SetStatusCode(500)
				ctx.Logger.Error("unsupported body type")
				return
			}
		}
		if ctxRecv >= 0 {
			callParams[ctxRecv] = reflect.ValueOf(ctx)
		}

		res := funcV.Call(callParams)
		shouldProcessErr := false
		shouldProcessReturn := false
		var err error
		if checkForErrorAt >= 0 && !res[checkForErrorAt].IsNil() {
			resErr, ok := res[checkForErrorAt].Interface().(error)
			if ok {
				err = resErr
			} else {
				err = errUnknown
			}
			shouldProcessErr = true
		}

		var v interface{}
		if encodeDataAt >= 0 {
			v = res[encodeDataAt].Interface()
			shouldProcessReturn = true
		}
		if shouldProcessErr {
			if err != nil {
				ctx.jsonErrorLog(err)
				return
			}
		}
		if shouldProcessReturn {
			if v == nil { // err == nil here
				ctx.SetStatusCode(fasthttp.StatusNoContent)
				return
			}
			if err = ctx.JSON(v); err != nil {
				ctx.jsonErrorLog(err)
			}
		}
		return
	}

	return handler, nil
}

var errUnknown = errors.New("Unknown Server Error")
<|endoftext|>


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

package tikv

import (
	"fmt"
	"math"
	"math/rand"
	"strings"
	"time"

	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/metrics"
	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/terror"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

const (
	// NoJitter makes the backoff sequence strict exponential.
	NoJitter = 1 + iota
	// FullJitter applies random factors to strict exponential.
	FullJitter
	// EqualJitter is also randomized, but prevents very short sleeps.
	EqualJitter
	// DecorrJitter increases the maximum jitter based on the last random value.
	DecorrJitter
)

// NewBackoffFn creates a backoff func which implements exponential backoff with
// optional jitters.
// See http://www.awsarchitectureblog.com/2015/03/backoff.html
func NewBackoffFn(base, cap, jitter int) func(ctx context.Context) int {
	if base < 2 {
		// Top prevent panic in 'rand.Intn'.
		base = 2
	}
	attempts := 0
	lastSleep := base
	return func(ctx context.Context) int {
		var sleep int
		switch jitter {
		case NoJitter:
			sleep = expo(base, cap, attempts)
		case FullJitter:
			v := expo(base, cap, attempts)
			sleep = rand.Intn(v)
		case EqualJitter:
			v := expo(base, cap, attempts)
			sleep = v/2 + rand.Intn(v/2)
		case DecorrJitter:
			sleep = int(math.Min(float64(cap), float64(base+rand.Intn(lastSleep*3-base))))
		}
		log.Debugf("backoff base %d, sleep %d", base, sleep)
		select {
		case <-time.After(time.Duration(sleep) * time.Millisecond):
		case <-ctx.Done():
		}

		attempts++
		lastSleep = sleep
		return lastSleep
	}
}

func expo(base, cap, n int) int {
	return int(math.Min(float64(cap), float64(base)*math.Pow(2.0, float64(n))))
}

type backoffType int

// Back off types.
const (
	boTiKVRPC backoffType = iota
	BoTxnLock
	boTxnLockFast
	boPDRPC
	BoRegionMiss
	BoUpdateLeader
	boServerBusy
)

func (t backoffType) createFn(vars *kv.Variables) func(context.Context) int {
	if vars.Hook != nil {
		vars.Hook(t.String(), vars)
	}
	switch t {
	case boTiKVRPC:
		return NewBackoffFn(100, 2000, EqualJitter)
	case BoTxnLock:
		return NewBackoffFn(200, 3000, EqualJitter)
	case boTxnLockFast:
		return NewBackoffFn(vars.BackoffLockFast, 3000, EqualJitter)
	case boPDRPC:
		return NewBackoffFn(500, 3000, EqualJitter)
	case BoRegionMiss:
		return NewBackoffFn(100, 500, NoJitter)
	case BoUpdateLeader:
		return NewBackoffFn(1, 10, NoJitter)
	case boServerBusy:
		return NewBackoffFn(2000, 10000, EqualJitter)
	}
	return nil
}

func (t backoffType) String() string {
	switch t {
	case boTiKVRPC:
		return "tikvRPC"
	case BoTxnLock:
		return "txnLock"
	case boTxnLockFast:
		return "txnLockFast"
	case boPDRPC:
		return "pdRPC"
	case BoRegionMiss:
		return "regionMiss"
	case BoUpdateLeader:
		return "updateLeader"
	case boServerBusy:
		return "serverBusy"
	}
	return ""
}

func (t backoffType) TError() error {
	switch t {
	case boTiKVRPC:
		return ErrTiKVServerTimeout
	case BoTxnLock, boTxnLockFast:
		return ErrResolveLockTimeout
	case boPDRPC:
		return ErrPDServerTimeout.GenWithStackByArgs(txnRetryableMark)
	case BoRegionMiss, BoUpdateLeader:
		return ErrRegionUnavailable
	case boServerBusy:
		return ErrTiKVServerBusy
	}
	return terror.ClassTiKV.New(mysql.ErrUnknown, mysql.MySQLErrName[mysql.ErrUnknown])
}

// Maximum total sleep time(in ms) for kv/cop commands.
const (
	copBuildTaskMaxBackoff         = 5000
	tsoMaxBackoff                  = 15000
	scannerNextMaxBackoff          = 20000
	batchGetMaxBackoff             = 20000
	copNextMaxBackoff              = 20000
	getMaxBackoff                  = 20000
	prewriteMaxBackoff             = 20000
	cleanupMaxBackoff              = 20000
	GcOneRegionMaxBackoff          = 20000
	GcResolveLockMaxBackoff        = 100000
	deleteRangeOneRegionMaxBackoff = 100000
	rawkvMaxBackoff                = 20000
	splitRegionBackoff             = 20000
)

// CommitMaxBackoff is max sleep time of the 'commit' command
var CommitMaxBackoff = 41000

// Backoffer is a utility for retrying queries.
type Backoffer struct {
	ctx context.Context

	fn         map[backoffType]func(context.Context) int
	maxSleep   int
	totalSleep int
	errors     []error
	types      []backoffType
	vars       *kv.Variables
}

// NewBackoffer creates a Backoffer with maximum sleep time(in ms).
func NewBackoffer(ctx context.Context, maxSleep int) *Backoffer {
	return &Backoffer{
		ctx:      ctx,
		maxSleep: maxSleep,
		vars:     kv.DefaultVars,
	}
}

// WithVars sets the kv.Variables to the Backoffer and return it.
func (b *Backoffer) WithVars(vars *kv.Variables) *Backoffer {
	b.vars = vars
	return b
}

// Backoff sleeps a while base on the backoffType and records the error message.
// It returns a retryable error if total sleep time exceeds maxSleep.
func (b *Backoffer) Backoff(typ backoffType, err error) error {
	if strings.Contains(err.Error(), mismatchClusterID) {
		log.Fatalf("critical error %v", err)
	}
	select {
	case <-b.ctx.Done():
		return errors.Trace(err)
	default:
	}

	metrics.TiKVBackoffCounter.WithLabelValues(typ.String()).Inc()
	// Lazy initialize.
	if b.fn == nil {
		b.fn = make(map[backoffType]func(context.Context) int)
	}
	f, ok := b.fn[typ]
	if !ok {
		f = typ.createFn(b.vars)
		b.fn[typ] = f
	}

	b.totalSleep += f(b.ctx)
	b.types = append(b.types, typ)

	log.Debugf("%v, retry later(totalsleep %dms, maxsleep %dms)", err, b.totalSleep, b.maxSleep)

	b.errors = append(b.errors, errors.Errorf("%s at %s", err.Error(), time.Now().Format(time.RFC3339Nano)))
	if b.maxSleep > 0 && b.totalSleep >= b.maxSleep {
		errMsg := fmt.Sprintf("backoffer.maxSleep %dms is exceeded, errors:", b.maxSleep)
		for i, err := range b.errors {
			// Print only last 3 errors for non-DEBUG log levels.
			if log.GetLevel() == log.DebugLevel || i >= len(b.errors)-3 {
				errMsg += "\n" + err.Error()
			}
		}
		log.Warn(errMsg)
		// Use the first backoff type to generate a MySQL error.
		return b.types[0].TError()
	}
	return nil
}

func (b *Backoffer) String() string {
	if b.totalSleep == 0 {
		return ""
	}
	return fmt.Sprintf(" backoff(%dms %v)", b.totalSleep, b.types)
}

// Clone creates a new Backoffer which keeps current Backoffer's sleep time and errors, and shares
// current Backoffer's context.
func (b *Backoffer) Clone() *Backoffer {
	return &Backoffer{
		ctx:        b.ctx,
		maxSleep:   b.maxSleep,
		totalSleep: b.totalSleep,
		errors:     b.errors,
		vars:       b.vars,
	}
}

// Fork creates a new Backoffer which keeps current Backoffer's sleep time and errors, and holds
// a child context of current Backoffer's context.
func (b *Backoffer) Fork() (*Backoffer, context.CancelFunc) {
	ctx, cancel := context.WithCancel(b.ctx)
	return &Backoffer{
		ctx:        ctx,
		maxSleep:   b.maxSleep,
		totalSleep: b.totalSleep,
		errors:     b.errors,
		vars:       b.vars,
	}, cancel
}
<commit_msg>change the backoff base time of BoRegionMiss to 2m (#7775)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package tikv

import (
	"fmt"
	"math"
	"math/rand"
	"strings"
	"time"

	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/metrics"
	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/terror"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

const (
	// NoJitter makes the backoff sequence strict exponential.
	NoJitter = 1 + iota
	// FullJitter applies random factors to strict exponential.
	FullJitter
	// EqualJitter is also randomized, but prevents very short sleeps.
	EqualJitter
	// DecorrJitter increases the maximum jitter based on the last random value.
	DecorrJitter
)

// NewBackoffFn creates a backoff func which implements exponential backoff with
// optional jitters.
// See http://www.awsarchitectureblog.com/2015/03/backoff.html
func NewBackoffFn(base, cap, jitter int) func(ctx context.Context) int {
	if base < 2 {
		// Top prevent panic in 'rand.Intn'.
		base = 2
	}
	attempts := 0
	lastSleep := base
	return func(ctx context.Context) int {
		var sleep int
		switch jitter {
		case NoJitter:
			sleep = expo(base, cap, attempts)
		case FullJitter:
			v := expo(base, cap, attempts)
			sleep = rand.Intn(v)
		case EqualJitter:
			v := expo(base, cap, attempts)
			sleep = v/2 + rand.Intn(v/2)
		case DecorrJitter:
			sleep = int(math.Min(float64(cap), float64(base+rand.Intn(lastSleep*3-base))))
		}
		log.Debugf("backoff base %d, sleep %d", base, sleep)
		select {
		case <-time.After(time.Duration(sleep) * time.Millisecond):
		case <-ctx.Done():
		}

		attempts++
		lastSleep = sleep
		return lastSleep
	}
}

func expo(base, cap, n int) int {
	return int(math.Min(float64(cap), float64(base)*math.Pow(2.0, float64(n))))
}

type backoffType int

// Back off types.
const (
	boTiKVRPC backoffType = iota
	BoTxnLock
	boTxnLockFast
	boPDRPC
	BoRegionMiss
	BoUpdateLeader
	boServerBusy
)

func (t backoffType) createFn(vars *kv.Variables) func(context.Context) int {
	if vars.Hook != nil {
		vars.Hook(t.String(), vars)
	}
	switch t {
	case boTiKVRPC:
		return NewBackoffFn(100, 2000, EqualJitter)
	case BoTxnLock:
		return NewBackoffFn(200, 3000, EqualJitter)
	case boTxnLockFast:
		return NewBackoffFn(vars.BackoffLockFast, 3000, EqualJitter)
	case boPDRPC:
		return NewBackoffFn(500, 3000, EqualJitter)
	case BoRegionMiss:
		// change base time to 2ms, because it may recover soon.
		return NewBackoffFn(2, 500, NoJitter)
	case BoUpdateLeader:
		return NewBackoffFn(1, 10, NoJitter)
	case boServerBusy:
		return NewBackoffFn(2000, 10000, EqualJitter)
	}
	return nil
}

func (t backoffType) String() string {
	switch t {
	case boTiKVRPC:
		return "tikvRPC"
	case BoTxnLock:
		return "txnLock"
	case boTxnLockFast:
		return "txnLockFast"
	case boPDRPC:
		return "pdRPC"
	case BoRegionMiss:
		return "regionMiss"
	case BoUpdateLeader:
		return "updateLeader"
	case boServerBusy:
		return "serverBusy"
	}
	return ""
}

func (t backoffType) TError() error {
	switch t {
	case boTiKVRPC:
		return ErrTiKVServerTimeout
	case BoTxnLock, boTxnLockFast:
		return ErrResolveLockTimeout
	case boPDRPC:
		return ErrPDServerTimeout.GenWithStackByArgs(txnRetryableMark)
	case BoRegionMiss, BoUpdateLeader:
		return ErrRegionUnavailable
	case boServerBusy:
		return ErrTiKVServerBusy
	}
	return terror.ClassTiKV.New(mysql.ErrUnknown, mysql.MySQLErrName[mysql.ErrUnknown])
}

// Maximum total sleep time(in ms) for kv/cop commands.
const (
	copBuildTaskMaxBackoff         = 5000
	tsoMaxBackoff                  = 15000
	scannerNextMaxBackoff          = 20000
	batchGetMaxBackoff             = 20000
	copNextMaxBackoff              = 20000
	getMaxBackoff                  = 20000
	prewriteMaxBackoff             = 20000
	cleanupMaxBackoff              = 20000
	GcOneRegionMaxBackoff          = 20000
	GcResolveLockMaxBackoff        = 100000
	deleteRangeOneRegionMaxBackoff = 100000
	rawkvMaxBackoff                = 20000
	splitRegionBackoff             = 20000
)

// CommitMaxBackoff is max sleep time of the 'commit' command
var CommitMaxBackoff = 41000

// Backoffer is a utility for retrying queries.
type Backoffer struct {
	ctx context.Context

	fn         map[backoffType]func(context.Context) int
	maxSleep   int
	totalSleep int
	errors     []error
	types      []backoffType
	vars       *kv.Variables
}

// NewBackoffer creates a Backoffer with maximum sleep time(in ms).
func NewBackoffer(ctx context.Context, maxSleep int) *Backoffer {
	return &Backoffer{
		ctx:      ctx,
		maxSleep: maxSleep,
		vars:     kv.DefaultVars,
	}
}

// WithVars sets the kv.Variables to the Backoffer and return it.
func (b *Backoffer) WithVars(vars *kv.Variables) *Backoffer {
	b.vars = vars
	return b
}

// Backoff sleeps a while base on the backoffType and records the error message.
// It returns a retryable error if total sleep time exceeds maxSleep.
func (b *Backoffer) Backoff(typ backoffType, err error) error {
	if strings.Contains(err.Error(), mismatchClusterID) {
		log.Fatalf("critical error %v", err)
	}
	select {
	case <-b.ctx.Done():
		return errors.Trace(err)
	default:
	}

	metrics.TiKVBackoffCounter.WithLabelValues(typ.String()).Inc()
	// Lazy initialize.
	if b.fn == nil {
		b.fn = make(map[backoffType]func(context.Context) int)
	}
	f, ok := b.fn[typ]
	if !ok {
		f = typ.createFn(b.vars)
		b.fn[typ] = f
	}

	b.totalSleep += f(b.ctx)
	b.types = append(b.types, typ)

	log.Debugf("%v, retry later(totalsleep %dms, maxsleep %dms)", err, b.totalSleep, b.maxSleep)

	b.errors = append(b.errors, errors.Errorf("%s at %s", err.Error(), time.Now().Format(time.RFC3339Nano)))
	if b.maxSleep > 0 && b.totalSleep >= b.maxSleep {
		errMsg := fmt.Sprintf("backoffer.maxSleep %dms is exceeded, errors:", b.maxSleep)
		for i, err := range b.errors {
			// Print only last 3 errors for non-DEBUG log levels.
			if log.GetLevel() == log.DebugLevel || i >= len(b.errors)-3 {
				errMsg += "\n" + err.Error()
			}
		}
		log.Warn(errMsg)
		// Use the first backoff type to generate a MySQL error.
		return b.types[0].TError()
	}
	return nil
}

func (b *Backoffer) String() string {
	if b.totalSleep == 0 {
		return ""
	}
	return fmt.Sprintf(" backoff(%dms %v)", b.totalSleep, b.types)
}

// Clone creates a new Backoffer which keeps current Backoffer's sleep time and errors, and shares
// current Backoffer's context.
func (b *Backoffer) Clone() *Backoffer {
	return &Backoffer{
		ctx:        b.ctx,
		maxSleep:   b.maxSleep,
		totalSleep: b.totalSleep,
		errors:     b.errors,
		vars:       b.vars,
	}
}

// Fork creates a new Backoffer which keeps current Backoffer's sleep time and errors, and holds
// a child context of current Backoffer's context.
func (b *Backoffer) Fork() (*Backoffer, context.CancelFunc) {
	ctx, cancel := context.WithCancel(b.ctx)
	return &Backoffer{
		ctx:        ctx,
		maxSleep:   b.maxSleep,
		totalSleep: b.totalSleep,
		errors:     b.errors,
		vars:       b.vars,
	}, cancel
}
<|endoftext|>


<commit_before>package billing

// Assent represents an assent by a person on behalf of an account to an agreement
type Assent struct {
	AgreementID string `json:"-"`
	AccountID   int    `json:"account_id"`
	PersonID    int    `json:"person_id"`
	// Name is the full name of the person
	Name  string `json:"name"`
	Email string `json:"email"`
}
<commit_msg>fillout agreement id field<commit_after>package billing

// Assent represents an assent by a person on behalf of an account to an agreement
type Assent struct {
	AgreementID string `json:"agreement_id,omitempty"`
	AccountID   int    `json:"account_id"`
	PersonID    int    `json:"person_id"`
	// Name is the full name of the person
	Name  string `json:"name"`
	Email string `json:"email"`
}
<|endoftext|>


<commit_before>package analysis

type CachingTokenFilter struct {
	*TokenFilter
}

func NewCachingTokenFilter(input TokenStream) *CachingTokenFilter {
	panic("not implemented yet")
}

func (f *CachingTokenFilter) Reset() {
	panic("not implemented yet")
}
<commit_msg>implement CachingTokenFilter ctor<commit_after>package analysis

import (
	"github.com/balzaczyy/golucene/core/util"
)

type CachingTokenFilter struct {
	*TokenFilter
	cache      []*util.AttributeState
	iterator   func() (*util.AttributeState, bool)
	finalState *util.AttributeState
}

func NewCachingTokenFilter(input TokenStream) *CachingTokenFilter {
	return &CachingTokenFilter{
		TokenFilter: NewTokenFilter(input),
	}
}

func (f *CachingTokenFilter) Reset() {
	panic("not implemented yet")
}
<|endoftext|>