<commit_before>package prometheus

import (
	"errors"
	"strconv"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/crosbymichael/cgroups"
	metrics "github.com/docker/go-metrics"
	"github.com/prometheus/client_golang/prometheus"
)

var ErrAlreadyCollected = errors.New("cgroup is already being collected")

// New registers the Collector with the provided namespace and returns it so
// that cgroups can be added for collection
func New(ns *metrics.Namespace) *Collector {
	// add machine cpus and memory info
	c := &Collector{
		ns:      ns,
		cgroups: make(map[string]cgroups.Cgroup),
	}
	c.metrics = append(c.metrics, pidMetrics...)
	c.metrics = append(c.metrics, cpuMetrics...)
	c.metrics = append(c.metrics, memoryMetrics...)
	c.metrics = append(c.metrics, hugetlbMetrics...)
	c.metrics = append(c.metrics, blkioMetrics...)
	ns.Add(c)
	return c
}

// Collector provides the ability to collect container stats and export
// them in the prometheus format
type Collector struct {
	mu sync.RWMutex

	cgroups map[string]cgroups.Cgroup
	ns      *metrics.Namespace
	metrics []*metric
}

func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
	for _, m := range c.metrics {
		ch <- m.desc(c.ns)
	}
}

func (c *Collector) Collect(ch chan<- prometheus.Metric) {
	c.mu.RLock()
	wg := &sync.WaitGroup{}
	for id, cg := range c.cgroups {
		wg.Add(1)
		go c.collect(id, cg, ch, wg)
	}
	c.mu.RUnlock()
	wg.Wait()
}

func (c *Collector) collect(id string, cg cgroups.Cgroup, ch chan<- prometheus.Metric, wg *sync.WaitGroup) {
	defer wg.Done()
	stats, err := cg.Stat()
	if err != nil {
		logrus.WithError(err).Errorf("stat cgroup %s", id)
		return
	}
	for _, m := range c.metrics {
		m.collect(id, stats, c.ns, ch)
	}
}

// Add adds the provided cgroup and id so that metrics are collected and exported
func (c *Collector) Add(id string, cg cgroups.Cgroup) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if _, ok := c.cgroups[id]; ok {
		return ErrAlreadyCollected
	}
	c.cgroups[id] = cg
	return nil
}

func blkioValues(l []cgroups.BlkioEntry) []value {
	var out []value
	for _, e := range l {
		out = append(out, value{
			v: float64(e.Value),
			l: []string{e.Op, strconv.FormatUint(e.Major, 10), strconv.FormatUint(e.Minor, 10)},
		})
	}
	return out
}
<commit_msg>Add Remove method from the collector<commit_after>package prometheus

import (
	"errors"
	"strconv"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/crosbymichael/cgroups"
	metrics "github.com/docker/go-metrics"
	"github.com/prometheus/client_golang/prometheus"
)

var ErrAlreadyCollected = errors.New("cgroup is already being collected")

// New registers the Collector with the provided namespace and returns it so
// that cgroups can be added for collection
func New(ns *metrics.Namespace) *Collector {
	// add machine cpus and memory info
	c := &Collector{
		ns:      ns,
		cgroups: make(map[string]cgroups.Cgroup),
	}
	c.metrics = append(c.metrics, pidMetrics...)
	c.metrics = append(c.metrics, cpuMetrics...)
	c.metrics = append(c.metrics, memoryMetrics...)
	c.metrics = append(c.metrics, hugetlbMetrics...)
	c.metrics = append(c.metrics, blkioMetrics...)
	ns.Add(c)
	return c
}

// Collector provides the ability to collect container stats and export
// them in the prometheus format
type Collector struct {
	mu sync.RWMutex

	cgroups map[string]cgroups.Cgroup
	ns      *metrics.Namespace
	metrics []*metric
}

func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
	for _, m := range c.metrics {
		ch <- m.desc(c.ns)
	}
}

func (c *Collector) Collect(ch chan<- prometheus.Metric) {
	c.mu.RLock()
	wg := &sync.WaitGroup{}
	for id, cg := range c.cgroups {
		wg.Add(1)
		go c.collect(id, cg, ch, wg)
	}
	c.mu.RUnlock()
	wg.Wait()
}

func (c *Collector) collect(id string, cg cgroups.Cgroup, ch chan<- prometheus.Metric, wg *sync.WaitGroup) {
	defer wg.Done()
	stats, err := cg.Stat()
	if err != nil {
		logrus.WithError(err).Errorf("stat cgroup %s", id)
		return
	}
	for _, m := range c.metrics {
		m.collect(id, stats, c.ns, ch)
	}
}

// Add adds the provided cgroup and id so that metrics are collected and exported
func (c *Collector) Add(id string, cg cgroups.Cgroup) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if _, ok := c.cgroups[id]; ok {
		return ErrAlreadyCollected
	}
	c.cgroups[id] = cg
	return nil
}

// Remove removes the provided cgroup by id from the collector
func (c *Collector) Remove(id string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	delete(c.cgroups, id)
}

func blkioValues(l []cgroups.BlkioEntry) []value {
	var out []value
	for _, e := range l {
		out = append(out, value{
			v: float64(e.Value),
			l: []string{e.Op, strconv.FormatUint(e.Major, 10), strconv.FormatUint(e.Minor, 10)},
		})
	}
	return out
}
<|endoftext|>


<commit_before>package sub

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/triggers"
	"github.com/Symantec/Dominator/proto/common"
	"github.com/Symantec/Dominator/sub/scanner"
)

type Configuration struct {
	ScanSpeedPercent    uint
	NetworkSpeedPercent uint
	ScanExclusionList   []string
}

type FetchRequest struct {
	ServerAddress string
	Hashes        []hash.Hash
}

type FetchResponse common.StatusResponse

type GetConfigurationRequest struct {
}

type GetConfigurationResponse Configuration

type PollRequest struct {
	HaveGeneration uint64
}

type PollResponse struct {
	NetworkSpeed     uint64
	FetchInProgress  bool // Fetch() and Update() are mutually exclusive.
	UpdateInProgress bool
	GenerationCount  uint64
	FileSystem       *scanner.FileSystem
}

type SetConfigurationRequest Configuration

type SetConfigurationResponse common.StatusResponse

type Directory struct {
	Name string
	Mode filesystem.FileMode
	Uid  uint32
	Gid  uint32
}

type Hardlink struct {
	Source string
	Target string
}

type UpdateRequest struct {
	PathsToDelete       []string
	DirectoriesToMake   []Directory
	DirectoriesToChange []Directory
	HardlinksToMake     []Hardlink
	Triggers            *triggers.Triggers
}

type UpdateResponse struct{}
<commit_msg>Add InodesToChange to UpdateRequest RPC message.<commit_after>package sub

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/triggers"
	"github.com/Symantec/Dominator/proto/common"
	"github.com/Symantec/Dominator/sub/scanner"
)

type Configuration struct {
	ScanSpeedPercent    uint
	NetworkSpeedPercent uint
	ScanExclusionList   []string
}

type FetchRequest struct {
	ServerAddress string
	Hashes        []hash.Hash
}

type FetchResponse common.StatusResponse

type GetConfigurationRequest struct {
}

type GetConfigurationResponse Configuration

type PollRequest struct {
	HaveGeneration uint64
}

type PollResponse struct {
	NetworkSpeed     uint64
	FetchInProgress  bool // Fetch() and Update() are mutually exclusive.
	UpdateInProgress bool
	GenerationCount  uint64
	FileSystem       *scanner.FileSystem
}

type SetConfigurationRequest Configuration

type SetConfigurationResponse common.StatusResponse

type Directory struct {
	Name string
	Mode filesystem.FileMode
	Uid  uint32
	Gid  uint32
}

type Hardlink struct {
	Source string
	Target string
}

type Inode struct {
	Name string
	filesystem.GenericInode
}

type UpdateRequest struct {
	PathsToDelete       []string
	DirectoriesToMake   []Directory
	DirectoriesToChange []Directory
	InodesToChange      []Inode
	HardlinksToMake     []Hardlink
	Triggers            *triggers.Triggers
}

type UpdateResponse struct{}
<|endoftext|>


<commit_before>package protocol

import (
	"net/http"
	"net/url"
	"testing"

	"github.com/mefellows/muxy/muxy"
)

func TestMatchRule_Hit(t *testing.T) {
	proxy := HTTPProxy{}
	defaultProxyRule := proxy.defaultProxyRule()
	subPathProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path: "/foo/",
		},
		Pass: ProxyPass{},
	}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{
			Host: "foo\\.com",
		},
		Pass: ProxyPass{},
	}
	methodProxyRule := ProxyRule{
		Request: ProxyRequest{
			Method: "(?i)get",
		},
		Pass: ProxyPass{},
	}
	allProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path:   "/foo/bar",
			Host:   ".*foo.*",
			Method: "(?i)get",
		},
		Pass: ProxyPass{},
	}

	defaultRequest := http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Scheme: "https",
		},
		Host:   "foo.com",
		Method: "GET",
	}

	testCases := map[ProxyRule]http.Request{
		defaultProxyRule: defaultRequest,
		subPathProxyRule: defaultRequest,
		hostProxyRule:    defaultRequest,
		methodProxyRule:  defaultRequest,
		allProxyRule:     defaultRequest,
	}

	for rule, req := range testCases {
		if MatchRule(rule, req) != true {
			t.Fatal("Expected ProxyRule", rule, "to match request", req, "but did not")
		}
	}
}

func TestMatchRule_Miss(t *testing.T) {
	subPathProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path: "^/bar",
		},
		Pass: ProxyPass{},
	}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{
			Host: "bar\\.com",
		},
		Pass: ProxyPass{},
	}
	methodProxyRule := ProxyRule{
		Request: ProxyRequest{
			Method: "(?i)post",
		},
		Pass: ProxyPass{},
	}
	allProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path:   "^/baz",
			Host:   ".*bar.*",
			Method: "(?i)post",
		},
		Pass: ProxyPass{},
	}

	defaultRequest := http.Request{
		Host: "foo.com",
		URL: &url.URL{
			Path:   "/foo/bar",
			Scheme: "https",
		},
		Method: "GET",
	}

	testCases := map[ProxyRule]http.Request{
		subPathProxyRule: defaultRequest,
		hostProxyRule:    defaultRequest,
		methodProxyRule:  defaultRequest,
		allProxyRule:     defaultRequest,
	}

	for rule, req := range testCases {
		if MatchRule(rule, req) != false {
			t.Fatal("Expected ProxyRule", rule, "to not match request", req, "but did")
		}
	}
}

func TestApplyProxyPassRule_Path(t *testing.T) {
	proxy := HTTPProxy{}
	subPathProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Path: "/newstart",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(subPathProxyRule, defaultRequest)

	if defaultRequest.URL.Path != "/newstart/foo/bar" {
		t.Fatal("Expected URL to be translated to /newstart/foo/bar but got", defaultRequest.URL.Path)
	}
	rootProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass:    ProxyPass{},
	}
	defaultRequest = &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(rootProxyRule, defaultRequest)

	if defaultRequest.URL.Path != "/foo/bar" {
		t.Fatal("Expected URL to be unmodified at /foo/bar but got", defaultRequest.URL.Path)
	}
}

func TestApplyProxyPassRule_Method(t *testing.T) {
	proxy := HTTPProxy{}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Method: "POST",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(hostProxyRule, defaultRequest)

	if defaultRequest.Method != "POST" {
		t.Fatal("Expected request method to be POST but got", defaultRequest.Method)
	}
}

func TestApplyProxyPassRule_Scheme(t *testing.T) {
	proxy := HTTPProxy{}
	schemeProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Scheme: "http",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(schemeProxyRule, defaultRequest)

	if defaultRequest.URL.Scheme != "http" {
		t.Fatal("Expected URL scheme to be http but got", defaultRequest.URL.Scheme)
	}
}

func TestApplyProxyPassRule_Host(t *testing.T) {
	proxy := HTTPProxy{}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Host: "bar.com",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(hostProxyRule, defaultRequest)

	if defaultRequest.URL.Host != "bar.com" {
		t.Fatal("Expected URL Host to be bar.com but got", defaultRequest.URL.Host)
	}
}

func TestSetup(t *testing.T) {
	proxy := HTTPProxy{}
	proxy.Setup([]muxy.Middleware{})

	if len(proxy.ProxyRules) != 1 {
		t.Fatal("Expected default ProxyRules to be present")
	}

	proxy = HTTPProxy{
		ProxyRules: []ProxyRule{
			ProxyRule{},
		},
	}
	proxy.Setup([]muxy.Middleware{})

	if len(proxy.ProxyRules) != 2 {
		t.Fatal("Expected default ProxyRules to be present")
	}
}

func TestDefaultProxyRule(t *testing.T) {
	proxy := HTTPProxy{
		ProxyHost: "foo.com",
		ProxyPort: 1234,
	}
	rule := proxy.defaultProxyRule()

	expected := "foo.com:1234"
	if rule.Pass.Host != expected {
		t.Fatal("Expected host to be", expected, "but got", rule.Pass.Host)
	}
}
<commit_msg>chore(test): improve test coverage for HTTPProxy<commit_after>package protocol

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"testing"

	"github.com/mefellows/muxy/muxy"
)

var proxiedServerBody = "proxied server up!"

func TestHTTPProxy_MatchRule_Hit(t *testing.T) {
	proxy := HTTPProxy{}
	defaultProxyRule := proxy.defaultProxyRule()
	subPathProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path: "/foo/",
		},
		Pass: ProxyPass{},
	}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{
			Host: "foo\\.com",
		},
		Pass: ProxyPass{},
	}
	methodProxyRule := ProxyRule{
		Request: ProxyRequest{
			Method: "(?i)get",
		},
		Pass: ProxyPass{},
	}
	allProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path:   "/foo/bar",
			Host:   ".*foo.*",
			Method: "(?i)get",
		},
		Pass: ProxyPass{},
	}

	defaultRequest := http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Scheme: "https",
		},
		Host:   "foo.com",
		Method: "GET",
	}

	testCases := map[ProxyRule]http.Request{
		defaultProxyRule: defaultRequest,
		subPathProxyRule: defaultRequest,
		hostProxyRule:    defaultRequest,
		methodProxyRule:  defaultRequest,
		allProxyRule:     defaultRequest,
	}

	for rule, req := range testCases {
		if MatchRule(rule, req) != true {
			t.Fatal("Expected ProxyRule", rule, "to match request", req, "but did not")
		}
	}
}

func TestHTTPProxy_MatchRule_Miss(t *testing.T) {
	subPathProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path: "^/bar",
		},
		Pass: ProxyPass{},
	}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{
			Host: "bar\\.com",
		},
		Pass: ProxyPass{},
	}
	methodProxyRule := ProxyRule{
		Request: ProxyRequest{
			Method: "(?i)post",
		},
		Pass: ProxyPass{},
	}
	allProxyRule := ProxyRule{
		Request: ProxyRequest{
			Path:   "^/baz",
			Host:   ".*bar.*",
			Method: "(?i)post",
		},
		Pass: ProxyPass{},
	}

	defaultRequest := http.Request{
		Host: "foo.com",
		URL: &url.URL{
			Path:   "/foo/bar",
			Scheme: "https",
		},
		Method: "GET",
	}

	testCases := map[ProxyRule]http.Request{
		subPathProxyRule: defaultRequest,
		hostProxyRule:    defaultRequest,
		methodProxyRule:  defaultRequest,
		allProxyRule:     defaultRequest,
	}

	for rule, req := range testCases {
		if MatchRule(rule, req) != false {
			t.Fatal("Expected ProxyRule", rule, "to not match request", req, "but did")
		}
	}
}

func TestHTTPProxy_ApplyProxyPassRule_Path(t *testing.T) {
	proxy := HTTPProxy{}
	subPathProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Path: "/newstart",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(subPathProxyRule, defaultRequest)

	if defaultRequest.URL.Path != "/newstart/foo/bar" {
		t.Fatal("Expected URL to be translated to /newstart/foo/bar but got", defaultRequest.URL.Path)
	}
	rootProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass:    ProxyPass{},
	}
	defaultRequest = &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(rootProxyRule, defaultRequest)

	if defaultRequest.URL.Path != "/foo/bar" {
		t.Fatal("Expected URL to be unmodified at /foo/bar but got", defaultRequest.URL.Path)
	}
}

func TestHTTPProxy_ApplyProxyPassRule_Method(t *testing.T) {
	proxy := HTTPProxy{}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Method: "POST",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(hostProxyRule, defaultRequest)

	if defaultRequest.Method != "POST" {
		t.Fatal("Expected request method to be POST but got", defaultRequest.Method)
	}
}

func TestHTTPProxy_ApplyProxyPassRule_Scheme(t *testing.T) {
	proxy := HTTPProxy{}
	schemeProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Scheme: "http",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(schemeProxyRule, defaultRequest)

	if defaultRequest.URL.Scheme != "http" {
		t.Fatal("Expected URL scheme to be http but got", defaultRequest.URL.Scheme)
	}
}

func TestHTTPProxy_ApplyProxyPassRule_Host(t *testing.T) {
	proxy := HTTPProxy{}
	hostProxyRule := ProxyRule{
		Request: ProxyRequest{},
		Pass: ProxyPass{
			Host: "bar.com",
		},
	}
	defaultRequest := &http.Request{
		URL: &url.URL{
			Path:   "/foo/bar",
			Host:   "foo.com",
			Scheme: "https",
		},
		Method: "GET",
	}
	proxy.ApplyProxyPassRule(hostProxyRule, defaultRequest)

	if defaultRequest.URL.Host != "bar.com" {
		t.Fatal("Expected URL Host to be bar.com but got", defaultRequest.URL.Host)
	}
}

func TestHTTPProxy_Setup(t *testing.T) {
	proxy := HTTPProxy{}
	proxy.Setup([]muxy.Middleware{})

	if len(proxy.ProxyRules) != 1 {
		t.Fatal("Expected default ProxyRules to be present")
	}

	proxy = HTTPProxy{
		ProxyRules: []ProxyRule{
			ProxyRule{},
		},
	}
	proxy.Setup([]muxy.Middleware{})

	if len(proxy.ProxyRules) != 2 {
		t.Fatal("Expected default ProxyRules to be present")
	}
}

func TestHTTPProxy_Teardown(t *testing.T) {
	proxy := HTTPProxy{}
	proxy.Teardown()
}

func TestHTTPProxy_DefaultProxyRule(t *testing.T) {
	proxy := HTTPProxy{
		ProxyHost: "foo.com",
		ProxyPort: 1234,
	}
	rule := proxy.defaultProxyRule()

	expected := "foo.com:1234"
	if rule.Pass.Host != expected {
		t.Fatal("Expected host to be", expected, "but got", rule.Pass.Host)
	}
}

func TestHTTPProxy_checkHTTPServerError(t *testing.T) {
	checkHTTPServerError(errors.New("fake error"))
}

func TestHTTPProxy_ProxyWithHTTP(t *testing.T) {
	proxyPort := GetFreePort()
	port := GetFreePort()

	// Run Proxied Server
	runTestServer(proxyPort)

	// Run Muxy Proxy
	proxy := HTTPProxy{
		Port:          port,
		Host:          "localhost",
		Protocol:      "http",
		Insecure:      true,
		ProxyHost:     "localhost",
		ProxyPort:     proxyPort,
		ProxyProtocol: "http",
	}
	proxy.Setup([]muxy.Middleware{})
	go proxy.Proxy()

	// Wait for servers to be up
	waitForPort(proxyPort, t)
	waitForPort(port, t)

	res, err := http.Get(fmt.Sprintf("http://localhost:%d", port))
	if err != nil {
		log.Fatal(err)
	}

	// Check body
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	if string(body) != proxiedServerBody {
		t.Fatal("Want", proxiedServerBody, "got", string(body))
	}
}

func TestHTTPProxy_ProxyWithHTTPs(t *testing.T) {
	proxyPort := GetFreePort()
	port := GetFreePort()

	// Run Proxied Server
	runTestServer(proxyPort)

	// Run Muxy Proxy
	proxy := HTTPProxy{
		Port:          port,
		Host:          "localhost",
		Protocol:      "https",
		Insecure:      true,
		ProxyHost:     "localhost",
		ProxyPort:     proxyPort,
		ProxyProtocol: "http",
	}
	proxy.Setup([]muxy.Middleware{})
	go proxy.Proxy()

	// Wait for servers to be up
	waitForPort(proxyPort, t)
	waitForPort(port, t)

	// Insecure
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	res, err := client.Get(fmt.Sprintf("https://localhost:%d", port))
	if err != nil {
		log.Fatal(err)
	}

	// Check body
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	if string(body) != proxiedServerBody {
		t.Fatal("Want", proxiedServerBody, "got", string(body))
	}
}

func runTestServer(port int) {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		w.Header().Add("Content-Type", "application/json")
		w.Write([]byte(proxiedServerBody))
	})

	go http.ListenAndServe(fmt.Sprintf(":%d", port), mux)
}
<|endoftext|>


<commit_before>// Copyright 2014 The Mangos Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use 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 pair implements the PAIR protocol.  This protocol is a 1:1
// peering protocol.
package pair

import (
	"github.com/gdamore/mangos"
	"sync"
)

type pair struct {
	sock mangos.ProtocolSocket
	peer mangos.Endpoint
	raw  bool
	sync.Mutex
}

func (x *pair) Init(sock mangos.ProtocolSocket) {
	x.sock = sock
}

func (x *pair) sender(ep mangos.Endpoint) {
	// This is pretty easy because we have only one peer at a time.
	// If the peer goes away, we'll just drop the message on the floor.
	for {
		var msg *mangos.Message
		select {
		case msg = <-x.sock.SendChannel():
		case <-x.sock.DrainChannel():
			return
		}

		if ep.SendMsg(msg) != nil {
			msg.Free()
			return
		}
	}
}

func (x *pair) receiver(ep mangos.Endpoint) {
	for {
		msg := ep.RecvMsg()
		if msg == nil {
			return
		}

		select {
		case x.sock.RecvChannel() <- msg:
		case <-x.sock.CloseChannel():
			return
		}
	}
}

func (x *pair) AddEndpoint(ep mangos.Endpoint) {
	x.Lock()
	if x.peer != nil {
		x.Unlock()
		ep.Close()
		return
	}
	x.peer = ep
	go x.receiver(ep)
	go x.sender(ep)
	x.Unlock()
}

func (x *pair) RemoveEndpoint(ep mangos.Endpoint) {
	x.Lock()
	if x.peer == ep {
		x.peer = nil
	}
	x.Unlock()
}

func (*pair) Number() uint16 {
	return mangos.ProtoPair
}

func (*pair) ValidPeer(peer uint16) bool {
	if peer == mangos.ProtoPair {
		return true
	}
	return false
}

func (x *pair) SetOption(name string, v interface{}) error {
	switch name {
	case mangos.OptionRaw:
		x.raw = v.(bool)
		return nil
	default:
		return mangos.ErrBadOption
	}
}

func (x *pair) GetOption(name string) (interface{}, error) {
	switch name {
	case mangos.OptionRaw:
		return x.raw, nil
	default:
		return nil, mangos.ErrBadOption
	}
}

// NewSocket allocates a new Socket using the STAR protocol.
func NewSocket() (mangos.Socket, error) {
	return mangos.MakeSocket(&pair{}), nil
}
<commit_msg>pair.go edited, typo fixed.<commit_after>// Copyright 2014 The Mangos Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use 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 pair implements the PAIR protocol.  This protocol is a 1:1
// peering protocol.
package pair

import (
	"github.com/gdamore/mangos"
	"sync"
)

type pair struct {
	sock mangos.ProtocolSocket
	peer mangos.Endpoint
	raw  bool
	sync.Mutex
}

func (x *pair) Init(sock mangos.ProtocolSocket) {
	x.sock = sock
}

func (x *pair) sender(ep mangos.Endpoint) {
	// This is pretty easy because we have only one peer at a time.
	// If the peer goes away, we'll just drop the message on the floor.
	for {
		var msg *mangos.Message
		select {
		case msg = <-x.sock.SendChannel():
		case <-x.sock.DrainChannel():
			return
		}

		if ep.SendMsg(msg) != nil {
			msg.Free()
			return
		}
	}
}

func (x *pair) receiver(ep mangos.Endpoint) {
	for {
		msg := ep.RecvMsg()
		if msg == nil {
			return
		}

		select {
		case x.sock.RecvChannel() <- msg:
		case <-x.sock.CloseChannel():
			return
		}
	}
}

func (x *pair) AddEndpoint(ep mangos.Endpoint) {
	x.Lock()
	if x.peer != nil {
		x.Unlock()
		ep.Close()
		return
	}
	x.peer = ep
	go x.receiver(ep)
	go x.sender(ep)
	x.Unlock()
}

func (x *pair) RemoveEndpoint(ep mangos.Endpoint) {
	x.Lock()
	if x.peer == ep {
		x.peer = nil
	}
	x.Unlock()
}

func (*pair) Number() uint16 {
	return mangos.ProtoPair
}

func (*pair) ValidPeer(peer uint16) bool {
	if peer == mangos.ProtoPair {
		return true
	}
	return false
}

func (x *pair) SetOption(name string, v interface{}) error {
	switch name {
	case mangos.OptionRaw:
		x.raw = v.(bool)
		return nil
	default:
		return mangos.ErrBadOption
	}
}

func (x *pair) GetOption(name string) (interface{}, error) {
	switch name {
	case mangos.OptionRaw:
		return x.raw, nil
	default:
		return nil, mangos.ErrBadOption
	}
}

// NewSocket allocates a new Socket using the PAIR protocol.
func NewSocket() (mangos.Socket, error) {
	return mangos.MakeSocket(&pair{}), nil
}
<|endoftext|>


<commit_before>package v1

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	redis "gopkg.in/redis.v5"
	"zxq.co/ripple/rippleapi/common"
)

type setAllowedData struct {
	UserID  int `json:"user_id"`
	Allowed int `json:"allowed"`
}

// UserManageSetAllowedPOST allows to set the allowed status of an user.
func UserManageSetAllowedPOST(md common.MethodData) common.CodeMessager {
	var data setAllowedData
	if err := md.Unmarshal(&data); err != nil {
		return ErrBadJSON
	}
	if data.Allowed < 0 || data.Allowed > 2 {
		return common.SimpleResponse(400, "Allowed status must be between 0 and 2")
	}
	var banDatetime int64
	var privsSet string
	if data.Allowed == 0 {
		banDatetime = time.Now().Unix()
		privsSet = "privileges = (privileges & ~3)"
	} else if data.Allowed == 1 {
		banDatetime = 0
		privsSet = "privileges = (privileges | 3)"
	} else if data.Allowed == 2 {
		banDatetime = time.Now().Unix()
		privsSet = "privileges = (privileges | 2) & (privileges & ~1)"
	}
	_, err := md.DB.Exec("UPDATE users SET "+privsSet+", ban_datetime = ? WHERE id = ?", banDatetime, data.UserID)
	if err != nil {
		md.Err(err)
		return Err500
	}
	rapLog(md, fmt.Sprintf("changed UserID:%d's allowed to %d. This was done using the API's terrible ManageSetAllowed.", data.UserID, data.Allowed))
	go fixPrivileges(data.UserID, md.DB)
	query := `
SELECT users.id, users.username, register_datetime, privileges,
	latest_activity, users_stats.username_aka,
	users_stats.country
FROM users
LEFT JOIN users_stats
ON users.id=users_stats.id
WHERE users.id=?
LIMIT 1`
	return userPutsSingle(md, md.DB.QueryRowx(query, data.UserID))
}

type userEditData struct {
	ID            int          `json:"id"`
	Username      *string      `json:"username"`
	UsernameAKA   *string      `json:"username_aka"`
	Privileges    *uint64      `json:"privileges"`
	Country       *string      `json:"country"`
	SilenceInfo   *silenceInfo `json:"silence_info"`
	ResetUserpage bool         `json:"reset_userpage"`
	//ResetAvatar bool `json:"reset_avatar"`
}

var privChangeList = [...]string{
	"banned",
	"locked",
	"restricted",
	"removed all restrictions on",
}

// UserEditPOST allows to edit an user's information.
func UserEditPOST(md common.MethodData) common.CodeMessager {
	var data userEditData
	if err := md.Unmarshal(&data); err != nil {
		fmt.Println(err)
		return ErrBadJSON
	}

	if data.ID == 0 {
		return common.SimpleResponse(404, "That user could not be found")
	}

	var prevUser struct {
		Username   string
		Privileges uint64
	}
	err := md.DB.Get(&prevUser, "SELECT username, privileges FROM users WHERE id = ? LIMIT 1", data.ID)

	switch err {
	case nil: // carry on
	case sql.ErrNoRows:
		return common.SimpleResponse(404, "That user could not be found")
	default:
		md.Err(err)
		return Err500
	}

	const initQuery = "UPDATE users SET\n"
	q := initQuery
	var args []interface{}

	// totally did not realise I had to update some fields in users_stats as well
	// and just copy pasting the above code by prefixing "stats" to every
	// variable
	const statsInitQuery = "UPDATE users_stats SET\n"
	statsQ := statsInitQuery
	var statsArgs []interface{}

	if common.UserPrivileges(prevUser.Privileges)&common.AdminPrivilegeManageUsers != 0 &&
		data.ID != md.User.UserID {
		return common.SimpleResponse(403, "Can't edit that user")
	}

	var isBanned bool
	var isSilenced bool
	if data.Privileges != nil {
		// If we want to modify privileges other than Normal/Public, we need to have
		// the right privilege ourselves and AdminManageUsers won't suffice.
		if (*data.Privileges&^3) != (prevUser.Privileges&^3) &&
			md.User.UserPrivileges&common.AdminPrivilegeManagePrivilege == 0 {
			return common.SimpleResponse(403, "Can't modify user privileges without AdminManagePrivileges")
		}
		q += "privileges = ?,\n"
		args = append(args, *data.Privileges)

		// UserPublic became 0, so banned or restricted
		const uPublic = uint64(common.UserPrivilegePublic)
		if *data.Privileges&uPublic == 0 && prevUser.Privileges&uPublic != 0 {
			q += "ban_datetime = ?,\n"
			args = append(args, time.Now().Unix())
			isBanned = true
		}

		// If we modified other privileges apart from Normal and Public, we use a generic
		// "changed user's privileges". Otherwise, we are more descriptive.
		if *data.Privileges^prevUser.Privileges > 3 {
			rapLog(md, fmt.Sprintf("has changed %s's privileges", prevUser.Username))
		} else {
			rapLog(md, fmt.Sprintf("has %s %s", privChangeList[*data.Privileges&3], prevUser.Username))
		}
	}
	if data.Username != nil {
		if strings.Contains(*data.Username, " ") && strings.Contains(*data.Username, "_") {
			return common.SimpleResponse(400, "Mixed spaces and underscores")
		}
		if usernameAvailable(md, *data.Username, data.ID) {
			return common.SimpleResponse(409, "User with that username exists")
		}
		jsonData, _ := json.Marshal(struct {
			UserID      int    `json:"userID"`
			NewUsername string `json:"newUsername"`
		}{data.ID, *data.Username})
		md.R.Publish("peppy:change_username", string(jsonData))
	}
	if data.UsernameAKA != nil {
		statsQ += "username_aka = ?,\n"
		statsArgs = append(statsArgs, *data.UsernameAKA)
	}
	if data.Country != nil {
		statsQ += "country = ?,\n"
		statsArgs = append(statsArgs, *data.Country)
		rapLog(md, fmt.Sprintf("has changed %s country to %s", prevUser.Username, *data.Country))
		appendToUserNotes(md, "country changed to "+*data.Country, data.ID)
	}
	if data.SilenceInfo != nil && md.User.UserPrivileges&common.AdminPrivilegeSilenceUsers != 0 {
		q += "silence_end = ?, silence_reason = ?,\n"
		args = append(args, time.Time(data.SilenceInfo.End).Unix(), data.SilenceInfo.Reason)
		isSilenced = true
	}
	if data.ResetUserpage {
		statsQ += "userpage_content = '',\n"
	}

	if q != initQuery {
		q = q[:len(q)-2] + " WHERE id = ? LIMIT 1"
		args = append(args, data.ID)
		_, err = md.DB.Exec(q, args...)
		if err != nil {
			md.Err(err)
			return Err500
		}

	}
	if statsQ != statsInitQuery {
		statsQ = statsQ[:len(statsQ)-2] + " WHERE id = ? LIMIT 1"
		statsArgs = append(statsArgs, data.ID)
		_, err = md.DB.Exec(statsQ, statsArgs...)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}

	if isBanned || isSilenced {
		if err := updateBanBancho(md.R, data.ID); err != nil {
			md.Err(err)
			return Err500
		}
	}

	rapLog(md, fmt.Sprintf("has updated user %s", prevUser.Username))

	return userPutsSingle(md, md.DB.QueryRowx(userFields+" WHERE users.id = ? LIMIT 1", data.ID))
}

func updateBanBancho(r *redis.Client, user int) error {
	return r.Publish("peppy:ban", strconv.Itoa(user)).Err()
}

type wipeUserData struct {
	ID    int   `json:"id"`
	Modes []int `json:"modes"`
}

// WipeUserPOST wipes an user's scores.
func WipeUserPOST(md common.MethodData) common.CodeMessager {
	var data wipeUserData
	if err := md.Unmarshal(&data); err != nil {
		return ErrBadJSON
	}
	if data.ID == 0 {
		return ErrMissingField("id")
	}
	if len(data.Modes) == 0 {
		return ErrMissingField("modes")
	}

	var userData struct {
		Username   string
		Privileges uint64
	}
	err := md.DB.Get(&userData, "SELECT username, privileges FROM users WHERE id = ?", data.ID)
	switch err {
	case sql.ErrNoRows:
		return common.SimpleResponse(404, "That user could not be found!")
	case nil: // carry on
	default:
		md.Err(err)
		return Err500
	}

	if common.UserPrivileges(userData.Privileges)&common.AdminPrivilegeManageUsers != 0 {
		return common.SimpleResponse(403, "Can't edit that user")
	}

	tx, err := md.DB.Beginx()
	if err != nil {
		md.Err(err)
		return Err500
	}

	for _, mode := range data.Modes {
		if mode < 0 || mode > 3 {
			continue
		}
		_, err = tx.Exec("INSERT INTO scores_removed SELECT * FROM scores WHERE userid = ? AND play_mode = ?", data.ID, mode)
		if err != nil {
			md.Err(err)
		}
		_, err = tx.Exec("DELETE FROM scores WHERE userid = ? AND play_mode = ?", data.ID, mode)
		if err != nil {
			md.Err(err)
		}
		_, err = tx.Exec(strings.Replace(
			`UPDATE users_stats SET total_score_MODE = 0, ranked_score_MODE = 0, replays_watched_MODE = 0,
			playcount_MODE = 0, avg_accuracy_MODE = 0, total_hits_MODE = 0, level_MODE = 0, pp_MODE = 0
			WHERE id = ?`, "MODE", modesToReadable[mode], -1,
		), data.ID)
		if err != nil {
			md.Err(err)
		}
		_, err = tx.Exec("DELETE FROM users_beatmap_playcount WHERE user_id = ? AND game_mode = ?", data.ID, mode)
		if err != nil {
			md.Err(err)
		}
	}

	if err = tx.Commit(); err != nil {
		md.Err(err)
		return Err500
	}

	rapLog(md, fmt.Sprintf("has wiped %s's account", userData.Username))

	return userPutsSingle(md, md.DB.QueryRowx(userFields+" WHERE users.id = ? LIMIT 1", data.ID))
}

func appendToUserNotes(md common.MethodData, message string, user int) {
	message = "\n[" + time.Now().Format("2006-01-02 15:04:05") + "] API: " + message
	_, err := md.DB.Exec("UPDATE users SET notes = CONCAT(COALESCE(notes, ''), ?) WHERE id = ?",
		message, user)
	if err != nil {
		md.Err(err)
	}
}

func usernameAvailable(md common.MethodData, u string, userID int) (r bool) {
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE username_safe = ? AND id != ?)", common.SafeUsername(u), userID).Scan(&r)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
	}
	return
}
<commit_msg>Wipe classic & relax (or both)<commit_after>package v1

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	redis "gopkg.in/redis.v5"
	"zxq.co/ripple/rippleapi/common"
)

type setAllowedData struct {
	UserID  int `json:"user_id"`
	Allowed int `json:"allowed"`
}

// UserManageSetAllowedPOST allows to set the allowed status of an user.
func UserManageSetAllowedPOST(md common.MethodData) common.CodeMessager {
	var data setAllowedData
	if err := md.Unmarshal(&data); err != nil {
		return ErrBadJSON
	}
	if data.Allowed < 0 || data.Allowed > 2 {
		return common.SimpleResponse(400, "Allowed status must be between 0 and 2")
	}
	var banDatetime int64
	var privsSet string
	if data.Allowed == 0 {
		banDatetime = time.Now().Unix()
		privsSet = "privileges = (privileges & ~3)"
	} else if data.Allowed == 1 {
		banDatetime = 0
		privsSet = "privileges = (privileges | 3)"
	} else if data.Allowed == 2 {
		banDatetime = time.Now().Unix()
		privsSet = "privileges = (privileges | 2) & (privileges & ~1)"
	}
	_, err := md.DB.Exec("UPDATE users SET "+privsSet+", ban_datetime = ? WHERE id = ?", banDatetime, data.UserID)
	if err != nil {
		md.Err(err)
		return Err500
	}
	rapLog(md, fmt.Sprintf("changed UserID:%d's allowed to %d. This was done using the API's terrible ManageSetAllowed.", data.UserID, data.Allowed))
	go fixPrivileges(data.UserID, md.DB)
	query := `
SELECT users.id, users.username, register_datetime, privileges,
	latest_activity, users_stats.username_aka,
	users_stats.country
FROM users
LEFT JOIN users_stats
ON users.id=users_stats.id
WHERE users.id=?
LIMIT 1`
	return userPutsSingle(md, md.DB.QueryRowx(query, data.UserID))
}

type userEditData struct {
	ID            int          `json:"id"`
	Username      *string      `json:"username"`
	UsernameAKA   *string      `json:"username_aka"`
	Privileges    *uint64      `json:"privileges"`
	Country       *string      `json:"country"`
	SilenceInfo   *silenceInfo `json:"silence_info"`
	ResetUserpage bool         `json:"reset_userpage"`
	//ResetAvatar bool `json:"reset_avatar"`
}

var privChangeList = [...]string{
	"banned",
	"locked",
	"restricted",
	"removed all restrictions on",
}

// UserEditPOST allows to edit an user's information.
func UserEditPOST(md common.MethodData) common.CodeMessager {
	var data userEditData
	if err := md.Unmarshal(&data); err != nil {
		fmt.Println(err)
		return ErrBadJSON
	}

	if data.ID == 0 {
		return common.SimpleResponse(404, "That user could not be found")
	}

	var prevUser struct {
		Username   string
		Privileges uint64
	}
	err := md.DB.Get(&prevUser, "SELECT username, privileges FROM users WHERE id = ? LIMIT 1", data.ID)

	switch err {
	case nil: // carry on
	case sql.ErrNoRows:
		return common.SimpleResponse(404, "That user could not be found")
	default:
		md.Err(err)
		return Err500
	}

	const initQuery = "UPDATE users SET\n"
	q := initQuery
	var args []interface{}

	// totally did not realise I had to update some fields in users_stats as well
	// and just copy pasting the above code by prefixing "stats" to every
	// variable
	const statsInitQuery = "UPDATE users_stats SET\n"
	statsQ := statsInitQuery
	var statsArgs []interface{}

	if common.UserPrivileges(prevUser.Privileges)&common.AdminPrivilegeManageUsers != 0 &&
		data.ID != md.User.UserID {
		return common.SimpleResponse(403, "Can't edit that user")
	}

	var isBanned bool
	var isSilenced bool
	if data.Privileges != nil {
		// If we want to modify privileges other than Normal/Public, we need to have
		// the right privilege ourselves and AdminManageUsers won't suffice.
		if (*data.Privileges&^3) != (prevUser.Privileges&^3) &&
			md.User.UserPrivileges&common.AdminPrivilegeManagePrivilege == 0 {
			return common.SimpleResponse(403, "Can't modify user privileges without AdminManagePrivileges")
		}
		q += "privileges = ?,\n"
		args = append(args, *data.Privileges)

		// UserPublic became 0, so banned or restricted
		const uPublic = uint64(common.UserPrivilegePublic)
		if *data.Privileges&uPublic == 0 && prevUser.Privileges&uPublic != 0 {
			q += "ban_datetime = ?,\n"
			args = append(args, time.Now().Unix())
			isBanned = true
		}

		// If we modified other privileges apart from Normal and Public, we use a generic
		// "changed user's privileges". Otherwise, we are more descriptive.
		if *data.Privileges^prevUser.Privileges > 3 {
			rapLog(md, fmt.Sprintf("has changed %s's privileges", prevUser.Username))
		} else {
			rapLog(md, fmt.Sprintf("has %s %s", privChangeList[*data.Privileges&3], prevUser.Username))
		}
	}
	if data.Username != nil {
		if strings.Contains(*data.Username, " ") && strings.Contains(*data.Username, "_") {
			return common.SimpleResponse(400, "Mixed spaces and underscores")
		}
		if usernameAvailable(md, *data.Username, data.ID) {
			return common.SimpleResponse(409, "User with that username exists")
		}
		jsonData, _ := json.Marshal(struct {
			UserID      int    `json:"userID"`
			NewUsername string `json:"newUsername"`
		}{data.ID, *data.Username})
		md.R.Publish("peppy:change_username", string(jsonData))
	}
	if data.UsernameAKA != nil {
		statsQ += "username_aka = ?,\n"
		statsArgs = append(statsArgs, *data.UsernameAKA)
	}
	if data.Country != nil {
		statsQ += "country = ?,\n"
		statsArgs = append(statsArgs, *data.Country)
		rapLog(md, fmt.Sprintf("has changed %s country to %s", prevUser.Username, *data.Country))
		appendToUserNotes(md, "country changed to "+*data.Country, data.ID)
	}
	if data.SilenceInfo != nil && md.User.UserPrivileges&common.AdminPrivilegeSilenceUsers != 0 {
		q += "silence_end = ?, silence_reason = ?,\n"
		args = append(args, time.Time(data.SilenceInfo.End).Unix(), data.SilenceInfo.Reason)
		isSilenced = true
	}
	if data.ResetUserpage {
		statsQ += "userpage_content = '',\n"
	}

	if q != initQuery {
		q = q[:len(q)-2] + " WHERE id = ? LIMIT 1"
		args = append(args, data.ID)
		_, err = md.DB.Exec(q, args...)
		if err != nil {
			md.Err(err)
			return Err500
		}

	}
	if statsQ != statsInitQuery {
		statsQ = statsQ[:len(statsQ)-2] + " WHERE id = ? LIMIT 1"
		statsArgs = append(statsArgs, data.ID)
		_, err = md.DB.Exec(statsQ, statsArgs...)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}

	if isBanned || isSilenced {
		if err := updateBanBancho(md.R, data.ID); err != nil {
			md.Err(err)
			return Err500
		}
	}

	rapLog(md, fmt.Sprintf("has updated user %s", prevUser.Username))

	return userPutsSingle(md, md.DB.QueryRowx(userFields+" WHERE users.id = ? LIMIT 1", data.ID))
}

func updateBanBancho(r *redis.Client, user int) error {
	return r.Publish("peppy:ban", strconv.Itoa(user)).Err()
}

type wipeUserData struct {
	ID    int   `json:"id"`
	Modes []int `json:"modes"`
	Relax int   `json:"relax"`
}

// WipeUserPOST wipes an user's scores.
func WipeUserPOST(md common.MethodData) common.CodeMessager {
	data := wipeUserData{
		// Wipe both classic & relax by default
		Relax: -1,
	}
	if err := md.Unmarshal(&data); err != nil {
		return ErrBadJSON
	}
	if data.ID == 0 {
		return ErrMissingField("id")
	}
	if len(data.Modes) == 0 {
		return ErrMissingField("modes")
	}

	var userData struct {
		Username   string
		Privileges uint64
	}
	err := md.DB.Get(&userData, "SELECT username, privileges FROM users WHERE id = ?", data.ID)
	switch err {
	case sql.ErrNoRows:
		return common.SimpleResponse(404, "That user could not be found!")
	case nil: // carry on
	default:
		md.Err(err)
		return Err500
	}

	/*if common.UserPrivileges(userData.Privileges)&common.AdminPrivilegeManageUsers != 0 {
		return common.SimpleResponse(403, "Can't edit that user")
	}*/

	tx, err := md.DB.Beginx()
	if err != nil {
		md.Err(err)
		return Err500
	}

	for _, mode := range data.Modes {
		if mode < 0 || mode > 3 {
			continue
		}
		var suffix string
		if data.Relax > -1 {
			suffix = fmt.Sprintf("AND is_relax = %d", data.Relax)
		}
		_, err = tx.Exec("INSERT INTO scores_removed SELECT * FROM scores WHERE userid = ? AND play_mode = ? "+suffix, data.ID, mode)
		if err != nil {
			md.Err(err)
		}
		_, err = tx.Exec("DELETE FROM scores WHERE userid = ? AND play_mode = ? "+suffix, data.ID, mode)
		if err != nil {
			md.Err(err)
		}
		if data.Relax <= 0 {
			// Wipe classic stats
			_, err = tx.Exec(strings.Replace(
				`UPDATE users_stats SET total_score_MODE = 0, ranked_score_MODE = 0, replays_watched_MODE = 0,
				playcount_MODE = 0, avg_accuracy_MODE = 0, total_hits_MODE = 0, level_MODE = 0, pp_MODE = 0
				WHERE id = ?`, "MODE", modesToReadable[mode], -1,
			), data.ID)
			if err != nil {
				md.Err(err)
			}
		}
		if data.Relax < 0 || data.Relax == 1 {
			// Wipe relax stats
			_, err = tx.Exec(strings.Replace(
				`UPDATE users_stats_relax SET total_score_MODE = 0, ranked_score_MODE = 0,
				playcount_MODE = 0, avg_accuracy_MODE = 0, total_hits_MODE = 0, level_MODE = 0, pp_MODE = 0
				WHERE id = ?`, "MODE", modesToReadable[mode], -1,
			), data.ID)
			if err != nil {
				md.Err(err)
			}
		}
		_, err = tx.Exec("DELETE FROM users_beatmap_playcount WHERE user_id = ? AND game_mode = ?", data.ID, mode)
		if err != nil {
			md.Err(err)
		}
	}

	if err = tx.Commit(); err != nil {
		md.Err(err)
		return Err500
	}

	rapLog(md, fmt.Sprintf("has wiped %s's account", userData.Username))

	return userPutsSingle(md, md.DB.QueryRowx(userFields+" WHERE users.id = ? LIMIT 1", data.ID))
}

func appendToUserNotes(md common.MethodData, message string, user int) {
	message = "\n[" + time.Now().Format("2006-01-02 15:04:05") + "] API: " + message
	_, err := md.DB.Exec("UPDATE users SET notes = CONCAT(COALESCE(notes, ''), ?) WHERE id = ?",
		message, user)
	if err != nil {
		md.Err(err)
	}
}

func usernameAvailable(md common.MethodData, u string, userID int) (r bool) {
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE username_safe = ? AND id != ?)", common.SafeUsername(u), userID).Scan(&r)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"text/template"
)

type ChoiceRule struct {
	Name         string
	VariableType string
}

var choiceStatePrelude = `
// Code generated by github.com/mweagle/Sparta/aws/step/generator/main.go. DO NOT EDIT.

package step

import (
	"encoding/json"
	"time"
)

/*******************************************************************************
   ___ ___  __  __ ___  _   ___ ___ ___  ___  _  _ ___
  / __/ _ \|  \/  | _ \/_\ | _ \_ _/ __|/ _ \| \| / __|
 | (_| (_) | |\/| |  _/ _ \|   /| |\__ \ (_) |    \__ \
  \___\___/|_|  |_|_|/_/ \_\_|_\___|___/\___/|_|\_|___/

/******************************************************************************/

// For path based selectors see the
// JSONPath: https://github.com/NodePrime/jsonpath
// documentation

`
var choiceStateTemplate = `
////////////////////////////////////////////////////////////////////////////////
// {{.Name}}
////////////////////////////////////////////////////////////////////////////////

// {{.Name}} comparison
type {{.Name}} struct {
	Comparison
	Variable string
	Value    {{.VariableType}}
}

// MarshalJSON for custom marshalling
func (cmp *{{.Name}}) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Variable       string
		{{.Name}} 		 {{.VariableType}}
	}{
		Variable:       cmp.Variable,
		{{.Name}}: 			cmp.Value,
	})
}
`

type choiceRuleVariableDef struct {
	Variable string
}
type choiceDefinitions struct {
	Choices map[string]choiceRuleVariableDef `json:"choices"`
}

func main() {
	// Params are the path to the input definitions and the
	// output path.
	if len(os.Args) != 3 {
		fmt.Printf("Please provide path to the source definition as arg1, path to the output file as arg2\n")
		os.Exit(1)
	}
	inputDefFile := os.Args[1]
	outputSourceFile := os.Args[2]

	inputFileBytes, inputFileBytesErr := ioutil.ReadFile(inputDefFile)
	if inputFileBytesErr != nil {
		fmt.Printf("Failed to read %s: %v\n", inputDefFile, inputFileBytesErr)
		os.Exit(1)
	}
	var choiceDefs choiceDefinitions
	unmarshalErr := json.Unmarshal(inputFileBytes, &choiceDefs)
	if unmarshalErr != nil {
		fmt.Printf("Failed to unmarshal %s: %v", inputDefFile, unmarshalErr)
		os.Exit(1)
	}

	// Rip the definitions and write each one
	ruleTemplate, ruleTemplateErr := template.New("ruleTemplate").Parse(choiceStateTemplate)
	if ruleTemplateErr != nil {
		fmt.Printf("Failed to parse template: %v\n", ruleTemplateErr)
		os.Exit(1)
	}

	outputSource, outputSourceErr := os.Create(outputSourceFile)
	if outputSourceErr != nil {
		fmt.Printf("Failed to open %s: %v\n", outputSourceFile, outputSourceErr)
		os.Exit(1)
	}
	defer outputSource.Close()
	io.WriteString(outputSource, choiceStatePrelude)

	for eachRuleName, eachRuleDef := range choiceDefs.Choices {
		templateParams := ChoiceRule{
			Name:         eachRuleName,
			VariableType: eachRuleDef.Variable,
		}
		executeErr := ruleTemplate.Execute(outputSource, templateParams)
		if executeErr != nil {
			fmt.Printf("Failed to execute template: %v\n", executeErr)
			os.Exit(1)
		}
	}
	fmt.Printf("All done!\n")
}
<commit_msg>Patch up static check<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"text/template"
)

type ChoiceRule struct {
	Name         string
	VariableType string
}

var choiceStatePrelude = `
// Code generated by github.com/mweagle/Sparta/aws/step/generator/main.go. DO NOT EDIT.

package step

import (
	"encoding/json"
	"time"
)

/*******************************************************************************
   ___ ___  __  __ ___  _   ___ ___ ___  ___  _  _ ___
  / __/ _ \|  \/  | _ \/_\ | _ \_ _/ __|/ _ \| \| / __|
 | (_| (_) | |\/| |  _/ _ \|   /| |\__ \ (_) |    \__ \
  \___\___/|_|  |_|_|/_/ \_\_|_\___|___/\___/|_|\_|___/

/******************************************************************************/

// For path based selectors see the
// JSONPath: https://github.com/NodePrime/jsonpath
// documentation

`
var choiceStateTemplate = `
////////////////////////////////////////////////////////////////////////////////
// {{.Name}}
////////////////////////////////////////////////////////////////////////////////

// {{.Name}} comparison
type {{.Name}} struct {
	Comparison
	Variable string
	Value    {{.VariableType}}
}

// MarshalJSON for custom marshalling
func (cmp *{{.Name}}) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Variable       string
		{{.Name}} 		 {{.VariableType}}
	}{
		Variable:       cmp.Variable,
		{{.Name}}: 			cmp.Value,
	})
}
`

type choiceRuleVariableDef struct {
	Variable string
}
type choiceDefinitions struct {
	Choices map[string]choiceRuleVariableDef `json:"choices"`
}

func main() {
	// Params are the path to the input definitions and the
	// output path.
	if len(os.Args) != 3 {
		fmt.Printf("Please provide path to the source definition as arg1, path to the output file as arg2\n")
		os.Exit(1)
	}
	inputDefFile := os.Args[1]
	outputSourceFile := os.Args[2]
	/* #nosec G304 */
	inputFileBytes, inputFileBytesErr := ioutil.ReadFile(inputDefFile)
	if inputFileBytesErr != nil {
		fmt.Printf("Failed to read %s: %v\n", inputDefFile, inputFileBytesErr)
		os.Exit(1)
	}
	var choiceDefs choiceDefinitions
	unmarshalErr := json.Unmarshal(inputFileBytes, &choiceDefs)
	if unmarshalErr != nil {
		fmt.Printf("Failed to unmarshal %s: %v", inputDefFile, unmarshalErr)
		os.Exit(1)
	}

	// Rip the definitions and write each one
	ruleTemplate, ruleTemplateErr := template.New("ruleTemplate").Parse(choiceStateTemplate)
	if ruleTemplateErr != nil {
		fmt.Printf("Failed to parse template: %v\n", ruleTemplateErr)
		os.Exit(1)
	}

	outputSource, outputSourceErr := os.Create(outputSourceFile)
	if outputSourceErr != nil {
		fmt.Printf("Failed to open %s: %v\n", outputSourceFile, outputSourceErr)
		os.Exit(1)
	}
	defer func() {
		closeErr := outputSource.Close()
		if closeErr != nil {
			fmt.Printf("Failed to close output stream: %#v", closeErr)
		}
	}()
	_, writeErr := io.WriteString(outputSource, choiceStatePrelude)
	if writeErr != nil {
		fmt.Printf("Failed to write: %v\n", writeErr)
		os.Exit(1)
	}
	for eachRuleName, eachRuleDef := range choiceDefs.Choices {
		templateParams := ChoiceRule{
			Name:         eachRuleName,
			VariableType: eachRuleDef.Variable,
		}
		executeErr := ruleTemplate.Execute(outputSource, templateParams)
		if executeErr != nil {
			fmt.Printf("Failed to execute template: %v\n", executeErr)
			os.Exit(1)
		}
	}
	fmt.Printf("All done!\n")
}
<|endoftext|>


<commit_before>package geos

/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>

extern void goLogString(char *msg);
extern void debug_wrap(const char *fmt, ...);
extern GEOSContextHandle_t initGEOS_r_debug();
extern void initGEOS_debug();
*/
import "C"

import (
	"imposm3/logging"
	"runtime"
	"unsafe"
)

var log = logging.NewLogger("GEOS")

//export goLogString
func goLogString(msg *C.char) {
	log.Printf(C.GoString(msg))
}

type Geos struct {
	v    C.GEOSContextHandle_t
	srid int
}

type Geom struct {
	v *C.GEOSGeometry
}

type CreateError string
type Error string

func (e Error) Error() string {
	return string(e)
}

func (e CreateError) Error() string {
	return string(e)
}

func NewGeos() *Geos {
	geos := &Geos{}
	geos.v = C.initGEOS_r_debug()
	return geos
}

func (this *Geos) Finish() {
	if this.v != nil {
		C.finishGEOS_r(this.v)
		this.v = nil
	}
}

func init() {
	/*
		Init global GEOS handle for non _r calls.
		In theory we need to always call the _r functions
		with a thread/goroutine-local GEOS instance to get thread
		safe behaviour. Some functions don't need a GEOS instance though
		and we can make use of that e.g. to call GEOSGeom_destroy in
		finalizer.
	*/
	C.initGEOS_debug()
}

func (this *Geos) Destroy(geom *Geom) {
	runtime.SetFinalizer(geom, nil)
	if geom.v != nil {
		C.GEOSGeom_destroy_r(this.v, geom.v)
		geom.v = nil
	} else {
		log.Printf("double free?")
	}
}

func destroyGeom(geom *Geom) {
	C.GEOSGeom_destroy(geom.v)
}

func (this *Geos) DestroyLater(geom *Geom) {
	runtime.SetFinalizer(geom, destroyGeom)
}

func (this *Geos) Clone(geom *Geom) *Geom {
	if geom == nil || geom.v == nil {
		return nil
	}

	result := C.GEOSGeom_clone_r(this.v, geom.v)
	if result == nil {
		return nil
	}
	return &Geom{result}
}

func (this *Geos) SetHandleSrid(srid int) {
	this.srid = srid
}

func (this *Geos) NumGeoms(geom *Geom) int32 {
	count := int32(C.GEOSGetNumGeometries_r(this.v, geom.v))
	return count
}

func (this *Geos) Geoms(geom *Geom) []*Geom {
	count := this.NumGeoms(geom)
	var result []*Geom
	for i := 0; int32(i) < count; i++ {
		part := C.GEOSGetGeometryN_r(this.v, geom.v, C.int(i))
		if part == nil {
			return nil
		}
		result = append(result, &Geom{part})
	}
	return result
}

func (this *Geos) ExteriorRing(geom *Geom) *Geom {
	ring := C.GEOSGetExteriorRing_r(this.v, geom.v)
	if ring == nil {
		return nil
	}
	return &Geom{ring}
}

func (this *Geos) BoundsPolygon(bounds Bounds) *Geom {
	coordSeq, err := this.CreateCoordSeq(5, 2)
	if err != nil {
		return nil
	}
	// coordSeq inherited by LineString, no destroy

	if err := coordSeq.SetXY(this, 0, bounds.MinX, bounds.MinY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 1, bounds.MaxX, bounds.MinY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 2, bounds.MaxX, bounds.MaxY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 3, bounds.MinX, bounds.MaxY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 4, bounds.MinX, bounds.MinY); err != nil {
		return nil
	}

	geom, err := coordSeq.AsLinearRing(this)
	if err != nil {
		return nil
	}
	// geom inherited by Polygon, no destroy

	geom = this.Polygon(geom, nil)
	return geom

}

func (this *Geos) Point(x, y float64) *Geom {
	coordSeq, err := this.CreateCoordSeq(1, 2)
	if err != nil {
		return nil
	}
	// coordSeq inherited by LineString
	coordSeq.SetXY(this, 0, x, y)
	geom, err := coordSeq.AsPoint(this)
	if err != nil {
		return nil
	}
	return geom
}

func (this *Geos) Polygon(exterior *Geom, interiors []*Geom) *Geom {
	if len(interiors) == 0 {
		geom := C.GEOSGeom_createPolygon_r(this.v, exterior.v, nil, C.uint(0))
		if geom == nil {
			return nil
		}
		err := C.GEOSNormalize_r(this.v, geom)
		if err != 0 {
			C.GEOSGeom_destroy(geom)
			return nil
		}
		return &Geom{geom}
	}

	interiorPtr := make([]*C.GEOSGeometry, len(interiors))
	for i, geom := range interiors {
		interiorPtr[i] = geom.v
	}
	geom := C.GEOSGeom_createPolygon_r(this.v, exterior.v, &interiorPtr[0], C.uint(len(interiors)))
	if geom == nil {
		return nil
	}
	err := C.GEOSNormalize_r(this.v, geom)
	if err != 0 {
		C.GEOSGeom_destroy(geom)
		return nil
	}
	return &Geom{geom}
}

func (this *Geos) MultiPolygon(polygons []*Geom) *Geom {
	if len(polygons) == 0 {
		return nil
	}
	polygonPtr := make([]*C.GEOSGeometry, len(polygons))
	for i, geom := range polygons {
		polygonPtr[i] = geom.v
	}
	geom := C.GEOSGeom_createCollection_r(this.v, C.GEOS_MULTIPOLYGON, &polygonPtr[0], C.uint(len(polygons)))
	if geom == nil {
		return nil
	}
	return &Geom{geom}
}
func (this *Geos) MultiLineString(lines []*Geom) *Geom {
	if len(lines) == 0 {
		return nil
	}
	linePtr := make([]*C.GEOSGeometry, len(lines))
	for i, geom := range lines {
		linePtr[i] = geom.v
	}
	geom := C.GEOSGeom_createCollection_r(this.v, C.GEOS_MULTILINESTRING, &linePtr[0], C.uint(len(lines)))
	if geom == nil {
		return nil
	}
	return &Geom{geom}
}

func (this *Geos) IsValid(geom *Geom) bool {
	if C.GEOSisValid_r(this.v, geom.v) == 1 {
		return true
	}
	return false
}

func (this *Geos) IsEmpty(geom *Geom) bool {
	if C.GEOSisEmpty_r(this.v, geom.v) == 1 {
		return true
	}
	return false
}

func (this *Geos) Type(geom *Geom) string {
	geomType := C.GEOSGeomType_r(this.v, geom.v)
	if geomType == nil {
		return "Unknown"
	}
	defer C.free(unsafe.Pointer(geomType))
	return C.GoString(geomType)
}

func (this *Geos) Equals(a, b *Geom) bool {
	result := C.GEOSEquals_r(this.v, a.v, b.v)
	if result == 1 {
		return true
	}
	return false
}

func (this *Geom) Area() float64 {
	var area C.double
	if ret := C.GEOSArea(this.v, &area); ret == 1 {
		return float64(area)
	} else {
		return 0
	}
}

func (this *Geom) Length() float64 {
	var length C.double
	if ret := C.GEOSLength(this.v, &length); ret == 1 {
		return float64(length)
	} else {
		return 0
	}
}

type Bounds struct {
	MinX float64
	MinY float64
	MaxX float64
	MaxY float64
}

var NilBounds = Bounds{1e20, 1e20, -1e20, -1e20}

func (this *Geom) Bounds() Bounds {
	geom := C.GEOSEnvelope(this.v)
	if geom == nil {
		return NilBounds
	}
	extRing := C.GEOSGetExteriorRing(geom)
	if extRing == nil {
		return NilBounds
	}
	cs := C.GEOSGeom_getCoordSeq(extRing)
	var csLen C.uint
	C.GEOSCoordSeq_getSize(cs, &csLen)
	minx := 1.e+20
	maxx := -1e+20
	miny := 1.e+20
	maxy := -1e+20
	var temp C.double
	for i := 0; i < int(csLen); i++ {
		C.GEOSCoordSeq_getX(cs, C.uint(i), &temp)
		x := float64(temp)
		if x < minx {
			minx = x
		}
		if x > maxx {
			maxx = x
		}
		C.GEOSCoordSeq_getY(cs, C.uint(i), &temp)
		y := float64(temp)
		if y < miny {
			miny = y
		}
		if y > maxy {
			maxy = y
		}
	}

	return Bounds{minx, miny, maxx, maxy}
}
<commit_msg>add -lgeos to LDFLAGS<commit_after>package geos

/*
#cgo LDFLAGS: -lgeos_c -lgeos
#include "geos_c.h"
#include <stdlib.h>

extern void goLogString(char *msg);
extern void debug_wrap(const char *fmt, ...);
extern GEOSContextHandle_t initGEOS_r_debug();
extern void initGEOS_debug();
*/
import "C"

import (
	"imposm3/logging"
	"runtime"
	"unsafe"
)

var log = logging.NewLogger("GEOS")

//export goLogString
func goLogString(msg *C.char) {
	log.Printf(C.GoString(msg))
}

type Geos struct {
	v    C.GEOSContextHandle_t
	srid int
}

type Geom struct {
	v *C.GEOSGeometry
}

type CreateError string
type Error string

func (e Error) Error() string {
	return string(e)
}

func (e CreateError) Error() string {
	return string(e)
}

func NewGeos() *Geos {
	geos := &Geos{}
	geos.v = C.initGEOS_r_debug()
	return geos
}

func (this *Geos) Finish() {
	if this.v != nil {
		C.finishGEOS_r(this.v)
		this.v = nil
	}
}

func init() {
	/*
		Init global GEOS handle for non _r calls.
		In theory we need to always call the _r functions
		with a thread/goroutine-local GEOS instance to get thread
		safe behaviour. Some functions don't need a GEOS instance though
		and we can make use of that e.g. to call GEOSGeom_destroy in
		finalizer.
	*/
	C.initGEOS_debug()
}

func (this *Geos) Destroy(geom *Geom) {
	runtime.SetFinalizer(geom, nil)
	if geom.v != nil {
		C.GEOSGeom_destroy_r(this.v, geom.v)
		geom.v = nil
	} else {
		log.Printf("double free?")
	}
}

func destroyGeom(geom *Geom) {
	C.GEOSGeom_destroy(geom.v)
}

func (this *Geos) DestroyLater(geom *Geom) {
	runtime.SetFinalizer(geom, destroyGeom)
}

func (this *Geos) Clone(geom *Geom) *Geom {
	if geom == nil || geom.v == nil {
		return nil
	}

	result := C.GEOSGeom_clone_r(this.v, geom.v)
	if result == nil {
		return nil
	}
	return &Geom{result}
}

func (this *Geos) SetHandleSrid(srid int) {
	this.srid = srid
}

func (this *Geos) NumGeoms(geom *Geom) int32 {
	count := int32(C.GEOSGetNumGeometries_r(this.v, geom.v))
	return count
}

func (this *Geos) Geoms(geom *Geom) []*Geom {
	count := this.NumGeoms(geom)
	var result []*Geom
	for i := 0; int32(i) < count; i++ {
		part := C.GEOSGetGeometryN_r(this.v, geom.v, C.int(i))
		if part == nil {
			return nil
		}
		result = append(result, &Geom{part})
	}
	return result
}

func (this *Geos) ExteriorRing(geom *Geom) *Geom {
	ring := C.GEOSGetExteriorRing_r(this.v, geom.v)
	if ring == nil {
		return nil
	}
	return &Geom{ring}
}

func (this *Geos) BoundsPolygon(bounds Bounds) *Geom {
	coordSeq, err := this.CreateCoordSeq(5, 2)
	if err != nil {
		return nil
	}
	// coordSeq inherited by LineString, no destroy

	if err := coordSeq.SetXY(this, 0, bounds.MinX, bounds.MinY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 1, bounds.MaxX, bounds.MinY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 2, bounds.MaxX, bounds.MaxY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 3, bounds.MinX, bounds.MaxY); err != nil {
		return nil
	}
	if err := coordSeq.SetXY(this, 4, bounds.MinX, bounds.MinY); err != nil {
		return nil
	}

	geom, err := coordSeq.AsLinearRing(this)
	if err != nil {
		return nil
	}
	// geom inherited by Polygon, no destroy

	geom = this.Polygon(geom, nil)
	return geom

}

func (this *Geos) Point(x, y float64) *Geom {
	coordSeq, err := this.CreateCoordSeq(1, 2)
	if err != nil {
		return nil
	}
	// coordSeq inherited by LineString
	coordSeq.SetXY(this, 0, x, y)
	geom, err := coordSeq.AsPoint(this)
	if err != nil {
		return nil
	}
	return geom
}

func (this *Geos) Polygon(exterior *Geom, interiors []*Geom) *Geom {
	if len(interiors) == 0 {
		geom := C.GEOSGeom_createPolygon_r(this.v, exterior.v, nil, C.uint(0))
		if geom == nil {
			return nil
		}
		err := C.GEOSNormalize_r(this.v, geom)
		if err != 0 {
			C.GEOSGeom_destroy(geom)
			return nil
		}
		return &Geom{geom}
	}

	interiorPtr := make([]*C.GEOSGeometry, len(interiors))
	for i, geom := range interiors {
		interiorPtr[i] = geom.v
	}
	geom := C.GEOSGeom_createPolygon_r(this.v, exterior.v, &interiorPtr[0], C.uint(len(interiors)))
	if geom == nil {
		return nil
	}
	err := C.GEOSNormalize_r(this.v, geom)
	if err != 0 {
		C.GEOSGeom_destroy(geom)
		return nil
	}
	return &Geom{geom}
}

func (this *Geos) MultiPolygon(polygons []*Geom) *Geom {
	if len(polygons) == 0 {
		return nil
	}
	polygonPtr := make([]*C.GEOSGeometry, len(polygons))
	for i, geom := range polygons {
		polygonPtr[i] = geom.v
	}
	geom := C.GEOSGeom_createCollection_r(this.v, C.GEOS_MULTIPOLYGON, &polygonPtr[0], C.uint(len(polygons)))
	if geom == nil {
		return nil
	}
	return &Geom{geom}
}
func (this *Geos) MultiLineString(lines []*Geom) *Geom {
	if len(lines) == 0 {
		return nil
	}
	linePtr := make([]*C.GEOSGeometry, len(lines))
	for i, geom := range lines {
		linePtr[i] = geom.v
	}
	geom := C.GEOSGeom_createCollection_r(this.v, C.GEOS_MULTILINESTRING, &linePtr[0], C.uint(len(lines)))
	if geom == nil {
		return nil
	}
	return &Geom{geom}
}

func (this *Geos) IsValid(geom *Geom) bool {
	if C.GEOSisValid_r(this.v, geom.v) == 1 {
		return true
	}
	return false
}

func (this *Geos) IsEmpty(geom *Geom) bool {
	if C.GEOSisEmpty_r(this.v, geom.v) == 1 {
		return true
	}
	return false
}

func (this *Geos) Type(geom *Geom) string {
	geomType := C.GEOSGeomType_r(this.v, geom.v)
	if geomType == nil {
		return "Unknown"
	}
	defer C.free(unsafe.Pointer(geomType))
	return C.GoString(geomType)
}

func (this *Geos) Equals(a, b *Geom) bool {
	result := C.GEOSEquals_r(this.v, a.v, b.v)
	if result == 1 {
		return true
	}
	return false
}

func (this *Geom) Area() float64 {
	var area C.double
	if ret := C.GEOSArea(this.v, &area); ret == 1 {
		return float64(area)
	} else {
		return 0
	}
}

func (this *Geom) Length() float64 {
	var length C.double
	if ret := C.GEOSLength(this.v, &length); ret == 1 {
		return float64(length)
	} else {
		return 0
	}
}

type Bounds struct {
	MinX float64
	MinY float64
	MaxX float64
	MaxY float64
}

var NilBounds = Bounds{1e20, 1e20, -1e20, -1e20}

func (this *Geom) Bounds() Bounds {
	geom := C.GEOSEnvelope(this.v)
	if geom == nil {
		return NilBounds
	}
	extRing := C.GEOSGetExteriorRing(geom)
	if extRing == nil {
		return NilBounds
	}
	cs := C.GEOSGeom_getCoordSeq(extRing)
	var csLen C.uint
	C.GEOSCoordSeq_getSize(cs, &csLen)
	minx := 1.e+20
	maxx := -1e+20
	miny := 1.e+20
	maxy := -1e+20
	var temp C.double
	for i := 0; i < int(csLen); i++ {
		C.GEOSCoordSeq_getX(cs, C.uint(i), &temp)
		x := float64(temp)
		if x < minx {
			minx = x
		}
		if x > maxx {
			maxx = x
		}
		C.GEOSCoordSeq_getY(cs, C.uint(i), &temp)
		y := float64(temp)
		if y < miny {
			miny = y
		}
		if y > maxy {
			maxy = y
		}
	}

	return Bounds{minx, miny, maxx, maxy}
}
<|endoftext|>


<commit_before>// skip

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

package life

// #cgo windows LDFLAGS: -lmsvcrt
// #include "life.h"
import "C"

import "unsafe"

func Run(gen, x, y int, a []int) {
	n := make([]int, x*y)
	for i := 0; i < gen; i++ {
		C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0])))
		copy(a, n)
	}
}

// Keep the channels visible from Go.
var chans [4]chan bool

//export GoStart
// Double return value is just for testing.
func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) {
	c := make(chan bool, int(C.MYCONST))
	go func() {
		C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n)
		c <- true
	}()
	chans[i] = c
	return int(i), int(i + 100)
}

//export GoWait
func GoWait(i C.int) {
	<-chans[i]
	chans[i] = nil
}
<commit_msg>misc/cgo/life: remove -lmsvcrt to fix windows/amd64 build         I guess this is the problem as I can't reproduce the failure.<commit_after>// skip

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

package life

// #include "life.h"
import "C"

import "unsafe"

func Run(gen, x, y int, a []int) {
	n := make([]int, x*y)
	for i := 0; i < gen; i++ {
		C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0])))
		copy(a, n)
	}
}

// Keep the channels visible from Go.
var chans [4]chan bool

//export GoStart
// Double return value is just for testing.
func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) {
	c := make(chan bool, int(C.MYCONST))
	go func() {
		C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n)
		c <- true
	}()
	chans[i] = c
	return int(i), int(i + 100)
}

//export GoWait
func GoWait(i C.int) {
	<-chans[i]
	chans[i] = nil
}
<|endoftext|>


<commit_before>package main

import (
  "log"
  "os"
  "reflect"
)

type FileStateOS struct {
  Vol    uint32  `json:"vol,omitempty"`
  IdxHi  uint32  `json:"idxhi,omitempty"`
  IdxLo  uint32  `json:"idxlo,omitempty"`
}

func (fs *FileStateOS) PopulateFileIds(info os.FileInfo) {
  // For information on the following, see Go source: src/pkg/os/types_windows.go
  // This is the only way we can get at the idxhi and idxlo
  // Unix it is much easier as syscall.Stat_t is exposed and os.FileInfo interface has a Sys() method to get a syscall.Stat_t
  // Unfortunately, the relevant Windows information is in a private struct so we have to dig inside

  // NOTE: This WILL be prone to break if Go source changes, but I'd rather just fix it if it does or make it fail gracefully

  // info is os.FileInfo which is an interface to a
  // - *os.fileStat (holding methods) which is a pointer to a
  // - os.fileStat (holding data)

  // Ensure that the numbers are loaded by calling os.SameFile
  // os.SameFile will call sameFile (types_windows.go) which will call *os.fileStat's loadFileId
  // Reflection panics if we try to call loadFileId directly as its a hidden method; regardless this is much safer and more reliable
  os.SameFile(info, info)

  // If any of the following fails, report the library has changed and recover and return 0s
  defer func() {
    if r := recover(); r != nil {
      log.Printf("WARNING: File rotations that occur while LogStash Forwarder is not running will NOT be detected due to an incompatible change to the Go library used for compiling. This is a bug, please report it.\n")
      fs.Vol = 0
      fs.IdxHi = 0
      fs.IdxLo = 0
    }
  }()

  // Following makes fstat hold os.fileStat
  fstat := reflect.ValueOf(info).Elem().Elem()

  // To get the data, we need the os.fileStat that fstat points to, so one more Elem()
  fs.Vol = uint32(fstat.FieldByName("vol").Uint())
  fs.IdxHi = uint32(fstat.FieldByName("idxhi").Uint())
  fs.IdxLo = uint32(fstat.FieldByName("idxlo").Uint())
}

func (fs *FileStateOS) SameAs(info os.FileInfo) bool {
  state := &FileStateOS{}
  state.PopulateFileIds(info)
  return (fs.Vol == state.Vol && fs.IdxHi == state.IdxHi && fs.IdxLo == state.IdxLo)
}
<commit_msg>Fix rotation on Windows that regressed when we switched to non-pointer os.FileInfo<commit_after>package main

import (
  "log"
  "os"
  "reflect"
)

type FileStateOS struct {
  Vol    uint32  `json:"vol,omitempty"`
  IdxHi  uint32  `json:"idxhi,omitempty"`
  IdxLo  uint32  `json:"idxlo,omitempty"`
}

func (fs *FileStateOS) PopulateFileIds(info os.FileInfo) {
  // For information on the following, see Go source: src/pkg/os/types_windows.go
  // This is the only way we can get at the idxhi and idxlo
  // Unix it is much easier as syscall.Stat_t is exposed and os.FileInfo interface has a Sys() method to get a syscall.Stat_t
  // Unfortunately, the relevant Windows information is in a private struct so we have to dig inside

  // NOTE: This WILL be prone to break if Go source changes, but I'd rather just fix it if it does or make it fail gracefully

  // info is os.FileInfo which is an interface to a
  // - *os.fileStat (holding methods) which is a pointer to a
  // - os.fileStat (holding data)
  // ValueOf will pick up the interface contents immediately, so we need a single Elem()

  // Ensure that the numbers are loaded by calling os.SameFile
  // os.SameFile will call sameFile (types_windows.go) which will call *os.fileStat's loadFileId
  // Reflection panics if we try to call loadFileId directly as its a hidden method; regardless this is much safer and more reliable
  os.SameFile(info, info)

  // If any of the following fails, report the library has changed and recover and return 0s
  defer func() {
    if r := recover(); r != nil {
      log.Printf("WARNING: File rotations that occur while LogStash Forwarder is not running will NOT be detected due to an incompatible change to the Go library used for compiling. This is a bug, please report it.\n")
      fs.Vol = 0
      fs.IdxHi = 0
      fs.IdxLo = 0
    }
  }()

  // Following makes fstat hold os.fileStat
  fstat := reflect.ValueOf(info).Elem()

  // To get the data, we need the os.fileStat that fstat points to, so one more Elem()
  fs.Vol = uint32(fstat.FieldByName("vol").Uint())
  fs.IdxHi = uint32(fstat.FieldByName("idxhi").Uint())
  fs.IdxLo = uint32(fstat.FieldByName("idxlo").Uint())
}

func (fs *FileStateOS) SameAs(info os.FileInfo) bool {
  state := &FileStateOS{}
  state.PopulateFileIds(info)
  return (fs.Vol == state.Vol && fs.IdxHi == state.IdxHi && fs.IdxLo == state.IdxLo)
}
<|endoftext|>


<commit_before>// go-board-money
package main

import (
	//	"flag"
	"fmt"
	"go-board-money/pick"
	//	"go-bot-news/pkg/html"
	//	"io"
	"io/ioutil"
	//	"log"
	"net/http"
	//	"os"
	"strconv"
	"strings"
	//	"time"

	"golang.org/x/net/html/charset"
)

type Kurs struct {
	namebank string  // название банка
	valuta   string  // название валюты
	pokupka  float64 // покупка валюты (покупает банк)
	prodaja  float64 // продажа валюты  (продает банк)
}

//удаление пустых или строк в которых только пробелы
func delspace(ss []string) []string {
	res := make([]string, 0)
	for _, s := range ss {
		if strings.TrimSpace(s) != "" {
			res = append(res, strings.TrimSpace(s))
		}
	}
	return res
}

func convstrtofloat(s string) float64 {
	res, _ := strconv.ParseFloat(strings.Replace(s, ",", ".", 1), 64)
	return res
}

//получение страницы из урла url
func gethtmlpage(url string) []byte {
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("HTTP error:", err)
		panic("HTTP error")
	}
	defer resp.Body.Close()
	// вот здесь и начинается самое интересное
	utf8, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		fmt.Println("Encoding error:", err)
		panic("Encoding error")
	}
	body, err := ioutil.ReadAll(utf8)
	if err != nil {
		fmt.Println("IO error:", err)
		panic("IO error")
	}
	return body
}

// вывод на печать массива строк
func printarray(s []string) {
	for i := 0; i < len(s); i++ {
		fmt.Println(s[i])
	}
	return
}

func printarraykurs(s []Kurs) {
	//	fmt.Println("BANK", "VALUTA", "POKUPKA", "PRODAJA")
	for _, v := range s {
		fmt.Println(v.namebank, v.valuta, v.pokupka, v.prodaja)
	}
	return
}

//парсер валют со сбербанка
func ParserValutaSbrf(url string) []Kurs {

	kursvaluta := make([]Kurs, 0)

	if url == "" {
		return kursvaluta
	}

	body := gethtmlpage(url)
	shtml := string(body)
	//	fmt.Println(shtml)

	// выделяем данные из таблицы
	stable, _ := pick.PickText(&pick.Option{
		&shtml,
		"table",
		&pick.Attr{
			"class",
			"table3_eggs4",
		},
	})

	stable = delspace(stable)
	//	fmt.Println(stable)

	kursvaluta = append(kursvaluta, Kurs{namebank: "SBRF", valuta: "USD"})
	kursvaluta = append(kursvaluta, Kurs{namebank: "SBRF", valuta: "EUR"})
	if len(stable) >= 6 {
		// USD
		kursvaluta[0].pokupka = convstrtofloat(stable[2])
		kursvaluta[0].prodaja = convstrtofloat(stable[3])
		// EUR
		kursvaluta[1].pokupka = convstrtofloat(stable[4])
		kursvaluta[1].prodaja = convstrtofloat(stable[5])
	} else {
		fmt.Println("Error parse ParserSbrf ")
		fmt.Println(stable)
	}

	return kursvaluta
}

//парсер валют с ак барс банка
func ParserValutaAkBars(url string) []Kurs {

	kursvaluta := make([]Kurs, 0)

	if url == "" {
		return kursvaluta
	}

	body := gethtmlpage(url)
	shtml := string(body)
	//	fmt.Println(shtml)

	// выделяем данные из таблицы
	stable, _ := pick.PickText(&pick.Option{
		&shtml,
		"table",
		&pick.Attr{
			"class",
			"tableDesc",
		},
	})

	stable = delspace(stable)
	//	fmt.Println(stable)

	kursvaluta = append(kursvaluta, Kurs{namebank: "AKBARS", valuta: "USD"})
	kursvaluta = append(kursvaluta, Kurs{namebank: "AKBARS", valuta: "EUR"})
	if len(stable) >= 14 {
		//		// USD
		kursvaluta[0].pokupka = convstrtofloat(stable[3])
		kursvaluta[0].prodaja = convstrtofloat(stable[6])
		// EUR
		kursvaluta[1].pokupka = convstrtofloat(stable[10])
		kursvaluta[1].prodaja = convstrtofloat(stable[13])
	} else {
		fmt.Println("Error parse ParserAkBars ")
		fmt.Println(stable)
	}

	return kursvaluta
}

func main() {
	//	var vkurs Kurs
	board_valuta := make([]Kurs, 0)

	fmt.Println("Start parser")

	vkurs := ParserValutaSbrf("http://data.sberbank.ru/tatarstan/ru/quotes/currencies/?base=beta")
	board_valuta = append(board_valuta, vkurs[0])
	board_valuta = append(board_valuta, vkurs[1])

	vkurs = ParserValutaAkBars("https://www.akbars.ru/")
	board_valuta = append(board_valuta, vkurs[0])
	board_valuta = append(board_valuta, vkurs[1])

	printarraykurs(board_valuta)

	fmt.Println("End parser")

}
<commit_msg>add parse valuta tfb<commit_after>// go-board-money
package main

import (
	//	"flag"
	"fmt"
	"go-board-money/pick"
	//	"go-bot-news/pkg/html"
	//	"io"
	"io/ioutil"
	//	"log"
	"net/http"
	//	"os"
	"strconv"
	"strings"
	//	"time"

	"golang.org/x/net/html/charset"
)

type Kurs struct {
	namebank string  // название банка
	valuta   string  // название валюты
	pokupka  float64 // покупка валюты (покупает банк)
	prodaja  float64 // продажа валюты  (продает банк)
}

//удаление пустых или строк в которых только пробелы
func delspace(ss []string) []string {
	res := make([]string, 0)
	for _, s := range ss {
		if strings.TrimSpace(s) != "" {
			res = append(res, strings.TrimSpace(s))
		}
	}
	return res
}

func convstrtofloat(s string) float64 {
	res, _ := strconv.ParseFloat(strings.Replace(s, ",", ".", 1), 64)
	return res
}

//получение страницы из урла url
func gethtmlpage(url string) []byte {
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("HTTP error:", err)
		panic("HTTP error")
	}
	defer resp.Body.Close()
	// вот здесь и начинается самое интересное
	utf8, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		fmt.Println("Encoding error:", err)
		panic("Encoding error")
	}
	body, err := ioutil.ReadAll(utf8)
	if err != nil {
		fmt.Println("IO error:", err)
		panic("IO error")
	}
	return body
}

// вывод на печать массива строк
func printarray(s []string) {
	for i := 0; i < len(s); i++ {
		fmt.Println(s[i])
	}
	return
}

func printarraykurs(s []Kurs) {
	//	fmt.Println("BANK", "VALUTA", "POKUPKA", "PRODAJA")
	for _, v := range s {
		fmt.Println(v.namebank, v.valuta, v.pokupka, v.prodaja)
	}
	return
}

//парсер валют со сбербанка
func ParserValutaSbrf(url string) []Kurs {

	kursvaluta := make([]Kurs, 0)

	if url == "" {
		return kursvaluta
	}

	body := gethtmlpage(url)
	shtml := string(body)
	//	fmt.Println(shtml)

	// выделяем данные из таблицы
	stable, _ := pick.PickText(&pick.Option{
		&shtml,
		"table",
		&pick.Attr{
			"class",
			"table3_eggs4",
		},
	})

	stable = delspace(stable)
	//	fmt.Println(stable)

	kursvaluta = append(kursvaluta, Kurs{namebank: "SBRF", valuta: "USD"})
	kursvaluta = append(kursvaluta, Kurs{namebank: "SBRF", valuta: "EUR"})
	if len(stable) >= 6 {
		// USD
		kursvaluta[0].pokupka = convstrtofloat(stable[2])
		kursvaluta[0].prodaja = convstrtofloat(stable[3])
		// EUR
		kursvaluta[1].pokupka = convstrtofloat(stable[4])
		kursvaluta[1].prodaja = convstrtofloat(stable[5])
	} else {
		fmt.Println("Error parse ParserSbrf ")
		fmt.Println(stable)
	}

	return kursvaluta
}

//парсер валют с ак барс банка
func ParserValutaAkBars(url string) []Kurs {

	kursvaluta := make([]Kurs, 0)

	if url == "" {
		return kursvaluta
	}

	body := gethtmlpage(url)
	shtml := string(body)
	//	fmt.Println(shtml)

	// выделяем данные из таблицы
	stable, _ := pick.PickText(&pick.Option{
		&shtml,
		"table",
		&pick.Attr{
			"class",
			"tableDesc",
		},
	})

	stable = delspace(stable)
	//	fmt.Println(stable)

	kursvaluta = append(kursvaluta, Kurs{namebank: "AKBARS", valuta: "USD"})
	kursvaluta = append(kursvaluta, Kurs{namebank: "AKBARS", valuta: "EUR"})
	if len(stable) >= 14 {
		//		// USD
		kursvaluta[0].pokupka = convstrtofloat(stable[3])
		kursvaluta[0].prodaja = convstrtofloat(stable[6])
		// EUR
		kursvaluta[1].pokupka = convstrtofloat(stable[10])
		kursvaluta[1].prodaja = convstrtofloat(stable[13])
	} else {
		fmt.Println("Error parse ParserAkBars ")
		fmt.Println(stable)
	}

	return kursvaluta
}

//парсер валют с Татфондбанка банка
func ParserValutaTfb(url string) []Kurs {

	kursvaluta := make([]Kurs, 0)

	if url == "" {
		return kursvaluta
	}

	body := gethtmlpage(url)
	shtml := string(body)
	//	fmt.Println(shtml)

	// выделяем данные из таблицы
	stable, _ := pick.PickText(&pick.Option{
		&shtml,
		"tr",
		&pick.Attr{
			"class",
			"usd",
		},
	})

	stable2, _ := pick.PickText(&pick.Option{
		&shtml,
		"tr",
		&pick.Attr{
			"class",
			"euro",
		},
	})

	stable = delspace(stable)
	stable2 = delspace(stable2)
	//	fmt.Println(stable2)

	kursvaluta = append(kursvaluta, Kurs{namebank: "TFB", valuta: "USD"})
	kursvaluta = append(kursvaluta, Kurs{namebank: "TFB", valuta: "EUR"})
	if (len(stable) >= 3) && (len(stable2) >= 3) {
		//		// USD
		kursvaluta[0].pokupka = convstrtofloat(stable[1])
		kursvaluta[0].prodaja = convstrtofloat(stable[2])
		//		// EUR
		kursvaluta[1].pokupka = convstrtofloat(stable2[1])
		kursvaluta[1].prodaja = convstrtofloat(stable2[2])
	} else {
		fmt.Println("Error parse ParserAkBars ")
		fmt.Println("stable = ", stable)
		fmt.Println("stable2 = ", stable2)
	}

	return kursvaluta
}

func main() {
	//	var vkurs Kurs
	board_valuta := make([]Kurs, 0)

	fmt.Println("Start parser")

	vkurs := ParserValutaSbrf("http://data.sberbank.ru/tatarstan/ru/quotes/currencies/?base=beta")
	board_valuta = append(board_valuta, vkurs[0])
	board_valuta = append(board_valuta, vkurs[1])

	vkurs = ParserValutaAkBars("https://www.akbars.ru/")
	board_valuta = append(board_valuta, vkurs[0])
	board_valuta = append(board_valuta, vkurs[1])

	vkurs = ParserValutaTfb("http://tfb.ru/")
	board_valuta = append(board_valuta, vkurs[0])
	board_valuta = append(board_valuta, vkurs[1])

	printarraykurs(board_valuta)

	fmt.Println("End parser")

}
<|endoftext|>


<commit_before>package goanna

import (
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"reflect"
)

type ControllerFactoryFunc func() ControllerInterface

// ControllerHandler is a http.Handler for handling incoming requests
// and despatching to controllers
type ControllerHandler struct {
	factory    ControllerFactoryFunc
	methodName string
}

// ServeHTTP handles a http request
func (handler ControllerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	handler.getResponse(r).Send(w)
}

// getResponse executes the specified controller's method using reflection
// and returns the response object
func (handler ControllerHandler) getResponse(r *http.Request) Response {
	controller := handler.factory()
	controller.SetRequest(r)
	controller.Init()
	rController := reflect.ValueOf(controller)
	method := rController.MethodByName(handler.methodName)

	// get args from gorilla mux
	var args []reflect.Value
	for _, val := range mux.Vars(r) {
		args = append(args, reflect.ValueOf(val))
	}

	// make sure number of args matches the controller method
	expected := len(args)
	actual := method.Type().NumIn()
	if expected != actual {
		panic(fmt.Sprintf("Method '%s' has %d args, expected %d", handler.methodName, actual, expected))
	}

	out := method.Call(args)
	if out[0].IsNil() {
		panic("Response from controller was nil")
	}

	resp := out[0].Interface().(Response)
	if resp == nil {
		panic("Response from controller was not Response interface")
	}

	controller.Session().WriteToResponse(resp)
	return resp
}

// isValid checks that the controller and method specifies
// will sucessfully execute if getResponse is called on it
func (handler ControllerHandler) isValid() bool {
	controller := handler.factory()
	rController := reflect.ValueOf(controller)
	method := rController.MethodByName(handler.methodName)
	if (method == reflect.Value{}) {
		panic("No such method: " + handler.methodName)
	}
	typeOfMethod := method.Type()

	var r *Response
	responseType := reflect.TypeOf(r).Elem()

	return (method.Kind() == reflect.Func) &&
		(typeOfMethod.NumMethod() == 0) &&
		(typeOfMethod.NumOut() == 1) &&
		typeOfMethod.Out(0) == responseType

}

// NewHandler creates a ControllerHandler from the factory and methodName
func NewHandler(factory ControllerFactoryFunc, methodName string) ControllerHandler {
	handler := ControllerHandler{factory: factory, methodName: methodName}
	if !handler.isValid() {
		panic("Invalid handler: " + methodName)
	}
	return handler
}
<commit_msg>Handler for using goanna request and response objects<commit_after>package goanna

import (
	"fmt"
	"net/http"
	"reflect"

	"github.com/gorilla/mux"
)

type ControllerFactoryFunc func() ControllerInterface

// ControllerHandler is a http.Handler for handling incoming requests
// and despatching to controllers
type ControllerHandler struct {
	factory    ControllerFactoryFunc
	methodName string
}

// ServeHTTP handles a http request
func (handler ControllerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	handler.getResponse(r).Send(w)
}

// getResponse executes the specified controller's method using reflection
// and returns the response object
func (handler ControllerHandler) getResponse(r *http.Request) Response {
	controller := handler.factory()
	controller.SetRequest(r)
	controller.Init()
	rController := reflect.ValueOf(controller)
	method := rController.MethodByName(handler.methodName)

	// get args from gorilla mux
	var args []reflect.Value
	for _, val := range mux.Vars(r) {
		args = append(args, reflect.ValueOf(val))
	}

	// make sure number of args matches the controller method
	expected := len(args)
	actual := method.Type().NumIn()
	if expected != actual {
		panic(fmt.Sprintf("Method '%s' has %d args, expected %d", handler.methodName, actual, expected))
	}

	out := method.Call(args)
	if out[0].IsNil() {
		panic("Response from controller was nil")
	}

	resp := out[0].Interface().(Response)
	if resp == nil {
		panic("Response from controller was not Response interface")
	}

	controller.Session().WriteToResponse(resp)
	return resp
}

// isValid checks that the controller and method specifies
// will sucessfully execute if getResponse is called on it
func (handler ControllerHandler) isValid() bool {
	controller := handler.factory()
	rController := reflect.ValueOf(controller)
	method := rController.MethodByName(handler.methodName)
	if (method == reflect.Value{}) {
		panic("No such method: " + handler.methodName)
	}
	typeOfMethod := method.Type()

	var r *Response
	responseType := reflect.TypeOf(r).Elem()

	return (method.Kind() == reflect.Func) &&
		(typeOfMethod.NumMethod() == 0) &&
		(typeOfMethod.NumOut() == 1) &&
		typeOfMethod.Out(0) == responseType

}

// NewHandler creates a ControllerHandler from the factory and methodName
func NewHandler(factory ControllerFactoryFunc, methodName string) ControllerHandler {
	handler := ControllerHandler{factory: factory, methodName: methodName}
	if !handler.isValid() {
		panic("Invalid handler: " + methodName)
	}
	return handler
}

// GoannaHandlerFunc is a function type that can be handled by
// GoannaHandler
type GoannaHandlerFunc func(r *Request) Response

// Handler handles functions of type GoannaHandlerFunc
func Handler(gf GoannaHandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		gr := &Request{Request: r}
		gf(gr).Send(w)
	}
}
<|endoftext|>