21         $msg = trim($message->getMethod() . 
' ' 
   22                 . $message->getRequestTarget())
 
   25             $msg .= 
"\r\nHost: " . $message->getUri()->getHost();
 
   29             . $message->getStatusCode() . 
' ' 
   30             . $message->getReasonPhrase();
 
   32         throw new \InvalidArgumentException(
'Unknown message type');
 
   35     foreach ($message->
getHeaders() as $name => $values) {
 
   36         $msg .= 
"\r\n{$name}: " . implode(
', ', $values);
 
   39     return "{$msg}\r\n\r\n" . $message->
getBody();
 
   58     } elseif (is_string($uri)) {
 
   62     throw new \InvalidArgumentException(
'URI must be a string or UriInterface');
 
   80     if (is_scalar($resource)) {
 
   81         $stream = fopen(
'php://temp', 
'r+');
 
   82         if ($resource !== 
'') {
 
   83             fwrite($stream, $resource);
 
   86         return new Stream($stream, $options);
 
   89     switch (gettype($resource)) {
 
   91             return new Stream($resource, $options);
 
   95             } elseif ($resource instanceof \Iterator) {
 
   96                 return new PumpStream(
function () use ($resource) {
 
   97                     if (!$resource->valid()) {
 
  100                     $result = $resource->current();
 
  104             } elseif (method_exists($resource, 
'__toString')) {
 
  105                 return stream_for((
string) $resource, $options);
 
  109             return new Stream(fopen(
'php://temp', 
'r+'), $options);
 
  112     if (is_callable($resource)) {
 
  116     throw new \InvalidArgumentException(
'Invalid resource type: ' . gettype($resource));
 
  131     static $trimmed = 
"\"'  \n\t\r";
 
  132     $params = $matches = [];
 
  136         foreach (preg_split(
'/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
 
  137             if (preg_match_all(
'/<[^>]+>|[^=]+/', $kvp, $matches)) {
 
  140                     $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
 
  142                     $part[] = trim($m[0], $trimmed);
 
  164     if (!is_array($header)) {
 
  165         return array_map(
'trim', explode(
',', $header));
 
  169     foreach ($header as $value) {
 
  170         foreach ((array) $value as $v) {
 
  171             if (strpos($v, 
',') === 
false) {
 
  175             foreach (preg_split(
'/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
 
  176                 $result[] = trim($vv);
 
  209     if (!isset($changes[
'uri'])) {
 
  210         $uri = $request->
getUri();
 
  213         if ($host = $changes[
'uri']->getHost()) {
 
  214             $changes[
'set_headers'][
'Host'] = $host;
 
  216             if ($port = $changes[
'uri']->getPort()) {
 
  217                 $standardPorts = [
'http' => 80, 
'https' => 443];
 
  218                 $scheme = $changes[
'uri']->getScheme();
 
  219                 if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
 
  220                     $changes[
'set_headers'][
'Host'] .= 
':'.$port;
 
  224         $uri = $changes[
'uri'];
 
  227     if (!empty($changes[
'remove_headers'])) {
 
  231     if (!empty($changes[
'set_headers'])) {
 
  233         $headers = $changes[
'set_headers'] + $headers;
 
  236     if (isset($changes[
'query'])) {
 
  237         $uri = $uri->withQuery($changes[
'query']);
 
  242             isset($changes[
'method']) ? $changes[
'method'] : $request->
getMethod(),
 
  245             isset($changes[
'body']) ? $changes[
'body'] : $request->
getBody(),
 
  246             isset($changes[
'version'])
 
  247                 ? $changes[
'version']
 
  249             $request->getServerParams()
 
  251         ->withParsedBody($request->getParsedBody())
 
  252         ->withQueryParams($request->getQueryParams())
 
  253         ->withCookieParams($request->getCookieParams())
 
  254         ->withUploadedFiles($request->getUploadedFiles());
 
  258         isset($changes[
'method']) ? $changes[
'method'] : $request->
getMethod(),
 
  261         isset($changes[
'body']) ? $changes[
'body'] : $request->
getBody(),
 
  262         isset($changes[
'version'])
 
  263             ? $changes[
'version']
 
  302     set_error_handler(
function () use ($filename, $mode, &$ex) {
 
  303         $ex = new \RuntimeException(sprintf(
 
  304             'Unable to open %s using mode %s: %s',
 
  311     $handle = fopen($filename, $mode);
 
  312     restore_error_handler();
 
  336     if ($maxLen === -1) {
 
  337         while (!$stream->eof()) {
 
  338             $buf = $stream->read(1048576);
 
  349     while (!$stream->eof() && $len < $maxLen) {
 
  350         $buf = $stream->read($maxLen - $len);
 
  356         $len = strlen($buffer);
 
  380     if ($maxLen === -1) {
 
  381         while (!$source->eof()) {
 
  382             if (!$dest->
write($source->read($bufferSize))) {
 
  387         $remaining = $maxLen;
 
  388         while ($remaining > 0 && !$source->eof()) {
 
  389             $buf = $source->read(min($bufferSize, $remaining));
 
  415     $pos = $stream->tell();
 
  421     $ctx = hash_init($algo);
 
  422     while (!$stream->eof()) {
 
  423         hash_update($ctx, $stream->read(1048576));
 
  426     $out = hash_final($ctx, (
bool) $rawOutput);
 
  445     while (!$stream->eof()) {
 
  447         if (
null == ($byte = $stream->read(1))) {
 
  452         if ($byte === 
"\n" || ++$size === $maxLength - 1) {
 
  471     if (!preg_match(
'/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data[
'start-line'], $matches)) {
 
  472         throw new \InvalidArgumentException(
'Invalid request string');
 
  474     $parts = explode(
' ', $data[
'start-line'], 3);
 
  475     $version = isset($parts[2]) ? explode(
'/', $parts[2])[1] : 
'1.1';
 
  485     return $matches[1] === 
'/' ? $request : $request->withRequestTarget($parts[1]);
 
  501     if (!preg_match(
'/^HTTP\/.* [0-9]{3}( .*|$)/', $data[
'start-line'])) {
 
  502         throw new \InvalidArgumentException(
'Invalid response string: ' . $data[
'start-line']);
 
  504     $parts = explode(
' ', $data[
'start-line'], 3);
 
  510         explode(
'/', $parts[0])[1],
 
  511         isset($parts[2]) ? $parts[2] : 
null 
  536     if ($urlEncoding === 
true) {
 
  537         $decoder = 
function ($value) {
 
  538             return rawurldecode(str_replace(
'+', 
' ', $value));
 
  540     } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
 
  541         $decoder = 
'rawurldecode';
 
  542     } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
 
  543         $decoder = 
'urldecode';
 
  545         $decoder = 
function ($str) { 
return $str; };
 
  548     foreach (explode(
'&', $str) as $kvp) {
 
  549         $parts = explode(
'=', $kvp, 2);
 
  550         $key = $decoder($parts[0]);
 
  551         $value = isset($parts[1]) ? $decoder($parts[1]) : 
null;
 
  552         if (!isset($result[$key])) {
 
  553             $result[$key] = $value;
 
  555             if (!is_array($result[$key])) {
 
  556                 $result[$key] = [$result[$key]];
 
  558             $result[$key][] = $value;
 
  578 function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
 
  584     if ($encoding === 
false) {
 
  585         $encoder = 
function ($str) { 
return $str; };
 
  586     } elseif ($encoding === PHP_QUERY_RFC3986) {
 
  587         $encoder = 
'rawurlencode';
 
  588     } elseif ($encoding === PHP_QUERY_RFC1738) {
 
  589         $encoder = 
'urlencode';
 
  591         throw new \InvalidArgumentException(
'Invalid type');
 
  595     foreach ($params as $k => $v) {
 
  600                 $qs .= 
'=' . $encoder($v);
 
  604             foreach ($v as $vv) {
 
  607                     $qs .= 
'=' . $encoder($vv);
 
  614     return $qs ? (string) substr($qs, 0, -1) : 
'';
 
  639     static $mimetypes = [
 
  640         '3gp' => 
'video/3gpp',
 
  641         '7z' => 
'application/x-7z-compressed',
 
  642         'aac' => 
'audio/x-aac',
 
  643         'ai' => 
'application/postscript',
 
  644         'aif' => 
'audio/x-aiff',
 
  645         'asc' => 
'text/plain',
 
  646         'asf' => 
'video/x-ms-asf',
 
  647         'atom' => 
'application/atom+xml',
 
  648         'avi' => 
'video/x-msvideo',
 
  649         'bmp' => 
'image/bmp',
 
  650         'bz2' => 
'application/x-bzip2',
 
  651         'cer' => 
'application/pkix-cert',
 
  652         'crl' => 
'application/pkix-crl',
 
  653         'crt' => 
'application/x-x509-ca-cert',
 
  656         'cu' => 
'application/cu-seeme',
 
  657         'deb' => 
'application/x-debian-package',
 
  658         'doc' => 
'application/msword',
 
  659         'docx' => 
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
 
  660         'dvi' => 
'application/x-dvi',
 
  661         'eot' => 
'application/vnd.ms-fontobject',
 
  662         'eps' => 
'application/postscript',
 
  663         'epub' => 
'application/epub+zip',
 
  664         'etx' => 
'text/x-setext',
 
  665         'flac' => 
'audio/flac',
 
  666         'flv' => 
'video/x-flv',
 
  667         'gif' => 
'image/gif',
 
  668         'gz' => 
'application/gzip',
 
  669         'htm' => 
'text/html',
 
  670         'html' => 
'text/html',
 
  671         'ico' => 
'image/x-icon',
 
  672         'ics' => 
'text/calendar',
 
  673         'ini' => 
'text/plain',
 
  674         'iso' => 
'application/x-iso9660-image',
 
  675         'jar' => 
'application/java-archive',
 
  676         'jpe' => 
'image/jpeg',
 
  677         'jpeg' => 
'image/jpeg',
 
  678         'jpg' => 
'image/jpeg',
 
  679         'js' => 
'text/javascript',
 
  680         'json' => 
'application/json',
 
  681         'latex' => 
'application/x-latex',
 
  682         'log' => 
'text/plain',
 
  683         'm4a' => 
'audio/mp4',
 
  684         'm4v' => 
'video/mp4',
 
  685         'mid' => 
'audio/midi',
 
  686         'midi' => 
'audio/midi',
 
  687         'mov' => 
'video/quicktime',
 
  688         'mkv' => 
'video/x-matroska',
 
  689         'mp3' => 
'audio/mpeg',
 
  690         'mp4' => 
'video/mp4',
 
  691         'mp4a' => 
'audio/mp4',
 
  692         'mp4v' => 
'video/mp4',
 
  693         'mpe' => 
'video/mpeg',
 
  694         'mpeg' => 
'video/mpeg',
 
  695         'mpg' => 
'video/mpeg',
 
  696         'mpg4' => 
'video/mp4',
 
  697         'oga' => 
'audio/ogg',
 
  698         'ogg' => 
'audio/ogg',
 
  699         'ogv' => 
'video/ogg',
 
  700         'ogx' => 
'application/ogg',
 
  701         'pbm' => 
'image/x-portable-bitmap',
 
  702         'pdf' => 
'application/pdf',
 
  703         'pgm' => 
'image/x-portable-graymap',
 
  704         'png' => 
'image/png',
 
  705         'pnm' => 
'image/x-portable-anymap',
 
  706         'ppm' => 
'image/x-portable-pixmap',
 
  707         'ppt' => 
'application/vnd.ms-powerpoint',
 
  708         'pptx' => 
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
 
  709         'ps' => 
'application/postscript',
 
  710         'qt' => 
'video/quicktime',
 
  711         'rar' => 
'application/x-rar-compressed',
 
  712         'ras' => 
'image/x-cmu-raster',
 
  713         'rss' => 
'application/rss+xml',
 
  714         'rtf' => 
'application/rtf',
 
  715         'sgm' => 
'text/sgml',
 
  716         'sgml' => 
'text/sgml',
 
  717         'svg' => 
'image/svg+xml',
 
  718         'swf' => 
'application/x-shockwave-flash',
 
  719         'tar' => 
'application/x-tar',
 
  720         'tif' => 
'image/tiff',
 
  721         'tiff' => 
'image/tiff',
 
  722         'torrent' => 
'application/x-bittorrent',
 
  723         'ttf' => 
'application/x-font-ttf',
 
  724         'txt' => 
'text/plain',
 
  725         'wav' => 
'audio/x-wav',
 
  726         'webm' => 
'video/webm',
 
  727         'webp' => 
'image/webp',
 
  728         'wma' => 
'audio/x-ms-wma',
 
  729         'wmv' => 
'video/x-ms-wmv',
 
  730         'woff' => 
'application/x-font-woff',
 
  731         'wsdl' => 
'application/wsdl+xml',
 
  732         'xbm' => 
'image/x-xbitmap',
 
  733         'xls' => 
'application/vnd.ms-excel',
 
  734         'xlsx' => 
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
 
  735         'xml' => 
'application/xml',
 
  736         'xpm' => 
'image/x-xpixmap',
 
  737         'xwd' => 
'image/x-xwindowdump',
 
  738         'yaml' => 
'text/yaml',
 
  739         'yml' => 
'text/yaml',
 
  740         'zip' => 
'application/zip',
 
  743     $extension = strtolower($extension);
 
  745     return isset($mimetypes[$extension])
 
  746         ? $mimetypes[$extension]
 
  765         throw new \InvalidArgumentException(
'Invalid message');
 
  768     $message = ltrim($message, 
"\r\n");
 
  770     $messageParts = preg_split(
"/\r?\n\r?\n/", $message, 2);
 
  772     if ($messageParts === 
false || count($messageParts) !== 2) {
 
  773         throw new \InvalidArgumentException(
'Invalid message: Missing header delimiter');
 
  776     list($rawHeaders, $body) = $messageParts;
 
  777     $rawHeaders .= 
"\r\n"; 
 
  778     $headerParts = preg_split(
"/\r?\n/", $rawHeaders, 2);
 
  780     if ($headerParts === 
false || count($headerParts) !== 2) {
 
  781         throw new \InvalidArgumentException(
'Invalid message: Missing status line');
 
  784     list($startLine, $rawHeaders) = $headerParts;
 
  786     if (preg_match(
"/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === 
'1.0') {
 
  795     if ($count !== substr_count($rawHeaders, 
"\n")) {
 
  798             throw new \InvalidArgumentException(
'Invalid header syntax: Obsolete line folding');
 
  801         throw new \InvalidArgumentException(
'Invalid header syntax');
 
  806     foreach ($headerLines as $headerLine) {
 
  807         $headers[$headerLine[1]][] = $headerLine[2];
 
  811         'start-line' => $startLine,
 
  812         'headers' => $headers,
 
  828     $hostKey = array_filter(array_keys($headers), 
function ($k) {
 
  829         return strtolower($k) === 
'host';
 
  837     $host = $headers[reset($hostKey)][0];
 
  838     $scheme = substr($host, -4) === 
':443' ? 
'https' : 
'http';
 
  840     return $scheme . 
'://' . $host . 
'/' . ltrim($path, 
'/');
 
  857     if (!$body->isSeekable() || !$body->isReadable()) {
 
  861     $size = $body->getSize();
 
  867     $summary = $body->read($truncateAt);
 
  870     if ($size > $truncateAt) {
 
  871         $summary .= 
' (truncated...)';
 
  876     if (preg_match(
'/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
 
  888     foreach ($keys as &$key) {
 
  889         $key = strtolower($key);
 
  892     foreach ($data as $k => $v) {
 
  893         if (!in_array(strtolower($k), $keys)) {