<commit_before>// Copyright 2013 go-dockerclient 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 docker provides a client for the Docker remote API.
//
// See http://goo.gl/mxyql for more details on the remote API.
package docker

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dotcloud/docker/term"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
)

const userAgent = "go-dockerclient"

var (
	// ErrInvalidEndpoint is returned when the endpoint is not a valid HTTP URL.
	ErrInvalidEndpoint = errors.New("Invalid endpoint")

	// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
	ErrConnectionRefused = errors.New("Cannot connect to Docker endpoint")
)

// Client is the basic type of this package. It provides methods for
// interaction with the API.
type Client struct {
	endpoint    string
	endpointURL *url.URL
	client      *http.Client
	out         io.WriteCloser
}

// NewClient returns a Client instance ready for communication with the
// given server endpoint.
func NewClient(endpoint string) (*Client, error) {
	u, err := parseEndpoint(endpoint)
	if err != nil {
		return nil, err
	}
	return &Client{
		endpoint:    endpoint,
		endpointURL: u,
		client:      http.DefaultClient,
		out:         os.Stdout,
	}, nil
}

func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) {
	var params io.Reader
	if data != nil {
		buf, err := json.Marshal(data)
		if err != nil {
			return nil, -1, err
		}
		params = bytes.NewBuffer(buf)
	}
	req, err := http.NewRequest(method, c.getURL(path), params)
	if err != nil {
		return nil, -1, err
	}
	req.Header.Set("User-Agent", userAgent)
	if data != nil {
		req.Header.Set("Content-Type", "application/json")
	} else if method == "POST" {
		req.Header.Set("Content-Type", "plain/text")
	}
	protocol := c.endpointURL.Scheme
	var resp *http.Response
	if protocol == "unix" {
		address := c.endpointURL.Path
		dial, err := net.Dial(protocol, address)
		if err != nil {
			return nil, -1, err
		}
		clientconn := httputil.NewClientConn(dial, nil)
		resp, err = clientconn.Do(req)
		defer clientconn.Close()
	} else {
		resp, err = c.client.Do(req)
	}
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return nil, -1, ErrConnectionRefused
		}
		return nil, -1, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, -1, err
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		return nil, resp.StatusCode, newError(resp.StatusCode, body)
	}
	return body, resp.StatusCode, nil
}

func (c *Client) stream(method, path string, in io.Reader, out io.Writer) error {
	if (method == "POST" || method == "PUT") && in == nil {
		in = bytes.NewReader(nil)
	}
	req, err := http.NewRequest(method, c.getURL(path), in)
	if err != nil {
		return err
	}
	req.Header.Set("User-Agent", userAgent)
	if method == "POST" {
		req.Header.Set("Content-Type", "plain/text")
	}
	resp, err := c.client.Do(req)
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return ErrConnectionRefused
		}
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		return newError(resp.StatusCode, body)
	}
	if resp.Header.Get("Content-Type") == "application/json" {
		dec := json.NewDecoder(resp.Body)
		for {
			var m jsonMessage
			if err := dec.Decode(&m); err == io.EOF {
				break
			} else if err != nil {
				return err
			}
			if m.Progress != "" {
				fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress)
			} else if m.Error != "" {
				return errors.New(m.Error)
			} else {
				fmt.Fprintf(out, "%s\n", m.Status)
			}
		}
	} else {
		if _, err := io.Copy(out, resp.Body); err != nil {
			return err
		}
	}
	return nil
}

func (c *Client) hijack(method, path string, setRawTerminal bool, in *os.File, errStream io.Writer, out io.Writer) error {
	req, err := http.NewRequest(method, c.getURL(path), nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "plain/text")
	protocol := c.endpointURL.Scheme
	if protocol != "unix" {
		protocol = "tcp"
	}
	dial, err := net.Dial(protocol, c.endpointURL.Host)
	if err != nil {
		return err
	}
	clientconn := httputil.NewClientConn(dial, nil)
	clientconn.Do(req)
	defer clientconn.Close()
	rwc, br := clientconn.Hijack()
	defer rwc.Close()
	errStdout := make(chan error, 1)
	go func() {
		_, err := io.Copy(out, br)
		errStdout <- err
	}()
	if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
		oldState, err := term.SetRawTerminal(in.Fd())
		if err != nil {
			return err
		}
		defer term.RestoreTerminal(in.Fd(), oldState)
	}
	go func() {
		io.Copy(rwc, in)
		if err := rwc.(*net.TCPConn).CloseWrite(); err != nil {
			fmt.Fprintf(errStream, "Couldn't send EOF: %s\n", err)
		}
	}()
	if err := <-errStdout; err != nil {
		return err
	}
	return nil
}

func (c *Client) getURL(path string) string {
	urlStr := strings.TrimRight(c.endpoint, "/")
	if c.endpointURL.Scheme == "unix" {
		urlStr = ""
	}
	return fmt.Sprintf("%s%s", urlStr, path)
}

type jsonMessage struct {
	Status   string `json:"status,omitempty"`
	Progress string `json:"progress,omitempty"`
	Error    string `json:"error,omitempty"`
}

func queryString(opts interface{}) string {
	if opts == nil {
		return ""
	}
	value := reflect.ValueOf(opts)
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}
	if value.Kind() != reflect.Struct {
		return ""
	}
	items := url.Values(map[string][]string{})
	for i := 0; i < value.NumField(); i++ {
		field := value.Type().Field(i)
		if field.PkgPath != "" {
			continue
		}
		key := field.Tag.Get("qs")
		if key == "" {
			key = strings.ToLower(field.Name)
		}
		v := value.Field(i)
		switch v.Kind() {
		case reflect.Bool:
			if v.Bool() {
				items.Add(key, "1")
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if v.Int() > 0 {
				items.Add(key, strconv.FormatInt(v.Int(), 10))
			}
		case reflect.Float32, reflect.Float64:
			if v.Float() > 0 {
				items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
			}
		case reflect.String:
			if v.String() != "" {
				items.Add(key, v.String())
			}
		case reflect.Ptr:
			if !v.IsNil() {
				if b, err := json.Marshal(v.Interface()); err == nil {
					items.Add(key, string(b))
				}
			}
		}
	}
	return items.Encode()
}

// Error represents failures in the API. It represents a failure from the API.
type Error struct {
	Status  int
	Message string
}

func newError(status int, body []byte) *Error {
	return &Error{Status: status, Message: string(body)}
}

func (e *Error) Error() string {
	return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
}

func parseEndpoint(endpoint string) (*url.URL, error) {
	u, err := url.Parse(endpoint)
	if err != nil {
		return nil, ErrInvalidEndpoint
	}
	if u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "unix" {
		return nil, ErrInvalidEndpoint
	}
	if u.Scheme != "unix" {
		_, port, err := net.SplitHostPort(u.Host)
		if err != nil {
			if e, ok := err.(*net.AddrError); ok {
				if e.Err == "missing port in address" {
					return u, nil
				}
			}
			return nil, ErrInvalidEndpoint
		}
		number, err := strconv.ParseInt(port, 10, 64)
		if err == nil && number > 0 && number < 65536 {
			return u, nil
		}
	} else {
		return u, nil // we don't need port when using a unix socket
	}
	return nil, ErrInvalidEndpoint
}
<commit_msg>Fix stream when using socket<commit_after>// Copyright 2013 go-dockerclient 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 docker provides a client for the Docker remote API.
//
// See http://goo.gl/mxyql for more details on the remote API.
package docker

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dotcloud/docker/term"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
)

const userAgent = "go-dockerclient"

var (
	// ErrInvalidEndpoint is returned when the endpoint is not a valid HTTP URL.
	ErrInvalidEndpoint = errors.New("Invalid endpoint")

	// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
	ErrConnectionRefused = errors.New("Cannot connect to Docker endpoint")
)

// Client is the basic type of this package. It provides methods for
// interaction with the API.
type Client struct {
	endpoint    string
	endpointURL *url.URL
	client      *http.Client
	out         io.WriteCloser
}

// NewClient returns a Client instance ready for communication with the
// given server endpoint.
func NewClient(endpoint string) (*Client, error) {
	u, err := parseEndpoint(endpoint)
	if err != nil {
		return nil, err
	}
	return &Client{
		endpoint:    endpoint,
		endpointURL: u,
		client:      http.DefaultClient,
		out:         os.Stdout,
	}, nil
}

func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) {
	var params io.Reader
	if data != nil {
		buf, err := json.Marshal(data)
		if err != nil {
			return nil, -1, err
		}
		params = bytes.NewBuffer(buf)
	}
	req, err := http.NewRequest(method, c.getURL(path), params)
	if err != nil {
		return nil, -1, err
	}
	req.Header.Set("User-Agent", userAgent)
	if data != nil {
		req.Header.Set("Content-Type", "application/json")
	} else if method == "POST" {
		req.Header.Set("Content-Type", "plain/text")
	}
	var resp *http.Response
	resp, err = c.execRequest(req)
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return nil, -1, ErrConnectionRefused
		}
		return nil, -1, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, -1, err
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		return nil, resp.StatusCode, newError(resp.StatusCode, body)
	}
	return body, resp.StatusCode, nil
}

func (c *Client) stream(method, path string, in io.Reader, out io.Writer) error {
	if (method == "POST" || method == "PUT") && in == nil {
		in = bytes.NewReader(nil)
	}
	req, err := http.NewRequest(method, c.getURL(path), in)
	if err != nil {
		return err
	}
	req.Header.Set("User-Agent", userAgent)
	if method == "POST" {
		req.Header.Set("Content-Type", "plain/text")
	}
	var resp *http.Response
	resp, err = c.execRequest(req)
	if err != nil {
		if strings.Contains(err.Error(), "connection refused") {
			return ErrConnectionRefused
		}
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		return newError(resp.StatusCode, body)
	}
	if resp.Header.Get("Content-Type") == "application/json" {
		dec := json.NewDecoder(resp.Body)
		for {
			var m jsonMessage
			if err := dec.Decode(&m); err == io.EOF {
				break
			} else if err != nil {
				return err
			}
			if m.Progress != "" {
				fmt.Fprintf(out, "%s %s\r", m.Status, m.Progress)
			} else if m.Error != "" {
				return errors.New(m.Error)
			} else {
				fmt.Fprintf(out, "%s\n", m.Status)
			}
		}
	} else {
		if _, err := io.Copy(out, resp.Body); err != nil {
			return err
		}
	}
	return nil
}

func (c *Client) hijack(method, path string, setRawTerminal bool, in *os.File, errStream io.Writer, out io.Writer) error {
	req, err := http.NewRequest(method, c.getURL(path), nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "plain/text")
	protocol := c.endpointURL.Scheme
	if protocol != "unix" {
		protocol = "tcp"
	}
	dial, err := net.Dial(protocol, c.endpointURL.Host)
	if err != nil {
		return err
	}
	clientconn := httputil.NewClientConn(dial, nil)
	clientconn.Do(req)
	defer clientconn.Close()
	rwc, br := clientconn.Hijack()
	defer rwc.Close()
	errStdout := make(chan error, 1)
	go func() {
		_, err := io.Copy(out, br)
		errStdout <- err
	}()
	if in != nil && setRawTerminal && term.IsTerminal(in.Fd()) && os.Getenv("NORAW") == "" {
		oldState, err := term.SetRawTerminal(in.Fd())
		if err != nil {
			return err
		}
		defer term.RestoreTerminal(in.Fd(), oldState)
	}
	go func() {
		io.Copy(rwc, in)
		if err := rwc.(*net.TCPConn).CloseWrite(); err != nil {
			fmt.Fprintf(errStream, "Couldn't send EOF: %s\n", err)
		}
	}()
	if err := <-errStdout; err != nil {
		return err
	}
	return nil
}

func (c *Client) getURL(path string) string {
	urlStr := strings.TrimRight(c.endpoint, "/")
	if c.endpointURL.Scheme == "unix" {
		urlStr = ""
	}
	return fmt.Sprintf("%s%s", urlStr, path)
}

type jsonMessage struct {
	Status   string `json:"status,omitempty"`
	Progress string `json:"progress,omitempty"`
	Error    string `json:"error,omitempty"`
}

func queryString(opts interface{}) string {
	if opts == nil {
		return ""
	}
	value := reflect.ValueOf(opts)
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}
	if value.Kind() != reflect.Struct {
		return ""
	}
	items := url.Values(map[string][]string{})
	for i := 0; i < value.NumField(); i++ {
		field := value.Type().Field(i)
		if field.PkgPath != "" {
			continue
		}
		key := field.Tag.Get("qs")
		if key == "" {
			key = strings.ToLower(field.Name)
		}
		v := value.Field(i)
		switch v.Kind() {
		case reflect.Bool:
			if v.Bool() {
				items.Add(key, "1")
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if v.Int() > 0 {
				items.Add(key, strconv.FormatInt(v.Int(), 10))
			}
		case reflect.Float32, reflect.Float64:
			if v.Float() > 0 {
				items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
			}
		case reflect.String:
			if v.String() != "" {
				items.Add(key, v.String())
			}
		case reflect.Ptr:
			if !v.IsNil() {
				if b, err := json.Marshal(v.Interface()); err == nil {
					items.Add(key, string(b))
				}
			}
		}
	}
	return items.Encode()
}

// Error represents failures in the API. It represents a failure from the API.
type Error struct {
	Status  int
	Message string
}

func newError(status int, body []byte) *Error {
	return &Error{Status: status, Message: string(body)}
}

func (e *Error) Error() string {
	return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
}

func parseEndpoint(endpoint string) (*url.URL, error) {
	u, err := url.Parse(endpoint)
	if err != nil {
		return nil, ErrInvalidEndpoint
	}
	if u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "unix" {
		return nil, ErrInvalidEndpoint
	}
	if u.Scheme != "unix" {
		_, port, err := net.SplitHostPort(u.Host)
		if err != nil {
			if e, ok := err.(*net.AddrError); ok {
				if e.Err == "missing port in address" {
					return u, nil
				}
			}
			return nil, ErrInvalidEndpoint
		}
		number, err := strconv.ParseInt(port, 10, 64)
		if err == nil && number > 0 && number < 65536 {
			return u, nil
		}
	} else {
		return u, nil // we don't need port when using a unix socket
	}
	return nil, ErrInvalidEndpoint
}

func (c *Client) execRequest(req *http.Request) (resp *http.Response, err error) {
	protocol := c.endpointURL.Scheme
	address := c.endpointURL.Path
	if protocol == "unix" {
		dial, err := net.Dial(protocol, address)
		if err != nil {
			return nil, err
		}
		clientconn := httputil.NewClientConn(dial, nil)
		resp, err = clientconn.Do(req)
		defer clientconn.Close()
	} else {
		resp, err = c.client.Do(req)
	}
	return resp, err
}
<|endoftext|>


<commit_before>package prisclient

import (
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/priscillachat/prislog"
	"io"
	"net"
	"time"
)

type Client struct {
	raw        net.Conn
	decoder    *json.Decoder
	encoder    *json.Encoder
	SourceId   string
	clientType string
	logger     *prislog.PrisLog
	autoRetry  bool
	host       string
	port       string
}

type CommandBlock struct {
	Id      string   `json:"id,omitempty"`
	Action  string   `json:"action,omitempty"`
	Type    string   `json:"type,omitempty"`
	Time    int64    `json:"time,omitempty"`
	Data    string   `json:"data,omitempty"`
	Array   []string `json:"array,omitempty"`
	Options []string `json:"options,omitempty"`
}

type MessageBlock struct {
	Message       string   `json:"message,omitempty"`
	From          string   `json:"from,omitempty"`
	Room          string   `json:"room,omitempty"`
	Mentioned     bool     `json:"mentioned,omitempty"`
	Stripped      string   `json:"stripped,omitempty"`
	MentionNotify []string `json:"mentionnotify,omitempty"`
}

type Query struct {
	Type    string        `json:"type,omitempty"`
	Source  string        `json:"source,omitempty"`
	To      string        `json:"to,omitempty"`
	Command *CommandBlock `json:"command,omitempty"`
	Message *MessageBlock `json:"message,omitempty"`
}

func RandomId() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)
}

func NewClient(host, port, clientType, sourceid string, autoretry bool,
	logger *prislog.PrisLog) (*Client, error) {

	if clientType != "adapter" && clientType != "responder" {
		return nil, errors.New("client type has to be adapter or responder")
	}

	pris := new(Client)

	pris.logger = logger
	pris.SourceId = sourceid
	pris.clientType = clientType
	pris.autoRetry = autoretry
	pris.host = host
	pris.port = port

	return pris, nil
}

func (pris *Client) connect() error {
	conn, err := net.Dial("tcp", pris.host+":"+pris.port)

	if err != nil {
		return err
	}

	// we can't assign it to pris until the engagement is complete, because as
	// soon as the encoder/decoder is assigned, communication starts
	decoder := json.NewDecoder(conn)
	encoder := json.NewEncoder(conn)

	err = encoder.Encode(&Query{
		Type:   "command",
		Source: pris.SourceId,
		To:     "server",
		Command: &CommandBlock{
			Id:     RandomId(),
			Action: "engage",
			Type:   pris.clientType,
			Time:   time.Now().Unix(),
		},
	})

	if err != nil {
		return err
	}

	ack := Query{}

	err = decoder.Decode(&ack)

	if err != nil {
		return err
	}

	if ack.Type != "command" || ack.Command.Action != "proceed" {
		pris.logger.Error.Println("Unexpected response from server:",
			ack.Command)
		return errors.New("Unexpected response from server")
	}

	pris.encoder = encoder
	pris.decoder = decoder
	pris.raw = conn

	if err != nil {
		pris.logger.Error.Println("Failed to engage:", err)
		return err
	}

	pris.logger.Info.Println("Priscilla engaged")

	return nil
}

func (pris *Client) disconnect() {
	if pris.raw != nil {
		pris.raw.(*net.TCPConn).Close()
	}
}

func (pris *Client) listen(out chan<- *Query) {
	for err := pris.connect(); err != nil; err = pris.connect() {
		if err == nil {
			break
		}

		pris.logger.Error.Println("Error connecting to priscilla server")

		if pris.autoRetry {
			pris.disconnect()
			pris.logger.Error.Println("Auto retry in 5 seconds...")
			time.Sleep(5 * time.Second)
		} else {
			pris.logger.Error.Fatal(
				"Error connecting to priscilla server, and autoRetry is not set")
		}
	}

	var q *Query
	for {
		q = new(Query)
		err := pris.decoder.Decode(q)

		if err != nil {
			fmt.Println(err)
			if err.Error() == "EOF" {
				pris.logger.Error.Println("Priscilla disconnected")
			} else {
				pris.logger.Error.Println("Priscilla connection error:", err)
				pris.disconnect()
			}

			if pris.autoRetry {
				pris.logger.Error.Println("Auto reconnect in 5 seconds...")
				time.Sleep(5 * time.Second)
				err := pris.connect()
				if err != nil {
					pris.logger.Error.Println("Connect error:", err)
				}
			} else {
				out <- &Query{
					Type:   "command",
					Source: "pris",
					Command: &CommandBlock{
						Action: "disengage",
					},
				}
				break
			}
			// }
		}

		if pris.ValidateQuery(q) {
			pris.logger.Debug.Println("Query received:", *q)
			if q.Type == "message" {
				pris.logger.Debug.Println("Message:", q.Message.Message)
				pris.logger.Debug.Println("From:", q.Message.From)
				pris.logger.Debug.Println("Room:", q.Message.Room)
			}
			out <- q
		} else {
			pris.logger.Error.Println("Invalid query from server(?!!):", q)
		}
	}
}

func (pris *Client) Run(toPris <-chan *Query, fromPris chan<- *Query) {

	go pris.listen(fromPris)
	var q *Query
	for {
		q = <-toPris
		if pris.ValidateQuery(q) {
			if pris.encoder != nil {
				pris.encoder.Encode(q)
			}
		} else {
			pris.logger.Error.Println("Invalid query:", q)
		}
	}
}

func (pris *Client) ValidateQuery(q *Query) bool {
	switch {
	case q.Type == "command" && q.Command != nil:
		return true
	case q.Type == "message" && q.Message != nil && q.Message.Room != "":
		return true
	}
	return false
}
<commit_msg>engagement authentication<commit_after>package prisclient

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/priscillachat/prislog"
	"io"
	"net"
	"time"
)

type Client struct {
	raw        net.Conn
	decoder    *json.Decoder
	encoder    *json.Encoder
	sourceId   string
	clientType string
	logger     *prislog.PrisLog
	autoRetry  bool
	host       string
	port       string
	secret     string
}

type CommandBlock struct {
	Id      string   `json:"id,omitempty"`
	Action  string   `json:"action,omitempty"`
	Type    string   `json:"type,omitempty"`
	Time    int64    `json:"time,omitempty"`
	Data    string   `json:"data,omitempty"`
	Array   []string `json:"array,omitempty"`
	Options []string `json:"options,omitempty"`
}

type MessageBlock struct {
	Message       string   `json:"message,omitempty"`
	From          string   `json:"from,omitempty"`
	Room          string   `json:"room,omitempty"`
	Mentioned     bool     `json:"mentioned,omitempty"`
	Stripped      string   `json:"stripped,omitempty"`
	MentionNotify []string `json:"mentionnotify,omitempty"`
}

type Query struct {
	Type    string        `json:"type,omitempty"`
	Source  string        `json:"source,omitempty"`
	To      string        `json:"to,omitempty"`
	Command *CommandBlock `json:"command,omitempty"`
	Message *MessageBlock `json:"message,omitempty"`
}

func RandomId() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)
}

func NewClient(host, port, clientType, sourceId, secret string, autoretry bool,
	logger *prislog.PrisLog) (*Client, error) {

	if clientType != "adapter" && clientType != "responder" {
		return nil, errors.New("client type has to be adapter or responder")
	}

	pris := new(Client)

	pris.logger = logger
	pris.sourceId = sourceId
	pris.clientType = clientType
	pris.autoRetry = autoretry
	pris.host = host
	pris.port = port
	pris.secret = secret

	return pris, nil
}

func (pris *Client) connect() error {
	conn, err := net.Dial("tcp", pris.host+":"+pris.port)

	if err != nil {
		return err
	}

	// we can't assign it to pris until the engagement is complete, because as
	// soon as the encoder/decoder is assigned, communication starts
	decoder := json.NewDecoder(conn)
	encoder := json.NewEncoder(conn)

	timestamp := time.Now().Unix()
	authMsg := fmt.Sprintf("%d%s%s", timestamp, pris.sourceId, pris.secret)

	mac := hmac.New(sha256.New, []byte(pris.secret))
	mac.Write([]byte(authMsg))

	err = encoder.Encode(&Query{
		Type:   "command",
		Source: pris.sourceId,
		To:     "server",
		Command: &CommandBlock{
			Id:     RandomId(),
			Action: "engage",
			Type:   pris.clientType,
			Time:   timestamp,
			Data:   base64.StdEncoding.EncodeToString(mac.Sum(nil)),
		},
	})

	if err != nil {
		return err
	}

	ack := Query{}

	err = decoder.Decode(&ack)

	if err != nil {
		return err
	}

	if ack.Type != "command" || ack.Command.Action != "proceed" {
		pris.logger.Error.Fatal("Unexpected response from server:",
			ack.Command)
		return errors.New("Unexpected response from server")
	}

	pris.sourceId = ack.Command.Data

	pris.encoder = encoder
	pris.decoder = decoder
	pris.raw = conn

	if err != nil {
		pris.logger.Error.Println("Failed to engage:", err)
		return err
	}

	pris.logger.Info.Println("Priscilla engaged")

	return nil
}

func (pris *Client) disconnect() {
	if pris.raw != nil {
		pris.raw.(*net.TCPConn).Close()
	}
}

func (pris *Client) listen(out chan<- *Query) {
	for err := pris.connect(); err != nil; err = pris.connect() {
		if err == nil {
			break
		}

		pris.logger.Error.Println("Error connecting to priscilla server")

		if pris.autoRetry {
			pris.disconnect()
			pris.logger.Error.Println("Auto retry in 5 seconds...")
			time.Sleep(5 * time.Second)
		} else {
			pris.logger.Error.Fatal(
				"Error connecting to priscilla server, and autoRetry is not set")
		}
	}

	var q *Query
	for {
		q = new(Query)
		err := pris.decoder.Decode(q)

		if err != nil {
			fmt.Println(err)
			if err.Error() == "EOF" {
				pris.logger.Error.Println("Priscilla disconnected")
			} else {
				pris.logger.Error.Println("Priscilla connection error:", err)
				pris.disconnect()
			}

			if pris.autoRetry {
				pris.logger.Error.Println("Auto reconnect in 5 seconds...")
				time.Sleep(5 * time.Second)
				err := pris.connect()
				if err != nil {
					pris.logger.Error.Println("Connect error:", err)
				}
			} else {
				out <- &Query{
					Type:   "command",
					Source: "pris",
					Command: &CommandBlock{
						Action: "disengage",
					},
				}
				break
			}
			// }
		}

		if pris.ValidateQuery(q) {
			pris.logger.Debug.Println("Query received:", *q)
			if q.Type == "message" {
				pris.logger.Debug.Println("Message:", q.Message.Message)
				pris.logger.Debug.Println("From:", q.Message.From)
				pris.logger.Debug.Println("Room:", q.Message.Room)
			}
			out <- q
		} else {
			pris.logger.Error.Println("Invalid query from server(?!!):", q)
		}
	}
}

func (pris *Client) Run(toPris <-chan *Query, fromPris chan<- *Query) {

	go pris.listen(fromPris)
	var q *Query
	for {
		q = <-toPris
		if pris.ValidateQuery(q) {
			if pris.encoder != nil {
				pris.encoder.Encode(q)
			}
		} else {
			pris.logger.Error.Println("Invalid query:", q)
		}
	}
}

func (pris *Client) ValidateQuery(q *Query) bool {
	switch {
	case q.Type == "command" && q.Command != nil:
		return true
	case q.Type == "message" && q.Message != nil && q.Message.Room != "":
		return true
	}
	return false
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package sse

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	backoff "gopkg.in/cenkalti/backoff.v1"
)

var (
	headerID    = []byte("id:")
	headerData  = []byte("data:")
	headerEvent = []byte("event:")
	headerRetry = []byte("retry:")
)

// ConnCallback defines a function to be called on a particular connection event
type ConnCallback func(c *Client)

// Client handles an incoming server stream
type Client struct {
	URL            string
	Connection     *http.Client
	Retry          time.Time
	subscribed     map[chan *Event]chan bool
	Headers        map[string]string
	EncodingBase64 bool
	EventID        string
	disconnectcb   ConnCallback
	mu             sync.Mutex
}

// NewClient creates a new client
func NewClient(url string) *Client {
	return &Client{
		URL:        url,
		Connection: &http.Client{},
		Headers:    make(map[string]string),
		subscribed: make(map[chan *Event]chan bool),
	}
}

// Subscribe to a data stream
func (c *Client) Subscribe(stream string, handler func(msg *Event)) error {
	operation := func() error {
		resp, err := c.request(stream)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		reader := NewEventStreamReader(resp.Body)

		for {
			// Read each new line and process the type of event
			event, err := reader.ReadEvent()
			if err != nil {
				if err == io.EOF {
					return nil
				}

				// run user specified disconnect function
				if c.disconnectcb != nil {
					c.disconnectcb(c)
				}

				return err
			}

			// If we get an error, ignore it.
			if msg, err := c.processEvent(event); err == nil {
				if len(msg.ID) > 0 {
					c.EventID = string(msg.ID)
				} else {
					msg.ID = []byte(c.EventID)
				}

				handler(msg)
			}
		}
	}
	return backoff.Retry(operation, backoff.NewExponentialBackOff())
}

// SubscribeChan sends all events to the provided channel
func (c *Client) SubscribeChan(stream string, ch chan *Event) error {
	var connected bool
	errch := make(chan error)
	c.subscribed[ch] = make(chan bool)

	go func() {
		operation := func() error {
			resp, err := c.request(stream)
			if err != nil {
				c.cleanup(resp, ch)
				return err
			}

			if resp.StatusCode != 200 {
				c.cleanup(resp, ch)
				return errors.New("could not connect to stream")
			}

			if !connected {
				errch <- nil
				connected = true
			}

			reader := NewEventStreamReader(resp.Body)

			for {
				// Read each new line and process the type of event
				event, err := reader.ReadEvent()
				if err != nil {
					if err == io.EOF {
						c.cleanup(resp, ch)
						return nil
					}

					// run user specified disconnect function
					if c.disconnectcb != nil {
						c.disconnectcb(c)
					}

					return err
				}

				// If we get an error, ignore it.
				if msg, err := c.processEvent(event); err == nil {
					if len(msg.ID) > 0 {
						c.EventID = string(msg.ID)
					} else {
						msg.ID = []byte(c.EventID)
					}

					select {
					case <-c.subscribed[ch]:
						c.cleanup(resp, ch)
						return nil
					case ch <- msg:
						// message sent
					}
				}
			}
		}

		err := backoff.Retry(operation, backoff.NewExponentialBackOff())
		if err != nil && !connected {
			errch <- err
		}
	}()
	err := <-errch
	close(errch)

	return err
}

// SubscribeRaw to an sse endpoint
func (c *Client) SubscribeRaw(handler func(msg *Event)) error {
	return c.Subscribe("", handler)
}

// SubscribeChanRaw sends all events to the provided channel
func (c *Client) SubscribeChanRaw(ch chan *Event) error {
	return c.SubscribeChan("", ch)
}

// Unsubscribe unsubscribes a channel
func (c *Client) Unsubscribe(ch chan *Event) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.subscribed[ch] != nil {
		c.subscribed[ch] <- true
	}
}

// OnDisconnect specifies the function to run when the connection disconnects
func (c *Client) OnDisconnect(fn ConnCallback) {
	c.disconnectcb = fn
}

func (c *Client) request(stream string) (*http.Response, error) {
	req, err := http.NewRequest("GET", c.URL, nil)
	if err != nil {
		return nil, err
	}

	// Setup request, specify stream to connect to
	if stream != "" {
		query := req.URL.Query()
		query.Add("stream", stream)
		req.URL.RawQuery = query.Encode()
	}

	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Accept", "text/event-stream")
	req.Header.Set("Connection", "keep-alive")

	if c.EventID != "" {
		req.Header.Set("Last-Event-ID", c.EventID)
	}

	// Add user specified headers
	for k, v := range c.Headers {
		req.Header.Set(k, v)
	}

	return c.Connection.Do(req)
}

func (c *Client) processEvent(msg []byte) (event *Event, err error) {
	var e Event

	if len(msg) < 1 {
		return nil, errors.New("event message was empty")
	}

	// Normalize the crlf to lf to make it easier to split the lines.
	bytes.Replace(msg, []byte("\n\r"), []byte("\n"), -1)
	// Split the line by "\n" or "\r", per the spec.
	for _, line := range bytes.FieldsFunc(msg, func(r rune) bool { return r == '\n' || r == '\r' }) {
		switch {
		case bytes.HasPrefix(line, headerID):
			e.ID = trimHeader(len(headerID), line)
		case bytes.HasPrefix(line, headerData):
			// The spec allows for multiple data fields per event, concatenated them with "\n".
			e.Data = append(append(trimHeader(len(headerData), line), e.Data[:]...), byte('\n'))
		// The spec says that a line that simply contains the string "data" should be treated as a data field with an empty body.
		case bytes.Equal(line, bytes.TrimSuffix(headerData, []byte(":"))):
			e.Data = append(e.Data, byte('\n'))
		case bytes.HasPrefix(line, headerEvent):
			e.Event = trimHeader(len(headerEvent), line)
		case bytes.HasPrefix(line, headerRetry):
			e.Retry = trimHeader(len(headerRetry), line)
		default:
			// Ignore any garbage that doesn't match what we're looking for.
		}
	}

	// Trim the last "\n" per the spec.
	e.Data = bytes.TrimSuffix(e.Data, []byte("\n"))

	if c.EncodingBase64 {
		buf := make([]byte, base64.StdEncoding.DecodedLen(len(e.Data)))

		_, err := base64.StdEncoding.Decode(buf, e.Data)
		if err != nil {
			err = fmt.Errorf("failed to decode event message: %s", err)
		}
		e.Data = buf
	}
	return &e, err
}

func (c *Client) cleanup(resp *http.Response, ch chan *Event) {
	if resp != nil {
		resp.Body.Close()
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	if c.subscribed[ch] != nil {
		close(c.subscribed[ch])
		close(ch)
		delete(c.subscribed, ch)
	}
}

func trimHeader(size int, data []byte) []byte {
	data = data[size:]
	// Remove optional leading whitespace
	if data[0] == 32 {
		data = data[1:]
	}
	// Remove trailing new line
	if len(data) > 0 && data[len(data)-1] == 10 {
		data = data[:len(data)-1]
	}
	return data
}
<commit_msg>Issue #48: Wrapped access to subscribed map in mutex.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package sse

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	backoff "gopkg.in/cenkalti/backoff.v1"
)

var (
	headerID    = []byte("id:")
	headerData  = []byte("data:")
	headerEvent = []byte("event:")
	headerRetry = []byte("retry:")
)

// ConnCallback defines a function to be called on a particular connection event
type ConnCallback func(c *Client)

// Client handles an incoming server stream
type Client struct {
	URL            string
	Connection     *http.Client
	Retry          time.Time
	subscribed     map[chan *Event]chan bool
	Headers        map[string]string
	EncodingBase64 bool
	EventID        string
	disconnectcb   ConnCallback
	mu             sync.Mutex
}

// NewClient creates a new client
func NewClient(url string) *Client {
	return &Client{
		URL:        url,
		Connection: &http.Client{},
		Headers:    make(map[string]string),
		subscribed: make(map[chan *Event]chan bool),
	}
}

// Subscribe to a data stream
func (c *Client) Subscribe(stream string, handler func(msg *Event)) error {
	operation := func() error {
		resp, err := c.request(stream)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		reader := NewEventStreamReader(resp.Body)

		for {
			// Read each new line and process the type of event
			event, err := reader.ReadEvent()
			if err != nil {
				if err == io.EOF {
					return nil
				}

				// run user specified disconnect function
				if c.disconnectcb != nil {
					c.disconnectcb(c)
				}

				return err
			}

			// If we get an error, ignore it.
			if msg, err := c.processEvent(event); err == nil {
				if len(msg.ID) > 0 {
					c.EventID = string(msg.ID)
				} else {
					msg.ID = []byte(c.EventID)
				}

				handler(msg)
			}
		}
	}
	return backoff.Retry(operation, backoff.NewExponentialBackOff())
}

// SubscribeChan sends all events to the provided channel
func (c *Client) SubscribeChan(stream string, ch chan *Event) error {
	var connected bool
	errch := make(chan error)
	c.mu.Lock()
	c.subscribed[ch] = make(chan bool)
	c.mu.Unlock()

	go func() {
		operation := func() error {
			resp, err := c.request(stream)
			if err != nil {
				c.cleanup(resp, ch)
				return err
			}

			if resp.StatusCode != 200 {
				c.cleanup(resp, ch)
				return errors.New("could not connect to stream")
			}

			if !connected {
				errch <- nil
				connected = true
			}

			reader := NewEventStreamReader(resp.Body)

			for {
				// Read each new line and process the type of event
				event, err := reader.ReadEvent()
				if err != nil {
					if err == io.EOF {
						c.cleanup(resp, ch)
						return nil
					}

					// run user specified disconnect function
					if c.disconnectcb != nil {
						c.disconnectcb(c)
					}

					return err
				}

				// If we get an error, ignore it.
				if msg, err := c.processEvent(event); err == nil {
					if len(msg.ID) > 0 {
						c.EventID = string(msg.ID)
					} else {
						msg.ID = []byte(c.EventID)
					}

					select {
					case <-c.subscribed[ch]:
						c.cleanup(resp, ch)
						return nil
					case ch <- msg:
						// message sent
					}
				}
			}
		}

		err := backoff.Retry(operation, backoff.NewExponentialBackOff())
		if err != nil && !connected {
			errch <- err
		}
	}()
	err := <-errch
	close(errch)

	return err
}

// SubscribeRaw to an sse endpoint
func (c *Client) SubscribeRaw(handler func(msg *Event)) error {
	return c.Subscribe("", handler)
}

// SubscribeChanRaw sends all events to the provided channel
func (c *Client) SubscribeChanRaw(ch chan *Event) error {
	return c.SubscribeChan("", ch)
}

// Unsubscribe unsubscribes a channel
func (c *Client) Unsubscribe(ch chan *Event) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.subscribed[ch] != nil {
		c.subscribed[ch] <- true
	}
}

// OnDisconnect specifies the function to run when the connection disconnects
func (c *Client) OnDisconnect(fn ConnCallback) {
	c.disconnectcb = fn
}

func (c *Client) request(stream string) (*http.Response, error) {
	req, err := http.NewRequest("GET", c.URL, nil)
	if err != nil {
		return nil, err
	}

	// Setup request, specify stream to connect to
	if stream != "" {
		query := req.URL.Query()
		query.Add("stream", stream)
		req.URL.RawQuery = query.Encode()
	}

	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Accept", "text/event-stream")
	req.Header.Set("Connection", "keep-alive")

	if c.EventID != "" {
		req.Header.Set("Last-Event-ID", c.EventID)
	}

	// Add user specified headers
	for k, v := range c.Headers {
		req.Header.Set(k, v)
	}

	return c.Connection.Do(req)
}

func (c *Client) processEvent(msg []byte) (event *Event, err error) {
	var e Event

	if len(msg) < 1 {
		return nil, errors.New("event message was empty")
	}

	// Normalize the crlf to lf to make it easier to split the lines.
	bytes.Replace(msg, []byte("\n\r"), []byte("\n"), -1)
	// Split the line by "\n" or "\r", per the spec.
	for _, line := range bytes.FieldsFunc(msg, func(r rune) bool { return r == '\n' || r == '\r' }) {
		switch {
		case bytes.HasPrefix(line, headerID):
			e.ID = trimHeader(len(headerID), line)
		case bytes.HasPrefix(line, headerData):
			// The spec allows for multiple data fields per event, concatenated them with "\n".
			e.Data = append(append(trimHeader(len(headerData), line), e.Data[:]...), byte('\n'))
		// The spec says that a line that simply contains the string "data" should be treated as a data field with an empty body.
		case bytes.Equal(line, bytes.TrimSuffix(headerData, []byte(":"))):
			e.Data = append(e.Data, byte('\n'))
		case bytes.HasPrefix(line, headerEvent):
			e.Event = trimHeader(len(headerEvent), line)
		case bytes.HasPrefix(line, headerRetry):
			e.Retry = trimHeader(len(headerRetry), line)
		default:
			// Ignore any garbage that doesn't match what we're looking for.
		}
	}

	// Trim the last "\n" per the spec.
	e.Data = bytes.TrimSuffix(e.Data, []byte("\n"))

	if c.EncodingBase64 {
		buf := make([]byte, base64.StdEncoding.DecodedLen(len(e.Data)))

		_, err := base64.StdEncoding.Decode(buf, e.Data)
		if err != nil {
			err = fmt.Errorf("failed to decode event message: %s", err)
		}
		e.Data = buf
	}
	return &e, err
}

func (c *Client) cleanup(resp *http.Response, ch chan *Event) {
	if resp != nil {
		resp.Body.Close()
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	if c.subscribed[ch] != nil {
		close(c.subscribed[ch])
		close(ch)
		delete(c.subscribed, ch)
	}
}

func trimHeader(size int, data []byte) []byte {
	data = data[size:]
	// Remove optional leading whitespace
	if data[0] == 32 {
		data = data[1:]
	}
	// Remove trailing new line
	if len(data) > 0 && data[len(data)-1] == 10 {
		data = data[:len(data)-1]
	}
	return data
}
<|endoftext|>


<commit_before>package gohttp

import (
	"bytes"
	"crypto/tls"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type Request struct {
	*http.Request
}

func (req *Request) SetHeader(k, v string) *Request {
	req.Header.Add(k, v)
	return req
}

func (req *Request) SetHeaders(kv map[string]string) *Request {
	for k, v := range kv {
		req.SetHeader(k, v)
	}
	return req
}

type Response struct {
	*http.Response
}

func (resp *Response) Code() int {
	return resp.StatusCode
}

func (resp *Response) Bytes() ([]byte, error) {
	return ioutil.ReadAll(resp.Body)
}

func (resp *Response) String() string {
	if response, err := resp.Bytes(); err != nil {
		return ""
	} else {
		return string(response)
	}

}

func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
	req, err := http.NewRequest(method, urlStr, body)
	return &Request{req}, err
}

type Client struct {
	method     string
	url        string
	path       string
	query      string
	fragment   string
	cookies    map[string]string
	headers    map[string]string
	body       interface{}
	proxy      string
	timeout    int
	retries    int
	verifySsl  bool //true:强制使用https,false:不校验https证书
	keepAlived bool
	transport  http.Transport
}

func NewClient() *Client {
	return &Client{
		method:     "GET",
		cookies:    make(map[string]string),
		headers:    make(map[string]string),
		verifySsl:  false,
		keepAlived: true,
	}
}

//构造request body [interface{} -> io.Reader]
func parseBody(v interface{}) (io.Reader, error) {
	if v == nil {
		return nil, nil
	}
	bts, err := ToBytes(v)
	if err != nil {
		return nil, err
	}
	body := bytes.NewBuffer(bts)
	return body, nil
}

func (c *Client) newRequest() (*http.Request, error) {
	u, err := c.newURL()
	if err != nil {
		return nil, err
	}

	body, err := parseBody(c.body)
	if err != nil {
		return nil, err
	}

	req, err := NewRequest(c.method, u.String(), body)
	for k, v := range c.headers {
		req.Header.Set(k, v)
	}
	for k, v := range c.cookies {
		req.AddCookie(&http.Cookie{Name: k, Value: v})
	}
	return req.Request, err
}

func (c *Client) setClient() (*http.Client, error) {
	if c.proxy != "" {
		proxy, err := url.Parse(c.proxy)
		if err != nil {
			return nil, err
		}
		c.transport.Proxy = http.ProxyURL(proxy)
	}
	c.transport.DisableKeepAlives = !c.keepAlived
	c.transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: !c.verifySsl}
	client := &http.Client{Transport: &c.transport}
	client.Timeout = time.Duration(c.timeout) * time.Second
	return client, nil
}

func (c *Client) Reset() *Client {
	c.method = "GET"
	c.url = ""
	c.path = ""
	c.query = ""
	c.fragment = ""
	c.cookies = make(map[string]string)
	c.headers = make(map[string]string)
	c.body = nil
	c.proxy = ""
	c.timeout = 0
	c.retries = 0
	c.verifySsl = false
	c.keepAlived = true
	c.transport = http.Transport{}
	return c
}

func (c *Client) doReq(method string) (*Response, error) {
	c.method = method
	req, err := c.newRequest()
	if err != nil {
		return nil, err
	}

	client, err := c.setClient()
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	return &Response{resp}, err
}

// 长连接,Default is true
func (c *Client) KeepAlived(used bool) *Client {
	c.keepAlived = used
	return c
}

// 强制使用HTTPS,Default is false
func (c *Client) VerifySSL(used bool) *Client {
	c.verifySsl = used
	return c
}

func (c *Client) URL(urlstr string) *Client {
	c.url = urlstr
	return c
}

func (c *Client) Path(path string) *Client {
	c.path += path
	return c
}

func (c *Client) Query(kv map[string]string) *Client {
	query := []string{}
	for k, v := range kv {
		s := k + "=" + url.QueryEscape(v)
		query = append(query, s)
	}
	c.query = strings.Join(query, "&")
	return c
}

func (c *Client) Proxy(proxy string) *Client {
	c.proxy = proxy
	return c
}

func (c *Client) Timeout(timeout int) *Client {
	c.timeout = timeout
	return c
}

func (c *Client) Cookie(k, v string) *Client {
	c.cookies[k] = v
	return c
}

func (c *Client) Header(k, v string) *Client {
	c.headers[k] = v
	return c
}

func (c *Client) Headers(kv map[string]string) *Client {
	for k, v := range kv {
		c.Header(k, v)
	}
	return c
}

func (c *Client) Body(body interface{}) *Client {
	c.body = body
	return c
}

func (c *Client) Retries(count int) *Client {
	c.retries = count
	return c
}

func (c *Client) newURL() (*url.URL, error) {
	u, err := url.Parse(c.url)
	if err != nil {
		return u, err
	}
	if c.path != "" {
		u.Path = c.path
	}
	if c.query != "" {
		u.RawQuery = c.query
	}
	return u, err
}

func (c *Client) URLString() string {
	url, _ := c.newURL()
	return url.String()
}

func (c *Client) Get() (*Response, error)  { return c.doReq("GET") }
func (c *Client) Post() (*Response, error) { return c.doReq("POST") }
func (c *Client) Head() (*Response, error) { return c.doReq("HEAD") }
func (c *Client) Put() (*Response, error)  { return c.doReq("PUT") }
<commit_msg>Add http.Client<commit_after>package gohttp

import (
	"bytes"
	"crypto/tls"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type Request struct {
	*http.Request
}

func (req *Request) SetHeader(k, v string) *Request {
	req.Header.Add(k, v)
	return req
}

func (req *Request) SetHeaders(kv map[string]string) *Request {
	for k, v := range kv {
		req.SetHeader(k, v)
	}
	return req
}

type Response struct {
	*http.Response
}

func (resp *Response) Code() int {
	return resp.StatusCode
}

func (resp *Response) Bytes() ([]byte, error) {
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

func (resp *Response) String() string {
	if response, err := resp.Bytes(); err != nil {
		return ""
	} else {
		return string(response)
	}

}

func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
	req, err := http.NewRequest(method, urlStr, body)
	return &Request{req}, err
}

type Client struct {
	client     *http.Client
	method     string
	url        string
	path       string
	query      string
	fragment   string
	cookies    map[string]string
	headers    map[string]string
	body       interface{}
	proxy      string
	timeout    int
	retries    int
	verifySsl  bool //true:强制使用https,false:不校验https证书
	keepAlived bool
	transport  http.Transport
}

func NewClient() *Client {
	return &Client{
		method:     "GET",
		cookies:    make(map[string]string),
		headers:    make(map[string]string),
		verifySsl:  false,
		keepAlived: true,
	}
}

//构造request body [interface{} -> io.Reader]
func parseBody(v interface{}) (io.Reader, error) {
	if v == nil {
		return nil, nil
	}
	bts, err := ToBytes(v)
	if err != nil {
		return nil, err
	}
	body := bytes.NewBuffer(bts)
	return body, nil
}

func (c *Client) newRequest() (*http.Request, error) {
	u, err := c.newURL()
	if err != nil {
		return nil, err
	}

	body, err := parseBody(c.body)
	if err != nil {
		return nil, err
	}

	req, err := NewRequest(c.method, u.String(), body)
	for k, v := range c.headers {
		req.Header.Set(k, v)
	}
	for k, v := range c.cookies {
		req.AddCookie(&http.Cookie{Name: k, Value: v})
	}
	return req.Request, err
}

func (c *Client) setClient() error {
	if c.proxy != "" {
		proxy, err := url.Parse(c.proxy)
		if err != nil {
			return err
		}
		c.transport.Proxy = http.ProxyURL(proxy)
	}
	c.transport.DisableKeepAlives = !c.keepAlived
	c.transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: !c.verifySsl}
	c.client = &http.Client{Transport: &c.transport}
	c.client.Timeout = time.Duration(c.timeout) * time.Second
	return nil
}

func (c *Client) Reset() *Client {
	c.method = "GET"
	c.url = ""
	c.path = ""
	c.query = ""
	c.fragment = ""
	c.cookies = make(map[string]string)
	c.headers = make(map[string]string)
	c.body = nil
	c.proxy = ""
	c.timeout = 0
	c.retries = 0
	c.verifySsl = false
	c.keepAlived = true
	c.transport = http.Transport{}
	return c
}

func (c *Client) doReq(method string) (*Response, error) {
	c.method = method
	req, err := c.newRequest()
	if err != nil {
		return nil, err
	}

	err = c.setClient()
	if err != nil {
		return nil, err
	}

	resp, err := c.client.Do(req)
	return &Response{resp}, err
}

// 长连接,Default is true
func (c *Client) KeepAlived(used bool) *Client {
	c.keepAlived = used
	return c
}

// 强制使用HTTPS,Default is false
func (c *Client) VerifySSL(used bool) *Client {
	c.verifySsl = used
	return c
}

func (c *Client) URL(urlstr string) *Client {
	c.url = urlstr
	return c
}

func (c *Client) Path(path string) *Client {
	c.path += path
	return c
}

func (c *Client) Query(kv map[string]string) *Client {
	query := []string{}
	for k, v := range kv {
		s := k + "=" + url.QueryEscape(v)
		query = append(query, s)
	}
	c.query = strings.Join(query, "&")
	return c
}

func (c *Client) Proxy(proxy string) *Client {
	c.proxy = proxy
	return c
}

func (c *Client) Timeout(timeout int) *Client {
	c.timeout = timeout
	return c
}

func (c *Client) Cookie(k, v string) *Client {
	c.cookies[k] = v
	return c
}

func (c *Client) Header(k, v string) *Client {
	c.headers[k] = v
	return c
}

func (c *Client) Headers(kv map[string]string) *Client {
	for k, v := range kv {
		c.Header(k, v)
	}
	return c
}

func (c *Client) Body(body interface{}) *Client {
	c.body = body
	return c
}

func (c *Client) Retries(count int) *Client {
	c.retries = count
	return c
}

func (c *Client) newURL() (*url.URL, error) {
	u, err := url.Parse(c.url)
	if err != nil {
		return u, err
	}
	if c.path != "" {
		u.Path = c.path
	}
	if c.query != "" {
		u.RawQuery = c.query
	}
	return u, err
}

func (c *Client) URLString() string {
	url, _ := c.newURL()
	return url.String()
}

func (c *Client) Get() (*Response, error)  { return c.doReq("GET") }
func (c *Client) Post() (*Response, error) { return c.doReq("POST") }
func (c *Client) Head() (*Response, error) { return c.doReq("HEAD") }
func (c *Client) Put() (*Response, error)  { return c.doReq("PUT") }
<|endoftext|>


<commit_before>package dns

// A concurrent client implementation.

import (
	"io"
	"net"
	"time"
)

type reply struct {
	client         *Client
	addr           string
	req            *Msg
	conn           net.Conn
	tsigRequestMAC string
	tsigTimersOnly bool
	tsigStatus     error
	rtt            time.Duration
	t              time.Time
}

// A Client defines parameter for a DNS client. A nil
// Client is usable for sending queries.
type Client struct {
	Net          string            // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
	Attempts     int               // number of attempts, if not set defaults to 1
	Retry        bool              // retry with TCP
	ReadTimeout  time.Duration     // the net.Conn.SetReadTimeout value for new connections (ns), defauls to 2 * 1e9
	WriteTimeout time.Duration     // the net.Conn.SetWriteTimeout value for new connections (ns), defauls to 2 * 1e9
	TsigSecret   map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
}

func (w *reply) RemoteAddr() net.Addr {
	if w.conn != nil {
		return w.conn.RemoteAddr()
	}
	return nil
}

// Do performs an asynchronous query. The msg *Msg is the question to ask, the
// string addr is the address of the nameserver, the parameter data is used
// in the callback function. The call backback function is called with the
// original query, the answer returned from the nameserver an optional error and
// data.
func (c *Client) Do(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, error, interface{})) {
	go func() {
		r, err := c.Exchange(msg, addr)
		callback(msg, r, err, data)
	}()
}

// DoRtt is equivalent to Do, except that is calls ExchangeRtt.
func (c *Client) DoRtt(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, time.Duration, error, interface{})) {
	go func() {
		r, rtt, err := c.ExchangeRtt(msg, addr)
		callback(msg, r, rtt, err, data)
	}()
}

func (c *Client) exchangeBuffer(inbuf []byte, a string, outbuf []byte) (n int, w *reply, err error) {
	w = new(reply)
	w.client = c
	w.addr = a
	// attempts useful for dialing?
	if err = w.dial(); err != nil {
		return 0, w, err
	}
	defer w.conn.Close()
	w.t = time.Now()
	if n, err = w.write(inbuf); err != nil {
		return 0, w, err
	}
	if n, err = w.read(outbuf); err != nil {
		return n, w, err
	}
	w.rtt = time.Since(w.t)
	return n, w, nil
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, err := c.Exchange(message, "127.0.0.1:53")
//
// See Client.ExchangeRtt(...) to get the round trip time.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, err error) {
	r, _, err = c.ExchangeRtt(m, a)
	return
}

// ExchangeRtt performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, rtt, err := c.ExchangeRtt(message, "127.0.0.1:53")
//
func (c *Client) ExchangeRtt(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	w := new(reply)
	w.client = c
	w.addr = a
	if err = w.dial(); err != nil {
		return nil, 0, err
	}
	if err = w.send(m); err != nil {
		return nil, 0, err
	}
	r, err = w.receive()
	return r, w.rtt, err
}

// dial connects to the address addr for the network set in c.Net
func (w *reply) dial() (err error) {
	var conn net.Conn
	if w.client.Net == "" {
		conn, err = net.Dial("udp", w.addr)
	} else {
		conn, err = net.Dial(w.client.Net, w.addr)
	}
	if err != nil {
		return
	}
	w.conn = conn
	return nil
}

func (w *reply) receive() (*Msg, error) {
	var p []byte
	m := new(Msg)
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		p = make([]byte, MaxMsgSize)
	case "", "udp", "udp4", "udp6":
		// OPT! TODO(mg)
		p = make([]byte, DefaultMsgSize)
	}
	n, err := w.read(p)
	if err != nil && n == 0 {
		return nil, err
	}
	p = p[:n]
	if err := m.Unpack(p); err != nil {
		return nil, err
	}
	w.rtt = time.Since(w.t)
	m.Size = n
	if t := m.IsTsig(); t != nil {
		secret := t.Hdr.Name
		if _, ok := w.client.TsigSecret[secret]; !ok {
			w.tsigStatus = ErrSecret
			return m, ErrSecret
		}
		// Need to work on the original message p, as that was used to calculate the tsig.
		w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
	}
	return m, w.tsigStatus
}

func (w *reply) read(p []byte) (n int, err error) {
	if w.conn == nil {
		return 0, ErrConnEmpty
	}
	if len(p) < 2 {
		return 0, io.ErrShortBuffer
	}
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		setTimeouts(w)
		for a := 0; a < attempts; a++ {
			n, err = w.conn.(*net.TCPConn).Read(p[0:2])
			if err != nil || n != 2 {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			l, _ := unpackUint16(p[0:2], 0)
			if l == 0 {
				return 0, ErrShortRead
			}
			if int(l) > len(p) {
				return int(l), io.ErrShortBuffer
			}
			n, err = w.conn.(*net.TCPConn).Read(p[:l])
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			for i < int(l) {
				j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our read...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p)
			if err == nil {
				return n, err
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

// send sends a dns msg to the address specified in w.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (w *reply) send(m *Msg) (err error) {
	var out []byte
	if t := m.IsTsig(); t != nil {
		mac := ""
		name := t.Hdr.Name
		if _, ok := w.client.TsigSecret[name]; !ok {
			return ErrSecret
		}
		out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly)
		w.tsigRequestMAC = mac
	} else {
		out, err = m.Pack()
	}
	if err != nil {
		return err
	}
	w.t = time.Now()
	if _, err = w.write(out); err != nil {
		return err
	}
	return nil
}

func (w *reply) write(p []byte) (n int, err error) {
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 2 {
			return 0, io.ErrShortBuffer
		}
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			a, b := packUint16(uint16(len(p)))
			n, err = w.conn.Write([]byte{a, b})
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			if n != 2 {
				return n, io.ErrShortWrite
			}
			n, err = w.conn.Write(p)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			if i < len(p) {
				j, err := w.conn.Write(p[i:len(p)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our write...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, err = w.conn.(*net.UDPConn).Write(p)
			if err == nil {
				return
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

func setTimeouts(w *reply) {
	if w.client.ReadTimeout == 0 {
		w.conn.SetReadDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetReadDeadline(time.Now().Add(w.client.ReadTimeout))
	}

	if w.client.WriteTimeout == 0 {
		w.conn.SetWriteDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetWriteDeadline(time.Now().Add(w.client.WriteTimeout))
	}
}
<commit_msg>exchangeBuffer was and is not needed<commit_after>package dns

// A concurrent client implementation.

import (
	"io"
	"net"
	"time"
)

// Order of events:
// client -> reply -> Exchange* -> dial/write/read

type reply struct {
	client         *Client
	addr           string
	req            *Msg
	conn           net.Conn
	tsigRequestMAC string
	tsigTimersOnly bool
	tsigStatus     error
	rtt            time.Duration
	t              time.Time
}

// A Client defines parameter for a DNS client. A nil
// Client is usable for sending queries.
type Client struct {
	Net          string            // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
	Attempts     int               // number of attempts, if not set defaults to 1
	Retry        bool              // retry with TCP
	ReadTimeout  time.Duration     // the net.Conn.SetReadTimeout value for new connections (ns), defauls to 2 * 1e9
	WriteTimeout time.Duration     // the net.Conn.SetWriteTimeout value for new connections (ns), defauls to 2 * 1e9
	TsigSecret   map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
}

func (w *reply) RemoteAddr() net.Addr {
	if w.conn != nil {
		return w.conn.RemoteAddr()
	}
	return nil
}

// Do performs an asynchronous query. The msg *Msg is the question to ask, the
// string addr is the address of the nameserver, the parameter data is used
// in the callback function. The call backback function is called with the
// original query, the answer returned from the nameserver an optional error and
// data.
func (c *Client) Do(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, error, interface{})) {
	go func() {
		r, err := c.Exchange(msg, addr)
		callback(msg, r, err, data)
	}()
}

// DoRtt is equivalent to Do, except that is calls ExchangeRtt.
func (c *Client) DoRtt(msg *Msg, addr string, data interface{}, callback func(*Msg, *Msg, time.Duration, error, interface{})) {
	go func() {
		r, rtt, err := c.ExchangeRtt(msg, addr)
		callback(msg, r, rtt, err, data)
	}()
}

// Exchange performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, err := c.Exchange(message, "127.0.0.1:53")
//
// See Client.ExchangeRtt(...) to get the round trip time.
func (c *Client) Exchange(m *Msg, a string) (r *Msg, err error) {
	r, _, err = c.ExchangeRtt(m, a)
	return
}

// ExchangeRtt performs an synchronous query. It sends the message m to the address
// contained in a and waits for an reply. Basic use pattern with a *Client:
//
//	c := new(dns.Client)
//	in, rtt, err := c.ExchangeRtt(message, "127.0.0.1:53")
//
func (c *Client) ExchangeRtt(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
	w := new(reply)
	w.client = c
	w.addr = a
	if err = w.dial(); err != nil {
		return nil, 0, err
	}
	if err = w.send(m); err != nil {
		return nil, 0, err
	}
	r, err = w.receive()
	return r, w.rtt, err
}

// dial connects to the address addr for the network set in c.Net
func (w *reply) dial() (err error) {
	var conn net.Conn
	if w.client.Net == "" {
		conn, err = net.Dial("udp", w.addr)
	} else {
		conn, err = net.Dial(w.client.Net, w.addr)
	}
	if err != nil {
		return
	}
	w.conn = conn
	return nil
}

func (w *reply) receive() (*Msg, error) {
	var p []byte
	m := new(Msg)
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		p = make([]byte, MaxMsgSize)
	case "", "udp", "udp4", "udp6":
		// OPT! TODO(mg)
		p = make([]byte, DefaultMsgSize)
	}
	n, err := w.read(p)
	if err != nil && n == 0 {
		return nil, err
	}
	p = p[:n]
	if err := m.Unpack(p); err != nil {
		return nil, err
	}
	w.rtt = time.Since(w.t)
	m.Size = n
	if t := m.IsTsig(); t != nil {
		secret := t.Hdr.Name
		if _, ok := w.client.TsigSecret[secret]; !ok {
			w.tsigStatus = ErrSecret
			return m, ErrSecret
		}
		// Need to work on the original message p, as that was used to calculate the tsig.
		w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly)
	}
	return m, w.tsigStatus
}

func (w *reply) read(p []byte) (n int, err error) {
	if w.conn == nil {
		return 0, ErrConnEmpty
	}
	if len(p) < 2 {
		return 0, io.ErrShortBuffer
	}
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		setTimeouts(w)
		for a := 0; a < attempts; a++ {
			n, err = w.conn.(*net.TCPConn).Read(p[0:2])
			if err != nil || n != 2 {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			l, _ := unpackUint16(p[0:2], 0)
			if l == 0 {
				return 0, ErrShortRead
			}
			if int(l) > len(p) {
				return int(l), io.ErrShortBuffer
			}
			n, err = w.conn.(*net.TCPConn).Read(p[:l])
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			for i < int(l) {
				j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our read...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p)
			if err == nil {
				return n, err
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

// send sends a dns msg to the address specified in w.
// If the message m contains a TSIG record the transaction
// signature is calculated.
func (w *reply) send(m *Msg) (err error) {
	var out []byte
	if t := m.IsTsig(); t != nil {
		mac := ""
		name := t.Hdr.Name
		if _, ok := w.client.TsigSecret[name]; !ok {
			return ErrSecret
		}
		out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly)
		w.tsigRequestMAC = mac
	} else {
		out, err = m.Pack()
	}
	if err != nil {
		return err
	}
	w.t = time.Now()
	if _, err = w.write(out); err != nil {
		return err
	}
	return nil
}

func (w *reply) write(p []byte) (n int, err error) {
	attempts := w.client.Attempts
	if attempts == 0 {
		attempts = 1
	}
	switch w.client.Net {
	case "tcp", "tcp4", "tcp6":
		if len(p) < 2 {
			return 0, io.ErrShortBuffer
		}
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			a, b := packUint16(uint16(len(p)))
			n, err = w.conn.Write([]byte{a, b})
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			if n != 2 {
				return n, io.ErrShortWrite
			}
			n, err = w.conn.Write(p)
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
			i := n
			if i < len(p) {
				j, err := w.conn.Write(p[i:len(p)])
				if err != nil {
					if e, ok := err.(net.Error); ok && e.Timeout() {
						// We are half way in our write...
						continue
					}
					return i, err
				}
				i += j
			}
			n = i
		}
	case "", "udp", "udp4", "udp6":
		for a := 0; a < attempts; a++ {
			setTimeouts(w)
			n, err = w.conn.(*net.UDPConn).Write(p)
			if err == nil {
				return
			}
			if err != nil {
				if e, ok := err.(net.Error); ok && e.Timeout() {
					continue
				}
				return n, err
			}
		}
	}
	return
}

func setTimeouts(w *reply) {
	if w.client.ReadTimeout == 0 {
		w.conn.SetReadDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetReadDeadline(time.Now().Add(w.client.ReadTimeout))
	}

	if w.client.WriteTimeout == 0 {
		w.conn.SetWriteDeadline(time.Now().Add(2 * 1e9))
	} else {
		w.conn.SetWriteDeadline(time.Now().Add(w.client.WriteTimeout))
	}
}
<|endoftext|>


<commit_before>package grestclient

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	rt "reflect"
)

type Client interface {

	//Headers returns the default headers that will
	//be set with every request made with the client.
	Headers() http.Header

	//SetHeaders sets the default headers that will be sent with
	//every request made with this client.
	SetHeaders(http.Header)

	//Query returns the default query to use for all requests
	Query() url.Values

	//SetQuery sets the default query to use for all requests
	SetQuery(url.Values)

	//SetBaseUrl sets the base url to use for all requests
	//If you want to use a different temporarily it is best to
	//create a new client with the new base url. Call
	//Clone() to get a clone of this
	//client's settings and then change the url on the clone.
	//An error should be returned if the url is "unsupported" by
	//the implementation. For example, "unix://tmp.soc".
	//Any query parameters added here should be ignored.
	//Clients should use the SetQuery method to set default
	//query parameters
	SetBaseUrl(*url.URL) error

	//BaseUrl returns the base url being used. This implementation
	//allows you to change the base url here directly but other
	//implementations might give you a clone so changing it won't affect
	//the client. In those cases, use SetBaseUrl to change the url after
	//making your changes.
	BaseUrl() *url.URL

	//Clones the client with everything the old client had
	//Ideally, clones should be independent of the original and can be changed
	//without affecting the original and vice versa.
	//This implementation, however, shared the http.Client among clones.
	//All other 'things' like headers, base url, query, marshalers are separate and
	//can be adjusted without affecting the original/clones.
	Clone() Client

	//GetHttpClient returns the current http.Client being used
	//If none has been set, this should return http.DefaultClient
	GetHttpClient() *http.Client

	//SetHttpClient sets the http.Client to use during requests
	//Use this to customize your http.Client as you wish. If you
	//don't set one, the default http.Client will be used.
	SetHttpClient(*http.Client)

	//SetMarshaler sets the marshal function to be used
	//to marshal the request bodies for requests
	//Doesn't have to mirror the Unmarshaler. Send plain text, get back json
	//Default is a json marshaler
	SetMarshaler(MarshalerFunc)
	//SetUnmarshaler sets the unmarshal function to be used
	//to unmarshal the response body for responses
	//Doesn't have to mirror the Marshaler. Send XML, get back json
	//Default is a json unmarshaler
	SetUnmarshaler(UnmarshalerFunc)

	//AddRequestMutator adds a mutator that the request will be
	//passed through before executing the request. All RequestMutators
	//are called AFTER the Marshaler is used.
	//RequestMutators should be called in the order they were added
	AddRequestMutators(...RequestMutator) Client

	//AddResponseMutator adds a mutator that the response will be
	//passed through after the server responds. All ResponseMutators
	//are called BEFORE the Unmarshaler is used.
	//ResponseMutators should be called in the order they were added
	AddResponseMutators(...ResponseMutator) Client

	//RemoveRequestMutator removes a request mutator
	SetRequestMutators(...RequestMutator) Client
	//RemoveResponseMutator removes a response mutator
	SetResponseMutators(...ResponseMutator) Client

	//Returns the RequestMutators
	RequestMutators() []RequestMutator
	//Returns the ResponseMutators
	ResponseMutators() []ResponseMutator

	Getter
	Poster
	Putter
	Patcher
	Header
	Optioner
	Deleter
}

//Getter is something capable of performing
//a get on a given url path.
type Getter interface {
	//Get performs a get request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Get(
		path string,
		headers http.Header,
		query url.Values,
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Poster is something capable of performing
//a get on a given url path.
type Poster interface {
	//Post performs a post request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//With post you can also provide a post body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Post(
		path string,
		headers http.Header,
		query url.Values,
		postBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Putter is something capable of performing
//a get on a given url path.
type Putter interface {
	//Put performs a put request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//With put you can also provide a put body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Put(
		path string,
		headers http.Header,
		query url.Values,
		putBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Patcher is something capable of performing
//a get on a given url path.
type Patcher interface {
	//Patch performs a patch request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//With patch you can also provide a patch body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Patch(
		path string,
		headers http.Header,
		query url.Values,
		patchBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Header is something capable of performing
//a get on a given url path.
type Header interface {
	//Head performs a head request with the base url plus the path appended to it.
	//You can send header values and supply a successResult that will be populated
	//if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Head(
		path string,
		headers http.Header,
		query url.Values,
	) (*http.Response, error)
}

//Optioner is something capable of performing
//a get on a given url path.
type Optioner interface {
	//Option performs an option request with the base url plus the path appended to it.
	//You can send header values and supply a successResult that will be populated
	//if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Options(
		path string,
		headers http.Header,
		query url.Values,
		optionsBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Deleter is something capable of performing
//a get on a given url path.
type Deleter interface {
	//Delete performs an delete request with the base url plus the path appended to it.
	//You can send header values and supply a successResult that will be populated
	//if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Delete(
		path string,
		headers http.Header,
		query url.Values,
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//UnmarshalMap represents a mapping from HTTP status
//codes to interfaces that a client should unmarshal
//to.
//For example, you call
//Get( path, headers, query, UnmarshalMap{ 200 : success, 201 : someothersuccess, 202 : success, 404 : uauthorizedPiece }
//If the http response is either a 202 or a 200 then the response body is unmarshaled into success.
//A 201 response unmarshals into someothersuccess, and a 404 unmarshals into unauthorizedPiece
type UnmarshalMap map[int]interface{}

//ReadLener is an io.Reader than can
//tell you the length of its content.
//Len can either be 0 for no bytes,
//-1 for an unknown number of bytes,
//or >0 for a specific number of bytes.
type ReadLener interface {
	io.Reader
	Len() int
}

//MarshalerFunc takes something and converts it into a
//ReadLener that can be used for the request body
//I made ReadLener because ContentLength needs to be set
//to something in the http.Request. Your Len method can return 0 or -1 if you
//want but some APIs depend on the ContentLength being set correctly and accurately.
//bytes.Buffers is already a ReadLener, it has Read and Len methods
//so no worries there.
//I ran into issues with the ArangoDB REST API where it failed
//to answer requests properly if ContentLength was -1 or if it was
//not correct/accurate.
type MarshalerFunc func(v interface{}) (ReadLener, error)

//UnmarshalerFunc takes the response body and converts it into
//something you can use.
type UnmarshalerFunc func(b []byte, v interface{}) error

//ByteSliceToReadCloser takes a byte slice and converts it to an
//ReadLener that can be used as a request/resonse body
func ByteSliceToReadLener(b []byte) (ReadLener, error) {
	if b == nil {
		return nil, errors.New("ByteSliceToReadLener received a nil byte slice.")
	}

	buf := bytes.NewBuffer(b)
	return buf, nil
}

//StringToReadCloser takes a string and converts it to an
//ReadLener that can be used as a request/resonse body
func StringToReadLener(s string) ReadLener {
	buf := bytes.NewBufferString(s)
	return buf
}

//JsonMarshalerFunc can be used to marshal request bodies
//into json
func JsonMarshalerFunc(v interface{}) (ReadLener, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}
	body, err := ByteSliceToReadLener(b)
	if err != nil {
		return nil, err
	}
	return body, nil
}

//JsonUnmarshalerFunc can be used to unmarshal response bodies
//from json
func JsonUnmarshalerFunc(b []byte, v interface{}) error {

	err := json.Unmarshal(b, v)

	if err != nil {
		return err
	}

	return nil
}

//StringMarshalerFunc can be used to marshal strings into a request.
func StringMarshalerFunc(v interface{}) (ReadLener, error) {
	switch t := v.(type) {
	case fmt.Stringer:
		return StringToReadLener(t.String()), nil
	case string:
		return StringToReadLener(t), nil
	}

	return nil, errors.New("Did not know how to use the body as text.")
}

//StringUnmarshalerFunc can be used to unmarshal strings from a response.
func StringUnmarshalerFunc(b []byte, v interface{}) error {

	switch v.(type) {
	case *string:
		elem := rt.ValueOf(v).Elem()

		if elem.CanSet() {
			elem.SetString(string(b))
			return nil
		}
	case string:
		return errors.New("You must pass the string by reference or pass a pointer. For example, ( &stringVar )")
	}

	return errors.New("Did not know how to unmarshal the text coming back.")
}

//JsonContentTypeMutator sets the Content-Type of the request to be
//application/json
func JsonContentTypeMutator(r *http.Request) error {
	r.Header.Add("Content-Type", "application/json")
	return nil
}

func JsonAcceptMutator(r *http.Request) error {
	r.Header.Add("Accept", "application/json")
	return nil
}

//RequestMutators are called before the request is made but after the marshaler function has been
//called.
type RequestMutator func(*http.Request) error
type ResponseMutator func(*http.Response) error

//SetupClientForJson is a convenience method that sets the
//marshaler and unmarshaler funcs on the client to be the
//Json funcs in this package. It also sets a request mutator
//to set the Content-Type to json.
func SetupForJson(c Client) {
	c.SetMarshaler(JsonMarshalerFunc)
	c.SetUnmarshaler(JsonUnmarshalerFunc)
	c.AddRequestMutators(JsonContentTypeMutator)
	c.AddRequestMutators(JsonAcceptMutator)
}
<commit_msg>Changed comment.<commit_after>package grestclient

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	rt "reflect"
)

type Client interface {

	//Headers returns the default headers that will
	//be set with every request made with the client.
	Headers() http.Header

	//SetHeaders sets the default headers that will be sent with
	//every request made with this client.
	SetHeaders(http.Header)

	//Query returns the default query to use for all requests
	Query() url.Values

	//SetQuery sets the default query to use for all requests
	SetQuery(url.Values)

	//SetBaseUrl sets the base url to use for all requests
	//If you want to use a different temporarily it is best to
	//create a new client with the new base url. Call
	//Clone() to get a clone of this
	//client's settings and then change the url on the clone.
	//An error should be returned if the url is "unsupported" by
	//the implementation. For example, "unix://tmp.soc".
	//Any query parameters added here should be ignored.
	//Clients should use the SetQuery method to set default
	//query parameters
	SetBaseUrl(*url.URL) error

	//BaseUrl returns the base url being used. This implementation
	//allows you to change the base url here directly but other
	//implementations might give you a clone so changing it won't affect
	//the client. In those cases, use SetBaseUrl to change the url after
	//making your changes.
	BaseUrl() *url.URL

	//Clones the client with everything the old client had
	//Ideally, clones should be independent of the original and can be changed
	//without affecting the original and vice versa.
	//This implementation, however, shared the http.Client among clones.
	//All other 'things' like headers, base url, query, marshalers are separate and
	//can be adjusted without affecting the original/clones.
	Clone() Client

	//GetHttpClient returns the current http.Client being used
	//If none has been set, this should return http.DefaultClient
	GetHttpClient() *http.Client

	//SetHttpClient sets the http.Client to use during requests
	//Use this to customize your http.Client as you wish. If you
	//don't set one, the default http.Client will be used.
	SetHttpClient(*http.Client)

	//SetMarshaler sets the marshal function to be used
	//to marshal the request bodies for requests
	//Doesn't have to mirror the Unmarshaler. Send plain text, get back json
	//Default is a json marshaler
	SetMarshaler(MarshalerFunc)
	//SetUnmarshaler sets the unmarshal function to be used
	//to unmarshal the response body for responses
	//Doesn't have to mirror the Marshaler. Send XML, get back json
	//Default is a json unmarshaler
	SetUnmarshaler(UnmarshalerFunc)

	//AddRequestMutator adds a mutator that the request will be
	//passed through before executing the request. All RequestMutators
	//are called AFTER the Marshaler is used.
	//RequestMutators should be called in the order they were added
	AddRequestMutators(...RequestMutator) Client

	//AddResponseMutator adds a mutator that the response will be
	//passed through after the server responds. All ResponseMutators
	//are called BEFORE the Unmarshaler is used.
	//ResponseMutators should be called in the order they were added
	AddResponseMutators(...ResponseMutator) Client

	//RemoveRequestMutator removes a request mutator
	SetRequestMutators(...RequestMutator) Client
	//RemoveResponseMutator removes a response mutator
	SetResponseMutators(...ResponseMutator) Client

	//Returns the RequestMutators
	RequestMutators() []RequestMutator
	//Returns the ResponseMutators
	ResponseMutators() []ResponseMutator

	Getter
	Poster
	Putter
	Patcher
	Header
	Optioner
	Deleter
}

//Getter is something capable of performing an HTTP GET
//on a given url path.
type Getter interface {
	//Get performs a get request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Get(
		path string,
		headers http.Header,
		query url.Values,
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Poster is something capable of performing an HTTP POST
//on a given url path.
type Poster interface {
	//Post performs a post request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//With post you can also provide a post body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Post(
		path string,
		headers http.Header,
		query url.Values,
		postBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Putter is something capable of performing an HTTP PUT
//on a given url path.
type Putter interface {
	//Put performs a put request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//With put you can also provide a put body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Put(
		path string,
		headers http.Header,
		query url.Values,
		putBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Patcher is something capable of performing an HTTP PATCH
//on a given url path.
type Patcher interface {
	//Patch performs a patch request with the base url plus the path appended to it.
	//You can send query values, header values and
	//supply a successResult that will be populated if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//With patch you can also provide a patch body.
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Patch(
		path string,
		headers http.Header,
		query url.Values,
		patchBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Header is something capable of performing an HTTP HEAD
//on a given url path.
type Header interface {
	//Head performs a head request with the base url plus the path appended to it.
	//You can send header values and supply a successResult that will be populated
	//if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Head(
		path string,
		headers http.Header,
		query url.Values,
	) (*http.Response, error)
}

//Optioner is something capable of performing an HTTP OPTIONS
//on a given url path.
type Optioner interface {
	//Option performs an option request with the base url plus the path appended to it.
	//You can send header values and supply a successResult that will be populated
	//if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Options(
		path string,
		headers http.Header,
		query url.Values,
		optionsBody interface{},
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//Deleter is something capable of performing an HTTP DELETE
//on a given url path.
type Deleter interface {
	//Delete performs an delete request with the base url plus the path appended to it.
	//You can send header values and supply a successResult that will be populated
	//if the http response has a return code less than 400.
	//errorResult is populated if the error code is 400 or more
	//Returns the raw http.Response and error similar to Do method of http.Client
	//The returned http.Response might be non-nil even though an error was also returned
	//depending on where the operation failed.
	Delete(
		path string,
		headers http.Header,
		query url.Values,
		unmarshalMap UnmarshalMap,
	) (*http.Response, error)
}

//UnmarshalMap represents a mapping from HTTP status
//codes to interfaces that a client should unmarshal
//to.
//For example, you call
//Get( path, headers, query, UnmarshalMap{ 200 : success, 201 : someothersuccess, 202 : success, 404 : uauthorizedPiece }
//If the http response is either a 202 or a 200 then the response body is unmarshaled into success.
//A 201 response unmarshals into someothersuccess, and a 404 unmarshals into unauthorizedPiece
type UnmarshalMap map[int]interface{}

//ReadLener is an io.Reader than can
//tell you the length of its content.
//Len can either be 0 for no bytes,
//-1 for an unknown number of bytes,
//or >0 for a specific number of bytes.
type ReadLener interface {
	io.Reader
	Len() int
}

//MarshalerFunc takes something and converts it into a
//ReadLener that can be used for the request body
//I made ReadLener because ContentLength needs to be set
//to something in the http.Request. Your Len method can return 0 or -1 if you
//want but some APIs depend on the ContentLength being set correctly and accurately.
//bytes.Buffers is already a ReadLener, it has Read and Len methods
//so no worries there.
//I ran into issues with the ArangoDB REST API where it failed
//to answer requests properly if ContentLength was -1 or if it was
//not correct/accurate.
type MarshalerFunc func(v interface{}) (ReadLener, error)

//UnmarshalerFunc takes the response body and converts it into
//something you can use.
type UnmarshalerFunc func(b []byte, v interface{}) error

//ByteSliceToReadCloser takes a byte slice and converts it to an
//ReadLener that can be used as a request/resonse body
func ByteSliceToReadLener(b []byte) (ReadLener, error) {
	if b == nil {
		return nil, errors.New("ByteSliceToReadLener received a nil byte slice.")
	}

	buf := bytes.NewBuffer(b)
	return buf, nil
}

//StringToReadCloser takes a string and converts it to an
//ReadLener that can be used as a request/resonse body
func StringToReadLener(s string) ReadLener {
	buf := bytes.NewBufferString(s)
	return buf
}

//JsonMarshalerFunc can be used to marshal request bodies
//into json
func JsonMarshalerFunc(v interface{}) (ReadLener, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}
	body, err := ByteSliceToReadLener(b)
	if err != nil {
		return nil, err
	}
	return body, nil
}

//JsonUnmarshalerFunc can be used to unmarshal response bodies
//from json
func JsonUnmarshalerFunc(b []byte, v interface{}) error {

	err := json.Unmarshal(b, v)

	if err != nil {
		return err
	}

	return nil
}

//StringMarshalerFunc can be used to marshal strings into a request.
func StringMarshalerFunc(v interface{}) (ReadLener, error) {
	switch t := v.(type) {
	case fmt.Stringer:
		return StringToReadLener(t.String()), nil
	case string:
		return StringToReadLener(t), nil
	}

	return nil, errors.New("Did not know how to use the body as text.")
}

//StringUnmarshalerFunc can be used to unmarshal strings from a response.
func StringUnmarshalerFunc(b []byte, v interface{}) error {

	switch v.(type) {
	case *string:
		elem := rt.ValueOf(v).Elem()

		if elem.CanSet() {
			elem.SetString(string(b))
			return nil
		}
	case string:
		return errors.New("You must pass the string by reference or pass a pointer. For example, ( &stringVar )")
	}

	return errors.New("Did not know how to unmarshal the text coming back.")
}

//JsonContentTypeMutator sets the Content-Type of the request to be
//application/json
func JsonContentTypeMutator(r *http.Request) error {
	r.Header.Add("Content-Type", "application/json")
	return nil
}

func JsonAcceptMutator(r *http.Request) error {
	r.Header.Add("Accept", "application/json")
	return nil
}

//RequestMutators are called before the request is made but after the marshaler function has been
//called.
type RequestMutator func(*http.Request) error
type ResponseMutator func(*http.Response) error

//SetupClientForJson is a convenience method that sets the
//marshaler and unmarshaler funcs on the client to be the
//Json funcs in this package. It also sets a request mutator
//to set the Content-Type to json.
func SetupForJson(c Client) {
	c.SetMarshaler(JsonMarshalerFunc)
	c.SetUnmarshaler(JsonUnmarshalerFunc)
	c.AddRequestMutators(JsonContentTypeMutator)
	c.AddRequestMutators(JsonAcceptMutator)
}
<|endoftext|>