| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_CURL_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_CURL_H_ |
| |
| #include <curl/curl.h> |
| |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| namespace zxdb { |
| |
| namespace { |
| class CurlFDWatcher; |
| } // namespace |
| |
| class Curl { |
| public: |
| class Error { |
| public: |
| explicit Error(CURLcode code) : code_(code) {} |
| |
| Error& operator=(CURLcode code) { |
| code_ = code; |
| return *this; |
| } |
| |
| bool operator==(const Error& other) { return other.code_ == code_; } |
| |
| operator CURLcode() { return code_; } |
| operator bool() { return code_ != CURLE_OK; } |
| |
| std::string ToString() { return curl_easy_strerror(code_); } |
| |
| private: |
| CURLcode code_; |
| }; |
| |
| // Callback when we receive data from libcurl. The return value should be the |
| // number of bytes successfully processed (i.e. if we are passing this data |
| // to the write() syscall and it returns a short bytes written count, we |
| // should as well). |
| using DataCallback = std::function<size_t(const std::string&)>; |
| |
| Curl(); |
| ~Curl(); |
| Curl(Curl&) = delete; |
| Curl(Curl&& other) = delete; |
| |
| static std::shared_ptr<Curl> MakeShared(); |
| |
| Error SetURL(const std::string& url) { |
| return Error(curl_easy_setopt(curl_, CURLOPT_URL, url.c_str())); |
| } |
| |
| const std::string& post_data() { return post_data_; } |
| void set_post_data(const std::string& data) { post_data_ = data; } |
| void set_post_data(const std::map<std::string, std::string>& items); |
| |
| std::vector<std::string>& headers() { return headers_; } |
| bool& get_body() { return get_body_; } |
| |
| void set_data_callback(DataCallback handler) { data_callback_ = std::move(handler); } |
| void set_header_callback(DataCallback handler) { header_callback_ = std::move(handler); } |
| |
| // Believe it or not this takes a curl handle, so it can't be static. |
| std::string Escape(const std::string& input); |
| |
| // Run the curl request synchronously. |
| Error Perform(); |
| |
| // Run the curl request asynchronously. Invoke the callback when done. |
| void Perform(std::function<void(Curl*, Error)> cb); |
| |
| // Get the response code from the request. Undefined if the request hasn't |
| // run. |
| long ResponseCode(); |
| |
| private: |
| friend class CurlFDWatcher; |
| |
| friend size_t DoHeaderCallback(char* data, size_t size, size_t nitems, void* curl); |
| friend size_t DoDataCallback(char* data, size_t size, size_t nitems, void* curl); |
| |
| static CURLM* multi_handle; |
| |
| static void InitMulti(); |
| |
| void FreeSList(); |
| void PrepareToPerform(); |
| |
| // Effectively a count of the number of Curl objects outstanding. When it |
| // first becomes nonzero we run curl_global_init() and when it becomes zero |
| // again we run curl_global_cleanup(). |
| static size_t global_init; |
| |
| CURL* curl_ = nullptr; |
| struct curl_slist* slist_ = nullptr; |
| bool get_body_ = true; |
| |
| std::string post_data_; |
| std::weak_ptr<Curl> weak_self_ref_; |
| std::shared_ptr<Curl> self_ref_; |
| std::vector<std::string> headers_; |
| std::function<void(Curl*, Error)> multi_cb_ = nullptr; |
| DataCallback header_callback_ = [](const std::string& data) { return data.size(); }; |
| DataCallback data_callback_ = [](const std::string& data) { return data.size(); }; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_CURL_H_ |