<commit_before>package recurly

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
)

const defaultBaseURL = "https://%s.com/"

// Client manages communication with the Recurly API.
type Client struct {
	// client is the HTTP Client used to communicate with the API.
	client *http.Client

	// subdomain is your account's sub domain used for authentication.
	subDomain string

	// apiKey is your account's API key used for authentication.
	apiKey string

	// BaseURL is the base url for api requests.
	BaseURL string

	// Services used for talking with different parts of the Recurly API
	Accounts      AccountsService
	Adjustments   AdjustmentsService
	Billing       BillingService
	Coupons       CouponsService
	Redemptions   RedemptionsService
	Invoices      InvoicesService
	Plans         PlansService
	AddOns        AddOnsService
	Subscriptions SubscriptionsService
	Transactions  TransactionsService
}

// NewClient returns a new instance of *Client.
func NewClient(subDomain, apiKey string, httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	client := &Client{
		client:    httpClient,
		subDomain: subDomain,
		apiKey:    apiKey,
		BaseURL:   fmt.Sprintf(defaultBaseURL, subDomain),
	}

	client.Accounts = NewAccountsImpl(client)
	client.Adjustments = NewAdjustmentsImpl(client)
	client.Billing = NewBillingImpl(client)
	client.Coupons = NewCouponsImpl(client)
	client.Redemptions = NewRedemptionsImpl(client)
	client.Invoices = NewInvoicesImpl(client)
	client.Plans = NewPlansImpl(client)
	client.AddOns = NewAddOnsImpl(client)
	client.Subscriptions = NewSubscriptionsImpl(client)
	client.Transactions = NewTransactionsImpl(client)

	return client
}

// newRequest creates an authenticated API request that is ready to send.
func (c *Client) newRequest(method string, action string, params Params, body interface{}) (*http.Request, error) {
	method = strings.ToUpper(method)
	endpoint := fmt.Sprintf("%sv2/%s", c.BaseURL, action)

	// Query String
	qs := url.Values{}
	for k, v := range params {
		qs.Add(k, fmt.Sprintf("%v", v))
	}

	if len(qs) > 0 {
		endpoint += "?" + qs.Encode()
	}

	// Request body
	var buf bytes.Buffer
	if body != nil {
		err := xml.NewEncoder(&buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, endpoint, &buf)

	req.SetBasicAuth(c.apiKey, "")
	req.Header.Set("Accept", "application/xml")
	if req.Method == "POST" || req.Method == "PUT" {
		req.Header.Set("Content-Type", "application/xml; charset=utf-8")
	}

	return req, err
}

// do takes a prepared API request and makes the API call to Recurly.
// It will decode the XML into a destination struct you provide as well
// as parse any validation errors that may have occurred.
// It returns a Response object that provides a wrapper around http.Response
// with some convenience methods.
func (c *Client) do(req *http.Request, v interface{}) (*Response, error) {
	req.Close = true
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	response := &Response{Response: resp}
	decoder := xml.NewDecoder(resp.Body)
	if response.IsError() { // Parse validation errors
		if response.StatusCode == 422 {
			var ve struct {
				XMLName          xml.Name          `xml:"errors"`
				Errors           []Error           `xml:"error"`
				Transaction      *Transaction      `xml:"transaction,omitempty"`
				TransactionError *TransactionError `xml:"transaction_error,omitempty"`
			}

			if err = decoder.Decode(&ve); err != nil {
				return response, err
			}

			response.Errors = ve.Errors
			response.Transaction = ve.Transaction
			response.TransactionError = ve.TransactionError
		} else if response.IsClientError() { // Parse possible individual error message
			var ve struct {
				XMLName     xml.Name `xml:"error"`
				Symbol      string   `xml:"symbol"`
				Description string   `xml:"description"`
			}
			if err = decoder.Decode(&ve); err != nil {
				return response, err
			}

			response.Errors = []Error{
				{
					Symbol:  ve.Symbol,
					Message: ve.Description,
				},
			}
		}

		return response, nil
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = decoder.Decode(&v)
		}
	}

	return response, err
}
<commit_msg>Add version 2.4 header. Add recurly.com to subdomain (#15)<commit_after>package recurly

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
)

const defaultBaseURL = "https://%s.recurly.com/"

// Client manages communication with the Recurly API.
type Client struct {
	// client is the HTTP Client used to communicate with the API.
	client *http.Client

	// subdomain is your account's sub domain used for authentication.
	subDomain string

	// apiKey is your account's API key used for authentication.
	apiKey string

	// BaseURL is the base url for api requests.
	BaseURL string

	// Services used for talking with different parts of the Recurly API
	Accounts      AccountsService
	Adjustments   AdjustmentsService
	Billing       BillingService
	Coupons       CouponsService
	Redemptions   RedemptionsService
	Invoices      InvoicesService
	Plans         PlansService
	AddOns        AddOnsService
	Subscriptions SubscriptionsService
	Transactions  TransactionsService
}

// NewClient returns a new instance of *Client.
func NewClient(subDomain, apiKey string, httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	client := &Client{
		client:    httpClient,
		subDomain: subDomain,
		apiKey:    apiKey,
		BaseURL:   fmt.Sprintf(defaultBaseURL, subDomain),
	}

	client.Accounts = NewAccountsImpl(client)
	client.Adjustments = NewAdjustmentsImpl(client)
	client.Billing = NewBillingImpl(client)
	client.Coupons = NewCouponsImpl(client)
	client.Redemptions = NewRedemptionsImpl(client)
	client.Invoices = NewInvoicesImpl(client)
	client.Plans = NewPlansImpl(client)
	client.AddOns = NewAddOnsImpl(client)
	client.Subscriptions = NewSubscriptionsImpl(client)
	client.Transactions = NewTransactionsImpl(client)

	return client
}

// newRequest creates an authenticated API request that is ready to send.
func (c *Client) newRequest(method string, action string, params Params, body interface{}) (*http.Request, error) {
	method = strings.ToUpper(method)
	endpoint := fmt.Sprintf("%sv2/%s", c.BaseURL, action)

	// Query String
	qs := url.Values{}
	for k, v := range params {
		qs.Add(k, fmt.Sprintf("%v", v))
	}

	if len(qs) > 0 {
		endpoint += "?" + qs.Encode()
	}

	// Request body
	var buf bytes.Buffer
	if body != nil {
		err := xml.NewEncoder(&buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, endpoint, &buf)

	req.SetBasicAuth(c.apiKey, "")
	req.Header.Set("Accept", "application/xml")
	req.Header.Set("X-Api-Version", "2.4")
	if req.Method == "POST" || req.Method == "PUT" {
		req.Header.Set("Content-Type", "application/xml; charset=utf-8")
	}

	return req, err
}

// do takes a prepared API request and makes the API call to Recurly.
// It will decode the XML into a destination struct you provide as well
// as parse any validation errors that may have occurred.
// It returns a Response object that provides a wrapper around http.Response
// with some convenience methods.
func (c *Client) do(req *http.Request, v interface{}) (*Response, error) {
	req.Close = true
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	response := &Response{Response: resp}
	decoder := xml.NewDecoder(resp.Body)
	if response.IsError() { // Parse validation errors
		if response.StatusCode == 422 {
			var ve struct {
				XMLName          xml.Name          `xml:"errors"`
				Errors           []Error           `xml:"error"`
				Transaction      *Transaction      `xml:"transaction,omitempty"`
				TransactionError *TransactionError `xml:"transaction_error,omitempty"`
			}

			if err = decoder.Decode(&ve); err != nil {
				return response, err
			}

			response.Errors = ve.Errors
			response.Transaction = ve.Transaction
			response.TransactionError = ve.TransactionError
		} else if response.IsClientError() { // Parse possible individual error message
			var ve struct {
				XMLName     xml.Name `xml:"error"`
				Symbol      string   `xml:"symbol"`
				Description string   `xml:"description"`
			}
			if err = decoder.Decode(&ve); err != nil {
				return response, err
			}

			response.Errors = []Error{
				{
					Symbol:  ve.Symbol,
					Message: ve.Description,
				},
			}
		}

		return response, nil
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = decoder.Decode(&v)
		}
	}

	return response, err
}
<|endoftext|>


<commit_before>package roll

import (
	"bytes"
	"encoding/json"
	"fmt"
	"hash/adler32"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"runtime"
	"strings"
	"time"
)

const (
	NAME     = "go-roll"
	ENDPOINT = "https://api.rollbar.com/api/1/item/"
	VERSION  = "0.0.1"
	LANGUAGE = "go"

	// Severity levels
	CRIT  = "critical"
	ERR   = "error"
	WARN  = "warning"
	INFO  = "info"
	DEBUG = "debug"
)

var (
	// Rollbar access token for the global client. If this is blank, no items
	// will be sent to Rollbar.
	Token = ""

	// Environment for all items reported with the global client.
	Environment = "development"
)

type rollbarSuccess struct {
	Result map[string]string `json:"result"`
}

// Client reports items to a single Rollbar project.
type Client interface {
	Critical(err error, custom map[string]string) (uuid string, e error)
	Error(err error, custom map[string]string) (uuid string, e error)
	Warning(err error, custom map[string]string) (uuid string, e error)
	Info(msg string, custom map[string]string) (uuid string, e error)
	Debug(msg string, custom map[string]string) (uuid string, e error)
}

type rollbarClient struct {
	token string
	env   string
}

// New creates a new Rollbar client that reports items to the given project
// token and with the given environment (eg. "production", "development", etc).
func New(token, env string) Client {
	return &rollbarClient{token, env}
}

func Critical(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack(CRIT, err, 3, custom)
}

func Error(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack(ERR, err, 3, custom)
}

func Warning(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack(WARN, err, 3, custom)
}

func Info(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Info(msg, custom)
}

func Debug(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Debug(msg, custom)
}

func (c *rollbarClient) Critical(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack(CRIT, err, 3, custom)
}

func (c *rollbarClient) Error(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack(ERR, err, 3, custom)
}

func (c *rollbarClient) Warning(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack(WARN, err, 3, custom)
}

func (c *rollbarClient) Info(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem(INFO, msg, custom)
	return c.send(item)
}

func (c *rollbarClient) Debug(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem(DEBUG, msg, custom)
	return c.send(item)
}

func (c *rollbarClient) skipStack(level string, err error, skip int, custom map[string]string) (uuid string, e error) {
	item := c.buildTraceItem(level, err, buildStack(skip), custom)
	return c.send(item)
}

func (c *rollbarClient) buildTraceItem(level string, err error, s stack, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, err.Error(), custom)
	itemData := item["data"].(map[string]interface{})
	itemData["fingerprint"] = stackFingerprint(err.Error(), s)
	itemData["body"] = map[string]interface{}{
		"trace": map[string]interface{}{
			"frames": s,
			"exception": map[string]interface{}{
				"class":   errorClass(err),
				"message": err.Error(),
			},
		},
	}

	return item
}

func (c *rollbarClient) buildMessageItem(level string, msg string, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, msg, custom)
	itemData := item["data"].(map[string]interface{})
	itemData["body"] = map[string]interface{}{
		"message": map[string]interface{}{
			"body": msg,
		},
	}

	return item
}

func (c *rollbarClient) buildItem(level, title string, custom map[string]string) map[string]interface{} {
	hostname, _ := os.Hostname()

	return map[string]interface{}{
		"access_token": c.token,
		"data": map[string]interface{}{
			"environment": c.env,
			"title":       title,
			"level":       level,
			"timestamp":   time.Now().Unix(),
			"platform":    runtime.GOOS,
			"language":    LANGUAGE,
			"server": map[string]interface{}{
				"host": hostname,
			},
			"notifier": map[string]interface{}{
				"name":    NAME,
				"version": VERSION,
			},
			"custom": custom,
		},
	}
}

// send reports the given item to Rollbar and returns either a UUID for the
// reported item or an error.
func (c *rollbarClient) send(item map[string]interface{}) (uuid string, err error) {
	if len(c.token) == 0 {
		return "", nil
	}

	jsonBody, err := json.Marshal(item)
	if err != nil {
		return "", err
	}

	resp, err := http.Post(ENDPOINT, "application/json", bytes.NewReader(jsonBody))
	if err != nil {
		return "", err
	}
	defer func() { resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		return "", fmt.Errorf("Rollbar returned %s", resp.Status)
	}

	// Extract UUID from JSON response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", nil
	}
	success := rollbarSuccess{}
	json.Unmarshal(body, &success)

	return success.Result["uuid"], nil
}

// errorClass returns a class name for an error (eg.  "ErrUnexpectedEOF").  For
// string errors, it returns a checksum of the error string.
func errorClass(err error) string {
	class := reflect.TypeOf(err).String()
	if class == "" {
		return "panic"
	} else if class == "*errors.errorString" {
		checksum := adler32.Checksum([]byte(err.Error()))
		return fmt.Sprintf("{%x}", checksum)
	} else {
		return strings.TrimPrefix(class, "*")
	}
}
<commit_msg>Remove unneeded exports.<commit_after>package roll

import (
	"bytes"
	"encoding/json"
	"fmt"
	"hash/adler32"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"runtime"
	"strings"
	"time"
)

const (
	// By default, all Rollbar API requests are sent to this endpoint.
	endpoint = "https://api.rollbar.com/api/1/item/"

	// Identify this Rollbar client library to the Rollbar API.
	clientName     = "go-roll"
	clientVersion  = "0.0.1"
	clientLanguage = "go"
)

var (
	// Rollbar access token for the global client. If this is blank, no items
	// will be sent to Rollbar.
	Token = ""

	// Environment for all items reported with the global client.
	Environment = "development"
)

type rollbarSuccess struct {
	Result map[string]string `json:"result"`
}

// Client reports items to a single Rollbar project.
type Client interface {
	Critical(err error, custom map[string]string) (uuid string, e error)
	Error(err error, custom map[string]string) (uuid string, e error)
	Warning(err error, custom map[string]string) (uuid string, e error)
	Info(msg string, custom map[string]string) (uuid string, e error)
	Debug(msg string, custom map[string]string) (uuid string, e error)
}

type rollbarClient struct {
	token string
	env   string
}

// New creates a new Rollbar client that reports items to the given project
// token and with the given environment (eg. "production", "development", etc).
func New(token, env string) Client {
	return &rollbarClient{token, env}
}

func Critical(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("critical", err, 3, custom)
}

func Error(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("error", err, 3, custom)
}

func Warning(err error, custom map[string]string) (uuid string, e error) {
	client := rollbarClient{Token, Environment}
	return client.skipStack("warning", err, 3, custom)
}

func Info(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Info(msg, custom)
}

func Debug(msg string, custom map[string]string) (uuid string, e error) {
	return New(Token, Environment).Debug(msg, custom)
}

func (c *rollbarClient) Critical(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("critical", err, 3, custom)
}

func (c *rollbarClient) Error(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("error", err, 3, custom)
}

func (c *rollbarClient) Warning(err error, custom map[string]string) (uuid string, e error) {
	return c.skipStack("warning", err, 3, custom)
}

func (c *rollbarClient) Info(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem("info", msg, custom)
	return c.send(item)
}

func (c *rollbarClient) Debug(msg string, custom map[string]string) (uuid string, e error) {
	item := c.buildMessageItem("debug", msg, custom)
	return c.send(item)
}

func (c *rollbarClient) skipStack(level string, err error, skip int, custom map[string]string) (uuid string, e error) {
	item := c.buildTraceItem(level, err, buildStack(skip), custom)
	return c.send(item)
}

func (c *rollbarClient) buildTraceItem(level string, err error, s stack, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, err.Error(), custom)
	itemData := item["data"].(map[string]interface{})
	itemData["fingerprint"] = stackFingerprint(err.Error(), s)
	itemData["body"] = map[string]interface{}{
		"trace": map[string]interface{}{
			"frames": s,
			"exception": map[string]interface{}{
				"class":   errorClass(err),
				"message": err.Error(),
			},
		},
	}

	return item
}

func (c *rollbarClient) buildMessageItem(level string, msg string, custom map[string]string) (item map[string]interface{}) {
	item = c.buildItem(level, msg, custom)
	itemData := item["data"].(map[string]interface{})
	itemData["body"] = map[string]interface{}{
		"message": map[string]interface{}{
			"body": msg,
		},
	}

	return item
}

func (c *rollbarClient) buildItem(level, title string, custom map[string]string) map[string]interface{} {
	hostname, _ := os.Hostname()

	return map[string]interface{}{
		"access_token": c.token,
		"data": map[string]interface{}{
			"environment": c.env,
			"title":       title,
			"level":       level,
			"timestamp":   time.Now().Unix(),
			"platform":    runtime.GOOS,
			"language":    clientLanguage,
			"server": map[string]interface{}{
				"host": hostname,
			},
			"notifier": map[string]interface{}{
				"name":    clientName,
				"version": clientVersion,
			},
			"custom": custom,
		},
	}
}

// send reports the given item to Rollbar and returns either a UUID for the
// reported item or an error.
func (c *rollbarClient) send(item map[string]interface{}) (uuid string, err error) {
	if len(c.token) == 0 {
		return "", nil
	}

	jsonBody, err := json.Marshal(item)
	if err != nil {
		return "", err
	}

	resp, err := http.Post(endpoint, "application/json", bytes.NewReader(jsonBody))
	if err != nil {
		return "", err
	}
	defer func() { resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		return "", fmt.Errorf("Rollbar returned %s", resp.Status)
	}

	// Extract UUID from JSON response
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", nil
	}
	success := rollbarSuccess{}
	json.Unmarshal(body, &success)

	return success.Result["uuid"], nil
}

// errorClass returns a class name for an error (eg.  "ErrUnexpectedEOF").  For
// string errors, it returns a checksum of the error string.
func errorClass(err error) string {
	class := reflect.TypeOf(err).String()
	if class == "" {
		return "panic"
	} else if class == "*errors.errorString" {
		checksum := adler32.Checksum([]byte(err.Error()))
		return fmt.Sprintf("{%x}", checksum)
	} else {
		return strings.TrimPrefix(class, "*")
	}
}
<|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 append(resp.Answer, resp.Extra...) {
				// 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>use golang.org/x/net<commit_after>package mdns

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

	"github.com/miekg/dns"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

// 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 append(resp.Answer, resp.Extra...) {
				// 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 allscripts

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
)

const ehrtokenregex = "[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}"
const dateregex = `^(19|20)[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}`

func checkParamValid(validvals []string, passed string) (err error) {
	for _, p := range validvals {
		if p == passed {
			return
		}
	}
	return errors.New(fmt.Sprintf("%s is not a valid value", passed))
}

func checkDateValid(date string) (err error) {
	var matched bool
	matched, err = regexp.Match(dateregex, []byte(date))
	if err != nil || !matched {
		return errors.New("not a valid date")
	}

	return
}

func checkValid(val []byte, regex string) (err error) {
	var matched bool
	matched, err = regexp.Match(regex, val)
	if err != nil || !matched {
		return errors.New("not a valid value")
	}

	return
}

// Client is a HTTP client of the Allscripts Professional API.
type Client struct {
	Userid      string
	Appname     string
	Appusername string
	Apppassword string
	Endpoint    string
	Token       string
}

type magicJsonRequest struct {
	Userid  string `json:"AppUserID"`
	Action  string `json:"Action"`
	Appname string `json:"Appname"`
	Patient string `json:"PatientID"`
	Token   string `json:"Token"`
	Param1  string `json:"Parameter1`
	Param2  string `json:"Parameter2`
	Param3  string `json:"Parameter3`
	Param4  string `json:"Parameter4`
	Param5  string `json:"Parameter5`
	Param6  string `json:"Parameter6`
	Data    string `json:"Data"`
}

type tokenRequest struct {
	Username string `json:"Username"`
	Password string `json:"Password"`
}

// NewClient creates a new Client and returns it.
func NewClient(userid string, appname string, appusername string,
	apppassword string, endpoint string) Client {
	return Client{userid, appname, appusername, apppassword, endpoint, ""}
}

func (c *Client) getToken() (token string, err error) {
	var (
		raw     []byte
		req     *http.Request
		res     *http.Response
		resRaw  []byte
		matched bool
	)

	tr := tokenRequest{
		c.Appusername,
		c.Apppassword,
	}

	raw, err = json.Marshal(tr)
	if err != nil {
		return "", errors.New("unable to get app token")
	}

	client := &http.Client{}
	req, err = http.NewRequest("POST", c.Endpoint+"/GetToken",
		bytes.NewReader(raw))
	if err != nil {
		return "", err
	}
	req.Header.Add("Content-Type", "application/json")

	res, err = client.Do(req)
	if err != nil {
		return "", err
	}

	resRaw, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	res.Body.Close()

	matched, err = regexp.Match(ehrtokenregex, resRaw)
	if err != nil || !matched {
		return "", errors.New("unable to fetch GRT token from Allscripts")
	}

	token = string(resRaw)

	return
}

func (c *Client) constructReq(action string, data map[string]string) (req magicJsonRequest,
	err error) {
	if c.Token == "" {
		var token string
		token, err = c.getToken()
		if err != nil {
			return
		}
		c.Token = token
	}

	req = magicJsonRequest{
		c.Userid,
		action,
		c.Appname,
		data["Patient"],
		c.Token,
		data["Param1"],
		data["Param2"],
		data["Param3"],
		data["Param4"],
		data["Param5"],
		data["Param6"],
		data["Data"],
	}

	return
}

func (c *Client) makeRequest(reqbody magicJsonRequest) (resp []byte,
	err error) {
	var (
		raw []byte
		req *http.Request
		res *http.Response
	)

	raw, err = json.Marshal(reqbody)
	if err != nil {
		return
	}

	client := &http.Client{}
	req, err = http.NewRequest("POST", c.Endpoint+"/MagicJson",
		bytes.NewReader(raw))
	if err != nil {
		return []byte(nil), err
	}
	req.Header.Add("Content-Type", "application/json")

	res, err = client.Do(req)
	if err != nil {
		return []byte(nil), err
	}

	resp, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return []byte(nil), err
	}
	res.Body.Close()

	return
}
<commit_msg>Adding WithHTTPClient method so that users can add their own customizer http.Client<commit_after>package allscripts

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
)

const ehrtokenregex = "[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}"
const dateregex = `^(19|20)[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}`

func checkParamValid(validvals []string, passed string) (err error) {
	for _, p := range validvals {
		if p == passed {
			return
		}
	}
	return errors.New(fmt.Sprintf("%s is not a valid value", passed))
}

func checkDateValid(date string) (err error) {
	var matched bool
	matched, err = regexp.Match(dateregex, []byte(date))
	if err != nil || !matched {
		return errors.New("not a valid date")
	}

	return
}

func checkValid(val []byte, regex string) (err error) {
	var matched bool
	matched, err = regexp.Match(regex, val)
	if err != nil || !matched {
		return errors.New("not a valid value")
	}

	return
}

// Client is a HTTP client of the Allscripts Professional API.
type Client struct {
	Userid      string
	Appname     string
	Appusername string
	Apppassword string
	Endpoint    string
	Token       string
	HTTPClient  *http.Client
}

type magicJsonRequest struct {
	Userid  string `json:"AppUserID"`
	Action  string `json:"Action"`
	Appname string `json:"Appname"`
	Patient string `json:"PatientID"`
	Token   string `json:"Token"`
	Param1  string `json:"Parameter1`
	Param2  string `json:"Parameter2`
	Param3  string `json:"Parameter3`
	Param4  string `json:"Parameter4`
	Param5  string `json:"Parameter5`
	Param6  string `json:"Parameter6`
	Data    string `json:"Data"`
}

type tokenRequest struct {
	Username string `json:"Username"`
	Password string `json:"Password"`
}

// NewClient creates a new Client and returns it.
func NewClient(userid string, appname string, appusername string,
	apppassword string, endpoint string) Client {
	return Client{userid, appname, appusername, apppassword,
		endpoint, "", new(http.Client)}
}

// WithHTTPClient sets a Client's HTTP client for making HTTP requests, returning
// a Client pointer for chaining.
func (c *Client) WithHTTPClient(hc *http.Client) *Client {
	c.HTTPClient = hc
	return c
}

func (c *Client) getToken() (token string, err error) {
	var (
		raw     []byte
		req     *http.Request
		res     *http.Response
		resRaw  []byte
		matched bool
	)

	tr := tokenRequest{
		c.Appusername,
		c.Apppassword,
	}

	raw, err = json.Marshal(tr)
	if err != nil {
		return "", errors.New("unable to get app token")
	}

	req, err = http.NewRequest("POST", c.Endpoint+"/GetToken",
		bytes.NewReader(raw))
	if err != nil {
		return "", err
	}
	req.Header.Add("Content-Type", "application/json")

	res, err = c.HTTPClient.Do(req)
	if err != nil {
		return "", err
	}

	resRaw, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	res.Body.Close()

	matched, err = regexp.Match(ehrtokenregex, resRaw)
	if err != nil || !matched {
		return "", errors.New("unable to fetch GRT token from Allscripts")
	}

	token = string(resRaw)

	return
}

func (c *Client) constructReq(action string, data map[string]string) (req magicJsonRequest,
	err error) {
	if c.Token == "" {
		var token string
		token, err = c.getToken()
		if err != nil {
			return
		}
		c.Token = token
	}

	req = magicJsonRequest{
		c.Userid,
		action,
		c.Appname,
		data["Patient"],
		c.Token,
		data["Param1"],
		data["Param2"],
		data["Param3"],
		data["Param4"],
		data["Param5"],
		data["Param6"],
		data["Data"],
	}

	return
}

func (c *Client) makeRequest(reqbody magicJsonRequest) (resp []byte,
	err error) {
	var (
		raw []byte
		req *http.Request
		res *http.Response
	)

	raw, err = json.Marshal(reqbody)
	if err != nil {
		return
	}

	req, err = http.NewRequest("POST", c.Endpoint+"/MagicJson",
		bytes.NewReader(raw))
	if err != nil {
		return []byte(nil), err
	}
	req.Header.Add("Content-Type", "application/json")

	res, err = c.HTTPClient.Do(req)
	if err != nil {
		return []byte(nil), err
	}

	resp, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return []byte(nil), err
	}
	res.Body.Close()

	return
}
<|endoftext|>


<commit_before>package lbapi

import (
	"encoding/json"
	"net/http"
	"time"
)

type Client struct {
	http.Client
	URL string
	ID  string
	Key string
}

// NewClient creates a client structure with a HTTP client for the specified API.
func NewClient(api, resellerid, apikey string) *Client {
	if api == "" {
		api = APIURL
	}

	return &Client{
		Client: http.Client{Timeout: time.Second * 10},
		URL:    api,
		ID:     resellerid,
		Key:    apikey,
	}
}

// getReponse is the internal call to fetch a URL's JSON,
// decode it into a maplist and hand it over to massage into
// a proper structure.
func (c *Client) getResponse(url string) (*maplist, error) {
	res, err := c.Client.Get(url)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	decoder := json.NewDecoder(res.Body)
	var list maplist
	err = decoder.Decode(&list)
	if err != nil {
		return nil, err
	}

	return &list, nil
}
<commit_msg>Some commands oddly use POST.<commit_after>package lbapi

import (
	"encoding/json"
	"net/http"
	"time"
)

type Client struct {
	http.Client
	URL string
	ID  string
	Key string
}

// NewClient creates a client structure with a HTTP client for the specified API.
func NewClient(api, resellerid, apikey string) *Client {
	if api == "" {
		api = APIURL
	}

	return &Client{
		Client: http.Client{Timeout: time.Second * 10},
		URL:    api,
		ID:     resellerid,
		Key:    apikey,
	}
}

// getReponse is the internal call to fetch a URL's JSON,
// decode it into a maplist and hand it over to massage into
// a proper structure.
func (c *Client) getResponse(url string) (*maplist, error) {
	res, err := c.Client.Get(url)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	decoder := json.NewDecoder(res.Body)
	var list maplist
	err = decoder.Decode(&list)
	if err != nil {
		return nil, err
	}

	return &list, nil
}

// postResponse does pretty much the same as getResponse().
// Some LogicBoxes API calls use POST instead, but there doesn't
// seem to be any actual logic to why, as they aren't actually
// posting any body content.
func (c *Client) postResponse(url string) (*maplist, error) {
	res, err := c.Client.Post(url, "", nil)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	decoder := json.NewDecoder(res.Body)
	var list maplist
	err = decoder.Decode(&list)
	if err != nil {
		return nil, err
	}

	return &list, nil
}
<|endoftext|>


<commit_before>package quic

import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/qerr"
)

type client struct {
	mutex     sync.Mutex
	listenErr error

	conn     connection
	hostname string

	errorChan     chan struct{}
	handshakeChan <-chan handshakeEvent

	config            *Config
	versionNegotiated bool // has version negotiation completed yet

	connectionID protocol.ConnectionID
	version      protocol.VersionNumber

	session packetHandler
}

var (
	errCloseSessionForNewVersion = errors.New("closing session in order to recreate it with a new version")
)

// DialAddr establishes a new QUIC connection to a server.
// The hostname for SNI is taken from the given address.
func DialAddr(addr string, config *Config) (Session, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return Dial(udpConn, udpAddr, addr, config)
}

// DialAddrNonFWSecure establishes a new QUIC connection to a server.
// The hostname for SNI is taken from the given address.
func DialAddrNonFWSecure(addr string, config *Config) (NonFWSession, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return DialNonFWSecure(udpConn, udpAddr, addr, config)
}

// DialNonFWSecure establishes a new non-forward-secure QUIC connection to a server using a net.PacketConn.
// The host parameter is used for SNI.
func DialNonFWSecure(pconn net.PacketConn, remoteAddr net.Addr, host string, config *Config) (NonFWSession, error) {
	connID, err := utils.GenerateConnectionID()
	if err != nil {
		return nil, err
	}

	hostname, _, err := net.SplitHostPort(host)
	if err != nil {
		return nil, err
	}

	clientConfig := populateClientConfig(config)
	c := &client{
		conn:         &conn{pconn: pconn, currentAddr: remoteAddr},
		connectionID: connID,
		hostname:     hostname,
		config:       clientConfig,
		version:      clientConfig.Versions[0],
		errorChan:    make(chan struct{}),
	}

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

	utils.Infof("Starting new connection to %s (%s -> %s), connectionID %x, version %d", hostname, c.conn.LocalAddr().String(), c.conn.RemoteAddr().String(), c.connectionID, c.version)

	return c.session.(NonFWSession), c.establishSecureConnection()
}

// Dial establishes a new QUIC connection to a server using a net.PacketConn.
// The host parameter is used for SNI.
func Dial(pconn net.PacketConn, remoteAddr net.Addr, host string, config *Config) (Session, error) {
	sess, err := DialNonFWSecure(pconn, remoteAddr, host, config)
	if err != nil {
		return nil, err
	}
	err = sess.WaitUntilHandshakeComplete()
	if err != nil {
		return nil, err
	}
	return sess, nil
}

func populateClientConfig(config *Config) *Config {
	versions := config.Versions
	if len(versions) == 0 {
		versions = protocol.SupportedVersions
	}

	handshakeTimeout := protocol.DefaultHandshakeTimeout
	if config.HandshakeTimeout != 0 {
		handshakeTimeout = config.HandshakeTimeout
	}

	maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow
	if maxReceiveStreamFlowControlWindow == 0 {
		maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindowClient
	}
	maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow
	if maxReceiveConnectionFlowControlWindow == 0 {
		maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindowClient
	}

	return &Config{
		TLSConfig:                             config.TLSConfig,
		Versions:                              versions,
		HandshakeTimeout:                      handshakeTimeout,
		RequestConnectionIDTruncation:         config.RequestConnectionIDTruncation,
		MaxReceiveStreamFlowControlWindow:     maxReceiveStreamFlowControlWindow,
		MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,
	}
}

// establishSecureConnection returns as soon as the connection is secure (as opposed to forward-secure)
func (c *client) establishSecureConnection() error {
	go c.listen()

	select {
	case <-c.errorChan:
		return c.listenErr
	case ev := <-c.handshakeChan:
		if ev.err != nil {
			return ev.err
		}
		if ev.encLevel != protocol.EncryptionSecure {
			return fmt.Errorf("Client BUG: Expected encryption level to be secure, was %s", ev.encLevel)
		}
		return nil
	}
}

// Listen listens
func (c *client) listen() {
	var err error

	for {
		var n int
		var addr net.Addr
		data := getPacketBuffer()
		data = data[:protocol.MaxReceivePacketSize]
		// The packet size should not exceed protocol.MaxReceivePacketSize bytes
		// If it does, we only read a truncated packet, which will then end up undecryptable
		n, addr, err = c.conn.Read(data)
		if err != nil {
			if !strings.HasSuffix(err.Error(), "use of closed network connection") {
				c.session.Close(err)
			}
			break
		}
		data = data[:n]

		err = c.handlePacket(addr, data)
		if err != nil {
			utils.Errorf("error handling packet: %s", err.Error())
			c.session.Close(err)
			break
		}
	}
}

func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) error {
	rcvTime := time.Now()

	r := bytes.NewReader(packet)
	hdr, err := ParsePublicHeader(r, protocol.PerspectiveServer)
	if err != nil {
		return qerr.Error(qerr.InvalidPacketHeader, err.Error())
	}
	hdr.Raw = packet[:len(packet)-r.Len()]

	c.mutex.Lock()
	defer c.mutex.Unlock()

	// ignore delayed / duplicated version negotiation packets
	if c.versionNegotiated && hdr.VersionFlag {
		return nil
	}

	// this is the first packet after the client sent a packet with the VersionFlag set
	// if the server doesn't send a version negotiation packet, it supports the suggested version
	if !hdr.VersionFlag && !c.versionNegotiated {
		c.versionNegotiated = true
	}

	if hdr.VersionFlag {
		// version negotiation packets have no payload
		return c.handlePacketWithVersionFlag(hdr)
	}

	c.session.handlePacket(&receivedPacket{
		remoteAddr:   remoteAddr,
		publicHeader: hdr,
		data:         packet[len(packet)-r.Len():],
		rcvTime:      rcvTime,
	})
	return nil
}

func (c *client) handlePacketWithVersionFlag(hdr *PublicHeader) error {
	for _, v := range hdr.SupportedVersions {
		if v == c.version {
			// the version negotiation packet contains the version that we offered
			// this might be a packet sent by an attacker (or by a terribly broken server implementation)
			// ignore it
			return nil
		}
	}

	newVersion := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions)
	if newVersion == protocol.VersionUnsupported {
		return qerr.InvalidVersion
	}

	// switch to negotiated version
	c.version = newVersion
	c.versionNegotiated = true
	var err error
	c.connectionID, err = utils.GenerateConnectionID()
	if err != nil {
		return err
	}
	utils.Infof("Switching to QUIC version %d. New connection ID: %x", newVersion, c.connectionID)

	c.session.Close(errCloseSessionForNewVersion)
	return c.createNewSession(hdr.SupportedVersions)
}

func (c *client) createNewSession(negotiatedVersions []protocol.VersionNumber) error {
	var err error
	c.session, c.handshakeChan, err = newClientSession(
		c.conn,
		c.hostname,
		c.version,
		c.connectionID,
		c.config,
		negotiatedVersions,
	)
	if err != nil {
		return err
	}

	go func() {
		// session.run() returns as soon as the session is closed
		err := c.session.run()
		if err == errCloseSessionForNewVersion {
			return
		}
		c.listenErr = err
		close(c.errorChan)

		utils.Infof("Connection %x closed.", c.connectionID)
		c.conn.Close()
	}()
	return nil
}
<commit_msg>use Config.TLSConfig.ServerName as client hostname if provided<commit_after>package quic

import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/qerr"
)

type client struct {
	mutex     sync.Mutex
	listenErr error

	conn     connection
	hostname string

	errorChan     chan struct{}
	handshakeChan <-chan handshakeEvent

	config            *Config
	versionNegotiated bool // has version negotiation completed yet

	connectionID protocol.ConnectionID
	version      protocol.VersionNumber

	session packetHandler
}

var (
	errCloseSessionForNewVersion = errors.New("closing session in order to recreate it with a new version")
)

// DialAddr establishes a new QUIC connection to a server.
// The hostname for SNI is taken from the given address.
func DialAddr(addr string, config *Config) (Session, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return Dial(udpConn, udpAddr, addr, config)
}

// DialAddrNonFWSecure establishes a new QUIC connection to a server.
// The hostname for SNI is taken from the given address.
func DialAddrNonFWSecure(addr string, config *Config) (NonFWSession, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return DialNonFWSecure(udpConn, udpAddr, addr, config)
}

// DialNonFWSecure establishes a new non-forward-secure QUIC connection to a server using a net.PacketConn.
// The host parameter is used for SNI.
func DialNonFWSecure(pconn net.PacketConn, remoteAddr net.Addr, host string, config *Config) (NonFWSession, error) {
	connID, err := utils.GenerateConnectionID()
	if err != nil {
		return nil, err
	}

	var hostname string
	if config.TLSConfig != nil {
		hostname = config.TLSConfig.ServerName
	}

	if hostname == "" {
		hostname, _, err = net.SplitHostPort(host)
		if err != nil {
			return nil, err
		}
	}

	clientConfig := populateClientConfig(config)
	c := &client{
		conn:         &conn{pconn: pconn, currentAddr: remoteAddr},
		connectionID: connID,
		hostname:     hostname,
		config:       clientConfig,
		version:      clientConfig.Versions[0],
		errorChan:    make(chan struct{}),
	}

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

	utils.Infof("Starting new connection to %s (%s -> %s), connectionID %x, version %d", hostname, c.conn.LocalAddr().String(), c.conn.RemoteAddr().String(), c.connectionID, c.version)

	return c.session.(NonFWSession), c.establishSecureConnection()
}

// Dial establishes a new QUIC connection to a server using a net.PacketConn.
// The host parameter is used for SNI.
func Dial(pconn net.PacketConn, remoteAddr net.Addr, host string, config *Config) (Session, error) {
	sess, err := DialNonFWSecure(pconn, remoteAddr, host, config)
	if err != nil {
		return nil, err
	}
	err = sess.WaitUntilHandshakeComplete()
	if err != nil {
		return nil, err
	}
	return sess, nil
}

func populateClientConfig(config *Config) *Config {
	versions := config.Versions
	if len(versions) == 0 {
		versions = protocol.SupportedVersions
	}

	handshakeTimeout := protocol.DefaultHandshakeTimeout
	if config.HandshakeTimeout != 0 {
		handshakeTimeout = config.HandshakeTimeout
	}

	maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow
	if maxReceiveStreamFlowControlWindow == 0 {
		maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindowClient
	}
	maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow
	if maxReceiveConnectionFlowControlWindow == 0 {
		maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindowClient
	}

	return &Config{
		TLSConfig:                             config.TLSConfig,
		Versions:                              versions,
		HandshakeTimeout:                      handshakeTimeout,
		RequestConnectionIDTruncation:         config.RequestConnectionIDTruncation,
		MaxReceiveStreamFlowControlWindow:     maxReceiveStreamFlowControlWindow,
		MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,
	}
}

// establishSecureConnection returns as soon as the connection is secure (as opposed to forward-secure)
func (c *client) establishSecureConnection() error {
	go c.listen()

	select {
	case <-c.errorChan:
		return c.listenErr
	case ev := <-c.handshakeChan:
		if ev.err != nil {
			return ev.err
		}
		if ev.encLevel != protocol.EncryptionSecure {
			return fmt.Errorf("Client BUG: Expected encryption level to be secure, was %s", ev.encLevel)
		}
		return nil
	}
}

// Listen listens
func (c *client) listen() {
	var err error

	for {
		var n int
		var addr net.Addr
		data := getPacketBuffer()
		data = data[:protocol.MaxReceivePacketSize]
		// The packet size should not exceed protocol.MaxReceivePacketSize bytes
		// If it does, we only read a truncated packet, which will then end up undecryptable
		n, addr, err = c.conn.Read(data)
		if err != nil {
			if !strings.HasSuffix(err.Error(), "use of closed network connection") {
				c.session.Close(err)
			}
			break
		}
		data = data[:n]

		err = c.handlePacket(addr, data)
		if err != nil {
			utils.Errorf("error handling packet: %s", err.Error())
			c.session.Close(err)
			break
		}
	}
}

func (c *client) handlePacket(remoteAddr net.Addr, packet []byte) error {
	rcvTime := time.Now()

	r := bytes.NewReader(packet)
	hdr, err := ParsePublicHeader(r, protocol.PerspectiveServer)
	if err != nil {
		return qerr.Error(qerr.InvalidPacketHeader, err.Error())
	}
	hdr.Raw = packet[:len(packet)-r.Len()]

	c.mutex.Lock()
	defer c.mutex.Unlock()

	// ignore delayed / duplicated version negotiation packets
	if c.versionNegotiated && hdr.VersionFlag {
		return nil
	}

	// this is the first packet after the client sent a packet with the VersionFlag set
	// if the server doesn't send a version negotiation packet, it supports the suggested version
	if !hdr.VersionFlag && !c.versionNegotiated {
		c.versionNegotiated = true
	}

	if hdr.VersionFlag {
		// version negotiation packets have no payload
		return c.handlePacketWithVersionFlag(hdr)
	}

	c.session.handlePacket(&receivedPacket{
		remoteAddr:   remoteAddr,
		publicHeader: hdr,
		data:         packet[len(packet)-r.Len():],
		rcvTime:      rcvTime,
	})
	return nil
}

func (c *client) handlePacketWithVersionFlag(hdr *PublicHeader) error {
	for _, v := range hdr.SupportedVersions {
		if v == c.version {
			// the version negotiation packet contains the version that we offered
			// this might be a packet sent by an attacker (or by a terribly broken server implementation)
			// ignore it
			return nil
		}
	}

	newVersion := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions)
	if newVersion == protocol.VersionUnsupported {
		return qerr.InvalidVersion
	}

	// switch to negotiated version
	c.version = newVersion
	c.versionNegotiated = true
	var err error
	c.connectionID, err = utils.GenerateConnectionID()
	if err != nil {
		return err
	}
	utils.Infof("Switching to QUIC version %d. New connection ID: %x", newVersion, c.connectionID)

	c.session.Close(errCloseSessionForNewVersion)
	return c.createNewSession(hdr.SupportedVersions)
}

func (c *client) createNewSession(negotiatedVersions []protocol.VersionNumber) error {
	var err error
	c.session, c.handshakeChan, err = newClientSession(
		c.conn,
		c.hostname,
		c.version,
		c.connectionID,
		c.config,
		negotiatedVersions,
	)
	if err != nil {
		return err
	}

	go func() {
		// session.run() returns as soon as the session is closed
		err := c.session.run()
		if err == errCloseSessionForNewVersion {
			return
		}
		c.listenErr = err
		close(c.errorChan)

		utils.Infof("Connection %x closed.", c.connectionID)
		c.conn.Close()
	}()
	return nil
}
<|endoftext|>


<commit_before>package paypalsdk

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
)

// NewClient returns new Client struct
func NewClient(clientID string, secret string, APIBase string) (*Client, error) {
	if clientID == "" || secret == "" || APIBase == "" {
		return &Client{}, errors.New("ClientID, Secret and APIBase are required to create a Client")
	}

	return &Client{
		&http.Client{},
		clientID,
		secret,
		APIBase,
		nil,
	}, nil
}

// Send makes a request to the API, the response body will be
// unmarshaled into v, or if v is an io.Writer, the response will
// be written to it without decoding
func (c *Client) Send(req *http.Request, v interface{}) error {
	// Set default headers
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Accept-Language", "en_US")

	// Default values for headers
	if req.Header.Get("Content-type") == "" {
		req.Header.Set("Content-type", "application/json")
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		errResp := &ErrorResponse{Response: resp}
		data, err := ioutil.ReadAll(resp.Body)

		if err == nil && len(data) > 0 {
			json.Unmarshal(data, errResp)
		}

		return errResp
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
<commit_msg>SetAccessToken<commit_after>package paypalsdk

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
)

// NewClient returns new Client struct
func NewClient(clientID string, secret string, APIBase string) (*Client, error) {
	if clientID == "" || secret == "" || APIBase == "" {
		return &Client{}, errors.New("ClientID, Secret and APIBase are required to create a Client")
	}

	return &Client{
		&http.Client{},
		clientID,
		secret,
		APIBase,
		nil,
	}, nil
}

// SetAccessToken sets saved token to current client
func (c *Client) SetAccessToken(token string) error {
	c.Token = &TokenResponse{
		Token: token,
	}

	return nil
}

// Send makes a request to the API, the response body will be
// unmarshaled into v, or if v is an io.Writer, the response will
// be written to it without decoding
func (c *Client) Send(req *http.Request, v interface{}) error {
	// Set default headers
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Accept-Language", "en_US")

	// Default values for headers
	if req.Header.Get("Content-type") == "" {
		req.Header.Set("Content-type", "application/json")
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		errResp := &ErrorResponse{Response: resp}
		data, err := ioutil.ReadAll(resp.Body)

		if err == nil && len(data) > 0 {
			json.Unmarshal(data, errResp)
		}

		return errResp
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"reflect"

	"github.com/jmoiron/sqlx"
)

type Client struct {
	db               *sqlx.DB
	history          []string
	connectionString string
}

type Row []interface{}

type Result struct {
	Columns []string `json:"columns"`
	Rows    []Row    `json:"rows"`
}

func NewClient() (*Client, error) {
	str, err := buildConnectionString(options)

	if options.Debug {
		fmt.Println("Creating a new client with: %s", str)
	}

	if err != nil {
		return nil, err
	}

	db, err := sqlx.Open("postgres", str)

	if err != nil {
		return nil, err
	}

	return &Client{db: db, connectionString: str}, nil
}

func NewClientFromUrl(url string) (*Client, error) {
	if options.Debug {
		fmt.Println("Creating a new client with: %s", url)
	}

	db, err := sqlx.Open("postgres", url)

	if err != nil {
		return nil, err
	}

	return &Client{db: db, connectionString: url}, nil
}

func (client *Client) Test() error {
	return client.db.Ping()
}

func (client *Client) Info() (*Result, error) {
	return client.query(PG_INFO)
}

func (client *Client) Databases() ([]string, error) {
	return client.fetchRows(PG_DATABASES)
}

func (client *Client) Tables() ([]string, error) {
	return client.fetchRows(PG_TABLES)
}

func (client *Client) Table(table string) (*Result, error) {
	return client.query(PG_TABLE_SCHEMA, table)
}

func (client *Client) TableInfo(table string) (*Result, error) {
	return client.query(PG_TABLE_INFO, table)
}

func (client *Client) TableIndexes(table string) (*Result, error) {
	res, err := client.query(PG_TABLE_INDEXES, table)

	if err != nil {
		return nil, err
	}

	return res, err
}

func (client *Client) Query(query string) (*Result, error) {
	client.recordQuery(query)
	return client.query(query)
}

func (client *Client) query(query string, args ...interface{}) (*Result, error) {
	rows, err := client.db.Queryx(query, args...)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	cols, err := rows.Columns()

	if err != nil {
		return nil, err
	}

	result := Result{
		Columns: cols,
	}

	for rows.Next() {
		obj, err := rows.SliceScan()

		for i, item := range obj {
			if item == nil {
				obj[i] = nil
			} else {
				t := reflect.TypeOf(item).Kind().String()

				if t == "slice" {
					obj[i] = string(item.([]byte))
				}
			}
		}

		if err == nil {
			result.Rows = append(result.Rows, obj)
		}
	}

	return &result, nil
}

func (res *Result) Format() []map[string]interface{} {
	var items []map[string]interface{}

	for _, row := range res.Rows {
		item := make(map[string]interface{})

		for i, c := range res.Columns {
			item[c] = row[i]
		}

		items = append(items, item)
	}

	return items
}

func (res *Result) CSV() []byte {
	buff := &bytes.Buffer{}
	writer := csv.NewWriter(buff)

	writer.Write(res.Columns)

	for _, row := range res.Rows {
		record := make([]string, len(res.Columns))

		for i, item := range row {
			if item != nil {
				record[i] = fmt.Sprintf("%v", item)
			} else {
				record[i] = ""
			}
		}

		err := writer.Write(record)

		if err != nil {
			fmt.Println(err)
			break
		}
	}

	writer.Flush()
	return buff.Bytes()
}

// Fetch all rows as strings for a single column
func (client *Client) fetchRows(q string) ([]string, error) {
	res, err := client.query(q)

	if err != nil {
		return nil, err
	}

	// Init empty slice so json.Marshal will encode it to "[]" instead of "null"
	results := make([]string, 0)

	for _, row := range res.Rows {
		results = append(results, row[0].(string))
	}

	return results, nil
}

func (client *Client) recordQuery(query string) {
	client.history = append(client.history, query)
}
<commit_msg>Fix debug connection string output<commit_after>package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"reflect"

	"github.com/jmoiron/sqlx"
)

type Client struct {
	db               *sqlx.DB
	history          []string
	connectionString string
}

type Row []interface{}

type Result struct {
	Columns []string `json:"columns"`
	Rows    []Row    `json:"rows"`
}

func NewClient() (*Client, error) {
	str, err := buildConnectionString(options)

	if options.Debug {
		fmt.Println("Creating a new client with:", str)
	}

	if err != nil {
		return nil, err
	}

	db, err := sqlx.Open("postgres", str)

	if err != nil {
		return nil, err
	}

	return &Client{db: db, connectionString: str}, nil
}

func NewClientFromUrl(url string) (*Client, error) {
	if options.Debug {
		fmt.Println("Creating a new client with: %s", url)
	}

	db, err := sqlx.Open("postgres", url)

	if err != nil {
		return nil, err
	}

	return &Client{db: db, connectionString: url}, nil
}

func (client *Client) Test() error {
	return client.db.Ping()
}

func (client *Client) Info() (*Result, error) {
	return client.query(PG_INFO)
}

func (client *Client) Databases() ([]string, error) {
	return client.fetchRows(PG_DATABASES)
}

func (client *Client) Tables() ([]string, error) {
	return client.fetchRows(PG_TABLES)
}

func (client *Client) Table(table string) (*Result, error) {
	return client.query(PG_TABLE_SCHEMA, table)
}

func (client *Client) TableInfo(table string) (*Result, error) {
	return client.query(PG_TABLE_INFO, table)
}

func (client *Client) TableIndexes(table string) (*Result, error) {
	res, err := client.query(PG_TABLE_INDEXES, table)

	if err != nil {
		return nil, err
	}

	return res, err
}

func (client *Client) Query(query string) (*Result, error) {
	client.recordQuery(query)
	return client.query(query)
}

func (client *Client) query(query string, args ...interface{}) (*Result, error) {
	rows, err := client.db.Queryx(query, args...)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	cols, err := rows.Columns()

	if err != nil {
		return nil, err
	}

	result := Result{
		Columns: cols,
	}

	for rows.Next() {
		obj, err := rows.SliceScan()

		for i, item := range obj {
			if item == nil {
				obj[i] = nil
			} else {
				t := reflect.TypeOf(item).Kind().String()

				if t == "slice" {
					obj[i] = string(item.([]byte))
				}
			}
		}

		if err == nil {
			result.Rows = append(result.Rows, obj)
		}
	}

	return &result, nil
}

func (res *Result) Format() []map[string]interface{} {
	var items []map[string]interface{}

	for _, row := range res.Rows {
		item := make(map[string]interface{})

		for i, c := range res.Columns {
			item[c] = row[i]
		}

		items = append(items, item)
	}

	return items
}

func (res *Result) CSV() []byte {
	buff := &bytes.Buffer{}
	writer := csv.NewWriter(buff)

	writer.Write(res.Columns)

	for _, row := range res.Rows {
		record := make([]string, len(res.Columns))

		for i, item := range row {
			if item != nil {
				record[i] = fmt.Sprintf("%v", item)
			} else {
				record[i] = ""
			}
		}

		err := writer.Write(record)

		if err != nil {
			fmt.Println(err)
			break
		}
	}

	writer.Flush()
	return buff.Bytes()
}

// Fetch all rows as strings for a single column
func (client *Client) fetchRows(q string) ([]string, error) {
	res, err := client.query(q)

	if err != nil {
		return nil, err
	}

	// Init empty slice so json.Marshal will encode it to "[]" instead of "null"
	results := make([]string, 0)

	for _, row := range res.Rows {
		results = append(results, row[0].(string))
	}

	return results, nil
}

func (client *Client) recordQuery(query string) {
	client.history = append(client.history, query)
}
<|endoftext|>


<commit_before>package quic

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"

	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/internal/wire"
	"github.com/lucas-clemente/quic-go/qlog"
)

type client struct {
	mutex sync.Mutex

	conn connection
	// If the client is created with DialAddr, we create a packet conn.
	// If it is started with Dial, we take a packet conn as a parameter.
	createdPacketConn bool

	use0RTT bool

	packetHandlers packetHandlerManager

	versionNegotiated                utils.AtomicBool // has the server accepted our version
	receivedVersionNegotiationPacket bool
	negotiatedVersions               []protocol.VersionNumber // the list of versions from the version negotiation packet

	tlsConf *tls.Config
	config  *Config

	srcConnID  protocol.ConnectionID
	destConnID protocol.ConnectionID

	initialPacketNumber protocol.PacketNumber

	initialVersion protocol.VersionNumber
	version        protocol.VersionNumber

	handshakeChan chan struct{}

	session quicSession

	qlogger qlog.Tracer
	logger  utils.Logger
}

var _ packetHandler = &client{}

var (
	// make it possible to mock connection ID generation in the tests
	generateConnectionID           = protocol.GenerateConnectionID
	generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial
	// make it possible to the qlogger
	newQlogger = qlog.NewTracer
)

// DialAddr establishes a new QUIC connection to a server.
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
// The hostname for SNI is taken from the given address.
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
func DialAddr(
	addr string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return DialAddrContext(context.Background(), addr, tlsConf, config)
}

// DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
// The hostname for SNI is taken from the given address.
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
func DialAddrEarly(
	addr string,
	tlsConf *tls.Config,
	config *Config,
) (EarlySession, error) {
	sess, err := dialAddrContext(context.Background(), addr, tlsConf, config, true)
	if err != nil {
		return nil, err
	}
	utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early session")
	return sess, nil
}

// DialAddrContext establishes a new QUIC connection to a server using the provided context.
// See DialAddr for details.
func DialAddrContext(
	ctx context.Context,
	addr string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return dialAddrContext(ctx, addr, tlsConf, config, false)
}

func dialAddrContext(
	ctx context.Context,
	addr string,
	tlsConf *tls.Config,
	config *Config,
	use0RTT bool,
) (quicSession, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return dialContext(ctx, udpConn, udpAddr, addr, tlsConf, config, use0RTT, true)
}

// Dial establishes a new QUIC connection to a server using a net.PacketConn.
// The same PacketConn can be used for multiple calls to Dial and Listen,
// QUIC connection IDs are used for demultiplexing the different connections.
// The host parameter is used for SNI.
// The tls.Config must define an application protocol (using NextProtos).
func Dial(
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false)
}

// DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn.
// The same PacketConn can be used for multiple calls to Dial and Listen,
// QUIC connection IDs are used for demultiplexing the different connections.
// The host parameter is used for SNI.
// The tls.Config must define an application protocol (using NextProtos).
func DialEarly(
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, true, false)
}

// DialContext establishes a new QUIC connection to a server using a net.PacketConn using the provided context.
// See Dial for details.
func DialContext(
	ctx context.Context,
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false)
}

func dialContext(
	ctx context.Context,
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
	use0RTT bool,
	createdPacketConn bool,
) (quicSession, error) {
	if tlsConf == nil {
		return nil, errors.New("quic: tls.Config not set")
	}
	config = populateClientConfig(config, createdPacketConn)
	packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength, config.StatelessResetKey)
	if err != nil {
		return nil, err
	}
	c, err := newClient(pconn, remoteAddr, config, tlsConf, host, use0RTT, createdPacketConn)
	if err != nil {
		return nil, err
	}
	c.packetHandlers = packetHandlers

	if c.config.GetLogWriter != nil {
		if w := c.config.GetLogWriter(c.destConnID); w != nil {
			c.qlogger = newQlogger(w, protocol.PerspectiveClient, c.destConnID)
		}
	}
	if err := c.dial(ctx); err != nil {
		return nil, err
	}
	return c.session, nil
}

func newClient(
	pconn net.PacketConn,
	remoteAddr net.Addr,
	config *Config,
	tlsConf *tls.Config,
	host string,
	use0RTT bool,
	createdPacketConn bool,
) (*client, error) {
	if tlsConf == nil {
		tlsConf = &tls.Config{}
	}
	if tlsConf.ServerName == "" {
		sni := host
		if strings.IndexByte(sni, ':') != -1 {
			var err error
			sni, _, err = net.SplitHostPort(sni)
			if err != nil {
				return nil, err
			}
		}

		tlsConf.ServerName = sni
	}

	// check that all versions are actually supported
	if config != nil {
		for _, v := range config.Versions {
			if !protocol.IsValidVersion(v) {
				return nil, fmt.Errorf("%s is not a valid QUIC version", v)
			}
		}
	}

	srcConnID, err := generateConnectionID(config.ConnectionIDLength)
	if err != nil {
		return nil, err
	}
	destConnID, err := generateConnectionIDForInitial()
	if err != nil {
		return nil, err
	}
	c := &client{
		srcConnID:         srcConnID,
		destConnID:        destConnID,
		conn:              &conn{pconn: pconn, currentAddr: remoteAddr},
		createdPacketConn: createdPacketConn,
		use0RTT:           use0RTT,
		tlsConf:           tlsConf,
		config:            config,
		version:           config.Versions[0],
		handshakeChan:     make(chan struct{}),
		logger:            utils.DefaultLogger.WithPrefix("client"),
	}
	return c, nil
}

func (c *client) dial(ctx context.Context) error {
	c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
	if c.qlogger != nil {
		c.qlogger.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.version, c.srcConnID, c.destConnID)
	}

	c.mutex.Lock()
	c.session = newClientSession(
		c.conn,
		c.packetHandlers,
		c.destConnID,
		c.srcConnID,
		c.config,
		c.tlsConf,
		c.initialPacketNumber,
		c.initialVersion,
		c.use0RTT,
		c.qlogger,
		c.logger,
		c.version,
	)
	c.mutex.Unlock()
	c.packetHandlers.Add(c.srcConnID, c)

	errorChan := make(chan error, 1)
	go func() {
		err := c.session.run() // returns as soon as the session is closed
		if err != errCloseForRecreating && c.createdPacketConn {
			c.packetHandlers.Destroy()
		}
		errorChan <- err
	}()

	// only set when we're using 0-RTT
	// Otherwise, earlySessionChan will be nil. Receiving from a nil chan blocks forever.
	var earlySessionChan <-chan struct{}
	if c.use0RTT {
		earlySessionChan = c.session.earlySessionReady()
	}

	select {
	case <-ctx.Done():
		c.session.shutdown()
		return ctx.Err()
	case err := <-errorChan:
		if err == errCloseForRecreating {
			return c.dial(ctx)
		}
		return err
	case <-earlySessionChan:
		// ready to send 0-RTT data
		return nil
	case <-c.session.HandshakeComplete().Done():
		// handshake successfully completed
		return nil
	}
}

func (c *client) handlePacket(p *receivedPacket) {
	if wire.IsVersionNegotiationPacket(p.data) {
		go c.handleVersionNegotiationPacket(p)
		return
	}

	// this is the first packet we are receiving
	// since it is not a Version Negotiation Packet, this means the server supports the suggested version
	if !c.versionNegotiated.Get() {
		c.versionNegotiated.Set(true)
	}

	c.session.handlePacket(p)
}

func (c *client) handleVersionNegotiationPacket(p *receivedPacket) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	hdr, _, _, err := wire.ParsePacket(p.data, 0)
	if err != nil {
		if c.qlogger != nil {
			c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropHeaderParseError)
		}
		c.logger.Debugf("Error parsing Version Negotiation packet: %s", err)
		return
	}

	// ignore delayed / duplicated version negotiation packets
	if c.receivedVersionNegotiationPacket || c.versionNegotiated.Get() {
		if c.qlogger != nil {
			c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropUnexpectedPacket)
		}
		c.logger.Debugf("Received a delayed Version Negotiation packet.")
		return
	}

	for _, v := range hdr.SupportedVersions {
		if v == c.version {
			if c.qlogger != nil {
				c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropUnexpectedVersion)
			}
			// The Version Negotiation packet contains the version that we offered.
			// This might be a packet sent by an attacker (or by a terribly broken server implementation).
			return
		}
	}

	c.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", hdr.SupportedVersions)
	if c.qlogger != nil {
		c.qlogger.ReceivedVersionNegotiationPacket(hdr)
	}
	newVersion, ok := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions)
	if !ok {
		//nolint:stylecheck
		c.session.destroy(fmt.Errorf("No compatible QUIC version found. We support %s, server offered %s", c.config.Versions, hdr.SupportedVersions))
		c.logger.Debugf("No compatible QUIC version found.")
		return
	}
	c.receivedVersionNegotiationPacket = true
	c.negotiatedVersions = hdr.SupportedVersions

	// switch to negotiated version
	c.initialVersion = c.version
	c.version = newVersion

	c.logger.Infof("Switching to QUIC version %s. New connection ID: %s", newVersion, c.destConnID)
	c.initialPacketNumber = c.session.closeForRecreating()
}

func (c *client) shutdown() {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	if c.session == nil {
		return
	}
	c.session.shutdown()
}

func (c *client) destroy(e error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	if c.session == nil {
		return
	}
	c.session.destroy(e)
}

func (c *client) GetVersion() protocol.VersionNumber {
	c.mutex.Lock()
	v := c.version
	c.mutex.Unlock()
	return v
}

func (c *client) getPerspective() protocol.Perspective {
	return protocol.PerspectiveClient
}
<commit_msg>make DialEarly return EarlySession<commit_after>package quic

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"

	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/internal/wire"
	"github.com/lucas-clemente/quic-go/qlog"
)

type client struct {
	mutex sync.Mutex

	conn connection
	// If the client is created with DialAddr, we create a packet conn.
	// If it is started with Dial, we take a packet conn as a parameter.
	createdPacketConn bool

	use0RTT bool

	packetHandlers packetHandlerManager

	versionNegotiated                utils.AtomicBool // has the server accepted our version
	receivedVersionNegotiationPacket bool
	negotiatedVersions               []protocol.VersionNumber // the list of versions from the version negotiation packet

	tlsConf *tls.Config
	config  *Config

	srcConnID  protocol.ConnectionID
	destConnID protocol.ConnectionID

	initialPacketNumber protocol.PacketNumber

	initialVersion protocol.VersionNumber
	version        protocol.VersionNumber

	handshakeChan chan struct{}

	session quicSession

	qlogger qlog.Tracer
	logger  utils.Logger
}

var _ packetHandler = &client{}

var (
	// make it possible to mock connection ID generation in the tests
	generateConnectionID           = protocol.GenerateConnectionID
	generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial
	// make it possible to the qlogger
	newQlogger = qlog.NewTracer
)

// DialAddr establishes a new QUIC connection to a server.
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
// The hostname for SNI is taken from the given address.
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
func DialAddr(
	addr string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return DialAddrContext(context.Background(), addr, tlsConf, config)
}

// DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
// The hostname for SNI is taken from the given address.
// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
func DialAddrEarly(
	addr string,
	tlsConf *tls.Config,
	config *Config,
) (EarlySession, error) {
	sess, err := dialAddrContext(context.Background(), addr, tlsConf, config, true)
	if err != nil {
		return nil, err
	}
	utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early session")
	return sess, nil
}

// DialAddrContext establishes a new QUIC connection to a server using the provided context.
// See DialAddr for details.
func DialAddrContext(
	ctx context.Context,
	addr string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return dialAddrContext(ctx, addr, tlsConf, config, false)
}

func dialAddrContext(
	ctx context.Context,
	addr string,
	tlsConf *tls.Config,
	config *Config,
	use0RTT bool,
) (quicSession, error) {
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		return nil, err
	}
	return dialContext(ctx, udpConn, udpAddr, addr, tlsConf, config, use0RTT, true)
}

// Dial establishes a new QUIC connection to a server using a net.PacketConn.
// The same PacketConn can be used for multiple calls to Dial and Listen,
// QUIC connection IDs are used for demultiplexing the different connections.
// The host parameter is used for SNI.
// The tls.Config must define an application protocol (using NextProtos).
func Dial(
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false)
}

// DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn.
// The same PacketConn can be used for multiple calls to Dial and Listen,
// QUIC connection IDs are used for demultiplexing the different connections.
// The host parameter is used for SNI.
// The tls.Config must define an application protocol (using NextProtos).
func DialEarly(
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
) (EarlySession, error) {
	return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, true, false)
}

// DialContext establishes a new QUIC connection to a server using a net.PacketConn using the provided context.
// See Dial for details.
func DialContext(
	ctx context.Context,
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
) (Session, error) {
	return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false)
}

func dialContext(
	ctx context.Context,
	pconn net.PacketConn,
	remoteAddr net.Addr,
	host string,
	tlsConf *tls.Config,
	config *Config,
	use0RTT bool,
	createdPacketConn bool,
) (quicSession, error) {
	if tlsConf == nil {
		return nil, errors.New("quic: tls.Config not set")
	}
	config = populateClientConfig(config, createdPacketConn)
	packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength, config.StatelessResetKey)
	if err != nil {
		return nil, err
	}
	c, err := newClient(pconn, remoteAddr, config, tlsConf, host, use0RTT, createdPacketConn)
	if err != nil {
		return nil, err
	}
	c.packetHandlers = packetHandlers

	if c.config.GetLogWriter != nil {
		if w := c.config.GetLogWriter(c.destConnID); w != nil {
			c.qlogger = newQlogger(w, protocol.PerspectiveClient, c.destConnID)
		}
	}
	if err := c.dial(ctx); err != nil {
		return nil, err
	}
	return c.session, nil
}

func newClient(
	pconn net.PacketConn,
	remoteAddr net.Addr,
	config *Config,
	tlsConf *tls.Config,
	host string,
	use0RTT bool,
	createdPacketConn bool,
) (*client, error) {
	if tlsConf == nil {
		tlsConf = &tls.Config{}
	}
	if tlsConf.ServerName == "" {
		sni := host
		if strings.IndexByte(sni, ':') != -1 {
			var err error
			sni, _, err = net.SplitHostPort(sni)
			if err != nil {
				return nil, err
			}
		}

		tlsConf.ServerName = sni
	}

	// check that all versions are actually supported
	if config != nil {
		for _, v := range config.Versions {
			if !protocol.IsValidVersion(v) {
				return nil, fmt.Errorf("%s is not a valid QUIC version", v)
			}
		}
	}

	srcConnID, err := generateConnectionID(config.ConnectionIDLength)
	if err != nil {
		return nil, err
	}
	destConnID, err := generateConnectionIDForInitial()
	if err != nil {
		return nil, err
	}
	c := &client{
		srcConnID:         srcConnID,
		destConnID:        destConnID,
		conn:              &conn{pconn: pconn, currentAddr: remoteAddr},
		createdPacketConn: createdPacketConn,
		use0RTT:           use0RTT,
		tlsConf:           tlsConf,
		config:            config,
		version:           config.Versions[0],
		handshakeChan:     make(chan struct{}),
		logger:            utils.DefaultLogger.WithPrefix("client"),
	}
	return c, nil
}

func (c *client) dial(ctx context.Context) error {
	c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
	if c.qlogger != nil {
		c.qlogger.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.version, c.srcConnID, c.destConnID)
	}

	c.mutex.Lock()
	c.session = newClientSession(
		c.conn,
		c.packetHandlers,
		c.destConnID,
		c.srcConnID,
		c.config,
		c.tlsConf,
		c.initialPacketNumber,
		c.initialVersion,
		c.use0RTT,
		c.qlogger,
		c.logger,
		c.version,
	)
	c.mutex.Unlock()
	c.packetHandlers.Add(c.srcConnID, c)

	errorChan := make(chan error, 1)
	go func() {
		err := c.session.run() // returns as soon as the session is closed
		if err != errCloseForRecreating && c.createdPacketConn {
			c.packetHandlers.Destroy()
		}
		errorChan <- err
	}()

	// only set when we're using 0-RTT
	// Otherwise, earlySessionChan will be nil. Receiving from a nil chan blocks forever.
	var earlySessionChan <-chan struct{}
	if c.use0RTT {
		earlySessionChan = c.session.earlySessionReady()
	}

	select {
	case <-ctx.Done():
		c.session.shutdown()
		return ctx.Err()
	case err := <-errorChan:
		if err == errCloseForRecreating {
			return c.dial(ctx)
		}
		return err
	case <-earlySessionChan:
		// ready to send 0-RTT data
		return nil
	case <-c.session.HandshakeComplete().Done():
		// handshake successfully completed
		return nil
	}
}

func (c *client) handlePacket(p *receivedPacket) {
	if wire.IsVersionNegotiationPacket(p.data) {
		go c.handleVersionNegotiationPacket(p)
		return
	}

	// this is the first packet we are receiving
	// since it is not a Version Negotiation Packet, this means the server supports the suggested version
	if !c.versionNegotiated.Get() {
		c.versionNegotiated.Set(true)
	}

	c.session.handlePacket(p)
}

func (c *client) handleVersionNegotiationPacket(p *receivedPacket) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	hdr, _, _, err := wire.ParsePacket(p.data, 0)
	if err != nil {
		if c.qlogger != nil {
			c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropHeaderParseError)
		}
		c.logger.Debugf("Error parsing Version Negotiation packet: %s", err)
		return
	}

	// ignore delayed / duplicated version negotiation packets
	if c.receivedVersionNegotiationPacket || c.versionNegotiated.Get() {
		if c.qlogger != nil {
			c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropUnexpectedPacket)
		}
		c.logger.Debugf("Received a delayed Version Negotiation packet.")
		return
	}

	for _, v := range hdr.SupportedVersions {
		if v == c.version {
			if c.qlogger != nil {
				c.qlogger.DroppedPacket(qlog.PacketTypeVersionNegotiation, protocol.ByteCount(len(p.data)), qlog.PacketDropUnexpectedVersion)
			}
			// The Version Negotiation packet contains the version that we offered.
			// This might be a packet sent by an attacker (or by a terribly broken server implementation).
			return
		}
	}

	c.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", hdr.SupportedVersions)
	if c.qlogger != nil {
		c.qlogger.ReceivedVersionNegotiationPacket(hdr)
	}
	newVersion, ok := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions)
	if !ok {
		//nolint:stylecheck
		c.session.destroy(fmt.Errorf("No compatible QUIC version found. We support %s, server offered %s", c.config.Versions, hdr.SupportedVersions))
		c.logger.Debugf("No compatible QUIC version found.")
		return
	}
	c.receivedVersionNegotiationPacket = true
	c.negotiatedVersions = hdr.SupportedVersions

	// switch to negotiated version
	c.initialVersion = c.version
	c.version = newVersion

	c.logger.Infof("Switching to QUIC version %s. New connection ID: %s", newVersion, c.destConnID)
	c.initialPacketNumber = c.session.closeForRecreating()
}

func (c *client) shutdown() {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	if c.session == nil {
		return
	}
	c.session.shutdown()
}

func (c *client) destroy(e error) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	if c.session == nil {
		return
	}
	c.session.destroy(e)
}

func (c *client) GetVersion() protocol.VersionNumber {
	c.mutex.Lock()
	v := c.version
	c.mutex.Unlock()
	return v
}

func (c *client) getPerspective() protocol.Perspective {
	return protocol.PerspectiveClient
}
<|endoftext|>