diff --git a/.gitignore b/.gitignore
index 1ce198e..a1a4498 100644
--- a/.gitignore
+++ b/.gitignore
@@ -289,3 +289,4 @@ __pycache__/
*.xsd.cs
settings.local.json
+examples/**/output
diff --git a/GotenbergSharpApiClient.sln b/GotenbergSharpApiClient.sln
index 2e11290..d19cbde 100644
--- a/GotenbergSharpApiClient.sln
+++ b/GotenbergSharpApiClient.sln
@@ -5,12 +5,32 @@ VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gotenberg.Sharp.Api.Client", "src\Gotenberg.Sharp.Api.Client\Gotenberg.Sharp.Api.Client.csproj", "{75F783A4-9392-412F-9DFE-00EE89527C10}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FE638D0D-6A76-4BF4-AF06-D8AAB9726723}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GotenbergSharpClient.Tests", "test\GotenbergSharpClient.Tests\GotenbergSharpClient.Tests.csproj", "{EEAF9CA2-7962-176A-E851-BF81D8DE31F0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
+ examples\appsettings.json = examples\appsettings.json
+ examples\Directory.Build.props = examples\Directory.Build.props
+ examples\README.md = examples\README.md
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GotenbergSharpClient.Tests", "test\GotenbergSharpClient.Tests\GotenbergSharpClient.Tests.csproj", "{EEAF9CA2-7962-176A-E851-BF81D8DE31F0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DIExample", "examples\DIExample\DIExample.csproj", "{C022147F-DA63-5B3C-9349-FEB9675362DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlConvert", "examples\HtmlConvert\HtmlConvert.csproj", "{6113FC36-F04E-B6CA-5C64-EA6156640C94}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlWithMarkdown", "examples\HtmlWithMarkdown\HtmlWithMarkdown.csproj", "{893AD0F6-7E7E-8550-56D7-BBCB77933BD3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OfficeMerge", "examples\OfficeMerge\OfficeMerge.csproj", "{2A1FC5B6-EB87-C86A-71BB-42784913627C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfConvert", "examples\PdfConvert\PdfConvert.csproj", "{BB512649-8BE2-38B1-49D1-E8CA8701BA1A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfMerge", "examples\PdfMerge\PdfMerge.csproj", "{DDD4236E-F481-4DE9-306C-66E77A8D2CB9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UrlConvert", "examples\UrlConvert\UrlConvert.csproj", "{9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UrlsToMergedPdf", "examples\UrlsToMergedPdf\UrlsToMergedPdf.csproj", "{A4D0B498-A934-2F11-2AA7-18D1D1C9E881}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webhook", "examples\Webhook\Webhook.csproj", "{01C9F662-6378-3FC4-B629-9FD37A38033D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -46,10 +66,129 @@ Global
{EEAF9CA2-7962-176A-E851-BF81D8DE31F0}.Release|x64.Build.0 = Release|Any CPU
{EEAF9CA2-7962-176A-E851-BF81D8DE31F0}.Release|x86.ActiveCfg = Release|Any CPU
{EEAF9CA2-7962-176A-E851-BF81D8DE31F0}.Release|x86.Build.0 = Release|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Debug|x64.Build.0 = Debug|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Debug|x86.Build.0 = Debug|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Release|x64.ActiveCfg = Release|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Release|x64.Build.0 = Release|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Release|x86.ActiveCfg = Release|Any CPU
+ {C022147F-DA63-5B3C-9349-FEB9675362DF}.Release|x86.Build.0 = Release|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Debug|x64.Build.0 = Debug|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Debug|x86.Build.0 = Debug|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Release|x64.ActiveCfg = Release|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Release|x64.Build.0 = Release|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Release|x86.ActiveCfg = Release|Any CPU
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94}.Release|x86.Build.0 = Release|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Debug|x64.Build.0 = Debug|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Debug|x86.Build.0 = Debug|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Release|x64.ActiveCfg = Release|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Release|x64.Build.0 = Release|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Release|x86.ActiveCfg = Release|Any CPU
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3}.Release|x86.Build.0 = Release|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Debug|x64.Build.0 = Debug|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Debug|x86.Build.0 = Debug|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Release|x64.ActiveCfg = Release|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Release|x64.Build.0 = Release|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Release|x86.ActiveCfg = Release|Any CPU
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C}.Release|x86.Build.0 = Release|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Debug|x64.Build.0 = Debug|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Debug|x86.Build.0 = Debug|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Release|x64.ActiveCfg = Release|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Release|x64.Build.0 = Release|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Release|x86.ActiveCfg = Release|Any CPU
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A}.Release|x86.Build.0 = Release|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Debug|x64.Build.0 = Debug|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Debug|x86.Build.0 = Debug|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Release|x64.ActiveCfg = Release|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Release|x64.Build.0 = Release|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Release|x86.ActiveCfg = Release|Any CPU
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9}.Release|x86.Build.0 = Release|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Debug|x64.Build.0 = Debug|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Debug|x86.Build.0 = Debug|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Release|x64.ActiveCfg = Release|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Release|x64.Build.0 = Release|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Release|x86.ActiveCfg = Release|Any CPU
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17}.Release|x86.Build.0 = Release|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Debug|x64.Build.0 = Debug|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Debug|x86.Build.0 = Debug|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Release|x64.ActiveCfg = Release|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Release|x64.Build.0 = Release|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Release|x86.ActiveCfg = Release|Any CPU
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881}.Release|x86.Build.0 = Release|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Debug|x64.Build.0 = Debug|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Debug|x86.Build.0 = Debug|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Release|x64.ActiveCfg = Release|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Release|x64.Build.0 = Release|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Release|x86.ActiveCfg = Release|Any CPU
+ {01C9F662-6378-3FC4-B629-9FD37A38033D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {C022147F-DA63-5B3C-9349-FEB9675362DF} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {6113FC36-F04E-B6CA-5C64-EA6156640C94} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {893AD0F6-7E7E-8550-56D7-BBCB77933BD3} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {2A1FC5B6-EB87-C86A-71BB-42784913627C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {BB512649-8BE2-38B1-49D1-E8CA8701BA1A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {DDD4236E-F481-4DE9-306C-66E77A8D2CB9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {9C18F2D1-E9E9-613A-DE10-4263A2EA8A17} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {A4D0B498-A934-2F11-2AA7-18D1D1C9E881} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {01C9F662-6378-3FC4-B629-9FD37A38033D} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E732CE09-693A-4EB7-BC1C-34E0D4E2E19F}
EndGlobalSection
diff --git a/README.md b/README.md
index f6f03b2..9ea6045 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ public void ConfigureServices(IServiceCollection services)
```
# Using GotenbergSharpClient
-*See the [linqPad folder](linqpad/)* for complete examples.
+*See the [examples folder](examples/)* for complete working examples as console applications.
## Required Using Statements
```csharp
diff --git a/examples/DIExample/DIExample.csproj b/examples/DIExample/DIExample.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/DIExample/DIExample.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/DIExample/Program.cs b/examples/DIExample/Program.cs
new file mode 100644
index 0000000..06f9dcc
--- /dev/null
+++ b/examples/DIExample/Program.cs
@@ -0,0 +1,70 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
+using Gotenberg.Sharp.API.Client.Domain.Requests;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Extensions;
+
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+// Builds a simple DI container with logging enabled.
+// Client retrieved through the service provider is configured with options defined in appsettings.json
+// Watch the polly-retry policy in action:
+// Turn off gotenberg, run this script and let it fail/retry two or three times.
+// Turn gotenberg back on & the request will successfully complete.
+// Example builds a 1 page PDF from the specified TargetUrl
+
+const string TargetUrl = "https://www.cnn.com";
+var saveToPath = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(saveToPath);
+
+var services = BuildServiceCollection();
+var sp = services.BuildServiceProvider();
+
+var sharpClient = sp.GetRequiredService();
+var request = await CreateUrlRequest();
+var response = await sharpClient.UrlToPdfAsync(request);
+
+var resultPath = Path.Combine(saveToPath, $"GotenbergFromUrl-{DateTime.Now:yyyyMMddHHmmss}.pdf");
+
+await using (var destinationStream = File.Create(resultPath))
+{
+ await response.CopyToAsync(destinationStream, CancellationToken.None);
+}
+
+Console.WriteLine($"PDF created: {resultPath}");
+
+IServiceCollection BuildServiceCollection()
+{
+ var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+ return new ServiceCollection()
+ .AddOptions()
+ .Bind(config.GetSection(nameof(GotenbergSharpClient))).Services
+ .AddGotenbergSharpClient()
+ .Services.AddLogging(s => s.AddSimpleConsole(ops =>
+ {
+ ops.IncludeScopes = true;
+ ops.SingleLine = false;
+ ops.TimestampFormat = "hh:mm:ss ";
+ }));
+}
+
+Task CreateUrlRequest()
+{
+ var builder = new UrlRequestBuilder()
+ .SetUrl(TargetUrl)
+ .ConfigureRequest(b => b.SetPageRanges("1-2"))
+ .WithPageProperties(b =>
+ {
+ b.SetPaperSize(PaperSizes.A4)
+ .SetMargins(Margins.None);
+ });
+
+ return builder.BuildAsync();
+}
\ No newline at end of file
diff --git a/examples/Directory.Build.props b/examples/Directory.Build.props
new file mode 100644
index 0000000..8e0da1b
--- /dev/null
+++ b/examples/Directory.Build.props
@@ -0,0 +1,31 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
diff --git a/examples/HtmlConvert/HtmlConvert.csproj b/examples/HtmlConvert/HtmlConvert.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/HtmlConvert/HtmlConvert.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/HtmlConvert/Program.cs b/examples/HtmlConvert/Program.cs
new file mode 100644
index 0000000..91bd776
--- /dev/null
+++ b/examples/HtmlConvert/Program.cs
@@ -0,0 +1,71 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+
+using Microsoft.Extensions.Configuration;
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var destinationDirectory = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(destinationDirectory);
+
+var resourcePath = Path.Combine(AppContext.BaseDirectory, "resources", "Html", "ConvertExample");
+var path = await CreateFromHtml(destinationDirectory, resourcePath, options);
+
+Console.WriteLine($"PDF created: {path}");
+
+static async Task CreateFromHtml(string destinationDirectory, string resourcePath, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var httpClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = options.TimeOut
+ };
+
+ var sharpClient = new GotenbergSharpClient(httpClient);
+
+ var builder = new HtmlRequestBuilder()
+ .AddAsyncDocument(async doc =>
+ doc.SetBody(await GetHtmlFile(resourcePath, "body.html"))
+ .SetFooter(await GetHtmlFile(resourcePath, "footer.html"))
+ ).WithPageProperties(dims => dims.UseChromeDefaults())
+ .WithAsyncAssets(async assets =>
+ assets.AddItem("ear-on-beach.jpg", await GetImageBytes(resourcePath))
+ )
+ .SetConversionBehaviors(b =>
+ b.AddAdditionalHeaders("hello", "from-earth")
+ )
+ .ConfigureRequest(b => b.SetPageRanges("1"));
+
+ var request = await builder.BuildAsync();
+
+ var resultPath = Path.Combine(destinationDirectory, $"GotenbergFromHtml-{DateTime.Now:yyyyMMddHHmmss}.pdf");
+ var response = await sharpClient.HtmlToPdfAsync(request);
+
+ await using var destinationStream = File.Create(resultPath);
+ await response.CopyToAsync(destinationStream, CancellationToken.None);
+
+ return resultPath;
+}
+
+static Task GetImageBytes(string resourcePath)
+{
+ return File.ReadAllBytesAsync(Path.Combine(resourcePath, "ear-on-beach.jpg"));
+}
+
+static Task GetHtmlFile(string resourcePath, string fileName)
+{
+ return File.ReadAllBytesAsync(Path.Combine(resourcePath, fileName));
+}
\ No newline at end of file
diff --git a/examples/HtmlWithMarkdown/HtmlWithMarkdown.csproj b/examples/HtmlWithMarkdown/HtmlWithMarkdown.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/HtmlWithMarkdown/HtmlWithMarkdown.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/HtmlWithMarkdown/Program.cs b/examples/HtmlWithMarkdown/Program.cs
new file mode 100644
index 0000000..c0b7476
--- /dev/null
+++ b/examples/HtmlWithMarkdown/Program.cs
@@ -0,0 +1,82 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+using Microsoft.Extensions.Configuration;
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var destinationDirectory = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(destinationDirectory);
+
+var resourcePath = Path.Combine(AppContext.BaseDirectory, "resources", "Markdown");
+var path = await CreateFromMarkdown(destinationDirectory, resourcePath, options);
+
+Console.WriteLine($"PDF created from Markdown: {path}");
+
+static async Task CreateFromMarkdown(string destinationDirectory, string resourcePath, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var httpClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = options.TimeOut
+ };
+
+ var sharpClient = new GotenbergSharpClient(httpClient);
+
+ var builder = new HtmlRequestBuilder()
+ .AddAsyncDocument(async b =>
+ b.SetHeader(await GetFile(resourcePath, "header.html"))
+ .SetBody(await GetFile(resourcePath, "index.html"))
+ .SetContainsMarkdown()
+ .SetFooter(await GetFile(resourcePath, "footer.html"))
+ ).WithPageProperties(b =>
+ {
+ b.UseChromeDefaults()
+ .SetLandscape()
+ .SetScale(.90);
+ }).WithAsyncAssets(async b =>
+ b.AddItems(await GetMarkdownAssets(resourcePath))
+ )
+ .ConfigureRequest(b => b.SetResultFileName("hello.pdf"))
+ .SetConversionBehaviors(b => b.SetBrowserWaitDelay(2));
+
+ var request = await builder.BuildAsync();
+ var response = await sharpClient.HtmlToPdfAsync(request);
+
+ var outPath = Path.Combine(destinationDirectory, $"GotenbergFromMarkDown-{DateTime.Now:yyyyMMddHHmmss}.pdf");
+
+ await using var destinationStream = File.Create(outPath);
+ await response.CopyToAsync(destinationStream, CancellationToken.None);
+
+ return outPath;
+}
+
+static async Task GetFile(string resourcePath, string fileName)
+ => await File.ReadAllTextAsync(Path.Combine(resourcePath, fileName));
+
+static async Task>> GetMarkdownAssets(string resourcePath)
+{
+ var bodyAssetNames = new[] { "img.gif", "font.woff", "style.css" };
+ var markdownFiles = new[] { "paragraph1.md", "paragraph2.md", "paragraph3.md" };
+
+ var bodyAssetTasks = bodyAssetNames.Select(ba => GetFile(resourcePath, ba));
+ var mdTasks = markdownFiles.Select(md => GetFile(resourcePath, md));
+
+ var bodyAssets = await Task.WhenAll(bodyAssetTasks);
+ var mdParagraphs = await Task.WhenAll(mdTasks);
+
+ return bodyAssetNames.Select((name, index) => KeyValuePair.Create(name, bodyAssets[index]))
+ .Concat(markdownFiles.Select((name, index) => KeyValuePair.Create(name, mdParagraphs[index])));
+}
diff --git a/examples/OfficeMerge/OfficeMerge.csproj b/examples/OfficeMerge/OfficeMerge.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/OfficeMerge/OfficeMerge.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/OfficeMerge/Program.cs b/examples/OfficeMerge/Program.cs
new file mode 100644
index 0000000..ab07eb0
--- /dev/null
+++ b/examples/OfficeMerge/Program.cs
@@ -0,0 +1,66 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+
+using Microsoft.Extensions.Configuration;
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var sourceDirectory = args.Length > 0 ? args[0] : Path.Combine(AppContext.BaseDirectory, "resources", "OfficeDocs");
+var destinationDirectory = args.Length > 1 ? args[1] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(destinationDirectory);
+
+var path = await DoOfficeMerge(sourceDirectory, destinationDirectory, options);
+Console.WriteLine($"Merged Office documents PDF created: {path}");
+
+static async Task DoOfficeMerge(string sourceDirectory, string destinationDirectory, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var httpClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = options.TimeOut
+ };
+
+ var client = new GotenbergSharpClient(httpClient);
+
+ var builder = new MergeOfficeBuilder()
+ .ConfigureRequest(c => c.SetTrace("ConsoleExample"))
+ .WithAsyncAssets(async b => b.AddItems(await GetDocsAsync(sourceDirectory)))
+ .SetPdfFormat(LibrePdfFormats.A2b)
+ .SetPageRanges("1-3"); // Only one of the files has more than 1 page.
+
+ var response = await client.MergeOfficeDocsAsync(builder).ConfigureAwait(false);
+
+ var mergeResultPath = Path.Combine(destinationDirectory, $"GotenbergOfficeMerge-{DateTime.Now:yyyyMMddHHmmss}.pdf");
+
+ await using var destinationStream = File.Create(mergeResultPath);
+
+ await response.CopyToAsync(destinationStream, CancellationToken.None);
+
+ return mergeResultPath;
+}
+
+static async Task>> GetDocsAsync(string sourceDirectory)
+{
+ var paths = Directory.GetFiles(sourceDirectory, "*.*", SearchOption.TopDirectoryOnly);
+ var names = paths.Select(p => new FileInfo(p).Name);
+ var tasks = paths.Select(f => File.ReadAllBytesAsync(f));
+
+ var docs = await Task.WhenAll(tasks);
+
+ return names.Select((name, index) => KeyValuePair.Create(name, docs[index]))
+ .Take(10);
+}
\ No newline at end of file
diff --git a/examples/PdfConvert/PdfConvert.csproj b/examples/PdfConvert/PdfConvert.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/PdfConvert/PdfConvert.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/PdfConvert/Program.cs b/examples/PdfConvert/Program.cs
new file mode 100644
index 0000000..bf76850
--- /dev/null
+++ b/examples/PdfConvert/Program.cs
@@ -0,0 +1,71 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+
+using Microsoft.Extensions.Configuration;
+
+// If you get 1 file, the result is a PDF; get more and the API returns a zip containing the results
+// Currently, Gotenberg supports these formats: A2b & A3b
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var sourcePath = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "pdfs");
+var destinationPath = args.Length > 1 ? args[1] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(destinationPath);
+
+var result = await DoConversion(sourcePath, destinationPath, options);
+Console.WriteLine($"Converted PDF created: {result}");
+
+static async Task DoConversion(string sourcePath, string destinationPath, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var httpClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = options.TimeOut
+ };
+
+ var sharpClient = new GotenbergSharpClient(httpClient);
+
+ var items = Directory.GetFiles(sourcePath, "*.pdf", SearchOption.TopDirectoryOnly)
+ .Select(p => new { Info = new FileInfo(p), Path = p })
+ .OrderBy(item => item.Info.CreationTime)
+ .Take(2);
+
+ Console.WriteLine($"Converting {items.Count()} PDFs:");
+ foreach (var item in items)
+ {
+ Console.WriteLine($" - {item.Info.Name}");
+ }
+
+ var toConvert = items.Select(item => KeyValuePair.Create(item.Info.Name, File.ReadAllBytes(item.Path)));
+
+ var builder = new PdfConversionBuilder()
+ .WithPdfs(b => b.AddItems(toConvert))
+ .SetPdfFormat(LibrePdfFormats.A2b);
+
+ var request = builder.Build();
+ var response = await sharpClient.ConvertPdfDocumentsAsync(request);
+
+ // If you send one in -- the result is PDF.
+ var extension = items.Count() > 1 ? "zip" : "pdf";
+ var outPath = Path.Combine(destinationPath, $"GotenbergConvertResult.{extension}");
+
+ await using var destinationStream = File.Create(outPath);
+
+ await response.CopyToAsync(destinationStream, CancellationToken.None);
+
+ return outPath;
+}
\ No newline at end of file
diff --git a/examples/PdfMerge/PdfMerge.csproj b/examples/PdfMerge/PdfMerge.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/PdfMerge/PdfMerge.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/PdfMerge/Program.cs b/examples/PdfMerge/Program.cs
new file mode 100644
index 0000000..4b1c093
--- /dev/null
+++ b/examples/PdfMerge/Program.cs
@@ -0,0 +1,66 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+using Microsoft.Extensions.Configuration;
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var sourcePath = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "pdfs");
+var destinationPath = args.Length > 1 ? args[1] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(destinationPath);
+
+var result = await DoMerge(sourcePath, destinationPath, options);
+Console.WriteLine($"Merged PDF created: {result}");
+
+static async Task DoMerge(string sourcePath, string destinationPath, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var httpClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = options.TimeOut
+ };
+
+ var sharpClient = new GotenbergSharpClient(httpClient);
+
+ var items = Directory.GetFiles(sourcePath, "*.pdf", SearchOption.TopDirectoryOnly)
+ .Select(p => new { Info = new FileInfo(p), Path = p })
+ .Where(item => !item.Info.Name.Contains("GotenbergMergeResult.pdf"))
+ .OrderBy(item => item.Info.CreationTime)
+ .Take(2);
+
+ Console.WriteLine($"Merging {items.Count()} PDFs:");
+ foreach (var item in items)
+ {
+ Console.WriteLine($" - {item.Info.Name}");
+ }
+
+ var toMerge = items.Select(item => KeyValuePair.Create(item.Info.Name, File.ReadAllBytes(item.Path)));
+
+ var builder = new MergeBuilder()
+ .SetPdfFormat(LibrePdfFormats.A2b)
+ .WithAssets(b => { b.AddItems(toMerge); });
+
+ var request = builder.Build();
+ var response = await sharpClient.MergePdfsAsync(request);
+
+ var outPath = Path.Combine(destinationPath, "GotenbergMergeResult.pdf");
+
+ await using var destinationStream = File.Create(outPath);
+
+ await response.CopyToAsync(destinationStream, CancellationToken.None);
+
+ return outPath;
+}
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..4273aa9
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,119 @@
+# Gotenberg Sharp Client Examples
+
+This directory contains console application examples demonstrating various features of the Gotenberg Sharp API Client.
+
+## Prerequisites
+
+1. **Gotenberg Server**: You need a running Gotenberg instance with basic authentication. See the [main README](../README.md) for setup instructions.
+
+ The examples are pre-configured to use the docker-compose setup with basic auth:
+ ```bash
+ docker-compose -f docker/docker-compose-basic-auth.yml up -d
+ ```
+
+2. **.NET 8.0 SDK**: All examples target .NET 8.0.
+
+## Configuration
+
+All examples share a common configuration file: `appsettings.json`
+
+The default configuration includes:
+- **Service URL**: `http://localhost:3000`
+- **Basic Auth**: Username `testuser`, Password `testpass` (matching docker-compose-basic-auth.yml)
+- **Retry Policy**: Enabled with 4 retries and exponential backoff
+
+You can modify these settings in `appsettings.json` or update the credentials directly in the example code.
+
+## Examples
+
+### HtmlConvert
+Converts HTML to PDF with embedded assets.
+
+```bash
+cd HtmlConvert
+dotnet run [output-directory]
+```
+
+### DIExample
+Demonstrates dependency injection setup with logging and Polly retry policy.
+
+```bash
+cd DIExample
+dotnet run [output-directory]
+```
+
+### PdfMerge
+Merges multiple PDF files into a single PDF.
+
+```bash
+cd PdfMerge
+dotnet run [source-directory] [output-directory]
+```
+
+### UrlsToMergedPdf
+Converts multiple URLs to PDFs and merges them into a single document. Creates a news summary from major news sites.
+
+**Note**: Requires increased Gotenberg timeout (`--api-timeout=1800s`)
+
+```bash
+cd UrlsToMergedPdf
+dotnet run [output-directory]
+```
+
+### HtmlWithMarkdown
+Converts HTML containing Markdown to PDF.
+
+```bash
+cd HtmlWithMarkdown
+dotnet run [output-directory]
+```
+
+### OfficeMerge
+Merges Office documents (Word, Excel, PowerPoint) into a single PDF.
+
+```bash
+cd OfficeMerge
+dotnet run [source-directory] [output-directory]
+```
+
+### UrlConvert
+Converts a URL to PDF with custom header and footer.
+
+```bash
+cd UrlConvert
+dotnet run [output-directory]
+```
+
+### Webhook
+Demonstrates webhook functionality for async PDF generation.
+
+**Note**: Requires a webhook receiver API running on `localhost:5000`
+
+```bash
+cd Webhook
+dotnet run
+```
+
+### PdfConvert
+Converts PDF files to PDF/A formats (A1a, A2b, A3b).
+
+```bash
+cd PdfConvert
+dotnet run [source-directory] [output-directory]
+```
+
+## Project Structure
+
+- **Directory.Build.props**: Shared configuration for all example projects (target framework, dependencies, resources)
+- **appsettings.json**: Shared Gotenberg client configuration
+- **resources/**: Shared resource files used by the examples (HTML templates, images, Office documents, etc.)
+
+## Running Examples
+
+Each example can be run independently. Most examples accept optional command-line arguments for specifying input/output directories. If no arguments are provided, they use sensible defaults (usually `./output` for generated files).
+
+Example:
+```bash
+cd PdfMerge
+dotnet run C:\MyPdfs C:\Output
+```
diff --git a/examples/UrlConvert/Program.cs b/examples/UrlConvert/Program.cs
new file mode 100644
index 0000000..4533ba1
--- /dev/null
+++ b/examples/UrlConvert/Program.cs
@@ -0,0 +1,67 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+
+using Microsoft.Extensions.Configuration;
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var destinationPath = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(destinationPath);
+
+var resourcePath = Path.Combine(AppContext.BaseDirectory, "resources", "Html");
+var headerPath = Path.Combine(resourcePath, "UrlHeader.html");
+var footerPath = Path.Combine(resourcePath, "UrlFooter.html");
+
+var path = await CreateFromUrl(destinationPath, headerPath, footerPath, options);
+Console.WriteLine($"PDF created from URL: {path}");
+
+static async Task CreateFromUrl(string destinationPath, string headerPath, string footerPath, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var httpClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = options.TimeOut
+ };
+
+ var sharpClient = new GotenbergSharpClient(httpClient);
+
+ var builder = new UrlRequestBuilder()
+ .SetUrl("https://www.cnn.com")
+ .SetConversionBehaviors(b => b.SetBrowserWaitDelay(1))
+ .ConfigureRequest(b => b.SetTrace("ConsoleExample").SetPageRanges("1-2"))
+ .AddAsyncHeaderFooter(async b =>
+ b.SetHeader(await File.ReadAllBytesAsync(headerPath))
+ .SetFooter(await File.ReadAllBytesAsync(footerPath))
+ )
+ .WithPageProperties(b =>
+ b.SetPaperSize(PaperSizes.A4)
+ .UseChromeDefaults()
+ .SetMarginLeft(0)
+ .SetMarginRight(0)
+ );
+
+ var request = await builder.BuildAsync();
+ var response = await sharpClient.UrlToPdfAsync(request);
+
+ var resultPath = Path.Combine(destinationPath, $"GotenbergFromUrl-{DateTime.Now:yyyyMMddHHmmss}.pdf");
+
+ await using var destinationStream = File.Create(resultPath);
+
+ await response.CopyToAsync(destinationStream, CancellationToken.None);
+
+ return resultPath;
+}
\ No newline at end of file
diff --git a/examples/UrlConvert/UrlConvert.csproj b/examples/UrlConvert/UrlConvert.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/UrlConvert/UrlConvert.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/UrlsToMergedPdf/Program.cs b/examples/UrlsToMergedPdf/Program.cs
new file mode 100644
index 0000000..de75d82
--- /dev/null
+++ b/examples/UrlsToMergedPdf/Program.cs
@@ -0,0 +1,107 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
+using Gotenberg.Sharp.API.Client.Domain.Requests;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+using Microsoft.Extensions.Configuration;
+
+// NOTE: You need to increase gotenberg api's timeout for this to work
+// by passing --api-timeout=1800s when running the container.
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var destinationDirectory = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "output");
+Directory.CreateDirectory(destinationDirectory);
+
+var path = await CreateWorldNewsSummary(destinationDirectory, options);
+Console.WriteLine($"News summary PDF created: {path}");
+
+static async Task CreateWorldNewsSummary(string destinationDirectory, GotenbergSharpClientOptions options)
+{
+ var sites = new[]
+ {
+ "https://www.nytimes.com", "https://www.axios.com/",
+ "https://www.cnn.com", "https://www.csmonitor.com",
+ "https://www.wsj.com", "https://www.usatoday.com",
+ "https://www.irishtimes.com", "https://www.lemonde.fr",
+ "https://calgaryherald.com", "https://www.bbc.com/news/uk",
+ "https://english.elpais.com/", "https://www.thehindu.com",
+ "https://www.theaustralian.com.au", "https://www.welt.de",
+ "https://www.cankaoxiaoxi.com", "https://www.novinky.cz",
+ "https://www.elobservador.com.uy"
+ }
+ .Select(u => new Uri(u));
+
+ var builders = CreateRequestBuilders(sites);
+ var requests = builders.Select(b => b.Build());
+
+ return await ExecuteRequestsAndMerge(requests, destinationDirectory, options);
+}
+
+static IEnumerable CreateRequestBuilders(IEnumerable uris)
+{
+ foreach (var uri in uris)
+ {
+ yield return new UrlRequestBuilder()
+ .SetUrl(uri)
+ .ConfigureRequest(b =>
+ {
+ b.SetPageRanges("1-2");
+ })
+ .WithPageProperties(b =>
+ {
+ b.SetMargins(Margins.None)
+ .SetMarginLeft(.3)
+ .SetMarginRight(.3);
+ });
+ }
+}
+
+static async Task ExecuteRequestsAndMerge(IEnumerable requests, string destinationDirectory, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var innerClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = TimeSpan.FromMinutes(7)
+ };
+
+ var sharpClient = new GotenbergSharpClient(innerClient);
+
+ Console.WriteLine("Converting URLs to PDFs...");
+ var tasks = requests.Select(r => sharpClient.UrlToPdfAsync(r, CancellationToken.None));
+ var results = await Task.WhenAll(tasks);
+
+ Console.WriteLine("Merging PDFs...");
+ var mergeBuilder = new MergeBuilder()
+ .WithAssets(b =>
+ {
+ b.AddItems(results.Select((r, i) => KeyValuePair.Create($"{i}.pdf", r)));
+ });
+
+ var response = await sharpClient.MergePdfsAsync(mergeBuilder.Build(), CancellationToken.None);
+
+ return await WriteFileAndGetPath(response, destinationDirectory);
+}
+
+static async Task WriteFileAndGetPath(Stream responseStream, string destinationDirectory)
+{
+ var fullPath = Path.Combine(destinationDirectory, $"{DateTime.Now:yyyy-MM-dd}-{DateTime.Now.Ticks}.pdf");
+
+ await using var destinationStream = File.Create(fullPath);
+
+ await responseStream.CopyToAsync(destinationStream, CancellationToken.None);
+
+ return fullPath;
+}
diff --git a/examples/UrlsToMergedPdf/UrlsToMergedPdf.csproj b/examples/UrlsToMergedPdf/UrlsToMergedPdf.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/UrlsToMergedPdf/UrlsToMergedPdf.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/Webhook/Program.cs b/examples/Webhook/Program.cs
new file mode 100644
index 0000000..60415e1
--- /dev/null
+++ b/examples/Webhook/Program.cs
@@ -0,0 +1,68 @@
+using Gotenberg.Sharp.API.Client;
+using Gotenberg.Sharp.API.Client.Domain.Builders;
+using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
+using Gotenberg.Sharp.API.Client.Domain.Settings;
+using Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+using Microsoft.Extensions.Configuration;
+
+// For this to work you need an API running on localhost:5000 with an endpoint to receive the webhook
+
+var config = new ConfigurationBuilder()
+ .SetBasePath(AppContext.BaseDirectory)
+ .AddJsonFile("appsettings.json")
+ .Build();
+
+var options = new GotenbergSharpClientOptions();
+config.GetSection(nameof(GotenbergSharpClient)).Bind(options);
+
+var resourcePath = Path.Combine(AppContext.BaseDirectory, "resources", "Html");
+var headerPath = Path.Combine(resourcePath, "UrlHeader.html");
+var footerPath = Path.Combine(resourcePath, "UrlFooter.html");
+
+Console.WriteLine($"Header: {headerPath}");
+Console.WriteLine($"Footer: {footerPath}");
+
+await CreateFromUrl(headerPath, footerPath, options);
+
+Console.WriteLine("Webhook request sent...");
+
+static async Task CreateFromUrl(string headerPath, string footerPath, GotenbergSharpClientOptions options)
+{
+ using var handler = new HttpClientHandler();
+ using var authHandler = !string.IsNullOrWhiteSpace(options.BasicAuthUsername) && !string.IsNullOrWhiteSpace(options.BasicAuthPassword)
+ ? new BasicAuthHandler(options.BasicAuthUsername, options.BasicAuthPassword) { InnerHandler = handler }
+ : null;
+
+ using var httpClient = new HttpClient(authHandler ?? (HttpMessageHandler)handler)
+ {
+ BaseAddress = options.ServiceUrl,
+ Timeout = options.TimeOut
+ };
+
+ var sharpClient = new GotenbergSharpClient(httpClient);
+
+ var builder = new UrlRequestBuilder()
+ .SetUrl("https://www.newyorker.com")
+ .ConfigureRequest(b =>
+ {
+ b.AddWebhook(hook =>
+ {
+ hook.SetUrl("http://host.docker.internal:5000/api/WebhookReceiver")
+ .SetErrorUrl("http://host.docker.internal:5000/api/WebhookReceiver")
+ .AddExtraHeader("custom-header", "value");
+ }).SetPageRanges("1-2");
+ })
+ .AddAsyncHeaderFooter(async b =>
+ b.SetHeader(await File.ReadAllTextAsync(headerPath))
+ .SetFooter(await File.ReadAllBytesAsync(footerPath))
+ )
+ .WithPageProperties(b =>
+ {
+ b.SetPaperSize(PaperSizes.A4)
+ .SetMargins(Margins.None);
+ });
+
+ var request = await builder.BuildAsync();
+
+ await sharpClient.FireWebhookAndForgetAsync(request);
+}
diff --git a/examples/Webhook/Webhook.csproj b/examples/Webhook/Webhook.csproj
new file mode 100644
index 0000000..35e3d84
--- /dev/null
+++ b/examples/Webhook/Webhook.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/appsettings.json b/examples/appsettings.json
new file mode 100644
index 0000000..94bc1e9
--- /dev/null
+++ b/examples/appsettings.json
@@ -0,0 +1,14 @@
+{
+ "GotenbergSharpClient": {
+ "ServiceUrl": "http://localhost:3000",
+ "HealthCheckUrl": "http://localhost:3000/health",
+ "BasicAuthUsername": "testuser",
+ "BasicAuthPassword": "testpass",
+ "RetryPolicy": {
+ "Enabled": true,
+ "RetryCount": 4,
+ "BackoffPower": 1.5,
+ "LoggingEnabled": true
+ }
+ }
+}
diff --git a/linqpad/Resources/Html/ConvertExample/body.html b/examples/resources/Html/ConvertExample/body.html
similarity index 100%
rename from linqpad/Resources/Html/ConvertExample/body.html
rename to examples/resources/Html/ConvertExample/body.html
diff --git a/linqpad/Resources/Html/ConvertExample/ear-on-beach.jpg b/examples/resources/Html/ConvertExample/ear-on-beach.jpg
similarity index 100%
rename from linqpad/Resources/Html/ConvertExample/ear-on-beach.jpg
rename to examples/resources/Html/ConvertExample/ear-on-beach.jpg
diff --git a/linqpad/Resources/Html/ConvertExample/footer.html b/examples/resources/Html/ConvertExample/footer.html
similarity index 100%
rename from linqpad/Resources/Html/ConvertExample/footer.html
rename to examples/resources/Html/ConvertExample/footer.html
diff --git a/linqpad/Resources/Html/UrlFooter.html b/examples/resources/Html/UrlFooter.html
similarity index 100%
rename from linqpad/Resources/Html/UrlFooter.html
rename to examples/resources/Html/UrlFooter.html
diff --git a/linqpad/Resources/Html/UrlHeader.html b/examples/resources/Html/UrlHeader.html
similarity index 100%
rename from linqpad/Resources/Html/UrlHeader.html
rename to examples/resources/Html/UrlHeader.html
diff --git a/linqpad/Resources/Html/font.woff b/examples/resources/Html/font.woff
similarity index 100%
rename from linqpad/Resources/Html/font.woff
rename to examples/resources/Html/font.woff
diff --git a/linqpad/Resources/Html/footer.html b/examples/resources/Html/footer.html
similarity index 100%
rename from linqpad/Resources/Html/footer.html
rename to examples/resources/Html/footer.html
diff --git a/linqpad/Resources/Html/header.html b/examples/resources/Html/header.html
similarity index 100%
rename from linqpad/Resources/Html/header.html
rename to examples/resources/Html/header.html
diff --git a/linqpad/Resources/Html/img.gif b/examples/resources/Html/img.gif
similarity index 100%
rename from linqpad/Resources/Html/img.gif
rename to examples/resources/Html/img.gif
diff --git a/linqpad/Resources/Html/index.html b/examples/resources/Html/index.html
similarity index 100%
rename from linqpad/Resources/Html/index.html
rename to examples/resources/Html/index.html
diff --git a/linqpad/Resources/Html/style.css b/examples/resources/Html/style.css
similarity index 100%
rename from linqpad/Resources/Html/style.css
rename to examples/resources/Html/style.css
diff --git a/linqpad/Resources/Markdown/font.woff b/examples/resources/Markdown/font.woff
similarity index 100%
rename from linqpad/Resources/Markdown/font.woff
rename to examples/resources/Markdown/font.woff
diff --git a/linqpad/Resources/Markdown/footer.html b/examples/resources/Markdown/footer.html
similarity index 100%
rename from linqpad/Resources/Markdown/footer.html
rename to examples/resources/Markdown/footer.html
diff --git a/linqpad/Resources/Markdown/header.html b/examples/resources/Markdown/header.html
similarity index 100%
rename from linqpad/Resources/Markdown/header.html
rename to examples/resources/Markdown/header.html
diff --git a/linqpad/Resources/Markdown/img.gif b/examples/resources/Markdown/img.gif
similarity index 100%
rename from linqpad/Resources/Markdown/img.gif
rename to examples/resources/Markdown/img.gif
diff --git a/linqpad/Resources/Markdown/index.html b/examples/resources/Markdown/index.html
similarity index 100%
rename from linqpad/Resources/Markdown/index.html
rename to examples/resources/Markdown/index.html
diff --git a/linqpad/Resources/Markdown/paragraph1.md b/examples/resources/Markdown/paragraph1.md
similarity index 100%
rename from linqpad/Resources/Markdown/paragraph1.md
rename to examples/resources/Markdown/paragraph1.md
diff --git a/linqpad/Resources/Markdown/paragraph2.md b/examples/resources/Markdown/paragraph2.md
similarity index 100%
rename from linqpad/Resources/Markdown/paragraph2.md
rename to examples/resources/Markdown/paragraph2.md
diff --git a/linqpad/Resources/Markdown/paragraph3.md b/examples/resources/Markdown/paragraph3.md
similarity index 100%
rename from linqpad/Resources/Markdown/paragraph3.md
rename to examples/resources/Markdown/paragraph3.md
diff --git a/linqpad/Resources/Markdown/style.css b/examples/resources/Markdown/style.css
similarity index 100%
rename from linqpad/Resources/Markdown/style.css
rename to examples/resources/Markdown/style.css
diff --git a/linqpad/Resources/OfficeDocs/LorumIpsem.txt b/examples/resources/OfficeDocs/LorumIpsem.txt
similarity index 100%
rename from linqpad/Resources/OfficeDocs/LorumIpsem.txt
rename to examples/resources/OfficeDocs/LorumIpsem.txt
diff --git a/linqpad/Resources/OfficeDocs/Visual-Studio-NOTICE.docx b/examples/resources/OfficeDocs/Visual-Studio-NOTICE.docx
similarity index 100%
rename from linqpad/Resources/OfficeDocs/Visual-Studio-NOTICE.docx
rename to examples/resources/OfficeDocs/Visual-Studio-NOTICE.docx
diff --git a/linqpad/Resources/OfficeDocs/document.docx b/examples/resources/OfficeDocs/document.docx
similarity index 100%
rename from linqpad/Resources/OfficeDocs/document.docx
rename to examples/resources/OfficeDocs/document.docx
diff --git a/linqpad/Resources/OfficeDocs/document.rtf b/examples/resources/OfficeDocs/document.rtf
similarity index 100%
rename from linqpad/Resources/OfficeDocs/document.rtf
rename to examples/resources/OfficeDocs/document.rtf
diff --git a/linqpad/Resources/OfficeDocs/document2.docx b/examples/resources/OfficeDocs/document2.docx
similarity index 100%
rename from linqpad/Resources/OfficeDocs/document2.docx
rename to examples/resources/OfficeDocs/document2.docx
diff --git a/linqpad/Resources/Settings/appsettings.json b/examples/resources/Settings/appsettings.json
similarity index 100%
rename from linqpad/Resources/Settings/appsettings.json
rename to examples/resources/Settings/appsettings.json
diff --git a/linqpad/Resources/office/document.docx b/examples/resources/office/document.docx
similarity index 100%
rename from linqpad/Resources/office/document.docx
rename to examples/resources/office/document.docx
diff --git a/src/Gotenberg.Sharp.Api.Client/Extensions/TypedClientServiceCollectionExtensions.cs b/src/Gotenberg.Sharp.Api.Client/Extensions/TypedClientServiceCollectionExtensions.cs
index cdf89b6..beb2a7f 100644
--- a/src/Gotenberg.Sharp.Api.Client/Extensions/TypedClientServiceCollectionExtensions.cs
+++ b/src/Gotenberg.Sharp.Api.Client/Extensions/TypedClientServiceCollectionExtensions.cs
@@ -52,19 +52,10 @@ public static IHttpClientBuilder AddGotenbergSharpClient(
var ops = GetOptions(sp);
client.Timeout = ops.TimeOut;
client.BaseAddress = ops.ServiceUrl;
-
- // Add basic auth header if credentials are provided
- if (!string.IsNullOrWhiteSpace(ops.BasicAuthUsername) &&
- !string.IsNullOrWhiteSpace(ops.BasicAuthPassword))
- {
- var credentials = Convert.ToBase64String(
- System.Text.Encoding.ASCII.GetBytes($"{ops.BasicAuthUsername}:{ops.BasicAuthPassword}"));
- client.DefaultRequestHeaders.Authorization =
- new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);
- }
});
}
+
///
/// Registers GotenbergSharpClient with dependency injection using a custom HttpClient configuration.
///
@@ -82,7 +73,7 @@ public static IHttpClientBuilder AddGotenbergSharpClient(
{
if (configureClient == null) throw new ArgumentNullException(nameof(configureClient));
- return services
+ var builder = services
.AddHttpClient(nameof(GotenbergSharpClient), configureClient)
.AddTypedClient()
.ConfigurePrimaryHttpMessageHandler(
@@ -92,8 +83,33 @@ public static IHttpClientBuilder AddGotenbergSharpClient(
AutomaticDecompression = DecompressionMethods.GZip
| DecompressionMethods.Deflate
}))
+ .AddHttpMessageHandler(sp =>
+ {
+ var ops = GetOptions(sp);
+
+ var hasUsername = !string.IsNullOrWhiteSpace(ops.BasicAuthUsername);
+ var hasPassword = !string.IsNullOrWhiteSpace(ops.BasicAuthPassword);
+
+ // Validate that both username and password are provided together
+ if (hasUsername ^ hasPassword)
+ {
+ throw new InvalidOperationException(
+ "BasicAuth configuration is incomplete. Both BasicAuthUsername and BasicAuthPassword must be set, or neither should be set.");
+ }
+
+ // Add basic auth handler if credentials are configured
+ if (hasUsername && hasPassword)
+ {
+ return new BasicAuthHandler(ops.BasicAuthUsername!, ops.BasicAuthPassword!);
+ }
+
+ // Return a pass-through handler if no auth is configured
+ return new PassThroughHandler();
+ })
.AddPolicyHandler(PolicyFactory.CreatePolicyFromSettings)
.SetHandlerLifetime(TimeSpan.FromMinutes(6));
+
+ return builder;
}
private static GotenbergSharpClientOptions GetOptions(IServiceProvider sp)
diff --git a/src/Gotenberg.Sharp.Api.Client/Infrastructure/Pipeline/BasicAuthHandler.cs b/src/Gotenberg.Sharp.Api.Client/Infrastructure/Pipeline/BasicAuthHandler.cs
new file mode 100644
index 0000000..3cd7d96
--- /dev/null
+++ b/src/Gotenberg.Sharp.Api.Client/Infrastructure/Pipeline/BasicAuthHandler.cs
@@ -0,0 +1,49 @@
+// Copyright 2019-2025 Chris Mohan, Jaben Cargman
+// and GotenbergSharpApiClient Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Net.Http.Headers;
+using System.Text;
+
+namespace Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+
+///
+/// HTTP message handler that adds Basic Authentication headers to outgoing requests
+///
+public class BasicAuthHandler : DelegatingHandler
+{
+ private readonly string _username;
+ private readonly string _password;
+
+ ///
+ /// Creates a new BasicAuthHandler with the specified credentials
+ ///
+ /// Basic auth username
+ /// Basic auth password
+ public BasicAuthHandler(string username, string password)
+ {
+ _username = username ?? throw new ArgumentNullException(nameof(username));
+ _password = password ?? throw new ArgumentNullException(nameof(password));
+ }
+
+ protected override Task SendAsync(
+ HttpRequestMessage request,
+ CancellationToken cancellationToken)
+ {
+ var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_username}:{_password}"));
+ request.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials);
+
+ return base.SendAsync(request, cancellationToken);
+ }
+}
diff --git a/src/Gotenberg.Sharp.Api.Client/Infrastructure/Pipeline/PassThroughHandler.cs b/src/Gotenberg.Sharp.Api.Client/Infrastructure/Pipeline/PassThroughHandler.cs
new file mode 100644
index 0000000..81f830b
--- /dev/null
+++ b/src/Gotenberg.Sharp.Api.Client/Infrastructure/Pipeline/PassThroughHandler.cs
@@ -0,0 +1,29 @@
+// Copyright 2019-2025 Chris Mohan, Jaben Cargman
+// and GotenbergSharpApiClient Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Gotenberg.Sharp.API.Client.Infrastructure.Pipeline;
+
+///
+/// HTTP message handler that passes requests through without modification
+///
+internal class PassThroughHandler : DelegatingHandler
+{
+ protected override Task SendAsync(
+ HttpRequestMessage request,
+ CancellationToken cancellationToken)
+ {
+ return base.SendAsync(request, cancellationToken);
+ }
+}