client = $clientImplementation; $this->requestFactory = $requestFactory; } /** Sends an HTTP request and returns a response * * Redirects are followed up to the configured threshold. If credentials * are supplied in the URL, these only apply to the original request; * credentials are not sent to redirecr URLs, even for the same origin. */ public function sendRequest(RequestInterface $request): ResponseInterface { $stop = $this->maxRedirects; for ($a = 0; $a <= $stop; $a++) { $response = $this->client->sendRequest($request); $code = $response->getStatusCode(); if ($code < 300 || $code === 304) { return $response; } elseif ($code >= 400) { throw new Exception("httpStatus".$code); } else { $loc = $response->getHeader("Location"); for ($b = 0; $b < sizeof($loc); $b++) { if ($url = Url::fromString($loc[$b], (string) $request->getUri()->withUserInfo("", ""))) { $request = $request->withUri($url); if ($code === 303 && !in_array($request->getMethod(), ["GET", "HEAD"])) { // this is merely for correctness; the client should not normally be using other methods $request = $request->withMethod("GET"); // @codeCoverageIgnore } continue 2; } } return $response; } } throw new Exception("tooManyRedirects"); } public function createRequest(string $method, $uri, array $headers = []): RequestInterface { $req = $this->requestFactory->createRequest($method, $uri); if (strlen($this->userAgent)) { $req = $req->withHeader("User-Agent", $this->userAgent); } foreach ($headers as $k => $v) { if (!is_array($v)) { $v = [$v]; } else { $v = array_values($v); } foreach ($v as $kk => $vv) { if ($vv instanceof \DateTimeImmutable) { $vv = $vv->setTimezone(new \DateTimeZone("UTC"))->format(\DateTimeInterface::RFC7231); } elseif ($vv instanceof \DateTime) { $vv = clone $vv; $vv->setTimezone(new \DateTimeZone("UTC")); $v = $vv->format(\DateTimeInterface::RFC7231); } $m = $kk === 0 ? "withHeader" : "withAddedHeader"; $req->$m($k, (string) $vv); } } return $req; } public function fetch($uri, string $accept = "*/*", string $etag = "", ?\DateTimeInterface $lastModified = null): ResponseInterface { $headers = ['Accept' => $accept]; if (strlen($etag)) { $headers['ETag'] = $etag; } if ($lastModified) { $headers['Last-Modified'] = $lastModified; } return $this->sendRequest($this->createRequest("GET", $uri, $headers)); } }