Program Listing for File fetch_native.hpp

Return to documentation for file (include/uitsl/fetch/fetch_native.hpp)

#pragma once
#ifndef UITSL_FETCH_FETCH_NATIVE_HPP_INCLUDE
#define UITSL_FETCH_FETCH_NATIVE_HPP_INCLUDE

#include <chrono>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <sstream>
#include <thread>

#include <curl/curl.h>

#include "../debug/err_verify.hpp"
#include "../debug/uitsl_always_assert.hpp"
#include "../nonce/ScopeGuard.hpp"
#include "../polyfill/filesystem.hpp"

#include "make_temp_filepath.hpp"
#include "resolve_fetched_filename.hpp"

namespace uitsl {

/*
 * Fetch data from a url to a temporary file, return temporary file path.
 * Requires -lcurl.
 * Adapted from https://stackoverflow.com/a/21573625.
 */
inline std::filesystem::path fetch_native( const std::string& url ) {

  const uitsl::ScopeGuard curl_global_guard(
    [](){ curl_global_init(CURL_GLOBAL_ALL); },
    [](){ curl_global_cleanup(); }
  );

  const std::filesystem::path bodypath{ uitsl::make_temp_filepath() };
  FILE *bodyfile;
  const uitsl::ScopeGuard bodyfile_guard(
    [&bodyfile, bodypath](){ bodyfile = fopen(bodypath.c_str(), "wb"); },
    [&bodyfile](){ fclose(bodyfile); }
  );

  const std::filesystem::path headerpath{ uitsl::make_temp_filepath() };
  FILE *headerfile;
  const uitsl::ScopeGuard headerfile_guard(
    [&](){ headerfile = fopen(headerpath.c_str(), "wb"); },
    [&](){ fclose(headerfile); }
  );

  CURL* curl_handle;
  const uitsl::ScopeGuard curl_handle_guard(
    [&curl_handle](){ curl_handle = curl_easy_init(); },
    [&curl_handle](){ curl_easy_cleanup( curl_handle ); }
  );

  uitsl_always_assert( curl_handle, "" );

  uitsl::err_verify(
    curl_easy_setopt( curl_handle, CURLOPT_URL, url.c_str())
  );
  uitsl::err_verify(
    curl_easy_setopt( curl_handle, CURLOPT_WRITEFUNCTION, nullptr)
  );
  uitsl::err_verify(
    curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, bodyfile)
  );
  uitsl::err_verify(
    curl_easy_setopt( curl_handle, CURLOPT_HEADERDATA, headerfile)
  );
  uitsl::err_verify(
    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L)
  );

  for ( size_t retry{}; true; ++retry ) {

    CURLcode err = curl_easy_perform(curl_handle);

    long http_code{};
    if ( err == CURLE_OK ) {
      curl_easy_getinfo( curl_handle, CURLINFO_RESPONSE_CODE, &http_code );
      if ( http_code == 200 ) break;
    }

    uitsl_always_assert( retry < 5, retry << err << http_code << url );

    using namespace std::chrono_literals;
    std::this_thread::sleep_for( 10s );
  }

  fflush( bodyfile );
  fflush( headerfile );

  return uitsl::resolve_fetched_filename( url, bodypath, headerpath );

}

} // namespace uitsl

#endif // #ifndef UITSL_FETCH_FETCH_NATIVE_HPP_INCLUDE