nghttpx: Add basic infrastructure for mruby support

This commit is contained in:
Tatsuhiro Tsujikawa 2015-09-02 00:19:32 +09:00
parent dd0a72579f
commit 1508c50a45
17 changed files with 573 additions and 11 deletions

View File

@ -98,6 +98,7 @@ OPTIONS = [
"tls-ticket-key-memcached-interval", "tls-ticket-key-memcached-interval",
"tls-ticket-key-memcached-max-retry", "tls-ticket-key-memcached-max-retry",
"tls-ticket-key-memcached-max-fail", "tls-ticket-key-memcached-max-fail",
"on-request-mruby-file",
"conf", "conf",
] ]

View File

@ -124,6 +124,8 @@ NGHTTPX_SRCS = \
shrpx_memcached_connection.cc shrpx_memcached_connection.h \ shrpx_memcached_connection.cc shrpx_memcached_connection.h \
shrpx_memcached_request.h \ shrpx_memcached_request.h \
shrpx_memcached_result.h \ shrpx_memcached_result.h \
shrpx_mruby.cc shrpx_mruby.h \
shrpx_mruby_module.cc shrpx_mruby_module.h \
buffer.h memchunk.h template.h buffer.h memchunk.h template.h
if HAVE_SPDYLAY if HAVE_SPDYLAY
@ -134,7 +136,7 @@ noinst_LIBRARIES = libnghttpx.a
libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
nghttpx_SOURCES = shrpx.cc shrpx.h nghttpx_SOURCES = shrpx.cc shrpx.h
nghttpx_LDADD = libnghttpx.a ${LDADD} nghttpx_LDADD = libnghttpx.a ${LDADD} -lmruby
if HAVE_CUNIT if HAVE_CUNIT
check_PROGRAMS += nghttpx-unittest check_PROGRAMS += nghttpx-unittest
@ -150,7 +152,7 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
memchunk_test.cc memchunk_test.h memchunk_test.cc memchunk_test.h
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} -lmruby @CUNIT_LIBS@ @TESTLDADD@
TESTS += nghttpx-unittest TESTS += nghttpx-unittest
endif # HAVE_CUNIT endif # HAVE_CUNIT

View File

@ -926,9 +926,13 @@ int event_loop() {
#endif // !NOTHREADS #endif // !NOTHREADS
if (get_config()->num_worker == 1) { if (get_config()->num_worker == 1) {
conn_handler->create_single_worker(); rv = conn_handler->create_single_worker();
} else { } else {
conn_handler->create_worker_thread(get_config()->num_worker); rv = conn_handler->create_worker_thread(get_config()->num_worker);
}
if (rv != 0) {
return -1;
} }
#ifndef NOTHREADS #ifndef NOTHREADS
@ -1899,6 +1903,7 @@ int main(int argc, char **argv) {
89}, 89},
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, required_argument, &flag, {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, required_argument, &flag,
90}, 90},
{SHRPX_OPT_ON_REQUEST_MRUBY_FILE, required_argument, &flag, 91},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
@ -2296,6 +2301,10 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL,
optarg); optarg);
break; break;
case 91:
// --on-request-mruby-file
cmdcfgs.emplace_back(SHRPX_OPT_ON_REQUEST_MRUBY_FILE, optarg);
break;
default: default:
break; break;
} }
@ -2613,7 +2622,9 @@ int main(int argc, char **argv) {
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, nullptr); sigaction(SIGPIPE, &act, nullptr);
event_loop(); if (event_loop() != 0) {
return -1;
}
LOG(NOTICE) << "Shutdown momentarily"; LOG(NOTICE) << "Shutdown momentarily";

View File

@ -690,6 +690,7 @@ enum {
SHRPX_OPTID_NO_VIA, SHRPX_OPTID_NO_VIA,
SHRPX_OPTID_NPN_LIST, SHRPX_OPTID_NPN_LIST,
SHRPX_OPTID_OCSP_UPDATE_INTERVAL, SHRPX_OPTID_OCSP_UPDATE_INTERVAL,
SHRPX_OPTID_ON_REQUEST_MRUBY_FILE,
SHRPX_OPTID_PADDING, SHRPX_OPTID_PADDING,
SHRPX_OPTID_PID_FILE, SHRPX_OPTID_PID_FILE,
SHRPX_OPTID_PRIVATE_KEY_FILE, SHRPX_OPTID_PRIVATE_KEY_FILE,
@ -1089,6 +1090,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD; return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
} }
break; break;
case 'e':
if (util::strieq_l("on-request-mruby-fil", name, 20)) {
return SHRPX_OPTID_ON_REQUEST_MRUBY_FILE;
}
break;
case 'r': case 'r':
if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) { if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER; return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
@ -1938,6 +1944,10 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL: case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL:
return parse_uint(&mod_config()->tls_ticket_key_memcached_max_fail, opt, return parse_uint(&mod_config()->tls_ticket_key_memcached_max_fail, opt,
optarg); optarg);
case SHRPX_OPTID_ON_REQUEST_MRUBY_FILE:
mod_config()->on_request_mruby_file = strcopy(optarg);
return 0;
case SHRPX_OPTID_CONF: case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored"; LOG(WARN) << "conf: ignored";

View File

@ -183,6 +183,7 @@ constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY[] =
"tls-ticket-key-memcached-max-retry"; "tls-ticket-key-memcached-max-retry";
constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL[] = constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL[] =
"tls-ticket-key-memcached-max-fail"; "tls-ticket-key-memcached-max-fail";
constexpr char SHRPX_OPT_ON_REQUEST_MRUBY_FILE[] = "on-request-mruby-file";
union sockaddr_union { union sockaddr_union {
sockaddr_storage storage; sockaddr_storage storage;
@ -314,6 +315,8 @@ struct Config {
std::unique_ptr<char[]> user; std::unique_ptr<char[]> user;
std::unique_ptr<char[]> session_cache_memcached_host; std::unique_ptr<char[]> session_cache_memcached_host;
std::unique_ptr<char[]> tls_ticket_key_memcached_host; std::unique_ptr<char[]> tls_ticket_key_memcached_host;
std::unique_ptr<char[]> on_request_mruby_file;
std::unique_ptr<char[]> on_response_mruby_file;
FILE *http2_upstream_dump_request_header; FILE *http2_upstream_dump_request_header;
FILE *http2_upstream_dump_response_header; FILE *http2_upstream_dump_response_header;
nghttp2_session_callbacks *http2_upstream_callbacks; nghttp2_session_callbacks *http2_upstream_callbacks;

View File

@ -149,7 +149,7 @@ void ConnectionHandler::worker_reopen_log_files() {
} }
} }
void ConnectionHandler::create_single_worker() { int ConnectionHandler::create_single_worker() {
auto cert_tree = ssl::create_cert_lookup_tree(); auto cert_tree = ssl::create_cert_lookup_tree();
auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree); auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree);
auto cl_ssl_ctx = ssl::setup_client_ssl_context(); auto cl_ssl_ctx = ssl::setup_client_ssl_context();
@ -160,9 +160,14 @@ void ConnectionHandler::create_single_worker() {
single_worker_ = make_unique<Worker>(loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree, single_worker_ = make_unique<Worker>(loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree,
ticket_keys_); ticket_keys_);
if (single_worker_->create_mruby_context() != 0) {
return -1;
}
return 0;
} }
void ConnectionHandler::create_worker_thread(size_t num) { int ConnectionHandler::create_worker_thread(size_t num) {
#ifndef NOTHREADS #ifndef NOTHREADS
assert(workers_.size() == 0); assert(workers_.size() == 0);
@ -179,14 +184,23 @@ void ConnectionHandler::create_worker_thread(size_t num) {
auto worker = make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree, auto worker = make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree,
ticket_keys_); ticket_keys_);
worker->run_async(); if (worker->create_mruby_context() != 0) {
return -1;
}
workers_.push_back(std::move(worker)); workers_.push_back(std::move(worker));
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LLOG(INFO, this) << "Created thread #" << workers_.size() - 1; LLOG(INFO, this) << "Created thread #" << workers_.size() - 1;
} }
} }
for (auto &worker : workers_) {
worker->run_async();
}
#endif // NOTHREADS #endif // NOTHREADS
return 0;
} }
void ConnectionHandler::join_worker() { void ConnectionHandler::join_worker() {

View File

@ -73,10 +73,10 @@ public:
~ConnectionHandler(); ~ConnectionHandler();
int handle_connection(int fd, sockaddr *addr, int addrlen); int handle_connection(int fd, sockaddr *addr, int addrlen);
// Creates Worker object for single threaded configuration. // Creates Worker object for single threaded configuration.
void create_single_worker(); int create_single_worker();
// Creates |num| Worker objects for multi threaded configuration. // Creates |num| Worker objects for multi threaded configuration.
// The |num| must be strictly more than 1. // The |num| must be strictly more than 1.
void create_worker_thread(size_t num); int create_worker_thread(size_t num);
void void
set_ticket_keys_to_worker(const std::shared_ptr<TicketKeys> &ticket_keys); set_ticket_keys_to_worker(const std::shared_ptr<TicketKeys> &ticket_keys);
void worker_reopen_log_files(); void worker_reopen_log_files();

View File

@ -127,7 +127,7 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
request_http2_expect_body_(false), chunked_response_(false), request_http2_expect_body_(false), chunked_response_(false),
response_connection_close_(false), response_header_key_prev_(false), response_connection_close_(false), response_header_key_prev_(false),
response_trailer_key_prev_(false), expect_final_response_(false), response_trailer_key_prev_(false), expect_final_response_(false),
request_pending_(false) { request_pending_(false), request_headers_dirty_(false) {
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
get_config()->stream_read_timeout); get_config()->stream_read_timeout);
@ -240,6 +240,8 @@ const Headers &Downstream::get_request_headers() const {
return request_headers_; return request_headers_;
} }
Headers &Downstream::get_request_headers() { return request_headers_; }
void Downstream::assemble_request_cookie() { void Downstream::assemble_request_cookie() {
std::string &cookie = assembled_request_cookie_; std::string &cookie = assembled_request_cookie_;
cookie = ""; cookie = "";
@ -336,6 +338,9 @@ void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
namespace { namespace {
int index_headers(http2::HeaderIndex &hdidx, Headers &headers, int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
int64_t &content_length) { int64_t &content_length) {
http2::init_hdidx(hdidx);
content_length = -1;
for (size_t i = 0; i < headers.size(); ++i) { for (size_t i = 0; i < headers.size(); ++i) {
auto &kv = headers[i]; auto &kv = headers[i];
util::inp_strlower(kv.name); util::inp_strlower(kv.name);
@ -1213,4 +1218,12 @@ bool Downstream::can_detach_downstream_connection() const {
!response_connection_close_; !response_connection_close_;
} }
void Downstream::set_request_headers_dirty(bool f) {
request_headers_dirty_ = f;
}
bool Downstream::get_request_headers_dirty() const {
return request_headers_dirty_;
}
} // namespace shrpx } // namespace shrpx

View File

@ -96,6 +96,7 @@ public:
const std::string &get_http2_settings() const; const std::string &get_http2_settings() const;
// downstream request API // downstream request API
const Headers &get_request_headers() const; const Headers &get_request_headers() const;
Headers &get_request_headers();
// Crumbles (split cookie by ";") in request_headers_ and returns // Crumbles (split cookie by ";") in request_headers_ and returns
// them. Headers::no_index is inherited. // them. Headers::no_index is inherited.
Headers crumble_request_cookie(); Headers crumble_request_cookie();
@ -126,6 +127,8 @@ public:
void append_last_request_header_value(const char *data, size_t len); void append_last_request_header_value(const char *data, size_t len);
// Empties request headers. // Empties request headers.
void clear_request_headers(); void clear_request_headers();
void set_request_headers_dirty(bool f);
bool get_request_headers_dirty() const;
size_t get_request_headers_sum() const; size_t get_request_headers_sum() const;
@ -454,6 +457,8 @@ private:
// has not been established or should be checked before use; // has not been established or should be checked before use;
// currently used only with HTTP/2 connection. // currently used only with HTTP/2 connection.
bool request_pending_; bool request_pending_;
// true if we need to execute index_request_headers()
bool request_headers_dirty_;
}; };
} // namespace shrpx } // namespace shrpx

View File

@ -37,6 +37,7 @@
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_worker.h" #include "shrpx_worker.h"
#include "shrpx_http2_session.h" #include "shrpx_http2_session.h"
#include "shrpx_mruby.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
#include "base64.h" #include "base64.h"
@ -306,6 +307,13 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
downstream->set_request_http2_expect_body(true); downstream->set_request_http2_expect_body(true);
} }
auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
auto worker = handler->get_worker();
auto mruby_ctx = worker->get_mruby_context();
mruby_ctx->run_on_request_proc(downstream);
downstream->inspect_http2_request(); downstream->inspect_http2_request();
downstream->set_request_state(Downstream::HEADER_COMPLETE); downstream->set_request_state(Downstream::HEADER_COMPLETE);

View File

@ -37,6 +37,7 @@
#include "shrpx_log_config.h" #include "shrpx_log_config.h"
#include "shrpx_worker.h" #include "shrpx_worker.h"
#include "shrpx_http2_session.h" #include "shrpx_http2_session.h"
#include "shrpx_mruby.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
#include "template.h" #include "template.h"
@ -272,6 +273,12 @@ int htp_hdrs_completecb(http_parser *htp) {
return -1; return -1;
} }
auto handler = upstream->get_client_handler();
auto worker = handler->get_worker();
auto mruby_ctx = worker->get_mruby_context();
mruby_ctx->run_on_request_proc(downstream);
downstream->inspect_http1_request(); downstream->inspect_http1_request();
if (downstream->get_request_method() != HTTP_CONNECT) { if (downstream->get_request_method() != HTTP_CONNECT) {

163
src/shrpx_mruby.cc Normal file
View File

@ -0,0 +1,163 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_mruby.h"
#include <mruby/compile.h>
#include <mruby/string.h>
#include "shrpx_downstream.h"
#include "shrpx_config.h"
#include "shrpx_mruby_module.h"
#include "template.h"
namespace shrpx {
namespace mruby {
MRubyContext::MRubyContext(mrb_state *mrb, RProc *on_request_proc,
RProc *on_response_proc)
: mrb_(mrb), on_request_proc_(on_request_proc),
on_response_proc_(on_response_proc) {}
MRubyContext::~MRubyContext() { mrb_close(mrb_); }
int MRubyContext::run_on_request_proc(Downstream *downstream) {
if (!on_request_proc_) {
return 0;
}
mrb_->ud = downstream;
int rv = 0;
auto ai = mrb_gc_arena_save(mrb_);
auto res = mrb_run(mrb_, on_request_proc_, mrb_top_self(mrb_));
(void)res;
if (mrb_->exc) {
rv = -1;
auto error =
mrb_str_ptr(mrb_funcall(mrb_, mrb_obj_value(mrb_->exc), "inspect", 0));
LOG(ERROR) << "Exception caught while executing mruby code: "
<< error->as.heap.ptr;
mrb_->exc = 0;
}
mrb_->ud = nullptr;
mrb_gc_arena_restore(mrb_, ai);
if (downstream->get_request_headers_dirty()) {
downstream->set_request_headers_dirty(false);
downstream->index_request_headers();
}
return rv;
}
int run_on_response_proc(Downstream *downstream) {
// TODO not implemented yet
return 0;
}
// Based on
// https://github.com/h2o/h2o/blob/master/lib/handler/mruby.c. It is
// very hard to write these kind of code because mruby has almost no
// documentation aobut compiling or generating code, at least at the
// time of this writing.
RProc *compile(mrb_state *mrb, const char *filename) {
if (filename == nullptr) {
return nullptr;
}
auto infile = fopen(filename, "rb");
if (infile == nullptr) {
return nullptr;
}
auto infile_d = defer(fclose, infile);
auto mrbc = mrbc_context_new(mrb);
if (mrbc == nullptr) {
LOG(ERROR) << "mrb_context_new failed";
return nullptr;
}
auto mrbc_d = defer(mrbc_context_free, mrb, mrbc);
auto parser = mrb_parse_file(mrb, infile, nullptr);
if (parser == nullptr) {
LOG(ERROR) << "mrb_parse_nstring failed";
return nullptr;
}
auto parser_d = defer(mrb_parser_free, parser);
if (parser->nerr != 0) {
LOG(ERROR) << "mruby parser detected parse error";
return nullptr;
}
auto proc = mrb_generate_code(mrb, parser);
if (proc == nullptr) {
LOG(ERROR) << "mrb_generate_code failed";
return nullptr;
}
return proc;
}
std::unique_ptr<MRubyContext> create_mruby_context() {
auto mrb = mrb_open();
if (mrb == nullptr) {
LOG(ERROR) << "mrb_open failed";
return nullptr;
}
init_module(mrb);
auto req_file = get_config()->on_request_mruby_file.get();
auto res_file = get_config()->on_response_mruby_file.get();
auto req_proc = compile(mrb, req_file);
if (req_file && !req_proc) {
LOG(ERROR) << "Could not compile mruby code " << req_file;
mrb_close(mrb);
return nullptr;
}
auto res_proc = compile(mrb, res_file);
if (res_file && !res_proc) {
LOG(ERROR) << "Could not compile mruby code " << res_file;
mrb_close(mrb);
return nullptr;
}
return make_unique<MRubyContext>(mrb, req_proc, res_proc);
}
} // namespace mruby
} // namespace shrpx

63
src/shrpx_mruby.h Normal file
View File

@ -0,0 +1,63 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SHRPX_MRUBY_H
#define SHRPX_MRUBY_H
#include "shrpx.h"
#include <memory>
#include <mruby.h>
#include <mruby/proc.h>
using namespace nghttp2;
namespace shrpx {
namespace mruby {
class MRubyContext {
public:
MRubyContext(mrb_state *mrb, RProc *on_request_proc, RProc *on_response_proc);
~MRubyContext();
int run_on_request_proc(Downstream *downstream);
int run_on_response_proc(Downstream *downstream);
private:
mrb_state *mrb_;
RProc *on_request_proc_;
RProc *on_response_proc_;
};
RProc *compile(mrb_state *mrb, const char *filename);
std::unique_ptr<MRubyContext> create_mruby_context();
} // namespace mruby
} // namespace shrpx
#endif // SHRPX_MRUBY_H

193
src/shrpx_mruby_module.cc Normal file
View File

@ -0,0 +1,193 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_mruby_module.h"
#include <mruby/variable.h>
#include <mruby/string.h>
#include "shrpx_downstream.h"
#include "util.h"
namespace shrpx {
namespace mruby {
namespace {
mrb_value request_init(mrb_state *mrb, mrb_value self) { return self; }
} // namespace
namespace {
mrb_value request_get_path(mrb_state *mrb, mrb_value self) {
auto downstream = static_cast<Downstream *>(mrb->ud);
auto &path = downstream->get_request_path();
return mrb_str_new_static(mrb, path.c_str(), path.size());
}
} // namespace
namespace {
mrb_value request_set_path(mrb_state *mrb, mrb_value self) {
auto downstream = static_cast<Downstream *>(mrb->ud);
const char *path;
mrb_int pathlen;
mrb_get_args(mrb, "s", &path, &pathlen);
if (pathlen == 0) {
mrb_raise(mrb, E_RUNTIME_ERROR, "path must not be empty string");
}
downstream->set_request_path(std::string(path, pathlen));
return self;
}
} // namespace
namespace {
mrb_value request_get_headers(mrb_state *mrb, mrb_value self) {
auto headers = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "RequestHeaders"));
if (mrb_nil_p(headers)) {
auto module = mrb_module_get(mrb, "Nghttpx");
auto headers_class = mrb_class_get_under(mrb, module, "RequestHeaders");
headers = mrb_obj_new(mrb, headers_class, 0, nullptr);
mrb_iv_set(mrb, self, mrb_intern_lit(mrb, "RequestHeaders"), headers);
}
return headers;
}
} // namespace
namespace {
mrb_value headers_init(mrb_state *mrb, mrb_value self) { return self; }
} // namespace
namespace {
mrb_value request_headers_get(mrb_state *mrb, mrb_value self) {
auto downstream = static_cast<Downstream *>(mrb->ud);
mrb_value key;
mrb_get_args(mrb, "o", &key);
key = mrb_funcall(mrb, key, "downcase", 0);
if (RSTRING_LEN(key) == 0) {
return key;
}
auto hd = downstream->get_request_header(
std::string(RSTRING_PTR(key), RSTRING_LEN(key)));
if (hd == nullptr) {
return mrb_nil_value();
}
return mrb_str_new_static(mrb, hd->value.c_str(), hd->value.size());
}
} // namespace
namespace {
mrb_value request_headers_set(mrb_state *mrb, mrb_value self, bool repl) {
auto downstream = static_cast<Downstream *>(mrb->ud);
mrb_value key, value;
mrb_get_args(mrb, "oo", &key, &value);
key = mrb_funcall(mrb, key, "downcase", 0);
if (RSTRING_LEN(key) == 0) {
mrb_raise(mrb, E_RUNTIME_ERROR, "empty key is not allowed");
}
if (repl) {
for (auto &hd : downstream->get_request_headers()) {
if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key),
RSTRING_LEN(key))) {
hd.name = "";
}
}
}
downstream->add_request_header(
std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
downstream->set_request_headers_dirty(true);
return key;
}
} // namespace
namespace {
mrb_value request_headers_set(mrb_state *mrb, mrb_value self) {
return request_headers_set(mrb, self, true);
}
} // namespace
namespace {
mrb_value request_headers_add(mrb_state *mrb, mrb_value self) {
return request_headers_set(mrb, self, false);
}
} // namespace
namespace {
void init_headers_class(mrb_state *mrb, RClass *module, const char *name,
mrb_func_t get, mrb_func_t set, mrb_func_t add) {
auto headers_class =
mrb_define_class_under(mrb, module, name, mrb->object_class);
mrb_define_method(mrb, headers_class, "initialize", headers_init,
MRB_ARGS_NONE());
mrb_define_method(mrb, headers_class, "get", get, MRB_ARGS_REQ(1));
mrb_define_method(mrb, headers_class, "set", set, MRB_ARGS_REQ(2));
mrb_define_method(mrb, headers_class, "add", set, MRB_ARGS_REQ(2));
}
} // namespace
namespace {
void init_request_class(mrb_state *mrb, RClass *module) {
auto request_class =
mrb_define_class_under(mrb, module, "Request", mrb->object_class);
mrb_define_method(mrb, request_class, "initialize", request_init,
MRB_ARGS_NONE());
mrb_define_method(mrb, request_class, "path", request_get_path,
MRB_ARGS_NONE());
mrb_define_method(mrb, request_class, "path=", request_set_path,
MRB_ARGS_REQ(1));
mrb_define_method(mrb, request_class, "headers", request_get_headers,
MRB_ARGS_NONE());
init_headers_class(mrb, module, "RequestHeaders", request_headers_get,
request_headers_set, request_headers_add);
}
} // namespace
void init_module(mrb_state *mrb) {
auto module = mrb_define_module(mrb, "Nghttpx");
init_request_class(mrb, module);
}
} // namespace mruby
} // namespace shrpx

44
src/shrpx_mruby_module.h Normal file
View File

@ -0,0 +1,44 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SHRPX_MRUBY_MODULE_H
#define SHRPX_MRUBY_MODULE_H
#include "shrpx.h"
#include <mruby.h>
using namespace nghttp2;
namespace shrpx {
namespace mruby {
void init_module(mrb_state *mrb);
} // namespace mruby
} // namespace shrpx
#endif // SHRPX_MRUBY_MODULE_H

View File

@ -37,6 +37,7 @@
#include "shrpx_log_config.h" #include "shrpx_log_config.h"
#include "shrpx_connect_blocker.h" #include "shrpx_connect_blocker.h"
#include "shrpx_memcached_dispatcher.h" #include "shrpx_memcached_dispatcher.h"
#include "shrpx_mruby.h"
#include "util.h" #include "util.h"
#include "template.h" #include "template.h"
@ -265,4 +266,17 @@ MemcachedDispatcher *Worker::get_session_cache_memcached_dispatcher() {
return session_cache_memcached_dispatcher_.get(); return session_cache_memcached_dispatcher_.get();
} }
int Worker::create_mruby_context() {
mruby_ctx_ = mruby::create_mruby_context();
if (!mruby_ctx_) {
return -1;
}
return 0;
}
mruby::MRubyContext *Worker::get_mruby_context() const {
return mruby_ctx_.get();
}
} // namespace shrpx } // namespace shrpx

View File

@ -51,6 +51,12 @@ class Http2Session;
class ConnectBlocker; class ConnectBlocker;
class MemcachedDispatcher; class MemcachedDispatcher;
namespace mruby {
class MRubyContext;
} // namespace mruby
namespace ssl { namespace ssl {
class CertLookupTree; class CertLookupTree;
} // namespace ssl } // namespace ssl
@ -124,6 +130,10 @@ public:
MemcachedDispatcher *get_session_cache_memcached_dispatcher(); MemcachedDispatcher *get_session_cache_memcached_dispatcher();
int create_mruby_context();
mruby::MRubyContext *get_mruby_context() const;
private: private:
#ifndef NOTHREADS #ifndef NOTHREADS
std::future<void> fut_; std::future<void> fut_;
@ -137,6 +147,7 @@ private:
WorkerStat worker_stat_; WorkerStat worker_stat_;
std::vector<DownstreamGroup> dgrps_; std::vector<DownstreamGroup> dgrps_;
std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_; std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_;
std::unique_ptr<mruby::MRubyContext> mruby_ctx_;
struct ev_loop *loop_; struct ev_loop *loop_;
// Following fields are shared across threads if // Following fields are shared across threads if