From efcb1118be2d591dcc98534418c615aa98adcedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Isnard?= Date: Tue, 4 Jun 2024 11:05:26 +0200 Subject: [PATCH 1/2] feat: Return explicit NetworkError when intercepted by a middlebox We can't do things like look at the certificate in JS to know that the request was intercepted, but if we detect a non-JSON response, we know that it must come from an intermediary on the path (proxy, middlebox, cloud gateway, ...) In particular some prats are stuck behind ZScaler, and it sometimes block Tanker decrypt calls. Today this is accounted as a Tanker internal error and creates a Sentry alert for us. The new NetworkError message should allow support to identify the issue, and potentially help the client identity their network as the problem. --- modules/sdk-core/src/Network/HttpClient.cpp | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/modules/sdk-core/src/Network/HttpClient.cpp b/modules/sdk-core/src/Network/HttpClient.cpp index 480aa7a24..6b08f670e 100644 --- a/modules/sdk-core/src/Network/HttpClient.cpp +++ b/modules/sdk-core/src/Network/HttpClient.cpp @@ -68,17 +68,24 @@ AppdErrc getErrorFromCode(std::string_view code) return AppdErrc::UnknownError; } -HttpError handleErrorResponse(HttpResponse const& res, HttpRequest const& req) { +// We assume non-JSON responses come from a proxy or other middlebox +Exception newMiddleboxError(HttpResponse const& res, HttpRequest const& req) +{ + return Errors::formatEx(Errors::Errc::NetworkError, + "Request may have been intercepted by proxy, received non-JSON response for " + "{} {}, status: {}, body: {}", + httpMethodToString(req.method), + req.url, + res.statusCode, + res.body); +} + +HttpError handleErrorResponse(HttpResponse const& res, HttpRequest const& req) +{ auto contentType = res.headers.get(HttpHeader::CONTENT_TYPE); if (!(contentType && boost::algorithm::starts_with(*contentType, "application/json"))) - { - throw Errors::formatEx(Errors::AppdErrc::InternalError, - "{} {}, status: {}", - httpMethodToString(req.method), - req.url, - res.statusCode); - } + throw newMiddleboxError(res, req); try { @@ -90,7 +97,7 @@ HttpError handleErrorResponse(HttpResponse const& res, HttpRequest const& req) { } catch (nlohmann::json::exception const& ex) { - throw Errors::formatEx(Errors::AppdErrc::InternalError, "invalid {} http response format: {}", res.statusCode, res.body); + throw newMiddleboxError(res, req); } } @@ -110,7 +117,7 @@ HttpResult handleResponse(HttpResponse res, HttpRequest const& req) } catch (nlohmann::json::exception const& ex) { - throw Errors::formatEx(Errors::AppdErrc::InternalError, "invalid http response format: {}", res.body); + throw newMiddleboxError(res, req); } } } @@ -383,8 +390,7 @@ tc::cotask HttpClient::fetch(HttpRequest req) TC_RETURN(handleResponse(std::move(res), req)); } -tc::cotask HttpClient::asyncGetRedirectLocation(std::string_view target, - std::optional cookie) +tc::cotask HttpClient::asyncGetRedirectLocation(std::string_view target, std::optional cookie) { HttpRequest req; req.method = HttpMethod::Get; From cc1f392d11e0ac9a243e831226dc022a05964b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Isnard?= Date: Tue, 4 Jun 2024 11:51:53 +0200 Subject: [PATCH 2/2] fix: Add curl_version() as default User-Agent to mollify fly.io Right now Fly returns 402 Payment Required when User-Agent is blank --- modules/tcurl/src/curl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/tcurl/src/curl.cpp b/modules/tcurl/src/curl.cpp index b69b88cfa..4ccd49129 100644 --- a/modules/tcurl/src/curl.cpp +++ b/modules/tcurl/src/curl.cpp @@ -403,6 +403,8 @@ request::request() : _easy(curl_easy_init()) if (!_easy) throw std::runtime_error("curl_easy_init() failed"); + curl_easy_setopt(_easy.get(), CURLOPT_USERAGENT, curl_version()); + curl_easy_setopt(_easy.get(), CURLOPT_WRITEFUNCTION, &write_cb_c); curl_easy_setopt(_easy.get(), CURLOPT_WRITEDATA, this); // curl_easy_setopt(_easy.get(), CURLOPT_VERBOSE, 1L);