1<?php 2 3namespace GuzzleHttp; 4 5use GuzzleHttp\Promise\PromiseInterface; 6use Psr\Http\Message\RequestInterface; 7 8/** 9 * Prepares requests that contain a body, adding the Content-Length, 10 * Content-Type, and Expect headers. 11 * 12 * @final 13 */ 14class PrepareBodyMiddleware 15{ 16 /** 17 * @var callable(RequestInterface, array): PromiseInterface 18 */ 19 private $nextHandler; 20 21 /** 22 * @param callable(RequestInterface, array): PromiseInterface $nextHandler Next handler to invoke. 23 */ 24 public function __construct(callable $nextHandler) 25 { 26 $this->nextHandler = $nextHandler; 27 } 28 29 public function __invoke(RequestInterface $request, array $options): PromiseInterface 30 { 31 $fn = $this->nextHandler; 32 33 // Don't do anything if the request has no body. 34 if ($request->getBody()->getSize() === 0) { 35 return $fn($request, $options); 36 } 37 38 $modify = []; 39 40 // Add a default content-type if possible. 41 if (!$request->hasHeader('Content-Type')) { 42 if ($uri = $request->getBody()->getMetadata('uri')) { 43 if (is_string($uri) && $type = Psr7\MimeType::fromFilename($uri)) { 44 $modify['set_headers']['Content-Type'] = $type; 45 } 46 } 47 } 48 49 // Add a default content-length or transfer-encoding header. 50 if (!$request->hasHeader('Content-Length') 51 && !$request->hasHeader('Transfer-Encoding') 52 ) { 53 $size = $request->getBody()->getSize(); 54 if ($size !== null) { 55 $modify['set_headers']['Content-Length'] = $size; 56 } else { 57 $modify['set_headers']['Transfer-Encoding'] = 'chunked'; 58 } 59 } 60 61 // Add the expect header if needed. 62 $this->addExpectHeader($request, $options, $modify); 63 64 return $fn(Psr7\Utils::modifyRequest($request, $modify), $options); 65 } 66 67 /** 68 * Add expect header 69 */ 70 private function addExpectHeader(RequestInterface $request, array $options, array &$modify): void 71 { 72 // Determine if the Expect header should be used 73 if ($request->hasHeader('Expect')) { 74 return; 75 } 76 77 $expect = $options['expect'] ?? null; 78 79 // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 80 if ($expect === false || $request->getProtocolVersion() < 1.1) { 81 return; 82 } 83 84 // The expect header is unconditionally enabled 85 if ($expect === true) { 86 $modify['set_headers']['Expect'] = '100-Continue'; 87 88 return; 89 } 90 91 // By default, send the expect header when the payload is > 1mb 92 if ($expect === null) { 93 $expect = 1048576; 94 } 95 96 // Always add if the body cannot be rewound, the size cannot be 97 // determined, or the size is greater than the cutoff threshold 98 $body = $request->getBody(); 99 $size = $body->getSize(); 100 101 if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { 102 $modify['set_headers']['Expect'] = '100-Continue'; 103 } 104 } 105} 106