• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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