Merge branch 'nghttpx-mruby'

This commit is contained in:
Tatsuhiro Tsujikawa 2015-10-08 23:32:42 +09:00
commit 8e6b92bdd7
18 changed files with 209 additions and 182 deletions

View File

@ -185,23 +185,20 @@ server. These hooks allows users to modify header fields, or common
HTTP variables, like authority or request path, and even return custom HTTP variables, like authority or request path, and even return custom
response without forwarding request to backend servers. response without forwarding request to backend servers.
To set request phase hook, use :option:`--request-phase-file` option. To specify mruby script file, use :option:`--mruby-file` option. The
To set response phase hook, use :option:`--response-phase-file` script will be evaluated once per thread on startup, and it must
option. instantiate object and evaluate it as the return value (e.g.,
``App.new``). This object is called app object. If app object
For request and response phase hook, user calls :rb:meth:`Nghttpx.run` defines ``on_req`` method, it is called with :rb:class:`Nghttpx::Env`
with block. The :rb:class:`Nghttpx::Env` is passed to the block. object on request hook. Similarly, if app object defines ``on_resp``
User can can access :rb:class:`Nghttpx::Request` and method, it is called with :rb:class:`Nghttpx::Env` object on response
:rb:class:`Nghttpx::Response` objects via :rb:attr:`Nghttpx::Env#req` hook. For each method invocation, user can can access
and :rb:attr:`Nghttpx::Env#resp` respectively. :rb:class:`Nghttpx::Request` and :rb:class:`Nghttpx::Response` objects
via :rb:attr:`Nghttpx::Env#req` and :rb:attr:`Nghttpx::Env#resp`
respectively.
.. rb:module:: Nghttpx .. rb:module:: Nghttpx
.. rb:classmethod:: run(&block)
Run request or response phase hook with given *block*.
:rb:class:`Nghttpx::Env` object is passed to the given block.
.. rb:const:: REQUEST_PHASE .. rb:const:: REQUEST_PHASE
Constant to represent request phase. Constant to represent request phase.
@ -379,19 +376,23 @@ Modify requet path:
.. code-block:: ruby .. code-block:: ruby
Nghttpx.run do |env| class App
def on_req(env)
env.req.path = "/apps#{env.req.path}" env.req.path = "/apps#{env.req.path}"
end end
end
Note that the file containing the above script must be set with App.new
:option:`--request-phase-file` option since we modify request path.
Don't forget to instantiate and evaluate object at the last line.
Restrict permission of viewing a content to a specific client Restrict permission of viewing a content to a specific client
addresses: addresses:
.. code-block:: ruby .. code-block:: ruby
Nghttpx.run do |env| class App
def on_req(env)
allowed_clients = ["127.0.0.1", "::1"] allowed_clients = ["127.0.0.1", "::1"]
if env.req.path.start_with?("/log/") && if env.req.path.start_with?("/log/") &&
@ -400,6 +401,9 @@ addresses:
env.resp.return "permission denied" env.resp.return "permission denied"
end end
end end
end
App.new
SEE ALSO SEE ALSO
-------- --------

View File

@ -98,8 +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",
"request-phase-file", "mruby-file",
"response-phase-file",
"accept-proxy-protocol", "accept-proxy-protocol",
"conf", "conf",
"fastopen", "fastopen",

View File

@ -360,7 +360,7 @@ func TestH1H1Websocket(t *testing.T) {
// TestH1H1ReqPhaseSetHeader tests mruby request phase hook // TestH1H1ReqPhaseSetHeader tests mruby request phase hook
// modifies request header fields. // modifies request header fields.
func TestH1H1ReqPhaseSetHeader(t *testing.T) { func TestH1H1ReqPhaseSetHeader(t *testing.T) {
st := newServerTester([]string{"--request-phase-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTester([]string{"--mruby-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("User-Agent"), "mruby"; got != want { if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
t.Errorf("User-Agent = %v; want %v", got, want) t.Errorf("User-Agent = %v; want %v", got, want)
} }
@ -382,7 +382,7 @@ func TestH1H1ReqPhaseSetHeader(t *testing.T) {
// TestH1H1ReqPhaseReturn tests mruby request phase hook returns // TestH1H1ReqPhaseReturn tests mruby request phase hook returns
// custom response. // custom response.
func TestH1H1ReqPhaseReturn(t *testing.T) { func TestH1H1ReqPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("request should not be forwarded") t.Fatalf("request should not be forwarded")
}) })
defer st.Close() defer st.Close()
@ -401,7 +401,7 @@ func TestH1H1ReqPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "20"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -410,7 +410,7 @@ func TestH1H1ReqPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from req"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -418,7 +418,7 @@ func TestH1H1ReqPhaseReturn(t *testing.T) {
// TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies // TestH1H1RespPhaseSetHeader tests mruby response phase hook modifies
// response header fields. // response header fields.
func TestH1H1RespPhaseSetHeader(t *testing.T) { func TestH1H1RespPhaseSetHeader(t *testing.T) {
st := newServerTester([]string{"--response-phase-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler) st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.http1(requestParam{ res, err := st.http1(requestParam{
@ -440,7 +440,7 @@ func TestH1H1RespPhaseSetHeader(t *testing.T) {
// TestH1H1RespPhaseReturn tests mruby response phase hook returns // TestH1H1RespPhaseReturn tests mruby response phase hook returns
// custom response. // custom response.
func TestH1H1RespPhaseReturn(t *testing.T) { func TestH1H1RespPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler) st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.http1(requestParam{ res, err := st.http1(requestParam{
@ -457,7 +457,7 @@ func TestH1H1RespPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "21"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -466,7 +466,7 @@ func TestH1H1RespPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from resp"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -667,7 +667,7 @@ func TestH1H2NoVia(t *testing.T) {
// TestH1H2ReqPhaseReturn tests mruby request phase hook returns // TestH1H2ReqPhaseReturn tests mruby request phase hook returns
// custom response. // custom response.
func TestH1H2ReqPhaseReturn(t *testing.T) { func TestH1H2ReqPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("request should not be forwarded") t.Fatalf("request should not be forwarded")
}) })
defer st.Close() defer st.Close()
@ -686,7 +686,7 @@ func TestH1H2ReqPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "20"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -695,7 +695,7 @@ func TestH1H2ReqPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from req"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -703,7 +703,7 @@ func TestH1H2ReqPhaseReturn(t *testing.T) {
// TestH1H2RespPhaseReturn tests mruby response phase hook returns // TestH1H2RespPhaseReturn tests mruby response phase hook returns
// custom response. // custom response.
func TestH1H2RespPhaseReturn(t *testing.T) { func TestH1H2RespPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler) st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.http1(requestParam{ res, err := st.http1(requestParam{
@ -720,7 +720,7 @@ func TestH1H2RespPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "21"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -729,7 +729,7 @@ func TestH1H2RespPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from resp"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }

View File

@ -644,7 +644,7 @@ func TestH2H1HeaderFields(t *testing.T) {
// TestH2H1ReqPhaseSetHeader tests mruby request phase hook // TestH2H1ReqPhaseSetHeader tests mruby request phase hook
// modifies request header fields. // modifies request header fields.
func TestH2H1ReqPhaseSetHeader(t *testing.T) { func TestH2H1ReqPhaseSetHeader(t *testing.T) {
st := newServerTester([]string{"--request-phase-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTester([]string{"--mruby-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("User-Agent"), "mruby"; got != want { if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
t.Errorf("User-Agent = %v; want %v", got, want) t.Errorf("User-Agent = %v; want %v", got, want)
} }
@ -666,7 +666,7 @@ func TestH2H1ReqPhaseSetHeader(t *testing.T) {
// TestH2H1ReqPhaseReturn tests mruby request phase hook returns // TestH2H1ReqPhaseReturn tests mruby request phase hook returns
// custom response. // custom response.
func TestH2H1ReqPhaseReturn(t *testing.T) { func TestH2H1ReqPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTester([]string{"--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("request should not be forwarded") t.Fatalf("request should not be forwarded")
}) })
defer st.Close() defer st.Close()
@ -685,7 +685,7 @@ func TestH2H1ReqPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "20"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -694,7 +694,7 @@ func TestH2H1ReqPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from req"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -702,7 +702,7 @@ func TestH2H1ReqPhaseReturn(t *testing.T) {
// TestH2H1RespPhaseSetHeader tests mruby response phase hook modifies // TestH2H1RespPhaseSetHeader tests mruby response phase hook modifies
// response header fields. // response header fields.
func TestH2H1RespPhaseSetHeader(t *testing.T) { func TestH2H1RespPhaseSetHeader(t *testing.T) {
st := newServerTester([]string{"--response-phase-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler) st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.http2(requestParam{ res, err := st.http2(requestParam{
@ -724,7 +724,7 @@ func TestH2H1RespPhaseSetHeader(t *testing.T) {
// TestH2H1RespPhaseReturn tests mruby response phase hook returns // TestH2H1RespPhaseReturn tests mruby response phase hook returns
// custom response. // custom response.
func TestH2H1RespPhaseReturn(t *testing.T) { func TestH2H1RespPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler) st := newServerTester([]string{"--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.http2(requestParam{ res, err := st.http2(requestParam{
@ -741,7 +741,7 @@ func TestH2H1RespPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "21"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -750,7 +750,7 @@ func TestH2H1RespPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from resp"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -1351,7 +1351,7 @@ func TestH2H2TLSXfp(t *testing.T) {
// TestH2H2ReqPhaseReturn tests mruby request phase hook returns // TestH2H2ReqPhaseReturn tests mruby request phase hook returns
// custom response. // custom response.
func TestH2H2ReqPhaseReturn(t *testing.T) { func TestH2H2ReqPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("request should not be forwarded") t.Fatalf("request should not be forwarded")
}) })
defer st.Close() defer st.Close()
@ -1370,7 +1370,7 @@ func TestH2H2ReqPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "20"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -1379,7 +1379,7 @@ func TestH2H2ReqPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from req"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -1387,7 +1387,7 @@ func TestH2H2ReqPhaseReturn(t *testing.T) {
// TestH2H2RespPhaseReturn tests mruby response phase hook returns // TestH2H2RespPhaseReturn tests mruby response phase hook returns
// custom response. // custom response.
func TestH2H2RespPhaseReturn(t *testing.T) { func TestH2H2RespPhaseReturn(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler) st := newServerTester([]string{"--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.http2(requestParam{ res, err := st.http2(requestParam{
@ -1404,7 +1404,7 @@ func TestH2H2RespPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "21"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -1413,7 +1413,7 @@ func TestH2H2RespPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from resp"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }

View File

@ -233,7 +233,7 @@ func TestS3H1InvalidMethod(t *testing.T) {
// TestS3H1ReqPhaseSetHeader tests mruby request phase hook // TestS3H1ReqPhaseSetHeader tests mruby request phase hook
// modifies request header fields. // modifies request header fields.
func TestS3H1ReqPhaseSetHeader(t *testing.T) { func TestS3H1ReqPhaseSetHeader(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--request-phase-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("User-Agent"), "mruby"; got != want { if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
t.Errorf("User-Agent = %v; want %v", got, want) t.Errorf("User-Agent = %v; want %v", got, want)
} }
@ -255,7 +255,7 @@ func TestS3H1ReqPhaseSetHeader(t *testing.T) {
// TestS3H1ReqPhaseReturn tests mruby request phase hook returns // TestS3H1ReqPhaseReturn tests mruby request phase hook returns
// custom response. // custom response.
func TestS3H1ReqPhaseReturn(t *testing.T) { func TestS3H1ReqPhaseReturn(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("request should not be forwarded") t.Fatalf("request should not be forwarded")
}) })
defer st.Close() defer st.Close()
@ -274,7 +274,7 @@ func TestS3H1ReqPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "20"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -283,7 +283,7 @@ func TestS3H1ReqPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from req"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -291,7 +291,7 @@ func TestS3H1ReqPhaseReturn(t *testing.T) {
// TestS3H1RespPhaseSetHeader tests mruby response phase hook modifies // TestS3H1RespPhaseSetHeader tests mruby response phase hook modifies
// response header fields. // response header fields.
func TestS3H1RespPhaseSetHeader(t *testing.T) { func TestS3H1RespPhaseSetHeader(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--response-phase-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler) st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.spdy(requestParam{ res, err := st.spdy(requestParam{
@ -313,7 +313,7 @@ func TestS3H1RespPhaseSetHeader(t *testing.T) {
// TestS3H1RespPhaseReturn tests mruby response phase hook returns // TestS3H1RespPhaseReturn tests mruby response phase hook returns
// custom response. // custom response.
func TestS3H1RespPhaseReturn(t *testing.T) { func TestS3H1RespPhaseReturn(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler) st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.spdy(requestParam{ res, err := st.spdy(requestParam{
@ -330,7 +330,7 @@ func TestS3H1RespPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "21"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -339,7 +339,7 @@ func TestS3H1RespPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from resp"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -368,7 +368,7 @@ func TestS3H2ConnectFailure(t *testing.T) {
// TestS3H2ReqPhaseReturn tests mruby request phase hook returns // TestS3H2ReqPhaseReturn tests mruby request phase hook returns
// custom response. // custom response.
func TestS3H2ReqPhaseReturn(t *testing.T) { func TestS3H2ReqPhaseReturn(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--request-phase-file=" + testDir + "/return.rb"}, t, func(w http.ResponseWriter, r *http.Request) { st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("request should not be forwarded") t.Fatalf("request should not be forwarded")
}) })
defer st.Close() defer st.Close()
@ -387,7 +387,7 @@ func TestS3H2ReqPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "20"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -396,7 +396,7 @@ func TestS3H2ReqPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from req"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }
@ -404,7 +404,7 @@ func TestS3H2ReqPhaseReturn(t *testing.T) {
// TestS3H2RespPhaseReturn tests mruby response phase hook returns // TestS3H2RespPhaseReturn tests mruby response phase hook returns
// custom response. // custom response.
func TestS3H2RespPhaseReturn(t *testing.T) { func TestS3H2RespPhaseReturn(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--response-phase-file=" + testDir + "/return.rb"}, t, noopHandler) st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
defer st.Close() defer st.Close()
res, err := st.spdy(requestParam{ res, err := st.spdy(requestParam{
@ -421,7 +421,7 @@ func TestS3H2RespPhaseReturn(t *testing.T) {
hdtests := []struct { hdtests := []struct {
k, v string k, v string
}{ }{
{"content-length", "11"}, {"content-length", "21"},
{"from", "mruby"}, {"from", "mruby"},
} }
for _, tt := range hdtests { for _, tt := range hdtests {
@ -430,7 +430,7 @@ func TestS3H2RespPhaseReturn(t *testing.T) {
} }
} }
if got, want := string(res.body), "Hello World"; got != want { if got, want := string(res.body), "Hello World from resp"; got != want {
t.Errorf("body = %v; want %v", got, want) t.Errorf("body = %v; want %v", got, want)
} }
} }

View File

@ -0,0 +1,12 @@
class App
def on_req(env)
resp = env.resp
resp.clear_headers
resp.status = 404
resp.add_header "from", "mruby"
resp.return "Hello World from req"
end
end
App.new

View File

@ -1,3 +1,7 @@
Nghttpx.run do |env| class App
def on_req(env)
env.req.set_header "User-Agent", "mruby" env.req.set_header "User-Agent", "mruby"
end end
end
App.new

View File

@ -0,0 +1,12 @@
class App
def on_resp(env)
resp = env.resp
resp.clear_headers
resp.status = 404
resp.add_header "from", "mruby"
resp.return "Hello World from resp"
end
end
App.new

View File

@ -1,3 +1,7 @@
Nghttpx.run do |env| class App
def on_resp(env)
env.resp.set_header "Alpha", "bravo" env.resp.set_header "Alpha", "bravo"
end end
end
App.new

View File

@ -1,8 +0,0 @@
Nghttpx.run do |env|
resp = env.resp
resp.clear_headers
resp.status = 404
resp.add_header "from", "mruby"
resp.return "Hello World"
end

View File

@ -1801,8 +1801,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_REQUEST_PHASE_FILE, required_argument, &flag, 91}, {SHRPX_OPT_MRUBY_FILE, required_argument, &flag, 91},
{SHRPX_OPT_RESPONSE_PHASE_FILE, required_argument, &flag, 92},
{SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, no_argument, &flag, 93}, {SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, no_argument, &flag, 93},
{SHRPX_OPT_FASTOPEN, required_argument, &flag, 94}, {SHRPX_OPT_FASTOPEN, required_argument, &flag, 94},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
@ -2199,12 +2198,8 @@ int main(int argc, char **argv) {
optarg); optarg);
break; break;
case 91: case 91:
// --request-phase-file // --mruby-file
cmdcfgs.emplace_back(SHRPX_OPT_REQUEST_PHASE_FILE, optarg); cmdcfgs.emplace_back(SHRPX_OPT_MRUBY_FILE, optarg);
break;
case 92:
// --response-phase-file
cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_PHASE_FILE, optarg);
break; break;
case 93: case 93:
// --accept-proxy-protocol // --accept-proxy-protocol

View File

@ -672,6 +672,7 @@ enum {
SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT, SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT,
SHRPX_OPTID_LOG_LEVEL, SHRPX_OPTID_LOG_LEVEL,
SHRPX_OPTID_MAX_HEADER_FIELDS, SHRPX_OPTID_MAX_HEADER_FIELDS,
SHRPX_OPTID_MRUBY_FILE,
SHRPX_OPTID_NO_HOST_REWRITE, SHRPX_OPTID_NO_HOST_REWRITE,
SHRPX_OPTID_NO_LOCATION_REWRITE, SHRPX_OPTID_NO_LOCATION_REWRITE,
SHRPX_OPTID_NO_OCSP, SHRPX_OPTID_NO_OCSP,
@ -685,8 +686,6 @@ enum {
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE, SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
SHRPX_OPTID_READ_BURST, SHRPX_OPTID_READ_BURST,
SHRPX_OPTID_READ_RATE, SHRPX_OPTID_READ_RATE,
SHRPX_OPTID_REQUEST_PHASE_FILE,
SHRPX_OPTID_RESPONSE_PHASE_FILE,
SHRPX_OPTID_RLIMIT_NOFILE, SHRPX_OPTID_RLIMIT_NOFILE,
SHRPX_OPTID_STREAM_READ_TIMEOUT, SHRPX_OPTID_STREAM_READ_TIMEOUT,
SHRPX_OPTID_STREAM_WRITE_TIMEOUT, SHRPX_OPTID_STREAM_WRITE_TIMEOUT,
@ -844,6 +843,9 @@ int option_lookup_token(const char *name, size_t namelen) {
case 10: case 10:
switch (name[9]) { switch (name[9]) {
case 'e': case 'e':
if (util::strieq_l("mruby-fil", name, 9)) {
return SHRPX_OPTID_MRUBY_FILE;
}
if (util::strieq_l("write-rat", name, 9)) { if (util::strieq_l("write-rat", name, 9)) {
return SHRPX_OPTID_WRITE_RATE; return SHRPX_OPTID_WRITE_RATE;
} }
@ -1013,11 +1015,6 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 18: case 18:
switch (name[17]) { switch (name[17]) {
case 'e':
if (util::strieq_l("request-phase-fil", name, 17)) {
return SHRPX_OPTID_REQUEST_PHASE_FILE;
}
break;
case 'r': case 'r':
if (util::strieq_l("add-request-heade", name, 17)) { if (util::strieq_l("add-request-heade", name, 17)) {
return SHRPX_OPTID_ADD_REQUEST_HEADER; return SHRPX_OPTID_ADD_REQUEST_HEADER;
@ -1036,9 +1033,6 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("no-location-rewrit", name, 18)) { if (util::strieq_l("no-location-rewrit", name, 18)) {
return SHRPX_OPTID_NO_LOCATION_REWRITE; return SHRPX_OPTID_NO_LOCATION_REWRITE;
} }
if (util::strieq_l("response-phase-fil", name, 18)) {
return SHRPX_OPTID_RESPONSE_PHASE_FILE;
}
if (util::strieq_l("tls-ticket-key-fil", name, 18)) { if (util::strieq_l("tls-ticket-key-fil", name, 18)) {
return SHRPX_OPTID_TLS_TICKET_KEY_FILE; return SHRPX_OPTID_TLS_TICKET_KEY_FILE;
} }
@ -1967,17 +1961,9 @@ 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_REQUEST_PHASE_FILE: case SHRPX_OPTID_MRUBY_FILE:
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
mod_config()->request_phase_file = strcopy(optarg); mod_config()->mruby_file = strcopy(optarg);
#else // !HAVE_MRUBY
LOG(WARN) << opt
<< ": ignored because mruby support is disabled at build time.";
#endif // !HAVE_MRUBY
return 0;
case SHRPX_OPTID_RESPONSE_PHASE_FILE:
#ifdef HAVE_MRUBY
mod_config()->response_phase_file = strcopy(optarg);
#else // !HAVE_MRUBY #else // !HAVE_MRUBY
LOG(WARN) << opt LOG(WARN) << opt
<< ": ignored because mruby support is disabled at build time."; << ": ignored because mruby support is disabled at build time.";

View File

@ -184,8 +184,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_REQUEST_PHASE_FILE[] = "request-phase-file"; constexpr char SHRPX_OPT_MRUBY_FILE[] = "mruby-file";
constexpr char SHRPX_OPT_RESPONSE_PHASE_FILE[] = "response-phase-file";
constexpr char SHRPX_OPT_ACCEPT_PROXY_PROTOCOL[] = "accept-proxy-protocol"; constexpr char SHRPX_OPT_ACCEPT_PROXY_PROTOCOL[] = "accept-proxy-protocol";
constexpr char SHRPX_OPT_FASTOPEN[] = "fastopen"; constexpr char SHRPX_OPT_FASTOPEN[] = "fastopen";
@ -325,8 +324,7 @@ 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[]> request_phase_file; std::unique_ptr<char[]> mruby_file;
std::unique_ptr<char[]> response_phase_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

@ -37,10 +37,8 @@ namespace shrpx {
namespace mruby { namespace mruby {
MRubyContext::MRubyContext(mrb_state *mrb, RProc *on_request_proc, MRubyContext::MRubyContext(mrb_state *mrb, mrb_value app, mrb_value env)
RProc *on_response_proc) : mrb_(mrb), app_(std::move(app)), env_(std::move(env)) {}
: mrb_(mrb), on_request_proc_(on_request_proc),
on_response_proc_(on_response_proc), running_(false) {}
MRubyContext::~MRubyContext() { MRubyContext::~MRubyContext() {
if (mrb_) { if (mrb_) {
@ -48,22 +46,38 @@ MRubyContext::~MRubyContext() {
} }
} }
int MRubyContext::run_request_proc(Downstream *downstream, RProc *proc, int MRubyContext::run_app(Downstream *downstream, int phase) {
int phase) { if (!mrb_) {
if (!proc || running_) {
return 0; return 0;
} }
running_ = true;
MRubyAssocData data{downstream, phase}; MRubyAssocData data{downstream, phase};
mrb_->ud = &data; mrb_->ud = &data;
int rv = 0; int rv = 0;
auto ai = mrb_gc_arena_save(mrb_); auto ai = mrb_gc_arena_save(mrb_);
auto ai_d = defer([ai, this]() { mrb_gc_arena_restore(mrb_, ai); });
auto res = mrb_run(mrb_, proc, mrb_top_self(mrb_)); const char *method;
switch (phase) {
case PHASE_REQUEST:
if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_req"))) {
return 0;
}
method = "on_req";
break;
case PHASE_RESPONSE:
if (!mrb_respond_to(mrb_, app_, mrb_intern_lit(mrb_, "on_resp"))) {
return 0;
}
method = "on_resp";
break;
default:
assert(0);
}
auto res = mrb_funcall(mrb_, app_, method, 1, env_);
(void)res; (void)res;
if (mrb_->exc) { if (mrb_->exc) {
@ -71,18 +85,16 @@ int MRubyContext::run_request_proc(Downstream *downstream, RProc *proc,
if (downstream->get_response_state() != Downstream::MSG_COMPLETE) { if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
rv = -1; rv = -1;
} }
auto error =
mrb_str_ptr(mrb_funcall(mrb_, mrb_obj_value(mrb_->exc), "inspect", 0)); auto exc = mrb_obj_value(mrb_->exc);
auto inspect = mrb_inspect(mrb_, exc);
LOG(ERROR) << "Exception caught while executing mruby code: " LOG(ERROR) << "Exception caught while executing mruby code: "
<< error->as.heap.ptr; << mrb_str_to_cstr(mrb_, inspect);
mrb_->exc = 0;
} }
mrb_->ud = nullptr; mrb_->ud = nullptr;
mrb_gc_arena_restore(mrb_, ai);
if (data.request_headers_dirty) { if (data.request_headers_dirty) {
downstream->index_request_headers(); downstream->index_request_headers();
} }
@ -91,17 +103,15 @@ int MRubyContext::run_request_proc(Downstream *downstream, RProc *proc,
downstream->index_response_headers(); downstream->index_response_headers();
} }
running_ = false;
return rv; return rv;
} }
int MRubyContext::run_on_request_proc(Downstream *downstream) { int MRubyContext::run_on_request_proc(Downstream *downstream) {
return run_request_proc(downstream, on_request_proc_, PHASE_REQUEST); return run_app(downstream, PHASE_REQUEST);
} }
int MRubyContext::run_on_response_proc(Downstream *downstream) { int MRubyContext::run_on_response_proc(Downstream *downstream) {
return run_request_proc(downstream, on_response_proc_, PHASE_RESPONSE); return run_app(downstream, PHASE_RESPONSE);
} }
void MRubyContext::delete_downstream(Downstream *downstream) { void MRubyContext::delete_downstream(Downstream *downstream) {
@ -111,6 +121,26 @@ void MRubyContext::delete_downstream(Downstream *downstream) {
delete_downstream_from_module(mrb_, downstream); delete_downstream_from_module(mrb_, downstream);
} }
namespace {
mrb_value instantiate_app(mrb_state *mrb, RProc *proc) {
mrb->ud = nullptr;
auto res = mrb_run(mrb, proc, mrb_top_self(mrb));
if (mrb->exc) {
auto exc = mrb_obj_value(mrb->exc);
auto inspect = mrb_inspect(mrb, exc);
LOG(ERROR) << "Exception caught while executing mruby code: "
<< mrb_str_to_cstr(mrb, inspect);
return mrb_nil_value();
}
return res;
}
} // namespace
// Based on // Based on
// https://github.com/h2o/h2o/blob/master/lib/handler/mruby.c. It is // 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 // very hard to write these kind of code because mruby has almost no
@ -155,12 +185,9 @@ RProc *compile(mrb_state *mrb, const char *filename) {
return proc; return proc;
} }
std::unique_ptr<MRubyContext> create_mruby_context() { std::unique_ptr<MRubyContext> create_mruby_context(const char *filename) {
auto req_file = get_config()->request_phase_file.get(); if (!filename) {
auto res_file = get_config()->response_phase_file.get(); return make_unique<MRubyContext>(nullptr, mrb_nil_value(), mrb_nil_value());
if (!req_file && !res_file) {
return make_unique<MRubyContext>(nullptr, nullptr, nullptr);
} }
auto mrb = mrb_open(); auto mrb = mrb_open();
@ -169,25 +196,34 @@ std::unique_ptr<MRubyContext> create_mruby_context() {
return nullptr; return nullptr;
} }
init_module(mrb); auto ai = mrb_gc_arena_save(mrb);
auto req_proc = compile(mrb, req_file); auto req_proc = compile(mrb, filename);
if (req_file && !req_proc) { if (!req_proc) {
LOG(ERROR) << "Could not compile mruby code " << req_file; mrb_gc_arena_restore(mrb, ai);
LOG(ERROR) << "Could not compile mruby code " << filename;
mrb_close(mrb); mrb_close(mrb);
return nullptr; return nullptr;
} }
auto res_proc = compile(mrb, res_file); auto env = init_module(mrb);
if (res_file && !res_proc) { auto app = instantiate_app(mrb, req_proc);
LOG(ERROR) << "Could not compile mruby code " << res_file; if (mrb_nil_p(app)) {
mrb_gc_arena_restore(mrb, ai);
LOG(ERROR) << "Could not instantiate mruby app from " << filename;
mrb_close(mrb); mrb_close(mrb);
return nullptr; return nullptr;
} }
return make_unique<MRubyContext>(mrb, req_proc, res_proc); mrb_gc_arena_restore(mrb, ai);
// TODO These are not necessary, because we retain app and env?
mrb_gc_protect(mrb, env);
mrb_gc_protect(mrb, app);
return make_unique<MRubyContext>(mrb, std::move(app), std::move(env));
} }
mrb_sym intern_ptr(mrb_state *mrb, void *ptr) { mrb_sym intern_ptr(mrb_state *mrb, void *ptr) {

View File

@ -40,21 +40,20 @@ namespace mruby {
class MRubyContext { class MRubyContext {
public: public:
MRubyContext(mrb_state *mrb, RProc *on_request_proc, RProc *on_response_proc); MRubyContext(mrb_state *mrb, mrb_value app, mrb_value env);
~MRubyContext(); ~MRubyContext();
int run_on_request_proc(Downstream *downstream); int run_on_request_proc(Downstream *downstream);
int run_on_response_proc(Downstream *downstream); int run_on_response_proc(Downstream *downstream);
int run_request_proc(Downstream *downstream, RProc *proc, int phase); int run_app(Downstream *downstream, int phase);
void delete_downstream(Downstream *downstream); void delete_downstream(Downstream *downstream);
private: private:
mrb_state *mrb_; mrb_state *mrb_;
RProc *on_request_proc_; mrb_value app_;
RProc *on_response_proc_; mrb_value env_;
bool running_;
}; };
enum { enum {
@ -72,7 +71,7 @@ struct MRubyAssocData {
RProc *compile(mrb_state *mrb, const char *filename); RProc *compile(mrb_state *mrb, const char *filename);
std::unique_ptr<MRubyContext> create_mruby_context(); std::unique_ptr<MRubyContext> create_mruby_context(const char *filename);
// Return interned |ptr|. // Return interned |ptr|.
mrb_sym intern_ptr(mrb_state *mrb, void *ptr); mrb_sym intern_ptr(mrb_state *mrb, void *ptr);

View File

@ -41,36 +41,21 @@ namespace shrpx {
namespace mruby { namespace mruby {
namespace { namespace {
mrb_value run(mrb_state *mrb, mrb_value self) { mrb_value create_env(mrb_state *mrb) {
mrb_value b;
mrb_get_args(mrb, "&", &b);
if (mrb_nil_p(b)) {
return mrb_nil_value();
}
auto module = mrb_module_get(mrb, "Nghttpx"); auto module = mrb_module_get(mrb, "Nghttpx");
auto env_sym = mrb_intern_lit(mrb, "env");
auto env = mrb_obj_iv_get(mrb, reinterpret_cast<RObject *>(module), env_sym);
if (mrb_nil_p(env)) {
auto env_class = mrb_class_get_under(mrb, module, "Env"); auto env_class = mrb_class_get_under(mrb, module, "Env");
auto request_class = mrb_class_get_under(mrb, module, "Request"); auto request_class = mrb_class_get_under(mrb, module, "Request");
auto response_class = mrb_class_get_under(mrb, module, "Response"); auto response_class = mrb_class_get_under(mrb, module, "Response");
env = mrb_obj_new(mrb, env_class, 0, nullptr); auto env = mrb_obj_new(mrb, env_class, 0, nullptr);
auto req = mrb_obj_new(mrb, request_class, 0, nullptr); auto req = mrb_obj_new(mrb, request_class, 0, nullptr);
auto resp = mrb_obj_new(mrb, response_class, 0, nullptr); auto resp = mrb_obj_new(mrb, response_class, 0, nullptr);
mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "req"), req); mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "req"), req);
mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "resp"), resp); mrb_iv_set(mrb, env, mrb_intern_lit(mrb, "resp"), resp);
mrb_obj_iv_set(mrb, reinterpret_cast<RObject *>(module), env_sym, env); return env;
}
std::array<mrb_value, 1> args{{env}};
return mrb_yield_argv(mrb, b, args.size(), args.data());
} }
} // namespace } // namespace
@ -85,11 +70,9 @@ void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream) {
mrb_iv_remove(mrb, env, intern_ptr(mrb, downstream)); mrb_iv_remove(mrb, env, intern_ptr(mrb, downstream));
} }
void init_module(mrb_state *mrb) { mrb_value init_module(mrb_state *mrb) {
auto module = mrb_define_module(mrb, "Nghttpx"); auto module = mrb_define_module(mrb, "Nghttpx");
mrb_define_class_method(mrb, module, "run", run,
MRB_ARGS_REQ(1) | MRB_ARGS_BLOCK());
mrb_define_const(mrb, module, "REQUEST_PHASE", mrb_define_const(mrb, module, "REQUEST_PHASE",
mrb_fixnum_value(PHASE_REQUEST)); mrb_fixnum_value(PHASE_REQUEST));
mrb_define_const(mrb, module, "RESPONSE_PHASE", mrb_define_const(mrb, module, "RESPONSE_PHASE",
@ -98,6 +81,8 @@ void init_module(mrb_state *mrb) {
init_env_class(mrb, module); init_env_class(mrb, module);
init_request_class(mrb, module); init_request_class(mrb, module);
init_response_class(mrb, module); init_response_class(mrb, module);
return create_env(mrb);
} }
mrb_value create_headers_hash(mrb_state *mrb, const Headers &headers) { mrb_value create_headers_hash(mrb_state *mrb, const Headers &headers) {

View File

@ -39,7 +39,7 @@ class Downstream;
namespace mruby { namespace mruby {
void init_module(mrb_state *mrb); mrb_value init_module(mrb_state *mrb);
void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream); void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream);

View File

@ -270,7 +270,8 @@ MemcachedDispatcher *Worker::get_session_cache_memcached_dispatcher() {
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
int Worker::create_mruby_context() { int Worker::create_mruby_context() {
mruby_ctx_ = mruby::create_mruby_context(); auto mruby_file = get_config()->mruby_file.get();
mruby_ctx_ = mruby::create_mruby_context(mruby_file);
if (!mruby_ctx_) { if (!mruby_ctx_) {
return -1; return -1;
} }