diff options
| author | Joel Klinghed <the_jk@yahoo.com> | 2015-06-05 00:54:46 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@yahoo.com> | 2015-06-05 00:54:46 +0200 |
| commit | a7bb0f26b3434b7064b01aefd56d8600cc466bd1 (patch) | |
| tree | 42de0a173ca6e514b4c31fc9c565e40f0a2c7e9c /src/sender.cc | |
| parent | 0b8e0ca6e1e8d9a0e36e2e3c960c84a289822675 (diff) | |
Send the responses to an url
Diffstat (limited to 'src/sender.cc')
| -rw-r--r-- | src/sender.cc | 147 |
1 files changed, 126 insertions, 21 deletions
diff --git a/src/sender.cc b/src/sender.cc index b9b6a36..e9b4a94 100644 --- a/src/sender.cc +++ b/src/sender.cc @@ -14,6 +14,8 @@ #include <unistd.h> #include <vector> +#include <curl/curl.h> + #include "config.hh" #include "json.hh" @@ -142,6 +144,48 @@ private: std::string message_; }; +size_t ignore_all(char* ptr, size_t size, size_t nmemb, void* userdata) { + return size * nmemb; +} + +class Request { +public: + Request(CURLM* multi, const std::string& url, const std::string& json) + : multi_(multi), headers_(nullptr) { + curl_ = curl_easy_init(); + curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl_, CURLOPT_MAXREDIRS, 10L); + curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl_, CURLOPT_POST, 1); + headers_ = curl_slist_append(headers_, + "Content-Type: application/json"); + curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_); + curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, json.size()); + curl_easy_setopt(curl_, CURLOPT_COPYPOSTFIELDS, json.data()); + + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ignore_all); + + curl_multi_add_handle(multi_, curl_); + } + + ~Request() { + curl_multi_remove_handle(multi_, curl_); + curl_easy_cleanup(curl_); + curl_slist_free_all(headers_); + } + + bool done() { + long status; + curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &status); + return status != 0; + } + +private: + CURL* curl_; + CURLM* multi_; + struct curl_slist* headers_; +}; + bool g_quit; void quit(int sig) { @@ -154,6 +198,12 @@ struct Info { std::string username; std::string icon_url; std::string icon_emoji; + + std::string url; + + CURLM* multi; + + std::vector<Request> requests; }; Info g_info; @@ -169,7 +219,7 @@ void queue_message(const std::string& channel, const std::string& message) { obj->put("channel", "#" + channel); obj->put("text", message); - + g_info.requests.emplace_back(g_info.multi, g_info.url, obj->str()); } } // namespace @@ -180,6 +230,11 @@ int main() { cfg->load(SYSCONFDIR "/sender.config"); } g_info.username = cfg->get("username", "stuff-sender-bot"); + g_info.url = cfg->get("url", ""); + if (g_info.url.empty()) { + std::cerr << "No url configured" << std::endl; + return EXIT_FAILURE; + } g_info.icon_url = cfg->get("icon_url", ""); g_info.icon_emoji = cfg->get("icon_emoji", ""); auto const& listener = cfg->get("listener", ""); @@ -188,6 +243,19 @@ int main() { return EXIT_FAILURE; } + if (curl_global_init(CURL_GLOBAL_ALL)) { + std::cerr << "CURL failed to initialize" << std::endl; + return EXIT_FAILURE; + } + g_info.multi = curl_multi_init(); + if (!g_info.multi) { + std::cerr << "CURL didn't initialize" << std::endl; + return EXIT_FAILURE; + } + + std::vector<Client> clients; + int still_running; + int exitvalue; int sock_ = -1; size_t pos = listener.find(':'); if (pos != std::string::npos) { @@ -201,7 +269,7 @@ int main() { if (getaddrinfo(pos == 0 ? nullptr : listener.substr(0, pos).c_str(), listener.substr(pos + 1).c_str(), &hints, &res)) { std::cerr << "Invalid host or port in: " << listener << std::endl; - return EXIT_FAILURE; + goto error; } for (auto ptr = res; ptr; ptr = ptr->ai_next) { sock_ = socket(ptr->ai_family, ptr->ai_socktype, @@ -217,7 +285,7 @@ int main() { freeaddrinfo(res); if (sock_ == -1) { std::cerr << "Unable to bind: " << listener << std::endl; - return EXIT_FAILURE; + goto error; } } else { // socket @@ -225,7 +293,7 @@ int main() { if (sock_ == -1) { std::cerr << "Unable to create local socket: " << strerror(errno) << std::endl; - return EXIT_FAILURE; + goto error; } struct sockaddr_un name; name.sun_family = AF_LOCAL; @@ -234,47 +302,74 @@ int main() { if (bind(sock_, reinterpret_cast<struct sockaddr*>(&name), SUN_LEN(&name))) { std::cerr << "Bind failed: " << strerror(errno) << std::endl; - close(sock_); - sock_ = 1; - return EXIT_FAILURE; + goto error; } } if (listen(sock_, 10)) { std::cerr << "Listen failed: " << strerror(errno) << std::endl; - close(sock_); - return EXIT_FAILURE; + goto error; } - int value = 1; - setsockopt(sock_, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)); + { + int value = 1; + setsockopt(sock_, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)); + } signal(SIGPIPE, SIG_IGN); signal(SIGINT, quit); signal(SIGTERM, quit); - std::vector<Client> clients; - while (!g_quit) { + curl_multi_perform(g_info.multi, &still_running); + + for (auto it = g_info.requests.begin(); it != g_info.requests.end();) { + if (it->done()) { + it = g_info.requests.erase(it); + } else { + ++it; + } + } + fd_set read_set; - auto max = sock_ + 1; + fd_set write_set; + fd_set err_set; + int max = -1; FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&err_set); + + long timeout = -1; + struct timeval *to = nullptr; + struct timeval _to; + curl_multi_timeout(g_info.multi, &timeout); + if (timeout >= 0) { + _to.tv_sec = timeout / 1000; + _to.tv_usec = (timeout % 1000) * 1000; + to = &_to; + } + if (curl_multi_fdset(g_info.multi, &read_set, &write_set, &err_set, + &max) != CURLM_OK) { + std::cerr << "curl_multi_fdset failed" << std::endl; + goto error; + } + + max = std::max(max, sock_); FD_SET(sock_, &read_set); for (auto it = clients.begin(); it != clients.end();) { if (it->sock() == -1) { it = clients.erase(it); } else { - max = std::max(max, it->sock() + 1); + max = std::max(max, it->sock()); FD_SET(it->sock(), &read_set); ++it; } } - auto ret = select(max, &read_set, nullptr, nullptr, nullptr); + auto ret = select(max + 1, &read_set, &write_set, &err_set, to); if (ret < 0) { if (errno == EINTR) continue; std::cerr << "Select failed: " << strerror(errno); - close(sock_); - return EXIT_FAILURE; + goto error; } for (auto it = clients.begin(); ret > 0 && it != clients.end();) { if (FD_ISSET(it->sock(), &read_set)) { @@ -295,8 +390,7 @@ int main() { if (errno == EINTR) continue; if (errno == EWOULDBLOCK || errno == EAGAIN) continue; std::cerr << "Accept failed: " << strerror(errno); - close(sock_); - return EXIT_FAILURE; + goto error; } if (clients.size() == MAX_CLIENTS) { // Remove oldest @@ -305,6 +399,17 @@ int main() { clients.emplace_back(sock); } } + + exitvalue = EXIT_SUCCESS; + goto end; + error: + exitvalue = EXIT_FAILURE; + + end: + clients.clear(); + g_info.requests.clear(); + curl_multi_cleanup(g_info.multi); + curl_global_cleanup(); close(sock_); - return EXIT_SUCCESS; + return exitvalue; } |
