blob: 9c0dc786dcc0947981b9543d092cf5e98ca1dc5f [file] [log] [blame]
// Copyright 2015 The Chromium OS 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/ubuntu/event_http_server.h"
#include <vector>
#include <base/bind.h>
#include <base/time/time.h>
#include <event2/bufferevent_ssl.h>
#include <openssl/err.h>
#include "examples/ubuntu/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;
}
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);
}
class MemoryReadStream : public Stream {
public:
MemoryReadStream(const std::vector<uint8_t>& data,
provider::TaskRunner* task_runner)
: data_{data}, task_runner_{task_runner} {}
void Read(void* buffer,
size_t size_to_read,
const ReadSuccessCallback& success_callback,
const ErrorCallback& error_callback) override {
CHECK_LE(read_position_, data_.size());
size_t size_read = std::min(size_to_read, data_.size() - read_position_);
if (size_read > 0)
memcpy(buffer, data_.data() + read_position_, size_read);
read_position_ += size_read;
success_callback.Run(size_read);
}
void Write(const void* buffer,
size_t size_to_write,
const SuccessCallback& success_callback,
const ErrorCallback& error_callback) override {
LOG(FATAL) << "Unsupported";
}
void CancelPendingOperations() override {}
private:
std::vector<uint8_t> data_;
provider::TaskRunner* task_runner_;
size_t read_position_{0};
};
} // namespace
class HttpServerImpl::RequestImpl : public Request {
public:
explicit RequestImpl(evhttp_request* req, provider::TaskRunner* task_runner)
: path_{evhttp_request_uri(req)}, task_runner_{task_runner} {
path_ = path_.substr(0, path_.find("?"));
path_ = path_.substr(0, path_.find("#"));
req_.reset(req);
data_.resize(evbuffer_get_length(req_->input_buffer));
evbuffer_remove(req_->input_buffer, data_.data(), data_.size());
}
~RequestImpl() {}
const std::string& GetPath() const override { return path_; }
std::string GetFirstHeader(const std::string& name) const override {
const char* header = evhttp_find_header(req_->input_headers, name.c_str());
if (!header)
return {};
return header;
}
const std::vector<uint8_t>& GetData() const override { return data_; }
std::unique_ptr<Stream> GetDataStream() const override {
return std::unique_ptr<Stream>{new MemoryReadStream{data_, task_runner_}};
}
evhttp_request* ReleaseHandler() { return req_.release(); }
private:
std::vector<uint8_t> data_;
std::unique_ptr<evhttp_request, decltype(&evhttp_cancel_request)> req_{
nullptr, &evhttp_cancel_request};
std::string path_;
provider::TaskRunner* task_runner_;
};
HttpServerImpl::HttpServerImpl(EventTaskRunner* task_runner)
: task_runner_{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()));
CHECK(httpd_);
httpsd_.reset(evhttp_new(task_runner_->GetEventBase()));
CHECK(httpsd_);
evhttp_set_bevcb(httpsd_.get(), BuffetEventCallback, ctx_.get());
evhttp_set_gencb(httpd_.get(), ProcessRequestCallback, this);
evhttp_set_gencb(httpsd_.get(), ProcessRequestCallback, this);
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()));
}
void HttpServerImpl::GenerateX509() {
x509_.reset(X509_new());
CHECK(x509_) << GetSslError();
X509_set_version(x509_.get(), 2);
X509_gmtime_adj(X509_get_notBefore(x509_.get()), 0);
X509_gmtime_adj(X509_get_notAfter(x509_.get()),
base::TimeDelta::FromDays(365).InSeconds());
pkey_.reset(EVP_PKEY_new());
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();
X509_set_pubkey(x509_.get(), pkey_.get());
CHECK(X509_sign(x509_.get(), pkey_.get(), 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_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::ProcessRequest(evhttp_request* req) {
std::string path = evhttp_request_uri(req);
for (auto i = handlers_.rbegin(); i != handlers_.rend(); ++i) {
if (path.compare(0, i->first.size(), i->first) == 0) {
auto request = std::make_shared<RequestImpl>(req, task_runner_);
i->second.Run(*request,
base::Bind(&HttpServerImpl::ProcessReply,
weak_ptr_factory_.GetWeakPtr(), request));
return;
}
}
NotFound(req);
}
void HttpServerImpl::ProcessReply(std::shared_ptr<RequestImpl> request,
int status_code,
const std::string& data,
const std::string& mime_type) {
std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(),
&evbuffer_free};
evbuffer_add(buf.get(), data.data(), data.size());
evhttp_request* req = request->ReleaseHandler();
evhttp_add_header(req->output_headers, "Content-Type", mime_type.c_str());
evhttp_send_reply(req, status_code, "None", buf.get());
}
void HttpServerImpl::AddOnStateChangedCallback(
const OnStateChangedCallback& callback) {
callback.Run(*this);
}
void HttpServerImpl::AddRequestHandler(const std::string& path_prefix,
const OnRequestCallback& callback) {
handlers_.emplace(path_prefix, callback);
}
uint16_t HttpServerImpl::GetHttpPort() const {
return 7780;
}
uint16_t HttpServerImpl::GetHttpsPort() const {
return 7781;
}
const std::vector<uint8_t>& HttpServerImpl::GetHttpsCertificateFingerprint()
const {
return cert_fingerprint_;
}
} // namespace examples
} // namespace weave