Replace bleeding-edge libevent with libevhtp.

A libevhtp package exists for recent releases of Linux distros. An
exception for these will come in a future patch.

Change-Id: I15b96be469b91f19c06c326fc52b42027c097df9
Bug: 26850406
Bug: 24204638
Reviewed-on: https://weave-review.googlesource.com/2384
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/examples/provider/event_deleter.h b/examples/provider/event_deleter.h
index 078c326..9bb53f4 100644
--- a/examples/provider/event_deleter.h
+++ b/examples/provider/event_deleter.h
@@ -7,9 +7,10 @@
 
 #include <memory>
 
-#include <third_party/include/event2/event.h>
-#include <third_party/include/event2/event_struct.h>
-#include <third_party/include/event2/http.h>
+#include <evhtp.h>
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <openssl/ssl.h>
 
 namespace weave {
 namespace examples {
@@ -18,9 +19,19 @@
 // so we can use one unique_ptr definition for all of them
 class EventDeleter {
  public:
-  void operator()(evhttp_uri* http_uri) { evhttp_uri_free(http_uri); }
-  void operator()(evhttp_connection* conn) { evhttp_connection_free(conn); }
-  void operator()(evhttp_request* req) { evhttp_request_free(req); }
+  void operator()(evbuffer* buf) { evbuffer_free(buf); }
+  void operator()(evhtp_t* evhtp) {
+    if (evhtp->ssl_ctx) {
+      // Work around a double-free bug in recent versions of libevhtp.
+      // https://github.com/ellzey/libevhtp/pull/208
+      SSL_CTX_free(evhtp->ssl_ctx);
+      evhtp->ssl_ctx = nullptr;
+    }
+    evhtp_unbind_socket(evhtp);
+    evhtp_free(evhtp);
+  }
+  void operator()(evhtp_connection_t* conn) { evhtp_connection_free(conn); }
+  void operator()(evhtp_request_t* req) { evhtp_request_free(req); }
   void operator()(event_base* base) { event_base_free(base); }
   void operator()(event* ev) {
     event_del(ev);
diff --git a/examples/provider/event_http_server.cc b/examples/provider/event_http_server.cc
index ae9833e..1bf58f6 100644
--- a/examples/provider/event_http_server.cc
+++ b/examples/provider/event_http_server.cc
@@ -9,6 +9,7 @@
 #include <base/bind.h>
 #include <base/time/time.h>
 #include <event2/bufferevent_ssl.h>
+#include <evhtp.h>
 #include <openssl/err.h>
 
 #include "examples/provider/event_task_runner.h"
@@ -24,53 +25,45 @@
   return error;
 }
 
-bufferevent* BuffetEventCallback(event_base* base, void* arg) {
-  SSL_CTX* ctx = static_cast<SSL_CTX*>(arg);
-  return bufferevent_openssl_socket_new(
-      base, -1, SSL_new(ctx), BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE);
-}
-
 }  // namespace
 
 class HttpServerImpl::RequestImpl : public Request {
  public:
-  RequestImpl(evhttp_request* req) {
-    req_.reset(req);
-    uri_ = evhttp_request_get_evhttp_uri(req_.get());
-
-    data_.resize(evbuffer_get_length(req_->input_buffer));
-    evbuffer_remove(req_->input_buffer, &data_[0], data_.size());
+  RequestImpl(EventPtr<evhtp_request_t> req) : req_(std::move(req)) {
+    evbuf_t* input_buffer =
+        bufferevent_get_input(evhtp_request_get_bev(req_.get()));
+    data_.resize(evbuffer_get_length(input_buffer));
+    evbuffer_remove(input_buffer, &data_[0], data_.size());
   }
 
   ~RequestImpl() {}
 
-  std::string GetPath() const override {
-    const char* path = evhttp_uri_get_path(uri_);
-    return path ? path : "";
-  }
+  std::string GetPath() const override { return req_->uri->path->path; }
+
   std::string GetFirstHeader(const std::string& name) const override {
-    const char* header = evhttp_find_header(req_->input_headers, name.c_str());
+    const char* header = evhtp_header_find(req_->headers_in, name.c_str());
     if (!header)
       return {};
     return header;
   }
+
   std::string GetData() { return data_; }
 
   void SendReply(int status_code,
                  const std::string& data,
                  const std::string& mime_type) override {
-    std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(),
-                                                            &evbuffer_free};
+    EventPtr<evbuffer> buf{evbuffer_new()};
     evbuffer_add(buf.get(), data.data(), data.size());
-    evhttp_add_header(req_->output_headers, "Content-Type", mime_type.c_str());
-    evhttp_send_reply(req_.release(), status_code, "None", buf.get());
+    evhtp_header_key_add(req_->headers_out, "Content-Type", 0);
+    evhtp_header_val_add(req_->headers_out, mime_type.c_str(), 1);
+    evhtp_send_reply_start(req_.get(), status_code);
+    evhtp_send_reply_body(req_.get(), buf.get());
+    evhtp_send_reply_end(req_.get());
   }
 
  private:
-  std::unique_ptr<evhttp_request, decltype(&evhttp_cancel_request)> req_{
-      nullptr, &evhttp_cancel_request};
+  EventPtr<evhtp_request_t> req_;
   std::string data_;
-  const evhttp_uri* uri_{nullptr};
 };
 
 HttpServerImpl::HttpServerImpl(EventTaskRunner* task_runner)
@@ -78,74 +71,75 @@
   SSL_load_error_strings();
   SSL_library_init();
 
-  ctx_.reset(SSL_CTX_new(TLSv1_2_server_method()));
-  SSL_CTX_set_options(ctx_.get(), SSL_OP_SINGLE_DH_USE |
-                                      SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_SSLv2);
+  std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx{
+      SSL_CTX_new(TLSv1_2_server_method()), &SSL_CTX_free};
+  CHECK(ctx);
+  SSL_CTX_set_options(ctx.get(), SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE |
+                                     SSL_OP_NO_SSLv2);
 
-  ec_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
-  CHECK(ec_key_) << GetSslError();
-  CHECK_EQ(1, SSL_CTX_set_tmp_ecdh(ctx_.get(), ec_key_.get())) << GetSslError();
+  std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ec_key{
+      EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), &EC_KEY_free};
+  CHECK(ec_key) << GetSslError();
+  CHECK_EQ(1, SSL_CTX_set_tmp_ecdh(ctx.get(), ec_key.get())) << GetSslError();
 
-  GenerateX509();
-  CHECK_EQ(1, SSL_CTX_use_PrivateKey(ctx_.get(), pkey_.get())) << GetSslError();
-  CHECK_EQ(1, SSL_CTX_use_certificate(ctx_.get(), x509_.get()))
-      << GetSslError();
+  std::unique_ptr<X509, decltype(&X509_free)> x509{X509_new(), &X509_free};
+  CHECK(x509);
+  std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> pkey{EVP_PKEY_new(),
+                                                           &EVP_PKEY_free};
+  CHECK(pkey);
+  GenerateX509(x509.get(), pkey.get());
+  CHECK_EQ(1, SSL_CTX_use_PrivateKey(ctx.get(), pkey.get())) << GetSslError();
+  CHECK_EQ(1, SSL_CTX_use_certificate(ctx.get(), x509.get())) << GetSslError();
 
-  CHECK_EQ(1, SSL_CTX_check_private_key(ctx_.get())) << GetSslError();
+  CHECK_EQ(1, SSL_CTX_check_private_key(ctx.get())) << GetSslError();
 
-  httpd_.reset(evhttp_new(task_runner_->GetEventBase()));
+  httpd_.reset(evhtp_new(task_runner_->GetEventBase(), nullptr));
   CHECK(httpd_);
-  httpsd_.reset(evhttp_new(task_runner_->GetEventBase()));
+  httpsd_.reset(evhtp_new(task_runner_->GetEventBase(), nullptr));
   CHECK(httpsd_);
 
-  evhttp_set_bevcb(httpsd_.get(), BuffetEventCallback, ctx_.get());
+  httpsd_.get()->ssl_ctx = ctx.release();
 
-  CHECK_EQ(0, evhttp_bind_socket(httpd_.get(), "0.0.0.0", GetHttpPort()));
-  CHECK_EQ(0, evhttp_bind_socket(httpsd_.get(), "0.0.0.0", GetHttpsPort()));
+  CHECK_EQ(0, evhtp_bind_socket(httpd_.get(), "0.0.0.0", GetHttpPort(), -1));
+  CHECK_EQ(0, evhtp_bind_socket(httpsd_.get(), "0.0.0.0", GetHttpsPort(), -1));
 }
 
-void HttpServerImpl::GenerateX509() {
-  x509_.reset(X509_new());
-  CHECK(x509_) << GetSslError();
+void HttpServerImpl::GenerateX509(X509* x509, EVP_PKEY* pkey) {
+  CHECK(x509) << GetSslError();
 
-  X509_set_version(x509_.get(), 2);
+  X509_set_version(x509, 2);
 
-  X509_gmtime_adj(X509_get_notBefore(x509_.get()), 0);
-  X509_gmtime_adj(X509_get_notAfter(x509_.get()),
+  X509_gmtime_adj(X509_get_notBefore(x509), 0);
+  X509_gmtime_adj(X509_get_notAfter(x509),
                   base::TimeDelta::FromDays(365).InSeconds());
 
-  pkey_.reset(EVP_PKEY_new());
-  CHECK(pkey_) << GetSslError();
+  CHECK(pkey) << GetSslError();
   std::unique_ptr<BIGNUM, decltype(&BN_free)> big_num(BN_new(), &BN_free);
   CHECK(BN_set_word(big_num.get(), 65537)) << GetSslError();
   auto rsa = RSA_new();
   RSA_generate_key_ex(rsa, 2048, big_num.get(), nullptr);
-  CHECK(EVP_PKEY_assign_RSA(pkey_.get(), rsa)) << GetSslError();
+  CHECK(EVP_PKEY_assign_RSA(pkey, rsa)) << GetSslError();
 
-  X509_set_pubkey(x509_.get(), pkey_.get());
+  X509_set_pubkey(x509, pkey);
 
-  CHECK(X509_sign(x509_.get(), pkey_.get(), EVP_sha256())) << GetSslError();
+  CHECK(X509_sign(x509, pkey, EVP_sha256())) << GetSslError();
 
   cert_fingerprint_.resize(EVP_MD_size(EVP_sha256()));
   uint32_t len = 0;
-  CHECK(X509_digest(x509_.get(), EVP_sha256(), cert_fingerprint_.data(), &len));
+  CHECK(X509_digest(x509, EVP_sha256(), cert_fingerprint_.data(), &len));
   CHECK_EQ(len, cert_fingerprint_.size());
 }
 
-void HttpServerImpl::ProcessRequestCallback(evhttp_request* req, void* arg) {
-  static_cast<HttpServerImpl*>(arg)->ProcessRequest(req);
+void HttpServerImpl::NotFound(evhtp_request_t* req) {
+  EventPtr<evbuffer> buf{evbuffer_new()};
+  evbuffer_add_printf(buf.get(), "404 Not Found: %s\n", req->uri->path->path);
+  evhtp_send_reply_start(req, 404);
+  evhtp_send_reply_body(req, buf.get());
+  evhtp_send_reply_end(req);
 }
 
-void HttpServerImpl::NotFound(evhttp_request* req) {
-  std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(),
-                                                          &evbuffer_free};
-  evbuffer_add_printf(buf.get(), "404 Not Found: %s\n",
-                      evhttp_request_uri(req));
-  evhttp_send_reply(req, 404, "Not Found", buf.get());
-}
-
-void HttpServerImpl::ProcessRequest(evhttp_request* req) {
-  std::unique_ptr<RequestImpl> request{new RequestImpl{req}};
+void HttpServerImpl::ProcessRequest(evhtp_request_t* req) {
+  std::unique_ptr<RequestImpl> request{new RequestImpl{EventPtr<evhtp_request_t>{req}}};
   std::string path = request->GetPath();
   auto it = handlers_.find(path);
   if (it != handlers_.end()) {
@@ -154,25 +148,29 @@
   NotFound(req);
 }
 
-void HttpServerImpl::ProcessReply(std::shared_ptr<RequestImpl> request,
-                                  int status_code,
-                                  const std::string& data,
-                                  const std::string& mime_type) {}
+void HttpServerImpl::ProcessRequestCallback(evhtp_request_t* req, void* arg) {
+  static_cast<HttpServerImpl*>(arg)->ProcessRequest(req);
+}
 
 void HttpServerImpl::AddHttpRequestHandler(
     const std::string& path,
     const RequestHandlerCallback& callback) {
   handlers_.insert(std::make_pair(path, callback));
-  evhttp_set_cb(httpd_.get(), path.c_str(), &ProcessRequestCallback, this);
+  evhtp_set_cb(httpd_.get(), path.c_str(), &ProcessRequestCallback, this);
 }
 
 void HttpServerImpl::AddHttpsRequestHandler(
     const std::string& path,
     const RequestHandlerCallback& callback) {
   handlers_.insert(std::make_pair(path, callback));
-  evhttp_set_cb(httpsd_.get(), path.c_str(), &ProcessRequestCallback, this);
+  evhtp_set_cb(httpsd_.get(), path.c_str(), &ProcessRequestCallback, this);
 }
 
+void HttpServerImpl::ProcessReply(std::shared_ptr<RequestImpl> request,
+                                  int status_code,
+                                  const std::string& data,
+                                  const std::string& mime_type) {}
+
 uint16_t HttpServerImpl::GetHttpPort() const {
   return 7780;
 }
diff --git a/examples/provider/event_http_server.h b/examples/provider/event_http_server.h
index 950e536..8bb5dd9 100644
--- a/examples/provider/event_http_server.h
+++ b/examples/provider/event_http_server.h
@@ -5,8 +5,7 @@
 #ifndef LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_SERVER_H_
 #define LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_SERVER_H_
 
-#include <event2/http.h>
-#include <evhttp.h>
+#include <evhtp.h>
 #include <openssl/ssl.h>
 
 #include <map>
@@ -16,12 +15,14 @@
 #include <base/memory/weak_ptr.h>
 #include <weave/provider/http_server.h>
 
+#include "examples/provider/event_deleter.h"
+
 namespace weave {
 namespace examples {
 
 class EventTaskRunner;
 
-// HTTP/HTTPS server implemented with libevent.
+// HTTP/HTTPS server implemented with libevhtp.
 class HttpServerImpl : public provider::HttpServer {
  public:
   class RequestImpl;
@@ -38,32 +39,21 @@
   std::vector<uint8_t> GetHttpsCertificateFingerprint() const override;
 
  private:
-  void GenerateX509();
-  static void ProcessRequestCallback(evhttp_request* req, void* arg);
-  void ProcessRequest(evhttp_request* req);
+  void GenerateX509(X509* x509, EVP_PKEY* pkey);
+  static void ProcessRequestCallback(evhtp_request_t* req, void* arg);
+  void ProcessRequest(evhtp_request_t* req);
   void ProcessReply(std::shared_ptr<RequestImpl> request,
                     int status_code,
                     const std::string& data,
                     const std::string& mime_type);
-  void NotFound(evhttp_request* req);
+  void NotFound(evhtp_request_t* req);
 
   std::map<std::string, RequestHandlerCallback> handlers_;
 
-  std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ec_key_{nullptr,
-                                                          &EC_KEY_free};
-
-  std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> pkey_{nullptr,
-                                                            &EVP_PKEY_free};
-
-  std::unique_ptr<X509, decltype(&X509_free)> x509_{nullptr, &X509_free};
-
-  std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx_{nullptr,
-                                                         &SSL_CTX_free};
   std::vector<uint8_t> cert_fingerprint_;
   EventTaskRunner* task_runner_{nullptr};
-  std::unique_ptr<evhttp, decltype(&evhttp_free)> httpd_{nullptr, &evhttp_free};
-  std::unique_ptr<evhttp, decltype(&evhttp_free)> httpsd_{nullptr,
-                                                          &evhttp_free};
+  EventPtr<evhtp_t> httpd_;
+  EventPtr<evhtp_t> httpsd_;
 
   base::WeakPtrFactory<HttpServerImpl> weak_ptr_factory_{this};
 };
diff --git a/examples/provider/event_task_runner.cc b/examples/provider/event_task_runner.cc
index 1d94612..a86ffff 100644
--- a/examples/provider/event_task_runner.cc
+++ b/examples/provider/event_task_runner.cc
@@ -31,7 +31,9 @@
   int16_t flags = EV_PERSIST | EV_ET;
   flags |= (what & kReadable) ? EV_READ : 0;
   flags |= (what & kWriteable) ? EV_WRITE : 0;
+#if LIBEVENT_VERSION_NUMBER >= 0x02010400
   flags |= (what & kClosed) ? EV_CLOSED : 0;
+#endif
   event* ioevent = event_new(base_.get(), fd, flags, FdEventHandler, this);
   EventPtr<event> ioeventPtr{ioevent};
   fd_task_map_.insert(
@@ -53,7 +55,9 @@
   sigfillset(&sa.sa_mask);
   sigaction(SIGINT, &sa, nullptr);
 
-  event_base_loop(g_event_base, EVLOOP_NO_EXIT_ON_EMPTY);
+  do {
+    event_base_loop(g_event_base, EVLOOP_ONCE);
+  } while (!event_base_got_exit(g_event_base));
   g_event_base = nullptr;
 }