<commit_before>package wechat

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/http/httputil"
	"os"
	"strings"
	"time"

	"github.com/KevinGong2013/ggbot/utils"
	log "github.com/Sirupsen/logrus"
	"github.com/allegro/bigcache"
)

var logger = log.WithFields(log.Fields{
	"module": "wechat",
})

// Debug is a flag, if turn it on, will record all wx `send` api result to json file.
var Debug = true

// Version is wechat's main version
var Version = `0.9.0`

const httpOK = `200`

var debugPath = `.ggbot/debug/`

// BaseRequest is a base for all wx api request.
type BaseRequest struct {
	XMLName xml.Name `xml:"error" json:"-"`

	Ret        int    `xml:"ret" json:"-"`
	Message    string `xml:"message" json:"-"`
	Wxsid      string `xml:"wxsid" json:"Sid"`
	Skey       string `xml:"skey"`
	DeviceID   string `xml:"-"`
	Wxuin      int64  `xml:"wxuin" json:"Uin"`
	PassTicket string `xml:"pass_ticket" json:"-"`
}

// Caller is a interface, All response need implement this.
type Caller interface {
	IsSuccess() bool
	Error() error
}

// Response is a wrapper.
type Response struct {
	BaseResponse *BaseResponse
}

// IsSuccess flag this request is success or failed.
func (response *Response) IsSuccess() bool {
	return response.BaseResponse.Ret == 0
}

// response's error msg.
func (response *Response) Error() error {
	return fmt.Errorf("error message:[%s]", response.BaseResponse.ErrMsg)
}

// BaseResponse for all api resp.
type BaseResponse struct {
	Ret    int
	ErrMsg string
}

// WeChat container a default http client and base request.
type WeChat struct {
	Client        *http.Client
	BaseURL       string
	BaseRequest   *BaseRequest
	UUIDProcessor UUIDProcessor
	MySelf        Contact
	IsLogin       bool
	contactCache  *bigcache.BigCache
	nicknameCache *bigcache.BigCache
	syncKey       map[string]interface{}
	syncHost      string
}

// NewWeChat is desined for Create a new Wechat instance.
func newWeChat(up UUIDProcessor) (*WeChat, error) {

	if up == nil {
		return nil, errors.New(`UUID Processor must be not nil`)
	}
	if _, err := os.Stat(debugPath); err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(debugPath, os.ModePerm)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}

	client, err := newClient()
	if err != nil {
		return nil, err
	}

	bc, err := bigcache.NewBigCache(bigcache.DefaultConfig(7 * 24 * time.Hour))
	if err != nil {
		return nil, err
	}
	nc, err := bigcache.NewBigCache(bigcache.DefaultConfig(7 * 24 * time.Hour))
	if err != nil {
		return nil, err
	}

	baseReq := new(BaseRequest)
	baseReq.Ret = 1

	wechat := &WeChat{
		Client:        client,
		BaseRequest:   baseReq,
		UUIDProcessor: up,
		IsLogin:       false,
		contactCache:  bc,
		nicknameCache: nc,
	}

	return wechat, nil
}

func newClient() (*http.Client, error) {

	jar, err := cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	transport := http.Transport{
		Dial: (&net.Dialer{
			Timeout: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 30 * time.Second,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}

	client := &http.Client{
		Transport: &transport,
		Jar:       jar,
		Timeout:   30 * time.Second,
	}

	return client, nil
}

// WakeUp is start point for wx bot.
func WakeUp(up UUIDProcessor) (*WeChat, error) {

	if up == nil {
		up = new(defaultUUIDProcessor)
	}

	wechat, err := newWeChat(up)
	if err != nil {
		return nil, err
	}

	wechat.handleServerEvent()
	wechat.keepAlive()

	// 处理群消息的
	wechat.RegisterModule(newFlatten(wechat))

	return wechat, nil
}

// ExcuteRequest is desined for perform http request
func (wechat *WeChat) ExcuteRequest(req *http.Request, call Caller) error {

	ps := strings.Split(req.URL.Path, `/`)
	lastP := strings.Split(ps[len(ps)-1], `?`)[0][5:]
	filename := debugPath + lastP

	if Debug {
		reqData, _ := httputil.DumpRequest(req, true)
		utils.CreateFile(filename+`_req.kv`, reqData, false)
	}

	resp, err := wechat.Client.Do(req)

	if err != nil {
		return err
	}
	defer resp.Body.Close()

	reader := resp.Body.(io.Reader)

	if Debug {

		data, e := ioutil.ReadAll(reader)
		if e != nil {
			return e
		}

		utils.CreateFile(filename+`_resp.json`, data, true)
		reader = bytes.NewReader(data)
	}

	if err = json.NewDecoder(reader).Decode(call); err != nil {
		return err
	}

	if !call.IsSuccess() {
		return call.Error()
	}

	wechat.refreshCookieCache(resp.Cookies())

	return nil
}

// Excute a http request by default http client.
func (wechat *WeChat) Excute(path string, body io.Reader, call Caller) error {
	method := "GET"
	if body != nil {
		method = "POST"
	}
	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	req.Header.Set(`User-Agent`, `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/602.3.12 (KHTML, like Gecko) Version/10.0.2 Safari/602.3.12`)

	return wechat.ExcuteRequest(req, call)
}

// PassTicketKV return a string like `pass_ticket=sdfewsvdwd=`
func (wechat *WeChat) PassTicketKV() string {
	return fmt.Sprintf(`pass_ticket=%s`, wechat.BaseRequest.PassTicket)
}

// SkeyKV return a string like `skey=ewfwoefjwofjskfwes`
func (wechat *WeChat) SkeyKV() string {
	return fmt.Sprintf(`skey=%s`, wechat.BaseRequest.Skey)
}
<commit_msg>Change client timeout to 1mintue<commit_after>package wechat

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/http/httputil"
	"os"
	"strings"
	"time"

	"github.com/KevinGong2013/ggbot/utils"
	log "github.com/Sirupsen/logrus"
	"github.com/allegro/bigcache"
)

var logger = log.WithFields(log.Fields{
	"module": "wechat",
})

// Debug is a flag, if turn it on, will record all wx `send` api result to json file.
var Debug = true

// Version is wechat's main version
var Version = `0.9.0`

const httpOK = `200`

var debugPath = `.ggbot/debug/`

// BaseRequest is a base for all wx api request.
type BaseRequest struct {
	XMLName xml.Name `xml:"error" json:"-"`

	Ret        int    `xml:"ret" json:"-"`
	Message    string `xml:"message" json:"-"`
	Wxsid      string `xml:"wxsid" json:"Sid"`
	Skey       string `xml:"skey"`
	DeviceID   string `xml:"-"`
	Wxuin      int64  `xml:"wxuin" json:"Uin"`
	PassTicket string `xml:"pass_ticket" json:"-"`
}

// Caller is a interface, All response need implement this.
type Caller interface {
	IsSuccess() bool
	Error() error
}

// Response is a wrapper.
type Response struct {
	BaseResponse *BaseResponse
}

// IsSuccess flag this request is success or failed.
func (response *Response) IsSuccess() bool {
	return response.BaseResponse.Ret == 0
}

// response's error msg.
func (response *Response) Error() error {
	return fmt.Errorf("error message:[%s]", response.BaseResponse.ErrMsg)
}

// BaseResponse for all api resp.
type BaseResponse struct {
	Ret    int
	ErrMsg string
}

// WeChat container a default http client and base request.
type WeChat struct {
	Client        *http.Client
	BaseURL       string
	BaseRequest   *BaseRequest
	UUIDProcessor UUIDProcessor
	MySelf        Contact
	IsLogin       bool
	contactCache  *bigcache.BigCache
	nicknameCache *bigcache.BigCache
	syncKey       map[string]interface{}
	syncHost      string
}

// NewWeChat is desined for Create a new Wechat instance.
func newWeChat(up UUIDProcessor) (*WeChat, error) {

	if up == nil {
		return nil, errors.New(`UUID Processor must be not nil`)
	}
	if _, err := os.Stat(debugPath); err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(debugPath, os.ModePerm)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}

	client, err := newClient()
	if err != nil {
		return nil, err
	}

	bc, err := bigcache.NewBigCache(bigcache.DefaultConfig(7 * 24 * time.Hour))
	if err != nil {
		return nil, err
	}
	nc, err := bigcache.NewBigCache(bigcache.DefaultConfig(7 * 24 * time.Hour))
	if err != nil {
		return nil, err
	}

	baseReq := new(BaseRequest)
	baseReq.Ret = 1

	wechat := &WeChat{
		Client:        client,
		BaseRequest:   baseReq,
		UUIDProcessor: up,
		IsLogin:       false,
		contactCache:  bc,
		nicknameCache: nc,
	}

	return wechat, nil
}

func newClient() (*http.Client, error) {

	jar, err := cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	transport := http.Transport{
		Dial: (&net.Dialer{
			Timeout: 1 * time.Minute,
		}).Dial,
		TLSHandshakeTimeout: 1 * time.Minute,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}

	client := &http.Client{
		Transport: &transport,
		Jar:       jar,
		Timeout:   30 * time.Second,
	}

	return client, nil
}

// WakeUp is start point for wx bot.
func WakeUp(up UUIDProcessor) (*WeChat, error) {

	if up == nil {
		up = new(defaultUUIDProcessor)
	}

	wechat, err := newWeChat(up)
	if err != nil {
		return nil, err
	}

	wechat.handleServerEvent()
	wechat.keepAlive()

	// 处理群消息的
	wechat.RegisterModule(newFlatten(wechat))

	return wechat, nil
}

// ExcuteRequest is desined for perform http request
func (wechat *WeChat) ExcuteRequest(req *http.Request, call Caller) error {

	ps := strings.Split(req.URL.Path, `/`)
	lastP := strings.Split(ps[len(ps)-1], `?`)[0][5:]
	filename := debugPath + lastP

	if Debug {
		reqData, _ := httputil.DumpRequest(req, true)
		utils.CreateFile(filename+`_req.kv`, reqData, false)
	}

	resp, err := wechat.Client.Do(req)

	if err != nil {
		return err
	}
	defer resp.Body.Close()

	reader := resp.Body.(io.Reader)

	if Debug {

		data, e := ioutil.ReadAll(reader)
		if e != nil {
			return e
		}

		utils.CreateFile(filename+`_resp.json`, data, true)
		reader = bytes.NewReader(data)
	}

	if err = json.NewDecoder(reader).Decode(call); err != nil {
		return err
	}

	if !call.IsSuccess() {
		return call.Error()
	}

	wechat.refreshCookieCache(resp.Cookies())

	return nil
}

// Excute a http request by default http client.
func (wechat *WeChat) Excute(path string, body io.Reader, call Caller) error {
	method := "GET"
	if body != nil {
		method = "POST"
	}
	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	req.Header.Set(`User-Agent`, `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/602.3.12 (KHTML, like Gecko) Version/10.0.2 Safari/602.3.12`)

	return wechat.ExcuteRequest(req, call)
}

// PassTicketKV return a string like `pass_ticket=sdfewsvdwd=`
func (wechat *WeChat) PassTicketKV() string {
	return fmt.Sprintf(`pass_ticket=%s`, wechat.BaseRequest.PassTicket)
}

// SkeyKV return a string like `skey=ewfwoefjwofjskfwes`
func (wechat *WeChat) SkeyKV() string {
	return fmt.Sprintf(`skey=%s`, wechat.BaseRequest.Skey)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes 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 distributions

import (
	"fmt"
)

// Distribution represents a particular version of an operating system.
// This enables OS-dependent logic.
type Distribution struct {
	// packageFormat is the packaging format used by this distro; either deb or rpm, or "" for immutable OSes
	packageFormat string

	// project is the entity that produces the distribution e.g. "debian" or "ubuntu" or "rhel" or "centos"
	project string

	// id is the name of the actual distribution version e.g. "buster" or "xenial"
	id string

	// version is a numeric identifier for comparison purposes within a particular project
	version float32
}

var (
	DistributionDebian10     = Distribution{packageFormat: "deb", project: "debian", id: "buster", version: 10}
	DistributionDebian11     = Distribution{packageFormat: "deb", project: "debian", id: "bullseye", version: 11}
	DistributionUbuntu1804   = Distribution{packageFormat: "deb", project: "ubuntu", id: "bionic", version: 18.04}
	DistributionUbuntu2004   = Distribution{packageFormat: "deb", project: "ubuntu", id: "focal", version: 20.04}
	DistributionUbuntu2010   = Distribution{packageFormat: "deb", project: "ubuntu", id: "groovy", version: 20.10}
	DistributionUbuntu2104   = Distribution{packageFormat: "deb", project: "ubuntu", id: "hirsute", version: 21.04}
	DistributionUbuntu2110   = Distribution{packageFormat: "deb", project: "ubuntu", id: "impish", version: 21.10}
	DistributionUbuntu2204   = Distribution{packageFormat: "deb", project: "ubuntu", id: "jammy", version: 22.04}
	DistributionAmazonLinux2 = Distribution{packageFormat: "rpm", project: "amazonlinux2", id: "amazonlinux2", version: 0}
	DistributionRhel8        = Distribution{packageFormat: "rpm", project: "rhel", id: "rhel8", version: 8}
	DistributionRocky8       = Distribution{packageFormat: "rpm", project: "rocky", id: "rocky8", version: 8}
	DistributionFlatcar      = Distribution{packageFormat: "", project: "flatcar", id: "flatcar", version: 0}
	DistributionContainerOS  = Distribution{packageFormat: "", project: "containeros", id: "containeros", version: 0}
)

// IsDebianFamily returns true if this distribution uses deb packages and generally follows debian package names
func (d *Distribution) IsDebianFamily() bool {
	return d.packageFormat == "deb"
}

// IsUbuntu returns true if this distribution is Ubuntu (but not debian)
func (d *Distribution) IsUbuntu() bool {
	return d.project == "ubuntu"
}

// IsRHELFamily returns true if this distribution uses rpm packages and generally follows rhel package names
func (d *Distribution) IsRHELFamily() bool {
	return d.packageFormat == "rpm"
}

// IsSystemd returns true if this distribution uses systemd
func (d *Distribution) IsSystemd() bool {
	return true
}

// DefaultUsers returns the name of the system users for this distribution
func (d *Distribution) DefaultUsers() ([]string, error) {
	switch d.project {
	case "debian":
		return []string{"admin", "root"}, nil
	case "ubuntu":
		return []string{"ubuntu", "root"}, nil
	case "centos":
		return []string{"centos"}, nil
	case "rhel", "amazonlinux2":
		return []string{"ec2-user"}, nil
	case "rocky":
		return []string{"rocky"}, nil
	case "flatcar":
		return []string{"core"}, nil
	default:
		return nil, fmt.Errorf("unknown distro %v", d)
	}
}

// HasLoopbackEtcResolvConf is true if systemd-resolved has put the loopback address 127.0.0.53 as a nameserver in /etc/resolv.conf
// See https://github.com/coredns/coredns/blob/master/plugin/loop/README.md#troubleshooting-loops-in-kubernetes-clusters
func (d *Distribution) HasLoopbackEtcResolvConf() bool {
	if d.IsUbuntu() {
		// Ubuntu > 16.04 has it
		return d.version > 16.04
	}
	return false
}

// Version returns the (project scoped) numeric version
func (d *Distribution) Version() float32 {
	return d.version
}
<commit_msg>Avoid resolv.conf file loopback for Flatcar distro<commit_after>/*
Copyright 2019 The Kubernetes 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 distributions

import (
	"fmt"
)

// Distribution represents a particular version of an operating system.
// This enables OS-dependent logic.
type Distribution struct {
	// packageFormat is the packaging format used by this distro; either deb or rpm, or "" for immutable OSes
	packageFormat string

	// project is the entity that produces the distribution e.g. "debian" or "ubuntu" or "rhel" or "centos"
	project string

	// id is the name of the actual distribution version e.g. "buster" or "xenial"
	id string

	// version is a numeric identifier for comparison purposes within a particular project
	version float32
}

var (
	DistributionDebian10     = Distribution{packageFormat: "deb", project: "debian", id: "buster", version: 10}
	DistributionDebian11     = Distribution{packageFormat: "deb", project: "debian", id: "bullseye", version: 11}
	DistributionUbuntu1804   = Distribution{packageFormat: "deb", project: "ubuntu", id: "bionic", version: 18.04}
	DistributionUbuntu2004   = Distribution{packageFormat: "deb", project: "ubuntu", id: "focal", version: 20.04}
	DistributionUbuntu2010   = Distribution{packageFormat: "deb", project: "ubuntu", id: "groovy", version: 20.10}
	DistributionUbuntu2104   = Distribution{packageFormat: "deb", project: "ubuntu", id: "hirsute", version: 21.04}
	DistributionUbuntu2110   = Distribution{packageFormat: "deb", project: "ubuntu", id: "impish", version: 21.10}
	DistributionUbuntu2204   = Distribution{packageFormat: "deb", project: "ubuntu", id: "jammy", version: 22.04}
	DistributionAmazonLinux2 = Distribution{packageFormat: "rpm", project: "amazonlinux2", id: "amazonlinux2", version: 0}
	DistributionRhel8        = Distribution{packageFormat: "rpm", project: "rhel", id: "rhel8", version: 8}
	DistributionRocky8       = Distribution{packageFormat: "rpm", project: "rocky", id: "rocky8", version: 8}
	DistributionFlatcar      = Distribution{packageFormat: "", project: "flatcar", id: "flatcar", version: 0}
	DistributionContainerOS  = Distribution{packageFormat: "", project: "containeros", id: "containeros", version: 0}
)

// IsDebianFamily returns true if this distribution uses deb packages and generally follows debian package names
func (d *Distribution) IsDebianFamily() bool {
	return d.packageFormat == "deb"
}

// IsUbuntu returns true if this distribution is Ubuntu (but not debian)
func (d *Distribution) IsUbuntu() bool {
	return d.project == "ubuntu"
}

// IsRHELFamily returns true if this distribution uses rpm packages and generally follows rhel package names
func (d *Distribution) IsRHELFamily() bool {
	return d.packageFormat == "rpm"
}

// IsSystemd returns true if this distribution uses systemd
func (d *Distribution) IsSystemd() bool {
	return true
}

// DefaultUsers returns the name of the system users for this distribution
func (d *Distribution) DefaultUsers() ([]string, error) {
	switch d.project {
	case "debian":
		return []string{"admin", "root"}, nil
	case "ubuntu":
		return []string{"ubuntu", "root"}, nil
	case "centos":
		return []string{"centos"}, nil
	case "rhel", "amazonlinux2":
		return []string{"ec2-user"}, nil
	case "rocky":
		return []string{"rocky"}, nil
	case "flatcar":
		return []string{"core"}, nil
	default:
		return nil, fmt.Errorf("unknown distro %v", d)
	}
}

// HasLoopbackEtcResolvConf is true if systemd-resolved has put the loopback address 127.0.0.53 as a nameserver in /etc/resolv.conf
// See https://github.com/coredns/coredns/blob/master/plugin/loop/README.md#troubleshooting-loops-in-kubernetes-clusters
func (d *Distribution) HasLoopbackEtcResolvConf() bool {
	if d.IsUbuntu() {
		// Ubuntu > 16.04 has it
		return d.version > 16.04
	}
	if d.project == "flatcar" {
		return true
	}
	return false
}

// Version returns the (project scoped) numeric version
func (d *Distribution) Version() float32 {
	return d.version
}
<|endoftext|>


<commit_before>package intercept

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"github.com/nbio/st"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

var errRead = errors.New("read error")

type errorReader struct{}

func (r *errorReader) Read(p []byte) (int, error) {
	return 0, errRead
}

type user struct {
	XMLName xml.Name `xml:"Person"`
	Name    string
}

func TestNewRequestModifier(t *testing.T) {
	h := http.Header{}
	h.Set("foo", "bar")
	req := &http.Request{Header: h}
	modifier := NewRequestModifier(req)
	st.Expect(t, modifier.Request, req)
	st.Expect(t, modifier.Header, h)
}

func TestReadString(t *testing.T) {
	bodyStr := `{"name":"Rick"}`
	strReader := strings.NewReader(bodyStr)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	str, err := modifier.ReadString()
	st.Expect(t, err, nil)
	st.Expect(t, str, bodyStr)
}

func TestReadStringError(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	str, err := modifier.ReadString()
	st.Expect(t, err, errRead)
	st.Expect(t, str, "")
}

func TestReadBytes(t *testing.T) {
	bodyBytes := []byte(`{"name":"Rick"}`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	str, err := modifier.ReadBytes()
	st.Expect(t, err, nil)
	st.Expect(t, str, bodyBytes)
}

func TestReadBytesError(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	buf, err := modifier.ReadBytes()
	st.Expect(t, err, errRead)
	st.Expect(t, len(buf), 0)
}

func TestDecodeJSON(t *testing.T) {
	bodyBytes := []byte(`{"name":"Rick"}`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "Rick")
}

func TestDecodeJSONErrorFromReadBytes(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	st.Expect(t, err, errRead)
	st.Expect(t, u.Name, "")
}

func TestDecodeJSONEOF(t *testing.T) {
	bodyBytes := []byte("")
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "")
}

func TestDecodeJSONErrorFromDecode(t *testing.T) {
	bodyBytes := []byte(`/`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	_, ok := (err).(*json.SyntaxError)
	st.Expect(t, ok, true)
	st.Expect(t, err.Error(), "invalid character '/' looking for beginning of value")
	st.Expect(t, u.Name, "")
}

func TestDecodeXML(t *testing.T) {
	bodyBytes := []byte(`<Person><Name>Rick</Name></Person>`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "Rick")
}

func TestDecodeXMLErrorFromReadBytes(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	st.Expect(t, err, errRead)
	st.Expect(t, u.Name, "")
}

func TestDecodeXMLErrorFromDecode(t *testing.T) {
	bodyBytes := []byte(`]]>`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	_, ok := (err).(*xml.SyntaxError)
	st.Expect(t, ok, true)
	st.Expect(t, err.Error(), "XML syntax error on line 1: unescaped ]]> not in CDATA section")
	st.Expect(t, u.Name, "")
}

func TestDecodeXMLEOF(t *testing.T) {
	bodyBytes := []byte("")
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "")
}

func TestBytes(t *testing.T) {
	bodyBytes := []byte("")
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	modifier.Bytes([]byte("hello"))
	modifiedBody, err := ioutil.ReadAll(req.Body)
	st.Expect(t, err, nil)
	st.Expect(t, modifiedBody, []byte("hello"))
}
<commit_msg>feat(test): Add test for String<commit_after>package intercept

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"github.com/nbio/st"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

var errRead = errors.New("read error")

type errorReader struct{}

func (r *errorReader) Read(p []byte) (int, error) {
	return 0, errRead
}

type user struct {
	XMLName xml.Name `xml:"Person"`
	Name    string
}

func TestNewRequestModifier(t *testing.T) {
	h := http.Header{}
	h.Set("foo", "bar")
	req := &http.Request{Header: h}
	modifier := NewRequestModifier(req)
	st.Expect(t, modifier.Request, req)
	st.Expect(t, modifier.Header, h)
}

func TestReadString(t *testing.T) {
	bodyStr := `{"name":"Rick"}`
	strReader := strings.NewReader(bodyStr)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	str, err := modifier.ReadString()
	st.Expect(t, err, nil)
	st.Expect(t, str, bodyStr)
}

func TestReadStringError(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	str, err := modifier.ReadString()
	st.Expect(t, err, errRead)
	st.Expect(t, str, "")
}

func TestReadBytes(t *testing.T) {
	bodyBytes := []byte(`{"name":"Rick"}`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	str, err := modifier.ReadBytes()
	st.Expect(t, err, nil)
	st.Expect(t, str, bodyBytes)
}

func TestReadBytesError(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	buf, err := modifier.ReadBytes()
	st.Expect(t, err, errRead)
	st.Expect(t, len(buf), 0)
}

func TestDecodeJSON(t *testing.T) {
	bodyBytes := []byte(`{"name":"Rick"}`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "Rick")
}

func TestDecodeJSONErrorFromReadBytes(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	st.Expect(t, err, errRead)
	st.Expect(t, u.Name, "")
}

func TestDecodeJSONEOF(t *testing.T) {
	bodyBytes := []byte("")
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "")
}

func TestDecodeJSONErrorFromDecode(t *testing.T) {
	bodyBytes := []byte(`/`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeJSON(&u)
	_, ok := (err).(*json.SyntaxError)
	st.Expect(t, ok, true)
	st.Expect(t, err.Error(), "invalid character '/' looking for beginning of value")
	st.Expect(t, u.Name, "")
}

func TestDecodeXML(t *testing.T) {
	bodyBytes := []byte(`<Person><Name>Rick</Name></Person>`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "Rick")
}

func TestDecodeXMLErrorFromReadBytes(t *testing.T) {
	body := ioutil.NopCloser(&errorReader{})
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	st.Expect(t, err, errRead)
	st.Expect(t, u.Name, "")
}

func TestDecodeXMLErrorFromDecode(t *testing.T) {
	bodyBytes := []byte(`]]>`)
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	_, ok := (err).(*xml.SyntaxError)
	st.Expect(t, ok, true)
	st.Expect(t, err.Error(), "XML syntax error on line 1: unescaped ]]> not in CDATA section")
	st.Expect(t, u.Name, "")
}

func TestDecodeXMLEOF(t *testing.T) {
	bodyBytes := []byte("")
	strReader := bytes.NewBuffer(bodyBytes)
	body := ioutil.NopCloser(strReader)
	req := &http.Request{Header: http.Header{}, Body: body}
	modifier := NewRequestModifier(req)
	u := user{}
	err := modifier.DecodeXML(&u, nil)
	st.Expect(t, err, nil)
	st.Expect(t, u.Name, "")
}

func TestBytes(t *testing.T) {
	req := &http.Request{Header: http.Header{}}
	modifier := NewRequestModifier(req)
	modifier.Bytes([]byte("hello"))
	modifiedBody, err := ioutil.ReadAll(req.Body)
	st.Expect(t, err, nil)
	st.Expect(t, modifiedBody, []byte("hello"))
}

func TestStringGet(t *testing.T) {
	req := &http.Request{Method: "GET", Header: http.Header{}}
	modifier := NewRequestModifier(req)
	modifier.String("hello")
	st.Expect(t, req.Body, nil)
}
<|endoftext|>


<commit_before>// Copyright 2020-2021 The Cloudprober 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 file implements a file-based targets for cloudprober.
*/
package file

import (
	"context"

	"github.com/cloudprober/cloudprober/logger"
	"github.com/cloudprober/cloudprober/rds/client"
	client_configpb "github.com/cloudprober/cloudprober/rds/client/proto"
	"github.com/cloudprober/cloudprober/rds/file"
	file_configpb "github.com/cloudprober/cloudprober/rds/file/proto"
	rdspb "github.com/cloudprober/cloudprober/rds/proto"
	configpb "github.com/cloudprober/cloudprober/targets/file/proto"
	dnsRes "github.com/cloudprober/cloudprober/targets/resolver"
)

// New returns new file targets.
func New(opts *configpb.TargetsConf, res *dnsRes.Resolver, l *logger.Logger) (*client.Client, error) {
	lister, err := file.New(&file_configpb.ProviderConfig{
		FilePath: []string{opts.GetFilePath()},
	}, l)
	if err != nil {
		return nil, err
	}

	clientConf := &client_configpb.ClientConf{
		Request:   &rdspb.ListResourcesRequest{Filter: opts.GetFilter()},
		ReEvalSec: opts.ReEvalSec,
	}

	return client.New(clientConf, func(_ context.Context, req *rdspb.ListResourcesRequest) (*rdspb.ListResourcesResponse, error) {
		return lister.ListResources(req)
	}, l)
}
<commit_msg>[targets][file] Fix file targets' re-eval strategy (See: #136).<commit_after>// Copyright 2020-2021 The Cloudprober 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 file implements a file-based targets for cloudprober.
*/
package file

import (
	"context"

	"github.com/cloudprober/cloudprober/logger"
	"github.com/cloudprober/cloudprober/rds/client"
	client_configpb "github.com/cloudprober/cloudprober/rds/client/proto"
	"github.com/cloudprober/cloudprober/rds/file"
	file_configpb "github.com/cloudprober/cloudprober/rds/file/proto"
	rdspb "github.com/cloudprober/cloudprober/rds/proto"
	configpb "github.com/cloudprober/cloudprober/targets/file/proto"
	dnsRes "github.com/cloudprober/cloudprober/targets/resolver"
	"google.golang.org/protobuf/proto"
)

// New returns new file targets.
func New(opts *configpb.TargetsConf, res *dnsRes.Resolver, l *logger.Logger) (*client.Client, error) {
	lister, err := file.New(&file_configpb.ProviderConfig{
		FilePath:  []string{opts.GetFilePath()},
		ReEvalSec: proto.Int32(opts.GetReEvalSec()),
	}, l)
	if err != nil {
		return nil, err
	}

	// We can be really aggressive about client refresh interval as "file" RDS
	// provider is smart about using the last_modified field of the response,
	// and client checking for targets before file has been reloaded will not
	// trigger unnecessary recreation of target objects.
	// Ref: https://github.com/cloudprober/cloudprober/blob/5bec0db1ac908e69bff0fbca3182415c4e267d64/rds/client/client.go#L103
	clientConf := &client_configpb.ClientConf{
		Request:   &rdspb.ListResourcesRequest{Filter: opts.GetFilter()},
		ReEvalSec: proto.Int32(1),
	}

	return client.New(clientConf, func(_ context.Context, req *rdspb.ListResourcesRequest) (*rdspb.ListResourcesResponse, error) {
		return lister.ListResources(req)
	}, l)
}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.711"
<commit_msg>fnserver: v0.3.712 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.712"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.336"
<commit_msg>fnserver: 0.3.337 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.337"
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.285"
<commit_msg>fnserver: 0.3.286 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.286"
<|endoftext|>


<commit_before>// TODO make a header.
package window

import (
	"code.google.com/a/google.com/p/gojira/content"
	"github.com/go-gl/gl"
	"github.com/go-gl/glfw"
	"image"
	"log"
)

// | These together to record which button is up or down.
// TODO(rjkroege): Bring this into alignment with webkit events.
const (
	MOUSE_BUTTON_NONE = 0
)

// Reset iota.
const (
	MOUSE_BUTTON_LEFT = 1 << iota
	MOUSE_BUTTON_MIDDLE
	MOUSE_BUTTON_RIGHT
)

// Stores the current mouse pointer position.
type Mousepointer struct {
	x, y       int
	buttonmask uint32
}

// TODO: when this gets big, it might want to be a package.
type Window struct {
	width, height uint32
	frame         *content.Frame
	pointer       Mousepointer

	// Position of the Frame's origin in the viewport. Starts as 0, 0 where the
	// Window (aka viewport) will show the upper left corner of the content.Frame.
	// rjk presumes that positive y extends down the page and positive x increase
	// towards the right. As a result, x, y are <= 0.
	//
	// rjk:  T S cs = ws for Translation and Scale and cs is in content coordinates. Verify.
	x, y float32

	// Width, height of the frame. Must start the same size as the Window.
	fw, fh float32

	// TODO(rjkroege): When the mouse wheel handling is rational and actually
	// ships us deltas, then we can remove this code.
	previous_absolute_displacement int

	// Event handling interface
	ev content.EventHandler
}

func NewWindow(width int, height int) *Window {
	c := content.NewFrame()
	return &Window{uint32(width), uint32(height), c, Mousepointer{0, 0, 0}, 0.0, 0.0,
		float32(width), float32(height), 0, c}
}

func (window *Window) RunMessageLoop() {
	should_continue := true
	for should_continue {
		// TODO(rjkroege): full generality: provide the transform to bring the Frame into
		// Window coordinates and the width and height.
		window.fw, window.fh = window.frame.Draw(window.x, window.y, float32(window.width), float32(window.height))
		glfw.SwapBuffers()
		should_continue = glfw.WindowParam(glfw.Opened) == 1
	}
}

// Based on https://raw.github.com/go-gl/examples/master/glfw/simplewindow
func (window *Window) Open() {
	red_bits := 8
	green_bits := 8
	blue_bits := 8
	alpha_bits := 8
	depth_bits := 0
	stencil_bits := 0
	mode := glfw.Windowed

	// TODO: what's go style here? How do you get clang-format for go?
	// TODO(rjkroege): sizes should be uints
	err := glfw.OpenWindow(int(window.width), int(window.height),
		red_bits, green_bits, blue_bits, alpha_bits,
		depth_bits, stencil_bits,
		mode)

	if err != nil {
		// TODO: error?
	}

	defer glfw.CloseWindow()

	// Apparantly, this enables vsync?
	glfw.SetSwapInterval(1)
	glfw.SetWindowTitle("Hello, World!")
	glfw.SetWindowSizeCallback(func(w, h int) {
		window.onResize(w, h)
	})

	glfw.SetMouseButtonCallback(func(b, s int) {
		window.onMouseBtn(b, s)
	})

	glfw.SetMouseWheelCallback(func(d int) {
		window.onMouseWheel(d)
	})

	glfw.SetKeyCallback(func(k, s int) {
		window.onKey(k, s)
	})

	glfw.SetCharCallback(func(k, s int) {
		window.onChar(k, s)
	})

	glfw.SetMousePosCallback(func(x, y int) {
		window.onMousePos(x, y)
	})

	window.RunMessageLoop()
}

func (window *Window) onResize(w, h int) {
	gl.DrawBuffer(gl.FRONT_AND_BACK)

	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	gl.Viewport(0, 0, w, h)
	gl.Ortho(0, float64(w), float64(h), 0, -1.0, 1.0)
	gl.ClearColor(1, 1, 1, 0)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()

	log.Printf("Resize %d %d", window.width, window.height)
}

func (window *Window) onMouseBtn(button, state int) {
	if button < 0 || button > 31 || state < 0 || state > 1 {
		log.Fatal("button/state values from glfw are silly: ", button, state)
	}

	b := uint32(button)
	p := window.mousePositionInFrame()
	if state == 1 {
		window.pointer.buttonmask |= 1 << b
		window.ev.Mousedown(p, b, window.pointer.buttonmask)
	} else {
		window.pointer.buttonmask &= ^(1 << b)
		window.ev.Mouseup(p, b, window.pointer.buttonmask)
	}
}

// Get the position of the mouse in the coordinates of the frame.
// This function is currently broken when we're scrolled.
func (w *Window) mousePositionInFrame() image.Point {
	p := image.Pt(w.pointer.x, w.pointer.y)
	p.Add(image.Pt(int(w.x), int(w.y)))
	return p
}

// Mini-essay on the way of scrolling. In scrolling, there are two
// conceptual entities: the viewport and the contents.
//
// I maintain that scrolling should be handled by the viewport. The
// contents should provide the capability of drawing an arbitrary
// subrectangle of itself. But the choice of subrectangle is managed by
// the viewport.
//
// The principle here is to handle the event as "close" to where it
// arrives as possible. In particular: scrolling will happen in the
// browser.
func (window *Window) onMouseWheel(absolute_displacement int) {
	log.Printf("mouse wheel abs: %d\n", absolute_displacement)

	// TODO(rjkroege): displacement is stupid because of glfw issue. Fix.
	// delta is not a delta. I am instead interested in the difference.
	delta := absolute_displacement - window.previous_absolute_displacement
	window.previous_absolute_displacement = absolute_displacement
	// log.Printf("mouse wheel delta: %d\n", delta)

	dx := float32(delta)
	// Scrolling is currently only in one dimension because of glfw limitation.
	// TODO(rjkroege): enable two-dimensional scrolling.

	p := window.mousePositionInFrame()
	if window.ev.Wheel(p, window.pointer.buttonmask, dx, 0, 0) != content.EVD_PREVDEF {
		// window.x = content.Max(window.width - window.fw, window.x + float32(delta.x))
		// Consider putting Max in some kind of base-like class.
		window.y = content.Max(float32(window.height)-window.fh, window.y + dx)

		// Can't scroll before the start of the content area (need x limit in the future.)
		if window.y > 0.0 {
			window.y = 0.0
		}

		log.Printf("window.y is max of %f %f\n", float32(window.height)-window.fh, window.y+float32(delta))

		if window.y >= 0.0 {
			log.Print("i have the scrolling sign backwards")
		}
	}
}

// TODO(rjkroege): Add support for delivering of key events.
func (window *Window) onKey(key, state int) {
	log.Printf("key: %d, %d\n", key, state)
}

func (window *Window) onChar(key, state int) {
	log.Printf("char: %d, %d\n", key, state)
}

func (window *Window) onMousePos(x, y int) {
	// log.Printf("mouse motion %d %d\n", x, y)
	window.pointer.x = x
	window.pointer.y = y

	p := window.mousePositionInFrame()

	// TODO(rjkroege): filter/collapse/schedule the events as desirable.
	window.ev.Mousemove(p, window.pointer.buttonmask)
}
<commit_msg>fixed transforms on scrolled layers<commit_after>// TODO make a header.
package window

import (
	"code.google.com/a/google.com/p/gojira/content"
	"github.com/go-gl/gl"
	"github.com/go-gl/glfw"
	"image"
	"log"
)

// | These together to record which button is up or down.
// TODO(rjkroege): Bring this into alignment with webkit events.
const (
	MOUSE_BUTTON_NONE = 0
)

// Reset iota.
const (
	MOUSE_BUTTON_LEFT = 1 << iota
	MOUSE_BUTTON_MIDDLE
	MOUSE_BUTTON_RIGHT
)

// Stores the current mouse pointer position.
type Mousepointer struct {
	x, y       int
	buttonmask uint32
}

// TODO: when this gets big, it might want to be a package.
type Window struct {
	width, height uint32
	frame         *content.Frame
	pointer       Mousepointer

	// Position of the Frame's origin in the viewport. Starts as 0, 0 where the
	// Window (aka viewport) will show the upper left corner of the content.Frame.
	// rjk presumes that positive y extends down the page and positive x increase
	// towards the right. As a result, x, y are <= 0.
	//
	// rjk:  T S cs = ws for Translation and Scale and cs is in content coordinates. Verify.
	x, y float32

	// Width, height of the frame. Must start the same size as the Window.
	fw, fh float32

	// TODO(rjkroege): When the mouse wheel handling is rational and actually
	// ships us deltas, then we can remove this code.
	previous_absolute_displacement int

	// Event handling interface
	ev content.EventHandler
}

func NewWindow(width int, height int) *Window {
	c := content.NewFrame()
	return &Window{uint32(width), uint32(height), c, Mousepointer{0, 0, 0}, 0.0, 0.0,
		float32(width), float32(height), 0, c}
}

func (window *Window) RunMessageLoop() {
	should_continue := true
	for should_continue {
		// TODO(rjkroege): full generality: provide the transform to bring the Frame into
		// Window coordinates and the width and height.
		window.fw, window.fh = window.frame.Draw(window.x, window.y, float32(window.width), float32(window.height))
		glfw.SwapBuffers()
		should_continue = glfw.WindowParam(glfw.Opened) == 1
	}
}

// Based on https://raw.github.com/go-gl/examples/master/glfw/simplewindow
func (window *Window) Open() {
	red_bits := 8
	green_bits := 8
	blue_bits := 8
	alpha_bits := 8
	depth_bits := 0
	stencil_bits := 0
	mode := glfw.Windowed

	// TODO: what's go style here? How do you get clang-format for go?
	// TODO(rjkroege): sizes should be uints
	err := glfw.OpenWindow(int(window.width), int(window.height),
		red_bits, green_bits, blue_bits, alpha_bits,
		depth_bits, stencil_bits,
		mode)

	if err != nil {
		// TODO: error?
	}

	defer glfw.CloseWindow()

	// Apparantly, this enables vsync?
	glfw.SetSwapInterval(1)
	glfw.SetWindowTitle("Hello, World!")
	glfw.SetWindowSizeCallback(func(w, h int) {
		window.onResize(w, h)
	})

	glfw.SetMouseButtonCallback(func(b, s int) {
		window.onMouseBtn(b, s)
	})

	glfw.SetMouseWheelCallback(func(d int) {
		window.onMouseWheel(d)
	})

	glfw.SetKeyCallback(func(k, s int) {
		window.onKey(k, s)
	})

	glfw.SetCharCallback(func(k, s int) {
		window.onChar(k, s)
	})

	glfw.SetMousePosCallback(func(x, y int) {
		window.onMousePos(x, y)
	})

	window.RunMessageLoop()
}

func (window *Window) onResize(w, h int) {
	gl.DrawBuffer(gl.FRONT_AND_BACK)

	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	gl.Viewport(0, 0, w, h)
	gl.Ortho(0, float64(w), float64(h), 0, -1.0, 1.0)
	gl.ClearColor(1, 1, 1, 0)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()

	log.Printf("Resize %d %d", window.width, window.height)
}

func (window *Window) onMouseBtn(button, state int) {
	if button < 0 || button > 31 || state < 0 || state > 1 {
		log.Fatal("button/state values from glfw are silly: ", button, state)
	}

	b := uint32(button)
	p := window.mousePositionInFrame()
	if state == 1 {
		window.pointer.buttonmask |= 1 << b
		window.ev.Mousedown(p, b, window.pointer.buttonmask)
	} else {
		window.pointer.buttonmask &= ^(1 << b)
		window.ev.Mouseup(p, b, window.pointer.buttonmask)
	}
}

// Get the position of the mouse in the coordinates of the frame.
// The sub is right for y-axis but since we can't scroll x-axis.
func (w *Window) mousePositionInFrame() image.Point {
	p := image.Pt(w.pointer.x, w.pointer.y)
	p = p.Sub(image.Pt(int(w.x), int(w.y)))
	return p
}

// Mini-essay on the way of scrolling. In scrolling, there are two
// conceptual entities: the viewport and the contents.
//
// I maintain that scrolling should be handled by the viewport. The
// contents should provide the capability of drawing an arbitrary
// subrectangle of itself. But the choice of subrectangle is managed by
// the viewport.
//
// The principle here is to handle the event as "close" to where it
// arrives as possible. In particular: scrolling will happen in the
// browser.
func (window *Window) onMouseWheel(absolute_displacement int) {
	log.Printf("mouse wheel abs: %d\n", absolute_displacement)

	// TODO(rjkroege): displacement is stupid because of glfw issue. Fix.
	// delta is not a delta. I am instead interested in the difference.
	delta := absolute_displacement - window.previous_absolute_displacement
	window.previous_absolute_displacement = absolute_displacement
	// log.Printf("mouse wheel delta: %d\n", delta)

	dx := float32(delta)
	// Scrolling is currently only in one dimension because of glfw limitation.
	// TODO(rjkroege): enable two-dimensional scrolling.

	p := window.mousePositionInFrame()
	if window.ev.Wheel(p, window.pointer.buttonmask, dx, 0, 0) != content.EVD_PREVDEF {
		// window.x = content.Max(window.width - window.fw, window.x + float32(delta.x))
		// Consider putting Max in some kind of base-like class.
		window.y = content.Max(float32(window.height)-window.fh, window.y + dx)

		// Can't scroll before the start of the content area (need x limit in the future.)
		if window.y > 0.0 {
			window.y = 0.0
		}

		log.Printf("window.y is max of %f %f\n", float32(window.height)-window.fh, window.y+float32(delta))

		if window.y >= 0.0 {
			log.Print("i have the scrolling sign backwards")
		}
	}
}

// TODO(rjkroege): Add support for delivering of key events.
func (window *Window) onKey(key, state int) {
	log.Printf("key: %d, %d\n", key, state)
}

func (window *Window) onChar(key, state int) {
	log.Printf("char: %d, %d\n", key, state)
}

func (window *Window) onMousePos(x, y int) {
	// log.Printf("mouse motion %d %d\n", x, y)
	window.pointer.x = x
	window.pointer.y = y

	p := window.mousePositionInFrame()

	// TODO(rjkroege): filter/collapse/schedule the events as desirable.
	window.ev.Mousemove(p, window.pointer.buttonmask)
}
<|endoftext|>


<commit_before>package worker

import (
	"sync"
	"time"

	"github.com/concourse/baggageclaim"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
)

const volumeKeepalive = 30 * time.Second

//go:generate counterfeiter . VolumeFactoryDB

type VolumeFactoryDB interface {
	GetVolumeTTL(volumeHandle string) (time.Duration, bool, error)
	ReapVolume(handle string) error
	SetVolumeTTL(string, time.Duration) error
}

//go:generate counterfeiter . VolumeFactory

type VolumeFactory interface {
	Build(lager.Logger, baggageclaim.Volume) (Volume, bool, error)
}

type volumeFactory struct {
	db    VolumeFactoryDB
	clock clock.Clock
}

func NewVolumeFactory(db VolumeFactoryDB, clock clock.Clock) VolumeFactory {
	return &volumeFactory{
		db:    db,
		clock: clock,
	}
}

func (vf *volumeFactory) Build(logger lager.Logger, bcVol baggageclaim.Volume) (Volume, bool, error) {
	bcVol.Release(nil)
	return newVolume(logger, bcVol, vf.clock, vf.db)
}

//go:generate counterfeiter . Volume

type Volume interface {
	baggageclaim.Volume

	// a noop method to ensure things aren't just returning baggageclaim.Volume
	HeartbeatingToDB()
}

type volume struct {
	baggageclaim.Volume

	db VolumeFactoryDB

	release      chan *time.Duration
	heartbeating *sync.WaitGroup
	releaseOnce  sync.Once
}

type VolumeMount struct {
	Volume    Volume
	MountPath string
}

func newVolume(logger lager.Logger, bcVol baggageclaim.Volume, clock clock.Clock, db VolumeFactoryDB) (Volume, bool, error) {
	logger = logger.WithData(lager.Data{"volume": bcVol.Handle()})

	vol := &volume{
		Volume: bcVol,
		db:     db,

		heartbeating: new(sync.WaitGroup),
		release:      make(chan *time.Duration, 1),
	}

	ttl, found, err := vol.db.GetVolumeTTL(vol.Handle())
	if err != nil {
		logger.Error("failed-to-lookup-expiration-of-volume", err)
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	vol.heartbeat(logger.Session("initial-heartbeat"), ttl)

	vol.heartbeating.Add(1)
	go vol.heartbeatContinuously(
		logger.Session("continuous-heartbeat"),
		clock.NewTicker(volumeKeepalive),
		ttl,
	)

	return vol, true, nil
}

func (*volume) HeartbeatingToDB() {}

func (v *volume) Release(finalTTL *time.Duration) {
	v.releaseOnce.Do(func() {
		v.release <- finalTTL
		v.heartbeating.Wait()
	})

	return
}

func (v *volume) heartbeatContinuously(logger lager.Logger, pacemaker clock.Ticker, initialTTL time.Duration) {
	defer v.heartbeating.Done()
	defer pacemaker.Stop()

	logger.Debug("start")
	defer logger.Debug("done")

	ttlToSet := initialTTL

	for {
		select {
		case <-pacemaker.C():
			ttl, found, err := v.db.GetVolumeTTL(v.Handle())
			if err != nil {
				logger.Error("failed-to-lookup-ttl", err)
			} else {
				if !found {
					logger.Info("volume-expired-from-database")
					return
				}

				ttlToSet = ttl
			}

			v.heartbeat(logger.Session("tick"), ttlToSet)

		case finalTTL := <-v.release:
			if finalTTL != nil {
				v.heartbeat(logger.Session("final"), *finalTTL)
			}

			return
		}
	}
}

func (v *volume) heartbeat(logger lager.Logger, ttl time.Duration) {
	logger.Debug("start")
	defer logger.Debug("done")

	err := v.SetTTL(ttl)
	if err != nil {
		if err == baggageclaim.ErrVolumeNotFound {
			v.db.ReapVolume(v.Handle())
		}
		logger.Error("failed-to-heartbeat-to-volume", err)
	}

	err = v.db.SetVolumeTTL(v.Handle(), ttl)
	if err != nil {
		logger.Error("failed-to-heartbeat-to-database", err)
	}
}
<commit_msg>inline newVolume<commit_after>package worker

import (
	"sync"
	"time"

	"github.com/concourse/baggageclaim"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
)

const volumeKeepalive = 30 * time.Second

//go:generate counterfeiter . VolumeFactoryDB

type VolumeFactoryDB interface {
	GetVolumeTTL(volumeHandle string) (time.Duration, bool, error)
	ReapVolume(handle string) error
	SetVolumeTTL(string, time.Duration) error
}

//go:generate counterfeiter . VolumeFactory

type VolumeFactory interface {
	Build(lager.Logger, baggageclaim.Volume) (Volume, bool, error)
}

type volumeFactory struct {
	db    VolumeFactoryDB
	clock clock.Clock
}

func NewVolumeFactory(db VolumeFactoryDB, clock clock.Clock) VolumeFactory {
	return &volumeFactory{
		db:    db,
		clock: clock,
	}
}

func (vf *volumeFactory) Build(logger lager.Logger, bcVol baggageclaim.Volume) (Volume, bool, error) {
	bcVol.Release(nil)

	logger = logger.WithData(lager.Data{"volume": bcVol.Handle()})

	vol := &volume{
		Volume: bcVol,
		db:     vf.db,

		heartbeating: new(sync.WaitGroup),
		release:      make(chan *time.Duration, 1),
	}

	ttl, found, err := vf.db.GetVolumeTTL(vol.Handle())
	if err != nil {
		logger.Error("failed-to-lookup-expiration-of-volume", err)
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	vol.heartbeat(logger.Session("initial-heartbeat"), ttl)

	vol.heartbeating.Add(1)
	go vol.heartbeatContinuously(
		logger.Session("continuous-heartbeat"),
		vf.clock.NewTicker(volumeKeepalive),
		ttl,
	)

	return vol, true, nil
}

//go:generate counterfeiter . Volume

type Volume interface {
	baggageclaim.Volume

	// a noop method to ensure things aren't just returning baggageclaim.Volume
	HeartbeatingToDB()
}

type volume struct {
	baggageclaim.Volume

	db VolumeFactoryDB

	release      chan *time.Duration
	heartbeating *sync.WaitGroup
	releaseOnce  sync.Once
}

type VolumeMount struct {
	Volume    Volume
	MountPath string
}

func (*volume) HeartbeatingToDB() {}

func (v *volume) Release(finalTTL *time.Duration) {
	v.releaseOnce.Do(func() {
		v.release <- finalTTL
		v.heartbeating.Wait()
	})

	return
}

func (v *volume) heartbeatContinuously(logger lager.Logger, pacemaker clock.Ticker, initialTTL time.Duration) {
	defer v.heartbeating.Done()
	defer pacemaker.Stop()

	logger.Debug("start")
	defer logger.Debug("done")

	ttlToSet := initialTTL

	for {
		select {
		case <-pacemaker.C():
			ttl, found, err := v.db.GetVolumeTTL(v.Handle())
			if err != nil {
				logger.Error("failed-to-lookup-ttl", err)
			} else {
				if !found {
					logger.Info("volume-expired-from-database")
					return
				}

				ttlToSet = ttl
			}

			v.heartbeat(logger.Session("tick"), ttlToSet)

		case finalTTL := <-v.release:
			if finalTTL != nil {
				v.heartbeat(logger.Session("final"), *finalTTL)
			}

			return
		}
	}
}

func (v *volume) heartbeat(logger lager.Logger, ttl time.Duration) {
	logger.Debug("start")
	defer logger.Debug("done")

	err := v.SetTTL(ttl)
	if err != nil {
		if err == baggageclaim.ErrVolumeNotFound {
			v.db.ReapVolume(v.Handle())
		}
		logger.Error("failed-to-heartbeat-to-volume", err)
	}

	err = v.db.SetVolumeTTL(v.Handle(), ttl)
	if err != nil {
		logger.Error("failed-to-heartbeat-to-database", err)
	}
}
<|endoftext|>


<commit_before>package termite

import (
	"log"
	"os"
	"strings"
	"sync"
)

// A in-memory cache of attributes.
//
// Invariants: for all entries, we have their parent directories too
type AttributeCache struct {
	mutex      sync.RWMutex
	attributes map[string]*FileAttr
	cond       *sync.Cond
	busy       map[string]bool
	getter     func(name string) *FileAttr
	statter    func(name string) *os.FileInfo
}

func NewAttributeCache(getter func(n string) *FileAttr,
	statter func(n string) *os.FileInfo) *AttributeCache {
	me := &AttributeCache{
		attributes: make(map[string]*FileAttr),
		busy:       map[string]bool{},
	}
	me.cond = sync.NewCond(&me.mutex)
	me.getter = getter
	me.statter = statter
	return me
}

var paranoia = false

func (me *AttributeCache) verify() {
	if !paranoia {
		return
	}
	me.mutex.RLock()
	defer me.mutex.RUnlock()

	for k, v := range me.attributes {
		if v.Path != k {
			log.Panicf("attributes mismatch %q %#v", k, v)
		}
		if _, ok := me.busy[k]; ok {
			log.Panicf("busy and attributes entry for %q", k)
		}
		if v.Deletion() {
			log.Panicf("Attribute cache may not contain deletions %q", k)
		}
		if v.IsDirectory() && v.NameModeMap == nil {
			log.Panicf("dir has no NameModeMap %q", k)
		}

		dir, base := SplitPath(k)
		if base != k {
			parent := me.attributes[dir]
			if v.Deletion() && parent != nil && parent.NameModeMap[base] != 0 {
				log.Panicf("Parent %q has entry for deleted %q", dir, base)
			}
			if !v.Deletion() && parent == nil {
				log.Panicf("Missing parent for %q", k)
			}
			if !v.Deletion() && parent.NameModeMap[base] == 0 {
				log.Panicf("Parent %q has no entry for %q", dir, base)
			}
		}
	}
}

func (me *AttributeCache) Have(name string) bool {
	me.mutex.RLock()
	defer me.mutex.RUnlock()
	_, ok := me.attributes[name]
	return ok
}

func (me *AttributeCache) Get(name string) (rep *FileAttr) {
	return me.get(name, false)
}

func (me *AttributeCache) GetDir(name string) (rep *FileAttr) {
	return me.get(name, true)
}

func (me *AttributeCache) get(name string, withdir bool) (rep *FileAttr) {
	me.mutex.RLock()
	rep, ok := me.attributes[name]
	dir, base := SplitPath(name)
	var dirAttr *FileAttr
	parentNegate := false
	if !ok && name != "" {
		dirAttr = me.attributes[dir]
		parentNegate = dirAttr != nil && dirAttr.NameModeMap != nil && dirAttr.NameModeMap[base] == 0
	}
	me.mutex.RUnlock()
	if ok {
		return rep.Copy(withdir)
	}
	if parentNegate {
		return &FileAttr{Path: name}
	}

	if dirAttr == nil && name != "" {
		dirAttr = me.get(dir, true)
		parentNegate = dirAttr.NameModeMap != nil && dirAttr.NameModeMap[base] == 0
		if parentNegate {
			return &FileAttr{Path: name}
		}
	}

	defer me.verify()
	me.mutex.Lock()
	defer me.mutex.Unlock()
	for me.busy[name] && me.attributes[name] == nil {
		me.cond.Wait()
	}
	rep, ok = me.attributes[name]
	if ok {
		return rep
	}
	me.busy[name] = true
	me.mutex.Unlock()

	rep = me.getter(name)
	rep.Path = name

	me.mutex.Lock()
	if !rep.Deletion() {
		me.attributes[name] = rep
	}
	me.cond.Broadcast()
	me.busy[name] = false, false
	return rep.Copy(withdir)
}

func (me *AttributeCache) Update(files []*FileAttr) {
	defer me.verify()
	me.mutex.Lock()
	defer me.mutex.Unlock()
	me.update(files)
}

func (me *AttributeCache) update(files []*FileAttr) {
	attributes := me.attributes
	for _, inF := range files {
		r := *inF
		if len(r.Path) > 0 && r.Path[0] == '/' {
			panic("Leading slash.")
		}

		dir, basename := SplitPath(r.Path)
		dirAttr := attributes[dir]
		if dirAttr == nil {
			continue
		}
		if dirAttr.NameModeMap == nil {
			log.Panicf("parent dir has no NameModeMap: %q", dir)
		}
		if r.Deletion() {
			dirAttr.NameModeMap[basename] = 0, false
		} else {
			dirAttr.NameModeMap[basename] = r.Mode &^ 0777
		}

		if r.Deletion() {
			attributes[r.Path] = nil, false
			continue
		}

		old := attributes[r.Path]
		if old == nil {
			old = &r
			attributes[r.Path] = old
		}
		old.Merge(r)
	}
}

func (me *AttributeCache) Refresh(prefix string) FileSet {
	defer me.verify()
	me.mutex.Lock()
	defer me.mutex.Unlock()

	if prefix != "" && prefix[0] == '/' {
		panic("leading /")
	}

	updated := []*FileAttr{}
	for key, attr := range me.attributes {
		// TODO -should just do everything?
		if !strings.HasPrefix(key, prefix) {
			continue
		}

		fi := me.statter(key)
		if fi == nil && !attr.Deletion() {
			del := FileAttr{
				Path: key,
			}
			updated = append(updated, &del)
		}
		// TODO - does this handle symlinks corrrectly?
		if fi != nil && attr.FileInfo != nil && EncodeFileInfo(*attr.FileInfo) != EncodeFileInfo(*fi) {
			newEnt := me.getter(key)
			newEnt.Path = key
			updated = append(updated, newEnt)
		}
	}
	fs := FileSet{updated}
	fs.Sort()

	me.update(fs.Files)
	return fs
}

func (me *AttributeCache) Copy() FileSet {
	me.mutex.RLock()
	defer me.mutex.RUnlock()

	dump := []*FileAttr{}
	for _, attr := range me.attributes {
		dump = append(dump, attr.Copy(true))
	}

	fs := FileSet{dump}
	fs.Sort()
	return fs
}
<commit_msg>AttributeCache: move local lookup with Rlock into separate function.<commit_after>package termite

import (
	"log"
	"os"
	"strings"
	"sync"
)

// A in-memory cache of attributes.
//
// Invariants: for all entries, we have their parent directories too
type AttributeCache struct {
	mutex      sync.RWMutex
	attributes map[string]*FileAttr
	cond       *sync.Cond
	busy       map[string]bool
	getter     func(name string) *FileAttr
	statter    func(name string) *os.FileInfo
}

func NewAttributeCache(getter func(n string) *FileAttr,
	statter func(n string) *os.FileInfo) *AttributeCache {
	me := &AttributeCache{
		attributes: make(map[string]*FileAttr),
		busy:       map[string]bool{},
	}
	me.cond = sync.NewCond(&me.mutex)
	me.getter = getter
	me.statter = statter
	return me
}

var paranoia = false

func (me *AttributeCache) verify() {
	if !paranoia {
		return
	}
	me.mutex.RLock()
	defer me.mutex.RUnlock()

	for k, v := range me.attributes {
		if v.Path != k {
			log.Panicf("attributes mismatch %q %#v", k, v)
		}
		if _, ok := me.busy[k]; ok {
			log.Panicf("busy and attributes entry for %q", k)
		}
		if v.Deletion() {
			log.Panicf("Attribute cache may not contain deletions %q", k)
		}
		if v.IsDirectory() && v.NameModeMap == nil {
			log.Panicf("dir has no NameModeMap %q", k)
		}

		dir, base := SplitPath(k)
		if base != k {
			parent := me.attributes[dir]
			if v.Deletion() && parent != nil && parent.NameModeMap[base] != 0 {
				log.Panicf("Parent %q has entry for deleted %q", dir, base)
			}
			if !v.Deletion() && parent == nil {
				log.Panicf("Missing parent for %q", k)
			}
			if !v.Deletion() && parent.NameModeMap[base] == 0 {
				log.Panicf("Parent %q has no entry for %q", dir, base)
			}
		}
	}
}

func (me *AttributeCache) Have(name string) bool {
	me.mutex.RLock()
	defer me.mutex.RUnlock()
	_, ok := me.attributes[name]
	return ok
}

func (me *AttributeCache) Get(name string) (rep *FileAttr) {
	return me.get(name, false)
}

func (me *AttributeCache) GetDir(name string) (rep *FileAttr) {
	return me.get(name, true)
}

func (me *AttributeCache) localGet(name string, withdir bool) (rep *FileAttr) {
	me.mutex.RLock()
	defer me.mutex.RUnlock()

	rep, ok := me.attributes[name]
	if ok {
		return rep.Copy(withdir)
	}

	if name != "" {
		dir, base := SplitPath(name)
		dirAttr := me.attributes[dir]
		if dirAttr != nil && dirAttr.NameModeMap != nil && dirAttr.NameModeMap[base] == 0 {
			return &FileAttr{Path: name}
		}
	}
	return nil
}

func (me *AttributeCache) get(name string, withdir bool) (rep *FileAttr) {
	rep = me.localGet(name, withdir)
	if rep != nil {
		return rep
	}

	if name != "" {
		dir, base := SplitPath(name)
		dirAttr := me.get(dir, true)
		if dirAttr.NameModeMap != nil && dirAttr.NameModeMap[base] == 0 {
			return &FileAttr{Path: name}
		}
	}

	defer me.verify()
	me.mutex.Lock()
	defer me.mutex.Unlock()
	for me.busy[name] && me.attributes[name] == nil {
		me.cond.Wait()
	}
	rep, ok := me.attributes[name]
	if ok {
		return rep
	}
	me.busy[name] = true
	me.mutex.Unlock()

	rep = me.getter(name)
	rep.Path = name

	me.mutex.Lock()
	if !rep.Deletion() {
		me.attributes[name] = rep
	}
	me.cond.Broadcast()
	me.busy[name] = false, false
	return rep.Copy(withdir)
}

func (me *AttributeCache) Update(files []*FileAttr) {
	defer me.verify()
	me.mutex.Lock()
	defer me.mutex.Unlock()
	me.update(files)
}

func (me *AttributeCache) update(files []*FileAttr) {
	attributes := me.attributes
	for _, inF := range files {
		r := *inF
		if len(r.Path) > 0 && r.Path[0] == '/' {
			panic("Leading slash.")
		}

		dir, basename := SplitPath(r.Path)
		dirAttr := attributes[dir]
		if dirAttr == nil {
			continue
		}
		if dirAttr.NameModeMap == nil {
			log.Panicf("parent dir has no NameModeMap: %q", dir)
		}
		if r.Deletion() {
			dirAttr.NameModeMap[basename] = 0, false
		} else {
			dirAttr.NameModeMap[basename] = r.Mode &^ 0777
		}

		if r.Deletion() {
			attributes[r.Path] = nil, false
			continue
		}

		old := attributes[r.Path]
		if old == nil {
			old = &r
			attributes[r.Path] = old
		}
		old.Merge(r)
	}
}

func (me *AttributeCache) Refresh(prefix string) FileSet {
	defer me.verify()
	me.mutex.Lock()
	defer me.mutex.Unlock()

	if prefix != "" && prefix[0] == '/' {
		panic("leading /")
	}

	updated := []*FileAttr{}
	for key, attr := range me.attributes {
		// TODO -should just do everything?
		if !strings.HasPrefix(key, prefix) {
			continue
		}

		fi := me.statter(key)
		if fi == nil && !attr.Deletion() {
			del := FileAttr{
				Path: key,
			}
			updated = append(updated, &del)
		}
		// TODO - does this handle symlinks corrrectly?
		if fi != nil && attr.FileInfo != nil && EncodeFileInfo(*attr.FileInfo) != EncodeFileInfo(*fi) {
			newEnt := me.getter(key)
			newEnt.Path = key
			updated = append(updated, newEnt)
		}
	}
	fs := FileSet{updated}
	fs.Sort()

	me.update(fs.Files)
	return fs
}

func (me *AttributeCache) Copy() FileSet {
	me.mutex.RLock()
	defer me.mutex.RUnlock()

	dump := []*FileAttr{}
	for _, attr := range me.attributes {
		dump = append(dump, attr.Copy(true))
	}

	fs := FileSet{dump}
	fs.Sort()
	return fs
}
<|endoftext|>


<commit_before>package test

import "testing"

func TestAuth(t *testing.T) {
	// t.Fatal("Unauthorized clients cannot call any function other than method.auth and method.close") // call to randomized and all registered routes here
	// t.Fatal("Anonymous calls to method.auth and method.close should be allowed")
}

func TestGoogleAuth(t *testing.T) {
	// t.Fatal("Google+ first sign-in (registration) failed with valid credentials")
	// t.Fatal("Google+ second sign-in (regular) failed with valid credentials")
	// t.Fatal("Google+ sign-in passed with invalid credentials")
	// t.Fatal("Authentication was not ACKed")
}

func TestValidClientCertAuth(t *testing.T) {
	h := NewClientServerHelper(t, true)
	defer h.Close()

	id := h.Client.WriteRequest("auth.cert", nil)
	m := h.Client.ReadMsg()

	if m.ID != id || m.Result != "OK" {
		t.Fatal("Authentication failed with a valid client certificate. Got server response:", m)
	}

	// t.Fatal("Authenticated with invalid/expired client certificate")
	// t.Fatal("Authentication was not ACKed")
}

func TestTokenAuth(t *testing.T) {
	// t.Fatal("Authentication failed with a valid token")
	// t.Fatal("Authenticated with invalid/expired token")
	// t.Fatal("Authentication was not ACKed")
}
<commit_msg>add invalid cert test<commit_after>package test

import "testing"

func TestAuth(t *testing.T) {
	// t.Fatal("Unauthorized clients cannot call any function other than method.auth and method.close") // call to randomized and all registered routes here
	// t.Fatal("Anonymous calls to method.auth and method.close should be allowed")
}

func TestGoogleAuth(t *testing.T) {
	// t.Fatal("Google+ first sign-in (registration) failed with valid credentials")
	// t.Fatal("Google+ second sign-in (regular) failed with valid credentials")
	// t.Fatal("Google+ sign-in passed with invalid credentials")
	// t.Fatal("Authentication was not ACKed")
}

func TestValidClientCertAuth(t *testing.T) {
	h := NewClientServerHelper(t, true)
	defer h.Close()

	id := h.Client.WriteRequest("auth.cert", nil)
	m := h.Client.ReadMsg()

	if m.ID != id || m.Result != "OK" {
		t.Fatal("Authentication failed with a valid client certificate. Got server response:", m)
	}
}

func TestInvalidClientCertAuth(t *testing.T) {
	h := NewClientServerHelper(t, false)
	defer h.Close()

	id := h.Client.WriteRequest("auth.cert", nil)
	m := h.Client.ReadMsg()

	if m.ID != id || m.Result == "OK" {
		t.Fatal("Authenticated with invalid/expired client certificate. Got server response:", m)
	}
}

func TestTokenAuth(t *testing.T) {
	// t.Fatal("Authentication failed with a valid token")
	// t.Fatal("Authenticated with invalid/expired token")
	// t.Fatal("Authentication was not ACKed")
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.8.6"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<commit_msg>update master version to 0.9.0-dev<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.9.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package terraform

// The main version number that is being run at the moment.
const Version = "0.6.3"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""
<commit_msg>release: cleanup after v0.6.3<commit_after>package terraform

// The main version number that is being run at the moment.
const Version = "0.6.4"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.9.8"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var VersionPrerelease = ""

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<commit_msg>release: clean up after v0.9.8<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.10.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package wsmux

import (
	"bytes"
	"encoding/binary"
	"io/ioutil"
	"net"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

const (
	defaultQueueSize       = 10
	defaultStreamQueueSize = 10
)

type Session struct {
	mu       sync.Mutex
	streams  map[uint32]*stream
	streamCh chan *stream
	writes   chan frame
	conn     *websocket.Conn

	acceptDeadline time.Time
	readDeadline   time.Time
	writeDeadline  time.Time

	logger Logger

	nextID uint32

	closed       chan struct{} // nil channel
	remoteClosed chan struct{} // nil channel

	remoteCloseCallback func()
}

func newSession(conn *websocket.Conn, server bool, conf Config) *Session {
	s := &Session{}
	s.conn = conn
	s.streams = make(map[uint32]*stream)
	s.streamCh = make(chan *stream, defaultStreamQueueSize)
	s.writes = make(chan frame, defaultQueueSize)

	s.closed = make(chan struct{})
	s.remoteClosed = make(chan struct{})

	s.acceptDeadline = conf.AcceptDeadline
	s.readDeadline = conf.ReadDeadline
	s.writeDeadline = conf.WriteDeadline
	s.remoteCloseCallback = conf.RemoteCloseCallback

	if server {
		s.nextID = 0
	} else {
		s.nextID = 1
	}

	if conf.Log == nil {
		s.logger = &nilLogger{}
	} else {
		s.logger = conf.Log
	}
	go s.sendLoop()
	go s.recvLoop()
	return s
}

func (s *Session) Accept() (net.Conn, error) {
	var timeout <-chan time.Time
	var timer *time.Timer
	if !s.acceptDeadline.IsZero() {
		timer = time.NewTimer(s.acceptDeadline.Sub(time.Now()))
		timeout = timer.C
	}

	select {
	case <-s.closed:
		return nil, ErrSessionClosed
	case <-s.remoteClosed:
		return nil, ErrRemoteClosed
	case <-timeout:
		return nil, ErrAcceptTimeout
	case str := <-s.streamCh:
		return str, nil
	}
}

func (s *Session) Open() (net.Conn, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	select {
	case <-s.remoteClosed:
		return nil, ErrRemoteClosed
	case <-s.closed:
		return nil, ErrSessionClosed
	default:
	}

	id := s.nextID
	if _, ok := s.streams[id]; ok {
		return nil, ErrDuplicateStream
	}

	str := newStream(id, s)

	select {
	case s.writes <- newSynFrame(id):
		s.streams[id] = str
	default:
		return nil, ErrBrokenPipe
	}

	s.nextID += 2

	return str, nil
}

func (s *Session) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Check if channel has been closed
	select {
	case <-s.closed:
		return ErrSessionClosed
	default:
	}

	// write cls frame and close write channel
	select {
	case s.writes <- newClsFrame(0):
		close(s.writes)
	default:
		s.logger.Printf("write channel closed")
	}

	close(s.closed)

	return nil
}

func (s *Session) Addr() net.Addr {
	return s.conn.LocalAddr()
}

func (s *Session) sendLoop() {
	for {
		select {
		case <-s.closed:
			return

		case fr, ok := <-s.writes:
			if !ok {
				s.logger.Print("write channel closed")
				return
			}
			err := s.conn.WriteMessage(websocket.BinaryMessage, fr.Write())
			s.logger.Printf("wrote message: %v", fr)
			if err != nil {
				s.logger.Print(err)
			}
		}
	}
}

func (s *Session) recvLoop() {
	for {
		_, msgReader, err := s.conn.NextReader()
		if err != nil {
			_ = s.Close()
			break
		}
		h, err := getHeader(msgReader)
		id, msgType := h.id(), h.msg()
		switch msgType {
		//Used for creating a new stream
		case msgSYN:
			s.mu.Lock()
			if _, ok := s.streams[id]; ok {
				s.logger.Printf("received duplicate syn frame for stream %d", id)
				s.mu.Unlock()
				break
			}
			str := newStream(id, s)
			s.streams[id] = str
			s.asyncPushStream(str)
			s.mu.Unlock()

		//received data
		case msgDAT:
			s.mu.Lock()
			str, ok := s.streams[id]
			s.mu.Unlock()
			if !ok {
				s.logger.Printf("received data frame for unknown stream %d", id)
				break
			}
			b, err := ioutil.ReadAll(msgReader)
			if err != nil {
				s.logger.Print(err)
				break
			}
			str.addToBuffer(b)
			str.notifyRead()
			s.logger.Printf("received DAT frame on stream %d: %v", id, bytes.NewBuffer(b))

		//received ack frame
		case msgACK:
			s.mu.Lock()
			str, ok := s.streams[id]
			s.mu.Unlock()
			if !ok {
				s.logger.Printf("received ack frame for unknown stream %d", id)
				break
			}

			b := make([]byte, 4)
			_, err := msgReader.Read(b)
			if err != nil {
				s.logger.Print(err)
				break
			}
			read := binary.LittleEndian.Uint32(b)
			s.logger.Printf("received ack frame: id %d: remote read %d bytes", id, read)
			str.updateRemoteCapacity(read)
			str.notifyWrite()

		// received fin frame
		case msgFIN:
			s.mu.Lock()
			str, ok := s.streams[id]
			s.mu.Unlock()
			if !ok {
				s.logger.Printf("received fin frame for unknown stream %d", id)
				break
			}

			err := str.setRemoteClosed()
			if str != nil {
				s.logger.Print(err)
			}
			s.logger.Printf("remote stream %d closed connection", id)

		case msgCLS:
			s.logger.Printf("remote session closed")
			s.mu.Lock()
			close(s.remoteClosed)
			s.mu.Unlock()
			if s.remoteCloseCallback != nil {
				s.remoteCloseCallback()
			}
		}

	}
}

func (s *Session) removeStream(id uint32) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.streams[id]; ok {
		delete(s.streams, id)
	}
}

func (s *Session) asyncPushStream(str *stream) {
	select {
	case s.streamCh <- str:
	default:
	}
}
<commit_msg>remoteClosed not meaningful in session<commit_after>package wsmux

import (
	"bytes"
	"encoding/binary"
	"io/ioutil"
	"net"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

const (
	defaultQueueSize       = 10
	defaultStreamQueueSize = 10
)

type Session struct {
	mu       sync.Mutex
	streams  map[uint32]*stream
	streamCh chan *stream
	writes   chan frame
	conn     *websocket.Conn

	acceptDeadline time.Time
	readDeadline   time.Time
	writeDeadline  time.Time

	logger Logger

	nextID uint32

	closed chan struct{} // nil channel

	closeConn bool

	remoteCloseCallback func()
}

func newSession(conn *websocket.Conn, server bool, conf Config) *Session {
	s := &Session{}
	s.conn = conn
	s.streams = make(map[uint32]*stream)
	s.streamCh = make(chan *stream, defaultStreamQueueSize)
	s.writes = make(chan frame, defaultQueueSize)

	s.closed = make(chan struct{})
	s.closeConn = true

	s.acceptDeadline = conf.AcceptDeadline
	s.readDeadline = conf.ReadDeadline
	s.writeDeadline = conf.WriteDeadline
	s.remoteCloseCallback = conf.RemoteCloseCallback

	if server {
		s.nextID = 0
	} else {
		s.nextID = 1
	}

	if conf.Log == nil {
		s.logger = &nilLogger{}
	} else {
		s.logger = conf.Log
	}

	s.conn.SetCloseHandler(s.closeHandler)

	go s.sendLoop()
	go s.recvLoop()
	return s
}

func (s *Session) Accept() (net.Conn, error) {
	var timeout <-chan time.Time
	var timer *time.Timer
	if !s.acceptDeadline.IsZero() {
		timer = time.NewTimer(s.acceptDeadline.Sub(time.Now()))
		timeout = timer.C
	}

	select {
	case <-s.closed:
		return nil, ErrSessionClosed
	case <-timeout:
		return nil, ErrAcceptTimeout
	case str := <-s.streamCh:
		return str, nil
	}
}

func (s *Session) Open() (net.Conn, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	select {
	case <-s.closed:
		return nil, ErrSessionClosed
	default:
	}

	id := s.nextID
	if _, ok := s.streams[id]; ok {
		return nil, ErrDuplicateStream
	}

	str := newStream(id, s)

	select {
	case s.writes <- newSynFrame(id):
		s.streams[id] = str
	default:
		return nil, ErrBrokenPipe
	}

	s.nextID += 2

	return str, nil
}

func (s *Session) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Check if channel has been closed
	select {
	case <-s.closed:
		return ErrSessionClosed
	default:
	}

	// write cls frame and close write channel
	select {
	case s.writes <- newClsFrame(0):
		close(s.writes)
	default:
		s.logger.Printf("write channel closed")
	}

	close(s.closed)
	if s.closeConn {
		_ = s.conn.Close()
	}

	for _, v := range s.streams {
		_ = v.Close()
	}

	return nil
}

func (s *Session) Addr() net.Addr {
	return s.conn.LocalAddr()
}

func (s *Session) closeHandler(code int, text string) error {
	s.logger.Printf("ws conn closed: code %d : %s", code, text)
	s.mu.Lock()
	defer s.mu.Unlock()
	s.closeConn = false
	return s.Close()
}

func (s *Session) sendLoop() {
	for {
		select {
		case <-s.closed:
			return

		case fr, ok := <-s.writes:
			if !ok {
				s.logger.Print("write channel closed")
				return
			}
			err := s.conn.WriteMessage(websocket.BinaryMessage, fr.Write())
			s.logger.Printf("wrote message: %v", fr)
			if err != nil {
				s.logger.Print(err)
			}
		}
	}
}

func (s *Session) recvLoop() {
	for {
		select {
		case <-s.closed:
			return
		default:
		}
		_, msgReader, err := s.conn.NextReader()
		if err != nil {
			_ = s.Close()
			break
		}
		h, err := getHeader(msgReader)
		id, msgType := h.id(), h.msg()
		switch msgType {
		//Used for creating a new stream
		case msgSYN:
			s.mu.Lock()
			if _, ok := s.streams[id]; ok {
				s.logger.Printf("received duplicate syn frame for stream %d", id)
				s.mu.Unlock()
				break
			}
			str := newStream(id, s)
			s.streams[id] = str
			s.asyncPushStream(str)
			s.mu.Unlock()

		//received data
		case msgDAT:
			s.mu.Lock()
			str, ok := s.streams[id]
			s.mu.Unlock()
			if !ok {
				s.logger.Printf("received data frame for unknown stream %d", id)
				break
			}
			b, err := ioutil.ReadAll(msgReader)
			if err != nil {
				s.logger.Print(err)
				break
			}
			str.addToBuffer(b)
			str.notifyRead()
			s.logger.Printf("received DAT frame on stream %d: %v", id, bytes.NewBuffer(b))

		//received ack frame
		case msgACK:
			s.mu.Lock()
			str, ok := s.streams[id]
			s.mu.Unlock()
			if !ok {
				s.logger.Printf("received ack frame for unknown stream %d", id)
				break
			}

			b := make([]byte, 4)
			_, err := msgReader.Read(b)
			if err != nil {
				s.logger.Print(err)
				break
			}
			read := binary.LittleEndian.Uint32(b)
			s.logger.Printf("received ack frame: id %d: remote read %d bytes", id, read)
			str.updateRemoteCapacity(read)
			str.notifyWrite()

		// received fin frame
		case msgFIN:
			s.mu.Lock()
			str, ok := s.streams[id]
			s.mu.Unlock()
			if !ok {
				s.logger.Printf("received fin frame for unknown stream %d", id)
				break
			}

			err := str.setRemoteClosed()
			if str != nil {
				s.logger.Print(err)
			}
			s.logger.Printf("remote stream %d closed connection", id)
		}

	}
}

func (s *Session) removeStream(id uint32) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.streams[id]; ok {
		delete(s.streams, id)
	}
}

func (s *Session) asyncPushStream(str *stream) {
	select {
	case s.streamCh <- str:
	default:
	}
}
<|endoftext|>


<commit_before>package wsutil

import (
	"bytes"
	"io"
	"io/ioutil"

	"github.com/gobwas/ws"
)

// Message represents a message from peer, that could be presented in one or
// more frames. That is, it contains payload of all message fragments and
// operation code of initial frame for this message.
type Message struct {
	OpCode  ws.OpCode
	Payload []byte
}

// ReadMessage is a helper function that reads next message from r. It appends
// received message(s) to the third argument and returns the result of it and
// an error if some failure happened. That is, it probably could receive more
// than one message when peer sending fragmented message in multiple frames and
// want to send some control frame between fragments. Then returned slice will
// contain those control frames at first, and then result of gluing fragments.
func ReadMessage(r io.Reader, s ws.State, m []Message) ([]Message, error) {
	rd := Reader{
		Source:    r,
		State:     s,
		CheckUTF8: true,
		OnIntermediate: func(hdr ws.Header, src io.Reader) error {
			bts, err := ioutil.ReadAll(src)
			if err != nil {
				return err
			}
			m = append(m, Message{hdr.OpCode, bts})
			return nil
		},
	}
	bts, err := ioutil.ReadAll(&rd)
	if err != nil {
		return m, err
	}

	return append(m, Message{rd.Header().OpCode, bts}), nil
}

// ReadClientMessage reads next message from r, considering that caller
// represents server side.
// It is a shortcut for ReadMessage(r, ws.StateServerSide, m)
func ReadClientMessage(r io.Reader, m []Message) ([]Message, error) {
	return ReadMessage(r, ws.StateServerSide, m)
}

// ReadServerMessage reads next message from r, considering that caller
// represents client side.
// It is a shortcut for ReadMessage(r, ws.StateClientSide, m)
func ReadServerMessage(r io.Reader, m []Message) ([]Message, error) {
	return ReadMessage(r, ws.StateClientSide, m)
}

// ReadData is a helper function that reads next data (non-control) message
// from rw.
// It takes care on handling all control frames. It will write response on
// control frames to the write part of rw. It blocks until some data frame
// will be received.
func ReadData(rw io.ReadWriter, s ws.State) ([]byte, ws.OpCode, error) {
	return readData(rw, s, ws.OpText|ws.OpBinary)
}

// ReadClientData reads next data message from rw, considering that caller
// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
func ReadClientData(rw io.ReadWriter) ([]byte, ws.OpCode, error) {
	return ReadData(rw, ws.StateServerSide)
}

// ReadClientText reads next text message from rw, considering that caller
// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
// It discards received binary messages.
func ReadClientText(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateServerSide, ws.OpText)
	return p, err
}

// ReadClientBinary reads next binary message from rw, considering that caller
// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
// It discards received text messages.
func ReadClientBinary(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateServerSide, ws.OpBinary)
	return p, err
}

// ReadServerData reads next data message from rw, considering that caller
// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
func ReadServerData(rw io.ReadWriter) ([]byte, ws.OpCode, error) {
	return ReadData(rw, ws.StateClientSide)
}

// ReadServerText reads next text message from rw, considering that caller
// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
// It discards received binary messages.
func ReadServerText(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateClientSide, ws.OpText)
	return p, err
}

// ReadServerBinary reads next binary message from rw, considering that caller
// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
// It discards received text messages.
func ReadServerBinary(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateClientSide, ws.OpBinary)
	return p, err
}

// WriteMessage is a helper function that writes message to the w. It
// constructs single frame with given operation code and payload.
// It uses given state to prepare side-dependtend things, like cipher
// payload bytes from client to server. It will not mutate p bytes if
// cipher must be made.
//
// If you want to write message in fragmented frames, use Writer instead.
func WriteMessage(w io.Writer, s ws.State, op ws.OpCode, p []byte) error {
	return writeFrame(w, s, op, true, p)
}

// WriteServerMessage writes message to w, considering that caller
// represents server side.
func WriteServerMessage(w io.Writer, op ws.OpCode, p []byte) error {
	return WriteMessage(w, ws.StateServerSide, op, p)
}

// WriteServerText is the same as WriteServerMessage with
// ws.OpText.
func WriteServerText(w io.Writer, p []byte) error {
	return WriteServerMessage(w, ws.OpText, p)
}

// WriteServerBinary is the same as WriteServerMessage with
// ws.OpBinary.
func WriteServerBinary(w io.Writer, p []byte) error {
	return WriteServerMessage(w, ws.OpBinary, p)
}

// WriteClientMessage writes message to w, considering that caller
// represents client side.
func WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error {
	return WriteMessage(w, ws.StateClientSide, op, p)
}

// WriteClientText is the same as WriteClientMessage with
// ws.OpText.
func WriteClientText(w io.Writer, p []byte) error {
	return WriteClientMessage(w, ws.OpText, p)
}

// WriteClientBinary is the same as WriteClientMessage with
// ws.OpBinary.
func WriteClientBinary(w io.Writer, p []byte) error {
	return WriteClientMessage(w, ws.OpBinary, p)
}

// HandleClientControl handles control frame from conn and writes
// response when needed. It considers that caller represents server
// side.
func HandleClientControl(conn io.ReadWriter, op ws.OpCode, p []byte) error {
	return ControlHandler(conn, ws.StateServerSide)(ws.Header{
		Length: int64(len(p)),
		OpCode: op,
	}, bytes.NewReader(p))
}

// HandleServerControl handles control frame from conn and writes
// response when needed. It considers that caller represents client
// side.
func HandleServerControl(conn io.ReadWriter, op ws.OpCode, p []byte) error {
	return ControlHandler(conn, ws.StateClientSide)(ws.Header{
		Length: int64(len(p)),
		OpCode: op,
	}, bytes.NewReader(p))
}

func readData(rw io.ReadWriter, s ws.State, want ws.OpCode) ([]byte, ws.OpCode, error) {
	ch := ControlHandler(rw, s)

	rd := Reader{
		Source:         rw,
		State:          s,
		CheckUTF8:      true,
		OnIntermediate: ch,
	}

	for {
		hdr, err := rd.NextFrame()
		if err != nil {
			return nil, 0, err
		}
		if hdr.OpCode.IsControl() {
			if err := ch(hdr, &rd); err != nil {
				return nil, 0, err
			}
			continue
		}
		if hdr.OpCode&want == 0 {
			if err := rd.Discard(); err != nil {
				return nil, 0, err
			}
			continue
		}

		bts, err := ioutil.ReadAll(&rd)

		return bts, hdr.OpCode, err
	}
}
<commit_msg>wsutil: reduce interface for HandleClientControl() to io.Writer (#28)<commit_after>package wsutil

import (
	"bytes"
	"io"
	"io/ioutil"

	"github.com/gobwas/ws"
)

// Message represents a message from peer, that could be presented in one or
// more frames. That is, it contains payload of all message fragments and
// operation code of initial frame for this message.
type Message struct {
	OpCode  ws.OpCode
	Payload []byte
}

// ReadMessage is a helper function that reads next message from r. It appends
// received message(s) to the third argument and returns the result of it and
// an error if some failure happened. That is, it probably could receive more
// than one message when peer sending fragmented message in multiple frames and
// want to send some control frame between fragments. Then returned slice will
// contain those control frames at first, and then result of gluing fragments.
func ReadMessage(r io.Reader, s ws.State, m []Message) ([]Message, error) {
	rd := Reader{
		Source:    r,
		State:     s,
		CheckUTF8: true,
		OnIntermediate: func(hdr ws.Header, src io.Reader) error {
			bts, err := ioutil.ReadAll(src)
			if err != nil {
				return err
			}
			m = append(m, Message{hdr.OpCode, bts})
			return nil
		},
	}
	bts, err := ioutil.ReadAll(&rd)
	if err != nil {
		return m, err
	}

	return append(m, Message{rd.Header().OpCode, bts}), nil
}

// ReadClientMessage reads next message from r, considering that caller
// represents server side.
// It is a shortcut for ReadMessage(r, ws.StateServerSide, m)
func ReadClientMessage(r io.Reader, m []Message) ([]Message, error) {
	return ReadMessage(r, ws.StateServerSide, m)
}

// ReadServerMessage reads next message from r, considering that caller
// represents client side.
// It is a shortcut for ReadMessage(r, ws.StateClientSide, m)
func ReadServerMessage(r io.Reader, m []Message) ([]Message, error) {
	return ReadMessage(r, ws.StateClientSide, m)
}

// ReadData is a helper function that reads next data (non-control) message
// from rw.
// It takes care on handling all control frames. It will write response on
// control frames to the write part of rw. It blocks until some data frame
// will be received.
func ReadData(rw io.ReadWriter, s ws.State) ([]byte, ws.OpCode, error) {
	return readData(rw, s, ws.OpText|ws.OpBinary)
}

// ReadClientData reads next data message from rw, considering that caller
// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
func ReadClientData(rw io.ReadWriter) ([]byte, ws.OpCode, error) {
	return ReadData(rw, ws.StateServerSide)
}

// ReadClientText reads next text message from rw, considering that caller
// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
// It discards received binary messages.
func ReadClientText(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateServerSide, ws.OpText)
	return p, err
}

// ReadClientBinary reads next binary message from rw, considering that caller
// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
// It discards received text messages.
func ReadClientBinary(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateServerSide, ws.OpBinary)
	return p, err
}

// ReadServerData reads next data message from rw, considering that caller
// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
func ReadServerData(rw io.ReadWriter) ([]byte, ws.OpCode, error) {
	return ReadData(rw, ws.StateClientSide)
}

// ReadServerText reads next text message from rw, considering that caller
// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
// It discards received binary messages.
func ReadServerText(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateClientSide, ws.OpText)
	return p, err
}

// ReadServerBinary reads next binary message from rw, considering that caller
// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
// It discards received text messages.
func ReadServerBinary(rw io.ReadWriter) ([]byte, error) {
	p, _, err := readData(rw, ws.StateClientSide, ws.OpBinary)
	return p, err
}

// WriteMessage is a helper function that writes message to the w. It
// constructs single frame with given operation code and payload.
// It uses given state to prepare side-dependtend things, like cipher
// payload bytes from client to server. It will not mutate p bytes if
// cipher must be made.
//
// If you want to write message in fragmented frames, use Writer instead.
func WriteMessage(w io.Writer, s ws.State, op ws.OpCode, p []byte) error {
	return writeFrame(w, s, op, true, p)
}

// WriteServerMessage writes message to w, considering that caller
// represents server side.
func WriteServerMessage(w io.Writer, op ws.OpCode, p []byte) error {
	return WriteMessage(w, ws.StateServerSide, op, p)
}

// WriteServerText is the same as WriteServerMessage with
// ws.OpText.
func WriteServerText(w io.Writer, p []byte) error {
	return WriteServerMessage(w, ws.OpText, p)
}

// WriteServerBinary is the same as WriteServerMessage with
// ws.OpBinary.
func WriteServerBinary(w io.Writer, p []byte) error {
	return WriteServerMessage(w, ws.OpBinary, p)
}

// WriteClientMessage writes message to w, considering that caller
// represents client side.
func WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error {
	return WriteMessage(w, ws.StateClientSide, op, p)
}

// WriteClientText is the same as WriteClientMessage with
// ws.OpText.
func WriteClientText(w io.Writer, p []byte) error {
	return WriteClientMessage(w, ws.OpText, p)
}

// WriteClientBinary is the same as WriteClientMessage with
// ws.OpBinary.
func WriteClientBinary(w io.Writer, p []byte) error {
	return WriteClientMessage(w, ws.OpBinary, p)
}

// HandleClientControl handles control frame from conn and writes
// response when needed. It considers that caller represents server
// side.
func HandleClientControl(conn io.Writer, op ws.OpCode, p []byte) error {
	return ControlHandler(conn, ws.StateServerSide)(ws.Header{
		Length: int64(len(p)),
		OpCode: op,
	}, bytes.NewReader(p))
}

// HandleServerControl handles control frame from conn and writes
// response when needed. It considers that caller represents client
// side.
func HandleServerControl(conn io.ReadWriter, op ws.OpCode, p []byte) error {
	return ControlHandler(conn, ws.StateClientSide)(ws.Header{
		Length: int64(len(p)),
		OpCode: op,
	}, bytes.NewReader(p))
}

func readData(rw io.ReadWriter, s ws.State, want ws.OpCode) ([]byte, ws.OpCode, error) {
	ch := ControlHandler(rw, s)

	rd := Reader{
		Source:         rw,
		State:          s,
		CheckUTF8:      true,
		OnIntermediate: ch,
	}

	for {
		hdr, err := rd.NextFrame()
		if err != nil {
			return nil, 0, err
		}
		if hdr.OpCode.IsControl() {
			if err := ch(hdr, &rd); err != nil {
				return nil, 0, err
			}
			continue
		}
		if hdr.OpCode&want == 0 {
			if err := rd.Discard(); err != nil {
				return nil, 0, err
			}
			continue
		}

		bts, err := ioutil.ReadAll(&rd)

		return bts, hdr.OpCode, err
	}
}
<|endoftext|>