Repository navigation
Releases: Lemric/BatchRequest
Release list
3.1.4
3.1.3
The license has been changed to a more useful one. It is now the MIT license.
v3.1.2
v3.1.1
v3.1.0
Security
- Parser: byte-accurate
strlenpayload limit (wasmb_strlen); per-
transaction body cap (default 256 KiB); strict whitelist of forwarded
parent headers (default: accept, accept-language, content-type,
user-agent); blacklist now drops cookie, set-cookie, authorization,
x-csrf-token;$_SERVERfiltered through whitelist;parse_str
guarded against array-bomb (max 1000 fields). - Validator: combined path-traversal regex; URI re-checked after
iterativerawurldecode(defeats double-encoded%2e%2e%2f,
protocol-relative variants); per-transaction content-length guard;
recursive sub-batch SSRF guard via metadata flag. - ResponseFormatter: strips
set-cookie,authorization,proxy-auth*,
server,x-powered-byfrom sub-responses to prevent session/header
leakage through the batch envelope. - Facades: parent
Authorizationheader never propagated; new top-level
"authorization"field in each sub-request injects a per-transaction
bearer token; trace logs redactauthorization|password|token|secret| api_keyand truncate to 4 KiB; rate-limit decoding done once.
Performance
- Handler: optional
ExecutionStrategyInterface(default sequential,
BC-safe). NewFiberExecutionStrategyparallelises contiguous read-
only blocks (GET/HEAD only, write ops always serialised) via PHP
Fibers with boundedmaxConcurrency(default 8). - Parser: generator-driven transaction yielding; pre-flipped O(1) header
maps; single-pass header/cookie/server filtering. - ResponseFormatter:
BinaryFileResponsestreamed in 8190-byte chunks
(peak RAM ≈ chunk size instead of full file); pre-compiled regex/map
constants. - BatchResponse: manual loop in
getFailureCount(noarray_filter
closure allocation).
Testing
- Add
FiberVsSequentialBenchmarkTestcovering pure-CPU, blocking I/O
and cooperative I/O scenarios with deterministic assertions. - Update
LaravelTransactionExecutorTestto assertSet-Cookieis
stripped from sub-responses (new secure default).
BREAKING CHANGE: Set-Cookie headers are no longer forwarded from sub-
responses into the batch envelope; parent-request Authorization /
Cookie headers are no longer forwarded into sub-requests. Use the new
top-level "authorization" payload field per sub-request, or override
forwardedHeadersWhitelist in the facade constructor.
v3.0.2
A single batch can now safely mix sub-requests that return any
Content-Type (JSON, problem+json, vendor +json, HTML, text, XML/SVG,
PDF, PNG, octet-stream, BinaryFileResponse, StreamedResponse, 204).
Previously binary bodies corrupted the outer JsonResponse envelope and
BinaryFileResponse/StreamedResponse payloads were silently dropped.
Changes
-
New
Lemric\BatchRequest\Bridge\ResponseFormatter(framework-agnostic,
shared by both bridges — works for Laravel too because
Illuminate\Http\Responseextends Symfony's and Laravel reuses
Symfony'sBinaryFileResponse/StreamedResponse).- JSON /
*/*+json→ decoded array. - text/* and
application/{xml,*+xml,javascript,yaml,…}→ raw string. - Anything else → base64-encoded body +
body_encoding: "base64". BinaryFileResponsematerialised viagetFile().StreamedResponsecaptured via output buffering.- Malformed JSON with a JSON content type stays as raw string
(server claimed text, no base64). - Empty/no-content responses return
""(consistent string typing).
- JSON /
-
Symfony/SymfonyTransactionExecutorand
Laravel/LaravelTransactionExecutordelegate response shaping to the
new formatter; duplicatedformatResponse/extractHeaders/
isJsonContentTyperemoved. -
README: new "Mixed content types in a single batch" section with the
Content-Type → body-shape table and an example mixed response.
Tests
- New
tests/Bridge/ResponseFormatterTest.php(17 cases) covers every
branch and a mixed-batch round-trip throughjson_encode. - New
tests/Bridge/ResponseFormatterLaravelTest.php(9 cases) proves
the formatter handlesIlluminate\Http\{Response,JsonResponse}and
SymfonyBinaryFileResponse/StreamedResponsereturned by Laravel
helpers. SymfonyTransactionExecutorTest::testExecuteHandlesResponseWithoutContent
updated: empty body is now""instead of[].
v3.0.1
Security
-
Validator/TransactionValidator
- Reject CRLF, percent-encoded CRLF and NULL bytes in URI; fix the
previously broken backslash regex (/\\\/) that matched nothing
relevant. Mitigates HTTP Response Splitting / smuggling (A03). - Reject protocol-relative URIs (
//host,/\host) which Symfony's
Request::create()interprets as havinghostas authority.
Mitigates SSRF / Open Redirect (A10). - New
validateHeaders(): enforce RFC 7230 §3.2.6 tchar header names
and forbid CR/LF/NULL in header values (A03).
- Reject CRLF, percent-encoded CRLF and NULL bytes in URI; fix the
-
Validator/BatchRequestValidator
- Drop the "skip per-transaction validation for batches >1000" path.
Security must not depend on batch size; the maxBatchSize cap is the
only correct performance lever (A04 Insecure Design).
- Drop the "skip per-transaction validation for batches >1000" path.
-
Parser/JsonBatchRequestParser
json_decodenow usesdepth=32andJSON_THROW_ON_ERROR, plus a
configurablemaxContentLength(default 5 MiB) checked before
decoding. Mitigates billion-laughs / memory-exhaustion DoS (A04).- Add a denylist of sensitive context headers that must not be
propagated from the parent request to sub-requests:Host,
X-Forwarded-*,X-Real-IP,Forwarded, and RFC 7230 §6.1
hop-by-hop headers (A01 BAC + A03 spoofing).
-
Bridge facades and executors
- SymfonyBatchRequestFacade / LaravelBatchRequestFacade: replace raw
$e->getMessage()in 500 responses with a generic
"Internal server error" message; full context is still logged
(A09 Information Disclosure). - SymfonyTransactionExecutor: keep informative error type/message for
HttpExceptionInterface(NotFound, etc.) but return a generic
payload for all otherThrowable(A09). - LaravelTransactionExecutor: stop returning
get_class($e)and the
raw exception message in the per-transaction response (A09).
- SymfonyBatchRequestFacade / LaravelBatchRequestFacade: replace raw
Bugs
-
Parser/JsonBatchRequestParser::extractParameters
parse_stris no longer applied to JSON-looking bodies. Previously
a body like{"a":1}was silently mangled into
['{"a":1}' => '']. The raw body still flows through
prepareContent.
-
Bridge/Symfony/SymfonyBatchRequestFacade
- Remove the redundant "isLargeBatch" branch in
extractContextthat
decoded the entire payload a second time, and remove the rate-limit
decode that ran without depth/throw flags. Rate-limit decode is now
safe and tolerant of invalid input (count=1 fallback). - Remove the unused
getErrorType()method (dead code). logErrorno longer allocates a freshNullLoggeron each call.
- Remove the redundant "isLargeBatch" branch in
-
Handler/BatchRequestHandler
- Stop disabling the logger for batches >1000. Silently dropping
validation-error logs hid both bugs and DoS attempts.
- Stop disabling the logger for batches >1000. Silently dropping
-
Bridge/Laravel/LaravelServiceProvider
- Actually load
config/batch-request.phpviamergeConfigFrom,
publish it under thebatch-request-configtag, and read
batch-request.max_batch_size(with a safe fallback to 50 when the
configrepository is not bound, so test containers keep working).
The config file existed and was documented but had no effect. - Mark the provider as
final.
- Actually load
Tests
- Update Symfony/LaravelTransactionExecutor tests to assert the new
generic error contract. - Replace the PHPUnit-12
$this->isType('callable')(removed in
PHPUnit 13) with acallbackconstraint in
LaravelServiceProviderTest.