From 8eeb9590b488c32f428d7a202ed63edc5217c353 Mon Sep 17 00:00:00 2001 From: elipeter Date: Wed, 27 May 2026 12:30:24 -0500 Subject: [PATCH] **refactor(dynamic): introduce framework-specific fallback logic for Quartz, Spring, Celery, Django, Express, and Socket.IO, enhance middleware/request handling and extend test coverage** --- src/dynamic/lang/java.rs | 117 ++++++++++++++++++- src/dynamic/lang/js_shared.rs | 165 ++++++++++++++++++++++++++ src/dynamic/lang/php.rs | 22 +++- src/dynamic/lang/python.rs | 211 ++++++++++++++++++++++++++++------ src/dynamic/lang/ruby.rs | 66 +++++++++-- tests/phase21_corpus.rs | 27 +++++ 6 files changed, 554 insertions(+), 54 deletions(-) diff --git a/src/dynamic/lang/java.rs b/src/dynamic/lang/java.rs index f7cd2ac6..025ff145 100644 --- a/src/dynamic/lang/java.rs +++ b/src/dynamic/lang/java.rs @@ -4457,6 +4457,9 @@ public class NyxHarness {{ System.out.println("__NYX_SINK_HIT__"); try {{ Class cls = Class.forName({entry_class:?}); + if (nyxTryQuartz(cls, payload)) {{ + return; + }} Constructor ctor = cls.getDeclaredConstructor(); ctor.setAccessible(true); Object instance = ctor.newInstance(); @@ -4469,6 +4472,9 @@ public class NyxHarness {{ System.exit(78); }} m.setAccessible(true); + if (nyxTrySpringHandlerInterceptor(instance, m, payload)) {{ + return; + }} Class[] params = m.getParameterTypes(); Object[] mArgs = new Object[params.length]; for (int i = 0; i < params.length; i++) {{ @@ -4493,6 +4499,50 @@ public class NyxHarness {{ }} return ""; }} + + static boolean nyxTryQuartz(Class cls, String payload) {{ + try {{ + Class jobClass = Class.forName("org.quartz.Job"); + if (!jobClass.isAssignableFrom(cls)) {{ + return false; + }} + System.setProperty("org.quartz.scheduler.skipUpdateCheck", "true"); + System.setProperty("org.quartz.threadPool.threadCount", "1"); + + Class jobBuilderClass = Class.forName("org.quartz.JobBuilder"); + Object jobBuilder = jobBuilderClass.getMethod("newJob", Class.class) + .invoke(null, cls.asSubclass(jobClass)); + jobBuilder = jobBuilder.getClass().getMethod("withIdentity", String.class) + .invoke(jobBuilder, "nyx-job"); + jobBuilder = jobBuilder.getClass().getMethod("usingJobData", String.class, String.class) + .invoke(jobBuilder, "payload", payload); + Object jobDetail = jobBuilder.getClass().getMethod("build").invoke(jobBuilder); + + Class triggerBuilderClass = Class.forName("org.quartz.TriggerBuilder"); + Object triggerBuilder = triggerBuilderClass.getMethod("newTrigger").invoke(null); + triggerBuilder = triggerBuilder.getClass().getMethod("withIdentity", String.class) + .invoke(triggerBuilder, "nyx-trigger"); + triggerBuilder = triggerBuilder.getClass().getMethod("startNow").invoke(triggerBuilder); + Object trigger = triggerBuilder.getClass().getMethod("build").invoke(triggerBuilder); + + Object scheduler = Class.forName("org.quartz.impl.StdSchedulerFactory") + .getMethod("getDefaultScheduler") + .invoke(null); + Class schedulerClass = Class.forName("org.quartz.Scheduler"); + Class jobDetailClass = Class.forName("org.quartz.JobDetail"); + Class triggerClass = Class.forName("org.quartz.Trigger"); + schedulerClass.getMethod("start").invoke(scheduler); + schedulerClass.getMethod("scheduleJob", jobDetailClass, triggerClass) + .invoke(scheduler, jobDetail, trigger); + schedulerClass.getMethod("shutdown", boolean.class).invoke(scheduler, true); + return true; + }} catch (ClassNotFoundException missingQuartz) {{ + return false; + }} catch (Throwable e) {{ + System.err.println("NYX_QUARTZ_FALLBACK: " + e.getClass().getName() + ": " + e.getMessage()); + return false; + }} + }} }} "#, entry_class = entry_class, @@ -4506,7 +4556,7 @@ public class NyxHarness {{ command: vec![ "java".to_owned(), "-cp".to_owned(), - ".".to_owned(), + ".:lib/*".to_owned(), "NyxHarness".to_owned(), ], extra_files: framework_dependency_files(spec), @@ -4569,6 +4619,69 @@ public class NyxHarness {{ }} return ""; }} + + static boolean nyxTrySpringHandlerInterceptor(Object instance, Method m, String payload) {{ + Class[] params = m.getParameterTypes(); + if (params.length < 3 || !m.getName().equals("preHandle")) {{ + return false; + }} + try {{ + Object[] args = new Object[params.length]; + for (int i = 0; i < params.length; i++) {{ + String name = params[i].getName(); + if (name.endsWith("HttpServletRequest")) {{ + args[i] = nyxServletProxy(params[i], payload); + }} else if (name.endsWith("HttpServletResponse")) {{ + args[i] = nyxServletProxy(params[i], payload); + }} else if (params[i].equals(String.class)) {{ + args[i] = payload; + }} else {{ + args[i] = new Object(); + }} + }} + m.invoke(instance, args); + return true; + }} catch (InvocationTargetException ite) {{ + Throwable cause = ite.getCause() == null ? ite : ite.getCause(); + System.err.println("NYX_SPRING_INTERCEPTOR_FALLBACK: " + cause.getClass().getName() + ": " + cause.getMessage()); + return false; + }} catch (Throwable e) {{ + System.err.println("NYX_SPRING_INTERCEPTOR_FALLBACK: " + e.getClass().getName() + ": " + e.getMessage()); + return false; + }} + }} + + static Object nyxServletProxy(Class iface, String payload) {{ + if (!iface.isInterface()) {{ + return null; + }} + return java.lang.reflect.Proxy.newProxyInstance( + iface.getClassLoader(), + new Class[] {{ iface }}, + (proxy, method, args) -> {{ + String name = method.getName(); + if (name.equals("getParameter")) return payload; + if (name.equals("getQueryString")) return "q=" + java.net.URLEncoder.encode(payload, java.nio.charset.StandardCharsets.UTF_8); + if (name.equals("getRequestURI")) return "/nyx"; + if (name.equals("getRequestURL")) return new StringBuffer("http://localhost/nyx"); + if (name.equals("getMethod")) return "POST"; + if (name.equals("getHeader")) return null; + if (name.equals("getWriter")) return new java.io.PrintWriter(System.out, true); + if (name.equals("toString")) return "NyxServletProxy(" + iface.getName() + ")"; + Class ret = method.getReturnType(); + if (!ret.isPrimitive()) return null; + if (ret.equals(boolean.class)) return false; + if (ret.equals(byte.class)) return (byte) 0; + if (ret.equals(short.class)) return (short) 0; + if (ret.equals(int.class)) return 0; + if (ret.equals(long.class)) return 0L; + if (ret.equals(float.class)) return 0.0f; + if (ret.equals(double.class)) return 0.0d; + if (ret.equals(char.class)) return '\0'; + return null; + }} + ); + }} }} "#, entry_class = entry_class, @@ -4582,7 +4695,7 @@ public class NyxHarness {{ command: vec![ "java".to_owned(), "-cp".to_owned(), - ".".to_owned(), + ".:lib/*".to_owned(), "NyxHarness".to_owned(), ], extra_files: framework_dependency_files(spec), diff --git a/src/dynamic/lang/js_shared.rs b/src/dynamic/lang/js_shared.rs index 0fbec238..79c4b66d 100644 --- a/src/dynamic/lang/js_shared.rs +++ b/src/dynamic/lang/js_shared.rs @@ -1095,8 +1095,39 @@ if (_h == null) {{ process.stderr.write('NYX_HANDLER_NOT_FOUND: ' + {handler:?} + '\n'); process.exit(78); }} +async function _nyxTryNodeCron(schedule, handler) {{ + let cron; + try {{ + cron = require('node-cron'); + }} catch (_) {{ + return false; + }} + try {{ + if (typeof cron.validate === 'function' && !cron.validate(schedule)) return false; + let ran = false; + let task = cron.schedule(schedule, async function () {{ + ran = true; + const value = await Promise.resolve(handler(payload)); + if (value != null) process.stdout.write(String(value) + '\n'); + return value; + }}, {{ scheduled: false }}); + if (task && typeof task.execute === 'function') {{ + await Promise.resolve(task.execute()); + if (task && typeof task.stop === 'function') task.stop(); + if (task && typeof task.destroy === 'function') task.destroy(); + return ran; + }} + if (task && typeof task.stop === 'function') task.stop(); + if (task && typeof task.destroy === 'function') task.destroy(); + return false; + }} catch (e) {{ + process.stderr.write('NYX_NODE_CRON_FALLBACK: ' + (e && e.message ? e.message : String(e)) + '\n'); + return false; + }} +}} (async () => {{ try {{ + if (await _nyxTryNodeCron({schedule:?}, _h)) return; const _result = await Promise.resolve(_h(payload)); if (_result != null) process.stdout.write(String(_result) + '\n'); }} catch (e) {{ @@ -1134,8 +1165,48 @@ if (_h == null) {{ process.stderr.write('NYX_RESOLVER_NOT_FOUND: ' + {handler:?} + '\n'); process.exit(78); }} +async function _nyxTryGraphqlJs(typeName, fieldName, resolver) {{ + let graphql; + let buildSchema; + try {{ + const gql = require('graphql'); + graphql = gql.graphql; + buildSchema = gql.buildSchema; + }} catch (_) {{ + return false; + }} + const safeField = /^[A-Za-z_][A-Za-z0-9_]*$/.test(fieldName) ? fieldName : 'nyxField'; + try {{ + const schema = buildSchema('type Query {{ ' + safeField + '(id: String, input: String): String }}'); + const rootValue = {{}}; + rootValue[safeField] = async function (args, context, info) {{ + const value = await Promise.resolve(resolver( + null, + {{ id: payload, input: payload, value: payload }}, + context || {{}}, + info || {{ fieldName: safeField, parentType: typeName }} + )); + return value == null ? null : String(value); + }}; + const result = await graphql({{ + schema, + source: 'query($value: String) {{ ' + safeField + '(id: $value) }}', + rootValue, + variableValues: {{ value: payload }}, + }}); + if (result.errors && result.errors.length) return false; + if (result.data && result.data[safeField] != null) {{ + process.stdout.write(String(result.data[safeField]) + '\n'); + }} + return true; + }} catch (e) {{ + process.stderr.write('NYX_GRAPHQL_JS_FALLBACK: ' + (e && e.message ? e.message : String(e)) + '\n'); + return false; + }} +}} (async () => {{ try {{ + if (await _nyxTryGraphqlJs({type_name:?}, {field:?}, _h)) return; // Apollo resolver shape: (parent, args, context, info). const _info = {{ fieldName: {field:?}, parentType: {type_name:?} }}; const _result = await Promise.resolve(_h(null, {{ id: payload, input: payload }}, {{}}, _info)); @@ -1171,8 +1242,48 @@ if (_h == null) {{ process.stderr.write('NYX_HANDLER_NOT_FOUND: ' + {handler:?} + '\n'); process.exit(78); }} +async function _nyxTryWs(handler) {{ + let Ws; + try {{ + Ws = require('ws'); + }} catch (_) {{ + return false; + }} + try {{ + const events = require('events'); + const Server = Ws.WebSocketServer || Ws.Server; + if (!Server) return false; + const wss = new Server({{ noServer: true }}); + const pending = []; + let delivered = false; + wss.on('connection', (socket) => {{ + socket.on('message', (data, isBinary) => {{ + delivered = true; + pending.push(Promise.resolve(handler(data, isBinary)).then((result) => {{ + if (result != null) process.stdout.write(String(result) + '\n'); + }})); + }}); + }}); + const socket = new events.EventEmitter(); + socket.readyState = Ws.OPEN || 1; + socket.send = (data) => {{ + if (data != null) process.stdout.write(String(data) + '\n'); + }}; + socket.close = () => {{}}; + wss.emit('connection', socket, {{ url: {path:?}, headers: {{}} }}); + socket.emit('message', Buffer.from(String(payload), 'utf8'), false); + await Promise.all(pending); + if (typeof wss.close === 'function') wss.close(); + return delivered; + }} catch (_) {{ + return false; + }} +}} (async () => {{ try {{ + if (await _nyxTryWs(_h)) {{ + return; + }} // ws library: handler(message); socket.io: handler(socket, data). let _result; try {{ @@ -1217,8 +1328,62 @@ if (_h == null) {{ }} const _req = {{ body: payload, query: {{ q: payload }}, params: {{ id: payload }}, headers: {{}}, method: 'POST', url: '/nyx' }}; const _res = {{ statusCode: 200, headers: {{}}, end: function(d){{ if (d != null) process.stdout.write(String(d) + '\n'); }}, setHeader: function(k, v){{ this.headers[k] = v; }} }}; +async function _nyxTryExpressMiddleware(handler) {{ + let express; + try {{ + express = require('express'); + }} catch (_) {{ + return false; + }} + try {{ + const stream = require('stream'); + const app = express(); + app.use(express.text({{ type: '*/*' }})); + app.use(handler); + const req = new stream.Readable({{ + read() {{ + this.push(Buffer.from(String(payload), 'utf8')); + this.push(null); + }}, + }}); + req.method = 'POST'; + req.url = '/nyx?q=' + encodeURIComponent(String(payload)); + req.headers = {{ + host: 'localhost', + 'content-type': 'text/plain', + 'content-length': Buffer.byteLength(String(payload), 'utf8'), + }}; + const chunks = []; + await new Promise((resolve) => {{ + const res = new stream.Writable({{ + write(chunk, _enc, cb) {{ + chunks.push(Buffer.from(chunk)); + cb(); + }}, + }}); + res.statusCode = 200; + res.headers = {{}}; + res.setHeader = function (k, v) {{ this.headers[String(k).toLowerCase()] = v; }}; + res.getHeader = function (k) {{ return this.headers[String(k).toLowerCase()]; }}; + res.end = function (chunk) {{ + if (chunk != null) chunks.push(Buffer.from(String(chunk))); + resolve(); + }}; + app.handle(req, res, function (err) {{ + if (err) process.stderr.write('NYX_EXPRESS_NEXT_ERR: ' + err + '\n'); + resolve(); + }}); + }}); + if (chunks.length > 0) process.stdout.write(Buffer.concat(chunks).toString('utf8') + '\n'); + return true; + }} catch (e) {{ + process.stderr.write('NYX_EXPRESS_MIDDLEWARE_FALLBACK: ' + (e && e.message ? e.message : String(e)) + '\n'); + return false; + }} +}} (async () => {{ try {{ + if (await _nyxTryExpressMiddleware(_h)) return; const _result = await Promise.resolve(_h(_req, _res, function(_e){{ if (_e) process.stderr.write('NYX_NEXT_ERR: ' + _e + '\n'); }})); if (_result != null) process.stdout.write(String(_result) + '\n'); }} catch (e) {{ diff --git a/src/dynamic/lang/php.rs b/src/dynamic/lang/php.rs index 7297d4eb..ecaa8ca3 100644 --- a/src/dynamic/lang/php.rs +++ b/src/dynamic/lang/php.rs @@ -3116,11 +3116,23 @@ fn emit_middleware_harness(spec: &HarnessSpec, name: &str) -> HarnessSource { r#"{preamble} echo "__NYX_MIDDLEWARE__: " . {name:?} . "\n"; -$req = new stdClass(); -$req->body = $payload; -$req->path = '/nyx'; -$req->method = 'POST'; -$req->query = [ 'q' => $payload ]; +function __nyx_make_middleware_request(string $payload) {{ + if (class_exists('Illuminate\\Http\\Request')) {{ + try {{ + return Illuminate\Http\Request::create('/nyx', 'POST', ['q' => $payload], [], [], [], $payload); + }} catch (Throwable $e) {{ + fwrite(STDERR, 'NYX_LARAVEL_REQUEST_FALLBACK: ' . get_class($e) . ': ' . $e->getMessage() . "\n"); + }} + }} + $req = new stdClass(); + $req->body = $payload; + $req->path = '/nyx'; + $req->method = 'POST'; + $req->query = [ 'q' => $payload ]; + return $req; +}} + +$req = __nyx_make_middleware_request($payload); $next = function ($r) {{ return $r; }}; if (class_exists({handler:?})) {{ diff --git a/src/dynamic/lang/python.rs b/src/dynamic/lang/python.rs index 33b2ab14..acd578eb 100644 --- a/src/dynamic/lang/python.rs +++ b/src/dynamic/lang/python.rs @@ -1299,13 +1299,40 @@ _h = getattr(_entry_mod, {handler:?}, None) if _h is None: print("NYX_HANDLER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True) sys.exit(78) +def _nyx_try_celery_eager(task, body): + try: + import celery # noqa: F401 + except Exception: + return False + if not hasattr(task, "apply"): + return False + try: + app = getattr(task, "app", None) + if app is not None and hasattr(app, "conf"): + try: + app.conf.task_always_eager = True + app.conf.task_eager_propagates = False + except Exception: + pass + _result = task.apply(args=(body,), throw=False) + _value = getattr(_result, "result", None) + if _value is not None: + print(str(_value), flush=True) + return True + except SystemExit: + raise + except Exception as _e: + print(f"NYX_CELERY_EAGER_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True) + return False + try: - _result = _h(payload) - if _result is not None: - try: - print(str(_result), flush=True) - except Exception: - pass + if not _nyx_try_celery_eager(_h, payload): + _result = _h(payload) + if _result is not None: + try: + print(str(_result), flush=True) + except Exception: + pass except SystemExit as _e: sys.exit(_e.code) except Exception as _e: @@ -1345,16 +1372,61 @@ if _resolver is None: print("NYX_RESOLVER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True) sys.exit(78) try: - # Graphene resolvers are `resolve_field(self, info, **args)`; we - # synthesise `self = None`, `info = _NyxGraphQLInfo`, and pass the - # payload positionally so a `def resolve_foo(self, info, id):` shape - # binds `id = payload`. - _result = _resolver(None, _NyxGraphQLInfo({field:?}), payload) - if _result is not None: + def _nyx_try_graphene(resolver, type_name, field_name, body): try: - print(str(_result), flush=True) + import graphene except Exception: - pass + return False + safe_field = "".join(ch if (ch.isalnum() or ch == "_") else "_" for ch in str(field_name)) + if not safe_field or safe_field[0].isdigit(): + safe_field = "nyx_" + safe_field + resolver_name = "resolve_" + safe_field + try: + def _nyx_resolve(root, info, id=None, input=None, **kwargs): + arg = id if id is not None else (input if input is not None else body) + try: + return resolver(root, info, arg) + except TypeError: + try: + return resolver(info, arg) + except TypeError: + return resolver(root, info, **{{safe_field: arg}}) + + Query = type( + "NyxDynamicQuery", + (graphene.ObjectType,), + {{ + safe_field: graphene.String(id=graphene.String(), input=graphene.String()), + resolver_name: _nyx_resolve, + }}, + ) + schema = graphene.Schema(query=Query) + query = "query($value: String) {{ " + safe_field + "(id: $value) }}" + result = schema.execute(query, variable_values={{"value": body}}) + if getattr(result, "errors", None): + return False + data = getattr(result, "data", None) or {{}} + value = data.get(safe_field) + if value is not None: + print(str(value), flush=True) + return True + except SystemExit: + raise + except Exception as _e: + print(f"NYX_GRAPHENE_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True) + return False + + if not _nyx_try_graphene(_resolver, {type_name:?}, {field:?}, payload): + # Graphene resolvers are `resolve_field(self, info, **args)`; we + # synthesise `self = None`, `info = _NyxGraphQLInfo`, and pass the + # payload positionally so a `def resolve_foo(self, info, id):` shape + # binds `id = payload`. + _result = _resolver(None, _NyxGraphQLInfo({field:?}), payload) + if _result is not None: + try: + print(str(_result), flush=True) + except Exception: + pass except SystemExit as _e: sys.exit(_e.code) except TypeError: @@ -1396,21 +1468,40 @@ if _h is None: print("NYX_HANDLER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True) sys.exit(78) try: - # python-socketio handlers are `def message(sid, data)`; Channels - # consumers are `def receive(self, text_data=None, bytes_data=None)`. - # Try (sid, payload) first, then fall back to (payload). - try: - _result = _h("nyx-sid", payload) - except TypeError: + def _nyx_try_socketio(handler_name, handler, body): try: - _result = _h(payload) - except TypeError: - _result = _h(None, payload) - if _result is not None: - try: - print(str(_result), flush=True) + import socketio except Exception: - pass + return False + try: + server = socketio.Server(async_mode="threading") + server.on(handler_name, handler=handler, namespace="/") + result = server._trigger_event(handler_name, "/", "nyx-sid", body) + if result is not None: + print(str(result), flush=True) + return True + except SystemExit: + raise + except Exception as _e: + print(f"NYX_SOCKETIO_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True) + return False + + if not _nyx_try_socketio({handler:?}, _h, payload): + # python-socketio handlers are `def message(sid, data)`; Channels + # consumers are `def receive(self, text_data=None, bytes_data=None)`. + # Try (sid, payload) first, then fall back to (payload). + try: + _result = _h("nyx-sid", payload) + except TypeError: + try: + _result = _h(payload) + except TypeError: + _result = _h(None, payload) + if _result is not None: + try: + print(str(_result), flush=True) + except Exception: + pass except SystemExit as _e: sys.exit(_e.code) except Exception as _e: @@ -1454,19 +1545,63 @@ if _h is None: print("NYX_HANDLER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True) sys.exit(78) try: - _req = _NyxRequest(payload) - # Try class-shaped middleware (instantiate with a get_response stub). - try: - _mw = _h(lambda r: r) - _result = _mw(_req) - except TypeError: - # Method on an existing class instance. - _result = _h(_req) - if _result is not None: + def _nyx_try_django_middleware(factory, body): try: - print(str(_result), flush=True) + from django.conf import settings + if not settings.configured: + settings.configure( + DEFAULT_CHARSET="utf-8", + SECRET_KEY="nyx-dynamic-harness", + ROOT_URLCONF=__name__, + ALLOWED_HOSTS=["testserver", "localhost", "127.0.0.1"], + INSTALLED_APPS=[], + MIDDLEWARE=[], + ) + import django + django.setup() + from django.test import RequestFactory except Exception: - pass + return False + try: + request = RequestFactory().post("/nyx", data={{"q": body}}) + request._body = str(body).encode("utf-8", "replace") + try: + mw = factory(lambda req: req) + result = mw(request) + except TypeError: + try: + instance = factory() + if hasattr(instance, "process_request"): + result = instance.process_request(request) + elif callable(instance): + result = instance(request) + else: + return False + except TypeError: + result = factory(request) + if result is not None: + print(str(result), flush=True) + return True + except SystemExit: + raise + except Exception as _e: + print(f"NYX_DJANGO_MIDDLEWARE_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True) + return False + + if not _nyx_try_django_middleware(_h, payload): + _req = _NyxRequest(payload) + # Try class-shaped middleware (instantiate with a get_response stub). + try: + _mw = _h(lambda r: r) + _result = _mw(_req) + except TypeError: + # Method on an existing class instance. + _result = _h(_req) + if _result is not None: + try: + print(str(_result), flush=True) + except Exception: + pass except SystemExit as _e: sys.exit(_e.code) except Exception as _e: diff --git a/src/dynamic/lang/ruby.rs b/src/dynamic/lang/ruby.rs index c91443fc..f0a558d1 100644 --- a/src/dynamic/lang/ruby.rs +++ b/src/dynamic/lang/ruby.rs @@ -760,7 +760,19 @@ puts "__NYX_SCHEDULED_JOB__: " + {sched:?} target = nil if Object.const_defined?({handler:?}) begin - target = Object.const_get({handler:?}).new + cls = Object.const_get({handler:?}) + begin + require 'sidekiq/testing' + if cls.respond_to?(:perform_async) + Sidekiq::Testing.inline! do + cls.perform_async($nyx_payload) + end + exit 0 + end + rescue LoadError, StandardError => e + STDERR.puts("NYX_SIDEKIQ_INLINE_FALLBACK: #{{e.class.name}}: #{{e.message}}") if ENV['NYX_DEBUG'] + end + target = cls.new if target.respond_to?(:perform) begin result = target.perform($nyx_payload) @@ -859,17 +871,53 @@ puts "__NYX_MIDDLEWARE__: " + {name:?} require 'stringio' -# Rack-shape middleware: class with #call(env). -env = {{ - 'REQUEST_METHOD' => 'POST', - 'PATH_INFO' => '/nyx', - 'QUERY_STRING' => "q=#{{$nyx_payload}}", - 'rack.input' => StringIO.new($nyx_payload), - 'nyx.payload' => $nyx_payload, -}} +def nyx_middleware_env + {{ + 'REQUEST_METHOD' => 'POST', + 'PATH_INFO' => '/nyx', + 'QUERY_STRING' => "q=#{{$nyx_payload}}", + 'rack.input' => StringIO.new($nyx_payload), + 'nyx.payload' => $nyx_payload, + }} +end +def nyx_try_rack_middleware(cls) + begin + require 'rack/mock' + rescue LoadError + return false + end + begin + terminal = lambda {{ |_env| [200, {{ 'content-type' => 'text/plain' }}, ['ok']] }} + middleware = begin + cls.new(terminal) + rescue ArgumentError + cls.new + end + return false unless middleware.respond_to?(:call) + app = lambda do |env| + env['nyx.payload'] = $nyx_payload + middleware.call(env) + end + response = Rack::MockRequest.new(app).request( + 'POST', + '/nyx?q=' + Rack::Utils.escape($nyx_payload.to_s), + input: $nyx_payload + ) + print(response.body.to_s) if response && response.respond_to?(:body) && !response.body.to_s.empty? + true + rescue StandardError => e + STDERR.puts("NYX_EXCEPTION: #{{e.class.name}}: #{{e.message}}") + false + end +end + +env = nyx_middleware_env + +# Rack-shape middleware: class with #call(env). if Object.const_defined?({handler:?}) cls = Object.const_get({handler:?}) + exit 0 if nyx_try_rack_middleware(cls) begin inst = cls.new(lambda {{ |e| [200, {{}}, ['ok']] }}) if inst.respond_to?(:call) diff --git a/tests/phase21_corpus.rs b/tests/phase21_corpus.rs index 365b9914..b8ab6e4f 100644 --- a/tests/phase21_corpus.rs +++ b/tests/phase21_corpus.rs @@ -667,6 +667,8 @@ fn scheduled_job_python_harness_carries_sentinel_and_handler() { assert!(h.source.contains("__NYX_SCHEDULED_JOB__")); assert!(h.source.contains("\"tick\"")); assert!(h.source.contains("*/5 * * * *")); + assert!(h.source.contains("_nyx_try_celery_eager")); + assert!(h.source.contains("task.apply")); } #[test] @@ -682,6 +684,8 @@ fn scheduled_job_js_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_SCHEDULED_JOB__")); assert!(h.source.contains("\"tick\"")); + assert!(h.source.contains("_nyxTryNodeCron")); + assert!(h.source.contains("require('node-cron')")); } #[test] @@ -695,6 +699,9 @@ fn scheduled_job_java_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_SCHEDULED_JOB__")); assert!(h.source.contains("\"execute\"")); + assert!(h.source.contains("nyxTryQuartz")); + assert!(h.source.contains("org.quartz.JobBuilder")); + assert_eq!(h.command, vec!["java", "-cp", ".:lib/*", "NyxHarness"]); } #[test] @@ -708,6 +715,8 @@ fn scheduled_job_ruby_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_SCHEDULED_JOB__")); assert!(h.source.contains("TickWorker")); + assert!(h.source.contains("sidekiq/testing")); + assert!(h.source.contains("perform_async")); } #[test] @@ -725,6 +734,8 @@ fn graphql_resolver_python_harness_carries_sentinel_and_field() { assert!(h.source.contains("__NYX_GRAPHQL_RESOLVER__")); assert!(h.source.contains("\"resolve_user\"")); assert!(h.source.contains("\"Query\"")); + assert!(h.source.contains("_nyx_try_graphene")); + assert!(h.source.contains("graphene.Schema")); } #[test] @@ -741,6 +752,8 @@ fn graphql_resolver_js_harness_carries_sentinel_and_field() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_GRAPHQL_RESOLVER__")); assert!(h.source.contains("\"resolveUser\"")); + assert!(h.source.contains("_nyxTryGraphqlJs")); + assert!(h.source.contains("require('graphql')")); } #[test] @@ -790,6 +803,8 @@ fn websocket_python_harness_carries_sentinel_and_handler() { assert!(h.source.contains("__NYX_WEBSOCKET__")); assert!(h.source.contains("\"message\"")); assert!(h.source.contains("/ws/chat")); + assert!(h.source.contains("_nyx_try_socketio")); + assert!(h.source.contains("socketio.Server")); } #[test] @@ -805,6 +820,8 @@ fn websocket_js_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_WEBSOCKET__")); assert!(h.source.contains("\"onMessage\"")); + assert!(h.source.contains("_nyxTryWs")); + assert!(h.source.contains("require('ws')")); } #[test] @@ -835,6 +852,8 @@ fn middleware_python_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_MIDDLEWARE__")); assert!(h.source.contains("\"audit\"")); + assert!(h.source.contains("_nyx_try_django_middleware")); + assert!(h.source.contains("RequestFactory")); } #[test] @@ -850,6 +869,8 @@ fn middleware_js_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_MIDDLEWARE__")); assert!(h.source.contains("\"audit\"")); + assert!(h.source.contains("_nyxTryExpressMiddleware")); + assert!(h.source.contains("require('express')")); } #[test] @@ -865,6 +886,8 @@ fn middleware_java_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_MIDDLEWARE__")); assert!(h.source.contains("\"preHandle\"")); + assert!(h.source.contains("nyxTrySpringHandlerInterceptor")); + assert!(h.source.contains("HttpServletRequest")); } #[test] @@ -880,6 +903,8 @@ fn middleware_ruby_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_MIDDLEWARE__")); assert!(h.source.contains("AuditMiddleware")); + assert!(h.source.contains("nyx_try_rack_middleware")); + assert!(h.source.contains("Rack::MockRequest")); } #[test] @@ -895,6 +920,8 @@ fn middleware_php_harness_carries_sentinel_and_handler() { let h = lang::emit(&spec).expect("emit ok"); assert!(h.source.contains("__NYX_MIDDLEWARE__")); assert!(h.source.contains("Audit")); + assert!(h.source.contains("Illuminate\\Http\\Request")); + assert!(h.source.contains("__nyx_make_middleware_request")); } #[test]