nghttpx: Add basic infrastructure for mruby support
This commit is contained in:
parent
dd0a72579f
commit
1508c50a45
|
@ -98,6 +98,7 @@ OPTIONS = [
|
|||
"tls-ticket-key-memcached-interval",
|
||||
"tls-ticket-key-memcached-max-retry",
|
||||
"tls-ticket-key-memcached-max-fail",
|
||||
"on-request-mruby-file",
|
||||
"conf",
|
||||
]
|
||||
|
||||
|
|
|
@ -124,6 +124,8 @@ NGHTTPX_SRCS = \
|
|||
shrpx_memcached_connection.cc shrpx_memcached_connection.h \
|
||||
shrpx_memcached_request.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
|
||||
|
||||
if HAVE_SPDYLAY
|
||||
|
@ -134,7 +136,7 @@ noinst_LIBRARIES = libnghttpx.a
|
|||
libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
|
||||
|
||||
nghttpx_SOURCES = shrpx.cc shrpx.h
|
||||
nghttpx_LDADD = libnghttpx.a ${LDADD}
|
||||
nghttpx_LDADD = libnghttpx.a ${LDADD} -lmruby
|
||||
|
||||
if HAVE_CUNIT
|
||||
check_PROGRAMS += nghttpx-unittest
|
||||
|
@ -150,7 +152,7 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
|
|||
memchunk_test.cc memchunk_test.h
|
||||
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\
|
||||
-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
|
||||
endif # HAVE_CUNIT
|
||||
|
|
17
src/shrpx.cc
17
src/shrpx.cc
|
@ -926,9 +926,13 @@ int event_loop() {
|
|||
#endif // !NOTHREADS
|
||||
|
||||
if (get_config()->num_worker == 1) {
|
||||
conn_handler->create_single_worker();
|
||||
rv = conn_handler->create_single_worker();
|
||||
} 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
|
||||
|
@ -1899,6 +1903,7 @@ int main(int argc, char **argv) {
|
|||
89},
|
||||
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, required_argument, &flag,
|
||||
90},
|
||||
{SHRPX_OPT_ON_REQUEST_MRUBY_FILE, required_argument, &flag, 91},
|
||||
{nullptr, 0, nullptr, 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,
|
||||
optarg);
|
||||
break;
|
||||
case 91:
|
||||
// --on-request-mruby-file
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_ON_REQUEST_MRUBY_FILE, optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2613,7 +2622,9 @@ int main(int argc, char **argv) {
|
|||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
|
||||
event_loop();
|
||||
if (event_loop() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(NOTICE) << "Shutdown momentarily";
|
||||
|
||||
|
|
|
@ -690,6 +690,7 @@ enum {
|
|||
SHRPX_OPTID_NO_VIA,
|
||||
SHRPX_OPTID_NPN_LIST,
|
||||
SHRPX_OPTID_OCSP_UPDATE_INTERVAL,
|
||||
SHRPX_OPTID_ON_REQUEST_MRUBY_FILE,
|
||||
SHRPX_OPTID_PADDING,
|
||||
SHRPX_OPTID_PID_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;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("on-request-mruby-fil", name, 20)) {
|
||||
return SHRPX_OPTID_ON_REQUEST_MRUBY_FILE;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
|
||||
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:
|
||||
return parse_uint(&mod_config()->tls_ticket_key_memcached_max_fail, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_ON_REQUEST_MRUBY_FILE:
|
||||
mod_config()->on_request_mruby_file = strcopy(optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
|
|
|
@ -183,6 +183,7 @@ constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY[] =
|
|||
"tls-ticket-key-memcached-max-retry";
|
||||
constexpr char SHRPX_OPT_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 {
|
||||
sockaddr_storage storage;
|
||||
|
@ -314,6 +315,8 @@ struct Config {
|
|||
std::unique_ptr<char[]> user;
|
||||
std::unique_ptr<char[]> session_cache_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_response_header;
|
||||
nghttp2_session_callbacks *http2_upstream_callbacks;
|
||||
|
|
|
@ -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 sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree);
|
||||
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,
|
||||
ticket_keys_);
|
||||
if (single_worker_->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ConnectionHandler::create_worker_thread(size_t num) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConnectionHandler::create_worker_thread(size_t num) {
|
||||
#ifndef NOTHREADS
|
||||
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,
|
||||
ticket_keys_);
|
||||
worker->run_async();
|
||||
if (worker->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
workers_.push_back(std::move(worker));
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LLOG(INFO, this) << "Created thread #" << workers_.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &worker : workers_) {
|
||||
worker->run_async();
|
||||
}
|
||||
#endif // NOTHREADS
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ConnectionHandler::join_worker() {
|
||||
|
|
|
@ -73,10 +73,10 @@ public:
|
|||
~ConnectionHandler();
|
||||
int handle_connection(int fd, sockaddr *addr, int addrlen);
|
||||
// Creates Worker object for single threaded configuration.
|
||||
void create_single_worker();
|
||||
int create_single_worker();
|
||||
// Creates |num| Worker objects for multi threaded configuration.
|
||||
// The |num| must be strictly more than 1.
|
||||
void create_worker_thread(size_t num);
|
||||
int create_worker_thread(size_t num);
|
||||
void
|
||||
set_ticket_keys_to_worker(const std::shared_ptr<TicketKeys> &ticket_keys);
|
||||
void worker_reopen_log_files();
|
||||
|
|
|
@ -127,7 +127,7 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
|
|||
request_http2_expect_body_(false), chunked_response_(false),
|
||||
response_connection_close_(false), response_header_key_prev_(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.,
|
||||
get_config()->stream_read_timeout);
|
||||
|
@ -240,6 +240,8 @@ const Headers &Downstream::get_request_headers() const {
|
|||
return request_headers_;
|
||||
}
|
||||
|
||||
Headers &Downstream::get_request_headers() { return request_headers_; }
|
||||
|
||||
void Downstream::assemble_request_cookie() {
|
||||
std::string &cookie = assembled_request_cookie_;
|
||||
cookie = "";
|
||||
|
@ -336,6 +338,9 @@ void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
|
|||
namespace {
|
||||
int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
|
||||
int64_t &content_length) {
|
||||
http2::init_hdidx(hdidx);
|
||||
content_length = -1;
|
||||
|
||||
for (size_t i = 0; i < headers.size(); ++i) {
|
||||
auto &kv = headers[i];
|
||||
util::inp_strlower(kv.name);
|
||||
|
@ -1213,4 +1218,12 @@ bool Downstream::can_detach_downstream_connection() const {
|
|||
!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
|
||||
|
|
|
@ -96,6 +96,7 @@ public:
|
|||
const std::string &get_http2_settings() const;
|
||||
// downstream request API
|
||||
const Headers &get_request_headers() const;
|
||||
Headers &get_request_headers();
|
||||
// Crumbles (split cookie by ";") in request_headers_ and returns
|
||||
// them. Headers::no_index is inherited.
|
||||
Headers crumble_request_cookie();
|
||||
|
@ -126,6 +127,8 @@ public:
|
|||
void append_last_request_header_value(const char *data, size_t len);
|
||||
// Empties 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;
|
||||
|
||||
|
@ -454,6 +457,8 @@ private:
|
|||
// has not been established or should be checked before use;
|
||||
// currently used only with HTTP/2 connection.
|
||||
bool request_pending_;
|
||||
// true if we need to execute index_request_headers()
|
||||
bool request_headers_dirty_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "shrpx_http.h"
|
||||
#include "shrpx_worker.h"
|
||||
#include "shrpx_http2_session.h"
|
||||
#include "shrpx_mruby.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "base64.h"
|
||||
|
@ -306,6 +307,13 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
|||
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->set_request_state(Downstream::HEADER_COMPLETE);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "shrpx_log_config.h"
|
||||
#include "shrpx_worker.h"
|
||||
#include "shrpx_http2_session.h"
|
||||
#include "shrpx_mruby.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
@ -272,6 +273,12 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
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();
|
||||
|
||||
if (downstream->get_request_method() != HTTP_CONNECT) {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -37,6 +37,7 @@
|
|||
#include "shrpx_log_config.h"
|
||||
#include "shrpx_connect_blocker.h"
|
||||
#include "shrpx_memcached_dispatcher.h"
|
||||
#include "shrpx_mruby.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
||||
|
@ -265,4 +266,17 @@ MemcachedDispatcher *Worker::get_session_cache_memcached_dispatcher() {
|
|||
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
|
||||
|
|
|
@ -51,6 +51,12 @@ class Http2Session;
|
|||
class ConnectBlocker;
|
||||
class MemcachedDispatcher;
|
||||
|
||||
namespace mruby {
|
||||
|
||||
class MRubyContext;
|
||||
|
||||
} // namespace mruby
|
||||
|
||||
namespace ssl {
|
||||
class CertLookupTree;
|
||||
} // namespace ssl
|
||||
|
@ -124,6 +130,10 @@ public:
|
|||
|
||||
MemcachedDispatcher *get_session_cache_memcached_dispatcher();
|
||||
|
||||
int create_mruby_context();
|
||||
|
||||
mruby::MRubyContext *get_mruby_context() const;
|
||||
|
||||
private:
|
||||
#ifndef NOTHREADS
|
||||
std::future<void> fut_;
|
||||
|
@ -137,6 +147,7 @@ private:
|
|||
WorkerStat worker_stat_;
|
||||
std::vector<DownstreamGroup> dgrps_;
|
||||
std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_;
|
||||
std::unique_ptr<mruby::MRubyContext> mruby_ctx_;
|
||||
struct ev_loop *loop_;
|
||||
|
||||
// Following fields are shared across threads if
|
||||
|
|
Loading…
Reference in New Issue