• 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.c_str(), 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(reinterpret_cast<const uint8_t *>(method), 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.c_str(), 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.c_str(), 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.c_str(), 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 =
233       make_string_ref(balloc, StringRef{RSTRING_PTR(key),
234                                         static_cast<size_t>(RSTRING_LEN(key))});
235 
236   mrb_gc_arena_restore(mrb, ai);
237 
238   auto token = http2::lookup_token(keyref.byte(), keyref.size());
239 
240   if (repl) {
241     size_t p = 0;
242     auto &headers = req.fs.headers();
243     for (size_t i = 0; i < headers.size(); ++i) {
244       auto &kv = headers[i];
245       if (kv.name == keyref) {
246         continue;
247       }
248       if (i != p) {
249         headers[p] = std::move(kv);
250       }
251       ++p;
252     }
253     headers.resize(p);
254   }
255 
256   if (mrb_array_p(values)) {
257     auto n = RARRAY_LEN(values);
258     for (int i = 0; i < n; ++i) {
259       auto value = mrb_ary_ref(mrb, values, i);
260       if (!mrb_string_p(value)) {
261         mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
262       }
263 
264       req.fs.add_header_token(
265           keyref,
266           make_string_ref(balloc,
267                           StringRef{RSTRING_PTR(value),
268                                     static_cast<size_t>(RSTRING_LEN(value))}),
269           false, token);
270     }
271   } else if (mrb_string_p(values)) {
272     req.fs.add_header_token(
273         keyref,
274         make_string_ref(balloc,
275                         StringRef{RSTRING_PTR(values),
276                                   static_cast<size_t>(RSTRING_LEN(values))}),
277         false, token);
278   } else {
279     mrb_raise(mrb, E_RUNTIME_ERROR, "value must be string");
280   }
281 
282   return mrb_nil_value();
283 }
284 } // namespace
285 
286 namespace {
request_set_header(mrb_state * mrb,mrb_value self)287 mrb_value request_set_header(mrb_state *mrb, mrb_value self) {
288   return request_mod_header(mrb, self, true);
289 }
290 } // namespace
291 
292 namespace {
request_add_header(mrb_state * mrb,mrb_value self)293 mrb_value request_add_header(mrb_state *mrb, mrb_value self) {
294   return request_mod_header(mrb, self, false);
295 }
296 } // namespace
297 
298 namespace {
request_clear_headers(mrb_state * mrb,mrb_value self)299 mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) {
300   auto data = static_cast<MRubyAssocData *>(mrb->ud);
301   auto downstream = data->downstream;
302   auto &req = downstream->request();
303 
304   check_phase(mrb, data->phase, PHASE_REQUEST);
305 
306   req.fs.clear_headers();
307 
308   return mrb_nil_value();
309 }
310 } // namespace
311 
312 namespace {
request_push(mrb_state * mrb,mrb_value self)313 mrb_value request_push(mrb_state *mrb, mrb_value self) {
314   auto data = static_cast<MRubyAssocData *>(mrb->ud);
315   auto downstream = data->downstream;
316   auto upstream = downstream->get_upstream();
317 
318   const char *uri;
319   mrb_int len;
320   mrb_get_args(mrb, "s", &uri, &len);
321 
322   upstream->initiate_push(downstream, StringRef{uri, static_cast<size_t>(len)});
323 
324   return mrb_nil_value();
325 }
326 } // namespace
327 
init_request_class(mrb_state * mrb,RClass * module)328 void init_request_class(mrb_state *mrb, RClass *module) {
329   auto request_class =
330       mrb_define_class_under(mrb, module, "Request", mrb->object_class);
331 
332   mrb_define_method(mrb, request_class, "initialize", request_init,
333                     MRB_ARGS_NONE());
334   mrb_define_method(mrb, request_class, "http_version_major",
335                     request_get_http_version_major, MRB_ARGS_NONE());
336   mrb_define_method(mrb, request_class, "http_version_minor",
337                     request_get_http_version_minor, MRB_ARGS_NONE());
338   mrb_define_method(mrb, request_class, "method", request_get_method,
339                     MRB_ARGS_NONE());
340   mrb_define_method(mrb, request_class, "method=", request_set_method,
341                     MRB_ARGS_REQ(1));
342   mrb_define_method(mrb, request_class, "authority", request_get_authority,
343                     MRB_ARGS_NONE());
344   mrb_define_method(mrb, request_class, "authority=", request_set_authority,
345                     MRB_ARGS_REQ(1));
346   mrb_define_method(mrb, request_class, "scheme", request_get_scheme,
347                     MRB_ARGS_NONE());
348   mrb_define_method(mrb, request_class, "scheme=", request_set_scheme,
349                     MRB_ARGS_REQ(1));
350   mrb_define_method(mrb, request_class, "path", request_get_path,
351                     MRB_ARGS_NONE());
352   mrb_define_method(mrb, request_class, "path=", request_set_path,
353                     MRB_ARGS_REQ(1));
354   mrb_define_method(mrb, request_class, "headers", request_get_headers,
355                     MRB_ARGS_NONE());
356   mrb_define_method(mrb, request_class, "add_header", request_add_header,
357                     MRB_ARGS_REQ(2));
358   mrb_define_method(mrb, request_class, "set_header", request_set_header,
359                     MRB_ARGS_REQ(2));
360   mrb_define_method(mrb, request_class, "clear_headers", request_clear_headers,
361                     MRB_ARGS_NONE());
362   mrb_define_method(mrb, request_class, "push", request_push, MRB_ARGS_REQ(1));
363 }
364 
365 } // namespace mruby
366 
367 } // namespace shrpx
368