func NewCookie(ns *checkers.Namespace, ms macaroon.Slice) (*http.Cookie, error) {
	if len(ms) == 0 {
		return nil, errgo.New("no macaroons in cookie")
	}
	data, err := json.Marshal(ms)
	if err != nil {
		return nil, errgo.Notef(err, "cannot marshal macaroons")
	}
	cookie := &http.Cookie{
		Name:  fmt.Sprintf("macaroon-%x", ms[0].Signature()),
		Value: base64.StdEncoding.EncodeToString(data),
	}
	expires, found := checkers.MacaroonsExpiryTime(ns, ms)
	if !found {
		expires = time.Now().Add(PermanentExpiryDuration)
	} else if expires.Sub(time.Now()) < time.Minute {
		expires = time.Time{}
	}
	cookie.Expires = expires
	return cookie, nil
}
NewCookie takes a slice of macaroons and returns them
encoded as a cookie. The slice should contain a single primary
macaroon in its first element, and any discharges after that.

The given namespace specifies the first party caveat namespace,
used for deriving the expiry time of the cookie.
package httpbakery
import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"time"
	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
	"golang.org/x/net/publicsuffix"
	"gopkg.in/errgo.v1"
	"gopkg.in/httprequest.v1"
	"gopkg.in/macaroon.v2"
	"gopkg.in/macaroon-bakery.v2/bakery"
	"gopkg.in/macaroon-bakery.v2/bakery/checkers"
)
var unmarshalError = httprequest.ErrorUnmarshaler(&Error{})
const maxDischargeRetries = 3
type DischargeError struct {
	Reason *Error
}
func (e *DischargeError) Error() string {
	return fmt.Sprintf("third party refused discharge: %v", e.Reason)
}
func IsDischargeError(err error) bool {
	_, ok := err.(*DischargeError)
	return ok
}
type InteractionError struct {
	Reason error
}
func (e *InteractionError) Error() string {
	return fmt.Sprintf("cannot start interactive session: %v", e.Reason)
}
func IsInteractionError(err error) bool {
	_, ok := err.(*InteractionError)
	return ok
}
func NewHTTPClient() *http.Client {
	c := *http.DefaultClient
	c.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		if len(via) >= 10 {
			return fmt.Errorf("too many redirects")
		}
		if len(via) == 0 {
			return nil
		}
		for attr, val := range via[0].Header {
			if attr == "Cookie" {
				continue
			}
			if _, ok := req.Header[attr]; !ok {
				req.Header[attr] = val
			}
		}
		return nil
	}
	jar, err := cookiejar.New(&cookiejar.Options{
		PublicSuffixList: publicsuffix.List,
	})
	if err != nil {
		panic(err)
	}
	c.Jar = jar
	return &c
}
type Client struct {
	*http.Client
	InteractionMethods []Interactor
	Key *bakery.KeyPair
	Logger bakery.Logger
}
type Interactor interface {
	Kind() string
	Interact(ctx context.Context, client *Client, location string, interactionRequiredErr *Error) (*DischargeToken, error)
}
type DischargeToken struct {
	Kind string `json:"kind"`
	Value []byte `json:"value"`
}
type LegacyInteractor interface {
	LegacyInteract(ctx context.Context, client *Client, location string, visitURL *url.URL) error
}
func NewClient() *Client {
	return &Client{
		Client: NewHTTPClient(),
	}
}
func (c *Client) AddInteractor(i Interactor) {
	c.InteractionMethods = append(c.InteractionMethods, i)
}
func (c *Client) DischargeAll(ctx context.Context, m *bakery.Macaroon) (macaroon.Slice, error) {
	return bakery.DischargeAllWithKey(ctx, m, c.AcquireDischarge, c.Key)
}
func (c *Client) DischargeAllUnbound(ctx context.Context, ms bakery.Slice) (bakery.Slice, error) {
	return ms.DischargeAll(ctx, c.AcquireDischarge, c.Key)
}
func (c *Client) Do(req *http.Request) (*http.Response, error) {
	return c.do(contextFromRequest(req), req, nil)
}
func (c *Client) DoWithContext(ctx context.Context, req *http.Request) (*http.Response, error) {
	return c.do(ctx, req, nil)
}
func (c *Client) DoWithCustomError(req *http.Request, getError func(resp *http.Response) error) (*http.Response, error) {
	return c.do(contextFromRequest(req), req, getError)
}
func (c *Client) do(ctx context.Context, req *http.Request, getError func(resp *http.Response) error) (*http.Response, error) {
	c.logDebugf(ctx, "client do %s %s {", req.Method, req.URL)
	resp, err := c.do1(ctx, req, getError)
	c.logDebugf(ctx, "} -> error %#v", err)
	return resp, err
}
func (c *Client) do1(ctx context.Context, req *http.Request, getError func(resp *http.Response) error) (*http.Response, error) {
	if getError == nil {
		getError = DefaultGetError
	}
	if c.Client.Jar == nil {
		return nil, errgo.New("no cookie jar supplied in HTTP client")
	}
	rreq, ok := newRetryableRequest(c.Client, req)
	if !ok {
		return nil, fmt.Errorf("request body is not seekable")
	}
	defer rreq.close()
	req.Header.Set(BakeryProtocolHeader, fmt.Sprint(bakery.LatestVersion))
	retry := 0
	for {
		resp, err := c.do2(ctx, rreq, getError)
		if err == nil || !isDischargeRequiredError(err) {
			return resp, errgo.Mask(err, errgo.Any)
		}
		if retry++; retry > maxDischargeRetries {
			return nil, errgo.NoteMask(err, fmt.Sprintf("too many (%d) discharge requests", retry-1), errgo.Any)
		}
		if err1 := c.HandleError(ctx, req.URL, err); err1 != nil {
			return nil, errgo.Mask(err1, errgo.Any)
		}
		c.logDebugf(ctx, "discharge succeeded; retry %d", retry)
	}
}
func (c *Client) do2(ctx context.Context, rreq *retryableRequest, getError func(resp *http.Response) error) (*http.Response, error) {
	httpResp, err := rreq.do(ctx)
	if err != nil {
		return nil, errgo.Mask(err, errgo.Any)
	}
	err = getError(httpResp)
	if err == nil {
		c.logInfof(ctx, "HTTP response OK (status %v)", httpResp.Status)
		return httpResp, nil
	}
	httpResp.Body.Close()
	return nil, errgo.Mask(err, errgo.Any)
}
func (c *Client) HandleError(ctx context.Context, reqURL *url.URL, err error) error {
	respErr, ok := errgo.Cause(err).(*Error)
	if !ok {
		return err
	}
	if respErr.Code != ErrDischargeRequired {
		return respErr
	}
	if respErr.Info == nil || respErr.Info.Macaroon == nil {
		return errgo.New("no macaroon found in discharge-required response")
	}
	mac := respErr.Info.Macaroon
	macaroons, err := bakery.DischargeAllWithKey(ctx, mac, c.AcquireDischarge, c.Key)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}
	var cookiePath string
	if path := respErr.Info.MacaroonPath; path != "" {
		relURL, err := parseURLPath(path)
		if err != nil {
			c.logInfof(ctx, "ignoring invalid path in discharge-required response: %v", err)
		} else {
			cookiePath = reqURL.ResolveReference(relURL).Path
		}
	}
	cookie, err := NewCookie(nil, macaroons)
	if err != nil {
		return errgo.Notef(err, "cannot make cookie")
	}
	cookie.Path = cookiePath
	if name := respErr.Info.CookieNameSuffix; name != "" {
		cookie.Name = "macaroon-" + name
	}
	c.Jar.SetCookies(reqURL, []*http.Cookie{cookie})
	return nil
}
func DefaultGetError(httpResp *http.Response) error {
	if httpResp.StatusCode != http.StatusProxyAuthRequired && httpResp.StatusCode != http.StatusUnauthorized {
		return nil
	}
	if httpResp.StatusCode == http.StatusUnauthorized && httpResp.Header.Get("WWW-Authenticate") != "Macaroon" {
		return nil
	}
	if httpResp.Header.Get("Content-Type") != "application/json" {
		return nil
	}
	var resp Error
	if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
		return fmt.Errorf("cannot unmarshal error response: %v", err)
	}
	return &resp
}
func parseURLPath(path string) (*url.URL, error) {
	u, err := url.Parse(path)
	if err != nil {
		return nil, errgo.Mask(err)
	}
	if u.Scheme != "" ||
		u.Opaque != "" ||
		u.User != nil ||
		u.Host != "" ||
		u.RawQuery != "" ||
		u.Fragment != "" {
		return nil, errgo.Newf("URL path %q is not clean", path)
	}
	return u, nil
}
const PermanentExpiryDuration = 100 * 365 * 24 * time.Hour