#pragma once ///@file #include #include #include "logging.hh" #include "types.hh" #include "ref.hh" #include "config.hh" #include "serialise.hh" namespace nix { struct FileTransferSettings : Config { Setting enableHttp2{this, true, "http2", "Whether to enable HTTP/2 support."}; Setting userAgentSuffix{this, "", "user-agent-suffix", "String appended to the user agent in HTTP requests."}; Setting httpConnections{ this, 25, "http-connections", R"( The maximum number of parallel TCP connections used to fetch files from binary caches and by other downloads. It defaults to 25. 0 means no limit. )", {"binary-caches-parallel-connections"}}; Setting connectTimeout{ this, 0, "connect-timeout", R"( The timeout (in seconds) for establishing connections in the binary cache substituter. It corresponds to `curl`’s `--connect-timeout` option. A value of 0 means no limit. )"}; Setting stalledDownloadTimeout{ this, 300, "stalled-download-timeout", R"( The timeout (in seconds) for receiving data from servers during download. Nix cancels idle downloads after this timeout's duration. )"}; Setting tries{this, 5, "download-attempts", "How often Nix will attempt to download a file before giving up."}; }; extern FileTransferSettings fileTransferSettings; struct FileTransferRequest { std::string uri; Headers headers; std::string expectedETag; bool verifyTLS = true; bool head = false; size_t tries = fileTransferSettings.tries; unsigned int baseRetryTimeMs = 250; ActivityId parentAct; bool decompress = true; std::optional data; std::string mimeType; std::function dataCallback; FileTransferRequest(std::string_view uri) : uri(uri), parentAct(getCurActivity()) { } std::string verb() { return data ? "upload" : "download"; } }; struct FileTransferResult { /** * Whether this is a cache hit (i.e. the ETag supplied in the * request is still valid). If so, `data` is empty. */ bool cached = false; /** * The ETag of the object. */ std::string etag; /** * All URLs visited in the redirect chain. */ std::vector urls; /** * The response body. */ std::string data; uint64_t bodySize = 0; /** * An "immutable" URL for this resource (i.e. one whose contents * will never change), as returned by the `Link: ; * rel="immutable"` header. */ std::optional immutableUrl; }; class Store; struct FileTransfer { virtual ~FileTransfer() { } /** * Enqueue a data transfer request, returning a future to the result of * the download. The future may throw a FileTransferError * exception. */ virtual void enqueueFileTransfer(const FileTransferRequest & request, Callback callback) = 0; std::future enqueueFileTransfer(const FileTransferRequest & request); /** * Synchronously download a file. */ FileTransferResult download(const FileTransferRequest & request); /** * Synchronously upload a file. */ FileTransferResult upload(const FileTransferRequest & request); /** * Download a file, writing its data to a sink. The sink will be * invoked on the thread of the caller. */ void download( FileTransferRequest && request, Sink & sink, std::function resultCallback = {}); enum Error { NotFound, Forbidden, Misc, Transient, Interrupted }; }; /** * @return a shared FileTransfer object. * * Using this object is preferred because it enables connection reuse * and HTTP/2 multiplexing. */ ref getFileTransfer(); /** * @return a new FileTransfer object * * Prefer getFileTransfer() to this; see its docs for why. */ ref makeFileTransfer(); class FileTransferError : public Error { public: FileTransfer::Error error; /// intentionally optional std::optional response; template FileTransferError(FileTransfer::Error error, std::optional response, const Args & ... args); }; }