| // 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 <vector> |
| |
| #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" |
| |
| 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<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 { 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<evbuffer> 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<evhtp_request_t> req_; |
| std::string data_; |
| }; |
| |
| HttpServerImpl::HttpServerImpl(EventTaskRunner* task_runner) |
| : task_runner_{task_runner} { |
| SSL_load_error_strings(); |
| SSL_library_init(); |
| |
| 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(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<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, 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<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(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()) { |
| return it->second.Run(std::move(request)); |
| } |
| NotFound(req); |
| } |
| |
| 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)); |
| 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<RequestImpl> 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<uint8_t> HttpServerImpl::GetHttpsCertificateFingerprint() const { |
| return cert_fingerprint_; |
| } |
| |
| } // namespace examples |
| } // namespace weave |