-
-
Notifications
You must be signed in to change notification settings - Fork 113
Description
Summary
Multipart requests work fine when the body is defined only in the request itself.
The issue appears only when a connector has a defaultBody
defined as multipart and that default body is merged with the request body.
In this scenario, the HasMultipartBody
trait sets the Content-Type
header manually before the multipart body stream is created. However, Guzzle's MultipartStream
generates its own random boundary internally, which results in a mismatch between the boundary declared in the Content-Type
header and the boundary actually used in the multipart body payload.
Impact:
Many servers treat the multipart request body as empty (input: [], files: []
) because the boundary in the header and body don't match.
Solution implemented locally
This is a small workaround to always generate the multipart body stream first, and then set the
Content-Type
header with the boundary from MultipartBodyRepository
.
Example change in createPsrRequest
:
public function createPsrRequest(): RequestInterface
{
$factories = $this->factoryCollection;
$request = $factories->requestFactory->createRequest(
method: $this->getMethod()->value,
uri: $this->getUri(),
);
foreach ($this->headers()->all() as $headerName => $headerValue) {
$request = $request->withHeader($headerName, $headerValue);
}
if ($this->body() instanceof BodyRepository) {
$request = $request->withBody($this->body()->toStream($factories->streamFactory));
}
/**
* FIX WHEN DEFAULT BODY FROM CONNECTOR IS MERGED WITH REQUEST
* Convert the body repository into a stream using the fixed boundary
*
* This ensures that the boundary used in the Content-Type header
* matches the one used in the body itself.
*/
if ($this->body() instanceof MultipartBodyRepository) {
$boundary = $this->body()->getBoundary();
$request = $request
->withBody($this->body()->toStream($factories->streamFactory))
->withHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
}
// Run connector and request hooks for any final changes to the PSR request.
$request = $this->connector->handlePsrRequest($request, $this);
return $this->request->handlePsrRequest($request, $this);
}
Thanks,
PrunaCatalin