12c593315Sopenharmony_ci/* 22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library 32c593315Sopenharmony_ci * 42c593315Sopenharmony_ci * Copyright (c) 2015 Tatsuhiro Tsujikawa 52c593315Sopenharmony_ci * 62c593315Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining 72c593315Sopenharmony_ci * a copy of this software and associated documentation files (the 82c593315Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 92c593315Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 102c593315Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to 112c593315Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 122c593315Sopenharmony_ci * the following conditions: 132c593315Sopenharmony_ci * 142c593315Sopenharmony_ci * The above copyright notice and this permission notice shall be 152c593315Sopenharmony_ci * included in all copies or substantial portions of the Software. 162c593315Sopenharmony_ci * 172c593315Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 182c593315Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 192c593315Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 202c593315Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 212c593315Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 222c593315Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 232c593315Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 242c593315Sopenharmony_ci */ 252c593315Sopenharmony_ci#include "shrpx_mruby.h" 262c593315Sopenharmony_ci 272c593315Sopenharmony_ci#include <mruby/compile.h> 282c593315Sopenharmony_ci#include <mruby/string.h> 292c593315Sopenharmony_ci 302c593315Sopenharmony_ci#include "shrpx_downstream.h" 312c593315Sopenharmony_ci#include "shrpx_config.h" 322c593315Sopenharmony_ci#include "shrpx_mruby_module.h" 332c593315Sopenharmony_ci#include "shrpx_downstream_connection.h" 342c593315Sopenharmony_ci#include "shrpx_log.h" 352c593315Sopenharmony_ci 362c593315Sopenharmony_cinamespace shrpx { 372c593315Sopenharmony_ci 382c593315Sopenharmony_cinamespace mruby { 392c593315Sopenharmony_ci 402c593315Sopenharmony_ciMRubyContext::MRubyContext(mrb_state *mrb, mrb_value app, mrb_value env) 412c593315Sopenharmony_ci : mrb_(mrb), app_(std::move(app)), env_(std::move(env)) {} 422c593315Sopenharmony_ci 432c593315Sopenharmony_ciMRubyContext::~MRubyContext() { 442c593315Sopenharmony_ci if (mrb_) { 452c593315Sopenharmony_ci mrb_close(mrb_); 462c593315Sopenharmony_ci } 472c593315Sopenharmony_ci} 482c593315Sopenharmony_ci 492c593315Sopenharmony_ciint MRubyContext::run_app(Downstream *downstream, int phase) { 502c593315Sopenharmony_ci if (!mrb_) { 512c593315Sopenharmony_ci return 0; 522c593315Sopenharmony_ci } 532c593315Sopenharmony_ci 542c593315Sopenharmony_ci MRubyAssocData data{downstream, phase}; 552c593315Sopenharmony_ci 562c593315Sopenharmony_ci mrb_->ud = &data; 572c593315Sopenharmony_ci 582c593315Sopenharmony_ci int rv = 0; 592c593315Sopenharmony_ci auto ai = mrb_gc_arena_save(mrb_); 602c593315Sopenharmony_ci auto ai_d = defer([ai, this]() { mrb_gc_arena_restore(mrb_, ai); }); 612c593315Sopenharmony_ci 622c593315Sopenharmony_ci const char *method; 632c593315Sopenharmony_ci switch (phase) { 642c593315Sopenharmony_ci case PHASE_REQUEST: 652c593315Sopenharmony_ci if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_req"))) { 662c593315Sopenharmony_ci return 0; 672c593315Sopenharmony_ci } 682c593315Sopenharmony_ci method = "on_req"; 692c593315Sopenharmony_ci break; 702c593315Sopenharmony_ci case PHASE_RESPONSE: 712c593315Sopenharmony_ci if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_resp"))) { 722c593315Sopenharmony_ci return 0; 732c593315Sopenharmony_ci } 742c593315Sopenharmony_ci method = "on_resp"; 752c593315Sopenharmony_ci break; 762c593315Sopenharmony_ci default: 772c593315Sopenharmony_ci assert(0); 782c593315Sopenharmony_ci abort(); 792c593315Sopenharmony_ci } 802c593315Sopenharmony_ci 812c593315Sopenharmony_ci auto res = mrb_funcall(mrb_, app_, method, 1, env_); 822c593315Sopenharmony_ci (void)res; 832c593315Sopenharmony_ci 842c593315Sopenharmony_ci if (mrb_->exc) { 852c593315Sopenharmony_ci // If response has been committed, ignore error 862c593315Sopenharmony_ci if (downstream->get_response_state() != DownstreamState::MSG_COMPLETE) { 872c593315Sopenharmony_ci rv = -1; 882c593315Sopenharmony_ci } 892c593315Sopenharmony_ci 902c593315Sopenharmony_ci auto exc = mrb_obj_value(mrb_->exc); 912c593315Sopenharmony_ci auto inspect = mrb_inspect(mrb_, exc); 922c593315Sopenharmony_ci 932c593315Sopenharmony_ci LOG(ERROR) << "Exception caught while executing mruby code: " 942c593315Sopenharmony_ci << mrb_str_to_cstr(mrb_, inspect); 952c593315Sopenharmony_ci } 962c593315Sopenharmony_ci 972c593315Sopenharmony_ci mrb_->ud = nullptr; 982c593315Sopenharmony_ci 992c593315Sopenharmony_ci return rv; 1002c593315Sopenharmony_ci} 1012c593315Sopenharmony_ci 1022c593315Sopenharmony_ciint MRubyContext::run_on_request_proc(Downstream *downstream) { 1032c593315Sopenharmony_ci return run_app(downstream, PHASE_REQUEST); 1042c593315Sopenharmony_ci} 1052c593315Sopenharmony_ci 1062c593315Sopenharmony_ciint MRubyContext::run_on_response_proc(Downstream *downstream) { 1072c593315Sopenharmony_ci return run_app(downstream, PHASE_RESPONSE); 1082c593315Sopenharmony_ci} 1092c593315Sopenharmony_ci 1102c593315Sopenharmony_civoid MRubyContext::delete_downstream(Downstream *downstream) { 1112c593315Sopenharmony_ci if (!mrb_) { 1122c593315Sopenharmony_ci return; 1132c593315Sopenharmony_ci } 1142c593315Sopenharmony_ci delete_downstream_from_module(mrb_, downstream); 1152c593315Sopenharmony_ci} 1162c593315Sopenharmony_ci 1172c593315Sopenharmony_cinamespace { 1182c593315Sopenharmony_cimrb_value instantiate_app(mrb_state *mrb, RProc *proc) { 1192c593315Sopenharmony_ci mrb->ud = nullptr; 1202c593315Sopenharmony_ci 1212c593315Sopenharmony_ci auto res = mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); 1222c593315Sopenharmony_ci 1232c593315Sopenharmony_ci if (mrb->exc) { 1242c593315Sopenharmony_ci auto exc = mrb_obj_value(mrb->exc); 1252c593315Sopenharmony_ci auto inspect = mrb_inspect(mrb, exc); 1262c593315Sopenharmony_ci 1272c593315Sopenharmony_ci LOG(ERROR) << "Exception caught while executing mruby code: " 1282c593315Sopenharmony_ci << mrb_str_to_cstr(mrb, inspect); 1292c593315Sopenharmony_ci 1302c593315Sopenharmony_ci return mrb_nil_value(); 1312c593315Sopenharmony_ci } 1322c593315Sopenharmony_ci 1332c593315Sopenharmony_ci return res; 1342c593315Sopenharmony_ci} 1352c593315Sopenharmony_ci} // namespace 1362c593315Sopenharmony_ci 1372c593315Sopenharmony_ci// Based on 1382c593315Sopenharmony_ci// https://github.com/h2o/h2o/blob/master/lib/handler/mruby.c. It is 1392c593315Sopenharmony_ci// very hard to write these kind of code because mruby has almost no 1402c593315Sopenharmony_ci// documentation about compiling or generating code, at least at the 1412c593315Sopenharmony_ci// time of this writing. 1422c593315Sopenharmony_ciRProc *compile(mrb_state *mrb, const StringRef &filename) { 1432c593315Sopenharmony_ci if (filename.empty()) { 1442c593315Sopenharmony_ci return nullptr; 1452c593315Sopenharmony_ci } 1462c593315Sopenharmony_ci 1472c593315Sopenharmony_ci auto infile = fopen(filename.c_str(), "rb"); 1482c593315Sopenharmony_ci if (infile == nullptr) { 1492c593315Sopenharmony_ci LOG(ERROR) << "Could not open mruby file " << filename; 1502c593315Sopenharmony_ci return nullptr; 1512c593315Sopenharmony_ci } 1522c593315Sopenharmony_ci auto infile_d = defer(fclose, infile); 1532c593315Sopenharmony_ci 1542c593315Sopenharmony_ci auto mrbc = mrbc_context_new(mrb); 1552c593315Sopenharmony_ci if (mrbc == nullptr) { 1562c593315Sopenharmony_ci LOG(ERROR) << "mrb_context_new failed"; 1572c593315Sopenharmony_ci return nullptr; 1582c593315Sopenharmony_ci } 1592c593315Sopenharmony_ci auto mrbc_d = defer(mrbc_context_free, mrb, mrbc); 1602c593315Sopenharmony_ci 1612c593315Sopenharmony_ci auto parser = mrb_parse_file(mrb, infile, nullptr); 1622c593315Sopenharmony_ci if (parser == nullptr) { 1632c593315Sopenharmony_ci LOG(ERROR) << "mrb_parse_nstring failed"; 1642c593315Sopenharmony_ci return nullptr; 1652c593315Sopenharmony_ci } 1662c593315Sopenharmony_ci auto parser_d = defer(mrb_parser_free, parser); 1672c593315Sopenharmony_ci 1682c593315Sopenharmony_ci if (parser->nerr != 0) { 1692c593315Sopenharmony_ci LOG(ERROR) << "mruby parser detected parse error"; 1702c593315Sopenharmony_ci return nullptr; 1712c593315Sopenharmony_ci } 1722c593315Sopenharmony_ci 1732c593315Sopenharmony_ci auto proc = mrb_generate_code(mrb, parser); 1742c593315Sopenharmony_ci if (proc == nullptr) { 1752c593315Sopenharmony_ci LOG(ERROR) << "mrb_generate_code failed"; 1762c593315Sopenharmony_ci return nullptr; 1772c593315Sopenharmony_ci } 1782c593315Sopenharmony_ci 1792c593315Sopenharmony_ci return proc; 1802c593315Sopenharmony_ci} 1812c593315Sopenharmony_ci 1822c593315Sopenharmony_cistd::unique_ptr<MRubyContext> create_mruby_context(const StringRef &filename) { 1832c593315Sopenharmony_ci if (filename.empty()) { 1842c593315Sopenharmony_ci return std::make_unique<MRubyContext>(nullptr, mrb_nil_value(), 1852c593315Sopenharmony_ci mrb_nil_value()); 1862c593315Sopenharmony_ci } 1872c593315Sopenharmony_ci 1882c593315Sopenharmony_ci auto mrb = mrb_open(); 1892c593315Sopenharmony_ci if (mrb == nullptr) { 1902c593315Sopenharmony_ci LOG(ERROR) << "mrb_open failed"; 1912c593315Sopenharmony_ci return nullptr; 1922c593315Sopenharmony_ci } 1932c593315Sopenharmony_ci 1942c593315Sopenharmony_ci auto ai = mrb_gc_arena_save(mrb); 1952c593315Sopenharmony_ci 1962c593315Sopenharmony_ci auto req_proc = compile(mrb, filename); 1972c593315Sopenharmony_ci 1982c593315Sopenharmony_ci if (!req_proc) { 1992c593315Sopenharmony_ci mrb_gc_arena_restore(mrb, ai); 2002c593315Sopenharmony_ci LOG(ERROR) << "Could not compile mruby code " << filename; 2012c593315Sopenharmony_ci mrb_close(mrb); 2022c593315Sopenharmony_ci return nullptr; 2032c593315Sopenharmony_ci } 2042c593315Sopenharmony_ci 2052c593315Sopenharmony_ci auto env = init_module(mrb); 2062c593315Sopenharmony_ci 2072c593315Sopenharmony_ci auto app = instantiate_app(mrb, req_proc); 2082c593315Sopenharmony_ci if (mrb_nil_p(app)) { 2092c593315Sopenharmony_ci mrb_gc_arena_restore(mrb, ai); 2102c593315Sopenharmony_ci LOG(ERROR) << "Could not instantiate mruby app from " << filename; 2112c593315Sopenharmony_ci mrb_close(mrb); 2122c593315Sopenharmony_ci return nullptr; 2132c593315Sopenharmony_ci } 2142c593315Sopenharmony_ci 2152c593315Sopenharmony_ci mrb_gc_arena_restore(mrb, ai); 2162c593315Sopenharmony_ci 2172c593315Sopenharmony_ci // TODO These are not necessary, because we retain app and env? 2182c593315Sopenharmony_ci mrb_gc_protect(mrb, env); 2192c593315Sopenharmony_ci mrb_gc_protect(mrb, app); 2202c593315Sopenharmony_ci 2212c593315Sopenharmony_ci return std::make_unique<MRubyContext>(mrb, std::move(app), std::move(env)); 2222c593315Sopenharmony_ci} 2232c593315Sopenharmony_ci 2242c593315Sopenharmony_cimrb_sym intern_ptr(mrb_state *mrb, void *ptr) { 2252c593315Sopenharmony_ci auto p = reinterpret_cast<uintptr_t>(ptr); 2262c593315Sopenharmony_ci 2272c593315Sopenharmony_ci return mrb_intern(mrb, reinterpret_cast<const char *>(&p), sizeof(p)); 2282c593315Sopenharmony_ci} 2292c593315Sopenharmony_ci 2302c593315Sopenharmony_civoid check_phase(mrb_state *mrb, int phase, int phase_mask) { 2312c593315Sopenharmony_ci if ((phase & phase_mask) == 0) { 2322c593315Sopenharmony_ci mrb_raise(mrb, E_RUNTIME_ERROR, "operation was not allowed in this phase"); 2332c593315Sopenharmony_ci } 2342c593315Sopenharmony_ci} 2352c593315Sopenharmony_ci 2362c593315Sopenharmony_ci} // namespace mruby 2372c593315Sopenharmony_ci 2382c593315Sopenharmony_ci} // namespace shrpx 239