<commit_before>package irmaserver

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-errors/errors"
	"strings"
	"sync"
	"time"

	"github.com/bsm/redislock"

	"github.com/alexandrevicenzi/go-sse"
	"github.com/go-redis/redis/v8"
	"github.com/privacybydesign/gabi"
	"github.com/privacybydesign/gabi/big"
	irma "github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/common"
	"github.com/privacybydesign/irmago/server"

	"github.com/sirupsen/logrus"
)

type session struct {
	sync.Mutex
	sse            *sse.Server
	locked         bool
	lock           *redislock.Lock
	sessions       sessionStore
	conf           *server.Configuration
	request        irma.SessionRequest
	statusChannels []chan irma.ServerStatus

	sessionData
}

type sessionData struct {
	Action             irma.Action
	RequestorToken     irma.RequestorToken
	ClientToken        irma.ClientToken
	Version            *irma.ProtocolVersion `json:",omitempty"`
	Rrequest           irma.RequestorRequest
	LegacyCompatible   bool // if the request is convertible to pre-condiscon format
	Status             irma.ServerStatus
	PrevStatus         irma.ServerStatus
	ResponseCache      responseCache
	LastActive         time.Time
	Result             *server.SessionResult
	KssProofs          map[irma.SchemeManagerIdentifier]*gabi.ProofP
	Next               *irma.Qr
	FrontendAuth       irma.FrontendAuthorization
	ImplicitDisclosure irma.AttributeConDisCon
	Options            irma.SessionOptions
	ClientAuth         irma.ClientAuthorization
}

type responseCache struct {
	Endpoint      string
	Message       []byte
	Response      []byte
	Status        int
	SessionStatus irma.ServerStatus
}

type sessionStore interface {
	get(token irma.RequestorToken) (*session, error)
	clientGet(token irma.ClientToken) (*session, error)
	add(session *session) error
	update(session *session) error
	lock(session *session) error
	unlock(session *session) error
	stop()
}

type memorySessionStore struct {
	sync.RWMutex
	conf *server.Configuration

	requestor map[irma.RequestorToken]*session
	client    map[irma.ClientToken]*session
}

type redisSessionStore struct {
	client *redis.Client
	locker *redislock.Client
	conf   *server.Configuration
}

type RedisError struct {
	err error
}

func (err *RedisError) Error() string {
	return fmt.Sprintf("redis error: %s", err.err)
}

type UnknownSessionError struct {
	requestorToken irma.RequestorToken
	clientToken    irma.ClientToken
}

func (err *UnknownSessionError) Error() string {
	if err.requestorToken != "" {
		return fmt.Sprintf("session result requested of unknown session %s", err.requestorToken)
	} else {
		return fmt.Sprintf("session result requested of unknown session with clientToken %s", err.clientToken)
	}
}

const (
	maxLockLifetime            = 500 * time.Millisecond // After this the Redis lock self-deletes, preventing a deadlock
	minLockRetryTime           = 30 * time.Millisecond
	maxLockRetryTime           = 2 * time.Second
	requestorTokenLookupPrefix = "token:"
	clientTokenLookupPrefix    = "session:"
	lockPrefix                 = "lock:"
)

var (
	minProtocolVersion = irma.NewVersion(2, 4)
	maxProtocolVersion = irma.NewVersion(2, 8)

	minFrontendProtocolVersion = irma.NewVersion(1, 0)
	maxFrontendProtocolVersion = irma.NewVersion(1, 1)

	lockingRetryOptions = &redislock.Options{RetryStrategy: redislock.ExponentialBackoff(minLockRetryTime, maxLockRetryTime)}
)

func (s *memorySessionStore) get(t irma.RequestorToken) (*session, error) {
	s.RLock()
	defer s.RUnlock()
	if s.requestor[t] != nil {
		return s.requestor[t], nil
	} else {
		return nil, server.LogError(&UnknownSessionError{t, ""})
	}
}

func (s *memorySessionStore) clientGet(t irma.ClientToken) (*session, error) {
	s.RLock()
	defer s.RUnlock()

	if s.client[t] != nil {
		return s.client[t], nil
	} else {
		return nil, server.LogError(&UnknownSessionError{"", t})
	}
}

func (s *memorySessionStore) add(session *session) error {
	s.Lock()
	defer s.Unlock()
	s.requestor[session.RequestorToken] = session
	s.client[session.ClientToken] = session
	return nil
}

func (s *memorySessionStore) update(_ *session) error {
	return nil
}

func (s *memorySessionStore) lock(session *session) error {
	session.Lock()
	session.locked = true

	return nil
}

func (s *memorySessionStore) unlock(session *session) error {
	session.locked = false
	session.Unlock()

	return nil
}

func (s *memorySessionStore) stop() {
	s.Lock()
	defer s.Unlock()
	for _, session := range s.requestor {
		if session.sse != nil {
			session.sse.CloseChannel("session/" + string(session.RequestorToken))
			session.sse.CloseChannel("session/" + string(session.ClientToken))
			session.sse.CloseChannel("frontendsession/" + string(session.ClientToken))
		}
	}
}

func (s *memorySessionStore) deleteExpired() {
	// First check which sessions have expired
	// We don't need a write lock for this yet, so postpone that for actual deleting
	s.RLock()
	toCheck := make(map[irma.RequestorToken]*session, len(s.requestor))
	for token, session := range s.requestor {
		toCheck[token] = session
	}
	s.RUnlock()

	expired := make([]irma.RequestorToken, 0, len(toCheck))
	for token, session := range toCheck {
		session.Lock()

		timeout := s.conf.MaxSessionLifetime
		if session.Status == irma.ServerStatusInitialized && session.Rrequest.Base().ClientTimeout != 0 {
			timeout = time.Duration(session.Rrequest.Base().ClientTimeout) * time.Second
		}

		if session.LastActive.Add(timeout).Before(time.Now()) {
			if !session.Status.Finished() {
				s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Infof("Session expired")
				session.markAlive()
				session.setStatus(irma.ServerStatusTimeout)
			} else {
				s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Infof("Deleting session")
				expired = append(expired, token)
			}
		}
		session.Unlock()
	}

	// Using a write lock, delete the expired sessions
	s.Lock()
	for _, token := range expired {
		session := s.requestor[token]
		if session.sse != nil {
			session.sse.CloseChannel("session/" + string(session.RequestorToken))
			session.sse.CloseChannel("session/" + string(session.ClientToken))
			session.sse.CloseChannel("frontendsession/" + string(session.ClientToken))
		}
		delete(s.client, session.ClientToken)
		delete(s.requestor, token)
	}
	s.Unlock()
}

func (s *redisSessionStore) get(t irma.RequestorToken) (*session, error) {
	val, err := s.client.Get(context.Background(), requestorTokenLookupPrefix+string(t)).Result()
	if err == redis.Nil {
		return nil, server.LogError(&UnknownSessionError{t, ""})
	} else if err != nil {
		return nil, logAsRedisError(err)
	}

	clientToken, err := irma.ParseClientToken(val)
	if err != nil {
		return nil, logAsRedisError(err)
	}
	s.conf.Logger.WithFields(logrus.Fields{"session": t}).Debugf("clientToken %s found in Redis datastore", clientToken)

	return s.clientGet(clientToken)
}

func (s *redisSessionStore) clientGet(t irma.ClientToken) (*session, error) {
	val, err := s.client.Get(context.Background(), clientTokenLookupPrefix+string(t)).Result()
	if err == redis.Nil {
		return nil, server.LogError(&UnknownSessionError{"", t})
	} else if err != nil {
		return nil, logAsRedisError(err)
	}

	var session session
	session.conf = s.conf
	session.sessions = s
	if err := json.Unmarshal([]byte(val), &session.sessionData); err != nil {
		return nil, logAsRedisError(err)
	}
	session.request = session.Rrequest.SessionRequest()
	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("Session received from Redis datastore")

	if session.LastActive.Add(s.conf.MaxSessionLifetime).Before(time.Now()) && !session.Status.Finished() {
		s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Infof("Session expired")
		session.markAlive()
		session.setStatus(irma.ServerStatusTimeout)
	}

	return &session, nil
}

func (s *redisSessionStore) add(session *session) error {
	timeout := 2 * s.conf.MaxSessionLifetime // logic similar to memory store
	if session.Status == irma.ServerStatusInitialized && session.Rrequest.Base().ClientTimeout != 0 {
		timeout = time.Duration(session.Rrequest.Base().ClientTimeout) * time.Second
	} else if session.Status.Finished() {
		timeout = s.conf.MaxSessionLifetime
	}

	sessionJSON, err := json.Marshal(session.sessionData)
	if err != nil {
		return server.LogError(err)
	}

	err = s.client.Set(context.Background(), requestorTokenLookupPrefix+string(session.sessionData.RequestorToken), string(session.sessionData.ClientToken), timeout).Err()
	if err != nil {
		return logAsRedisError(err)
	}
	err = s.client.Set(context.Background(), clientTokenLookupPrefix+string(session.sessionData.ClientToken), sessionJSON, timeout).Err()
	if err != nil {
		return logAsRedisError(err)
	}

	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("session added to Redis datastore")
	return nil
}

func (s *redisSessionStore) update(session *session) error {
	// Time passes between acquiring the lock and writing to Redis. Check before write action that lock is still valid.
	if session.lock == nil {
		return logAsRedisError(errors.Errorf("lock is not set for session with requestorToken %s", session.RequestorToken))
	} else if ttl, err := session.lock.TTL(context.Background()); err != nil {
		return logAsRedisError(err)
	} else if ttl == 0 {
		return logAsRedisError(errors.Errorf("no session lock available for session with requestorToken %s", session.RequestorToken))
	}
	return s.add(session)
}

func (s *redisSessionStore) lock(session *session) error {
	lock, err := s.locker.Obtain(context.Background(), lockPrefix+string(session.ClientToken), maxLockLifetime, lockingRetryOptions)
	if err != nil {
		// It is possible that the session is already locked. However, it should not happen often.
		// If you get the redislock.ErrNotObtained error often, you should investigate why.
		return logAsRedisError(err)
	}
	session.locked = true
	session.lock = lock
	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("session locked successfully")

	return nil
}

func (s *redisSessionStore) unlock(session *session) error {
	session.locked = false
	err := session.lock.Release(context.Background())
	if err == redislock.ErrLockNotHeld {
		s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Info("lock could not be released as the lock was not held")
		return nil
	} else if err != nil {
		return logAsRedisError(err)
	}
	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("session unlocked successfully")

	return nil
}

func (s *redisSessionStore) stop() {
	err := s.client.Close()
	if err != nil {
		_ = logAsRedisError(err)
	}
	s.conf.Logger.Info("Redis client closed successfully")
}

var one *big.Int = big.NewInt(1)

func (s *Server) newSession(action irma.Action, request irma.RequestorRequest, disclosed irma.AttributeConDisCon, FrontendAuth irma.FrontendAuthorization) (*session, error) {
	clientToken := irma.ClientToken(common.NewSessionToken())
	requestorToken := irma.RequestorToken(common.NewSessionToken())
	if len(FrontendAuth) == 0 {
		FrontendAuth = irma.FrontendAuthorization(common.NewSessionToken())
	}

	base := request.SessionRequest().Base()
	if s.conf.AugmentClientReturnURL && base.AugmentReturnURL && base.ClientReturnURL != "" {
		if strings.Contains(base.ClientReturnURL, "?") {
			base.ClientReturnURL += "&token=" + string(requestorToken)
		} else {
			base.ClientReturnURL += "?token=" + string(requestorToken)
		}
	}

	sd := sessionData{
		Action:         action,
		Rrequest:       request,
		LastActive:     time.Now(),
		RequestorToken: requestorToken,
		ClientToken:    clientToken,
		Status:         irma.ServerStatusInitialized,
		PrevStatus:     irma.ServerStatusInitialized,
		Result: &server.SessionResult{
			LegacySession: request.SessionRequest().Base().Legacy(),
			Token:         requestorToken,
			Type:          action,
			Status:        irma.ServerStatusInitialized,
		},
		Options: irma.SessionOptions{
			LDContext:     irma.LDContextSessionOptions,
			PairingMethod: irma.PairingMethodNone,
		},
		FrontendAuth:       FrontendAuth,
		ImplicitDisclosure: disclosed,
	}
	ses := &session{
		sessionData: sd,
		sessions:    s.sessions,
		sse:         s.serverSentEvents,
		conf:        s.conf,
		request:     request.SessionRequest(),
	}

	s.conf.Logger.WithFields(logrus.Fields{"session": ses.RequestorToken}).Debug("New session started")
	nonce, _ := gabi.GenerateNonce()
	base.Nonce = nonce
	base.Context = one

	err := s.sessions.add(ses)
	if err != nil {
		return nil, err
	}

	return ses, nil
}

func logAsRedisError(err error) error {
	return server.LogError(&RedisError{err})
}
<commit_msg>style: improve debug log message to indicate that session is either added OR updated in Redis store.<commit_after>package irmaserver

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-errors/errors"
	"strings"
	"sync"
	"time"

	"github.com/bsm/redislock"

	"github.com/alexandrevicenzi/go-sse"
	"github.com/go-redis/redis/v8"
	"github.com/privacybydesign/gabi"
	"github.com/privacybydesign/gabi/big"
	irma "github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/common"
	"github.com/privacybydesign/irmago/server"

	"github.com/sirupsen/logrus"
)

type session struct {
	sync.Mutex
	sse            *sse.Server
	locked         bool
	lock           *redislock.Lock
	sessions       sessionStore
	conf           *server.Configuration
	request        irma.SessionRequest
	statusChannels []chan irma.ServerStatus

	sessionData
}

type sessionData struct {
	Action             irma.Action
	RequestorToken     irma.RequestorToken
	ClientToken        irma.ClientToken
	Version            *irma.ProtocolVersion `json:",omitempty"`
	Rrequest           irma.RequestorRequest
	LegacyCompatible   bool // if the request is convertible to pre-condiscon format
	Status             irma.ServerStatus
	PrevStatus         irma.ServerStatus
	ResponseCache      responseCache
	LastActive         time.Time
	Result             *server.SessionResult
	KssProofs          map[irma.SchemeManagerIdentifier]*gabi.ProofP
	Next               *irma.Qr
	FrontendAuth       irma.FrontendAuthorization
	ImplicitDisclosure irma.AttributeConDisCon
	Options            irma.SessionOptions
	ClientAuth         irma.ClientAuthorization
}

type responseCache struct {
	Endpoint      string
	Message       []byte
	Response      []byte
	Status        int
	SessionStatus irma.ServerStatus
}

type sessionStore interface {
	get(token irma.RequestorToken) (*session, error)
	clientGet(token irma.ClientToken) (*session, error)
	add(session *session) error
	update(session *session) error
	lock(session *session) error
	unlock(session *session) error
	stop()
}

type memorySessionStore struct {
	sync.RWMutex
	conf *server.Configuration

	requestor map[irma.RequestorToken]*session
	client    map[irma.ClientToken]*session
}

type redisSessionStore struct {
	client *redis.Client
	locker *redislock.Client
	conf   *server.Configuration
}

type RedisError struct {
	err error
}

func (err *RedisError) Error() string {
	return fmt.Sprintf("redis error: %s", err.err)
}

type UnknownSessionError struct {
	requestorToken irma.RequestorToken
	clientToken    irma.ClientToken
}

func (err *UnknownSessionError) Error() string {
	if err.requestorToken != "" {
		return fmt.Sprintf("session result requested of unknown session %s", err.requestorToken)
	} else {
		return fmt.Sprintf("session result requested of unknown session with clientToken %s", err.clientToken)
	}
}

const (
	maxLockLifetime            = 500 * time.Millisecond // After this the Redis lock self-deletes, preventing a deadlock
	minLockRetryTime           = 30 * time.Millisecond
	maxLockRetryTime           = 2 * time.Second
	requestorTokenLookupPrefix = "token:"
	clientTokenLookupPrefix    = "session:"
	lockPrefix                 = "lock:"
)

var (
	minProtocolVersion = irma.NewVersion(2, 4)
	maxProtocolVersion = irma.NewVersion(2, 8)

	minFrontendProtocolVersion = irma.NewVersion(1, 0)
	maxFrontendProtocolVersion = irma.NewVersion(1, 1)

	lockingRetryOptions = &redislock.Options{RetryStrategy: redislock.ExponentialBackoff(minLockRetryTime, maxLockRetryTime)}
)

func (s *memorySessionStore) get(t irma.RequestorToken) (*session, error) {
	s.RLock()
	defer s.RUnlock()
	if s.requestor[t] != nil {
		return s.requestor[t], nil
	} else {
		return nil, server.LogError(&UnknownSessionError{t, ""})
	}
}

func (s *memorySessionStore) clientGet(t irma.ClientToken) (*session, error) {
	s.RLock()
	defer s.RUnlock()

	if s.client[t] != nil {
		return s.client[t], nil
	} else {
		return nil, server.LogError(&UnknownSessionError{"", t})
	}
}

func (s *memorySessionStore) add(session *session) error {
	s.Lock()
	defer s.Unlock()
	s.requestor[session.RequestorToken] = session
	s.client[session.ClientToken] = session
	return nil
}

func (s *memorySessionStore) update(_ *session) error {
	return nil
}

func (s *memorySessionStore) lock(session *session) error {
	session.Lock()
	session.locked = true

	return nil
}

func (s *memorySessionStore) unlock(session *session) error {
	session.locked = false
	session.Unlock()

	return nil
}

func (s *memorySessionStore) stop() {
	s.Lock()
	defer s.Unlock()
	for _, session := range s.requestor {
		if session.sse != nil {
			session.sse.CloseChannel("session/" + string(session.RequestorToken))
			session.sse.CloseChannel("session/" + string(session.ClientToken))
			session.sse.CloseChannel("frontendsession/" + string(session.ClientToken))
		}
	}
}

func (s *memorySessionStore) deleteExpired() {
	// First check which sessions have expired
	// We don't need a write lock for this yet, so postpone that for actual deleting
	s.RLock()
	toCheck := make(map[irma.RequestorToken]*session, len(s.requestor))
	for token, session := range s.requestor {
		toCheck[token] = session
	}
	s.RUnlock()

	expired := make([]irma.RequestorToken, 0, len(toCheck))
	for token, session := range toCheck {
		session.Lock()

		timeout := s.conf.MaxSessionLifetime
		if session.Status == irma.ServerStatusInitialized && session.Rrequest.Base().ClientTimeout != 0 {
			timeout = time.Duration(session.Rrequest.Base().ClientTimeout) * time.Second
		}

		if session.LastActive.Add(timeout).Before(time.Now()) {
			if !session.Status.Finished() {
				s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Infof("Session expired")
				session.markAlive()
				session.setStatus(irma.ServerStatusTimeout)
			} else {
				s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Infof("Deleting session")
				expired = append(expired, token)
			}
		}
		session.Unlock()
	}

	// Using a write lock, delete the expired sessions
	s.Lock()
	for _, token := range expired {
		session := s.requestor[token]
		if session.sse != nil {
			session.sse.CloseChannel("session/" + string(session.RequestorToken))
			session.sse.CloseChannel("session/" + string(session.ClientToken))
			session.sse.CloseChannel("frontendsession/" + string(session.ClientToken))
		}
		delete(s.client, session.ClientToken)
		delete(s.requestor, token)
	}
	s.Unlock()
}

func (s *redisSessionStore) get(t irma.RequestorToken) (*session, error) {
	val, err := s.client.Get(context.Background(), requestorTokenLookupPrefix+string(t)).Result()
	if err == redis.Nil {
		return nil, server.LogError(&UnknownSessionError{t, ""})
	} else if err != nil {
		return nil, logAsRedisError(err)
	}

	clientToken, err := irma.ParseClientToken(val)
	if err != nil {
		return nil, logAsRedisError(err)
	}
	s.conf.Logger.WithFields(logrus.Fields{"session": t}).Debugf("clientToken %s found in Redis datastore", clientToken)

	return s.clientGet(clientToken)
}

func (s *redisSessionStore) clientGet(t irma.ClientToken) (*session, error) {
	val, err := s.client.Get(context.Background(), clientTokenLookupPrefix+string(t)).Result()
	if err == redis.Nil {
		return nil, server.LogError(&UnknownSessionError{"", t})
	} else if err != nil {
		return nil, logAsRedisError(err)
	}

	var session session
	session.conf = s.conf
	session.sessions = s
	if err := json.Unmarshal([]byte(val), &session.sessionData); err != nil {
		return nil, logAsRedisError(err)
	}
	session.request = session.Rrequest.SessionRequest()
	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("Session received from Redis datastore")

	if session.LastActive.Add(s.conf.MaxSessionLifetime).Before(time.Now()) && !session.Status.Finished() {
		s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Infof("Session expired")
		session.markAlive()
		session.setStatus(irma.ServerStatusTimeout)
	}

	return &session, nil
}

func (s *redisSessionStore) add(session *session) error {
	timeout := 2 * s.conf.MaxSessionLifetime // logic similar to memory store
	if session.Status == irma.ServerStatusInitialized && session.Rrequest.Base().ClientTimeout != 0 {
		timeout = time.Duration(session.Rrequest.Base().ClientTimeout) * time.Second
	} else if session.Status.Finished() {
		timeout = s.conf.MaxSessionLifetime
	}

	sessionJSON, err := json.Marshal(session.sessionData)
	if err != nil {
		return server.LogError(err)
	}

	err = s.client.Set(context.Background(), requestorTokenLookupPrefix+string(session.sessionData.RequestorToken), string(session.sessionData.ClientToken), timeout).Err()
	if err != nil {
		return logAsRedisError(err)
	}
	err = s.client.Set(context.Background(), clientTokenLookupPrefix+string(session.sessionData.ClientToken), sessionJSON, timeout).Err()
	if err != nil {
		return logAsRedisError(err)
	}

	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("session added or updated in Redis datastore")
	return nil
}

func (s *redisSessionStore) update(session *session) error {
	// Time passes between acquiring the lock and writing to Redis. Check before write action that lock is still valid.
	if session.lock == nil {
		return logAsRedisError(errors.Errorf("lock is not set for session with requestorToken %s", session.RequestorToken))
	} else if ttl, err := session.lock.TTL(context.Background()); err != nil {
		return logAsRedisError(err)
	} else if ttl == 0 {
		return logAsRedisError(errors.Errorf("no session lock available for session with requestorToken %s", session.RequestorToken))
	}
	return s.add(session)
}

func (s *redisSessionStore) lock(session *session) error {
	lock, err := s.locker.Obtain(context.Background(), lockPrefix+string(session.ClientToken), maxLockLifetime, lockingRetryOptions)
	if err != nil {
		// It is possible that the session is already locked. However, it should not happen often.
		// If you get the redislock.ErrNotObtained error often, you should investigate why.
		return logAsRedisError(err)
	}
	session.locked = true
	session.lock = lock
	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("session locked successfully")

	return nil
}

func (s *redisSessionStore) unlock(session *session) error {
	session.locked = false
	err := session.lock.Release(context.Background())
	if err == redislock.ErrLockNotHeld {
		s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Info("lock could not be released as the lock was not held")
		return nil
	} else if err != nil {
		return logAsRedisError(err)
	}
	s.conf.Logger.WithFields(logrus.Fields{"session": session.RequestorToken}).Debugf("session unlocked successfully")

	return nil
}

func (s *redisSessionStore) stop() {
	err := s.client.Close()
	if err != nil {
		_ = logAsRedisError(err)
	}
	s.conf.Logger.Info("Redis client closed successfully")
}

var one *big.Int = big.NewInt(1)

func (s *Server) newSession(action irma.Action, request irma.RequestorRequest, disclosed irma.AttributeConDisCon, FrontendAuth irma.FrontendAuthorization) (*session, error) {
	clientToken := irma.ClientToken(common.NewSessionToken())
	requestorToken := irma.RequestorToken(common.NewSessionToken())
	if len(FrontendAuth) == 0 {
		FrontendAuth = irma.FrontendAuthorization(common.NewSessionToken())
	}

	base := request.SessionRequest().Base()
	if s.conf.AugmentClientReturnURL && base.AugmentReturnURL && base.ClientReturnURL != "" {
		if strings.Contains(base.ClientReturnURL, "?") {
			base.ClientReturnURL += "&token=" + string(requestorToken)
		} else {
			base.ClientReturnURL += "?token=" + string(requestorToken)
		}
	}

	sd := sessionData{
		Action:         action,
		Rrequest:       request,
		LastActive:     time.Now(),
		RequestorToken: requestorToken,
		ClientToken:    clientToken,
		Status:         irma.ServerStatusInitialized,
		PrevStatus:     irma.ServerStatusInitialized,
		Result: &server.SessionResult{
			LegacySession: request.SessionRequest().Base().Legacy(),
			Token:         requestorToken,
			Type:          action,
			Status:        irma.ServerStatusInitialized,
		},
		Options: irma.SessionOptions{
			LDContext:     irma.LDContextSessionOptions,
			PairingMethod: irma.PairingMethodNone,
		},
		FrontendAuth:       FrontendAuth,
		ImplicitDisclosure: disclosed,
	}
	ses := &session{
		sessionData: sd,
		sessions:    s.sessions,
		sse:         s.serverSentEvents,
		conf:        s.conf,
		request:     request.SessionRequest(),
	}

	s.conf.Logger.WithFields(logrus.Fields{"session": ses.RequestorToken}).Debug("New session started")
	nonce, _ := gabi.GenerateNonce()
	base.Nonce = nonce
	base.Context = one

	err := s.sessions.add(ses)
	if err != nil {
		return nil, err
	}

	return ses, nil
}

func logAsRedisError(err error) error {
	return server.LogError(&RedisError{err})
}
<|endoftext|>


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

package tcpopt_test

import (
	"reflect"
	"runtime"
	"testing"
	"time"

	"github.com/mikioh/tcpopt"
)

func TestOption(t *testing.T) {
	opts := []tcpopt.Option{
		tcpopt.NoDelay(true),
		tcpopt.SendBuffer(1<<16 - 1),
		tcpopt.ReceiveBuffer(1<<16 - 1),
		tcpopt.KeepAlive(true),
	}
	switch runtime.GOOS {
	case "openbsd":
	case "windows":
		opts = append(opts, tcpopt.KeepAliveIdleInterval(1*time.Hour))
		opts = append(opts, tcpopt.KeepAliveProbeInterval(10*time.Minute))
	default:
		opts = append(opts, tcpopt.KeepAliveIdleInterval(1*time.Hour))
		opts = append(opts, tcpopt.KeepAliveProbeInterval(10*time.Minute))
		opts = append(opts, tcpopt.KeepAliveProbeCount(3))
	}
	switch runtime.GOOS {
	case "netbsd", "windows":
	default:
		opts = append(opts, tcpopt.Cork(true))
	}
	switch runtime.GOOS {
	case "darwin", "linux":
		opts = append(opts, tcpopt.NotSentLowWMK(1))
	}
	for _, o := range opts {
		if o.Level() == 0 {
			t.Fatalf("got %#x; want non-zero value", o.Level())
		}
		if o.Name() == 0 {
			t.Fatalf("got %#x; want non-zero value", o.Name())
		}
		b, err := o.Marshal()
		if err != nil {
			t.Fatal(err)
		}
		oo, err := tcpopt.Parse(o.Level(), o.Name(), b)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(oo, o) {
			t.Fatalf("got %#v; want %#v", oo, o)
		}
	}
}

const (
	testOptLevel = 0xfffe
	testOptName  = 0xfffd
)

type testOption struct{}

func (*testOption) Level() int                        { return testOptLevel }
func (*testOption) Name() int                         { return testOptName }
func (*testOption) Marshal() ([]byte, error)          { return make([]byte, 16), nil }
func parseTestOption(_ []byte) (tcpopt.Option, error) { return &testOption{}, nil }

func TestParse(t *testing.T) {
	var b [16]byte
	tcpopt.Register(testOptLevel, testOptName, parseTestOption)
	o, err := tcpopt.Parse(testOptLevel, testOptName, b[:])
	if err != nil {
		t.Fatal(err)
	}
	tcpopt.Unregister(testOptLevel, testOptName)
	o, err = tcpopt.Parse(testOptLevel, testOptName, b[:])
	if err == nil || o != nil {
		t.Fatalf("got %v, %v; want nil, error", o, err)
	}
}
<commit_msg>tcpopt: tweak test<commit_after>// Copyright 2016 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tcpopt_test

import (
	"reflect"
	"runtime"
	"testing"
	"time"

	"github.com/mikioh/tcpopt"
)

func TestOption(t *testing.T) {
	opts := []tcpopt.Option{
		tcpopt.NoDelay(true),
		tcpopt.SendBuffer(1<<16 - 1),
		tcpopt.ReceiveBuffer(1<<16 - 1),
		tcpopt.KeepAlive(true),
	}
	switch runtime.GOOS {
	case "openbsd":
	case "windows":
		opts = append(opts, tcpopt.KeepAliveIdleInterval(1*time.Hour))
		opts = append(opts, tcpopt.KeepAliveProbeInterval(10*time.Minute))
	default:
		opts = append(opts, tcpopt.KeepAliveIdleInterval(1*time.Hour))
		opts = append(opts, tcpopt.KeepAliveProbeInterval(10*time.Minute))
		opts = append(opts, tcpopt.KeepAliveProbeCount(3))
	}
	switch runtime.GOOS {
	case "netbsd", "windows":
	default:
		opts = append(opts, tcpopt.Cork(true))
	}
	switch runtime.GOOS {
	case "darwin", "linux":
		opts = append(opts, tcpopt.NotSentLowWMK(1))
	}

	for _, o := range opts {
		if o.Level() <= 0 {
			t.Fatalf("got %#x; want greater than zero", o.Level())
		}
		if o.Name() <= 0 {
			t.Fatalf("got %#x; want greater than zero", o.Name())
		}
		b, err := o.Marshal()
		if err != nil {
			t.Fatal(err)
		}
		oo, err := tcpopt.Parse(o.Level(), o.Name(), b)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(oo, o) {
			t.Fatalf("got %#v; want %#v", oo, o)
		}
	}
}

const (
	testOptLevel = 0xfff1
	testOptName  = 0xfff2
)

type testOption struct{}

func (*testOption) Level() int                        { return testOptLevel }
func (*testOption) Name() int                         { return testOptName }
func (*testOption) Marshal() ([]byte, error)          { return make([]byte, 16), nil }
func parseTestOption(_ []byte) (tcpopt.Option, error) { return &testOption{}, nil }

func TestParse(t *testing.T) {
	var b [16]byte
	tcpopt.Register(testOptLevel, testOptName, parseTestOption)
	o, err := tcpopt.Parse(testOptLevel, testOptName, b[:])
	if err != nil {
		t.Fatal(err)
	}
	tcpopt.Unregister(testOptLevel, testOptName)
	o, err = tcpopt.Parse(testOptLevel, testOptName, b[:])
	if err == nil || o != nil {
		t.Fatalf("got %v, %v; want nil, error", o, err)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/intervention-engine/fhir/models"
	"github.com/intervention-engine/fhir/search"
)

// ResourceController provides the necessary CRUD handlers for a given resource.
type ResourceController struct {
	Name string
	DAL  DataAccessLayer
}

// NewResourceController creates a new resource controller for the passed in resource name and the passed in
// DataAccessLayer.
func NewResourceController(name string, dal DataAccessLayer) *ResourceController {
	return &ResourceController{
		Name: name,
		DAL:  dal,
	}
}

// IndexHandler handles requests to list resource instances or search for them.
func (rc *ResourceController) IndexHandler(c *gin.Context) {
	defer func() {
		if r := recover(); r != nil {
			switch x := r.(type) {
			case *search.Error:
				c.JSON(x.HTTPStatus, x.OperationOutcome)
				return
			default:
				outcome := models.NewOperationOutcome("fatal", "exception", "")
				c.JSON(http.StatusInternalServerError, outcome)
				return
			}
		}
	}()

	searchQuery := search.Query{Resource: rc.Name, Query: c.Request.URL.RawQuery}
	baseURL := responseURL(c.Request, rc.Name)
	bundle, err := rc.DAL.Search(*baseURL, searchQuery)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set("bundle", bundle)
	c.Set("Resource", rc.Name)
	c.Set("Action", "search")

	c.JSON(http.StatusOK, bundle)
}

// LoadResource uses the resource id in the request to get a resource from the DataAccessLayer and store it in the
// context.
func (rc *ResourceController) LoadResource(c *gin.Context) (interface{}, error) {
	result, err := rc.DAL.Get(c.Param("id"), rc.Name)
	if err != nil {
		return nil, err
	}

	c.Set(rc.Name, result)
	c.Set("Resource", rc.Name)
	return result, nil
}

// ShowHandler handles requests to get a particular resource by ID.
func (rc *ResourceController) ShowHandler(c *gin.Context) {
	c.Set("Action", "read")
	_, err := rc.LoadResource(c)
	if err != nil && err != ErrNotFound {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	if err == ErrNotFound {
		c.Status(http.StatusNotFound)
		return
	}
	resource, _ := c.Get(rc.Name)
	c.JSON(http.StatusOK, resource)
}

// CreateHandler handles requests to create a new resource instance, assigning it a new ID.
func (rc *ResourceController) CreateHandler(c *gin.Context) {
	resource := models.NewStructForResourceName(rc.Name)
	err := FHIRBind(c, resource)
	if err != nil {
		oo := models.NewOperationOutcome("fatal", "exception", err.Error())
		c.JSON(http.StatusBadRequest, oo)
		return
	}

	id, err := rc.DAL.Post(resource)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set(rc.Name, resource)
	c.Set("Resource", rc.Name)
	c.Set("Action", "create")

	c.Header("Location", responseURL(c.Request, rc.Name, id).String())
	c.JSON(http.StatusCreated, resource)
}

// UpdateHandler handles requests to update a resource having a given ID.  If the resource with that ID does not
// exist, a new resource is created with that ID.
func (rc *ResourceController) UpdateHandler(c *gin.Context) {
	resource := models.NewStructForResourceName(rc.Name)
	err := FHIRBind(c, resource)
	if err != nil {
		oo := models.NewOperationOutcome("fatal", "exception", err.Error())
		c.JSON(http.StatusBadRequest, oo)
		return
	}

	createdNew, err := rc.DAL.Put(c.Param("id"), resource)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set(rc.Name, resource)
	c.Set("Resource", rc.Name)

	c.Header("Location", responseURL(c.Request, rc.Name, c.Param("id")).String())
	if createdNew {
		c.Set("Action", "create")
		c.JSON(http.StatusCreated, resource)
	} else {
		c.Set("Action", "update")
		c.JSON(http.StatusOK, resource)
	}
}

// ConditionalUpdateHandler handles requests for conditional updates.  These requests contain search criteria for the
// resource to update.  If the criteria results in no found resources, a new resource is created.  If the criteria
// results in one found resource, that resource will be updated.  Criteria resulting in more than one found resource
// is considered an error.
func (rc *ResourceController) ConditionalUpdateHandler(c *gin.Context) {
	resource := models.NewStructForResourceName(rc.Name)
	err := FHIRBind(c, resource)
	if err != nil {
		oo := models.NewOperationOutcome("fatal", "exception", err.Error())
		c.JSON(http.StatusBadRequest, oo)
		return
	}

	query := search.Query{Resource: rc.Name, Query: c.Request.URL.RawQuery}
	id, createdNew, err := rc.DAL.ConditionalPut(query, resource)
	if err == ErrMultipleMatches {
		c.AbortWithStatus(http.StatusPreconditionFailed)
		return
	} else if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set("Resource", rc.Name)

	c.Header("Location", responseURL(c.Request, rc.Name, id).String())
	c.Header("Access-Control-Allow-Origin", "*")
	if createdNew {
		c.Set("Action", "create")
		c.JSON(http.StatusCreated, resource)
	} else {
		c.Set("Action", "update")
		c.JSON(http.StatusOK, resource)
	}
}

// DeleteHandler handles requests to delete a resource instance identified by its ID.
func (rc *ResourceController) DeleteHandler(c *gin.Context) {
	id := c.Param("id")

	if err := rc.DAL.Delete(id, rc.Name); err != nil && err != ErrNotFound {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set(rc.Name, id)
	c.Set("Resource", rc.Name)
	c.Set("Action", "delete")

	c.Header("Access-Control-Allow-Origin", "*")
	c.Status(http.StatusNoContent)
}

// ConditionalDeleteHandler handles requests to delete resources identified by search criteria.  All resources
// matching the search criteria will be deleted.
func (rc *ResourceController) ConditionalDeleteHandler(c *gin.Context) {
	query := search.Query{Resource: rc.Name, Query: c.Request.URL.RawQuery}
	_, err := rc.DAL.ConditionalDelete(query)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set("Resource", rc.Name)
	c.Set("Action", "delete")

	c.Header("Access-Control-Allow-Origin", "*")
	c.Status(http.StatusNoContent)
}

func responseURL(r *http.Request, paths ...string) *url.URL {
	responseURL := url.URL{}
	if r.TLS == nil {
		responseURL.Scheme = "http"
	} else {
		responseURL.Scheme = "https"
	}
	responseURL.Host = r.Host
	responseURL.Path = fmt.Sprintf("/%s", strings.Join(paths, "/"))

	return &responseURL
}
<commit_msg>Removed headers<commit_after>package server

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/intervention-engine/fhir/models"
	"github.com/intervention-engine/fhir/search"
)

// ResourceController provides the necessary CRUD handlers for a given resource.
type ResourceController struct {
	Name string
	DAL  DataAccessLayer
}

// NewResourceController creates a new resource controller for the passed in resource name and the passed in
// DataAccessLayer.
func NewResourceController(name string, dal DataAccessLayer) *ResourceController {
	return &ResourceController{
		Name: name,
		DAL:  dal,
	}
}

// IndexHandler handles requests to list resource instances or search for them.
func (rc *ResourceController) IndexHandler(c *gin.Context) {
	defer func() {
		if r := recover(); r != nil {
			switch x := r.(type) {
			case *search.Error:
				c.JSON(x.HTTPStatus, x.OperationOutcome)
				return
			default:
				outcome := models.NewOperationOutcome("fatal", "exception", "")
				c.JSON(http.StatusInternalServerError, outcome)
				return
			}
		}
	}()

	searchQuery := search.Query{Resource: rc.Name, Query: c.Request.URL.RawQuery}
	baseURL := responseURL(c.Request, rc.Name)
	bundle, err := rc.DAL.Search(*baseURL, searchQuery)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set("bundle", bundle)
	c.Set("Resource", rc.Name)
	c.Set("Action", "search")

	c.JSON(http.StatusOK, bundle)
}

// LoadResource uses the resource id in the request to get a resource from the DataAccessLayer and store it in the
// context.
func (rc *ResourceController) LoadResource(c *gin.Context) (interface{}, error) {
	result, err := rc.DAL.Get(c.Param("id"), rc.Name)
	if err != nil {
		return nil, err
	}

	c.Set(rc.Name, result)
	c.Set("Resource", rc.Name)
	return result, nil
}

// ShowHandler handles requests to get a particular resource by ID.
func (rc *ResourceController) ShowHandler(c *gin.Context) {
	c.Set("Action", "read")
	_, err := rc.LoadResource(c)
	if err != nil && err != ErrNotFound {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	if err == ErrNotFound {
		c.Status(http.StatusNotFound)
		return
	}
	resource, _ := c.Get(rc.Name)
	c.JSON(http.StatusOK, resource)
}

// CreateHandler handles requests to create a new resource instance, assigning it a new ID.
func (rc *ResourceController) CreateHandler(c *gin.Context) {
	resource := models.NewStructForResourceName(rc.Name)
	err := FHIRBind(c, resource)
	if err != nil {
		oo := models.NewOperationOutcome("fatal", "exception", err.Error())
		c.JSON(http.StatusBadRequest, oo)
		return
	}

	id, err := rc.DAL.Post(resource)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set(rc.Name, resource)
	c.Set("Resource", rc.Name)
	c.Set("Action", "create")

	c.Header("Location", responseURL(c.Request, rc.Name, id).String())
	c.JSON(http.StatusCreated, resource)
}

// UpdateHandler handles requests to update a resource having a given ID.  If the resource with that ID does not
// exist, a new resource is created with that ID.
func (rc *ResourceController) UpdateHandler(c *gin.Context) {
	resource := models.NewStructForResourceName(rc.Name)
	err := FHIRBind(c, resource)
	if err != nil {
		oo := models.NewOperationOutcome("fatal", "exception", err.Error())
		c.JSON(http.StatusBadRequest, oo)
		return
	}

	createdNew, err := rc.DAL.Put(c.Param("id"), resource)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set(rc.Name, resource)
	c.Set("Resource", rc.Name)

	c.Header("Location", responseURL(c.Request, rc.Name, c.Param("id")).String())
	if createdNew {
		c.Set("Action", "create")
		c.JSON(http.StatusCreated, resource)
	} else {
		c.Set("Action", "update")
		c.JSON(http.StatusOK, resource)
	}
}

// ConditionalUpdateHandler handles requests for conditional updates.  These requests contain search criteria for the
// resource to update.  If the criteria results in no found resources, a new resource is created.  If the criteria
// results in one found resource, that resource will be updated.  Criteria resulting in more than one found resource
// is considered an error.
func (rc *ResourceController) ConditionalUpdateHandler(c *gin.Context) {
	resource := models.NewStructForResourceName(rc.Name)
	err := FHIRBind(c, resource)
	if err != nil {
		oo := models.NewOperationOutcome("fatal", "exception", err.Error())
		c.JSON(http.StatusBadRequest, oo)
		return
	}

	query := search.Query{Resource: rc.Name, Query: c.Request.URL.RawQuery}
	id, createdNew, err := rc.DAL.ConditionalPut(query, resource)
	if err == ErrMultipleMatches {
		c.AbortWithStatus(http.StatusPreconditionFailed)
		return
	} else if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set("Resource", rc.Name)

	c.Header("Location", responseURL(c.Request, rc.Name, id).String())
	if createdNew {
		c.Set("Action", "create")
		c.JSON(http.StatusCreated, resource)
	} else {
		c.Set("Action", "update")
		c.JSON(http.StatusOK, resource)
	}
}

// DeleteHandler handles requests to delete a resource instance identified by its ID.
func (rc *ResourceController) DeleteHandler(c *gin.Context) {
	id := c.Param("id")

	if err := rc.DAL.Delete(id, rc.Name); err != nil && err != ErrNotFound {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set(rc.Name, id)
	c.Set("Resource", rc.Name)
	c.Set("Action", "delete")

	c.Status(http.StatusNoContent)
}

// ConditionalDeleteHandler handles requests to delete resources identified by search criteria.  All resources
// matching the search criteria will be deleted.
func (rc *ResourceController) ConditionalDeleteHandler(c *gin.Context) {
	query := search.Query{Resource: rc.Name, Query: c.Request.URL.RawQuery}
	_, err := rc.DAL.ConditionalDelete(query)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	c.Set("Resource", rc.Name)
	c.Set("Action", "delete")

	c.Status(http.StatusNoContent)
}

func responseURL(r *http.Request, paths ...string) *url.URL {
	responseURL := url.URL{}
	if r.TLS == nil {
		responseURL.Scheme = "http"
	} else {
		responseURL.Scheme = "https"
	}
	responseURL.Host = r.Host
	responseURL.Path = fmt.Sprintf("/%s", strings.Join(paths, "/"))

	return &responseURL
}
<|endoftext|>


<commit_before>package httpservice

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"time"

	"github.com/herb-go/herb/service"
)

//Config http server config.
type Config struct {
	service.ListenerConfig
	service.TLSConfig
	//BaseURL http scheme and host."http://127.0.0.1:8000" for example.
	BaseURL string
	//ReadTimeoutInSecond http conn read time out.
	ReadTimeoutInSecond int64
	//ReadTimeoutInSecond http conn read Header time out.
	ReadHeaderTimeoutInSecond int64
	//WriteTimeoutInSecond http conn write time out.
	WriteTimeoutInSecond int64
	//IdleTimeoutInSecond conn idle time out.
	IdleTimeoutInSecond int64
	//MaxHeaderBytes max header length in bytes.
	MaxHeaderBytes int
}

func (c *Config) Clone() *Config {
	return &Config{
		ListenerConfig:            *c.ListenerConfig.Clone(),
		TLSConfig:                 *c.TLSConfig.Clone(),
		BaseURL:                   c.BaseURL,
		ReadTimeoutInSecond:       c.ReadTimeoutInSecond,
		ReadHeaderTimeoutInSecond: c.ReadHeaderTimeoutInSecond,
		WriteTimeoutInSecond:      c.WriteTimeoutInSecond,
		IdleTimeoutInSecond:       c.IdleTimeoutInSecond,
		MaxHeaderBytes:            c.MaxHeaderBytes,
	}
}

//IsEmpty check if config is empty
func (c *Config) IsEmpty() bool {
	if c == nil {
		return true
	}
	if c.Addr != "" {
		return false
	}
	if c.Net != "" {
		return false
	}
	if c.BaseURL != "" {
		return false
	}
	if c.ReadTimeoutInSecond != 0 {
		return false
	}
	if c.ReadHeaderTimeoutInSecond != 0 {
		return false
	}
	if c.WriteTimeoutInSecond != 0 {
		return false
	}
	if c.IdleTimeoutInSecond != 0 {
		return false
	}
	if c.MaxHeaderBytes != 0 {
		return false
	}
	if !c.TLS {
		return false
	}
	if c.TLSCertPath != "" {
		return false
	}
	if c.TLSKeyPath != "" {
		return false
	}
	return true
}

//Server create http server with config.
func (c *Config) Server() *http.Server {
	server := &http.Server{
		Addr:              c.Addr,
		ReadTimeout:       time.Duration(c.ReadTimeoutInSecond) * time.Second,
		ReadHeaderTimeout: time.Duration(c.ReadHeaderTimeoutInSecond) * time.Second,
		WriteTimeout:      time.Duration(c.WriteTimeoutInSecond) * time.Second,
		IdleTimeout:       time.Duration(c.IdleTimeoutInSecond) * time.Second,
		MaxHeaderBytes:    c.MaxHeaderBytes,
	}
	server.ErrorLog = log.New(ioutil.Discard, "", 0)
	return server
}

func (c *Config) Serve(h http.Handler) (*http.Server, error) {
	var err error
	l, err := net.Listen(c.Net, c.Addr)
	if err != nil {
		return nil, err
	}
	server := c.Server()
	server.Handler = h
	go func() {
		if !c.TLS {
			server.Serve(l)
		} else {
			server.ServeTLS(l, c.TLSCertPath, c.TLSKeyPath)
		}
	}()
	return server, nil

}

//NewConfig create new config.
func NewConfig() *Config {
	return &Config{}
}
<commit_msg>update<commit_after>package httpservice

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"time"

	"github.com/herb-go/herb/service"
)

//Config http server config.
type Config struct {
	service.ListenerConfig
	service.TLSConfig
	//Http server disabled
	Disabled bool
	//BaseURL http scheme and host."http://127.0.0.1:8000" for example.
	BaseURL string
	//ReadTimeoutInSecond http conn read time out.
	ReadTimeoutInSecond int64
	//ReadTimeoutInSecond http conn read Header time out.
	ReadHeaderTimeoutInSecond int64
	//WriteTimeoutInSecond http conn write time out.
	WriteTimeoutInSecond int64
	//IdleTimeoutInSecond conn idle time out.
	IdleTimeoutInSecond int64
	//MaxHeaderBytes max header length in bytes.
	MaxHeaderBytes int
}

func (c *Config) Clone() *Config {
	return &Config{
		ListenerConfig:            *c.ListenerConfig.Clone(),
		TLSConfig:                 *c.TLSConfig.Clone(),
		BaseURL:                   c.BaseURL,
		ReadTimeoutInSecond:       c.ReadTimeoutInSecond,
		ReadHeaderTimeoutInSecond: c.ReadHeaderTimeoutInSecond,
		WriteTimeoutInSecond:      c.WriteTimeoutInSecond,
		IdleTimeoutInSecond:       c.IdleTimeoutInSecond,
		MaxHeaderBytes:            c.MaxHeaderBytes,
	}
}

//IsEmpty check if config is empty
func (c *Config) IsEmpty() bool {
	if c == nil {
		return true
	}
	if c.Addr != "" {
		return false
	}
	if c.Net != "" {
		return false
	}
	if c.BaseURL != "" {
		return false
	}
	if c.ReadTimeoutInSecond != 0 {
		return false
	}
	if c.ReadHeaderTimeoutInSecond != 0 {
		return false
	}
	if c.WriteTimeoutInSecond != 0 {
		return false
	}
	if c.IdleTimeoutInSecond != 0 {
		return false
	}
	if c.MaxHeaderBytes != 0 {
		return false
	}
	if !c.TLS {
		return false
	}
	if c.TLSCertPath != "" {
		return false
	}
	if c.TLSKeyPath != "" {
		return false
	}
	return true
}

//Server create http server with config.
func (c *Config) Server() *http.Server {
	server := &http.Server{
		Addr:              c.Addr,
		ReadTimeout:       time.Duration(c.ReadTimeoutInSecond) * time.Second,
		ReadHeaderTimeout: time.Duration(c.ReadHeaderTimeoutInSecond) * time.Second,
		WriteTimeout:      time.Duration(c.WriteTimeoutInSecond) * time.Second,
		IdleTimeout:       time.Duration(c.IdleTimeoutInSecond) * time.Second,
		MaxHeaderBytes:    c.MaxHeaderBytes,
	}
	server.ErrorLog = log.New(ioutil.Discard, "", 0)
	return server
}

func (c *Config) Serve(h http.Handler) (*http.Server, error) {
	var err error
	l, err := net.Listen(c.Net, c.Addr)
	if err != nil {
		return nil, err
	}
	server := c.Server()
	server.Handler = h
	go func() {
		if !c.TLS {
			server.Serve(l)
		} else {
			server.ServeTLS(l, c.TLSCertPath, c.TLSKeyPath)
		}
	}()
	return server, nil

}

//NewConfig create new config.
func NewConfig() *Config {
	return &Config{}
}
<|endoftext|>


<commit_before>package tarantool

type packedPacketPool struct {
	queue chan *packedPacket
}

func NewPackedPacketPool() *packedPacketPool {
	return &packedPacketPool{
		queue: make(chan *packedPacket, 10240),
	}
}

func (p *packedPacketPool) Get() *packedPacket {
	select {
	case pp := <-p.queue:
		pp.pool = p
		pp.code = 0
		pp.requestID = 0
		pp.body = nil
		pp.buffer.Reset()
		return pp
	default:
		pp := &packedPacket{}
		return pp
	}
}

func (p *packedPacketPool) Put(pp *packedPacket) {
	pp.pool = nil
	p.queue <- pp
}

func (p *packedPacketPool) Close() {
	close(p.queue)
}
<commit_msg>Reduce packet pool size<commit_after>package tarantool

type packedPacketPool struct {
	queue chan *packedPacket
}

func NewPackedPacketPool() *packedPacketPool {
	return &packedPacketPool{
		queue: make(chan *packedPacket, 1024),
	}
}

func (p *packedPacketPool) Get() *packedPacket {
	select {
	case pp := <-p.queue:
		pp.pool = p
		pp.code = 0
		pp.requestID = 0
		pp.body = nil
		pp.buffer.Reset()
		return pp
	default:
		pp := &packedPacket{}
		return pp
	}
}

func (p *packedPacketPool) Put(pp *packedPacket) {
	pp.pool = nil
	p.queue <- pp
}

func (p *packedPacketPool) Close() {
	close(p.queue)
}
<|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.

// Common Playground functionality.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"net/http"
)

// The server that will service compile and share requests.
const playgroundBaseURL = "http://play.golang.org"

func registerPlaygroundHandlers(mux *http.ServeMux) {
	if *showPlayground {
		mux.HandleFunc("/compile", bounceToPlayground)
		mux.HandleFunc("/share", bounceToPlayground)
	} else {
		mux.HandleFunc("/compile", disabledHandler)
		mux.HandleFunc("/share", disabledHandler)
	}
	http.HandleFunc("/fmt", fmtHandler)
}

type fmtResponse struct {
	Body  string
	Error string
}

// fmtHandler takes a Go program in its "body" form value, formats it with
// standard gofmt formatting, and writes a fmtResponse as a JSON object.
func fmtHandler(w http.ResponseWriter, r *http.Request) {
	resp := new(fmtResponse)
	body, err := gofmt(r.FormValue("body"))
	if err != nil {
		resp.Error = err.Error()
	} else {
		resp.Body = body
	}
	json.NewEncoder(w).Encode(resp)
}

// gofmt takes a Go program, formats it using the standard Go formatting
// rules, and returns it or an error.
func gofmt(body string) (string, error) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "prog.go", body, parser.ParseComments)
	if err != nil {
		return "", err
	}
	ast.SortImports(fset, f)
	var buf bytes.Buffer
	err = printer.Fprint(&buf, fset, f)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}

// disabledHandler serves a 501 "Not Implemented" response.
func disabledHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusNotImplemented)
	fmt.Fprint(w, "This functionality is not available via local godoc.")
}
<commit_msg>cmd/godoc: use normal gofmt printer settings for playground fmt<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.

// Common Playground functionality.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"net/http"
)

// The server that will service compile and share requests.
const playgroundBaseURL = "http://play.golang.org"

func registerPlaygroundHandlers(mux *http.ServeMux) {
	if *showPlayground {
		mux.HandleFunc("/compile", bounceToPlayground)
		mux.HandleFunc("/share", bounceToPlayground)
	} else {
		mux.HandleFunc("/compile", disabledHandler)
		mux.HandleFunc("/share", disabledHandler)
	}
	http.HandleFunc("/fmt", fmtHandler)
}

type fmtResponse struct {
	Body  string
	Error string
}

// fmtHandler takes a Go program in its "body" form value, formats it with
// standard gofmt formatting, and writes a fmtResponse as a JSON object.
func fmtHandler(w http.ResponseWriter, r *http.Request) {
	resp := new(fmtResponse)
	body, err := gofmt(r.FormValue("body"))
	if err != nil {
		resp.Error = err.Error()
	} else {
		resp.Body = body
	}
	json.NewEncoder(w).Encode(resp)
}

// gofmt takes a Go program, formats it using the standard Go formatting
// rules, and returns it or an error.
func gofmt(body string) (string, error) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "prog.go", body, parser.ParseComments)
	if err != nil {
		return "", err
	}
	ast.SortImports(fset, f)
	var buf bytes.Buffer
	config := printer.Config{
		Mode:     printer.UseSpaces | printer.TabIndent,
		Tabwidth: 8,
	}
	err = config.Fprint(&buf, fset, f)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}

// disabledHandler serves a 501 "Not Implemented" response.
func disabledHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusNotImplemented)
	fmt.Fprint(w, "This functionality is not available via local godoc.")
}
<|endoftext|>


<commit_before>// Copyright 2014 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 main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var testData uint32

func checkSymbols(t *testing.T, nmoutput []byte) {
	var checkSymbolsFound, testDataFound bool
	scanner := bufio.NewScanner(bytes.NewBuffer(nmoutput))
	for scanner.Scan() {
		f := strings.Fields(scanner.Text())
		if len(f) < 3 {
			continue
		}
		switch f[2] {
		case "cmd/nm.checkSymbols":
			checkSymbolsFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", checkSymbols) {
				t.Errorf("nm shows wrong address %v for checkSymbols (%p)", addr, checkSymbols)
			}
		case "cmd/nm.testData":
			testDataFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", &testData) {
				t.Errorf("nm shows wrong address %v for testData (%p)", addr, &testData)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		t.Errorf("error while reading symbols: %v", err)
		return
	}
	if !checkSymbolsFound {
		t.Error("nm shows no checkSymbols symbol")
	}
	if !testDataFound {
		t.Error("nm shows no testData symbol")
	}
}

func TestNM(t *testing.T) {
	out, err := exec.Command("go", "build", "-o", "testnm.exe", "cmd/nm").CombinedOutput()
	if err != nil {
		t.Fatalf("go build -o testnm.exe cmd/nm: %v\n%s", err, string(out))
	}
	defer os.Remove("testnm.exe")

	testfiles := []string{
		"elf/testdata/gcc-386-freebsd-exec",
		"elf/testdata/gcc-amd64-linux-exec",
		"macho/testdata/gcc-386-darwin-exec",
		"macho/testdata/gcc-amd64-darwin-exec",
		"pe/testdata/gcc-amd64-mingw-exec",
		"pe/testdata/gcc-386-mingw-exec",
		"plan9obj/testdata/amd64-plan9-exec",
		"plan9obj/testdata/386-plan9-exec",
	}
	for _, f := range testfiles {
		exepath := filepath.Join(runtime.GOROOT(), "src", "pkg", "debug", f)
		cmd := exec.Command("./testnm.exe", exepath)
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out))
		}
	}

	cmd := exec.Command("./testnm.exe", os.Args[0])
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
	}
	checkSymbols(t, out)
}
<commit_msg>cmd/nm: do not write to GOROOT testdata directories during TestNM<commit_after>// Copyright 2014 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 main

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var testData uint32

func checkSymbols(t *testing.T, nmoutput []byte) {
	var checkSymbolsFound, testDataFound bool
	scanner := bufio.NewScanner(bytes.NewBuffer(nmoutput))
	for scanner.Scan() {
		f := strings.Fields(scanner.Text())
		if len(f) < 3 {
			continue
		}
		switch f[2] {
		case "cmd/nm.checkSymbols":
			checkSymbolsFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", checkSymbols) {
				t.Errorf("nm shows wrong address %v for checkSymbols (%p)", addr, checkSymbols)
			}
		case "cmd/nm.testData":
			testDataFound = true
			addr := "0x" + f[0]
			if addr != fmt.Sprintf("%p", &testData) {
				t.Errorf("nm shows wrong address %v for testData (%p)", addr, &testData)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		t.Errorf("error while reading symbols: %v", err)
		return
	}
	if !checkSymbolsFound {
		t.Error("nm shows no checkSymbols symbol")
	}
	if !testDataFound {
		t.Error("nm shows no testData symbol")
	}
}

func TestNM(t *testing.T) {
	tmpDir, err := ioutil.TempDir("", "TestNM")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}
	defer os.RemoveAll(tmpDir)

	testnmpath := filepath.Join(tmpDir, "testnm.exe")
	out, err := exec.Command("go", "build", "-o", testnmpath, "cmd/nm").CombinedOutput()
	if err != nil {
		t.Fatalf("go build -o %v cmd/nm: %v\n%s", testnmpath, err, string(out))
	}

	testfiles := []string{
		"elf/testdata/gcc-386-freebsd-exec",
		"elf/testdata/gcc-amd64-linux-exec",
		"macho/testdata/gcc-386-darwin-exec",
		"macho/testdata/gcc-amd64-darwin-exec",
		"pe/testdata/gcc-amd64-mingw-exec",
		"pe/testdata/gcc-386-mingw-exec",
		"plan9obj/testdata/amd64-plan9-exec",
		"plan9obj/testdata/386-plan9-exec",
	}
	for _, f := range testfiles {
		exepath := filepath.Join(runtime.GOROOT(), "src", "pkg", "debug", f)
		cmd := exec.Command(testnmpath, exepath)
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out))
		}
	}

	cmd := exec.Command(testnmpath, os.Args[0])
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
	}
	checkSymbols(t, out)
}
<|endoftext|>


<commit_before>// +build linux darwin

/*
Copyright 2012 Google Inc.

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 fs

import (
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/schema"
	"camlistore.org/pkg/search"
	"camlistore.org/pkg/syncutil"
	"camlistore.org/third_party/code.google.com/p/rsc/fuse"
)

const refreshTime = 1 * time.Minute

type rootsDir struct {
	fs *CamliFileSystem

	mu        sync.Mutex // guards following
	lastQuery time.Time
	m         map[string]blob.Ref // ent name => permanode
}

func (n *rootsDir) Attr() fuse.Attr {
	return fuse.Attr{
		Mode: os.ModeDir | 0700,
		Uid:  uint32(os.Getuid()),
		Gid:  uint32(os.Getgid()),
	}
}

func (n *rootsDir) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) {
	n.mu.Lock()
	defer n.mu.Unlock()
	if err := n.condRefresh(); err != nil {
		return nil, fuse.EIO
	}
	var ents []fuse.Dirent
	for name := range n.m {
		ents = append(ents, fuse.Dirent{Name: name})
	}
	return ents, nil
}

func (n *rootsDir) Remove(req *fuse.RemoveRequest, intr fuse.Intr) fuse.Error {
	n.mu.Lock()
	defer n.mu.Unlock()

	if err := n.condRefresh(); err != nil {
		return err
	}
	br := n.m[req.Name]
	if !br.Valid() {
		return fuse.ENOENT
	}

	claim := schema.NewDelAttributeClaim(br, "camliRoot", "")
	_, err := n.fs.client.UploadAndSignBlob(claim)
	if err != nil {
		log.Println("rootsDir.Remove:", err)
		return fuse.EIO
	}

	delete(n.m, req.Name)

	return nil
}

func (n *rootsDir) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) {
	log.Printf("fs.roots: Lookup(%q)", name)
	n.mu.Lock()
	defer n.mu.Unlock()
	if err := n.condRefresh(); err != nil {
		return nil, err
	}
	br := n.m[name]
	if !br.Valid() {
		return nil, fuse.ENOENT
	}
	nod := &mutDir{
		fs:        n.fs,
		permanode: br,
		name:      name,
	}
	return nod, nil
}

// requires n.mu is held
func (n *rootsDir) condRefresh() fuse.Error {
	if n.lastQuery.After(time.Now().Add(-refreshTime)) {
		return nil
	}
	log.Printf("fs.roots: querying")

	var rootRes, impRes *search.WithAttrResponse
	var grp syncutil.Group
	grp.Go(func() (err error) {
		rootRes, err = n.fs.client.GetPermanodesWithAttr(&search.WithAttrRequest{N: 100, Attr: "camliRoot"})
		return
	})
	grp.Go(func() (err error) {
		impRes, err = n.fs.client.GetPermanodesWithAttr(&search.WithAttrRequest{N: 100, Attr: "camliImportRoot"})
		return
	})
	if err := grp.Err(); err != nil {
		log.Printf("fs.recent: GetRecentPermanodes error in ReadDir: %v", err)
		return fuse.EIO
	}

	dr := &search.DescribeRequest{
		Depth: 1,
	}
	for _, wi := range rootRes.WithAttr {
		dr.BlobRefs = append(dr.BlobRefs, wi.Permanode)
	}
	for _, wi := range impRes.WithAttr {
		dr.BlobRefs = append(dr.BlobRefs, wi.Permanode)
	}
	dres, err := n.fs.client.Describe(dr)
	if err != nil {
		log.Printf("Describe failure: %v", err)
		return fuse.EIO
	}

	n.m = make(map[string]blob.Ref)

	// Roots
	for _, wi := range rootRes.WithAttr {
		pn := wi.Permanode
		db := dres.Meta[pn.String()]
		if db != nil && db.Permanode != nil {
			name := db.Permanode.Attr.Get("camliRoot")
			if name != "" {
				n.m[name] = pn
			}
		}
	}

	// Importers (mapped as roots for now)
	for _, wi := range impRes.WithAttr {
		pn := wi.Permanode
		db := dres.Meta[pn.String()]
		if db != nil && db.Permanode != nil {
			name := db.Permanode.Attr.Get("camliImportRoot")
			if name != "" {
				name = strings.Replace(name, ":", "-", -1)
				name = strings.Replace(name, "/", "-", -1)
				n.m["importer-"+name] = pn
			}
		}
	}

	n.lastQuery = time.Now()
	return nil
}

func (n *rootsDir) Mkdir(req *fuse.MkdirRequest, intr fuse.Intr) (fuse.Node, fuse.Error) {
	name := req.Name

	// Create a Permanode for the root.
	pr, err := n.fs.client.UploadNewPermanode()
	if err != nil {
		log.Printf("rootsDir.Create(%q): %v", name, err)
		return nil, fuse.EIO
	}

	// Add a camliRoot attribute to the root permanode.
	claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliRoot", name)
	_, err = n.fs.client.UploadAndSignBlob(claim)
	if err != nil {
		log.Printf("rootsDir.Create(%q): %v", name, err)
		return nil, fuse.EIO
	}

	nod := &mutDir{
		fs:        n.fs,
		permanode: pr.BlobRef,
		name:      name,
	}
	n.mu.Lock()
	n.m[name] = pr.BlobRef
	n.mu.Unlock()

	return nod, nil
}
<commit_msg>FUSE: support renaming roots<commit_after>// +build linux darwin

/*
Copyright 2012 Google Inc.

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 fs

import (
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/schema"
	"camlistore.org/pkg/search"
	"camlistore.org/pkg/syncutil"
	"camlistore.org/third_party/code.google.com/p/rsc/fuse"
)

const refreshTime = 1 * time.Minute

type rootsDir struct {
	fs *CamliFileSystem

	mu        sync.Mutex // guards following
	lastQuery time.Time
	m         map[string]blob.Ref // ent name => permanode
}

func (n *rootsDir) Attr() fuse.Attr {
	return fuse.Attr{
		Mode: os.ModeDir | 0700,
		Uid:  uint32(os.Getuid()),
		Gid:  uint32(os.Getgid()),
	}
}

func (n *rootsDir) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) {
	n.mu.Lock()
	defer n.mu.Unlock()
	if err := n.condRefresh(); err != nil {
		return nil, fuse.EIO
	}
	var ents []fuse.Dirent
	for name := range n.m {
		ents = append(ents, fuse.Dirent{Name: name})
	}
	return ents, nil
}

func (n *rootsDir) Remove(req *fuse.RemoveRequest, intr fuse.Intr) fuse.Error {
	n.mu.Lock()
	defer n.mu.Unlock()

	if err := n.condRefresh(); err != nil {
		return err
	}
	br := n.m[req.Name]
	if !br.Valid() {
		return fuse.ENOENT
	}

	claim := schema.NewDelAttributeClaim(br, "camliRoot", "")
	_, err := n.fs.client.UploadAndSignBlob(claim)
	if err != nil {
		log.Println("rootsDir.Remove:", err)
		return fuse.EIO
	}

	delete(n.m, req.Name)

	return nil
}

func (n *rootsDir) Rename(req *fuse.RenameRequest, newDir fuse.Node, intr fuse.Intr) fuse.Error {
	log.Printf("rootsDir.Rename %q -> %q", req.OldName, req.NewName)
	n.mu.Lock()
	target, exists := n.m[req.OldName]
	_, collision := n.m[req.NewName]
	n.mu.Unlock()
	if !exists {
		log.Printf("*rootsDir.Rename src name %q isn't known", req.OldName)
		return fuse.ENOENT
	}
	if collision {
		log.Printf("*rootsDir.Rename dest %q already exists", req.NewName)
		return fuse.EIO
	}

	// Don't allow renames if the root contains content.  Rename
	// is mostly implemented to make GUIs that create directories
	// before asking for the directory name.
	res, err := n.fs.client.Describe(&search.DescribeRequest{BlobRef: target})
	if err != nil {
		log.Println("rootsDir.Rename:", err)
		return fuse.EIO
	}
	db := res.Meta[target.String()]
	if db == nil {
		log.Printf("Failed to pull meta for target: %v", target)
		return fuse.EIO
	}

	for k := range db.Permanode.Attr {
		const p = "camliPath:"
		if strings.HasPrefix(k, p) {
			log.Printf("Found file in %q: %q, disallowing rename", req.OldName, k[len(p):])
			return fuse.EIO
		}
	}

	claim := schema.NewSetAttributeClaim(target, "camliRoot", req.NewName)
	_, err = n.fs.client.UploadAndSignBlob(claim)
	if err != nil {
		log.Printf("Upload rename link error: %v", err)
		return fuse.EIO
	}

	// Comment transplanted from mutDir.Rename
	// TODO(bradfitz): this locking would be racy, if the kernel
	// doesn't do it properly. (It should) Let's just trust the
	// kernel for now. Later we can verify and remove this
	// comment.
	n.mu.Lock()
	if n.m[req.OldName] != target {
		panic("Race.")
	}
	delete(n.m, req.OldName)
	n.m[req.NewName] = target
	n.mu.Unlock()

	return nil
}

func (n *rootsDir) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) {
	log.Printf("fs.roots: Lookup(%q)", name)
	n.mu.Lock()
	defer n.mu.Unlock()
	if err := n.condRefresh(); err != nil {
		return nil, err
	}
	br := n.m[name]
	if !br.Valid() {
		return nil, fuse.ENOENT
	}
	nod := &mutDir{
		fs:        n.fs,
		permanode: br,
		name:      name,
	}
	return nod, nil
}

// requires n.mu is held
func (n *rootsDir) condRefresh() fuse.Error {
	if n.lastQuery.After(time.Now().Add(-refreshTime)) {
		return nil
	}
	log.Printf("fs.roots: querying")

	var rootRes, impRes *search.WithAttrResponse
	var grp syncutil.Group
	grp.Go(func() (err error) {
		rootRes, err = n.fs.client.GetPermanodesWithAttr(&search.WithAttrRequest{N: 100, Attr: "camliRoot"})
		return
	})
	grp.Go(func() (err error) {
		impRes, err = n.fs.client.GetPermanodesWithAttr(&search.WithAttrRequest{N: 100, Attr: "camliImportRoot"})
		return
	})
	if err := grp.Err(); err != nil {
		log.Printf("fs.recent: GetRecentPermanodes error in ReadDir: %v", err)
		return fuse.EIO
	}

	dr := &search.DescribeRequest{
		Depth: 1,
	}
	for _, wi := range rootRes.WithAttr {
		dr.BlobRefs = append(dr.BlobRefs, wi.Permanode)
	}
	for _, wi := range impRes.WithAttr {
		dr.BlobRefs = append(dr.BlobRefs, wi.Permanode)
	}
	dres, err := n.fs.client.Describe(dr)
	if err != nil {
		log.Printf("Describe failure: %v", err)
		return fuse.EIO
	}

	n.m = make(map[string]blob.Ref)

	// Roots
	for _, wi := range rootRes.WithAttr {
		pn := wi.Permanode
		db := dres.Meta[pn.String()]
		if db != nil && db.Permanode != nil {
			name := db.Permanode.Attr.Get("camliRoot")
			if name != "" {
				n.m[name] = pn
			}
		}
	}

	// Importers (mapped as roots for now)
	for _, wi := range impRes.WithAttr {
		pn := wi.Permanode
		db := dres.Meta[pn.String()]
		if db != nil && db.Permanode != nil {
			name := db.Permanode.Attr.Get("camliImportRoot")
			if name != "" {
				name = strings.Replace(name, ":", "-", -1)
				name = strings.Replace(name, "/", "-", -1)
				n.m["importer-"+name] = pn
			}
		}
	}

	n.lastQuery = time.Now()
	return nil
}

func (n *rootsDir) Mkdir(req *fuse.MkdirRequest, intr fuse.Intr) (fuse.Node, fuse.Error) {
	name := req.Name

	// Create a Permanode for the root.
	pr, err := n.fs.client.UploadNewPermanode()
	if err != nil {
		log.Printf("rootsDir.Create(%q): %v", name, err)
		return nil, fuse.EIO
	}

	// Add a camliRoot attribute to the root permanode.
	claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliRoot", name)
	_, err = n.fs.client.UploadAndSignBlob(claim)
	if err != nil {
		log.Printf("rootsDir.Create(%q): %v", name, err)
		return nil, fuse.EIO
	}

	nod := &mutDir{
		fs:        n.fs,
		permanode: pr.BlobRef,
		name:      name,
	}
	n.mu.Lock()
	n.m[name] = pr.BlobRef
	n.mu.Unlock()

	return nod, nil
}
<|endoftext|>


<commit_before>package updaters

import (
	"fmt"
	"github.com/robmerrell/vtcboard/models"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Network struct{}

var networkBaseUrl = "http://explorer.vertcoin.org/chain/Vertcoin/q"

// Update retrieves VTC netork information from a blockchain api.
func (n *Network) Update() error {
	hashRate, err := getHashRate()
	if err != nil {
		return err
	}

	diff, err := getDifficulty()
	if err != nil {
		return err
	}

	mined, err := getMined()
	if err != nil {
		return err
	}

	blockCount, err := getBlockCount()
	if err != nil {
		return err
	}

	conn := models.CloneConnection()
	defer conn.Close()

	network := &models.Network{
		HashRate:    hashRate,
		Difficulty:  diff,
		Mined:       mined,
		BlockCount:  blockCount,
		GeneratedAt: time.Now().UTC(),
	}
	return network.Insert(conn)
}

// networkQuery queryies the network api at the given url.
func networkQuery(url string) (string, error) {
	resp, err := http.Get(networkBaseUrl + url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	trimmed := strings.TrimSpace(string(body))

	return trimmed, nil
}

// getHashRate gets the current hash rate and converts into a human readable number.
func getHashRate() (string, error) {
	hash, err := networkQuery("/nethash/120/-121/-1")
	if err != nil {
		return "", err
	}

	split := strings.Split(hash, "\n")
	info := strings.TrimSpace(split[len(split)-1])

	nethashSplit := strings.Split(info, ",")
	nethash := nethashSplit[len(nethashSplit)-1]

	converted, err := strconv.ParseFloat(nethash, 64)
	if err != nil {
		return "", err
	}

	mhs := converted / 1000000

	return fmt.Sprintf("%.2f", mhs), nil
}

// getDifficulty gets the current network difficulty
func getDifficulty() (string, error) {
	return networkQuery("/getdifficulty")
}

// getMined gets the total number of mined coins
func getMined() (string, error) {
	mined, err := networkQuery("/totalbc")
	if err != nil {
		return "", err
	}

	converted, err := strconv.ParseFloat(mined, 64)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%.0f", converted), nil
}

// getBlockCount gets the current block count
func getBlockCount() (string, error) {
	return networkQuery("/getblockcount")
}
<commit_msg>use a different block explorer because cloudflare is blocking api traffic to explorer.vertcoin.org<commit_after>package updaters

import (
	"fmt"
	"github.com/robmerrell/vtcboard/models"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Network struct{}

// var networkBaseUrl = "http://explorer.vertcoin.org/chain/Vertcoin/q"
var networkBaseUrl = "http://cryptexplorer.com/chain/VertCoin/q"

// Update retrieves VTC netork information from a blockchain api.
func (n *Network) Update() error {
	hashRate, err := getHashRate()
	if err != nil {
		return err
	}

	diff, err := getDifficulty()
	if err != nil {
		return err
	}

	mined, err := getMined()
	if err != nil {
		return err
	}

	blockCount, err := getBlockCount()
	if err != nil {
		return err
	}

	conn := models.CloneConnection()
	defer conn.Close()

	network := &models.Network{
		HashRate:    hashRate,
		Difficulty:  diff,
		Mined:       mined,
		BlockCount:  blockCount,
		GeneratedAt: time.Now().UTC(),
	}
	return network.Insert(conn)
}

// networkQuery queryies the network api at the given url.
func networkQuery(url string) (string, error) {
	resp, err := http.Get(networkBaseUrl + url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	trimmed := strings.TrimSpace(string(body))

	return trimmed, nil
}

// getHashRate gets the current hash rate and converts into a human readable number.
func getHashRate() (string, error) {
	hash, err := networkQuery("/nethash/120/-121/-1")
	if err != nil {
		return "", err
	}

	split := strings.Split(hash, "\n")
	info := strings.TrimSpace(split[len(split)-1])

	nethashSplit := strings.Split(info, ",")
	nethash := nethashSplit[len(nethashSplit)-1]

	converted, err := strconv.ParseFloat(nethash, 64)
	if err != nil {
		return "", err
	}

	mhs := converted / 1000000

	return fmt.Sprintf("%.2f", mhs), nil
}

// getDifficulty gets the current network difficulty
func getDifficulty() (string, error) {
	return networkQuery("/getdifficulty")
}

// getMined gets the total number of mined coins
func getMined() (string, error) {
	mined, err := networkQuery("/totalbc")
	if err != nil {
		return "", err
	}

	converted, err := strconv.ParseFloat(mined, 64)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%.0f", converted), nil
}

// getBlockCount gets the current block count
func getBlockCount() (string, error) {
	return networkQuery("/getblockcount")
}
<|endoftext|>


<commit_before>// +build json

package internet

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"strings"

	v2net "github.com/v2ray/v2ray-core/common/net"
)

func (this *TLSSettings) UnmarshalJSON(data []byte) error {
	type JSONCertConfig struct {
		CertFile string `json:"certificateFile"`
		KeyFile  string `json:"keyFile"`
	}
	type JSONConfig struct {
		Insecure bool              `json:"allowInsecure"`
		Certs    []*JSONCertConfig `json:"certificates"`
	}
	jsonConfig := new(JSONConfig)
	if err := json.Unmarshal(data, jsonConfig); err != nil {
		return err
	}
	this.Certs = make([]tls.Certificate, len(jsonConfig.Certs))
	for idx, certConf := range jsonConfig.Certs {
		cert, err := tls.LoadX509KeyPair(certConf.CertFile, certConf.KeyFile)
		if err != nil {
			return errors.New("Internet|TLS: Failed to load certificate file: " + err.Error())
		}
		this.Certs[idx] = cert
	}
	this.AllowInsecure = jsonConfig.Insecure
	return nil
}

func (this *StreamSettings) UnmarshalJSON(data []byte) error {
	type JSONConfig struct {
		Network     v2net.NetworkList `json:"network"`
		Security    string            `json:"security"`
		TLSSettings *TLSSettings      `json:"tlsSettings"`
	}
	this.Type = StreamConnectionTypeRawTCP
	jsonConfig := new(JSONConfig)
	if err := json.Unmarshal(data, jsonConfig); err != nil {
		return err
	}
	if jsonConfig.Network.HasNetwork(v2net.KCPNetwork) {
		this.Type |= StreamConnectionTypeKCP
	}
	if jsonConfig.Network.HasNetwork(v2net.TCPNetwork) {
		this.Type |= StreamConnectionTypeTCP
	}
	this.Security = StreamSecurityTypeNone
	if strings.ToLower(jsonConfig.Security) == "tls" {
		this.Security = StreamSecurityTypeTLS
	}
	if jsonConfig.TLSSettings != nil {
		this.TLSSettings = jsonConfig.TLSSettings
	}
	return nil
}
<commit_msg>Load Websocket Streamtype from json<commit_after>// +build json

package internet

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"strings"

	v2net "github.com/v2ray/v2ray-core/common/net"
)

func (this *TLSSettings) UnmarshalJSON(data []byte) error {
	type JSONCertConfig struct {
		CertFile string `json:"certificateFile"`
		KeyFile  string `json:"keyFile"`
	}
	type JSONConfig struct {
		Insecure bool              `json:"allowInsecure"`
		Certs    []*JSONCertConfig `json:"certificates"`
	}
	jsonConfig := new(JSONConfig)
	if err := json.Unmarshal(data, jsonConfig); err != nil {
		return err
	}
	this.Certs = make([]tls.Certificate, len(jsonConfig.Certs))
	for idx, certConf := range jsonConfig.Certs {
		cert, err := tls.LoadX509KeyPair(certConf.CertFile, certConf.KeyFile)
		if err != nil {
			return errors.New("Internet|TLS: Failed to load certificate file: " + err.Error())
		}
		this.Certs[idx] = cert
	}
	this.AllowInsecure = jsonConfig.Insecure
	return nil
}

func (this *StreamSettings) UnmarshalJSON(data []byte) error {
	type JSONConfig struct {
		Network     v2net.NetworkList `json:"network"`
		Security    string            `json:"security"`
		TLSSettings *TLSSettings      `json:"tlsSettings"`
	}
	this.Type = StreamConnectionTypeRawTCP
	jsonConfig := new(JSONConfig)
	if err := json.Unmarshal(data, jsonConfig); err != nil {
		return err
	}
	if jsonConfig.Network.HasNetwork(v2net.KCPNetwork) {
		this.Type |= StreamConnectionTypeKCP
	}
	if jsonConfig.Network.HasNetwork(v2net.WSNetwork) {
		this.Type |= StreamConnectionTypeWebSocket
	}
	if jsonConfig.Network.HasNetwork(v2net.TCPNetwork) {
		this.Type |= StreamConnectionTypeTCP
	}
	this.Security = StreamSecurityTypeNone
	if strings.ToLower(jsonConfig.Security) == "tls" {
		this.Security = StreamSecurityTypeTLS
	}
	if jsonConfig.TLSSettings != nil {
		this.TLSSettings = jsonConfig.TLSSettings
	}
	return nil
}
<|endoftext|>


<commit_before>package index

import (
	"bytes"
	"context"
	"io"
	"strings"

	"github.com/gogo/protobuf/proto"
	"github.com/pachyderm/pachyderm/src/client/pkg/grpcutil"
	"github.com/pachyderm/pachyderm/src/server/pkg/obj"
	"github.com/pachyderm/pachyderm/src/server/pkg/storage/chunk"
	"github.com/pachyderm/pachyderm/src/server/pkg/storage/fileset/tar"
	"modernc.org/mathutil"
)

type levelReader struct {
	cr               *chunk.Reader
	tr               *tar.Reader
	currHdr, peekHdr *Header
}

type pathFilter struct {
	pathRange *PathRange
	prefix    string
}

// Reader is used for reading a multi-level index.
type Reader struct {
	ctx    context.Context
	objC   obj.Client
	chunks *chunk.Storage
	path   string
	filter *pathFilter
	levels []*levelReader
	done   bool
}

// NewReader create a new Reader.
func NewReader(ctx context.Context, objC obj.Client, chunks *chunk.Storage, path string, opts ...Option) *Reader {
	r := &Reader{
		ctx:    ctx,
		objC:   objC,
		chunks: chunks,
		path:   path,
	}
	for _, opt := range opts {
		opt(r)
	}
	return r
}

// Next gets the next header in the index.
func (r *Reader) Next() (*Header, error) {
	if err := r.setupLevels(); err != nil {
		return nil, err
	}
	if r.levels[0].peekHdr != nil {
		hdr := r.levels[0].peekHdr
		r.levels[0].peekHdr = nil
		return hdr, nil
	}
	if r.done {
		return nil, io.EOF
	}
	return r.next(len(r.levels) - 1)
}

func (r *Reader) setupLevels() error {
	if r.levels != nil {
		return nil
	}
	// Setup top level.
	objR, err := r.objC.Reader(r.ctx, r.path, 0, 0)
	if err != nil {
		return err
	}
	buf := &bytes.Buffer{}
	if _, err := io.Copy(buf, objR); err != nil {
		return err
	}
	if err := objR.Close(); err != nil {
		return err
	}
	r.levels = []*levelReader{&levelReader{tr: tar.NewReader(buf)}}
	// Traverse until we reach the first entry in the lowest level.
	for {
		hdr, err := r.peek(len(r.levels) - 1)
		if err != nil {
			return err
		}
		// Return when we are at the lowest level.
		if hdr.Hdr.Typeflag == indexType {
			return nil
		}
		_, err = r.next(len(r.levels) - 1)
		if err != nil {
			return err
		}
		// Setup next level.
		// (bryce) this whole process of reading at the offset is janky, needs to be re-thought.
		cr := r.chunks.NewReader(r.ctx, r.callback(len(r.levels)))
		dataRef := hdr.Idx.DataOp.DataRefs[0]
		dataRef.OffsetBytes = hdr.Idx.Range.Offset
		dataRef.SizeBytes -= hdr.Idx.Range.Offset
		cr.NextRange([]*chunk.DataRef{dataRef})
		r.levels = append(r.levels, &levelReader{
			cr: cr,
			tr: tar.NewReader(cr),
		})
	}
}

func (r *Reader) peek(level int) (*Header, error) {
	l := r.levels[level]
	if l.peekHdr != nil {
		return l.peekHdr, nil
	}
	var err error
	l.peekHdr, err = r.next(level)
	return l.peekHdr, err
}

func (r *Reader) next(level int) (*Header, error) {
	l := r.levels[level]
	if l.peekHdr != nil {
		hdr := l.peekHdr
		l.peekHdr = nil
		return hdr, nil
	}
	for {
		hdr, err := l.tr.Next()
		if err != nil {
			return nil, err
		}
		// Return if done.
		if r.atEnd(hdr.Name) {
			r.done = true
			return nil, io.EOF
		}
		// Handle lowest level index.
		if hdr.Typeflag == indexType {
			// Skip to the starting header.
			if !r.atStart(hdr.Name) {
				continue
			}
			return deserialize(l.tr, hdr)
		}
		// Handle index level above lowest.
		fullHdr, err := deserialize(l.tr, hdr)
		if err != nil {
			return nil, err
		}
		// Skip to the starting header.
		if !r.atStart(fullHdr.Idx.Range.LastPath) {
			continue
		}
		return fullHdr, nil
	}
}

func (r *Reader) atStart(name string) bool {
	if r.filter == nil {
		return true
	}
	if r.filter.pathRange != nil {
		return name >= r.filter.pathRange.Lower
	}
	return name >= r.filter.prefix
}

func (r *Reader) atEnd(name string) bool {
	if r.filter == nil {
		return false
	}
	if r.filter.pathRange != nil {
		return name > r.filter.pathRange.Upper
	}
	// Name is past a prefix when the first len(prefix) bytes are greater than the prefix
	// (use len(name) bytes for comparison when len(name) < len(prefix)).
	// Cannot use a simple greater than check (for paths a, ab, abc, and b, prefix a should return a, ab, abc).
	cmpSize := mathutil.Min(len(name), len(r.filter.prefix))
	return name[:cmpSize] > r.filter.prefix[:cmpSize]
}

// Peek peeks ahead in the index.
func (r *Reader) Peek() (*Header, error) {
	if err := r.setupLevels(); err != nil {
		return nil, err
	}
	if r.levels[0].peekHdr != nil {
		return r.levels[0].peekHdr, nil
	}
	var err error
	r.levels[0].peekHdr, err = r.Next()
	return r.levels[0].peekHdr, err
}

func (r *Reader) callback(level int) chunk.ReaderFunc {
	return func() ([]*chunk.DataRef, error) {
		hdr, err := r.next(level - 1)
		if err != nil {
			return nil, err
		}
		r.levels[level-1].currHdr = hdr
		return hdr.Idx.DataOp.DataRefs, nil
	}
}

// Copy is the basic data structure to represent a copy of data from
// a reader to a writer.
type Copy struct {
	level int
	raw   *chunk.Copy
	hdrs  []*Header
}

// ReadCopyFunc returns a function for copying data from the reader.
func (r *Reader) ReadCopyFunc(pathBound ...string) func() (*Copy, error) {
	level := -1
	var offset int64
	var done bool
	return func() (*Copy, error) {
		if done {
			return nil, io.EOF
		}
		// Setup levels, initialize first level.
		if err := r.setupLevels(); err != nil {
			return nil, err
		}
		if level < 0 {
			level = len(r.levels) - 1
		}
		c := &Copy{level: r.wLevel(level)}
		cr := r.levels[level].cr
		// Handle index entries that span multiple chunks.
		if offset > 0 {
			raw, err := cr.ReadCopy(offset)
			if err != nil {
				return nil, err
			}
			c.raw = raw
		}
		// (bryce) this is janky, but we need the current header when copying a level above.
		if r.levels[level].currHdr != nil {
			r.levels[level].peekHdr, r.levels[level].currHdr = r.levels[level].currHdr, nil
		}
		// While not past a split point, get index entries to copy.
		pastSplit := false
		cr.OnSplit(func() { pastSplit = true })
		for !pastSplit {
			hdr, err := r.peek(level)
			if err != nil {
				if err == io.EOF {
					done = true
					return c, nil
				}
				return nil, err
			}
			// Stop copying when the last referenced (directly or indirectly) content
			// chunk has a path that is >= the path bound.
			if !BeforeBound(hdr.Idx.LastPathChunk, pathBound...) {
				if hdr.Idx.Range == nil {
					done = true
					return c, nil
				}
				level++
				offset = hdr.Idx.Range.Offset
				return c, nil
			}
			c.hdrs = append(c.hdrs, hdr)
			_, err = r.next(level)
			if err != nil {
				return nil, err
			}
		}
		level--
		return c, nil
	}
}

func (r *Reader) wLevel(rLevel int) int {
	return len(r.levels) - 1 - rLevel
}

// BeforeBound checks if the passed in string is before the string bound (exclusive).
// The string bound is optional, so if no string bound is passed then it returns true.
func BeforeBound(str string, strBound ...string) bool {
	return len(strBound) == 0 || strings.Compare(str, strBound[0]) < 0
}

// Close closes the reader.
func (r *Reader) Close() error {
	for i := 1; i < len(r.levels); i++ {
		if err := r.levels[i].cr.Close(); err != nil {
			return err
		}
	}
	return nil
}

func deserialize(tr *tar.Reader, hdr *tar.Header) (*Header, error) {
	data := &bytes.Buffer{}
	buf := grpcutil.GetBuffer()
	defer grpcutil.PutBuffer(buf)
	if _, err := io.CopyBuffer(data, tr, buf); err != nil {
		return nil, err
	}
	idx := &Index{}
	if err := proto.Unmarshal(data.Bytes(), idx); err != nil {
		return nil, err
	}
	return &Header{
		Hdr: hdr,
		Idx: idx,
	}, nil
}
<commit_msg>Add some documentation to atStart and atEnd functions<commit_after>package index

import (
	"bytes"
	"context"
	"io"
	"strings"

	"github.com/gogo/protobuf/proto"
	"github.com/pachyderm/pachyderm/src/client/pkg/grpcutil"
	"github.com/pachyderm/pachyderm/src/server/pkg/obj"
	"github.com/pachyderm/pachyderm/src/server/pkg/storage/chunk"
	"github.com/pachyderm/pachyderm/src/server/pkg/storage/fileset/tar"
	"modernc.org/mathutil"
)

type levelReader struct {
	cr               *chunk.Reader
	tr               *tar.Reader
	currHdr, peekHdr *Header
}

type pathFilter struct {
	pathRange *PathRange
	prefix    string
}

// Reader is used for reading a multi-level index.
type Reader struct {
	ctx    context.Context
	objC   obj.Client
	chunks *chunk.Storage
	path   string
	filter *pathFilter
	levels []*levelReader
	done   bool
}

// NewReader create a new Reader.
func NewReader(ctx context.Context, objC obj.Client, chunks *chunk.Storage, path string, opts ...Option) *Reader {
	r := &Reader{
		ctx:    ctx,
		objC:   objC,
		chunks: chunks,
		path:   path,
	}
	for _, opt := range opts {
		opt(r)
	}
	return r
}

// Next gets the next header in the index.
func (r *Reader) Next() (*Header, error) {
	if err := r.setupLevels(); err != nil {
		return nil, err
	}
	if r.levels[0].peekHdr != nil {
		hdr := r.levels[0].peekHdr
		r.levels[0].peekHdr = nil
		return hdr, nil
	}
	if r.done {
		return nil, io.EOF
	}
	return r.next(len(r.levels) - 1)
}

func (r *Reader) setupLevels() error {
	if r.levels != nil {
		return nil
	}
	// Setup top level.
	objR, err := r.objC.Reader(r.ctx, r.path, 0, 0)
	if err != nil {
		return err
	}
	buf := &bytes.Buffer{}
	if _, err := io.Copy(buf, objR); err != nil {
		return err
	}
	if err := objR.Close(); err != nil {
		return err
	}
	r.levels = []*levelReader{&levelReader{tr: tar.NewReader(buf)}}
	// Traverse until we reach the first entry in the lowest level.
	for {
		hdr, err := r.peek(len(r.levels) - 1)
		if err != nil {
			return err
		}
		// Return when we are at the lowest level.
		if hdr.Hdr.Typeflag == indexType {
			return nil
		}
		_, err = r.next(len(r.levels) - 1)
		if err != nil {
			return err
		}
		// Setup next level.
		// (bryce) this whole process of reading at the offset is janky, needs to be re-thought.
		cr := r.chunks.NewReader(r.ctx, r.callback(len(r.levels)))
		dataRef := hdr.Idx.DataOp.DataRefs[0]
		dataRef.OffsetBytes = hdr.Idx.Range.Offset
		dataRef.SizeBytes -= hdr.Idx.Range.Offset
		cr.NextRange([]*chunk.DataRef{dataRef})
		r.levels = append(r.levels, &levelReader{
			cr: cr,
			tr: tar.NewReader(cr),
		})
	}
}

func (r *Reader) peek(level int) (*Header, error) {
	l := r.levels[level]
	if l.peekHdr != nil {
		return l.peekHdr, nil
	}
	var err error
	l.peekHdr, err = r.next(level)
	return l.peekHdr, err
}

func (r *Reader) next(level int) (*Header, error) {
	l := r.levels[level]
	if l.peekHdr != nil {
		hdr := l.peekHdr
		l.peekHdr = nil
		return hdr, nil
	}
	for {
		hdr, err := l.tr.Next()
		if err != nil {
			return nil, err
		}
		// Return if done.
		if r.atEnd(hdr.Name) {
			r.done = true
			return nil, io.EOF
		}
		// Handle lowest level index.
		if hdr.Typeflag == indexType {
			// Skip to the starting header.
			if !r.atStart(hdr.Name) {
				continue
			}
			return deserialize(l.tr, hdr)
		}
		// Handle index level above lowest.
		fullHdr, err := deserialize(l.tr, hdr)
		if err != nil {
			return nil, err
		}
		// Skip to the starting header.
		if !r.atStart(fullHdr.Idx.Range.LastPath) {
			continue
		}
		return fullHdr, nil
	}
}

// atStart returns true when the name is in the valid range for a filter (always true if no filter is set).
// For a range filter, this means the name is >= to the lower bound.
// For a prefix filter, this means the name is >= to the prefix.
func (r *Reader) atStart(name string) bool {
	if r.filter == nil {
		return true
	}
	if r.filter.pathRange != nil {
		return name >= r.filter.pathRange.Lower
	}
	return name >= r.filter.prefix
}

// atEnd returns true when the name is past the valid range for a filter (always false if no filter is set).
// For a range filter, this means the name is > than the upper bound.
// For a prefix filter, this means the name does not have the prefix and a name with the prefix cannot show up after it.
func (r *Reader) atEnd(name string) bool {
	if r.filter == nil {
		return false
	}
	if r.filter.pathRange != nil {
		return name > r.filter.pathRange.Upper
	}
	// Name is past a prefix when the first len(prefix) bytes are greater than the prefix
	// (use len(name) bytes for comparison when len(name) < len(prefix)).
	// A simple greater than check would not suffice here for the prefix filter functionality
	// (for example, if the index consisted of the paths "a", "ab", "abc", and "b", then a
	// reader with the prefix filter set to "a" would end at the "ab" path rather than the "b" path).
	cmpSize := mathutil.Min(len(name), len(r.filter.prefix))
	return name[:cmpSize] > r.filter.prefix[:cmpSize]
}

// Peek peeks ahead in the index.
func (r *Reader) Peek() (*Header, error) {
	if err := r.setupLevels(); err != nil {
		return nil, err
	}
	if r.levels[0].peekHdr != nil {
		return r.levels[0].peekHdr, nil
	}
	var err error
	r.levels[0].peekHdr, err = r.Next()
	return r.levels[0].peekHdr, err
}

func (r *Reader) callback(level int) chunk.ReaderFunc {
	return func() ([]*chunk.DataRef, error) {
		hdr, err := r.next(level - 1)
		if err != nil {
			return nil, err
		}
		r.levels[level-1].currHdr = hdr
		return hdr.Idx.DataOp.DataRefs, nil
	}
}

// Copy is the basic data structure to represent a copy of data from
// a reader to a writer.
type Copy struct {
	level int
	raw   *chunk.Copy
	hdrs  []*Header
}

// ReadCopyFunc returns a function for copying data from the reader.
func (r *Reader) ReadCopyFunc(pathBound ...string) func() (*Copy, error) {
	level := -1
	var offset int64
	var done bool
	return func() (*Copy, error) {
		if done {
			return nil, io.EOF
		}
		// Setup levels, initialize first level.
		if err := r.setupLevels(); err != nil {
			return nil, err
		}
		if level < 0 {
			level = len(r.levels) - 1
		}
		c := &Copy{level: r.wLevel(level)}
		cr := r.levels[level].cr
		// Handle index entries that span multiple chunks.
		if offset > 0 {
			raw, err := cr.ReadCopy(offset)
			if err != nil {
				return nil, err
			}
			c.raw = raw
		}
		// (bryce) this is janky, but we need the current header when copying a level above.
		if r.levels[level].currHdr != nil {
			r.levels[level].peekHdr, r.levels[level].currHdr = r.levels[level].currHdr, nil
		}
		// While not past a split point, get index entries to copy.
		pastSplit := false
		cr.OnSplit(func() { pastSplit = true })
		for !pastSplit {
			hdr, err := r.peek(level)
			if err != nil {
				if err == io.EOF {
					done = true
					return c, nil
				}
				return nil, err
			}
			// Stop copying when the last referenced (directly or indirectly) content
			// chunk has a path that is >= the path bound.
			if !BeforeBound(hdr.Idx.LastPathChunk, pathBound...) {
				if hdr.Idx.Range == nil {
					done = true
					return c, nil
				}
				level++
				offset = hdr.Idx.Range.Offset
				return c, nil
			}
			c.hdrs = append(c.hdrs, hdr)
			_, err = r.next(level)
			if err != nil {
				return nil, err
			}
		}
		level--
		return c, nil
	}
}

func (r *Reader) wLevel(rLevel int) int {
	return len(r.levels) - 1 - rLevel
}

// BeforeBound checks if the passed in string is before the string bound (exclusive).
// The string bound is optional, so if no string bound is passed then it returns true.
func BeforeBound(str string, strBound ...string) bool {
	return len(strBound) == 0 || strings.Compare(str, strBound[0]) < 0
}

// Close closes the reader.
func (r *Reader) Close() error {
	for i := 1; i < len(r.levels); i++ {
		if err := r.levels[i].cr.Close(); err != nil {
			return err
		}
	}
	return nil
}

func deserialize(tr *tar.Reader, hdr *tar.Header) (*Header, error) {
	data := &bytes.Buffer{}
	buf := grpcutil.GetBuffer()
	defer grpcutil.PutBuffer(buf)
	if _, err := io.CopyBuffer(data, tr, buf); err != nil {
		return nil, err
	}
	idx := &Index{}
	if err := proto.Unmarshal(data.Bytes(), idx); err != nil {
		return nil, err
	}
	return &Header{
		Hdr: hdr,
		Idx: idx,
	}, nil
}
<|endoftext|>


<commit_before><commit_msg>go fmt<commit_after><|endoftext|>