mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-29 04:22:06 -05:00
Fix "context canceled" with partial
Make sure the context used for timeouts isn't created based on the incoming context, as we have cases where this can cancel the context prematurely. Fixes #10789
This commit is contained in:
parent
184a67ac47
commit
3bbeb5688c
5 changed files with 40 additions and 14 deletions
|
@ -180,14 +180,15 @@ func (ini *Init) checkDone() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ini *Init) withTimeout(ctx context.Context, timeout time.Duration, f func(ctx context.Context) (any, error)) (any, error) {
|
func (ini *Init) withTimeout(ctx context.Context, timeout time.Duration, f func(ctx context.Context) (any, error)) (any, error) {
|
||||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
// Create a new context with a timeout not connected to the incoming context.
|
||||||
|
waitCtx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
c := make(chan verr, 1)
|
c := make(chan verr, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
v, err := f(ctx)
|
v, err := f(ctx)
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-waitCtx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
c <- verr{v: v, err: err}
|
c <- verr{v: v, err: err}
|
||||||
|
@ -195,7 +196,7 @@ func (ini *Init) withTimeout(ctx context.Context, timeout time.Duration, f func(
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-waitCtx.Done():
|
||||||
return nil, errors.New("timed out initializing value. You may have a circular loop in a shortcode, or your site may have resources that take longer to build than the `timeout` limit in your Hugo config file.")
|
return nil, errors.New("timed out initializing value. You may have a circular loop in a shortcode, or your site may have resources that take longer to build than the `timeout` limit in your Hugo config file.")
|
||||||
case ve := <-c:
|
case ve := <-c:
|
||||||
return ve.v, ve.err
|
return ve.v, ve.err
|
||||||
|
|
|
@ -126,12 +126,6 @@ func TestInitAddWithTimeoutTimeout(t *testing.T) {
|
||||||
|
|
||||||
init := New().AddWithTimeout(100*time.Millisecond, func(ctx context.Context) (any, error) {
|
init := New().AddWithTimeout(100*time.Millisecond, func(ctx context.Context) (any, error) {
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, nil
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
t.Fatal("slept")
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -164,12 +164,12 @@ type resourceAdapter struct {
|
||||||
*resourceAdapterInner
|
*resourceAdapterInner
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceAdapter) Content(context.Context) (any, error) {
|
func (r *resourceAdapter) Content(ctx context.Context) (any, error) {
|
||||||
r.init(false, true)
|
r.init(false, true)
|
||||||
if r.transformationsErr != nil {
|
if r.transformationsErr != nil {
|
||||||
return nil, r.transformationsErr
|
return nil, r.transformationsErr
|
||||||
}
|
}
|
||||||
return r.target.Content(context.Background())
|
return r.target.Content(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceAdapter) Err() resource.ResourceError {
|
func (r *resourceAdapter) Err() resource.ResourceError {
|
||||||
|
|
|
@ -324,3 +324,31 @@ timeout = '200ms'
|
||||||
b.Assert(err.Error(), qt.Contains, "timed out")
|
b.Assert(err.Error(), qt.Contains, "timed out")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See Issue #10789
|
||||||
|
func TestReturnExecuteFromTemplateInPartial(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- config.toml --
|
||||||
|
baseURL = 'http://example.com/'
|
||||||
|
-- layouts/index.html --
|
||||||
|
{{ $r := partial "foo" }}
|
||||||
|
FOO:{{ $r.Content }}
|
||||||
|
-- layouts/partials/foo.html --
|
||||||
|
{{ $r := §§{{ partial "bar" }}§§ | resources.FromString "bar.html" | resources.ExecuteAsTemplate "bar.html" . }}
|
||||||
|
{{ return $r }}
|
||||||
|
-- layouts/partials/bar.html --
|
||||||
|
BAR
|
||||||
|
`
|
||||||
|
|
||||||
|
b := hugolib.NewIntegrationTestBuilder(
|
||||||
|
hugolib.IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: files,
|
||||||
|
},
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/index.html", "OO:BAR")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -129,7 +129,10 @@ func (ns *Namespace) Include(ctx context.Context, name string, contextList ...an
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *Namespace) includWithTimeout(ctx context.Context, name string, dataList ...any) includeResult {
|
func (ns *Namespace) includWithTimeout(ctx context.Context, name string, dataList ...any) includeResult {
|
||||||
ctx, cancel := context.WithTimeout(ctx, ns.deps.Timeout)
|
// There are situation where the ctx we pass on to the partial lives longer than
|
||||||
|
// the partial itself. For example, when the partial returns the result from reosurces.ExecuteAsTemplate.
|
||||||
|
// Because of that, create a completely new context here.
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(context.Background(), ns.deps.Timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
res := make(chan includeResult, 1)
|
res := make(chan includeResult, 1)
|
||||||
|
@ -141,8 +144,8 @@ func (ns *Namespace) includWithTimeout(ctx context.Context, name string, dataLis
|
||||||
select {
|
select {
|
||||||
case r := <-res:
|
case r := <-res:
|
||||||
return r
|
return r
|
||||||
case <-ctx.Done():
|
case <-timeoutCtx.Done():
|
||||||
err := ctx.Err()
|
err := timeoutCtx.Err()
|
||||||
if err == context.DeadlineExceeded {
|
if err == context.DeadlineExceeded {
|
||||||
err = fmt.Errorf("partial %q timed out after %s. This is most likely due to infinite recursion. If this is just a slow template, you can try to increase the 'timeout' config setting.", name, ns.deps.Timeout)
|
err = fmt.Errorf("partial %q timed out after %s. This is most likely due to infinite recursion. If this is just a slow template, you can try to increase the 'timeout' config setting.", name, ns.deps.Timeout)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue