<commit_before>package kitsu

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
)

var (
	// client is the kitsu client used for these tests.
	client *Client

	// server is a test HTTP server that is being started on each test with
	// setup() to provide mock API responses.
	server *httptest.Server

	// mux is the HTTP request multiplexer that the test HTTP server uses.
	mux *http.ServeMux
)

func setup() {
	// Starting new test server with mux as its multiplexer.
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	// Configuring kitsu.Client to use the test HTTP server URL.
	client = NewClient(nil)
	client.BaseURL, _ = url.Parse(server.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	server.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

type values map[string]string

func testFormValues(t *testing.T, r *http.Request, values values) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}

	if err := r.ParseForm(); err != nil {
		t.Error("ParseForm returned error:", err)
	}
	if got := r.Form; !reflect.DeepEqual(got, want) {
		t.Errorf("Request parameters: \n%v, want \n%v", got, want)
	}
}

func testHeader(t *testing.T, r *http.Request, header string, want string) {
	if got := r.Header.Get(header); got != want {
		t.Errorf("Header.Get(%q) returned %q, want %q", header, got, want)
	}
}

func TestNewClient(t *testing.T) {
	c := NewClient(nil)

	if got, want := c.BaseURL.String(), defaultBaseURL; got != want {
		t.Errorf("NewClient BaseURL is %v, want %v", got, want)
	}
}

func TestClient_NewRequest(t *testing.T) {
	c := NewClient(nil)

	inURL, outURL := "/foo", defaultBaseURL+"foo"
	inBody, outBody := &struct{ Foo string }{Foo: "bar"}, `{"Foo":"bar"}`+"\n"
	req, _ := c.NewRequest("GET", inURL, inBody)

	// Test that the client's base URL is added to the endpoint.
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %q, want %q", inURL, got, want)
	}

	// Test that body gets encoded to JSON.
	body, _ := ioutil.ReadAll(req.Body)
	if got, want := string(body), outBody; got != want {
		t.Errorf("NewRequest(%#v) Body is \n%q, want \n%q", inBody, got, want)
	}

	// Test that the correct Content-Type gets added.
	if got, want := req.Header.Get("Content-Type"), defaultMediaType; got != want {
		t.Errorf("NewRequest() Content-Type is %q, want %q", got, want)
	}
}

func TestClient_NewRequest_badURL(t *testing.T) {
	c := NewClient(nil)
	inURL := ":"
	_, err := c.NewRequest("GET", inURL, nil)
	if err == nil {
		t.Errorf("NewRequest(%q) should return parse err", inURL)
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

func TestClient_NewRequest_badBody(t *testing.T) {
	c := NewClient(nil)

	type Foo struct {
		Bar map[interface{}]interface{}
	}
	inBody := &Foo{}
	_, err := c.NewRequest("GET", "/", inBody)

	if err == nil {
		t.Errorf("NewRequest(%#v) should return err", inBody)
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected JSON Unsupported type error, got %#v.", err)
	}
}

func TestClient_NewRequest_emptyBody(t *testing.T) {
	c := NewClient(nil)
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest with empty body returned error: %v", err)
	}
	if req.Body != nil {
		t.Fatalf("NewRequest with empty body should construct request with nil Body.")
	}
}

func TestClient_Do(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		Bar string `jsonapi:"primary,foo"`
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"data":{"id":"foobar","type":"foo"}}`)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	got := new(foo)
	_, _ = client.Do(req, got)

	want := &foo{Bar: "foobar"}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Response body = %v, want %v", got, want)
	}
}

func TestClient_Do_httpError(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, `{"errors":[{"status":"400","title":"Bad Request"}]}`, http.StatusBadRequest)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)
	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

func TestClient_Do_redirectLoop(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected URL error, got %#v.", err)
	}
}

func TestErrorResponse_Error(t *testing.T) {
	resp := &http.Response{Request: &http.Request{}}
	err := ErrorResponse{Response: resp}
	if err.Error() == "" {
		t.Errorf("Expected non-empty ErrorResponse.Error()")
	}
}

func TestError_Error(t *testing.T) {
	err := Error{}
	if err.Error() == "" {
		t.Errorf("Expected non-empty Error.Error()")
	}
}
<commit_msg>Modify kitsu tests for simplified Do<commit_after>package kitsu

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
)

var (
	// client is the kitsu client used for these tests.
	client *Client

	// server is a test HTTP server that is being started on each test with
	// setup() to provide mock API responses.
	server *httptest.Server

	// mux is the HTTP request multiplexer that the test HTTP server uses.
	mux *http.ServeMux
)

func setup() {
	// Starting new test server with mux as its multiplexer.
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	// Configuring kitsu.Client to use the test HTTP server URL.
	client = NewClient(nil)
	client.BaseURL, _ = url.Parse(server.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	server.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

type values map[string]string

func testFormValues(t *testing.T, r *http.Request, values values) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}

	if err := r.ParseForm(); err != nil {
		t.Error("ParseForm returned error:", err)
	}
	if got := r.Form; !reflect.DeepEqual(got, want) {
		t.Errorf("Request parameters: \n%v, want \n%v", got, want)
	}
}

func testHeader(t *testing.T, r *http.Request, header string, want string) {
	if got := r.Header.Get(header); got != want {
		t.Errorf("Header.Get(%q) returned %q, want %q", header, got, want)
	}
}

func TestNewClient(t *testing.T) {
	c := NewClient(nil)

	if got, want := c.BaseURL.String(), defaultBaseURL; got != want {
		t.Errorf("NewClient BaseURL is %v, want %v", got, want)
	}
}

func TestClient_NewRequest(t *testing.T) {
	c := NewClient(nil)

	inURL, outURL := "/foo", defaultBaseURL+"foo"
	req, err := c.NewRequest("GET", inURL, nil)
	if err != nil {
		t.Fatalf("NewRequest(%q) returned err: %v", inURL, err)
	}

	// Test that the client's base URL is added to the endpoint.
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %q, want %q", inURL, got, want)
	}

}

func TestClient_NewRequest_encodeUnknownType(t *testing.T) {
	c := NewClient(nil)

	body := &struct{ Foo string }{Foo: "bar"}
	_, err := c.NewRequest("GET", "/foo", body)
	if err == nil {
		t.Errorf("NewRequest(%#v) expected to return err", body)
	}
}

func TestClient_NewRequest_encode(t *testing.T) {
	var tests = []struct {
		in  interface{}
		out string
	}{
		{
			&Anime{ID: "1"},
			`{"data":{"type":"anime","id":"1"}}` + "\n",
		},
	}
	c := NewClient(nil)
	for _, tt := range tests {
		req, err := c.NewRequest("GET", "/foo", tt.in)
		if err != nil {
			t.Fatalf("NewRequest(%#v) returned err: %v", tt.in, err)
		}

		// Test that body gets encoded to JSON API.
		body, _ := ioutil.ReadAll(req.Body)
		if got, want := string(body), tt.out; got != want {
			t.Errorf("NewRequest(%#v) Body \nhave: %q\nwant: %q", tt.in, got, want)
		}

		// Test that the correct Content-Type gets added.
		if got, want := req.Header.Get("Content-Type"), defaultMediaType; got != want {
			t.Errorf("NewRequest() Content-Type is %q, want %q", got, want)
		}
	}
}

func TestClient_NewRequest_badURL(t *testing.T) {
	c := NewClient(nil)
	inURL := ":"
	_, err := c.NewRequest("GET", inURL, nil)
	if err == nil {
		t.Errorf("NewRequest(%q) should return parse err", inURL)
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

//func TestClient_NewRequest_badBody(t *testing.T) {
//	c := NewClient(nil)
//
//	type Foo struct {
//		Bar map[interface{}]interface{}
//	}
//	inBody := &Foo{}
//	_, err := c.NewRequest("GET", "/", inBody)
//
//	if err == nil {
//		t.Errorf("NewRequest(%#v) should return err", inBody)
//	}
//	if err, ok := err.(*json.UnsupportedTypeError); !ok {
//		t.Errorf("Expected JSON Unsupported type error, got %#v.", err)
//	}
//}

func TestClient_NewRequest_emptyBody(t *testing.T) {
	c := NewClient(nil)
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest with empty body returned error: %v", err)
	}
	if req.Body != nil {
		t.Fatalf("NewRequest with empty body should construct request with nil Body.")
	}
}

func TestClient_Do(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		Bar string `jsonapi:"primary,foo"`
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"data":{"id":"foobar","type":"foo"}}`)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	got := new(foo)
	_, _ = client.DoOne(req, got)

	want := &foo{Bar: "foobar"}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Response body = %v, want %v", got, want)
	}
}

func TestClient_Do_httpError(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, `{"errors":[{"status":"400","title":"Bad Request"}]}`, http.StatusBadRequest)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.DoOne(req, nil)
	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

func TestClient_Do_redirectLoop(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.DoOne(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected URL error, got %#v.", err)
	}
}

func TestErrorResponse_Error(t *testing.T) {
	resp := &http.Response{Request: &http.Request{}}
	err := ErrorResponse{Response: resp}
	if err.Error() == "" {
		t.Errorf("Expected non-empty ErrorResponse.Error()")
	}
}

func TestError_Error(t *testing.T) {
	err := Error{}
	if err.Error() == "" {
		t.Errorf("Expected non-empty Error.Error()")
	}
}
<|endoftext|>


<commit_before>package xmlrpc

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/rpc"
	"net/url"
	"sync"
)

type Client struct {
	*rpc.Client
}

// clientCodec is rpc.ClientCodec interface implementation.
type clientCodec struct {
	// url presents url of xmlrpc service
	url *url.URL

	// httpClient works with HTTP protocol
	httpClient *http.Client

	// cookies stores cookies received on last request
	cookies http.CookieJar

	// responses presents map of active requests. It is required to return request id, that
	// rpc.Client can mark them as done.
	responses map[uint64]*http.Response
	mutex     sync.Mutex

	response Response

	// ready presents channel, that is used to link request and it`s response.
	ready chan uint64

	// close notifies codec is closed.
	close chan uint64
}

func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
	httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)

	if codec.cookies != nil {
		for _, cookie := range codec.cookies.Cookies(codec.url) {
			httpRequest.AddCookie(cookie)
		}
	}

	if err != nil {
		return err
	}

	var httpResponse *http.Response
	httpResponse, err = codec.httpClient.Do(httpRequest)

	if err != nil {
		return err
	}

	if codec.cookies != nil {
		codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
	}

	codec.mutex.Lock()
	codec.responses[request.Seq] = httpResponse
	codec.mutex.Unlock()

	codec.ready <- request.Seq

	return nil
}

func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
	var seq uint64
	select {
	case seq = <-codec.ready:
	case <-codec.close:
		return errors.New("codec is closed")
	}
	response.Seq = seq

	codec.mutex.Lock()
	httpResponse := codec.responses[seq]
	delete(codec.responses, seq)
	codec.mutex.Unlock()

	defer httpResponse.Body.Close()

	if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
		response.Error = fmt.Sprintf("request error: bad status code - %d", httpResponse.StatusCode)
		return nil
	}

	body, err := ioutil.ReadAll(httpResponse.Body)
	if err != nil {
		response.Error = err.Error()
		return nil
	}

	resp := Response(body)
	if err := resp.Err(); err != nil {
		response.Error = err.Error()
		return nil
	}

	codec.response = resp

	return nil
}

func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
	if v == nil {
		return nil
	}
	return codec.response.Unmarshal(v)
}

func (codec *clientCodec) Close() error {
	if transport, ok := codec.httpClient.Transport.(*http.Transport); ok {
		transport.CloseIdleConnections()
	}

	close(codec.close)

	return nil
}

// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
	if transport == nil {
		transport = http.DefaultTransport
	}

	httpClient := &http.Client{Transport: transport}

	jar, err := cookiejar.New(nil)

	if err != nil {
		return nil, err
	}

	u, err := url.Parse(requrl)

	if err != nil {
		return nil, err
	}

	codec := clientCodec{
		url:        u,
		httpClient: httpClient,
		close:      make(chan uint64),
		ready:      make(chan uint64),
		responses:  make(map[uint64]*http.Response),
		cookies:    jar,
	}

	return &Client{rpc.NewClientWithCodec(&codec)}, nil
}
<commit_msg>client: Handle NewRequest errors before adding cookies<commit_after>package xmlrpc

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/rpc"
	"net/url"
	"sync"
)

type Client struct {
	*rpc.Client
}

// clientCodec is rpc.ClientCodec interface implementation.
type clientCodec struct {
	// url presents url of xmlrpc service
	url *url.URL

	// httpClient works with HTTP protocol
	httpClient *http.Client

	// cookies stores cookies received on last request
	cookies http.CookieJar

	// responses presents map of active requests. It is required to return request id, that
	// rpc.Client can mark them as done.
	responses map[uint64]*http.Response
	mutex     sync.Mutex

	response Response

	// ready presents channel, that is used to link request and it`s response.
	ready chan uint64

	// close notifies codec is closed.
	close chan uint64
}

func (codec *clientCodec) WriteRequest(request *rpc.Request, args interface{}) (err error) {
	httpRequest, err := NewRequest(codec.url.String(), request.ServiceMethod, args)

	if err != nil {
		return err
	}

	if codec.cookies != nil {
		for _, cookie := range codec.cookies.Cookies(codec.url) {
			httpRequest.AddCookie(cookie)
		}
	}

	var httpResponse *http.Response
	httpResponse, err = codec.httpClient.Do(httpRequest)

	if err != nil {
		return err
	}

	if codec.cookies != nil {
		codec.cookies.SetCookies(codec.url, httpResponse.Cookies())
	}

	codec.mutex.Lock()
	codec.responses[request.Seq] = httpResponse
	codec.mutex.Unlock()

	codec.ready <- request.Seq

	return nil
}

func (codec *clientCodec) ReadResponseHeader(response *rpc.Response) (err error) {
	var seq uint64
	select {
	case seq = <-codec.ready:
	case <-codec.close:
		return errors.New("codec is closed")
	}
	response.Seq = seq

	codec.mutex.Lock()
	httpResponse := codec.responses[seq]
	delete(codec.responses, seq)
	codec.mutex.Unlock()

	defer httpResponse.Body.Close()

	if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
		response.Error = fmt.Sprintf("request error: bad status code - %d", httpResponse.StatusCode)
		return nil
	}

	body, err := ioutil.ReadAll(httpResponse.Body)
	if err != nil {
		response.Error = err.Error()
		return nil
	}

	resp := Response(body)
	if err := resp.Err(); err != nil {
		response.Error = err.Error()
		return nil
	}

	codec.response = resp

	return nil
}

func (codec *clientCodec) ReadResponseBody(v interface{}) (err error) {
	if v == nil {
		return nil
	}
	return codec.response.Unmarshal(v)
}

func (codec *clientCodec) Close() error {
	if transport, ok := codec.httpClient.Transport.(*http.Transport); ok {
		transport.CloseIdleConnections()
	}

	close(codec.close)

	return nil
}

// NewClient returns instance of rpc.Client object, that is used to send request to xmlrpc service.
func NewClient(requrl string, transport http.RoundTripper) (*Client, error) {
	if transport == nil {
		transport = http.DefaultTransport
	}

	httpClient := &http.Client{Transport: transport}

	jar, err := cookiejar.New(nil)

	if err != nil {
		return nil, err
	}

	u, err := url.Parse(requrl)

	if err != nil {
		return nil, err
	}

	codec := clientCodec{
		url:        u,
		httpClient: httpClient,
		close:      make(chan uint64),
		ready:      make(chan uint64),
		responses:  make(map[uint64]*http.Response),
		cookies:    jar,
	}

	return &Client{rpc.NewClientWithCodec(&codec)}, nil
}
<|endoftext|>


<commit_before>package mdns

import (
	"fmt"
	"log"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/go.net/ipv4"
	"github.com/hashicorp/go.net/ipv6"
	"github.com/miekg/dns"
)

// ServiceEntry is returned after we query for a service
type ServiceEntry struct {
	Name   string
	Host   string
	AddrV4 net.IP
	AddrV6 net.IP
	Port   int
	Info   string

	Addr net.IP // @Deprecated

	hasTXT bool
	sent   bool
}

// complete is used to check if we have all the info we need
func (s *ServiceEntry) complete() bool {
	return (s.AddrV4 != nil || s.AddrV6 != nil || s.Addr != nil) && s.Port != 0 && s.hasTXT
}

// QueryParam is used to customize how a Lookup is performed
type QueryParam struct {
	Service             string               // Service to lookup
	Domain              string               // Lookup domain, default "local"
	Timeout             time.Duration        // Lookup timeout, default 1 second
	Interface           *net.Interface       // Multicast interface to use
	Entries             chan<- *ServiceEntry // Entries Channel
	WantUnicastResponse bool                 // Unicast response desired, as per 5.4 in RFC
}

// DefaultParams is used to return a default set of QueryParam's
func DefaultParams(service string) *QueryParam {
	return &QueryParam{
		Service:             service,
		Domain:              "local",
		Timeout:             time.Second,
		Entries:             make(chan *ServiceEntry),
		WantUnicastResponse: false, // TODO(reddaly): Change this default.
	}
}

// Query looks up a given service, in a domain, waiting at most
// for a timeout before finishing the query. The results are streamed
// to a channel. Sends will not block, so clients should make sure to
// either read or buffer.
func Query(params *QueryParam) error {
	// Create a new client
	client, err := newClient()
	if err != nil {
		return err
	}
	defer client.Close()

	// Set the multicast interface
	if params.Interface != nil {
		if err := client.setInterface(params.Interface); err != nil {
			return err
		}
	}

	// Ensure defaults are set
	if params.Domain == "" {
		params.Domain = "local"
	}
	if params.Timeout == 0 {
		params.Timeout = time.Second
	}

	// Run the query
	return client.query(params)
}

// Lookup is the same as Query, however it uses all the default parameters
func Lookup(service string, entries chan<- *ServiceEntry) error {
	params := DefaultParams(service)
	params.Entries = entries
	return Query(params)
}

// Client provides a query interface that can be used to
// search for service providers using mDNS
type client struct {
	ipv4UnicastConn *net.UDPConn
	ipv6UnicastConn *net.UDPConn

	ipv4MulticastConn *net.UDPConn
	ipv6MulticastConn *net.UDPConn

	closed    bool
	closedCh  chan struct{} // TODO(reddaly): This doesn't appear to be used.
	closeLock sync.Mutex
}

// NewClient creates a new mdns Client that can be used to query
// for records
func newClient() (*client, error) {
	// TODO(reddaly): At least attempt to bind to the port required in the spec.
	// Create a IPv4 listener
	uconn4, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	uconn6, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if uconn4 == nil && uconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any unicast udp port")
	}

	mconn4, err := net.ListenMulticastUDP("udp4", nil, ipv4Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	mconn6, err := net.ListenMulticastUDP("udp6", nil, ipv6Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if mconn4 == nil && mconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any multicast udp port")
	}

	c := &client{
		ipv4MulticastConn: mconn4,
		ipv6MulticastConn: mconn6,
		ipv4UnicastConn:   uconn4,
		ipv6UnicastConn:   uconn6,
		closedCh:          make(chan struct{}),
	}
	return c, nil
}

// Close is used to cleanup the client
func (c *client) Close() error {
	c.closeLock.Lock()
	defer c.closeLock.Unlock()

	if c.closed {
		return nil
	}
	c.closed = true

	log.Printf("[INFO] mdns: Closing client %v", *c)
	close(c.closedCh)

	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.Close()
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.Close()
	}
	if c.ipv4MulticastConn != nil {
		c.ipv4MulticastConn.Close()
	}
	if c.ipv6MulticastConn != nil {
		c.ipv6MulticastConn.Close()
	}

	return nil
}

// setInterface is used to set the query interface, uses sytem
// default if not provided
func (c *client) setInterface(iface *net.Interface) error {
	p := ipv4.NewPacketConn(c.ipv4UnicastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 := ipv6.NewPacketConn(c.ipv6UnicastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	p = ipv4.NewPacketConn(c.ipv4MulticastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 = ipv6.NewPacketConn(c.ipv6MulticastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	return nil
}

// query is used to perform a lookup and stream results
func (c *client) query(params *QueryParam) error {
	// Create the service name
	serviceAddr := fmt.Sprintf("%s.%s.", trimDot(params.Service), trimDot(params.Domain))

	// Start listening for response packets
	msgCh := make(chan *dns.Msg, 32)
	go c.recv(c.ipv4UnicastConn, msgCh)
	go c.recv(c.ipv6UnicastConn, msgCh)
	go c.recv(c.ipv4MulticastConn, msgCh)
	go c.recv(c.ipv6MulticastConn, msgCh)

	// Send the query
	m := new(dns.Msg)
	m.SetQuestion(serviceAddr, dns.TypePTR)
	// RFC 6762, section 18.12.  Repurposing of Top Bit of qclass in Question
	// Section
	//
	// In the Question Section of a Multicast DNS query, the top bit of the qclass
	// field is used to indicate that unicast responses are preferred for this
	// particular question.  (See Section 5.4.)
	if params.WantUnicastResponse {
		m.Question[0].Qclass |= 1 << 15
	}
	m.RecursionDesired = false
	if err := c.sendQuery(m); err != nil {
		return err
	}

	// Map the in-progress responses
	inprogress := make(map[string]*ServiceEntry)

	// Listen until we reach the timeout
	finish := time.After(params.Timeout)
	for {
		select {
		case resp := <-msgCh:
			var inp *ServiceEntry
			for _, answer := range resp.Answer {
				// TODO(reddaly): Check that response corresponds to serviceAddr?
				switch rr := answer.(type) {
				case *dns.PTR:
					// Create new entry for this
					inp = ensureName(inprogress, rr.Ptr)

				case *dns.SRV:
					// Check for a target mismatch
					if rr.Target != rr.Hdr.Name {
						alias(inprogress, rr.Hdr.Name, rr.Target)
					}

					// Get the port
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Host = rr.Target
					inp.Port = int(rr.Port)

				case *dns.TXT:
					// Pull out the txt
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Info = strings.Join(rr.Txt, "|")
					inp.hasTXT = true

				case *dns.A:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.A // @Deprecated
					inp.AddrV4 = rr.A

				case *dns.AAAA:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.AAAA // @Deprecated
					inp.AddrV6 = rr.AAAA
				}
			}

			if inp == nil {
				continue
			}

			// Check if this entry is complete
			if inp.complete() {
				if inp.sent {
					continue
				}
				inp.sent = true
				select {
				case params.Entries <- inp:
				default:
				}
			} else {
				// Fire off a node specific query
				m := new(dns.Msg)
				m.SetQuestion(inp.Name, dns.TypePTR)
				m.RecursionDesired = false
				if err := c.sendQuery(m); err != nil {
					log.Printf("[ERR] mdns: Failed to query instance %s: %v", inp.Name, err)
				}
			}
		case <-finish:
			return nil
		}
	}
}

// sendQuery is used to multicast a query out
func (c *client) sendQuery(q *dns.Msg) error {
	buf, err := q.Pack()
	if err != nil {
		return err
	}
	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.WriteToUDP(buf, ipv4Addr)
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.WriteToUDP(buf, ipv6Addr)
	}
	return nil
}

// recv is used to receive until we get a shutdown
func (c *client) recv(l *net.UDPConn, msgCh chan *dns.Msg) {
	if l == nil {
		return
	}
	buf := make([]byte, 65536)
	for !c.closed {
		n, err := l.Read(buf)
		if err != nil {
			log.Printf("[ERR] mdns: Failed to read packet: %v", err)
			continue
		}
		msg := new(dns.Msg)
		if err := msg.Unpack(buf[:n]); err != nil {
			log.Printf("[ERR] mdns: Failed to unpack packet: %v", err)
			continue
		}
		select {
		case msgCh <- msg:
		case <-c.closedCh:
			return
		}
	}
}

// ensureName is used to ensure the named node is in progress
func ensureName(inprogress map[string]*ServiceEntry, name string) *ServiceEntry {
	if inp, ok := inprogress[name]; ok {
		return inp
	}
	inp := &ServiceEntry{
		Name: name,
	}
	inprogress[name] = inp
	return inp
}

// alias is used to setup an alias between two entries
func alias(inprogress map[string]*ServiceEntry, src, dst string) {
	srcEntry := ensureName(inprogress, src)
	inprogress[dst] = srcEntry
}
<commit_msg>Revert "Switch to fork of go.net"<commit_after>package mdns

import (
	"fmt"
	"log"
	"net"
	"strings"
	"sync"
	"time"

	"code.google.com/p/go.net/ipv4"
	"code.google.com/p/go.net/ipv6"
	"github.com/miekg/dns"
)

// ServiceEntry is returned after we query for a service
type ServiceEntry struct {
	Name   string
	Host   string
	AddrV4 net.IP
	AddrV6 net.IP
	Port   int
	Info   string

	Addr net.IP // @Deprecated

	hasTXT bool
	sent   bool
}

// complete is used to check if we have all the info we need
func (s *ServiceEntry) complete() bool {
	return (s.AddrV4 != nil || s.AddrV6 != nil || s.Addr != nil) && s.Port != 0 && s.hasTXT
}

// QueryParam is used to customize how a Lookup is performed
type QueryParam struct {
	Service             string               // Service to lookup
	Domain              string               // Lookup domain, default "local"
	Timeout             time.Duration        // Lookup timeout, default 1 second
	Interface           *net.Interface       // Multicast interface to use
	Entries             chan<- *ServiceEntry // Entries Channel
	WantUnicastResponse bool                 // Unicast response desired, as per 5.4 in RFC
}

// DefaultParams is used to return a default set of QueryParam's
func DefaultParams(service string) *QueryParam {
	return &QueryParam{
		Service:             service,
		Domain:              "local",
		Timeout:             time.Second,
		Entries:             make(chan *ServiceEntry),
		WantUnicastResponse: false, // TODO(reddaly): Change this default.
	}
}

// Query looks up a given service, in a domain, waiting at most
// for a timeout before finishing the query. The results are streamed
// to a channel. Sends will not block, so clients should make sure to
// either read or buffer.
func Query(params *QueryParam) error {
	// Create a new client
	client, err := newClient()
	if err != nil {
		return err
	}
	defer client.Close()

	// Set the multicast interface
	if params.Interface != nil {
		if err := client.setInterface(params.Interface); err != nil {
			return err
		}
	}

	// Ensure defaults are set
	if params.Domain == "" {
		params.Domain = "local"
	}
	if params.Timeout == 0 {
		params.Timeout = time.Second
	}

	// Run the query
	return client.query(params)
}

// Lookup is the same as Query, however it uses all the default parameters
func Lookup(service string, entries chan<- *ServiceEntry) error {
	params := DefaultParams(service)
	params.Entries = entries
	return Query(params)
}

// Client provides a query interface that can be used to
// search for service providers using mDNS
type client struct {
	ipv4UnicastConn *net.UDPConn
	ipv6UnicastConn *net.UDPConn

	ipv4MulticastConn *net.UDPConn
	ipv6MulticastConn *net.UDPConn

	closed    bool
	closedCh  chan struct{} // TODO(reddaly): This doesn't appear to be used.
	closeLock sync.Mutex
}

// NewClient creates a new mdns Client that can be used to query
// for records
func newClient() (*client, error) {
	// TODO(reddaly): At least attempt to bind to the port required in the spec.
	// Create a IPv4 listener
	uconn4, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	uconn6, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0})
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if uconn4 == nil && uconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any unicast udp port")
	}

	mconn4, err := net.ListenMulticastUDP("udp4", nil, ipv4Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
	}
	mconn6, err := net.ListenMulticastUDP("udp6", nil, ipv6Addr)
	if err != nil {
		log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
	}

	if mconn4 == nil && mconn6 == nil {
		return nil, fmt.Errorf("failed to bind to any multicast udp port")
	}

	c := &client{
		ipv4MulticastConn: mconn4,
		ipv6MulticastConn: mconn6,
		ipv4UnicastConn:   uconn4,
		ipv6UnicastConn:   uconn6,
		closedCh:          make(chan struct{}),
	}
	return c, nil
}

// Close is used to cleanup the client
func (c *client) Close() error {
	c.closeLock.Lock()
	defer c.closeLock.Unlock()

	if c.closed {
		return nil
	}
	c.closed = true

	log.Printf("[INFO] mdns: Closing client %v", *c)
	close(c.closedCh)

	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.Close()
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.Close()
	}
	if c.ipv4MulticastConn != nil {
		c.ipv4MulticastConn.Close()
	}
	if c.ipv6MulticastConn != nil {
		c.ipv6MulticastConn.Close()
	}

	return nil
}

// setInterface is used to set the query interface, uses sytem
// default if not provided
func (c *client) setInterface(iface *net.Interface) error {
	p := ipv4.NewPacketConn(c.ipv4UnicastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 := ipv6.NewPacketConn(c.ipv6UnicastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	p = ipv4.NewPacketConn(c.ipv4MulticastConn)
	if err := p.SetMulticastInterface(iface); err != nil {
		return err
	}
	p2 = ipv6.NewPacketConn(c.ipv6MulticastConn)
	if err := p2.SetMulticastInterface(iface); err != nil {
		return err
	}
	return nil
}

// query is used to perform a lookup and stream results
func (c *client) query(params *QueryParam) error {
	// Create the service name
	serviceAddr := fmt.Sprintf("%s.%s.", trimDot(params.Service), trimDot(params.Domain))

	// Start listening for response packets
	msgCh := make(chan *dns.Msg, 32)
	go c.recv(c.ipv4UnicastConn, msgCh)
	go c.recv(c.ipv6UnicastConn, msgCh)
	go c.recv(c.ipv4MulticastConn, msgCh)
	go c.recv(c.ipv6MulticastConn, msgCh)

	// Send the query
	m := new(dns.Msg)
	m.SetQuestion(serviceAddr, dns.TypePTR)
	// RFC 6762, section 18.12.  Repurposing of Top Bit of qclass in Question
	// Section
	//
	// In the Question Section of a Multicast DNS query, the top bit of the qclass
	// field is used to indicate that unicast responses are preferred for this
	// particular question.  (See Section 5.4.)
	if params.WantUnicastResponse {
		m.Question[0].Qclass |= 1 << 15
	}
	m.RecursionDesired = false
	if err := c.sendQuery(m); err != nil {
		return err
	}

	// Map the in-progress responses
	inprogress := make(map[string]*ServiceEntry)

	// Listen until we reach the timeout
	finish := time.After(params.Timeout)
	for {
		select {
		case resp := <-msgCh:
			var inp *ServiceEntry
			for _, answer := range resp.Answer {
				// TODO(reddaly): Check that response corresponds to serviceAddr?
				switch rr := answer.(type) {
				case *dns.PTR:
					// Create new entry for this
					inp = ensureName(inprogress, rr.Ptr)

				case *dns.SRV:
					// Check for a target mismatch
					if rr.Target != rr.Hdr.Name {
						alias(inprogress, rr.Hdr.Name, rr.Target)
					}

					// Get the port
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Host = rr.Target
					inp.Port = int(rr.Port)

				case *dns.TXT:
					// Pull out the txt
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Info = strings.Join(rr.Txt, "|")
					inp.hasTXT = true

				case *dns.A:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.A // @Deprecated
					inp.AddrV4 = rr.A

				case *dns.AAAA:
					// Pull out the IP
					inp = ensureName(inprogress, rr.Hdr.Name)
					inp.Addr = rr.AAAA // @Deprecated
					inp.AddrV6 = rr.AAAA
				}
			}

			if inp == nil {
				continue
			}

			// Check if this entry is complete
			if inp.complete() {
				if inp.sent {
					continue
				}
				inp.sent = true
				select {
				case params.Entries <- inp:
				default:
				}
			} else {
				// Fire off a node specific query
				m := new(dns.Msg)
				m.SetQuestion(inp.Name, dns.TypePTR)
				m.RecursionDesired = false
				if err := c.sendQuery(m); err != nil {
					log.Printf("[ERR] mdns: Failed to query instance %s: %v", inp.Name, err)
				}
			}
		case <-finish:
			return nil
		}
	}
}

// sendQuery is used to multicast a query out
func (c *client) sendQuery(q *dns.Msg) error {
	buf, err := q.Pack()
	if err != nil {
		return err
	}
	if c.ipv4UnicastConn != nil {
		c.ipv4UnicastConn.WriteToUDP(buf, ipv4Addr)
	}
	if c.ipv6UnicastConn != nil {
		c.ipv6UnicastConn.WriteToUDP(buf, ipv6Addr)
	}
	return nil
}

// recv is used to receive until we get a shutdown
func (c *client) recv(l *net.UDPConn, msgCh chan *dns.Msg) {
	if l == nil {
		return
	}
	buf := make([]byte, 65536)
	for !c.closed {
		n, err := l.Read(buf)
		if err != nil {
			log.Printf("[ERR] mdns: Failed to read packet: %v", err)
			continue
		}
		msg := new(dns.Msg)
		if err := msg.Unpack(buf[:n]); err != nil {
			log.Printf("[ERR] mdns: Failed to unpack packet: %v", err)
			continue
		}
		select {
		case msgCh <- msg:
		case <-c.closedCh:
			return
		}
	}
}

// ensureName is used to ensure the named node is in progress
func ensureName(inprogress map[string]*ServiceEntry, name string) *ServiceEntry {
	if inp, ok := inprogress[name]; ok {
		return inp
	}
	inp := &ServiceEntry{
		Name: name,
	}
	inprogress[name] = inp
	return inp
}

// alias is used to setup an alias between two entries
func alias(inprogress map[string]*ServiceEntry, src, dst string) {
	srcEntry := ensureName(inprogress, src)
	inprogress[dst] = srcEntry
}
<|endoftext|>


<commit_before>package golf

import (
	"compress/gzip"
	"compress/zlib"
	"errors"
	"io"
	"net"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

// Compression type to use for GELF messages that are sent
const (
	COMP_NONE = iota // No compression
	COMP_GZIP        // gzip compression
	COMP_ZLIB        // zlib compression
)

type Client struct {
	hostname string

	conn net.Conn

	queue      []*Message
	queueMutex sync.Mutex

	msgChan  chan *Message
	queueCtl chan int
	sendCtl  chan int

	config ClientConfig
}

// Configuration used when creating a server instance
type ClientConfig struct {
	ChunkSize   int // The data size for each chunk sent to the server
	Compression int // Compression to use for messagec.
}

/*
Create a new Client instance with the default values for ClientConfig:

 {
	ChunkSize: 1420,
	Compression: COMP_GZIP,
 }
*/
func NewClient() (*Client, error) {
	cc := ClientConfig{
		ChunkSize:   1420,
		Compression: COMP_GZIP,
	}
	return NewClientWithConfig(cc)
}

// Create a new Client instance with the given ClientConfig
func NewClientWithConfig(config ClientConfig) (*Client, error) {
	c := &Client{
		config: config,
		queue:  make([]*Message, 0),

		msgChan:  make(chan *Message, 500),
		queueCtl: make(chan int),
		sendCtl:  make(chan int),
	}

	host, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	c.hostname = host

	return c, nil
}

// Connect to a GELF server at the given URI.
func (c *Client) Dial(uri string) error {
	parsedUri, err := url.Parse(uri)
	if err != nil {
		return err
	}

	if !strings.Contains(parsedUri.Host, ":") {
		parsedUri.Host = parsedUri.Host + ":12201"
	}

	switch parsedUri.Scheme {
	case "udp":
	case "tcp":
	default:
		return errors.New("Unsupported scheme provided")
	}

	conn, err := net.Dial(parsedUri.Scheme, parsedUri.Host)
	if err != nil {
		return err
	}
	c.conn = conn

	go c.queueReceiver()
	go c.msgSender()

	return nil
}

// Close the connection to the server. This call will block until all the
// currently queued messages for the client are sent.
func (c *Client) Close() error {
	if c.conn == nil {
		// Already shut down so it doesn't need to run again
		return nil
	}

	// First quit the queue and wait for it to respond
	// that it's quit
	c.queueCtl <- 1
	for {
		quitVal := <-c.queueCtl
		if quitVal == 2 {
			break
		}
		c.queueCtl <- quitVal
	}

	// Then quit the sender and wait for it to respond
	// that it's quit
	c.sendCtl <- 1
	for {
		quitVal := <-c.sendCtl
		if quitVal == 2 {
			break
		}
		c.sendCtl <- quitVal
	}

	err := c.conn.Close()
	if err != nil {
		return err
	}
	c.conn = nil

	return nil
}

// Queue the given message at the end of the message queue
func (c *Client) QueueMsg(msg *Message) error {
	if msg.Timestamp == nil {
		curTime := time.Now()
		msg.Timestamp = &curTime
	}

	c.msgChan <- msg
	return nil
}

func (c *Client) queueReceiver() {
	for {
		select {
		case msg := <-c.msgChan:
			c.queueMutex.Lock()
			c.queue = append(c.queue, msg)
			c.queueMutex.Unlock()
		case quitVal := <-c.queueCtl:
			if quitVal == 1 {
				// Don't quit if there are still
				// messages in the channel
				if len(c.msgChan) > 0 {
					c.queueCtl <- 1
					continue
				}
				c.queueCtl <- 2
				return
			}
		}
	}
}

func (c *Client) msgSender() {
	var msg *Message
	for {
		if len(c.queue) > 0 {
			c.queueMutex.Lock()
			msg, c.queue = c.queue[0], c.queue[1:]
			c.queueMutex.Unlock()

			data, err := generateMsgJson(msg)
			if err != nil {
				// TODO Not sure what to do at this point? Fail the
				// message silently?
				// Might be able to add an error channel that the
				// user can watch for errors
				continue
			}
			err = c.writeMsg(data, c.conn, COMP_GZIP)
			if err != nil {
				// TODO Same as above...
			}
		} else {
			time.Sleep(1 * time.Second)

			select {
			case quitVal := <-c.sendCtl:
				if quitVal == 1 {
					if len(c.queue) > 0 {
						c.sendCtl <- 1
						continue
					}
					c.sendCtl <- 2
					return
				}
			default:
			}
		}
	}
}

func (c *Client) writeMsg(data string, w io.Writer, compression int) error {
	chnk, err := newChunker(w, c.config.ChunkSize)
	if err != nil {
		return err
	}
	defer chnk.Flush()

	switch compression {
	case COMP_GZIP:
		gz, err := gzip.NewWriterLevel(chnk, gzip.DefaultCompression)
		if err != nil {
			return err
		}
		gz.Write([]byte(data))
		gz.Close()
	case COMP_ZLIB:
		zz, err := zlib.NewWriterLevel(chnk, zlib.DefaultCompression)
		if err != nil {
			return err
		}
		zz.Write([]byte(data))
		zz.Close()
	default:
		chnk.Write([]byte(data))
	}

	return nil
}
<commit_msg>mutex-protect calls to len(c.queue)<commit_after>package golf

import (
	"compress/gzip"
	"compress/zlib"
	"errors"
	"io"
	"net"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

// Compression type to use for GELF messages that are sent
const (
	COMP_NONE = iota // No compression
	COMP_GZIP        // gzip compression
	COMP_ZLIB        // zlib compression
)

type Client struct {
	hostname string

	conn net.Conn

	queue      []*Message
	queueMutex sync.Mutex

	msgChan  chan *Message
	queueCtl chan int
	sendCtl  chan int

	config ClientConfig
}

// Configuration used when creating a server instance
type ClientConfig struct {
	ChunkSize   int // The data size for each chunk sent to the server
	Compression int // Compression to use for messagec.
}

/*
Create a new Client instance with the default values for ClientConfig:

 {
	ChunkSize: 1420,
	Compression: COMP_GZIP,
 }
*/
func NewClient() (*Client, error) {
	cc := ClientConfig{
		ChunkSize:   1420,
		Compression: COMP_GZIP,
	}
	return NewClientWithConfig(cc)
}

// Create a new Client instance with the given ClientConfig
func NewClientWithConfig(config ClientConfig) (*Client, error) {
	c := &Client{
		config: config,
		queue:  make([]*Message, 0),

		msgChan:  make(chan *Message, 500),
		queueCtl: make(chan int),
		sendCtl:  make(chan int),
	}

	host, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	c.hostname = host

	return c, nil
}

// Connect to a GELF server at the given URI.
func (c *Client) Dial(uri string) error {
	parsedUri, err := url.Parse(uri)
	if err != nil {
		return err
	}

	if !strings.Contains(parsedUri.Host, ":") {
		parsedUri.Host = parsedUri.Host + ":12201"
	}

	switch parsedUri.Scheme {
	case "udp":
	case "tcp":
	default:
		return errors.New("Unsupported scheme provided")
	}

	conn, err := net.Dial(parsedUri.Scheme, parsedUri.Host)
	if err != nil {
		return err
	}
	c.conn = conn

	go c.queueReceiver()
	go c.msgSender()

	return nil
}

// Close the connection to the server. This call will block until all the
// currently queued messages for the client are sent.
func (c *Client) Close() error {
	if c.conn == nil {
		// Already shut down so it doesn't need to run again
		return nil
	}

	// First quit the queue and wait for it to respond
	// that it's quit
	c.queueCtl <- 1
	for {
		quitVal := <-c.queueCtl
		if quitVal == 2 {
			break
		}
		c.queueCtl <- quitVal
	}

	// Then quit the sender and wait for it to respond
	// that it's quit
	c.sendCtl <- 1
	for {
		quitVal := <-c.sendCtl
		if quitVal == 2 {
			break
		}
		c.sendCtl <- quitVal
	}

	err := c.conn.Close()
	if err != nil {
		return err
	}
	c.conn = nil

	return nil
}

// Queue the given message at the end of the message queue
func (c *Client) QueueMsg(msg *Message) error {
	if msg.Timestamp == nil {
		curTime := time.Now()
		msg.Timestamp = &curTime
	}

	c.msgChan <- msg
	return nil
}

func (c *Client) queueReceiver() {
	for {
		select {
		case msg := <-c.msgChan:
			c.queueMutex.Lock()
			c.queue = append(c.queue, msg)
			c.queueMutex.Unlock()
		case quitVal := <-c.queueCtl:
			if quitVal == 1 {
				// Don't quit if there are still
				// messages in the channel
				if len(c.msgChan) > 0 {
					c.queueCtl <- 1
					continue
				}
				c.queueCtl <- 2
				return
			}
		}
	}
}

func (c *Client) msgSender() {
	var msg *Message
	for {
		c.queueMutex.Lock()
		if len(c.queue) > 0 {
			msg, c.queue = c.queue[0], c.queue[1:]
			c.queueMutex.Unlock()

			data, err := generateMsgJson(msg)
			if err != nil {
				// TODO Not sure what to do at this point? Fail the
				// message silently?
				// Might be able to add an error channel that the
				// user can watch for errors
				continue
			}
			err = c.writeMsg(data, c.conn, COMP_GZIP)
			if err != nil {
				// TODO Same as above...
			}
		} else {
			c.queueMutex.Unlock()
			time.Sleep(1 * time.Second)

			select {
			case quitVal := <-c.sendCtl:
				if quitVal == 1 {
					c.queueMutex.Lock()
					if len(c.queue) > 0 {
						c.queueMutex.Unlock()
						c.sendCtl <- 1
						continue
					}
					c.queueMutex.Unlock()
					c.sendCtl <- 2
					return
				}
			default:
			}
		}
	}
}

func (c *Client) writeMsg(data string, w io.Writer, compression int) error {
	chnk, err := newChunker(w, c.config.ChunkSize)
	if err != nil {
		return err
	}
	defer chnk.Flush()

	switch compression {
	case COMP_GZIP:
		gz, err := gzip.NewWriterLevel(chnk, gzip.DefaultCompression)
		if err != nil {
			return err
		}
		gz.Write([]byte(data))
		gz.Close()
	case COMP_ZLIB:
		zz, err := zlib.NewWriterLevel(chnk, zlib.DefaultCompression)
		if err != nil {
			return err
		}
		zz.Write([]byte(data))
		zz.Close()
	default:
		chnk.Write([]byte(data))
	}

	return nil
}
<|endoftext|>


<commit_before>package gode

import (
	"path/filepath"
	"runtime"
)

// The Node version to install.
// Override this by setting client.Version.
const DefaultNodeVersion = "1.1.0"

// The NPM version to install.
// Override this by setting client.Version.
const DefaultNpmVersion = "2.4.1"

// This is a Cloudfront CDN that mirrors http://iojs.org/dist
// It is much faster.
const NodeCDN = "http://d1gnwkq6ucr1xr.cloudfront.net"

// const NodeCDN = "http://d1nhjzpj45o0rc.cloudfront.net" for http://nodejs.org/dist

// Client is the interface between Node and Go.
// It also setups up the Node environment if needed.
type Client struct {
	RootPath    string
	NodeVersion string
	NpmVersion  string
	Registry    string
}

// NewClient creates a new Client at the specified rootPath
// The Node installation can then be setup here with client.Setup()
func NewClient(rootPath string) *Client {
	client := &Client{
		RootPath:    rootPath,
		NodeVersion: DefaultNodeVersion,
		NpmVersion:  DefaultNpmVersion,
	}

	return client
}

func (c *Client) nodeBase() string {
	switch {
	case runtime.GOARCH == "386":
		return "iojs-v" + c.NodeVersion + "-" + runtime.GOOS + "-x86"
	default:
		return "iojs-v" + c.NodeVersion + "-" + runtime.GOOS + "-x64"
	}
}

func (c *Client) nodeURL() string {
	switch {
	case runtime.GOOS == "windows" && runtime.GOARCH == "386":
		return NodeCDN + "/v" + c.NodeVersion + "/iojs.exe"
	case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
		return NodeCDN + "/v" + c.NodeVersion + "/x64/iojs.exe"
	case runtime.GOARCH == "386":
		return NodeCDN + "/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	default:
		return NodeCDN + "/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	}
}

func (c *Client) nodePath() string {
	switch {
	case runtime.GOOS == "windows":
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node.exe")
	default:
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node")
	}
}

func (c *Client) npmURL() string {
	return "http://github.com/npm/npm/archive/v" + c.NpmVersion + ".zip"
}

func (c *Client) npmPath() string {
	return filepath.Join(c.RootPath, c.nodeBase(), "lib", "node_modules", "npm", "cli.js")
}
<commit_msg>removed iojs cdn it turns out that iojs.org is faster than cloudfront<commit_after>package gode

import (
	"path/filepath"
	"runtime"
)

// The Node version to install.
// Override this by setting client.Version.
const DefaultNodeVersion = "1.1.0"

// The NPM version to install.
// Override this by setting client.Version.
const DefaultNpmVersion = "2.4.1"

// Client is the interface between Node and Go.
// It also setups up the Node environment if needed.
type Client struct {
	RootPath    string
	NodeVersion string
	NpmVersion  string
	Registry    string
}

// NewClient creates a new Client at the specified rootPath
// The Node installation can then be setup here with client.Setup()
func NewClient(rootPath string) *Client {
	client := &Client{
		RootPath:    rootPath,
		NodeVersion: DefaultNodeVersion,
		NpmVersion:  DefaultNpmVersion,
	}

	return client
}

func (c *Client) nodeBase() string {
	switch {
	case runtime.GOARCH == "386":
		return "iojs-v" + c.NodeVersion + "-" + runtime.GOOS + "-x86"
	default:
		return "iojs-v" + c.NodeVersion + "-" + runtime.GOOS + "-x64"
	}
}

func (c *Client) nodeURL() string {
	switch {
	case runtime.GOOS == "windows" && runtime.GOARCH == "386":
		return "https://iojs.org/dist/v" + c.NodeVersion + "/iojs.exe"
	case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
		return "https://iojs.org/dist/v" + c.NodeVersion + "/x64/iojs.exe"
	case runtime.GOARCH == "386":
		return "https://iojs.org/dist/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	default:
		return "https://iojs.org/dist/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	}
}

func (c *Client) nodePath() string {
	switch {
	case runtime.GOOS == "windows":
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node.exe")
	default:
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node")
	}
}

func (c *Client) npmURL() string {
	return "http://github.com/npm/npm/archive/v" + c.NpmVersion + ".zip"
}

func (c *Client) npmPath() string {
	return filepath.Join(c.RootPath, c.nodeBase(), "lib", "node_modules", "npm", "cli.js")
}
<|endoftext|>


<commit_before>package irc

type Channel struct {
	banList []UserMask
	flags   ChannelModeSet
	key     string
	members MemberSet
	name    string
	server  *Server
	topic   string
}

func IsChannel(target string) bool {
	return ChannelNameExpr.MatchString(target)
}

// NewChannel creates a new channel from a `Server` and a `name`
// string, which must be unique on the server.
func NewChannel(s *Server, name string) *Channel {
	channel := &Channel{
		banList: make([]UserMask, 0),
		flags:   make(ChannelModeSet),
		members: make(MemberSet),
		name:    name,
		server:  s,
	}
	return channel
}

func (channel *Channel) IsEmpty() bool {
	return len(channel.members) == 0
}

func (channel *Channel) Names(client *Client) {
	client.MultilineReply(channel.Nicks(), RPL_NAMREPLY,
		"= %s :%s", channel.name)
	client.RplEndOfNames(channel)
}

func (channel *Channel) ClientIsOperator(client *Client) bool {
	return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator)
}

func (channel *Channel) Nicks() []string {
	nicks := make([]string, len(channel.members))
	i := 0
	for client, modes := range channel.members {
		switch {
		case modes[ChannelOperator]:
			nicks[i] = "@" + client.Nick()
		case modes[Voice]:
			nicks[i] = "+" + client.Nick()
		default:
			nicks[i] = client.Nick()
		}
		i += 1
	}
	return nicks
}

func (channel *Channel) Id() string {
	return channel.name
}

func (channel *Channel) Nick() string {
	return channel.name
}

func (channel *Channel) String() string {
	return channel.Id()
}

// <mode> <mode params>
func (channel *Channel) ModeString() (str string) {
	if channel.key != "" {
		str += Key.String()
	}

	for mode := range channel.flags {
		str += mode.String()
	}

	if len(str) > 0 {
		str = "+" + str
	}

	if channel.key != "" {
		str += " " + channel.key
	}

	return
}

func (channel *Channel) Join(client *Client, key string) {
	if (channel.key != "") && (channel.key != key) {
		client.ErrBadChannelKey(channel)
		return
	}

	client.channels.Add(channel)
	channel.members.Add(client)
	if len(channel.members) == 1 {
		channel.members[client][ChannelCreator] = true
		channel.members[client][ChannelOperator] = true
	}

	reply := RplJoin(client, channel)
	for member := range channel.members {
		member.replies <- reply
	}
	channel.GetTopic(client)
	channel.Names(client)
}

func (channel *Channel) Part(client *Client, message string) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	for member := range channel.members {
		member.replies <- RplPart(client, channel, message)
	}
	channel.Quit(client)

	if channel.IsEmpty() {
		channel.server.channels.Remove(channel)
	}
}

func (channel *Channel) GetTopic(client *Client) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.topic == "" {
		// clients appear not to expect this
		//replier.Reply(RplNoTopic(channel))
		return
	}

	client.RplTopic(channel)
}

func (channel *Channel) SetTopic(client *Client, topic string) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.flags[OpOnlyTopic] && !channel.members[client][ChannelOperator] {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}

	channel.topic = topic
	for member := range channel.members {
		member.replies <- RplTopicMsg(client, channel)
	}
}

func (channel *Channel) PrivMsg(client *Client, message string) {
	if channel.flags[NoOutside] && !channel.members.Has(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	for member := range channel.members {
		if member == client {
			continue
		}
		member.replies <- RplPrivMsg(client, channel, message)
	}
}

func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
	if len(changes) == 0 {
		client.RplChannelModeIs(channel)
		return
	}

	applied := make(ChannelModeChanges, 0)

	for _, change := range changes {
		switch change.mode {
		case BanMask:
			// TODO add/remove

			for _, banMask := range channel.banList {
				client.RplBanList(channel, banMask)
			}
			client.RplEndOfBanList(channel)

		case NoOutside, Private, Secret, OpOnlyTopic:
			if !channel.ClientIsOperator(client) {
				client.ErrChanOPrivIsNeeded(channel)
				continue
			}

			switch change.op {
			case Add:
				channel.flags[change.mode] = true
				applied = append(applied, change)

			case Remove:
				delete(channel.flags, change.mode)
				applied = append(applied, change)
			}

		case Key:
			if !channel.ClientIsOperator(client) {
				client.ErrChanOPrivIsNeeded(channel)
				continue
			}

			switch change.op {
			case Add:
				if change.arg == "" {
					// TODO err reply
					continue
				}

				channel.key = change.arg
				applied = append(applied, change)

			case Remove:
				channel.key = ""
				applied = append(applied, change)
			}

		case ChannelOperator, Voice:
			if !channel.ClientIsOperator(client) {
				client.ErrChanOPrivIsNeeded(channel)
				continue
			}

			if change.arg == "" {
				// TODO err reply
				continue
			}

			target := channel.server.clients[change.arg]
			if target == nil {
				// TODO err reply
				continue
			}

			if channel.members[target] == nil {
				// TODO err reply
				continue
			}

			switch change.op {
			case Add:
				channel.members[target][change.mode] = true
				applied = append(applied, change)

			case Remove:
				channel.members[target][change.mode] = false
				applied = append(applied, change)
			}
		}
	}

	if len(applied) > 0 {
		for member := range channel.members {
			member.replies <- RplChannelMode(client, channel, applied)
		}
	}
}

func (channel *Channel) Notice(client *Client, message string) {
	if channel.flags[NoOutside] && !channel.members.Has(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	for member := range channel.members {
		if member == client {
			continue
		}
		member.replies <- RplNotice(client, channel, message)
	}
}

func (channel *Channel) Quit(client *Client) {
	channel.members.Remove(client)
	client.channels.Remove(channel)
}

func (channel *Channel) Kick(client *Client, target *Client, comment string) {
	if !client.flags[Operator] && !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}
	if !channel.members.Has(target) {
		client.ErrUserNotInChannel(channel, target)
		return
	}

	for member := range channel.members {
		member.replies <- RplKick(channel, client, target, comment)
	}
	channel.Quit(target)
}
<commit_msg>render a single part string for all channel members<commit_after>package irc

type Channel struct {
	banList []UserMask
	flags   ChannelModeSet
	key     string
	members MemberSet
	name    string
	server  *Server
	topic   string
}

func IsChannel(target string) bool {
	return ChannelNameExpr.MatchString(target)
}

// NewChannel creates a new channel from a `Server` and a `name`
// string, which must be unique on the server.
func NewChannel(s *Server, name string) *Channel {
	channel := &Channel{
		banList: make([]UserMask, 0),
		flags:   make(ChannelModeSet),
		members: make(MemberSet),
		name:    name,
		server:  s,
	}
	return channel
}

func (channel *Channel) IsEmpty() bool {
	return len(channel.members) == 0
}

func (channel *Channel) Names(client *Client) {
	client.MultilineReply(channel.Nicks(), RPL_NAMREPLY,
		"= %s :%s", channel.name)
	client.RplEndOfNames(channel)
}

func (channel *Channel) ClientIsOperator(client *Client) bool {
	return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator)
}

func (channel *Channel) Nicks() []string {
	nicks := make([]string, len(channel.members))
	i := 0
	for client, modes := range channel.members {
		switch {
		case modes[ChannelOperator]:
			nicks[i] = "@" + client.Nick()
		case modes[Voice]:
			nicks[i] = "+" + client.Nick()
		default:
			nicks[i] = client.Nick()
		}
		i += 1
	}
	return nicks
}

func (channel *Channel) Id() string {
	return channel.name
}

func (channel *Channel) Nick() string {
	return channel.name
}

func (channel *Channel) String() string {
	return channel.Id()
}

// <mode> <mode params>
func (channel *Channel) ModeString() (str string) {
	if channel.key != "" {
		str += Key.String()
	}

	for mode := range channel.flags {
		str += mode.String()
	}

	if len(str) > 0 {
		str = "+" + str
	}

	if channel.key != "" {
		str += " " + channel.key
	}

	return
}

func (channel *Channel) Join(client *Client, key string) {
	if (channel.key != "") && (channel.key != key) {
		client.ErrBadChannelKey(channel)
		return
	}

	client.channels.Add(channel)
	channel.members.Add(client)
	if len(channel.members) == 1 {
		channel.members[client][ChannelCreator] = true
		channel.members[client][ChannelOperator] = true
	}

	reply := RplJoin(client, channel)
	for member := range channel.members {
		member.replies <- reply
	}
	channel.GetTopic(client)
	channel.Names(client)
}

func (channel *Channel) Part(client *Client, message string) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	reply := RplPart(client, channel, message)
	for member := range channel.members {
		member.replies <- reply
	}
	channel.Quit(client)

	if channel.IsEmpty() {
		channel.server.channels.Remove(channel)
	}
}

func (channel *Channel) GetTopic(client *Client) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.topic == "" {
		// clients appear not to expect this
		//replier.Reply(RplNoTopic(channel))
		return
	}

	client.RplTopic(channel)
}

func (channel *Channel) SetTopic(client *Client, topic string) {
	if !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}

	if channel.flags[OpOnlyTopic] && !channel.members[client][ChannelOperator] {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}

	channel.topic = topic
	for member := range channel.members {
		member.replies <- RplTopicMsg(client, channel)
	}
}

func (channel *Channel) PrivMsg(client *Client, message string) {
	if channel.flags[NoOutside] && !channel.members.Has(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	for member := range channel.members {
		if member == client {
			continue
		}
		member.replies <- RplPrivMsg(client, channel, message)
	}
}

func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
	if len(changes) == 0 {
		client.RplChannelModeIs(channel)
		return
	}

	applied := make(ChannelModeChanges, 0)

	for _, change := range changes {
		switch change.mode {
		case BanMask:
			// TODO add/remove

			for _, banMask := range channel.banList {
				client.RplBanList(channel, banMask)
			}
			client.RplEndOfBanList(channel)

		case NoOutside, Private, Secret, OpOnlyTopic:
			if !channel.ClientIsOperator(client) {
				client.ErrChanOPrivIsNeeded(channel)
				continue
			}

			switch change.op {
			case Add:
				channel.flags[change.mode] = true
				applied = append(applied, change)

			case Remove:
				delete(channel.flags, change.mode)
				applied = append(applied, change)
			}

		case Key:
			if !channel.ClientIsOperator(client) {
				client.ErrChanOPrivIsNeeded(channel)
				continue
			}

			switch change.op {
			case Add:
				if change.arg == "" {
					// TODO err reply
					continue
				}

				channel.key = change.arg
				applied = append(applied, change)

			case Remove:
				channel.key = ""
				applied = append(applied, change)
			}

		case ChannelOperator, Voice:
			if !channel.ClientIsOperator(client) {
				client.ErrChanOPrivIsNeeded(channel)
				continue
			}

			if change.arg == "" {
				// TODO err reply
				continue
			}

			target := channel.server.clients[change.arg]
			if target == nil {
				// TODO err reply
				continue
			}

			if channel.members[target] == nil {
				// TODO err reply
				continue
			}

			switch change.op {
			case Add:
				channel.members[target][change.mode] = true
				applied = append(applied, change)

			case Remove:
				channel.members[target][change.mode] = false
				applied = append(applied, change)
			}
		}
	}

	if len(applied) > 0 {
		for member := range channel.members {
			member.replies <- RplChannelMode(client, channel, applied)
		}
	}
}

func (channel *Channel) Notice(client *Client, message string) {
	if channel.flags[NoOutside] && !channel.members.Has(client) {
		client.ErrCannotSendToChan(channel)
		return
	}
	for member := range channel.members {
		if member == client {
			continue
		}
		member.replies <- RplNotice(client, channel, message)
	}
}

func (channel *Channel) Quit(client *Client) {
	channel.members.Remove(client)
	client.channels.Remove(channel)
}

func (channel *Channel) Kick(client *Client, target *Client, comment string) {
	if !client.flags[Operator] && !channel.members.Has(client) {
		client.ErrNotOnChannel(channel)
		return
	}
	if !channel.ClientIsOperator(client) {
		client.ErrChanOPrivIsNeeded(channel)
		return
	}
	if !channel.members.Has(target) {
		client.ErrUserNotInChannel(channel, target)
		return
	}

	for member := range channel.members {
		member.replies <- RplKick(channel, client, target, comment)
	}
	channel.Quit(target)
}
<|endoftext|>


<commit_before>package gomq

import "github.com/zeromq/gomq/zmtp"

// ClientSocket is a ZMQ_CLIENT socket type.
// See: http://rfc.zeromq.org/spec:41
type ClientSocket struct {
	*Socket
}

// NewClient accepts a zmtp.SecurityMechanism and returns
// a ClientSocket as a gomq.Client interface.
func NewClient(mechanism zmtp.SecurityMechanism) Client {
	return &ClientSocket{
		Socket: NewSocket(false, zmtp.ClientSocketType, nil, mechanism),
	}
}

// Connect accepts a zeromq endpoint and connects the
// client socket to it. Currently the only transport
// supported is TCP. The endpoint string should be
// in the format "tcp://<address>:<port>".
func (c *ClientSocket) Connect(endpoint string) error {
	return ConnectClient(c, endpoint)
}
<commit_msg>fix: add uuid for client<commit_after>package gomq

import "github.com/zeromq/gomq/zmtp"

// ClientSocket is a ZMQ_CLIENT socket type.
// See: http://rfc.zeromq.org/spec:41
type ClientSocket struct {
	*Socket
}

// NewClient accepts a zmtp.SecurityMechanism and returns
// a ClientSocket as a gomq.Client interface.
func NewClient(mechanism zmtp.SecurityMechanism) Client {
	uuid, _ := newUUID()
	return &ClientSocket{
		Socket: NewSocket(false, zmtp.ClientSocketType, []byte(uuid), mechanism),
	}
}

// Connect accepts a zeromq endpoint and connects the
// client socket to it. Currently the only transport
// supported is TCP. The endpoint string should be
// in the format "tcp://<address>:<port>".
func (c *ClientSocket) Connect(endpoint string) error {
	return ConnectClient(c, endpoint)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio";
	"fmt";
	"net";
	"os";
	"strconv";
	"strings";
)

type Client struct {
	conn	net.Conn;
	rw	*bufio.ReadWriter;
}

type Song struct {
	file		string;
	time		int;
	title		string;
	artist		string;
	album		string;
	track		int;
	performer	string;
	pos		int;
	id		int;
}

func Connect(addr string) (c *Client, err os.Error) {
	conn, err := net.Dial("tcp", "", addr);
	if err != nil {
		return nil, err
	}
	c = new(Client);
	c.rw = bufio.NewReadWriter(bufio.NewReader(conn),
		bufio.NewWriter(conn));
	line, err := c.readLine();
	if err != nil {
		return nil, err
	}
	if line[0:6] != "OK MPD" {
		return c, os.NewError("no greeting")
	}
	return c, nil;
}

func (c *Client) Disconnect() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) readLine() (line string, err os.Error) {
	line, err = c.rw.ReadString('\n');
	if err != nil {
		return
	}
	if line[len(line)-1] == '\n' {
		line = line[0 : len(line)-1]
	}
	fmt.Println("-->", line);
	return line, nil;
}

func (c *Client) writeLine(line string) (err os.Error) {
	fmt.Println("<--", line);
	_, err = c.rw.Write(strings.Bytes(line + "\n"));
	// TODO: try again if # written != len(buf)
	c.rw.Flush();
	return err;
}

func (c *Client) getAttrs() (attrs map[string]string, err os.Error) {
	attrs = make(map[string]string);
	for {
		line, err := c.readLine();
		if err != nil {
			return nil, err
		}
		if line == "OK" {
			break
		}
		i := strings.Index(line, ": ");
		if i < 0 {
			return nil, os.NewError("can't parse line: " + line)
		}
		key := line[0:i];
		attrs[key] = line[i+2:];
	}
	return attrs, nil;
}

func atoi(s string) (n int) {
	n, _ = strconv.Atoi(s);
	return;
}

func (c *Client) CurrentSong() (song *Song, err os.Error) {
	c.writeLine("currentsong");
	song = new(Song);
	attrs, err := c.getAttrs();
	if err != nil {
		return nil, err
	}
	fmt.Println("attrs:", attrs);
	for key, val := range attrs {
		switch key {
		case "file":
			song.file = val
		case "Time":
			song.time = atoi(val)
		case "Title":
			song.title = val
		case "Artist":
			song.artist = val
		case "Album":
			song.album = val
		case "Track":
			song.track = atoi(val)
		case "Performer":
			song.performer = val
		case "Pos":
			song.pos = atoi(val)
		case "Id":
			song.id = atoi(val)
		}
	}
	return song, nil;
}

func main() {
	cli, err := Connect("127.0.0.1:6600");
	if err != nil {
		goto err
	}
	song, err := cli.CurrentSong();
	if err != nil {
		goto err
	}
	fmt.Println(song);
	cli.Disconnect();
	return;
err:
	fmt.Fprintln(os.Stderr, err);
	os.Exit(1);
}
<commit_msg>clean up<commit_after>// MPD (Music Player Daemon) client

package main

import (
	"bufio";
	"fmt";
	"net";
	"os";
	"strconv";
	"strings";
)

type Client struct {
	conn	net.Conn;
	rw	*bufio.ReadWriter;
}

type Song struct {
	file		string;
	time		int;
	title		string;
	artist		string;
	album		string;
	track		int;
	performer	string;
	pos		int;
	id		int;
}

func Connect(addr string) (c *Client, err os.Error) {
	conn, err := net.Dial("tcp", "", addr);
	if err != nil {
		return nil, err
	}
	c = new(Client);
	c.rw = bufio.NewReadWriter(bufio.NewReader(conn),
		bufio.NewWriter(conn));
	line, err := c.readLine();
	if err != nil {
		return nil, err
	}
	if line[0:6] != "OK MPD" {
		return nil, os.NewError("no greeting")
	}
	return;
}

func (c *Client) Disconnect() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) readLine() (line string, err os.Error) {
	line, err = c.rw.ReadString('\n');
	if err != nil {
		return
	}
	if line[len(line)-1] == '\n' {
		line = line[0 : len(line)-1]
	}
	fmt.Println("-->", line);
	return;
}

func (c *Client) writeLine(line string) (err os.Error) {
	fmt.Println("<--", line);
	_, err = c.rw.Write(strings.Bytes(line + "\n"));
	// TODO: try again if # written != len(buf)
	c.rw.Flush();
	return;
}

func (c *Client) getAttrs() (attrs map[string]string, err os.Error) {
	attrs = make(map[string]string);
	for {
		line, err := c.readLine();
		if err != nil {
			return nil, err
		}
		if line == "OK" {
			break
		}
		i := strings.Index(line, ": ");
		if i < 0 {
			return nil, os.NewError("can't parse line: " + line)
		}
		key := line[0:i];
		attrs[key] = line[i+2:];
	}
	return;
}

func atoi(s string) (n int) {
	n, _ = strconv.Atoi(s);
	return;
}

func (c *Client) CurrentSong() (song *Song, err os.Error) {
	c.writeLine("currentsong");
	song = new(Song);
	attrs, err := c.getAttrs();
	if err != nil {
		return nil, err
	}
	for key, val := range attrs {
		switch key {
		case "file":
			song.file = val
		case "Time":
			song.time = atoi(val)
		case "Title":
			song.title = val
		case "Artist":
			song.artist = val
		case "Album":
			song.album = val
		case "Track":
			song.track = atoi(val)
		case "Performer":
			song.performer = val
		case "Pos":
			song.pos = atoi(val)
		case "Id":
			song.id = atoi(val)
		}
	}
	return;
}

func main() {
	cli, err := Connect("127.0.0.1:6600");
	if err != nil {
		goto err
	}
	song, err := cli.CurrentSong();
	if err != nil {
		goto err
	}
	fmt.Println(song);
	cli.Disconnect();
	return;
err:
	fmt.Fprintln(os.Stderr, err);
	os.Exit(1);
}
<|endoftext|>


<commit_before>package gordon

import (
	"time"

	"github.com/vito/gordon/connection"
	"github.com/vito/gordon/warden"
)

type Client interface {
	Connect() error

	Create() (*warden.CreateResponse, error)
	Stop(handle string, background, kill bool) (*warden.StopResponse, error)
	Destroy(handle string) (*warden.DestroyResponse, error)
	Run(handle, script string) (uint32, <-chan *warden.ProcessPayload, error)
	Attach(handle string, processID uint32) (<-chan *warden.ProcessPayload, error)
	NetIn(handle string) (*warden.NetInResponse, error)
	LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error)
	GetMemoryLimit(handle string) (uint64, error)
	LimitDisk(handle string, limit uint64) (*warden.LimitDiskResponse, error)
	GetDiskLimit(handle string) (uint64, error)
	List() (*warden.ListResponse, error)
	Info(handle string) (*warden.InfoResponse, error)
	CopyIn(handle, src, dst string) (*warden.CopyInResponse, error)
}

type client struct {
	connectionProvider ConnectionProvider
	connection         chan *connection.Connection
}

func NewClient(cp ConnectionProvider) Client {
	return &client{
		connectionProvider: cp,
		connection:         make(chan *connection.Connection),
	}
}

func (c *client) Connect() error {
	conn, err := c.connectionProvider.ProvideConnection()
	if err != nil {
		return err
	}

	go c.serveConnection(conn)

	return nil
}

func (c *client) Create() (*warden.CreateResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Create()
}

func (c *client) Stop(handle string, background, kill bool) (*warden.StopResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Stop(handle, background, kill)
}

func (c *client) Destroy(handle string) (*warden.DestroyResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Destroy(handle)
}

func (c *client) Run(handle, script string) (uint32, <-chan *warden.ProcessPayload, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	processID, stream, err := conn.Run(handle, script)

	if err != nil {
		c.release(conn)
		return 0, nil, err
	}

	proxy := make(chan *warden.ProcessPayload)

	go func() {
		for payload := range stream {
			proxy <- payload
		}
		close(proxy)
		c.release(conn)
	}()

	return processID, proxy, err
}

func (c *client) Attach(handle string, jobID uint32) (<-chan *warden.ProcessPayload, error) {
	conn := c.acquireConnection()

	stream, err := conn.Attach(handle, jobID)
	if err != nil {
		c.release(conn)
		return nil, err
	}

	proxy := make(chan *warden.ProcessPayload)

	go func() {
		for payload := range stream {
			proxy <- payload
		}
		close(proxy)
		c.release(conn)
	}()

	return proxy, err
}

func (c *client) NetIn(handle string) (*warden.NetInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.NetIn(handle)
}

func (c *client) LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitMemory(handle, limit)
}

func (c *client) GetMemoryLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetMemoryLimit(handle)
}

func (c *client) LimitDisk(handle string, limit uint64) (*warden.LimitDiskResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitDisk(handle, limit)
}

func (c *client) GetDiskLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetDiskLimit(handle)
}

func (c *client) List() (*warden.ListResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.List()
}

func (c *client) Info(handle string) (*warden.InfoResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Info(handle)
}

func (c *client) CopyIn(handle, src, dst string) (*warden.CopyInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.CopyIn(handle, src, dst)
}

func (c *client) serveConnection(conn *connection.Connection) {
	select {
	case <-conn.Disconnected:

	case c.connection <- conn:

	case <-time.After(5 * time.Second):
		conn.Close()
	}
}

func (c *client) release(conn *connection.Connection) {
	go c.serveConnection(conn)
}

func (c *client) acquireConnection() *connection.Connection {
	select {
	case conn := <-c.connection:
		return conn

	case <-time.After(1 * time.Second):
		return c.connect()
	}
}

func (c *client) connect() *connection.Connection {
	for {
		conn, err := c.connectionProvider.ProvideConnection()
		if err == nil {
			return conn
		}

		time.Sleep(500 * time.Millisecond)
	}
}
<commit_msg>fix early connection release in Run<commit_after>package gordon

import (
	"time"

	"github.com/vito/gordon/connection"
	"github.com/vito/gordon/warden"
)

type Client interface {
	Connect() error

	Create() (*warden.CreateResponse, error)
	Stop(handle string, background, kill bool) (*warden.StopResponse, error)
	Destroy(handle string) (*warden.DestroyResponse, error)
	Run(handle, script string) (uint32, <-chan *warden.ProcessPayload, error)
	Attach(handle string, processID uint32) (<-chan *warden.ProcessPayload, error)
	NetIn(handle string) (*warden.NetInResponse, error)
	LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error)
	GetMemoryLimit(handle string) (uint64, error)
	LimitDisk(handle string, limit uint64) (*warden.LimitDiskResponse, error)
	GetDiskLimit(handle string) (uint64, error)
	List() (*warden.ListResponse, error)
	Info(handle string) (*warden.InfoResponse, error)
	CopyIn(handle, src, dst string) (*warden.CopyInResponse, error)
}

type client struct {
	connectionProvider ConnectionProvider
	connection         chan *connection.Connection
}

func NewClient(cp ConnectionProvider) Client {
	return &client{
		connectionProvider: cp,
		connection:         make(chan *connection.Connection),
	}
}

func (c *client) Connect() error {
	conn, err := c.connectionProvider.ProvideConnection()
	if err != nil {
		return err
	}

	go c.serveConnection(conn)

	return nil
}

func (c *client) Create() (*warden.CreateResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Create()
}

func (c *client) Stop(handle string, background, kill bool) (*warden.StopResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Stop(handle, background, kill)
}

func (c *client) Destroy(handle string) (*warden.DestroyResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Destroy(handle)
}

func (c *client) Run(handle, script string) (uint32, <-chan *warden.ProcessPayload, error) {
	conn := c.acquireConnection()

	processID, stream, err := conn.Run(handle, script)

	if err != nil {
		c.release(conn)
		return 0, nil, err
	}

	proxy := make(chan *warden.ProcessPayload)

	go func() {
		for payload := range stream {
			proxy <- payload
		}
		close(proxy)
		c.release(conn)
	}()

	return processID, proxy, err
}

func (c *client) Attach(handle string, jobID uint32) (<-chan *warden.ProcessPayload, error) {
	conn := c.acquireConnection()

	stream, err := conn.Attach(handle, jobID)
	if err != nil {
		c.release(conn)
		return nil, err
	}

	proxy := make(chan *warden.ProcessPayload)

	go func() {
		for payload := range stream {
			proxy <- payload
		}
		close(proxy)
		c.release(conn)
	}()

	return proxy, err
}

func (c *client) NetIn(handle string) (*warden.NetInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.NetIn(handle)
}

func (c *client) LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitMemory(handle, limit)
}

func (c *client) GetMemoryLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetMemoryLimit(handle)
}

func (c *client) LimitDisk(handle string, limit uint64) (*warden.LimitDiskResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.LimitDisk(handle, limit)
}

func (c *client) GetDiskLimit(handle string) (uint64, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.GetDiskLimit(handle)
}

func (c *client) List() (*warden.ListResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.List()
}

func (c *client) Info(handle string) (*warden.InfoResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.Info(handle)
}

func (c *client) CopyIn(handle, src, dst string) (*warden.CopyInResponse, error) {
	conn := c.acquireConnection()
	defer c.release(conn)

	return conn.CopyIn(handle, src, dst)
}

func (c *client) serveConnection(conn *connection.Connection) {
	select {
	case <-conn.Disconnected:

	case c.connection <- conn:

	case <-time.After(5 * time.Second):
		conn.Close()
	}
}

func (c *client) release(conn *connection.Connection) {
	go c.serveConnection(conn)
}

func (c *client) acquireConnection() *connection.Connection {
	select {
	case conn := <-c.connection:
		return conn

	case <-time.After(1 * time.Second):
		return c.connect()
	}
}

func (c *client) connect() *connection.Connection {
	for {
		conn, err := c.connectionProvider.ProvideConnection()
		if err == nil {
			return conn
		}

		time.Sleep(500 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>// A generic resource pool for databases etc
package pool

import (
	"errors"
	"io"
	"time"
)

var (
	TimeoutError    = errors.New("Timeout")
	PoolClosedError = errors.New("Pool is closed")
)

// ResourceOpener opens a resource
type ResourceOpener interface {
	Open() (Resource, error)
}

type Resource interface {
	io.Closer
	// Good returns true when the resource is in good state
	Good() bool
}

type PooledResource interface {
	// Release releases the resource back to the pool for reuse
	Release() error
	// Destroy destroys the resource. It is no longer usable.
	Destroy() error
	// Resource returns the underlying Resource
	Resource() Resource
}

type PoolMetrics interface {
	ReportResources(stats ResourcePoolStat)
	ReportWait(wt time.Duration)
}

type ResourcePoolStat struct {
	AvailableNow  uint32
	ResourcesOpen uint32
	Cap           uint32
	InUse         uint32
}

// ResourcePool manages a pool of resources for reuse.
type ResourcePool struct {
	metrics PoolMetrics //metrics interface to track how the pool performs

	// To get a resource, get a ticket first and then get the resource from
	// either the reserve or open a new one.
	// To release a resource, first put the resource back to the reserve
	// and then put the ticket back.
	// The order is important to make sure we never create >cap(tickets)
	// number of resources.
	reserve chan Resource // idle resources, ready for use
	tickets chan struct{} // ticket to own resources

	opener ResourceOpener
	closed chan struct{}
}

// New creates a new pool of Clients.
func New(maxReserve, maxOpen uint32, opener ResourceOpener, m PoolMetrics) *ResourcePool {
	if maxOpen < maxReserve {
		panic("maxOpen must be > maxReserve")
	}
	tickets := make(chan struct{}, maxOpen)
	for i := 0; i < maxOpen; i++ {
		tickets <- struct{}{}
	}
	return &ResourcePool{
		metrics: m,
		reserve: make(chan Resource, maxReserve),
		tickets: tickets,
		opener:  opener,
		closed:  make(chan struct{}),
	}
}

func (p *ResourcePool) Get() (PooledResource, error) {
	return p.GetWithTimeout(365 * 24 * time.Hour) // 1 year is forever
}

type pooledResource struct {
	p   *ResourcePool
	res Resource
}

func (pr *pooledResource) Release() error {
	return pr.p.release(pr)
}

func (pr *pooledResource) Destroy() error {
	return pr.p.destroy(pr)
}

func (pr *pooledResource) Resource() Resource {
	return pr.res
}

func (p *ResourcePool) GetWithTimeout(timeout time.Duration) (PooledResource, error) {
	// order is important: first ticket then reserve
	start := time.Now()
	select {
	case <-p.tickets:
	case <-time.After(timeout):
		return nil, TimeoutError
	case <-p.closed:
		return nil, PoolClosedError
	}
	if p.isClosed() {
		return nil, PoolClosedError
	}
	p.reportMetrics(time.Now().Sub(start))

	for {
		select {
		case r := <-p.reserve:
			if r.Good() {
				return &pooledResource{p: p, res: r}, nil
			}
			r.Close()

		default:
			// no reserve
			break
		}
	}

	r, err := p.opener.Open()
	if err != nil {
		// release ticket on error
		p.tickets <- struct{}{}
		return nil, err
	}
	return &pooledResource{p: p, res: r}, nil
}

func (p *ResourcePool) release(pr PooledResource) error {
	var err error
	// order is important: first reserve then ticket
	res := pr.Resource()
	select {
	case p.reserve <- res:
	default:
		// reserve is full
		err = res.Close()
	}
	p.tickets <- struct{}{}

	if p.isClosed() {
		p.drainReserve()
	}
	return err
}

func (p *ResourcePool) destroy(pr PooledResource) error {
	p.tickets <- struct{}{}
	return pr.Resource().Close()
}

// Close closes the pool. Resources in use are not affected.
func (p *ResourcePool) Close() error {
	close(p.closed)
	p.drainReserve()
	return nil
}

func (p *ResourcePool) isClosed() bool {
	select {
	case <-p.closed:
		return true
	default:
		return false
	}
}

func (p *ResourcePool) drainReserve() {
	for {
		select {
		case r := <-p.reserve:
			r.Close()
		default:
		}
	}
}

/**
Metrics
**/
func (p *ResourcePool) reportMetrics(wt time.Duration) {
	if p.metrics != nil {
		go p.metrics.ReportWait(wt)
		go p.metrics.ReportResources(p.Stats())
	}
}

func (p *ResourcePool) Stats() ResourcePoolStat {
	tot := uint32(cap(p.tickets))
	n := uint32(len(p.tickets))
	inuse := tot - n
	available := uint32(len(p.reserve))

	return ResourcePoolStat{
		AvailableNow:  available,
		ResourcesOpen: inuse + available,
		Cap:           tot,
		InUse:         inuse,
	}
}
<commit_msg>bug fix of ResourcePool<commit_after>// A generic resource pool for databases etc
package pool

import (
	"errors"
	"io"
	"time"
)

var (
	TimeoutError    = errors.New("Timeout")
	PoolClosedError = errors.New("Pool is closed")
)

// ResourceOpener opens a resource
type ResourceOpener interface {
	Open() (Resource, error)
}

type Resource interface {
	io.Closer
	// Good returns true when the resource is in good state
	Good() bool
}

type PooledResource interface {
	// Release releases the resource back to the pool for reuse
	Release() error
	// Destroy destroys the resource. It is no longer usable.
	Destroy() error
	// Resource returns the underlying Resource
	Resource() Resource
}

type PoolMetrics interface {
	ReportResources(stats ResourcePoolStat)
	ReportWait(wt time.Duration)
}

type ResourcePoolStat struct {
	AvailableNow  uint32
	ResourcesOpen uint32
	Cap           uint32
	InUse         uint32
}

// ResourcePool manages a pool of resources for reuse.
type ResourcePool struct {
	metrics PoolMetrics //metrics interface to track how the pool performs

	// To get a resource, get a ticket first and then get the resource from
	// either the reserve or open a new one.
	// To release a resource, first put the resource back to the reserve
	// and then put the ticket back.
	// The order is important to make sure we never create >cap(tickets)
	// number of resources.
	reserve chan Resource // idle resources, ready for use
	tickets chan struct{} // ticket to own resources

	opener ResourceOpener
	closed chan struct{}
}

// NewPool creates a new pool of Clients.
func NewPool(maxReserve, maxOpen uint32, opener ResourceOpener, m PoolMetrics) *ResourcePool {
	if maxOpen < maxReserve {
		panic("maxOpen must be > maxReserve")
	}
	tickets := make(chan struct{}, maxOpen)
	for i := uint32(0); i < maxOpen; i++ {
		tickets <- struct{}{}
	}
	return &ResourcePool{
		metrics: m,
		reserve: make(chan Resource, maxReserve),
		tickets: tickets,
		opener:  opener,
		closed:  make(chan struct{}),
	}
}

func (p *ResourcePool) Get() (PooledResource, error) {
	return p.GetWithTimeout(365 * 24 * time.Hour) // 1 year is forever
}

type pooledResource struct {
	p   *ResourcePool
	res Resource
}

func (pr *pooledResource) Release() error {
	return pr.p.release(pr)
}

func (pr *pooledResource) Destroy() error {
	return pr.p.destroy(pr)
}

func (pr *pooledResource) Resource() Resource {
	return pr.res
}

func (p *ResourcePool) GetWithTimeout(timeout time.Duration) (PooledResource, error) {
	// order is important: first ticket then reserve
	start := time.Now()
	select {
	case <-p.tickets:
	case <-time.After(timeout):
		return nil, TimeoutError
	case <-p.closed:
		return nil, PoolClosedError
	}
	if p.isClosed() {
		return nil, PoolClosedError
	}
	p.reportMetrics(time.Now().Sub(start))

L:
	for {
		select {
		case r := <-p.reserve:
			if r.Good() {
				return &pooledResource{p: p, res: r}, nil
			}
			r.Close()

		default:
			// no reserve
			break L
		}
	}

	r, err := p.opener.Open()
	if err != nil {
		// release ticket on error
		p.tickets <- struct{}{}
		return nil, err
	}
	return &pooledResource{p: p, res: r}, nil
}

func (p *ResourcePool) release(pr PooledResource) error {
	var err error
	// order is important: first reserve then ticket
	res := pr.Resource()
	select {
	case p.reserve <- res:
	default:
		// reserve is full
		err = res.Close()
	}
	p.tickets <- struct{}{}

	if p.isClosed() {
		p.drainReserve()
	}
	return err
}

func (p *ResourcePool) destroy(pr PooledResource) error {
	p.tickets <- struct{}{}
	return pr.Resource().Close()
}

// Close closes the pool. Resources in use are not affected.
func (p *ResourcePool) Close() error {
	close(p.closed)
	p.drainReserve()
	return nil
}

func (p *ResourcePool) isClosed() bool {
	select {
	case <-p.closed:
		return true
	default:
		return false
	}
}

func (p *ResourcePool) drainReserve() {
	for {
		select {
		case r := <-p.reserve:
			r.Close()
		default:
			return
		}
	}
}

/**
Metrics
**/
func (p *ResourcePool) reportMetrics(wt time.Duration) {
	if p.metrics != nil {
		go p.metrics.ReportWait(wt)
		go p.metrics.ReportResources(p.Stats())
	}
}

func (p *ResourcePool) Stats() ResourcePoolStat {
	tot := uint32(cap(p.tickets))
	n := uint32(len(p.tickets))
	inuse := tot - n
	available := uint32(len(p.reserve))

	return ResourcePoolStat{
		AvailableNow:  available,
		ResourcesOpen: inuse + available,
		Cap:           tot,
		InUse:         inuse,
	}
}
<|endoftext|>