mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-13 10:52:15 +01:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e69ec4367 | ||
|
|
4623c01896 | ||
|
|
e85322de62 | ||
|
|
090ff3da13 | ||
|
|
fe068b1bfb | ||
|
|
d3b0a685b9 | ||
|
|
245c04fc5a | ||
|
|
94acb0c6ae | ||
|
|
ad4a16cc4e | ||
|
|
dae39113f6 | ||
|
|
1e1cb8fe57 | ||
|
|
606254d941 | ||
|
|
8fd51b9e9e | ||
|
|
f90442cfb1 | ||
|
|
2a44c0f65a |
@@ -47,5 +47,9 @@ securityContext:
|
|||||||
runAsUser: 1001
|
runAsUser: 1001
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Cloud Run (Google Cloud)
|
||||||
|
|
||||||
|
If you're looking for cost savings, you might be interested by [Cloud Run](https://cloud.google.com/run).
|
||||||
|
|
||||||
In the following examples, we will assume your
|
In the following examples, we will assume your
|
||||||
Gotenberg API is available at [http://localhost:3000](http://localhost:3000).
|
Gotenberg API is available at [http://localhost:3000](http://localhost:3000).
|
||||||
|
|||||||
@@ -23,3 +23,7 @@ Then the PHP client:
|
|||||||
```bash
|
```bash
|
||||||
$ composer require thecodingmachine/gotenberg-php-client
|
$ composer require thecodingmachine/gotenberg-php-client
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Community clients
|
||||||
|
|
||||||
|
* [JavaScript/TypeScript client](https://github.com/yumauri/gotenberg-js-client) by [yumauri](https://github.com/yumauri)
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ There are some limitations:
|
|||||||
* `footer.html` CSS properties override the ones from `header.html`
|
* `footer.html` CSS properties override the ones from `header.html`
|
||||||
* only fonts installed in the Docker image are loaded (see the [fonts section](#fonts))
|
* only fonts installed in the Docker image are loaded (see the [fonts section](#fonts))
|
||||||
* images only work using a `base64` encoded source (`<img src="data:image/png;base64, iVBORw0K... />`)
|
* images only work using a `base64` encoded source (`<img src="data:image/png;base64, iVBORw0K... />`)
|
||||||
|
* `background-color` and `color` CSS properties require an additional `-webkit-print-color-adjust: exact` CSS property in order to work
|
||||||
|
|
||||||
### cURL
|
### cURL
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ title: Scalability
|
|||||||
The API uses under the hood intricate programs.
|
The API uses under the hood intricate programs.
|
||||||
|
|
||||||
Gotenberg tries to abstract as much complexity as possible but it can
|
Gotenberg tries to abstract as much complexity as possible but it can
|
||||||
only do it to a certain extend.
|
only do it to a certain extent.
|
||||||
|
|
||||||
For instance, [Office](#office) and [Merge](#merge) endpoints will start respectively as many LibreOffice (unoconv) and PDTk
|
For instance, [Office](#office) and [Merge](#merge) endpoints will start respectively as many LibreOffice (unoconv) and PDTk
|
||||||
instances are there are requests. The limitation here is the available memory and CPU usage.
|
instances as there are requests. The limitation here is the available memory and CPU usage.
|
||||||
|
|
||||||
On another hand, for the [HTML](#html), [URL](#url) and [Markdown](#markdown) endpoints, the API does only 6 conversions in parallel.
|
On another hand, for the [HTML](#html), [URL](#url) and [Markdown](#markdown) endpoints, the API does only 6 conversions in parallel.
|
||||||
Indeed, Google Chrome misbehaves if there are too many concurrent conversions.
|
Indeed, Google Chrome misbehaves if there are too many concurrent conversions.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# | Binary
|
# | Binary
|
||||||
# |--------------------------------------------------------------------------
|
# |--------------------------------------------------------------------------
|
||||||
# |
|
# |
|
||||||
# | Buils Gotenberg binary.
|
# | Builds Gotenberg binary.
|
||||||
# |
|
# |
|
||||||
|
|
||||||
FROM thecodingmachine/gotenberg:workspace AS workspace
|
FROM thecodingmachine/gotenberg:workspace AS workspace
|
||||||
@@ -42,7 +42,7 @@ LABEL authors="Julien Neuhart <j.neuhart@thecodingmachine.com>"
|
|||||||
|
|
||||||
ARG TINI_VERSION
|
ARG TINI_VERSION
|
||||||
|
|
||||||
ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tini
|
ADD --chown=gotenberg https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static /tini
|
||||||
RUN chmod +x /tini
|
RUN chmod +x /tini
|
||||||
ENTRYPOINT [ "/tini", "--" ]
|
ENTRYPOINT [ "/tini", "--" ]
|
||||||
|
|
||||||
@@ -59,4 +59,4 @@ USER gotenberg
|
|||||||
WORKDIR /gotenberg
|
WORKDIR /gotenberg
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
CMD [ "gotenberg" ]
|
CMD [ "gotenberg" ]
|
||||||
|
|||||||
@@ -184,6 +184,12 @@
|
|||||||
runAsUser: 1001
|
runAsUser: 1001
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="install.cloud_run_google_cloud" href="#install.cloud_run_google_cloud">
|
||||||
|
<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>
|
||||||
|
</a>Cloud Run (Google Cloud)</h2>
|
||||||
|
|
||||||
|
<p>If you’re looking for cost savings, you might be interested by <a href="https://cloud.google.com/run">Cloud Run</a>.</p>
|
||||||
|
|
||||||
<p>In the following examples, we will assume your
|
<p>In the following examples, we will assume your
|
||||||
Gotenberg API is available at <a href="http://localhost:3000">http://localhost:3000</a>.</p>
|
Gotenberg API is available at <a href="http://localhost:3000">http://localhost:3000</a>.</p>
|
||||||
|
|
||||||
@@ -216,6 +222,14 @@ Gotenberg API is available at <a href="http://localhost:3000">http://localhost:3
|
|||||||
<pre class="chroma">$ composer require thecodingmachine/gotenberg-php-client
|
<pre class="chroma">$ composer require thecodingmachine/gotenberg-php-client
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="clients.community_clients" href="#clients.community_clients">
|
||||||
|
<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>
|
||||||
|
</a>Community clients</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://github.com/yumauri/gotenberg-js-client">JavaScript/TypeScript client</a> by <a href="https://github.com/yumauri">yumauri</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="Page" id="environment_variables">
|
<div class="Page" id="environment_variables">
|
||||||
@@ -475,6 +489,7 @@ Respectively, a file named <code>header.html</code> and <code>footer.html</code>
|
|||||||
<li><code>footer.html</code> CSS properties override the ones from <code>header.html</code></li>
|
<li><code>footer.html</code> CSS properties override the ones from <code>header.html</code></li>
|
||||||
<li>only fonts installed in the Docker image are loaded (see the <a href="#fonts">fonts section</a>)</li>
|
<li>only fonts installed in the Docker image are loaded (see the <a href="#fonts">fonts section</a>)</li>
|
||||||
<li>images only work using a <code>base64</code> encoded source (<code><img src="data:image/png;base64, iVBORw0K... /></code>)</li>
|
<li>images only work using a <code>base64</code> encoded source (<code><img src="data:image/png;base64, iVBORw0K... /></code>)</li>
|
||||||
|
<li><code>background-color</code> and <code>color</code> CSS properties require an additional <code>-webkit-print-color-adjust: exact</code> CSS property in order to work</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.header_and_footer.c_url" href="#html.header_and_footer.c_url">
|
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="html.header_and_footer.c_url" href="#html.header_and_footer.c_url">
|
||||||
@@ -1394,10 +1409,10 @@ $resp = $client->post($request);
|
|||||||
<p>The API uses under the hood intricate programs.</p>
|
<p>The API uses under the hood intricate programs.</p>
|
||||||
|
|
||||||
<p>Gotenberg tries to abstract as much complexity as possible but it can
|
<p>Gotenberg tries to abstract as much complexity as possible but it can
|
||||||
only do it to a certain extend.</p>
|
only do it to a certain extent.</p>
|
||||||
|
|
||||||
<p>For instance, <a href="#office">Office</a> and <a href="#merge">Merge</a> endpoints will start respectively as many LibreOffice (unoconv) and PDTk
|
<p>For instance, <a href="#office">Office</a> and <a href="#merge">Merge</a> endpoints will start respectively as many LibreOffice (unoconv) and PDTk
|
||||||
instances are there are requests. The limitation here is the available memory and CPU usage.</p>
|
instances as there are requests. The limitation here is the available memory and CPU usage.</p>
|
||||||
|
|
||||||
<p>On another hand, for the <a href="#html">HTML</a>, <a href="#url">URL</a> and <a href="#markdown">Markdown</a> endpoints, the API does only 6 conversions in parallel.
|
<p>On another hand, for the <a href="#html">HTML</a>, <a href="#url">URL</a> and <a href="#markdown">Markdown</a> endpoints, the API does only 6 conversions in parallel.
|
||||||
Indeed, Google Chrome misbehaves if there are too many concurrent conversions.</p>
|
Indeed, Google Chrome misbehaves if there are too many concurrent conversions.</p>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/context"
|
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/context"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/resource"
|
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/resource"
|
||||||
|
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/pkg/printer"
|
"github.com/thecodingmachine/gotenberg/internal/pkg/printer"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/pkg/xrand"
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xrand"
|
||||||
@@ -24,6 +25,31 @@ const (
|
|||||||
officeEndpoint string = "/office"
|
officeEndpoint string = "/office"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func isMultipartFormDataEndpoint(config conf.Config, path string) bool {
|
||||||
|
var multipartFormDataEndpoints []string
|
||||||
|
multipartFormDataEndpoints = append(multipartFormDataEndpoints, mergeEndpoint)
|
||||||
|
if !config.DisableGoogleChrome() {
|
||||||
|
multipartFormDataEndpoints = append(
|
||||||
|
multipartFormDataEndpoints,
|
||||||
|
fmt.Sprintf("%s%s", convertGroupEndpoint, htmlEndpoint),
|
||||||
|
fmt.Sprintf("%s%s", convertGroupEndpoint, urlEndpoint),
|
||||||
|
fmt.Sprintf("%s%s", convertGroupEndpoint, markdownEndpoint),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !config.DisableUnoconv() {
|
||||||
|
multipartFormDataEndpoints = append(
|
||||||
|
multipartFormDataEndpoints,
|
||||||
|
fmt.Sprintf("%s%s", convertGroupEndpoint, officeEndpoint),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for _, endpoint := range multipartFormDataEndpoints {
|
||||||
|
if endpoint == path {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// pingHandler is the handler for healthcheck.
|
// pingHandler is the handler for healthcheck.
|
||||||
func pingHandler(c echo.Context) error {
|
func pingHandler(c echo.Context) error {
|
||||||
const op string = "xhttp.pingHandler"
|
const op string = "xhttp.pingHandler"
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ func TestPingHandler(t *testing.T) {
|
|||||||
srv := New(config)
|
srv := New(config)
|
||||||
req := httptest.NewRequest(http.MethodGet, pingEndpoint, nil)
|
req := httptest.NewRequest(http.MethodGet, pingEndpoint, nil)
|
||||||
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
||||||
|
// should return 405 as Method is wrong.
|
||||||
|
req = httptest.NewRequest(http.MethodPost, pingEndpoint, nil)
|
||||||
|
test.AssertStatusCode(t, http.StatusMethodNotAllowed, srv, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeHandler(t *testing.T) {
|
func TestMergeHandler(t *testing.T) {
|
||||||
@@ -31,6 +34,13 @@ func TestMergeHandler(t *testing.T) {
|
|||||||
req := httptest.NewRequest(http.MethodPost, mergeEndpoint, body)
|
req := httptest.NewRequest(http.MethodPost, mergeEndpoint, body)
|
||||||
req.Header.Set(echo.HeaderContentType, contentType)
|
req.Header.Set(echo.HeaderContentType, contentType)
|
||||||
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
||||||
|
// should return 405 as Method is wrong.
|
||||||
|
req = httptest.NewRequest(http.MethodGet, mergeEndpoint, nil)
|
||||||
|
test.AssertStatusCode(t, http.StatusMethodNotAllowed, srv, req)
|
||||||
|
// should return 415 as Content-Type is wrong.
|
||||||
|
body, _ = test.MergeMultipartForm(t, nil)
|
||||||
|
req = httptest.NewRequest(http.MethodPost, mergeEndpoint, body)
|
||||||
|
test.AssertStatusCode(t, http.StatusUnsupportedMediaType, srv, req)
|
||||||
// should return 400 as "waitTimeout" form field
|
// should return 400 as "waitTimeout" form field
|
||||||
// value is < 0.
|
// value is < 0.
|
||||||
body, contentType = test.MergeMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
body, contentType = test.MergeMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
||||||
@@ -65,6 +75,13 @@ func TestHTMLHandler(t *testing.T) {
|
|||||||
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
req.Header.Set(echo.HeaderContentType, contentType)
|
req.Header.Set(echo.HeaderContentType, contentType)
|
||||||
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
||||||
|
// should return 405 as Method is wrong.
|
||||||
|
req = httptest.NewRequest(http.MethodGet, endpoint, nil)
|
||||||
|
test.AssertStatusCode(t, http.StatusMethodNotAllowed, srv, req)
|
||||||
|
// should return 415 as Content-Type is wrong.
|
||||||
|
body, _ = test.HTMLMultipartForm(t, nil)
|
||||||
|
req = httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
|
test.AssertStatusCode(t, http.StatusUnsupportedMediaType, srv, req)
|
||||||
// should return 400 as "waitTimeout" form field
|
// should return 400 as "waitTimeout" form field
|
||||||
// value is < 0.
|
// value is < 0.
|
||||||
body, contentType = test.HTMLMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
body, contentType = test.HTMLMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
||||||
@@ -213,6 +230,13 @@ func TestURLHandler(t *testing.T) {
|
|||||||
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
req.Header.Set(echo.HeaderContentType, contentType)
|
req.Header.Set(echo.HeaderContentType, contentType)
|
||||||
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
||||||
|
// should return 405 as Method is wrong.
|
||||||
|
req = httptest.NewRequest(http.MethodGet, endpoint, nil)
|
||||||
|
test.AssertStatusCode(t, http.StatusMethodNotAllowed, srv, req)
|
||||||
|
// should return 415 as Content-Type is wrong.
|
||||||
|
body, _ = test.URLMultipartForm(t, nil)
|
||||||
|
req = httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
|
test.AssertStatusCode(t, http.StatusUnsupportedMediaType, srv, req)
|
||||||
// should return 400 as "waitTimeout" form field
|
// should return 400 as "waitTimeout" form field
|
||||||
// value is < 0.
|
// value is < 0.
|
||||||
body, contentType = test.URLMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
body, contentType = test.URLMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
||||||
@@ -361,6 +385,13 @@ func TestMarkdownHandler(t *testing.T) {
|
|||||||
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
req.Header.Set(echo.HeaderContentType, contentType)
|
req.Header.Set(echo.HeaderContentType, contentType)
|
||||||
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
||||||
|
// should return 405 as Method is wrong.
|
||||||
|
req = httptest.NewRequest(http.MethodGet, endpoint, nil)
|
||||||
|
test.AssertStatusCode(t, http.StatusMethodNotAllowed, srv, req)
|
||||||
|
// should return 415 as Content-Type is wrong.
|
||||||
|
body, _ = test.MarkdownMultipartForm(t, nil)
|
||||||
|
req = httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
|
test.AssertStatusCode(t, http.StatusUnsupportedMediaType, srv, req)
|
||||||
// should return 400 as "waitTimeout" form field
|
// should return 400 as "waitTimeout" form field
|
||||||
// value is < 0.
|
// value is < 0.
|
||||||
body, contentType = test.MarkdownMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
body, contentType = test.MarkdownMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
||||||
@@ -509,6 +540,13 @@ func TestOfficeHandler(t *testing.T) {
|
|||||||
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
req := httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
req.Header.Set(echo.HeaderContentType, contentType)
|
req.Header.Set(echo.HeaderContentType, contentType)
|
||||||
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
||||||
|
// should return 405 as Method is wrong.
|
||||||
|
req = httptest.NewRequest(http.MethodGet, endpoint, nil)
|
||||||
|
test.AssertStatusCode(t, http.StatusMethodNotAllowed, srv, req)
|
||||||
|
// should return 415 as Content-Type is wrong.
|
||||||
|
body, _ = test.OfficeMultipartForm(t, nil)
|
||||||
|
req = httptest.NewRequest(http.MethodPost, endpoint, body)
|
||||||
|
test.AssertStatusCode(t, http.StatusUnsupportedMediaType, srv, req)
|
||||||
// should return 400 as "waitTimeout" form field
|
// should return 400 as "waitTimeout" form field
|
||||||
// value is < 0.
|
// value is < 0.
|
||||||
body, contentType = test.OfficeMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
body, contentType = test.OfficeMultipartForm(t, map[string]string{string(resource.WaitTimeoutArgKey): "-1"})
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package xhttp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/context"
|
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/context"
|
||||||
@@ -25,12 +26,28 @@ func contextMiddleware(config conf.Config) echo.MiddlewareFunc {
|
|||||||
// extend the current echo context with our custom
|
// extend the current echo context with our custom
|
||||||
// context.
|
// context.
|
||||||
ctx := context.New(c, logger, config)
|
ctx := context.New(c, logger, config)
|
||||||
// if its an healthcheck request, there
|
// if it's not a multipart/form-data request,
|
||||||
// is no need to create a Resource.
|
// there is no need to create a Resource.
|
||||||
if ctx.Path() == pingEndpoint {
|
if !isMultipartFormDataEndpoint(config, ctx.Path()) {
|
||||||
|
// validate method for healthcheck endpoint.
|
||||||
|
if ctx.Path() == pingEndpoint && ctx.Request().Method != http.MethodGet {
|
||||||
|
err := doErr(ctx, echo.NewHTTPError(http.StatusMethodNotAllowed))
|
||||||
|
return ctx.LogRequestResult(err, false)
|
||||||
|
}
|
||||||
return next(ctx)
|
return next(ctx)
|
||||||
}
|
}
|
||||||
// if the endpoint is not for healthcheck, create a
|
// validate method.
|
||||||
|
if ctx.Request().Method != http.MethodPost {
|
||||||
|
err := doErr(ctx, echo.NewHTTPError(http.StatusMethodNotAllowed))
|
||||||
|
return ctx.LogRequestResult(err, false)
|
||||||
|
}
|
||||||
|
// validate Content-Type.
|
||||||
|
contentType := ctx.Request().Header.Get("Content-Type")
|
||||||
|
if !strings.Contains(contentType, "multipart/form-data") {
|
||||||
|
err := doErr(ctx, echo.NewHTTPError(http.StatusUnsupportedMediaType))
|
||||||
|
return ctx.LogRequestResult(err, false)
|
||||||
|
}
|
||||||
|
// it's a multipart/form-data request, create a
|
||||||
// Resource.
|
// Resource.
|
||||||
if err := ctx.WithResource(trace); err != nil {
|
if err := ctx.WithResource(trace); err != nil {
|
||||||
err = doCleanup(ctx, err)
|
err = doCleanup(ctx, err)
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ import (
|
|||||||
"github.com/thecodingmachine/gotenberg/test"
|
"github.com/thecodingmachine/gotenberg/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestNonExistingEndpoint(t *testing.T) {
|
||||||
|
config, err := conf.FromEnv()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
srv := New(config)
|
||||||
|
// "/" endpoint should return 404.
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
test.AssertStatusCode(t, http.StatusNotFound, srv, req)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDisableChromeEndpoints(t *testing.T) {
|
func TestDisableChromeEndpoints(t *testing.T) {
|
||||||
os.Setenv(conf.DisableGoogleChromeEnvVar, "1")
|
os.Setenv(conf.DisableGoogleChromeEnvVar, "1")
|
||||||
config, err := conf.FromEnv()
|
config, err := conf.FromEnv()
|
||||||
@@ -73,7 +82,7 @@ func TestDisableUnoconvEndpoints(t *testing.T) {
|
|||||||
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", convertGroupEndpoint, urlEndpoint), body)
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", convertGroupEndpoint, urlEndpoint), body)
|
||||||
req.Header.Set(echo.HeaderContentType, contentType)
|
req.Header.Set(echo.HeaderContentType, contentType)
|
||||||
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
test.AssertStatusCode(t, http.StatusOK, srv, req)
|
||||||
// Markdown endpoint should return 404.
|
// Markdown endpoint should return 200.
|
||||||
body, contentType = test.MarkdownMultipartForm(t, nil)
|
body, contentType = test.MarkdownMultipartForm(t, nil)
|
||||||
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", convertGroupEndpoint, markdownEndpoint), body)
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", convertGroupEndpoint, markdownEndpoint), body)
|
||||||
req.Header.Set(echo.HeaderContentType, contentType)
|
req.Header.Set(echo.HeaderContentType, contentType)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package printer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
|
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/pkg/xcontext"
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xcontext"
|
||||||
@@ -54,6 +55,8 @@ func (p mergePrinter) Print(destination string) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
p.ctx = ctx
|
p.ctx = ctx
|
||||||
}
|
}
|
||||||
|
// see https://github.com/thecodingmachine/gotenberg/issues/139.
|
||||||
|
sort.Strings(p.fpaths)
|
||||||
p.logger.DebugfOp(op, "merging '%v'...", p.fpaths)
|
p.logger.DebugfOp(op, "merging '%v'...", p.fpaths)
|
||||||
resolver := func() error {
|
resolver := func() error {
|
||||||
var args []string
|
var args []string
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/phayes/freeport"
|
"github.com/phayes/freeport"
|
||||||
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
|
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
|
||||||
@@ -53,6 +54,8 @@ func (p officePrinter) Print(destination string) error {
|
|||||||
ctx, cancel := xcontext.WithTimeout(p.logger, p.opts.WaitTimeout)
|
ctx, cancel := xcontext.WithTimeout(p.logger, p.opts.WaitTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
resolver := func() error {
|
resolver := func() error {
|
||||||
|
// see https://github.com/thecodingmachine/gotenberg/issues/139.
|
||||||
|
sort.Strings(p.fpaths)
|
||||||
fpaths := make([]string, len(p.fpaths))
|
fpaths := make([]string, len(p.fpaths))
|
||||||
dirPath := filepath.Dir(destination)
|
dirPath := filepath.Dir(destination)
|
||||||
for i, fpath := range p.fpaths {
|
for i, fpath := range p.fpaths {
|
||||||
|
|||||||
Reference in New Issue
Block a user