I needed a clean, simple and robust piece of code to retrieve the date of publication of the latest release of a given GitHub Project.
My constraints were that it needed to be written in the C++11 subset supported by Visual Studio 2012, and that it would require as few dependencies as possible.
Here is what I came up with. The code uses the Windows Internet API to query the GitHub REST API, and RapidJSON to parse the JSON response.
Here are a few specific questions:
- Am I using the Windows Internet API correctly?
- Will my code work in the presence of proxies or other exotic settings?
- Is there a simpler way to parse the ISO date returned by GitHub and pretty-print it?
- Is my usage of
TCHAR
and_T()
correct? Or should I usewchar_t
andL
prefixes?
I would appreciate any other comment or suggestion on how to improve (or fix) this code.
#include "rapidjson/document.h"
#include <Windows.h>
#include <wininet.h>
#include <tchar.h>
#include <cassert>
#include <ctime>
#include <iostream>
#include <string>
DWORD query_release_information(std::string& response)
{
response.clear();
HINTERNET session =
InternetOpen(
_T("appleseed"),
INTERNET_OPEN_TYPE_DIRECT,
nullptr,
nullptr,
0);
if (session == nullptr)
return GetLastError();
HINTERNET connection =
InternetConnect(
session,
_T("api.github.com"),
INTERNET_DEFAULT_HTTPS_PORT,
nullptr,
nullptr,
INTERNET_SERVICE_HTTP,
0,
0);
if (connection == nullptr)
{
const DWORD result = GetLastError();
InternetCloseHandle(session);
return result;
}
static const TCHAR* AcceptTypes[] = { _T("application/json"), nullptr };
HINTERNET request =
HttpOpenRequest(
connection,
_T("GET"),
_T("/repos/appleseedhq/appleseed-max/releases"),
nullptr,
nullptr,
AcceptTypes,
INTERNET_FLAG_SECURE | INTERNET_FLAG_RELOAD,
0);
if (request == nullptr)
{
const DWORD result = GetLastError();
InternetCloseHandle(connection);
InternetCloseHandle(session);
return result;
}
if (!HttpSendRequest(request, nullptr, 0, nullptr, 0))
{
const DWORD result = GetLastError();
InternetCloseHandle(request);
InternetCloseHandle(connection);
InternetCloseHandle(session);
return result;
}
char buffer[4096];
DWORD bytes_read = -1;
BOOL success = TRUE;
while (success && bytes_read != 0)
{
success = InternetReadFile(request, buffer, sizeof(buffer), &bytes_read);
response.append(buffer, bytes_read);
}
const DWORD result = GetLastError();
InternetCloseHandle(request);
InternetCloseHandle(connection);
InternetCloseHandle(session);
return result;
}
int main()
{
std::string json;
const DWORD result = query_release_information(json);
rapidjson::Document doc;
doc.Parse(json.c_str());
assert(doc.IsArray());
const auto& release = doc[0];
const auto& tag_name = release["tag_name"];
const auto& published_date = release["published_at"];
const auto& asset = release["assets"][0];
const auto& asset_download_url = asset["browser_download_url"];
int year, month, day, hours, minutes, seconds;
sscanf(
published_date.GetString(),
"%d-%d-%dT%d:%d:%d:%dZ",
&year, &month, &day, &hours, &minutes, &seconds);
std::tm time;
time.tm_year = year - 1900;
time.tm_mon = month - 1;
time.tm_mday = day;
time.tm_hour = hours;
time.tm_min = minutes;
time.tm_sec = seconds;
const std::time_t t = std::mktime(&time);
const std::tm* local_time = std::localtime(&t);
char buffer[1024];
std::strftime(buffer, sizeof(buffer), "%b %d %Y", local_time);
std::cout << buffer << std::endl;
return 0;
}