Skip to content

Commit aca347f

Browse files
Merge pull request #3 from klkvsk/main
fix RDAP response error handling
2 parents d1d4f8f + 778e363 commit aca347f

3 files changed

Lines changed: 117 additions & 5 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArrayAccess\RdapClient\Exceptions;
6+
7+
use RuntimeException;
8+
9+
/**
10+
* Thrown when the remote RDAP server returns an error response payload.
11+
*
12+
* The exception message is assembled from the `title` and `description`
13+
* properties supplied by the server. The exception code contains the
14+
* numeric `errorCode` value. The full decoded response is available via
15+
* {@see getResponse()}.
16+
*/
17+
class RdapResponseException extends RuntimeException
18+
{
19+
/**
20+
* Full decoded payload returned by the server.
21+
*
22+
* Keeping the entire array allows callers to inspect additional
23+
* properties without having to parse/reconstruct them from the message
24+
* or code.
25+
*
26+
* @var array<string, mixed>
27+
*/
28+
private array $response;
29+
30+
/**
31+
* Construct from a previously prepared message.
32+
*
33+
* Use the named factory if possible.
34+
*
35+
* @param string $message human readable description
36+
* @param int $code errorCode from the payload
37+
* @param array<string, mixed> $response original decoded response
38+
* @param \Throwable|null $previous previous exception
39+
*/
40+
public function __construct(
41+
string $message = "",
42+
int $code = 0,
43+
array $response = [],
44+
?\Throwable $previous = null
45+
) {
46+
parent::__construct($message, $code, $previous);
47+
$this->response = $response;
48+
}
49+
50+
/**
51+
* Create an exception instance from a decoded RDAP error response.
52+
*
53+
* @param array<string, mixed> $response
54+
* @return self
55+
*/
56+
public static function fromResponse(array $response): self
57+
{
58+
$code = isset($response['errorCode']) ? (int) $response['errorCode'] : 0;
59+
$title = is_string($response['title'] ?? null) ? $response['title'] : '';
60+
$description = '';
61+
if (isset($response['description'])) {
62+
if (is_string($response['description'])) {
63+
$description = $response['description'];
64+
} elseif (is_array($response['description'])) {
65+
$description = implode(' ', $response['description']);
66+
}
67+
}
68+
69+
$parts = array_filter([$title, $description]);
70+
$message = $parts ? implode(' - ', $parts) : 'RDAP error response';
71+
72+
return new self($message, $code, $response);
73+
}
74+
75+
/**
76+
* Get the raw response array that triggered the exception.
77+
*
78+
* @return array<string, mixed>
79+
*/
80+
public function getResponse(): array
81+
{
82+
return $this->response;
83+
}
84+
}
85+

src/Interfaces/RdapRequestInterface.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ public function getTarget() : string;
4646

4747
/**
4848
* Get RDAP Response
49-
*
49+
*
5050
* @return RdapResponseInterface
51+
*
52+
* @throws \ArrayAccess\RdapClient\Exceptions\RdapRemoteRequestException
53+
* @throws \ArrayAccess\RdapClient\Exceptions\RdapResponseException
5154
*/
5255
public function getResponse() : RdapResponseInterface;
5356

src/Response/Abstracts/AbstractResponse.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
use ArrayAccess\RdapClient\Exceptions\InvalidDataTypeException;
77
use ArrayAccess\RdapClient\Exceptions\MismatchProtocolBehaviorException;
8+
use ArrayAccess\RdapClient\Exceptions\RdapResponseException;
89
use ArrayAccess\RdapClient\Interfaces\RdapProtocolInterface;
910
use ArrayAccess\RdapClient\Interfaces\RdapRequestInterface;
1011
use ArrayAccess\RdapClient\Interfaces\RdapResponseInterface;
@@ -67,19 +68,42 @@ public function __construct(
6768
}
6869

6970
/**
70-
* Assert response
71+
* Assert that the raw JSON response is valid and populate internal
72+
* array representation.
73+
*
7174
* @param string $responseJson
7275
* @return void
76+
*
77+
* @throws InvalidDataTypeException when the content cannot be decoded or
78+
* does not contain any of the required fields: objectClassName,
79+
* rdapConformance, or errorCode.
80+
* @throws RdapResponseException when the payload represents an error
81+
* response (``errorCode`` field present). The returned exception
82+
* may be inspected via {@see RdapResponseException::getResponse()}
83+
* for the full payload; its message and code are derived from the
84+
* `title`/`description`/`errorCode` fields.
7385
*/
7486
private function assertResponse(string $responseJson): void
7587
{
76-
$responseJson = json_decode($responseJson, true);
77-
if (!is_array($responseJson) || !is_string($responseJson['objectClassName']??null)) {
88+
$decoded = json_decode($responseJson, true);
89+
90+
if (!is_array($decoded) ||
91+
(!isset($decoded['objectClassName']) &&
92+
!isset($decoded['rdapConformance']) &&
93+
!isset($decoded['errorCode']))
94+
) {
7895
throw new InvalidDataTypeException(
7996
'Response is not valid json content'
8097
);
8198
}
82-
$this->responseArray = $responseJson;
99+
100+
if (isset($decoded['errorCode'])) {
101+
// convert immediately into an exception; no need to validate
102+
// required fields for error payloads.
103+
throw RdapResponseException::fromResponse($decoded);
104+
}
105+
106+
$this->responseArray = $decoded;
83107
}
84108

85109
/**

0 commit comments

Comments
 (0)