Skip to content

Commit

Permalink
Merge pull request #370 from Art4/implement-http-client-in-clients
Browse files Browse the repository at this point in the history
Let Clients implements `HttpClient`
  • Loading branch information
Art4 authored Feb 1, 2024
2 parents 1e7ff16 + ef1b7ce commit ab5f4eb
Show file tree
Hide file tree
Showing 10 changed files with 470 additions and 86 deletions.
44 changes: 5 additions & 39 deletions src/Redmine/Api/AbstractApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Redmine\Exception;
use Redmine\Exception\SerializerException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Http\Request;
use Redmine\Http\Response;
use Redmine\Serializer\JsonSerializer;
Expand Down Expand Up @@ -73,7 +74,7 @@ final protected function getHttpClient(): HttpClient

final protected function getLastResponse(): Response
{
return $this->lastResponse !== null ? $this->lastResponse : $this->createResponse(0, '', '');
return $this->lastResponse !== null ? $this->lastResponse : HttpFactory::makeResponse(0, '', '');
}

/**
Expand Down Expand Up @@ -412,16 +413,12 @@ private function getResponseAsArray(Response $response): array

private function handleClient(Client $client): HttpClient
{
$responseFactory = Closure::fromCallable([$this, 'createResponse']);

return new class ($client, $responseFactory) implements HttpClient {
return new class ($client) implements HttpClient {
private $client;
private $responseFactory;

public function __construct(Client $client, Closure $responseFactory)
public function __construct(Client $client)
{
$this->client = $client;
$this->responseFactory = $responseFactory;
}

public function request(Request $request): Response
Expand All @@ -436,7 +433,7 @@ public function request(Request $request): Response
$this->client->requestGet($request->getPath());
}

return ($this->responseFactory)(
return HttpFactory::makeResponse(
$this->client->getLastResponseStatusCode(),
$this->client->getLastResponseContentType(),
$this->client->getLastResponseBody()
Expand All @@ -445,37 +442,6 @@ public function request(Request $request): Response
};
}

private function createResponse(int $statusCode, string $contentType, string $body): Response
{
return new class ($statusCode, $contentType, $body) implements Response {
private $statusCode;
private $contentType;
private $body;

public function __construct(int $statusCode, string $contentType, string $body)
{
$this->statusCode = $statusCode;
$this->contentType = $contentType;
$this->body = $body;
}

public function getStatusCode(): int
{
return $this->statusCode;
}

public function getContentType(): string
{
return $this->contentType;
}

public function getContent(): string
{
return $this->body;
}
};
}

private function createRequest(string $method, string $path, string $contentType, string $content = ''): Request
{
return new class ($method, $path, $contentType, $content) implements Request {
Expand Down
71 changes: 50 additions & 21 deletions src/Redmine/Client/NativeCurlClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
namespace Redmine\Client;

use Redmine\Exception\ClientException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Http\Request;
use Redmine\Http\Response;

/**
* Native cURL client.
*/
final class NativeCurlClient implements Client
final class NativeCurlClient implements Client, HttpClient
{
use ClientApiTrait;

Expand Down Expand Up @@ -55,6 +59,27 @@ public function __construct(
}
}

/**
* Create and send a HTTP request and return the response
*
* @throws ClientException If anything goes wrong on creating or sending the request
*/
public function request(Request $request): Response
{
$this->runRequest(
$request->getMethod(),
$request->getPath(),
$request->getContent(),
$request->getContentType()
);

return HttpFactory::makeResponse(
$this->lastResponseStatusCode,
$this->lastResponseContentType,
$this->lastResponseBody
);
}

/**
* Sets to an existing username so api calls can be
* impersonated to this user.
Expand All @@ -77,31 +102,31 @@ public function stopImpersonateUser(): void
*/
public function requestGet(string $path): bool
{
return $this->request('get', $path);
return $this->runRequest('GET', $path);
}

/**
* Create and send a POST request.
*/
public function requestPost(string $path, string $body): bool
{
return $this->request('post', $path, $body);
return $this->runRequest('POST', $path, $body);
}

/**
* Create and send a PUT request.
*/
public function requestPut(string $path, string $body): bool
{
return $this->request('put', $path, $body);
return $this->runRequest('PUT', $path, $body);
}

/**
* Create and send a DELETE request.
*/
public function requestDelete(string $path): bool
{
return $this->request('delete', $path);
return $this->runRequest('DELETE', $path);
}

/**
Expand Down Expand Up @@ -211,13 +236,13 @@ private function unsetHttpHeader(string $name): void
/**
* @throws ClientException If anything goes wrong on curl request
*/
private function request(string $method, string $path, string $body = ''): bool
private function runRequest(string $method, string $path, string $body = '', string $contentType = ''): bool
{
$this->lastResponseStatusCode = 0;
$this->lastResponseContentType = '';
$this->lastResponseBody = '';

$curl = $this->createCurl($method, $path, $body);
$curl = $this->createCurl($method, $path, $body, $contentType);

$response = curl_exec($curl);

Expand Down Expand Up @@ -249,7 +274,7 @@ private function request(string $method, string $path, string $body = ''): bool
*
* @return \CurlHandle a cURL handle on success, <b>FALSE</b> on errors
*/
private function createCurl(string $method, string $path, string $body = '')
private function createCurl(string $method, string $path, string $body = '', string $contentType = '')
{
// General cURL options
$curlOptions = [
Expand All @@ -264,13 +289,13 @@ private function createCurl(string $method, string $path, string $body = '')
$curlOptions[CURLOPT_URL] = $this->url . $path;

// Set the HTTP request headers
$curlOptions[CURLOPT_HTTPHEADER] = $this->createHttpHeader($path);
$curlOptions[CURLOPT_HTTPHEADER] = $this->createHttpHeader($path, $contentType);

unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
unset($curlOptions[CURLOPT_POST]);
unset($curlOptions[CURLOPT_POSTFIELDS]);
switch ($method) {
case 'post':
case 'POST':
$curlOptions[CURLOPT_POST] = 1;
if ($this->isUploadCall($path) && $this->isValidFilePath($body)) {
@trigger_error('Uploading an attachment by filepath is deprecated, use file_get_contents() to upload the file content instead.', E_USER_DEPRECATED);
Expand All @@ -286,13 +311,13 @@ private function createCurl(string $method, string $path, string $body = '')
$curlOptions[CURLOPT_POSTFIELDS] = $body;
}
break;
case 'put':
case 'PUT':
$curlOptions[CURLOPT_CUSTOMREQUEST] = 'PUT';
if ($body !== '') {
$curlOptions[CURLOPT_POSTFIELDS] = $body;
}
break;
case 'delete':
case 'DELETE':
$curlOptions[CURLOPT_CUSTOMREQUEST] = 'DELETE';
break;
default: // GET
Expand All @@ -314,7 +339,7 @@ private function createCurl(string $method, string $path, string $body = '')
return $curl;
}

private function createHttpHeader(string $path): array
private function createHttpHeader(string $path, string $contentType = ''): array
{
// Additional request headers
$httpHeaders = [
Expand Down Expand Up @@ -352,14 +377,18 @@ private function createHttpHeader(string $path): array
// Now set or reset mandatory headers

// Content type headers
$tmp = parse_url($this->url . $path);

if ($this->isUploadCall($path)) {
$httpHeaders[] = 'Content-Type: application/octet-stream';
} elseif ('json' === substr($tmp['path'], -4)) {
$httpHeaders[] = 'Content-Type: application/json';
} elseif ('xml' === substr($tmp['path'], -3)) {
$httpHeaders[] = 'Content-Type: text/xml';
if ($contentType !== '') {
$httpHeaders[] = 'Content-Type: ' . $contentType;
} else {
$tmp = parse_url($this->url . $path);

if ($this->isUploadCall($path)) {
$httpHeaders[] = 'Content-Type: application/octet-stream';
} elseif ('json' === substr($tmp['path'], -4)) {
$httpHeaders[] = 'Content-Type: application/json';
} elseif ('xml' === substr($tmp['path'], -3)) {
$httpHeaders[] = 'Content-Type: text/xml';
}
}

return $httpHeaders;
Expand Down
Loading

0 comments on commit ab5f4eb

Please sign in to comment.