Vitaly Buka | 4615e0d | 2015-10-14 15:35:12 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Weave Authors. All rights reserved. |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Johan Euphrosine | 3523fdd | 2015-10-14 20:46:05 -0700 | [diff] [blame] | 5 | #include "examples/provider/event_http_server.h" |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 6 | |
| 7 | #include <vector> |
| 8 | |
| 9 | #include <base/bind.h> |
| 10 | #include <base/time/time.h> |
| 11 | #include <event2/bufferevent_ssl.h> |
| 12 | #include <openssl/err.h> |
| 13 | |
Johan Euphrosine | 3523fdd | 2015-10-14 20:46:05 -0700 | [diff] [blame] | 14 | #include "examples/provider/event_task_runner.h" |
Alex Vakulenko | 57fbee3 | 2015-09-21 11:04:46 -0700 | [diff] [blame] | 15 | |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 16 | namespace weave { |
| 17 | namespace examples { |
| 18 | |
| 19 | namespace { |
| 20 | |
| 21 | std::string GetSslError() { |
| 22 | char error[1000] = {}; |
| 23 | ERR_error_string_n(ERR_get_error(), error, sizeof(error)); |
| 24 | return error; |
| 25 | } |
| 26 | |
| 27 | bufferevent* BuffetEventCallback(event_base* base, void* arg) { |
| 28 | SSL_CTX* ctx = static_cast<SSL_CTX*>(arg); |
| 29 | return bufferevent_openssl_socket_new( |
| 30 | base, -1, SSL_new(ctx), BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE); |
| 31 | } |
| 32 | |
| 33 | } // namespace |
| 34 | |
| 35 | class HttpServerImpl::RequestImpl : public Request { |
| 36 | public: |
Vitaly Buka | 5fe7609 | 2015-10-08 13:37:53 -0700 | [diff] [blame] | 37 | RequestImpl(evhttp_request* req, provider::TaskRunner* task_runner) |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 38 | : task_runner_{task_runner} { |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 39 | req_.reset(req); |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 40 | uri_ = evhttp_request_get_evhttp_uri(req_.get()); |
Vitaly Buka | 533dd42 | 2015-10-09 18:57:51 -0700 | [diff] [blame] | 41 | |
| 42 | data_.resize(evbuffer_get_length(req_->input_buffer)); |
| 43 | evbuffer_remove(req_->input_buffer, &data_[0], data_.size()); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 44 | } |
| 45 | |
| 46 | ~RequestImpl() {} |
| 47 | |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 48 | std::string GetPath() const override { |
| 49 | const char* path = evhttp_uri_get_path(uri_); |
| 50 | return path ? path : ""; |
| 51 | } |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 52 | std::string GetFirstHeader(const std::string& name) const override { |
| 53 | const char* header = evhttp_find_header(req_->input_headers, name.c_str()); |
| 54 | if (!header) |
| 55 | return {}; |
| 56 | return header; |
| 57 | } |
Vitaly Buka | 533dd42 | 2015-10-09 18:57:51 -0700 | [diff] [blame] | 58 | std::string GetData() { return data_; } |
Vitaly Buka | 5fe7609 | 2015-10-08 13:37:53 -0700 | [diff] [blame] | 59 | |
| 60 | void SendReply(int status_code, |
| 61 | const std::string& data, |
| 62 | const std::string& mime_type) override { |
| 63 | std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(), |
| 64 | &evbuffer_free}; |
| 65 | evbuffer_add(buf.get(), data.data(), data.size()); |
| 66 | evhttp_add_header(req_->output_headers, "Content-Type", mime_type.c_str()); |
| 67 | evhttp_send_reply(req_.release(), status_code, "None", buf.get()); |
Alex Vakulenko | 57fbee3 | 2015-09-21 11:04:46 -0700 | [diff] [blame] | 68 | } |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 69 | |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 70 | private: |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 71 | std::unique_ptr<evhttp_request, decltype(&evhttp_cancel_request)> req_{ |
| 72 | nullptr, &evhttp_cancel_request}; |
Vitaly Buka | 5fe7609 | 2015-10-08 13:37:53 -0700 | [diff] [blame] | 73 | provider::TaskRunner* task_runner_{nullptr}; |
Vitaly Buka | 533dd42 | 2015-10-09 18:57:51 -0700 | [diff] [blame] | 74 | std::string data_; |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 75 | const evhttp_uri* uri_{nullptr}; |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 76 | }; |
| 77 | |
Alex Vakulenko | 57fbee3 | 2015-09-21 11:04:46 -0700 | [diff] [blame] | 78 | HttpServerImpl::HttpServerImpl(EventTaskRunner* task_runner) |
| 79 | : task_runner_{task_runner} { |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 80 | SSL_load_error_strings(); |
| 81 | SSL_library_init(); |
| 82 | |
| 83 | ctx_.reset(SSL_CTX_new(TLSv1_2_server_method())); |
| 84 | SSL_CTX_set_options(ctx_.get(), SSL_OP_SINGLE_DH_USE | |
| 85 | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_SSLv2); |
| 86 | |
| 87 | ec_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); |
| 88 | CHECK(ec_key_) << GetSslError(); |
| 89 | CHECK_EQ(1, SSL_CTX_set_tmp_ecdh(ctx_.get(), ec_key_.get())) << GetSslError(); |
| 90 | |
| 91 | GenerateX509(); |
| 92 | CHECK_EQ(1, SSL_CTX_use_PrivateKey(ctx_.get(), pkey_.get())) << GetSslError(); |
| 93 | CHECK_EQ(1, SSL_CTX_use_certificate(ctx_.get(), x509_.get())) |
| 94 | << GetSslError(); |
| 95 | |
| 96 | CHECK_EQ(1, SSL_CTX_check_private_key(ctx_.get())) << GetSslError(); |
| 97 | |
Alex Vakulenko | 57fbee3 | 2015-09-21 11:04:46 -0700 | [diff] [blame] | 98 | httpd_.reset(evhttp_new(task_runner_->GetEventBase())); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 99 | CHECK(httpd_); |
Alex Vakulenko | 57fbee3 | 2015-09-21 11:04:46 -0700 | [diff] [blame] | 100 | httpsd_.reset(evhttp_new(task_runner_->GetEventBase())); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 101 | CHECK(httpsd_); |
| 102 | |
| 103 | evhttp_set_bevcb(httpsd_.get(), BuffetEventCallback, ctx_.get()); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 104 | |
| 105 | CHECK_EQ(0, evhttp_bind_socket(httpd_.get(), "0.0.0.0", GetHttpPort())); |
| 106 | CHECK_EQ(0, evhttp_bind_socket(httpsd_.get(), "0.0.0.0", GetHttpsPort())); |
| 107 | } |
| 108 | |
| 109 | void HttpServerImpl::GenerateX509() { |
| 110 | x509_.reset(X509_new()); |
| 111 | CHECK(x509_) << GetSslError(); |
| 112 | |
| 113 | X509_set_version(x509_.get(), 2); |
| 114 | |
| 115 | X509_gmtime_adj(X509_get_notBefore(x509_.get()), 0); |
| 116 | X509_gmtime_adj(X509_get_notAfter(x509_.get()), |
| 117 | base::TimeDelta::FromDays(365).InSeconds()); |
| 118 | |
| 119 | pkey_.reset(EVP_PKEY_new()); |
| 120 | CHECK(pkey_) << GetSslError(); |
| 121 | std::unique_ptr<BIGNUM, decltype(&BN_free)> big_num(BN_new(), &BN_free); |
| 122 | CHECK(BN_set_word(big_num.get(), 65537)) << GetSslError(); |
| 123 | auto rsa = RSA_new(); |
| 124 | RSA_generate_key_ex(rsa, 2048, big_num.get(), nullptr); |
| 125 | CHECK(EVP_PKEY_assign_RSA(pkey_.get(), rsa)) << GetSslError(); |
| 126 | |
| 127 | X509_set_pubkey(x509_.get(), pkey_.get()); |
| 128 | |
| 129 | CHECK(X509_sign(x509_.get(), pkey_.get(), EVP_sha256())) << GetSslError(); |
| 130 | |
| 131 | cert_fingerprint_.resize(EVP_MD_size(EVP_sha256())); |
| 132 | uint32_t len = 0; |
| 133 | CHECK(X509_digest(x509_.get(), EVP_sha256(), cert_fingerprint_.data(), &len)); |
| 134 | CHECK_EQ(len, cert_fingerprint_.size()); |
| 135 | } |
| 136 | |
| 137 | void HttpServerImpl::ProcessRequestCallback(evhttp_request* req, void* arg) { |
| 138 | static_cast<HttpServerImpl*>(arg)->ProcessRequest(req); |
| 139 | } |
| 140 | |
| 141 | void HttpServerImpl::NotFound(evhttp_request* req) { |
| 142 | std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(), |
| 143 | &evbuffer_free}; |
| 144 | evbuffer_add_printf(buf.get(), "404 Not Found: %s\n", |
| 145 | evhttp_request_uri(req)); |
| 146 | evhttp_send_reply(req, 404, "Not Found", buf.get()); |
| 147 | } |
| 148 | |
| 149 | void HttpServerImpl::ProcessRequest(evhttp_request* req) { |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 150 | std::unique_ptr<RequestImpl> request{new RequestImpl{req, task_runner_}}; |
| 151 | std::string path = request->GetPath(); |
| 152 | auto it = handlers_.find(path); |
| 153 | if (it != handlers_.end()) { |
| 154 | return it->second.Run(std::move(request)); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 155 | } |
| 156 | NotFound(req); |
| 157 | } |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 158 | |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 159 | void HttpServerImpl::ProcessReply(std::shared_ptr<RequestImpl> request, |
| 160 | int status_code, |
| 161 | const std::string& data, |
Johan Euphrosine | 1ca3c22 | 2015-10-15 20:43:42 -0700 | [diff] [blame] | 162 | const std::string& mime_type) {} |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 163 | |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 164 | void HttpServerImpl::AddHttpRequestHandler( |
| 165 | const std::string& path, |
| 166 | const RequestHandlerCallback& callback) { |
| 167 | handlers_.emplace(path, callback); |
| 168 | evhttp_set_cb(httpd_.get(), path.c_str(), &ProcessRequestCallback, this); |
| 169 | } |
| 170 | |
| 171 | void HttpServerImpl::AddHttpsRequestHandler( |
| 172 | const std::string& path, |
| 173 | const RequestHandlerCallback& callback) { |
| 174 | handlers_.emplace(path, callback); |
| 175 | evhttp_set_cb(httpsd_.get(), path.c_str(), &ProcessRequestCallback, this); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 176 | } |
| 177 | |
| 178 | uint16_t HttpServerImpl::GetHttpPort() const { |
| 179 | return 7780; |
| 180 | } |
| 181 | |
| 182 | uint16_t HttpServerImpl::GetHttpsPort() const { |
| 183 | return 7781; |
| 184 | } |
| 185 | |
Alex Vakulenko | efee3a2 | 2015-11-17 15:08:38 -0800 | [diff] [blame] | 186 | base::TimeDelta HttpServerImpl::GetRequestTimeout() const { |
| 187 | return base::TimeDelta::Max(); |
| 188 | } |
| 189 | |
Vitaly Buka | 138aec4 | 2015-10-08 10:17:48 -0700 | [diff] [blame] | 190 | std::vector<uint8_t> HttpServerImpl::GetHttpsCertificateFingerprint() const { |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 191 | return cert_fingerprint_; |
| 192 | } |
| 193 | |
| 194 | } // namespace examples |
| 195 | } // namespace weave |