<?php 
namespace GuzzleHttp; 
 
use GuzzleHttp\Promise\PromiseInterface; 
use GuzzleHttp\Psr7; 
use Psr\Http\Message\RequestInterface; 
 
/** 
 * Prepares requests that contain a body, adding the Content-Length, 
 * Content-Type, and Expect headers. 
 */ 
class PrepareBodyMiddleware 
{ 
    /** @var callable  */ 
    private $nextHandler; 
 
    /** @var array */ 
    private static $skipMethods = ['GET' => true, 'HEAD' => true]; 
 
    /** 
     * @param callable $nextHandler Next handler to invoke. 
     */ 
    public function __construct(callable $nextHandler) 
    { 
        $this->nextHandler = $nextHandler; 
    } 
 
    /** 
     * @param RequestInterface $request 
     * @param array            $options 
     * 
     * @return PromiseInterface 
     */ 
    public function __invoke(RequestInterface $request, array $options) 
    { 
        $fn = $this->nextHandler; 
 
        // Don't do anything if the request has no body. 
        if (isset(self::$skipMethods[$request->getMethod()]) 
            || $request->getBody()->getSize() === 0 
        ) { 
            return $fn($request, $options); 
        } 
 
        $modify = []; 
 
        // Add a default content-type if possible. 
        if (!$request->hasHeader('Content-Type')) { 
            if ($uri = $request->getBody()->getMetadata('uri')) { 
                if ($type = Psr7\mimetype_from_filename($uri)) { 
                    $modify['set_headers']['Content-Type'] = $type; 
                } 
            } 
        } 
 
        // Add a default content-length or transfer-encoding header. 
        if (!isset(self::$skipMethods[$request->getMethod()]) 
            && !$request->hasHeader('Content-Length') 
            && !$request->hasHeader('Transfer-Encoding') 
        ) { 
            $size = $request->getBody()->getSize(); 
            if ($size !== null) { 
                $modify['set_headers']['Content-Length'] = $size; 
            } else { 
                $modify['set_headers']['Transfer-Encoding'] = 'chunked'; 
            } 
        } 
 
        // Add the expect header if needed. 
        $this->addExpectHeader($request, $options, $modify); 
 
        return $fn(Psr7\modify_request($request, $modify), $options); 
    } 
 
    private function addExpectHeader( 
        RequestInterface $request, 
        array $options, 
        array &$modify 
    ) { 
        // Determine if the Expect header should be used 
        if ($request->hasHeader('Expect')) { 
            return; 
        } 
 
        $expect = isset($options['expect']) ? $options['expect'] : null; 
 
        // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 
        if ($expect === false || $request->getProtocolVersion() < 1.1) { 
            return; 
        } 
 
        // The expect header is unconditionally enabled 
        if ($expect === true) { 
            $modify['set_headers']['Expect'] = '100-Continue'; 
            return; 
        } 
 
        // By default, send the expect header when the payload is > 1mb 
        if ($expect === null) { 
            $expect = 1048576; 
        } 
 
        // Always add if the body cannot be rewound, the size cannot be 
        // determined, or the size is greater than the cutoff threshold 
        $body = $request->getBody(); 
        $size = $body->getSize(); 
 
        if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { 
            $modify['set_headers']['Expect'] = '100-Continue'; 
        } 
    } 
} 
 
 |