<commit_before>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"strconv"
	"sync/atomic"
	"testing"
	"time"

	"github.com/buger/gor/listener"
	"github.com/buger/gor/replay"

	"math/rand"
)

func isEqual(t *testing.T, a interface{}, b interface{}) {
	if a != b {
		t.Error("Original and Replayed request not match\n", a, "!=", b)
	}
}

var envs int

type Env struct {
	Verbose bool

	ListenHandler http.HandlerFunc
	ReplayHandler http.HandlerFunc

	ReplayLimit   int
	ListenerLimit int
	ForwardPort   int
}

func (e *Env) start() (p int) {
	p = 50000 + envs*10

	go e.startHTTP(p, http.HandlerFunc(e.ListenHandler))
	go e.startHTTP(p+2, http.HandlerFunc(e.ReplayHandler))
	go e.startListener(p, p+1)
	go e.startReplay(p+1, p+2)

	// Time to start http and gor instances
	time.Sleep(time.Millisecond * 100)

	envs++

	return
}

func (e *Env) startListener(port int, replayPort int) {
	listener.Settings.Verbose = e.Verbose
	listener.Settings.Address = "127.0.0.1"
	listener.Settings.ReplayAddress = "127.0.0.1:" + strconv.Itoa(replayPort)
	listener.Settings.Port = port

	if e.ListenerLimit != 0 {
		listener.Settings.ReplayLimit = e.ListenerLimit
	}

	listener.Run()
}

func (e *Env) startReplay(port int, forwardPort int) {
	replay.Settings.Verbose = e.Verbose
	replay.Settings.Host = "127.0.0.1"
	replay.Settings.Address = "127.0.0.1:" + strconv.Itoa(port)
	replay.Settings.ForwardAddress = "127.0.0.1:" + strconv.Itoa(forwardPort)
	replay.Settings.Port = port

	if e.ReplayLimit != 0 {
		replay.Settings.ForwardAddress += "|" + strconv.Itoa(e.ReplayLimit)
	}

	replay.Run()
}

func (e *Env) startHTTP(port int, handler http.Handler) {
	err := http.ListenAndServe(":"+strconv.Itoa(port), handler)

	if err != nil {
		fmt.Println("Error while starting http server:", err)
	}
}

func getRequest(port int) *http.Request {
	var req *http.Request

	if rand.Int()%2 == 0 {
		req, _ = http.NewRequest("GET", "http://localhost:"+strconv.Itoa(port)+"/test", nil)
	} else {
		buf := bytes.NewReader([]byte("a=b&c=d"))

		req, _ = http.NewRequest("POST", "http://localhost:"+strconv.Itoa(port)+"/test", buf)
	}

	ck1 := new(http.Cookie)
	ck1.Name = "test"
	ck1.Value = "value"

	req.AddCookie(ck1)

	return req
}

func TestReplay(t *testing.T) {
	var request *http.Request
	received := make(chan int)

	listenHandler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "OK", http.StatusAccepted)
	}

	replayHandler := func(w http.ResponseWriter, r *http.Request) {
		isEqual(t, r.URL.Path, request.URL.Path)

		if len(r.Cookies()) > 0 {
			isEqual(t, r.Cookies()[0].Value, request.Cookies()[0].Value)
		} else {
			t.Error("Cookies should not be blank")
		}

		http.Error(w, "OK", http.StatusAccepted)

		if t.Failed() {
			fmt.Println("\nReplayed:", r, "\nOriginal:", request)
		}

		received <- 1
	}

	env := &Env{
		Verbose:       true,
		ListenHandler: listenHandler,
		ReplayHandler: replayHandler,
	}
	p := env.start()

	request = getRequest(p)

	_, err := http.DefaultClient.Do(request)

	if err != nil {
		t.Error("Can't make request", err)
	}

	select {
	case <-received:
	case <-time.After(time.Second):
		t.Error("Timeout error")
	}
}

func rateLimitEnv(replayLimit int, listenerLimit int, connCount int) int32 {
	var processed int32

	listenHandler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "OK", http.StatusAccepted)
	}

	replayHandler := func(w http.ResponseWriter, r *http.Request) {
		atomic.AddInt32(&processed, 1)
		http.Error(w, "OK", http.StatusAccepted)
	}

	env := &Env{
		ListenHandler: listenHandler,
		ReplayHandler: replayHandler,
		ReplayLimit:   replayLimit,
		ListenerLimit: listenerLimit,
		Verbose:       true,
	}

	p := env.start()
	req := getRequest(p)

	for i := 0; i < connCount; i++ {
		go func() {
			http.DefaultClient.Do(req)
		}()
	}

	time.Sleep(time.Millisecond * 500)

	return processed
}

func TestWithoutReplayRateLimit(t *testing.T) {
	processed := rateLimitEnv(0, 0, 10)

	if processed != 10 {
		t.Error("It should forward all requests without rate-limiting, got:", processed)
	}
}

func TestReplayRateLimit(t *testing.T) {
	processed := rateLimitEnv(5, 0, 10)

	if processed != 5 {
		t.Error("It should forward only 5 requests with rate-limiting, got:", processed)
	}
}

func TestListenerRateLimit(t *testing.T) {
	processed := rateLimitEnv(0, 3, 100)

	if processed != 3 {
		t.Error("It should forward only 3 requests with rate-limiting, got:", processed)
	}
}

func (e *Env) startFileListener() (p int) {
	p = 50000 + envs*10

	e.ForwardPort = p + 2
	go e.startHTTP(p, http.HandlerFunc(e.ListenHandler))
	go e.startHTTP(p+2, http.HandlerFunc(e.ReplayHandler))
	go e.startFileUsingListener(p, p+1)

	// Time to start http and gor instances
	time.Sleep(time.Millisecond * 100)

	envs++

	return
}

func (e *Env) startFileUsingListener(port int, replayPort int) {
	listener.Settings.Verbose = e.Verbose
	listener.Settings.Address = "127.0.0.1"
	listener.Settings.FileToReplyPath = "integration_request.gor"
	listener.Settings.Port = port

	if e.ListenerLimit != 0 {
		listener.Settings.ReplayAddress += "|" + strconv.Itoa(e.ListenerLimit)
	}

	listener.Run()
}

func (e *Env) startFileUsingReplay() {
	replay.Settings.Verbose = e.Verbose
	replay.Settings.FileToReplyPath = "integration_request.gor"
	replay.Settings.ForwardAddress = "127.0.0.1:" + strconv.Itoa(e.ForwardPort)

	if e.ReplayLimit != 0 {
		replay.Settings.ForwardAddress += "|" + strconv.Itoa(e.ReplayLimit)
	}

	replay.Run()
}

func TestSavingRequestToFileAndReplyThem(t *testing.T) {
	var request *http.Request
	processed := make(chan int)

	listenHandler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "OK", http.StatusNotFound)
	}

	requestsCount := 0
	var replayedRequests []*http.Request
	replayHandler := func(w http.ResponseWriter, r *http.Request) {
		requestsCount++

		isEqual(t, r.URL.Path, request.URL.Path)
		isEqual(t, r.Cookies()[0].Value, request.Cookies()[0].Value)

		http.Error(w, "404 page not found", http.StatusNotFound)

		replayedRequests = append(replayedRequests, r)
		if t.Failed() {
			fmt.Println("\nReplayed:", r, "\nOriginal:", request)
		}

		if requestsCount > 1 {
			processed <- 1
		}
	}

	env := &Env{
		Verbose:       true,
		ListenHandler: listenHandler,
		ReplayHandler: replayHandler,
	}

	p := env.startFileListener()

	request = getRequest(p)

	for i := 0; i < 2; i++ {
		go func() {
			_, err := http.DefaultClient.Do(request)

			if err != nil {
				t.Error("Can't make request", err)
			}
		}()
	}

	// TODO: wait until gor will process response, should be kind of flag/semaphore
	time.Sleep(time.Millisecond * 700)
	go env.startFileUsingReplay()

	select {
	case <-processed:
	case <-time.After(2 * time.Second):
		for _, value := range replayedRequests {
			fmt.Println(value)
		}
		t.Error("Timeout error")
	}
}
<commit_msg>Fixed test<commit_after>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"strconv"
	"sync/atomic"
	"testing"
	"time"

	"github.com/buger/gor/listener"
	"github.com/buger/gor/replay"

	"math/rand"
)

func isEqual(t *testing.T, a interface{}, b interface{}) {
	if a != b {
		t.Error("Original and Replayed request not match\n", a, "!=", b)
	}
}

var envs int

type Env struct {
	Verbose bool

	ListenHandler http.HandlerFunc
	ReplayHandler http.HandlerFunc

	ReplayLimit   int
	ListenerLimit int
	ForwardPort   int
}

func (e *Env) start() (p int) {
	p = 50000 + envs*10

	go e.startHTTP(p, http.HandlerFunc(e.ListenHandler))
	go e.startHTTP(p+2, http.HandlerFunc(e.ReplayHandler))
	go e.startListener(p, p+1)
	go e.startReplay(p+1, p+2)

	// Time to start http and gor instances
	time.Sleep(time.Millisecond * 100)

	envs++

	return
}

func (e *Env) startListener(port int, replayPort int) {
	listener.Settings.Verbose = e.Verbose
	listener.Settings.Address = "127.0.0.1"
	listener.Settings.ReplayAddress = "127.0.0.1:" + strconv.Itoa(replayPort)
	listener.Settings.Port = port

	if e.ListenerLimit != 0 {
		listener.Settings.ReplayLimit = e.ListenerLimit
	}

	listener.Run()
}

func (e *Env) startReplay(port int, forwardPort int) {
	replay.Settings.Verbose = e.Verbose
	replay.Settings.Host = "127.0.0.1"
	replay.Settings.Address = "127.0.0.1:" + strconv.Itoa(port)
	replay.Settings.ForwardAddress = "127.0.0.1:" + strconv.Itoa(forwardPort)
	replay.Settings.Port = port

	if e.ReplayLimit != 0 {
		replay.Settings.ForwardAddress += "|" + strconv.Itoa(e.ReplayLimit)
	}

	replay.Run()
}

func (e *Env) startHTTP(port int, handler http.Handler) {
	err := http.ListenAndServe(":"+strconv.Itoa(port), handler)

	if err != nil {
		fmt.Println("Error while starting http server:", err)
	}
}

func getRequest(port int) *http.Request {
	var req *http.Request

	rand.Seed(time.Now().UTC().UnixNano())

	if rand.Int31n(2) == 0 {
		req, _ = http.NewRequest("GET", "http://localhost:"+strconv.Itoa(port)+"/test", nil)
	} else {
		buf := bytes.NewReader([]byte("a=b&c=d"))
		req, _ = http.NewRequest("POST", "http://localhost:"+strconv.Itoa(port)+"/test", buf)
	}

	ck1 := new(http.Cookie)
	ck1.Name = "test"
	ck1.Value = "value"

	req.AddCookie(ck1)

	return req
}

func TestReplay(t *testing.T) {
	var request *http.Request
	received := make(chan int)

	listenHandler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "OK", http.StatusAccepted)
	}

	replayHandler := func(w http.ResponseWriter, r *http.Request) {
		isEqual(t, r.URL.Path, request.URL.Path)

		if len(r.Cookies()) > 0 {
			isEqual(t, r.Cookies()[0].Value, request.Cookies()[0].Value)
		} else {
			t.Error("Cookies should not be blank")
		}

		http.Error(w, "OK", http.StatusAccepted)

		if t.Failed() {
			fmt.Println("\nReplayed:", r, "\nOriginal:", request)
		}

		received <- 1
	}

	env := &Env{
		Verbose:       true,
		ListenHandler: listenHandler,
		ReplayHandler: replayHandler,
	}
	p := env.start()

	request = getRequest(p)

	_, err := http.DefaultClient.Do(request)

	if err != nil {
		t.Error("Can't make request", err)
	}

	select {
	case <-received:
	case <-time.After(time.Second):
		t.Error("Timeout error")
	}
}

func rateLimitEnv(replayLimit int, listenerLimit int, connCount int, t *testing.T) int32 {
	var processed int32

	listenHandler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "OK", http.StatusAccepted)
	}

	replayHandler := func(w http.ResponseWriter, r *http.Request) {
		atomic.AddInt32(&processed, 1)
		http.Error(w, "OK", http.StatusAccepted)
	}

	env := &Env{
		ListenHandler: listenHandler,
		ReplayHandler: replayHandler,
		ReplayLimit:   replayLimit,
		ListenerLimit: listenerLimit,
		Verbose:       true,
	}

	p := env.start()

	for i := 0; i < connCount; i++ {
		req := getRequest(p)

		go func() {
			resp, err := http.DefaultClient.Do(req)
			if err == nil {
				resp.Body.Close()
			} else {
				t.Errorf("", err)
			}
		}()
	}

	time.Sleep(time.Millisecond * 500)

	return processed
}

func TestWithoutReplayRateLimit(t *testing.T) {
	processed := rateLimitEnv(0, 0, 10, t)

	if processed != 10 {
		t.Error("It should forward all requests without rate-limiting, got:", processed)
	}
}

func TestReplayRateLimit(t *testing.T) {
	processed := rateLimitEnv(5, 0, 10, t)

	if processed != 5 {
		t.Error("It should forward only 5 requests with rate-limiting, got:", processed)
	}
}

func TestListenerRateLimit(t *testing.T) {
	processed := rateLimitEnv(0, 3, 100, t)

	if processed != 3 {
		t.Error("It should forward only 3 requests with rate-limiting, got:", processed)
	}
}

func (e *Env) startFileListener() (p int) {
	p = 50000 + envs*10

	e.ForwardPort = p + 2
	go e.startHTTP(p, http.HandlerFunc(e.ListenHandler))
	go e.startHTTP(p+2, http.HandlerFunc(e.ReplayHandler))
	go e.startFileUsingListener(p, p+1)

	// Time to start http and gor instances
	time.Sleep(time.Millisecond * 100)

	envs++

	return
}

func (e *Env) startFileUsingListener(port int, replayPort int) {
	listener.Settings.Verbose = e.Verbose
	listener.Settings.Address = "127.0.0.1"
	listener.Settings.FileToReplyPath = "integration_request.gor"
	listener.Settings.Port = port

	if e.ListenerLimit != 0 {
		listener.Settings.ReplayAddress += "|" + strconv.Itoa(e.ListenerLimit)
	}

	listener.Run()
}

func (e *Env) startFileUsingReplay() {
	replay.Settings.Verbose = e.Verbose
	replay.Settings.FileToReplyPath = "integration_request.gor"
	replay.Settings.ForwardAddress = "127.0.0.1:" + strconv.Itoa(e.ForwardPort)

	if e.ReplayLimit != 0 {
		replay.Settings.ForwardAddress += "|" + strconv.Itoa(e.ReplayLimit)
	}

	replay.Run()
}

func TestSavingRequestToFileAndReplyThem(t *testing.T) {
	var request *http.Request
	processed := make(chan int)

	listenHandler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "OK", http.StatusNotFound)
	}

	requestsCount := 0
	var replayedRequests []*http.Request
	replayHandler := func(w http.ResponseWriter, r *http.Request) {
		requestsCount++

		isEqual(t, r.URL.Path, request.URL.Path)
		isEqual(t, r.Cookies()[0].Value, request.Cookies()[0].Value)

		http.Error(w, "404 page not found", http.StatusNotFound)

		replayedRequests = append(replayedRequests, r)
		if t.Failed() {
			fmt.Println("\nReplayed:", r, "\nOriginal:", request)
		}

		if requestsCount > 1 {
			processed <- 1
		}
	}

	env := &Env{
		Verbose:       true,
		ListenHandler: listenHandler,
		ReplayHandler: replayHandler,
	}

	p := env.startFileListener()

	request = getRequest(p)

	for i := 0; i < 2; i++ {
		go func() {
			_, err := http.DefaultClient.Do(request)

			if err != nil {
				t.Error("Can't make request", err)
			}
		}()
	}

	// TODO: wait until gor will process response, should be kind of flag/semaphore
	time.Sleep(time.Millisecond * 700)
	go env.startFileUsingReplay()

	select {
	case <-processed:
	case <-time.After(2 * time.Second):
		for _, value := range replayedRequests {
			fmt.Println(value)
		}
		t.Error("Timeout error")
	}
}
<|endoftext|>


<commit_before>// Package cloudflare implements the Cloudflare v4 API.
package cloudflare

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/pkg/errors"
)

const apiURL = "https://api.cloudflare.com/client/v4"

// API holds the configuration for the current API client. A client should not
// be modified concurrently.
type API struct {
	APIKey     string
	APIEmail   string
	BaseURL    string
	headers    http.Header
	httpClient *http.Client
}

// New creates a new Cloudflare v4 API client.
func New(key, email string, opts ...Option) (*API, error) {
	if key == "" || email == "" {
		return nil, errors.New(errEmptyCredentials)
	}

	api := &API{
		APIKey:   key,
		APIEmail: email,
		BaseURL:  apiURL,
		headers:  make(http.Header),
	}

	err := api.parseOptions(opts...)
	if err != nil {
		return nil, errors.Wrap(err, "options parsing failed")
	}

	// Fall back to http.DefaultClient if the package user does not provide
	// their own.
	if api.httpClient == nil {
		api.httpClient = http.DefaultClient
	}

	return api, nil
}

// ZoneIDByName retrieves a zone's ID from the name.
func (api *API) ZoneIDByName(zoneName string) (string, error) {
	res, err := api.ListZones(zoneName)
	if err != nil {
		return "", errors.Wrap(err, "ListZones command failed")
	}
	for _, zone := range res {
		if zone.Name == zoneName {
			return zone.ID, nil
		}
	}
	return "", errors.New("Zone could not be found")
}

// makeRequest makes a HTTP request and returns the body as a byte slice,
// closing it before returnng. params will be serialized to JSON.
func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) {
	// Replace nil with a JSON object if needed
	var reqBody io.Reader
	if params != nil {
		json, err := json.Marshal(params)
		if err != nil {
			return nil, errors.Wrap(err, "error marshalling params to JSON")
		}
		reqBody = bytes.NewReader(json)
	} else {
		reqBody = nil
	}

	resp, err := api.request(method, uri, reqBody)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Wrap(err, "could not read response body")
	}

	switch resp.StatusCode {
	case http.StatusOK:
		break
	case http.StatusUnauthorized:
		return nil, errors.Errorf("HTTP status %d: invalid credentials", resp.StatusCode)
	case http.StatusForbidden:
		return nil, errors.Errorf("HTTP status %d: insufficient permissions", resp.StatusCode)
	case http.StatusServiceUnavailable, http.StatusBadGateway, http.StatusGatewayTimeout,
		522, 523, 524:
		return nil, errors.Errorf("HTTP status %d: service failure", resp.StatusCode)
	default:
		var s string
		if body != nil {
			s = string(body)
		}
		return nil, errors.Errorf("HTTP status %d: content %q", resp.StatusCode, s)
	}

	return body, nil
}

// request makes a HTTP request to the given API endpoint, returning the raw
// *http.Response, or an error if one occurred. The caller is responsible for
// closing the response body.
func (api *API) request(method, uri string, reqBody io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, api.BaseURL+uri, reqBody)
	if err != nil {
		return nil, errors.Wrap(err, "HTTP request creation failed")
	}

	// Apply any user-defined headers first.
	req.Header = cloneHeader(api.headers)
	req.Header.Set("X-Auth-Key", api.APIKey)
	req.Header.Set("X-Auth-Email", api.APIEmail)

	resp, err := api.httpClient.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "HTTP request failed")
	}

	return resp, nil
}

// cloneHeader returns a shallow copy of the header.
// copied from https://godoc.org/github.com/golang/gddo/httputil/header#Copy
func cloneHeader(header http.Header) http.Header {
	h := make(http.Header)
	for k, vs := range header {
		h[k] = vs
	}
	return h
}

// ResponseInfo contains a code and message returned by the API as errors or
// informational messages inside the response.
type ResponseInfo struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// Response is a template.  There will also be a result struct.  There will be a
// unique response type for each response, which will include this type.
type Response struct {
	Success  bool           `json:"success"`
	Errors   []ResponseInfo `json:"errors"`
	Messages []ResponseInfo `json:"messages"`
}

// ResultInfo contains metadata about the Response.
type ResultInfo struct {
	Page    int `json:"page"`
	PerPage int `json:"per_page"`
	Count   int `json:"count"`
	Total   int `json:"total_count"`
}
<commit_msg>feat(ResultInfo): add TotalPages field<commit_after>// Package cloudflare implements the Cloudflare v4 API.
package cloudflare

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/pkg/errors"
)

const apiURL = "https://api.cloudflare.com/client/v4"

// API holds the configuration for the current API client. A client should not
// be modified concurrently.
type API struct {
	APIKey     string
	APIEmail   string
	BaseURL    string
	headers    http.Header
	httpClient *http.Client
}

// New creates a new Cloudflare v4 API client.
func New(key, email string, opts ...Option) (*API, error) {
	if key == "" || email == "" {
		return nil, errors.New(errEmptyCredentials)
	}

	api := &API{
		APIKey:   key,
		APIEmail: email,
		BaseURL:  apiURL,
		headers:  make(http.Header),
	}

	err := api.parseOptions(opts...)
	if err != nil {
		return nil, errors.Wrap(err, "options parsing failed")
	}

	// Fall back to http.DefaultClient if the package user does not provide
	// their own.
	if api.httpClient == nil {
		api.httpClient = http.DefaultClient
	}

	return api, nil
}

// ZoneIDByName retrieves a zone's ID from the name.
func (api *API) ZoneIDByName(zoneName string) (string, error) {
	res, err := api.ListZones(zoneName)
	if err != nil {
		return "", errors.Wrap(err, "ListZones command failed")
	}
	for _, zone := range res {
		if zone.Name == zoneName {
			return zone.ID, nil
		}
	}
	return "", errors.New("Zone could not be found")
}

// makeRequest makes a HTTP request and returns the body as a byte slice,
// closing it before returnng. params will be serialized to JSON.
func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) {
	// Replace nil with a JSON object if needed
	var reqBody io.Reader
	if params != nil {
		json, err := json.Marshal(params)
		if err != nil {
			return nil, errors.Wrap(err, "error marshalling params to JSON")
		}
		reqBody = bytes.NewReader(json)
	} else {
		reqBody = nil
	}

	resp, err := api.request(method, uri, reqBody)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Wrap(err, "could not read response body")
	}

	switch resp.StatusCode {
	case http.StatusOK:
		break
	case http.StatusUnauthorized:
		return nil, errors.Errorf("HTTP status %d: invalid credentials", resp.StatusCode)
	case http.StatusForbidden:
		return nil, errors.Errorf("HTTP status %d: insufficient permissions", resp.StatusCode)
	case http.StatusServiceUnavailable, http.StatusBadGateway, http.StatusGatewayTimeout,
		522, 523, 524:
		return nil, errors.Errorf("HTTP status %d: service failure", resp.StatusCode)
	default:
		var s string
		if body != nil {
			s = string(body)
		}
		return nil, errors.Errorf("HTTP status %d: content %q", resp.StatusCode, s)
	}

	return body, nil
}

// request makes a HTTP request to the given API endpoint, returning the raw
// *http.Response, or an error if one occurred. The caller is responsible for
// closing the response body.
func (api *API) request(method, uri string, reqBody io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, api.BaseURL+uri, reqBody)
	if err != nil {
		return nil, errors.Wrap(err, "HTTP request creation failed")
	}

	// Apply any user-defined headers first.
	req.Header = cloneHeader(api.headers)
	req.Header.Set("X-Auth-Key", api.APIKey)
	req.Header.Set("X-Auth-Email", api.APIEmail)

	resp, err := api.httpClient.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "HTTP request failed")
	}

	return resp, nil
}

// cloneHeader returns a shallow copy of the header.
// copied from https://godoc.org/github.com/golang/gddo/httputil/header#Copy
func cloneHeader(header http.Header) http.Header {
	h := make(http.Header)
	for k, vs := range header {
		h[k] = vs
	}
	return h
}

// ResponseInfo contains a code and message returned by the API as errors or
// informational messages inside the response.
type ResponseInfo struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// Response is a template.  There will also be a result struct.  There will be a
// unique response type for each response, which will include this type.
type Response struct {
	Success  bool           `json:"success"`
	Errors   []ResponseInfo `json:"errors"`
	Messages []ResponseInfo `json:"messages"`
}

// ResultInfo contains metadata about the Response.
type ResultInfo struct {
	Page       int `json:"page"`
	PerPage    int `json:"per_page"`
	TotalPages int `json:"total_pages"`
	Count      int `json:"count"`
	Total      int `json:"total_count"`
}
<|endoftext|>


<commit_before>package internal

// ContextKey is a type used for Goji's context.Context keys.
type ContextKey int

const (
	// Path is the context key used to store the path Goji uses for its
	// PathPrefix optimization.
	Path ContextKey = iota
	// Pattern is the context key used to store the Pattern that Goji last
	// matched.
	Pattern
	// Handler is the context key used to store the Handler that Goji last
	// mached (and will therefore dispatch to at the end of the middleware
	// stack).
	Handler
)
<commit_msg>internal: pre-bake interface{} objects<commit_after>package internal

// ContextKey is a type used for Goji's context.Context keys.
type ContextKey int

var (
	// Path is the context key used to store the path Goji uses for its
	// PathPrefix optimization.
	Path interface{} = ContextKey(0)
	// Pattern is the context key used to store the Pattern that Goji last
	// matched.
	Pattern interface{} = ContextKey(1)
	// Handler is the context key used to store the Handler that Goji last
	// mached (and will therefore dispatch to at the end of the middleware
	// stack).
	Handler interface{} = ContextKey(2)
)
<|endoftext|>


<commit_before>package log

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"go.uber.org/zap"
	"golang.org/x/term"
)

var mu sync.Mutex
var wr io.Writer
var tty bool
var LogJSON = false
var logger *zap.SugaredLogger

// Level is the log level
// 0: silent  - do not log
// 1: normal  - show everything except debug and warn
// 2: verbose - show everything except debug
// 3: very verbose - show everything
var Level = 1

// SetOutput sets the output of the logger
func SetOutput(w io.Writer) {
	f, ok := w.(*os.File)
	tty = ok && term.IsTerminal(int(f.Fd()))
	wr = w
}

// Build a zap logger from default or custom config
func Build(c string) error {
	if c == "" {
		zcfg := zap.NewProductionConfig()
		// to be able to filter with Tile38 levels
		zcfg.Level.SetLevel(zap.DebugLevel)
		core, err := zcfg.Build()
		if err != nil {
			return err
		}
		defer core.Sync()
		logger = core.Sugar()
	} else {
		var zcfg zap.Config
		err := json.Unmarshal([]byte(c), &zcfg)
		if err != nil {
			return err
		}
		// to be able to filter with Tile38 levels
		zcfg.Level.SetLevel(zap.DebugLevel)
		core, err := zcfg.Build()
		if err != nil {
			return err
		}
		defer core.Sync()
		logger = core.Sugar()
	}
	return nil
}

// Set a zap logger
func Set(sl *zap.SugaredLogger) {
	logger = sl
}

// Get a zap logger
func Get() *zap.SugaredLogger {
	return logger
}

func init() {
	SetOutput(os.Stderr)
}

// Output retuns the output writer
func Output() io.Writer {
	return wr
}

func log(level int, tag, color string, formatted bool, format string, args ...interface{}) {
	if Level < level {
		return
	}
	s := []byte(time.Now().Format("2006/01/02 15:04:05"))
	s = append(s, ' ')
	if tty {
		s = append(s, color...)
	}
	s = append(s, '[')
	s = append(s, tag...)
	s = append(s, ']')
	if tty {
		s = append(s, "\x1b[0m"...)
	}
	s = append(s, ' ')
	if formatted {
		s = append(s, fmt.Sprintf(format, args...)...)
	} else {
		s = append(s, fmt.Sprint(args...)...)
	}
	if s[len(s)-1] != '\n' {
		s = append(s, '\n')
	}
	mu.Lock()
	wr.Write(s)
	mu.Unlock()
}

var emptyFormat string

// Infof ...
func Infof(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Infof(format, args...)
			return
		}
		log(1, "INFO", "\x1b[36m", true, format, args...)
	}
}

// Info ...
func Info(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Info(args...)
			return
		}
		log(1, "INFO", "\x1b[36m", false, emptyFormat, args...)
	}
}

// HTTPf ...
func HTTPf(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Infof(format, args...)
			return
		}
		log(1, "HTTP", "\x1b[1m\x1b[30m", true, format, args...)
	}
}

// HTTP ...
func HTTP(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Info(args...)
			return
		}
		log(1, "HTTP", "\x1b[1m\x1b[30m", false, emptyFormat, args...)
	}
}

// Errorf ...
func Errorf(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Errorf(format, args...)
			return
		}
		log(1, "ERRO", "\x1b[1m\x1b[31m", true, format, args...)
	}
}

// Error ..
func Error(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Errorf(emptyFormat, args...)
			return
		}
		log(1, "ERRO", "\x1b[1m\x1b[31m", false, emptyFormat, args...)
	}
}

// Warnf ...
func Warnf(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Warnf(format, args...)
			return
		}
		log(2, "WARN", "\x1b[33m", true, format, args...)
	}
}

// Warn ...
func Warn(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Warnf(emptyFormat, args...)
			return
		}
		log(2, "WARN", "\x1b[33m", false, emptyFormat, args...)
	}
}

// Debugf ...
func Debugf(format string, args ...interface{}) {
	if Level >= 3 {
		if LogJSON {
			logger.Debugf(format, args...)
			return
		}
		log(3, "DEBU", "\x1b[35m", true, format, args...)
	}
}

// Debug ...
func Debug(args ...interface{}) {
	if Level >= 3 {
		if LogJSON {
			logger.Debugf(emptyFormat, args...)
			return
		}
		log(3, "DEBU", "\x1b[35m", false, emptyFormat, args...)
	}
}

// Printf ...
func Printf(format string, args ...interface{}) {
	Infof(format, args...)
}

// Print ...
func Print(args ...interface{}) {
	Info(args...)
}

// Fatalf ...
func Fatalf(format string, args ...interface{}) {
	if LogJSON {
		logger.Fatalf(format, args...)
		os.Exit(1)
		return
	}

	log(1, "FATA", "\x1b[31m", true, format, args...)
	os.Exit(1)
}

// Fatal ...
func Fatal(args ...interface{}) {
	if LogJSON {
		logger.Fatalf(emptyFormat, args...)
		os.Exit(1)
		return
	}
	log(1, "FATA", "\x1b[31m", false, emptyFormat, args...)
	os.Exit(1)
}
<commit_msg>fix: disable caller<commit_after>package log

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"go.uber.org/zap"
	"golang.org/x/term"
)

var mu sync.Mutex
var wr io.Writer
var tty bool
var LogJSON = false
var logger *zap.SugaredLogger

// Level is the log level
// 0: silent  - do not log
// 1: normal  - show everything except debug and warn
// 2: verbose - show everything except debug
// 3: very verbose - show everything
var Level = 1

// SetOutput sets the output of the logger
func SetOutput(w io.Writer) {
	f, ok := w.(*os.File)
	tty = ok && term.IsTerminal(int(f.Fd()))
	wr = w
}

// Build a zap logger from default or custom config
func Build(c string) error {
	if c == "" {
		zcfg := zap.NewProductionConfig()

		// to be able to filter with Tile38 levels
		zcfg.Level.SetLevel(zap.DebugLevel)
		// disable caller because caller is always log.go
		zcfg.DisableCaller = true

		core, err := zcfg.Build()
		if err != nil {
			return err
		}
		defer core.Sync()
		logger = core.Sugar()
	} else {
		var zcfg zap.Config
		err := json.Unmarshal([]byte(c), &zcfg)
		if err != nil {
			return err
		}

		// to be able to filter with Tile38 levels
		zcfg.Level.SetLevel(zap.DebugLevel)
		// disable caller because caller is always log.go
		zcfg.DisableCaller = true

		core, err := zcfg.Build()
		if err != nil {
			return err
		}
		defer core.Sync()
		logger = core.Sugar()
	}
	return nil
}

// Set a zap logger
func Set(sl *zap.SugaredLogger) {
	logger = sl
}

// Get a zap logger
func Get() *zap.SugaredLogger {
	return logger
}

func init() {
	SetOutput(os.Stderr)
}

// Output retuns the output writer
func Output() io.Writer {
	return wr
}

func log(level int, tag, color string, formatted bool, format string, args ...interface{}) {
	if Level < level {
		return
	}
	s := []byte(time.Now().Format("2006/01/02 15:04:05"))
	s = append(s, ' ')
	if tty {
		s = append(s, color...)
	}
	s = append(s, '[')
	s = append(s, tag...)
	s = append(s, ']')
	if tty {
		s = append(s, "\x1b[0m"...)
	}
	s = append(s, ' ')
	if formatted {
		s = append(s, fmt.Sprintf(format, args...)...)
	} else {
		s = append(s, fmt.Sprint(args...)...)
	}
	if s[len(s)-1] != '\n' {
		s = append(s, '\n')
	}
	mu.Lock()
	wr.Write(s)
	mu.Unlock()
}

var emptyFormat string

// Infof ...
func Infof(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Infof(format, args...)
			return
		}
		log(1, "INFO", "\x1b[36m", true, format, args...)
	}
}

// Info ...
func Info(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Info(args...)
			return
		}
		log(1, "INFO", "\x1b[36m", false, emptyFormat, args...)
	}
}

// HTTPf ...
func HTTPf(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Infof(format, args...)
			return
		}
		log(1, "HTTP", "\x1b[1m\x1b[30m", true, format, args...)
	}
}

// HTTP ...
func HTTP(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Info(args...)
			return
		}
		log(1, "HTTP", "\x1b[1m\x1b[30m", false, emptyFormat, args...)
	}
}

// Errorf ...
func Errorf(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Errorf(format, args...)
			return
		}
		log(1, "ERRO", "\x1b[1m\x1b[31m", true, format, args...)
	}
}

// Error ..
func Error(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Errorf(emptyFormat, args...)
			return
		}
		log(1, "ERRO", "\x1b[1m\x1b[31m", false, emptyFormat, args...)
	}
}

// Warnf ...
func Warnf(format string, args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Warnf(format, args...)
			return
		}
		log(2, "WARN", "\x1b[33m", true, format, args...)
	}
}

// Warn ...
func Warn(args ...interface{}) {
	if Level >= 1 {
		if LogJSON {
			logger.Warnf(emptyFormat, args...)
			return
		}
		log(2, "WARN", "\x1b[33m", false, emptyFormat, args...)
	}
}

// Debugf ...
func Debugf(format string, args ...interface{}) {
	if Level >= 3 {
		if LogJSON {
			logger.Debugf(format, args...)
			return
		}
		log(3, "DEBU", "\x1b[35m", true, format, args...)
	}
}

// Debug ...
func Debug(args ...interface{}) {
	if Level >= 3 {
		if LogJSON {
			logger.Debugf(emptyFormat, args...)
			return
		}
		log(3, "DEBU", "\x1b[35m", false, emptyFormat, args...)
	}
}

// Printf ...
func Printf(format string, args ...interface{}) {
	Infof(format, args...)
}

// Print ...
func Print(args ...interface{}) {
	Info(args...)
}

// Fatalf ...
func Fatalf(format string, args ...interface{}) {
	if LogJSON {
		logger.Fatalf(format, args...)
		os.Exit(1)
		return
	}

	log(1, "FATA", "\x1b[31m", true, format, args...)
	os.Exit(1)
}

// Fatal ...
func Fatal(args ...interface{}) {
	if LogJSON {
		logger.Fatalf(emptyFormat, args...)
		os.Exit(1)
		return
	}
	log(1, "FATA", "\x1b[31m", false, emptyFormat, args...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package jc

import (
	"reflect"
	"unicode"
	"fmt"
	"errors"
	"strings"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2"
	"github.com/kalcok/jc/tools"
)

type document interface {
	setCollection(string)
	CollectionName() string
	SetDatabase(string)
	Database() string
	Init(reflect.Value, reflect.Type)
	InitDB() error
	Info()
	Save(bool) (*mgo.ChangeInfo, error)
}

type Collection struct {
	_collectionName  string                `bson:"-"json:"-"`
	_collectionDB    string                `bson:"-"json:"-"`
	_parent          reflect.Value         `bson:"-"json:"-"`
	_parentType      reflect.Type          `bson:"-"json:"-"`
	_hasExplicitID   bool                  `bson:"-"json:"-"`
	_explicitIDField string                `bson:"-"json:"-"`
	_implicitIDValue bson.ObjectId         `bson:"-"json:"-"`
	_skeleton        []reflect.StructField `bson:"-"json:"-"`
}

func (c *Collection) setCollection(name string) {
	c._collectionName = name
}

func (c *Collection) CollectionName() string {
	return c._collectionName
}

func (c *Collection) SetDatabase(name string) {
	c._collectionDB = name

}

func (c *Collection) Database() string {
	return c._collectionDB
}

func (c *Collection) Info() {
	fmt.Printf("Database %s\n", c._collectionDB)
	fmt.Printf("Collection %s\n", c._collectionName)
	fmt.Printf("Parent__ %s\n", c._parent)
}

func (c *Collection) Save(reuseSocket bool) (info *mgo.ChangeInfo, err error) {
	var session *mgo.Session
	master_session, err := tools.GetSession()
	var documentID interface{}
	idField := "_id"

	if err != nil {
		return info, err
	}

	if reuseSocket {
		session = master_session.Clone()
	} else {
		session = master_session.Copy()
	}

	if c._hasExplicitID {
		idField = c._explicitIDField
		documentID = c._parent.Elem().FieldByName(c._explicitIDField).Interface()
	} else if len(c._implicitIDValue) > 0 {
		documentID = c._implicitIDValue
	} else {
		c._implicitIDValue = bson.NewObjectId()
		documentID = c._implicitIDValue
	}

	collection := session.DB(c._collectionDB).C(c._collectionName)
	info, err = collection.Upsert(bson.M{idField: documentID}, c._parent.Interface())

	return info, err
}

func (c *Collection) Init(parent reflect.Value, parentType reflect.Type) {

	c._parent = parent
	c._parentType = parentType
	c._hasExplicitID = false
	for i := 0; i < reflect.Indirect(c._parent).NumField(); i++ {
		field := c._parentType.Field(i)

		// Find explicit Collection name
		if field.Type == reflect.TypeOf(Collection{}) {
			explicitName := false
			odm_tag, tag_present := field.Tag.Lookup("odm")
			if tag_present {
				odm_fields := strings.Split(odm_tag, ",")
				if len(odm_fields) > 0 && odm_fields[0] != "" {
					c.setCollection(odm_fields[0])
					explicitName = true
				}
			}
			if !explicitName {
				c.setCollection(camelToSnake(parentType.Name()))
			}
		}

		// Find explicit index field
		bson_tag, tag_present := field.Tag.Lookup("bson")
		if tag_present {
			field_id := strings.Split(bson_tag, ",")
			switch field_id[0] {
			case "_id":
				c._explicitIDField = field.Name
				c._hasExplicitID = true
				break
			case "-":
				continue
			default:
				break
			}
		}
		c._skeleton = append(c._skeleton, field)
	}

}

func (c *Collection) InitDB() error {
	session, err := tools.GetSession()
	if err == nil {
		c.SetDatabase(session.DB("").Name)
	} else {
		err = errors.New("database not initialized")
	}
	return err
}

func NewDocument(c document) error {
	var err error
	objectType := reflect.TypeOf(c).Elem()
	objectValue := reflect.ValueOf(c)
	c.Init(objectValue, objectType)
	err = c.InitDB()

	return err
}

func camelToSnake(camel string) string {
	var (
		snake_name []rune
		next       rune
	)
	for i, c := range camel {
		if unicode.IsUpper(c) && i != 0 {
			snake_name = append(snake_name, '_')
		}
		next = unicode.ToLower(c)
		snake_name = append(snake_name, next)
	}
	return string(snake_name)
}

func initPrototype(prototype reflect.Value, internalType reflect.Type) reflect.Value {
	var inputs []reflect.Value
	inputs = append(inputs, reflect.ValueOf(prototype))
	inputs = append(inputs, reflect.ValueOf(internalType))
	prototype.MethodByName("Init").Call(inputs)
	prototype.MethodByName("InitDB").Call(nil)

	return reflect.Indirect(prototype)

}
<commit_msg>Interface to retrieve ID of document (whether implicit or explicit)<commit_after>package jc

import (
	"reflect"
	"unicode"
	"fmt"
	"errors"
	"strings"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2"
	"github.com/kalcok/jc/tools"
)

type document interface {
	setCollection(string)
	CollectionName() string
	SetDatabase(string)
	Database() string
	ID() interface{}
	Init(reflect.Value, reflect.Type)
	InitDB() error
	Info()
	Save(bool) (*mgo.ChangeInfo, error)
}

type Collection struct {
	_collectionName  string                `bson:"-"json:"-"`
	_collectionDB    string                `bson:"-"json:"-"`
	_parent          reflect.Value         `bson:"-"json:"-"`
	_parentType      reflect.Type          `bson:"-"json:"-"`
	_hasExplicitID   bool                  `bson:"-"json:"-"`
	_explicitIDField string                `bson:"-"json:"-"`
	_implicitIDValue bson.ObjectId         `bson:"-"json:"-"`
	_skeleton        []reflect.StructField `bson:"-"json:"-"`
}

func (c *Collection) setCollection(name string) {
	c._collectionName = name
}

func (c *Collection) CollectionName() string {
	return c._collectionName
}

func (c *Collection) SetDatabase(name string) {
	c._collectionDB = name

}

func (c *Collection) Database() string {
	return c._collectionDB
}

func (c *Collection) Info() {
	fmt.Printf("Database %s\n", c._collectionDB)
	fmt.Printf("Collection %s\n", c._collectionName)
	fmt.Printf("Parent__ %s\n", c._parent)
}

func (c *Collection) Save(reuseSocket bool) (info *mgo.ChangeInfo, err error) {
	var session *mgo.Session
	master_session, err := tools.GetSession()
	var documentID interface{}
	idField := "_id"

	if err != nil {
		return info, err
	}

	if reuseSocket {
		session = master_session.Clone()
	} else {
		session = master_session.Copy()
	}

	if c._hasExplicitID {
		idField = c._explicitIDField
		documentID = c._parent.Elem().FieldByName(c._explicitIDField).Interface()
	} else if len(c._implicitIDValue) > 0 {
		documentID = c._implicitIDValue
	} else {
		c._implicitIDValue = bson.NewObjectId()
		documentID = c._implicitIDValue
	}

	collection := session.DB(c._collectionDB).C(c._collectionName)
	info, err = collection.Upsert(bson.M{idField: documentID}, c._parent.Interface())

	return info, err
}

func (c *Collection) ID() (id interface{}) {
	if c._hasExplicitID {
		id = c._parent.Elem().FieldByName(c._explicitIDField).Interface()
	} else {
		id = c._implicitIDValue
	}
	return id
}

func (c *Collection) Init(parent reflect.Value, parentType reflect.Type) {

	c._parent = parent
	c._parentType = parentType
	c._hasExplicitID = false
	for i := 0; i < reflect.Indirect(c._parent).NumField(); i++ {
		field := c._parentType.Field(i)

		// Find explicit Collection name
		if field.Type == reflect.TypeOf(Collection{}) {
			explicitName := false
			odm_tag, tag_present := field.Tag.Lookup("odm")
			if tag_present {
				odm_fields := strings.Split(odm_tag, ",")
				if len(odm_fields) > 0 && odm_fields[0] != "" {
					c.setCollection(odm_fields[0])
					explicitName = true
				}
			}
			if !explicitName {
				c.setCollection(camelToSnake(parentType.Name()))
			}
		}

		// Find explicit index field
		bson_tag, tag_present := field.Tag.Lookup("bson")
		if tag_present {
			field_id := strings.Split(bson_tag, ",")
			switch field_id[0] {
			case "_id":
				c._explicitIDField = field.Name
				c._hasExplicitID = true
				break
			case "-":
				continue
			default:
				break
			}
		}
		c._skeleton = append(c._skeleton, field)
	}

}

func (c *Collection) InitDB() error {
	session, err := tools.GetSession()
	if err == nil {
		c.SetDatabase(session.DB("").Name)
	} else {
		err = errors.New("database not initialized")
	}
	return err
}

func NewDocument(c document) error {
	var err error
	objectType := reflect.TypeOf(c).Elem()
	objectValue := reflect.ValueOf(c)
	c.Init(objectValue, objectType)
	err = c.InitDB()

	return err
}

func camelToSnake(camel string) string {
	var (
		snake_name []rune
		next       rune
	)
	for i, c := range camel {
		if unicode.IsUpper(c) && i != 0 {
			snake_name = append(snake_name, '_')
		}
		next = unicode.ToLower(c)
		snake_name = append(snake_name, next)
	}
	return string(snake_name)
}

func initPrototype(prototype reflect.Value, internalType reflect.Type) reflect.Value {
	var inputs []reflect.Value
	inputs = append(inputs, reflect.ValueOf(prototype))
	inputs = append(inputs, reflect.ValueOf(internalType))
	prototype.MethodByName("Init").Call(inputs)
	prototype.MethodByName("InitDB").Call(nil)

	return reflect.Indirect(prototype)

}
<|endoftext|>


<commit_before>// Copyright 2013 The go-logger Authors. All rights reserved.
// This code is MIT licensed. See the LICENSE file for more info.

package log

import (
	"fmt"
	"testing"

	"github.com/demizer/rgbterm"
)

var colorTests = []struct {
	escapeCodes string
	output      string
}{
	{rgbterm.String("red foreground color", 255, 0, 0),
		"\x1b[38;5;196mred foreground color\x1b[0;00m"},
	{rgbterm.String("green foreground color", 0, 255, 0),
		"\x1b[38;5;46mgreen foreground color\x1b[0;00m"},
	{rgbterm.String("blue foreground color", 0, 0, 255),
		"\x1b[38;5;21mblue foreground color\x1b[0;00m"},
	// {[]interface{}{ANSI_BOLD, "bold text attribute"},
	// "\x1b[1mbold text attribute\x1b[0m"},
	// {[]interface{}{ANSI_UNDERLINE, "underline text attribute"},
	// "\x1b[4munderline text attribute\x1b[0m"},
	// {[]interface{}{ANSI_BLINK, "blink text attribute"},
	// "\x1b[5mblink text attribute\x1b[0m"},
	// {[]interface{}{ANSI_REVERSE, "reverse text attribute"},
	// "\x1b[7mreverse text attribute\x1b[0m"},
	// {[]interface{}{ANSI_CONCEALED, "concealed text attribute"},
	// "\x1b[8mconcealed text attribute\x1b[0m"},
	// {[]interface{}{ANSI_BLACK, "black foreground color"},
	// "\x1b[30mblack foreground color\x1b[0m"},
	// {[]interface{}{ANSI_BG_YELLOW, ANSI_BOLD, ANSI_RED,
	// "bold red text with yellow background",
	// ANSI_BG_CYAN, ANSI_GREEN, "green text with cyan background"},
	// "\x1b[43m\x1b[1m\x1b[31mbold red text with yellow background" +
	// "\x1b[46m\x1b[32mgreen text with cyan background\x1b[0m"},
	// {[]interface{}{ANSI_UNDERLINE, ANSI_GREEN, "green underline text"},
	// "\x1b[4m\x1b[32mgreen underline text\x1b[0m"},
	// {[]interface{}{ANSI_BOLD, ANSI_UNDERLINE, ANSI_GREEN,
	// "bold green underline text"},
	// "\x1b[1m\x1b[4m\x1b[32mbold green underline text\x1b[0m"},
	// {[]interface{}{ANSI_BOLD, ANSI_GREEN, "colored ", ANSI_OFF,
	// "to normal text"},
	// "\x1b[1m\x1b[32mcolored \x1b[0mto normal text\x1b[0m"},
	// {[]interface{}{ANSI_BOLD, ANSI_GREEN, "colored", ANSI_OFF},
	// "\x1b[1m\x1b[32mcolored\x1b[0m"},
}

func TestColors(t *testing.T) {
	for i, v := range colorTests {
		if out := v.escapeCodes; out != v.output {
			fmt.Println(v.escapeCodes)
			t.Errorf("Test Number: %d\nGot:\t%q\nExpect:\t%q\n", i,
				v.escapeCodes, v.output)
		}
	}
}
<commit_msg>Remove background color tests<commit_after>// Copyright 2013 The go-logger Authors. All rights reserved.
// This code is MIT licensed. See the LICENSE file for more info.

package log

import (
	"fmt"
	"testing"

	"github.com/demizer/rgbterm"
)

var colorTests = []struct {
	escapeCodes string
	output      string
}{
	{rgbterm.String("red foreground color", 255, 0, 0),
		"\x1b[38;5;196mred foreground color\x1b[0;00m"},
	{rgbterm.String("green foreground color", 0, 255, 0),
		"\x1b[38;5;46mgreen foreground color\x1b[0;00m"},
	{rgbterm.String("blue foreground color", 0, 0, 255),
		"\x1b[38;5;21mblue foreground color\x1b[0;00m"},
}

func TestColors(t *testing.T) {
	for i, v := range colorTests {
		if out := v.escapeCodes; out != v.output {
			fmt.Println(v.escapeCodes)
			t.Errorf("Test Number: %d\nGot:\t%q\nExpect:\t%q\n", i,
				v.escapeCodes, v.output)
		}
	}
}
<|endoftext|>


<commit_before>package pprof

import (
	"net/http"
	"net/http/pprof"
	runtime_pprof "runtime/pprof"
	"strings"
)

type handler struct {
}

var profileHandlers map[string]http.Handler

func init() {
	profileHandlers = make(map[string]http.Handler)
	for _, profile := range runtime_pprof.Profiles() {
		profileHandlers[profile.Name()] = pprof.Handler(profile.Name())
	}
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	// horrible
	parts := strings.Split(r.URL.Path, "/")
	if len(parts) < 3 || len(parts) > 4 || parts[1] != "debug" || parts[2] != "pprof" {
		next(w, r)
		return
	}

	if len(parts) == 3 || (len(parts) == 4 && parts[3] == "") {
		pprof.Index(w, r)
		return
	}

	handler := profileHandlers[parts[3]]
	if handler == nil {
		next(w, r)
		return
	}
	handler.ServeHTTP(w, r)
	return
}

func Pprof() *handler {
	return &handler{}
}
<commit_msg>cleanup the path detection<commit_after>package pprof

import (
	"net/http"
	"net/http/pprof"
	runtime_pprof "runtime/pprof"
	"strings"
)

type handler struct {
}

var profileHandlers map[string]http.Handler

func init() {
	profileHandlers = make(map[string]http.Handler)
	for _, profile := range runtime_pprof.Profiles() {
		profileHandlers[profile.Name()] = pprof.Handler(profile.Name())
	}
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

	if !strings.HasPrefix(r.URL.Path, "/debug/pprof") {
		next(w, r)
		return
	}

	parts := strings.Split(r.URL.Path, "/")

	if len(parts) > 4 {
		next(w, r)
		return
	}

	if len(parts) == 3 || (len(parts) == 4 && parts[3] == "") {
		pprof.Index(w, r)
		return
	}

	handler := profileHandlers[parts[3]]
	if handler == nil {
		next(w, r)
		return
	}
	handler.ServeHTTP(w, r)
	return
}

func Pprof() *handler {
	return &handler{}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/serbe/pool"
)

func findProxy() {
	var addedProxy int64
	debugmsg("Start find proxy")
	p := pool.New(numWorkers)
	p.SetTimeout(timeout)
	ml := getMapLink()
	list := getProxyListFromDB()
	mp := newMapProxy()
	mp.fillMapProxy(list)

	mp.loadProxyFromFile()

	debugmsg("start add to pool")
	p.SetTimeout(timeout)
	p.SetQuitTimeout(2000)
	for _, link := range ml.values {
		if link.Iterate && time.Since(link.UpdateAt) > time.Duration(1)*time.Hour {
			chkErr("findProxy p.Add", p.Add(link.Hostname, nil))
		}
	}
	if p.GetAddedTasks() == 0 {
		debugmsg("not added tasks to pool")
		return
	}
	debugmsg("end add to pool, added", p.GetAddedTasks(), "links")
	debugmsg("start get from chan")
	for result := range p.ResultChan {
		if result.Error != nil {
			continue
		}
		ml.update(result.Hostname)
		links := ml.getNewLinksFromTask(result)
		num := mp.numOfNewProxyInTask(result)
		if num > 0 {
			if link, ok := ml.get(result.Hostname); ok {
				link.Num = link.Num + num
				ml.set(link)
				debugmsg("find", num, "in", result.Hostname)
			}
		}
		for _, l := range links {
			chkErr("findProxy add to pool", p.Add(l.Hostname, nil))
			debugmsg("add to pool", l.Hostname)
		}
		addedProxy = addedProxy + num
	}
	if testLink == "" {
		debugmsg("save proxy")
		ml.saveAll()
	}
	debugmsg(addedProxy, "new proxy found")
	debugmsg("end findProxy")
}

func checkProxy() {
	debugmsg("start checkProxy")
	var (
		totalProxy int64
		anonProxy  int64
		err        error
	)
	list := getProxyListFromDB()
	for len(list) > 0 {
		mp := newMapProxy()
		var r = 10000
		if r > len(list) {
			r = len(list)
		}
		for i := 0; i < r; i++ {
			mp.set(list[i])
		}
		list = list[r:]
		p := pool.New(numWorkers)
		p.SetTimeout(timeout)
		targetURL := getTarget()
		myIP, err = getExternalIP()
		if err != nil {
			errmsg("checkProxy getExternalIP", err)
			return
		}
		debugmsg("start add to pool")
		for _, proxy := range mp.values {
			if useCheckAll || proxyIsOld(proxy) {
				proxyURL, err := url.Parse(proxy.Hostname)
				chkErr("parse url", err)
				chkErr("add to pool", p.Add(targetURL, proxyURL))
			}
		}
		debugmsg("end add to pool")
		p.EndWaitingTasks()
		log.Println("Start check", p.GetAddedTasks(), "proxies")
		if p.GetAddedTasks() == 0 {
			debugmsg("no task added to pool")
			return
		}
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		var checked int
	checkProxyLoop:
		for {
			select {
			case task, ok := <-p.ResultChan:
				if !ok {
					debugmsg("break loop by close chan ResultChan")
					break checkProxyLoop
				}
				checked++
				proxy, isOk := mp.taskToProxy(task)
				if !isOk {
					continue
				}
				mp.set(proxy)
				saveProxy(proxy)
				if proxy.IsWork {
					log.Printf("%d/%d %-15v %-5v %-12v anon=%v\n", checked, p.GetAddedTasks(), task.Proxy.Hostname(), task.Proxy.Port(), task.ResponceTime, proxy.IsAnon)
					totalProxy++
					if proxy.IsAnon {
						anonProxy++
					}
				}
			case <-c:
				debugmsg("break loop by pressing ctrl+c")
				break checkProxyLoop
			}
		}
		log.Printf("checked %d ip\n", p.GetAddedTasks())
	}
	log.Printf("%d is good\n", totalProxy)
	log.Printf("%d is anon\n", anonProxy)
	debugmsg("end checkProxy")
}
<commit_msg>add global break<commit_after>package main

import (
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/serbe/pool"
)

func findProxy() {
	var addedProxy int64
	debugmsg("Start find proxy")
	p := pool.New(numWorkers)
	p.SetTimeout(timeout)
	ml := getMapLink()
	list := getProxyListFromDB()
	mp := newMapProxy()
	mp.fillMapProxy(list)

	mp.loadProxyFromFile()

	debugmsg("start add to pool")
	p.SetTimeout(timeout)
	p.SetQuitTimeout(2000)
	for _, link := range ml.values {
		if link.Iterate && time.Since(link.UpdateAt) > time.Duration(1)*time.Hour {
			chkErr("findProxy p.Add", p.Add(link.Hostname, nil))
		}
	}
	if p.GetAddedTasks() == 0 {
		debugmsg("not added tasks to pool")
		return
	}
	debugmsg("end add to pool, added", p.GetAddedTasks(), "links")
	debugmsg("start get from chan")
	for result := range p.ResultChan {
		if result.Error != nil {
			continue
		}
		ml.update(result.Hostname)
		links := ml.getNewLinksFromTask(result)
		num := mp.numOfNewProxyInTask(result)
		if num > 0 {
			if link, ok := ml.get(result.Hostname); ok {
				link.Num = link.Num + num
				ml.set(link)
				debugmsg("find", num, "in", result.Hostname)
			}
		}
		for _, l := range links {
			chkErr("findProxy add to pool", p.Add(l.Hostname, nil))
			debugmsg("add to pool", l.Hostname)
		}
		addedProxy = addedProxy + num
	}
	if testLink == "" {
		debugmsg("save proxy")
		ml.saveAll()
	}
	debugmsg(addedProxy, "new proxy found")
	debugmsg("end findProxy")
}

func checkProxy() {
	debugmsg("start checkProxy")
	var (
		totalProxy int64
		anonProxy  int64
		err        error
	)
	list := getProxyListFromDB()
breakCheckProxyLoop:
	for len(list) > 0 {
		mp := newMapProxy()
		var r = 10000
		if r > len(list) {
			r = len(list)
		}
		for i := 0; i < r; i++ {
			mp.set(list[i])
		}
		list = list[r:]
		p := pool.New(numWorkers)
		p.SetTimeout(timeout)
		targetURL := getTarget()
		myIP, err = getExternalIP()
		if err != nil {
			errmsg("checkProxy getExternalIP", err)
			return
		}
		debugmsg("start add to pool")
		for _, proxy := range mp.values {
			if useCheckAll || proxyIsOld(proxy) {
				proxyURL, err := url.Parse(proxy.Hostname)
				chkErr("parse url", err)
				chkErr("add to pool", p.Add(targetURL, proxyURL))
			}
		}
		debugmsg("end add to pool")
		p.EndWaitingTasks()
		log.Println("Start check", p.GetAddedTasks(), "proxies")
		if p.GetAddedTasks() == 0 {
			debugmsg("no task added to pool")
			return
		}
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		var checked int
	checkProxyLoop:
		for {
			select {
			case task, ok := <-p.ResultChan:
				if !ok {
					debugmsg("break loop by close chan ResultChan")
					break checkProxyLoop
				}
				checked++
				proxy, isOk := mp.taskToProxy(task)
				if !isOk {
					continue
				}
				mp.set(proxy)
				if !useFUP {
					saveProxy(proxy)
				}
				if proxy.IsWork {
					if useFUP {
						saveProxy(proxy)
					}
					log.Printf("%d/%d %-15v %-5v %-12v anon=%v\n", checked, p.GetAddedTasks(), task.Proxy.Hostname(), task.Proxy.Port(), task.ResponceTime, proxy.IsAnon)
					totalProxy++
					if proxy.IsAnon {
						anonProxy++
					}
				}
			case <-c:
				debugmsg("break loop by pressing ctrl+c")
				break breakCheckProxyLoop
			}
		}
		log.Printf("checked %d ip\n", p.GetAddedTasks())
	}
	log.Printf("%d is good\n", totalProxy)
	log.Printf("%d is anon\n", anonProxy)
	debugmsg("end checkProxy")
}
<|endoftext|>


<commit_before>/*
Package psort implement a topological sort on a collection of partially ordered items.
*/
package psort

// Interface defines a collection of items that are partially ordered
type Interface interface {
	// Len is the number of elements in the collection
	Len() int

	// LessOrEqual reports whether the element I with index i
	// and the element J with index j are related such that i <= j
	LessOrEqual(i, j int) bool

	// Swap the elements at indexes i and j
	Swap(i, j int)
}

// Sort performs an in-place topological sort on a partially ordered collection of items.
// Returns false if the list could not be sorted, which happens if a cycle is found.
//
// The implementation is based on Tarjan's algorithm.
// See: https://en.wikipedia.org/wiki/Topological_sorting#Tarjan.27s_algorithm
//
// It is an order O(n) operation.
func Sort(data Interface) bool {
	// A mapping of indicies to real indicies of the data
	index := make([]int, data.Len())
	for i := 0; i < len(index); i++ {
		index[i] = i
	}

	// Indicates which elements have been marked
	marked := make([]bool, len(index))

	// An index into the data collection, indicating the current location of the head of the list
	head := len(index) - 1

	var visitFn func(int) bool
	visitFn = func(n int) bool {
		if marked[n] {
			return false
		}
		marked[n] = true

		for m := 0; m < len(index); m++ {
			if m == n || index[m] > head {
				continue
			}

			// Check if there is an 'edge' from n to m
			// Equivalent to saying n <= m
			if data.LessOrEqual(index[n], index[m]) {
				if !visitFn(m) {
					return false
				}
			}
		}

		// Add n to the head
		realN := index[n]
		if realN != head {
			var headIndex int
			for headIndex = 0; index[headIndex] != head; headIndex++ {
			}
			data.Swap(realN, head)
			index[headIndex] = realN
			index[n] = head
			marked[n] = marked[headIndex]
		}
		head--
		return true
	}

	for head >= 0 {
		var i int
		for i = 0; i < len(index) && index[i] > head; i++ {
		}
		if i == len(index) {
			return false
		}
		if !visitFn(i) {
			return false
		}
	}

	return true
}

// IsSorted determines if the given collection of partialy ordered items is sorted
func IsSorted(data Interface) bool {
	length := data.Len()
	for i := 1; i < length; i++ {
		if data.LessOrEqual(i, i-1) {
			return false
		}
	}
	return true
}

// Reverse performs an in-place reversal of the order of the items in the collection
func Reverse(data Interface) {
	head := 0
	tail := data.Len() - 1
	for head < tail {
		data.Swap(head, tail)
		head++
		tail--
	}
}
<commit_msg>Removed comment about algorithmic complexity-not true given in-place update scheme<commit_after>/*
Package psort implement a topological sort on a collection of partially ordered items.
*/
package psort

// Interface defines a collection of items that are partially ordered
type Interface interface {
	// Len is the number of elements in the collection
	Len() int

	// LessOrEqual reports whether the element I with index i
	// and the element J with index j are related such that i <= j
	LessOrEqual(i, j int) bool

	// Swap the elements at indexes i and j
	Swap(i, j int)
}

// Sort performs an in-place topological sort on a partially ordered collection of items.
// Returns false if the list could not be sorted, which happens if a cycle is found.
//
// The implementation is based on Tarjan's algorithm.
// See: https://en.wikipedia.org/wiki/Topological_sorting#Tarjan.27s_algorithm
func Sort(data Interface) bool {
	// A mapping of indicies to real indicies of the data
	index := make([]int, data.Len())
	for i := 0; i < len(index); i++ {
		index[i] = i
	}

	// Indicates which elements have been marked
	marked := make([]bool, len(index))

	// An index into the data collection, indicating the current location of the head of the list
	head := len(index) - 1

	var visitFn func(int) bool
	visitFn = func(n int) bool {
		if marked[n] {
			return false
		}
		marked[n] = true

		for m := 0; m < len(index); m++ {
			if m == n || index[m] > head {
				continue
			}

			// Check if there is an 'edge' from n to m
			// Equivalent to saying n <= m
			if data.LessOrEqual(index[n], index[m]) {
				if !visitFn(m) {
					return false
				}
			}
		}

		// Add n to the head
		realN := index[n]
		if realN != head {
			var headIndex int
			for headIndex = 0; index[headIndex] != head; headIndex++ {
			}
			data.Swap(realN, head)
			index[headIndex] = realN
			index[n] = head
			marked[n] = marked[headIndex]
		}
		head--
		return true
	}

	for head >= 0 {
		var i int
		for i = 0; i < len(index) && index[i] > head; i++ {
		}
		if i == len(index) {
			return false
		}
		if !visitFn(i) {
			return false
		}
	}

	return true
}

// IsSorted determines if the given collection of partialy ordered items is sorted
func IsSorted(data Interface) bool {
	length := data.Len()
	for i := 1; i < length; i++ {
		if data.LessOrEqual(i, i-1) {
			return false
		}
	}
	return true
}

// Reverse performs an in-place reversal of the order of the items in the collection
func Reverse(data Interface) {
	head := 0
	tail := data.Len() - 1
	for head < tail {
		data.Swap(head, tail)
		head++
		tail--
	}
}
<|endoftext|>


<commit_before>package putio

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

const (
	BaseUrl = "https://api.put.io/v2/"
)

var Paths = map[string]string{
	// values in {{ }} are variables.  Marked thusly for template use
	"FilesList":         "/files/list",
	"FilesSearch":       "/files/search/{{query}}/page/{{pageno}}",
	"FilesUpload":       "/files/upload",
	"FilesCreateFolder": "/files/create-folder",
	"FilesId":           "/files/{{id}}",
	"FilesDelete":       "/files/delete",
	"FilesRename":       "/files/rename",
	"FilesMove":         "/files/move",
	"FilesMP4":          "/files/{{id}}/mp4",
	"FilesDowload":      "/files/{{id}}/download",
	"FilesZip":          "/files/zip",
	"TransfersList":     "/transfers/list",
	"TransfersAdd":      "/transfers/add",
	"TransfersId":       "/transfers/{{id}}",
	"TransfersCancel":   "/transfers/cancel",
	"AccountInfo":       "/account/info",
	"AccountSettings":   "/account/settings",
	"FriendsList":       "/friends/list",
	"FriendsWaiting":    "/friends/waiting-requests",
	"FriendsRequest":    "/friends/{{username}}/request",
	"FriendsDeny":       "/friends/{{username}}/deny",
}

type Files struct {
}

type Transfers struct {
}

type Account struct {
}

type Friends struct {
}

type Putio struct {
	OauthToken string
	Files
	Transfers
	Account
	Friends
}

// NewPutio takes in the apps oauth information and gets the token that will be used for all other calls
// This function doesn't have to be used if you provied the OauthToken when creating a Putio struct.
func NewPutio(appid, appsecret, appredirect, usercode string) (*Putio, error) {
	// get the user token using the calling apps credentials
	url := "https://api.put.io/v2/oauth2/access_token?client_id=" + appid + "&client_secret=" + appsecret + "&grant_type=authorization_code&redirect_uri=" + appredirect + "&code=" + usercode
	fmt.Println(url)
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("Error calling oauth service : " + err.Error())
		return nil, err
	}
	// read in the body of the response
	defer resp.Body.Close()
	bodybytes, _ := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error reading oauth response : " + err.Error())
		return nil, err
	}

	// token returns as json result like { "access_token": "ABV9KDHN" }
	type oauthtoken struct {
		Access_token string
	}
	token := oauthtoken{}
	if err = json.Unmarshal(bodybytes, &token); err != nil {
		fmt.Println("Error reading json from oauth : " + err.Error() + " response:" + string(bodybytes))
		return nil, err
	}

	return &Putio{OauthToken: token.Access_token}, nil
}
<commit_msg>restructuring<commit_after>package putio

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
)

const (
	BaseUrl = "https://api.put.io/v2/"
)

var oauthparam = "?oauth_token="
var oathtoken string

type File struct {
	Is_shared          *bool
	Name               *string
	Screenshot         *string // returns url to image
	Created_at         *string // in iso8601 format
	Opensubtitles_hash *string
	Parent_id          *int // parent folder id
	Is_mp4_available   *bool
	Content_type       *string
	Crc32              *string
	Icon               *string // returns url to screenshot image in icon size
	Id                 *int
	Size               *int64
}

type Files struct {
	Status string `json: "status"`
	List   []File `json:"Files"`
	Parent File   `json:"parent"`
}

type Transfers struct {
}

type Account struct {
}

type Friends struct {
	token int
}

type Putio struct {
	OauthToken string
}

func (p *Putio) ListFiles() (files *Files, jsonstr string, err error) {
	url := BaseUrl + "/files/list" + oauthparam + p.OauthToken
	resp, err := http.Get(url)
	if err != nil {
		return nil, "", err
	}
	// read in the body of the response
	defer resp.Body.Close()
	bodybytes, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(bodybytes, &files)
	if err != nil {
		return nil, string(bodybytes), err
	}

	return files, string(bodybytes), nil
}

// NewPutio takes in the apps oauth information and gets the token that will be used for all other calls
// This function doesn't have to be used if you provied the OauthToken when creating a Putio struct.
func NewPutio(appid, appsecret, appredirect, usercode string) (*Putio, error) {
	// get the user token using the calling apps credentials
	url := "https://api.put.io/v2/oauth2/access_token?client_id=" + appid + "&client_secret=" + appsecret + "&grant_type=authorization_code&redirect_uri=" + appredirect + "&code=" + usercode
	//fmt.Println(url)
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	// read in the body of the response
	defer resp.Body.Close()
	bodybytes, _ := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// token returns as json result like { "access_token": "ABV9KDHN" }
	type oauthtoken struct {
		Access_token string
	}
	token := oauthtoken{}
	if err = json.Unmarshal(bodybytes, &token); err != nil {
		return nil, err
	}

	return &Putio{OauthToken: token.Access_token}, nil
}
<|endoftext|>


<commit_before>package api

// InstanceConsoleControl represents a message on the instance console "control" socket.
//
// API extension: instances
type InstanceConsoleControl struct {
	Command string            `json:"command" yaml:"command"`
	Args    map[string]string `json:"args" yaml:"args"`
}

// InstanceConsolePost represents a LXD instance console request.
//
// API extension: instances
type InstanceConsolePost struct {
	Width  int `json:"width" yaml:"width"`
	Height int `json:"height" yaml:"height"`

	// API extension: console_vga_type
	Type string `json:"type" yaml:"type"`
}
<commit_msg>shared/api: Add swagger metadata for instance console<commit_after>package api

// InstanceConsoleControl represents a message on the instance console "control" socket.
//
// API extension: instances
type InstanceConsoleControl struct {
	Command string            `json:"command" yaml:"command"`
	Args    map[string]string `json:"args" yaml:"args"`
}

// InstanceConsolePost represents a LXD instance console request.
//
// swagger:model
//
// API extension: instances
type InstanceConsolePost struct {
	// Console width in columns (console type only)
	// Example: 80
	Width int `json:"width" yaml:"width"`

	// Console height in rows (console type only)
	// Example: 24
	Height int `json:"height" yaml:"height"`

	// Type of console to attach to (console or vga)
	// Example: console
	//
	// API extension: console_vga_type
	Type string `json:"type" yaml:"type"`
}
<|endoftext|>


<commit_before>// Copyright 2016, Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"bytes"
	"net"
	"testing"

	"github.com/google/gopacket"
)

func TestDHCPv4EncodeRequest(t *testing.T) {
	dhcp := &DHCPv4{Operation: DHCPOpRequest, HardwareType: LinkTypeEthernet, Xid: 0x12345678,
		ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{0, 0, 0, 0}, NextServerIP: net.IP{0, 0, 0, 0}, RelayAgentIP: net.IP{0, 0, 0, 0},
		ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
		ServerName:   make([]byte, 64), File: make([]byte, 128)}

	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptMessageType, []byte{byte(DHCPMsgTypeDiscover)}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptHostname, []byte{'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm'}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptParamsRequest,
		[]byte{byte(DHCPOptSubnetMask), byte(DHCPOptBroadcastAddr), byte(DHCPOptTimeOffset),
			byte(DHCPOptRouter), byte(DHCPOptDomainName), byte(DHCPOptDNS), byte(DHCPOptDomainSearch),
			byte(DHCPOptHostname), byte(DHCPOptNetBIOSTCPNS), byte(DHCPOptInterfaceMTU), byte(DHCPOptClasslessStaticRoute),
			byte(DHCPOptNTPServers)}))

	buf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{FixLengths: true}
	err := gopacket.SerializeLayers(buf, opts, dhcp)
	if err != nil {
		t.Fatal(err)
	}

	p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDHCPv4, testDecodeOptions)
	dhcp2 := p2.Layer(LayerTypeDHCPv4).(*DHCPv4)
	testDHCPEqual(t, dhcp, dhcp2)
}

func TestDHCPv4EncodeResponse(t *testing.T) {
	dhcp := &DHCPv4{Operation: DHCPOpReply, HardwareType: LinkTypeEthernet, Xid: 0x12345678,
		ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{192, 168, 0, 123}, NextServerIP: net.IP{192, 168, 0, 1}, RelayAgentIP: net.IP{0, 0, 0, 0},
		ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
		ServerName:   make([]byte, 64), File: make([]byte, 128)}

	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptMessageType, []byte{byte(DHCPMsgTypeOffer)}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptSubnetMask, []byte{255, 255, 255, 0}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptT1, []byte{0x00, 0x00, 0x0e, 0x10}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptT2, []byte{0x00, 0x00, 0x0e, 0x10}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptLeaseTime, []byte{0x00, 0x00, 0x0e, 0x10}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptServerID, []byte{192, 168, 0, 1}))

	buf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{FixLengths: true}
	err := gopacket.SerializeLayers(buf, opts, dhcp)
	if err != nil {
		t.Fatal(err)
	}

	p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDHCPv4, testDecodeOptions)
	dhcp2 := p2.Layer(LayerTypeDHCPv4).(*DHCPv4)
	testDHCPEqual(t, dhcp, dhcp2)
}

func testDHCPEqual(t *testing.T, d1, d2 *DHCPv4) {
	if d1.Operation != d2.Operation {
		t.Errorf("expected Operation=%s, got %s", d1.Operation, d2.Operation)
	}
	if d1.HardwareType != d2.HardwareType {
		t.Errorf("expected HardwareType=%s, got %s", d1.HardwareType, d2.HardwareType)
	}
	if d1.HardwareLen != d2.HardwareLen {
		t.Errorf("expected HardwareLen=%v, got %v", d1.HardwareLen, d2.HardwareLen)
	}
	if d1.HardwareOpts != d2.HardwareOpts {
		t.Errorf("expected HardwareOpts=%v, got %v", d1.HardwareOpts, d2.HardwareOpts)
	}
	if d1.Xid != d2.Xid {
		t.Errorf("expected Xid=%v, got %v", d1.Xid, d2.Xid)
	}
	if d1.Secs != d2.Secs {
		t.Errorf("expected Secs=%v, got %v", d1.Secs, d2.Secs)
	}
	if d1.Flags != d2.Flags {
		t.Errorf("expected Flags=%v, got %v", d1.Flags, d2.Flags)
	}
	if !d1.ClientIP.Equal(d2.ClientIP) {
		t.Errorf("expected ClientIP=%v, got %v", d1.ClientIP, d2.ClientIP)
	}
	if !d1.YourClientIP.Equal(d2.YourClientIP) {
		t.Errorf("expected YourClientIP=%v, got %v", d1.YourClientIP, d2.YourClientIP)
	}
	if !d1.NextServerIP.Equal(d2.NextServerIP) {
		t.Errorf("expected NextServerIP=%v, got %v", d1.NextServerIP, d2.NextServerIP)
	}
	if !d1.RelayAgentIP.Equal(d2.RelayAgentIP) {
		t.Errorf("expected RelayAgentIP=%v, got %v", d1.RelayAgentIP, d2.RelayAgentIP)
	}
	if !bytes.Equal(d1.ClientHWAddr, d2.ClientHWAddr) {
		t.Errorf("expected ClientHWAddr=%v, got %v", d1.ClientHWAddr, d2.ClientHWAddr)
	}
	if !bytes.Equal(d1.ServerName, d2.ServerName) {
		t.Errorf("expected ServerName=%v, got %v", d1.ServerName, d2.ServerName)
	}
	if !bytes.Equal(d1.File, d2.File) {
		t.Errorf("expected File=%v, got %v", d1.File, d2.File)
	}
	if len(d1.Options) != len(d2.Options) {
		t.Errorf("expected %d options, got %d", len(d1.Options), len(d2.Options))
	}

	for i, o := range d1.Options {
		testDHCPOptionEqual(t, i, o, d2.Options[i])
	}
}

func testDHCPOptionEqual(t *testing.T, idx int, d1, d2 DHCPOption) {
	if d1.Type != d2.Type {
		t.Errorf("expection Options[%d].Type = %s, got %s", idx, d1.Type, d2.Type)
	}
	if !bytes.Equal(d1.Data, d2.Data) {
		t.Errorf("expection Options[%d].Data to be = %v, got %v", idx, d1.Data, d2.Data)
	}
}
<commit_msg>Add test for DHCPOptPad off by one<commit_after>// Copyright 2016, Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"bytes"
	"net"
	"testing"

	"github.com/google/gopacket"
)

func TestDHCPv4EncodeRequest(t *testing.T) {
	dhcp := &DHCPv4{Operation: DHCPOpRequest, HardwareType: LinkTypeEthernet, Xid: 0x12345678,
		ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{0, 0, 0, 0}, NextServerIP: net.IP{0, 0, 0, 0}, RelayAgentIP: net.IP{0, 0, 0, 0},
		ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
		ServerName:   make([]byte, 64), File: make([]byte, 128)}

	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptMessageType, []byte{byte(DHCPMsgTypeDiscover)}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptHostname, []byte{'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm'}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptPad, nil))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptParamsRequest,
		[]byte{byte(DHCPOptSubnetMask), byte(DHCPOptBroadcastAddr), byte(DHCPOptTimeOffset),
			byte(DHCPOptRouter), byte(DHCPOptDomainName), byte(DHCPOptDNS), byte(DHCPOptDomainSearch),
			byte(DHCPOptHostname), byte(DHCPOptNetBIOSTCPNS), byte(DHCPOptInterfaceMTU), byte(DHCPOptClasslessStaticRoute),
			byte(DHCPOptNTPServers)}))

	buf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{FixLengths: true}
	err := gopacket.SerializeLayers(buf, opts, dhcp)
	if err != nil {
		t.Fatal(err)
	}

	p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDHCPv4, testDecodeOptions)
	dhcp2 := p2.Layer(LayerTypeDHCPv4).(*DHCPv4)
	testDHCPEqual(t, dhcp, dhcp2)
}

func TestDHCPv4EncodeResponse(t *testing.T) {
	dhcp := &DHCPv4{Operation: DHCPOpReply, HardwareType: LinkTypeEthernet, Xid: 0x12345678,
		ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{192, 168, 0, 123}, NextServerIP: net.IP{192, 168, 0, 1}, RelayAgentIP: net.IP{0, 0, 0, 0},
		ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
		ServerName:   make([]byte, 64), File: make([]byte, 128)}

	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptMessageType, []byte{byte(DHCPMsgTypeOffer)}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptSubnetMask, []byte{255, 255, 255, 0}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptPad, nil))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptT1, []byte{0x00, 0x00, 0x0e, 0x10}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptT2, []byte{0x00, 0x00, 0x0e, 0x10}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptLeaseTime, []byte{0x00, 0x00, 0x0e, 0x10}))
	dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptServerID, []byte{192, 168, 0, 1}))

	buf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{FixLengths: true}
	err := gopacket.SerializeLayers(buf, opts, dhcp)
	if err != nil {
		t.Fatal(err)
	}

	p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDHCPv4, testDecodeOptions)
	dhcp2 := p2.Layer(LayerTypeDHCPv4).(*DHCPv4)
	testDHCPEqual(t, dhcp, dhcp2)
}

func testDHCPEqual(t *testing.T, d1, d2 *DHCPv4) {
	if d1.Operation != d2.Operation {
		t.Errorf("expected Operation=%s, got %s", d1.Operation, d2.Operation)
	}
	if d1.HardwareType != d2.HardwareType {
		t.Errorf("expected HardwareType=%s, got %s", d1.HardwareType, d2.HardwareType)
	}
	if d1.HardwareLen != d2.HardwareLen {
		t.Errorf("expected HardwareLen=%v, got %v", d1.HardwareLen, d2.HardwareLen)
	}
	if d1.HardwareOpts != d2.HardwareOpts {
		t.Errorf("expected HardwareOpts=%v, got %v", d1.HardwareOpts, d2.HardwareOpts)
	}
	if d1.Xid != d2.Xid {
		t.Errorf("expected Xid=%v, got %v", d1.Xid, d2.Xid)
	}
	if d1.Secs != d2.Secs {
		t.Errorf("expected Secs=%v, got %v", d1.Secs, d2.Secs)
	}
	if d1.Flags != d2.Flags {
		t.Errorf("expected Flags=%v, got %v", d1.Flags, d2.Flags)
	}
	if !d1.ClientIP.Equal(d2.ClientIP) {
		t.Errorf("expected ClientIP=%v, got %v", d1.ClientIP, d2.ClientIP)
	}
	if !d1.YourClientIP.Equal(d2.YourClientIP) {
		t.Errorf("expected YourClientIP=%v, got %v", d1.YourClientIP, d2.YourClientIP)
	}
	if !d1.NextServerIP.Equal(d2.NextServerIP) {
		t.Errorf("expected NextServerIP=%v, got %v", d1.NextServerIP, d2.NextServerIP)
	}
	if !d1.RelayAgentIP.Equal(d2.RelayAgentIP) {
		t.Errorf("expected RelayAgentIP=%v, got %v", d1.RelayAgentIP, d2.RelayAgentIP)
	}
	if !bytes.Equal(d1.ClientHWAddr, d2.ClientHWAddr) {
		t.Errorf("expected ClientHWAddr=%v, got %v", d1.ClientHWAddr, d2.ClientHWAddr)
	}
	if !bytes.Equal(d1.ServerName, d2.ServerName) {
		t.Errorf("expected ServerName=%v, got %v", d1.ServerName, d2.ServerName)
	}
	if !bytes.Equal(d1.File, d2.File) {
		t.Errorf("expected File=%v, got %v", d1.File, d2.File)
	}
	if len(d1.Options) != len(d2.Options) {
		t.Errorf("expected %d options, got %d", len(d1.Options), len(d2.Options))
	}

	for i, o := range d1.Options {
		testDHCPOptionEqual(t, i, o, d2.Options[i])
	}
}

func testDHCPOptionEqual(t *testing.T, idx int, d1, d2 DHCPOption) {
	if d1.Type != d2.Type {
		t.Errorf("expection Options[%d].Type = %s, got %s", idx, d1.Type, d2.Type)
	}
	if !bytes.Equal(d1.Data, d2.Data) {
		t.Errorf("expection Options[%d].Data to be = %v, got %v", idx, d1.Data, d2.Data)
	}
}
<|endoftext|>


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

package lease

import (
	"errors"
	"io"
	"os"
	"sync"
)

// A sentinel error used when a lease has been revoked.
type RevokedError struct {
}

func (re *RevokedError) Error() string {
	return "Lease revoked"
}

// A read-only wrapper around a file that may be revoked, when e.g. there is
// temporary disk space pressure. A read lease may also be upgraded to a write
// lease, if it is still valid.
//
// All methods are safe for concurrent access.
type ReadLease interface {
	io.ReadSeeker
	io.ReaderAt

	// Return the size of the underlying file.
	Size() (size int64, err error)

	// Attempt to upgrade the lease to a read/write lease, returning nil if the
	// lease has been revoked. After upgrading, it is as if the lease has been
	// revoked.
	Upgrade() (rwl ReadWriteLease)

	// Cause the lease to be revoked and any associated resources to be cleaned
	// up, if it has not already been revoked.
	Revoke()
}

type readLease struct {
	mu sync.Mutex

	/////////////////////////
	// Dependencies
	/////////////////////////

	// The leaser that issued this lease.
	leaser *FileLeaser

	// The underlying file, set to nil once revoked.
	//
	// GUARDED_BY(mu)
	file *os.File
}

var _ ReadLease = &readLease{}

func newReadLease(
	leaser *FileLeaser,
	file *os.File) (rl *readLease) {
	rl = &readLease{
		leaser: leaser,
		file:   file,
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Read(p []byte) (n int, err error) {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	// Have we been revoked?
	if rl.file == nil {
		err = &RevokedError{}
		return
	}

	n, err = rl.file.Read(p)
	return
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Seek(
	offset int64,
	whence int) (off int64, err error) {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	// Have we been revoked?
	if rl.file == nil {
		err = &RevokedError{}
		return
	}

	off, err = rl.file.Seek(offset, whence)
	return
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) ReadAt(p []byte, off int64) (n int, err error) {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	// Have we been revoked?
	if rl.file == nil {
		err = &RevokedError{}
		return
	}

	n, err = rl.file.ReadAt(p, off)
	return
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Size() (size int64, err error) {
	err = errors.New("TODO")
	return
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Upgrade() (rwl ReadWriteLease) {
	panic("TODO")
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Revoke() {
	panic("TODO")
}
<commit_msg>readLease.Size<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"io"
	"os"
	"sync"
)

// A sentinel error used when a lease has been revoked.
type RevokedError struct {
}

func (re *RevokedError) Error() string {
	return "Lease revoked"
}

// A read-only wrapper around a file that may be revoked, when e.g. there is
// temporary disk space pressure. A read lease may also be upgraded to a write
// lease, if it is still valid.
//
// All methods are safe for concurrent access.
type ReadLease interface {
	io.ReadSeeker
	io.ReaderAt

	// Return the size of the underlying file.
	Size() (size int64, err error)

	// Attempt to upgrade the lease to a read/write lease, returning nil if the
	// lease has been revoked. After upgrading, it is as if the lease has been
	// revoked.
	Upgrade() (rwl ReadWriteLease)

	// Cause the lease to be revoked and any associated resources to be cleaned
	// up, if it has not already been revoked.
	Revoke()
}

type readLease struct {
	mu sync.Mutex

	/////////////////////////
	// Constant data
	/////////////////////////

	// The size, to be used only when file != nil. This is redundant with file
	// because file.Stat() may fail, and we want FileLeaser to be able to depend
	// on Size() never failing for non-revoked leases.
	size int64

	/////////////////////////
	// Dependencies
	/////////////////////////

	// The leaser that issued this lease.
	leaser *FileLeaser

	// The underlying file, set to nil once revoked.
	//
	// GUARDED_BY(mu)
	file *os.File
}

var _ ReadLease = &readLease{}

func newReadLease(
	size int64,
	leaser *FileLeaser,
	file *os.File) (rl *readLease) {
	rl = &readLease{
		size:   size,
		leaser: leaser,
		file:   file,
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Read(p []byte) (n int, err error) {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	// Have we been revoked?
	if rl.file == nil {
		err = &RevokedError{}
		return
	}

	n, err = rl.file.Read(p)
	return
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Seek(
	offset int64,
	whence int) (off int64, err error) {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	// Have we been revoked?
	if rl.file == nil {
		err = &RevokedError{}
		return
	}

	off, err = rl.file.Seek(offset, whence)
	return
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) ReadAt(p []byte, off int64) (n int, err error) {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	// Have we been revoked?
	if rl.file == nil {
		err = &RevokedError{}
		return
	}

	n, err = rl.file.ReadAt(p, off)
	return
}

// Guaranteed not to fail if the lease is still valid.
//
// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Size() (size int64, err error) {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	// Have we been revoked?
	if rl.file == nil {
		err = &RevokedError{}
		return
	}

	size = rl.size
	return
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Upgrade() (rwl ReadWriteLease) {
	panic("TODO")
}

// LOCKS_EXCLUDED(rl.mu)
func (rl *readLease) Revoke() {
	panic("TODO")
}
<|endoftext|>


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

package lease

import (
	"fmt"
	"io"
	"log"

	"golang.org/x/net/context"
)

// A function used by read proxies to refresh their contents. See notes on
// NewReadProxy.
type RefreshContentsFunc func(context.Context) (io.ReadCloser, error)

// Create a read proxy.
//
// The supplied function will be used to obtain the proxy's contents, the first
// time they're needed and whenever the supplied file leaser decides to expire
// the temporary copy thus obtained. It must return the same contents every
// time, and the contents must be of the given size.
func NewReadProxy(
	fl FileLeaser,
	size int64,
	refresh RefreshContentsFunc) (rp *ReadProxy) {
	rp = &ReadProxy{
		leaser:  fl,
		size:    size,
		refresh: refresh,
	}

	return
}

// A wrapper around a read lease, exposing a similar interface with the
// following differences:
//
//  *  Contents are fetched and re-fetched automatically when needed. Therefore
//     the user need not worry about lease expiration.
//
//  *  Methods that may involve fetching the contents (reading, seeking) accept
//     context arguments, so as to be cancellable.
//
// External synchronization is required.
type ReadProxy struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	size int64

	/////////////////////////
	// Dependencies
	/////////////////////////

	leaser  FileLeaser
	refresh RefreshContentsFunc

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The current wrapped lease, or nil if one has never been issued.
	lease ReadLease
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Attempt to clean up after the supplied read/write lease.
func destroyReadWriteLease(rwl ReadWriteLease) {
	var err error
	defer func() {
		if err != nil {
			log.Printf("Error destroying read/write lease: %v", err)
		}
	}()

	// Downgrade to a read lease.
	rl, err := rwl.Downgrade()
	if err != nil {
		err = fmt.Errorf("Downgrade: %v", err)
		return
	}

	// Revoke the read lease.
	rl.Revoke()
}

func isRevokedErr(err error) bool {
	_, ok := err.(*RevokedError)
	return ok
}

// Set up a read/write lease and fill in our contents.
//
// REQUIRES: The caller has observed that rp.lease has expired.
func (rp *ReadProxy) getContents(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// Obtain some space to write the contents.
	rwl, err = rp.leaser.NewFile()
	if err != nil {
		err = fmt.Errorf("NewFile: %v", err)
		return
	}

	// Attempt to clean up if we exit early.
	defer func() {
		if err != nil {
			destroyReadWriteLease(rwl)
		}
	}()

	// Obtain the reader for our contents.
	rc, err := rp.refresh(ctx)
	if err != nil {
		err = fmt.Errorf("User function: %v", err)
		return
	}

	defer func() {
		closeErr := rc.Close()
		if closeErr != nil && err == nil {
			err = fmt.Errorf("Close: %v", closeErr)
		}
	}()

	// Copy into the read/write lease.
	copied, err := io.Copy(rwl, rc)
	if err != nil {
		err = fmt.Errorf("Copy: %v", err)
		return
	}

	// Did the user lie about the size?
	if copied != rp.Size() {
		err = fmt.Errorf("Copied %v bytes; expected %v", copied, rp.Size())
		return
	}

	return
}

// Downgrade and save the supplied read/write lease obtained with getContents
// for later use.
func (rp *ReadProxy) saveContents(rwl ReadWriteLease) {
	downgraded, err := rwl.Downgrade()
	if err != nil {
		log.Printf("Failed to downgrade write lease (%q); abandoning.", err.Error())
		return
	}

	rp.lease = downgraded
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Semantics matching io.Reader, except with context support.
func (rp *ReadProxy) Read(
	ctx context.Context,
	p []byte) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.Read(p)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.Read(p)

	return
}

// Semantics matching io.Seeker, except with context support.
func (rp *ReadProxy) Seek(
	ctx context.Context,
	offset int64,
	whence int) (off int64, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		off, err = rp.lease.Seek(offset, whence)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	off, err = rwl.Seek(offset, whence)

	return
}

// Semantics matching io.ReaderAt, except with context support.
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.ReadAt(p, off)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.ReadAt(p, off)

	return
}

// Return the size of the proxied content. Guarantees to not block.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.size
	return
}

// For testing use only; do not touch.
func (rp *ReadProxy) Destroyed() (destroyed bool) {
	panic("TODO")
}

// Return a read/write lease for the proxied contents. The read proxy must not
// be used after calling this method.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// If we succeed, we are now destroyed.
	defer func() {
		if err == nil {
			rp.Destroy()
		}
	}()

	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		rwl, err = rp.lease.Upgrade()
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Build the read/write lease anew.
	rwl, err = rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	return
}

// Destroy any resources in use by the read proxy. It must not be used further.
func (rl *autoRefreshingReadLease) Destroy() {
	rl.revoked = true
	if rl.wrapped != nil {
		rl.wrapped.Revoke()
	}
}
<commit_msg>ReadProxy.Destroy<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"fmt"
	"io"
	"log"

	"golang.org/x/net/context"
)

// A function used by read proxies to refresh their contents. See notes on
// NewReadProxy.
type RefreshContentsFunc func(context.Context) (io.ReadCloser, error)

// Create a read proxy.
//
// The supplied function will be used to obtain the proxy's contents, the first
// time they're needed and whenever the supplied file leaser decides to expire
// the temporary copy thus obtained. It must return the same contents every
// time, and the contents must be of the given size.
func NewReadProxy(
	fl FileLeaser,
	size int64,
	refresh RefreshContentsFunc) (rp *ReadProxy) {
	rp = &ReadProxy{
		leaser:  fl,
		size:    size,
		refresh: refresh,
	}

	return
}

// A wrapper around a read lease, exposing a similar interface with the
// following differences:
//
//  *  Contents are fetched and re-fetched automatically when needed. Therefore
//     the user need not worry about lease expiration.
//
//  *  Methods that may involve fetching the contents (reading, seeking) accept
//     context arguments, so as to be cancellable.
//
// External synchronization is required.
type ReadProxy struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	size int64

	/////////////////////////
	// Dependencies
	/////////////////////////

	leaser  FileLeaser
	refresh RefreshContentsFunc

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The current wrapped lease, or nil if one has never been issued.
	lease ReadLease
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Attempt to clean up after the supplied read/write lease.
func destroyReadWriteLease(rwl ReadWriteLease) {
	var err error
	defer func() {
		if err != nil {
			log.Printf("Error destroying read/write lease: %v", err)
		}
	}()

	// Downgrade to a read lease.
	rl, err := rwl.Downgrade()
	if err != nil {
		err = fmt.Errorf("Downgrade: %v", err)
		return
	}

	// Revoke the read lease.
	rl.Revoke()
}

func isRevokedErr(err error) bool {
	_, ok := err.(*RevokedError)
	return ok
}

// Set up a read/write lease and fill in our contents.
//
// REQUIRES: The caller has observed that rp.lease has expired.
func (rp *ReadProxy) getContents(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// Obtain some space to write the contents.
	rwl, err = rp.leaser.NewFile()
	if err != nil {
		err = fmt.Errorf("NewFile: %v", err)
		return
	}

	// Attempt to clean up if we exit early.
	defer func() {
		if err != nil {
			destroyReadWriteLease(rwl)
		}
	}()

	// Obtain the reader for our contents.
	rc, err := rp.refresh(ctx)
	if err != nil {
		err = fmt.Errorf("User function: %v", err)
		return
	}

	defer func() {
		closeErr := rc.Close()
		if closeErr != nil && err == nil {
			err = fmt.Errorf("Close: %v", closeErr)
		}
	}()

	// Copy into the read/write lease.
	copied, err := io.Copy(rwl, rc)
	if err != nil {
		err = fmt.Errorf("Copy: %v", err)
		return
	}

	// Did the user lie about the size?
	if copied != rp.Size() {
		err = fmt.Errorf("Copied %v bytes; expected %v", copied, rp.Size())
		return
	}

	return
}

// Downgrade and save the supplied read/write lease obtained with getContents
// for later use.
func (rp *ReadProxy) saveContents(rwl ReadWriteLease) {
	downgraded, err := rwl.Downgrade()
	if err != nil {
		log.Printf("Failed to downgrade write lease (%q); abandoning.", err.Error())
		return
	}

	rp.lease = downgraded
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Semantics matching io.Reader, except with context support.
func (rp *ReadProxy) Read(
	ctx context.Context,
	p []byte) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.Read(p)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.Read(p)

	return
}

// Semantics matching io.Seeker, except with context support.
func (rp *ReadProxy) Seek(
	ctx context.Context,
	offset int64,
	whence int) (off int64, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		off, err = rp.lease.Seek(offset, whence)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	off, err = rwl.Seek(offset, whence)

	return
}

// Semantics matching io.ReaderAt, except with context support.
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		n, err = rp.lease.ReadAt(p, off)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rp.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.ReadAt(p, off)

	return
}

// Return the size of the proxied content. Guarantees to not block.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.size
	return
}

// For testing use only; do not touch.
func (rp *ReadProxy) Destroyed() (destroyed bool) {
	panic("TODO")
}

// Return a read/write lease for the proxied contents. The read proxy must not
// be used after calling this method.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// If we succeed, we are now destroyed.
	defer func() {
		if err == nil {
			rp.Destroy()
		}
	}()

	// Common case: is the existing lease still valid?
	if rp.lease != nil {
		rwl, err = rp.lease.Upgrade()
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Build the read/write lease anew.
	rwl, err = rp.getContents(ctx)
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	return
}

// Destroy any resources in use by the read proxy. It must not be used further.
func (rp *ReadProxy) Destroy() {
	if rp.lease != nil {
		rp.lease.Revoke()
	}

	// Make use-after-destroy errors obvious.
	rp.size = 0
	rp.leaser = nil
	rp.refresh = nil
	rp.lease = nil
}
<|endoftext|>