Arachne 1.0
Arachne - the perpetual stitcher of Wikidata entities.
Loading...
Searching...
No Matches
arachne::pheidippides::transport Class Referencefinal

#include <include/pheidippides/transport.hpp>

Public Member Functions

 transport (std::filesystem::path artifact_root)
const std::filesystem::path & artifact_root () const noexcept
acquired_artifact_v1 execute (const fetch_request_v1 &request) const
acquired_artifact_v1 execute (const nlohmann::json &request_contract) const

Private Attributes

std::filesystem::path artifact_root_

Detailed Description

Intentionally blind HTTP transport.

The transport never parses response content. It streams a final response to a staging file, hashes it while writing, and publishes it without replacing an existing artifact.

Definition at line 159 of file transport.hpp.

Constructor & Destructor Documentation

◆ transport()

arachne::pheidippides::transport::transport ( std::filesystem::path artifact_root)
explicit

Definition at line 1569 of file transport.cpp.

1569 {
1571 if (artifact_root.empty()) {
1572 throw std::invalid_argument("artifact root must not be empty");
1573 }
1574 std::error_code ec;
1575 std::filesystem::create_directories(artifact_root, ec);
1576 if (ec) {
1577 throw std::system_error(ec, "cannot create artifact root");
1578 }
1579 artifact_root_ = std::filesystem::canonical(artifact_root, ec);
1580 if (ec || !std::filesystem::is_directory(artifact_root_)) {
1581 throw std::system_error(
1582 ec ? ec : std::make_error_code(std::errc::not_a_directory),
1583 "artifact root is not a directory"
1584 );
1585 }
1586}
const std::filesystem::path & artifact_root() const noexcept
std::filesystem::path artifact_root_

References arachne::pheidippides::anonymous_namespace{transport.cpp}::initialize_curl().

Here is the call graph for this function:

Member Function Documentation

◆ artifact_root()

const std::filesystem::path & arachne::pheidippides::transport::artifact_root ( ) const
inlinenodiscardnoexcept

Definition at line 163 of file transport.hpp.

163 {
164 return artifact_root_;
165 }

◆ execute() [1/2]

acquired_artifact_v1 arachne::pheidippides::transport::execute ( const fetch_request_v1 & request) const
nodiscard

Definition at line 1588 of file transport.cpp.

1588 {
1589 acquired_artifact_v1 result;
1590 result.artifact_id = "artifact-invalid";
1591 result.request_id = request.request_id;
1592 result.door_id = request.door_id;
1593 result.operation = request.operation;
1594 result.delivered_via
1595 = request.operation == transport_operation::resume_download
1598 result.source_url = request.url;
1599 result.started_at = std::chrono::system_clock::now();
1600
1601 if (request.contract != "fetch_request_v1"
1602 || request.format_version != 1U) {
1603 return fail_result(
1604 std::move(result), transport_status::invalid_request,
1605 "unsupported fetch request format version"
1606 );
1607 }
1608 if (!valid_request_id(request.request_id)) {
1609 return fail_result(
1610 std::move(result), transport_status::invalid_request,
1611 "request_id is not a supported stable identifier"
1612 );
1613 }
1614 result.artifact_id = artifact_id_for(request.request_id);
1615 if (request.timeout.count() <= 0 || request.connect_timeout.count() <= 0
1616 || request.read_timeout.count() <= 0
1617 || request.write_timeout.count() <= 0 || request.max_bytes == 0U) {
1618 return fail_result(
1619 std::move(result), transport_status::invalid_request,
1620 "timeouts and max_bytes must be positive"
1621 );
1622 }
1623 if (request.initial_retry_delay.count() < 0
1624 || request.maximum_retry_delay.count() < 0
1625 || request.total_retry_delay_budget.count() < 0
1626 || request.initial_retry_delay > request.maximum_retry_delay) {
1627 return fail_result(
1628 std::move(result), transport_status::invalid_request,
1629 "retry delays are negative or internally inconsistent"
1630 );
1631 }
1632 if (request.maximum_attempts == 0U
1633 || request.maximum_attempts > maximum_transport_attempts) {
1634 return fail_result(
1635 std::move(result), transport_status::invalid_request,
1636 "maximum_attempts must be between one and twenty"
1637 );
1638 }
1639 if (request.redirects.maximum_redirects > maximum_redirects
1640 || (!request.redirects.follow
1641 && request.redirects.maximum_redirects != 0U)) {
1642 return fail_result(
1643 std::move(result), transport_status::invalid_request,
1644 "redirect limit is inconsistent or exceeds the safety maximum"
1645 );
1646 }
1647 if (request.method == http_method::get
1648 && (!request.body.empty() || request.body_artifact)) {
1649 return fail_result(
1650 std::move(result), transport_status::invalid_request,
1651 "GET requests cannot contain a body"
1652 );
1653 }
1654 if (!request.body.empty() && request.body_artifact) {
1655 return fail_result(
1656 std::move(result), transport_status::invalid_request,
1657 "request cannot contain both inline and artifact body bytes"
1658 );
1659 }
1660 if ((request.operation == transport_operation::resume_download)
1661 != request.resume_artifact.has_value()) {
1662 return fail_result(
1663 std::move(result), transport_status::invalid_request,
1664 "resume_download requires exactly one resume_artifact"
1665 );
1666 }
1667 if (request.resume_artifact
1668 && (request.method != http_method::get
1669 || request.resume_artifact->byte_length == 0U
1670 || request.resume_artifact->byte_length >= request.max_bytes
1671 || request.resume_artifact->storage_ref
1672 == request.target_artifact_ref)) {
1673 return fail_result(
1674 std::move(result), transport_status::invalid_request,
1675 "resume artifact, method, target, or size is inconsistent"
1676 );
1677 }
1678 if (request.body.size()
1679 > static_cast<std::size_t>(std::numeric_limits<curl_off_t>::max())) {
1680 return fail_result(
1681 std::move(result), transport_status::invalid_request,
1682 "inline request body is too large for this transport build"
1683 );
1684 }
1685 if (!std::ranges::all_of(request.headers, valid_header)) {
1686 return fail_result(
1687 std::move(result), transport_status::invalid_request,
1688 "request contains an invalid HTTP header"
1689 );
1690 }
1691 if (!crypto::is_safe_relative_artifact_ref(request.target_artifact_ref)) {
1692 return fail_result(
1693 std::move(result), transport_status::unsafe_artifact_ref,
1694 "target artifact reference is not a safe relative path"
1695 );
1696 }
1697
1698 std::string validation_error;
1699 auto current = parse_url(request.url, validation_error);
1700 if (!current) {
1701 return fail_result(
1702 std::move(result), transport_status::invalid_request,
1703 std::move(validation_error)
1704 );
1705 }
1706 if (!host_allowed(
1707 *current, request.redirects.allowed_hosts, validation_error
1708 )) {
1709 return fail_result(
1710 std::move(result), transport_status::disallowed_host,
1711 std::move(validation_error)
1712 );
1713 }
1714
1715 std::optional<verified_body_file> body_file;
1716 if (request.body_artifact) {
1718 body_file = verified_body_file::open_and_verify(
1719 artifact_root_, *request.body_artifact, body_status,
1720 validation_error
1721 );
1722 if (!body_file) {
1723 return fail_result(
1724 std::move(result), body_status, std::move(validation_error)
1725 );
1726 }
1727 }
1728 std::optional<verified_body_file> resume_file;
1729 if (request.resume_artifact) {
1731 resume_file = verified_body_file::open_and_verify(
1732 artifact_root_, *request.resume_artifact, resume_status,
1733 validation_error
1734 );
1735 if (!resume_file) {
1736 return fail_result(
1737 std::move(result), resume_status, std::move(validation_error)
1738 );
1739 }
1740 }
1741
1742 const std::filesystem::path target = crypto::safe_artifact_path(
1743 artifact_root_, request.target_artifact_ref
1744 );
1745 if (!ensure_safe_parent(artifact_root_, target, validation_error)) {
1746 return fail_result(
1747 std::move(result), transport_status::storage_error,
1748 std::move(validation_error)
1749 );
1750 }
1751 std::error_code ec;
1752 const auto target_state = std::filesystem::symlink_status(target, ec);
1753 if ((!ec && std::filesystem::exists(target_state))
1754 || ec == std::errc::too_many_symbolic_link_levels) {
1755 return fail_result(
1756 std::move(result), transport_status::artifact_exists,
1757 "target artifact already exists"
1758 );
1759 }
1760 if (ec && ec != std::errc::no_such_file_or_directory) {
1761 return fail_result(
1762 std::move(result), transport_status::storage_error,
1763 "cannot inspect target artifact: " + ec.message()
1764 );
1765 }
1766
1767 std::optional<staging_file> staging;
1768 try {
1769 staging.emplace(target.parent_path());
1770 } catch (const std::exception& exception) {
1771 return fail_result(
1772 std::move(result), transport_status::storage_error, exception.what()
1773 );
1774 }
1775
1776 easy_handle handle(curl_easy_init());
1777 if (!handle) {
1778 return fail_result(
1779 std::move(result), transport_status::network_error,
1780 "curl_easy_init failed"
1781 );
1782 }
1783
1784 header_list request_headers;
1785 try {
1786 request_headers = build_request_headers(request.headers);
1787 } catch (const std::exception& exception) {
1788 return fail_result(
1789 std::move(result), transport_status::invalid_request,
1790 exception.what()
1791 );
1792 }
1793
1794 receive_context received(staging->descriptor(), request.max_bytes);
1795 progress_context progress {
1796 .read_timeout = request.read_timeout,
1797 .write_timeout = request.write_timeout,
1798 .upload_expected = request.method == http_method::post
1799 && (!request.body.empty()
1800 || (body_file && body_file->length() != 0U)),
1801 };
1802 progress.reset();
1803 std::array<char, CURL_ERROR_SIZE> curl_error {};
1804 curl_easy_setopt(handle.get(), CURLOPT_ERRORBUFFER, curl_error.data());
1805 curl_easy_setopt(handle.get(), CURLOPT_NOSIGNAL, 1L);
1806 curl_easy_setopt(handle.get(), CURLOPT_SHARE, curl_connection_share);
1807 curl_easy_setopt(handle.get(), CURLOPT_MAXCONNECTS, 64L);
1808 curl_easy_setopt(handle.get(), CURLOPT_FOLLOWLOCATION, 0L);
1809 curl_easy_setopt(handle.get(), CURLOPT_NOPROGRESS, 0L);
1810 curl_easy_setopt(
1811 handle.get(), CURLOPT_XFERINFOFUNCTION, transfer_progress_callback
1812 );
1813 curl_easy_setopt(handle.get(), CURLOPT_XFERINFODATA, &progress);
1814 curl_easy_setopt(handle.get(), CURLOPT_PROTOCOLS_STR, "http,https");
1815 curl_easy_setopt(handle.get(), CURLOPT_REDIR_PROTOCOLS_STR, "http,https");
1816 curl_easy_setopt(handle.get(), CURLOPT_HTTP_CONTENT_DECODING, 0L);
1817 curl_easy_setopt(handle.get(), CURLOPT_FAILONERROR, 0L);
1818 curl_easy_setopt(
1819 handle.get(), CURLOPT_CONNECTTIMEOUT_MS,
1820 static_cast<long>(std::min<std::int64_t>(
1821 request.connect_timeout.count(), std::numeric_limits<long>::max()
1822 ))
1823 );
1824 curl_easy_setopt(handle.get(), CURLOPT_WRITEFUNCTION, body_callback);
1825 curl_easy_setopt(handle.get(), CURLOPT_WRITEDATA, &received);
1826 curl_easy_setopt(
1827 handle.get(), CURLOPT_HEADERFUNCTION, response_header_callback
1828 );
1829 curl_easy_setopt(handle.get(), CURLOPT_HEADERDATA, &received);
1830 curl_easy_setopt(
1831 handle.get(), CURLOPT_MAXFILESIZE_LARGE,
1832 static_cast<curl_off_t>(std::min<std::uint64_t>(
1833 request.max_bytes,
1834 static_cast<std::uint64_t>(std::numeric_limits<curl_off_t>::max())
1835 ))
1836 );
1837 if (request_headers) {
1838 curl_easy_setopt(
1839 handle.get(), CURLOPT_HTTPHEADER, request_headers.get()
1840 );
1841 }
1842 if (request.method == http_method::post) {
1843 curl_easy_setopt(handle.get(), CURLOPT_POST, 1L);
1844 if (body_file) {
1845 curl_easy_setopt(
1846 handle.get(), CURLOPT_READFUNCTION, body_read_callback
1847 );
1848 curl_easy_setopt(handle.get(), CURLOPT_READDATA, &*body_file);
1849 curl_easy_setopt(
1850 handle.get(), CURLOPT_SEEKFUNCTION, body_seek_callback
1851 );
1852 curl_easy_setopt(handle.get(), CURLOPT_SEEKDATA, &*body_file);
1853 curl_easy_setopt(
1854 handle.get(), CURLOPT_POSTFIELDSIZE_LARGE,
1855 static_cast<curl_off_t>(body_file->length())
1856 );
1857 } else {
1858 curl_easy_setopt(
1859 handle.get(), CURLOPT_POSTFIELDS, request.body.data()
1860 );
1861 curl_easy_setopt(
1862 handle.get(), CURLOPT_POSTFIELDSIZE_LARGE,
1863 static_cast<curl_off_t>(request.body.size())
1864 );
1865 }
1866 } else {
1867 curl_easy_setopt(handle.get(), CURLOPT_HTTPGET, 1L);
1868 }
1869 if (resume_file) {
1870 curl_easy_setopt(
1871 handle.get(), CURLOPT_RESUME_FROM_LARGE,
1872 static_cast<curl_off_t>(resume_file->length())
1873 );
1874 }
1875
1876 const auto deadline = std::chrono::steady_clock::now() + request.timeout;
1877 const parsed_url initial_url = *current;
1878 bool transfer_completed = false;
1879 std::chrono::milliseconds retry_delay_spent { 0 };
1880 auto prepare_staging = [&]() -> bool {
1881 if (!staging->reset(validation_error)) {
1882 return false;
1883 }
1884 received.reset();
1885 if (!resume_file) {
1886 return true;
1887 }
1888 if (!resume_file->rewind(validation_error)) {
1889 return false;
1890 }
1891 std::array<char, 64U * 1024U> buffer {};
1892 for (;;) {
1893 const std::size_t count
1894 = resume_file->read(buffer.data(), buffer.size());
1895 if (resume_file->read_failed()) {
1896 validation_error = resume_file->read_error();
1897 return false;
1898 }
1899 if (count == 0U) {
1900 break;
1901 }
1902 if (count > received.max_bytes
1903 || received.byte_count > received.max_bytes - count
1904 || !write_all(
1905 staging->descriptor(), buffer.data(), count,
1906 validation_error
1907 )) {
1908 if (validation_error.empty()) {
1909 validation_error = "resume artifact exceeds max_bytes";
1910 }
1911 return false;
1912 }
1913 received.hasher.update(
1914 std::as_bytes(std::span(buffer.data(), count))
1915 );
1916 received.byte_count += static_cast<std::uint64_t>(count);
1917 }
1918 return true;
1919 };
1920 for (std::size_t attempt = 1;
1921 attempt <= request.maximum_attempts && !transfer_completed;
1922 ++attempt) {
1923 result.attempts = attempt;
1924 current = initial_url;
1925 result.redirect_chain.clear();
1926 std::size_t redirect_count = 0;
1927 if (!prepare_staging()) {
1928 return fail_result(
1929 std::move(result), transport_status::storage_error,
1930 std::move(validation_error)
1931 );
1932 }
1933
1934 for (;;) {
1935 const long remaining = timeout_milliseconds(deadline);
1936 if (remaining <= 0) {
1937 return fail_result(
1938 std::move(result), transport_status::timed_out,
1939 "transport deadline expired"
1940 );
1941 }
1942 curl_easy_setopt(handle.get(), CURLOPT_TIMEOUT_MS, remaining);
1943 if (body_file) {
1944 std::string rewind_error;
1945 if (!body_file->rewind(rewind_error)) {
1946 return fail_result(
1947 std::move(result), transport_status::storage_error,
1948 std::move(rewind_error)
1949 );
1950 }
1951 }
1952 curl_easy_setopt(
1953 handle.get(), CURLOPT_URL, current->normalized_url.c_str()
1954 );
1955 progress.reset();
1956 curl_error.fill('\0');
1957 const CURLcode curl_status = curl_easy_perform(handle.get());
1958
1959 char* effective_raw = nullptr;
1960 curl_easy_getinfo(
1961 handle.get(), CURLINFO_EFFECTIVE_URL, &effective_raw
1962 );
1963 result.effective_url = effective_raw == nullptr
1964 ? current->normalized_url
1965 : std::string(effective_raw);
1966 curl_easy_getinfo(
1967 handle.get(), CURLINFO_RESPONSE_CODE, &result.http_status
1968 );
1969 result.response_headers = received.headers;
1970 result.byte_count = received.byte_count;
1971
1972 if (received.too_large || curl_status == CURLE_FILESIZE_EXCEEDED) {
1973 return fail_result(
1974 std::move(result), transport_status::response_too_large,
1975 "response exceeded max_bytes"
1976 );
1977 }
1978 if (received.headers_too_large) {
1979 return fail_result(
1980 std::move(result), transport_status::response_too_large,
1981 "response headers exceeded the safety limit"
1982 );
1983 }
1984 if (received.write_failed) {
1985 return fail_result(
1986 std::move(result), transport_status::storage_error,
1987 std::move(received.write_error)
1988 );
1989 }
1990 if (body_file && body_file->read_failed()) {
1991 return fail_result(
1992 std::move(result), transport_status::storage_error,
1993 body_file->read_error()
1994 );
1995 }
1996 if (curl_status != CURLE_OK) {
1997 const transport_status status
1998 = curl_status == CURLE_OPERATION_TIMEDOUT
1999 || progress.read_timed_out || progress.write_timed_out
2002 const std::string message = progress.read_timed_out
2003 ? "transport read-progress timeout expired"
2004 : progress.write_timed_out
2005 ? "transport write-progress timeout expired"
2006 : curl_error.front() == '\0'
2007 ? curl_easy_strerror(curl_status)
2008 : curl_error.data();
2009 const bool may_retry = is_retryable_curl_status(curl_status)
2010 && attempt < request.maximum_attempts
2011 && timeout_milliseconds(deadline) > 0;
2012 if (may_retry) {
2013 const auto delay = retry_delay(request, attempt, {});
2014 if (retry_delay_spent + delay
2015 > request.total_retry_delay_budget) {
2016 return fail_result(
2017 std::move(result),
2019 "network retry delay budget was exhausted"
2020 );
2021 }
2022 if (delay.count() >= timeout_milliseconds(deadline)) {
2023 return fail_result(
2024 std::move(result), transport_status::timed_out,
2025 "transport deadline would expire during retry delay"
2026 );
2027 }
2028 retry_delay_spent += delay;
2029 std::this_thread::sleep_for(delay);
2030 break;
2031 }
2032 return fail_result(std::move(result), status, message);
2033 }
2034
2035 if (is_redirect_status(result.http_status)
2036 && !received.location.empty()) {
2037 if (!request.redirects.follow) {
2038 return fail_result(
2039 std::move(result), transport_status::redirect_rejected,
2040 "response attempted a redirect while redirects are "
2041 "disabled"
2042 );
2043 }
2044 if (redirect_count >= request.redirects.maximum_redirects) {
2045 return fail_result(
2046 std::move(result),
2048 "response exceeded the configured redirect limit"
2049 );
2050 }
2051
2052 std::string redirect_error;
2053 auto redirected_url = resolve_redirect(
2054 result.effective_url, received.location, redirect_error
2055 );
2056 auto redirected = redirected_url
2057 ? parse_url(*redirected_url, redirect_error)
2058 : std::nullopt;
2059 if (!redirected) {
2060 return fail_result(
2061 std::move(result), transport_status::redirect_rejected,
2062 std::move(redirect_error)
2063 );
2064 }
2065 if (current->scheme == "https" && redirected->scheme == "http"
2066 && !request.redirects.allow_https_to_http) {
2067 return fail_result(
2068 std::move(result), transport_status::redirect_rejected,
2069 "HTTPS-to-HTTP redirect is prohibited"
2070 );
2071 }
2072 if (!host_allowed(
2073 *redirected, request.redirects.allowed_hosts,
2074 redirect_error
2075 )) {
2076 return fail_result(
2077 std::move(result), transport_status::disallowed_host,
2078 std::move(redirect_error)
2079 );
2080 }
2081 result.redirect_chain.emplace_back(redirected->normalized_url);
2082 current = std::move(redirected);
2083 ++redirect_count;
2084 if (!prepare_staging()) {
2085 return fail_result(
2086 std::move(result), transport_status::storage_error,
2087 std::move(validation_error)
2088 );
2089 }
2090 continue;
2091 }
2092 if (resume_file && result.http_status != 206) {
2093 return fail_result(
2094 std::move(result), transport_status::network_error,
2095 "provider did not honor the resumable byte-range request"
2096 );
2097 }
2098 result.retry_after = retry_after_delay(received.headers);
2099 if (is_retryable_status(result.http_status)
2100 && attempt < request.maximum_attempts) {
2101 const auto delay
2102 = retry_delay(request, attempt, received.headers);
2103 if (retry_delay_spent + delay
2104 > request.total_retry_delay_budget) {
2105 return fail_result(
2106 std::move(result),
2108 "HTTP retry delay budget was exhausted"
2109 );
2110 }
2111 if (delay.count() >= timeout_milliseconds(deadline)) {
2112 return fail_result(
2113 std::move(result), transport_status::timed_out,
2114 "transport deadline would expire during retry delay"
2115 );
2116 }
2117 retry_delay_spent += delay;
2118 std::this_thread::sleep_for(delay);
2119 break;
2120 }
2121 transfer_completed = true;
2122 break;
2123 }
2124 }
2125
2126 std::string synchronize_error;
2127 if (!staging->synchronize(synchronize_error)) {
2128 return fail_result(
2129 std::move(result), transport_status::storage_error,
2130 std::move(synchronize_error)
2131 );
2132 }
2133 const std::string digest = received.hasher.finish_hex();
2134 if (request.expected_sha256 && digest != *request.expected_sha256) {
2135 return fail_result(
2136 std::move(result), transport_status::checksum_mismatch,
2137 "delivered bytes do not match expected SHA-256"
2138 );
2139 }
2140 staging->close();
2141
2142 ec.clear();
2143 std::filesystem::create_hard_link(staging->path(), target, ec);
2144 if (ec) {
2145 const transport_status status = ec == std::errc::file_exists
2148 return fail_result(
2149 std::move(result), status,
2150 "cannot atomically publish artifact: " + ec.message()
2151 );
2152 }
2153 ec.clear();
2154 std::filesystem::remove(staging->path(), ec);
2155 if (!ec) {
2156 staging->forget();
2157 }
2158
2159 result.status = transport_status::delivered;
2160 result.artifact_ref = request.target_artifact_ref;
2161 result.sha256 = digest;
2162 result.byte_count = received.byte_count;
2163 result.error_message.clear();
2164 result.completed_at = std::chrono::system_clock::now();
2165 return result;
2166}
bool is_safe_relative_artifact_ref(std::string_view artifact_ref) noexcept
Definition crypto.cpp:249
std::filesystem::path safe_artifact_path(const std::filesystem::path &root, std::string_view artifact_ref)
Definition crypto.cpp:302
acquired_artifact_v1 fail_result(acquired_artifact_v1 result, const transport_status status, std::string message)
header_list build_request_headers(const std::vector< http_header > &headers)
std::string artifact_id_for(const std::string_view request_id)
bool valid_request_id(const std::string_view request_id)
std::unique_ptr< curl_slist, curl_slist_deleter > header_list
Definition transport.cpp:79
bool is_retryable_curl_status(const CURLcode status) noexcept
bool write_all(const int descriptor, const char *data, std::size_t size, std::string &error) noexcept
std::optional< std::chrono::milliseconds > retry_after_delay(const std::vector< http_header > &headers)
std::optional< std::string > resolve_redirect(const std::string &base, const std::string &location, std::string &error)
std::chrono::milliseconds retry_delay(const fetch_request_v1 &request, const std::size_t attempt, const std::vector< http_header > &headers)
bool ensure_safe_parent(const std::filesystem::path &root, const std::filesystem::path &target, std::string &error)
std::unique_ptr< CURL, curl_easy_deleter > easy_handle
Definition transport.cpp:77
std::optional< parsed_url > parse_url(const std::string &value, std::string &error)
long timeout_milliseconds(const std::chrono::steady_clock::time_point deadline) noexcept
bool host_allowed(const parsed_url &url, const std::vector< std::string > &allowed_hosts, std::string &error)

References arachne::pheidippides::artifact_exists, arachne::pheidippides::acquired_artifact_v1::artifact_id, arachne::pheidippides::acquired_artifact_v1::artifact_ref, arachne::pheidippides::acquired_artifact_v1::attempts, arachne::pheidippides::fetch_request_v1::body, arachne::pheidippides::anonymous_namespace{transport.cpp}::body_callback(), arachne::pheidippides::anonymous_namespace{transport.cpp}::body_read_callback(), arachne::pheidippides::anonymous_namespace{transport.cpp}::body_seek_callback(), arachne::pheidippides::acquired_artifact_v1::byte_count, arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::byte_count, arachne::pheidippides::checksum_mismatch, arachne::pheidippides::anonymous_namespace{transport.cpp}::curl_connection_share, arachne::pheidippides::delivered, arachne::pheidippides::acquired_artifact_v1::delivered_via, arachne::pheidippides::disallowed_host, arachne::pheidippides::acquired_artifact_v1::door_id, arachne::pheidippides::fetch_request_v1::door_id, arachne::pheidippides::acquired_artifact_v1::effective_url, arachne::pheidippides::acquired_artifact_v1::error_message, arachne::pheidippides::fetched, arachne::pheidippides::fetch_request_v1::format_version, arachne::pheidippides::get, arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::headers_too_large, arachne::pheidippides::acquired_artifact_v1::http_status, arachne::pheidippides::invalid_request, arachne::pheidippides::anonymous_namespace{transport.cpp}::is_redirect_status(), arachne::pheidippides::anonymous_namespace{transport.cpp}::is_retryable_curl_status(), arachne::pheidippides::anonymous_namespace{transport.cpp}::is_retryable_status(), arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::location, arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::max_bytes, arachne::pheidippides::fetch_request_v1::max_bytes, arachne::pheidippides::fetch_request_v1::maximum_attempts, arachne::pheidippides::anonymous_namespace{transport.cpp}::maximum_redirects, arachne::pheidippides::anonymous_namespace{transport.cpp}::maximum_transport_attempts, arachne::pheidippides::fetch_request_v1::method, arachne::pheidippides::network_error, arachne::pheidippides::acquired_artifact_v1::operation, arachne::pheidippides::fetch_request_v1::operation, arachne::pheidippides::post, arachne::pheidippides::anonymous_namespace{transport.cpp}::progress_context::read_timed_out, arachne::pheidippides::redirect_limit_exceeded, arachne::pheidippides::redirect_rejected, arachne::pheidippides::acquired_artifact_v1::request_id, arachne::pheidippides::fetch_request_v1::request_id, arachne::pheidippides::anonymous_namespace{transport.cpp}::progress_context::reset(), arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::reset(), arachne::pheidippides::anonymous_namespace{transport.cpp}::response_header_callback(), arachne::pheidippides::response_too_large, arachne::pheidippides::resume_download, arachne::pheidippides::resumed, arachne::pheidippides::retry_budget_exhausted, arachne::pheidippides::acquired_artifact_v1::sha256, arachne::pheidippides::acquired_artifact_v1::source_url, arachne::pheidippides::acquired_artifact_v1::status, arachne::pheidippides::storage_error, arachne::pheidippides::fetch_request_v1::target_artifact_ref, arachne::pheidippides::timed_out, arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::too_large, arachne::pheidippides::anonymous_namespace{transport.cpp}::transfer_progress_callback(), arachne::pheidippides::unsafe_artifact_ref, arachne::pheidippides::fetch_request_v1::url, arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::write_error, arachne::pheidippides::anonymous_namespace{transport.cpp}::receive_context::write_failed, and arachne::pheidippides::anonymous_namespace{transport.cpp}::progress_context::write_timed_out.

Here is the call graph for this function:

◆ execute() [2/2]

acquired_artifact_v1 arachne::pheidippides::transport::execute ( const nlohmann::json & request_contract) const
nodiscard

Validate a wire contract through the shared contract validator first.

Definition at line 2169 of file transport.cpp.

2169 {
2170 return execute(from_contract(request_contract));
2171}
acquired_artifact_v1 execute(const fetch_request_v1 &request) const
fetch_request_v1 from_contract(const nlohmann::json &document)

Member Data Documentation

◆ artifact_root_

std::filesystem::path arachne::pheidippides::transport::artifact_root_
private

Definition at line 175 of file transport.hpp.


The documentation for this class was generated from the following files: