summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/doc/configure/mruby.html
blob: d424351d882f7d10be5a28e125332bd9126d022c (plain)
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<base href="../" />

<!-- oktavia -->
<link rel="stylesheet" href="assets/searchstyle.css" type="text/css" />
<script src="search/jquery-1.9.1.min.js"></script>
<script src="search/oktavia-jquery-ui.js"></script>
<script src="search/oktavia-english-search.js"></script>
<!-- /oktavia -->

<link rel="stylesheet" href="assets/style.css" type="text/css" />

<title>Using Mruby - Configure - H2O - the optimized HTTP/2 server</title>
</head>
<body>
<div id="body">
<div id="top">

<h1>
<a href="index.html">H2O</a>
</h1>
<p class="description">the optimized HTTP/1.x, HTTP/2 server</p>

<!-- oktavia -->
<form id="searchform">
<input class="search" type="search" name="search" id="search" results="5" value="" placeholder="Search" />
<div id="searchresult_box">
<div id="close_search_box">&times;</div>
<div id="searchresult_summary"></div>
<div id="searchresult"></div>
<div id="searchresult_nav"></div>
<span class="pr">Powered by <a href="https://github.com/shibukawa/oktavia">Oktavia</a></span>
</div>
</form>
<!-- /oktavia -->

</div>

<table id="menu">
<tr>
<td><a href="index.html">Top</a></td>
<td><a href="install.html">Install</a></td>
<td class="selected">Configure</td>
<td><a href="faq.html">FAQ</a></td>
<td><a href="http://blog.kazuhooku.com/search/label/H2O" target="_blank">Blog</a></td>
<td><a href="http://github.com/h2o/h2o/" target="_blank">Source</a></td>
</tr>
</table>

<div id="main">

<h2>
<a href="configure.html">Configure</a> &gt;
Using Mruby
</h2>


<p>
<a href="https://github.com/mruby/mruby">mruby</a> is a lightweight implementation of the Ruby programming language.
With H2O, users can implement their own request handling logic using mruby, either to generate responses or to fix-up the request / response.
</p>

<h3 id="programming-interface">Rack-based Programming Interface</h3>

<p>
The interface between the mruby program and the H2O server is based on <a href="http://www.rubydoc.info/github/rack/rack/master/file/SPEC">Rack interface specification</a>.
Below is a simple configuration that returns <i>hello world</i>.
</p>

<div class="example">
<div class="caption">Example. Hello-world in mruby</div>
<pre><code>paths:
  &quot;/&quot;:
    mruby.handler: |
      Proc.new do |env|
        [200, {&#39;content-type&#39; =&gt; &#39;text/plain&#39;}, [&quot;Hello world\n&quot;]]
      end
</code></pre>
</div>


<p>
It should be noted that as of H2O version 1.7.0, there are limitations when compared to ordinary web application server with support for Rack such as Unicorn:
<ul>
<li>no libraries provided as part of Rack is available (only the interface is compatible)
</ul>
</p>

<p>
In addition to the Rack interface specification, H2O recognizes status code <code>399</code> which can be used to delegate request to the next handler.
The feature can be used to implement access control and response header modifiers.
</p>

<h3 id="access-control">Access Control</h3>

<p>
By using the <code>399</code> status code, it is possible to implement access control using mruby.
The example below restricts access to requests from <code>192.168.</code> private address.
</p>

<div class="example">
<div class="caption">Example. Restricting access to 192.168.</div>
<pre><code>paths:
  &quot;/&quot;:
    mruby.handler: |
      lambda do |env|
        if /\A192\.168\./.match(env[&quot;REMOTE_ADDR&quot;])
          return [399, {}, []]
        end
        [403, {&#39;content-type&#39; =&gt; &#39;text/plain&#39;}, [&quot;access forbidden\n&quot;]]
      end
</code></pre>
</div>


<p>
Support for <a href="configure/basic_auth.html">Basic Authentication</a> is also provided by an mruby script.
</p>

<h3 id="delegating-request">Delegating the Request</h3>

<p>
When enabled using the <a href="configure/reproxy_directives.html#reproxy"><code>reproxy</code></a> directive, it is possible to delegate the request from the mruby handler to any other handler.
</p>
<p>
<div class="example">
<div class="caption">Example. Rewriting URL with delegation</div>
<pre><code>paths:
  &quot;/&quot;:
    mruby.handler: |
      lambda do |env|
        if /\/user\/([^\/]+)/.match(env[&quot;PATH_INFO&quot;])
          return [307, {&quot;x-reproxy-url&quot; =&gt; &quot;/user.php?user=#{$1}&quot;}, []]
        end
        return [399, {}, []]
      end
</code></pre>
</div>


<h3 id="modifying-response">Modifying the Response</h3>

<p>
When the mruby handler returns status code <code>399</code>, H2O delegates the request to the next handler while preserving the headers emitted by the handler.
The feature can be used to add extra headers to the response.
</p>
<p>
For example, the following example sets <code>cache-control</code> header for requests against <code>.css</code> and <code>.js</code> files.
</p>

<div class="example">
<div class="caption">Example. Setting cache-control header for certain types of files</div>
<pre><code>paths:
  &quot;/&quot;:
    mruby.handler: |
      Proc.new do |env|
        headers = {}
        if /\.(css|js)\z/.match(env[&quot;PATH_INFO&quot;])
          headers[&quot;cache-control&quot;] = &quot;max-age=86400&quot;
        end
        [399, headers, []]
      end
    file.dir: /path/to/doc-root
</code></pre>
</div>


<p>
Or in the example below, the handler triggers <a href="configure/http2_directives.html#server-push">HTTP/2 server push</a> with the use of <code>Link: rel=preload</code> headers, and then requests a FastCGI application to process the request.
</p>

<div class="example">
<div class="caption">Example. Pushing asset files</div>
<pre><code>paths:
  &quot;/&quot;:
    mruby.handler: |
      Proc.new do |env|
        push_paths = []
        # push css and js when request is to dir root or HTML
        if /(\/|\.html)\z/.match(env[&quot;PATH_INFO&quot;])
          push_paths &lt;&lt; [&quot;/css/style.css&quot;, &quot;style&quot;]
          push_paths &lt;&lt; [&quot;/js/app.js&quot;, &quot;script&quot;]
        end
        [399, push_paths.empty? ? {} : {&quot;link&quot; =&gt; push_paths.map{|p| &quot;&lt;#{p[0]}&gt;; rel=preload; as=#{p[1]}&quot;}.join(&quot;\n&quot;)}, []]
      end
    fastcgi.connect: ...
</code></pre>
</div>


<h3 id="http-client">Using the HTTP Client</h3>

<p>
Starting from version 1.7, a HTTP client API is provided.
HTTP requests issued through the API will be handled asynchronously; the client does not block the event loop of the HTTP server.
</p>

<div class="example">
<div class="caption">Example. Mruby handler returning the response of http://example.com</div>
<pre><code>paths:
  &quot;/&quot;:
    mruby.handler: |
      Proc.new do |env|
        req = http_request(&quot;http://example.com&quot;)
        status, headers, body = req.join
        [status, headers, body]
      end
</code></pre>
</div>


<p>
<code>http_request</code> is the method that issues a HTTP request.
</p>
<p>
The method takes two arguments.
First argument is the target URI.
Second argument is an optional hash; <code>method</code> (defaults to <code>GET</code>), <code>header</code>, <code>body</code> attributes are recognized.
</p>
<p>
The method returns a promise object.
When <code>#join</code> method of the promise is invoked, a three-argument array containing the status code, response headers, and the body is returned.
The response body is also a promise.
Applications can choose from three ways when dealing with the body: a) call <code>#each</code> method to receive the contents, b) call <code>#join</code> to retrieve the body as a string, c) return the object as the response body of the mruby handler.
</p>
<p>
The header and the body object passed to <code>http_request</code> should conform to the requirements laid out by the Rack specification for request header and request body.
The response header and the response body object returned by the <code>#join</code> method of the promise returned by <code>http_request</code> conforms to the requirements of the Rack specification.
</p>
<p>
Since the API provides an asynchronous HTTP client, it is possible to effectively issue multiple HTTP requests concurrently and merge them into a single response.
</p>
<p>
When HTTPS is used, servers are verified using the properties of <a href="configure/proxy_directives.html#proxy.ssl.cafile"><code>proxy.ssl.cafile</code></a> and <a href="configure/proxy_directives.html#proxy.ssl.verify-peer"><code>proxy.ssl.verify-peer</code></a> specified at the global level.
</p>

<h3 id="logging-arbitrary-variable">Logging Arbitrary Variable</h3>

<p>
In version 2.3, it is possible from mruby to set and log an arbitrary-named variable that is associated to a HTTP request.
A HTTP response header that starts with <code>x-fallthru-set-</code> is handled specially by the H2O server. Instead of sending the header downstream, the server accepts the value as a request environment variable, taking the suffix of the header name as the name of the variable.
</p>
<p>
This example shows how to read request data, parse json and then log data from mruby.
</p>

<div class="example">
<div class="caption">Example. Logging the content of a POST request via request environment variable</div>
<pre><code>paths:
  &quot;/&quot;:
    mruby.handler: |
      Proc.new do |env|
        input = env[&quot;rack.input&quot;] ? env[&quot;rack.input&quot;].read : &#39;{&quot;default&quot;: &quot;true&quot;}&#39;
        parsed_json = JSON.parse(input)
        parsed_json[&quot;time&quot;] = Time.now.to_i
        logdata = parsed_json.to_s
        [204, {&quot;x-fallthru-set-POSTDATA&quot; =&gt; logdata}, []]
      end
    access-log:
      path: /path/to/access-log.json
      escape: json
      format: &#39;{&quot;POST&quot;: %{POSTDATA}e}&#39;
</code></pre>
</div>





</div>
<div id="footer">
<p>
Copyright &copy; 2015 <a href="http://dena.com/intl/">DeNA Co., Ltd.</a> et al.
</p>
</div>
</body>
</html>