Merge pull request #153 from thecodingmachine/custom_headers

Custom headers
This commit is contained in:
Julien Neuhart
2019-12-09 17:55:14 +01:00
committed by GitHub
19 changed files with 503 additions and 90 deletions

View File

@@ -18,7 +18,7 @@ Unless your project already has a PSR7 `HttpClient`, install `php-http/guzzle6-a
$ composer require php-http/guzzle6-adapter
```
Then the PHP client:
Then the [PHP client](https://github.com/thecodingmachine/gotenberg-php-client):
```bash
$ composer require thecodingmachine/gotenberg-php-client

View File

@@ -59,7 +59,7 @@ use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```
@@ -146,7 +146,7 @@ $footer = DocumentFactory::makeFromPath('footer.html', 'footer.html');
$request = new HTMLRequest($index);
$request->setHeader($header);
$request->setFooter($footer);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```
@@ -237,7 +237,7 @@ $assets = [
];
$request = new HTMLRequest($index);
$request->setAssets($assets);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```
@@ -296,7 +296,7 @@ $request = new HTMLRequest($index);
$request->setPaperSize(Request::A4);
$request->setMargins(Request::NO_MARGINS);
$request->setLandscape(true);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```
@@ -345,7 +345,7 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setWaitDelay(5.5);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```
@@ -397,6 +397,6 @@ $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";
$dest = 'result.pdf';
$client->store($request, $dest);
```

View File

@@ -51,6 +51,57 @@ use TheCodingMachine\Gotenberg\URLRequest;
$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$request = new URLRequest('https://google.com');
$request->setMargins(Request::NO_MARGINS);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```
## Custom HTTP headers
You may send your own HTTP headers to the `remoteURL`.
For instance, by adding the HTTP header `Gotenberg-Remoteurl-Your-Header` to your request,
the API will send a request to the `remoteURL` with the HTTP header `Your-Header`.
> **Attention:** the API uses a canonical format for the HTTP headers:
> it transforms the first
> letter and any letter following a hyphen to upper case;
> the rest are converted to lowercase. For example, the
> canonical key for `accept-encoding` is `Accept-Encoding`.
### cURL
```bash
$ curl --request POST \
--url http://localhost:3000/convert/url \
--header 'Content-Type: multipart/form-data' \
--header 'Gotenberg-Remoteurl-Your-Header: Foo' \
--form remoteURL=https://google.com \
-o result.pdf
```
### Go
```golang
import "github.com/thecodingmachine/gotenberg-go-client/v6"
func main() {
c := &gotenberg.Client{Hostname: "http://localhost:3000"}
req := gotenberg.NewURLRequest("https://google.com")
req.AddRemoteURLHTTPHeader("Your-Header", "Foo")
dest := "result.pdf"
c.Store(req, dest)
}
```
### PHP
```php
use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\URLRequest;
$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$request = new URLRequest('https://google.com');
$request->addRemoteURLHTTPHeader('Your-Header', 'Foo')
$dest = 'result.pdf';
$client->store($request, $dest);
```

View File

@@ -65,6 +65,6 @@ $markdowns = [
DocumentFactory::makeFromPath('file.md', 'file.md'),
];
$request = new MarkdownRequest($index, $markdowns);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```

View File

@@ -64,7 +64,7 @@ $files = [
DocumentFactory::makeFromPath('document2.docx', 'document2.docx'),
];
$request = new OfficeRequest($files);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```
@@ -112,6 +112,6 @@ $files = [
];
$request = new OfficeRequest($files);
$request->setLandscape(true);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```

View File

@@ -50,6 +50,6 @@ $files = [
DocumentFactory::makeFromPath('file2.pdf', 'file2.pdf'),
];
$request = new MergeRequest($files);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```

View File

@@ -48,6 +48,6 @@ $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setWaitTimeout(2.5);
$dest = "result.pdf";
$dest = 'result.pdf';
$client->store($request, $dest);
```

View File

@@ -58,9 +58,7 @@ It takes a float as value (e.g `2.5` for 2.5 seconds).
> You may also define this value globally: see the [environment variables](#environment_variables.default_webhook_url_timeout) section.
### Examples
#### cURL
### cURL
```bash
$ curl --request POST \
@@ -71,7 +69,7 @@ $ curl --request POST \
--form webhookURLTimeout=2.5
```
#### Go
### Go
```golang
import "github.com/thecodingmachine/gotenberg-go-client/v6"
@@ -85,7 +83,7 @@ func main() {
}
```
#### PHP
### PHP
```php
use TheCodingMachine\Gotenberg\Client;
@@ -99,3 +97,56 @@ $request->setWebhookURL('http://myapp.com/webhook/');
$request->setWebhookURLTimeout(2.5);
$resp = $client->post($request);
```
## Custom HTTP headers
You may send your own HTTP headers to the `webhookURL`.
For instance, by adding the HTTP header `Gotenberg-Webhookurl-Your-Header` to your request,
the API will send a request to the `webhookURL` with the HTTP header `Your-Header`.
> **Attention:** the API uses a canonical format for the HTTP headers:
> it transforms the first
> letter and any letter following a hyphen to upper case;
> the rest are converted to lowercase. For example, the
> canonical key for `accept-encoding` is `Accept-Encoding`.
### cURL
```bash
$ curl --request POST \
--url http://localhost:3000/convert/html \
--header 'Content-Type: multipart/form-data' \
--header 'Gotenberg-Webhookurl-Your-Header: Foo' \
--form files=@index.html \
--form webhookURL='http://myapp.com/webhook/'
```
### 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.WebhookURL("http://myapp.com/webhook/")
req.AddWebhookURLHTTPHeader("Your-Header", "Foo")
resp, _ := c.Post(req)
}
```
### PHP
```php
use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath('index.html', 'index.html');
$request = new HTMLRequest($index);
$request->setWebhookURL('http://myapp.com/webhook/');
$request->addWebhookURLHTTPHeader('Your-Header', 'Foo');
$resp = $client->post($request);
```

View File

@@ -233,7 +233,7 @@ Gotenberg API is available at <a href="http://localhost:3000">http://localhost:3
<pre class="chroma">$ composer require php-http/guzzle6-adapter
</pre>
<p>Then the PHP client:</p>
<p>Then the <a href="https://github.com/thecodingmachine/gotenberg-php-client">PHP client</a>:</p>
<pre class="chroma">$ composer require thecodingmachine/gotenberg-php-client
</pre>
@@ -474,7 +474,7 @@ use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -569,7 +569,7 @@ $footer = DocumentFactory::makeFromPath(&#39;footer.html&#39;, &#39;footer.html&
$request = new HTMLRequest($index);
$request-&gt;setHeader($header);
$request-&gt;setFooter($footer);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -665,7 +665,7 @@ $assets = [
];
$request = new HTMLRequest($index);
$request-&gt;setAssets($assets);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -731,7 +731,7 @@ $request = new HTMLRequest($index);
$request-&gt;setPaperSize(Request::A4);
$request-&gt;setMargins(Request::NO_MARGINS);
$request-&gt;setLandscape(true);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -787,7 +787,7 @@ $client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$request-&gt;setWaitDelay(5.5);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -846,7 +846,7 @@ $client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$request-&gt;setGoogleChromeRpccBufferSize(1048576);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -912,7 +912,65 @@ use TheCodingMachine\Gotenberg\URLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$request = new URLRequest(&#39;https://google.com&#39;);
$request-&gt;setMargins(Request::NO_MARGINS);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="url.custom_http_headers" href="#url.custom_http_headers">
<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>Custom HTTP headers</h2>
<p>You may send your own HTTP headers to the <code>remoteURL</code>.</p>
<p>For instance, by adding the HTTP header <code>Gotenberg-Remoteurl-Your-Header</code> to your request,
the API will send a request to the <code>remoteURL</code> with the HTTP header <code>Your-Header</code>.</p>
<blockquote>
<p><strong>Attention:</strong> the API uses a canonical format for the HTTP headers:
it transforms the first
letter and any letter following a hyphen to upper case;
the rest are converted to lowercase. For example, the
canonical key for <code>accept-encoding</code> is <code>Accept-Encoding</code>.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="url.custom_http_headers.c_url" href="#url.custom_http_headers.c_url">
<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>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/url <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Gotenberg-Remoteurl-Your-Header: Foo&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">remoteURL</span><span class="o">=</span>https://google.com <span class="se">\
</span><span class="se"></span> -o result.pdf
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="url.custom_http_headers.go" href="#url.custom_http_headers.go">
<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>Go</h3>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg-go-client/v6&#34;</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewURLRequest</span><span class="p">(</span><span class="s">&#34;https://google.com&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">AddRemoteURLHTTPHeader</span><span class="p">(</span><span class="s">&#34;Your-Header&#34;</span><span class="p">,</span> <span class="s">&#34;Foo&#34;</span><span class="p">)</span>
<span class="nx">dest</span> <span class="o">:=</span> <span class="s">&#34;result.pdf&#34;</span>
<span class="nx">c</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">dest</span><span class="p">)</span>
<span class="p">}</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="url.custom_http_headers.php" href="#url.custom_http_headers.php">
<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>PHP</h3>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\URLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$request = new URLRequest(&#39;https://google.com&#39;);
$request-&gt;addRemoteURLHTTPHeader(&#39;Your-Header&#39;, &#39;Foo&#39;)
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -989,7 +1047,7 @@ $markdowns = [
DocumentFactory::makeFromPath(&#39;file.md&#39;, &#39;file.md&#39;),
];
$request = new MarkdownRequest($index, $markdowns);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -1070,7 +1128,7 @@ $files = [
DocumentFactory::makeFromPath(&#39;document2.docx&#39;, &#39;document2.docx&#39;),
];
$request = new OfficeRequest($files);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -1123,7 +1181,7 @@ $files = [
];
$request = new OfficeRequest($files);
$request-&gt;setLandscape(true);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -1188,7 +1246,7 @@ $files = [
DocumentFactory::makeFromPath(&#39;file2.pdf&#39;, &#39;file2.pdf&#39;),
];
$request = new MergeRequest($files);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -1251,7 +1309,7 @@ $client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$request-&gt;setWaitTimeout(2.5);
$dest = &#34;result.pdf&#34;;
$dest = &#39;result.pdf&#39;;
$client-&gt;store($request, $dest);
</pre>
@@ -1326,13 +1384,9 @@ $resp = $client-&gt;post($request);
<p>You may also define this value globally: see the <a href="#environment_variables.default_webhook_url_timeout">environment variables</a> section.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.timeout.examples" href="#webhook.timeout.examples">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.timeout.c_url" href="#webhook.timeout.c_url">
<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>Examples</h3>
<h4 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.timeout.examples.c_url" href="#webhook.timeout.examples.c_url">
<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>cURL</h4>
</a>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/html <span class="se">\
@@ -1342,9 +1396,9 @@ $resp = $client-&gt;post($request);
</span><span class="se"></span> --form <span class="nv">webhookURLTimeout</span><span class="o">=</span>2.5
</pre>
<h4 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.timeout.examples.go" href="#webhook.timeout.examples.go">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.timeout.go" href="#webhook.timeout.go">
<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>Go</h4>
</a>Go</h3>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg-go-client/v6&#34;</span>
@@ -1357,9 +1411,9 @@ $resp = $client-&gt;post($request);
<span class="p">}</span>
</pre>
<h4 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.timeout.examples.php" href="#webhook.timeout.examples.php">
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.timeout.php" href="#webhook.timeout.php">
<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>PHP</h4>
</a>PHP</h3>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
@@ -1371,6 +1425,66 @@ $request = new HTMLRequest($index);
$request-&gt;setWebhookURL(&#39;http://myapp.com/webhook/&#39;);
$request-&gt;setWebhookURLTimeout(2.5);
$resp = $client-&gt;post($request);
</pre>
<h2 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.custom_http_headers" href="#webhook.custom_http_headers">
<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>Custom HTTP headers</h2>
<p>You may send your own HTTP headers to the <code>webhookURL</code>.</p>
<p>For instance, by adding the HTTP header <code>Gotenberg-Webhookurl-Your-Header</code> to your request,
the API will send a request to the <code>webhookURL</code> with the HTTP header <code>Your-Header</code>.</p>
<blockquote>
<p><strong>Attention:</strong> the API uses a canonical format for the HTTP headers:
it transforms the first
letter and any letter following a hyphen to upper case;
the rest are converted to lowercase. For example, the
canonical key for <code>accept-encoding</code> is <code>Accept-Encoding</code>.</p>
</blockquote>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.custom_http_headers.c_url" href="#webhook.custom_http_headers.c_url">
<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>cURL</h3>
<pre class="chroma">$ curl --request POST <span class="se">\
</span><span class="se"></span> --url http://localhost:3000/convert/html <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Content-Type: multipart/form-data&#39;</span> <span class="se">\
</span><span class="se"></span> --header <span class="s1">&#39;Gotenberg-Webhookurl-Your-Header: Foo&#39;</span> <span class="se">\
</span><span class="se"></span> --form <span class="nv">files</span><span class="o">=</span>@index.html <span class="se">\
</span><span class="se"></span> --form <span class="nv">webhookURL</span><span class="o">=</span><span class="s1">&#39;http://myapp.com/webhook/&#39;</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.custom_http_headers.go" href="#webhook.custom_http_headers.go">
<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>Go</h3>
<pre class="chroma"><span class="kn">import</span> <span class="s">&#34;github.com/thecodingmachine/gotenberg-go-client/v6&#34;</span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">c</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">gotenberg</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Hostname</span><span class="p">:</span> <span class="s">&#34;http://localhost:3000&#34;</span><span class="p">}</span>
<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">gotenberg</span><span class="p">.</span><span class="nf">NewHTMLRequest</span><span class="p">(</span><span class="s">&#34;index.html&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">WebhookURL</span><span class="p">(</span><span class="s">&#34;http://myapp.com/webhook/&#34;</span><span class="p">)</span>
<span class="nx">req</span><span class="p">.</span><span class="nf">AddWebhookURLHTTPHeader</span><span class="p">(</span><span class="s">&#34;Your-Header&#34;</span><span class="p">,</span> <span class="s">&#34;Foo&#34;</span><span class="p">)</span>
<span class="nx">resp</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Post</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span>
<span class="p">}</span>
</pre>
<h3 class="Heading"><a class="Anchor" aria-hidden="true" id="webhook.custom_http_headers.php" href="#webhook.custom_http_headers.php">
<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>PHP</h3>
<pre class="chroma">use TheCodingMachine\Gotenberg\Client;
use TheCodingMachine\Gotenberg\DocumentFactory;
use TheCodingMachine\Gotenberg\HTMLRequest;
$client = new Client(&#39;http://localhost:3000&#39;, new \Http\Adapter\Guzzle6\Client());
$index = DocumentFactory::makeFromPath(&#39;index.html&#39;, &#39;index.html&#39;);
$request = new HTMLRequest($index);
$request-&gt;setWebhookURL(&#39;http://myapp.com/webhook/&#39;);
$request-&gt;addWebhookURLHTTPHeader(&#39;Your-Header&#39;, &#39;Foo&#39;);
$resp = $client-&gt;post($request);
</pre>
</div>

View File

@@ -138,6 +138,7 @@ func urlHandler(c echo.Context) error {
if err != nil {
return err
}
opts.CustomHTTPHeaders = resource.RemoteURLCustomHTTPHeaders(r)
if !r.HasArg(resource.RemoteURLArgKey) {
return xerror.Invalid(
op,
@@ -322,20 +323,50 @@ func convertAsync(ctx context.Context, p printer.Printer, filename, fpath string
defer f.Close() // nolint: errcheck
logger.DebugfOp(
op,
"sending result file '%s' to '%s'",
"preparing to send result file '%s' to '%s'...",
filename,
webhookURL,
)
httpClient := &http.Client{
Timeout: xtime.Duration(webhookURLTimeout),
}
resp, err := httpClient.Post(webhookURL, "application/pdf", f) /* #nosec */
req, err := http.NewRequest(http.MethodPost, webhookURL, f)
if err != nil {
xerr := xerror.New(op, err)
logger.ErrorOp(xerror.Op(xerr), xerr)
return
}
req.Header.Set(echo.HeaderContentType, "application/pdf")
// set custom headers (if any).
customHTTPHeaders := resource.WebhookURLCustomHTTPHeaders(r)
if len(customHTTPHeaders) > 0 {
for key, value := range customHTTPHeaders {
req.Header.Set(key, value)
logger.DebugfOp(op, "set '%s' to custom HTTP header '%s'", value, key)
}
} else {
logger.DebugOp(op, "skipping custom HTTP headers as none have been provided...")
}
// send the result file.
logger.DebugfOp(
op,
"sending result file '%s' to '%s'...",
filename,
webhookURL,
)
resp, err := httpClient.Do(req) /* #nosec */
if err != nil {
xerr := xerror.New(op, err)
logger.ErrorOp(xerror.Op(xerr), xerr)
return
}
defer resp.Body.Close() // nolint: errcheck
logger.DebugfOp(
op,
"result file '%s' sent to '%s'",
filename,
webhookURL,
)
}()
return nil
}

View File

@@ -581,11 +581,18 @@ func TestOfficeHandler(t *testing.T) {
}
func TestWebhook(t *testing.T) {
customHeaderRealKey := http.CanonicalHeaderKey("MyCustomHeader")
customHeaderKey := fmt.Sprintf("%s%s", resource.WebhookURLCustomHTTPHeaderCanonicalBaseKey, customHeaderRealKey)
customHeaderValue := "foo"
status := make(chan error, 2)
rcv := echo.New()
rcv.POST("/foo", func(c echo.Context) error {
if c.Request().Header.Get("Content-type") != "application/pdf" {
status <- fmt.Errorf("wrong Content-type: got %s want %s", c.Request().Header.Get("Content-type"), "application/pdf")
if c.Request().Header.Get(echo.HeaderContentType) != "application/pdf" {
status <- fmt.Errorf("wrong Content-type: got '%s' want '%s'", c.Request().Header.Get(echo.HeaderContentType), "application/pdf")
return nil
}
if c.Request().Header.Get(customHeaderRealKey) != customHeaderValue {
status <- fmt.Errorf("wrong '%s': got '%s' want '%s'", customHeaderRealKey, c.Request().Header.Get(customHeaderRealKey), customHeaderValue)
return nil
}
body, err := ioutil.ReadAll(c.Request().Body)
@@ -609,6 +616,7 @@ func TestWebhook(t *testing.T) {
body, contentType := test.MergeMultipartForm(t, map[string]string{string(resource.WebhookURLArgKey): "http://localhost:3001/foo"})
req := httptest.NewRequest(http.MethodPost, mergeEndpoint(config), body)
req.Header.Set(echo.HeaderContentType, contentType)
req.Header.Set(customHeaderKey, customHeaderValue)
test.AssertStatusCode(t, http.StatusOK, srv, req)
err := <-status
assert.NoError(t, err)
@@ -622,5 +630,5 @@ func TestResultFilename(t *testing.T) {
req.Header.Set(echo.HeaderContentType, contentType)
rec := httptest.NewRecorder()
srv.ServeHTTP(rec, req)
assert.Equal(t, "attachment; filename=\"foo.pdf\"", rec.Header().Get("Content-Disposition"))
assert.Equal(t, "attachment; filename=\"foo.pdf\"", rec.Header().Get(echo.HeaderContentDisposition))
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/labstack/echo/v4"
"github.com/thecodingmachine/gotenberg/internal/app/xhttp/pkg/resource"
"github.com/thecodingmachine/gotenberg/internal/pkg/conf"
"github.com/thecodingmachine/gotenberg/internal/pkg/normalize"
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
"github.com/thecodingmachine/gotenberg/internal/pkg/xlog"
)
@@ -79,6 +78,10 @@ func (ctx *Context) WithResource(directoryName string) error {
if err != nil {
return r, err
}
// retrieve custom headers from request.
for key, value := range ctx.Request().Header {
r.WithCustomHTTPHeader(key, value[0])
}
// retrieve form values from request.
for _, key := range resource.ArgKeys() {
r.WithArg(key, ctx.FormValue(string(key)))
@@ -103,11 +106,7 @@ func (ctx *Context) WithResource(directoryName string) error {
return r, err
}
defer in.Close() // nolint: errcheck
filename, err := normalize.String(fh.Filename)
if err != nil {
return r, err
}
if err := r.WithFile(filename, in); err != nil {
if err := r.WithFile(fh.Filename, in); err != nil {
return r, err
}
}

View File

@@ -0,0 +1,37 @@
package resource
import (
"strings"
)
const (
// RemoteURLCustomHTTPHeaderCanonicalBaseKey is the base key
// of custom headers send to the remote URL.
RemoteURLCustomHTTPHeaderCanonicalBaseKey string = "Gotenberg-Remoteurl-"
// WebhookURLCustomHTTPHeaderCanonicalBaseKey is the base key
// of custom headers send to the webhook URL.
WebhookURLCustomHTTPHeaderCanonicalBaseKey string = "Gotenberg-Webhookurl-"
)
func fetchCustomHTTPHeaders(r Resource, baseKey string) map[string]string {
customHeaders := make(map[string]string)
for key, value := range r.customHeaders {
if strings.Contains(key, baseKey) {
realKey := strings.Replace(key, baseKey, "", 1)
customHeaders[realKey] = value
}
}
return customHeaders
}
// RemoteURLCustomHTTPHeaders is a helper for retrieving
// the custom headers for the URL conversion.
func RemoteURLCustomHTTPHeaders(r Resource) map[string]string {
return fetchCustomHTTPHeaders(r, RemoteURLCustomHTTPHeaderCanonicalBaseKey)
}
// WebhookURLCustomHTTPHeaders is a helper for retrieving
// the custom headers for the webhook URL.
func WebhookURLCustomHTTPHeaders(r Resource) map[string]string {
return fetchCustomHTTPHeaders(r, WebhookURLCustomHTTPHeaderCanonicalBaseKey)
}

View File

@@ -0,0 +1,54 @@
package resource
import (
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/thecodingmachine/gotenberg/test"
)
func TestRemoteURLCustomHeaders(t *testing.T) {
const resourceDirectoryName string = "foo"
logger := test.DebugLogger()
r, err := New(logger, resourceDirectoryName)
assert.Nil(t, err)
// should find the custom header.
customHeaderValue := "bar"
customHeaderCanonicalRealKey := "Foo"
customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", RemoteURLCustomHTTPHeaderCanonicalBaseKey, customHeaderCanonicalRealKey))
r.WithCustomHTTPHeader(customHeaderCanonicalKey, customHeaderValue)
r.WithCustomHTTPHeader("Bar", "Bar")
expected := map[string]string{
customHeaderCanonicalRealKey: customHeaderValue,
}
notExpected := map[string]string{
customHeaderCanonicalKey: customHeaderValue,
}
v := RemoteURLCustomHTTPHeaders(r)
assert.Equal(t, expected, v)
assert.NotEqual(t, notExpected, v)
}
func TestWebhookURLCustomHeaders(t *testing.T) {
const resourceDirectoryName string = "foo"
logger := test.DebugLogger()
r, err := New(logger, resourceDirectoryName)
assert.Nil(t, err)
// should find the custom header.
customHeaderValue := "bar"
customHeaderCanonicalRealKey := "Foo"
customHeaderCanonicalKey := http.CanonicalHeaderKey(fmt.Sprintf("%s%s", WebhookURLCustomHTTPHeaderCanonicalBaseKey, customHeaderCanonicalRealKey))
r.WithCustomHTTPHeader(customHeaderCanonicalKey, customHeaderValue)
r.WithCustomHTTPHeader("Bar", "Bar")
expected := map[string]string{
customHeaderCanonicalRealKey: customHeaderValue,
}
notExpected := map[string]string{
customHeaderCanonicalKey: customHeaderValue,
}
v := WebhookURLCustomHTTPHeaders(r)
assert.Equal(t, expected, v)
assert.NotEqual(t, notExpected, v)
}

View File

@@ -3,9 +3,12 @@ package resource
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/thecodingmachine/gotenberg/internal/pkg/normalize"
"github.com/thecodingmachine/gotenberg/internal/pkg/xassert"
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
"github.com/thecodingmachine/gotenberg/internal/pkg/xlog"
@@ -21,10 +24,11 @@ const TemporaryDirectory string = "tmp"
// Resource helps managing
// arguments and files for a conversion.
type Resource struct {
logger xlog.Logger
dirPath string
args map[ArgKey]string
files map[string]file
logger xlog.Logger
dirPath string
customHeaders map[string]string
args map[ArgKey]string
files map[string]file
}
// New creates a Resource where its files will
@@ -48,10 +52,11 @@ func New(logger xlog.Logger, directoryName string) (Resource, error) {
}
logger.DebugfOp(op, "resource directory '%s' created", directoryName)
return Resource{
logger: logger,
dirPath: dirPath,
args: make(map[ArgKey]string),
files: make(map[string]file),
logger: logger,
dirPath: dirPath,
customHeaders: make(map[string]string),
args: make(map[ArgKey]string),
files: make(map[string]file),
}, nil
}
@@ -70,6 +75,21 @@ func (r Resource) Close() error {
return nil
}
// WithCustomHTTPHeader add a new custom header to the Resource.
// Given key should be in canonical format.
func (r *Resource) WithCustomHTTPHeader(key string, value string) {
const op string = "resource.Resource.WithCustomHTTPHeader"
// should already be in canonical format.
canonicalKey := http.CanonicalHeaderKey(key)
if strings.Contains(canonicalKey, RemoteURLCustomHTTPHeaderCanonicalBaseKey) ||
strings.Contains(canonicalKey, WebhookURLCustomHTTPHeaderCanonicalBaseKey) {
r.customHeaders[canonicalKey] = value
r.logger.DebugfOp(op, "added '%s' with value '%s' to resource custom HTTP headers", canonicalKey, value)
return
}
r.logger.DebugfOp(op, "skipping '%s' as it is not a custom HTTP header...", canonicalKey)
}
// WithArg add a new argument to the Resource.
func (r *Resource) WithArg(key ArgKey, value string) {
const op string = "resource.Resource.WithArg"
@@ -80,13 +100,24 @@ func (r *Resource) WithArg(key ArgKey, value string) {
// WithFile add a new file to the Resource.
func (r *Resource) WithFile(filename string, in io.Reader) error {
const op string = "resource.Resource.WithFile"
fpath := fmt.Sprintf("%s/%s", r.dirPath, filename)
file := file{fpath: fpath}
if err := file.write(in); err != nil {
resolver := func() error {
// see https://github.com/thecodingmachine/gotenberg/issues/104.
normalized, err := normalize.String(filename)
if err != nil {
return err
}
fpath := fmt.Sprintf("%s/%s", r.dirPath, normalized)
file := file{fpath: fpath}
if err := file.write(in); err != nil {
return err
}
r.files[filename] = file
r.logger.DebugfOp(op, "resource file '%s' created", filename)
return nil
}
if err := resolver(); err != nil {
return xerror.New(op, err)
}
r.files[filename] = file
r.logger.DebugfOp(op, "resource file '%s' created", filename)
return nil
}

View File

@@ -2,6 +2,7 @@ package printer
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"strings"
@@ -30,18 +31,19 @@ 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
RpccBufferSize int64
WaitTimeout float64
WaitDelay float64
HeaderHTML string
FooterHTML string
PaperWidth float64
PaperHeight float64
MarginTop float64
MarginBottom float64
MarginLeft float64
MarginRight float64
Landscape bool
RpccBufferSize int64
CustomHTTPHeaders map[string]string
}
// DefaultChromePrinterOptions returns the default
@@ -49,18 +51,19 @@ type ChromePrinterOptions struct {
func DefaultChromePrinterOptions(config conf.Config) ChromePrinterOptions {
const defaultHeaderFooterHTML string = "<html><head></head><body></body></html>"
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,
RpccBufferSize: config.DefaultGoogleChromeRpccBufferSize(),
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(),
CustomHTTPHeaders: make(map[string]string),
}
}
@@ -144,6 +147,10 @@ func (p chromePrinter) Print(destination string) error {
if err := p.enableEvents(ctx, targetClient); err != nil {
return err
}
// add custom headers (if any).
if err := p.setCustomHTTPHeaders(ctx, targetClient); err != nil {
return err
}
// listen for all events.
if err := p.listenEvents(ctx, targetClient); err != nil {
return err
@@ -247,6 +254,32 @@ func (p chromePrinter) enableEvents(ctx context.Context, client *cdp.Client) err
return nil
}
func (p chromePrinter) setCustomHTTPHeaders(ctx context.Context, client *cdp.Client) error {
const op string = "printer.chromePrinter.setCustomHTTPHeaders"
resolver := func() error {
if len(p.opts.CustomHTTPHeaders) == 0 {
p.logger.DebugOp(op, "skipping custom HTTP headers as none have been provided...")
return nil
}
customHTTPHeaders := make(map[string]string)
// useless but for the logs.
for key, value := range p.opts.CustomHTTPHeaders {
customHTTPHeaders[key] = value
p.logger.DebugfOp(op, "set '%s' to custom HTTP header '%s'", value, key)
}
b, err := json.Marshal(customHTTPHeaders)
if err != nil {
return err
}
// should always be called after client.Network.Enable.
return client.Network.SetExtraHTTPHeaders(ctx, network.NewSetExtraHTTPHeadersArgs(b))
}
if err := resolver(); err != nil {
return xerror.New(op, err)
}
return nil
}
func (p chromePrinter) listenEvents(ctx context.Context, client *cdp.Client) error {
const op string = "printer.chromePrinter.listenEvents"
resolver := func() error {

View File

@@ -79,7 +79,7 @@ func multipartForm(
require.Nil(t, err)
}
if kind == "url" {
err := writer.WriteField("remoteURL", "http://google.com")
err := writer.WriteField("remoteURL", "https://google.com")
require.Nil(t, err)
}
for k, v := range formValues {

View File

@@ -75,6 +75,7 @@ func OfficeFpaths(t *testing.T) []string {
fpath(t, "office", "document.docx"),
fpath(t, "office", "document.rtf"),
fpath(t, "office", "document.txt"),
fpath(t, "office", "document_with_special_éà.txt"),
}
}

View File

@@ -0,0 +1,3 @@
Gutenberg
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.