void HTTPRequest::startHTTPRequest(std::unique_ptr<Response> &&res) { assert(uv_thread_self() == thread_id); assert(!http_baton); http_baton = std::make_shared<HTTPRequestBaton>(path); http_baton->request = this; http_baton->async = new uv_async_t; http_baton->response = std::move(res); http_baton->async->data = new util::ptr<HTTPRequestBaton>(http_baton); #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 uv_async_init(loop, http_baton->async, [](uv_async_t *async, int) { #else uv_async_init(loop, http_baton->async, [](uv_async_t *async) { #endif util::ptr<HTTPRequestBaton> &baton = *(util::ptr<HTTPRequestBaton> *)async->data; if (baton->request) { HTTPRequest *request = baton->request; request->http_baton.reset(); baton->request = nullptr; request->handleHTTPResponse(baton->type, std::move(baton->response)); } delete (util::ptr<HTTPRequestBaton> *)async->data; uv_close((uv_handle_t *)async, [](uv_handle_t *handle) { uv_async_t *async_handle = (uv_async_t *)handle; delete async_handle; }); }); attempts++; HTTPRequestBaton::start(http_baton); } void HTTPRequest::handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&res) { assert(uv_thread_self() == thread_id); assert(!http_baton); assert(!response); switch (responseType) { // This error was caused by a temporary error and it is likely that it will be resolved // immediately. We are going to try again right away. This is like the TemporaryError, // except that we will not perform exponential back-off. case HTTPResponseType::SingularError: if (attempts >= 4) { // Report as error after 4 attempts. response = std::move(res); notify(); } else if (attempts >= 2) { // Switch to the back-off algorithm after the second failure. retryHTTPRequest(std::move(res), (1 << attempts) * 1000); return; } else { startHTTPRequest(std::move(res)); } break; // This error might be resolved by waiting some time (e.g. server issues). // We are going to do an exponential back-off and will try again in a few seconds. case HTTPResponseType::TemporaryError: if (attempts >= 4) { // Report error back after it failed completely. response = std::move(res); notify(); } else { retryHTTPRequest(std::move(res), (1 << attempts) * 1000); } break; // This error might be resolved once the network reachability status changes. // We are going to watch the network status for changes and will retry as soon as the // operating system notifies us of a network status change. case HTTPResponseType::ConnectionError: if (attempts >= 4) { // Report error back after it failed completely. response = std::move(res); notify(); } else { // By default, we will retry every 60 seconds. retryHTTPRequest(std::move(res), 60000); } break; // The request was canceled programatically. case HTTPResponseType::Canceled: response.reset(); notify(); break; // This error probably won't be resolved by retrying anytime soon. We are giving up. case HTTPResponseType::PermanentError: response = std::move(res); notify(); break; // The request returned data successfully. We retrieved and decoded the data successfully. case HTTPResponseType::Successful: if (store) { store->put(path, type, *res); } response = std::move(res); notify(); break; // The request confirmed that the data wasn't changed. We already have the data. case HTTPResponseType::NotModified: if (store) { store->updateExpiration(path, res->expires); } response = std::move(res); notify(); break; default: assert(!"Response wasn't set"); break; } }