saloon icon indicating copy to clipboard operation
saloon copied to clipboard

Unable to use debugRequest with stream uploading

Open glukinho opened this issue 6 months ago • 0 comments

Hello,

Saloon 3.14.0 / PHP 8.3.10 / Laravel 12.19 / Windows 10

I have a connector with a method UploadFile where I stream a large file using fopen to save memory:

class UploadFile extends Request implements HasBody
{
    use HasMultipartBody;

    public function __construct (
        private string         $path,
        // ...
    ) {}

    protected function defaultBody (): array
    {
        return [
            new MultipartValue(name: 'file', value: fopen($this->path, 'r'), filename: basename($this->path)),
            // ...
        ];
    }
}

I also have my custom trait for logging requests/responses:

trait LogsRequestsAndResponses
{
    const int MAX_LOGGED_BODY_SIZE = 10;


    public static function bootLogsRequestsAndResponses (PendingRequest $pendingRequest): void
    {
        $connector = $pendingRequest->getConnector();

        $log_channel = $connector->log_channel ?? 'single';
        $request_id  = Str::random();

        $connector->debugRequest(function (PendingRequest $pendingRequest, RequestInterface $psrRequest) use ($log_channel, $request_id) {
            $log_data = [
                'url'       => $pendingRequest->getUrl(),
                'method'    => $pendingRequest->getMethod(),
                'headers'   => $pendingRequest->headers()->all(),
                'body_size' => $psrRequest->getBody()->getSize(),
            ];

            $log_data['body'] = $log_data['body_size'] <= self::MAX_LOGGED_BODY_SIZE
                ? (string)$pendingRequest->body()
                : '<request body truncated>';

            Log::channel($log_channel)->debug("request  | id {$request_id} | ", $log_data);
        });

        $connector->debugResponse(function (Response $response, ResponseInterface $psrResponse) use ($log_channel, $request_id) {
            $log_data = [
                'status'    => $response->status() . ' ' . $psrResponse->getReasonPhrase(),
                'headers'   => $response->headers()->all(),
                'body_size' => $psrResponse->getBody()->getSize(),
            ];

            $log_data['body'] = $log_data['body_size'] <= self::MAX_LOGGED_BODY_SIZE
                ? (string)$response->body()
                : '<response body truncated>';

            Log::channel($log_channel)->debug("response | id {$request_id} | ", $log_data);
        });
    }
}

I use the trait on connectors like this:

class MymeetConnector extends Connector
{
    use AcceptsJson;
    use LogsRequestsAndResponses;
// ...

When I send the request with this trait attached to the connector, it fails with the exception "Invalid resource type: resource (closed)":

[2025-07-08 14:32:17] local.ERROR: Invalid resource type: resource (closed) {"exception":"[object] (InvalidArgumentException(code: 0): Invalid resource type: resource (closed) at C:\\nastroim\\pipka\\vendor\\guzzlehttp\\psr7\\src\\Utils.php:355)
[stacktrace]
#0 C:\\nastroim\\pipka\\vendor\\guzzlehttp\\psr7\\src\\MultipartStream.php(94): GuzzleHttp\\Psr7\\Utils::streamFor()
#1 C:\\nastroim\\pipka\\vendor\\guzzlehttp\\psr7\\src\\MultipartStream.php(77): GuzzleHttp\\Psr7\\MultipartStream->addElement()
#2 C:\\nastroim\\pipka\\vendor\\guzzlehttp\\psr7\\src\\MultipartStream.php(38): GuzzleHttp\\Psr7\\MultipartStream->createStream()
#3 C:\\nastroim\\pipka\\vendor\\saloonphp\\saloon\\src\\Http\\Senders\\Factories\\GuzzleMultipartBodyFactory.php(31): GuzzleHttp\\Psr7\\MultipartStream->__construct()
#4 C:\\nastroim\\pipka\\vendor\\saloonphp\\saloon\\src\\Repositories\\Body\\MultipartBodyRepository.php(223): Saloon\\Http\\Senders\\Factories\\GuzzleMultipartBodyFactory->create()
#5 C:\\nastroim\\pipka\\vendor\\saloonphp\\saloon\\src\\Traits\\PendingRequest\\ManagesPsrRequests.php(55): Saloon\\Repositories\\Body\\MultipartBodyRepository->toStream()
#6 C:\\nastroim\\pipka\\vendor\\saloonphp\\saloon\\src\\Http\\Senders\\GuzzleSender.php(97): Saloon\\Http\\PendingRequest->createPsrRequest()
#7 C:\\nastroim\\pipka\\vendor\\saloonphp\\saloon\\src\\Traits\\Connector\\SendsRequests.php(72): Saloon\\Http\\Senders\\GuzzleSender->send()
#8 C:\\nastroim\\pipka\\app\\Console\\Commands\\TestCommand.php(73): Saloon\\Http\\Connector->send()
#9 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\BoundMethod.php(36): App\\Console\\Commands\\TestCommand->handle()
#10 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\Util.php(43): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#11 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\BoundMethod.php(96): Illuminate\\Container\\Util::unwrapIfClosure()
#12 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\BoundMethod.php(35): Illuminate\\Container\\BoundMethod::callBoundMethod()
#13 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\Container.php(754): Illuminate\\Container\\BoundMethod::call()
#14 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Console\\Command.php(209): Illuminate\\Container\\Container->call()
#15 C:\\nastroim\\pipka\\vendor\\symfony\\console\\Command\\Command.php(318): Illuminate\\Console\\Command->execute()
#16 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Console\\Command.php(178): Symfony\\Component\\Console\\Command\\Command->run()
#17 C:\\nastroim\\pipka\\vendor\\symfony\\console\\Application.php(1092): Illuminate\\Console\\Command->run()
#18 C:\\nastroim\\pipka\\vendor\\symfony\\console\\Application.php(341): Symfony\\Component\\Console\\Application->doRunCommand()
#19 C:\\nastroim\\pipka\\vendor\\symfony\\console\\Application.php(192): Symfony\\Component\\Console\\Application->doRun()
#20 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Console\\Kernel.php(197): Symfony\\Component\\Console\\Application->run()
#21 C:\\nastroim\\pipka\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Application.php(1234): Illuminate\\Foundation\\Console\\Kernel->handle()
#22 C:\\nastroim\\pipka\\artisan(16): Illuminate\\Foundation\\Application->handleCommand()
#23 {main}
"} 

When the trait is off, the uploading goes fine.

Can you suggest what can be wrong with my trait? It's pretty basic and I use it with other connectors/requests for logging. Thanks in advance!

glukinho avatar Jul 08 '25 15:07 glukinho