Aller au contenu
getnextpdf.com

Pro édition

Webview — Référence approfondie

Cette page documente la surface publique NextPDF\Pro\Webview, le modèle requête/réponse par plage d’octets et les modes de défaillance exacts, au-delà de la page d’accueil publique.

Cette capacité est livrée dans NextPDF Pro (nextpdf/pro) et s’active avec une enveloppe de licence de palier Pro. Un déploiement sans ce droit ne charge pas les classes de la capacité. Compare les éditions et obtiens une licence.

Il n’y a pas d’indicateur de licence par fonctionnalité ; le code est livré avec l’édition Pro. Les ResponseFactoryInterface / StreamFactoryInterface PSR-17 et le type de média du corps sont des paramètres de constructeur au runtime, non des contrôles de licence.

Fenêtre de terminal
composer require nextpdf/pro

Types publics sous NextPDF\Pro\Webview :

  • LinearizedDocument — un PDF linéarisé validé, préparé pour la livraison.
  • ByteRangeResponder — le responder HTTP par plage d’octets RFC 9110.
  • ByteRange — une plage d’octets inclusive satisfaisable sur une représentation.
  • FirstPageProber — la preuve structurelle « première page avant téléchargement complet ».

Types d’exception sous NextPDF\Pro\Webview\Exception :

  • WebviewException (interface marqueur), UnsupportedDocumentException, RangeNotSatisfiableException.

Une classe final readonly à constructeur privé ; instancie-la via le constructeur nommé.

  • static fromBytes(string $bytes): self — analyse les octets via la LinearizationView::fromPdf() côté lecture du Core. Lève UnsupportedDocumentException lorsque le document n’est pas linéarisé, lorsque la longueur /L déclarée ne correspond pas à la longueur réelle en octets, ou lorsque l’offset de fin de première page /E n’est pas un offset positif à l’intérieur du fichier.
  • length(): int — la longueur du document en octets.
  • firstPagePrefixLength(): int — le préfixe de tête minimal qui contient la première page complète : l’offset /E, borné à la longueur du fichier.
  • firstPageByteRange(): ByteRange — la plage inclusive [0, /E - 1] qui livre la première page. Lève RangeNotSatisfiableException uniquement si le préfixe est vide (défense en profondeur ; fromBytes() garantit déjà 0 < /E <= length).
  • slice(int $firstByte, int $lastByte): string — découpe programmatique stricte avec offsets inclusifs ; lève RangeNotSatisfiableException en cas de dépassement de bornes.
  • etag(): string — l’étiquette d’entité SHA-256 forte et déterministe pour les octets, mémoïsée une fois à la construction.

Propriétés publiques en lecture seule : bytes (les octets bruts du PDF) et view (la LinearizationView du Core).

Une classe final readonly implémentée uniquement contre PSR-7 / PSR-17.

  • __construct(ResponseFactoryInterface $responses, StreamFactoryInterface $streams, string $contentType = 'application/pdf') — lève InvalidArgumentException lorsque $contentType contient des caractères de contrôle (il est interpolé dans les en-têtes de réponse et de partie multipart ; CR/LF et autres octets de contrôle sont rejetés pour prévenir l’injection d’en-tête).
  • respond(LinearizedDocument $document, ServerRequestInterface $request): ResponseInterface — répond à une requête de plage pour un document linéarisé, en utilisant l’ETag propre au document.
  • respondToBytes(string $bytes, ServerRequestInterface $request, ?string $etag = null): ResponseInterface — répond à une requête de plage pour des octets arbitraires (une représentation qui devrait prendre en charge les plages sans être linéarisée). L’ETag est dérivé des octets lorsqu’il est null.
  • firstPageResponse(LinearizedDocument $document): ResponseInterface — construit un 206 portant exactement la plage d’octets de la première page ; la forme par poussée serveur de « première page avant téléchargement complet ».

Un objet valeur final readonly pour une plage d’octets inclusive satisfaisable unique (RFC 9110 §14.1.2).

  • __construct(int $firstByte, int $lastByte, int $contentLength) — impose la satisfaisabilité 0 <= firstByte <= lastByte <= contentLength - 1 ; lève RangeNotSatisfiableException sinon.
  • length(): int — l’étendue inclusive (lastByte - firstByte + 1), toujours >= 1.
  • contentRange(): string — la valeur du champ Content-Range RFC 9110 §14.4 bytes first-last/length.

Propriétés publiques en lecture seule : firstByte, lastByte, contentLength.

Une classe final readonly construite à partir d’un LinearizedDocument.

  • prefixLength(): int — nombre d’octets de tête minimal nécessaire pour afficher la première page.
  • prefixFraction(): float — fraction du fichier entier (0.0–1.0) que le préfixe représente ; renvoie 1.0 pour un fichier de longueur nulle.
  • hintStreamWithinPrefix(): bool — si l’objet de flux d’indices primaire se trouve entièrement à l’intérieur du préfixe de première page (de sorte que le préfixe seul permette à un lecteur de localiser les objets de la page 1). Le flux d’indices doit avoir une longueur positive.
  • isFirstPageSelfContained(): bool — la preuve structurelle combinée : un préfixe positif qui tient dans le fichier et contient entièrement le flux d’indices.

ByteRangeResponder suit RFC 9110 §14. Après avoir calculé la longueur et un ETag SHA-256 fort, il lit les en-têtes Range et If-Range et décide :

ConditionStatutNotes
Aucun Range applicable, ou If-Range ne correspond pas à l’ETag fort courant200 OKCorps complet. Seule la forme d’étiquette d’entité forte de If-Range est honorée (RFC 9110 §13.1.5).
Unité de plage non reconnue ou Range syntaxiquement invalide200 OKL’en-tête est ignoré (RFC 9110 §14.2).
Une plage satisfaisable206 Partial ContentPorte Content-Range.
Plusieurs plages satisfaisables206 Partial Contentmultipart/byteranges avec une frontière dérivée.
Plages d’octets valides, aucune satisfaisable416 Range Not SatisfiablePorte Content-Range: bytes */length (RFC 9110 §15.3.7).

Chaque réponse annonce Accept-Ranges: bytes et l’ETag fort. Les réponses 200 et 206 définissent aussi Content-Type et Content-Length.

L’analyse de plage n’accepte que l’unité bytes=. Elle prend en charge les first-last explicites, les first- ouverts (bornés à la fin) et le suffixe -N (les N derniers octets ; un suffixe au moins aussi grand que la représentation la sélectionne en entier). Un - seul (ou toute autre spec mal formée) rend l’en-tête Range entier syntaxiquement invalide, si bien que l’en-tête est ignoré et que la représentation complète 200 OK est renvoyée. Un suffixe -0, ou toute spec dont le premier offset est au niveau ou au-delà de la fin, est une spec insatisfaisable et est écartée ; si aucune spec de l’en-tête n’est satisfaisable, la réponse est 416 Range Not Satisfiable. Les grands offsets décimaux sont comparés sans s’appuyer sur la saturation par dépassement d’entier, de sorte qu’une valeur Range de 30 chiffres soit gérée indépendamment de la plateforme. Les plages satisfaisables chevauchantes sont fusionnées avant la construction de tout corps ; les plages véritablement distinctes (non chevauchantes) sont préservées en parties multipart séparées.

WebviewException est une interface marqueur qui étend Throwable ; capture-la pour gérer tout le sous-système de façon uniforme. Les deux exceptions concrètes l’implémentent.

  • UnsupportedDocumentException (étend InvalidArgumentException) — levée par LinearizedDocument::fromBytes() lorsque les octets ne sont pas un document linéarisé exploitable. Constructeurs nommés : notLinearized() (aucun dictionnaire de paramètres /Linearized), lengthMismatch($declaredLength, $actualLength) (le /L déclaré ne correspond pas à la longueur réelle — tronqué, augmenté par une mise à jour incrémentale au-delà de /L, ou non conforme), et malformedFirstPageOffset($firstPageEndOffset, $length) (l’offset /E n’est pas un offset positif à l’intérieur du fichier).
  • RangeNotSatisfiableException (étend OutOfRangeException) — l’erreur de découpe programmatique, levée par ByteRange::__construct() et LinearizedDocument::slice() / firstPageByteRange() lorsqu’une plage inclusive tombe en dehors du document. Constructeur nommé : outOfBounds($firstByte, $lastByte, $length).

Le responder HTTP ne lève pas RangeNotSatisfiableException pour les en-têtes Range du client — une plage HTTP insatisfaisable est une réponse 416 (RFC 9110 §15.3.7), pas une exception. Cette exception est réservée au découpage programmatique direct, où une requête hors bornes est une erreur de l’appelant. ByteRangeResponder::__construct() lève une InvalidArgumentException simple (pas une WebviewException) lorsque le contentType configuré contient des caractères de contrôle.

Le responder plafonne le nombre de plages fusionnées distinctes honorées par requête (la classe d’amplification de plage multipart, Apache HTTPD CVE-2011-3192). Lorsqu’une requête demande plus de plages fusionnées que le plafond, ou plus d’octets au total que la représentation entière, le Range est ignoré et un 200 complet est renvoyé. La frontière multipart est dérivée de façon déterministe et re-dérivée jusqu’à ce que son occurrence à l’intérieur du corps soit garantie impossible, préservant une sortie reproductible tout en excluant la collision de frontière.

Le comportement par plage d’octets suit RFC 9110 (HTTP Semantics) : §14 (requêtes de plage), §13.1.5 (If-Range), §14.4 (Content-Range) et §15.3.7 (416). L’agencement de document linéarisé est le modèle Fast Web View d’ISO 32000-2 Annex F. Le module n’affirme aucun autre identifiant de clause externe au-delà du comportement vérifié par ses tests.

  • respondToBytes() sert des plages sur des octets arbitraires lorsque la sémantique de première page n’est pas nécessaire.
  • Un validateur If-Range à date HTTP est traité comme une non-correspondance → 200 complet (le client se contente de re-récupérer).
  • L’ETag est un hachage SHA-256 utilisé purement comme validateur de cache fort ; ce module n’effectue aucune signature ni autre opération cryptographique et ne définit aucun comportement spécifique à FIPS.

Cette page documente uniquement le comportement observable de l’extérieur et la surface d’API publique prise en charge. Les chemins de namespace internes, les classes utilitaires, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de tickets sont hors périmètre.