aboutsummaryrefslogtreecommitdiff
path: root/libbutl/curl.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbutl/curl.hxx')
-rw-r--r--libbutl/curl.hxx255
1 files changed, 255 insertions, 0 deletions
diff --git a/libbutl/curl.hxx b/libbutl/curl.hxx
new file mode 100644
index 0000000..ea91807
--- /dev/null
+++ b/libbutl/curl.hxx
@@ -0,0 +1,255 @@
+// file : libbutl/curl.hxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#pragma once
+
+#include <string>
+#include <cstdint> // uint16_t
+#include <type_traits>
+
+#include <libbutl/path.hxx>
+#include <libbutl/process.hxx>
+#include <libbutl/fdstream.hxx>
+#include <libbutl/small-vector.hxx>
+
+#include <libbutl/export.hxx>
+
+namespace butl
+{
+ // Perform a method (GET, POST, PUT) on a URL using the curl(1) program.
+ // Throw process_error and io_error (both derive from system_error) in case
+ // of errors.
+ //
+ // The I (in) and O (out) can be of the following types/values:
+ //
+ // nullfd Signal that no input/output is expected.
+ //
+ // path Read input/write output from/to a file. If the special "-"
+ // value is used, then instead input is connected to the curl::out
+ // ofdstream member and output -- to the curl::in ifdstream member.
+ // Note that the argument type should be path, not string (i.e.,
+ // pass path("-")).
+ //
+ // other Forwarded as is to process_start(). Normally either int or
+ // auto_fd.
+ //
+ // For example:
+ //
+ // curl (nullfd, // No input expected for GET.
+ // path ("-"), // Write response to curl::in.
+ // 2,
+ // curl::get,
+ // "http://example.org");
+ //
+ // curl (path ("-"), // Read request from curl::out.
+ // path::temp_path (), // Write result to a file.
+ // 2,
+ // curl::post,
+ // "http://example.org");
+ //
+ // curl (nullfd,
+ // fdopen_null (), // Write result to /dev/null.
+ // 2,
+ // curl::get,
+ // "tftp://localhost/foo");
+ //
+ // Typical usage:
+ //
+ // try
+ // {
+ // curl c (nullfd, // No input expected.
+ // path ("-"), // Output to curl::in.
+ // 2, // Diagnostics to stderr.
+ // curl::get, // GET method.
+ // "https://example.org",
+ // "-A", "foobot/1.2.3"); // Additional curl(1) options.
+ //
+ // for (string s; getline (c.in, s); )
+ // cout << s << endl;
+ //
+ // c.in.close ();
+ //
+ // if (!c.wait ())
+ // ... // curl returned non-zero status.
+ // }
+ // catch (const std::system_error& e)
+ // {
+ // cerr << "curl error: " << e << endl;
+ // }
+ //
+ // Notes:
+ //
+ // 1. If opened, in/out streams are in the binary mode.
+ //
+ // 2. If opened, in/out must be explicitly closed before calling wait().
+ //
+ // 3. Only binary data HTTP POST is currently supported (the --data-binary
+ // curl option).
+ //
+ class LIBBUTL_SYMEXPORT curl: public process
+ {
+ public:
+ enum method_type {get, put, post};
+
+ // By default the -sS and, for the HTTP protocol, --fail and --location
+ // options are passed to curl on the command line. Optionally, these
+ // options can be suppressed.
+ //
+ enum class flags: std::uint16_t
+ {
+ no_fail = 0x01, // Don't pass --fail.
+ no_location = 0x02, // Don't pass --location
+ no_sS = 0x04, // Don't pass -sS
+
+ none = 0 // Default options set.
+ };
+
+ ifdstream in;
+ ofdstream out;
+
+ template <typename I,
+ typename O,
+ typename E,
+ typename... A>
+ curl (I&& in,
+ O&& out,
+ E&& err,
+ method_type,
+ const std::string& url,
+ A&&... options);
+
+ // Version with the command line callback (see process_run_callback() for
+ // details).
+ //
+ template <typename C,
+ typename I,
+ typename O,
+ typename E,
+ typename... A>
+ curl (const C&,
+ I&& in,
+ O&& out,
+ E&& err,
+ method_type,
+ const std::string& url,
+ A&&... options);
+
+ // Similar to the above, but allows to adjust the curl's default command
+ // line.
+ //
+ template <typename I,
+ typename O,
+ typename E,
+ typename... A>
+ curl (I&& in,
+ O&& out,
+ E&& err,
+ method_type,
+ flags,
+ const std::string& url,
+ A&&... options);
+
+ template <typename C,
+ typename I,
+ typename O,
+ typename E,
+ typename... A>
+ curl (const C&,
+ I&& in,
+ O&& out,
+ E&& err,
+ method_type,
+ flags,
+ const std::string& url,
+ A&&... options);
+
+ // Read the HTTP response status from an input stream.
+ //
+ // Specifically, read and parse the HTTP status line, by default skip over
+ // the remaining headers (leaving the stream at the beginning of the
+ // response body), and return the status code and the reason phrase. Throw
+ // std::invalid_argument if the status line could not be parsed. Pass
+ // through the ios::failure exception on the stream error.
+ //
+ // Note that if ios::failure is thrown the stream's exception mask may not
+ // be preserved.
+ //
+ struct http_status
+ {
+ std::uint16_t code;
+ std::string reason;
+ };
+
+ static http_status
+ read_http_status (ifdstream&, bool skip_headers = true);
+
+ // Parse and return the HTTP status code. Return 0 if the argument is
+ // invalid.
+ //
+ static std::uint16_t
+ parse_http_status_code (const std::string&);
+
+ // Read the CRLF-terminated line from an input stream, stripping the
+ // trailing CRLF. Pass through the ios::failure exception on the stream
+ // error.
+ //
+ static std::string
+ read_http_response_line (ifdstream&);
+
+ private:
+ enum method_proto {ftp_get, ftp_put, http_get, http_post};
+ using method_proto_options = small_vector<const char*, 2>;
+
+ method_proto
+ translate (method_type,
+ const std::string& url,
+ method_proto_options&,
+ flags);
+
+ private:
+ template <typename T>
+ struct is_other
+ {
+ using type = typename std::remove_reference<
+ typename std::remove_cv<T>::type>::type;
+
+ static const bool value = !(std::is_same<type, nullfd_t>::value ||
+ std::is_same<type, path>::value);
+ };
+
+ struct io_data
+ {
+ fdpipe pipe;
+ method_proto_options options;
+ std::string storage;
+ };
+
+ pipe
+ map_in (nullfd_t, method_proto, io_data&);
+
+ pipe
+ map_in (const path&, method_proto, io_data&);
+
+ template <typename I>
+ typename std::enable_if<is_other<I>::value, I>::type
+ map_in (I&&, method_proto, io_data&);
+
+ pipe
+ map_out (nullfd_t, method_proto, io_data&);
+
+ pipe
+ map_out (const path&, method_proto, io_data&);
+
+ template <typename O>
+ typename std::enable_if<is_other<O>::value, O>::type
+ map_out (O&&, method_proto, io_data&);
+ };
+
+ curl::flags operator& (curl::flags, curl::flags);
+ curl::flags operator| (curl::flags, curl::flags);
+ curl::flags operator&= (curl::flags&, curl::flags);
+ curl::flags operator|= (curl::flags&, curl::flags);
+}
+
+#include <libbutl/curl.ixx>
+#include <libbutl/curl.txx>