Skip to content

Commit 624bcab

Browse files
authored
Merge 855b0dd into 14989b8
2 parents 14989b8 + 855b0dd commit 624bcab

File tree

14 files changed

+101
-13
lines changed

14 files changed

+101
-13
lines changed

clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal/interceptors/RetryStrategy.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ private Response handleResponse(StatefulHost host, @Nonnull Response response) t
8484

8585
try {
8686
String message = response.body() != null ? response.body().string() : response.message();
87+
if (response.header("Content-Type", "application/json").contains("text/html")) {
88+
message = response.message();
89+
}
8790
throw isRetryable(response)
8891
? new AlgoliaRequestException(message, response.code())
8992
: new AlgoliaApiException(message, response.code());

clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,29 @@ export function deserializeSuccess<TObject>(response: Response): TObject {
8282
}
8383
}
8484

85+
const httpMessages: Record<number, string> = {
86+
400: 'Bad Request',
87+
401: 'Unauthorized',
88+
402: 'Payment Required',
89+
403: 'Forbidden',
90+
404: 'Not Found',
91+
405: 'Method Not Allowed',
92+
406: 'Not Acceptable',
93+
407: 'Proxy Authentication Required',
94+
408: 'Request Timeout',
95+
409: 'Conflict',
96+
410: 'Gone',
97+
411: 'Length Required',
98+
412: 'Precondition Required',
99+
413: 'Request Entry Too Large',
100+
414: 'Request-URI Too Long',
101+
415: 'Unsupported Media Type',
102+
416: 'Requested Range Not Satisfiable',
103+
417: 'Expectation Failed',
104+
418: 'I\'m a teapot',
105+
429: 'Too Many Requests',
106+
}
107+
85108
export function deserializeFailure({ content, status }: Response, stackFrame: StackFrame[]): Error {
86109
try {
87110
const parsed = JSON.parse(content);
@@ -92,5 +115,5 @@ export function deserializeFailure({ content, status }: Response, stackFrame: St
92115
} catch {
93116
// ..
94117
}
95-
return new ApiError(content, status, stackFrame);
118+
return new ApiError(status in httpMessages ? httpMessages[status] : content, status, stackFrame);
96119
}

clients/algoliasearch-client-php/lib/Http/CurlHttpClient.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ public function sendRequest(
116116
$statusCode = (int) curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
117117
$responseBody = curl_multi_getcontent($curlHandle);
118118
$error = curl_error($curlHandle);
119+
$contentType = curl_getinfo($curlHandle, CURLINFO_CONTENT_TYPE);
119120

120121
$this->releaseMHandle($curlHandle);
121122
curl_close($curlHandle);
122123

123-
return new Response($statusCode, [], $responseBody, '1.1', $error);
124+
return new Response($statusCode, ['Content-Type' => $contentType], $responseBody, '1.1', $error);
124125
}
125126

126127
private function getMHandle($curlHandle)

clients/algoliasearch-client-php/lib/RetryStrategy/ApiWrapper.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ private function handleResponse(
247247
throw new RetriableException('Retriable failure on '.$request->getUri()->getHost().': '.$reason, $statusCode);
248248
}
249249

250+
// handle HTML error responses
251+
if (false !== strpos($response->getHeaderLine('Content-Type'), 'text/html')) {
252+
throw new AlgoliaException($statusCode.': '.$response->getReasonPhrase(), $statusCode);
253+
}
254+
250255
$responseArray = Helpers::json_decode($body, true);
251256

252257
if (404 === $statusCode) {

clients/algoliasearch-client-ruby/lib/algolia/error.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ def initialize(message, errors = [])
2727
# which is also included in the response attribute.
2828
#
2929
class AlgoliaHttpError < AlgoliaError
30-
attr_accessor :code, :message
30+
attr_accessor :code, :http_message
3131

3232
def initialize(code, message)
3333
self.code = code
34-
self.message = message
35-
super("#{self.code()}: #{self.message()}")
34+
self.http_message = message
35+
super("#{code}: #{message}")
3636
end
3737
end
3838
end

clients/algoliasearch-client-ruby/lib/algolia/transport/http/http_requester.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ def send_request(host, method, path, body, query_params, headers, timeout, conne
3939
@logger.info("Request succeeded. Response status: #{response.status}, body: #{response.body}")
4040
end
4141

42-
return Http::Response.new(status: response.status, body: response.body, headers: response.headers)
42+
return Http::Response.new(status: response.status, reason_phrase: response.reason_phrase, body: response.body, headers: response.headers)
4343
end
4444

4545
if ENV["ALGOLIA_DEBUG"]
4646
@logger.info("Request failed. Response status: #{response.status}, error: #{response.body}")
4747
end
4848

49-
Http::Response.new(status: response.status, error: response.body, headers: response.headers)
49+
Http::Response.new(status: response.status, reason_phrase: response.reason_phrase, error: response.body, headers: response.headers)
5050
rescue Faraday::TimeoutError => e
5151
@logger.info("Request timed out. Error: #{e.message}") if ENV["ALGOLIA_DEBUG"]
5252
Http::Response.new(error: e.message, has_timed_out: true)

clients/algoliasearch-client-ruby/lib/algolia/transport/http/response.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
module Algolia
22
module Http
33
class Response
4-
attr_reader :status, :body, :error, :headers, :has_timed_out, :network_failure
4+
attr_reader :status, :reason_phrase, :body, :error, :headers, :has_timed_out, :network_failure
55

66
# used for the echo requester
77
attr_reader :method, :path, :query_params, :host, :timeout, :connect_timeout
88

99
#
1010
# @option status [String] Response status
11+
# @option reason_phrase [String] Response reason phrase
1112
# @option body [String] Response body
1213
# @option error [String] Response error or caught error
1314
# @option headers [String] Response headers
1415
# @option has_timed_out [String] If the request has timed out
1516
#
1617
def initialize(opts = {})
1718
@status = opts[:status]
19+
@reason_phrase = opts[:reason_phrase]
1820
@body = opts[:body]
1921
@error = opts[:error] || ""
2022
@headers = opts[:headers] || ""

clients/algoliasearch-client-ruby/lib/algolia/transport/transport.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ def request(call_type, method, path, body, opts = {})
6969
network_failure: response.network_failure
7070
)
7171
if outcome == FAILURE
72+
# handle HTML error
73+
if response.headers["content-type"]&.include?("text/html")
74+
raise Algolia::AlgoliaHttpError.new(response.status, response.reason_phrase)
75+
end
76+
7277
decoded_error = JSON.parse(response.error, :symbolize_names => true)
7378
raise Algolia::AlgoliaHttpError.new(response.status, decoded_error[:message])
7479
end

scripts/cts/testServer/timeout.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ function addRoutes(app: express.Express): void {
9797

9898
// no response, just hang
9999
});
100+
101+
app.get('/1/html-error', (req, res) => {
102+
res.setHeader('Content-Type', 'text/html');
103+
res.status(429).send('<html><body>429 Too Many Requests</body></html>');
104+
});
100105
}
101106

102107
export function timeoutServer(): Promise<Server> {

templates/ruby/tests/client/tests.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
{{#dynamicTemplate}}{{/dynamicTemplate}}
1919
assert(false, 'An error should have been raised')
2020
rescue => e
21-
assert_equal({{#lambda.codeSnakeCase}}'{{{expectedError}}}'{{/lambda.codeSnakeCase}}.sub('%localhost%', ENV.fetch('CI', nil) == 'true' ? 'localhost' : 'host.docker.internal'), e.message)
21+
assert_equal({{#lambda.codeSnakeCase}}%q({{{expectedError}}}){{/lambda.codeSnakeCase}}.sub('%localhost%', ENV.fetch('CI', nil) == 'true' ? 'localhost' : 'host.docker.internal'), e.message)
2222
end
2323
{{/isError}}
2424
{{^isError}}

tests/CTS/client/ingestion/api.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
[
2+
{
3+
"testName": "can handle HTML error",
4+
"autoCreateClient": false,
5+
"steps": [
6+
{
7+
"type": "createClient",
8+
"parameters": {
9+
"appId": "test-app-id",
10+
"apiKey": "test-api-key",
11+
"region": "us",
12+
"customHosts": [
13+
{
14+
"port": 6676
15+
}
16+
]
17+
}
18+
},
19+
{
20+
"type": "method",
21+
"method": "customGet",
22+
"parameters": {
23+
"path": "1/html-error"
24+
},
25+
"expected": {
26+
"error": {
27+
"csharp": "<html><body>429 too many requests</body></html>",
28+
"go": "API error [429] Too Many Requests",
29+
"java": "Status Code: 429 - Too Many Requests",
30+
"javascript": "Too Many Requests",
31+
"kotlin": "Client request(GET http://%localhost%:6676/1/html-error) invalid: 429 Too Many Requests. Text: \\\"<html><body>429 Too Many Requests</body></html>\\\"",
32+
"php": "429: Too Many Requests",
33+
"python": "Too Many Requests",
34+
"ruby": "429: Too Many Requests",
35+
"scala": "<html><body>429 Too Many Requests</body></html>",
36+
"swift": "HTTP error: Status code: 429 Message: No message"
37+
}
38+
}
39+
}
40+
]
41+
}
42+
]

tests/CTS/client/search/indexExists.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"kotlin": "Client request(GET http://%localhost%:6681/1/indexes/indexExistsERROR/settings) invalid: 403 Forbidden. Text: \\\"{\\\"message\\\":\\\"Invalid API key\\\"}\\\"",
8989
"php": "Invalid API key",
9090
"python": "Invalid API key",
91-
"ruby": "Invalid API key",
91+
"ruby": "403: Invalid API key",
9292
"swift": "HTTP error: Status code: 403 Message: Invalid API key"
9393
}
9494
}

tests/CTS/client/search/saveObjects.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@
8787
"kotlin": "Client request(POST http://%localhost%:6680/1/indexes/cts_e2e_saveObjects_kotlin/batch) invalid: 403 Forbidden. Text: \\\"{\\\"message\\\":\\\"Invalid Application-ID or API key\\\",\\\"status\\\":403}\\\"",
8888
"php": "Invalid Application-ID or API key",
8989
"python": "Invalid Application-ID or API key",
90-
"ruby": "Invalid Application-ID or API key",
91-
"scala": "Invalid Application-ID or API key",
90+
"ruby": "403: Invalid Application-ID or API key",
9291
"swift": "HTTP error: Status code: 403 Message: Invalid Application-ID or API key"
9392
}
9493
}

tests/output/scala/src/test/scala/algoliasearch/package.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ package object algoliasearch {
1010

1111
def assertError(message: String)(call: => Unit)(implicit ec: ExecutionContextExecutor): Unit = {
1212
val error = intercept[Exception](call)
13-
assert(error.getMessage == message)
13+
assert(
14+
error.getMessage == message,
15+
s"Error message does not match, expected: $message, got: ${error.getMessage}"
16+
)
1417
}
1518

1619
@targetName("assertErrorFuture")

0 commit comments

Comments
 (0)