跳到內容
getnextpdf.com

Pro 版本

Webview — 深入參考

本頁記錄公開的 NextPDF\Pro\Webview 介面、byte-range 請求/回應模型,以及超出公開登陸頁面之外的確切失敗模式。

此能力隨 NextPDF Pronextpdf/pro)出貨,並以 Pro 級授權封套(license envelope)啟用。缺少該權益的部署不會載入此能力的類別。比較各版本並取得授權

沒有個別功能的授權旗標;程式碼隨 Pro 版本一併出貨。PSR-17 ResponseFactoryInterface / StreamFactoryInterface 與主體媒體型別都是執行階段的建構子參數,而非授權控制。

Terminal window
composer require nextpdf/pro

NextPDF\Pro\Webview 下的公開型別:

  • LinearizedDocument——一份已驗證、備妥交付的線性化 PDF。
  • ByteRangeResponder——RFC 9110 byte-range HTTP responder。
  • ByteRange——某個表示上一個可滿足的包含式 byte range。
  • FirstPageProber——「在完整下載之前的第一頁」結構性證明。

NextPDF\Pro\Webview\Exception 下的例外型別:

  • WebviewException(marker interface)、UnsupportedDocumentExceptionRangeNotSatisfiableException

一個帶有 private constructor 的 final readonly 類別;請透過具名建構子實例化。

  • static fromBytes(string $bytes): self——透過 Core 的讀取端 LinearizationView::fromPdf() 解析這些位元組。當文件未線性化、宣告的 /L 與實際位元組長度不符,或 /E 第一頁結尾偏移不是檔案內的正向偏移時,擲出 UnsupportedDocumentException
  • length(): int——以位元組計的文件長度。
  • firstPagePrefixLength(): int——含完整第一頁的最小前導前綴:/E 偏移,夾限至檔案長度。
  • firstPageByteRange(): ByteRange——交付第一頁的包含式範圍 [0, /E - 1]。僅在前綴為空時擲出 RangeNotSatisfiableException(縱深防禦;fromBytes() 已保證 0 < /E <= length)。
  • slice(int $firstByte, int $lastByte): string——使用包含式偏移的嚴格程式化切片;越界時擲出 RangeNotSatisfiableException
  • etag(): string——位元組的強而確定性的 SHA-256 entity-tag,在建構時記憶化一次。

公開 readonly 屬性:bytes(原始 PDF 位元組)與 view(Core 的 LinearizationView)。

一個僅針對 PSR-7 / PSR-17 實作的 final readonly 類別。

  • __construct(ResponseFactoryInterface $responses, StreamFactoryInterface $streams, string $contentType = 'application/pdf')——當 $contentType 含控制字元時擲出 InvalidArgumentException(它會被內插進回應與 multipart 部分標頭;CR/LF 與其他控制位元組會被拒絕以防止標頭注入)。
  • respond(LinearizedDocument $document, ServerRequestInterface $request): ResponseInterface——使用文件自身的 ETag,回答一份線性化文件的範圍請求。
  • respondToBytes(string $bytes, ServerRequestInterface $request, ?string $etag = null): ResponseInterface——回答任意位元組的範圍請求(一個應支援範圍但不必線性化的表示)。當為 null 時,ETag 由位元組推導。
  • firstPageResponse(LinearizedDocument $document): ResponseInterface——建構一個恰好攜帶第一頁 byte range 的 206;亦即「在完整下載之前的第一頁」的伺服器推送形式。

一個用於單一可滿足包含式 byte range 的 final readonly 值物件(RFC 9110 §14.1.2)。

  • __construct(int $firstByte, int $lastByte, int $contentLength)——強制可滿足性 0 <= firstByte <= lastByte <= contentLength - 1;否則擲出 RangeNotSatisfiableException
  • length(): int——包含式跨度(lastByte - firstByte + 1),永遠 >= 1
  • contentRange(): string——RFC 9110 §14.4 的 Content-Range 欄位值 bytes first-last/length

公開 readonly 屬性:firstBytelastBytecontentLength

一個由 LinearizedDocument 建構的 final readonly 類別。

  • prefixLength(): int——算繪第一頁所需的最小前導位元組數。
  • prefixFraction(): float——前綴占整份檔案的比例(0.0–1.0);對一個零長度檔案回傳 1.0
  • hintStreamWithinPrefix(): bool——主要的 hint stream 物件是否完全落在第一頁前綴之內(使前綴本身就讓讀取端能定位第 1 頁物件)。hint stream 必須有正向長度。
  • isFirstPageSelfContained(): bool——合併的結構性證明:一個落在檔案內、且完全含有 hint stream 的正向前綴。

ByteRangeResponder 遵循 RFC 9110 §14。在計算長度與一個強 SHA-256 ETag 之後,它會讀取 RangeIf-Range 標頭並決定:

條件狀態備註
沒有適用的 Range,或 If-Range 與當前強 ETag 不符200 OK完整主體。只有強 entity-tag 形式的 If-Range 會被遵守(RFC 9110 §13.1.5)。
無法辨識的範圍單位或語法上無效的 Range200 OK標頭被忽略(RFC 9110 §14.2)。
一個可滿足的範圍206 Partial Content攜帶 Content-Range
多個可滿足的範圍206 Partial Content帶有一個推導出之邊界的 multipart/byteranges
有效的 byte range,但無一可滿足416 Range Not Satisfiable攜帶 Content-Range: bytes */length(RFC 9110 §15.3.7)。

每個回應都會通告 Accept-Ranges: bytes 與強 ETag200206 回應也會設定 Content-TypeContent-Length

範圍解析僅接受 bytes= 單位。它支援明確的 first-last、開放結尾的 first-(夾限至結尾),以及後綴 -N(最後 N 個位元組;一個至少與表示一樣大的後綴會選取其全部)。一個裸的 -(或任何其他格式不良的規格)會讓整個 Range 標頭在語法上無效,因此該標頭被忽略並回傳完整的 200 OK 表示。一個 -0 後綴,或任何首偏移在結尾處或之後的規格,是一個不可滿足的規格並被丟棄;若標頭中沒有任何規格可滿足,回應即為 416 Range Not Satisfiable。較大的十進位偏移在比較時不依賴整數溢位飽和,因此一個 30 位數的 Range 值會被以平台無關的方式處理。重疊的可滿足範圍會在任何主體被建構之前先被合併;真正相異(不重疊)的範圍會被保留為各自獨立的 multipart 部分。

WebviewException 是一個擴充 Throwable 的 marker interface;catch 它以一致地處理整個子系統。兩個具體例外都實作它。

  • UnsupportedDocumentException(擴充 InvalidArgumentException)——當位元組不是一份可用的線性化文件時,由 LinearizedDocument::fromBytes() 引發。具名建構子:notLinearized()(無 /Linearized 參數字典)、lengthMismatch($declaredLength, $actualLength)(宣告的 /L 與實際長度不符——被截斷、透過超出 /L 的增量更新被附加,或不符規範),以及 malformedFirstPageOffset($firstPageEndOffset, $length)/E 偏移不是檔案內的正向偏移)。
  • RangeNotSatisfiableException(擴充 OutOfRangeException)——程式化切片錯誤,當一個包含式範圍落在文件之外時,由 ByteRange::__construct()LinearizedDocument::slice() / firstPageByteRange() 引發。具名建構子:outOfBounds($firstByte, $lastByte, $length)

HTTP responder 不會為用戶端的 Range 標頭擲出 RangeNotSatisfiableException——一個不可滿足的 HTTP 範圍是一個 416 回應(RFC 9110 §15.3.7),而非例外。該例外保留給直接的程式化切片,在那裡一個越界的請求是呼叫端的錯誤。當設定好的 contentType 含控制字元時,ByteRangeResponder::__construct() 會擲出一個純粹的 InvalidArgumentException(而非 WebviewException)。

responder 會為每個請求所遵守的相異合併(coalesced)範圍數目設上限(multipart range-amplification 這一類,Apache HTTPD CVE-2011-3192)。當一個請求要求比合併範圍上限更多,或比整份表示更多的總位元組時,Range 會被忽略,並回傳一個完整的 200。multipart 邊界會被確定性地推導,並重新推導直到它保證不會出現在主體內,在排除邊界碰撞的同時保留可重現的輸出。

byte-range 行為遵循 RFC 9110(HTTP Semantics):§14(範圍請求)、§13.1.5(If-Range)、§14.4(Content-Range)與 §15.3.7(416)。線性化文件排版是 ISO 32000-2 Annex F 的 Fast Web View 模型。本模組除了其測試所驗證的行為之外,不主張任何進一步的外部條款識別碼。

  • 當不需要第一頁語意時,respondToBytes() 會在任意位元組上提供範圍。
  • 一個 If-Range HTTP-date 驗證器會被視為不符 → 完整的 200(用戶端只是重新抓取)。
  • ETag 是一個純粹用作強快取驗證器的 SHA-256 雜湊;本模組不進行任何簽署或其他密碼學運算,也未定義任何 FIPS 特定行為。

本頁僅記錄外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表格、runbook 檔名與工單前綴均不在範圍內。