Skip to content

Commit

Permalink
📚 Update cpp-httplib, log error code on failed HTTP requests (#81)
Browse files Browse the repository at this point in the history
This change updates us to `v0.8.4` of [cpp-httplib](https://github.com/yhirose/cpp-httplib), which includes a decent number of fixes to bugs that I suspect may be causing some of our HTTPS request failures we're seeing in production.

As a result of some of the changes in the httplib update, a few changes were made to the ServiceConnection implementations to avoid copying of `httplib::Client` and `httplib::Result` instances (they are now backed by `unique_ptr`).

Additional logging detail was also added in the Glimesh HTTP request failure case so we can determine root cause if this doesn't resolve the failures.

Verified by testing against local glimesh.tv instance.
  • Loading branch information
haydenmc authored Feb 19, 2021
1 parent 526aca4 commit ad2ab78
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 53 deletions.
39 changes: 14 additions & 25 deletions src/ServiceConnections/GlimeshServiceConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "../Utilities/FtlTypes.h"

#include <fmt/core.h>
#include <jansson.h>
#include <string.h>
#include <spdlog/spdlog.h>
Expand All @@ -23,6 +24,7 @@ GlimeshServiceConnection::GlimeshServiceConnection(
bool useHttps,
std::string clientId,
std::string clientSecret) :
httpClient(fmt::format("{}://{}:{}", (useHttps ? "https" : "http"), hostname, port).c_str()),
hostname(hostname),
port(port),
useHttps(useHttps),
Expand Down Expand Up @@ -193,30 +195,11 @@ Result<void> GlimeshServiceConnection::SendJpegPreviewImage(
#pragma endregion

#pragma region Private methods
httplib::Client GlimeshServiceConnection::getHttpClient()
{
std::stringstream baseUri;
baseUri << (useHttps ? "https" : "http") << "://" << hostname << ":" << port;
httplib::Client client = httplib::Client(baseUri.str().c_str());

if (accessToken.length() > 0)
{
httplib::Headers headers
{
{"Authorization", "Bearer " + accessToken}
};
client.set_default_headers(headers);
}

return client;
}

void GlimeshServiceConnection::ensureAuth()
{
std::lock_guard<std::mutex> lock(authMutex);

// Do we already have an access token that hasn't expired?
// TODO: Check expiration
if (accessToken.length() > 0)
{
std::time_t currentTime = std::time(nullptr);
Expand All @@ -235,7 +218,6 @@ void GlimeshServiceConnection::ensureAuth()
{ "scope", "streamkey" }
};

httplib::Client httpClient = getHttpClient();
if (httplib::Result res = httpClient.Post("/api/oauth/token", params))
{
if (res->status == 200)
Expand Down Expand Up @@ -267,6 +249,13 @@ void GlimeshServiceConnection::ensureAuth()
std::time_t currentTime = std::time(nullptr);
spdlog::info("Received new access token: {}, expires in {} - {} = {} seconds",
accessToken, expirationTime, currentTime, (expirationTime - currentTime));

// Update HTTP client Authorization header
httplib::Headers headers
{
{"Authorization", "Bearer " + accessToken}
};
httpClient.set_default_headers(headers);
return;
}
}
Expand Down Expand Up @@ -311,7 +300,6 @@ JsonPtr GlimeshServiceConnection::runGraphQlQuery(
int numRetries = 0;
while (true)
{
httplib::Client httpClient = getHttpClient();
JsonPtr result = nullptr;

// If we're doing files, use a multipart http request
Expand All @@ -334,7 +322,7 @@ JsonPtr GlimeshServiceConnection::runGraphQlQuery(

if (numRetries < MAX_RETRIES)
{
spdlog::warn("Attempt {} / {}: Glimesh file upload GraphQL query failed. "
spdlog::warn("Attempt {} / {}: Glimesh GraphQL query failed. "
"Retrying in {} ms...", (numRetries + 1), MAX_RETRIES, TIME_BETWEEN_RETRIES_MS);

std::this_thread::sleep_for(std::chrono::milliseconds(TIME_BETWEEN_RETRIES_MS));
Expand All @@ -347,13 +335,13 @@ JsonPtr GlimeshServiceConnection::runGraphQlQuery(
}

// We've exceeded our retry limit
spdlog::error("Aborting Glimesh file upload GraphQL query after {} failed attempts.",
spdlog::error("Aborting Glimesh GraphQL query after {} failed attempts.",
MAX_RETRIES);

throw ServiceConnectionCommunicationFailedException("Glimesh GraphQL query failed.");
}

JsonPtr GlimeshServiceConnection::processGraphQlResponse(httplib::Result result)
JsonPtr GlimeshServiceConnection::processGraphQlResponse(const httplib::Result& result)
{
if (result)
{
Expand Down Expand Up @@ -382,7 +370,8 @@ JsonPtr GlimeshServiceConnection::processGraphQlResponse(httplib::Result result)
}
else
{
spdlog::warn("Glimesh service connection HTTP request failed.");
spdlog::warn("Glimesh service connection HTTP request failed with error {}",
result.error());
return nullptr;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/ServiceConnections/GlimeshServiceConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ class GlimeshServiceConnection :

private:
/* Private members */
const int MAX_RETRIES = 5;
const int MAX_RETRIES = 10;
const int TIME_BETWEEN_RETRIES_MS = 3000;
httplib::Client httpClient;
std::string hostname;
uint16_t port;
bool useHttps;
Expand All @@ -59,9 +60,8 @@ class GlimeshServiceConnection :
std::mutex authMutex;

/* Private methods */
httplib::Client getHttpClient();
void ensureAuth();
JsonPtr runGraphQlQuery(std::string query, JsonPtr variables = nullptr, httplib::MultipartFormDataItems fileData = httplib::MultipartFormDataItems());
JsonPtr processGraphQlResponse(httplib::Result result);
JsonPtr processGraphQlResponse(const httplib::Result& result);
tm parseIso8601DateTime(std::string dateTimeString);
};
33 changes: 12 additions & 21 deletions src/ServiceConnections/RestServiceConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,22 @@ RestServiceConnection::RestServiceConnection(
std::string pathBase,
std::string authToken)
:
httpClient(createBaseUri(false).c_str()),
hostname(hostname),
port(port),
useHttps(useHttps),
pathBase(pathBase),
authToken(authToken)
{ }
{
if (authToken.length() > 0)
{
httplib::Headers headers
{
{"Authorization", authToken}
};
httpClient.set_default_headers(headers);
}
}
#pragma endregion

#pragma region Public methods
Expand Down Expand Up @@ -203,30 +213,12 @@ std::string RestServiceConnection::constructPath(std::string path)
return ss.str();
}

httplib::Client RestServiceConnection::getHttpClient()
{
std::string baseUri = createBaseUri(false);
httplib::Client client = httplib::Client(baseUri.c_str());

if (authToken.length() > 0)
{
httplib::Headers headers
{
{"Authorization", authToken}
};
client.set_default_headers(headers);
}

return client;
}

httplib::Result RestServiceConnection::runGetRequest(std::string url)
{
// Make the request, and retry if necessary
int numRetries = 0;
while (true)
{
httplib::Client httpClient = getHttpClient();
std::string fullUrl = constructPath(url);

httplib::Result response = httpClient.Get(fullUrl.c_str());
Expand Down Expand Up @@ -277,7 +269,6 @@ httplib::Result RestServiceConnection::runPostRequest(
int numRetries = 0;
while (true)
{
httplib::Client httpClient = getHttpClient();
std::string fullUrl = constructPath(url);

httplib::Result response = (fileData.size() > 0)
Expand Down Expand Up @@ -312,7 +303,7 @@ httplib::Result RestServiceConnection::runPostRequest(
throw ServiceConnectionCommunicationFailedException("REST POST request failed.");
}

JsonPtr RestServiceConnection::decodeRestResponse(httplib::Result result)
JsonPtr RestServiceConnection::decodeRestResponse(const httplib::Result& result)
{
if (result)
{
Expand Down
7 changes: 4 additions & 3 deletions src/ServiceConnections/RestServiceConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class RestServiceConnection : public ServiceConnection
/* Private members */
const int MAX_RETRIES = 5;
const int TIME_BETWEEN_RETRIES_MS = 3000;
httplib::Client httpClient;
std::string hostname;
uint16_t port;
bool useHttps;
Expand All @@ -56,8 +57,8 @@ class RestServiceConnection : public ServiceConnection
std::string createBaseUri(bool includeBase);
std::string constructPath(std::string path);

httplib::Client getHttpClient();
httplib::Result runGetRequest(std::string url);
httplib::Result runPostRequest(std::string url, JsonPtr body = nullptr, httplib::MultipartFormDataItems fileData = httplib::MultipartFormDataItems());
JsonPtr decodeRestResponse(httplib::Result result);
httplib::Result runPostRequest(std::string url, JsonPtr body = nullptr,
httplib::MultipartFormDataItems fileData = httplib::MultipartFormDataItems());
JsonPtr decodeRestResponse(const httplib::Result& result);
};

0 comments on commit ad2ab78

Please sign in to comment.