Quan es rebutja la càrrega d'un PDF gran: el límit de 100 MiB del cos de la petició i l'error que us porta pel camí equivocat
Un cos de petició pot fer 100 MiB, és a dir 104.857.600 bytes amb l'emmarcat multipart inclòs; per sobre del límit, els endpoints d'operació responen 400 amb el code bad_request i un detall sobre un camp multipart que ha fallat, no 413, i amb Idempotency-Key hi ha un segon límit de 100 MiB a la memòria cau de la resposta, que si se supera dona 500 i no desa res.

Un PDF gran que no es pot pujar normalment no és un fitxer corrupte. El cos de la petició ha tocat el límit per petició: 100 MiB, és a dir 104.857.600 bytes. Es mesura sobre tot el cos, amb les fronteres multipart i les capçaleres de camp incloses, i aquesta xifra exacta passa mentre que un byte més ja es rebutja.
L'error que torna apunta a un altre lloc. Els endpoints d'operació responen 400 amb el code posat a bad_request i un detall que diu que un camp multipart no s'ha pogut llegir, sense cap menció de la mida enlloc. Un client que ramifica segons el code ho arxiva com a error de paràmetre i se'n va a comprovar noms de camp, quan el que cal canviar és la mida del fitxer.
Aquests mateixos 100 MiB també governen la direcció oposada. Una petició que porta Idempotency-Key llegeix la resposta a memòria abans de desar-la a la memòria cau, contra la mateixa xifra, i per sobre el sol·licitant rep 500 mentre que l'operació ja ha acabat. Un sol número, dues maneres oposades de fallar.
100 MiB = 104.857.600 bytes (aquesta xifra exacta passa)
|
+------------------+------------------+
| |
entrant (cos de la petició) sortint (cos de resposta)
tot el cos, fronteres multipart i només amb Idempotency-Key,
capçaleres de camp incloses; una cap encert a la memòria cau
operació i un pipeline el comparteixen i un 2xx de l'origen
per sobre: 400 + bad_request per sobre: 500, res desat
(detall: camp multipart no llegit)
Nota de la figura: un sol límit, i les cares entrant i sortint en donen codis d'estat diferents amb conseqüències oposades.
El límit compta tot el cos de la petició, fronteres incloses
Els 100 MiB limiten el cos d'una sola petició, no la mida d'un sol fitxer ni la mida després de descomprimir.
- Una sola operació i un pipeline de diversos passos comparteixen aquesta xifra. Dividir la feina en 10 passos dins d'una crida a
/api/v1/pipelineno converteix el sostre en 1 GB; el nombre de passos només afecta el temps d'execució. - La xifra del límit mateixa passa: un cos de petició de 104.857.600 bytes hi entra, 104.857.601 bytes no.
- El cos porta les capçaleres de cada camp i els delimitadors de frontera a més dels bytes del fitxer, així que el marge que queda per a un sol fitxer és estrictament inferior a 100 MiB. Un fitxer d'exactament 104.857.600 bytes es rebutja.
Aquest últim punt és on la pràctica rellisca més fàcilment: curl -F afegeix les fronteres per vós, així que comparar la mida d'un fitxer amb aquesta línia no pot sortir mai bé.
Per sobre del límit rebeu 400 i bad_request
Una resposta per sobre del límit no fa servir 413 i no porta cap paraula sobre la mida. Reproduïu-ho amb un cos un byte per sobre:
head -c 104857601 /dev/zero > /tmp/over.bin
curl -s -X POST "$API_BASE/api/v1/misc/compress-pdf" \
-H "X-API-KEY: $API_KEY" \
-F "fileInput=@/tmp/over.bin"
HTTP/1.1 400 Bad Request
content-type: application/problem+json
{ "code": "bad_request",
"detail": "failed to read multipart field: Error parsing `multipart/form-data` request",
"hint": "Fix request parameters or upload a valid PDF.",
"status": 400,
"title": "Bad Request",
"type": "https://pdf123.xyz/developers/errors#bad_request" }
Tres coses per llegir juntes:
- L'estat és 400. Una lectura fallida del cos es classifica aquí com a
bad_requesti continua passant per problem+json, així que un client escrit per a «per sobre del límit vol dir 413» agafa la branca equivocada. - El
codeésbad_request, una entrada real a la taula de codis d'error. No acaba en una branca genèrica; comparteix un codi amb un nom de camp mal escrit o una codificació multipart trencada, i res d'aquella taula no tracta la mida. - El
hintus diu que pugeu un PDF vàlid. El fitxer que heu penjat és molt probablement un PDF vàlid que resulta ser uns quants centenars de bytes massa gran.
El senyal, doncs, no és el codi d'estat sinó el recompte de bytes del cos: en un 400 el detail del qual contingui failed to read multipart field, mesureu la mida en comptes de repassar el formulari.
413 sí que apareix en aquest lloc, només que no als endpoints d'operació. Tots els punts d'entrada que accepten una càrrega de fitxer s'han apujat a 100 MiB, mentre que els endpoints que no reben cap càrrega continuen amb el valor per defecte de 2 MiB del marc HTTP (axum de Rust), on un cos per sobre del límit rep 413 i una línia de text pla:
head -c 2097153 /dev/zero > /tmp/big.json
curl -s -w '\n%{http_code}\n' -X POST "$API_BASE/api/v1/auth/login" \
-H 'Content-Type: application/json' \
--data-binary @/tmp/big.json
Failed to buffer the request body: length limit exceeded
413
Un fet, dos codis d'estat i dos cossos de resposta segons l'endpoint. Portar qualsevol de les dues experiències a l'altra us farà anar equivocats.
L'altra fallada amb el mateix número, a la tornada
Una petició amb Idempotency-Key desa la resposta a la memòria cau perquè un reintent la pugui reproduir. Desar-la vol dir llegir primer el cos de la resposta a memòria, i aquesta lectura està limitada pels mateixos 100 MiB, tot i que calen unes condicions molt més estretes:
- La petició portava
Idempotency-Key. - La memòria cau no va encertar, així que aquesta clau és nova.
- L'operació d'origen va retornar 2xx.
Les fallades no es desen a la memòria cau i passen directament, així que només una sortida gran correcta arriba a aquest límit.
El que passa allà és el punt que val la pena recordar: rebeu 500 i no s'escriu res a la memòria cau. L'operació ja s'ha executat, però el sol·licitant veu una fallada; com que no s'ha desat res, el reintent torna a fer-ho tot. La clau d'idempotència existeix per eliminar feina duplicada i falla exactament quan més cal, disfressant una feina acabada de fallada del servidor. La memòria cau viu a la memòria del procés i no s'escriu mai a disc, així que un reinici l'esborra; per a la semàntica vegeu Idempotency-Key: reintents segurs per a tasques PDF.
Els 100 MiB no són cap paràmetre configurable de la plataforma
La xifra no es pot canviar. Cap variable d'entorn no l'apuja ni la baixa, en cap de les dues direccions; un número diferent vol dir canviar codi i reconstruir la imatge. Busqueu-la com a paràmetre de desplegament i no en trobareu cap.
També és més que una quota. Els bytes d'un cos de petició es llegeixen sencers a memòria abans del processament, així que cada càrrega gran concurrent en manté una quantitat comparable al costat. Apujar el sostre vol dir acceptar també un pic de memòria més alt: aquest número és també el que impedeix que una sola petició faci caure el procés.
Un desplegament autoallotjat per defecte no té cap proxy invers, i el portal no comprova la mida abans d'enviar, així que aquell rebuig ve del límit propi del servidor. Si poseu nginx al davant, hi topeu primer: client_max_body_size només permet 1 MiB per defecte i respon 413, una forma propera a la de la comparació de dalt i fàcil de confondre amb el mateix límit.
Què fer quan hi xoqueu
Del més barat al més car:
- Mesureu abans d'enviar. Compareu el recompte de bytes del cos de la petició amb 104.857.600 abans que surti la petició, i deixeu marge per a les fronteres. Això és millor que llegir un codi d'estat després.
- Comprimiu el fitxer per sota del límit. La mida d'un escanejat ve sobretot de la capa d'imatge, i recomprimir-ne treu habitualment una part visible. Comprimeix un PDF s'executa al navegador, sense cap script.
- Dividiu la feina en diverses crides. Quan el contingut es pot dividir, partiu-lo i envieu-lo en unes quantes peticions: menys feina que apujar el sostre, i no fa pujar la memòria que aguanta una sola petició.
- Canvieu el límit només si hi ha un requisit dur d'una sola petició. Això vol dir canviar codi i reconstruir, i acceptar el cost de memòria de la secció anterior.
Aquest límit obliga el client a decidir per endavant
Les dues fallades apunten a una sola conclusió: el client ha de calcular els 100 MiB abans d'enviar. D'entrada, us respon bad_request, un codi que no diu res sobre la mida; de tornada, 500, que sembla una fallada del servidor. Comptar els bytes abans que surti la petició és l'única manera de jutjar que no depèn del que digui un missatge d'error.