Example #1
0
 /**
  * Send the response to the client.
  * @param  RequestInterface|null $req  optional request object that triggered this response
  * @return self
  * @codeCoverageIgnore
  */
 public function send(RequestInterface $req = null)
 {
     $seekBeg = 0;
     $seekEnd = -1;
     // modify response according to request
     if ($req) {
         // process cached response (not modified)
         if ($req->hasHeader('If-Modified-Since') && $this->hasHeader('Last-Modified')) {
             $cached = strtotime($req->getHeader('If-Modified-Since'));
             $current = strtotime($this->getHeader('Last-Modified'));
             if ($cached === $current) {
                 $this->setStatusCode(304);
             }
         }
         // process cached response (ETag)
         if ($req->hasHeader('If-None-Match') && $this->hasHeader('ETag')) {
             if ($req->getHeader('If-None-Match') === $this->getHeader('ETag')) {
                 $this->setStatusCode(304);
             }
         }
         // process chunks
         if ($req->hasHeader('Range') && $this->hasHeader('Content-Length')) {
             $size = (int) $this->getHeader('Content-Length');
             $range = $req->getHeader('Range');
             $this->setHeader('Accept-Ranges', 'bytes');
             try {
                 if (!preg_match('@^bytes=\\d*-\\d*(,\\d*-\\d*)*$@', $range)) {
                     throw new \Exception('Invalid range');
                 }
                 $range = current(explode(',', substr($range, 6)));
                 list($seekBeg, $seekEnd) = explode('-', $range, 2);
                 $seekBeg = max((int) $seekBeg, 0);
                 $seekEnd = !(int) $seekEnd ? $size - 1 : min((int) $seekEnd, $size - 1);
                 if ($seekBeg > $seekEnd) {
                     throw new \Exception('Invalid range');
                 }
                 $this->setStatusCode(206);
                 $this->setHeader('Content-Range', 'bytes ' . $seekBeg . '-' . $seekEnd . '/' . $size);
                 $seekEnd = $seekEnd - $seekBeg;
             } catch (\Exception $e) {
                 $this->setStatusCode(416);
                 $this->setHeader('Content-Range', 'bytes * /' . $size);
                 $this->body = null;
             }
         }
     }
     if (!headers_sent()) {
         if ($this->getHeader('Location') && $this->code !== 201 && $this->code !== 202 && floor($this->code / 100) !== 3) {
             $this->setStatusCode(302);
         }
         http_response_code($this->code);
         foreach ($this->getHeaders() as $k => $v) {
             header($k . ': ' . $v);
         }
         foreach ($this->cookies as $k => $v) {
             header('Set-Cookie: ' . $k . '=' . urlencode($v[0]) . '; ' . $v[1], false);
         }
     }
     if ($this->body && !in_array($this->getStatusCode(), [204, 304, 416]) && (!$req || $req->getMethod() !== 'HEAD')) {
         $out = fopen('php://output', 'w');
         stream_copy_to_stream($this->body, $out, $seekEnd, $seekBeg);
         fclose($out);
     }
     return $this;
 }
 /**
  * Builds canonical string from request.
  *
  * @param RequestInterface $request Request to get canonical string from.
  *
  * @return string
  */
 public function getCanonicalString($request)
 {
     $parts = array($request->getHeader('content-type'), $request->getHeader('content-md5'), $request->getPath() . $request->getQuery(), $request->getHeader('date'));
     return join(',', $parts);
 }