bitcrowd / chromic_pdf

Convenient HTML to PDF/A rendering library for Elixir based on Chrome & Ghostscript
Apache License 2.0
409 stars 37 forks source link

Gracefully handle errors returned by chrome #244

Closed tonnenpinguin closed 1 year ago

tonnenpinguin commented 1 year ago

Hi!

While playing around with this I noticed that if I pass some arguably invalid options like in the following example, chromic ignores the error sent by Chrome and instead waits for the timeout and then fails with a generic message.

ChromicPDF.print_to_pdf({:html, "<h1>Hello,world!</h1>"}, print_to_pdf: %{pageRanges: "2-3"})

In the logs you can see that Chrome immediately tells us that the page range argument was faulty.

18:12:15.260 [debug] [ChromicPDF] msg out: %{"id" => 23, "method" => "Page.printToPDF", "params" => %{"footerTemplate" => "", "headerTemplate" => "", "pageRanges" => "2-3"}, "sessionId" => "CE865B72566BD21A8272A3F7EEF30EBE"}
18:12:15.262 [debug] [ChromicPDF] msg in: %{"error" => %{"code" => -32000, "message" => "Page range exceeds page count"}, "id" => 23, "sessionId" => "CE865B72566BD21A8272A3F7EEF30EBE"}
18:12:15.262 [debug] [ChromicPDF] msg in: %{"method" => "Page.frameResized", "params" => %{}, "sessionId" => "CE865B72566BD21A8272A3F7EEF30EBE"}
[... about 5 seconds later the error message appears]
** (ChromicPDF.Browser.ExecutionError) Timeout in Channel.run_protocol/3!

The underlying GenServer.call/3 exited with a timeout. This happens when the browser was
not able to complete the current operation (= PDF print job) within the configured
5000 milliseconds.

If you are printing large PDFs and expect long processing times, please consult the
documentation for the `timeout` option of the session pool.

If you are *not* printing large PDFs but your print jobs still time out, this is likely a
bug in ChromicPDF. Please open an issue on the issue tracker.

---

Current protocol:

%ChromicPDF.Protocol{
  steps: [
    await: &ChromicPDF.PrintToPDF.printed/2,
    call: &ChromicPDF.ResetTarget.reset_history/2,
    await: &ChromicPDF.ResetTarget.history_reset/2,
    call: &ChromicPDF.ResetTarget.blank/2,
    await: &ChromicPDF.ResetTarget.blanked/2,
    await: &ChromicPDF.ResetTarget.fsl_after_blank/2,
    output: &ChromicPDF.PrintToPDF.output/1
  ],
  state: %{
    :__protocol__ => ChromicPDF.PrintToPDF,
    :capture_screenshot => %{},
    :html => "[FILTERED]",
    :ignore_certificate_errors => false,
    :last_call_id => 23,
    :name => "[FILTERED]",
    :offline => false,
    :print_to_pdf => %{
      "footerTemplate" => "[FILTERED]",
      "headerTemplate" => "[FILTERED]",
      "pageRanges" => "2-3"
    },
    :source_type => :html,
    :unhandled_runtime_exceptions => "[FILTERED]",
    "frameId" => "43E1D2D96EBAEDF4BB82A95B74DE397A",
    "sessionId" => "CE865B72566BD21A8272A3F7EEF30EBE"
  }
}

    (chromic_pdf 1.8.1) lib/chromic_pdf/pdf/browser/channel.ex:23: ChromicPDF.Browser.Channel.run_protocol/3
    (chromic_pdf 1.8.1) lib/chromic_pdf/pdf/browser/session_pool.ex:115: ChromicPDF.Browser.SessionPool.do_run_protocol/4
    (nimble_pool 1.0.0) lib/nimble_pool.ex:349: NimblePool.checkout!/4
    (chromic_pdf 1.8.1) lib/chromic_pdf/pdf/browser/session_pool.ex:55: ChromicPDF.Browser.SessionPool.run_protocol/3
    (chromic_pdf 1.8.1) lib/chromic_pdf/api.ex:67: anonymous fn/3 in ChromicPDF.API.chrome_export/4
    (chromic_pdf 1.8.1) lib/chromic_pdf/api/telemetry.ex:10: anonymous fn/2 in ChromicPDF.Telemetry.with_telemetry/3
    (telemetry 1.1.0) /Users/bernhardhackl/src/chromic_pdf/deps/telemetry/src/telemetry.erl:320: :telemetry.span/3
    iex:4: (file)
18:12:20.252 [debug] [ChromicPDF] msg out: %{"id" => 24, "method" => "Target.closeTarget", "params" => %{targetId: "43E1D2D96EBAEDF4BB82A95B74DE397A"}}

This PR fixes this and instead of waiting for a timeout handles errors sent via rpc right away. There's one failing test, but as far as I can tell we should rewrite the test as it basically tests for the behaviour that I fixed here.

maltoe commented 1 year ago

Hey @tonnenpinguin,

thanks for the contribution :purple_heart:! Looks good and is clearly a way to do this, though I'd prefer a different solution. Let me know what you think:

So, I'd suggest to do the following:

Benefits of this approach

maltoe commented 1 year ago

Moin moin nach Hamburg im Übrigen

tonnenpinguin commented 1 year ago

Moin @maltoe!

Thanks for the detailed feedback, that helps a lot when playing around with a new codebase :)

I applied the suggested changes, let me know what you think! :)

A couple random things I noticed

maltoe commented 1 year ago

A couple random things I noticed

Feel free to fix these, if you like.

maltoe commented 1 year ago

merged to main manually, thanks again :purple_heart: