<commit_before>package sflow

import (
	"encoding/binary"
	"errors"
	"io"
	"net"
)

const (
	DataFlowSample    = 1 // Packet Flow Sampling
	DataCounterSample = 2 // Counter Sampling
)

type SFDecoder struct {
	reader io.ReadSeeker
	filter []uint32 // Filter data format(s)
}

type SFDatagram struct {
	Version    uint32 // Datagram version
	IPVersion  uint32 // Data gram sFlow version
	AgentSubId uint32 // Identifies a source of sFlow data
	SequenceNo uint32 // Sequence of sFlow Datagrams
	SysUpTime  uint32 // Current time (in milliseconds since device last booted
	SamplesNo  uint32 // Number of samples

	IPAddress net.IP // Agent IP address
}

type SFSampledHeader struct {
	HeaderProtocol uint32 // (enum SFHeaderProtocol)
	FrameLength    uint32 // Original length of packet before sampling
	Stripped       uint32 // Header/trailer bytes stripped by sender
	HeaderLength   uint32 // Length of sampled header bytes to follow
	HeaderBytes    []byte // Header bytes
}

type SFSample interface{}

var (
	nonEnterpriseStandard = errors.New("the enterprise is not standard sflow data")
	dataLengthUnknown     = errors.New("the sflow data length is unknown")
	sfVersionNotSupport   = errors.New("the sflow version doesn't support")
)

func NewSFDecoder(r io.ReadSeeker, f []uint32) SFDecoder {
	return SFDecoder{
		reader: r,
		filter: f,
	}
}

func (d *SFDecoder) SFDecode() (*SFDatagram, error) {
	var (
		datagram     = &SFDatagram{}
		ipLen    int = 4
		err      error
	)

	if err = read(d.reader, &datagram.Version); err != nil {
		return nil, err
	}

	if datagram.Version != 5 {
		return nil, sfVersionNotSupport
	}

	if err = read(d.reader, &datagram.IPVersion); err != nil {
		return nil, err
	}

	// read the agent ip address
	if datagram.IPVersion == 2 {
		ipLen = 16
	}
	buff := make([]byte, ipLen)
	if _, err = d.reader.Read(buff); err != nil {
		return nil, err
	}
	datagram.IPAddress = buff

	if err = read(d.reader, &datagram.AgentSubId); err != nil {
		return nil, err
	}
	if err = read(d.reader, &datagram.SequenceNo); err != nil {
		return nil, err
	}
	if err = read(d.reader, &datagram.SysUpTime); err != nil {
		return nil, err
	}
	if err = read(d.reader, &datagram.SamplesNo); err != nil {
		return nil, err
	}

	// decode sample(s) - loop over sample records
	for i := uint32(0); i < datagram.SamplesNo; i++ {
		if err = d.decodeSample(); err != nil {
			// TODO
			continue
		}
	}

	return datagram, nil
}

func (d *SFDecoder) decodeSample() error {
	var (
		sfType           uint32
		sfTypeFormat     uint32
		sfTypeEnterprise uint32
		sfDataLength     uint32

		err error
	)

	if err = read(d.reader, &sfType); err != nil {
		return err
	}

	sfTypeEnterprise = sfType >> 12 // 20 bytes enterprise
	sfTypeFormat = sfType & 4095    // 12 bytes format

	// supports standard sflow data
	if sfTypeEnterprise != 0 {
		d.reader.Seek(int64(sfDataLength), 1)
		return nonEnterpriseStandard
	}

	if err = read(d.reader, &sfDataLength); err != nil {
		return dataLengthUnknown
	}

	if m := d.isFilterMatch(sfTypeFormat); m {
		d.reader.Seek(int64(sfDataLength), 1)
		return err // TODO
	}

	switch sfTypeFormat {
	case DataFlowSample:
		decodeFlowSample(d.reader)
		d.reader.Seek(int64(sfDataLength), 1)
	case DataCounterSample:
		d.reader.Seek(int64(sfDataLength), 1)
		// TODO
	default:
		d.reader.Seek(int64(sfDataLength), 1)

	}

	return nil
}

func (d *SFDecoder) isFilterMatch(f uint32) bool {
	for _, v := range d.filter {
		if v == f {
			return true
		}
	}
	return false
}

func read(r io.Reader, v interface{}) error {
	return binary.Read(r, binary.BigEndian, v)
}
<commit_msg>rm seek as decoder already read<commit_after>package sflow

import (
	"encoding/binary"
	"errors"
	"io"
	"net"
)

const (
	DataFlowSample    = 1 // Packet Flow Sampling
	DataCounterSample = 2 // Counter Sampling
)

type SFDecoder struct {
	reader io.ReadSeeker
	filter []uint32 // Filter data format(s)
}

type SFDatagram struct {
	Version    uint32 // Datagram version
	IPVersion  uint32 // Data gram sFlow version
	AgentSubId uint32 // Identifies a source of sFlow data
	SequenceNo uint32 // Sequence of sFlow Datagrams
	SysUpTime  uint32 // Current time (in milliseconds since device last booted
	SamplesNo  uint32 // Number of samples

	IPAddress net.IP // Agent IP address
}

type SFSampledHeader struct {
	HeaderProtocol uint32 // (enum SFHeaderProtocol)
	FrameLength    uint32 // Original length of packet before sampling
	Stripped       uint32 // Header/trailer bytes stripped by sender
	HeaderLength   uint32 // Length of sampled header bytes to follow
	HeaderBytes    []byte // Header bytes
}

type SFSample interface{}

var (
	nonEnterpriseStandard = errors.New("the enterprise is not standard sflow data")
	dataLengthUnknown     = errors.New("the sflow data length is unknown")
	sfVersionNotSupport   = errors.New("the sflow version doesn't support")
)

func NewSFDecoder(r io.ReadSeeker, f []uint32) SFDecoder {
	return SFDecoder{
		reader: r,
		filter: f,
	}
}

func (d *SFDecoder) SFDecode() (*SFDatagram, error) {
	var (
		datagram     = &SFDatagram{}
		ipLen    int = 4
		err      error
	)

	if err = read(d.reader, &datagram.Version); err != nil {
		return nil, err
	}

	if datagram.Version != 5 {
		return nil, sfVersionNotSupport
	}

	if err = read(d.reader, &datagram.IPVersion); err != nil {
		return nil, err
	}

	// read the agent ip address
	if datagram.IPVersion == 2 {
		ipLen = 16
	}
	buff := make([]byte, ipLen)
	if _, err = d.reader.Read(buff); err != nil {
		return nil, err
	}
	datagram.IPAddress = buff

	if err = read(d.reader, &datagram.AgentSubId); err != nil {
		return nil, err
	}
	if err = read(d.reader, &datagram.SequenceNo); err != nil {
		return nil, err
	}
	if err = read(d.reader, &datagram.SysUpTime); err != nil {
		return nil, err
	}
	if err = read(d.reader, &datagram.SamplesNo); err != nil {
		return nil, err
	}

	// decode sample(s) - loop over sample records
	for i := uint32(0); i < datagram.SamplesNo; i++ {
		if err = d.decodeSample(); err != nil {
			// TODO
			continue
		}
	}

	return datagram, nil
}

func (d *SFDecoder) decodeSample() error {
	var (
		sfType           uint32
		sfTypeFormat     uint32
		sfTypeEnterprise uint32
		sfDataLength     uint32

		err error
	)

	if err = read(d.reader, &sfType); err != nil {
		return err
	}

	sfTypeEnterprise = sfType >> 12 // 20 bytes enterprise
	sfTypeFormat = sfType & 4095    // 12 bytes format

	// supports standard sflow data
	if sfTypeEnterprise != 0 {
		d.reader.Seek(int64(sfDataLength), 1)
		return nonEnterpriseStandard
	}

	if err = read(d.reader, &sfDataLength); err != nil {
		return dataLengthUnknown
	}

	if m := d.isFilterMatch(sfTypeFormat); m {
		d.reader.Seek(int64(sfDataLength), 1)
		return err // TODO
	}

	switch sfTypeFormat {
	case DataFlowSample:
		decodeFlowSample(d.reader)
	case DataCounterSample:
		d.reader.Seek(int64(sfDataLength), 1)
		// TODO
	default:
		d.reader.Seek(int64(sfDataLength), 1)

	}

	return nil
}

func (d *SFDecoder) isFilterMatch(f uint32) bool {
	for _, v := range d.filter {
		if v == f {
			return true
		}
	}
	return false
}

func read(r io.Reader, v interface{}) error {
	return binary.Read(r, binary.BigEndian, v)
}
<|endoftext|>


<commit_before>package gopencils

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

type respStruct struct {
	Login   string
	Id      int
	Name    string
	Message string
}
type binStruct struct {
	Args    string
	Headers map[string]string
	Url     string
	Json    map[string]interface{}
}

func TestResource_url(t *testing.T) {
	api := Api("https://test-url.com")
	assert.Equal(t, api.parseUrl(), "https://test-url.com/", "Parsed Url Should match")
	api.SetQuery(map[string]string{"key": "value"})
	assert.Equal(t, api.parseUrl(), "https://test-url.com/?key=value", "Parsed QueryString Url Should match")
	assert.Equal(t, api.Url, "", "Base Url Should be empty")
	assert.Equal(t, api.Api.Base, "https://test-url.com/", "Base Url Should be empty")
}

func TestResource_auth(t *testing.T) {
	api := Api("https://test-url.com", &BasicAuth{"username", "password"})
	assert.Equal(t, api.Api.BasicAuth.Username, "username", "Username should match")
	assert.Equal(t, api.Api.BasicAuth.Password, "password", "Password should match")
}

func TestResource_get(t *testing.T) {
	mux := http.NewServeMux()
	mux.HandleFunc("/users/bndr", func(rw http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(rw, `{"login": "bndr","id": 1145456,"avatar_url": "https://avatars.githubusercontent.com/u/1145456?","gravatar_id": "8d05db0b0b8b74d5a0f93d0b1db22909","url": "https://api.github.com/users/bndr","html_url": "https://github.com/bndr","followers_url": "https://api.github.com/users/bndr/followers","following_url": "https://api.github.com/users/bndr/following{/other_user}","gists_url": "https://api.github.com/users/bndr/gists{/gist_id}","starred_url": "https://api.github.com/users/bndr/starred{/owner}{/repo}","subscriptions_url": "https://api.github.com/users/bndr/subscriptions","organizations_url": "https://api.github.com/users/bndr/orgs","repos_url": "https://api.github.com/users/bndr/repos","events_url": "https://api.github.com/users/bndr/events{/privacy}","received_events_url": "https://api.github.com/users/bndr/received_events","type": "User","site_admin": false,"name": "Vadim Kravcenko","company": "","blog": "http://vadimkravcenko.com","location": "Germany","email": "bndrzz@gmail.com","hireable": false,"bio": null,"public_repos": 17,"public_gists": 2,"followers": 13,"following": 0,"created_at": "2011-10-22T19:21:17Z","updated_at": "2014-07-20T10:24:25Z"}`)
	})
	mux.HandleFunc("/users/torvalds", func(rw http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(rw, `{"login": "torvalds","id": 1024025,"avatar_url": "https://avatars.githubusercontent.com/u/1024025?","gravatar_id": "fb47627bc8c0bcdb36321edfbf02e916","url": "https://api.github.com/users/torvalds","html_url": "https://github.com/torvalds","followers_url": "https://api.github.com/users/torvalds/followers","following_url": "https://api.github.com/users/torvalds/following{/other_user}","gists_url": "https://api.github.com/users/torvalds/gists{/gist_id}","starred_url": "https://api.github.com/users/torvalds/starred{/owner}{/repo}","subscriptions_url": "https://api.github.com/users/torvalds/subscriptions","organizations_url": "https://api.github.com/users/torvalds/orgs","repos_url": "https://api.github.com/users/torvalds/repos","events_url": "https://api.github.com/users/torvalds/events{/privacy}","received_events_url": "https://api.github.com/users/torvalds/received_events","type": "User","site_admin": false,"name": "Linus Torvalds","company": "Linux Foundation","blog": null,"location": "Portland, OR","email": null,"hireable": false,"bio": null,"public_repos": 2,"public_gists": 0,"followers": 17605,"following": 0,"created_at": "2011-09-03T15:26:22Z","updated_at": "2014-07-20T10:27:30Z"}`)
	})

	testServer := httptest.NewServer(mux)

	api := Api(testServer.URL)
	// Users endpoint
	users := api.Res("users")

	usernames := []string{"bndr", "torvalds"}

	for _, username := range usernames {
		// Create a new pointer to response Struct
		r := new(respStruct)
		// Get user with id i into the newly created response struct
		_, err := users.Id(username, r).Get()
		if err != nil {
			t.Log("Error Getting Data from Test API\nErr:", err)
		} else {
			assert.Equal(t, r.Message, "", "Error message must be empty")
			assert.Equal(t, r.Login, username, "Username should be equal")
		}
	}
	resp := new(respStruct)
	api.Res("users", resp).Id("bndr").Get()
	assert.Equal(t, resp.Login, "bndr")
	resp2 := new(respStruct)
	api.Res("users").Id("bndr", resp2).Get()
	assert.Equal(t, resp2.Login, "bndr")
}

func TestResource_create(t *testing.T) {
	testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		assert.Equal(t, req.Method, "POST", "unexpected Method")
		assert.Equal(t, req.URL.Path, "/post", "unexpected Path")
		assert.Equal(t, req.Header.Get("Content-Type"), "application/json", "Expected json content type")
		fmt.Fprintln(rw, `{"args": {},"data": "{\"Key\":\"Value1\"}","files": {},"form": {},"headers": {"Accept": "*/*",  "Connection": "close",  "Content-Length": "16",  "Content-Type": "application/json",  "Host": "httpbin.org",  "User-Agent": "curl/7.37.0",  "X-Request-Id": "6268bee8-2ea0-4144-802a-6166fe18d84f"},"json": {"Key": "Value1"},"origin": "95.91.230.168","url": "https://httpbin.org/post"}`)
	}))

	api := Api(testServer.URL)
	payload := map[string]interface{}{"Key": "Value1"}
	r := new(binStruct)
	api.Res("post", r).Post(payload)
	assert.Equal(t, r.Json["Key"], "Value1", "Payload must match")
}

func TestResource_update(t *testing.T) {
	testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		assert.Equal(t, req.Method, "PUT", "unexpected Method")
		assert.Equal(t, req.URL.Path, "/put", "unexpected Path")
		assert.Equal(t, req.Header.Get("Content-Type"), "application/json", "Expected json content type")
		fmt.Fprintln(rw, `{"args": {},"data": "{\"Key\":\"Value1\"}","files": {},"form": {},"headers": {"Accept": "*/*",  "Connection": "close",  "Content-Length": "16",  "Content-Type": "application/json",  "Host": "httpbin.org",  "User-Agent": "curl/7.37.0",  "X-Request-Id": "6268bee8-2ea0-4144-802a-6166fe18d84f"},"json": {"Key": "Value1"},"origin": "95.91.230.168","url": "https://httpbin.org/post"}`)
	}))

	api := Api(testServer.URL)
	payload := map[string]interface{}{"Key": "Value1"}
	r := new(binStruct)
	api.Res("put", r).Put(payload)
	assert.Equal(t, r.Json["Key"], "Value1", "Payload must match")
}

func TestResource_delete(t *testing.T) {
	testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		assert.Equal(t, req.Method, "DELETE", "unexpected Method")
		assert.Equal(t, req.URL.Path, "/delete", "unexpected Path")
		fmt.Fprintln(rw, `{"args": {},"data": "","files": {},"form": {},"headers": {"Accept": "*/*",  "Connection": "close",  "Host": "httpbin.org",  "User-Agent": "curl/7.37.0",  "X-Request-Id": "b29e2435-926f-4fb4-bd1d-ec1b179e1523"},"json": null,"origin": "95.91.230.168","url": "https://httpbin.org/delete"}`)
	}))

	api := Api(testServer.URL)
	r := new(binStruct)
	api.Id("delete", r).Delete()
	assert.Equal(t, r.Url, "https://httpbin.org/delete", "Url must match")
}

func TestResource_id(t *testing.T) {
	api := Api("https://test-url.com")
	assert.Equal(t, api.Res("users").Id("test").Url, "users/test", "Url should match")
	assert.Equal(t, api.Res("users").Id("test").parseUrl(), "https://test-url.com/users/test", "Url should match")
	assert.Equal(t, api.Res("users").Id(123).Res("items").Id(111).Url, "users/123/items/111", "Multilevel Url should match")
}
<commit_msg>just use a single test server<commit_after>package gopencils

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

var (
	testMux *http.ServeMux
	testSrv *httptest.Server
)

func init() {
	testMux = http.NewServeMux()
	testSrv = httptest.NewServer(testMux)
}

type respStruct struct {
	Login   string
	Id      int
	Name    string
	Message string
}
type binStruct struct {
	Args    string
	Headers map[string]string
	Url     string
	Json    map[string]interface{}
}

func TestResource_url(t *testing.T) {
	api := Api("https://test-url.com")
	assert.Equal(t, api.parseUrl(), "https://test-url.com/", "Parsed Url Should match")
	api.SetQuery(map[string]string{"key": "value"})
	assert.Equal(t, api.parseUrl(), "https://test-url.com/?key=value", "Parsed QueryString Url Should match")
	assert.Equal(t, api.Url, "", "Base Url Should be empty")
	assert.Equal(t, api.Api.Base, "https://test-url.com/", "Base Url Should be empty")
}

func TestResource_auth(t *testing.T) {
	api := Api("https://test-url.com", &BasicAuth{"username", "password"})
	assert.Equal(t, api.Api.BasicAuth.Username, "username", "Username should match")
	assert.Equal(t, api.Api.BasicAuth.Password, "password", "Password should match")
}

func TestResource_get(t *testing.T) {
	// github stubs
	testMux.HandleFunc("/users/bndr", func(rw http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(rw, `{"login": "bndr","id": 1145456,"avatar_url": "https://avatars.githubusercontent.com/u/1145456?","gravatar_id": "8d05db0b0b8b74d5a0f93d0b1db22909","url": "https://api.github.com/users/bndr","html_url": "https://github.com/bndr","followers_url": "https://api.github.com/users/bndr/followers","following_url": "https://api.github.com/users/bndr/following{/other_user}","gists_url": "https://api.github.com/users/bndr/gists{/gist_id}","starred_url": "https://api.github.com/users/bndr/starred{/owner}{/repo}","subscriptions_url": "https://api.github.com/users/bndr/subscriptions","organizations_url": "https://api.github.com/users/bndr/orgs","repos_url": "https://api.github.com/users/bndr/repos","events_url": "https://api.github.com/users/bndr/events{/privacy}","received_events_url": "https://api.github.com/users/bndr/received_events","type": "User","site_admin": false,"name": "Vadim Kravcenko","company": "","blog": "http://vadimkravcenko.com","location": "Germany","email": "bndrzz@gmail.com","hireable": false,"bio": null,"public_repos": 17,"public_gists": 2,"followers": 13,"following": 0,"created_at": "2011-10-22T19:21:17Z","updated_at": "2014-07-20T10:24:25Z"}`)
	})
	testMux.HandleFunc("/users/torvalds", func(rw http.ResponseWriter, req *http.Request) {
		fmt.Fprintln(rw, `{"login": "torvalds","id": 1024025,"avatar_url": "https://avatars.githubusercontent.com/u/1024025?","gravatar_id": "fb47627bc8c0bcdb36321edfbf02e916","url": "https://api.github.com/users/torvalds","html_url": "https://github.com/torvalds","followers_url": "https://api.github.com/users/torvalds/followers","following_url": "https://api.github.com/users/torvalds/following{/other_user}","gists_url": "https://api.github.com/users/torvalds/gists{/gist_id}","starred_url": "https://api.github.com/users/torvalds/starred{/owner}{/repo}","subscriptions_url": "https://api.github.com/users/torvalds/subscriptions","organizations_url": "https://api.github.com/users/torvalds/orgs","repos_url": "https://api.github.com/users/torvalds/repos","events_url": "https://api.github.com/users/torvalds/events{/privacy}","received_events_url": "https://api.github.com/users/torvalds/received_events","type": "User","site_admin": false,"name": "Linus Torvalds","company": "Linux Foundation","blog": null,"location": "Portland, OR","email": null,"hireable": false,"bio": null,"public_repos": 2,"public_gists": 0,"followers": 17605,"following": 0,"created_at": "2011-09-03T15:26:22Z","updated_at": "2014-07-20T10:27:30Z"}`)
	})

	api := Api(testSrv.URL)
	// Users endpoint
	users := api.Res("users")

	usernames := []string{"bndr", "torvalds"}

	for _, username := range usernames {
		// Create a new pointer to response Struct
		r := new(respStruct)
		// Get user with id i into the newly created response struct
		_, err := users.Id(username, r).Get()
		if err != nil {
			t.Log("Error Getting Data from Test API\nErr:", err)
		} else {
			assert.Equal(t, r.Message, "", "Error message must be empty")
			assert.Equal(t, r.Login, username, "Username should be equal")
		}
	}
	resp := new(respStruct)
	api.Res("users", resp).Id("bndr").Get()
	assert.Equal(t, resp.Login, "bndr")
	resp2 := new(respStruct)
	api.Res("users").Id("bndr", resp2).Get()
	assert.Equal(t, resp2.Login, "bndr")
}

func TestResource_create(t *testing.T) {
	testMux.HandleFunc("/post", func(rw http.ResponseWriter, req *http.Request) {
		assert.Equal(t, req.Method, "POST", "unexpected Method")
		assert.Equal(t, req.URL.Path, "/post", "unexpected Path")
		assert.Equal(t, req.Header.Get("Content-Type"), "application/json", "Expected json content type")
		fmt.Fprintln(rw, `{"args": {},"data": "{\"Key\":\"Value1\"}","files": {},"form": {},"headers": {"Accept": "*/*",  "Connection": "close",  "Content-Length": "16",  "Content-Type": "application/json",  "Host": "httpbin.org",  "User-Agent": "curl/7.37.0",  "X-Request-Id": "6268bee8-2ea0-4144-802a-6166fe18d84f"},"json": {"Key": "Value1"},"origin": "95.91.230.168","url": "https://httpbin.org/post"}`)
	})

	api := Api(testSrv.URL)
	payload := map[string]interface{}{"Key": "Value1"}
	r := new(binStruct)
	api.Res("post", r).Post(payload)
	assert.Equal(t, r.Json["Key"], "Value1", "Payload must match")
}

func TestResource_update(t *testing.T) {
	testMux.HandleFunc("/put", func(rw http.ResponseWriter, req *http.Request) {
		assert.Equal(t, req.Method, "PUT", "unexpected Method")
		assert.Equal(t, req.URL.Path, "/put", "unexpected Path")
		assert.Equal(t, req.Header.Get("Content-Type"), "application/json", "Expected json content type")
		fmt.Fprintln(rw, `{"args": {},"data": "{\"Key\":\"Value1\"}","files": {},"form": {},"headers": {"Accept": "*/*",  "Connection": "close",  "Content-Length": "16",  "Content-Type": "application/json",  "Host": "httpbin.org",  "User-Agent": "curl/7.37.0",  "X-Request-Id": "6268bee8-2ea0-4144-802a-6166fe18d84f"},"json": {"Key": "Value1"},"origin": "95.91.230.168","url": "https://httpbin.org/post"}`)
	})

	api := Api(testSrv.URL)
	payload := map[string]interface{}{"Key": "Value1"}
	r := new(binStruct)
	api.Res("put", r).Put(payload)
	assert.Equal(t, r.Json["Key"], "Value1", "Payload must match")
}

func TestResource_delete(t *testing.T) {
	testMux.HandleFunc("/delete", func(rw http.ResponseWriter, req *http.Request) {
		assert.Equal(t, req.Method, "DELETE", "unexpected Method")
		assert.Equal(t, req.URL.Path, "/delete", "unexpected Path")
		fmt.Fprintln(rw, `{"args": {},"data": "","files": {},"form": {},"headers": {"Accept": "*/*",  "Connection": "close",  "Host": "httpbin.org",  "User-Agent": "curl/7.37.0",  "X-Request-Id": "b29e2435-926f-4fb4-bd1d-ec1b179e1523"},"json": null,"origin": "95.91.230.168","url": "https://httpbin.org/delete"}`)
	})

	api := Api(testSrv.URL)
	r := new(binStruct)
	api.Id("delete", r).Delete()
	assert.Equal(t, r.Url, "https://httpbin.org/delete", "Url must match")
}

func TestResource_id(t *testing.T) {
	api := Api("https://test-url.com")
	assert.Equal(t, api.Res("users").Id("test").Url, "users/test", "Url should match")
	assert.Equal(t, api.Res("users").Id("test").parseUrl(), "https://test-url.com/users/test", "Url should match")
	assert.Equal(t, api.Res("users").Id(123).Res("items").Id(111).Url, "users/123/items/111", "Multilevel Url should match")
}
<|endoftext|>


<commit_before>package app

import (
	"os"
	"path/filepath"
	"testing"
)

func TestResourcePathJoin(t *testing.T) {
	l := ResourcePath("resources")
	if j := l.Join("css"); j != "resources/css" {
		t.Error("j should be resources/css:", j)
	}
}

func TestResourcePathCSS(t *testing.T) {
	r := ResourcePath("resources")
	cssPath := r.Join("css")
	if err := os.MkdirAll(cssPath, os.ModePerm); err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(Resources().Path())

	os.Create(filepath.Join(cssPath, "foo.css"))
	os.Create(filepath.Join(cssPath, "hello"))
	os.Create(filepath.Join(cssPath, "world.txt"))
	os.Mkdir(filepath.Join(cssPath, "bar"), os.ModePerm)

	cssFilenames := r.CSS()
	if l := len(cssFilenames); l != 1 {
		t.Error("cssFilenames should have 1 element:", l)
	}
	if cssFilenames[0] != "css/foo.css" {
		t.Error("cssFilenames[0] should be css/foo.css:", cssFilenames[0])
	}
}

func TestResourcePathCSSError(t *testing.T) {
	// No css directory.
	r := ResourcePath("resources")
	r.CSS()

	// css is not a directory.
	cssPath := r.Join("css")
	os.Mkdir(r.Path(), os.ModePerm)
	os.Create(cssPath)
	defer os.RemoveAll(Resources().Path())
	r.CSS()
}

func TestResourcePathJS(t *testing.T) {
	r := ResourcePath("resources")
	jsPath := r.Join("js")
	if err := os.MkdirAll(jsPath, os.ModePerm); err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(Resources().Path())

	os.Create(filepath.Join(jsPath, "foo.js"))
	os.Create(filepath.Join(jsPath, "hello"))
	os.Create(filepath.Join(jsPath, "world.txt"))
	os.Mkdir(filepath.Join(jsPath, "bar"), os.ModePerm)

	jsFilenames := r.JS()
	if l := len(jsFilenames); l != 1 {
		t.Error("jsFilenames should have 1 element:", l)
	}
	if jsFilenames[0] != "js/foo.js" {
		t.Error("jsFilenames[0] should be js/foo.js:", jsFilenames[0])
	}
}

func TestResourcePathJSError(t *testing.T) {
	// No js directory.
	r := ResourcePath("resources")
	r.JS()

	// js is not a directory.
	jsPath := r.Join("js")
	os.Mkdir(r.Path(), os.ModePerm)
	os.Create(jsPath)
	defer os.RemoveAll(Resources().Path())
	r.JS()
}

func TestResources(t *testing.T) {
	t.Log(Resources())
}

func TestIsSupportedImageExtension(t *testing.T) {
	if !IsSupportedImageExtension("logo.png") {
		t.Error("logo.png should be a supported image")
	}

	if IsSupportedImageExtension("logo.txt") {
		t.Error("logo.txt should not be a supported image")
	}
}
<commit_msg>refactor tests<commit_after>package app

import (
	"os"
	"path/filepath"
	"testing"
)

func TestResourcePathJoin(t *testing.T) {
	l := ResourcePath("resources")
	if j, exp := l.Join("css"), filepath.Join("resources", "css"); j != exp {
		t.Errorf("j should be %v: %v", exp, j)
	}
}

func TestResourcePathCSS(t *testing.T) {
	r := ResourcePath("resources")
	cssPath := r.Join("css")
	if err := os.MkdirAll(cssPath, os.ModePerm); err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(Resources().Path())

	os.Mkdir(filepath.Join(cssPath, "bar"), os.ModePerm)
	ffoo, _ := os.Create(filepath.Join(cssPath, "foo.css"))
	fhello, _ := os.Create(filepath.Join(cssPath, "hello"))
	fworld, _ := os.Create(filepath.Join(cssPath, "world.txt"))
	defer ffoo.Close()
	defer fhello.Close()
	defer fworld.Close()

	cssFilenames := r.CSS()
	if l := len(cssFilenames); l != 1 {
		t.Error("cssFilenames should have 1 element:", l)
	}
	if exp := filepath.Join("css", "foo.css"); cssFilenames[0] != exp {
		t.Errorf("cssFilenames[0] should be %v: %v", exp, cssFilenames[0])
	}
}

func TestResourcePathCSSError(t *testing.T) {
	// No css directory.
	r := ResourcePath("resources")
	r.CSS()

	// css is not a directory.
	cssPath := r.Join("css")
	os.Mkdir(r.Path(), os.ModePerm)
	defer os.RemoveAll(Resources().Path())
	f, _ := os.Create(cssPath)
	defer f.Close()

	r.CSS()
}

func TestResourcePathJS(t *testing.T) {
	r := ResourcePath("resources")
	jsPath := r.Join("js")
	if err := os.MkdirAll(jsPath, os.ModePerm); err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(Resources().Path())

	os.Mkdir(filepath.Join(jsPath, "bar"), os.ModePerm)
	ffoo, _ := os.Create(filepath.Join(jsPath, "foo.js"))
	fhello, _ := os.Create(filepath.Join(jsPath, "hello"))
	fworld, _ := os.Create(filepath.Join(jsPath, "world.txt"))
	defer ffoo.Close()
	defer fhello.Close()
	defer fworld.Close()

	jsFilenames := r.JS()
	if l := len(jsFilenames); l != 1 {
		t.Error("jsFilenames should have 1 element:", l)
	}
	if exp := filepath.Join("js", "foo.js"); jsFilenames[0] != exp {
		t.Errorf("jsFilenames[0] should be %v: %v", exp, jsFilenames[0])
	}
}

func TestResourcePathJSError(t *testing.T) {
	// No js directory.
	r := ResourcePath("resources")
	r.JS()

	// js is not a directory.
	jsPath := r.Join("js")
	os.Mkdir(r.Path(), os.ModePerm)
	defer os.RemoveAll(Resources().Path())
	f, _ := os.Create(jsPath)
	defer f.Close()

	r.JS()
}

func TestResources(t *testing.T) {
	t.Log(Resources())
}

func TestIsSupportedImageExtension(t *testing.T) {
	if !IsSupportedImageExtension("logo.png") {
		t.Error("logo.png should be a supported image")
	}

	if IsSupportedImageExtension("logo.txt") {
		t.Error("logo.txt should not be a supported image")
	}
}
<|endoftext|>


<commit_before>package httputils

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
)

func ResponseJSON(w http.ResponseWriter, r *http.Request, code int, v interface{}) error {
	jsonBytes, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("response json marshal error: %w", err)
	}

	w.Header().Set("Content-Length", strconv.Itoa(len(jsonBytes)))
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	w.WriteHeader(code)

	_, err = w.Write(jsonBytes)
	return err
}
<commit_msg>Add ResponseJSONBytes<commit_after>package httputils

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
)

func ResponseJSON(w http.ResponseWriter, r *http.Request, code int, v interface{}) error {
	jsonBytes, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("response json marshal error: %w", err)
	}

	return ResponseJSONBytes(w, r, code, jsonBytes)
}

func ResponseJSONBytes(w http.ResponseWriter, r *http.Request, code int, jsonBytes []byte) error {
	w.Header().Set("Content-Length", strconv.Itoa(len(jsonBytes)))
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	w.WriteHeader(code)

	_, err := w.Write(jsonBytes)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"testing"
)

func TestResponse_NewResponse_Terms(t *testing.T) {
	terms := []string{"Foo-Foo", "!BAR*BAR?", "バズ"}

	actual := NewResponse(terms).Terms
	expected := []string{"foo-foo", "!bar*bar?", "バズ"}

	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestResponse_AddItem(t *testing.T) {
	r := NewResponse([]string{})

	actual := r.AddItem(&ResponseItem{Title: "title-foo"}).Items
	expected := []ResponseItem{ResponseItem{Title: "title-foo"}}

	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestResponse_ToXML(t *testing.T) {
	r := NewResponse([]string{})
	item := ResponseItem{
		Valid:    true,
		UID:      "f000-uid",
		Title:    "title-foo",
		Subtitle: "Subtitle foo.",
		Arg:      "arg-foo",
		Icon:     "./icons/title-foo.png",
		Unicode:  "f000-unicode",
	}
	r.AddItem(&item)

	actual := r.ToXML()
	expected := `<?xml version="1.0" encoding="UTF-8"?><items><item valid="true" arg="arg-foo" uid="f000-uid" unicode="f000-unicode"><title>title-foo</title><subtitle>Subtitle foo.</subtitle><icon>./icons/title-foo.png</icon></item></items>`
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}
<commit_msg>Add test: TestResponse_ToXML_Blank<commit_after>package main

import (
	"reflect"
	"testing"
)

func TestResponse_NewResponse_Terms(t *testing.T) {
	terms := []string{"Foo-Foo", "!BAR*BAR?", "バズ"}

	actual := NewResponse(terms).Terms
	expected := []string{"foo-foo", "!bar*bar?", "バズ"}

	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestResponse_AddItem(t *testing.T) {
	r := NewResponse([]string{})

	actual := r.AddItem(&ResponseItem{Title: "title-foo"}).Items
	expected := []ResponseItem{ResponseItem{Title: "title-foo"}}

	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestResponse_ToXML(t *testing.T) {
	r := NewResponse([]string{})
	item := ResponseItem{
		Valid:    true,
		UID:      "f000-uid",
		Title:    "title-foo",
		Subtitle: "Subtitle foo.",
		Arg:      "arg-foo",
		Icon:     "./icons/title-foo.png",
		Unicode:  "f000-unicode",
	}
	r.AddItem(&item)

	actual := r.ToXML()
	expected := `<?xml version="1.0" encoding="UTF-8"?><items><item valid="true" arg="arg-foo" uid="f000-uid" unicode="f000-unicode"><title>title-foo</title><subtitle>Subtitle foo.</subtitle><icon>./icons/title-foo.png</icon></item></items>`
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestResponse_ToXML_Blank(t *testing.T) {
	r := NewResponse([]string{})

	actual := r.ToXML()
	expected := `<?xml version="1.0" encoding="UTF-8"?><items></items>`
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}
<|endoftext|>


<commit_before>// Package revoke provides functionality for checking the validity of
// a cert. Specifically, the temporal validity of the certificate is
// checked first, then any CRL and OCSP url in the cert is checked.
package revoke

import (
	"bytes"
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	neturl "net/url"
	"sync"
	"time"

	"golang.org/x/crypto/ocsp"

	"github.com/cloudflare/cfssl/helpers"
	"github.com/cloudflare/cfssl/log"
)

// HardFail determines whether the failure to check the revocation
// status of a certificate (i.e. due to network failure) causes
// verification to fail (a hard failure).
var HardFail = false

// CRLSet associates a PKIX certificate list with the URL the CRL is
// fetched from.
var CRLSet = map[string]*pkix.CertificateList{}
var crlLock = new(sync.Mutex)

// We can't handle LDAP certificates, so this checks to see if the
// URL string points to an LDAP resource so that we can ignore it.
func ldapURL(url string) bool {
	u, err := neturl.Parse(url)
	if err != nil {
		log.Warningf("error parsing url %s: %v", url, err)
		return false
	}
	if u.Scheme == "ldap" {
		return true
	}
	return false
}

// revCheck should check the certificate for any revocations. It
// returns a pair of booleans: the first indicates whether the certificate
// is revoked, the second indicates whether the revocations were
// successfully checked.. This leads to the following combinations:
//
//  false, false: an error was encountered while checking revocations.
//
//  false, true:  the certificate was checked successfully and
//                  it is not revoked.
//
//  true, true:   the certificate was checked successfully and
//                  it is revoked.
//
//  true, false:  failure to check revocation status causes
//                  verification to fail
func revCheck(cert *x509.Certificate) (revoked, ok bool, err error) {
	for _, url := range cert.CRLDistributionPoints {
		if ldapURL(url) {
			log.Infof("skipping LDAP CRL: %s", url)
			continue
		}

		if revoked, ok, err := certIsRevokedCRL(cert, url); !ok {
			log.Warning("error checking revocation via CRL")
			if HardFail {
				return true, false, err
			}
			return false, false, err
		} else if revoked {
			log.Info("certificate is revoked via CRL")
			return true, true, err
		}
	}

	if revoked, ok, err := certIsRevokedOCSP(cert, HardFail); !ok {
		log.Warning("error checking revocation via OCSP")
		if HardFail {
			return true, false, err
		}
		return false, false, err
	} else if revoked {
		log.Info("certificate is revoked via OCSP")
		return true, true, err
	}

	return false, true, nil
}

// fetchCRL fetches and parses a CRL.
func fetchCRL(url string) (*pkix.CertificateList, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	} else if resp.StatusCode >= 300 {
		return nil, errors.New("failed to retrieve CRL")
	}

	body, err := crlRead(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	return x509.ParseCRL(body)
}

func getIssuer(cert *x509.Certificate) *x509.Certificate {
	var issuer *x509.Certificate
	var err error
	for _, issuingCert := range cert.IssuingCertificateURL {
		issuer, err = fetchRemote(issuingCert)
		if err != nil {
			continue
		}
		break
	}

	return issuer

}

// check a cert against a specific CRL. Returns the same bool pair
// as revCheck, plus an error if one occurred.
func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool, err error) {
	crl, ok := CRLSet[url]
	if ok && crl == nil {
		ok = false
		crlLock.Lock()
		delete(CRLSet, url)
		crlLock.Unlock()
	}

	var shouldFetchCRL = true
	if ok {
		if !crl.HasExpired(time.Now()) {
			shouldFetchCRL = false
		}
	}

	issuer := getIssuer(cert)

	if shouldFetchCRL {
		var err error
		crl, err = fetchCRL(url)
		if err != nil {
			log.Warningf("failed to fetch CRL: %v", err)
			return false, false, err
		}

		// check CRL signature
		if issuer != nil {
			err = issuer.CheckCRLSignature(crl)
			if err != nil {
				log.Warningf("failed to verify CRL: %v", err)
				return false, false, err
			}
		}

		crlLock.Lock()
		CRLSet[url] = crl
		crlLock.Unlock()
	}

	for _, revoked := range crl.TBSCertList.RevokedCertificates {
		if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 {
			log.Info("Serial number match: intermediate is revoked.")
			return true, true, err
		}
	}

	return false, true, err
}

// VerifyCertificate ensures that the certificate passed in hasn't
// expired and checks the CRL for the server.
func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) {
	revoked, ok, _ = VerifyCertificateError(cert)
	return revoked, ok
}

// VerifyCertificateError ensures that the certificate passed in hasn't
// expired and checks the CRL for the server.
func VerifyCertificateError(cert *x509.Certificate) (revoked, ok bool, err error) {
	if !time.Now().Before(cert.NotAfter) {
		msg := fmt.Sprintf("Certificate expired %s\n", cert.NotAfter)
		log.Info(msg)
		return true, true, fmt.Errorf(msg)
	} else if !time.Now().After(cert.NotBefore) {
		msg := fmt.Sprintf("Certificate isn't valid until %s\n", cert.NotBefore)
		log.Info(msg)
		return true, true, fmt.Errorf(msg)
	}
	return revCheck(cert)
}

func fetchRemote(url string) (*x509.Certificate, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}

	in, err := remoteRead(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	p, _ := pem.Decode(in)
	if p != nil {
		return helpers.ParseCertificatePEM(in)
	}

	return x509.ParseCertificate(in)
}

var ocspOpts = ocsp.RequestOptions{
	Hash: crypto.SHA1,
}

func certIsRevokedOCSP(leaf *x509.Certificate, strict bool) (revoked, ok bool, e error) {
	var err error

	ocspURLs := leaf.OCSPServer
	if len(ocspURLs) == 0 {
		// OCSP not enabled for this certificate.
		return false, true, nil
	}

	issuer := getIssuer(leaf)

	if issuer == nil {
		return false, false, nil
	}

	ocspRequest, err := ocsp.CreateRequest(leaf, issuer, &ocspOpts)
	if err != nil {
		return revoked, ok, err
	}

	for _, server := range ocspURLs {
		resp, err := sendOCSPRequest(server, ocspRequest, leaf, issuer)
		if err != nil {
			if strict {
				return revoked, ok, err
			}
			continue
		}

		// There wasn't an error fetching the OCSP status.
		ok = true

		if resp.Status != ocsp.Good {
			// The certificate was revoked.
			revoked = true
		}

		return revoked, ok, err
	}
	return revoked, ok, err
}

// sendOCSPRequest attempts to request an OCSP response from the
// server. The error only indicates a failure to *fetch* the
// certificate, and *does not* mean the certificate is valid.
func sendOCSPRequest(server string, req []byte, leaf, issuer *x509.Certificate) (*ocsp.Response, error) {
	var resp *http.Response
	var err error
	if len(req) > 256 {
		buf := bytes.NewBuffer(req)
		resp, err = http.Post(server, "application/ocsp-request", buf)
	} else {
		reqURL := server + "/" + base64.StdEncoding.EncodeToString(req)
		resp, err = http.Get(reqURL)
	}

	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New("failed to retrieve OSCP")
	}

	body, err := ocspRead(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	switch {
	case bytes.Equal(body, ocsp.UnauthorizedErrorResponse):
		return nil, errors.New("OSCP unauthorized")
	case bytes.Equal(body, ocsp.MalformedRequestErrorResponse):
		return nil, errors.New("OSCP malformed")
	case bytes.Equal(body, ocsp.InternalErrorErrorResponse):
		return nil, errors.New("OSCP internal error")
	case bytes.Equal(body, ocsp.TryLaterErrorResponse):
		return nil, errors.New("OSCP try later")
	case bytes.Equal(body, ocsp.SigRequredErrorResponse):
		return nil, errors.New("OSCP signature required")
	}

	return ocsp.ParseResponseForCert(body, leaf, issuer)
}

var crlRead = ioutil.ReadAll

// SetCRLFetcher sets the function to use to read from the http response body
func SetCRLFetcher(fn func(io.Reader) ([]byte, error)) {
	crlRead = fn
}

var remoteRead = ioutil.ReadAll

// SetRemoteFetcher sets the function to use to read from the http response body
func SetRemoteFetcher(fn func(io.Reader) ([]byte, error)) {
	remoteRead = fn
}

var ocspRead = ioutil.ReadAll

// SetOCSPFetcher sets the function to use to read from the http response body
func SetOCSPFetcher(fn func(io.Reader) ([]byte, error)) {
	ocspRead = fn
}
<commit_msg>Add neturl.QueryEscape to correctly encode URL for OCSP GET request (#994)<commit_after>// Package revoke provides functionality for checking the validity of
// a cert. Specifically, the temporal validity of the certificate is
// checked first, then any CRL and OCSP url in the cert is checked.
package revoke

import (
	"bytes"
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	neturl "net/url"
	"sync"
	"time"

	"golang.org/x/crypto/ocsp"

	"github.com/cloudflare/cfssl/helpers"
	"github.com/cloudflare/cfssl/log"
)

// HardFail determines whether the failure to check the revocation
// status of a certificate (i.e. due to network failure) causes
// verification to fail (a hard failure).
var HardFail = false

// CRLSet associates a PKIX certificate list with the URL the CRL is
// fetched from.
var CRLSet = map[string]*pkix.CertificateList{}
var crlLock = new(sync.Mutex)

// We can't handle LDAP certificates, so this checks to see if the
// URL string points to an LDAP resource so that we can ignore it.
func ldapURL(url string) bool {
	u, err := neturl.Parse(url)
	if err != nil {
		log.Warningf("error parsing url %s: %v", url, err)
		return false
	}
	if u.Scheme == "ldap" {
		return true
	}
	return false
}

// revCheck should check the certificate for any revocations. It
// returns a pair of booleans: the first indicates whether the certificate
// is revoked, the second indicates whether the revocations were
// successfully checked.. This leads to the following combinations:
//
//  false, false: an error was encountered while checking revocations.
//
//  false, true:  the certificate was checked successfully and
//                  it is not revoked.
//
//  true, true:   the certificate was checked successfully and
//                  it is revoked.
//
//  true, false:  failure to check revocation status causes
//                  verification to fail
func revCheck(cert *x509.Certificate) (revoked, ok bool, err error) {
	for _, url := range cert.CRLDistributionPoints {
		if ldapURL(url) {
			log.Infof("skipping LDAP CRL: %s", url)
			continue
		}

		if revoked, ok, err := certIsRevokedCRL(cert, url); !ok {
			log.Warning("error checking revocation via CRL")
			if HardFail {
				return true, false, err
			}
			return false, false, err
		} else if revoked {
			log.Info("certificate is revoked via CRL")
			return true, true, err
		}
	}

	if revoked, ok, err := certIsRevokedOCSP(cert, HardFail); !ok {
		log.Warning("error checking revocation via OCSP")
		if HardFail {
			return true, false, err
		}
		return false, false, err
	} else if revoked {
		log.Info("certificate is revoked via OCSP")
		return true, true, err
	}

	return false, true, nil
}

// fetchCRL fetches and parses a CRL.
func fetchCRL(url string) (*pkix.CertificateList, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	} else if resp.StatusCode >= 300 {
		return nil, errors.New("failed to retrieve CRL")
	}

	body, err := crlRead(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	return x509.ParseCRL(body)
}

func getIssuer(cert *x509.Certificate) *x509.Certificate {
	var issuer *x509.Certificate
	var err error
	for _, issuingCert := range cert.IssuingCertificateURL {
		issuer, err = fetchRemote(issuingCert)
		if err != nil {
			continue
		}
		break
	}

	return issuer

}

// check a cert against a specific CRL. Returns the same bool pair
// as revCheck, plus an error if one occurred.
func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool, err error) {
	crl, ok := CRLSet[url]
	if ok && crl == nil {
		ok = false
		crlLock.Lock()
		delete(CRLSet, url)
		crlLock.Unlock()
	}

	var shouldFetchCRL = true
	if ok {
		if !crl.HasExpired(time.Now()) {
			shouldFetchCRL = false
		}
	}

	issuer := getIssuer(cert)

	if shouldFetchCRL {
		var err error
		crl, err = fetchCRL(url)
		if err != nil {
			log.Warningf("failed to fetch CRL: %v", err)
			return false, false, err
		}

		// check CRL signature
		if issuer != nil {
			err = issuer.CheckCRLSignature(crl)
			if err != nil {
				log.Warningf("failed to verify CRL: %v", err)
				return false, false, err
			}
		}

		crlLock.Lock()
		CRLSet[url] = crl
		crlLock.Unlock()
	}

	for _, revoked := range crl.TBSCertList.RevokedCertificates {
		if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 {
			log.Info("Serial number match: intermediate is revoked.")
			return true, true, err
		}
	}

	return false, true, err
}

// VerifyCertificate ensures that the certificate passed in hasn't
// expired and checks the CRL for the server.
func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) {
	revoked, ok, _ = VerifyCertificateError(cert)
	return revoked, ok
}

// VerifyCertificateError ensures that the certificate passed in hasn't
// expired and checks the CRL for the server.
func VerifyCertificateError(cert *x509.Certificate) (revoked, ok bool, err error) {
	if !time.Now().Before(cert.NotAfter) {
		msg := fmt.Sprintf("Certificate expired %s\n", cert.NotAfter)
		log.Info(msg)
		return true, true, fmt.Errorf(msg)
	} else if !time.Now().After(cert.NotBefore) {
		msg := fmt.Sprintf("Certificate isn't valid until %s\n", cert.NotBefore)
		log.Info(msg)
		return true, true, fmt.Errorf(msg)
	}
	return revCheck(cert)
}

func fetchRemote(url string) (*x509.Certificate, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}

	in, err := remoteRead(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	p, _ := pem.Decode(in)
	if p != nil {
		return helpers.ParseCertificatePEM(in)
	}

	return x509.ParseCertificate(in)
}

var ocspOpts = ocsp.RequestOptions{
	Hash: crypto.SHA1,
}

func certIsRevokedOCSP(leaf *x509.Certificate, strict bool) (revoked, ok bool, e error) {
	var err error

	ocspURLs := leaf.OCSPServer
	if len(ocspURLs) == 0 {
		// OCSP not enabled for this certificate.
		return false, true, nil
	}

	issuer := getIssuer(leaf)

	if issuer == nil {
		return false, false, nil
	}

	ocspRequest, err := ocsp.CreateRequest(leaf, issuer, &ocspOpts)
	if err != nil {
		return revoked, ok, err
	}

	for _, server := range ocspURLs {
		resp, err := sendOCSPRequest(server, ocspRequest, leaf, issuer)
		if err != nil {
			if strict {
				return revoked, ok, err
			}
			continue
		}

		// There wasn't an error fetching the OCSP status.
		ok = true

		if resp.Status != ocsp.Good {
			// The certificate was revoked.
			revoked = true
		}

		return revoked, ok, err
	}
	return revoked, ok, err
}

// sendOCSPRequest attempts to request an OCSP response from the
// server. The error only indicates a failure to *fetch* the
// certificate, and *does not* mean the certificate is valid.
func sendOCSPRequest(server string, req []byte, leaf, issuer *x509.Certificate) (*ocsp.Response, error) {
	var resp *http.Response
	var err error
	if len(req) > 256 {
		buf := bytes.NewBuffer(req)
		resp, err = http.Post(server, "application/ocsp-request", buf)
	} else {
		reqURL := server + "/" + neturl.QueryEscape(base64.StdEncoding.EncodeToString(req))
		resp, err = http.Get(reqURL)
	}

	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New("failed to retrieve OSCP")
	}

	body, err := ocspRead(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	switch {
	case bytes.Equal(body, ocsp.UnauthorizedErrorResponse):
		return nil, errors.New("OSCP unauthorized")
	case bytes.Equal(body, ocsp.MalformedRequestErrorResponse):
		return nil, errors.New("OSCP malformed")
	case bytes.Equal(body, ocsp.InternalErrorErrorResponse):
		return nil, errors.New("OSCP internal error")
	case bytes.Equal(body, ocsp.TryLaterErrorResponse):
		return nil, errors.New("OSCP try later")
	case bytes.Equal(body, ocsp.SigRequredErrorResponse):
		return nil, errors.New("OSCP signature required")
	}

	return ocsp.ParseResponseForCert(body, leaf, issuer)
}

var crlRead = ioutil.ReadAll

// SetCRLFetcher sets the function to use to read from the http response body
func SetCRLFetcher(fn func(io.Reader) ([]byte, error)) {
	crlRead = fn
}

var remoteRead = ioutil.ReadAll

// SetRemoteFetcher sets the function to use to read from the http response body
func SetRemoteFetcher(fn func(io.Reader) ([]byte, error)) {
	remoteRead = fn
}

var ocspRead = ioutil.ReadAll

// SetOCSPFetcher sets the function to use to read from the http response body
func SetOCSPFetcher(fn func(io.Reader) ([]byte, error)) {
	ocspRead = fn
}
<|endoftext|>


<commit_before>/*
Copyright 2021 Intel Corporation

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 sst

import (
	"fmt"
	stdlog "log"
	"os"

	grclog "github.com/intel/goresctrl/pkg/log"
	"github.com/intel/goresctrl/pkg/utils"
)

// SstPackageInfo contains status of Intel Speed Select Technologies (SST)
// for one CPU package
type SstPackageInfo struct {
	// Gereric PP info
	PPSupported    bool
	PPLocked       bool
	PPVersion      int
	PPCurrentLevel int
	PPMaxLevel     int

	// Information about the currently active PP level
	CPSupported bool
	CPEnabled   bool
	CPPriority  CPPriorityType
	BFSupported bool
	BFEnabled   bool
	BFCores     utils.IDSet
	TFSupported bool
	TFEnabled   bool

	ClosInfo [NumClos]SstClosInfo
}

// NumClos is the number of CLOSes suported by SST-CP
const NumClos = 4

// SstClosInfo contains parameters of one CLOS of SST-CP
type SstClosInfo struct {
	EPP                  int
	ProportionalPriority int
	MinFreq              int
	MaxFreq              int
	DesiredFreq          int
}

// CPPriorityType denotes the type CLOS priority ordering used in SST-CP
type CPPriorityType int

const (
	Proportional CPPriorityType = 0
	Ordered      CPPriorityType = 1
)

const isstDevPath = "/dev/isst_interface"

var sstlog grclog.Logger = grclog.NewLoggerWrapper(stdlog.New(os.Stderr, "[ sst ] ", 0))

// SstSupported returns true if Intel Speed Select Technologies (SST) is supported
// by the system and can be interfaced via the Linux kernel device
func SstSupported() bool {
	if _, err := os.Stat(isstDevPath); err != nil {
		if !os.IsNotExist(err) {
			sstlog.Warnf("failed to access sst device %q: %v", isstDevPath, err)
		} else {
			sstlog.Debugf("sst device %q does not exist", isstDevPath)
		}
		return false
	}
	return true
}

// GetPackageInfo returns information of the SST configuration of one cpu
// package.
func GetPackageInfo(pkgId utils.ID) (SstPackageInfo, error) {
	info := SstPackageInfo{}

	// Get topology information from sysfs
	packages, err := getOnlineCpuPackages()
	if err != nil {
		return info, fmt.Errorf("failed to determine cpu topology: %w", err)
	}
	pkg, ok := packages[pkgId]
	if !ok {
		return info, fmt.Errorf("cpu package %d not present", pkgId)
	}
	cpu := pkg.cpus[0] // We just need to pass one logical cpu from the pkg as an arg

	var rsp uint32

	// Read perf-profile feature info
	if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_GET_LEVELS_INFO, 0); err != nil {
		return info, fmt.Errorf("failed to read SST PP info: %v", err)
	}
	info.PPSupported = getBits(rsp, 31, 31) != 0
	info.PPLocked = getBits(rsp, 24, 24) != 0
	info.PPCurrentLevel = int(getBits(rsp, 16, 23))
	info.PPMaxLevel = int(getBits(rsp, 8, 15))
	info.PPVersion = int(getBits(rsp, 0, 7))

	// Forget about older hw with partial/convoluted support
	if info.PPVersion < 3 {
		sstlog.Infof("SST PP version %d (less than 3), giving up...")
		return info, nil
	}

	// Read the status of currently active perf-profile
	if info.PPSupported {
		if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_CONTROL, uint32(info.PPCurrentLevel)); err != nil {
			return info, fmt.Errorf("failed to read SST CP status: %v", err)
		}

		info.BFSupported = isBitSet(rsp, 1)
		info.BFEnabled = isBitSet(rsp, 17)

		info.TFSupported = isBitSet(rsp, 0)
		info.TFEnabled = isBitSet(rsp, 16)

	}

	// Read base-frequency info
	if info.BFSupported {
		info.BFCores = utils.IDSet{}

		punitCoreIDs := make(map[utils.ID]utils.IDSet, len(pkg.cpus))
		var maxPunitCore utils.ID
		for _, id := range pkg.cpus {
			pc, err := punitCPU(id)
			if err != nil {
				return info, err
			}
			punitCore := pc >> 1
			if _, ok := punitCoreIDs[punitCore]; !ok {
				punitCoreIDs[punitCore] = utils.IDSet{}
			}
			punitCoreIDs[punitCore].Add(id)
			if punitCore > maxPunitCore {
				maxPunitCore = punitCore
			}
		}

		// Read out core masks in batches of 32 (32 bits per response)
		for i := 0; i <= int(maxPunitCore)/32; i++ {
			if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_CORE_MASK_INFO, uint32(info.PPCurrentLevel+(i<<8))); err != nil {
				return info, fmt.Errorf("failed to read SST BF core mask (#%d): %v", i, err)
			}
			for bit := 0; bit < 32; bit++ {
				if isBitSet(rsp, uint32(bit)) {
					info.BFCores.Add(punitCoreIDs[utils.ID(i*32+bit)].Members()...)
				}
			}
		}
	}

	// Read core-power feature info
	if rsp, err = sendMboxCmd(cpu, READ_PM_CONFIG, PM_FEATURE, 0); err != nil {
		return info, fmt.Errorf("failed to read SST CP info: %v", err)
	}

	info.CPSupported = isBitSet(rsp, 0)
	info.CPEnabled = isBitSet(rsp, 16)

	if info.CPSupported {
		if rsp, err = sendMboxCmd(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0); err != nil {
			return info, fmt.Errorf("failed to read SST CP status: %v", err)
		}

		closEnabled := isBitSet(rsp, 1)
		if closEnabled != info.CPEnabled {
			sstlog.Warnf("SST firmware returned conflicting CP enabled status %v vs. %v", info.CPEnabled, closEnabled)
		}
		info.CPEnabled = closEnabled
		info.CPPriority = CPPriorityType(getBits(rsp, 2, 2))

		for i := uint32(0); i < NumClos; i++ {
			if rsp, err = sendMMIOCmd(cpu, (i<<2)+PM_CLOS_OFFSET); err != nil {
				return info, fmt.Errorf("failed to read SST CLOS #%d info: %v", i, err)
			}
			info.ClosInfo[i] = SstClosInfo{
				EPP:                  int(getBits(rsp, 0, 3)),
				ProportionalPriority: int(getBits(rsp, 4, 7)),
				MinFreq:              int(getBits(rsp, 8, 15)),
				MaxFreq:              int(getBits(rsp, 16, 23)),
				DesiredFreq:          int(getBits(rsp, 24, 31)),
			}
		}
	}

	return info, nil
}

// GetCPUClosID returns the SST-CP CLOS id that a cpu is associated with.
func GetCPUClosID(cpu utils.ID) (int, error) {
	p, err := punitCPU(cpu)
	if err != nil {
		return -1, err
	}
	punitCore := uint32(p) >> 1

	rsp, err := sendMMIOCmd(cpu, (punitCore<<2)+PQR_ASSOC_OFFSET)
	if err != nil {
		return 0, fmt.Errorf("failed to read CLOS number of cpu %d: %v", cpu, err)
	}
	return int(getBits(rsp, 16, 17)), nil
}

func getBits(val, i, j uint32) uint32 {
	lsb := i
	msb := j
	if i > j {
		lsb = j
		msb = i
	}
	return (val >> lsb) & ((1 << (msb - lsb + 1)) - 1)
}

func isBitSet(val, n uint32) bool {
	return val&(1<<n) != 0
}
<commit_msg>sst: fix misleading error message<commit_after>/*
Copyright 2021 Intel Corporation

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 sst

import (
	"fmt"
	stdlog "log"
	"os"

	grclog "github.com/intel/goresctrl/pkg/log"
	"github.com/intel/goresctrl/pkg/utils"
)

// SstPackageInfo contains status of Intel Speed Select Technologies (SST)
// for one CPU package
type SstPackageInfo struct {
	// Gereric PP info
	PPSupported    bool
	PPLocked       bool
	PPVersion      int
	PPCurrentLevel int
	PPMaxLevel     int

	// Information about the currently active PP level
	CPSupported bool
	CPEnabled   bool
	CPPriority  CPPriorityType
	BFSupported bool
	BFEnabled   bool
	BFCores     utils.IDSet
	TFSupported bool
	TFEnabled   bool

	ClosInfo [NumClos]SstClosInfo
}

// NumClos is the number of CLOSes suported by SST-CP
const NumClos = 4

// SstClosInfo contains parameters of one CLOS of SST-CP
type SstClosInfo struct {
	EPP                  int
	ProportionalPriority int
	MinFreq              int
	MaxFreq              int
	DesiredFreq          int
}

// CPPriorityType denotes the type CLOS priority ordering used in SST-CP
type CPPriorityType int

const (
	Proportional CPPriorityType = 0
	Ordered      CPPriorityType = 1
)

const isstDevPath = "/dev/isst_interface"

var sstlog grclog.Logger = grclog.NewLoggerWrapper(stdlog.New(os.Stderr, "[ sst ] ", 0))

// SstSupported returns true if Intel Speed Select Technologies (SST) is supported
// by the system and can be interfaced via the Linux kernel device
func SstSupported() bool {
	if _, err := os.Stat(isstDevPath); err != nil {
		if !os.IsNotExist(err) {
			sstlog.Warnf("failed to access sst device %q: %v", isstDevPath, err)
		} else {
			sstlog.Debugf("sst device %q does not exist", isstDevPath)
		}
		return false
	}
	return true
}

// GetPackageInfo returns information of the SST configuration of one cpu
// package.
func GetPackageInfo(pkgId utils.ID) (SstPackageInfo, error) {
	info := SstPackageInfo{}

	// Get topology information from sysfs
	packages, err := getOnlineCpuPackages()
	if err != nil {
		return info, fmt.Errorf("failed to determine cpu topology: %w", err)
	}
	pkg, ok := packages[pkgId]
	if !ok {
		return info, fmt.Errorf("cpu package %d not present", pkgId)
	}
	cpu := pkg.cpus[0] // We just need to pass one logical cpu from the pkg as an arg

	var rsp uint32

	// Read perf-profile feature info
	if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_GET_LEVELS_INFO, 0); err != nil {
		return info, fmt.Errorf("failed to read SST PP info: %v", err)
	}
	info.PPSupported = getBits(rsp, 31, 31) != 0
	info.PPLocked = getBits(rsp, 24, 24) != 0
	info.PPCurrentLevel = int(getBits(rsp, 16, 23))
	info.PPMaxLevel = int(getBits(rsp, 8, 15))
	info.PPVersion = int(getBits(rsp, 0, 7))

	// Forget about older hw with partial/convoluted support
	if info.PPVersion < 3 {
		sstlog.Infof("SST PP version %d (less than 3), giving up...")
		return info, nil
	}

	// Read the status of currently active perf-profile
	if info.PPSupported {
		if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_CONTROL, uint32(info.PPCurrentLevel)); err != nil {
			return info, fmt.Errorf("failed to read SST BF/TF status: %v", err)
		}

		info.BFSupported = isBitSet(rsp, 1)
		info.BFEnabled = isBitSet(rsp, 17)

		info.TFSupported = isBitSet(rsp, 0)
		info.TFEnabled = isBitSet(rsp, 16)

	}

	// Read base-frequency info
	if info.BFSupported {
		info.BFCores = utils.IDSet{}

		punitCoreIDs := make(map[utils.ID]utils.IDSet, len(pkg.cpus))
		var maxPunitCore utils.ID
		for _, id := range pkg.cpus {
			pc, err := punitCPU(id)
			if err != nil {
				return info, err
			}
			punitCore := pc >> 1
			if _, ok := punitCoreIDs[punitCore]; !ok {
				punitCoreIDs[punitCore] = utils.IDSet{}
			}
			punitCoreIDs[punitCore].Add(id)
			if punitCore > maxPunitCore {
				maxPunitCore = punitCore
			}
		}

		// Read out core masks in batches of 32 (32 bits per response)
		for i := 0; i <= int(maxPunitCore)/32; i++ {
			if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_CORE_MASK_INFO, uint32(info.PPCurrentLevel+(i<<8))); err != nil {
				return info, fmt.Errorf("failed to read SST BF core mask (#%d): %v", i, err)
			}
			for bit := 0; bit < 32; bit++ {
				if isBitSet(rsp, uint32(bit)) {
					info.BFCores.Add(punitCoreIDs[utils.ID(i*32+bit)].Members()...)
				}
			}
		}
	}

	// Read core-power feature info
	if rsp, err = sendMboxCmd(cpu, READ_PM_CONFIG, PM_FEATURE, 0); err != nil {
		return info, fmt.Errorf("failed to read SST CP info: %v", err)
	}

	info.CPSupported = isBitSet(rsp, 0)
	info.CPEnabled = isBitSet(rsp, 16)

	if info.CPSupported {
		if rsp, err = sendMboxCmd(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0); err != nil {
			return info, fmt.Errorf("failed to read SST CP status: %v", err)
		}

		closEnabled := isBitSet(rsp, 1)
		if closEnabled != info.CPEnabled {
			sstlog.Warnf("SST firmware returned conflicting CP enabled status %v vs. %v", info.CPEnabled, closEnabled)
		}
		info.CPEnabled = closEnabled
		info.CPPriority = CPPriorityType(getBits(rsp, 2, 2))

		for i := uint32(0); i < NumClos; i++ {
			if rsp, err = sendMMIOCmd(cpu, (i<<2)+PM_CLOS_OFFSET); err != nil {
				return info, fmt.Errorf("failed to read SST CLOS #%d info: %v", i, err)
			}
			info.ClosInfo[i] = SstClosInfo{
				EPP:                  int(getBits(rsp, 0, 3)),
				ProportionalPriority: int(getBits(rsp, 4, 7)),
				MinFreq:              int(getBits(rsp, 8, 15)),
				MaxFreq:              int(getBits(rsp, 16, 23)),
				DesiredFreq:          int(getBits(rsp, 24, 31)),
			}
		}
	}

	return info, nil
}

// GetCPUClosID returns the SST-CP CLOS id that a cpu is associated with.
func GetCPUClosID(cpu utils.ID) (int, error) {
	p, err := punitCPU(cpu)
	if err != nil {
		return -1, err
	}
	punitCore := uint32(p) >> 1

	rsp, err := sendMMIOCmd(cpu, (punitCore<<2)+PQR_ASSOC_OFFSET)
	if err != nil {
		return 0, fmt.Errorf("failed to read CLOS number of cpu %d: %v", cpu, err)
	}
	return int(getBits(rsp, 16, 17)), nil
}

func getBits(val, i, j uint32) uint32 {
	lsb := i
	msb := j
	if i > j {
		lsb = j
		msb = i
	}
	return (val >> lsb) & ((1 << (msb - lsb + 1)) - 1)
}

func isBitSet(val, n uint32) bool {
	return val&(1<<n) != 0
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/fitbit"
)

var (
	clientSecret = flag.String("oauth2_client_secret",
		"",
		"OAuth Client (Consumer) Secret (see dev.fitbit.com)")

	cachePath = flag.String("oauth2_cache_path",
		// No default value because expanding ~ is tricky.
		"",
		"Path to a JSON-encoded file which will contain the OAuth2 token")
)

var errExpiredToken = errors.New("expired token")

type cacherTransport struct {
	Base *oauth2.Transport
	Path string
}

func (c *cacherTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	cachedToken, err := tokenFromFile(c.Path)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	if _, err := c.Base.Source.Token(); err != nil {
		return nil, errExpiredToken
	}
	resp, err = c.Base.RoundTrip(req)
	if err != nil {
		return nil, err
	}
	newTok, err := c.Base.Source.Token()
	if err != nil {
		// While we’re unable to obtain a new token, the request was still
		// successful, so let’s gracefully handle this error by not caching a
		// new token. In either case, the user will need to re-authenticate.
		return resp, nil
	}
	if cachedToken == nil ||
		cachedToken.AccessToken != newTok.AccessToken ||
		cachedToken.RefreshToken != newTok.RefreshToken {
		bytes, err := json.Marshal(&newTok)
		if err != nil {
			return nil, err
		}
		if err := ioutil.WriteFile(c.Path, bytes, 0600); err != nil {
			return nil, err
		}
	}
	return resp, nil
}

func tokenFromFile(path string) (*oauth2.Token, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var t oauth2.Token
	if err := json.Unmarshal(content, &t); err != nil {
		return nil, err
	}
	return &t, nil
}

func authorize(conf *oauth2.Config) (*oauth2.Token, error) {
	tokens := make(chan *oauth2.Token)
	errors := make(chan error)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		code := r.FormValue("code")
		if code == "" {
			http.Error(w, "Missing 'code' parameter", http.StatusBadRequest)
			return
		}
		tok, err := conf.Exchange(oauth2.NoContext, code)
		if err != nil {
			errors <- fmt.Errorf("could not exchange auth code for a token: %v", err)
			return
		}
		tokens <- tok
	})
	go func() {
		// Unfortunately, we need to hard-code this port — when registering
		// with fitbit, full RedirectURLs need to be whitelisted (incl. port).
		errors <- http.ListenAndServe(":7319", nil)
	}()

	authUrl := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Println("Please visit the following URL to authorize:")
	fmt.Println(authUrl)
	select {
	case err := <-errors:
		return nil, err
	case token := <-tokens:
		return token, nil
	}
}

// Like oauth2.Config.Client(), but using cacherTransport to persist tokens.
func client(config *oauth2.Config, token *oauth2.Token) *http.Client {
	return &http.Client{
		Transport: &cacherTransport{
			Path: *cachePath,
			Base: &oauth2.Transport{
				Source: config.TokenSource(oauth2.NoContext, token),
			},
		},
	}
}

func main() {
	flag.Parse()

	if *clientSecret == "" {
		log.Fatal("-oauth2_client_secret not specified. Register an app at https://dev.fitbit.com to get one.")
	}

	conf := &oauth2.Config{
		ClientID:     "228XTZ",
		ClientSecret: *clientSecret,
		Scopes:       []string{"weight"},
		Endpoint:     fitbit.Endpoint,
		RedirectURL:  "http://localhost:7319/",
	}

	token, err := tokenFromFile(*cachePath)
	if err != nil && os.IsNotExist(err) {
		token, err = authorize(conf)
	}
	if err != nil {
		log.Fatal(err)
	}

	c := client(conf, token)

	// Send a request just to see if it errors. This quickly detects expired
	// tokens and allows us to re-authorize.
	if _, err := c.Get("https://api.fitbit.com/1/user/-/body/weight/date/today/max.json"); err != nil {
		if urlErr, ok := err.(*url.Error); !ok || urlErr.Err != errExpiredToken {
			log.Fatal(err)
		}
		if token, tokenErr := authorize(conf); tokenErr == nil {
			c = client(conf, token)
		} else {
			log.Fatalf("Request resulted in %v, trying to re-authorize resulted in %v", err, tokenErr)
		}
	}

	type weight struct {
		Bmi    float64 `json:"bmi"`
		Date   string  `json:"date"`
		Logid  uint    `json:"logid"`
		Time   string  `json:"time"`
		Weight float64 `json:"weight"`
	}

	type timeSeriesEntry struct {
		DateTime string `json:"dateTime"`
		Value    string `json:"value"`
	}

	type timeSeriesReply struct {
		Entries []timeSeriesEntry `json:"body-weight"`
	}

	type weightReply struct {
		Weights []weight `json:"weight"`
	}

	var timeseries timeSeriesReply
	var weights weightReply

	// The get-time-series reply lacks the time, it only contains the date.
	// Also, it returns one entry for each day of the month with the averaged
	// values instead of the raw measurement data.
	//
	// Therefore, we use get-body-weight repeatedly and just use
	// get-time-series to figure out when the first entry was recorded. We
	// cannot use the user’s registration date since she might backfill data
	// into the system using the API.
	tries := 0
	var response *http.Response
	for tries < 2 {
		response, err = c.Get(
			"https://api.fitbit.com/1/user/-/body/weight/date/today/max.json")
		tries += 1
		if err != nil {
			log.Fatal(err)
		}
	}

	decoder := json.NewDecoder(response.Body)
	if err := decoder.Decode(&timeseries); err != nil {
		log.Fatal(err)
	}

	// No entries recorded? Nothing to do.
	if len(timeseries.Entries) == 0 {
		log.Printf("The fitbit API returned no values.")
		os.Exit(0)
	}

	endDate, err := time.Parse("2006-01-02", timeseries.Entries[0].DateTime)
	if err != nil {
		log.Fatalf(`Could not parse timeseries date value "%s": %v`,
			timeseries.Entries[0].DateTime,
			err)
	}

	// Subtract 24 hours to make sure that no value is missed.
	endDate = endDate.Add(-24 * time.Hour)

	for endDate.Before(time.Now()) {
		endDate = endDate.Add(30 * 24 * time.Hour)
		requestUrl := fmt.Sprintf(
			"https://api.fitbit.com/1/user/-/body/log/weight/date/%s/30d.json",
			endDate.Format("2006-01-02"))

		response, err := c.Get(
			requestUrl)
		if err != nil {
			log.Fatal(err)
		}

		decoder := json.NewDecoder(response.Body)
		err = decoder.Decode(&weights)
		if err != nil {
			log.Fatal(err)
		}
		for _, entry := range weights.Weights {
			fmt.Printf("%s %s %.1f\n", entry.Date, entry.Time[0:5], entry.Weight)
		}
	}
}
<commit_msg>Use anonymous types<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/fitbit"
)

var (
	clientSecret = flag.String("oauth2_client_secret",
		"",
		"OAuth Client (Consumer) Secret (see dev.fitbit.com)")

	cachePath = flag.String("oauth2_cache_path",
		// No default value because expanding ~ is tricky.
		"",
		"Path to a JSON-encoded file which will contain the OAuth2 token")
)

var errExpiredToken = errors.New("expired token")

type cacherTransport struct {
	Base *oauth2.Transport
	Path string
}

func (c *cacherTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	cachedToken, err := tokenFromFile(c.Path)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	if _, err := c.Base.Source.Token(); err != nil {
		return nil, errExpiredToken
	}
	resp, err = c.Base.RoundTrip(req)
	if err != nil {
		return nil, err
	}
	newTok, err := c.Base.Source.Token()
	if err != nil {
		// While we’re unable to obtain a new token, the request was still
		// successful, so let’s gracefully handle this error by not caching a
		// new token. In either case, the user will need to re-authenticate.
		return resp, nil
	}
	if cachedToken == nil ||
		cachedToken.AccessToken != newTok.AccessToken ||
		cachedToken.RefreshToken != newTok.RefreshToken {
		bytes, err := json.Marshal(&newTok)
		if err != nil {
			return nil, err
		}
		if err := ioutil.WriteFile(c.Path, bytes, 0600); err != nil {
			return nil, err
		}
	}
	return resp, nil
}

func tokenFromFile(path string) (*oauth2.Token, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var t oauth2.Token
	if err := json.Unmarshal(content, &t); err != nil {
		return nil, err
	}
	return &t, nil
}

func authorize(conf *oauth2.Config) (*oauth2.Token, error) {
	tokens := make(chan *oauth2.Token)
	errors := make(chan error)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		code := r.FormValue("code")
		if code == "" {
			http.Error(w, "Missing 'code' parameter", http.StatusBadRequest)
			return
		}
		tok, err := conf.Exchange(oauth2.NoContext, code)
		if err != nil {
			errors <- fmt.Errorf("could not exchange auth code for a token: %v", err)
			return
		}
		tokens <- tok
	})
	go func() {
		// Unfortunately, we need to hard-code this port — when registering
		// with fitbit, full RedirectURLs need to be whitelisted (incl. port).
		errors <- http.ListenAndServe(":7319", nil)
	}()

	authUrl := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Println("Please visit the following URL to authorize:")
	fmt.Println(authUrl)
	select {
	case err := <-errors:
		return nil, err
	case token := <-tokens:
		return token, nil
	}
}

// Like oauth2.Config.Client(), but using cacherTransport to persist tokens.
func client(config *oauth2.Config, token *oauth2.Token) *http.Client {
	return &http.Client{
		Transport: &cacherTransport{
			Path: *cachePath,
			Base: &oauth2.Transport{
				Source: config.TokenSource(oauth2.NoContext, token),
			},
		},
	}
}

func main() {
	flag.Parse()

	if *clientSecret == "" {
		log.Fatal("-oauth2_client_secret not specified. Register an app at https://dev.fitbit.com to get one.")
	}

	conf := &oauth2.Config{
		ClientID:     "228XTZ",
		ClientSecret: *clientSecret,
		Scopes:       []string{"weight"},
		Endpoint:     fitbit.Endpoint,
		RedirectURL:  "http://localhost:7319/",
	}

	token, err := tokenFromFile(*cachePath)
	if err != nil && os.IsNotExist(err) {
		token, err = authorize(conf)
	}
	if err != nil {
		log.Fatal(err)
	}

	c := client(conf, token)

	// Send a request just to see if it errors. This quickly detects expired
	// tokens and allows us to re-authorize.
	if _, err := c.Get("https://api.fitbit.com/1/user/-/body/weight/date/today/max.json"); err != nil {
		if urlErr, ok := err.(*url.Error); !ok || urlErr.Err != errExpiredToken {
			log.Fatal(err)
		}
		if token, tokenErr := authorize(conf); tokenErr == nil {
			c = client(conf, token)
		} else {
			log.Fatalf("Request resulted in %v, trying to re-authorize resulted in %v", err, tokenErr)
		}
	}

	var timeseries struct {
		Entries []struct {
			DateTime string `json:"dateTime"`
			Value    string `json:"value"`
		} `json:"body-weight"`
	}

	var weights struct {
		Weights []struct {
			Bmi    float64 `json:"bmi"`
			Date   string  `json:"date"`
			Logid  uint    `json:"logid"`
			Time   string  `json:"time"`
			Weight float64 `json:"weight"`
		} `json:"weight"`
	}

	// The get-time-series reply lacks the time, it only contains the date.
	// Also, it returns one entry for each day of the month with the averaged
	// values instead of the raw measurement data.
	//
	// Therefore, we use get-body-weight repeatedly and just use
	// get-time-series to figure out when the first entry was recorded. We
	// cannot use the user’s registration date since she might backfill data
	// into the system using the API.
	tries := 0
	var response *http.Response
	for tries < 2 {
		response, err = c.Get(
			"https://api.fitbit.com/1/user/-/body/weight/date/today/max.json")
		tries += 1
		if err != nil {
			log.Fatal(err)
		}
	}

	decoder := json.NewDecoder(response.Body)
	if err := decoder.Decode(&timeseries); err != nil {
		log.Fatal(err)
	}

	// No entries recorded? Nothing to do.
	if len(timeseries.Entries) == 0 {
		log.Printf("The fitbit API returned no values.")
		os.Exit(0)
	}

	endDate, err := time.Parse("2006-01-02", timeseries.Entries[0].DateTime)
	if err != nil {
		log.Fatalf(`Could not parse timeseries date value "%s": %v`,
			timeseries.Entries[0].DateTime,
			err)
	}

	// Subtract 24 hours to make sure that no value is missed.
	endDate = endDate.Add(-24 * time.Hour)

	for endDate.Before(time.Now()) {
		endDate = endDate.Add(30 * 24 * time.Hour)
		requestUrl := fmt.Sprintf(
			"https://api.fitbit.com/1/user/-/body/log/weight/date/%s/30d.json",
			endDate.Format("2006-01-02"))

		response, err := c.Get(
			requestUrl)
		if err != nil {
			log.Fatal(err)
		}

		decoder := json.NewDecoder(response.Body)
		err = decoder.Decode(&weights)
		if err != nil {
			log.Fatal(err)
		}
		for _, entry := range weights.Weights {
			fmt.Printf("%s %s %.1f\n", entry.Date, entry.Time[0:5], entry.Weight)
		}
	}
}
<|endoftext|>


<commit_before>package rpc

import (
	"encoding/hex"

	"getmelange.com/zooko/account"
	"github.com/btcsuite/btcjson"
)

// Endow will transfer money for a single Namecoin name registration
// (for 12 months) to a specified address. It will return the
// transaction details so that light clients can keep track of their
// UTXO.
func (r *Server) Endow(address string) (*account.UTXO, error) {
	// Send money to the address to endow
	sendCmd, err := btcjson.NewSendToAddressCmd(nil, address, endowmentAmount, nil)
	if err != nil {
		return nil, err
	}

	reply, err := r.Send(sendCmd)
	if err != nil {
		return nil, err
	}

	if reply.Result == nil {
		return nil, errNilReply
	} else if reply.Error != nil {
		// Error from the namecoin daemon. This generally
		// indicates an internal server issue.
		return nil, *reply.Error
	}

	txid := reply.Result.(string)

	// Get information about the transaction that we just sent
	rawTxCmd, err := btcjson.NewGetRawTransactionCmd(nil, txid, 1)
	if err != nil {
		return nil, err
	}

	reply, err = r.Send(rawTxCmd)
	if err != nil {
		return nil, err
	}

	if reply.Result == nil {
		return nil, errNilReply
	} else if reply.Error != nil {
		// Error from the namecoin daemon. This generally
		// indicates an internal server issue.
		return nil, *reply.Error
	}

	rawTx := reply.Result.(*btcjson.TxRawResult)

	outputId := 0
	pkScript := ""
vOutLoop:
	for index, out := range rawTx.Vout {
		for _, addr := range out.ScriptPubKey.Addresses {
			if addr == address {
				outputId = index
				pkScript = out.ScriptPubKey.Hex
				break vOutLoop
			}
		}
	}

	pkData, err := hex.DecodeString(pkScript)
	if err != nil {
		return nil, err
	}

	return &account.UTXO{
		TxID:     txid,
		Output:   uint32(outputId),
		Amount:   endowmentAmount,
		PkScript: pkData,
	}, nil
}

// Broadcast will broadcast a raw hex-encoded transaction to the
// Namecoin network.
func (r *Server) Broadcast(tx string) error {
	cmd, err := btcjson.NewSendRawTransactionCmd(nil, tx)
	if err != nil {
		return err
	}

	result, err := r.Send(cmd)
	if result.Error != nil {
		return *result.Error
	}

	return err
}

func (r *Server) Confirmations(tx string) (int, error) {
	cmd, err := btcjson.NewGetTransactionCmd(nil, tx)
	if err != nil {
		return -1, err
	}

	result, err := r.Send(cmd)
	if result.Error != nil {
		return -1, *result.Error
	}

	txResult := result.Result.(*btcjson.GetTransactionResult)

	return int(txResult.Confirmations), nil
}
<commit_msg>zooko/rpc: update import to use nmcd<commit_after>package rpc

import (
	"encoding/hex"

	"getmelange.com/zooko/account"
	"github.com/melange-app/nmcd/btcjson"
)

// Endow will transfer money for a single Namecoin name registration
// (for 12 months) to a specified address. It will return the
// transaction details so that light clients can keep track of their
// UTXO.
func (r *Server) Endow(address string) (*account.UTXO, error) {
	// Send money to the address to endow
	sendCmd, err := btcjson.NewSendToAddressCmd(nil, address, endowmentAmount, nil)
	if err != nil {
		return nil, err
	}

	reply, err := r.Send(sendCmd)
	if err != nil {
		return nil, err
	}

	if reply.Result == nil {
		return nil, errNilReply
	} else if reply.Error != nil {
		// Error from the namecoin daemon. This generally
		// indicates an internal server issue.
		return nil, *reply.Error
	}

	txid := reply.Result.(string)

	// Get information about the transaction that we just sent
	rawTxCmd, err := btcjson.NewGetRawTransactionCmd(nil, txid, 1)
	if err != nil {
		return nil, err
	}

	reply, err = r.Send(rawTxCmd)
	if err != nil {
		return nil, err
	}

	if reply.Result == nil {
		return nil, errNilReply
	} else if reply.Error != nil {
		// Error from the namecoin daemon. This generally
		// indicates an internal server issue.
		return nil, *reply.Error
	}

	rawTx := reply.Result.(*btcjson.TxRawResult)

	outputId := 0
	pkScript := ""
vOutLoop:
	for index, out := range rawTx.Vout {
		for _, addr := range out.ScriptPubKey.Addresses {
			if addr == address {
				outputId = index
				pkScript = out.ScriptPubKey.Hex
				break vOutLoop
			}
		}
	}

	pkData, err := hex.DecodeString(pkScript)
	if err != nil {
		return nil, err
	}

	return &account.UTXO{
		TxID:     txid,
		Output:   uint32(outputId),
		Amount:   endowmentAmount,
		PkScript: pkData,
	}, nil
}

// Broadcast will broadcast a raw hex-encoded transaction to the
// Namecoin network.
func (r *Server) Broadcast(tx string) error {
	cmd, err := btcjson.NewSendRawTransactionCmd(nil, tx)
	if err != nil {
		return err
	}

	result, err := r.Send(cmd)
	if result.Error != nil {
		return *result.Error
	}

	return err
}

func (r *Server) Confirmations(tx string) (int, error) {
	cmd, err := btcjson.NewGetTransactionCmd(nil, tx)
	if err != nil {
		return -1, err
	}

	result, err := r.Send(cmd)
	if result.Error != nil {
		return -1, *result.Error
	}

	txResult := result.Result.(*btcjson.GetTransactionResult)

	return int(txResult.Confirmations), nil
}
<|endoftext|>


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

package math

import (
	"fmt"
)

func cross(v0x, v0y, v1x, v1y float32) float32 {
	return v0x*v1y - v0y*v1x
}

func triangleCross(pt0, pt1, pt2 Point) float32 {
	return cross(pt1.X-pt0.X, pt1.Y-pt0.Y, pt2.X-pt1.X, pt2.Y-pt1.Y)
}

func adjacentIndices(indices []uint16, idx int) (uint16, uint16, uint16) {
	return indices[(idx+len(indices)-1)%len(indices)], indices[idx], indices[(idx+1)%len(indices)]
}

func InTriangle(pt, pt0, pt1, pt2 Point) bool {
	if pt.X <= pt0.X && pt.X <= pt1.X && pt.X <= pt2.X {
		return false
	}
	if pt.X >= pt0.X && pt.X >= pt1.X && pt.X >= pt2.X {
		return false
	}
	if pt.Y <= pt0.Y && pt.Y <= pt1.Y && pt.Y <= pt2.Y {
		return false
	}
	if pt.Y >= pt0.Y && pt.Y >= pt1.Y && pt.Y >= pt2.Y {
		return false
	}
	c0 := cross(pt.X-pt0.X, pt.Y-pt0.Y, pt1.X-pt0.X, pt1.Y-pt0.Y)
	c1 := cross(pt.X-pt1.X, pt.Y-pt1.Y, pt2.X-pt1.X, pt2.Y-pt1.Y)
	c2 := cross(pt.X-pt2.X, pt.Y-pt2.Y, pt0.X-pt2.X, pt0.Y-pt2.Y)
	return (c0 <= 0 && c1 <= 0 && c2 <= 0) || (c0 >= 0 && c1 >= 0 && c2 >= 0)
}

func Triangulate(pts []Point) []uint16 {
	if len(pts) < 3 {
		return nil
	}

	var currentIndices []uint16

	// Split pts into the two point groups if there are the same points.
	for i := range pts {
		for j := 0; j < i; j++ {
			if pts[i] == pts[j] {
				is0 := Triangulate(pts[j:i])
				for idx := range is0 {
					is0[idx] += uint16(j)
				}
				is1 := Triangulate(append(pts[i:], pts[:j]...))
				for idx := range is1 {
					is1[idx] = uint16((int(is1[idx]) + i) % len(pts))
				}
				return append(is0, is1...)
			}
		}
		currentIndices = append(currentIndices, uint16(i))
	}

	var indices []uint16

	// Triangulation by Ear Clipping.
	// https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
	for len(currentIndices) >= 3 {
		// Calculate cross-products and remove unneeded vertices.
		cs := make([]float32, len(currentIndices))
		idxToRemove := -1

		// Determine the direction of the polygon from the upper-left point.
		var upperLeft int
		for i := range currentIndices {
			i0, i1, i2 := adjacentIndices(currentIndices, i)
			pt0 := pts[i0]
			pt1 := pts[i1]
			pt2 := pts[i2]
			c := triangleCross(pt0, pt1, pt2)
			if c == 0 {
				idxToRemove = i
				break
			}
			cs[i] = c

			if pts[currentIndices[upperLeft]].X > pts[currentIndices[i]].X {
				upperLeft = i
			} else if pts[currentIndices[upperLeft]].X == pts[currentIndices[i]].X &&
				pts[currentIndices[upperLeft]].Y > pts[currentIndices[i]].Y {
				upperLeft = i
			}
		}
		if idxToRemove != -1 {
			currentIndices = append(currentIndices[:idxToRemove], currentIndices[idxToRemove+1:]...)
			continue
		}

		clockwise := cs[upperLeft] < 0

		idx := -1
	index:
		for i := range currentIndices {
			c := cs[i]
			if c == 0 {
				panic("math: cross value must not be 0")
			}
			if c < 0 && !clockwise || c > 0 && clockwise {
				// The angle is more than 180 degrees. This is not an ear.
				continue
			}

			i0, i1, i2 := adjacentIndices(currentIndices, i)
			pt0 := pts[i0]
			pt1 := pts[i1]
			pt2 := pts[i2]
			for j := range currentIndices {
				if l := len(currentIndices); j == (i+l-1)%l || j == i || j == (i+1)%l {
					continue
				}
				if InTriangle(pts[currentIndices[j]], pt0, pt1, pt2) {
					// If the triangle includes another point, the triangle is not an ear.
					continue index
				}
			}
			// The angle is less than 180 degrees. This is an ear.
			idx = i
			break
		}
		if idx < 0 {
			// TODO: This happens when there is self-crossing.
			panic(fmt.Sprintf("math: there is no ear in the polygon: %v", pts))
		}
		i0, i1, i2 := adjacentIndices(currentIndices, idx)
		indices = append(indices, i0, i1, i2)
		currentIndices = append(currentIndices[:idx], currentIndices[idx+1:]...)
	}
	return indices
}
<commit_msg>vector/internal/math: Optimization<commit_after>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package math

import (
	"fmt"
)

func cross(v0x, v0y, v1x, v1y float32) float32 {
	return v0x*v1y - v0y*v1x
}

func triangleCross(pt0, pt1, pt2 Point) float32 {
	return cross(pt1.X-pt0.X, pt1.Y-pt0.Y, pt2.X-pt1.X, pt2.Y-pt1.Y)
}

func adjacentIndices(indices []uint16, idx int) (uint16, uint16, uint16) {
	return indices[(idx+len(indices)-1)%len(indices)], indices[idx], indices[(idx+1)%len(indices)]
}

func InTriangle(pt, pt0, pt1, pt2 Point) bool {
	if pt.X <= pt0.X && pt.X <= pt1.X && pt.X <= pt2.X {
		return false
	}
	if pt.X >= pt0.X && pt.X >= pt1.X && pt.X >= pt2.X {
		return false
	}
	if pt.Y <= pt0.Y && pt.Y <= pt1.Y && pt.Y <= pt2.Y {
		return false
	}
	if pt.Y >= pt0.Y && pt.Y >= pt1.Y && pt.Y >= pt2.Y {
		return false
	}
	c0 := cross(pt.X-pt0.X, pt.Y-pt0.Y, pt1.X-pt0.X, pt1.Y-pt0.Y)
	c1 := cross(pt.X-pt1.X, pt.Y-pt1.Y, pt2.X-pt1.X, pt2.Y-pt1.Y)
	c2 := cross(pt.X-pt2.X, pt.Y-pt2.Y, pt0.X-pt2.X, pt0.Y-pt2.Y)
	return (c0 <= 0 && c1 <= 0 && c2 <= 0) || (c0 >= 0 && c1 >= 0 && c2 >= 0)
}

func Triangulate(pts []Point) []uint16 {
	if len(pts) < 3 {
		return nil
	}

	var currentIndices []uint16

	// Split pts into the two point groups if there are the same points.
	for i := range pts {
		for j := 0; j < i; j++ {
			if pts[i] == pts[j] {
				is0 := Triangulate(pts[j:i])
				for idx := range is0 {
					is0[idx] += uint16(j)
				}
				is1 := Triangulate(append(pts[i:], pts[:j]...))
				for idx := range is1 {
					is1[idx] = uint16((int(is1[idx]) + i) % len(pts))
				}
				return append(is0, is1...)
			}
		}
		currentIndices = append(currentIndices, uint16(i))
	}

	var indices []uint16

	// Triangulation by Ear Clipping.
	// https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
	for len(currentIndices) >= 3 {
		// Calculate cross-products and remove unneeded vertices.
		cs := make([]float32, len(currentIndices))
		idxToRemove := -1

		// Determine the direction of the polygon from the upper-left point.
		var upperLeft int
		for i := range currentIndices {
			i0, i1, i2 := adjacentIndices(currentIndices, i)
			pt0 := pts[i0]
			pt1 := pts[i1]
			pt2 := pts[i2]
			c := triangleCross(pt0, pt1, pt2)
			if c == 0 {
				idxToRemove = i
				break
			}
			cs[i] = c

			if pts[currentIndices[upperLeft]].X > pts[currentIndices[i]].X {
				upperLeft = i
			} else if pts[currentIndices[upperLeft]].X == pts[currentIndices[i]].X &&
				pts[currentIndices[upperLeft]].Y > pts[currentIndices[i]].Y {
				upperLeft = i
			}
		}
		if idxToRemove != -1 {
			currentIndices = append(currentIndices[:idxToRemove], currentIndices[idxToRemove+1:]...)
			continue
		}

		clockwise := cs[upperLeft] < 0

		idx := -1
	index:
		for i := range currentIndices {
			c := cs[i]
			if c == 0 {
				panic("math: cross value must not be 0")
			}
			if c < 0 && !clockwise || c > 0 && clockwise {
				// The angle is more than 180 degrees. This is not an ear.
				continue
			}

			i0, i1, i2 := adjacentIndices(currentIndices, i)
			pt0 := pts[i0]
			pt1 := pts[i1]
			pt2 := pts[i2]
			for _, j := range currentIndices {
				if j == i0 || j == i1 || j == i2 {
					continue
				}
				if InTriangle(pts[j], pt0, pt1, pt2) {
					// If the triangle includes another point, the triangle is not an ear.
					continue index
				}
			}
			// The angle is less than 180 degrees. This is an ear.
			idx = i
			break
		}
		if idx < 0 {
			// TODO: This happens when there is self-crossing.
			panic(fmt.Sprintf("math: there is no ear in the polygon: %v", pts))
		}
		i0, i1, i2 := adjacentIndices(currentIndices, idx)
		indices = append(indices, i0, i1, i2)
		currentIndices = append(currentIndices[:idx], currentIndices[idx+1:]...)
	}
	return indices
}
<|endoftext|>


<commit_before>package pooly

import (
	"math"
	"math/rand"
	"sync"
)

type SoftMax struct {
	temperature float32
}

func NewSoftMax(temperature float32) *SoftMax {
	return &SoftMax{temperature}
}

func (s *SoftMax) Select(hosts map[string]*Host) *Host {
	var sum, prob float64
	exp := make([]float64, len(hosts))

	i := 0
	for _, h := range hosts {
		score := h.Score()
		if score < 0 { // no score recorded
			exp[i] = 0
			continue
		}
		exp[i] = math.Exp(score / float64(s.temperature))
		sum += exp[i]
		i++
	}

	i = 0
	p := rand.Float64()
	for _, h := range hosts {
		if sum == 0 {
			return h
		}
		prob += exp[i] / sum // cumulative probability
		if prob > p {
			return h
		}
		i++
	}
	return nil
}

type EpsilonGreedy struct {
	epsilon float32
}

func NewEpsilonGreedy(epsilon float32) *EpsilonGreedy {
	return &EpsilonGreedy{epsilon}
}

func (e *EpsilonGreedy) Select(hosts map[string]*Host) (host *Host) {
	if rand.Float32() > e.epsilon { // exploit
		var max float64 = -1
		for _, h := range hosts {
			score := h.Score()
			if max = math.Max(max, score); max == score {
				host = h
			}
		}
	} else { // explore
		i, n := 0, rand.Intn(len(hosts))
		for _, h := range hosts {
			if i == n {
				host = h
				break
			}
			i++
		}
	}
	return
}

type RoundRobin struct {
	sync.Mutex
	nextSchedule  int64
	nextAvailSlot int64
}

func NewRoundRobin() *RoundRobin {
	return new(RoundRobin)
}

func (r *RoundRobin) Select(hosts map[string]*Host) (host *Host) {
	var offset int64
	var found bool

	// XXX score is not used, use it to attribute round robin scheduling instead
	// we don't need proper synchronization since score memoization isn't running here
	r.Lock()
	for _, h := range hosts {
		if h.score < 0 { // no score recorded
			h.score = float64(r.nextAvailSlot)
			r.nextAvailSlot++
		}
		if int64(h.score) == r.nextSchedule {
			offset = 1
			host = h
			found = true
		}
		if !found {
			// Find the next best schedule
			o := int64(h.score) - r.nextSchedule
			if o < 0 {
				o = r.nextAvailSlot + o
			}
			if offset == 0 || o < offset {
				offset = o + 1
				host = h
			}
		}
	}
	r.nextSchedule = (r.nextSchedule + offset) % r.nextAvailSlot
	r.Unlock()
	return
}
<commit_msg>Fix go1.3 map iteration<commit_after>package pooly

import (
	"math"
	"math/rand"
	"sync"
)

type SoftMax struct {
	temperature float32
}

func NewSoftMax(temperature float32) *SoftMax {
	return &SoftMax{temperature}
}

func (s *SoftMax) Select(hosts map[string]*Host) *Host {
	var sum, prob float64
	exp := make(map[*Host]float64, len(hosts))

	for _, h := range hosts {
		score := h.Score()
		if score < 0 { // no score recorded
			exp[h] = 0
			continue
		}
		exp[h] = math.Exp(score / float64(s.temperature))
		sum += exp[h]
	}

	p := rand.Float64()
	for _, h := range hosts {
		if sum == 0 {
			return h
		}
		prob += exp[h] / sum // cumulative probability
		if prob > p {
			return h
		}
	}
	return nil
}

type EpsilonGreedy struct {
	epsilon float32
}

func NewEpsilonGreedy(epsilon float32) *EpsilonGreedy {
	return &EpsilonGreedy{epsilon}
}

func (e *EpsilonGreedy) Select(hosts map[string]*Host) (host *Host) {
	if rand.Float32() > e.epsilon { // exploit
		var max float64 = -1
		for _, h := range hosts {
			score := h.Score()
			if max = math.Max(max, score); max == score {
				host = h
			}
		}
	} else { // explore
		i, n := 0, rand.Intn(len(hosts))
		for _, h := range hosts {
			if i == n {
				host = h
				break
			}
			i++
		}
	}
	return
}

type RoundRobin struct {
	sync.Mutex
	nextSchedule  int64
	nextAvailSlot int64
}

func NewRoundRobin() *RoundRobin {
	return new(RoundRobin)
}

func (r *RoundRobin) Select(hosts map[string]*Host) (host *Host) {
	var offset int64
	var found bool

	// XXX score is not used, use it to attribute round robin scheduling instead
	// we don't need proper synchronization since score memoization isn't running here
	r.Lock()
	for _, h := range hosts {
		if h.score < 0 { // no score recorded
			h.score = float64(r.nextAvailSlot)
			r.nextAvailSlot++
		}
		if int64(h.score) == r.nextSchedule {
			offset = 1
			host = h
			found = true
		}
		if !found {
			// Find the next best schedule
			o := int64(h.score) - r.nextSchedule
			if o < 0 {
				o = r.nextAvailSlot + o
			}
			if offset == 0 || o < offset {
				offset = o + 1
				host = h
			}
		}
	}
	r.nextSchedule = (r.nextSchedule + offset) % r.nextAvailSlot
	r.Unlock()
	return
}
<|endoftext|>