Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/Exceptions/RdapResponseException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace ArrayAccess\RdapClient\Exceptions;

use RuntimeException;

/**
* Thrown when the remote RDAP server returns an error response payload.
*
* The exception message is assembled from the `title` and `description`
* properties supplied by the server. The exception code contains the
* numeric `errorCode` value. The full decoded response is available via
* {@see getResponse()}.
*/
class RdapResponseException extends RuntimeException
{
/**
* Full decoded payload returned by the server.
*
* Keeping the entire array allows callers to inspect additional
* properties without having to parse/reconstruct them from the message
* or code.
*
* @var array<string, mixed>
*/
private array $response;

/**
* Construct from a previously prepared message.
*
* Use the named factory if possible.
*
* @param string $message human readable description
* @param int $code errorCode from the payload
* @param array<string, mixed> $response original decoded response
* @param \Throwable|null $previous previous exception
*/
public function __construct(
string $message = "",
int $code = 0,
array $response = [],
?\Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
$this->response = $response;
}

/**
* Create an exception instance from a decoded RDAP error response.
*
* @param array<string, mixed> $response
* @return self
*/
public static function fromResponse(array $response): self
{
$code = isset($response['errorCode']) ? (int) $response['errorCode'] : 0;
$title = is_string($response['title'] ?? null) ? $response['title'] : '';
$description = '';
if (isset($response['description'])) {
if (is_string($response['description'])) {
$description = $response['description'];
} elseif (is_array($response['description'])) {
$description = implode(' ', $response['description']);
}
}

$parts = array_filter([$title, $description]);
$message = $parts ? implode(' - ', $parts) : 'RDAP error response';

return new self($message, $code, $response);
}

/**
* Get the raw response array that triggered the exception.
*
* @return array<string, mixed>
*/
public function getResponse(): array
{
return $this->response;
}
}

5 changes: 4 additions & 1 deletion src/Interfaces/RdapRequestInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ public function getTarget() : string;

/**
* Get RDAP Response
*
*
* @return RdapResponseInterface
*
* @throws \ArrayAccess\RdapClient\Exceptions\RdapRemoteRequestException
* @throws \ArrayAccess\RdapClient\Exceptions\RdapResponseException
*/
public function getResponse() : RdapResponseInterface;

Expand Down
32 changes: 28 additions & 4 deletions src/Response/Abstracts/AbstractResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use ArrayAccess\RdapClient\Exceptions\InvalidDataTypeException;
use ArrayAccess\RdapClient\Exceptions\MismatchProtocolBehaviorException;
use ArrayAccess\RdapClient\Exceptions\RdapResponseException;
use ArrayAccess\RdapClient\Interfaces\RdapProtocolInterface;
use ArrayAccess\RdapClient\Interfaces\RdapRequestInterface;
use ArrayAccess\RdapClient\Interfaces\RdapResponseInterface;
Expand Down Expand Up @@ -67,19 +68,42 @@ public function __construct(
}

/**
* Assert response
* Assert that the raw JSON response is valid and populate internal
* array representation.
*
* @param string $responseJson
* @return void
*
* @throws InvalidDataTypeException when the content cannot be decoded or
* does not contain any of the required fields: objectClassName,
* rdapConformance, or errorCode.
* @throws RdapResponseException when the payload represents an error
* response (``errorCode`` field present). The returned exception
* may be inspected via {@see RdapResponseException::getResponse()}
* for the full payload; its message and code are derived from the
* `title`/`description`/`errorCode` fields.
*/
private function assertResponse(string $responseJson): void
{
$responseJson = json_decode($responseJson, true);
if (!is_array($responseJson) || !is_string($responseJson['objectClassName']??null)) {
$decoded = json_decode($responseJson, true);

if (!is_array($decoded) ||
(!isset($decoded['objectClassName']) &&
!isset($decoded['rdapConformance']) &&
!isset($decoded['errorCode']))
) {
throw new InvalidDataTypeException(
'Response is not valid json content'
);
}
$this->responseArray = $responseJson;

if (isset($decoded['errorCode'])) {
// convert immediately into an exception; no need to validate
// required fields for error payloads.
throw RdapResponseException::fromResponse($decoded);
}

$this->responseArray = $decoded;
}

/**
Expand Down