/** * @param Request $request * @param Response $response * @param int $code * @param \Throwable $previous */ public function __construct(Request $request, Response $response, int $code, \Throwable $previous = null) { $this->response = $response; $message = $response->getBody() ? $response->getBody() : 'Empty response body'; if (strlen($message) > 128) { $message = substr($message, 0, 128) . ' ...'; } parent::__construct($request, $message, $code, $previous); }
/** * @param Request $request * * @throws ConnectionException * @throws RequestException * * @return Response */ public function request(Request $request) : Response { if (!$this->resource) { $this->resource = curl_init(); } // options $options = [CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true]; // gzip, deflate if ($this->options[self::OPTIONS_ACCEPT_ENCODING]) { if ($this->options[self::OPTIONS_ACCEPT_ENCODING] == true) { $options[CURLOPT_ENCODING] = ''; //force curl to send all Accept-Encoding available } else { $options[CURLOPT_ENCODING] = $this->options[self::OPTIONS_ACCEPT_ENCODING]; } } // ssl if (isset($this->options[self::OPTIONS_SSL_VERIFY]) && $this->options[self::OPTIONS_SSL_VERIFY] === false) { $options[CURLOPT_SSL_VERIFYHOST] = false; $options[CURLOPT_SSL_VERIFYPEER] = false; } // timeout if ($this->options[self::OPTIONS_TIMEOUT]) { $options[CURLOPT_TIMEOUT_MS] = $this->options[self::OPTIONS_TIMEOUT]; } if ($this->options[self::OPTIONS_CONNECT_TIMEOUT]) { $options[CURLOPT_CONNECTTIMEOUT_MS] = $this->options[self::OPTIONS_CONNECT_TIMEOUT]; } // proxy if (!empty($this->options[self::OPTIONS_PROXY])) { $options[CURLOPT_PROXY] = $this->options[self::OPTIONS_PROXY]; } // proxy if (!empty($this->options[self::OPTIONS_FOLLOW_REDIRECTION])) { $options[CURLOPT_FOLLOWLOCATION] = $this->options[self::OPTIONS_FOLLOW_REDIRECTION]; if (!empty($this->options[self::OPTIONS_MAX_REDIRECTION])) { $options[CURLOPT_MAXREDIRS] = $this->options[self::OPTIONS_MAX_REDIRECTION]; } } // debug temporary files if (!empty($this->options[self::OPTIONS_DEBUG])) { $options[CURLOPT_VERBOSE] = true; $options[CURLOPT_STDERR] = fopen('php://temp', 'rw'); } // headers handling $headers = array_merge($this->defaultHeader, $request->getHeaders()); // add cookie to current headers if ($request->getCookies()) { $cookies = []; foreach ($request->getCookies() as $name => $cookie) { $cookies[] = $name . '=' . urlencode($cookie->getValue()); } $headers['Cookie'] = implode('; ', $cookies); } if ($headers) { $finalHeaders = []; foreach ($headers as $name => $value) { $finalHeaders[] = $name . ': ' . $value; } $options[CURLOPT_HTTPHEADER] = $finalHeaders; } // handle post $options[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); if ($request->getMethod() != 'GET') { if ($request->getPayload()) { $options[CURLOPT_POSTFIELDS] = $request->getPayload(); } elseif ($request->getHeader('Content-Type') == 'multipart/form-data') { // as an array(): The data will be sent as multipart/form-data // which is not always accepted by the serve // There are "@" issue on multipart POST requests. $options[CURLOPT_SAFE_UPLOAD] = true; $options[CURLOPT_POSTFIELDS] = $request->getPosts(); } else { // as url encoded string: The data will be sent as application/x-www-form-urlencoded, // which is the default encoding for submitted html form data. $options[CURLOPT_POSTFIELDS] = http_build_query($request->getPosts()); } } // progress if ($this->progress) { $options[CURLOPT_NOPROGRESS] = false; $options[CURLOPT_PROGRESSFUNCTION] = $this->progress; $options[CURLOPT_BUFFERSIZE] = 1024; } // user & password if ($request->getUri()->getUser()) { $options[CURLOPT_USERPWD] = $request->getUri()->getUser() . ($request->getUri()->getPassword() ? ':' . $request->getUri()->getPassword() : ''); } // final options $options[CURLOPT_URL] = $request->getUri()->get(false); curl_reset($this->resource); curl_setopt_array($this->resource, $options); // send request $requestEvent = new TimerEvent($this->options[self::OPTIONS_EVENTS_PREFIX] . '.request'); $response = curl_exec($this->resource); $infos = curl_getinfo($this->resource); // request event $requestEvent->addData(['method' => $request->getMethod(), 'url' => $request->getUri()->get(false), 'code' => $infos['http_code'], 'header size' => $infos['header_size'], 'request size' => $infos['request_size']]); self::emit($requestEvent); // generate events for all duration $data = ['method' => $request->getMethod(), 'url' => $request->getUri()->get(false), 'code' => $infos['http_code']]; $event = new ManualTimerEvent($this->options[self::OPTIONS_EVENTS_PREFIX] . '.nameLookup', $data); $event->setDuration($infos['namelookup_time'])->setStart($requestEvent->getStart()); self::emit($event); $event = new ManualTimerEvent($this->options[self::OPTIONS_EVENTS_PREFIX] . '.connect', $data); $event->setDuration($infos['connect_time'])->setStart($requestEvent->getStart() + $infos['namelookup_time']); self::emit($event); // debug log if (!empty($this->options[self::OPTIONS_DEBUG])) { rewind($options[CURLOPT_STDERR]); $log = stream_get_contents($options[CURLOPT_STDERR]); if (isset($options[CURLOPT_POSTFIELDS]) && is_array($options[CURLOPT_POSTFIELDS])) { $log .= json_encode($log) . "\r\n"; } elseif (isset($options[CURLOPT_POSTFIELDS])) { $log .= $options[CURLOPT_POSTFIELDS] . "\r\n"; } if ($response) { $log = $log . "\r\n" . htmlentities(explode("\r\n\r\n", $response)[1]); } self::logger()->debug($log); } // connection error if ($response === false) { $code = curl_errno($this->resource); $message = curl_error($this->resource); throw new ConnectionException($request, $message, $code); } // final response $response = new Response($response); if ($response->getStatus() >= 400) { throw new RequestException($request, $response, $response->getStatus()); } return $response; }
/** * {@inheritdoc} * * @throws \LogicException */ public function clearCookie(Cookie $cookie) { if ($this->headerSent($file, $line)) { throw new \LogicException(sprintf("Headers is already sent in '%s:%s'", $file, $line)); } return parent::clearCookie($cookie); }