<commit_before>package irmago

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
)

// This file contains the client side of the IRMA protocol, as well as the Handler interface
// which is used to communicate session info with the user.

// PermissionHandler is a callback for providing permission for an IRMA session
// and specifying the attributes to be disclosed.
type PermissionHandler func(proceed bool, choice *DisclosureChoice)

type PinHandler func(proceed bool, pin string)

// A Handler contains callbacks for communication to the user.
type Handler interface {
	StatusUpdate(action Action, status Status)
	Success(action Action)
	Cancelled(action Action)
	Failure(action Action, err *SessionError)
	UnsatisfiableRequest(action Action, missing AttributeDisjunctionList)
	MissingKeyshareEnrollment(manager SchemeManagerIdentifier)

	RequestIssuancePermission(request IssuanceRequest, ServerName string, callback PermissionHandler)
	RequestVerificationPermission(request DisclosureRequest, ServerName string, callback PermissionHandler)
	RequestSignaturePermission(request SignatureRequest, ServerName string, callback PermissionHandler)
	RequestSchemeManagerPermission(manager *SchemeManager, callback func(proceed bool))

	RequestPin(remainingAttempts int, callback PinHandler)
}

// A session is an IRMA session.
type session struct {
	Action    Action
	Version   Version
	ServerURL string
	Handler   Handler

	info        *SessionInfo
	credManager *CredentialManager
	jwt         RequestorJwt
	irmaSession IrmaSession
	transport   *HTTPTransport
	choice      *DisclosureChoice
	downloaded  *IrmaIdentifierSet
}

// We implement the handler for the keyshare protocol
var _ keyshareSessionHandler = (*session)(nil)

// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
	2: {2, 1},
}

func calcVersion(qr *Qr) (string, error) {
	// Parse range supported by server
	var minmajor, minminor, maxmajor, maxminor int
	var err error
	if minmajor, err = strconv.Atoi(string(qr.ProtocolVersion[0])); err != nil {
		return "", err
	}
	if minminor, err = strconv.Atoi(string(qr.ProtocolVersion[2])); err != nil {
		return "", err
	}
	if maxmajor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[0])); err != nil {
		return "", err
	}
	if maxminor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[2])); err != nil {
		return "", err
	}

	// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
	keys := make([]int, 0, len(supportedVersions))
	for k := range supportedVersions {
		keys = append(keys, k)
	}
	sort.Sort(sort.Reverse(sort.IntSlice(keys)))
	for _, major := range keys {
		for _, minor := range supportedVersions[major] {
			aboveMinimum := major > minmajor || (major == minmajor && minor >= minminor)
			underMaximum := major < maxmajor || (major == maxmajor && minor <= maxminor)
			if aboveMinimum && underMaximum {
				return fmt.Sprintf("%d.%d", major, minor), nil
			}
		}
	}
	return "", fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion, qr.ProtocolMaxVersion)
}

// NewSession creates and starts a new IRMA session.
func (cm *CredentialManager) NewSession(qr *Qr, handler Handler) {
	session := &session{
		Action:      Action(qr.Type),
		ServerURL:   qr.URL,
		Handler:     handler,
		transport:   NewHTTPTransport(qr.URL),
		credManager: cm,
	}
	version, err := calcVersion(qr)
	if err != nil {
		session.fail(&SessionError{ErrorType: ErrorProtocolVersionNotSupported, Err: err})
		return
	}
	session.Version = Version(version)

	// Check if the action is one of the supported types
	switch session.Action {
	case ActionDisclosing: // nop
	case ActionSigning: // nop
	case ActionIssuing: // nop
	//case ActionSchemeManager: // nop
	case ActionUnknown:
		fallthrough
	default:
		session.fail(&SessionError{ErrorType: ErrorUnknownAction, Info: string(session.Action)})
		return
	}

	if !strings.HasSuffix(session.ServerURL, "/") {
		session.ServerURL += "/"
	}

	go session.start()

	return
}

func handlePanic(callback func(*SessionError)) {
	if e := recover(); e != nil {
		var info string
		switch x := e.(type) {
		case string:
			info = x
		case error:
			info = x.Error()
		case fmt.Stringer:
			info = x.String()
		default: // nop
		}
		fmt.Printf("Recovered from panic: '%v'\n%s\n", e, info)
		if callback != nil {
			callback(&SessionError{ErrorType: ErrorPanic, Info: info})
		}
	}
}

func (session *session) fail(err *SessionError) {
	session.transport.Delete()
	err.Err = errors.Wrap(err.Err, 0)
	if session.downloaded != nil && !session.downloaded.Empty() {
		session.credManager.handler.UpdateConfigurationStore(session.downloaded)
	}
	session.Handler.Failure(session.Action, err)
}

func (session *session) cancel() {
	session.transport.Delete()
	if session.downloaded != nil && !session.downloaded.Empty() {
		session.credManager.handler.UpdateConfigurationStore(session.downloaded)
	}
	session.Handler.Cancelled(session.Action)
}

// start retrieves the first message in the IRMA protocol, checks if we can perform
// the request, and informs the user of the outcome.
func (session *session) start() {
	defer func() {
		handlePanic(func(err *SessionError) {
			if session.Handler != nil {
				session.Handler.Failure(session.Action, err)
			}
		})
	}()

	session.Handler.StatusUpdate(session.Action, StatusCommunicating)

	if session.Action == ActionSchemeManager {
		session.managerSession()
		return
	}

	// Get the first IRMA protocol message and parse it
	session.info = &SessionInfo{}
	Err := session.transport.Get("jwt", session.info)
	if Err != nil {
		session.fail(Err.(*SessionError))
		return
	}

	var err error
	session.jwt, err = parseRequestorJwt(session.Action, session.info.Jwt)
	if err != nil {
		session.fail(&SessionError{ErrorType: ErrorInvalidJWT, Err: err})
		return
	}
	session.irmaSession = session.jwt.IrmaSession()
	session.irmaSession.SetContext(session.info.Context)
	session.irmaSession.SetNonce(session.info.Nonce)
	if session.Action == ActionIssuing {
		ir := session.irmaSession.(*IssuanceRequest)
		// Store which public keys the server will use
		for _, credreq := range ir.Credentials {
			credreq.KeyCounter = session.info.Keys[credreq.CredentialTypeID.IssuerIdentifier()]
		}
	}

	// Check if we are enrolled into all involved keyshare servers
	for id := range session.irmaSession.Identifiers().SchemeManagers {
		manager, ok := session.credManager.ConfigurationStore.SchemeManagers[id]
		if !ok {
			session.fail(&SessionError{ErrorType: ErrorUnknownSchemeManager, Info: id.String()})
			return
		}
		distributed := manager.Distributed()
		_, enrolled := session.credManager.keyshareServers[id]
		if distributed && !enrolled {
			session.transport.Delete()
			session.Handler.MissingKeyshareEnrollment(id)
			return
		}
	}

	// Download missing credential types/issuers/public keys from the scheme manager
	if session.downloaded, err = session.credManager.ConfigurationStore.Download(session.irmaSession.Identifiers()); err != nil {
		session.Handler.Failure(
			session.Action,
			&SessionError{ErrorType: ErrorConfigurationStoreDownload, Err: err},
		)
		return
	}

	if session.Action == ActionIssuing {
		ir := session.irmaSession.(*IssuanceRequest)
		for _, credreq := range ir.Credentials {
			info, err := credreq.Info(session.credManager.ConfigurationStore)
			if err != nil {
				session.fail(&SessionError{ErrorType: ErrorUnknownCredentialType, Err: err})
				return
			}
			ir.CredentialInfoList = append(ir.CredentialInfoList, info)
		}
	}

	candidates, missing := session.credManager.CheckSatisfiability(session.irmaSession.ToDisclose())
	if len(missing) > 0 {
		session.Handler.UnsatisfiableRequest(session.Action, missing)
		// TODO: session.transport.Delete() on dialog cancel
		return
	}
	session.irmaSession.SetCandidates(candidates)

	// Ask for permission to execute the session
	callback := PermissionHandler(func(proceed bool, choice *DisclosureChoice) {
		session.choice = choice
		session.irmaSession.SetDisclosureChoice(choice)
		go session.do(proceed)
	})
	session.Handler.StatusUpdate(session.Action, StatusConnected)
	switch session.Action {
	case ActionDisclosing:
		session.Handler.RequestVerificationPermission(
			*session.irmaSession.(*DisclosureRequest), session.jwt.Requestor(), callback)
	case ActionSigning:
		session.Handler.RequestSignaturePermission(
			*session.irmaSession.(*SignatureRequest), session.jwt.Requestor(), callback)
	case ActionIssuing:
		session.Handler.RequestIssuancePermission(
			*session.irmaSession.(*IssuanceRequest), session.jwt.Requestor(), callback)
	default:
		panic("Invalid session type") // does not happen, session.Action has been checked earlier
	}
}

func (session *session) do(proceed bool) {
	defer func() {
		handlePanic(func(err *SessionError) {
			if session.Handler != nil {
				session.Handler.Failure(session.Action, err)
			}
		})
	}()

	if !proceed {
		session.cancel()
		return
	}
	session.Handler.StatusUpdate(session.Action, StatusCommunicating)

	if !session.irmaSession.Identifiers().Distributed(session.credManager.ConfigurationStore) {
		var message interface{}
		var err error
		switch session.Action {
		case ActionSigning:
			message, err = session.credManager.Proofs(session.choice, session.irmaSession, true)
		case ActionDisclosing:
			message, err = session.credManager.Proofs(session.choice, session.irmaSession, false)
		case ActionIssuing:
			message, err = session.credManager.IssueCommitments(session.irmaSession.(*IssuanceRequest))
		}
		if err != nil {
			session.fail(&SessionError{ErrorType: ErrorCrypto, Err: err})
			return
		}
		session.sendResponse(message)
	} else {
		var builders gabi.ProofBuilderList
		var err error
		switch session.Action {
		case ActionSigning:
			fallthrough
		case ActionDisclosing:
			builders, err = session.credManager.ProofBuilders(session.choice)
		case ActionIssuing:
			builders, err = session.credManager.IssuanceProofBuilders(session.irmaSession.(*IssuanceRequest))
		}
		if err != nil {
			session.fail(&SessionError{ErrorType: ErrorCrypto, Err: err})
		}

		startKeyshareSession(
			session,
			session.Handler,
			builders,
			session.irmaSession,
			session.credManager.ConfigurationStore,
			session.credManager.keyshareServers,
		)
	}
}

func (session *session) KeyshareDone(message interface{}) {
	session.sendResponse(message)
}

func (session *session) KeyshareCancelled() {
	session.cancel()
}

func (session *session) KeyshareBlocked(duration int) {
	session.fail(&SessionError{ErrorType: ErrorKeyshareBlocked, Info: strconv.Itoa(duration)})
}

func (session *session) KeyshareError(err error) {
	session.fail(&SessionError{ErrorType: ErrorKeyshare, Err: err})
}

type disclosureResponse string

func (session *session) sendResponse(message interface{}) {
	var log *LogEntry
	var err error

	switch session.Action {
	case ActionSigning:
		fallthrough
	case ActionDisclosing:
		var response disclosureResponse
		if err = session.transport.Post("proofs", &response, message); err != nil {
			session.fail(err.(*SessionError))
			return
		}
		if response != "VALID" {
			session.fail(&SessionError{ErrorType: ErrorRejected, Info: string(response)})
			return
		}
		log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err
	case ActionIssuing:
		response := []*gabi.IssueSignatureMessage{}
		if err = session.transport.Post("commitments", &response, message); err != nil {
			session.fail(err.(*SessionError))
			return
		}
		if err = session.credManager.ConstructCredentials(response, session.irmaSession.(*IssuanceRequest)); err != nil {
			session.fail(&SessionError{ErrorType: ErrorCrypto, Err: err})
			return
		}
		log, _ = session.createLogEntry(message) // TODO err
	}

	_ = session.credManager.addLogEntry(log) // TODO err
	if !session.downloaded.Empty() {
		session.credManager.handler.UpdateConfigurationStore(session.downloaded)
	}
	if session.Action == ActionIssuing {
		session.credManager.handler.UpdateAttributes()
	}
	session.Handler.Success(session.Action)
}

func (session *session) managerSession() {
	manager, err := session.credManager.ConfigurationStore.DownloadSchemeManager(session.ServerURL)
	if err != nil {
		session.Handler.Failure(session.Action, &SessionError{Err: err}) // TODO
		return
	}
	session.Handler.RequestSchemeManagerPermission(manager, func(proceed bool) {
		if !proceed {
			session.Handler.Cancelled(session.Action) // No need to DELETE session here
			return
		}
		if err := session.credManager.ConfigurationStore.AddSchemeManager(manager); err != nil {
			session.Handler.Failure(session.Action, &SessionError{})
			return
		}
		if manager.Distributed() {
			session.credManager.UnenrolledKeyshareServers = session.credManager.unenrolledKeyshareServers()
		}
		session.credManager.handler.UpdateConfigurationStore(
			&IrmaIdentifierSet{
				SchemeManagers:  map[SchemeManagerIdentifier]struct{}{manager.Identifier(): {}},
				Issuers:         map[IssuerIdentifier]struct{}{},
				CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			},
		)
		session.Handler.Success(session.Action)
	})
	return
}
<commit_msg>Ensure that non-accepted sessions are always DELETEd exactly once<commit_after>package irmago

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
)

// This file contains the client side of the IRMA protocol, as well as the Handler interface
// which is used to communicate session info with the user.

// PermissionHandler is a callback for providing permission for an IRMA session
// and specifying the attributes to be disclosed.
type PermissionHandler func(proceed bool, choice *DisclosureChoice)

type PinHandler func(proceed bool, pin string)

// A Handler contains callbacks for communication to the user.
type Handler interface {
	StatusUpdate(action Action, status Status)
	Success(action Action)
	Cancelled(action Action)
	Failure(action Action, err *SessionError)
	UnsatisfiableRequest(action Action, missing AttributeDisjunctionList)
	MissingKeyshareEnrollment(manager SchemeManagerIdentifier)

	RequestIssuancePermission(request IssuanceRequest, ServerName string, callback PermissionHandler)
	RequestVerificationPermission(request DisclosureRequest, ServerName string, callback PermissionHandler)
	RequestSignaturePermission(request SignatureRequest, ServerName string, callback PermissionHandler)
	RequestSchemeManagerPermission(manager *SchemeManager, callback func(proceed bool))

	RequestPin(remainingAttempts int, callback PinHandler)
}

type SessionDismisser interface {
	Dismiss()
}

// A session is an IRMA session.
type session struct {
	Action    Action
	Version   Version
	ServerURL string
	Handler   Handler

	info        *SessionInfo
	credManager *CredentialManager
	jwt         RequestorJwt
	irmaSession IrmaSession
	transport   *HTTPTransport
	choice      *DisclosureChoice
	downloaded  *IrmaIdentifierSet
	deleted     bool
}

// We implement the handler for the keyshare protocol
var _ keyshareSessionHandler = (*session)(nil)

// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
	2: {2, 1},
}

func calcVersion(qr *Qr) (string, error) {
	// Parse range supported by server
	var minmajor, minminor, maxmajor, maxminor int
	var err error
	if minmajor, err = strconv.Atoi(string(qr.ProtocolVersion[0])); err != nil {
		return "", err
	}
	if minminor, err = strconv.Atoi(string(qr.ProtocolVersion[2])); err != nil {
		return "", err
	}
	if maxmajor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[0])); err != nil {
		return "", err
	}
	if maxminor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[2])); err != nil {
		return "", err
	}

	// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
	keys := make([]int, 0, len(supportedVersions))
	for k := range supportedVersions {
		keys = append(keys, k)
	}
	sort.Sort(sort.Reverse(sort.IntSlice(keys)))
	for _, major := range keys {
		for _, minor := range supportedVersions[major] {
			aboveMinimum := major > minmajor || (major == minmajor && minor >= minminor)
			underMaximum := major < maxmajor || (major == maxmajor && minor <= maxminor)
			if aboveMinimum && underMaximum {
				return fmt.Sprintf("%d.%d", major, minor), nil
			}
		}
	}
	return "", fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion, qr.ProtocolMaxVersion)
}

// NewSession creates and starts a new IRMA session.
func (cm *CredentialManager) NewSession(qr *Qr, handler Handler) SessionDismisser {
	session := &session{
		Action:      Action(qr.Type),
		ServerURL:   qr.URL,
		Handler:     handler,
		transport:   NewHTTPTransport(qr.URL),
		credManager: cm,
	}
	version, err := calcVersion(qr)
	if err != nil {
		session.fail(&SessionError{ErrorType: ErrorProtocolVersionNotSupported, Err: err})
		return nil
	}
	session.Version = Version(version)

	// Check if the action is one of the supported types
	switch session.Action {
	case ActionDisclosing: // nop
	case ActionSigning: // nop
	case ActionIssuing: // nop
	//case ActionSchemeManager: // nop
	case ActionUnknown:
		fallthrough
	default:
		session.fail(&SessionError{ErrorType: ErrorUnknownAction, Info: string(session.Action)})
		return nil
	}

	if !strings.HasSuffix(session.ServerURL, "/") {
		session.ServerURL += "/"
	}

	go session.start()

	return session
}

// start retrieves the first message in the IRMA protocol, checks if we can perform
// the request, and informs the user of the outcome.
func (session *session) start() {
	defer func() {
		handlePanic(func(err *SessionError) {
			if session.Handler != nil {
				session.Handler.Failure(session.Action, err)
			}
		})
	}()

	session.Handler.StatusUpdate(session.Action, StatusCommunicating)

	if session.Action == ActionSchemeManager {
		session.managerSession()
		return
	}

	// Get the first IRMA protocol message and parse it
	session.info = &SessionInfo{}
	Err := session.transport.Get("jwt", session.info)
	if Err != nil {
		session.fail(Err.(*SessionError))
		return
	}

	var err error
	session.jwt, err = parseRequestorJwt(session.Action, session.info.Jwt)
	if err != nil {
		session.fail(&SessionError{ErrorType: ErrorInvalidJWT, Err: err})
		return
	}
	session.irmaSession = session.jwt.IrmaSession()
	session.irmaSession.SetContext(session.info.Context)
	session.irmaSession.SetNonce(session.info.Nonce)
	if session.Action == ActionIssuing {
		ir := session.irmaSession.(*IssuanceRequest)
		// Store which public keys the server will use
		for _, credreq := range ir.Credentials {
			credreq.KeyCounter = session.info.Keys[credreq.CredentialTypeID.IssuerIdentifier()]
		}
	}

	// Check if we are enrolled into all involved keyshare servers
	for id := range session.irmaSession.Identifiers().SchemeManagers {
		manager, ok := session.credManager.ConfigurationStore.SchemeManagers[id]
		if !ok {
			session.fail(&SessionError{ErrorType: ErrorUnknownSchemeManager, Info: id.String()})
			return
		}
		distributed := manager.Distributed()
		_, enrolled := session.credManager.keyshareServers[id]
		if distributed && !enrolled {
			session.delete()
			session.Handler.MissingKeyshareEnrollment(id)
			return
		}
	}

	// Download missing credential types/issuers/public keys from the scheme manager
	if session.downloaded, err = session.credManager.ConfigurationStore.Download(session.irmaSession.Identifiers()); err != nil {
		session.Handler.Failure(
			session.Action,
			&SessionError{ErrorType: ErrorConfigurationStoreDownload, Err: err},
		)
		return
	}

	if session.Action == ActionIssuing {
		ir := session.irmaSession.(*IssuanceRequest)
		for _, credreq := range ir.Credentials {
			info, err := credreq.Info(session.credManager.ConfigurationStore)
			if err != nil {
				session.fail(&SessionError{ErrorType: ErrorUnknownCredentialType, Err: err})
				return
			}
			ir.CredentialInfoList = append(ir.CredentialInfoList, info)
		}
	}

	candidates, missing := session.credManager.CheckSatisfiability(session.irmaSession.ToDisclose())
	if len(missing) > 0 {
		session.Handler.UnsatisfiableRequest(session.Action, missing)
		// TODO: session.transport.Delete() on dialog cancel
		return
	}
	session.irmaSession.SetCandidates(candidates)

	// Ask for permission to execute the session
	callback := PermissionHandler(func(proceed bool, choice *DisclosureChoice) {
		session.choice = choice
		session.irmaSession.SetDisclosureChoice(choice)
		go session.do(proceed)
	})
	session.Handler.StatusUpdate(session.Action, StatusConnected)
	switch session.Action {
	case ActionDisclosing:
		session.Handler.RequestVerificationPermission(
			*session.irmaSession.(*DisclosureRequest), session.jwt.Requestor(), callback)
	case ActionSigning:
		session.Handler.RequestSignaturePermission(
			*session.irmaSession.(*SignatureRequest), session.jwt.Requestor(), callback)
	case ActionIssuing:
		session.Handler.RequestIssuancePermission(
			*session.irmaSession.(*IssuanceRequest), session.jwt.Requestor(), callback)
	default:
		panic("Invalid session type") // does not happen, session.Action has been checked earlier
	}
}

func (session *session) do(proceed bool) {
	defer func() {
		handlePanic(func(err *SessionError) {
			if session.Handler != nil {
				session.Handler.Failure(session.Action, err)
			}
		})
	}()

	if !proceed {
		session.cancel()
		return
	}
	session.Handler.StatusUpdate(session.Action, StatusCommunicating)

	if !session.irmaSession.Identifiers().Distributed(session.credManager.ConfigurationStore) {
		var message interface{}
		var err error
		switch session.Action {
		case ActionSigning:
			message, err = session.credManager.Proofs(session.choice, session.irmaSession, true)
		case ActionDisclosing:
			message, err = session.credManager.Proofs(session.choice, session.irmaSession, false)
		case ActionIssuing:
			message, err = session.credManager.IssueCommitments(session.irmaSession.(*IssuanceRequest))
		}
		if err != nil {
			session.fail(&SessionError{ErrorType: ErrorCrypto, Err: err})
			return
		}
		session.sendResponse(message)
	} else {
		var builders gabi.ProofBuilderList
		var err error
		switch session.Action {
		case ActionSigning:
			fallthrough
		case ActionDisclosing:
			builders, err = session.credManager.ProofBuilders(session.choice)
		case ActionIssuing:
			builders, err = session.credManager.IssuanceProofBuilders(session.irmaSession.(*IssuanceRequest))
		}
		if err != nil {
			session.fail(&SessionError{ErrorType: ErrorCrypto, Err: err})
		}

		startKeyshareSession(
			session,
			session.Handler,
			builders,
			session.irmaSession,
			session.credManager.ConfigurationStore,
			session.credManager.keyshareServers,
		)
	}
}

func (session *session) KeyshareDone(message interface{}) {
	session.sendResponse(message)
}

func (session *session) KeyshareCancelled() {
	session.cancel()
}

func (session *session) KeyshareBlocked(duration int) {
	session.fail(&SessionError{ErrorType: ErrorKeyshareBlocked, Info: strconv.Itoa(duration)})
}

func (session *session) KeyshareError(err error) {
	session.fail(&SessionError{ErrorType: ErrorKeyshare, Err: err})
}

type disclosureResponse string

func (session *session) sendResponse(message interface{}) {
	var log *LogEntry
	var err error

	switch session.Action {
	case ActionSigning:
		fallthrough
	case ActionDisclosing:
		var response disclosureResponse
		if err = session.transport.Post("proofs", &response, message); err != nil {
			session.fail(err.(*SessionError))
			return
		}
		if response != "VALID" {
			session.fail(&SessionError{ErrorType: ErrorRejected, Info: string(response)})
			return
		}
		log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err
	case ActionIssuing:
		response := []*gabi.IssueSignatureMessage{}
		if err = session.transport.Post("commitments", &response, message); err != nil {
			session.fail(err.(*SessionError))
			return
		}
		if err = session.credManager.ConstructCredentials(response, session.irmaSession.(*IssuanceRequest)); err != nil {
			session.fail(&SessionError{ErrorType: ErrorCrypto, Err: err})
			return
		}
		log, _ = session.createLogEntry(message) // TODO err
	}

	_ = session.credManager.addLogEntry(log) // TODO err
	if !session.downloaded.Empty() {
		session.credManager.handler.UpdateConfigurationStore(session.downloaded)
	}
	if session.Action == ActionIssuing {
		session.credManager.handler.UpdateAttributes()
	}
	session.Handler.Success(session.Action)
}

func (session *session) managerSession() {
	manager, err := session.credManager.ConfigurationStore.DownloadSchemeManager(session.ServerURL)
	if err != nil {
		session.Handler.Failure(session.Action, &SessionError{Err: err}) // TODO
		return
	}
	session.Handler.RequestSchemeManagerPermission(manager, func(proceed bool) {
		if !proceed {
			session.Handler.Cancelled(session.Action) // No need to DELETE session here
			return
		}
		if err := session.credManager.ConfigurationStore.AddSchemeManager(manager); err != nil {
			session.Handler.Failure(session.Action, &SessionError{})
			return
		}
		if manager.Distributed() {
			session.credManager.UnenrolledKeyshareServers = session.credManager.unenrolledKeyshareServers()
		}
		session.credManager.handler.UpdateConfigurationStore(
			&IrmaIdentifierSet{
				SchemeManagers:  map[SchemeManagerIdentifier]struct{}{manager.Identifier(): {}},
				Issuers:         map[IssuerIdentifier]struct{}{},
				CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			},
		)
		session.Handler.Success(session.Action)
	})
	return
}

// Session lifetime functions

func handlePanic(callback func(*SessionError)) {
	if e := recover(); e != nil {
		var info string
		switch x := e.(type) {
		case string:
			info = x
		case error:
			info = x.Error()
		case fmt.Stringer:
			info = x.String()
		default: // nop
		}
		fmt.Printf("Recovered from panic: '%v'\n%s\n", e, info)
		if callback != nil {
			callback(&SessionError{ErrorType: ErrorPanic, Info: info})
		}
	}
}

// Idempotently send DELETE to remote server, returning whether or not we did something
func (session *session) delete() bool {
	if !session.deleted {
		session.transport.Delete()
		session.deleted = true
		return true
	}
	return false
}

func (session *session) fail(err *SessionError) {
	if session.delete() {
		err.Err = errors.Wrap(err.Err, 0)
		if session.downloaded != nil && !session.downloaded.Empty() {
			session.credManager.handler.UpdateConfigurationStore(session.downloaded)
		}
		session.Handler.Failure(session.Action, err)
	}
}

func (session *session) cancel() {
	if session.delete() {
		if session.downloaded != nil && !session.downloaded.Empty() {
			session.credManager.handler.UpdateConfigurationStore(session.downloaded)
		}
		session.Handler.Cancelled(session.Action)
	}
}

func (session *session) Dismiss() {
	session.cancel()
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAzureRMNetworkSecurityRule_basic(t *testing.T) {
	rInt := acctest.RandInt()
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
				),
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_disappears(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
					testCheckAzureRMNetworkSecurityRuleDisappears("azurerm_network_security_rule.test"),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_addingRules(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_updateBasic(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test1"),
				),
			},

			{
				Config: testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test2"),
				),
			},
		},
	})
}

func testCheckAzureRMNetworkSecurityRuleExists(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		resp, err := conn.Get(resourceGroup, sgName, sgrName)
		if err != nil {
			return fmt.Errorf("Bad: Get on secRuleClient: %+v", err)
		}

		if resp.StatusCode == http.StatusNotFound {
			return fmt.Errorf("Bad: Network Security Rule %q (resource group: %q) (network security group: %q) does not exist", sgrName, sgName, resourceGroup)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDisappears(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		_, error := conn.Delete(resourceGroup, sgName, sgrName, make(chan struct{}))
		err := <-error
		if err != nil {
			return fmt.Errorf("Bad: Delete on secRuleClient: %+v", err)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*ArmClient).secRuleClient

	for _, rs := range s.RootModule().Resources {

		if rs.Type != "azurerm_network_security_rule" {
			continue
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup := rs.Primary.Attributes["resource_group_name"]

		resp, err := conn.Get(resourceGroup, sgName, sgrName)

		if err != nil {
			return nil
		}

		if resp.StatusCode != http.StatusNotFound {
			return fmt.Errorf("Network Security Rule still exists:\n%#v", resp.SecurityRulePropertiesFormat)
		}
	}

	return nil
}

func testAccAzureRMNetworkSecurityRule_basic(rInt int, location string) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
  name     = "acctestRG-%d"
  location = "%s"
}

resource "azurerm_network_security_group" "test" {
  name                = "acceptanceTestSecurityGroup1"
  location            = "${azurerm_resource_group.test.location}"
  resource_group_name = "${azurerm_resource_group.test.name}"
}

resource "azurerm_network_security_rule" "test" {
  name                        = "test123"
  priority                    = 100
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test.name}"
  network_security_group_name = "${azurerm_network_security_group.test.name}"
}

`, rInt, location)
}

func testAccAzureRMNetworkSecurityRule_updateBasic(rInt int, location string) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
  name     = "acctestRG-%d"
  location = "%s"
}

resource "azurerm_network_security_group" "test1" {
  name                = "acceptanceTestSecurityGroup2"
  location            = "${azurerm_resource_group.test.location}"
  resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
  name                        = "test123"
  priority                    = 100
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test1.name}"
  network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt, location)
}

func testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt int, location string) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
  name     = "acctestRG-%d"
  location = "%s"
}

resource "azurerm_network_security_group" "test1" {
  name                = "acceptanceTestSecurityGroup2"
  location            = "${azurerm_resource_group.test.location}"
  resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
  name                        = "test123"
  priority                    = 100
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test1.name}"
  network_security_group_name = "${azurerm_network_security_group.test1.name}"
}

resource "azurerm_network_security_rule" "test2" {
  name                        = "testing456"
  priority                    = 101
  direction                   = "Inbound"
  access                      = "Deny"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test1.name}"
  network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt, location)
}
<commit_msg>Fixing the NSR test<commit_after>package azurerm

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAzureRMNetworkSecurityRule_basic(t *testing.T) {
	rInt := acctest.RandInt()
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
				),
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_disappears(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
					testCheckAzureRMNetworkSecurityRuleDisappears("azurerm_network_security_rule.test"),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_addingRules(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_updateBasic(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test1"),
				),
			},

			{
				Config: testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt, testLocation()),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test2"),
				),
			},
		},
	})
}

func testCheckAzureRMNetworkSecurityRuleExists(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		resp, err := conn.Get(resourceGroup, sgName, sgrName)
		if err != nil {
			return fmt.Errorf("Bad: Get on secRuleClient: %+v", err)
		}

		if resp.StatusCode == http.StatusNotFound {
			return fmt.Errorf("Bad: Network Security Rule %q (resource group: %q) (network security group: %q) does not exist", sgrName, sgName, resourceGroup)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDisappears(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		_, error := conn.Delete(resourceGroup, sgName, sgrName, make(chan struct{}))
		err := <-error
		if err != nil {
			return fmt.Errorf("Bad: Delete on secRuleClient: %+v", err)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*ArmClient).secRuleClient

	for _, rs := range s.RootModule().Resources {

		if rs.Type != "azurerm_network_security_rule" {
			continue
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup := rs.Primary.Attributes["resource_group_name"]

		resp, err := conn.Get(resourceGroup, sgName, sgrName)

		if err != nil {
			return nil
		}

		if resp.StatusCode != http.StatusNotFound {
			return fmt.Errorf("Network Security Rule still exists:\n%#v", resp.SecurityRulePropertiesFormat)
		}
	}

	return nil
}

func testAccAzureRMNetworkSecurityRule_basic(rInt int, location string) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
  name     = "acctestRG-%d"
  location = "%s"
}

resource "azurerm_network_security_group" "test" {
  name                = "acceptanceTestSecurityGroup1"
  location            = "${azurerm_resource_group.test.location}"
  resource_group_name = "${azurerm_resource_group.test.name}"
}

resource "azurerm_network_security_rule" "test" {
  name                        = "test123"
  priority                    = 100
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test.name}"
  network_security_group_name = "${azurerm_network_security_group.test.name}"
}

`, rInt, location)
}

func testAccAzureRMNetworkSecurityRule_updateBasic(rInt int, location string) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
  name     = "acctestRG-%d"
  location = "%s"
}

resource "azurerm_network_security_group" "test1" {
  name                = "acceptanceTestSecurityGroup2"
  location            = "${azurerm_resource_group.test1.location}"
  resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
  name                        = "test123"
  priority                    = 100
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test1.name}"
  network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt, location)
}

func testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt int, location string) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
  name     = "acctestRG-%d"
  location = "%s"
}

resource "azurerm_network_security_group" "test1" {
  name                = "acceptanceTestSecurityGroup2"
  location            = "${azurerm_resource_group.test1.location}"
  resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
  name                        = "test123"
  priority                    = 100
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test1.name}"
  network_security_group_name = "${azurerm_network_security_group.test1.name}"
}

resource "azurerm_network_security_rule" "test2" {
  name                        = "testing456"
  priority                    = 101
  direction                   = "Inbound"
  access                      = "Deny"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = "${azurerm_resource_group.test1.name}"
  network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt, location)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package validation

import (
	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/genericapiserver/options"
)

// TODO: Longer term we should read this from some config store, rather than a flag.
func verifyClusterIPFlags(options *options.ServerRunOptions) {
	if options.ServiceClusterIPRange.IP == nil {
		glog.Fatal("No --service-cluster-ip-range specified")
	}
	var ones, bits = options.ServiceClusterIPRange.Mask.Size()
	if bits-ones > 20 {
		glog.Fatal("Specified --service-cluster-ip-range is too large")
	}
}

func verifyServiceNodePort(options *options.ServerRunOptions) {
	if options.KubernetesServiceNodePort < 0 || options.KubernetesServiceNodePort > 65535 {
		glog.Fatalf("--kubernetes-service-node-port %v must be between 0 and 65535, inclusive. If 0, the Kubernetes master service will be of type ClusterIP.", options.KubernetesServiceNodePort)
	}

	if options.KubernetesServiceNodePort > 0 && !options.ServiceNodePortRange.Contains(options.KubernetesServiceNodePort) {
		glog.Fatalf("Kubernetes service port range %v doesn't contain %v", options.ServiceNodePortRange, (options.KubernetesServiceNodePort))
	}
}

func verifySecureAndInsecurePort(options *options.ServerRunOptions) {
	if options.SecurePort < 0 || options.SecurePort > 65535 {
		glog.Fatalf("--secure-port %v must be between 0 and 65535, inclusive. 0 for turning off secure port.", options.SecurePort)
	}

	// TODO: Allow 0 to turn off insecure port.
	if options.InsecurePort < 1 || options.InsecurePort > 65535 {
		glog.Fatalf("--insecure-port %v must be between 1 and 65535, inclusive.", options.InsecurePort)
	}

	if options.SecurePort == options.InsecurePort {
		glog.Fatalf("--secure-port and --insecure-port cannot use the same port.")
	}
}

func ValidateRunOptions(options *options.ServerRunOptions) {
	verifyClusterIPFlags(options)
	verifyServiceNodePort(options)
	verifySecureAndInsecurePort(options)
}
<commit_msg>combine the ValidateRunOptions errors<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package validation

import (
	"fmt"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/genericapiserver/options"
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
)

// TODO: Longer term we should read this from some config store, rather than a flag.
func verifyClusterIPFlags(options *options.ServerRunOptions) []error {
	errors := []error{}
	if options.ServiceClusterIPRange.IP == nil {
		errors = append(errors, fmt.Errorf("No --service-cluster-ip-range specified"))
	}
	var ones, bits = options.ServiceClusterIPRange.Mask.Size()
	if bits-ones > 20 {
		errors = append(errors, fmt.Errorf("Specified --service-cluster-ip-range is too large"))
	}
	return errors
}

func verifyServiceNodePort(options *options.ServerRunOptions) []error {
	errors := []error{}
	if options.KubernetesServiceNodePort < 0 || options.KubernetesServiceNodePort > 65535 {
		errors = append(errors, fmt.Errorf("--kubernetes-service-node-port %v must be between 0 and 65535, inclusive. If 0, the Kubernetes master service will be of type ClusterIP.", options.KubernetesServiceNodePort))
	}

	if options.KubernetesServiceNodePort > 0 && !options.ServiceNodePortRange.Contains(options.KubernetesServiceNodePort) {
		errors = append(errors, fmt.Errorf("Kubernetes service port range %v doesn't contain %v", options.ServiceNodePortRange, (options.KubernetesServiceNodePort)))
	}
	return errors
}

func verifySecureAndInsecurePort(options *options.ServerRunOptions) []error {
	errors := []error{}
	if options.SecurePort < 0 || options.SecurePort > 65535 {
		errors = append(errors, fmt.Errorf("--secure-port %v must be between 0 and 65535, inclusive. 0 for turning off secure port.", options.SecurePort))
	}

	// TODO: Allow 0 to turn off insecure port.
	if options.InsecurePort < 1 || options.InsecurePort > 65535 {
		errors = append(errors, fmt.Errorf("--insecure-port %v must be between 1 and 65535, inclusive.", options.InsecurePort))
	}

	if options.SecurePort == options.InsecurePort {
		errors = append(errors, fmt.Errorf("--secure-port and --insecure-port cannot use the same port."))
	}
	return errors
}

func ValidateRunOptions(options *options.ServerRunOptions) {
	errors := []error{}
	if errs := verifyClusterIPFlags(options); len(errs) > 0 {
		errors = append(errors, errs...)
	}
	if errs := verifyServiceNodePort(options); len(errs) > 0 {
		errors = append(errors, errs...)
	}
	if errs := verifySecureAndInsecurePort(options); len(errs) > 0 {
		errors = append(errors, errs...)
	}
	if err := utilerrors.NewAggregate(errors); err != nil {
		glog.Fatalf("Validate server run options failed: %v", err)
	}
}
<|endoftext|>


<commit_before>package smux

import (
	"encoding/binary"
	"io"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

const (
	defaultAcceptBacklog = 1024
)

const (
	errBrokenPipe      = "broken pipe"
	errConnReset       = "connection reset by peer"
	errInvalidProtocol = "invalid protocol version"
)

// Session defines a multiplexed connection for streams
type Session struct {
	conn      io.ReadWriteCloser
	writeLock sync.Mutex

	config       *Config
	nextStreamID uint32 // next stream identifier

	bucket     int32      // token bucket
	bucketCond *sync.Cond // used for waiting for tokens

	streams    map[uint32]*Stream // all streams in this session
	streamLock sync.Mutex         // locks streams

	die       chan struct{} // flag session has died
	dieLock   sync.Mutex
	chAccepts chan *Stream

	xmitPool  sync.Pool
	dataReady int32 // flag data has arrived
}

func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
	s := new(Session)
	s.die = make(chan struct{})
	s.conn = conn
	s.config = config
	s.streams = make(map[uint32]*Stream)
	s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
	s.bucket = int32(config.MaxReceiveBuffer)
	s.bucketCond = sync.NewCond(&sync.Mutex{})
	s.xmitPool.New = func() interface{} {
		return make([]byte, (1<<16)+headerSize)
	}

	if client {
		s.nextStreamID = 1
	} else {
		s.nextStreamID = 2
	}
	go s.recvLoop()
	go s.keepalive()
	return s
}

// OpenStream is used to create a new stream
func (s *Session) OpenStream() (*Stream, error) {
	if s.IsClosed() {
		return nil, errors.New(errBrokenPipe)
	}

	sid := atomic.AddUint32(&s.nextStreamID, 2)
	stream := newStream(sid, s.config.MaxFrameSize, s)

	if _, err := s.writeFrame(newFrame(cmdSYN, sid)); err != nil {
		return nil, errors.Wrap(err, "writeFrame")
	}

	s.streamLock.Lock()
	s.streams[sid] = stream
	s.streamLock.Unlock()
	return stream, nil
}

// AcceptStream is used to block until the next available stream
// is ready to be accepted.
func (s *Session) AcceptStream() (*Stream, error) {
	select {
	case stream := <-s.chAccepts:
		return stream, nil
	case <-s.die:
		return nil, errors.New(errBrokenPipe)
	}
}

// Close is used to close the session and all streams.
func (s *Session) Close() (err error) {
	s.dieLock.Lock()
	defer s.dieLock.Unlock()

	select {
	case <-s.die:
		return errors.New(errBrokenPipe)
	default:
		close(s.die)
		s.streamLock.Lock()
		for k := range s.streams {
			s.streams[k].sessionClose()
		}
		s.streamLock.Unlock()
		s.bucketCond.Signal()
		return s.conn.Close()
	}
}

// IsClosed does a safe check to see if we have shutdown
func (s *Session) IsClosed() bool {
	select {
	case <-s.die:
		return true
	default:
		return false
	}
}

// NumStreams returns the number of currently open streams
func (s *Session) NumStreams() int {
	if s.IsClosed() {
		return 0
	}
	s.streamLock.Lock()
	defer s.streamLock.Unlock()
	return len(s.streams)
}

// notify the session that a stream has closed
func (s *Session) streamClosed(sid uint32) {
	s.streamLock.Lock()
	if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
		if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
			s.bucketCond.Signal()
		}
	}
	delete(s.streams, sid)
	s.streamLock.Unlock()
}

// returnTokens is called by stream to return token after read
func (s *Session) returnTokens(n int) {
	if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
		s.bucketCond.Signal()
	}
}

// session read a frame from underlying connection
// it's data is pointed to the input buffer
func (s *Session) readFrame(buffer []byte) (f Frame, err error) {
	if _, err := io.ReadFull(s.conn, buffer[:headerSize]); err != nil {
		return f, errors.Wrap(err, "readFrame")
	}

	dec := rawHeader(buffer)
	if dec.Version() != version {
		return f, errors.New(errInvalidProtocol)
	}

	f.ver = dec.Version()
	f.cmd = dec.Cmd()
	f.sid = dec.StreamID()
	if length := dec.Length(); length > 0 {
		if _, err := io.ReadFull(s.conn, buffer[headerSize:headerSize+length]); err != nil {
			return f, errors.Wrap(err, "readFrame")
		}
		f.data = buffer[headerSize : headerSize+length]
	}
	return f, nil
}

// recvLoop keeps on reading from underlying connection if tokens are available
func (s *Session) recvLoop() {
	buffer := make([]byte, (1<<16)+headerSize)
	for {
		s.bucketCond.L.Lock()
		for atomic.LoadInt32(&s.bucket) <= 0 && !s.IsClosed() {
			s.bucketCond.Wait()
		}
		s.bucketCond.L.Unlock()

		if s.IsClosed() {
			return
		}

		if f, err := s.readFrame(buffer); err == nil {
			atomic.StoreInt32(&s.dataReady, 1)

			switch f.cmd {
			case cmdNOP:
			case cmdSYN:
				s.streamLock.Lock()
				if _, ok := s.streams[f.sid]; !ok {
					stream := newStream(f.sid, s.config.MaxFrameSize, s)
					s.streams[f.sid] = stream
					select {
					case s.chAccepts <- stream:
					case <-s.die:
					}
				}
				s.streamLock.Unlock()
			case cmdRST:
				s.streamLock.Lock()
				if stream, ok := s.streams[f.sid]; ok {
					stream.markRST()
					stream.notifyReadEvent()
				}
				s.streamLock.Unlock()
			case cmdPSH:
				s.streamLock.Lock()
				if stream, ok := s.streams[f.sid]; ok {
					atomic.AddInt32(&s.bucket, -int32(len(f.data)))
					stream.pushBytes(f.data)
					stream.notifyReadEvent()
				}
				s.streamLock.Unlock()
			default:
				s.Close()
				return
			}
		} else {
			s.Close()
			return
		}
	}
}

func (s *Session) keepalive() {
	tickerPing := time.NewTicker(s.config.KeepAliveInterval)
	tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
	defer tickerPing.Stop()
	defer tickerTimeout.Stop()
	for {
		select {
		case <-tickerPing.C:
			s.writeFrame(newFrame(cmdNOP, 0))
			s.bucketCond.Signal() // force a signal to the recvLoop
		case <-tickerTimeout.C:
			if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
				s.Close()
				return
			}
		case <-s.die:
			return
		}
	}
}

// writeFrame writes the frame to the underlying connection
// and returns the number of bytes written if successful
func (s *Session) writeFrame(f Frame) (n int, err error) {
	buf := s.xmitPool.Get().([]byte)
	buf[0] = f.ver
	buf[1] = f.cmd
	binary.LittleEndian.PutUint16(buf[2:], uint16(len(f.data)))
	binary.LittleEndian.PutUint32(buf[4:], f.sid)
	copy(buf[headerSize:], f.data)

	s.writeLock.Lock()
	n, err = s.conn.Write(buf[:headerSize+len(f.data)])
	s.writeLock.Unlock()
	s.xmitPool.Put(buf)
	return n, err
}
<commit_msg>optimize returnTokens<commit_after>package smux

import (
	"encoding/binary"
	"io"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

const (
	defaultAcceptBacklog = 1024
)

const (
	errBrokenPipe      = "broken pipe"
	errConnReset       = "connection reset by peer"
	errInvalidProtocol = "invalid protocol version"
)

// Session defines a multiplexed connection for streams
type Session struct {
	conn      io.ReadWriteCloser
	writeLock sync.Mutex

	config       *Config
	nextStreamID uint32 // next stream identifier

	bucket     int32      // token bucket
	bucketCond *sync.Cond // used for waiting for tokens

	streams    map[uint32]*Stream // all streams in this session
	streamLock sync.Mutex         // locks streams

	die       chan struct{} // flag session has died
	dieLock   sync.Mutex
	chAccepts chan *Stream

	xmitPool  sync.Pool
	dataReady int32 // flag data has arrived
}

func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
	s := new(Session)
	s.die = make(chan struct{})
	s.conn = conn
	s.config = config
	s.streams = make(map[uint32]*Stream)
	s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
	s.bucket = int32(config.MaxReceiveBuffer)
	s.bucketCond = sync.NewCond(&sync.Mutex{})
	s.xmitPool.New = func() interface{} {
		return make([]byte, (1<<16)+headerSize)
	}

	if client {
		s.nextStreamID = 1
	} else {
		s.nextStreamID = 2
	}
	go s.recvLoop()
	go s.keepalive()
	return s
}

// OpenStream is used to create a new stream
func (s *Session) OpenStream() (*Stream, error) {
	if s.IsClosed() {
		return nil, errors.New(errBrokenPipe)
	}

	sid := atomic.AddUint32(&s.nextStreamID, 2)
	stream := newStream(sid, s.config.MaxFrameSize, s)

	if _, err := s.writeFrame(newFrame(cmdSYN, sid)); err != nil {
		return nil, errors.Wrap(err, "writeFrame")
	}

	s.streamLock.Lock()
	s.streams[sid] = stream
	s.streamLock.Unlock()
	return stream, nil
}

// AcceptStream is used to block until the next available stream
// is ready to be accepted.
func (s *Session) AcceptStream() (*Stream, error) {
	select {
	case stream := <-s.chAccepts:
		return stream, nil
	case <-s.die:
		return nil, errors.New(errBrokenPipe)
	}
}

// Close is used to close the session and all streams.
func (s *Session) Close() (err error) {
	s.dieLock.Lock()
	defer s.dieLock.Unlock()

	select {
	case <-s.die:
		return errors.New(errBrokenPipe)
	default:
		close(s.die)
		s.streamLock.Lock()
		for k := range s.streams {
			s.streams[k].sessionClose()
		}
		s.streamLock.Unlock()
		s.bucketCond.Signal()
		return s.conn.Close()
	}
}

// IsClosed does a safe check to see if we have shutdown
func (s *Session) IsClosed() bool {
	select {
	case <-s.die:
		return true
	default:
		return false
	}
}

// NumStreams returns the number of currently open streams
func (s *Session) NumStreams() int {
	if s.IsClosed() {
		return 0
	}
	s.streamLock.Lock()
	defer s.streamLock.Unlock()
	return len(s.streams)
}

// notify the session that a stream has closed
func (s *Session) streamClosed(sid uint32) {
	s.streamLock.Lock()
	if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
		if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
			s.bucketCond.Signal()
		}
	}
	delete(s.streams, sid)
	s.streamLock.Unlock()
}

// returnTokens is called by stream to return token after read
func (s *Session) returnTokens(n int) {
	oldvalue := atomic.LoadInt32(&s.bucket)
	newvalue := atomic.AddInt32(&s.bucket, int32(n))
	if oldvalue <= 0 && newvalue > 0 {
		s.bucketCond.Signal()
	}

}

// session read a frame from underlying connection
// it's data is pointed to the input buffer
func (s *Session) readFrame(buffer []byte) (f Frame, err error) {
	if _, err := io.ReadFull(s.conn, buffer[:headerSize]); err != nil {
		return f, errors.Wrap(err, "readFrame")
	}

	dec := rawHeader(buffer)
	if dec.Version() != version {
		return f, errors.New(errInvalidProtocol)
	}

	f.ver = dec.Version()
	f.cmd = dec.Cmd()
	f.sid = dec.StreamID()
	if length := dec.Length(); length > 0 {
		if _, err := io.ReadFull(s.conn, buffer[headerSize:headerSize+length]); err != nil {
			return f, errors.Wrap(err, "readFrame")
		}
		f.data = buffer[headerSize : headerSize+length]
	}
	return f, nil
}

// recvLoop keeps on reading from underlying connection if tokens are available
func (s *Session) recvLoop() {
	buffer := make([]byte, (1<<16)+headerSize)
	for {
		s.bucketCond.L.Lock()
		for atomic.LoadInt32(&s.bucket) <= 0 && !s.IsClosed() {
			s.bucketCond.Wait()
		}
		s.bucketCond.L.Unlock()

		if s.IsClosed() {
			return
		}

		if f, err := s.readFrame(buffer); err == nil {
			atomic.StoreInt32(&s.dataReady, 1)

			switch f.cmd {
			case cmdNOP:
			case cmdSYN:
				s.streamLock.Lock()
				if _, ok := s.streams[f.sid]; !ok {
					stream := newStream(f.sid, s.config.MaxFrameSize, s)
					s.streams[f.sid] = stream
					select {
					case s.chAccepts <- stream:
					case <-s.die:
					}
				}
				s.streamLock.Unlock()
			case cmdRST:
				s.streamLock.Lock()
				if stream, ok := s.streams[f.sid]; ok {
					stream.markRST()
					stream.notifyReadEvent()
				}
				s.streamLock.Unlock()
			case cmdPSH:
				s.streamLock.Lock()
				if stream, ok := s.streams[f.sid]; ok {
					atomic.AddInt32(&s.bucket, -int32(len(f.data)))
					stream.pushBytes(f.data)
					stream.notifyReadEvent()
				}
				s.streamLock.Unlock()
			default:
				s.Close()
				return
			}
		} else {
			s.Close()
			return
		}
	}
}

func (s *Session) keepalive() {
	tickerPing := time.NewTicker(s.config.KeepAliveInterval)
	tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
	defer tickerPing.Stop()
	defer tickerTimeout.Stop()
	for {
		select {
		case <-tickerPing.C:
			s.writeFrame(newFrame(cmdNOP, 0))
			s.bucketCond.Signal() // force a signal to the recvLoop
		case <-tickerTimeout.C:
			if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
				s.Close()
				return
			}
		case <-s.die:
			return
		}
	}
}

// writeFrame writes the frame to the underlying connection
// and returns the number of bytes written if successful
func (s *Session) writeFrame(f Frame) (n int, err error) {
	buf := s.xmitPool.Get().([]byte)
	buf[0] = f.ver
	buf[1] = f.cmd
	binary.LittleEndian.PutUint16(buf[2:], uint16(len(f.data)))
	binary.LittleEndian.PutUint32(buf[4:], f.sid)
	copy(buf[headerSize:], f.data)

	s.writeLock.Lock()
	n, err = s.conn.Write(buf[:headerSize+len(f.data)])
	s.writeLock.Unlock()
	s.xmitPool.Put(buf)
	return n, err
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package sha512t implements the SHA512/t hash algorithms as defined
// in FIPS 180-4.
package sha512t

import (
	"hash"
	"strconv"
)

// The blocksize of SHA512/t in bytes.
const BlockSize = 128

const (
	chunk      = 128
	init0      = 0x6a09e667f3bcc908 ^ 0xa5a5a5a5a5a5a5a5
	init1      = 0xbb67ae8584caa73b ^ 0xa5a5a5a5a5a5a5a5
	init2      = 0x3c6ef372fe94f82b ^ 0xa5a5a5a5a5a5a5a5
	init3      = 0xa54ff53a5f1d36f1 ^ 0xa5a5a5a5a5a5a5a5
	init4      = 0x510e527fade682d1 ^ 0xa5a5a5a5a5a5a5a5
	init5      = 0x9b05688c2b3e6c1f ^ 0xa5a5a5a5a5a5a5a5
	init6      = 0x1f83d9abfb41bd6b ^ 0xa5a5a5a5a5a5a5a5
	init7      = 0x5be0cd19137e2179 ^ 0xa5a5a5a5a5a5a5a5
	sha512size = 64
)

// digest represents the partial evaluation of a checksum.
type digest struct {
	h   [8]uint64
	x   [chunk]byte
	nx  int
	len uint64
	t   int
}

func (d *digest) Reset() {

	d.h[0] = init0
	d.h[1] = init1
	d.h[2] = init2
	d.h[3] = init3
	d.h[4] = init4
	d.h[5] = init5
	d.h[6] = init6
	d.h[7] = init7

	d.Write([]byte("SHA-512/"))
	d.Write([]byte(strconv.Itoa(d.t)))
	d.writePad()

	d.nx = 0
	d.len = 0
}

// New returns a new hash.Hash computing the SHA512/t checksum.
func New(t int) hash.Hash {
	d := new(digest)

	if t >= 512 || t < 0 {
		panic("sha512t: t out of range")
	}

	if t%8 != 0 {
		panic("sha512t: t % 8 != 0")
	}

	if t == 384 {
		panic("sha512t: use sha384 for t=384")
	}

	d.t = t
	d.Reset()

	return d
}

func (d *digest) Size() int {
	return d.t / 8
}

func (d *digest) BlockSize() int { return BlockSize }

func (d *digest) Write(p []byte) (nn int, err error) {
	nn = len(p)
	d.len += uint64(nn)
	if d.nx > 0 {
		n := len(p)
		if n > chunk-d.nx {
			n = chunk - d.nx
		}
		for i := 0; i < n; i++ {
			d.x[d.nx+i] = p[i]
		}
		d.nx += n
		if d.nx == chunk {
			block(d, d.x[0:])
			d.nx = 0
		}
		p = p[n:]
	}
	if len(p) >= chunk {
		n := len(p) &^ (chunk - 1)
		block(d, p[:n])
		p = p[n:]
	}
	if len(p) > 0 {
		d.nx = copy(d.x[:], p)
	}
	return
}

func (d0 *digest) Sum(in []byte) []byte {
	// Make a copy of d0 so that caller can keep writing and summing.
	d := new(digest)
	*d = *d0

	d.writePad()

	size := d.t / 8
	digest := make([]byte, sha512size)

	for i, s := range d.h {
		digest[i*8] = byte(s >> 56)
		digest[i*8+1] = byte(s >> 48)
		digest[i*8+2] = byte(s >> 40)
		digest[i*8+3] = byte(s >> 32)
		digest[i*8+4] = byte(s >> 24)
		digest[i*8+5] = byte(s >> 16)
		digest[i*8+6] = byte(s >> 8)
		digest[i*8+7] = byte(s)
	}

	return append(in, digest[:size]...)
}

func (d *digest) writePad() {
	// Padding.  Add a 1 bit and 0 bits until 112 bytes mod 128.
	len := d.len
	var tmp [128]byte
	tmp[0] = 0x80
	if len%128 < 112 {
		d.Write(tmp[0 : 112-len%128])
	} else {
		d.Write(tmp[0 : 128+112-len%128])
	}

	// Length in bits.
	len <<= 3
	for i := uint(0); i < 16; i++ {
		tmp[i] = byte(len >> (120 - 8*i))
	}
	d.Write(tmp[0:16])

	if d.nx != 0 {
		panic("d.nx != 0")
	}
}
<commit_msg>ease go vet<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package sha512t implements the SHA512/t hash algorithms as defined
// in FIPS 180-4.
package sha512t

import (
	"hash"
	"strconv"
)

// The blocksize of SHA512/t in bytes.
const BlockSize = 128

const (
	chunk      = 128
	init0      = 0x6a09e667f3bcc908 ^ 0xa5a5a5a5a5a5a5a5
	init1      = 0xbb67ae8584caa73b ^ 0xa5a5a5a5a5a5a5a5
	init2      = 0x3c6ef372fe94f82b ^ 0xa5a5a5a5a5a5a5a5
	init3      = 0xa54ff53a5f1d36f1 ^ 0xa5a5a5a5a5a5a5a5
	init4      = 0x510e527fade682d1 ^ 0xa5a5a5a5a5a5a5a5
	init5      = 0x9b05688c2b3e6c1f ^ 0xa5a5a5a5a5a5a5a5
	init6      = 0x1f83d9abfb41bd6b ^ 0xa5a5a5a5a5a5a5a5
	init7      = 0x5be0cd19137e2179 ^ 0xa5a5a5a5a5a5a5a5
	sha512size = 64
)

// digest represents the partial evaluation of a checksum.
type digest struct {
	h   [8]uint64
	x   [chunk]byte
	nx  int
	len uint64
	t   int
}

func (d *digest) Reset() {

	d.h[0] = init0
	d.h[1] = init1
	d.h[2] = init2
	d.h[3] = init3
	d.h[4] = init4
	d.h[5] = init5
	d.h[6] = init6
	d.h[7] = init7

	d.Write([]byte("SHA-512/"))
	d.Write([]byte(strconv.Itoa(d.t)))
	d.writePad()

	d.nx = 0
	d.len = 0
}

// New returns a new hash.Hash computing the SHA512/t checksum.
func New(t int) hash.Hash {
	d := new(digest)

	if t >= 512 || t < 0 {
		panic("sha512t: t out of range")
	}

	if t%8 != 0 {
		panic("sha512t: t not a multiple of 8")
	}

	if t == 384 {
		panic("sha512t: use sha384 for t=384")
	}

	d.t = t
	d.Reset()

	return d
}

func (d *digest) Size() int {
	return d.t / 8
}

func (d *digest) BlockSize() int { return BlockSize }

func (d *digest) Write(p []byte) (nn int, err error) {
	nn = len(p)
	d.len += uint64(nn)
	if d.nx > 0 {
		n := len(p)
		if n > chunk-d.nx {
			n = chunk - d.nx
		}
		for i := 0; i < n; i++ {
			d.x[d.nx+i] = p[i]
		}
		d.nx += n
		if d.nx == chunk {
			block(d, d.x[0:])
			d.nx = 0
		}
		p = p[n:]
	}
	if len(p) >= chunk {
		n := len(p) &^ (chunk - 1)
		block(d, p[:n])
		p = p[n:]
	}
	if len(p) > 0 {
		d.nx = copy(d.x[:], p)
	}
	return
}

func (d0 *digest) Sum(in []byte) []byte {
	// Make a copy of d0 so that caller can keep writing and summing.
	d := new(digest)
	*d = *d0

	d.writePad()

	size := d.t / 8
	digest := make([]byte, sha512size)

	for i, s := range d.h {
		digest[i*8] = byte(s >> 56)
		digest[i*8+1] = byte(s >> 48)
		digest[i*8+2] = byte(s >> 40)
		digest[i*8+3] = byte(s >> 32)
		digest[i*8+4] = byte(s >> 24)
		digest[i*8+5] = byte(s >> 16)
		digest[i*8+6] = byte(s >> 8)
		digest[i*8+7] = byte(s)
	}

	return append(in, digest[:size]...)
}

func (d *digest) writePad() {
	// Padding.  Add a 1 bit and 0 bits until 112 bytes mod 128.
	len := d.len
	var tmp [128]byte
	tmp[0] = 0x80
	if len%128 < 112 {
		d.Write(tmp[0 : 112-len%128])
	} else {
		d.Write(tmp[0 : 128+112-len%128])
	}

	// Length in bits.
	len <<= 3
	for i := uint(0); i < 16; i++ {
		tmp[i] = byte(len >> (120 - 8*i))
	}
	d.Write(tmp[0:16])

	if d.nx != 0 {
		panic("d.nx != 0")
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	. "runtime"
	"testing"
	"unsafe"
)

// Simple serial sanity test for parallelfor.
func TestParFor(t *testing.T) {
	const P = 1
	const N = 20
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	ParForSetup(desc, P, N, nil, true, func(desc *ParFor, i uint32) {
		data[i] = data[i]*data[i] + 1
	})
	ParForDo(desc)
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that nonblocking parallelfor does not block.
func TestParFor2(t *testing.T) {
	const P = 7
	const N = 1003
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	ParForSetup(desc, P, N, (*byte)(unsafe.Pointer(&data)), false, func(desc *ParFor, i uint32) {
		d := *(*[]uint64)(unsafe.Pointer(desc.Ctx))
		d[i] = d[i]*d[i] + 1
	})
	for p := 0; p < P; p++ {
		ParForDo(desc)
	}
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that iterations are properly distributed.
func TestParForSetup(t *testing.T) {
	const P = 11
	const N = 101
	desc := NewParFor(P)
	for n := uint32(0); n < N; n++ {
		for p := uint32(1); p <= P; p++ {
			ParForSetup(desc, p, n, nil, true, func(desc *ParFor, i uint32) {})
			sum := uint32(0)
			size0 := uint32(0)
			end0 := uint32(0)
			for i := uint32(0); i < p; i++ {
				begin, end := ParForIters(desc, i)
				size := end - begin
				sum += size
				if i == 0 {
					size0 = size
					if begin != 0 {
						t.Fatalf("incorrect begin: %d (n=%d, p=%d)", begin, n, p)
					}
				} else {
					if size != size0 && size != size0+1 {
						t.Fatalf("incorrect size: %d/%d (n=%d, p=%d)", size, size0, n, p)
					}
					if begin != end0 {
						t.Fatalf("incorrect begin/end: %d/%d (n=%d, p=%d)", begin, end0, n, p)
					}
				}
				end0 = end
			}
			if sum != n {
				t.Fatalf("incorrect sum: %d/%d (p=%d)", sum, n, p)
			}
		}
	}
}

// Test parallel parallelfor.
func TestParForParallel(t *testing.T) {
	N := uint64(1e7)
	if testing.Short() {
		N /= 10
	}
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	P := GOMAXPROCS(-1)
	desc := NewParFor(uint32(P))
	ParForSetup(desc, uint32(P), uint32(N), nil, true, func(desc *ParFor, i uint32) {
		data[i] = data[i]*data[i] + 1
	})
	for p := 1; p < P; p++ {
		go ParForDo(desc)
	}
	ParForDo(desc)
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}
<commit_msg>runtime: disable TestParForParallel for now on 32-bit hosts Also add call to GC() to make it easier to re-enable the test.<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	. "runtime"
	"testing"
	"unsafe"
)

// Simple serial sanity test for parallelfor.
func TestParFor(t *testing.T) {
	const P = 1
	const N = 20
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	ParForSetup(desc, P, N, nil, true, func(desc *ParFor, i uint32) {
		data[i] = data[i]*data[i] + 1
	})
	ParForDo(desc)
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that nonblocking parallelfor does not block.
func TestParFor2(t *testing.T) {
	const P = 7
	const N = 1003
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	desc := NewParFor(P)
	ParForSetup(desc, P, N, (*byte)(unsafe.Pointer(&data)), false, func(desc *ParFor, i uint32) {
		d := *(*[]uint64)(unsafe.Pointer(desc.Ctx))
		d[i] = d[i]*d[i] + 1
	})
	for p := 0; p < P; p++ {
		ParForDo(desc)
	}
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}
}

// Test that iterations are properly distributed.
func TestParForSetup(t *testing.T) {
	const P = 11
	const N = 101
	desc := NewParFor(P)
	for n := uint32(0); n < N; n++ {
		for p := uint32(1); p <= P; p++ {
			ParForSetup(desc, p, n, nil, true, func(desc *ParFor, i uint32) {})
			sum := uint32(0)
			size0 := uint32(0)
			end0 := uint32(0)
			for i := uint32(0); i < p; i++ {
				begin, end := ParForIters(desc, i)
				size := end - begin
				sum += size
				if i == 0 {
					size0 = size
					if begin != 0 {
						t.Fatalf("incorrect begin: %d (n=%d, p=%d)", begin, n, p)
					}
				} else {
					if size != size0 && size != size0+1 {
						t.Fatalf("incorrect size: %d/%d (n=%d, p=%d)", size, size0, n, p)
					}
					if begin != end0 {
						t.Fatalf("incorrect begin/end: %d/%d (n=%d, p=%d)", begin, end0, n, p)
					}
				}
				end0 = end
			}
			if sum != n {
				t.Fatalf("incorrect sum: %d/%d (p=%d)", sum, n, p)
			}
		}
	}
}

// Test parallel parallelfor.
func TestParForParallel(t *testing.T) {
	if GOARCH != "amd64" {
		t.Log("temporarily disabled, see http://golang.org/issue/4155")
		return
	}

	N := uint64(1e7)
	if testing.Short() {
		N /= 10
	}
	data := make([]uint64, N)
	for i := uint64(0); i < N; i++ {
		data[i] = i
	}
	P := GOMAXPROCS(-1)
	desc := NewParFor(uint32(P))
	ParForSetup(desc, uint32(P), uint32(N), nil, true, func(desc *ParFor, i uint32) {
		data[i] = data[i]*data[i] + 1
	})
	for p := 1; p < P; p++ {
		go ParForDo(desc)
	}
	ParForDo(desc)
	for i := uint64(0); i < N; i++ {
		if data[i] != i*i+1 {
			t.Fatalf("Wrong element %d: %d", i, data[i])
		}
	}

	data, desc = nil, nil
	GC()
}
<|endoftext|>


<commit_before>package sourcegraph

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/google/go-querystring/query"
	"google.golang.org/grpc"
	"sourcegraph.com/sourcegraph/go-sourcegraph/router"
)

const (
	libraryVersion = "0.0.1"
	userAgent      = "sourcegraph-client/" + libraryVersion
)

// A Client communicates with the Sourcegraph API. All communication
// is done using gRPC over HTTP/2 except for BuildData (which uses
// HTTP/1).
type Client struct {
	// Services used to communicate with different parts of the Sourcegraph API.
	Accounts            AccountsClient
	Builds              BuildsClient
	BuildData           BuildDataService
	Defs                DefsClient
	Deltas              DeltasClient
	Markdown            MarkdownClient
	Meta                MetaClient
	MirrorRepos         MirrorReposClient
	MirroredRepoSSHKeys MirroredRepoSSHKeysClient
	Orgs                OrgsClient
	People              PeopleClient
	RepoBadges          RepoBadgesClient
	RepoStatuses        RepoStatusesClient
	RepoTree            RepoTreeClient
	Repos               ReposClient
	Search              SearchClient
	Units               UnitsClient
	UserAuth            UserAuthClient
	Users               UsersClient

	// Base URL for HTTP/1.1 requests, which should have a trailing slash.
	BaseURL *url.URL

	// User agent used for HTTP/1.1 requests to the Sourcegraph API.
	UserAgent string

	// HTTP client used to communicate with the Sourcegraph API.
	httpClient *http.Client

	// gRPC client connection used to communicate with the Sourcegraph
	// API.
	Conn *grpc.ClientConn
}

// NewClient returns a Sourcegraph API client. The gRPC conn is used
// for all services except for BuildData (which uses the
// httpClient). If httpClient is nil, http.DefaultClient is used.
func NewClient(httpClient *http.Client, conn *grpc.ClientConn) *Client {
	c := new(Client)

	// HTTP/1
	if httpClient == nil {
		cloned := *http.DefaultClient
		cloned.Transport = keepAliveTransport()
		httpClient = &cloned
	}
	c.httpClient = httpClient
	c.BaseURL = &url.URL{Scheme: "https", Host: "sourcegraph.com", Path: "/api/"}
	c.UserAgent = userAgent
	c.BuildData = &buildDataService{c}

	// gRPC (HTTP/2)
	c.Conn = conn
	c.Accounts = NewAccountsClient(conn)
	c.Builds = NewBuildsClient(conn)
	c.Defs = NewDefsClient(conn)
	c.Deltas = NewDeltasClient(conn)
	c.Markdown = NewMarkdownClient(conn)
	c.Meta = NewMetaClient(conn)
	c.MirrorRepos = NewMirrorReposClient(conn)
	c.MirroredRepoSSHKeys = NewMirroredRepoSSHKeysClient(conn)
	c.Orgs = NewOrgsClient(conn)
	c.People = NewPeopleClient(conn)
	c.RepoBadges = NewRepoBadgesClient(conn)
	c.RepoStatuses = NewRepoStatusesClient(conn)
	c.RepoTree = NewRepoTreeClient(conn)
	c.Repos = NewReposClient(conn)
	c.Search = NewSearchClient(conn)
	c.Units = NewUnitsClient(conn)
	c.UserAuth = NewUserAuthClient(conn)
	c.Users = NewUsersClient(conn)

	return c
}

// Router is used to generate URLs for the Sourcegraph API.
var Router = router.NewAPIRouter(nil)

// ResetRouter clears and reconstructs the preinitialized API
// router. It should be called after setting an router.ExtraConfig
// func but only during init time.
func ResetRouter() {
	Router = router.NewAPIRouter(nil)
}

// URL generates a URL for the given route, route variables, and
// querystring options. Unless you explicitly set a Host, Scheme,
// and/or Port on Router, the returned URL will contain only path and
// querystring components (and will not be an absolute URL).
func URL(route string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	rt := Router.Get(route)
	if rt == nil {
		return nil, fmt.Errorf("no Sourcegraph API route named %q", route)
	}

	routeVarsList := make([]string, 2*len(routeVars))
	i := 0
	for name, val := range routeVars {
		routeVarsList[i*2] = name
		routeVarsList[i*2+1] = val
		i++
	}
	url, err := rt.URL(routeVarsList...)
	if err != nil {
		return nil, err
	}

	if opt != nil {
		err = addOptions(url, opt)
		if err != nil {
			return nil, err
		}
	}

	return url, nil
}

// URL generates the absolute URL to the named Sourcegraph API endpoint, using the
// specified route variables and query options.
func (c *Client) URL(route string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	url, err := URL(route, routeVars, opt)
	if err != nil {
		return nil, err
	}

	// make the route URL path relative to BaseURL by trimming the leading "/"
	url.Path = strings.TrimPrefix(url.Path, "/")

	// make the route URL path relative to BaseURL's path and not the path parent
	baseURL := *c.BaseURL
	if !strings.HasSuffix(baseURL.Path, "/") {
		baseURL.Path = baseURL.Path + "/"
	}

	// make the URL absolute
	url = baseURL.ResolveReference(url)

	return url, nil
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. Relative
// URLs should always be specified without a preceding slash. If specified, the
// value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	url, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) Response {
	if r == nil {
		return nil
	}
	return &HTTPResponse{Response: r}
}

// HTTPResponse is a wrapped HTTP response from the Sourcegraph API with
// additional Sourcegraph-specific response information parsed out. It
// implements Response.
type HTTPResponse struct {
	*http.Response
}

// TotalCount implements Response.
func (r *HTTPResponse) TotalCount() int {
	tc := r.Header.Get("x-total-count")
	if tc == "" {
		return -1
	}
	n, err := strconv.Atoi(tc)
	if err != nil {
		return -1
	}
	return n
}

// Response is a response from the Sourcegraph API. When using the HTTP API,
// API methods return *HTTPResponse values that implement Response.
type Response interface {
	// TotalCount is the total number of items in the resource or result set
	// that exist remotely. Only a portion of the total may be in the response
	// body. If the endpoint did not return a total count, then TotalCount
	// returns -1.
	TotalCount() int
}

// SimpleResponse implements Response.
type SimpleResponse struct {
	Total int // see (Response).TotalCount()
}

func (r *SimpleResponse) TotalCount() int { return r.Total }

type doKey int // sentinel value type for (*Client).Do v parameter

const preserveBody doKey = iota // when passed as v to (*Client).Do, the resp body is neither parsed nor closed

// Do sends an API request and returns the API response.  The API
// response is decoded and stored in the value pointed to by v, or
// returned as an error if an API error has occurred. If v is
// preserveBody, then the HTTP response body is not closed by Do; the
// caller is responsible for closing it.
func (c *Client) Do(req *http.Request, v interface{}) (Response, error) {
	var resp Response
	rawResp, err := c.httpClient.Do(req)
	if rawResp != nil {
		if v != preserveBody && rawResp.Body != nil {
			defer rawResp.Body.Close()
		}
		resp = newResponse(rawResp)
		if err == nil {
			// Don't clobber error from Do, if any (it could be, e.g.,
			// a sentinel error returned by the HTTP client's
			// CheckRedirect func).
			if err := CheckResponse(rawResp); err != nil {
				// even though there was an error, we still return the response
				// in case the caller wants to inspect it further
				return resp, err
			}
		}
	}
	if err != nil {
		return resp, err
	}

	if v != nil {
		if bp, ok := v.(*[]byte); ok {
			*bp, err = ioutil.ReadAll(rawResp.Body)
		} else if v != preserveBody {
			err = json.NewDecoder(rawResp.Body).Decode(v)
		}
	}
	if err != nil {
		return resp, fmt.Errorf("error reading response from %s %s: %s", req.Method, req.URL.RequestURI(), err)
	}
	return resp, nil
}

// addOptions adds the parameters in opt as URL query parameters to u. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(u *url.URL, opt interface{}) error {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}

	qs, err := query.Values(opt)
	if err != nil {
		return err
	}

	u.RawQuery = qs.Encode()
	return nil
}

// keepAliveTransport returns a http.RoundTripper that uses a larger
// keep-alive pool than the default.
func keepAliveTransport() http.RoundTripper {
	return &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,

		// Allow more keep-alive connections per host to avoid
		// ephemeral port exhaustion due to getting stuck in
		// TIME_WAIT. Some systems have a very limited ephemeral port
		// supply (~1024). 20 connections is perfectly reasonable,
		// since this client will only ever hit one host.
		MaxIdleConnsPerHost: 20,
	}
}
<commit_msg>Added Changeset client to NewClient<commit_after>package sourcegraph

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/google/go-querystring/query"
	"google.golang.org/grpc"
	"sourcegraph.com/sourcegraph/go-sourcegraph/router"
)

const (
	libraryVersion = "0.0.1"
	userAgent      = "sourcegraph-client/" + libraryVersion
)

// A Client communicates with the Sourcegraph API. All communication
// is done using gRPC over HTTP/2 except for BuildData (which uses
// HTTP/1).
type Client struct {
	// Services used to communicate with different parts of the Sourcegraph API.
	Accounts            AccountsClient
	Builds              BuildsClient
	BuildData           BuildDataService
	Defs                DefsClient
	Deltas              DeltasClient
	Markdown            MarkdownClient
	Meta                MetaClient
	MirrorRepos         MirrorReposClient
	MirroredRepoSSHKeys MirroredRepoSSHKeysClient
	Orgs                OrgsClient
	People              PeopleClient
	RepoBadges          RepoBadgesClient
	RepoStatuses        RepoStatusesClient
	RepoTree            RepoTreeClient
	Repos               ReposClient
	Search              SearchClient
	Units               UnitsClient
	UserAuth            UserAuthClient
	Users               UsersClient

	// Base URL for HTTP/1.1 requests, which should have a trailing slash.
	BaseURL *url.URL

	// User agent used for HTTP/1.1 requests to the Sourcegraph API.
	UserAgent string

	// HTTP client used to communicate with the Sourcegraph API.
	httpClient *http.Client

	// gRPC client connection used to communicate with the Sourcegraph
	// API.
	Conn *grpc.ClientConn
}

// NewClient returns a Sourcegraph API client. The gRPC conn is used
// for all services except for BuildData (which uses the
// httpClient). If httpClient is nil, http.DefaultClient is used.
func NewClient(httpClient *http.Client, conn *grpc.ClientConn) *Client {
	c := new(Client)

	// HTTP/1
	if httpClient == nil {
		cloned := *http.DefaultClient
		cloned.Transport = keepAliveTransport()
		httpClient = &cloned
	}
	c.httpClient = httpClient
	c.BaseURL = &url.URL{Scheme: "https", Host: "sourcegraph.com", Path: "/api/"}
	c.UserAgent = userAgent
	c.BuildData = &buildDataService{c}

	// gRPC (HTTP/2)
	c.Conn = conn
	c.Accounts = NewAccountsClient(conn)
	c.Builds = NewBuildsClient(conn)
	c.Defs = NewDefsClient(conn)
	c.Deltas = NewDeltasClient(conn)
	c.Markdown = NewMarkdownClient(conn)
	c.Meta = NewMetaClient(conn)
	c.MirrorRepos = NewMirrorReposClient(conn)
	c.MirroredRepoSSHKeys = NewMirroredRepoSSHKeysClient(conn)
	c.Orgs = NewOrgsClient(conn)
	c.People = NewPeopleClient(conn)
	c.RepoBadges = NewRepoBadgesClient(conn)
	c.RepoStatuses = NewRepoStatusesClient(conn)
	c.RepoTree = NewRepoTreeClient(conn)
	c.Repos = NewReposClient(conn)
	c.Changesets = NewChangesetsClient(conn)
	c.Search = NewSearchClient(conn)
	c.Units = NewUnitsClient(conn)
	c.UserAuth = NewUserAuthClient(conn)
	c.Users = NewUsersClient(conn)

	return c
}

// Router is used to generate URLs for the Sourcegraph API.
var Router = router.NewAPIRouter(nil)

// ResetRouter clears and reconstructs the preinitialized API
// router. It should be called after setting an router.ExtraConfig
// func but only during init time.
func ResetRouter() {
	Router = router.NewAPIRouter(nil)
}

// URL generates a URL for the given route, route variables, and
// querystring options. Unless you explicitly set a Host, Scheme,
// and/or Port on Router, the returned URL will contain only path and
// querystring components (and will not be an absolute URL).
func URL(route string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	rt := Router.Get(route)
	if rt == nil {
		return nil, fmt.Errorf("no Sourcegraph API route named %q", route)
	}

	routeVarsList := make([]string, 2*len(routeVars))
	i := 0
	for name, val := range routeVars {
		routeVarsList[i*2] = name
		routeVarsList[i*2+1] = val
		i++
	}
	url, err := rt.URL(routeVarsList...)
	if err != nil {
		return nil, err
	}

	if opt != nil {
		err = addOptions(url, opt)
		if err != nil {
			return nil, err
		}
	}

	return url, nil
}

// URL generates the absolute URL to the named Sourcegraph API endpoint, using the
// specified route variables and query options.
func (c *Client) URL(route string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	url, err := URL(route, routeVars, opt)
	if err != nil {
		return nil, err
	}

	// make the route URL path relative to BaseURL by trimming the leading "/"
	url.Path = strings.TrimPrefix(url.Path, "/")

	// make the route URL path relative to BaseURL's path and not the path parent
	baseURL := *c.BaseURL
	if !strings.HasSuffix(baseURL.Path, "/") {
		baseURL.Path = baseURL.Path + "/"
	}

	// make the URL absolute
	url = baseURL.ResolveReference(url)

	return url, nil
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. Relative
// URLs should always be specified without a preceding slash. If specified, the
// value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	url, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) Response {
	if r == nil {
		return nil
	}
	return &HTTPResponse{Response: r}
}

// HTTPResponse is a wrapped HTTP response from the Sourcegraph API with
// additional Sourcegraph-specific response information parsed out. It
// implements Response.
type HTTPResponse struct {
	*http.Response
}

// TotalCount implements Response.
func (r *HTTPResponse) TotalCount() int {
	tc := r.Header.Get("x-total-count")
	if tc == "" {
		return -1
	}
	n, err := strconv.Atoi(tc)
	if err != nil {
		return -1
	}
	return n
}

// Response is a response from the Sourcegraph API. When using the HTTP API,
// API methods return *HTTPResponse values that implement Response.
type Response interface {
	// TotalCount is the total number of items in the resource or result set
	// that exist remotely. Only a portion of the total may be in the response
	// body. If the endpoint did not return a total count, then TotalCount
	// returns -1.
	TotalCount() int
}

// SimpleResponse implements Response.
type SimpleResponse struct {
	Total int // see (Response).TotalCount()
}

func (r *SimpleResponse) TotalCount() int { return r.Total }

type doKey int // sentinel value type for (*Client).Do v parameter

const preserveBody doKey = iota // when passed as v to (*Client).Do, the resp body is neither parsed nor closed

// Do sends an API request and returns the API response.  The API
// response is decoded and stored in the value pointed to by v, or
// returned as an error if an API error has occurred. If v is
// preserveBody, then the HTTP response body is not closed by Do; the
// caller is responsible for closing it.
func (c *Client) Do(req *http.Request, v interface{}) (Response, error) {
	var resp Response
	rawResp, err := c.httpClient.Do(req)
	if rawResp != nil {
		if v != preserveBody && rawResp.Body != nil {
			defer rawResp.Body.Close()
		}
		resp = newResponse(rawResp)
		if err == nil {
			// Don't clobber error from Do, if any (it could be, e.g.,
			// a sentinel error returned by the HTTP client's
			// CheckRedirect func).
			if err := CheckResponse(rawResp); err != nil {
				// even though there was an error, we still return the response
				// in case the caller wants to inspect it further
				return resp, err
			}
		}
	}
	if err != nil {
		return resp, err
	}

	if v != nil {
		if bp, ok := v.(*[]byte); ok {
			*bp, err = ioutil.ReadAll(rawResp.Body)
		} else if v != preserveBody {
			err = json.NewDecoder(rawResp.Body).Decode(v)
		}
	}
	if err != nil {
		return resp, fmt.Errorf("error reading response from %s %s: %s", req.Method, req.URL.RequestURI(), err)
	}
	return resp, nil
}

// addOptions adds the parameters in opt as URL query parameters to u. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(u *url.URL, opt interface{}) error {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}

	qs, err := query.Values(opt)
	if err != nil {
		return err
	}

	u.RawQuery = qs.Encode()
	return nil
}

// keepAliveTransport returns a http.RoundTripper that uses a larger
// keep-alive pool than the default.
func keepAliveTransport() http.RoundTripper {
	return &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,

		// Allow more keep-alive connections per host to avoid
		// ephemeral port exhaustion due to getting stuck in
		// TIME_WAIT. Some systems have a very limited ephemeral port
		// supply (~1024). 20 connections is perfectly reasonable,
		// since this client will only ever hit one host.
		MaxIdleConnsPerHost: 20,
	}
}
<|endoftext|>