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; }