-
Notifications
You must be signed in to change notification settings - Fork 0
/
ts_mruby.cpp
170 lines (134 loc) · 4.24 KB
/
ts_mruby.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <fstream>
#include <iostream>
#include <map>
#include <pthread.h>
#include <string>
#include <atscppapi/GlobalPlugin.h>
#include <atscppapi/PluginInit.h>
#include <atscppapi/RemapPlugin.h>
#include "ts_mruby.hpp"
#include "ts_mruby_init.hpp"
#include "ts_mruby_internal.hpp"
#include "ts_mruby_internal.hpp"
#include "ts_mruby_request.hpp"
#include "utils.hpp"
using namespace std;
using namespace atscppapi;
namespace {
// Global mruby scripts cache
static MrubyScriptsCache *scriptsCache = NULL;
// key specifying thread local data
pthread_key_t threadKey = 0;
// Initialize thread key when this plugin is loaded
__attribute__((constructor)) void create_thread_keys() {
if (threadKey == 0) {
if (pthread_key_create(&threadKey, NULL) != 0) {
// XXX fatal error
}
}
}
// Note: Use pthread API's directly to have thread local parameters
ThreadLocalMRubyStates *getMrubyStates() {
auto *state =
static_cast<ThreadLocalMRubyStates *>(pthread_getspecific(threadKey));
if (!state) {
state = new ThreadLocalMRubyStates();
if (pthread_setspecific(threadKey, state)) {
// XXX fatal error
}
}
return state;
}
} // anonymous namespace
class MRubyPluginBase {
protected:
MRubyPluginBase(const string &fpath) : filepath_(fpath) {}
TSMrubyResult executeMrubyScript(Transaction &transaction) {
// get or initialize thread local mruby VM
ThreadLocalMRubyStates *states = getMrubyStates();
mrb_state *mrb = states->getMrb();
// get or compile mruby script
RProc *proc = states->getRProc(filepath_);
// set execution context
context_ = shared_ptr<TSMrubyContext>(new TSMrubyContext());
context_->setTransaction(&transaction);
mrb->ud = reinterpret_cast<void *>(context_.get());
// execute mruby script when ATS pre-remap hook occurs.
mrb_run(mrb, proc, mrb_nil_value());
return context_->getResult();
}
private:
string filepath_;
shared_ptr<TSMrubyContext> context_;
};
ThreadLocalMRubyStates::ThreadLocalMRubyStates() {
state_ = mrb_open();
ts_mrb_class_init(state_);
}
ThreadLocalMRubyStates::~ThreadLocalMRubyStates() {
mrb_close(state_);
state_ = NULL;
}
RProc *ThreadLocalMRubyStates::getRProc(const std::string &key) {
RProc *proc = procCache_[key];
if (!proc) {
const std::string &code = scriptsCache->load(key);
// compile
mrbc_context *context = mrbc_context_new(state_);
auto *st = mrb_parse_string(state_, code.c_str(), context);
proc = mrb_generate_code(state_, st);
mrb_pool_close(st->pool);
// store to cache
procCache_.insert(make_pair(key, proc));
}
return proc;
}
class MRubyPlugin : public GlobalPlugin, MRubyPluginBase {
public:
MRubyPlugin(const string &fpath) : MRubyPluginBase(fpath) {
registerHook(HOOK_READ_REQUEST_HEADERS);
}
virtual void handleReadRequestHeaders(Transaction &transaction) {
TSMrubyResult result = executeMrubyScript(transaction);
if (result.isRemapped) {
transaction.setSkipRemapping(1);
}
transaction.resume();
}
};
class MRubyRemapPlugin : public RemapPlugin, MRubyPluginBase {
public:
MRubyRemapPlugin(void **instance_handle, const string &fpath)
: RemapPlugin(instance_handle), MRubyPluginBase(fpath) {}
Result doRemap(const Url &map_from_url, const Url &map_to_url,
Transaction &transaction, bool &redirect) {
TSMrubyResult result = executeMrubyScript(transaction);
return (result.isRemapped) ? RESULT_DID_REMAP : RESULT_NO_REMAP;
}
};
// As global plugin
void TSPluginInit(int argc, const char *argv[]) {
if (argc == 2) {
RegisterGlobalPlugin(TS_MRUBY_PLUGIN_NAME, TS_MRUBY_PLUGIN_AUTHOR,
TS_MRUBY_PLUGIN_EMAIL);
if (!scriptsCache) {
scriptsCache = ts_mruby::utils::mockable_ptr<MrubyScriptsCache>();
}
scriptsCache->store(argv[1]);
new MRubyPlugin(argv[1]);
}
}
// As remap plugin
TSReturnCode TSRemapNewInstance(int argc, char *argv[], void **ih,
char * /* ATS_UNUSED */, int /* ATS_UNUSED */) {
if (argc == 3) {
if (!scriptsCache) {
scriptsCache = ts_mruby::utils::mockable_ptr<MrubyScriptsCache>();
}
scriptsCache->store(argv[2]);
new MRubyRemapPlugin(ih, argv[2]);
return TS_SUCCESS;
} else {
return TS_ERROR;
}
}