diff options
author | Alex Vakulenko <avakulenko@google.com> | 2016-02-02 12:31:43 -0800 |
---|---|---|
committer | Alex Vakulenko <avakulenko@google.com> | 2016-02-02 12:33:52 -0800 |
commit | e851e48e0f6e71af1ed25c982aae51cac0004f28 (patch) | |
tree | 6f6ec4e0215e5fb7dab58640dcc49c27ef4923cc | |
parent | 9d187e6ddb3c0a570dd363c77cab2dc7f4966260 (diff) | |
parent | 98d1fee994302f5e2ad7a7b60de2f2d74f35408b (diff) | |
download | libweave-e851e48e0f6e71af1ed25c982aae51cac0004f28.tar.gz |
Merge remote-tracking branch 'weave/master' into 'weave/aosp-master'
919a8a9 Remove crypto type "None"
3313558 Replace bleeding-edge libevent with libevhtp.
c3d7714 Make default Makefile target "all".
d9e3061 Add libevent-dev to README.
ebfa60b Add a unit tests for deleting CloudCommandProxy along with CommandInstance
c920bb2 Fix memory leak when removing CommandInstance from CommandQueue
329ad80 Rename CommandQueue::DelayedRemove into RemoveLater()
6a1ba84 Changed meaning of some SSID flags
98d1fee Periodicly clean up command queue and remove old processed commands
Change-Id: If63cb3d9a34d405df06ab958c609bb46668737bc
44 files changed, 396 insertions, 285 deletions
@@ -2,4 +2,6 @@ /out/ /third_party/include /third_party/lib +/third_party/libevhtp +/third_party/googletest gomacc.lock @@ -88,7 +88,6 @@ include file_lists.mk third_party/third_party.mk examples/examples.mk tests.mk weave_obj_files := $(WEAVE_SRC_FILES:%.cc=out/$(BUILD_MODE)/%.o) -# TODO(jacobmarble): There are too many gtest/gmock deps in non-test targets. Fix. $(weave_obj_files) : out/$(BUILD_MODE)/%.o : %.cc mkdir -p $(dir $@) $(CXX) $(DEFS_$(BUILD_MODE)) $(INCLUDES) $(CFLAGS) $(CFLAGS_$(BUILD_MODE)) $(CFLAGS_CC) -c -o $@ $< @@ -102,7 +101,8 @@ all : out/$(BUILD_MODE)/libweave.so all-examples out/$(BUILD_MODE)/libweave_expo clean : rm -rf out -cleanall : clean clean-gtest clean-libevent +cleanall : clean clean-gtest clean-libevhtp .PHONY : clean cleanall all +.DEFAULT_GOAL := all @@ -49,7 +49,20 @@ repo sync ``` sudo apt-get update -sudo apt-get install autoconf automake binutils g++ hostapd libavahi-client-dev libcurl4-openssl-dev libexpat1-dev libnl-3-dev libnl-route-3-dev libssl-dev libtool +sudo apt-get install \ + autoconf \ + automake \ + binutils \ + g++ \ + hostapd \ + libavahi-client-dev \ + libcurl4-openssl-dev \ + libevent-dev \ + libexpat1-dev \ + libnl-3-dev \ + libnl-route-3-dev \ + libssl-dev \ + libtool ``` # Prerequisites @@ -72,7 +85,8 @@ sudo apt-get install autoconf automake binutils g++ hostapd libavahi-client-dev - hostapd - libavahi-client-dev - libcurl4-openssl-dev - - libevent 2.1.x-alpha (included; see third_party/get_libevent.sh) + - libevent 2.0.x + - libevhtp (included; see third_party/get_libevhtp.sh) # Compiling @@ -80,17 +94,16 @@ sudo apt-get install autoconf automake binutils g++ hostapd libavahi-client-dev The `make --jobs/-j` flag is encouraged, to speed up build time. For example ``` -make all -j +make -j ``` -### Build library - -``` -make +which happens to be the same as ``` +make all -j +```` -or +### Build library ``` make out/Debug/libweave.so diff --git a/examples/daemon/common/daemon.h b/examples/daemon/common/daemon.h index 6dc021d..985c5e5 100644 --- a/examples/daemon/common/daemon.h +++ b/examples/daemon/common/daemon.h @@ -20,7 +20,6 @@ class Daemon { public: struct Options { bool force_bootstrapping_{false}; - bool disable_security_{false}; bool disable_privet_{false}; std::string registration_ticket_; std::string model_id_{"AAAAA"}; @@ -31,7 +30,6 @@ class Daemon { << "\t-h,--help Show this help message\n" << "\t--v=LEVEL Logging level\n" << "\t-b,--bootstrapping Force WiFi bootstrapping\n" - << "\t-d,--disable_security Disable privet security\n" << "\t--registration_ticket=TICKET Register device with the " "given ticket\n" << "\t--disable_privet Disable local privet\n"; @@ -44,8 +42,6 @@ class Daemon { return false; } else if (arg == "-b" || arg == "--bootstrapping") { force_bootstrapping_ = true; - } else if (arg == "-d" || arg == "--disable_security") { - disable_security_ = true; } else if (arg == "--disable_privet") { disable_privet_ = true; } else if (arg.find("--registration_ticket") != std::string::npos) { @@ -71,8 +67,7 @@ class Daemon { Daemon(const Options& opts) : task_runner_{new weave::examples::EventTaskRunner}, config_store_{ - new weave::examples::FileConfigStore(opts.disable_security_, - opts.model_id_, + new weave::examples::FileConfigStore(opts.model_id_, task_runner_.get())}, http_client_{new weave::examples::CurlHttpClient(task_runner_.get())}, network_{new weave::examples::EventNetworkImpl(task_runner_.get())}, diff --git a/examples/examples.mk b/examples/examples.mk index 06266a3..f1e92b6 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -7,7 +7,7 @@ examples_provider_obj_files := $(EXAMPLES_PROVIDER_SRC_FILES:%.cc=out/$(BUILD_MODE)/%.o) -$(examples_provider_obj_files) : out/$(BUILD_MODE)/%.o : %.cc third_party/include/event2/event.h +$(examples_provider_obj_files) : out/$(BUILD_MODE)/%.o : %.cc third_party/include/evhtp.h mkdir -p $(dir $@) $(CXX) $(DEFS_$(BUILD_MODE)) $(INCLUDES) $(CFLAGS) $(CFLAGS_$(BUILD_MODE)) $(CFLAGS_CC) -c -o $@ $< @@ -15,7 +15,7 @@ out/$(BUILD_MODE)/examples_provider.a : $(examples_provider_obj_files) rm -f $@ $(AR) crsT $@ $^ -out/$(BUILD_MODE)/examples/daemon/%.o : examples/daemon/%.cc third_party/include/event2/event.h +out/$(BUILD_MODE)/examples/daemon/%.o : examples/daemon/%.cc third_party/include/evhtp.h mkdir -p $(dir $@) $(CXX) $(DEFS_$(BUILD_MODE)) $(INCLUDES) $(CFLAGS) $(CFLAGS_$(BUILD_MODE)) $(CFLAGS_CC) -c -o $@ $< @@ -32,22 +32,22 @@ daemon_common_flags := \ -lssl \ -lcrypto -out/$(BUILD_MODE)/weave_daemon_ledflasher : out/$(BUILD_MODE)/examples/daemon/ledflasher/ledflasher.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so +out/$(BUILD_MODE)/weave_daemon_ledflasher : out/$(BUILD_MODE)/examples/daemon/ledflasher/ledflasher.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so third_party/lib/libevhtp.a $(CXX) -o $@ $^ $(CFLAGS) $(daemon_common_flags) -out/$(BUILD_MODE)/weave_daemon_light : out/$(BUILD_MODE)/examples/daemon/light/light.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so +out/$(BUILD_MODE)/weave_daemon_light : out/$(BUILD_MODE)/examples/daemon/light/light.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so third_party/lib/libevhtp.a $(CXX) -o $@ $^ $(CFLAGS) $(daemon_common_flags) -out/$(BUILD_MODE)/weave_daemon_lock : out/$(BUILD_MODE)/examples/daemon/lock/lock.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so +out/$(BUILD_MODE)/weave_daemon_lock : out/$(BUILD_MODE)/examples/daemon/lock/lock.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so third_party/lib/libevhtp.a $(CXX) -o $@ $^ $(CFLAGS) $(daemon_common_flags) -out/$(BUILD_MODE)/weave_daemon_oven : out/$(BUILD_MODE)/examples/daemon/oven/oven.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so +out/$(BUILD_MODE)/weave_daemon_oven : out/$(BUILD_MODE)/examples/daemon/oven/oven.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so third_party/lib/libevhtp.a $(CXX) -o $@ $^ $(CFLAGS) $(daemon_common_flags) -out/$(BUILD_MODE)/weave_daemon_sample : out/$(BUILD_MODE)/examples/daemon/sample/sample.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so +out/$(BUILD_MODE)/weave_daemon_sample : out/$(BUILD_MODE)/examples/daemon/sample/sample.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so third_party/lib/libevhtp.a $(CXX) -o $@ $^ $(CFLAGS) $(daemon_common_flags) -out/$(BUILD_MODE)/weave_daemon_speaker : out/$(BUILD_MODE)/examples/daemon/speaker/speaker.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so +out/$(BUILD_MODE)/weave_daemon_speaker : out/$(BUILD_MODE)/examples/daemon/speaker/speaker.o out/$(BUILD_MODE)/examples_provider.a out/$(BUILD_MODE)/libweave.so third_party/lib/libevhtp.a $(CXX) -o $@ $^ $(CFLAGS) $(daemon_common_flags) all-examples : out/$(BUILD_MODE)/weave_daemon_ledflasher out/$(BUILD_MODE)/weave_daemon_light out/$(BUILD_MODE)/weave_daemon_lock out/$(BUILD_MODE)/weave_daemon_oven out/$(BUILD_MODE)/weave_daemon_sample out/$(BUILD_MODE)/weave_daemon_speaker 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 @@ namespace examples { // 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 @@ std::string GetSslError() { 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 @@ HttpServerImpl::HttpServerImpl(EventTaskRunner* task_runner) 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); - - 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(); - - 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(); - - CHECK_EQ(1, SSL_CTX_check_private_key(ctx_.get())) << GetSslError(); - - httpd_.reset(evhttp_new(task_runner_->GetEventBase())); + 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); + + 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(); + + 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(); + + 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(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::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::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 @@ void HttpServerImpl::ProcessRequest(evhttp_request* req) { 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 @@ class HttpServerImpl : public provider::HttpServer { 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 @@ void EventTaskRunner::AddIoCompletionTask( 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 @@ void EventTaskRunner::Run() { 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; } diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc index 31efaa7..a6c2e60 100644 --- a/examples/provider/file_config_store.cc +++ b/examples/provider/file_config_store.cc @@ -19,11 +19,9 @@ namespace examples { const char kSettingsDir[] = "/var/lib/weave/"; -FileConfigStore::FileConfigStore(bool disable_security, - const std::string& model_id, +FileConfigStore::FileConfigStore(const std::string& model_id, provider::TaskRunner* task_runner) - : disable_security_{disable_security}, - model_id_{model_id}, + : model_id_{model_id}, task_runner_{task_runner} {} std::string FileConfigStore::GetPath(const std::string& name) const { @@ -61,7 +59,6 @@ bool FileConfigStore::LoadDefaults(Settings* settings) { settings->client_secret = "LS_iPYo_WIOE0m2VnLdduhnx"; settings->api_key = "AIzaSyACK3oZtmIylUKXiTMqkZqfuRiCgQmQSAQ"; - settings->disable_security = disable_security_; return true; } diff --git a/examples/provider/file_config_store.h b/examples/provider/file_config_store.h index e7398d1..337e82a 100644 --- a/examples/provider/file_config_store.h +++ b/examples/provider/file_config_store.h @@ -17,8 +17,7 @@ namespace examples { class FileConfigStore : public provider::ConfigStore { public: - FileConfigStore(bool disable_security, - const std::string& model_id, + FileConfigStore(const std::string& model_id, provider::TaskRunner* task_runner); bool LoadDefaults(Settings* settings) override; @@ -31,7 +30,6 @@ class FileConfigStore : public provider::ConfigStore { private: std::string GetPath(const std::string& name) const; - const bool disable_security_; const std::string model_id_; provider::TaskRunner* task_runner_{nullptr}; }; diff --git a/examples/provider/wifi_manager.h b/examples/provider/wifi_manager.h index 2bfc5ca..72f54df 100644 --- a/examples/provider/wifi_manager.h +++ b/examples/provider/wifi_manager.h @@ -35,6 +35,8 @@ class WifiImpl : public provider::Wifi { const DoneCallback& callback) override; void StartAccessPoint(const std::string& ssid) override; void StopAccessPoint() override; + bool IsWifi24Supported() const override {return true;}; + bool IsWifi50Supported() const override {return false;}; static bool HasWifiCapability(); diff --git a/include/weave/provider/test/fake_task_runner.h b/include/weave/provider/test/fake_task_runner.h index bb79455..4080072 100644 --- a/include/weave/provider/test/fake_task_runner.h +++ b/include/weave/provider/test/fake_task_runner.h @@ -31,6 +31,7 @@ class FakeTaskRunner : public TaskRunner { void Run(size_t number_of_iterations = 1000); void Break(); base::Clock* GetClock(); + size_t GetTaskQueueSize() const; private: void SaveTask(const tracked_objects::Location& from_here, diff --git a/include/weave/provider/test/mock_wifi.h b/include/weave/provider/test/mock_wifi.h index 9fbe10f..7ede55e 100644 --- a/include/weave/provider/test/mock_wifi.h +++ b/include/weave/provider/test/mock_wifi.h @@ -23,6 +23,8 @@ class MockWifi : public Wifi { const DoneCallback&)); MOCK_METHOD1(StartAccessPoint, void(const std::string&)); MOCK_METHOD0(StopAccessPoint, void()); + MOCK_CONST_METHOD0(IsWifi24Supported, bool()); + MOCK_CONST_METHOD0(IsWifi50Supported, bool()); }; } // namespace test diff --git a/include/weave/provider/wifi.h b/include/weave/provider/wifi.h index 48ac651..9c7cfca 100644 --- a/include/weave/provider/wifi.h +++ b/include/weave/provider/wifi.h @@ -28,6 +28,9 @@ class Wifi { // Stops WiFi access point. virtual void StopAccessPoint() = 0; + virtual bool IsWifi24Supported() const = 0; + virtual bool IsWifi50Supported() const = 0; + protected: virtual ~Wifi() {} }; diff --git a/include/weave/settings.h b/include/weave/settings.h index eeb3f93..741fff2 100644 --- a/include/weave/settings.h +++ b/include/weave/settings.h @@ -71,7 +71,6 @@ struct Settings { // Internal options to tweak some library functionality. External code should // avoid using them. bool wifi_auto_setup_enabled{true}; - bool disable_security{false}; std::string test_privet_ssid; }; diff --git a/src/base_api_handler_unittest.cc b/src/base_api_handler_unittest.cc index 8b0f0b2..2a202d1 100644 --- a/src/base_api_handler_unittest.cc +++ b/src/base_api_handler_unittest.cc @@ -8,6 +8,7 @@ #include <base/time/default_clock.h> #include <base/values.h> #include <gtest/gtest.h> +#include <weave/provider/test/fake_task_runner.h> #include <weave/provider/test/mock_config_store.h> #include <weave/provider/test/mock_http_client.h> #include <weave/test/mock_device.h> @@ -93,7 +94,8 @@ class BaseApiHandlerTest : public ::testing::Test { Config config_{&config_store_}; StrictMock<provider::test::MockHttpClient> http_client_; std::unique_ptr<DeviceRegistrationInfo> dev_reg_; - ComponentManagerImpl component_manager_; + StrictMock<provider::test::FakeTaskRunner> task_runner_; + ComponentManagerImpl component_manager_{&task_runner_}; std::unique_ptr<BaseApiHandler> handler_; StrictMock<test::MockDevice> device_; }; diff --git a/src/commands/cloud_command_proxy.h b/src/commands/cloud_command_proxy.h index 13f4654..80efd70 100644 --- a/src/commands/cloud_command_proxy.h +++ b/src/commands/cloud_command_proxy.h @@ -29,7 +29,7 @@ class TaskRunner; } // Command proxy which publishes command updates to the cloud. -class CloudCommandProxy final : public CommandInstance::Observer { +class CloudCommandProxy : public CommandInstance::Observer { public: CloudCommandProxy(CommandInstance* command_instance, CloudCommandUpdateInterface* cloud_command_updater, diff --git a/src/commands/cloud_command_proxy_unittest.cc b/src/commands/cloud_command_proxy_unittest.cc index 013769d..0de67fe 100644 --- a/src/commands/cloud_command_proxy_unittest.cc +++ b/src/commands/cloud_command_proxy_unittest.cc @@ -7,6 +7,7 @@ #include <memory> #include <queue> +#include <base/bind.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <weave/provider/test/fake_task_runner.h> @@ -16,6 +17,7 @@ #include "src/mock_component_manager.h" using testing::_; +using testing::AnyNumber; using testing::DoAll; using testing::Invoke; using testing::Return; @@ -62,6 +64,27 @@ class TestBackoffEntry : public BackoffEntry { base::Time creation_time_; }; +class CloudCommandProxyWrapper : public CloudCommandProxy { + public: + CloudCommandProxyWrapper(CommandInstance* command_instance, + CloudCommandUpdateInterface* cloud_command_updater, + ComponentManager* component_manager, + std::unique_ptr<BackoffEntry> backoff_entry, + provider::TaskRunner* task_runner, + const base::Closure& destruct_callback) + : CloudCommandProxy{command_instance, cloud_command_updater, + component_manager, std::move(backoff_entry), + task_runner}, + destruct_callback_{destruct_callback} {} + + ~CloudCommandProxyWrapper() { + destruct_callback_.Run(); + } + + private: + base::Closure destruct_callback_; +}; + class CloudCommandProxyTest : public ::testing::Test { protected: void SetUp() override { @@ -100,15 +123,21 @@ class CloudCommandProxyTest : public ::testing::Test { new TestBackoffEntry{&policy, task_runner_.GetClock()}}; // Finally construct the CloudCommandProxy we are going to test here. - std::unique_ptr<CloudCommandProxy> proxy{new CloudCommandProxy{ + std::unique_ptr<CloudCommandProxy> proxy{new CloudCommandProxyWrapper{ command_instance_.get(), &cloud_updater_, &component_manager_, - std::move(backoff), &task_runner_}}; + std::move(backoff), &task_runner_, + base::Bind(&CloudCommandProxyTest::OnProxyDestroyed, + base::Unretained(this))}}; // CloudCommandProxy::CloudCommandProxy() subscribe itself to weave::Command // notifications. When weave::Command is being destroyed it sends // ::OnCommandDestroyed() and CloudCommandProxy deletes itself. proxy.release(); + + EXPECT_CALL(*this, OnProxyDestroyed()).Times(AnyNumber()); } + MOCK_METHOD0(OnProxyDestroyed, void()); + ComponentManager::UpdateID current_state_update_id_{0}; base::CallbackList<void(ComponentManager::UpdateID)> callbacks_; testing::StrictMock<MockCloudCommandUpdateInterface> cloud_updater_; @@ -120,6 +149,14 @@ class CloudCommandProxyTest : public ::testing::Test { } // anonymous namespace +TEST_F(CloudCommandProxyTest, EnsureDestroyed) { + EXPECT_CALL(*this, OnProxyDestroyed()).Times(1); + command_instance_.reset(); + // Verify that CloudCommandProxy has been destroyed already and not at some + // point during the destruction of CloudCommandProxyTest class. + testing::Mock::VerifyAndClearExpectations(this); +} + TEST_F(CloudCommandProxyTest, ImmediateUpdate) { const char expected[] = "{'state':'done'}"; EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); diff --git a/src/commands/command_instance.cc b/src/commands/command_instance.cc index fc9b0e7..590bbb1 100644 --- a/src/commands/command_instance.cc +++ b/src/commands/command_instance.cc @@ -284,7 +284,7 @@ bool CommandInstance::SetStatus(Command::State status, ErrorPtr* error) { void CommandInstance::RemoveFromQueue() { if (queue_) - queue_->DelayedRemove(GetID()); + queue_->RemoveLater(GetID()); } } // namespace weave diff --git a/src/commands/command_instance.h b/src/commands/command_instance.h index b1028d0..febe5c5 100644 --- a/src/commands/command_instance.h +++ b/src/commands/command_instance.h @@ -89,10 +89,7 @@ class CommandInstance final : public Command { // Sets the pointer to queue this command is part of. void AttachToQueue(CommandQueue* queue) { queue_ = queue; } - void DetachFromQueue() { - observers_.Clear(); - queue_ = nullptr; - } + void DetachFromQueue() { queue_ = nullptr; } private: // Helper function to update the command status. diff --git a/src/commands/command_queue.cc b/src/commands/command_queue.cc index 134dc1c..f0d2228 100644 --- a/src/commands/command_queue.cc +++ b/src/commands/command_queue.cc @@ -18,6 +18,10 @@ std::string GetCommandHandlerKey(const std::string& component_path, } } +CommandQueue::CommandQueue(provider::TaskRunner* task_runner, + base::Clock* clock) + : task_runner_{task_runner}, clock_{clock} {} + void CommandQueue::AddCommandAddedCallback(const CommandCallback& callback) { on_command_added_.push_back(callback); // Send all pre-existed commands. @@ -84,18 +88,19 @@ void CommandQueue::Add(std::unique_ptr<CommandInstance> instance) { it_handler->second.Run(pair.first->second); else if (!default_command_callback_.is_null()) default_command_callback_.Run(pair.first->second); - - Cleanup(); } -void CommandQueue::DelayedRemove(const std::string& id) { +void CommandQueue::RemoveLater(const std::string& id) { auto p = map_.find(id); if (p == map_.end()) return; - remove_queue_.push(std::make_pair( - base::Time::Now() + base::TimeDelta::FromMinutes(kRemoveCommandDelayMin), - id)); - Cleanup(); + auto remove_delay = base::TimeDelta::FromMinutes(kRemoveCommandDelayMin); + remove_queue_.push(std::make_pair(clock_->Now() + remove_delay, id)); + if (remove_queue_.size() == 1) { + // The queue was empty, this is the first command to be removed, schedule + // a clean-up task. + ScheduleCleanup(remove_delay); + } } bool CommandQueue::Remove(const std::string& id) { @@ -110,19 +115,26 @@ bool CommandQueue::Remove(const std::string& id) { return true; } -void CommandQueue::Cleanup() { - while (!remove_queue_.empty() && remove_queue_.front().first < Now()) { - Remove(remove_queue_.front().second); +void CommandQueue::Cleanup(const base::Time& cutoff_time) { + while (!remove_queue_.empty() && remove_queue_.top().first <= cutoff_time) { + Remove(remove_queue_.top().second); remove_queue_.pop(); } } -void CommandQueue::SetNowForTest(base::Time now) { - test_now_ = now; +void CommandQueue::ScheduleCleanup(base::TimeDelta delay) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&CommandQueue::PerformScheduledCleanup, + weak_ptr_factory_.GetWeakPtr()), + delay); } -base::Time CommandQueue::Now() const { - return test_now_.is_null() ? base::Time::Now() : test_now_; +void CommandQueue::PerformScheduledCleanup() { + base::Time now = clock_->Now(); + Cleanup(now); + if (!remove_queue_.empty()) + ScheduleCleanup(remove_queue_.top().first - now); } CommandInstance* CommandQueue::Find(const std::string& id) const { diff --git a/src/commands/command_queue.h b/src/commands/command_queue.h index 0f0a18b..a092c12 100644 --- a/src/commands/command_queue.h +++ b/src/commands/command_queue.h @@ -14,8 +14,10 @@ #include <base/callback.h> #include <base/macros.h> +#include <base/time/default_clock.h> #include <base/time/time.h> #include <weave/device.h> +#include <weave/provider/task_runner.h> #include "src/commands/command_instance.h" @@ -23,7 +25,7 @@ namespace weave { class CommandQueue final { public: - CommandQueue() = default; + CommandQueue(provider::TaskRunner* task_runner, base::Clock* clock); // TODO: Remove AddCommandAddedCallback and AddCommandRemovedCallback. using CommandCallback = base::Callback<void(Command* command)>; @@ -51,7 +53,7 @@ class CommandQueue final { // Selects command identified by |id| ready for removal. Command will actually // be removed after some time. - void DelayedRemove(const std::string& id); + void RemoveLater(const std::string& id); // Finds a command instance in the queue by the instance |id|. Returns // nullptr if the command with the given |id| is not found. The returned @@ -64,23 +66,29 @@ class CommandQueue final { // Removes a command identified by |id| from the queue. bool Remove(const std::string& id); - // Removes old commands selected with DelayedRemove. - void Cleanup(); + // Removes old commands scheduled by RemoveLater() to be deleted after + // |cutoff_time|. + void Cleanup(const base::Time& cutoff_time); - // Overrides CommandQueue::Now() for tests. - void SetNowForTest(base::Time now); + // Schedule a cleanup task to be run after the specified |delay|. + void ScheduleCleanup(base::TimeDelta delay); - // Returns current time. - base::Time Now() const; + // Perform removal of scheduled commands (by calling Cleanup()) and scheduling + // another cleanup task if the removal queue is still not empty. + void PerformScheduledCleanup(); - // Overridden value to be returned from Now(). - base::Time test_now_; + provider::TaskRunner* task_runner_{nullptr}; + base::Clock* clock_{nullptr}; // ID-to-CommandInstance map. std::map<std::string, std::shared_ptr<CommandInstance>> map_; - // Queue of commands to be removed. - std::queue<std::pair<base::Time, std::string>> remove_queue_; + // Queue of commands to be removed, keeps them sorted by the timestamp + // (earliest first). This is done to tolerate system clock changes. + template <typename T> + using InversePriorityQueue = + std::priority_queue<T, std::vector<T>, std::greater<T>>; + InversePriorityQueue<std::pair<base::Time, std::string>> remove_queue_; using CallbackList = std::vector<CommandCallback>; CallbackList on_command_added_; @@ -88,6 +96,9 @@ class CommandQueue final { std::map<std::string, Device::CommandHandlerCallback> command_callbacks_; Device::CommandHandlerCallback default_command_callback_; + // WeakPtr factory for controlling the lifetime of command queue cleanup + // tasks. + base::WeakPtrFactory<CommandQueue> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(CommandQueue); }; diff --git a/src/commands/command_queue_unittest.cc b/src/commands/command_queue_unittest.cc index b4c5938..1e2e0ac 100644 --- a/src/commands/command_queue_unittest.cc +++ b/src/commands/command_queue_unittest.cc @@ -10,12 +10,18 @@ #include <base/bind.h> #include <base/memory/weak_ptr.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> +#include <weave/provider/test/fake_task_runner.h> +#include "src/bind_lambda.h" #include "src/string_utils.h" namespace weave { +using testing::Return; +using testing::StrictMock; + class CommandQueueTest : public testing::Test { public: std::unique_ptr<CommandInstance> CreateDummyCommandInstance( @@ -30,11 +36,15 @@ class CommandQueueTest : public testing::Test { bool Remove(const std::string& id) { return queue_.Remove(id); } void Cleanup(const base::TimeDelta& interval) { - queue_.SetNowForTest(base::Time::Now() + interval); - return queue_.Cleanup(); + return queue_.Cleanup(task_runner_.GetClock()->Now() + interval); + } + + std::string GetFirstCommandToBeRemoved() const { + return queue_.remove_queue_.top().second; } - CommandQueue queue_; + StrictMock<provider::test::FakeTaskRunner> task_runner_; + CommandQueue queue_{&task_runner_, task_runner_.GetClock()}; }; // Keeps track of commands being added to and removed from the queue_. @@ -105,12 +115,12 @@ TEST_F(CommandQueueTest, Remove) { EXPECT_TRUE(queue_.IsEmpty()); } -TEST_F(CommandQueueTest, DelayedRemove) { +TEST_F(CommandQueueTest, RemoveLater) { const std::string id1 = "id1"; queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); EXPECT_EQ(1u, queue_.GetCount()); - queue_.DelayedRemove(id1); + queue_.RemoveLater(id1); EXPECT_EQ(1u, queue_.GetCount()); Cleanup(base::TimeDelta::FromMinutes(1)); @@ -120,6 +130,46 @@ TEST_F(CommandQueueTest, DelayedRemove) { EXPECT_EQ(0u, queue_.GetCount()); } +TEST_F(CommandQueueTest, RemoveLaterOnCleanupTask) { + const std::string id1 = "id1"; + queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); + EXPECT_EQ(1u, queue_.GetCount()); + + queue_.RemoveLater(id1); + EXPECT_EQ(1u, queue_.GetCount()); + ASSERT_EQ(1u, task_runner_.GetTaskQueueSize()); + + task_runner_.RunOnce(); + + EXPECT_EQ(0u, queue_.GetCount()); + EXPECT_EQ(0u, task_runner_.GetTaskQueueSize()); +} + +TEST_F(CommandQueueTest, CleanupMultipleCommands) { + const std::string id1 = "id1"; + const std::string id2 = "id2"; + + queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); + queue_.Add(CreateDummyCommandInstance("base.reboot", id2)); + auto remove_task = [this](const std::string& id) { queue_.RemoveLater(id); }; + remove_task(id1); + task_runner_.PostDelayedTask(FROM_HERE, base::Bind(remove_task, id2), + base::TimeDelta::FromSeconds(10)); + EXPECT_EQ(2u, queue_.GetCount()); + ASSERT_EQ(2u, task_runner_.GetTaskQueueSize()); + task_runner_.RunOnce(); // Executes "remove_task(id2) @ T+10s". + ASSERT_EQ(2u, queue_.GetCount()); + ASSERT_EQ(1u, task_runner_.GetTaskQueueSize()); + EXPECT_EQ(id1, GetFirstCommandToBeRemoved()); + task_runner_.RunOnce(); // Should remove task "id1" from queue. + ASSERT_EQ(1u, queue_.GetCount()); + ASSERT_EQ(1u, task_runner_.GetTaskQueueSize()); + EXPECT_EQ(id2, GetFirstCommandToBeRemoved()); + task_runner_.RunOnce(); // Should remove task "id2" from queue. + EXPECT_EQ(0u, queue_.GetCount()); + EXPECT_EQ(0u, task_runner_.GetTaskQueueSize()); +} + TEST_F(CommandQueueTest, Dispatch) { FakeDispatcher dispatch(&queue_); const std::string id1 = "id1"; diff --git a/src/component_manager_impl.cc b/src/component_manager_impl.cc index 550775d..dec4a48 100644 --- a/src/component_manager_impl.cc +++ b/src/component_manager_impl.cc @@ -31,8 +31,10 @@ template <> LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap() : EnumToStringMap(kMap) {} -ComponentManagerImpl::ComponentManagerImpl(base::Clock* clock) - : clock_{clock ? clock : &default_clock_} {} +ComponentManagerImpl::ComponentManagerImpl(provider::TaskRunner* task_runner, + base::Clock* clock) + : clock_{clock ? clock : &default_clock_}, + command_queue_{task_runner, clock_} {} ComponentManagerImpl::~ComponentManagerImpl() {} diff --git a/src/component_manager_impl.h b/src/component_manager_impl.h index 8c4ad16..f3c5451 100644 --- a/src/component_manager_impl.h +++ b/src/component_manager_impl.h @@ -15,7 +15,8 @@ namespace weave { class ComponentManagerImpl final : public ComponentManager { public: - explicit ComponentManagerImpl(base::Clock* clock = nullptr); + explicit ComponentManagerImpl(provider::TaskRunner* task_runner, + base::Clock* clock = nullptr); ~ComponentManagerImpl() override; // Loads trait definition schema. diff --git a/src/component_manager_unittest.cc b/src/component_manager_unittest.cc index 63fedac..97dc00d 100644 --- a/src/component_manager_unittest.cc +++ b/src/component_manager_unittest.cc @@ -7,6 +7,7 @@ #include <map> #include <gtest/gtest.h> +#include <weave/provider/test/fake_task_runner.h> #include <weave/test/unittest_utils.h> #include "src/bind_lambda.h" @@ -90,8 +91,9 @@ class ComponentManagerTest : public ::testing::Test { {"t5", "t6"}, nullptr)); } + StrictMock<provider::test::FakeTaskRunner> task_runner_; StrictMock<test::MockClock> clock_; - ComponentManagerImpl manager_{&clock_}; + ComponentManagerImpl manager_{&task_runner_, &clock_}; }; } // anonymous namespace diff --git a/src/config_unittest.cc b/src/config_unittest.cc index fbb558a..4b0e5b4 100644 --- a/src/config_unittest.cc +++ b/src/config_unittest.cc @@ -68,7 +68,6 @@ TEST_F(ConfigTest, Defaults) { EXPECT_FALSE(GetSettings().device_id.empty()); EXPECT_EQ("", GetSettings().firmware_version); EXPECT_TRUE(GetSettings().wifi_auto_setup_enabled); - EXPECT_FALSE(GetSettings().disable_security); EXPECT_EQ("", GetSettings().test_privet_ssid); EXPECT_EQ(std::set<PairingType>{PairingType::kPinCode}, GetSettings().pairing_modes); @@ -164,8 +163,6 @@ TEST_F(ConfigTest, LoadState) { EXPECT_EQ("state_device_id", GetSettings().device_id); EXPECT_EQ(GetDefaultSettings().wifi_auto_setup_enabled, GetSettings().wifi_auto_setup_enabled); - EXPECT_EQ(GetDefaultSettings().disable_security, - GetSettings().disable_security); EXPECT_EQ(GetDefaultSettings().test_privet_ssid, GetSettings().test_privet_ssid); EXPECT_EQ(GetDefaultSettings().pairing_modes, GetSettings().pairing_modes); diff --git a/src/device_manager.cc b/src/device_manager.cc index 04d7a6b..097f854 100644 --- a/src/device_manager.cc +++ b/src/device_manager.cc @@ -29,7 +29,7 @@ DeviceManager::DeviceManager(provider::ConfigStore* config_store, provider::Wifi* wifi, provider::Bluetooth* bluetooth) : config_{new Config{config_store}}, - component_manager_{new ComponentManagerImpl} { + component_manager_{new ComponentManagerImpl{task_runner}} { if (http_server) { auth_manager_.reset(new privet::AuthManager( config_.get(), http_server->GetHttpsCertificateFingerprint())); diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc index cd11ac9..7908c8b 100644 --- a/src/device_registration_info_unittest.cc +++ b/src/device_registration_info_unittest.cc @@ -208,7 +208,7 @@ class DeviceRegistrationInfoTest : public ::testing::Test { {}, &clock_}; std::unique_ptr<DeviceRegistrationInfo> dev_reg_; - ComponentManagerImpl component_manager_; + ComponentManagerImpl component_manager_{&task_runner_}; }; TEST_F(DeviceRegistrationInfoTest, GetServiceURL) { diff --git a/src/privet/privet_manager.cc b/src/privet/privet_manager.cc index edc7907..9c717ce 100644 --- a/src/privet/privet_manager.cc +++ b/src/privet/privet_manager.cc @@ -53,8 +53,6 @@ void Manager::Start(Network* network, CHECK(auth_manager); CHECK(device); - disable_security_ = device->GetSettings().disable_security; - device_ = DeviceDelegate::CreateDefault( task_runner_, http_server->GetHttpPort(), http_server->GetHttpsPort(), http_server->GetRequestTimeout()); @@ -129,9 +127,6 @@ void Manager::PrivetRequestHandlerWithData( const std::shared_ptr<provider::HttpServer::Request>& request, const std::string& data) { std::string auth_header = request->GetFirstHeader(http::kAuthorization); - if (auth_header.empty() && disable_security_) - auth_header = "Privet anonymous"; - base::DictionaryValue empty; auto value = base::JSONReader::Read(data); const base::DictionaryValue* dictionary = ∅ diff --git a/src/privet/privet_manager.h b/src/privet/privet_manager.h index 371d843..06eb89a 100644 --- a/src/privet/privet_manager.h +++ b/src/privet/privet_manager.h @@ -79,7 +79,6 @@ class Manager : public CloudDelegate::Observer { void OnChanged(); void OnConnectivityChanged(); - bool disable_security_{false}; provider::TaskRunner* task_runner_{nullptr}; std::unique_ptr<CloudDelegate> cloud_; std::unique_ptr<DeviceDelegate> device_; diff --git a/src/privet/privet_types.cc b/src/privet/privet_types.cc index dd291b3..9e50f94 100644 --- a/src/privet/privet_types.cc +++ b/src/privet/privet_types.cc @@ -52,7 +52,6 @@ const EnumToStringMap<WifiType>::Map kWifiTypeMap[] = { }; const EnumToStringMap<CryptoType>::Map kCryptoTypeMap[] = { - {CryptoType::kNone, "none"}, {CryptoType::kSpake_p224, "p224_spake2"}, }; diff --git a/src/privet/privet_types.h b/src/privet/privet_types.h index c738865..49c4522 100644 --- a/src/privet/privet_types.h +++ b/src/privet/privet_types.h @@ -15,7 +15,6 @@ namespace weave { namespace privet { enum class CryptoType { - kNone, kSpake_p224, }; diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc index 358876d..0f00699 100644 --- a/src/privet/security_manager.cc +++ b/src/privet/security_manager.cc @@ -67,25 +67,6 @@ class Spakep224Exchanger : public SecurityManager::KeyExchanger { crypto::P224EncryptedKeyExchange spake_; }; -class UnsecureKeyExchanger : public SecurityManager::KeyExchanger { - public: - explicit UnsecureKeyExchanger(const std::string& password) - : password_(password) {} - ~UnsecureKeyExchanger() override = default; - - // SecurityManager::KeyExchanger methods. - const std::string& GetMessage() override { return password_; } - - bool ProcessMessage(const std::string& message, ErrorPtr* error) override { - return true; - } - - const std::string& GetKey() const override { return password_; } - - private: - std::string password_; -}; - } // namespace SecurityManager::SecurityManager(const Config* config, @@ -218,8 +199,6 @@ std::set<PairingType> SecurityManager::GetPairingTypes() const { std::set<CryptoType> SecurityManager::GetCryptoTypes() const { std::set<CryptoType> result{CryptoType::kSpake_p224}; - if (GetSettings().disable_security) - result.insert(CryptoType::kNone); return result; } @@ -259,8 +238,6 @@ const Config::Settings& SecurityManager::GetSettings() const { bool SecurityManager::IsValidPairingCode( const std::vector<uint8_t>& auth_code) const { - if (GetSettings().disable_security) - return true; for (const auto& session : confirmed_sessions_) { const std::string& key = session.second->GetKey(); const std::string& id = session.first; @@ -309,11 +286,6 @@ bool SecurityManager::StartPairing(PairingType mode, case CryptoType::kSpake_p224: spake.reset(new Spakep224Exchanger(code)); break; - case CryptoType::kNone: - if (GetSettings().disable_security) { - spake.reset(new UnsecureKeyExchanger(code)); - break; - } // Fall through... default: return Error::AddTo(error, FROM_HERE, errors::kInvalidParams, @@ -428,9 +400,6 @@ void SecurityManager::RegisterPairingListeners( } bool SecurityManager::CheckIfPairingAllowed(ErrorPtr* error) { - if (GetSettings().disable_security) - return true; - if (block_pairing_until_ > auth_manager_->Now()) { return Error::AddTo(error, FROM_HERE, errors::kDeviceBusy, "Too many pairing attempts"); diff --git a/src/privet/wifi_bootstrap_manager.cc b/src/privet/wifi_bootstrap_manager.cc index 566da80..ce2016a 100644 --- a/src/privet/wifi_bootstrap_manager.cc +++ b/src/privet/wifi_bootstrap_manager.cc @@ -204,8 +204,12 @@ std::string WifiBootstrapManager::GetHostedSsid() const { } std::set<WifiType> WifiBootstrapManager::GetTypes() const { - // TODO(wiley) This should do some system work to figure this out. - return {WifiType::kWifi24}; + std::set<WifiType> result; + if (wifi_->IsWifi24Supported()) + result.insert(WifiType::kWifi24); + if (wifi_->IsWifi50Supported()) + result.insert(WifiType::kWifi50); + return result; } void WifiBootstrapManager::OnConnectDone(const std::string& ssid, @@ -255,9 +259,15 @@ void WifiBootstrapManager::OnMonitorTimeout() { void WifiBootstrapManager::UpdateConnectionState() { connection_state_ = ConnectionState{ConnectionState::kUnconfigured}; - Network::State service_state{network_->GetConnectionState()}; VLOG(3) << "New network state: " << EnumToString(service_state); + + // TODO: Make it true wifi state, currently it's rather online state. + if (service_state != Network::State::kOnline && + config_->GetSettings().last_configured_ssid.empty()) { + return; + } + switch (service_state) { case Network::State::kOffline: connection_state_ = ConnectionState{ConnectionState::kOffline}; diff --git a/src/privet/wifi_ssid_generator.cc b/src/privet/wifi_ssid_generator.cc index 697e5d8..4ad1602 100644 --- a/src/privet/wifi_ssid_generator.cc +++ b/src/privet/wifi_ssid_generator.cc @@ -52,20 +52,27 @@ WifiSsidGenerator::WifiSsidGenerator(const CloudDelegate* cloud, } std::string WifiSsidGenerator::GenerateFlags() const { - return GenerateFlagsInternal(false); + return GenerateFlagsInternal(); } -std::string WifiSsidGenerator::GenerateFlagsInternal(bool for_ssid) const { +std::string WifiSsidGenerator::GenerateFlagsInternal() const { std::bitset<6> flags1; // Device needs WiFi configuration. flags1[0] = wifi_ && IsSetupNeeded(wifi_->GetConnectionState()); + // Device needs GCD registration. flags1[1] = IsSetupNeeded(gcd_->GetConnectionState()); std::bitset<6> flags2; - // Device is discoverable over WiFi. - flags2[0] = for_ssid || (wifi_ && !wifi_->GetHostedSsid().empty()); + if (wifi_) { + std::set<WifiType> types = wifi_->GetTypes(); + // Device supports 2.4Ghz WiFi networks. + flags2[0] = types.find(WifiType::kWifi24) != types.end(); + + // Device supports 5.0Ghz WiFi networks. + flags2[1] = types.find(WifiType::kWifi50) != types.end(); + } std::string result{2, base64chars[0]}; result[0] = base64chars[flags1.to_ulong()]; @@ -82,7 +89,7 @@ std::string WifiSsidGenerator::GenerateSsid() const { std::string result = base::StringPrintf(kSsidFormat, name.c_str(), idx.c_str(), - model_id.c_str(), GenerateFlagsInternal(true).c_str()); + model_id.c_str(), GenerateFlagsInternal().c_str()); CHECK_EQ(result[result.size() - 11], '.'); return result; } diff --git a/src/privet/wifi_ssid_generator.h b/src/privet/wifi_ssid_generator.h index 2b86f28..1197e73 100644 --- a/src/privet/wifi_ssid_generator.h +++ b/src/privet/wifi_ssid_generator.h @@ -30,7 +30,7 @@ class WifiSsidGenerator final { // Sets object to use |n| instead of random number for SSID generation. void SetRandomForTests(int n); - std::string GenerateFlagsInternal(bool for_ssid) const; + std::string GenerateFlagsInternal() const; const CloudDelegate* gcd_{nullptr}; const WifiDelegate* wifi_{nullptr}; diff --git a/src/privet/wifi_ssid_generator_unittest.cc b/src/privet/wifi_ssid_generator_unittest.cc index 10680c8..406576d 100644 --- a/src/privet/wifi_ssid_generator_unittest.cc +++ b/src/privet/wifi_ssid_generator_unittest.cc @@ -22,40 +22,44 @@ class WifiSsidGeneratorTest : public testing::Test { WifiSsidGenerator ssid_generator_{&gcd_, &wifi_}; }; -TEST_F(WifiSsidGeneratorTest, GenerateFlagsNoHostedAp) { +TEST_F(WifiSsidGeneratorTest, GenerateFlagsWithWifi24) { + EXPECT_CALL(wifi_, GetTypes()) + .WillRepeatedly(Return(std::set<WifiType>{WifiType::kWifi24})); + EXPECT_EQ(ssid_generator_.GenerateFlags().size(), 2u); wifi_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured}; gcd_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured}; - EXPECT_EQ("DA", ssid_generator_.GenerateFlags()); + EXPECT_EQ("DB", ssid_generator_.GenerateFlags()); wifi_.connection_state_ = ConnectionState{ConnectionState::kOnline}; - EXPECT_EQ("CA", ssid_generator_.GenerateFlags()); + EXPECT_EQ("CB", ssid_generator_.GenerateFlags()); gcd_.connection_state_ = ConnectionState{ConnectionState::kOffline}; - EXPECT_EQ("AA", ssid_generator_.GenerateFlags()); + EXPECT_EQ("AB", ssid_generator_.GenerateFlags()); wifi_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured}; - EXPECT_EQ("BA", ssid_generator_.GenerateFlags()); + EXPECT_EQ("BB", ssid_generator_.GenerateFlags()); } -TEST_F(WifiSsidGeneratorTest, GenerateFlagsWithHostedAp) { - EXPECT_CALL(wifi_, GetHostedSsid()) - .WillRepeatedly(Return(ssid_generator_.GenerateSsid())); +TEST_F(WifiSsidGeneratorTest, GenerateFlagsWithWifi50) { + EXPECT_CALL(wifi_, GetTypes()) + .WillRepeatedly(Return(std::set<WifiType>{WifiType::kWifi50})); + EXPECT_EQ(ssid_generator_.GenerateFlags().size(), 2u); wifi_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured}; gcd_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured}; - EXPECT_EQ("DB", ssid_generator_.GenerateFlags()); + EXPECT_EQ("DC", ssid_generator_.GenerateFlags()); wifi_.connection_state_ = ConnectionState{ConnectionState::kOnline}; - EXPECT_EQ("CB", ssid_generator_.GenerateFlags()); + EXPECT_EQ("CC", ssid_generator_.GenerateFlags()); gcd_.connection_state_ = ConnectionState{ConnectionState::kOffline}; - EXPECT_EQ("AB", ssid_generator_.GenerateFlags()); + EXPECT_EQ("AC", ssid_generator_.GenerateFlags()); wifi_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured}; - EXPECT_EQ("BB", ssid_generator_.GenerateFlags()); + EXPECT_EQ("BC", ssid_generator_.GenerateFlags()); } TEST_F(WifiSsidGeneratorTest, GenerateSsid31orLess) { diff --git a/src/test/fake_task_runner.cc b/src/test/fake_task_runner.cc index 88e078b..68d5e32 100644 --- a/src/test/fake_task_runner.cc +++ b/src/test/fake_task_runner.cc @@ -52,6 +52,10 @@ void FakeTaskRunner::PostDelayedTask(const tracked_objects::Location& from_here, queue_.emplace(std::make_pair(test_clock_->Now() + delay, ++counter_), task); } +size_t FakeTaskRunner::GetTaskQueueSize() const { + return queue_.size(); +} + } // namespace test } // namespace provider } // namespace weave diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc index ebc66cd..b300f57 100644 --- a/src/weave_unittest.cc +++ b/src/weave_unittest.cc @@ -181,7 +181,10 @@ std::set<typename Map::key_type> GetKeys(const Map& map) { class WeaveTest : public ::testing::Test { protected: - void SetUp() override {} + void SetUp() override { + EXPECT_CALL(wifi_, IsWifi24Supported()).WillRepeatedly(Return(true)); + EXPECT_CALL(wifi_, IsWifi50Supported()).WillRepeatedly(Return(false)); + } template <class UrlMatcher> void ExpectRequest(HttpClient::Method method, diff --git a/third_party/get_libevent.sh b/third_party/get_libevent.sh deleted file mode 100755 index 9985bc0..0000000 --- a/third_party/get_libevent.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Copyright 2015 The Weave Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Make libevent. -# Example uses libevent to implement HTTPS server. This capability is -# available only in version 2.1.x-alpha. Step could be replaced with apt-get -# in future. -cd $(dirname "$0") -THIRD_PARTY=$(pwd) - -mkdir -p include lib - -rm -rf $THIRD_PARTY/libevent -git clone https://github.com/libevent/libevent.git || exit 1 -cd libevent || exit 1 - -./autogen.sh || exit 1 -./configure --disable-shared || exit 1 -make || exit 1 -if [ -z "$DISABLE_LIBEVENT_TEST" ]; then - echo -e "\n\nTesting libevent...\nCan take several minutes.\n" - make verify || exit 1 -fi -cp -rf include/*.h include/event2 $THIRD_PARTY/include/ || exit 1 -cp -f .libs/lib* $THIRD_PARTY/lib/ || exit 1 - -rm -rf $THIRD_PARTY/libevent diff --git a/third_party/get_libevhtp.sh b/third_party/get_libevhtp.sh new file mode 100755 index 0000000..c270813 --- /dev/null +++ b/third_party/get_libevhtp.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright 2016 The Weave Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Make libevhtp. +# Example uses libevhtp to implement HTTPS server. This step could be +# replaced with apt-get in future (Debian jessie, Ubuntu vivid). +cd $(dirname "$0") +THIRD_PARTY=$(pwd) + +mkdir -p include lib + +rm -rf $THIRD_PARTY/libevhtp +curl -L https://github.com/ellzey/libevhtp/archive/1.2.10.tar.gz | tar xz || exit 1 +mv libevhtp-1.2.10 $THIRD_PARTY/libevhtp || exit 1 +cd $THIRD_PARTY/libevhtp || exit 1 + +cmake -D EVHTP_DISABLE_REGEX:BOOL=ON . || exit 1 +make evhtp || exit 1 + +cp -rf evhtp-config.h evhtp.h evthr/evthr.h htparse/htparse.h $THIRD_PARTY/include/ || exit 1 +cp -f libevhtp.a $THIRD_PARTY/lib/ || exit 1 + +rm -rf $THIRD_PARTY/libevhtp diff --git a/third_party/third_party.mk b/third_party/third_party.mk index 8a11e2d..7f651a2 100644 --- a/third_party/third_party.mk +++ b/third_party/third_party.mk @@ -64,15 +64,15 @@ clean-gtest : rm -rf third_party/googletest ### -# libevent (third_party, downloaded on build) - -third_party/include/event2/event.h : - @echo Downloading and building libevent... - DISABLE_LIBEVENT_TEST=1 third_party/get_libevent.sh - @echo Finished downloading and building libevent. - -clean-libevent : - rm -rf third_party/include/ev* third_party/include/event2 - rm -rf third_party/lib/libevent* - rm -rf third_party/libevent - +# libevhtp (third_party, downloaded on build) + +third_party/lib/libevhtp.a : third_party/include/evhtp.h +third_party/include/evhtp.h : + @echo Downloading and building libevhtp... + third_party/get_libevhtp.sh + @echo Finished downloading and building libevhtp. + +clean-libevhtp : + rm -rf third_party/include/evhtp.h third_party/include/evhtp-config.h third_party/include/evthr.h third_party/include/htparse.h + rm -rf third_party/lib/libevhtp.a + rm -rf third_party/libevhtp |