fixes waitTimeout, waitDelay not working with float

This commit is contained in:
Julien Neuhart
2019-07-08 17:24:17 +02:00
parent 186c09f29b
commit 9af00d6d58
11 changed files with 110 additions and 18 deletions

View File

@@ -37,7 +37,8 @@ By default, the API will add a log entry when the [healthcheck endpoint](#ping)
You may turn off this logging so as to avoid unnecessary entries in your logs with the environment variable `DISABLE_HEALTHCHECK_LOGGING`. You may turn off this logging so as to avoid unnecessary entries in your logs with the environment variable `DISABLE_HEALTHCHECK_LOGGING`.
This environment variable operates in the same manner as the `DISABLE_GOOGLE_CHROME` and `DISABLE_UNOCONV` variables operate in that it accepts the strings `"0"` or `"1"` as values, where `1` is enabled. This environment variable operates in the same manner as the `DISABLE_GOOGLE_CHROME` and `DISABLE_UNOCONV` variables operate in that it accepts the strings `"0"` or `"1"` as values, where `"1"` is enabled.
## Default listen port ## Default listen port
By default, the API will listen on port `3000`. For most use cases this is perfectly fine, but at times there may be cases where you need to change this due to port conflicts. By default, the API will listen on port `3000`. For most use cases this is perfectly fine, but at times there may be cases where you need to change this due to port conflicts.
@@ -52,4 +53,4 @@ By default, `stdout` and `stderr` messages from the started processes are disabl
You may enable some debug logging from starting the process by setting the environment variable `DEBUG_PROCESS_STARTUP`. You may enable some debug logging from starting the process by setting the environment variable `DEBUG_PROCESS_STARTUP`.
This environment variable operates in the same manner as the `DISABLE_GOOGLE_CHROME` and `DISABLE_UNOCONV` variables operate in that it accepts the strings `"0"` or `"1"` as values, where `1` means `true`. This environment variable operates in the same manner as the `DISABLE_GOOGLE_CHROME` and `DISABLE_UNOCONV` variables operate in that it accepts the strings `"0"` or `"1"` as values, where `1` means `true`.

View File

@@ -296,7 +296,8 @@ $client->store($request, $dest);
## Wait delay ## Wait delay
In some cases, you may want to wait a certain amount of time to make sure the In some cases, you may want to wait a certain amount of time to make sure the
page you're trying to generate is fully rendered. page you're trying to generate is fully rendered. For instance, if your page relies
a lot on JavaScript for rendering.
> The wait delay is a duration in **seconds** (e.g `2.5` for 2.5 seconds). > The wait delay is a duration in **seconds** (e.g `2.5` for 2.5 seconds).
@@ -339,4 +340,4 @@ $request = new HTMLRequest($index);
$request->setWaitDelay(5.5); $request->setWaitDelay(5.5);
$dest = "result.pdf"; $dest = "result.pdf";
$client->store($request, $dest); $client->store($request, $dest);
``` ```

View File

@@ -272,7 +272,7 @@ See the <a href="#timeout">timeout section</a>.</p>
<p>You may turn off this logging so as to avoid unnecessary entries in your logs with the environment variable <code>DISABLE_HEALTHCHECK_LOGGING</code>.</p> <p>You may turn off this logging so as to avoid unnecessary entries in your logs with the environment variable <code>DISABLE_HEALTHCHECK_LOGGING</code>.</p>
<p>This environment variable operates in the same manner as the <code>DISABLE_GOOGLE_CHROME</code> and <code>DISABLE_UNOCONV</code> variables operate in that it accepts the strings <code>&#34;0&#34;</code> or <code>&#34;1&#34;</code> as values, where <code>1</code> is enabled.</p> <p>This environment variable operates in the same manner as the <code>DISABLE_GOOGLE_CHROME</code> and <code>DISABLE_UNOCONV</code> variables operate in that it accepts the strings <code>&#34;0&#34;</code> or <code>&#34;1&#34;</code> as values, where <code>&#34;1&#34;</code> is enabled.</p>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="environment_variables.default_listen_port" href="#environment_variables.default_listen_port"> <h2 class="Heading"><a class="Anchor" aria-hidden="true" id="environment_variables.default_listen_port" href="#environment_variables.default_listen_port">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
@@ -620,7 +620,8 @@ $client-&gt;store($request, $dest);
</a>Wait delay</h2> </a>Wait delay</h2>
<p>In some cases, you may want to wait a certain amount of time to make sure the <p>In some cases, you may want to wait a certain amount of time to make sure the
page youre trying to generate is fully rendered.</p> page youre trying to generate is fully rendered. For instance, if your page relies
a lot on JavaScript for rendering.</p>
<blockquote> <blockquote>
<p>The wait delay is a duration in <strong>seconds</strong> (e.g <code>2.5</code> for 2.5 seconds).</p> <p>The wait delay is a duration in <strong>seconds</strong> (e.g <code>2.5</code> for 2.5 seconds).</p>

View File

@@ -23,7 +23,6 @@ type Context struct {
// New creates a new context. // New creates a new context.
func New(c echo.Context, logger *logger.Logger, config *config.Config) *Context { func New(c echo.Context, logger *logger.Logger, config *config.Config) *Context {
// TODO timeout context?
return &Context{ return &Context{
c, c,
logger, logger,

View File

@@ -13,6 +13,7 @@ import (
"github.com/mafredri/cdp/protocol/target" "github.com/mafredri/cdp/protocol/target"
"github.com/mafredri/cdp/rpcc" "github.com/mafredri/cdp/rpcc"
"github.com/thecodingmachine/gotenberg/internal/pkg/standarderror" "github.com/thecodingmachine/gotenberg/internal/pkg/standarderror"
"github.com/thecodingmachine/gotenberg/internal/pkg/timeout"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -39,9 +40,7 @@ type ChromeOptions struct {
func (p *chrome) Print(destination string) error { func (p *chrome) Print(destination string) error {
const op = "printer.chrome.Print" const op = "printer.chrome.Print"
// FIXME duration not working with float ctx, cancel := timeout.Context(p.opts.WaitTimeout + p.opts.WaitDelay)
duration := time.Duration(p.opts.WaitTimeout+p.opts.WaitDelay) * time.Second
ctx, cancel := context.WithTimeout(context.Background(), duration)
defer cancel() defer cancel()
devt, err := devtool.New("http://localhost:9222").Version(ctx) devt, err := devtool.New("http://localhost:9222").Version(ctx)
if err != nil { if err != nil {
@@ -153,8 +152,7 @@ func (p *chrome) navigate(ctx context.Context, client *cdp.Client) error {
return &standarderror.Error{Op: op, Err: err} return &standarderror.Error{Op: op, Err: err}
} }
// wait for a given amount of time (useful for javascript delay). // wait for a given amount of time (useful for javascript delay).
// FIXME duration not working with float time.Sleep(timeout.Duration(p.opts.WaitDelay))
time.Sleep(time.Duration(p.opts.WaitDelay) * time.Second)
return nil return nil
} }

View File

@@ -3,9 +3,9 @@ package printer
import ( import (
"context" "context"
"os/exec" "os/exec"
"time"
"github.com/thecodingmachine/gotenberg/internal/pkg/standarderror" "github.com/thecodingmachine/gotenberg/internal/pkg/standarderror"
"github.com/thecodingmachine/gotenberg/internal/pkg/timeout"
) )
type merge struct { type merge struct {
@@ -31,8 +31,7 @@ func NewMerge(fpaths []string, opts *MergeOptions) Printer {
func (p *merge) Print(destination string) error { func (p *merge) Print(destination string) error {
const op = "printer.merge.Print" const op = "printer.merge.Print"
if p.ctx == nil { if p.ctx == nil {
// FIXME duration not working with float ctx, cancel := timeout.Context(p.opts.WaitTimeout)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(p.opts.WaitTimeout)*time.Second)
defer cancel() defer cancel()
p.ctx = ctx p.ctx = ctx
} }

View File

@@ -7,10 +7,10 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"sync" "sync"
"time"
"github.com/labstack/gommon/random" "github.com/labstack/gommon/random"
"github.com/thecodingmachine/gotenberg/internal/pkg/standarderror" "github.com/thecodingmachine/gotenberg/internal/pkg/standarderror"
"github.com/thecodingmachine/gotenberg/internal/pkg/timeout"
) )
type office struct { type office struct {
@@ -35,8 +35,7 @@ func NewOffice(fpaths []string, opts *OfficeOptions) Printer {
func (p *office) Print(destination string) error { func (p *office) Print(destination string) error {
const op = "printer.office.Print" const op = "printer.office.Print"
// FIXME duration not working with float ctx, cancel := timeout.Context(p.opts.WaitTimeout)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(p.opts.WaitTimeout)*time.Second)
defer cancel() defer cancel()
fpaths := make([]string, len(p.fpaths)) fpaths := make([]string, len(p.fpaths))
dirPath := filepath.Dir(destination) dirPath := filepath.Dir(destination)

View File

@@ -0,0 +1,3 @@
// Package timeout helps managing
// context with timeout.
package timeout

View File

@@ -0,0 +1,43 @@
package timeout
import (
"context"
"strings"
"time"
"github.com/thecodingmachine/gotenberg/internal/pkg/standarderror"
)
// Context creates a context with timeout for
// given second.
func Context(seconds float64) (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), Duration(seconds))
}
// Duration creates a duration from seconds.
func Duration(seconds float64) time.Duration {
return time.Duration(1000*seconds) * time.Millisecond
}
// Err returns a standarderror.Error
// if the context has an error.
func Err(ctx context.Context) error {
const op = "timeout.Err"
err := ctx.Err()
if err == nil {
return nil
}
if strings.Contains(err.Error(), context.DeadlineExceeded.Error()) {
return &standarderror.Error{
Code: standarderror.Timeout,
Message: "context has timed out",
Op: op,
Err: err,
}
}
return &standarderror.Error{
Message: "context finished with an error",
Op: op,
Err: err,
}
}

View File

@@ -0,0 +1,38 @@
package timeout
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/thecodingmachine/gotenberg/internal/pkg/standarderror"
"github.com/thecodingmachine/gotenberg/test"
)
func TestDuration(t *testing.T) {
expected := time.Duration(1500) * time.Millisecond
result := Duration(1.5)
assert.Equal(t, expected.String(), result.String())
}
func TestErr(t *testing.T) {
// should be OK.
ctx, cancel := Context(5)
defer cancel()
assert.Nil(t, Err(ctx))
// should timeout.
ctx, cancel = Context(0.5)
defer cancel()
time.Sleep(Duration(1))
err := Err(ctx)
assert.NotNil(t, err)
standardized := test.RequireStandardError(t, err)
assert.Equal(t, standardized.Code, standarderror.Timeout)
// should failed.
ctx, cancel = Context(5)
cancel()
err = Err(ctx)
assert.NotNil(t, err)
standardized = test.RequireStandardError(t, err)
assert.Equal(t, standarderror.Code(err), standarderror.Internal)
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/thecodingmachine/gotenberg/internal/pkg/standarderror"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -39,6 +40,15 @@ func AssertConcurrent(t *testing.T, fn func() error, amount int) {
assert.NoError(t, err) assert.NoError(t, err)
} }
// RequireStandardError validates that given error
// is of an instance of standarderror.Error.
// If so, returns the instance of standarderror.Error.
func RequireStandardError(t *testing.T, err error) *standarderror.Error {
standardized, ok := err.(*standarderror.Error)
require.Equal(t, ok, true)
return standardized
}
// HTMLTestMultipartForm returns the body // HTMLTestMultipartForm returns the body
// for a multipate/form-data request with all // for a multipate/form-data request with all
// files under "html" folder. // files under "html" folder.