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 41namespace shrpx { 42 43namespace mruby { 44 45namespace { 46mrb_value request_init(mrb_state *mrb, mrb_value self) { return self; } 47} // namespace 48 49namespace { 50mrb_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 58namespace { 59mrb_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 67namespace { 68mrb_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 78namespace { 79mrb_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 104namespace { 105mrb_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 114namespace { 115mrb_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 138namespace { 139mrb_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 148namespace { 149mrb_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 172namespace { 173mrb_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 182namespace { 183mrb_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 203namespace { 204mrb_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 212namespace { 213mrb_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 286namespace { 287mrb_value request_set_header(mrb_state *mrb, mrb_value self) { 288 return request_mod_header(mrb, self, true); 289} 290} // namespace 291 292namespace { 293mrb_value request_add_header(mrb_state *mrb, mrb_value self) { 294 return request_mod_header(mrb, self, false); 295} 296} // namespace 297 298namespace { 299mrb_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 312namespace { 313mrb_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 328void 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