static mrb_value mrb_http_object_initialize(mrb_state *mrb, mrb_value self) { OBJECT_SET(mrb, self, "headers", mrb_hash_new(mrb)); OBJECT_SET(mrb, self, "body", mrb_nil_value()); OBJECT_SET(mrb, self, "method", mrb_str_new_cstr(mrb, "GET")); return self; }
Value ffi_pointer_read(VMState *state, Object *type, void *ptr) { ValueCache *vcache = &state->shared->vcache; Object *string_base = vcache->string_base; FFIObject *ffi = (FFIObject*) vcache->ffi_obj; if (type == ffi->float_obj) { float f = *(float*) ptr; return FLOAT2VAL(f); } else if (type == ffi->int_obj) { int i = *(int*) ptr; return INT2VAL(i); } else if (type == ffi->uint_obj) { unsigned int i = *(unsigned int*) ptr; return INT2VAL(i); } else { bool has_pointer = false; OBJECT_LOOKUP_P(type, pointer, &has_pointer); if (!has_pointer) { Object *c_type_obj = OBJ_OR_NULL(OBJECT_LOOKUP(type, c_type)); StringObject *c_type = (StringObject*) obj_instance_of(c_type_obj, string_base); VM_ASSERT(c_type, "internal type error") VNULL; VM_ASSERT(false, "unhandled pointer read type: %s", c_type->value) VNULL; } Value res = make_object(state, type, false); char *error = OBJECT_SET(state, AS_OBJ(res), pointer, make_ffi_pointer(state, ptr)); VM_ASSERT(!error, error) VNULL; return res; } }
static mrb_value mrb_http_object_body_set(mrb_state *mrb, mrb_value self) { mrb_value arg; mrb_get_args(mrb, "S", &arg); OBJECT_SET(mrb, self, "body", arg); return mrb_nil_value(); }
static int parser_settings_on_message_complete(http_parser* parser) { mrb_http_parser_context *context = (mrb_http_parser_context*) parser->data; mrb_state* mrb = context->mrb; mrb_value c = context->instance; if (context->handle.field_set & (1<<UF_SCHEMA)) { OBJECT_SET(mrb, c, "schema", mrb_str_substr(mrb, OBJECT_GET(mrb, c, "buf"), context->handle.field_data[UF_SCHEMA].off, context->handle.field_data[UF_SCHEMA].len)); } if (context->handle.field_set & (1<<UF_HOST)) { OBJECT_SET(mrb, c, "host", mrb_str_substr(mrb, OBJECT_GET(mrb, c, "buf"), context->handle.field_data[UF_HOST].off, context->handle.field_data[UF_HOST].len)); } if (context->handle.field_set & (1<<UF_HOST)) { OBJECT_SET(mrb, c, "host", mrb_str_substr(mrb, OBJECT_GET(mrb, c, "buf"), context->handle.field_data[UF_HOST].off, context->handle.field_data[UF_HOST].len)); } if (context->handle.field_set & (1<<UF_PORT)) { OBJECT_SET(mrb, c, "port", mrb_fixnum_value(context->handle.port)); } else { if (context->handle.field_set & (1<<UF_SCHEMA)) { mrb_value schema = mrb_str_substr(mrb, OBJECT_GET(mrb, c, "buf"), context->handle.field_data[UF_SCHEMA].off, context->handle.field_data[UF_SCHEMA].len); if (!mrb_nil_p(schema) && !strcmp("https", (char*) RSTRING_PTR(schema))) { OBJECT_SET(mrb, c, "port", mrb_fixnum_value(443)); } } } if (context->handle.field_set & (1<<UF_PATH)) { OBJECT_SET(mrb, c, "path", mrb_str_substr(mrb, OBJECT_GET(mrb, c, "buf"), context->handle.field_data[UF_PATH].off, context->handle.field_data[UF_PATH].len)); } if (context->handle.field_set & (1<<UF_QUERY)) { OBJECT_SET(mrb, c, "query", mrb_str_substr(mrb, OBJECT_GET(mrb, c, "buf"), context->handle.field_data[UF_QUERY].off, context->handle.field_data[UF_QUERY].len)); } if (context->parser.method) OBJECT_SET(mrb, c, "method", mrb_str_new_cstr(mrb, http_method_str(context->parser.method))); if (context->parser.status_code) OBJECT_SET(mrb, c, "status_code", mrb_fixnum_value(context->parser.status_code)); if (context->parser.content_length) OBJECT_SET(mrb, c, "content_length", mrb_fixnum_value(context->parser.content_length)); OBJECT_REMOVE(mrb, c, "last_header_field"); OBJECT_REMOVE(mrb, c, "last_header_value"); OBJECT_REMOVE(mrb, c, "buf"); return 0; }
static int parser_settings_on_header_field(http_parser* parser, const char* at, size_t len) { mrb_http_parser_context *context = (mrb_http_parser_context*) parser->data; mrb_state* mrb = context->mrb; int ai = mrb_gc_arena_save(mrb); if (context->was_header_value) { if (!mrb_nil_p(OBJECT_GET(mrb, context->instance, "last_header_field"))) { mrb_str_concat(mrb, OBJECT_GET(mrb, context->instance, "last_header_field"), OBJECT_GET(mrb, context->instance, "last_header_value")); OBJECT_SET(mrb, context->instance, "last_header_value", mrb_nil_value()); } OBJECT_SET(mrb, context->instance, "last_header_field", mrb_str_new(mrb, at, len)); context->was_header_value = FALSE; } else { mrb_str_concat(mrb, OBJECT_GET(mrb, context->instance, "last_header_field"), mrb_str_new(mrb, at, len)); } mrb_gc_arena_restore(mrb, ai); return 0; }
static int parser_settings_on_body(http_parser *parser, const char *p, size_t len) { mrb_http_parser_context *context = (mrb_http_parser_context*) parser->data; mrb_state* mrb = context->mrb; int ai = mrb_gc_arena_save(mrb); OBJECT_SET(mrb, context->instance, "body", mrb_str_new(mrb, p, len)); mrb_gc_arena_restore(mrb, ai); return 0; }
static mrb_value mrb_http_parser_parse_url(mrb_state *mrb, mrb_value self) { mrb_value c; mrb_value arg_data; struct http_parser_url handle = {0}; struct RClass* _class_http, *_class_http_url; mrb_get_args(mrb, "S", &arg_data); if (http_parser_parse_url(RSTRING_PTR(arg_data), RSTRING_LEN(arg_data), FALSE, &handle)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid URL"); } _class_http = mrb_module_get(mrb, "HTTP"); _class_http_url = mrb_class_ptr(mrb_const_get(mrb, mrb_obj_value(_class_http), mrb_intern_cstr(mrb, "URL"))); c = mrb_obj_new(mrb, _class_http_url, 0, NULL); if (handle.field_set & (1<<UF_SCHEMA)) { OBJECT_SET(mrb, c, "schema", mrb_str_substr(mrb, arg_data, handle.field_data[UF_SCHEMA].off, handle.field_data[UF_SCHEMA].len)); } if (handle.field_set & (1<<UF_HOST)) { OBJECT_SET(mrb, c, "host", mrb_str_substr(mrb, arg_data, handle.field_data[UF_HOST].off, handle.field_data[UF_HOST].len)); } if (handle.field_set & (1<<UF_HOST)) { OBJECT_SET(mrb, c, "host", mrb_str_substr(mrb, arg_data, handle.field_data[UF_HOST].off, handle.field_data[UF_HOST].len)); } if (handle.field_set & (1<<UF_PORT)) { OBJECT_SET(mrb, c, "port", mrb_fixnum_value(handle.port)); } else { if (handle.field_set & (1<<UF_SCHEMA)) { mrb_value schema = mrb_str_substr(mrb, arg_data, handle.field_data[UF_SCHEMA].off, handle.field_data[UF_SCHEMA].len); if (!mrb_nil_p(schema) && !strcmp("https", (char*) RSTRING_PTR(schema))) { OBJECT_SET(mrb, c, "port", mrb_fixnum_value(443)); } } } if (handle.field_set & (1<<UF_PATH)) { OBJECT_SET(mrb, c, "path", mrb_str_substr(mrb, arg_data, handle.field_data[UF_PATH].off, handle.field_data[UF_PATH].len)); } if (handle.field_set & (1<<UF_QUERY)) { OBJECT_SET(mrb, c, "query", mrb_str_substr(mrb, arg_data, handle.field_data[UF_QUERY].off, handle.field_data[UF_QUERY].len)); } if (handle.field_set & (1<<UF_FRAGMENT)) { OBJECT_SET(mrb, c, "fragment", mrb_str_substr(mrb, arg_data, handle.field_data[UF_FRAGMENT].off, handle.field_data[UF_FRAGMENT].len)); } return c; }
static void ffi_open_fn(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 1, "wrong arity: expected 1, got %i", info->args_len); Object *root = state->root; Object *string_base = state->shared->vcache.string_base; Object *array_base = state->shared->vcache.array_base; Object *ffi = AS_OBJ(OBJECT_LOOKUP(root, ffi)); Object *handle_base = AS_OBJ(OBJECT_LOOKUP(ffi, handle)); StringObject *sarg = (StringObject*) obj_instance_of(OBJ_OR_NULL(load_arg(state->frame, INFO_ARGS_PTR(info)[0])), string_base); VM_ASSERT(sarg, "argument to ffi.open must be string!"); Object *libmap = AS_OBJ(OBJECT_LOOKUP(ffi, library_map)); char *file = sarg->value; bool file_found = false; FastKey file_key = prepare_key(file, strlen(file)); Value mapping = object_lookup_p(libmap, &file_key, &file_found); const char **file_list_ptr = NULL; int file_list_len = 0; if (file_found) { ArrayObject *aobj = (ArrayObject*) obj_instance_of(OBJ_OR_NULL(mapping), array_base); StringObject *sobj = (StringObject*) obj_instance_of(OBJ_OR_NULL(mapping), string_base); if (aobj) { file_list_len = aobj->length; file_list_ptr = malloc(sizeof(char*) * file_list_len); for (int i = 0; i < file_list_len; i++) { StringObject *file = (StringObject*) obj_instance_of(OBJ_OR_NULL(aobj->ptr[i]), string_base); VM_ASSERT(file, "library_map sub-entries must be string"); file_list_ptr[i] = my_asprintf("%s", file->value); // outside gc, make copy } } else if (sobj) { file_list_len = 1; file_list_ptr = malloc(sizeof(char*) * 1); file_list_ptr[0] = my_asprintf("%s", sobj->value); } else VM_ASSERT(false, "library_map entries must be string or array"); } else { file_list_len = 1; file_list_ptr = malloc(sizeof(char*) * 1); file_list_ptr[0] = file; } void *dlptr = my_dlopen(file_list_len, file_list_ptr); Object *handle_obj = AS_OBJ(make_object(state, handle_base, false)); handle_obj->flags |= OBJ_FROZEN; OBJECT_SET(state, handle_obj, pointer, make_ptr(state, dlptr)); vm_return(state, info, OBJ2VAL(handle_obj)); }
static mrb_value _http_parser_parse(mrb_state *mrb, mrb_value self, int type) { mrb_value arg_data = mrb_nil_value(); mrb_value value_context; mrb_http_parser_context* context; mrb_value b = mrb_nil_value(); struct RClass* _class_http; struct RClass* clazz; char* data; size_t len; char* eol; size_t done; value_context = mrb_iv_get(mrb, self, mrb_intern(mrb, "context")); Data_Get_Struct(mrb, value_context, &http_parser_context_type, context); if (!context) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument"); } mrb_get_args(mrb, "|&o", &b, &arg_data); if (mrb_nil_p(arg_data)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument"); } context->parser.data = context; _class_http = mrb_class_get(mrb, "HTTP"); if (type == HTTP_REQUEST) { clazz = mrb_class_ptr(mrb_const_get(mrb, mrb_obj_value(_class_http), mrb_intern(mrb, "Request"))); context->instance = mrb_class_new_instance(mrb, 0, NULL, clazz); } else { clazz = mrb_class_ptr(mrb_const_get(mrb, mrb_obj_value(_class_http), mrb_intern(mrb, "Response"))); context->instance = mrb_class_new_instance(mrb, 0, NULL, clazz); } context->was_header_value = TRUE; http_parser_init(&context->parser, type); context->type = type; context->settings.on_url = parser_settings_on_url; context->settings.on_header_field = parser_settings_on_header_field; context->settings.on_header_value = parser_settings_on_header_value; context->settings.on_headers_complete = parser_settings_on_headers_complete; context->settings.on_body = parser_settings_on_body; context->settings.on_message_complete = parser_settings_on_message_complete; data = RSTRING_PTR(arg_data); len = RSTRING_LEN(arg_data); eol = strpbrk(data, "\r\n"); if (eol) { } RETRY: if (len > 10 && (!strncmp(data+9, "200 Connection established\r\n", 28) || !strncmp(data+9, "100 Continue\r\n", 14) || *(data+9) == '3')) { char* next = strstr(data, "\r\n\r\n"); if (next) { len -= (next + 4 - data); data = next + 4; goto RETRY; } } done = http_parser_execute(&context->parser, &context->settings, data, len); if (done < len) { OBJECT_SET(mrb, context->instance, "body", mrb_str_new(mrb, data + done, len - done)); } if (!mrb_nil_p(b)) { mrb_value args[1]; args[0] = context->instance; mrb_yield_argv(mrb, b, 1, args); return mrb_nil_value(); } return context->instance; }