Skip to content

Commit c5b65a9

Browse files
Googlera-maurice
authored andcommitted
Implement the ability to set a timeout on rest requests.
I've tested this using CL 251703099, which is a work-in-progress CL to add timeouts to the Functions C++ SDK. I assume I should also add some tests under firebase/app/client/cpp/rest/, but I wanted to get feedback on this API before spending any more time on it. It seems I don't have permission to run the Guitar presubmits. :/ PiperOrigin-RevId: 252854599
1 parent 5ec5017 commit c5b65a9

File tree

12 files changed

+80
-49
lines changed

12 files changed

+80
-49
lines changed

app/instance_id/instance_id_desktop_impl.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ class SignalSemaphoreResponse : public rest::Response {
5151
complete_->Post();
5252
}
5353

54-
void MarkCanceled() override {
55-
rest::Response::MarkCompleted();
54+
void MarkFailed() override {
55+
rest::Response::MarkFailed();
5656
complete_->Post();
5757
}
5858

app/rest/request.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ class Request : public Transfer {
8181
// Mark the transfer completed.
8282
void MarkCompleted() override { completed_ = true; }
8383

84-
// Mark the transfer canceled.
85-
void MarkCanceled() override { completed_ = false; }
84+
// Mark the transfer failed, usually from cancellation or timeout.
85+
void MarkFailed() override { completed_ = false; }
8686

8787
bool completed() const { return completed_; }
8888

app/rest/request_options.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ namespace rest {
2727
// transport_interface.h. The actual HTTP transporter could be either library
2828
// Curl or a test mock.
2929
struct RequestOptions {
30-
RequestOptions() :
31-
method("GET"), stream_post_fields(false), verbose(false) {}
30+
RequestOptions()
31+
: method("GET"),
32+
stream_post_fields(false),
33+
timeout_ms(300000), // Same timeout used by Chromium.
34+
verbose(false) {}
3235

3336
// The URL to use in the request.
3437
std::string url;
@@ -42,6 +45,8 @@ struct RequestOptions {
4245
bool stream_post_fields;
4346
// Stores key-value pairs in header.
4447
std::map<std::string, std::string> header;
48+
// The maximum time in milliseconds to allow the request and response.
49+
int64_t timeout_ms;
4550

4651
// Set true to make the library display more verbose info to help debug. Does
4752
// not really affect the connection.

app/rest/response.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,11 @@ class Response : public Transfer {
6969
body_completed_ = true;
7070
}
7171

72-
// Marks the response as canceled. There will never be a response, so stop
72+
// Marks the response as failed. There will never be a response, so stop
7373
// waiting for one.
74-
void MarkCanceled() override {
74+
void MarkFailed() override {
7575
header_completed_ = false;
7676
body_completed_ = false;
77-
status_ = rest::util::HttpNoContent;
7877
}
7978

8079
// Getters.
@@ -85,6 +84,8 @@ class Response : public Transfer {
8584
std::time_t fetch_time() const { return fetch_time_; }
8685

8786
// Setters.
87+
void set_status(int status) { status_ = status; }
88+
8889
void set_sdk_error_code(int sdk_error_code) {
8990
sdk_error_code_ = sdk_error_code;
9091
}

app/rest/transfer_interface.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class Transfer {
2727
// Mark the transfer completed.
2828
virtual void MarkCompleted() = 0;
2929

30-
// Mark the transfer canceled.
31-
virtual void MarkCanceled() = 0;
30+
// Mark the transfer failed, usually from cancellation or timeout.
31+
virtual void MarkFailed() = 0;
3232
};
3333

3434
} // namespace rest

app/rest/transport_curl.cc

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ class BackgroundTransportCurl {
168168

169169
CURL* curl() const { return curl_; }
170170
Response* response() const { return response_; }
171-
void set_canceled(bool canceled) { canceled_ = true; }
171+
void set_canceled(bool canceled) { canceled_ = canceled; }
172+
void set_timed_out(bool timed_out) { timed_out_ = timed_out; }
172173
ControllerCurl* controller() const { return controller_; }
173174
TransportCurl* transport_curl() const { return transport_curl_; }
174175

@@ -203,6 +204,8 @@ class BackgroundTransportCurl {
203204
void* complete_data_;
204205
// Whether the operation has been canceled.
205206
bool canceled_;
207+
// Whether the operation timed out.
208+
bool timed_out_;
206209
};
207210

208211
// The data common to both threads. This is used to communicate when the
@@ -358,7 +361,8 @@ BackgroundTransportCurl::BackgroundTransportCurl(
358361
transport_curl_(transport_curl),
359362
complete_(complete),
360363
complete_data_(complete_data),
361-
canceled_(false) {
364+
canceled_(false),
365+
timed_out_(false) {
362366
assert(curl_multi_);
363367
assert(curl_);
364368
assert(transport_curl);
@@ -389,15 +393,15 @@ BackgroundTransportCurl::~BackgroundTransportCurl() {
389393
request_header_ = nullptr;
390394
}
391395

392-
// If this is an asynchronous operation, MarkCanceled() or MarkCompleted()
396+
// If this is an asynchronous operation, MarkFailed() or MarkCompleted()
393397
// could end up attempting to tear down TransportCurl so we signal
394398
// completion here.
395399
if (transport_curl_->is_async()) {
396400
transport_curl_->SignalTransferComplete();
397401
CompleteOperation();
398402
} else {
399403
// Synchronous operations need all data present in the response before
400-
// Perform() returns so signal complete after MarkCanceled() or
404+
// Perform() returns so signal complete after MarkFailed() or
401405
// MarkCompleted().
402406
CompleteOperation();
403407
transport_curl_->SignalTransferComplete();
@@ -407,8 +411,13 @@ BackgroundTransportCurl::~BackgroundTransportCurl() {
407411
void BackgroundTransportCurl::CompleteOperation() {
408412
if (complete_) complete_(this, complete_data_);
409413
if (canceled_) {
410-
request_->MarkCanceled();
411-
response_->MarkCanceled();
414+
response_->set_status(rest::util::HttpNoContent);
415+
request_->MarkFailed();
416+
response_->MarkFailed();
417+
} else if (timed_out_) {
418+
response_->set_status(rest::util::HttpRequestTimeout);
419+
request_->MarkFailed();
420+
response_->MarkFailed();
412421
} else {
413422
request_->MarkCompleted();
414423
response_->MarkCompleted();
@@ -456,6 +465,8 @@ bool BackgroundTransportCurl::PerformBackground(Request* request) {
456465
"set http body read callback");
457466
CheckOk(curl_easy_setopt(curl_, CURLOPT_READDATA, request),
458467
"set http body read callback data");
468+
CheckOk(curl_easy_setopt(curl_, CURLOPT_TIMEOUT_MS, options.timeout_ms),
469+
"set http timeout milliseconds");
459470

460471
// SDK error in initialization stage is not recoverable.
461472
FIREBASE_ASSERT(err_code_ == CURLE_OK);
@@ -872,9 +883,16 @@ void CurlThread::ProcessRequests() {
872883
char* char_pointer;
873884
curl_easy_getinfo(handle, CURLINFO_PRIVATE, &char_pointer);
874885
curl_multi_remove_handle(curl_multi, handle);
886+
BackgroundTransportCurl* transport =
887+
reinterpret_cast<BackgroundTransportCurl*>(char_pointer);
888+
889+
// Determine if the request timed out.
890+
if (message->data.result == CURLE_OPERATION_TIMEDOUT) {
891+
transport->set_timed_out(true);
892+
}
875893

876894
// Mark the response complete.
877-
delete reinterpret_cast<BackgroundTransportCurl*>(char_pointer);
895+
delete transport;
878896
expected_running_handles--;
879897
break;
880898
}

app/rest/util.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ enum HttpStatusCode {
5151
HttpPaymentRequired = 402,
5252
HttpUnauthorized = 401,
5353
HttpForbidden = 403,
54-
HttpNotFound = 404
54+
HttpNotFound = 404,
55+
HttpRequestTimeout = 408
5556
};
5657

5758
// Initialize utilities. This must be called before any functions that

functions/src/desktop/callable_reference_desktop.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ void HttpsCallableRequest::MarkCompleted() {
9797
response_);
9898
}
9999

100-
void HttpsCallableRequest::MarkCanceled() {
101-
rest::Request::MarkCanceled();
100+
void HttpsCallableRequest::MarkFailed() {
101+
rest::Request::MarkFailed();
102102
HttpsCallableReferenceInternal::ResolveFuture(future_impl_, future_handle_,
103103
response_);
104104
}

functions/src/desktop/callable_reference_desktop.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ class HttpsCallableRequest : public rest::Request {
3131
// Mark the transfer completed.
3232
void MarkCompleted() override;
3333

34-
// Mark the transfer canceled.
35-
void MarkCanceled() override;
34+
// Mark the transfer failed.
35+
void MarkFailed() override;
3636

3737
void set_future_impl(ReferenceCountedFutureImpl* impl) {
3838
future_impl_ = impl;

storage/src/desktop/curl_requests.cc

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include "storage/src/desktop/curl_requests.h"
16+
1517
#include <stdio.h>
16-
#include <string>
1718

18-
#include "storage/src/desktop/curl_requests.h"
19+
#include <string>
1920

2021
#include "app/rest/util.h"
2122
#include "storage/src/desktop/metadata_desktop.h"
2223
#include "storage/src/desktop/rest_operation.h"
24+
#include "storage/src/include/firebase/storage/common.h"
2325
#include "storage/src/include/firebase/storage/metadata.h"
2426

2527
namespace firebase {
@@ -64,24 +66,28 @@ BlockingResponse::BlockingResponse(FutureHandle handle,
6466

6567
BlockingResponse::~BlockingResponse() {
6668
// If the response isn't complete, cancel it.
67-
if (status() == rest::util::HttpInvalid) MarkCanceled();
69+
if (status() == rest::util::HttpInvalid) {
70+
set_status(rest::util::HttpNoContent);
71+
MarkFailed();
72+
}
6873
}
6974

7075
void BlockingResponse::MarkCompleted() { rest::Response::MarkCompleted(); }
7176

72-
void BlockingResponse::MarkCanceled() {
73-
rest::Response::MarkCanceled();
74-
ref_future_->Complete(SafeFutureHandle<void>(handle_), kErrorCancelled);
75-
NotifyCanceled();
77+
void BlockingResponse::MarkFailed() {
78+
rest::Response::MarkFailed();
79+
if (status() == rest::util::HttpRequestTimeout) {
80+
ref_future_->Complete(SafeFutureHandle<void>(handle_),
81+
kErrorRetryLimitExceeded);
82+
} else {
83+
ref_future_->Complete(SafeFutureHandle<void>(handle_), kErrorCancelled);
84+
}
85+
NotifyFailed();
7686
}
7787

78-
void BlockingResponse::NotifyComplete() {
79-
notifier_.NotifyComplete();
80-
}
88+
void BlockingResponse::NotifyComplete() { notifier_.NotifyComplete(); }
8189

82-
void BlockingResponse::NotifyCanceled() {
83-
notifier_.NotifyCanceled();
84-
}
90+
void BlockingResponse::NotifyFailed() { notifier_.NotifyFailed(); }
8591

8692
// Read in the raw response string, and try to make sense of it.
8793
bool StorageNetworkError::Parse(const char* json_txt) {

0 commit comments

Comments
 (0)