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