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