diff --git a/Makefile b/Makefile index 53885c12..ece45530 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ DEFAULT_LISTEN_PORT=3000 DISABLE_GOOGLE_CHROME=0 DISABLE_UNOCONV=0 LOG_LEVEL=INFO +DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE=1048576 # build the base Docker image. base: @@ -54,7 +55,7 @@ image: # start the API using previously built Docker image. gotenberg: - docker run -it --rm -e MAXIMUM_WAIT_TIMEOUT=$(MAXIMUM_WAIT_TIMEOUT) -e MAXIMUM_WAIT_DELAY=$(MAXIMUM_WAIT_DELAY) -e MAXIMUM_WEBHOOK_URL_TIMEOUT=$(MAXIMUM_WEBHOOK_URL_TIMEOUT) -e DEFAULT_WEBHOOK_URL_TIMEOUT=$(DEFAULT_WEBHOOK_URL_TIMEOUT) -e MAXIMUM_WEBHOOK_URL_TIMEOUT=$(MAXIMUM_WEBHOOK_URL_TIMEOUT) -e DEFAULT_LISTEN_PORT=$(DEFAULT_LISTEN_PORT) -e DISABLE_GOOGLE_CHROME=$(DISABLE_GOOGLE_CHROME) -e DISABLE_UNOCONV=$(DISABLE_UNOCONV) -e LOG_LEVEL=$(LOG_LEVEL) -p "$(DEFAULT_LISTEN_PORT):$(DEFAULT_LISTEN_PORT)" $(DOCKER_REPOSITORY)/gotenberg:$(VERSION) + docker run -it --rm -e MAXIMUM_WAIT_TIMEOUT=$(MAXIMUM_WAIT_TIMEOUT) -e MAXIMUM_WAIT_DELAY=$(MAXIMUM_WAIT_DELAY) -e MAXIMUM_WEBHOOK_URL_TIMEOUT=$(MAXIMUM_WEBHOOK_URL_TIMEOUT) -e DEFAULT_WEBHOOK_URL_TIMEOUT=$(DEFAULT_WEBHOOK_URL_TIMEOUT) -e MAXIMUM_WEBHOOK_URL_TIMEOUT=$(MAXIMUM_WEBHOOK_URL_TIMEOUT) -e DEFAULT_LISTEN_PORT=$(DEFAULT_LISTEN_PORT) -e DISABLE_GOOGLE_CHROME=$(DISABLE_GOOGLE_CHROME) -e DISABLE_UNOCONV=$(DISABLE_UNOCONV) -e LOG_LEVEL=$(LOG_LEVEL) -e DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE=$(DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE) -p "$(DEFAULT_LISTEN_PORT):$(DEFAULT_LISTEN_PORT)" $(DOCKER_REPOSITORY)/gotenberg:$(VERSION) # publish Gotenberg images according to version. publish: diff --git a/build/docs/content/03-environment-variables.md b/build/docs/content/03-environment-variables.md index 7e3c6b97..e1f18731 100644 --- a/build/docs/content/03-environment-variables.md +++ b/build/docs/content/03-environment-variables.md @@ -33,6 +33,19 @@ It takes the strings `"0"` or `"1"` as value where `1` means `true` > If Google Chrome is disabled, the following conversions will **not** be available anymore: > [HTML](#html), [URL](#url) and [Markdown](#markdown) +## Default Google Chrome rpcc buffer size + +When performing a [HTML](#html), [URL](#url) or [Markdown](#markdown) conversion, the API might return +a `400` HTTP code with the message `increase the Google Chrome rpcc buffer size`. + +If so, you may increase this buffer size with the environment variable `DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE`. + +It takes a string representation of an int as value (e.g. `"1048576"` for 1 MB). +The hard limit is 100 MB and is defined by Google Chrome itself. + +> The default Google Chrome rpcc buffer size may also be overridden per request thanks to the form field `googleChromeRpccBufferSize`. +> See the [rpcc buffer size section](#html.rpcc_buffer_size). + ## Disable LibreOffice (unoconv) You may also disable LibreOffice (unoconv) with `DISABLE_UNOCONV`. diff --git a/build/docs/content/04-html.md b/build/docs/content/04-html.md index 4eae9357..3f147df8 100644 --- a/build/docs/content/04-html.md +++ b/build/docs/content/04-html.md @@ -347,3 +347,55 @@ $request->setWaitDelay(5.5); $dest = "result.pdf"; $client->store($request, $dest); ``` + +## Rpcc buffer size + +The API might return a `400` HTTP code with the message `increase the Google Chrome rpcc buffer size`. + +If so, you may increase this buffer size with a form field named `googleChromeRpccBufferSize`. + +It takes an int as value (e.g. `1048576` for 1 MB). +The hard limit is 100 MB and is defined by Google Chrome itself. + +> You may also define this value globally: see the [environment variables](#environment_variables.default_google_chrome_rpcc_buffer_size) section. + +### cURL + +```bash +$ curl --request POST \ + --url http://localhost:3000/convert/html \ + --header 'Content-Type: multipart/form-data' \ + --form files=@index.html \ + --form googleChromeRpccBufferSize=1048576 \ + -o result.pdf +``` + +### Go + +```golang +import "github.com/thecodingmachine/gotenberg-go-client/v6" + +func main() { + c := &gotenberg.Client{Hostname: "http://localhost:3000"} + req, _ := gotenberg.NewHTMLRequest("index.html") + req.GoogleChromeRpccBufferSize(1048576) + dest := "result.pdf" + c.Store(req, dest) +} +``` + +### PHP + +```php +use TheCodingMachine\Gotenberg\Client; +use TheCodingMachine\Gotenberg\DocumentFactory; +use TheCodingMachine\Gotenberg\HTMLRequest; +use TheCodingMachine\Gotenberg\Request; + +$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); +$index = DocumentFactory::makeFromPath('index.html', 'index.html'); +$request = new HTMLRequest($index); +$request->setGoogleChromeRpccBufferSize(1048576); +$dest = "result.pdf"; +$client->store($request, $dest); +``` diff --git a/docs/index.html b/docs/index.html index 9b004cb1..79c8d652 100755 --- a/docs/index.html +++ b/docs/index.html @@ -263,6 +263,23 @@ for disabling Google Chrome.

HTML, URL and Markdown

+

Default Google Chrome rpcc buffer size

+ +

When performing a HTML, URL or Markdown conversion, the API might return +a 400 HTTP code with the message increase the Google Chrome rpcc buffer size.

+ +

If so, you may increase this buffer size with the environment variable DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE.

+ +

It takes a string representation of an int as value (e.g. "1048576" for 1 MB). +The hard limit is 100 MB and is defined by Google Chrome itself.

+ +
+

The default Google Chrome rpcc buffer size may also be overridden per request thanks to the form field googleChromeRpccBufferSize. +See the rpcc buffer size section.

+
+

Disable LibreOffice (unoconv)

@@ -724,6 +741,65 @@ $request = new HTMLRequest($index); $request->setWaitDelay(5.5); $dest = "result.pdf"; $client->store($request, $dest); + + +

Rpcc buffer size

+ +

The API might return a 400 HTTP code with the message increase the Google Chrome rpcc buffer size.

+ +

If so, you may increase this buffer size with a form field named googleChromeRpccBufferSize.

+ +

It takes an int as value (e.g. 1048576 for 1 MB). +The hard limit is 100 MB and is defined by Google Chrome itself.

+ +
+

You may also define this value globally: see the environment variables section.

+
+ +

cURL

+ +
$ curl --request POST \
+    --url http://localhost:3000/convert/html \
+    --header 'Content-Type: multipart/form-data' \
+    --form files=@index.html \
+    --form googleChromeRpccBufferSize=1048576 \
+    -o result.pdf
+
+ +

Go

+ +
import "github.com/thecodingmachine/gotenberg-go-client/v6"
+
+func main() {
+    c := &gotenberg.Client{Hostname: "http://localhost:3000"}
+    req, _ := gotenberg.NewHTMLRequest("index.html")
+    req.GoogleChromeRpccBufferSize(1048576)
+    dest := "result.pdf"
+    c.Store(req, dest)
+}
+
+ +

PHP

+ +
use TheCodingMachine\Gotenberg\Client;
+use TheCodingMachine\Gotenberg\DocumentFactory;
+use TheCodingMachine\Gotenberg\HTMLRequest;
+use TheCodingMachine\Gotenberg\Request;
+
+$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
+$index = DocumentFactory::makeFromPath('index.html', 'index.html');
+$request = new HTMLRequest($index);
+$request->setGoogleChromeRpccBufferSize(1048576);
+$dest = "result.pdf";
+$client->store($request, $dest);
 
diff --git a/internal/app/xhttp/handler_test.go b/internal/app/xhttp/handler_test.go index e61d0985..df63d2a8 100644 --- a/internal/app/xhttp/handler_test.go +++ b/internal/app/xhttp/handler_test.go @@ -184,6 +184,24 @@ func TestHTMLHandler(t *testing.T) { req = httptest.NewRequest(http.MethodPost, endpoint, body) req.Header.Set(echo.HeaderContentType, contentType) test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is < 0. + body, contentType = test.HTMLMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "-1"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is is > config.MaximumGoogleChromeRpccBufferSize(). + body, contentType = test.HTMLMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "104857601"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is invalid. + body, contentType = test.HTMLMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "not an int"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) } func TestURLHandler(t *testing.T) { @@ -314,6 +332,24 @@ func TestURLHandler(t *testing.T) { req = httptest.NewRequest(http.MethodPost, endpoint, body) req.Header.Set(echo.HeaderContentType, contentType) test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is < 0. + body, contentType = test.URLMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "-1"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is is > config.MaximumGoogleChromeRpccBufferSize(). + body, contentType = test.URLMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "104857601"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is invalid. + body, contentType = test.URLMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "not an int"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) } func TestMarkdownHandler(t *testing.T) { @@ -444,6 +480,24 @@ func TestMarkdownHandler(t *testing.T) { req = httptest.NewRequest(http.MethodPost, endpoint, body) req.Header.Set(echo.HeaderContentType, contentType) test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is < 0. + body, contentType = test.MarkdownMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "-1"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is is > config.MaximumGoogleChromeRpccBufferSize(). + body, contentType = test.MarkdownMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "104857601"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) + // should return 400 as "googleChromeRpccBufferSize" form field + // value is invalid. + body, contentType = test.MarkdownMultipartForm(t, map[string]string{string(resource.GoogleChromeRpccBufferSizeArgKey): "not an int"}) + req = httptest.NewRequest(http.MethodPost, endpoint, body) + req.Header.Set(echo.HeaderContentType, contentType) + test.AssertStatusCode(t, http.StatusBadRequest, srv, req) } func TestOfficeHandler(t *testing.T) { diff --git a/internal/app/xhttp/option.go b/internal/app/xhttp/option.go index d79841e8..2afb6984 100644 --- a/internal/app/xhttp/option.go +++ b/internal/app/xhttp/option.go @@ -48,18 +48,23 @@ func chromePrinterOptions(r resource.Resource, config conf.Config) (printer.Chro if err != nil { return printer.ChromePrinterOptions{}, err } + googleChromeRpccBufferSize, err := resource.GoogleChromeRpccBufferSizeArg(r, config) + if err != nil { + return printer.ChromePrinterOptions{}, err + } return printer.ChromePrinterOptions{ - WaitTimeout: waitTimeout, - WaitDelay: waitDelay, - HeaderHTML: headerHTML, - FooterHTML: footerHTML, - PaperWidth: paperWidth, - PaperHeight: paperHeight, - MarginTop: marginTop, - MarginBottom: marginBottom, - MarginLeft: marginLeft, - MarginRight: marginRight, - Landscape: landscape, + WaitTimeout: waitTimeout, + WaitDelay: waitDelay, + HeaderHTML: headerHTML, + FooterHTML: footerHTML, + PaperWidth: paperWidth, + PaperHeight: paperHeight, + MarginTop: marginTop, + MarginBottom: marginBottom, + MarginLeft: marginLeft, + MarginRight: marginRight, + Landscape: landscape, + RpccBufferSize: googleChromeRpccBufferSize, }, nil } opts, err := resolver() diff --git a/internal/app/xhttp/pkg/resource/arg.go b/internal/app/xhttp/pkg/resource/arg.go index 35c7800b..706e8952 100644 --- a/internal/app/xhttp/pkg/resource/arg.go +++ b/internal/app/xhttp/pkg/resource/arg.go @@ -51,6 +51,9 @@ const ( // LandscapeArgKey is the key // of the argument "landscape". LandscapeArgKey ArgKey = "landscape" + // GoogleChromeRpccBufferSizeArgKey is the key + // of the argument "googleChromeRpccBufferSize". + GoogleChromeRpccBufferSizeArgKey ArgKey = "googleChromeRpccBufferSize" ) /* @@ -73,6 +76,7 @@ func ArgKeys() []ArgKey { MarginLeftArgKey, MarginRightArgKey, LandscapeArgKey, + GoogleChromeRpccBufferSizeArgKey, } } @@ -265,3 +269,24 @@ func MarginArgs(r Resource, config conf.Config) (float64, float64, float64, floa marginRight, nil } + +/* +GoogleChromeRpccBufferSizeArg is a helper for retrieving +the "googleChromeRpccBufferSize" argument as int64. + +It also validates it against the application +configuration. +*/ +func GoogleChromeRpccBufferSizeArg(r Resource, config conf.Config) (int64, error) { + const op string = "resource.GoogleChromeRpccBufferSizeArg" + result, err := r.Int64Arg( + GoogleChromeRpccBufferSizeArgKey, + config.DefaultGoogleChromeRpccBufferSize(), + xassert.Int64NotInferiorTo(0.0), + xassert.Int64NotSuperiorTo(config.MaximumGoogleChromeRpccBufferSize()), + ) + if err != nil { + return result, xerror.New(op, err) + } + return result, nil +} diff --git a/internal/app/xhttp/pkg/resource/arg_test.go b/internal/app/xhttp/pkg/resource/arg_test.go index 3c7f6b02..1485c5d5 100644 --- a/internal/app/xhttp/pkg/resource/arg_test.go +++ b/internal/app/xhttp/pkg/resource/arg_test.go @@ -24,6 +24,7 @@ func TestArgKeys(t *testing.T) { MarginLeftArgKey, MarginRightArgKey, LandscapeArgKey, + GoogleChromeRpccBufferSizeArgKey, } assert.Equal(t, expected, ArgKeys()) } @@ -300,3 +301,47 @@ func TestMarginArgs(t *testing.T) { err = r.Close() assert.Nil(t, err) } + +func TestGoogleChromeRpccBufferSizeArg(t *testing.T) { + const resourceDirectoryName string = "foo" + var expected int64 + logger := test.DebugLogger() + config := conf.DefaultConfig() + r, err := New(logger, resourceDirectoryName) + assert.Nil(t, err) + // argument does not exist. + expected = config.DefaultGoogleChromeRpccBufferSize() + v, err := GoogleChromeRpccBufferSizeArg(r, config) + assert.Nil(t, err) + assert.Equal(t, expected, v) + // argument exist. + expected = 10 + r.WithArg(GoogleChromeRpccBufferSizeArgKey, "10") + v, err = GoogleChromeRpccBufferSizeArg(r, config) + assert.Nil(t, err) + assert.Equal(t, expected, v) + // should not be OK as argument + // value is < 0. + expected = config.DefaultGoogleChromeRpccBufferSize() + r.WithArg(GoogleChromeRpccBufferSizeArgKey, "-1") + v, err = GoogleChromeRpccBufferSizeArg(r, config) + test.AssertError(t, err) + assert.Equal(t, expected, v) + // should not be OK as argument + // value is > config.MaximumGoogleChromeRpccBufferSize(). + expected = config.DefaultGoogleChromeRpccBufferSize() + r.WithArg(GoogleChromeRpccBufferSizeArgKey, "104857601") + v, err = GoogleChromeRpccBufferSizeArg(r, config) + test.AssertError(t, err) + assert.Equal(t, expected, v) + // should not be OK as + // argument value is invalid. + expected = config.DefaultGoogleChromeRpccBufferSize() + r.WithArg(GoogleChromeRpccBufferSizeArgKey, "foo") + v, err = GoogleChromeRpccBufferSizeArg(r, config) + test.AssertError(t, err) + assert.Equal(t, expected, v) + // finally... + err = r.Close() + assert.Nil(t, err) +} diff --git a/internal/pkg/conf/conf.go b/internal/pkg/conf/conf.go index d0aca24a..89100c73 100644 --- a/internal/pkg/conf/conf.go +++ b/internal/pkg/conf/conf.go @@ -34,35 +34,42 @@ const ( // LogLevelEnvVar contains the name // of the environment variable "LOG_LEVEL". LogLevelEnvVar string = "LOG_LEVEL" + // DefaultGoogleChromeRpccBufferSizeEnvVar contains the name + // of the environment variable "DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE". + DefaultGoogleChromeRpccBufferSizeEnvVar string = "DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE" ) // Config contains the application // configuration. type Config struct { - maximumWaitTimeout float64 - maximumWaitDelay float64 - maximumWebhookURLTimeout float64 - defaultWaitTimeout float64 - defaultWebhookURLTimeout float64 - defaultListenPort int64 - disableGoogleChrome bool - disableUnoconv bool - logLevel xlog.Level + maximumWaitTimeout float64 + maximumWaitDelay float64 + maximumWebhookURLTimeout float64 + defaultWaitTimeout float64 + defaultWebhookURLTimeout float64 + defaultListenPort int64 + disableGoogleChrome bool + disableUnoconv bool + logLevel xlog.Level + maximumGoogleChromeRpccBufferSize int64 + defaultGoogleChromeRpccBufferSize int64 } // DefaultConfig returns the default // configuration. func DefaultConfig() Config { return Config{ - maximumWaitTimeout: 30.0, - maximumWaitDelay: 10.0, - maximumWebhookURLTimeout: 30.0, - defaultWaitTimeout: 10.0, - defaultWebhookURLTimeout: 10.0, - defaultListenPort: 3000, - disableGoogleChrome: false, - disableUnoconv: false, - logLevel: xlog.InfoLevel, + maximumWaitTimeout: 30.0, + maximumWaitDelay: 10.0, + maximumWebhookURLTimeout: 30.0, + defaultWaitTimeout: 10.0, + defaultWebhookURLTimeout: 10.0, + defaultListenPort: 3000, + disableGoogleChrome: false, + disableUnoconv: false, + logLevel: xlog.InfoLevel, + maximumGoogleChromeRpccBufferSize: 104857600, // ~100 MB + defaultGoogleChromeRpccBufferSize: 1048576, // 1 MB } } @@ -156,6 +163,16 @@ func FromEnv() (Config, error) { if err != nil { return c, err } + defaultGoogleChromeRpccBufferSize, err := xassert.Int64FromEnv( + DefaultGoogleChromeRpccBufferSizeEnvVar, + c.defaultGoogleChromeRpccBufferSize, + xassert.Int64NotInferiorTo(0), + xassert.Int64NotSuperiorTo(c.MaximumGoogleChromeRpccBufferSize()), + ) + c.defaultGoogleChromeRpccBufferSize = defaultGoogleChromeRpccBufferSize + if err != nil { + return c, err + } return c, nil } result, err := resolver() @@ -224,3 +241,15 @@ func (c Config) DisableUnoconv() bool { func (c Config) LogLevel() xlog.Level { return c.logLevel } + +// MaximumGoogleChromeRpccBufferSize returns the maximum +// Google Chrome rpcc buffer size from the configuration. +func (c Config) MaximumGoogleChromeRpccBufferSize() int64 { + return c.maximumGoogleChromeRpccBufferSize +} + +// DefaultGoogleChromeRpccBufferSize returns the default +// Google Chrome rpcc buffer size from the configuration. +func (c Config) DefaultGoogleChromeRpccBufferSize() int64 { + return c.defaultGoogleChromeRpccBufferSize +} diff --git a/internal/pkg/conf/conf_test.go b/internal/pkg/conf/conf_test.go index 262aa079..45ed01da 100644 --- a/internal/pkg/conf/conf_test.go +++ b/internal/pkg/conf/conf_test.go @@ -320,6 +320,43 @@ func TestLogLevelFromEnv(t *testing.T) { os.Unsetenv(LogLevelEnvVar) } +func TestDefaultGoogleChromeRpccBufferSizeFromEnv(t *testing.T) { + var ( + expected Config + result Config + err error + ) + // DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE correctly set. + os.Setenv(DefaultGoogleChromeRpccBufferSizeEnvVar, "100") + expected = DefaultConfig() + expected.defaultGoogleChromeRpccBufferSize = 100 + result, err = FromEnv() + assert.Nil(t, err) + assert.Equal(t, expected, result) + os.Unsetenv(DefaultGoogleChromeRpccBufferSizeEnvVar) + // DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE wrongly set. + os.Setenv(DefaultGoogleChromeRpccBufferSizeEnvVar, "foo") + expected = DefaultConfig() + result, err = FromEnv() + test.AssertError(t, err) + assert.Equal(t, expected, result) + os.Unsetenv(DefaultGoogleChromeRpccBufferSizeEnvVar) + // DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE < 0. + os.Setenv(DefaultGoogleChromeRpccBufferSizeEnvVar, "-1") + expected = DefaultConfig() + result, err = FromEnv() + test.AssertError(t, err) + assert.Equal(t, expected, result) + os.Unsetenv(DefaultGoogleChromeRpccBufferSizeEnvVar) + // DEFAULT_GOOGLE_CHROME_RPCC_BUFFER_SIZE > 100 MB (maximumGoogleChromeRpccBufferSize). + os.Setenv(DefaultGoogleChromeRpccBufferSizeEnvVar, "104857601") + expected = DefaultConfig() + result, err = FromEnv() + test.AssertError(t, err) + assert.Equal(t, expected, result) + os.Unsetenv(DefaultGoogleChromeRpccBufferSizeEnvVar) +} + func TestGetters(t *testing.T) { result := DefaultConfig() assert.Equal(t, result.maximumWaitTimeout, result.MaximumWaitTimeout()) @@ -331,4 +368,6 @@ func TestGetters(t *testing.T) { assert.Equal(t, result.disableGoogleChrome, result.DisableGoogleChrome()) assert.Equal(t, result.disableUnoconv, result.DisableUnoconv()) assert.Equal(t, result.logLevel, result.LogLevel()) + assert.Equal(t, result.maximumGoogleChromeRpccBufferSize, result.MaximumGoogleChromeRpccBufferSize()) + assert.Equal(t, result.defaultGoogleChromeRpccBufferSize, result.DefaultGoogleChromeRpccBufferSize()) } diff --git a/internal/pkg/printer/chrome.go b/internal/pkg/printer/chrome.go index aff21e75..8f1cc473 100644 --- a/internal/pkg/printer/chrome.go +++ b/internal/pkg/printer/chrome.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/ioutil" + "strings" "time" "github.com/mafredri/cdp" @@ -29,17 +30,18 @@ type chromePrinter struct { // ChromePrinterOptions helps customizing the // Google Chrome Printer behaviour. type ChromePrinterOptions struct { - WaitTimeout float64 - WaitDelay float64 - HeaderHTML string - FooterHTML string - PaperWidth float64 - PaperHeight float64 - MarginTop float64 - MarginBottom float64 - MarginLeft float64 - MarginRight float64 - Landscape bool + WaitTimeout float64 + WaitDelay float64 + HeaderHTML string + FooterHTML string + PaperWidth float64 + PaperHeight float64 + MarginTop float64 + MarginBottom float64 + MarginLeft float64 + MarginRight float64 + Landscape bool + RpccBufferSize int64 } // DefaultChromePrinterOptions returns the default @@ -47,17 +49,18 @@ type ChromePrinterOptions struct { func DefaultChromePrinterOptions(config conf.Config) ChromePrinterOptions { const defaultHeaderFooterHTML string = "" return ChromePrinterOptions{ - WaitTimeout: config.DefaultWaitTimeout(), - WaitDelay: 0.0, - HeaderHTML: defaultHeaderFooterHTML, - FooterHTML: defaultHeaderFooterHTML, - PaperWidth: 8.27, - PaperHeight: 11.7, - MarginTop: 1.0, - MarginBottom: 1.0, - MarginLeft: 1.0, - MarginRight: 1.0, - Landscape: false, + WaitTimeout: config.DefaultWaitTimeout(), + WaitDelay: 0.0, + HeaderHTML: defaultHeaderFooterHTML, + FooterHTML: defaultHeaderFooterHTML, + PaperWidth: 8.27, + PaperHeight: 11.7, + MarginTop: 1.0, + MarginBottom: 1.0, + MarginLeft: 1.0, + MarginRight: 1.0, + Landscape: false, + RpccBufferSize: config.DefaultGoogleChromeRpccBufferSize(), } } @@ -110,7 +113,18 @@ func (p chromePrinter) Print(destination string) error { } // connect the client to the new target. newTargetWsURL := fmt.Sprintf("ws://127.0.0.1:9222/devtools/page/%s", newTarget.TargetID) - newContextConn, err := rpcc.DialContext(ctx, newTargetWsURL) + newContextConn, err := rpcc.DialContext( + ctx, + newTargetWsURL, + /* + see: + https://github.com/thecodingmachine/gotenberg/issues/108 + https://github.com/mafredri/cdp/issues/4 + https://github.com/ChromeDevTools/devtools-protocol/issues/24 + */ + rpcc.WithWriteBufferSize(int(p.opts.RpccBufferSize)), + rpcc.WithCompression(), + ) if err != nil { return err } @@ -159,6 +173,16 @@ func (p chromePrinter) Print(destination string) error { SetPrintBackground(true), ) if err != nil { + if strings.Contains(err.Error(), "rpcc: message too large") { + return xerror.Invalid( + op, + fmt.Sprintf( + "'%d' bytes are not enough: increase the Google Chrome rpcc buffer size (up to 100 MB)", + p.opts.RpccBufferSize, + ), + err, + ) + } return err } if err := ioutil.WriteFile(destination, print.Data, 0644); err != nil {