// 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. #include "examples/provider/event_http_server.h" #include #include #include #include #include #include #include "examples/provider/event_task_runner.h" namespace weave { namespace examples { namespace { std::string GetSslError() { char error[1000] = {}; ERR_error_string_n(ERR_get_error(), error, sizeof(error)); return error; } } // namespace class HttpServerImpl::RequestImpl : public Request { public: RequestImpl(EventPtr 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 { return req_->uri->path->path; } std::string GetFirstHeader(const std::string& name) const override { 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 { EventPtr buf{evbuffer_new()}; evbuffer_add(buf.get(), data.data(), data.size()); 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: EventPtr req_; std::string data_; }; HttpServerImpl::HttpServerImpl(EventTaskRunner* task_runner) : task_runner_{task_runner} { SSL_load_error_strings(); SSL_library_init(); std::unique_ptr 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{ 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{X509_new(), &X509_free}; CHECK(x509); std::unique_ptr 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(evhtp_new(task_runner_->GetEventBase(), nullptr)); CHECK(httpsd_); httpsd_.get()->ssl_ctx = ctx.release(); 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* x509, EVP_PKEY* pkey) { CHECK(x509) << GetSslError(); X509_set_version(x509, 2); X509_gmtime_adj(X509_get_notBefore(x509), 0); X509_gmtime_adj(X509_get_notAfter(x509), base::TimeDelta::FromDays(365).InSeconds()); CHECK(pkey) << GetSslError(); std::unique_ptr 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, rsa)) << GetSslError(); X509_set_pubkey(x509, pkey); CHECK(X509_sign(x509, pkey, EVP_sha256())) << GetSslError(); cert_fingerprint_.resize(EVP_MD_size(EVP_sha256())); uint32_t len = 0; CHECK(X509_digest(x509, EVP_sha256(), cert_fingerprint_.data(), &len)); CHECK_EQ(len, cert_fingerprint_.size()); } void HttpServerImpl::NotFound(evhtp_request_t* req) { EventPtr 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(evhtp_request_t* req) { std::unique_ptr request{new RequestImpl{EventPtr{req}}}; std::string path = request->GetPath(); auto it = handlers_.find(path); if (it != handlers_.end()) { return it->second.Run(std::move(request)); } NotFound(req); } void HttpServerImpl::ProcessRequestCallback(evhtp_request_t* req, void* arg) { static_cast(arg)->ProcessRequest(req); } void HttpServerImpl::AddHttpRequestHandler( const std::string& path, const RequestHandlerCallback& callback) { handlers_.insert(std::make_pair(path, callback)); 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)); evhtp_set_cb(httpsd_.get(), path.c_str(), &ProcessRequestCallback, this); } void HttpServerImpl::ProcessReply(std::shared_ptr request, int status_code, const std::string& data, const std::string& mime_type) {} uint16_t HttpServerImpl::GetHttpPort() const { return 7780; } uint16_t HttpServerImpl::GetHttpsPort() const { return 7781; } base::TimeDelta HttpServerImpl::GetRequestTimeout() const { return base::TimeDelta::Max(); } std::vector HttpServerImpl::GetHttpsCertificateFingerprint() const { return cert_fingerprint_; } } // namespace examples } // namespace weave