1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2015 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "shrpx_mruby_module_request.h"
26
27 #include <mruby/variable.h>
28 #include <mruby/string.h>
29 #include <mruby/hash.h>
30 #include <mruby/array.h>
31
32 #include "shrpx_downstream.h"
33 #include "shrpx_upstream.h"
34 #include "shrpx_client_handler.h"
35 #include "shrpx_mruby.h"
36 #include "shrpx_mruby_module.h"
37 #include "shrpx_log.h"
38 #include "util.h"
39 #include "http2.h"
40
41 namespace shrpx {
42
43 namespace mruby {
44
45 namespace {
request_init(mrb_state * mrb,mrb_value self)46 mrb_value request_init(mrb_state *mrb, mrb_value self) { return self; }
47 } // namespace
48
49 namespace {
request_get_http_version_major(mrb_state * mrb,mrb_value self)50 mrb_value request_get_http_version_major(mrb_state *mrb, mrb_value self) {
51 auto data = static_cast<MRubyAssocData *>(mrb->ud);
52 auto downstream = data->downstream;
53 const auto &req = downstream->request();
54 return mrb_fixnum_value(req.http_major);
55 }
56 } // namespace
57
58 namespace {
request_get_http_version_minor(mrb_state * mrb,mrb_value self)59 mrb_value request_get_http_version_minor(mrb_state *mrb, mrb_value self) {
60 auto data = static_cast<MRubyAssocData *>(mrb->ud);
61 auto downstream = data->downstream;
62 const auto &req = downstream->request();
63 return mrb_fixnum_value(req.http_minor);
64 }
65 } // namespace
66
67 namespace {
request_get_method(mrb_state * mrb,mrb_value self)68 mrb_value request_get_method(mrb_state *mrb, mrb_value self) {
69 auto data = static_cast<MRubyAssocData *>(mrb->ud);
70 auto downstream = data->downstream;
71 const auto &req = downstream->request();
72 auto method = http2::to_method_string(req.method);
73
74 return mrb_str_new(mrb, method.data(), method.size());
75 }
76 } // namespace
77
78 namespace {
request_set_method(mrb_state * mrb,mrb_value self)79 mrb_value request_set_method(mrb_state *mrb, mrb_value self) {
80 auto data = static_cast<MRubyAssocData *>(mrb->ud);
81 auto downstream = data->downstream;
82 auto &req = downstream->request();
83
84 check_phase(mrb, data->phase, PHASE_REQUEST);
85
86 const char *method;
87 mrb_int n;
88 mrb_get_args(mrb, "s", &method, &n);
89 if (n == 0) {
90 mrb_raise(mrb, E_RUNTIME_ERROR, "method must not be empty string");
91 }
92 auto token =
93 http2::lookup_method_token(StringRef{method, static_cast<size_t>(n)});
94 if (token == -1) {
95 mrb_raise(mrb, E_RUNTIME_ERROR, "method not supported");
96 }
97
98 req.method = token;
99
100 return self;
101 }
102 } // namespace
103
104 namespace {
request_get_authority(mrb_state * mrb,mrb_value self)105 mrb_value request_get_authority(mrb_state *mrb, mrb_value self) {
106 auto data = static_cast<MRubyAssocData *>(mrb->ud);
107 auto downstream = data->downstream;
108 const auto &req = downstream->request();
109
110 return mrb_str_new(mrb, req.authority.data(), req.authority.size());
111 }
112 } // namespace
113
114 namespace {
request_set_authority(mrb_state * mrb,mrb_value self)115 mrb_value request_set_authority(mrb_state *mrb, mrb_value self) {
116 auto data = static_cast<MRubyAssocData *>(mrb->ud);
117 auto downstream = data->downstream;
118 auto &req = downstream->request();
119
120 auto &balloc = downstream->get_block_allocator();
121
122 check_phase(mrb, data->phase, PHASE_REQUEST);
123
124 const char *authority;
125 mrb_int n;
126 mrb_get_args(mrb, "s", &authority, &n);
127 if (n == 0) {
128 mrb_raise(mrb, E_RUNTIME_ERROR, "authority must not be empty string");
129 }
130
131 req.authority =
132 make_string_ref(balloc, StringRef{authority, static_cast<size_t>(n)});
133
134 return self;
135 }
136 } // namespace
137
138 namespace {
request_get_scheme(mrb_state * mrb,mrb_value self)139 mrb_value request_get_scheme(mrb_state *mrb, mrb_value self) {
140 auto data = static_cast<MRubyAssocData *>(mrb->ud);
141 auto downstream = data->downstream;
142 const auto &req = downstream->request();
143
144 return mrb_str_new(mrb, req.scheme.data(), req.scheme.size());
145 }
146 } // namespace
147
148 namespace {
request_set_scheme(mrb_state * mrb,mrb_value self)149 mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) {
150 auto data = static_cast<MRubyAssocData *>(mrb->ud);
151 auto downstream = data->downstream;
152 auto &req = downstream->request();
153
154 auto &balloc = downstream->get_block_allocator();
155
156 check_phase(mrb, data->phase, PHASE_REQUEST);
157
158 const char *scheme;
159 mrb_int n;
160 mrb_get_args(mrb, "s", &scheme, &n);
161 if (n == 0) {
162 mrb_raise(mrb, E_RUNTIME_ERROR, "scheme must not be empty string");
163 }
164
165 req.scheme =
166 make_string_ref(balloc, StringRef{scheme, static_cast<size_t>(n)});
167
168 return self;
169 }
170 } // namespace
171
172 namespace {
request_get_path(mrb_state * mrb,mrb_value self)173 mrb_value request_get_path(mrb_state *mrb, mrb_value self) {
174 auto data = static_cast<MRubyAssocData *>(mrb->ud);
175 auto downstream = data->downstream;
176 const auto &req = downstream->request();
177
178 return mrb_str_new(mrb, req.path.data(), req.path.size());
179 }
180 } // namespace
181
182 namespace {
request_set_path(mrb_state * mrb,mrb_value self)183 mrb_value request_set_path(mrb_state *mrb, mrb_value self) {
184 auto data = static_cast<MRubyAssocData *>(mrb->ud);
185 auto downstream = data->downstream;
186 auto &req = downstream->request();
187
188 auto &balloc = downstream->get_block_allocator();
189
190 check_phase(mrb, data->phase, PHASE_REQUEST);
191
192 const char *path;
193 mrb_int pathlen;
194 mrb_get_args(mrb, "s", &path, &pathlen);
195
196 req.path =
197 make_string_ref(balloc, StringRef{path, static_cast<size_t>(pathlen)});
198
199 return self;
200 }
201 } // namespace
202
203 namespace {
request_get_headers(mrb_state * mrb,mrb_value self)204 mrb_value request_get_headers(mrb_state *mrb, mrb_value self) {
205 auto data = static_cast<MRubyAssocData *>(mrb->ud);
206 auto downstream = data->downstream;
207 const auto &req = downstream->request();
208 return create_headers_hash(mrb, req.fs.headers());
209 }
210 } // namespace
211
212 namespace {
request_mod_header(mrb_state * mrb,mrb_value self,bool repl)213 mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
214 auto data = static_cast<MRubyAssocData *>(mrb->ud);
215 auto downstream = data->downstream;
216 auto &req = downstream->request();
217 auto &balloc = downstream->get_block_allocator();
218
219 check_phase(mrb, data->phase, PHASE_REQUEST);
220
221 mrb_value key, values;
222 mrb_get_args(mrb, "So", &key, &values);
223
224 if (RSTRING_LEN(key) == 0) {
225 mrb_raise(mrb, E_RUNTIME_ERROR, "empty key is not allowed");
226 }
227
228 auto ai = mrb_gc_arena_save(mrb);
229
230 key = mrb_funcall(mrb, key, "downcase", 0);
231
232 auto keyref = make_string_ref(
233 balloc, StringRef{RSTRING_PTR(key), static_cast<size_t>(RSTRING_LEN(key))});
234
235 mrb_gc_arena_restore(mrb, ai);
236
237 auto token = http2::lookup_token(keyref);
238
239 if (repl) {
240 size_t p = 0;
241 auto &headers = req.fs.headers();
242 for (size_t i = 0; i < headers.size(); ++i) {
243 auto &kv = headers[i];
244 if (kv.name == keyref) {
245 continue;
246 }
247 if (i != p) {
248 headers[p] = std::move(kv);
249 }
250 ++p;
251 }
252 headers.resize(p);
253 }
254
255 if (mrb_array_p(values)) {
256 auto n = RARRAY_LEN(values);
257 for (int i = 0; i < n; ++i) {
258 auto value = mrb_ary_ref(mrb, values, i);
259 if (!mrb_string_p(value)) {
260 mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
261 }
262
263 req.fs.add_header_token(
264 keyref,
265 make_string_ref(balloc,
266 StringRef{RSTRING_PTR(value),
267 static_cast<size_t>(RSTRING_LEN(value))}),
268 false, token);
269 }
270 } else if (mrb_string_p(values)) {
271 req.fs.add_header_token(
272 keyref,
273 make_string_ref(balloc,
274 StringRef{RSTRING_PTR(values),
275 static_cast<size_t>(RSTRING_LEN(values))}),
276 false, token);
277 } else {
278 mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
279 }
280
281 return mrb_nil_value();
282 }
283 } // namespace
284
285 namespace {
request_set_header(mrb_state * mrb,mrb_value self)286 mrb_value request_set_header(mrb_state *mrb, mrb_value self) {
287 return request_mod_header(mrb, self, true);
288 }
289 } // namespace
290
291 namespace {
request_add_header(mrb_state * mrb,mrb_value self)292 mrb_value request_add_header(mrb_state *mrb, mrb_value self) {
293 return request_mod_header(mrb, self, false);
294 }
295 } // namespace
296
297 namespace {
request_clear_headers(mrb_state * mrb,mrb_value self)298 mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) {
299 auto data = static_cast<MRubyAssocData *>(mrb->ud);
300 auto downstream = data->downstream;
301 auto &req = downstream->request();
302
303 check_phase(mrb, data->phase, PHASE_REQUEST);
304
305 req.fs.clear_headers();
306
307 return mrb_nil_value();
308 }
309 } // namespace
310
311 namespace {
request_push(mrb_state * mrb,mrb_value self)312 mrb_value request_push(mrb_state *mrb, mrb_value self) {
313 auto data = static_cast<MRubyAssocData *>(mrb->ud);
314 auto downstream = data->downstream;
315 auto upstream = downstream->get_upstream();
316
317 const char *uri;
318 mrb_int len;
319 mrb_get_args(mrb, "s", &uri, &len);
320
321 upstream->initiate_push(downstream, StringRef{uri, static_cast<size_t>(len)});
322
323 return mrb_nil_value();
324 }
325 } // namespace
326
init_request_class(mrb_state * mrb,RClass * module)327 void init_request_class(mrb_state *mrb, RClass *module) {
328 auto request_class =
329 mrb_define_class_under(mrb, module, "Request", mrb->object_class);
330
331 mrb_define_method(mrb, request_class, "initialize", request_init,
332 MRB_ARGS_NONE());
333 mrb_define_method(mrb, request_class, "http_version_major",
334 request_get_http_version_major, MRB_ARGS_NONE());
335 mrb_define_method(mrb, request_class, "http_version_minor",
336 request_get_http_version_minor, MRB_ARGS_NONE());
337 mrb_define_method(mrb, request_class, "method", request_get_method,
338 MRB_ARGS_NONE());
339 mrb_define_method(mrb, request_class, "method=", request_set_method,
340 MRB_ARGS_REQ(1));
341 mrb_define_method(mrb, request_class, "authority", request_get_authority,
342 MRB_ARGS_NONE());
343 mrb_define_method(mrb, request_class, "authority=", request_set_authority,
344 MRB_ARGS_REQ(1));
345 mrb_define_method(mrb, request_class, "scheme", request_get_scheme,
346 MRB_ARGS_NONE());
347 mrb_define_method(mrb, request_class, "scheme=", request_set_scheme,
348 MRB_ARGS_REQ(1));
349 mrb_define_method(mrb, request_class, "path", request_get_path,
350 MRB_ARGS_NONE());
351 mrb_define_method(mrb, request_class, "path=", request_set_path,
352 MRB_ARGS_REQ(1));
353 mrb_define_method(mrb, request_class, "headers", request_get_headers,
354 MRB_ARGS_NONE());
355 mrb_define_method(mrb, request_class, "add_header", request_add_header,
356 MRB_ARGS_REQ(2));
357 mrb_define_method(mrb, request_class, "set_header", request_set_header,
358 MRB_ARGS_REQ(2));
359 mrb_define_method(mrb, request_class, "clear_headers", request_clear_headers,
360 MRB_ARGS_NONE());
361 mrb_define_method(mrb, request_class, "push", request_push, MRB_ARGS_REQ(1));
362 }
363
364 } // namespace mruby
365
366 } // namespace shrpx
367