<commit_before>package stripe

import (
	"encoding/json"

	"github.com/stripe/stripe-go/form"
)

// SourceCodeVerificationFlowStatus represents the possible statuses of a code verification flow.
type SourceCodeVerificationFlowStatus string

// List of values that SourceCodeVerificationFlowStatus can take.
const (
	SourceCodeVerificationFlowStatusFailed    SourceCodeVerificationFlowStatus = "failed"
	SourceCodeVerificationFlowStatusPending   SourceCodeVerificationFlowStatus = "pending"
	SourceCodeVerificationFlowStatusSucceeded SourceCodeVerificationFlowStatus = "succeeded"
)

// SourceFlow represents the possible flows of a source object.
type SourceFlow string

// List of values that SourceFlow can take.
const (
	SourceFlowCodeVerification SourceFlow = "code_verification"
	SourceFlowNone             SourceFlow = "none"
	SourceFlowReceiver         SourceFlow = "receiver"
	SourceFlowRedirect         SourceFlow = "redirect"
)

// SourceMandateAcceptanceStatus represents the possible failure reasons of a redirect flow.
type SourceMandateAcceptanceStatus string

// List of values that SourceMandateAcceptanceStatus can take.
const (
	SourceMandateAcceptanceStatusAccepted SourceMandateAcceptanceStatus = "accepted"
	SourceMandateAcceptanceStatusRefused  SourceMandateAcceptanceStatus = "refused"
)

// SourceMandateNotificationMethod represents the possible methods of notification for a mandate.
type SourceMandateNotificationMethod string

// List of values that SourceMandateNotificationMethod can take.
const (
	SourceMandateNotificationMethodEmail  SourceMandateNotificationMethod = "email"
	SourceMandateNotificationMethodManual SourceMandateNotificationMethod = "manual"
	SourceMandateNotificationMethodNone   SourceMandateNotificationMethod = "none"
)

// SourceSourceOrderItemType describes the type of source order items on source
// orders for sources.
type SourceSourceOrderItemType string

// The list of possible values for source order item types.
const (
	SourceSourceOrderItemTypeDiscount SourceSourceOrderItemType = "discount"
	SourceSourceOrderItemTypeSKU      SourceSourceOrderItemType = "sku"
	SourceSourceOrderItemTypeShipping SourceSourceOrderItemType = "shipping"
	SourceSourceOrderItemTypeTax      SourceSourceOrderItemType = "tax"
)

// SourceRedirectFlowFailureReason represents the possible failure reasons of a redirect flow.
type SourceRedirectFlowFailureReason string

// List of values that SourceRedirectFlowFailureReason can take.
const (
	SourceRedirectFlowFailureReasonDeclined        SourceRedirectFlowFailureReason = "declined"
	SourceRedirectFlowFailureReasonProcessingError SourceRedirectFlowFailureReason = "processing_error"
	SourceRedirectFlowFailureReasonUserAbort       SourceRedirectFlowFailureReason = "user_abort"
)

// SourceRedirectFlowStatus represents the possible statuses of a redirect flow.
type SourceRedirectFlowStatus string

// List of values that SourceRedirectFlowStatus can take.
const (
	SourceRedirectFlowStatusFailed      SourceRedirectFlowStatus = "failed"
	SourceRedirectFlowStatusNotRequired SourceRedirectFlowStatus = "not_required"
	SourceRedirectFlowStatusPending     SourceRedirectFlowStatus = "pending"
	SourceRedirectFlowStatusSucceeded   SourceRedirectFlowStatus = "succeeded"
)

// SourceRefundAttributesMethod are the possible method to retrieve a receiver's refund attributes.
type SourceRefundAttributesMethod string

// List of values that SourceRefundAttributesMethod can take.
const (
	SourceRefundAttributesMethodEmail  SourceRefundAttributesMethod = "email"
	SourceRefundAttributesMethodManual SourceRefundAttributesMethod = "manual"
)

// SourceRefundAttributesStatus are the possible status of a receiver's refund attributes.
type SourceRefundAttributesStatus string

// List of values that SourceRefundAttributesStatus can take.
const (
	SourceRefundAttributesStatusAvailable SourceRefundAttributesStatus = "available"
	SourceRefundAttributesStatusMissing   SourceRefundAttributesStatus = "missing"
	SourceRefundAttributesStatusRequested SourceRefundAttributesStatus = "requested"
)

// SourceStatus represents the possible statuses of a source object.
type SourceStatus string

// List of values that SourceStatus can take.
const (
	SourceStatusCanceled   SourceStatus = "canceled"
	SourceStatusChargeable SourceStatus = "chargeable"
	SourceStatusConsumed   SourceStatus = "consumed"
	SourceStatusFailed     SourceStatus = "failed"
	SourceStatusPending    SourceStatus = "pending"
)

// SourceUsage represents the possible usages of a source object.
type SourceUsage string

// List of values that SourceUsage can take.
const (
	SourceUsageReusable  SourceUsage = "reusable"
	SourceUsageSingleUse SourceUsage = "single_use"
)

// SourceOwnerParams is the set of parameters allowed for the owner hash on
// source creation or update.
type SourceOwnerParams struct {
	Address *AddressParams `form:"address"`
	Email   *string        `form:"email"`
	Name    *string        `form:"name"`
	Phone   *string        `form:"phone"`
}

// RedirectParams is the set of parameters allowed for the redirect hash on
// source creation or update.
type RedirectParams struct {
	ReturnURL *string `form:"return_url"`
}

// SourceOrderItemsParams is the set of parameters allowed for the items on a
// source order for a source.
type SourceOrderItemsParams struct {
	Amount      *int64  `form:"amount"`
	Currency    *string `form:"currency"`
	Description *string `form:"description"`
	Parent      *string `form:"parent"`
	Quantity    *int64  `form:"quantity"`
	Type        *string `form:"type"`
}

// SourceOrderParams is the set of parameters allowed for the source order of a
// source.
type SourceOrderParams struct {
	Items    []*SourceOrderItemsParams `form:"items"`
	Shipping *ShippingDetailsParams    `form:"shipping"`
}

// SourceObjectParams is the set of parameters allowed on source creation or update.
type SourceObjectParams struct {
	Params              `form:"*"`
	Amount              *int64                `form:"amount"`
	Currency            *string               `form:"currency"`
	Customer            *string               `form:"customer"`
	Flow                *string               `form:"flow"`
	Mandate             *SourceMandateParams  `form:"mandate"`
	OriginalSource      *string               `form:"original_source"`
	Owner               *SourceOwnerParams    `form:"owner"`
	Receiver            *SourceReceiverParams `form:"receiver"`
	Redirect            *RedirectParams       `form:"redirect"`
	SourceOrder         *SourceOrderParams    `form:"source_order"`
	StatementDescriptor *string               `form:"statement_descriptor"`
	Token               *string               `form:"token"`
	Type                *string               `form:"type"`
	TypeData            map[string]string     `form:"-"`
	Usage               *string               `form:"usage"`
}

// SourceMandateAcceptanceParams describes the set of parameters allowed for the `acceptance`
// hash on source creation or update.
type SourceMandateAcceptanceParams struct {
	Date      *int64                                `form:"date"`
	IP        *string                               `form:"ip"`
	Offline   *SourceMandateAcceptanceOfflineParams `form:"offline"`
	Online    *SourceMandateAcceptanceOnlineParams  `form:"online"`
	Status    *string                               `form:"status"`
	Type      *string                               `form:"type"`
	UserAgent *string                               `form:"user_agent"`
}

type SourceMandateAcceptanceOnlineParams struct {
	Date      *int64  `form:"date"`
	IP        *string `form:"ip"`
	UserAgent *string `form:"user_agent"`
}

type SourceMandateAcceptanceOfflineParams struct {
	ContactEmail *string `form:"contact_email"`
}

// SourceMandateParams describes the set of parameters allowed for the `mandate` hash on
// source creation or update.
type SourceMandateParams struct {
	Amount             *int64                         `form:"amount"`
	Acceptance         *SourceMandateAcceptanceParams `form:"acceptance"`
	Currency           *string                        `form:"currency"`
	Interval           *string                        `form:"interval"`
	NotificationMethod *string                        `form:"notification_method"`
}

// SourceReceiverParams is the set of parameters allowed for the `receiver` hash on
// source creation or update.
type SourceReceiverParams struct {
	RefundAttributesMethod *string `form:"refund_attributes_method"`
}

// SourceObjectDetachParams is the set of parameters that can be used when detaching
// a source from a customer.
type SourceObjectDetachParams struct {
	Params   `form:"*"`
	Customer *string `form:"-"`
}

// SourceOwner describes the owner hash on a source.
type SourceOwner struct {
	Address         *Address `json:"address,omitempty"`
	Email           string   `json:"email"`
	Name            string   `json:"name"`
	Phone           string   `json:"phone"`
	VerifiedAddress *Address `json:"verified_address,omitempty"`
	VerifiedEmail   string   `json:"verified_email"`
	VerifiedName    string   `json:"verified_name"`
	VerifiedPhone   string   `json:"verified_phone"`
}

// RedirectFlow informs of the state of a redirect authentication flow.
type RedirectFlow struct {
	FailureReason SourceRedirectFlowFailureReason `json:"failure_reason"`
	ReturnURL     string                          `json:"return_url"`
	Status        SourceRedirectFlowStatus        `json:"status"`
	URL           string                          `json:"url"`
}

// ReceiverFlow informs of the state of a receiver authentication flow.
type ReceiverFlow struct {
	Address                string                       `json:"address"`
	AmountCharged          int64                        `json:"amount_charged"`
	AmountReceived         int64                        `json:"amount_received"`
	AmountReturned         int64                        `json:"amount_returned"`
	RefundAttributesMethod SourceRefundAttributesMethod `json:"refund_attributes_method"`
	RefundAttributesStatus SourceRefundAttributesStatus `json:"refund_attributes_status"`
}

// CodeVerificationFlow informs of the state of a verification authentication flow.
type CodeVerificationFlow struct {
	AttemptsRemaining int64                            `json:"attempts_remaining"`
	Status            SourceCodeVerificationFlowStatus `json:"status"`
}

// SourceMandateAcceptance describes a source mandate acceptance state.
type SourceMandateAcceptance struct {
	Date      int64                         `json:"date"`
	IP        string                        `json:"ip"`
	Status    SourceMandateAcceptanceStatus `json:"status"`
	UserAgent string                        `json:"user_agent"`
}

// SourceMandate describes a source mandate.
type SourceMandate struct {
	Acceptance         *SourceMandateAcceptance        `json:"acceptance"`
	NotificationMethod SourceMandateNotificationMethod `json:"notification_method"`
	Reference          string                          `json:"reference"`
	URL                string                          `json:"url"`
}

// SourceSourceOrderItems describes the items on source orders for sources.
type SourceSourceOrderItems struct {
	Amount      int64                     `json:"amount"`
	Currency    Currency                  `json:"currency"`
	Description string                    `json:"description"`
	Quantity    int64                     `json:"quantity"`
	Type        SourceSourceOrderItemType `json:"type"`
}

// SourceSourceOrder describes a source order for a source.
type SourceSourceOrder struct {
	Amount   int64                   `json:"amount"`
	Currency Currency                `json:"currency"`
	Email    string                  `json:"email"`
	Items    *SourceSourceOrderItems `json:"items"`
	Shipping *ShippingDetails        `json:"shipping"`
}

// Source is the resource representing a Source.
// For more details see https://stripe.com/docs/api#sources.
type Source struct {
	Amount              int64                 `json:"amount"`
	ClientSecret        string                `json:"client_secret"`
	CodeVerification    *CodeVerificationFlow `json:"code_verification,omitempty"`
	Created             int64                 `json:"created"`
	Currency            Currency              `json:"currency"`
	Customer            string                `json:"customer"`
	Flow                SourceFlow            `json:"flow"`
	ID                  string                `json:"id"`
	Livemode            bool                  `json:"livemode"`
	Mandate             *SourceMandate        `json:"mandate"`
	Metadata            map[string]string     `json:"metadata"`
	Owner               *SourceOwner          `json:"owner"`
	Receiver            *ReceiverFlow         `json:"receiver,omitempty"`
	Redirect            *RedirectFlow         `json:"redirect,omitempty"`
	StatementDescriptor string                `json:"statement_descriptor"`
	SourceOrder         *SourceSourceOrder    `json:"source_order"`
	Status              SourceStatus          `json:"status"`
	Type                string                `json:"type"`
	TypeData            map[string]interface{}
	Usage               SourceUsage `json:"usage"`
}

// AppendTo implements custom encoding logic for SourceObjectParams so that the special
// "TypeData" value for is sent as the correct parameter based on the Source type
func (p *SourceObjectParams) AppendTo(body *form.Values, keyParts []string) {
	if len(p.TypeData) > 0 && p.Type == nil {
		panic("You can not fill TypeData if you don't explicitly set Type")
	}

	for k, vs := range p.TypeData {
		body.Add(form.FormatKey(append(keyParts, StringValue(p.Type), k)), vs)
	}
}

// UnmarshalJSON handles deserialization of an Source. This custom unmarshaling
// is needed to extract the type specific data (accessible under `TypeData`)
// but stored in JSON under a hash named after the `type` of the source.
func (s *Source) UnmarshalJSON(data []byte) error {
	type source Source
	var v source
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}
	*s = Source(v)

	var raw map[string]interface{}
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	if d, ok := raw[s.Type]; ok {
		if m, ok := d.(map[string]interface{}); ok {
			s.TypeData = m
		}
	}

	return nil
}
<commit_msg>Fix linting error<commit_after>package stripe

import (
	"encoding/json"

	"github.com/stripe/stripe-go/form"
)

// SourceCodeVerificationFlowStatus represents the possible statuses of a code verification flow.
type SourceCodeVerificationFlowStatus string

// List of values that SourceCodeVerificationFlowStatus can take.
const (
	SourceCodeVerificationFlowStatusFailed    SourceCodeVerificationFlowStatus = "failed"
	SourceCodeVerificationFlowStatusPending   SourceCodeVerificationFlowStatus = "pending"
	SourceCodeVerificationFlowStatusSucceeded SourceCodeVerificationFlowStatus = "succeeded"
)

// SourceFlow represents the possible flows of a source object.
type SourceFlow string

// List of values that SourceFlow can take.
const (
	SourceFlowCodeVerification SourceFlow = "code_verification"
	SourceFlowNone             SourceFlow = "none"
	SourceFlowReceiver         SourceFlow = "receiver"
	SourceFlowRedirect         SourceFlow = "redirect"
)

// SourceMandateAcceptanceStatus represents the possible failure reasons of a redirect flow.
type SourceMandateAcceptanceStatus string

// List of values that SourceMandateAcceptanceStatus can take.
const (
	SourceMandateAcceptanceStatusAccepted SourceMandateAcceptanceStatus = "accepted"
	SourceMandateAcceptanceStatusRefused  SourceMandateAcceptanceStatus = "refused"
)

// SourceMandateNotificationMethod represents the possible methods of notification for a mandate.
type SourceMandateNotificationMethod string

// List of values that SourceMandateNotificationMethod can take.
const (
	SourceMandateNotificationMethodEmail  SourceMandateNotificationMethod = "email"
	SourceMandateNotificationMethodManual SourceMandateNotificationMethod = "manual"
	SourceMandateNotificationMethodNone   SourceMandateNotificationMethod = "none"
)

// SourceSourceOrderItemType describes the type of source order items on source
// orders for sources.
type SourceSourceOrderItemType string

// The list of possible values for source order item types.
const (
	SourceSourceOrderItemTypeDiscount SourceSourceOrderItemType = "discount"
	SourceSourceOrderItemTypeSKU      SourceSourceOrderItemType = "sku"
	SourceSourceOrderItemTypeShipping SourceSourceOrderItemType = "shipping"
	SourceSourceOrderItemTypeTax      SourceSourceOrderItemType = "tax"
)

// SourceRedirectFlowFailureReason represents the possible failure reasons of a redirect flow.
type SourceRedirectFlowFailureReason string

// List of values that SourceRedirectFlowFailureReason can take.
const (
	SourceRedirectFlowFailureReasonDeclined        SourceRedirectFlowFailureReason = "declined"
	SourceRedirectFlowFailureReasonProcessingError SourceRedirectFlowFailureReason = "processing_error"
	SourceRedirectFlowFailureReasonUserAbort       SourceRedirectFlowFailureReason = "user_abort"
)

// SourceRedirectFlowStatus represents the possible statuses of a redirect flow.
type SourceRedirectFlowStatus string

// List of values that SourceRedirectFlowStatus can take.
const (
	SourceRedirectFlowStatusFailed      SourceRedirectFlowStatus = "failed"
	SourceRedirectFlowStatusNotRequired SourceRedirectFlowStatus = "not_required"
	SourceRedirectFlowStatusPending     SourceRedirectFlowStatus = "pending"
	SourceRedirectFlowStatusSucceeded   SourceRedirectFlowStatus = "succeeded"
)

// SourceRefundAttributesMethod are the possible method to retrieve a receiver's refund attributes.
type SourceRefundAttributesMethod string

// List of values that SourceRefundAttributesMethod can take.
const (
	SourceRefundAttributesMethodEmail  SourceRefundAttributesMethod = "email"
	SourceRefundAttributesMethodManual SourceRefundAttributesMethod = "manual"
)

// SourceRefundAttributesStatus are the possible status of a receiver's refund attributes.
type SourceRefundAttributesStatus string

// List of values that SourceRefundAttributesStatus can take.
const (
	SourceRefundAttributesStatusAvailable SourceRefundAttributesStatus = "available"
	SourceRefundAttributesStatusMissing   SourceRefundAttributesStatus = "missing"
	SourceRefundAttributesStatusRequested SourceRefundAttributesStatus = "requested"
)

// SourceStatus represents the possible statuses of a source object.
type SourceStatus string

// List of values that SourceStatus can take.
const (
	SourceStatusCanceled   SourceStatus = "canceled"
	SourceStatusChargeable SourceStatus = "chargeable"
	SourceStatusConsumed   SourceStatus = "consumed"
	SourceStatusFailed     SourceStatus = "failed"
	SourceStatusPending    SourceStatus = "pending"
)

// SourceUsage represents the possible usages of a source object.
type SourceUsage string

// List of values that SourceUsage can take.
const (
	SourceUsageReusable  SourceUsage = "reusable"
	SourceUsageSingleUse SourceUsage = "single_use"
)

// SourceOwnerParams is the set of parameters allowed for the owner hash on
// source creation or update.
type SourceOwnerParams struct {
	Address *AddressParams `form:"address"`
	Email   *string        `form:"email"`
	Name    *string        `form:"name"`
	Phone   *string        `form:"phone"`
}

// RedirectParams is the set of parameters allowed for the redirect hash on
// source creation or update.
type RedirectParams struct {
	ReturnURL *string `form:"return_url"`
}

// SourceOrderItemsParams is the set of parameters allowed for the items on a
// source order for a source.
type SourceOrderItemsParams struct {
	Amount      *int64  `form:"amount"`
	Currency    *string `form:"currency"`
	Description *string `form:"description"`
	Parent      *string `form:"parent"`
	Quantity    *int64  `form:"quantity"`
	Type        *string `form:"type"`
}

// SourceOrderParams is the set of parameters allowed for the source order of a
// source.
type SourceOrderParams struct {
	Items    []*SourceOrderItemsParams `form:"items"`
	Shipping *ShippingDetailsParams    `form:"shipping"`
}

// SourceObjectParams is the set of parameters allowed on source creation or update.
type SourceObjectParams struct {
	Params              `form:"*"`
	Amount              *int64                `form:"amount"`
	Currency            *string               `form:"currency"`
	Customer            *string               `form:"customer"`
	Flow                *string               `form:"flow"`
	Mandate             *SourceMandateParams  `form:"mandate"`
	OriginalSource      *string               `form:"original_source"`
	Owner               *SourceOwnerParams    `form:"owner"`
	Receiver            *SourceReceiverParams `form:"receiver"`
	Redirect            *RedirectParams       `form:"redirect"`
	SourceOrder         *SourceOrderParams    `form:"source_order"`
	StatementDescriptor *string               `form:"statement_descriptor"`
	Token               *string               `form:"token"`
	Type                *string               `form:"type"`
	TypeData            map[string]string     `form:"-"`
	Usage               *string               `form:"usage"`
}

// SourceMandateAcceptanceParams describes the set of parameters allowed for the `acceptance`
// hash on source creation or update.
type SourceMandateAcceptanceParams struct {
	Date      *int64                                `form:"date"`
	IP        *string                               `form:"ip"`
	Offline   *SourceMandateAcceptanceOfflineParams `form:"offline"`
	Online    *SourceMandateAcceptanceOnlineParams  `form:"online"`
	Status    *string                               `form:"status"`
	Type      *string                               `form:"type"`
	UserAgent *string                               `form:"user_agent"`
}

// SourceMandateAcceptanceOnlineParams describes the set of parameters for online accepted mandate
type SourceMandateAcceptanceOnlineParams struct {
	Date      *int64  `form:"date"`
	IP        *string `form:"ip"`
	UserAgent *string `form:"user_agent"`
}

// SourceMandateAcceptanceOfflineParams describes the set of parameters for offline accepted mandate
type SourceMandateAcceptanceOfflineParams struct {
	ContactEmail *string `form:"contact_email"`
}

// SourceMandateParams describes the set of parameters allowed for the `mandate` hash on
// source creation or update.
type SourceMandateParams struct {
	Amount             *int64                         `form:"amount"`
	Acceptance         *SourceMandateAcceptanceParams `form:"acceptance"`
	Currency           *string                        `form:"currency"`
	Interval           *string                        `form:"interval"`
	NotificationMethod *string                        `form:"notification_method"`
}

// SourceReceiverParams is the set of parameters allowed for the `receiver` hash on
// source creation or update.
type SourceReceiverParams struct {
	RefundAttributesMethod *string `form:"refund_attributes_method"`
}

// SourceObjectDetachParams is the set of parameters that can be used when detaching
// a source from a customer.
type SourceObjectDetachParams struct {
	Params   `form:"*"`
	Customer *string `form:"-"`
}

// SourceOwner describes the owner hash on a source.
type SourceOwner struct {
	Address         *Address `json:"address,omitempty"`
	Email           string   `json:"email"`
	Name            string   `json:"name"`
	Phone           string   `json:"phone"`
	VerifiedAddress *Address `json:"verified_address,omitempty"`
	VerifiedEmail   string   `json:"verified_email"`
	VerifiedName    string   `json:"verified_name"`
	VerifiedPhone   string   `json:"verified_phone"`
}

// RedirectFlow informs of the state of a redirect authentication flow.
type RedirectFlow struct {
	FailureReason SourceRedirectFlowFailureReason `json:"failure_reason"`
	ReturnURL     string                          `json:"return_url"`
	Status        SourceRedirectFlowStatus        `json:"status"`
	URL           string                          `json:"url"`
}

// ReceiverFlow informs of the state of a receiver authentication flow.
type ReceiverFlow struct {
	Address                string                       `json:"address"`
	AmountCharged          int64                        `json:"amount_charged"`
	AmountReceived         int64                        `json:"amount_received"`
	AmountReturned         int64                        `json:"amount_returned"`
	RefundAttributesMethod SourceRefundAttributesMethod `json:"refund_attributes_method"`
	RefundAttributesStatus SourceRefundAttributesStatus `json:"refund_attributes_status"`
}

// CodeVerificationFlow informs of the state of a verification authentication flow.
type CodeVerificationFlow struct {
	AttemptsRemaining int64                            `json:"attempts_remaining"`
	Status            SourceCodeVerificationFlowStatus `json:"status"`
}

// SourceMandateAcceptance describes a source mandate acceptance state.
type SourceMandateAcceptance struct {
	Date      int64                         `json:"date"`
	IP        string                        `json:"ip"`
	Status    SourceMandateAcceptanceStatus `json:"status"`
	UserAgent string                        `json:"user_agent"`
}

// SourceMandate describes a source mandate.
type SourceMandate struct {
	Acceptance         *SourceMandateAcceptance        `json:"acceptance"`
	NotificationMethod SourceMandateNotificationMethod `json:"notification_method"`
	Reference          string                          `json:"reference"`
	URL                string                          `json:"url"`
}

// SourceSourceOrderItems describes the items on source orders for sources.
type SourceSourceOrderItems struct {
	Amount      int64                     `json:"amount"`
	Currency    Currency                  `json:"currency"`
	Description string                    `json:"description"`
	Quantity    int64                     `json:"quantity"`
	Type        SourceSourceOrderItemType `json:"type"`
}

// SourceSourceOrder describes a source order for a source.
type SourceSourceOrder struct {
	Amount   int64                   `json:"amount"`
	Currency Currency                `json:"currency"`
	Email    string                  `json:"email"`
	Items    *SourceSourceOrderItems `json:"items"`
	Shipping *ShippingDetails        `json:"shipping"`
}

// Source is the resource representing a Source.
// For more details see https://stripe.com/docs/api#sources.
type Source struct {
	Amount              int64                 `json:"amount"`
	ClientSecret        string                `json:"client_secret"`
	CodeVerification    *CodeVerificationFlow `json:"code_verification,omitempty"`
	Created             int64                 `json:"created"`
	Currency            Currency              `json:"currency"`
	Customer            string                `json:"customer"`
	Flow                SourceFlow            `json:"flow"`
	ID                  string                `json:"id"`
	Livemode            bool                  `json:"livemode"`
	Mandate             *SourceMandate        `json:"mandate"`
	Metadata            map[string]string     `json:"metadata"`
	Owner               *SourceOwner          `json:"owner"`
	Receiver            *ReceiverFlow         `json:"receiver,omitempty"`
	Redirect            *RedirectFlow         `json:"redirect,omitempty"`
	StatementDescriptor string                `json:"statement_descriptor"`
	SourceOrder         *SourceSourceOrder    `json:"source_order"`
	Status              SourceStatus          `json:"status"`
	Type                string                `json:"type"`
	TypeData            map[string]interface{}
	Usage               SourceUsage `json:"usage"`
}

// AppendTo implements custom encoding logic for SourceObjectParams so that the special
// "TypeData" value for is sent as the correct parameter based on the Source type
func (p *SourceObjectParams) AppendTo(body *form.Values, keyParts []string) {
	if len(p.TypeData) > 0 && p.Type == nil {
		panic("You can not fill TypeData if you don't explicitly set Type")
	}

	for k, vs := range p.TypeData {
		body.Add(form.FormatKey(append(keyParts, StringValue(p.Type), k)), vs)
	}
}

// UnmarshalJSON handles deserialization of an Source. This custom unmarshaling
// is needed to extract the type specific data (accessible under `TypeData`)
// but stored in JSON under a hash named after the `type` of the source.
func (s *Source) UnmarshalJSON(data []byte) error {
	type source Source
	var v source
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}
	*s = Source(v)

	var raw map[string]interface{}
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}

	if d, ok := raw[s.Type]; ok {
		if m, ok := d.(map[string]interface{}); ok {
			s.TypeData = m
		}
	}

	return nil
}
<|endoftext|>


<commit_before>/*
 * sshbox is a utility to encrypt a file using SSH keys.
 */
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/asn1"
	"encoding/pem"
	"flag"
	"fmt"
	"github.com/gokyle/cryptobox/secretbox"
	"github.com/gokyle/sshkey"
	"io/ioutil"
	"os"
	"regexp"
)

type boxPackage struct {
	LockedKey []byte
	Box       []byte
}

type sshPublicKey struct {
	Algorithm []byte
	Modulus   []byte
	Exponent  []byte
}

var pubkeyRegexp = regexp.MustCompile("(?m)^ssh-... (\\S+).*$")
var remoteCheck = regexp.MustCompile("^https?://")

func main() {
	flArmour := flag.Bool("a", false, "ASCII armour the box")
	flDecrypt := flag.Bool("d", false, "decrypt file")
	flEncrypt := flag.Bool("e", false, "encrypt file")
	flKeyFile := flag.String("k", "", "SSH key file")
	flag.Parse()

	if *flDecrypt && *flEncrypt {
		fmt.Println("[!] only one of -d or -e can be specified!")
		os.Exit(1)
	}

	if flag.NArg() != 2 {
		fmt.Println("[!] source and target must both be specified.")
		fmt.Printf("\t%s [options] source target\n", os.Args[0])
		os.Exit(1)
	}
	source := flag.Args()[0]
	target := flag.Args()[1]

	if *flKeyFile == "" {
		fmt.Println("[!] no key was specified!\n")
		os.Exit(1)
	}

	remote := remoteCheck.MatchString(*flKeyFile)
	if remote {
		if *flDecrypt {
			fmt.Println("[+] remotely fetching private keys is not allowed.")
			os.Exit(1)
		}
		fmt.Println("[+] will fetch key")
	}

	if *flEncrypt {
		err := encrypt(source, target, *flKeyFile, !remote, *flArmour)
		if err != nil {
			fmt.Println("[!] failed.")
			os.Exit(1)
		}
		fmt.Println("[+] success")
		os.Exit(0)
	} else {
		err := decrypt(source, target, *flKeyFile, *flArmour)
		if err != nil {
			fmt.Println("[!] failed.")
			os.Exit(1)
		}
		fmt.Println("[+] success.")
		os.Exit(0)
	}
}

// Generate a random box key, encrypt the key to the RSA public key,
// package the box appropriately, and write it out to a file.
func encrypt(in, out, keyfile string, local, armour bool) (err error) {
	pub, err := sshkey.LoadPublicKeyFile(keyfile, local)
	if err != nil {
		return
	}
	boxKey, err := secretbox.GenerateKey()
	if err != nil {
		fmt.Println("[!] failed to generate the box key.")
		return
	}

	hash := sha256.New()
	lockedKey, err := rsa.EncryptOAEP(hash, rand.Reader, pub, boxKey, nil)
	if err != nil {
		fmt.Println("[!] RSA encryption failed:", err.Error())
		return
	}

	message, err := ioutil.ReadFile(in)
	if err != nil {
		fmt.Println("[!]", err.Error())
		return
	}

	box, ok := secretbox.Seal(message, boxKey)
	if !ok {
		fmt.Println("[!] failed to seal the message.")
		err = fmt.Errorf("sealing failure")
		return
	}
	pkg, err := packageBox(lockedKey, box, armour)
	if err != nil {
		return
	}

	err = ioutil.WriteFile(out, pkg, 0644)
	if err != nil {
		fmt.Println("[!]", err.Error())
	}
	return
}

// packageBox actually handles boxing. It can output either PEM-encoded or
// DER-encoded boxes.
func packageBox(lockedKey, box []byte, armour bool) (pkg []byte, err error) {
	var pkgBox = boxPackage{lockedKey, box}

	pkg, err = asn1.Marshal(pkgBox)
	if err != nil {
		fmt.Println("[!] couldn't package the box")
		return
	}

	if armour {
		var block pem.Block
		block.Type = "SSHBOX ENCRYPTED FILE"
		block.Bytes = pkg
		pkg = pem.EncodeToMemory(&block)
	}
	return
}

// Decrypt loads the box, recovers the key using the RSA private key, open
// the box, and write the message to a file.
func decrypt(in, out, keyfile string, armour bool) (err error) {
	key, err := sshkey.LoadPrivateKeyFile(keyfile)
	if err != nil {
		return
	}

	pkg, err := ioutil.ReadFile(in)
	if err != nil {
		fmt.Println("[!]", err.Error())
		return
	}

	lockedKey, box, err := unpackageBox(pkg)
	if err != nil {
		return
	}

	hash := sha256.New()
	boxKey, err := rsa.DecryptOAEP(hash, rand.Reader, key, lockedKey, nil)
	if err != nil {
		fmt.Println("[!] RSA decryption failed:", err.Error())
		return
	}

	message, ok := secretbox.Open(box, boxKey)
	if !ok {
		fmt.Println("[!] failed to open box.")
		err = fmt.Errorf("opening box failed")
		return
	}
	err = ioutil.WriteFile(out, message, 0644)
	return
}

// unpackageBox handles the loading of a box; it first attempts to decode the
// box as a DER-encoded box. If this fails, it attempts to decode the box as
// a PEM-encoded box.
func unpackageBox(pkg []byte) (lockedKey, box []byte, err error) {
	var pkgStruct boxPackage

	_, err = asn1.Unmarshal(pkg, &pkgStruct)
	if err == nil {
		return pkgStruct.LockedKey, pkgStruct.Box, nil
	}

	block, _ := pem.Decode(pkg)
	if block == nil || block.Type != "SSHBOX ENCRYPTED FILE" {
		fmt.Println("[!] invalid box.")
		err = fmt.Errorf("invalid box")
		return
	}
	_, err = asn1.Unmarshal(block.Bytes, &pkgStruct)
	return pkgStruct.LockedKey, pkgStruct.Box, err
}
<commit_msg>Add error messages for key loading.<commit_after>/*
 * sshbox is a utility to encrypt a file using SSH keys.
 */
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/asn1"
	"encoding/pem"
	"flag"
	"fmt"
	"github.com/gokyle/cryptobox/secretbox"
	"github.com/gokyle/sshkey"
	"io/ioutil"
	"os"
	"regexp"
)

type boxPackage struct {
	LockedKey []byte
	Box       []byte
}

type sshPublicKey struct {
	Algorithm []byte
	Modulus   []byte
	Exponent  []byte
}

var pubkeyRegexp = regexp.MustCompile("(?m)^ssh-... (\\S+).*$")
var remoteCheck = regexp.MustCompile("^https?://")

func main() {
	flArmour := flag.Bool("a", false, "ASCII armour the box")
	flDecrypt := flag.Bool("d", false, "decrypt file")
	flEncrypt := flag.Bool("e", false, "encrypt file")
	flKeyFile := flag.String("k", "", "SSH key file")
	flag.Parse()

	if *flDecrypt && *flEncrypt {
		fmt.Println("[!] only one of -d or -e can be specified!")
		os.Exit(1)
	}

	if flag.NArg() != 2 {
		fmt.Println("[!] source and target must both be specified.")
		fmt.Printf("\t%s [options] source target\n", os.Args[0])
		os.Exit(1)
	}
	source := flag.Args()[0]
	target := flag.Args()[1]

	if *flKeyFile == "" {
		fmt.Println("[!] no key was specified!\n")
		os.Exit(1)
	}

	remote := remoteCheck.MatchString(*flKeyFile)
	if remote {
		if *flDecrypt {
			fmt.Println("[+] remotely fetching private keys is not allowed.")
			os.Exit(1)
		}
		fmt.Println("[+] will fetch key")
	}

	if *flEncrypt {
		err := encrypt(source, target, *flKeyFile, !remote, *flArmour)
		if err != nil {
			fmt.Println("[!] failed.")
			os.Exit(1)
		}
		fmt.Println("[+] success")
		os.Exit(0)
	} else {
		err := decrypt(source, target, *flKeyFile, *flArmour)
		if err != nil {
			fmt.Println("[!] failed.")
			os.Exit(1)
		}
		fmt.Println("[+] success.")
		os.Exit(0)
	}
}

// Generate a random box key, encrypt the key to the RSA public key,
// package the box appropriately, and write it out to a file.
func encrypt(in, out, keyfile string, local, armour bool) (err error) {
	pub, err := sshkey.LoadPublicKeyFile(keyfile, local)
	if err != nil {
		fmt.Printf("[!] failed to load the public key:\n\t%s\n",
		    err.Error())
		return
	}
	boxKey, err := secretbox.GenerateKey()
	if err != nil {
		fmt.Println("[!] failed to generate the box key.")
		return
	}

	hash := sha256.New()
	lockedKey, err := rsa.EncryptOAEP(hash, rand.Reader, pub, boxKey, nil)
	if err != nil {
		fmt.Println("[!] RSA encryption failed:", err.Error())
		return
	}

	message, err := ioutil.ReadFile(in)
	if err != nil {
		fmt.Println("[!]", err.Error())
		return
	}

	box, ok := secretbox.Seal(message, boxKey)
	if !ok {
		fmt.Println("[!] failed to seal the message.")
		err = fmt.Errorf("sealing failure")
		return
	}
	pkg, err := packageBox(lockedKey, box, armour)
	if err != nil {
		return
	}

	err = ioutil.WriteFile(out, pkg, 0644)
	if err != nil {
		fmt.Println("[!]", err.Error())
	}
	return
}

// packageBox actually handles boxing. It can output either PEM-encoded or
// DER-encoded boxes.
func packageBox(lockedKey, box []byte, armour bool) (pkg []byte, err error) {
	var pkgBox = boxPackage{lockedKey, box}

	pkg, err = asn1.Marshal(pkgBox)
	if err != nil {
		fmt.Println("[!] couldn't package the box")
		return
	}

	if armour {
		var block pem.Block
		block.Type = "SSHBOX ENCRYPTED FILE"
		block.Bytes = pkg
		pkg = pem.EncodeToMemory(&block)
	}
	return
}

// Decrypt loads the box, recovers the key using the RSA private key, open
// the box, and write the message to a file.
func decrypt(in, out, keyfile string, armour bool) (err error) {
	key, err := sshkey.LoadPrivateKeyFile(keyfile)
	if err != nil {
		fmt.Printf("[!] failed to load the private key:\n\t%s\n",
		    err.Error())
		return
	}

	pkg, err := ioutil.ReadFile(in)
	if err != nil {
		fmt.Println("[!]", err.Error())
		return
	}

	lockedKey, box, err := unpackageBox(pkg)
	if err != nil {
		return
	}

	hash := sha256.New()
	boxKey, err := rsa.DecryptOAEP(hash, rand.Reader, key, lockedKey, nil)
	if err != nil {
		fmt.Println("[!] RSA decryption failed:", err.Error())
		return
	}

	message, ok := secretbox.Open(box, boxKey)
	if !ok {
		fmt.Println("[!] failed to open box.")
		err = fmt.Errorf("opening box failed")
		return
	}
	err = ioutil.WriteFile(out, message, 0644)
	return
}

// unpackageBox handles the loading of a box; it first attempts to decode the
// box as a DER-encoded box. If this fails, it attempts to decode the box as
// a PEM-encoded box.
func unpackageBox(pkg []byte) (lockedKey, box []byte, err error) {
	var pkgStruct boxPackage

	_, err = asn1.Unmarshal(pkg, &pkgStruct)
	if err == nil {
		return pkgStruct.LockedKey, pkgStruct.Box, nil
	}

	block, _ := pem.Decode(pkg)
	if block == nil || block.Type != "SSHBOX ENCRYPTED FILE" {
		fmt.Println("[!] invalid box.")
		err = fmt.Errorf("invalid box")
		return
	}
	_, err = asn1.Unmarshal(block.Bytes, &pkgStruct)
	return pkgStruct.LockedKey, pkgStruct.Box, err
}
<|endoftext|>


<commit_before>package main

// This source file is for the special case of serving a single file.

import (
	"errors"
	log "github.com/sirupsen/logrus"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	defaultStaticCacheSize = 128 * MiB

	maxAttemptsAtIncreasingPortNumber = 128

	waitBeforeOpen = time.Second * 1
)

// nextPort increases the port number by 1
func nextPort(colonPort string) (string, error) {
	if !strings.HasPrefix(colonPort, ":") {
		return colonPort, errors.New("colonPort does not start with a colon! \"" + colonPort + "\"")
	}
	num, err := strconv.Atoi(colonPort[1:])
	if err != nil {
		return colonPort, errors.New("Could not convert port number to string: \"" + colonPort[1:] + "\"")
	}
	// Increase the port number by 1, add a colon, convert to string and return
	return ":" + strconv.Itoa(num+1), nil
}

// This is a bit hacky, but it's only used when serving a single static file
func openAfter(wait time.Duration, hostname, colonPort string, https bool, cancelChannel chan bool) {
	// Wait a bit
	time.Sleep(wait)
	select {
	case _ = <-cancelChannel:
		// Got a message on the cancelChannel:
		// don't open the URL with an external application.
		return
	case <-time.After(waitBeforeOpen):
		// Got timeout, assume the port was not busy
		openURL(hostname, colonPort, https)
	}
}

// shortInfo outputs a short string about which file is served where
func shortInfoAndOpen(filename, colonPort string, cancelChannel chan bool) {
	hostname := "localhost"
	if serverHost != "" {
		hostname = serverHost
	}
	log.Info("Serving " + filename + " on http://" + hostname + colonPort)

	if openURLAfterServing {
		go openAfter(waitBeforeOpen, hostname, colonPort, false, cancelChannel)
	}
}

// Convenience function for serving only a single file
// (quick and easy way to view a README.md file)
func serveStaticFile(filename, colonPort string) {
	log.Info("Single file mode. Not using the regular parameters.")

	cancelChannel := make(chan bool, 1)

	shortInfoAndOpen(filename, colonPort, cancelChannel)

	mux := http.NewServeMux()
	// 64 MiB cache, use cache compression, no per-file size limit, use best gzip compression
	preferSpeed = false
	cache := newFileCache(defaultStaticCacheSize, true, 0)
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Server", versionString)
		filePage(w, req, filename, nil, nil, cache)
	})
	HTTPserver := newGracefulServer(mux, false, serverHost+colonPort, 5*time.Second)

	// Attempt to serve just the single file
	if err := HTTPserver.ListenAndServe(); err != nil {
		// If it fails, try several times, increasing the port by 1 each time
		for i := 0; i < maxAttemptsAtIncreasingPortNumber; i++ {
			if err := HTTPserver.ListenAndServe(); err != nil {
				cancelChannel <- true
				if !strings.HasSuffix(err.Error(), "already in use") {
					// Not a problem with address already being in use
					fatalExit(err)
				}
				log.Warn("Address already in use. Using next port number.")
				if newPort, err2 := nextPort(colonPort); err2 != nil {
					fatalExit(err)
				} else {
					colonPort = newPort
				}

				// Make a new cancel channel, and use the new URL
				cancelChannel = make(chan bool, 1)
				shortInfoAndOpen(filename, colonPort, cancelChannel)

				HTTPserver = newGracefulServer(mux, false, serverHost+colonPort, 5*time.Second)
			}
		}
		// Several attempts failed
		fatalExit(err)
	}
}
<commit_msg>Decrease delay before opening Markdown in a browser<commit_after>package main

// This source file is for the special case of serving a single file.

import (
	"errors"
	log "github.com/sirupsen/logrus"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	defaultStaticCacheSize = 128 * MiB

	maxAttemptsAtIncreasingPortNumber = 128

	waitBeforeOpen = time.Millisecond * 200
)

// nextPort increases the port number by 1
func nextPort(colonPort string) (string, error) {
	if !strings.HasPrefix(colonPort, ":") {
		return colonPort, errors.New("colonPort does not start with a colon! \"" + colonPort + "\"")
	}
	num, err := strconv.Atoi(colonPort[1:])
	if err != nil {
		return colonPort, errors.New("Could not convert port number to string: \"" + colonPort[1:] + "\"")
	}
	// Increase the port number by 1, add a colon, convert to string and return
	return ":" + strconv.Itoa(num+1), nil
}

// This is a bit hacky, but it's only used when serving a single static file
func openAfter(wait time.Duration, hostname, colonPort string, https bool, cancelChannel chan bool) {
	// Wait a bit
	time.Sleep(wait)
	select {
	case _ = <-cancelChannel:
		// Got a message on the cancelChannel:
		// don't open the URL with an external application.
		return
	case <-time.After(waitBeforeOpen):
		// Got timeout, assume the port was not busy
		openURL(hostname, colonPort, https)
	}
}

// shortInfo outputs a short string about which file is served where
func shortInfoAndOpen(filename, colonPort string, cancelChannel chan bool) {
	hostname := "localhost"
	if serverHost != "" {
		hostname = serverHost
	}
	log.Info("Serving " + filename + " on http://" + hostname + colonPort)

	if openURLAfterServing {
		go openAfter(waitBeforeOpen, hostname, colonPort, false, cancelChannel)
	}
}

// Convenience function for serving only a single file
// (quick and easy way to view a README.md file)
func serveStaticFile(filename, colonPort string) {
	log.Info("Single file mode. Not using the regular parameters.")

	cancelChannel := make(chan bool, 1)

	shortInfoAndOpen(filename, colonPort, cancelChannel)

	mux := http.NewServeMux()
	// 64 MiB cache, use cache compression, no per-file size limit, use best gzip compression
	preferSpeed = false
	cache := newFileCache(defaultStaticCacheSize, true, 0)
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Server", versionString)
		filePage(w, req, filename, nil, nil, cache)
	})
	HTTPserver := newGracefulServer(mux, false, serverHost+colonPort, 5*time.Second)

	// Attempt to serve just the single file
	if err := HTTPserver.ListenAndServe(); err != nil {
		// If it fails, try several times, increasing the port by 1 each time
		for i := 0; i < maxAttemptsAtIncreasingPortNumber; i++ {
			if err := HTTPserver.ListenAndServe(); err != nil {
				cancelChannel <- true
				if !strings.HasSuffix(err.Error(), "already in use") {
					// Not a problem with address already being in use
					fatalExit(err)
				}
				log.Warn("Address already in use. Using next port number.")
				if newPort, err2 := nextPort(colonPort); err2 != nil {
					fatalExit(err)
				} else {
					colonPort = newPort
				}

				// Make a new cancel channel, and use the new URL
				cancelChannel = make(chan bool, 1)
				shortInfoAndOpen(filename, colonPort, cancelChannel)

				HTTPserver = newGracefulServer(mux, false, serverHost+colonPort, 5*time.Second)
			}
		}
		// Several attempts failed
		fatalExit(err)
	}
}
<|endoftext|>


<commit_before>/**
 * Copyright 2013-2016 Seagate Technology LLC.
 *
 * This Source Code Form is subject to the terms of the Mozilla
 * Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at
 * https://mozilla.org/MP:/2.0/.
 *
 * This program is distributed in the hope that it will be useful,
 * but is provided AS-IS, WITHOUT ANY WARRANTY; including without
 * the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or
 * FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public
 * License for more details.
 *
 * See www.openkinetic.org for more project information
 */

package kinetic

import (
	"strconv"

	kproto "github.com/Kinetic/kinetic-go/proto"
)

// StatusCode for kinetic message.
// Including status code get from device, or client internal error code.
type StatusCode int32

// StatusCode code value
const (
	RemoteNotAttempted                 StatusCode = iota
	OK                                 StatusCode = iota
	ClientIOError                      StatusCode = iota
	ClientShutdown                     StatusCode = iota
	ClientInternalError                StatusCode = iota
	ClientResponseHMACError            StatusCode = iota
	RemoteHMACError                    StatusCode = iota
	RemoteNotAuthorized                StatusCode = iota
	RemoteClusterVersionMismatch       StatusCode = iota
	RemoteInvalidRequest               StatusCode = iota
	RemoteInternalError                StatusCode = iota
	RemoteHeaderRequired               StatusCode = iota
	RemoteNotFound                     StatusCode = iota
	RemoteVersionMismatch              StatusCode = iota
	RemoteServiceBusy                  StatusCode = iota
	RemoteExpired                      StatusCode = iota
	RemoteDataError                    StatusCode = iota
	RemotePermDataError                StatusCode = iota
	RemoteConnectionError              StatusCode = iota
	RemoteNoSpace                      StatusCode = iota
	RemoteNoSuchHMACAlgorithm          StatusCode = iota
	RemoteOtherError                   StatusCode = iota
	ProtocolErrorResponseNoAckSequence StatusCode = iota
	RemoteNestedOperationErrors        StatusCode = iota
	RemoteDeviceLocked                 StatusCode = iota
	RemoteDeviceAlreadyUnlocked        StatusCode = iota
	RemoteConnectionTerminated         StatusCode = iota
	RemoteInvalidBatch                 StatusCode = iota
	RemoteInvalidExecute               StatusCode = iota
	RemoteExecuteComplete              StatusCode = iota
	RemoteHibernate                    StatusCode = iota
	RemoteShutdown                     StatusCode = iota
)

var statusName = map[StatusCode]string{
	RemoteNotAttempted:                 "REMOTE_NOT_ATTEMPTED",
	OK:                                 "OK",
	ClientIOError:                      "CLIENT_IO_ERROR",
	ClientShutdown:                     "CLIENT_SHUTDOWN",
	ClientInternalError:                "CLIENT_INTERNAL_ERROR",
	ClientResponseHMACError:            "CLIENT_RESPONSE_HMAC_VERIFICATION_ERROR",
	RemoteHMACError:                    "REMOTE_HMAC_ERROR",
	RemoteNotAuthorized:                "REMOTE_NOT_AUTHORIZED",
	RemoteClusterVersionMismatch:       "REMOTE_CLUSTER_VERSION_MISMATCH",
	RemoteInvalidRequest:               "REMOTE_INVALID_REQUEST",
	RemoteInternalError:                "REMOTE_INTERNAL_ERROR",
	RemoteHeaderRequired:               "REMOTE_HEADER_REQUIRED",
	RemoteNotFound:                     "REMOTE_NOT_FOUND",
	RemoteVersionMismatch:              "REMOTE_VERSION_MISMATCH",
	RemoteServiceBusy:                  "REMOTE_SERVICE_BUSY",
	RemoteExpired:                      "REMOTE_EXPIRED",
	RemoteDataError:                    "REMOTE_DATA_ERROR",
	RemotePermDataError:                "REMOTE_PERM_DATA_ERROR",
	RemoteConnectionError:              "REMOTE_CONNECTION_ERROR",
	RemoteNoSpace:                      "REMOTE_NO_SPACE",
	RemoteNoSuchHMACAlgorithm:          "REMOTE_NO_SUCH_HMAC_ALGORITHM",
	RemoteOtherError:                   "REMOTE_OTHER_ERROR",
	ProtocolErrorResponseNoAckSequence: "PROTOCOL_ERROR_RESPONSE_NO_ACKSEQUENCE ",
	RemoteNestedOperationErrors:        "REMOTE_NESTED_OPERATION_ERRORS",
	RemoteDeviceLocked:                 "REMOTE_DEVICE_LOCKED",
	RemoteDeviceAlreadyUnlocked:        "REMOTE_DEVICE_ALREADY_UNLOCKED",
	RemoteConnectionTerminated:         "REMOTE_CONNECTION_TERMINATED",
	RemoteInvalidBatch:                 "REMOTE_INVALID_BATCH",
	RemoteInvalidExecute:               "REMOTE_INVALID_EXECUTE",
	RemoteExecuteComplete:              "REMOTE_EXECUTE_COMPLETE",
	RemoteHibernate:                    "REMOTE_HIBERNATE",
	RemoteShutdown:                     "REMOTE_SHUTDOWN",
}

// Status for each kinetic message.
// Code is the status code and ErrorMsg is the detail message
type Status struct {
	Code                   StatusCode
	ErrorMsg               string
	ExpectedClusterVersion int64
}

// Error returns the detail status message if Status.Code != OK
func (s Status) Error() string {
	return s.ErrorMsg
}

func (s Status) String() string {
	ret := "Unknown Status"
	str, ok := statusName[s.Code]
	if ok {
		ret = str + " : " + s.ErrorMsg
		if s.Code == RemoteClusterVersionMismatch {
			ret = ret + ", Expected cluster version =" + strconv.Itoa(int(s.ExpectedClusterVersion))
		}
	}
	return ret
}

func convertStatusCodeToProto(s StatusCode) kproto.Command_Status_StatusCode {
	ret := kproto.Command_Status_INVALID_STATUS_CODE
	switch s {
	case RemoteNotAttempted:
		ret = kproto.Command_Status_NOT_ATTEMPTED
	case OK:
		ret = kproto.Command_Status_SUCCESS
	case RemoteHMACError:
		ret = kproto.Command_Status_HMAC_FAILURE
	case RemoteNotAuthorized:
		ret = kproto.Command_Status_NOT_AUTHORIZED
	case RemoteClusterVersionMismatch:
		ret = kproto.Command_Status_VERSION_FAILURE
	case RemoteInternalError:
		ret = kproto.Command_Status_INTERNAL_ERROR
	case RemoteHeaderRequired:
		ret = kproto.Command_Status_HEADER_REQUIRED
	case RemoteNotFound:
		ret = kproto.Command_Status_NOT_FOUND
	case RemoteVersionMismatch:
		ret = kproto.Command_Status_VERSION_MISMATCH
	case RemoteServiceBusy:
		ret = kproto.Command_Status_SERVICE_BUSY
	case RemoteExpired:
		ret = kproto.Command_Status_EXPIRED
	case RemoteDataError:
		ret = kproto.Command_Status_DATA_ERROR
	case RemotePermDataError:
		ret = kproto.Command_Status_PERM_DATA_ERROR
	case RemoteConnectionError:
		ret = kproto.Command_Status_REMOTE_CONNECTION_ERROR
	case RemoteNoSpace:
		ret = kproto.Command_Status_NO_SPACE
	case RemoteNoSuchHMACAlgorithm:
		ret = kproto.Command_Status_NO_SUCH_HMAC_ALGORITHM
	case RemoteInvalidRequest:
		ret = kproto.Command_Status_INVALID_REQUEST
	case RemoteNestedOperationErrors:
		ret = kproto.Command_Status_NESTED_OPERATION_ERRORS
	case RemoteDeviceLocked:
		ret = kproto.Command_Status_DEVICE_LOCKED
	case RemoteDeviceAlreadyUnlocked:
		ret = kproto.Command_Status_DEVICE_ALREADY_UNLOCKED
	case RemoteConnectionTerminated:
		ret = kproto.Command_Status_CONNECTION_TERMINATED
	case RemoteInvalidBatch:
		ret = kproto.Command_Status_INVALID_BATCH
	case RemoteHibernate:
		ret = kproto.Command_Status_HIBERNATE
	case RemoteShutdown:
		ret = kproto.Command_Status_SHUTDOWN
	}
	return ret
}

func convertStatusCodeFromProto(s kproto.Command_Status_StatusCode) StatusCode {
	ret := RemoteOtherError
	switch s {
	case kproto.Command_Status_NOT_ATTEMPTED:
		ret = RemoteNotAttempted
	case kproto.Command_Status_SUCCESS:
		ret = OK
	case kproto.Command_Status_HMAC_FAILURE:
		ret = RemoteHMACError
	case kproto.Command_Status_NOT_AUTHORIZED:
		ret = RemoteNotAuthorized
	case kproto.Command_Status_VERSION_FAILURE:
		ret = RemoteClusterVersionMismatch
	case kproto.Command_Status_INTERNAL_ERROR:
		ret = RemoteInternalError
	case kproto.Command_Status_HEADER_REQUIRED:
		ret = RemoteHeaderRequired
	case kproto.Command_Status_NOT_FOUND:
		ret = RemoteNotFound
	case kproto.Command_Status_VERSION_MISMATCH:
		ret = RemoteVersionMismatch
	case kproto.Command_Status_SERVICE_BUSY:
		ret = RemoteServiceBusy
	case kproto.Command_Status_EXPIRED:
		ret = RemoteExpired
	case kproto.Command_Status_DATA_ERROR:
		ret = RemoteDataError
	case kproto.Command_Status_PERM_DATA_ERROR:
		ret = RemotePermDataError
	case kproto.Command_Status_REMOTE_CONNECTION_ERROR:
		ret = RemoteConnectionError
	case kproto.Command_Status_NO_SPACE:
		ret = RemoteNoSpace
	case kproto.Command_Status_NO_SUCH_HMAC_ALGORITHM:
		ret = RemoteNoSuchHMACAlgorithm
	case kproto.Command_Status_INVALID_REQUEST:
		ret = RemoteInvalidRequest
	case kproto.Command_Status_NESTED_OPERATION_ERRORS:
		ret = RemoteNestedOperationErrors
	case kproto.Command_Status_DEVICE_LOCKED:
		ret = RemoteDeviceLocked
	case kproto.Command_Status_DEVICE_ALREADY_UNLOCKED:
		ret = RemoteDeviceAlreadyUnlocked
	case kproto.Command_Status_CONNECTION_TERMINATED:
		ret = RemoteConnectionTerminated
	case kproto.Command_Status_INVALID_BATCH:
		ret = RemoteInvalidBatch
	case kproto.Command_Status_HIBERNATE:
		ret = RemoteHibernate
	case kproto.Command_Status_SHUTDOWN:
		ret = RemoteShutdown
	}
	return ret
}

func getStatusFromProto(cmd *kproto.Command) Status {
	code := convertStatusCodeFromProto(cmd.GetStatus().GetCode())
	msg := cmd.GetStatus().GetStatusMessage()
	version := cmd.GetHeader().GetClusterVersion()

	return Status{code, msg, version}
}
<commit_msg>add function to return StatusCode as string<commit_after>/**
 * Copyright 2013-2016 Seagate Technology LLC.
 *
 * This Source Code Form is subject to the terms of the Mozilla
 * Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at
 * https://mozilla.org/MP:/2.0/.
 *
 * This program is distributed in the hope that it will be useful,
 * but is provided AS-IS, WITHOUT ANY WARRANTY; including without
 * the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or
 * FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public
 * License for more details.
 *
 * See www.openkinetic.org for more project information
 */

package kinetic

import (
	"strconv"

	kproto "github.com/Kinetic/kinetic-go/proto"
)

// StatusCode for kinetic message.
// Including status code get from device, or client internal error code.
type StatusCode int32

// StatusCode code value
const (
	RemoteNotAttempted                 StatusCode = iota
	OK                                 StatusCode = iota
	ClientIOError                      StatusCode = iota
	ClientShutdown                     StatusCode = iota
	ClientInternalError                StatusCode = iota
	ClientResponseHMACError            StatusCode = iota
	RemoteHMACError                    StatusCode = iota
	RemoteNotAuthorized                StatusCode = iota
	RemoteClusterVersionMismatch       StatusCode = iota
	RemoteInvalidRequest               StatusCode = iota
	RemoteInternalError                StatusCode = iota
	RemoteHeaderRequired               StatusCode = iota
	RemoteNotFound                     StatusCode = iota
	RemoteVersionMismatch              StatusCode = iota
	RemoteServiceBusy                  StatusCode = iota
	RemoteExpired                      StatusCode = iota
	RemoteDataError                    StatusCode = iota
	RemotePermDataError                StatusCode = iota
	RemoteConnectionError              StatusCode = iota
	RemoteNoSpace                      StatusCode = iota
	RemoteNoSuchHMACAlgorithm          StatusCode = iota
	RemoteOtherError                   StatusCode = iota
	ProtocolErrorResponseNoAckSequence StatusCode = iota
	RemoteNestedOperationErrors        StatusCode = iota
	RemoteDeviceLocked                 StatusCode = iota
	RemoteDeviceAlreadyUnlocked        StatusCode = iota
	RemoteConnectionTerminated         StatusCode = iota
	RemoteInvalidBatch                 StatusCode = iota
	RemoteInvalidExecute               StatusCode = iota
	RemoteExecuteComplete              StatusCode = iota
	RemoteHibernate                    StatusCode = iota
	RemoteShutdown                     StatusCode = iota
)

var statusName = map[StatusCode]string{
	RemoteNotAttempted:                 "REMOTE_NOT_ATTEMPTED",
	OK:                                 "OK",
	ClientIOError:                      "CLIENT_IO_ERROR",
	ClientShutdown:                     "CLIENT_SHUTDOWN",
	ClientInternalError:                "CLIENT_INTERNAL_ERROR",
	ClientResponseHMACError:            "CLIENT_RESPONSE_HMAC_VERIFICATION_ERROR",
	RemoteHMACError:                    "REMOTE_HMAC_ERROR",
	RemoteNotAuthorized:                "REMOTE_NOT_AUTHORIZED",
	RemoteClusterVersionMismatch:       "REMOTE_CLUSTER_VERSION_MISMATCH",
	RemoteInvalidRequest:               "REMOTE_INVALID_REQUEST",
	RemoteInternalError:                "REMOTE_INTERNAL_ERROR",
	RemoteHeaderRequired:               "REMOTE_HEADER_REQUIRED",
	RemoteNotFound:                     "REMOTE_NOT_FOUND",
	RemoteVersionMismatch:              "REMOTE_VERSION_MISMATCH",
	RemoteServiceBusy:                  "REMOTE_SERVICE_BUSY",
	RemoteExpired:                      "REMOTE_EXPIRED",
	RemoteDataError:                    "REMOTE_DATA_ERROR",
	RemotePermDataError:                "REMOTE_PERM_DATA_ERROR",
	RemoteConnectionError:              "REMOTE_CONNECTION_ERROR",
	RemoteNoSpace:                      "REMOTE_NO_SPACE",
	RemoteNoSuchHMACAlgorithm:          "REMOTE_NO_SUCH_HMAC_ALGORITHM",
	RemoteOtherError:                   "REMOTE_OTHER_ERROR",
	ProtocolErrorResponseNoAckSequence: "PROTOCOL_ERROR_RESPONSE_NO_ACKSEQUENCE ",
	RemoteNestedOperationErrors:        "REMOTE_NESTED_OPERATION_ERRORS",
	RemoteDeviceLocked:                 "REMOTE_DEVICE_LOCKED",
	RemoteDeviceAlreadyUnlocked:        "REMOTE_DEVICE_ALREADY_UNLOCKED",
	RemoteConnectionTerminated:         "REMOTE_CONNECTION_TERMINATED",
	RemoteInvalidBatch:                 "REMOTE_INVALID_BATCH",
	RemoteInvalidExecute:               "REMOTE_INVALID_EXECUTE",
	RemoteExecuteComplete:              "REMOTE_EXECUTE_COMPLETE",
	RemoteHibernate:                    "REMOTE_HIBERNATE",
	RemoteShutdown:                     "REMOTE_SHUTDOWN",
}

// String returns string value of StatusCode.
func (c StatusCode) String() string {
	str, ok := statusName[c]
	if ok {
		return str
	}

	return "Unknown Status"
}

// Status for each kinetic message.
// Code is the status code and ErrorMsg is the detail message
type Status struct {
	Code                   StatusCode
	ErrorMsg               string
	ExpectedClusterVersion int64
}

// Error returns the detail status message if Status.Code != OK
func (s Status) Error() string {
	return s.ErrorMsg
}

func (s Status) String() string {
	ret := "Unknown Status"
	str, ok := statusName[s.Code]
	if ok {
		ret = str + " : " + s.ErrorMsg
		if s.Code == RemoteClusterVersionMismatch {
			ret = ret + ", Expected cluster version =" + strconv.Itoa(int(s.ExpectedClusterVersion))
		}
	}
	return ret
}

func convertStatusCodeToProto(s StatusCode) kproto.Command_Status_StatusCode {
	ret := kproto.Command_Status_INVALID_STATUS_CODE
	switch s {
	case RemoteNotAttempted:
		ret = kproto.Command_Status_NOT_ATTEMPTED
	case OK:
		ret = kproto.Command_Status_SUCCESS
	case RemoteHMACError:
		ret = kproto.Command_Status_HMAC_FAILURE
	case RemoteNotAuthorized:
		ret = kproto.Command_Status_NOT_AUTHORIZED
	case RemoteClusterVersionMismatch:
		ret = kproto.Command_Status_VERSION_FAILURE
	case RemoteInternalError:
		ret = kproto.Command_Status_INTERNAL_ERROR
	case RemoteHeaderRequired:
		ret = kproto.Command_Status_HEADER_REQUIRED
	case RemoteNotFound:
		ret = kproto.Command_Status_NOT_FOUND
	case RemoteVersionMismatch:
		ret = kproto.Command_Status_VERSION_MISMATCH
	case RemoteServiceBusy:
		ret = kproto.Command_Status_SERVICE_BUSY
	case RemoteExpired:
		ret = kproto.Command_Status_EXPIRED
	case RemoteDataError:
		ret = kproto.Command_Status_DATA_ERROR
	case RemotePermDataError:
		ret = kproto.Command_Status_PERM_DATA_ERROR
	case RemoteConnectionError:
		ret = kproto.Command_Status_REMOTE_CONNECTION_ERROR
	case RemoteNoSpace:
		ret = kproto.Command_Status_NO_SPACE
	case RemoteNoSuchHMACAlgorithm:
		ret = kproto.Command_Status_NO_SUCH_HMAC_ALGORITHM
	case RemoteInvalidRequest:
		ret = kproto.Command_Status_INVALID_REQUEST
	case RemoteNestedOperationErrors:
		ret = kproto.Command_Status_NESTED_OPERATION_ERRORS
	case RemoteDeviceLocked:
		ret = kproto.Command_Status_DEVICE_LOCKED
	case RemoteDeviceAlreadyUnlocked:
		ret = kproto.Command_Status_DEVICE_ALREADY_UNLOCKED
	case RemoteConnectionTerminated:
		ret = kproto.Command_Status_CONNECTION_TERMINATED
	case RemoteInvalidBatch:
		ret = kproto.Command_Status_INVALID_BATCH
	case RemoteHibernate:
		ret = kproto.Command_Status_HIBERNATE
	case RemoteShutdown:
		ret = kproto.Command_Status_SHUTDOWN
	}
	return ret
}

func convertStatusCodeFromProto(s kproto.Command_Status_StatusCode) StatusCode {
	ret := RemoteOtherError
	switch s {
	case kproto.Command_Status_NOT_ATTEMPTED:
		ret = RemoteNotAttempted
	case kproto.Command_Status_SUCCESS:
		ret = OK
	case kproto.Command_Status_HMAC_FAILURE:
		ret = RemoteHMACError
	case kproto.Command_Status_NOT_AUTHORIZED:
		ret = RemoteNotAuthorized
	case kproto.Command_Status_VERSION_FAILURE:
		ret = RemoteClusterVersionMismatch
	case kproto.Command_Status_INTERNAL_ERROR:
		ret = RemoteInternalError
	case kproto.Command_Status_HEADER_REQUIRED:
		ret = RemoteHeaderRequired
	case kproto.Command_Status_NOT_FOUND:
		ret = RemoteNotFound
	case kproto.Command_Status_VERSION_MISMATCH:
		ret = RemoteVersionMismatch
	case kproto.Command_Status_SERVICE_BUSY:
		ret = RemoteServiceBusy
	case kproto.Command_Status_EXPIRED:
		ret = RemoteExpired
	case kproto.Command_Status_DATA_ERROR:
		ret = RemoteDataError
	case kproto.Command_Status_PERM_DATA_ERROR:
		ret = RemotePermDataError
	case kproto.Command_Status_REMOTE_CONNECTION_ERROR:
		ret = RemoteConnectionError
	case kproto.Command_Status_NO_SPACE:
		ret = RemoteNoSpace
	case kproto.Command_Status_NO_SUCH_HMAC_ALGORITHM:
		ret = RemoteNoSuchHMACAlgorithm
	case kproto.Command_Status_INVALID_REQUEST:
		ret = RemoteInvalidRequest
	case kproto.Command_Status_NESTED_OPERATION_ERRORS:
		ret = RemoteNestedOperationErrors
	case kproto.Command_Status_DEVICE_LOCKED:
		ret = RemoteDeviceLocked
	case kproto.Command_Status_DEVICE_ALREADY_UNLOCKED:
		ret = RemoteDeviceAlreadyUnlocked
	case kproto.Command_Status_CONNECTION_TERMINATED:
		ret = RemoteConnectionTerminated
	case kproto.Command_Status_INVALID_BATCH:
		ret = RemoteInvalidBatch
	case kproto.Command_Status_HIBERNATE:
		ret = RemoteHibernate
	case kproto.Command_Status_SHUTDOWN:
		ret = RemoteShutdown
	}
	return ret
}

func getStatusFromProto(cmd *kproto.Command) Status {
	code := convertStatusCodeFromProto(cmd.GetStatus().GetCode())
	msg := cmd.GetStatus().GetStatusMessage()
	version := cmd.GetHeader().GetClusterVersion()

	return Status{code, msg, version}
}
<|endoftext|>


<commit_before>package smux

import (
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

// Stream implements net.Conn
type Stream struct {
	id   uint32
	sess *Session

	buffers [][]byte
	heads   [][]byte // slice heads kept for recycle

	bufferLock sync.Mutex
	frameSize  int

	// notify a read event
	chReadEvent chan struct{}

	// flag the stream has closed
	die     chan struct{}
	dieOnce sync.Once

	// FIN
	chFinEvent   chan struct{}
	finEventOnce sync.Once

	// deadlines
	readDeadline  atomic.Value
	writeDeadline atomic.Value

	// count writes
	numWrite uint64
}

// newStream initiates a Stream struct
func newStream(id uint32, frameSize int, sess *Session) *Stream {
	s := new(Stream)
	s.id = id
	s.chReadEvent = make(chan struct{}, 1)
	s.frameSize = frameSize
	s.sess = sess
	s.die = make(chan struct{})
	s.chFinEvent = make(chan struct{})
	return s
}

// ID returns the unique stream ID.
func (s *Stream) ID() uint32 {
	return s.id
}

// Read implements net.Conn
func (s *Stream) Read(b []byte) (n int, err error) {
	if len(b) == 0 {
		return 0, nil
	}

	for {
		s.bufferLock.Lock()
		if len(s.buffers) > 0 {
			n = copy(b, s.buffers[0])
			s.buffers[0] = s.buffers[0][n:]
			if len(s.buffers[0]) == 0 {
				s.buffers[0] = nil
				s.buffers = s.buffers[1:]
				// full recycle
				defaultAllocator.Put(s.heads[0])
				s.heads = s.heads[1:]
			}
		}
		s.bufferLock.Unlock()

		if n > 0 {
			s.sess.returnTokens(n)
			return n, nil
		}

		if ew := s.waitRead(); ew != nil {
			return 0, ew
		}
	}
}

// WriteTo implements io.WriteTo
func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
	for {
		var buf []byte
		s.bufferLock.Lock()
		if len(s.buffers) > 0 {
			buf = s.buffers[0]
			s.buffers = s.buffers[1:]
			s.heads = s.heads[1:]
		}
		s.bufferLock.Unlock()

		if buf != nil {
			nw, ew := w.Write(buf)
			defaultAllocator.Put(buf)
			s.sess.returnTokens(len(buf))
			if nw > 0 {
				n += int64(nw)
			}

			if ew != nil {
				return n, ew
			}
		} else if ew := s.waitRead(); ew != nil {
			return n, ew
		}
	}
}

func (s *Stream) waitRead() error {
	var timer *time.Timer
	var deadline <-chan time.Time
	if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer = time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}

	select {
	case <-s.chReadEvent:
		return nil
	case <-s.chFinEvent:
		return errors.WithStack(io.EOF)
	case <-s.sess.chSocketReadError:
		return s.sess.socketReadError.Load().(error)
	case <-s.sess.chProtoError:
		return s.sess.protoError.Load().(error)
	case <-deadline:
		return errors.WithStack(ErrTimeout)
	case <-s.die:
		return errors.WithStack(io.ErrClosedPipe)
	}

}

// Write implements net.Conn
func (s *Stream) Write(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}

	// check if stream has closed
	select {
	case <-s.die:
		return 0, errors.WithStack(io.ErrClosedPipe)
	default:
	}

	// frame split and transmit
	sent := 0
	frame := newFrame(cmdPSH, s.id)
	bts := b
	for len(bts) > 0 {
		sz := len(bts)
		if sz > s.frameSize {
			sz = s.frameSize
		}
		frame.data = bts[:sz]
		bts = bts[sz:]
		n, err := s.sess.writeFrameInternal(frame, deadline, s.numWrite)
		s.numWrite++
		sent += n
		if err != nil {
			return sent, errors.WithStack(err)
		}
	}

	return sent, nil
}

// Close implements net.Conn
func (s *Stream) Close() error {
	var once bool
	var err error
	s.dieOnce.Do(func() {
		close(s.die)
		once = true
	})

	if once {
		_, err = s.sess.writeFrame(newFrame(cmdFIN, s.id))
		s.sess.streamClosed(s.id)
		return err
	} else {
		return errors.WithStack(io.ErrClosedPipe)
	}
}

// GetDieCh returns a readonly chan which can be readable
// when the stream is to be closed.
func (s *Stream) GetDieCh() <-chan struct{} {
	return s.die
}

// SetReadDeadline sets the read deadline as defined by
// net.Conn.SetReadDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline.Store(t)
	return nil
}

// SetWriteDeadline sets the write deadline as defined by
// net.Conn.SetWriteDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline.Store(t)
	return nil
}

// SetDeadline sets both read and write deadlines as defined by
// net.Conn.SetDeadline.
// A zero time value disables the deadlines.
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// session closes
func (s *Stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }

// LocalAddr satisfies net.Conn interface
func (s *Stream) LocalAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		LocalAddr() net.Addr
	}); ok {
		return ts.LocalAddr()
	}
	return nil
}

// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		RemoteAddr() net.Addr
	}); ok {
		return ts.RemoteAddr()
	}
	return nil
}

// pushBytes append buf to buffers
func (s *Stream) pushBytes(buf []byte) (written int, err error) {
	s.bufferLock.Lock()
	s.buffers = append(s.buffers, buf)
	s.heads = append(s.heads, buf)
	s.bufferLock.Unlock()
	return
}

// recycleTokens transform remaining bytes to tokens(will truncate buffer)
func (s *Stream) recycleTokens() (n int) {
	s.bufferLock.Lock()
	for k := range s.buffers {
		n += len(s.buffers[k])
		defaultAllocator.Put(s.heads[k])
	}
	s.buffers = nil
	s.heads = nil
	s.bufferLock.Unlock()
	return
}

// notify read event
func (s *Stream) notifyReadEvent() {
	select {
	case s.chReadEvent <- struct{}{}:
	default:
	}
}

// mark this stream has been closed in protocol
func (s *Stream) fin() {
	s.finEventOnce.Do(func() {
		close(s.chFinEvent)
	})
}
<commit_msg>fix a possible race condition<commit_after>package smux

import (
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

// Stream implements net.Conn
type Stream struct {
	id   uint32
	sess *Session

	buffers [][]byte
	heads   [][]byte // slice heads kept for recycle

	bufferLock sync.Mutex
	frameSize  int

	// notify a read event
	chReadEvent chan struct{}

	// flag the stream has closed
	die     chan struct{}
	dieOnce sync.Once

	// FIN
	chFinEvent   chan struct{}
	finEventOnce sync.Once

	// deadlines
	readDeadline  atomic.Value
	writeDeadline atomic.Value

	// count writes
	numWrite uint64
}

// newStream initiates a Stream struct
func newStream(id uint32, frameSize int, sess *Session) *Stream {
	s := new(Stream)
	s.id = id
	s.chReadEvent = make(chan struct{}, 1)
	s.frameSize = frameSize
	s.sess = sess
	s.die = make(chan struct{})
	s.chFinEvent = make(chan struct{})
	return s
}

// ID returns the unique stream ID.
func (s *Stream) ID() uint32 {
	return s.id
}

// Read implements net.Conn
func (s *Stream) Read(b []byte) (n int, err error) {
	if len(b) == 0 {
		return 0, nil
	}

	for {
		s.bufferLock.Lock()
		if len(s.buffers) > 0 {
			n = copy(b, s.buffers[0])
			s.buffers[0] = s.buffers[0][n:]
			if len(s.buffers[0]) == 0 {
				s.buffers[0] = nil
				s.buffers = s.buffers[1:]
				// full recycle
				defaultAllocator.Put(s.heads[0])
				s.heads = s.heads[1:]
			}
		}
		s.bufferLock.Unlock()

		if n > 0 {
			s.sess.returnTokens(n)
			return n, nil
		}

		if ew := s.waitRead(); ew != nil {
			return 0, ew
		}
	}
}

// WriteTo implements io.WriteTo
func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
	for {
		var buf []byte
		s.bufferLock.Lock()
		if len(s.buffers) > 0 {
			buf = s.buffers[0]
			s.buffers = s.buffers[1:]
			s.heads = s.heads[1:]
		}
		s.bufferLock.Unlock()

		if buf != nil {
			nw, ew := w.Write(buf)
			s.sess.returnTokens(len(buf))
			defaultAllocator.Put(buf)
			if nw > 0 {
				n += int64(nw)
			}

			if ew != nil {
				return n, ew
			}
		} else if ew := s.waitRead(); ew != nil {
			return n, ew
		}
	}
}

func (s *Stream) waitRead() error {
	var timer *time.Timer
	var deadline <-chan time.Time
	if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer = time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}

	select {
	case <-s.chReadEvent:
		return nil
	case <-s.chFinEvent:
		return errors.WithStack(io.EOF)
	case <-s.sess.chSocketReadError:
		return s.sess.socketReadError.Load().(error)
	case <-s.sess.chProtoError:
		return s.sess.protoError.Load().(error)
	case <-deadline:
		return errors.WithStack(ErrTimeout)
	case <-s.die:
		return errors.WithStack(io.ErrClosedPipe)
	}

}

// Write implements net.Conn
func (s *Stream) Write(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}

	// check if stream has closed
	select {
	case <-s.die:
		return 0, errors.WithStack(io.ErrClosedPipe)
	default:
	}

	// frame split and transmit
	sent := 0
	frame := newFrame(cmdPSH, s.id)
	bts := b
	for len(bts) > 0 {
		sz := len(bts)
		if sz > s.frameSize {
			sz = s.frameSize
		}
		frame.data = bts[:sz]
		bts = bts[sz:]
		n, err := s.sess.writeFrameInternal(frame, deadline, s.numWrite)
		s.numWrite++
		sent += n
		if err != nil {
			return sent, errors.WithStack(err)
		}
	}

	return sent, nil
}

// Close implements net.Conn
func (s *Stream) Close() error {
	var once bool
	var err error
	s.dieOnce.Do(func() {
		close(s.die)
		once = true
	})

	if once {
		_, err = s.sess.writeFrame(newFrame(cmdFIN, s.id))
		s.sess.streamClosed(s.id)
		return err
	} else {
		return errors.WithStack(io.ErrClosedPipe)
	}
}

// GetDieCh returns a readonly chan which can be readable
// when the stream is to be closed.
func (s *Stream) GetDieCh() <-chan struct{} {
	return s.die
}

// SetReadDeadline sets the read deadline as defined by
// net.Conn.SetReadDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline.Store(t)
	return nil
}

// SetWriteDeadline sets the write deadline as defined by
// net.Conn.SetWriteDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline.Store(t)
	return nil
}

// SetDeadline sets both read and write deadlines as defined by
// net.Conn.SetDeadline.
// A zero time value disables the deadlines.
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// session closes
func (s *Stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }

// LocalAddr satisfies net.Conn interface
func (s *Stream) LocalAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		LocalAddr() net.Addr
	}); ok {
		return ts.LocalAddr()
	}
	return nil
}

// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		RemoteAddr() net.Addr
	}); ok {
		return ts.RemoteAddr()
	}
	return nil
}

// pushBytes append buf to buffers
func (s *Stream) pushBytes(buf []byte) (written int, err error) {
	s.bufferLock.Lock()
	s.buffers = append(s.buffers, buf)
	s.heads = append(s.heads, buf)
	s.bufferLock.Unlock()
	return
}

// recycleTokens transform remaining bytes to tokens(will truncate buffer)
func (s *Stream) recycleTokens() (n int) {
	s.bufferLock.Lock()
	for k := range s.buffers {
		n += len(s.buffers[k])
		defaultAllocator.Put(s.heads[k])
	}
	s.buffers = nil
	s.heads = nil
	s.bufferLock.Unlock()
	return
}

// notify read event
func (s *Stream) notifyReadEvent() {
	select {
	case s.chReadEvent <- struct{}{}:
	default:
	}
}

// mark this stream has been closed in protocol
func (s *Stream) fin() {
	s.finEventOnce.Do(func() {
		close(s.chFinEvent)
	})
}
<|endoftext|>


<commit_before>package quic

import (
	"github.com/lucas-clemente/quic-go/frames"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/utils"
)

// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface
type Stream struct {
	Session        *Session
	StreamID       protocol.StreamID
	StreamFrames   chan *frames.StreamFrame
	CurrentFrame   *frames.StreamFrame
	ReadPosInFrame int
	WriteOffset    uint64
}

// NewStream creates a new Stream
func NewStream(session *Session, StreamID protocol.StreamID) *Stream {
	return &Stream{
		Session:      session,
		StreamID:     StreamID,
		StreamFrames: make(chan *frames.StreamFrame, 8), // ToDo: add config option for this number
	}
}

// Read reads data
func (s *Stream) Read(p []byte) (int, error) {
	bytesRead := 0
	for bytesRead < len(p) {
		if s.CurrentFrame == nil {
			select {
			case s.CurrentFrame = <-s.StreamFrames:
			default:
				if bytesRead == 0 {
					s.CurrentFrame = <-s.StreamFrames
				} else {
					return bytesRead, nil
				}
			}
			s.ReadPosInFrame = 0
		}
		m := utils.Min(len(p)-bytesRead, len(s.CurrentFrame.Data)-s.ReadPosInFrame)
		copy(p[bytesRead:], s.CurrentFrame.Data[s.ReadPosInFrame:])
		s.ReadPosInFrame += m
		bytesRead += m
		if s.ReadPosInFrame >= len(s.CurrentFrame.Data) {
			s.CurrentFrame = nil
		}
	}

	return bytesRead, nil
}

// ReadByte implements io.ByteReader
func (s *Stream) ReadByte() (byte, error) {
	// TODO: Optimize
	p := make([]byte, 1)
	n, err := s.Read(p)
	if err != nil {
		return 0, err
	}
	if n != 1 {
		panic("Stream: should have returned error")
	}
	return p[0], nil
}

func (s *Stream) Write(p []byte) (int, error) {
	frame := &frames.StreamFrame{
		StreamID: s.StreamID,
		Offset:   s.WriteOffset,
		Data:     p,
	}
	err := s.Session.SendFrames([]frames.Frame{frame})
	if err != nil {
		return 0, err
	}
	s.WriteOffset += uint64(len(p))
	return len(p), nil
}

// AddStreamFrame adds a new stream frame
func (s *Stream) AddStreamFrame(frame *frames.StreamFrame) error {
	s.StreamFrames <- frame
	return nil
}
<commit_msg>add stream.Close<commit_after>package quic

import (
	"github.com/lucas-clemente/quic-go/frames"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/utils"
)

// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface
type Stream struct {
	Session        *Session
	StreamID       protocol.StreamID
	StreamFrames   chan *frames.StreamFrame
	CurrentFrame   *frames.StreamFrame
	ReadPosInFrame int
	WriteOffset    uint64
}

// NewStream creates a new Stream
func NewStream(session *Session, StreamID protocol.StreamID) *Stream {
	return &Stream{
		Session:      session,
		StreamID:     StreamID,
		StreamFrames: make(chan *frames.StreamFrame, 8), // ToDo: add config option for this number
	}
}

// Read reads data
func (s *Stream) Read(p []byte) (int, error) {
	bytesRead := 0
	for bytesRead < len(p) {
		if s.CurrentFrame == nil {
			select {
			case s.CurrentFrame = <-s.StreamFrames:
			default:
				if bytesRead == 0 {
					s.CurrentFrame = <-s.StreamFrames
				} else {
					return bytesRead, nil
				}
			}
			s.ReadPosInFrame = 0
		}
		m := utils.Min(len(p)-bytesRead, len(s.CurrentFrame.Data)-s.ReadPosInFrame)
		copy(p[bytesRead:], s.CurrentFrame.Data[s.ReadPosInFrame:])
		s.ReadPosInFrame += m
		bytesRead += m
		if s.ReadPosInFrame >= len(s.CurrentFrame.Data) {
			s.CurrentFrame = nil
		}
	}

	return bytesRead, nil
}

// ReadByte implements io.ByteReader
func (s *Stream) ReadByte() (byte, error) {
	// TODO: Optimize
	p := make([]byte, 1)
	n, err := s.Read(p)
	if err != nil {
		return 0, err
	}
	if n != 1 {
		panic("Stream: should have returned error")
	}
	return p[0], nil
}

func (s *Stream) Write(p []byte) (int, error) {
	frame := &frames.StreamFrame{
		StreamID: s.StreamID,
		Offset:   s.WriteOffset,
		Data:     p,
	}
	err := s.Session.SendFrames([]frames.Frame{frame})
	if err != nil {
		return 0, err
	}
	s.WriteOffset += uint64(len(p))
	return len(p), nil
}

func (s *Stream) Close() error {
	return s.Session.SendFrame(&frames.StreamFrame{
		StreamID: s.StreamID,
		Offset:   s.WriteOffset,
		FinBit:   true,
	})
}

// AddStreamFrame adds a new stream frame
func (s *Stream) AddStreamFrame(frame *frames.StreamFrame) error {
	s.StreamFrames <- frame
	return nil
}
<|endoftext|>


<commit_before>// Package null provides an opinionated yet reasonable way of handling null values.
package null

import (
	"database/sql"
	"encoding/json"
)

// String is a nullable string.
type String struct {
	sql.NullString
}

// StringFrom creates a new String that will be null if s is blank.
func StringFrom(s string) String {
	return NewString(s, s != "")
}

// NewString creates a new String
func NewString(s string, valid bool) String {
	return String{
		NullString: sql.NullString{
			String: s,
			Valid:  s != "",
		},
	}
}

// UnmarshalJSON implements json.Unmarshaler.
// It supports string and null input. Blank string input produces a null String.
// It also supports unmarshalling a sql.NullString.
func (s *String) UnmarshalJSON(data []byte) error {
	var err error
	var v interface{}
	json.Unmarshal(data, &v)
	switch v.(type) {
	case string:
		err = json.Unmarshal(data, &s.String)
	case map[string]interface{}:
		err = json.Unmarshal(data, &s.NullString)
	case nil:
		s.Valid = false
		return nil
	}
	s.Valid = (err == nil) && (s.String != "")
	return err
}

// MarshalText implements encoding.TextMarshaler.
// It will encode a blank string when this String is null.
func (s String) MarshalText() ([]byte, error) {
	if !s.Valid {
		return []byte{}, nil
	}
	return []byte(s.String), nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
// It will unmarshal to a null String if the input is a blank string.
func (s *String) UnmarshalText(text []byte) error {
	s.String = string(text)
	s.Valid = s.String != ""
	return nil
}

// Pointer returns a pointer to this String's value, or a nil pointer if this String is null.
func (s String) Pointer() *string {
	if s.String == "" {
		return nil
	}
	return &s.String
}

// IsZero returns true for null or empty strings, for future omitempty support. (Go 1.4?)
func (s String) IsZero() bool {
	return !s.Valid || s.String == ""
}
<commit_msg>fix NewString()<commit_after>// Package null provides an opinionated yet reasonable way of handling null values.
package null

import (
	"database/sql"
	"encoding/json"
)

// String is a nullable string.
type String struct {
	sql.NullString
}

// StringFrom creates a new String that will be null if s is blank.
func StringFrom(s string) String {
	return NewString(s, s != "")
}

// NewString creates a new String
func NewString(s string, valid bool) String {
	return String{
		NullString: sql.NullString{
			String: s,
			Valid:  valid,
		},
	}
}

// UnmarshalJSON implements json.Unmarshaler.
// It supports string and null input. Blank string input produces a null String.
// It also supports unmarshalling a sql.NullString.
func (s *String) UnmarshalJSON(data []byte) error {
	var err error
	var v interface{}
	json.Unmarshal(data, &v)
	switch v.(type) {
	case string:
		err = json.Unmarshal(data, &s.String)
	case map[string]interface{}:
		err = json.Unmarshal(data, &s.NullString)
	case nil:
		s.Valid = false
		return nil
	}
	s.Valid = (err == nil) && (s.String != "")
	return err
}

// MarshalText implements encoding.TextMarshaler.
// It will encode a blank string when this String is null.
func (s String) MarshalText() ([]byte, error) {
	if !s.Valid {
		return []byte{}, nil
	}
	return []byte(s.String), nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
// It will unmarshal to a null String if the input is a blank string.
func (s *String) UnmarshalText(text []byte) error {
	s.String = string(text)
	s.Valid = s.String != ""
	return nil
}

// Pointer returns a pointer to this String's value, or a nil pointer if this String is null.
func (s String) Pointer() *string {
	if s.String == "" {
		return nil
	}
	return &s.String
}

// IsZero returns true for null or empty strings, for future omitempty support. (Go 1.4?)
func (s String) IsZero() bool {
	return !s.Valid || s.String == ""
}
<|endoftext|>