>bRZ慰'r9B#̙Qu@~~W189@O#Ms=vlCF9 CTcE+&Vy$9T2<@' 茌3HL8I;:AWxaH;'PlXx<_kH/%+$@H dȈC 1Dy 0، ԥ5maFO1݈,Y eb$32!BCc$TDZ^z./Ur tίLuUو-IfbYJ3 qi9i%y؃=?Hsa88w,/R/!?e K|=³ۛ?%sSpJkte;Udm i4/~_q$fܩݹx^.ڪZ4h[6{|z qb6= o*E ΛnY9faš)sͱ\ng/@cA#[ /vjÎAVS$?S"^sD:(!>%_"(;2gVT`3RB"r0F(֔s[="vu$bAӕVaߧQ/MMkӒ3.^18cS8TU Y46d>ޯ%_m+&D! [\SKW=wgFN5K]O+6E *IbZ<; lש$0Q4fwq& 7 - A!ۋ违? gp(xjHX@cZU>q7@Ebs0wNڭ ,լrÅ8k>0]6Ǭ(Iu79'j qAĪ]6`ړ:v$nk/3m]@!d|QYM儹n*((qo ,K71:.4=Lj3SVkH5̌,w|I190_=ǬIh27q8ê5ꔙ)Ҩ6$`Qb\8'W<,^(}{/nlDC4 |##o(~﮹/QKz7n:l&#IhRKRʘcPYQ,8C>c,i|FfcfD NA :1;AwerPQԦ@+|W@,~{6ݧsk$%\'uJ7YgpП,3yL\}ܼ}*~(.Oz#+juq+_NAz^ܢbb\ 6$K&7̾XL{aTQO,ZYLC*քY̭xmrү*a2΃i4/~_ԏ A| _+uj%\v)[ڗwS;E_k X|^ 1*̳P.XkBUzox :16H b ` fYe K|=³ۛ?%sSpj/s)z97ܒ^]zd EN N:,<"%8X}+%Xu`Tꚙ

1ap3gWR{Urm->_0+$?YM9FPvQGMM;&J\qWl#$C.>~qnr|_55 ZJz ,֌w0bg&Ar2K$qUӻ&<:v^h8奀kF"=jM̮U m2H~v,2[ 拨Z7U 9> Gq_ԄGaaESbZ؋ŷ((7-vXm5F=bJe{3`GauALχAs,Xl@g)Z;mOdzG]l/e5+6Ƭ&#$EE{E~t~iBbݞ.aInZp܄c˘:E,~  h[<J\:}&c[jħ7~6XʛҊOvKO9\Ckw ?yo4B"hb]oVp:</:m@Q!:xpGIRB:ICMU#-Lf28y/oFƄ(.,EM*Ÿ2Hf`4m(.i"ƟA`t,Zds`(Tr'.uQ!m"g*2 *$Z=eRuWǹ b%!,єnNo4(!pW #Ge=: 7GfO|^4FSL,єnNo4( ?%ty$]A{˹ ǯ0NhWlGuH 3 Q{k*i74cqe LXބ *iG2#1Qei1a]:,Q:ޙ!o jeŕǘN 4o' MY3 ->a*(ԢR^Ufa?J션!~߼YP}K0 7́!qg&'JuֆQ/fo I #Q=ӥBƗě2;WRޚL¶kS \54rߔ/`F-Q6 U1#6I,-wS)8{̿>뚮S(PC??63duTG)3`"RMwҼ%/c" bnm v>'D`+t{3h#q|P8VOF_FK Īj/й/ʆbOH }I]iF"G $startLine, 'headers' => $headers, 'body' => $body, ]; } /** * Constructs a URI for an HTTP request message. * * @param string $path Path from the start-line * @param array $headers Array of headers (each value an array). */ public static function parseRequestUri(string $path, array $headers): string { $hostKey = array_filter(array_keys($headers), function ($k) { // Numeric array keys are converted to int by PHP. $k = (string) $k; return strtolower($k) === 'host'; }); // If no host is found, then a full URI cannot be constructed. if (!$hostKey) { return $path; } $host = $headers[reset($hostKey)][0]; $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; return $scheme.'://'.$host.'/'.ltrim($path, '/'); } /** * Parses a request message string into a request object. * * @param string $message Request message string. */ public static function parseRequest(string $message): RequestInterface { $data = self::parseMessage($message); $matches = []; if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { throw new \InvalidArgumentException('Invalid request string'); } $parts = explode(' ', $data['start-line'], 3); $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; $request = new Request( $parts[0], $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1], $data['headers'], $data['body'], $version ); return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]); } /** * Parses a response message string into a response object. * * @param string $message Response message string. */ public static function parseResponse(string $message): ResponseInterface { $data = self::parseMessage($message); // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space // between status-code and reason-phrase is required. But browsers accept // responses without space and reason as well. if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']); } $parts = explode(' ', $data['start-line'], 3); return new Response( (int) $parts[1], $data['headers'], $data['body'], explode('/', $parts[0])[1], $parts[2] ?? null ); } }