google-chromepdfgoogle-chrome-devtoolspuppeteer-sharp

Puppeteer Sharp times out when running via IIS


I'm using PuppeteerSharp (20.0.2) in a .NET 6 Web Api project. It's working beautifully when I run in dev mode, but when I try to deploy to IIS, the call to PdfStreamAsync() times out after three minutes.

Originally, I thought the issue was this

https://www.puppeteersharp.com/docs/IssuesGeneratingPdfFiles.html

but I've seen other users reporting that issue, and they seem to specifically receive an error message mentioning sandbox permissions. Mine just says "Timeout of 180000 ms exceeded". I'm writing the value of InstalledBrowser.PermissionsFixed to the log and it's saying it's True.

[HttpGet()]
public async Task<ActionResult> Get() {
  var installedBrowser = await new BrowserFetcher().DownloadAsync();

  _logger.LogInformation($"PermissionsFixed is {installedBrowser.PermissionsFixed}");

  using (var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true })) {
    using (var page = await browser.NewPageAsync()) {
      await page.GoToAsync("https://www.google.co.uk");
      var stream = await page.PdfStreamAsync();
      return File(stream, "application/pdf");
    }
  }
}

I end up with a folder "Chrome" and another "ChromeHeadlessShell" in my application folder. The version for both is Win64-128.0.6613.119

Error log

System.TimeoutException: Timeout of 180000 ms exceeded
   at PuppeteerSharp.Helpers.TaskHelper.WithTimeout[T](Task`1 task, TimeSpan timeout, Func`2 exceptionFactory) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Helpers/TaskHelper.cs:line 185
   at PuppeteerSharp.Cdp.CdpCDPSession.SendAsync(String method, Object args, Boolean waitForCallback, CommandOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Cdp/CdpCDPSession.cs:line 113
   at PuppeteerSharp.CDPSession.SendAsync[T](String method, Object args, CommandOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/CDPSession.cs:line 48
   at PuppeteerSharp.Cdp.CdpPage.PdfInternalAsync(String file, PdfOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Cdp/CdpPage.cs:line 822
   at PuppeteerSharp.Page.PdfStreamAsync(PdfOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Page.cs:line 389
   at WebApplication6.Controllers.PdfController.Get() in C:\Users\paulh\source\repos\WebApplication6\WebApplication6\Controllers\PdfController.cs:line 23

Edit:

I altered my method to include more logging

[HttpGet()]
public async Task<ActionResult> Get() {
  var installedBrowser = await new BrowserFetcher().DownloadAsync();

  _logger.LogInformation($"PermissionsFixed is {installedBrowser.PermissionsFixed}");

  using (var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, DumpIO = true, Args = new[] { "--enable-logging", "--v=1" } })) {
    using (var page = await browser.NewPageAsync()) {

      page.Request += (sender, e) => { _logger.LogInformation($"CHROMERequest: {e.Request.Url}"); };
      page.Response += (sender, e) => { _logger.LogInformation($"CHROMEResponse: {e.Response.Url} - {e.Response.Status}"); };
      page.RequestFailed += (sender, e) => { _logger.LogInformation($"CHROMERequest failed: {e.Request.Url} - {e.Request.FailureText}"); };
      page.DOMContentLoaded += (sender, e) => { _logger.LogInformation($"CHROMEDOM Content Loaded {e.ToString}"); };
      page.Console += (sender, e) => { _logger.LogInformation($"CHROMEConsole {e.Message}"); };
      page.Error += (sender, e) => { _logger.LogInformation($"CHROMEError {e.Error}"); };
      page.PageError += (sender, e) => { _logger.LogInformation($"CHROMEPageError {e.Message}"); };

      _logger.LogInformation("LOG 1");

      await page.GoToAsync("https://www.e-idvglobal.com/cookie.php");

      _logger.LogInformation("LOG 2");

      var stream = await page.PdfStreamAsync();

      _logger.LogInformation("LOG 3");

      return File(stream, "application/pdf");
    }
  }
}

In the log, I get a load of Request and Response events as it loads all the different elements of the page, but no errors or anything out of the ordinary. I always get the LOG 2 message.

If I'm running from the dev environment, I then get

2024-09-26 16:44:47.025 +01:00 [INF] LOG 2
2024-09-26 16:44:47.212 +01:00 [INF] LOG 3
2024-09-26 16:44:47.240 +01:00 [INF] Executing FileStreamResult, sending file with download name '' ...
2024-09-26 16:44:47.242 +01:00 [DBG] The file result has not been enabled for processing range requests. To enable it, set the EnableRangeProcessing property on the result to 'true'.
2024-09-26 16:44:47.345 +01:00 [INF] Executed action WebApplication6.Controllers.PdfController.Get (WebApplication6) in 1984.0378ms
2024-09-26 16:44:47.391 +01:00 [INF] Executed endpoint 'WebApplication6.Controllers.PdfController.Get (WebApplication6)'
2024-09-26 16:44:47.394 +01:00 [INF] HTTP GET /pdf responded 200 in 2125.9885 ms
2024-09-26 16:44:47.397 +01:00 [INF] Request finished HTTP/2 GET https://localhost:7262/pdf - - - 200 36438 application/pdf 2140.8124ms

but if I'm running in IIS, at that point I get a gap of three minutes in the timestamps, followed by

2024-09-26 16:30:33.363 +01:00 [INF] LOG 2
2024-09-26 16:33:33.456 +01:00 [INF] Executed action WebApplication6.Controllers.PdfController.Get (WebApplication6) in 183781.045ms
2024-09-26 16:33:33.457 +01:00 [INF] Executed endpoint 'WebApplication6.Controllers.PdfController.Get (WebApplication6)'
2024-09-26 16:33:33.460 +01:00 [ERR] HTTP GET /pdf responded 500 in 183823.8211 ms
System.TimeoutException: Timeout of 180000 ms exceeded
   at PuppeteerSharp.Helpers.TaskHelper.WithTimeout[T](Task`1 task, TimeSpan timeout, Func`2 exceptionFactory) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Helpers/TaskHelper.cs:line 185
   at PuppeteerSharp.Cdp.CdpCDPSession.SendAsync(String method, Object args, Boolean waitForCallback, CommandOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Cdp/CdpCDPSession.cs:line 113
   at PuppeteerSharp.CDPSession.SendAsync[T](String method, Object args, CommandOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/CDPSession.cs:line 48
   at PuppeteerSharp.Cdp.CdpPage.PdfInternalAsync(String file, PdfOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Cdp/CdpPage.cs:line 822
   at PuppeteerSharp.Page.PdfStreamAsync(PdfOptions options) in /home/runner/work/puppeteer-sharp/puppeteer-sharp/lib/PuppeteerSharp/Page.cs:line 389
   at WebApplication6.Controllers.PdfController.Get() in C:\Users\paulh\source\repos\WebApplication6\WebApplication6\Controllers\PdfController.cs:line 63
   at lambda_method4(Closure , Object )

Solution

  • I managed to get it working.

    Downloaded Chrome binaries from here

    https://googlechromelabs.github.io/chrome-for-testing/

    (in my case I got Stable, chrome-headless-shell, win64)

    Unzipped it to a local folder and changed my method to

    [HttpGet()]
    public async Task<ActionResult> Get() {
      using (var browser = await Puppeteer.LaunchAsync(new LaunchOptions { ExecutablePath = @"C:\chrome-headless-shell-win64\chrome-headless-shell.exe", Headless = true } )) {
        using (var page = await browser.NewPageAsync()) {
          await page.GoToAsync("https://www.google.com");
          var stream = await page.PdfStreamAsync();
          return File(stream, "application/pdf");
        }
      }
    }
    

    And it's working great. And of course doing it this way is the recommended way of doing it anyway, so all happy.