mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-28 08:39:56 +00:00
TUN-528: Move cloudflared into a separate repo
This commit is contained in:
19
vendor/zombiezen.com/go/capnproto2/internal/fulfiller/BUILD.bazel
generated
vendored
Normal file
19
vendor/zombiezen.com/go/capnproto2/internal/fulfiller/BUILD.bazel
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["fulfiller.go"],
|
||||
importpath = "zombiezen.com/go/capnproto2/internal/fulfiller",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//:go_default_library",
|
||||
"//internal/queue:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["fulfiller_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//:go_default_library"],
|
||||
)
|
329
vendor/zombiezen.com/go/capnproto2/internal/fulfiller/fulfiller.go
generated
vendored
Normal file
329
vendor/zombiezen.com/go/capnproto2/internal/fulfiller/fulfiller.go
generated
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
// Package fulfiller provides a type that implements capnp.Answer that
|
||||
// resolves by calling setter methods.
|
||||
package fulfiller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"zombiezen.com/go/capnproto2"
|
||||
"zombiezen.com/go/capnproto2/internal/queue"
|
||||
)
|
||||
|
||||
// callQueueSize is the maximum number of pending calls.
|
||||
const callQueueSize = 64
|
||||
|
||||
// Fulfiller is a promise for a Struct. The zero value is an unresolved
|
||||
// answer. A Fulfiller is considered to be resolved once Fulfill or
|
||||
// Reject is called. Calls to the Fulfiller will queue up until it is
|
||||
// resolved. A Fulfiller is safe to use from multiple goroutines.
|
||||
type Fulfiller struct {
|
||||
once sync.Once
|
||||
resolved chan struct{} // initialized by init()
|
||||
|
||||
// Protected by mu
|
||||
mu sync.RWMutex
|
||||
answer capnp.Answer
|
||||
queue []pcall // initialized by init()
|
||||
}
|
||||
|
||||
// init initializes the Fulfiller. It is idempotent.
|
||||
// Should be called for each method on Fulfiller.
|
||||
func (f *Fulfiller) init() {
|
||||
f.once.Do(func() {
|
||||
f.resolved = make(chan struct{})
|
||||
f.queue = make([]pcall, 0, callQueueSize)
|
||||
})
|
||||
}
|
||||
|
||||
// Fulfill sets the fulfiller's answer to s. If there are queued
|
||||
// pipeline calls, the capabilities on the struct will be embargoed
|
||||
// until the queued calls finish. Fulfill will panic if the fulfiller
|
||||
// has already been resolved.
|
||||
func (f *Fulfiller) Fulfill(s capnp.Struct) {
|
||||
f.init()
|
||||
f.mu.Lock()
|
||||
if f.answer != nil {
|
||||
f.mu.Unlock()
|
||||
panic("Fulfiller.Fulfill called more than once")
|
||||
}
|
||||
f.answer = capnp.ImmediateAnswer(s)
|
||||
queues := f.emptyQueue(s)
|
||||
ctab := s.Segment().Message().CapTable
|
||||
for capIdx, q := range queues {
|
||||
ctab[capIdx] = newEmbargoClient(ctab[capIdx], q)
|
||||
}
|
||||
close(f.resolved)
|
||||
f.mu.Unlock()
|
||||
}
|
||||
|
||||
// emptyQueue splits the queue by which capability it targets and
|
||||
// drops any invalid calls. Once this function returns, f.queue will
|
||||
// be nil.
|
||||
func (f *Fulfiller) emptyQueue(s capnp.Struct) map[capnp.CapabilityID][]ecall {
|
||||
qs := make(map[capnp.CapabilityID][]ecall, len(f.queue))
|
||||
for i, pc := range f.queue {
|
||||
c, err := capnp.TransformPtr(s.ToPtr(), pc.transform)
|
||||
if err != nil {
|
||||
pc.f.Reject(err)
|
||||
continue
|
||||
}
|
||||
in := c.Interface()
|
||||
if !in.IsValid() {
|
||||
pc.f.Reject(capnp.ErrNullClient)
|
||||
continue
|
||||
}
|
||||
cn := in.Capability()
|
||||
if qs[cn] == nil {
|
||||
qs[cn] = make([]ecall, 0, len(f.queue)-i)
|
||||
}
|
||||
qs[cn] = append(qs[cn], pc.ecall)
|
||||
}
|
||||
f.queue = nil
|
||||
return qs
|
||||
}
|
||||
|
||||
// Reject sets the fulfiller's answer to err. If there are queued
|
||||
// pipeline calls, they will all return errors. Reject will panic if
|
||||
// the error is nil or the fulfiller has already been resolved.
|
||||
func (f *Fulfiller) Reject(err error) {
|
||||
if err == nil {
|
||||
panic("Fulfiller.Reject called with nil")
|
||||
}
|
||||
f.init()
|
||||
f.mu.Lock()
|
||||
if f.answer != nil {
|
||||
f.mu.Unlock()
|
||||
panic("Fulfiller.Reject called more than once")
|
||||
}
|
||||
f.answer = capnp.ErrorAnswer(err)
|
||||
for i := range f.queue {
|
||||
f.queue[i].f.Reject(err)
|
||||
f.queue[i] = pcall{}
|
||||
}
|
||||
close(f.resolved)
|
||||
f.mu.Unlock()
|
||||
}
|
||||
|
||||
// Done returns a channel that is closed once f is resolved.
|
||||
func (f *Fulfiller) Done() <-chan struct{} {
|
||||
f.init()
|
||||
return f.resolved
|
||||
}
|
||||
|
||||
// Peek returns f's resolved answer or nil if f has not been resolved.
|
||||
// The Struct method of an answer returned from Peek returns immediately.
|
||||
func (f *Fulfiller) Peek() capnp.Answer {
|
||||
f.init()
|
||||
f.mu.RLock()
|
||||
a := f.answer
|
||||
f.mu.RUnlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Struct waits until f is resolved and returns its struct if fulfilled
|
||||
// or an error if rejected.
|
||||
func (f *Fulfiller) Struct() (capnp.Struct, error) {
|
||||
<-f.Done()
|
||||
return f.Peek().Struct()
|
||||
}
|
||||
|
||||
// PipelineCall calls PipelineCall on the fulfilled answer or queues the
|
||||
// call if f has not been fulfilled.
|
||||
func (f *Fulfiller) PipelineCall(transform []capnp.PipelineOp, call *capnp.Call) capnp.Answer {
|
||||
f.init()
|
||||
|
||||
// Fast path: pass-through after fulfilled.
|
||||
if a := f.Peek(); a != nil {
|
||||
return a.PipelineCall(transform, call)
|
||||
}
|
||||
|
||||
f.mu.Lock()
|
||||
// Make sure that f wasn't fulfilled.
|
||||
if a := f.answer; a != nil {
|
||||
f.mu.Unlock()
|
||||
return a.PipelineCall(transform, call)
|
||||
}
|
||||
if len(f.queue) == cap(f.queue) {
|
||||
f.mu.Unlock()
|
||||
return capnp.ErrorAnswer(errCallQueueFull)
|
||||
}
|
||||
cc, err := call.Copy(nil)
|
||||
if err != nil {
|
||||
f.mu.Unlock()
|
||||
return capnp.ErrorAnswer(err)
|
||||
}
|
||||
g := new(Fulfiller)
|
||||
f.queue = append(f.queue, pcall{
|
||||
transform: transform,
|
||||
ecall: ecall{
|
||||
call: cc,
|
||||
f: g,
|
||||
},
|
||||
})
|
||||
f.mu.Unlock()
|
||||
return g
|
||||
}
|
||||
|
||||
// PipelineClose waits until f is resolved and then calls PipelineClose
|
||||
// on the fulfilled answer.
|
||||
func (f *Fulfiller) PipelineClose(transform []capnp.PipelineOp) error {
|
||||
<-f.Done()
|
||||
return f.Peek().PipelineClose(transform)
|
||||
}
|
||||
|
||||
// pcall is a queued pipeline call.
|
||||
type pcall struct {
|
||||
transform []capnp.PipelineOp
|
||||
ecall
|
||||
}
|
||||
|
||||
// EmbargoClient is a client that flushes a queue of calls.
|
||||
// Fulfiller will create these automatically when pipelined calls are
|
||||
// made on unresolved answers. EmbargoClient is exported so that rpc
|
||||
// can avoid making calls on its own Conn.
|
||||
type EmbargoClient struct {
|
||||
client capnp.Client
|
||||
|
||||
mu sync.RWMutex
|
||||
q queue.Queue
|
||||
calls ecallList
|
||||
}
|
||||
|
||||
func newEmbargoClient(client capnp.Client, queue []ecall) capnp.Client {
|
||||
ec := &EmbargoClient{
|
||||
client: client,
|
||||
calls: make(ecallList, callQueueSize),
|
||||
}
|
||||
ec.q.Init(ec.calls, copy(ec.calls, queue))
|
||||
go ec.flushQueue()
|
||||
return ec
|
||||
}
|
||||
|
||||
func (ec *EmbargoClient) push(cl *capnp.Call) capnp.Answer {
|
||||
f := new(Fulfiller)
|
||||
cl, err := cl.Copy(nil)
|
||||
if err != nil {
|
||||
return capnp.ErrorAnswer(err)
|
||||
}
|
||||
i := ec.q.Push()
|
||||
if i == -1 {
|
||||
return capnp.ErrorAnswer(errCallQueueFull)
|
||||
}
|
||||
ec.calls[i] = ecall{cl, f}
|
||||
return f
|
||||
}
|
||||
|
||||
// flushQueue is run in its own goroutine.
|
||||
func (ec *EmbargoClient) flushQueue() {
|
||||
var c ecall
|
||||
ec.mu.Lock()
|
||||
if i := ec.q.Front(); i != -1 {
|
||||
c = ec.calls[i]
|
||||
}
|
||||
ec.mu.Unlock()
|
||||
for c.call != nil {
|
||||
ans := ec.client.Call(c.call)
|
||||
go func(f *Fulfiller, ans capnp.Answer) {
|
||||
s, err := ans.Struct()
|
||||
if err == nil {
|
||||
f.Fulfill(s)
|
||||
} else {
|
||||
f.Reject(err)
|
||||
}
|
||||
}(c.f, ans)
|
||||
ec.mu.Lock()
|
||||
ec.q.Pop()
|
||||
if i := ec.q.Front(); i != -1 {
|
||||
c = ec.calls[i]
|
||||
} else {
|
||||
c = ecall{}
|
||||
}
|
||||
ec.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Client returns the underlying client if the embargo has been lifted
|
||||
// and nil otherwise.
|
||||
func (ec *EmbargoClient) Client() capnp.Client {
|
||||
ec.mu.RLock()
|
||||
ok := ec.isPassthrough()
|
||||
ec.mu.RUnlock()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return ec.client
|
||||
}
|
||||
|
||||
func (ec *EmbargoClient) isPassthrough() bool {
|
||||
return ec.q.Len() == 0
|
||||
}
|
||||
|
||||
// Call either queues a call to the underlying client or starts a call
|
||||
// if the embargo has been lifted.
|
||||
func (ec *EmbargoClient) Call(cl *capnp.Call) capnp.Answer {
|
||||
// Fast path: queue is flushed.
|
||||
ec.mu.RLock()
|
||||
ok := ec.isPassthrough()
|
||||
ec.mu.RUnlock()
|
||||
if ok {
|
||||
return ec.client.Call(cl)
|
||||
}
|
||||
|
||||
// Add to queue.
|
||||
ec.mu.Lock()
|
||||
// Since we released the lock, check that the queue hasn't been flushed.
|
||||
if ec.isPassthrough() {
|
||||
ec.mu.Unlock()
|
||||
return ec.client.Call(cl)
|
||||
}
|
||||
ans := ec.push(cl)
|
||||
ec.mu.Unlock()
|
||||
return ans
|
||||
}
|
||||
|
||||
// TryQueue will attempt to queue a call or return nil if the embargo
|
||||
// has been lifted.
|
||||
func (ec *EmbargoClient) TryQueue(cl *capnp.Call) capnp.Answer {
|
||||
ec.mu.Lock()
|
||||
if ec.isPassthrough() {
|
||||
ec.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
ans := ec.push(cl)
|
||||
ec.mu.Unlock()
|
||||
return ans
|
||||
}
|
||||
|
||||
// Close closes the underlying client, rejecting any queued calls.
|
||||
func (ec *EmbargoClient) Close() error {
|
||||
ec.mu.Lock()
|
||||
// reject all queued calls
|
||||
for ec.q.Len() > 0 {
|
||||
ec.calls[ec.q.Front()].f.Reject(errQueueCallCancel)
|
||||
ec.q.Pop()
|
||||
}
|
||||
ec.mu.Unlock()
|
||||
return ec.client.Close()
|
||||
}
|
||||
|
||||
// ecall is an queued embargoed call.
|
||||
type ecall struct {
|
||||
call *capnp.Call
|
||||
f *Fulfiller
|
||||
}
|
||||
|
||||
type ecallList []ecall
|
||||
|
||||
func (el ecallList) Len() int {
|
||||
return len(el)
|
||||
}
|
||||
|
||||
func (el ecallList) Clear(i int) {
|
||||
el[i] = ecall{}
|
||||
}
|
||||
|
||||
var (
|
||||
errCallQueueFull = errors.New("capnp: promised answer call queue full")
|
||||
errQueueCallCancel = errors.New("capnp: queued call canceled")
|
||||
)
|
123
vendor/zombiezen.com/go/capnproto2/internal/fulfiller/fulfiller_test.go
generated
vendored
Normal file
123
vendor/zombiezen.com/go/capnproto2/internal/fulfiller/fulfiller_test.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package fulfiller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"zombiezen.com/go/capnproto2"
|
||||
)
|
||||
|
||||
func TestFulfiller_NewShouldBeUnresolved(t *testing.T) {
|
||||
f := new(Fulfiller)
|
||||
|
||||
if a := f.Peek(); a != nil {
|
||||
t.Errorf("f.Peek() = %v; want nil", a)
|
||||
}
|
||||
select {
|
||||
case <-f.Done():
|
||||
t.Error("Done closed early")
|
||||
default:
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
func TestFulfiller_FulfillShouldResolve(t *testing.T) {
|
||||
f := new(Fulfiller)
|
||||
st := newStruct(t, capnp.ObjectSize{})
|
||||
|
||||
f.Fulfill(st)
|
||||
|
||||
select {
|
||||
case <-f.Done():
|
||||
default:
|
||||
t.Error("Done still closed after Fulfill")
|
||||
}
|
||||
ret, err := f.Struct()
|
||||
if err != nil {
|
||||
t.Errorf("f.Struct() error: %v", err)
|
||||
}
|
||||
if ret != st {
|
||||
t.Errorf("f.Struct() = %v; want %v", ret, st)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFulfiller_RejectShouldResolve(t *testing.T) {
|
||||
f := new(Fulfiller)
|
||||
e := errors.New("failure and rejection")
|
||||
|
||||
f.Reject(e)
|
||||
|
||||
select {
|
||||
case <-f.Done():
|
||||
default:
|
||||
t.Error("Done still closed after Reject")
|
||||
}
|
||||
ret, err := f.Struct()
|
||||
if err != e {
|
||||
t.Errorf("f.Struct() error = %v; want %v", err, e)
|
||||
}
|
||||
if capnp.IsValid(ret) {
|
||||
t.Errorf("f.Struct() = %v; want null", ret)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFulfiller_QueuedCallsDeliveredInOrder(t *testing.T) {
|
||||
f := new(Fulfiller)
|
||||
oc := new(orderClient)
|
||||
result := newStruct(t, capnp.ObjectSize{PointerCount: 1})
|
||||
in := result.Segment().Message().AddCap(oc)
|
||||
result.SetPointer(0, capnp.NewInterface(result.Segment(), in))
|
||||
|
||||
ans1 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call))
|
||||
ans2 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call))
|
||||
f.Fulfill(result)
|
||||
ans3 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call))
|
||||
ans3.Struct()
|
||||
ans4 := f.PipelineCall([]capnp.PipelineOp{{Field: 0}}, new(capnp.Call))
|
||||
|
||||
check := func(a capnp.Answer, n uint64) {
|
||||
r, err := a.Struct()
|
||||
if r.Uint64(0) != n {
|
||||
t.Errorf("r%d = %d; want %d", n+1, r.Uint64(0), n)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("err%d = %v", n+1, err)
|
||||
}
|
||||
}
|
||||
check(ans1, 0)
|
||||
check(ans2, 1)
|
||||
check(ans3, 2)
|
||||
check(ans4, 3)
|
||||
}
|
||||
|
||||
func newStruct(t *testing.T, sz capnp.ObjectSize) capnp.Struct {
|
||||
_, s, err := capnp.NewMessage(capnp.SingleSegment(nil))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st, err := capnp.NewStruct(s, sz)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
type orderClient int
|
||||
|
||||
func (oc *orderClient) Call(cl *capnp.Call) capnp.Answer {
|
||||
_, s, err := capnp.NewMessage(capnp.SingleSegment(nil))
|
||||
if err != nil {
|
||||
return capnp.ErrorAnswer(err)
|
||||
}
|
||||
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8})
|
||||
if err != nil {
|
||||
return capnp.ErrorAnswer(err)
|
||||
}
|
||||
st.SetUint64(0, uint64(*oc))
|
||||
*oc++
|
||||
return capnp.ImmediateAnswer(st)
|
||||
}
|
||||
|
||||
func (oc *orderClient) Close() error {
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user