diff options
Diffstat (limited to 'external/glim/hget.hpp')
-rw-r--r-- | external/glim/hget.hpp | 255 |
1 files changed, 0 insertions, 255 deletions
diff --git a/external/glim/hget.hpp b/external/glim/hget.hpp deleted file mode 100644 index cebbe416f..000000000 --- a/external/glim/hget.hpp +++ /dev/null @@ -1,255 +0,0 @@ -// Simple header-only wrapper around libevent's evhttp client.
-// See also: https://github.com/cpp-netlib/cpp-netlib/issues/160
-
-#ifndef _GLIM_HGET_INCLUDED
-#define _GLIM_HGET_INCLUDED
-
-#include <event2/event.h>
-#include <event2/dns.h>
-#include <evhttp.h> // http://stackoverflow.com/a/5237994; http://archives.seul.org/libevent/users/Sep-2010/msg00050.html
-
-#include <memory>
-#include <functional>
-#include <stdexcept>
-#include <iostream>
-#include <vector>
-
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-
-#include "exception.hpp"
-#include "gstring.hpp"
-
-namespace glim {
-
-/// HTTP results
-struct hgot {
- int32_t status = 0;
- /// Uses errno codes.
- int32_t error = 0;
- struct evbuffer* body = 0;
- struct evhttp_request* req = 0;
- size_t bodyLength() const {return body ? evbuffer_get_length (body) : 0;}
- /// Warning: the string is NOT zero-terminated.
- const char* bodyData() {return body ? (const char*) evbuffer_pullup (body, -1) : "";}
- /// Returns a zero-terminated string. Warning: modifies the `body` every time in order to add the terminator.
- const char* cbody() {if (!body) return ""; evbuffer_add (body, "", 1); return (const char*) evbuffer_pullup (body, -1);}
- /// A gstring *view* into the `body`.
- glim::gstring gbody() {
- if (!body) return glim::gstring();
- return glim::gstring (glim::gstring::ReferenceConstructor(), (const char*) evbuffer_pullup (body, -1), evbuffer_get_length (body));}
-};
-
-/// Used internally to pass both connection and handler into callback.
-struct hgetContext {
- struct evhttp_connection* conn;
- std::function<void(hgot&)> handler;
- hgetContext (struct evhttp_connection* conn, std::function<void(hgot&)> handler): conn (conn), handler (handler) {}
-};
-
-/// Invoked when evhttp finishes a request.
-inline void hgetCB (struct evhttp_request* req, void* ctx_){
- hgetContext* ctx = (hgetContext*) ctx_;
-
- hgot gt;
- if (req == NULL) gt.error = ETIMEDOUT;
- else if (req->response_code == 0) gt.error = ECONNREFUSED;
- else {
- gt.status = req->response_code;
- gt.body = req->input_buffer;
- gt.req = req;
- }
-
- try {
- ctx->handler (gt);
- } catch (const std::runtime_error& ex) { // Shouldn't normally happen:
- std::cerr << "glim::hget, handler exception: " << ex.what() << std::endl;
- }
-
- evhttp_connection_free ((struct evhttp_connection*) ctx->conn);
- //freed by libevent//if (req != NULL) evhttp_request_free (req);
- delete ctx;
-}
-
-/**
- C++ wrapper around libevent's http client.
- Example: \code
- hget (evbase, dnsbase) .setRequestBuilder ([](struct evhttp_request* req){
- evbuffer_add (req->output_buffer, "foo", 3);
- evhttp_add_header (req->output_headers, "Content-Length", "3");
- }) .go ("http://127.0.0.1:8080/test", [](hgot& got){
- if (got.error) log_warn ("127.0.0.1:8080 " << strerror (got.error));
- else if (got.status != 200) log_warn ("127.0.0.1:8080 != 200");
- else log_info ("got " << evbuffer_get_length (got.body) << " bytes from /test: " << evbuffer_pullup (got.body, -1));
- }); \endcode
- */
-class hget {
- public:
- std::shared_ptr<struct event_base> _evbase;
- std::shared_ptr<struct evdns_base> _dnsbase;
- std::function<void(struct evhttp_request*)> _requestBuilder;
- enum evhttp_cmd_type _method;
- public:
- typedef std::shared_ptr<struct evhttp_uri> uri_t;
- /// The third parameter is the request number, starting from 1.
- typedef std::function<float(hgot&,uri_t,int32_t)> until_handler_t;
- public:
- hget (std::shared_ptr<struct event_base> evbase, std::shared_ptr<struct evdns_base> dnsbase):
- _evbase (evbase), _dnsbase (dnsbase), _method (EVHTTP_REQ_GET) {}
-
- /// Modifies the request before its execution.
- hget& setRequestBuilder (std::function<void(struct evhttp_request*)> rb) {
- _requestBuilder = rb;
- return *this;
- }
-
- /** Uses a simple request builder to send the `str`.
- * `str` is a `char` string class with methods `data` and `size`. */
- template<typename STR> hget& payload (STR str, const char* contentType = nullptr, enum evhttp_cmd_type method = EVHTTP_REQ_POST) {
- _method = method;
- return setRequestBuilder ([str,contentType](struct evhttp_request* req) {
- if (contentType) evhttp_add_header (req->output_headers, "Content-Type", contentType);
- char buf[64];
- *glim::itoa (buf, (int) str.size()) = 0;
- evhttp_add_header (req->output_headers, "Content-Length", buf);
- evbuffer_add (req->output_buffer, (const void*) str.data(), (size_t) str.size());
- });
- }
-
- struct evhttp_request* go (uri_t uri, int32_t timeoutSec, std::function<void(hgot&)> handler) {
- int port = evhttp_uri_get_port (uri.get());
- if (port == -1) port = 80;
- struct evhttp_connection* conn = evhttp_connection_base_new (_evbase.get(), _dnsbase.get(),
- evhttp_uri_get_host (uri.get()), port);
- evhttp_connection_set_timeout (conn, timeoutSec);
- struct evhttp_request *req = evhttp_request_new (hgetCB, new hgetContext(conn, handler));
- int ret = evhttp_add_header (req->output_headers, "Host", evhttp_uri_get_host (uri.get()));
- if (ret) throw std::runtime_error ("hget: evhttp_add_header(Host) != 0");
- if (_requestBuilder) _requestBuilder (req);
- const char* get = evhttp_uri_get_path (uri.get());
- const char* qs = evhttp_uri_get_query (uri.get());
- if (qs == NULL) {
- ret = evhttp_make_request (conn, req, _method, get);
- } else {
- size_t getLen = strlen (get);
- size_t qsLen = strlen (qs);
- char buf[getLen + 1 + qsLen + 1];
- char* caret = stpcpy (buf, get);
- *caret++ = '?';
- caret = stpcpy (caret, qs);
- assert (caret - buf < sizeof (buf));
- ret = evhttp_make_request (conn, req, _method, buf);
- }
- if (ret) throw std::runtime_error ("hget: evhttp_make_request != 0");
- return req;
- }
- struct evhttp_request* go (const char* url, int32_t timeoutSec, std::function<void(hgot&)> handler) {
- return go (std::shared_ptr<struct evhttp_uri> (evhttp_uri_parse (url), evhttp_uri_free), timeoutSec, handler);
- }
-
- void goUntil (std::vector<uri_t> urls, until_handler_t handler, int32_t timeoutSec = 20);
- /**
- Parse urls and call `goUntil`.
- Example (trying ten times to reach the servers): \code
- std::string path ("/path");
- hget.goUntilS (boost::assign::list_of ("http://server1" + path) ("http://server2" + path),
- [](hgot& got, hget::uri_t uri, int32_t num)->float {
- std::cout << "server: " << evhttp_uri_get_host (uri.get()) << "; request number: " << num << std::endl;
- if (got.status != 200 && num < 10) return 1.f; // Retry in a second.
- return -1.f; // No need to retry the request.
- });
- \endcode
- @param urls is a for-compatible container of strings (where string has methods `data` and `size`).
- */
- template<typename URLS> void goUntilS (URLS&& urls, until_handler_t handler, int32_t timeoutSec = 20) {
- std::vector<uri_t> parsedUrls;
- for (auto&& url: urls) {
- // Copying to stack might be cheaper than malloc in c_str.
- int len = url.size(); char buf[len + 1]; memcpy (buf, url.data(), len); buf[len] = 0;
- struct evhttp_uri* uri = evhttp_uri_parse (buf);
- if (!uri) GTHROW (std::string ("!evhttp_uri_parse: ") + buf);
- parsedUrls.push_back (uri_t (uri, evhttp_uri_free));
- }
- goUntil (parsedUrls, handler, timeoutSec);
- }
- /**
- Parse urls and call `goUntil`.
- Example (trying ten times to reach the servers): \code
- hget.goUntilC (boost::assign::list_of ("http://server1/") ("http://server2/"),
- [](hgot& got, hget::uri_t uri, int32_t num)->float {
- std::cout << "server: " << evhttp_uri_get_host (uri.get()) << "; request number: " << num << std::endl;
- if (got.status != 200 && num < 10) return 1.f; // Retry in a second.
- return -1.f; // No need to retry the request.
- });
- \endcode
- Or with `std::array` instead of `boost::assign::list_of`: \code
- std::array<const char*, 2> urls {{"http://server1/", "http://server2/"}};
- hget.goUntilC (urls, [](hgot& got, hget::uri_t uri, int32_t num)->float {
- return got.status != 200 && num < 10 ? 0.f : -1.f;});
- \endcode
- @param urls is a for-compatible container of C strings (const char*).
- */
- template<typename URLS> void goUntilC (URLS&& urls, until_handler_t handler, int32_t timeoutSec = 20) {
- std::vector<uri_t> parsedUrls;
- for (auto url: urls) {
- struct evhttp_uri* uri = evhttp_uri_parse (url);
- if (!uri) GTHROW (std::string ("Can't parse url: ") + url);
- parsedUrls.push_back (uri_t (uri, evhttp_uri_free));
- }
- goUntil (parsedUrls, handler, timeoutSec);
- }
-};
-
-inline void hgetUntilRetryCB (evutil_socket_t, short, void* utilHandlerPtr); // event_callback_fn
-
-/** `hget::goUntil` implementation.
- * This function object is passed to `hget::go` as a handler and calls `hget::go` again if necessary. */
-struct HgetUntilHandler {
- hget _hget;
- hget::until_handler_t _handler;
- std::vector<hget::uri_t> _urls;
- int32_t _timeoutSec;
- int32_t _requestNum;
- uint8_t _nextUrl; ///< A round-robin pointer to the next url in `_urls`.
- HgetUntilHandler (hget& hg, hget::until_handler_t handler, std::vector<hget::uri_t> urls, int32_t timeoutSec):
- _hget (hg), _handler (handler), _urls (urls), _timeoutSec (timeoutSec), _requestNum (0), _nextUrl (0) {}
- void operator() (hgot& got) {
- uint8_t urlNum = _nextUrl ? _nextUrl - 1 : _urls.size() - 1;
- float retryAfterSec = _handler (got, _urls[urlNum], _requestNum);
- if (retryAfterSec == 0.f) retry();
- else if (retryAfterSec > 0.f) {
- struct timeval wait;
- wait.tv_sec = (int) retryAfterSec;
- retryAfterSec -= wait.tv_sec;
- wait.tv_usec = (int) (retryAfterSec * 1000000.f);
- int rc = event_base_once (_hget._evbase.get(), -1, EV_TIMEOUT, hgetUntilRetryCB, new HgetUntilHandler (*this), &wait);
- if (rc) throw std::runtime_error ("HgetUntilHandler: event_base_once != 0");
- }
- }
- void start() {retry();}
- void retry() {
- uint8_t nextUrl = _nextUrl++;
- if (_nextUrl >= _urls.size()) _nextUrl = 0;
- ++_requestNum;
- _hget.go (_urls[nextUrl], _timeoutSec, *this);
- }
-};
-
-/// Used in `hget::goUntil` to wait in `evtimer_new` before repeating the request.
-inline void hgetUntilRetryCB (evutil_socket_t, short, void* utilHandlerPtr) { // event_callback_fn
- std::unique_ptr<HgetUntilHandler> untilHandler ((HgetUntilHandler*) utilHandlerPtr);
- untilHandler->retry();
-}
-
-/**
- * Allows to retry the request using multiple URLs in a round-robin fashion.
- * The `handler` returns the number of seconds to wait before retrying the request or -1 if no retry is necessary.
- */
-inline void hget::goUntil (std::vector<uri_t> urls, until_handler_t handler, int32_t timeoutSec) {
- HgetUntilHandler (*this, handler, urls, timeoutSec) .start();
-}
-
-}
-
-#endif // _GLIM_HGET_INCLUDED
|