Skip to content

RequestConverter — Multipart form-data raw body exposed via getContent(), inconsistent with PHP-FPM #68

@s2x

Description

@s2x

Problem Description

For multipart/form-data requests, the converter passes $rawRequest->rawBody() as the $content parameter to Symfony's Request constructor. This means $request->getContent() returns the raw multipart body (including file contents). Under PHP-FPM, php://input is empty for multipart requests — PHP consumes the body during $_POST/$_FILES parsing.

Location

src/DTO/RequestConverter.php, line 29

Technical Analysis

PHP-FPM behavior

From the PHP manual:

php://input is not available with enctype="multipart/form-data".

Under PHP-FPM, $request->getContent() returns "" for multipart requests.

Current converter behavior

$request = new Request(
    ...
    content: $rawRequest->rawBody(),  // Raw multipart body with file contents
);

For a multipart request uploading a 10MB file, $rawRequest->rawBody() contains the entire 10MB body. This is stored in $request->content — meaning the file data exists in memory twice (once in the content string, once in the temp file created by Workerman).

Impact

  1. Memory usage — file data stored twice in memory. A 50MB upload consumes ~100MB.
  2. Data exposure — code that logs $request->getContent() will log raw file contents.
  3. Behavioral inconsistency — code tested under PHP-FPM that expects getContent() to be empty for multipart will behave differently under Workerman.

When this does NOT matter

  • Most Symfony applications never call getContent() on multipart requests
  • File access goes through $request->files, which works correctly

Proposed Solution

Detect multipart content type and pass empty content:

$contentType = $rawRequest->header('content-type', '');
$isMultipart = stripos($contentType, 'multipart/form-data') !== false;

$request = new Request(
    ...
    content: $isMultipart ? '' : $rawRequest->rawBody(),
);

Priority

🟡 LOW — does not affect typical application behavior. Primarily a memory optimization and consistency improvement.

Tests

// Multipart request
$this->assertSame('', $symfonyRequest->getContent());
$this->assertNotNull($symfonyRequest->files->get('file'));

// JSON request — body preserved
$this->assertSame('{"key":"value"}', $symfonyRequest->getContent());

// Form-urlencoded — body preserved
$this->assertSame('key=value', $symfonyRequest->getContent());

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestminorMinor priority - code quality

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions