requesthook.go 3.26 KB
Newer Older
Hannah Howard's avatar
Hannah Howard committed
1
package hooks
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

import (
	"errors"
	"sync"

	"github.com/ipfs/go-graphsync"
	"github.com/ipld/go-ipld-prime"
	"github.com/ipld/go-ipld-prime/traversal"
	peer "github.com/libp2p/go-libp2p-core/peer"
)

type requestHook struct {
	key  uint64
	hook graphsync.OnIncomingRequestHook
}

// PersistenceOptions is an interface for getting loaders by name
type PersistenceOptions interface {
	GetLoader(name string) (ipld.Loader, bool)
}

// IncomingRequestHooks is a set of incoming request hooks that can be processed
type IncomingRequestHooks struct {
	persistenceOptions PersistenceOptions
Hannah Howard's avatar
Hannah Howard committed
26 27 28
	hooksLk            sync.RWMutex
	nextKey            uint64
	hooks              []requestHook
29 30
}

Hannah Howard's avatar
Hannah Howard committed
31 32
// NewRequestHooks returns a new list of incoming request hooks
func NewRequestHooks(persistenceOptions PersistenceOptions) *IncomingRequestHooks {
33 34 35 36 37 38 39
	return &IncomingRequestHooks{
		persistenceOptions: persistenceOptions,
	}
}

// Register registers an extension to process new incoming requests
func (irh *IncomingRequestHooks) Register(hook graphsync.OnIncomingRequestHook) graphsync.UnregisterHookFunc {
Hannah Howard's avatar
Hannah Howard committed
40 41 42 43 44
	irh.hooksLk.Lock()
	rh := requestHook{irh.nextKey, hook}
	irh.nextKey++
	irh.hooks = append(irh.hooks, rh)
	irh.hooksLk.Unlock()
45
	return func() {
Hannah Howard's avatar
Hannah Howard committed
46 47 48
		irh.hooksLk.Lock()
		defer irh.hooksLk.Unlock()
		for i, matchHook := range irh.hooks {
49
			if rh.key == matchHook.key {
Hannah Howard's avatar
Hannah Howard committed
50
				irh.hooks = append(irh.hooks[:i], irh.hooks[i+1:]...)
51 52 53 54 55 56
				return
			}
		}
	}
}

Hannah Howard's avatar
Hannah Howard committed
57 58
// RequestResult is the outcome of running requesthooks
type RequestResult struct {
59 60
	IsValidated   bool
	CustomLoader  ipld.Loader
Hannah Howard's avatar
Hannah Howard committed
61
	CustomChooser traversal.LinkTargetNodeStyleChooser
62 63 64 65 66
	Err           error
	Extensions    []graphsync.ExtensionData
}

// ProcessRequestHooks runs request hooks against an incoming request
Hannah Howard's avatar
Hannah Howard committed
67 68 69 70
func (irh *IncomingRequestHooks) ProcessRequestHooks(p peer.ID, request graphsync.RequestData) RequestResult {
	irh.hooksLk.RLock()
	defer irh.hooksLk.RUnlock()
	ha := &requestHookActions{
71 72
		persistenceOptions: irh.persistenceOptions,
	}
Hannah Howard's avatar
Hannah Howard committed
73
	for _, requestHook := range irh.hooks {
74 75 76 77 78 79 80 81
		requestHook.hook(p, request, ha)
		if ha.hasError() {
			break
		}
	}
	return ha.result()
}

Hannah Howard's avatar
Hannah Howard committed
82
type requestHookActions struct {
83 84 85 86
	persistenceOptions PersistenceOptions
	isValidated        bool
	err                error
	loader             ipld.Loader
Hannah Howard's avatar
Hannah Howard committed
87
	chooser            traversal.LinkTargetNodeStyleChooser
88 89 90
	extensions         []graphsync.ExtensionData
}

Hannah Howard's avatar
Hannah Howard committed
91
func (ha *requestHookActions) hasError() bool {
92 93 94
	return ha.err != nil
}

Hannah Howard's avatar
Hannah Howard committed
95 96
func (ha *requestHookActions) result() RequestResult {
	return RequestResult{
97 98 99 100 101 102 103 104
		IsValidated:   ha.isValidated,
		CustomLoader:  ha.loader,
		CustomChooser: ha.chooser,
		Err:           ha.err,
		Extensions:    ha.extensions,
	}
}

Hannah Howard's avatar
Hannah Howard committed
105
func (ha *requestHookActions) SendExtensionData(ext graphsync.ExtensionData) {
106 107 108
	ha.extensions = append(ha.extensions, ext)
}

Hannah Howard's avatar
Hannah Howard committed
109
func (ha *requestHookActions) TerminateWithError(err error) {
110 111 112
	ha.err = err
}

Hannah Howard's avatar
Hannah Howard committed
113
func (ha *requestHookActions) ValidateRequest() {
114 115 116
	ha.isValidated = true
}

Hannah Howard's avatar
Hannah Howard committed
117
func (ha *requestHookActions) UsePersistenceOption(name string) {
118 119 120 121 122 123 124 125
	loader, ok := ha.persistenceOptions.GetLoader(name)
	if !ok {
		ha.TerminateWithError(errors.New("unknown loader option"))
		return
	}
	ha.loader = loader
}

Hannah Howard's avatar
Hannah Howard committed
126
func (ha *requestHookActions) UseLinkTargetNodeStyleChooser(chooser traversal.LinkTargetNodeStyleChooser) {
127 128
	ha.chooser = chooser
}