From d448d996f54b1e34b5a3b1445ca00ece07bccc5f Mon Sep 17 00:00:00 2001 From: Struma Date: Wed, 30 Dec 2020 01:43:40 -0500 Subject: [PATCH] Add JSON parse/stringify methods to HTTPLite::JSON --- binding/http-binding.cpp | 124 +++++++++++++++++++++++++++++++++++++-- binding/meson.build | 3 +- meson_options.txt | 1 + src/meson.build | 18 ++++-- 4 files changed, 133 insertions(+), 13 deletions(-) diff --git a/binding/http-binding.cpp b/binding/http-binding.cpp index 250ccd3..6ffb4b1 100644 --- a/binding/http-binding.cpp +++ b/binding/http-binding.cpp @@ -10,8 +10,9 @@ #include "binding-util.h" #include "net/net.h" +#include "util/json5pp.hpp" -RB_METHOD(netGet) { +RB_METHOD(httpGet) { RB_UNUSED_PARAM; VALUE path; @@ -42,7 +43,7 @@ RB_METHOD(netGet) { return ret; } -RB_METHOD(netPost) { +RB_METHOD(httpPost) { RB_UNUSED_PARAM; VALUE path, postDataHash; @@ -87,7 +88,7 @@ RB_METHOD(netPost) { return ret; } -RB_METHOD(netPostBody) { +RB_METHOD(httpPostBody) { RB_UNUSED_PARAM; VALUE path, body, ctype; @@ -122,9 +123,120 @@ RB_METHOD(netPostBody) { return ret; } +VALUE json2rb(json5pp::value v) { + if (v.is_null()) + return Qnil; + + if (v.is_number()) + return rb_float_new(v.as_number()); + + if (v.is_string()) + return rb_str_new_cstr(v.as_string().c_str()); + + if (v.is_boolean()) + return rb_bool_new(v.as_boolean()); + + if (v.is_integer()) + return LL2NUM(v.as_integer()); + + if (v.is_array()) { + auto &a = v.as_array(); + VALUE ret = rb_ary_new(); + for (auto item : a) { + rb_ary_push(ret, json2rb(item)); + } + return ret; + } + + if (v.is_object()) { + auto &o = v.as_object(); + VALUE ret = rb_hash_new(); + for (auto pair : o) { + rb_hash_aset(ret, rb_str_new_cstr(pair.first.c_str()), json2rb(pair.second)); + } + return ret; + } +} + +json5pp::value rb2json(VALUE v) { + if (v == Qnil) + return json5pp::value(nullptr); + + if (RB_TYPE_P(v, RUBY_T_FLOAT)) + return json5pp::value(RFLOAT_VALUE(v)); + + if (RB_TYPE_P(v, RUBY_T_STRING)) + return json5pp::value(RSTRING_PTR(v)); + + if (v == Qtrue || v == Qfalse) + return json5pp::value(RTEST(v)); + + if (RB_TYPE_P(v, RUBY_T_FIXNUM)) + return json5pp::value(NUM2DBL(v)); + + if (RB_TYPE_P(v, RUBY_T_ARRAY)) { + json5pp::value ret_value = json5pp::array({}); + auto &ret = ret_value.as_array(); + for (int i = 0; i < RARRAY_LEN(v); i++) { + ret.push_back(rb2json(rb_ary_entry(v, i))); + } + return ret_value; + } + + if (RTEST(rb_funcall(v, rb_intern("is_a?"), 1, rb_cHash))) { + json5pp::value ret_value = json5pp::object({}); + auto &ret = ret_value.as_object(); + + + VALUE keys = rb_funcall(v, rb_intern("keys"), 0); + + for (int i = 0; i < RARRAY_LEN(keys); i++) { + VALUE key = rb_ary_entry(keys, i); SafeStringValue(key); + VALUE val = rb_hash_aref(v, key); + ret.emplace(RSTRING_PTR(key), rb2json(val)); + } + + return ret_value; + } + + raiseRbExc(Exception(Exception::MKXPError, "Invalid value for JSON: %s", RSTRING_PTR(rb_inspect(v)))); +} + +RB_METHOD(httpJsonParse) { + RB_UNUSED_PARAM; + + VALUE jsonv; + rb_scan_args(argc, argv, "1", &jsonv); + SafeStringValue(jsonv); + + json5pp::value v; + try { + v = json5pp::parse5(RSTRING_PTR(jsonv)); + } + catch (...) { + raiseRbExc(Exception(Exception::MKXPError, "Failed to parse JSON")); + } + + return json2rb(v); +} + +RB_METHOD(httpJsonStringify) { + RB_UNUSED_PARAM; + + VALUE obj; + rb_scan_args(argc, argv, "1", &obj); + + json5pp::value v = rb2json(obj); + return rb_str_new_cstr(v.stringify().c_str()); +} + void httpBindingInit() { VALUE mNet = rb_define_module("HTTPLite"); - _rb_define_module_function(mNet, "get", netGet); - _rb_define_module_function(mNet, "post", netPost); - _rb_define_module_function(mNet, "post_body", netPostBody); + _rb_define_module_function(mNet, "get", httpGet); + _rb_define_module_function(mNet, "post", httpPost); + _rb_define_module_function(mNet, "post_body", httpPostBody); + + VALUE mNetJSON = rb_define_module_under(mNet, "JSON"); + _rb_define_module_function(mNetJSON, "stringify", httpJsonStringify); + _rb_define_module_function(mNetJSON, "parse", httpJsonParse); } diff --git a/binding/meson.build b/binding/meson.build index c3b4af4..763d3a6 100644 --- a/binding/meson.build +++ b/binding/meson.build @@ -36,7 +36,8 @@ binding_source = [files( 'module_rpg.cpp', 'filesystem-binding.cpp', 'windowvx-binding.cpp', - 'tilemapvx-binding.cpp' + 'tilemapvx-binding.cpp', + 'http-binding.cpp' )] if steamworks == true diff --git a/meson_options.txt b/meson_options.txt index cc50141..994616a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -12,6 +12,7 @@ option('shared_fluid', type: 'boolean', value: false, description: 'Dynamically option('cjk_fallback_font', type: 'boolean', value: false, description: 'Use WenQuanYi Micro Hei as the fallback font') option('use_miniffi', type: 'boolean', value: true, description: 'Enable MiniFFI Ruby module (Win32API)') option('easypoke', type: 'boolean', value: false, description: '"Fix" any incompatibilities with Pkmn Essentials.') +option('enable-https', type: 'boolean', value: true, description: 'Support HTTPS for get/post requests. Requires OpenSSL.') option('default_framerate', type: 'boolean', value: false, description: 'Disable syncToRefreshrate and fixedFramerate configuration options') option('no_preload_scripts', type: 'boolean', value: false, description: 'Disable the preloadScript configuration option') option('workdir_current', type: 'boolean', value: false, description: 'Keep current directory on startup') diff --git a/src/meson.build b/src/meson.build index cbe7511..54c1155 100644 --- a/src/meson.build +++ b/src/meson.build @@ -28,12 +28,14 @@ else endif # If OpenSSL is present, you get HTTPS support -openssl = dependency('openssl', required: false, static: build_static) -if openssl.found() == true - global_dependencies += openssl - global_args += '-DMKXPZ_SSL' -else - warning('Could not locate OpenSSL. HTTPS will be disabled.') +if get_option('enable-https') == true + openssl = dependency('openssl', required: false, static: build_static) + if openssl.found() == true + global_dependencies += openssl + global_args += '-DMKXPZ_SSL' + else + warning('Could not locate OpenSSL. HTTPS will be disabled.') + endif endif # Windows needs to be treated like a special needs child here @@ -72,6 +74,7 @@ global_include_dirs += include_directories('.', 'etc', 'filesystem', 'filesystem/ghc', 'input', + 'net', 'system', 'util' ) @@ -154,6 +157,9 @@ main_source = files( 'input/input.cpp', 'input/keybindings.cpp', + 'net/LUrlParser.cpp', + 'net/net.cpp', + 'system/fake-api.cpp', 'system/systemImpl.cpp' )