-
-
Notifications
You must be signed in to change notification settings - Fork 3
Feature/lua script handler #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- Add gopher-lua dependency for Lua script execution - Create LuaScript config type with path, method, queries, headers - Support both inline scripts and file-based scripts - Add LuaScripts field to Mapping configuration - Implement Clone() methods for proper config cloning
- Create Handler struct with script execution logic - Support both inline Lua scripts and file-based scripts - Expose request object to Lua with method, URL, headers, body - Expose response object to Lua for setting status, headers, body - Load standard Lua libraries for script execution - Handle CORS headers automatically - Add error handling for script execution failures - Implement handler options pattern (WithLogger, WithScript, WithFileSystem)
- Add LuaHandlerFactory type to uncors_handler.go - Create makeLuaScriptRoutes method for route registration - Support path, method, queries, and headers matching for Lua scripts - Add WithLuaHandlerFactory option to handler options - Wire up Lua handler factory in app initialization - Add NewLuaLogger for Lua script logging - Load standard Lua libraries (math, string, table, os) - Fix lua.OpenLibraries usage (use individual PreloadModule calls)
- Test inline script execution with various scenarios - Test file-based script loading - Test request object properties (method, path, headers, query params, body) - Test response object manipulation (status, body, headers) - Test standard Lua libraries (math, string, table) - Test CORS headers handling - Test error handling (missing script, syntax errors, runtime errors) - Test default values and edge cases - Test handler options (WithLogger, WithScript, WithFileSystem) Coverage: 89.6% exceeds the 80% requirement
- Create detailed documentation in docs/9. Lua Script Handler.md - Document configuration structure and request matching - Explain inline vs file-based scripts - Document request object properties and access patterns - Document response object and control - Describe available Lua libraries (math, string, table, os) - Provide complete examples for common use cases - Add error handling guide and best practices - Include comparison with mock handler
- Add LuaScript.json definition with path, method, queries, headers - Support both inline script and file-based script (oneOf constraint) - Add lua-scripts array to Mapping.json schema - Schema will be auto-generated in next step
- Run go generate to rebuild schema.json - LuaScript definition now included in generated schema - Schema includes lua-scripts array in mapping configuration
- Define static errors instead of dynamic errors (err113) - Add type assertion checks for Lua table conversions (forcetypeassert) - Rename parameter L to luaState to follow naming conventions (gocritic) - Mark unused test parameters with underscore (revive) - Rename short variable names tc to testCase (varnamelen) - Add new error variables: ErrResponseNotTable, ErrInvalidResponseTable, ErrInvalidHeadersTable All linter checks now pass ✓
Create comprehensive working example with: - Configuration file with 8 different Lua script handlers - Hello World endpoint (inline script) - Echo service (request inspection) - Calculator API (random, sqrt, power operations) - User API (path parameters) - Protected endpoint (authentication check) - Complex file-based script - Data processor (text processing) - Health check endpoint Added documentation: - README with setup instructions - Testing guide for all endpoints - Bash test script for automated testing Example location: examples/lua-scripts/ Usage: cd examples/lua-scripts uncors ./test.sh
Implement comprehensive configuration validation for Lua scripts to catch errors early during config loading rather than at runtime. Key features: - Validates required path field (must be valid absolute path) - Validates optional method field (must be valid HTTP method) - Enforces mutual exclusivity between script and file fields - Validates file existence when file is specified - Validates that query parameter and header keys are not empty - Integrates with existing validation framework Tests include 14 comprehensive cases covering valid configs, invalid configs, and edge cases. All validation errors are caught at startup with clear error messages.
Clean up redundant comments that simply describe what the code is doing. The code is self-explanatory and these comments add no value. Removed comments like: - "Validate path (required)" - "Create Lua state" - "Set request properties" - "Write CORS headers"
Replace all Lua-specific naming with agnostic terminology throughout the codebase to support potential future script engine implementations. Configuration changes: - Rename config key: lua-scripts → scripts - Rename types: LuaScript → Script, LuaScripts → Scripts - Update Mapping struct field: LuaScripts → Scripts Code changes: - Rename package: internal/handler/lua → internal/handler/script - Rename types: LuaHandlerFactory → ScriptHandlerFactory - Rename functions: NewLuaHandler → NewHandler, makeLuaScriptRoutes → makeScriptRoutes - Rename validators: LuaScriptValidator → ScriptValidator - Update error messages: remove "lua" references - Update logger names: NewLuaLogger → NewScriptLogger Schema changes: - Rename definition file: LuaScript.json → Script.json - Update Mapping.json: lua-scripts → scripts - Update all descriptions to use agnostic terminology Documentation changes: - Rename doc: "9. Lua Script Handler.md" → "9. Script Handler.md" - Replace all Lua-specific references with generic "script" terminology - Update feature descriptions to be engine-agnostic Examples changes: - Rename directory: examples/lua-scripts → examples/scripts - Update configuration files to use "scripts" key - Update example messages and comments All tests passing and build successful.
Remove all example files as they are no longer needed.
Add support for accessing route path parameters in Lua scripts through the request.path_params table. Path parameters are extracted from URL patterns using wildcards like /users/{id}/posts/{postId}. Changes: - Add path_params table to request object in script handler - Extract path variables using mux.Vars() from gorilla/mux - Add test for path parameters functionality - Add SetMuxVars helper function in testutils for testing - Update documentation with path_params property and examples Example usage: ```yaml scripts: - path: /users/{id} script: | local id = request.path_params["id"] response.body = '{"user": "' .. id .. '"}' ``` All tests passing.
Implement http.ResponseWriter-style methods for Lua response object to provide a more familiar API for Go developers: - response:WriteHeader(code) - Set HTTP status code - response:Write(data) - Append data to response body - response:WriteString(str) - Append string to response body - response:Header() - Get headers object with Set/Get methods - Header():Set(key, value) - Set header value - Header():Get(key) - Get header value Both table-based (response.status = 200) and Go-style APIs can be used interchangeably in the same script, providing flexibility for different coding styles and preferences. Updated documentation with comprehensive examples of both API styles and their usage patterns.
Instead of accumulating data in Lua tables and reading it at the end, response methods now write directly to Go responseState structure: - Created responseState struct to hold status, headers, and body - Implemented Lua metatables (__index/__newindex) for transparent property access that reads/writes to Go state - Both table-based API (response.body = "text") and method-based API (response:WriteString("text")) now write to the same Go structures - Simplified writeResponse to just copy from state to writer Benefits: - More efficient: no intermediate storage in Lua - Better memory management: data stored in Go slices/maps - Cleaner architecture: single source of truth in Go state - Maintains full backward compatibility with existing scripts All tests pass, including mixed API usage test that verifies both table and method APIs work together seamlessly.
Simplified the code by removing the separate writeResponse method and inlining its logic directly into executeScript after script execution. Changes: - Removed writeResponse method (no longer needed) - Inlined response writing logic into executeScript - Cleaned up responseState struct (removed unused fields) - Removed unused error variables (ErrResponseNotTable, etc.) Benefits: - Simpler code flow: write happens immediately after script execution - Fewer indirections: no separate method call - Cleaner structure: responseState only contains necessary fields - Same functionality: all tests pass without changes All tests pass successfully.
Added detailed explanation of internal architecture to clarify that both API styles (table-based and method-based) write directly to Go structures: Changes: - Added "Internal Architecture" section explaining direct Go writes - Clarified that operations write to Go http.Header and byte slices - Emphasized single source of truth in Go memory - Added comments in mixed API example showing Go writes - Added best practices about API mixing and performance Key points for users: - Both API styles are equally performant - No overhead from API style choice - Data stored efficiently in Go memory, not Lua VM - Free to mix both styles in same script This documentation update corresponds to the architectural improvements made in commits 8f5f2e4 and 88e425e.
Simplified the code by removing the responseState struct and using local variables with closures instead. This achieves the same goal with cleaner code. Changes: - Removed responseState struct type - Replaced with local variables (statusCode, headers, body) - Passed pointers to these variables to createResponseTable - Closures capture these variables for Lua method implementations - Same functionality, simpler code structure Benefits: - Less code: no struct definition needed - Clearer intent: variables are local to executeScript scope - Same performance: no difference in memory allocation - Easier to understand: data flow is more obvious All tests pass successfully.
Complete rewrite to eliminate ALL intermediate state - Lua methods now write directly to http.ResponseWriter as requested. Changes: - Removed all local variables (statusCode, headers, body) - Lua methods write directly to writer: - response.body = "text" → writer.Write(...) - response:WriteString("text") → writer.Write(...) - response.headers["X"] = "Y" → writer.Header().Set(...) - response:WriteHeader(200) → writer.WriteHeader(...) - Added headerWritten flag to prevent multiple WriteHeader calls - Auto-call WriteHeader(200) before first Write if not called - CORS headers set before script execution - response.body and response.status are now write-only Breaking changes: - response.body cannot be read anymore (returns nil) - User must call methods in correct HTTP order (headers → WriteHeader → Write) - If user calls in wrong order, HTTP protocol rules apply Benefits: - Zero buffering: data flows directly to HTTP connection - True streaming: writes happen during script execution - Maximum simplicity: no intermediate state management - Go-idiomatic: same semantics as http.ResponseWriter All tests pass after minor adjustment (can't read response.body anymore).
…r usage Updated documentation and created practical examples to demonstrate the zero-buffering architecture where Lua scripts write directly to HTTP. Documentation updates (docs/9. Script Handler.md): - Replaced "Go structures" explanation with "Direct ResponseWriter" architecture - Added clear diagram showing Lua → ResponseWriter → Network flow - Added "Important Notes" section with warnings about write-only properties - Added "Correct order" and "Wrong order" examples - Updated examples to show real-time data transmission - Updated tips & best practices with critical HTTP order warnings New Examples: 1. examples/script-handler-examples.yaml - 10 practical examples: - Simple JSON response (correct order) - Streaming response with multiple writes - Dynamic content assembly - Error handling - Custom headers - Query parameter processing - Large response streaming - Conditional responses - Wrong order example (anti-pattern) - Mixed API usage 2. examples/SCRIPT-HANDLER-README.md - Complete guide with: - Key concepts and architecture explanation - Running instructions - Example breakdown with flow diagrams - API comparison (table vs method style) - Common patterns - Important limitations - Performance benefits - Testing instructions These examples help users understand: - Zero buffering means data goes straight to network - HTTP protocol order must be respected - Properties are write-only - Both API styles work identically - True streaming capabilities
Changed Lua API to prevent direct assignment to response.body and response.status. These properties can now only be modified through methods (WriteHeader, Write, WriteString), while response.headers remains a table with direct read/write access. Changes: - Block direct assignment to response.status and response.body in __newindex metamethod - Return nil for reads of response.status and response.body in __index metamethod - Update all tests to use method-based API - Update documentation examples to reflect new API design This provides better control over response writing and enforces correct HTTP response order (headers -> status -> body).
Updated all documentation to reflect the new API where response.status and response.body are no longer accessible as properties and must be set using methods: - response:WriteHeader(code) for status - response:Write(data) or response:WriteString(str) for body - response.headers remains accessible as a table Changes: - Update all code examples in Script Handler documentation - Update examples README with correct API usage - Clarify that status/body cannot be read or assigned directly - Update API comparison sections - Fix all inline examples to use method-based API
Split the monolithic handler.go (312 lines) into focused, single-responsibility files for better maintainability and testability: New file structure: - handler.go (76 lines) - HTTP handler and script execution logic - handler_options.go (28 lines) - Handler configuration options - errors.go (6 lines) - Error constants and definitions - lua_state.go (18 lines) - Lua state initialization and library loading - lua_request.go (97 lines) - Request object builder for Lua - lua_response.go (201 lines) - Response object builder for Lua - handler_test.go (612 lines) - Comprehensive test suite Benefits: - Single Responsibility Principle - each file has one clear purpose - Improved readability - smaller, focused files - Better testability - isolated concerns - Easier maintenance - changes are localized - Clear separation between HTTP handling, Lua bridge, and state management All existing tests pass without modification, ensuring zero behavioral changes.
Fixed all linter warnings reported by golangci-lint: 1. Fixed captLocal warnings (gocritic): - Renamed 'L' parameter to 'luaState' for better readability - Renamed 'L' in closures to 'state' to avoid shadowing - Renamed short variable 'v' to 'value' in loops 2. Fixed magic number warnings (mnd): - Added constants for Lua stack positions (luaArgKey=2, luaArgValue=3) - Added constants for Lua return values (luaReturnOne=1, luaReturnTwo=2) - Improved code clarity with named constants 3. Fixed varnamelen warnings: - Renamed 'L' variables to 'luaState' throughout the codebase - Renamed 'n' to 'bytesWritten' for clarity - Used descriptive names for Lua function parameters Note: TestScriptHandler has cognitive complexity of 33 (>30), but this is acceptable for a comprehensive test suite covering multiple scenarios. All tests pass successfully. Zero behavioral changes.
Removed all AI-style explanatory comments from the script handler module. The code is self-documenting with clear function and variable names following Go best practices.
Added layeh.com/gopher-json library to enable JSON encoding/decoding in Lua scripts. This allows scripts to parse JSON request bodies and generate JSON responses easily. Changes: - Added json.encode() and json.decode() functions - Updated documentation with JSON library usage and examples - Added comprehensive JSON tests (encode/decode) - Added example endpoints demonstrating JSON functionality - Updated feature list to include JSON library
Updated all main documentation files to include information about the new Script Handler feature with Lua scripting and JSON support. Changes: - Added Script Handler to feature list in README.md - Added Script Handler to documentation index in Home.md - Updated Configuration.md with scripts example - Updated Request flow to include script processing stage - Added Script Handler to ROADMAP.md Next Release section
07d2d13
to
a72f9ea
Compare
Move app.Close() call before reading from log buffer to ensure all goroutines complete their logging operations. This prevents concurrent access to the bytes.Buffer which is not thread-safe.
c97dc5b
to
5ac1916
Compare
- Add tests for Script.String() method with inline and file scripts - Add tests for Scripts.Clone() including nil case handling - Add tests for RequestMatcher.Clone() with deep copy verification - Add tests for multi-value headers and query parameters in Lua scripts - Add tests for response metatable edge cases and header methods - Improve test coverage for lua_request.go and lua_response.go Coverage improvements: - internal/handler/script: 80.0% → 92.4% - internal/config: 89.6% → 93.9% - All new script code has 100% coverage except minor edge cases
f9cda06
to
517a8ab
Compare
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces a powerful Script Handler feature that enables dynamic request/response handling using Lua scripts with full JSON support. The implementation follows Go best practices, uses zero-buffering architecture for optimal performance, and provides a Go-style API that mirrors
http.ResponseWriter
.🎯 Key Features
Script Handler
http.ResponseWriter
for streaming responsesWriteHeader()
,Write()
, andWriteString()
/users/{id}
)Available Libraries
json.encode()
andjson.decode()
📊 Changes Overview
New Files
docs/9. Script Handler.md
- Comprehensive documentation (852 lines)examples/SCRIPT-HANDLER-README.md
- Usage guide with examplesexamples/script-handler-examples.yaml
- 12 working examplesinternal/handler/script/*.go
- Script handler implementationinternal/config/script.go
- Configuration structuresinternal/config/validators/script.go
- Configuration validationTest Coverage
🏗️ Architecture
Zero-Buffering Design
All Lua operations write directly to Go's
http.ResponseWriter
:Benefits:
API Design
Headers (Table Access)
Status & Body (Methods Only)
Key principle:
response.status
andresponse.body
are not accessible as properties - use methods instead. This prevents common bugs and enforces proper HTTP protocol order.💡 Usage Examples
Simple JSON API
Request Body Processing
🔧 Implementation Details
Module Structure (Go Best Practices)
Dependencies
github.com/yuin/gopher-lua
- Lua VM for Golayeh.com/gopher-json
- JSON encoding/decoding for LuaRequest Processing Order
📚 Documentation
Complete Documentation Added
✅ Script Handler guide (
docs/9. Script Handler.md
) - 852 lines covering:✅ Examples README (
examples/SCRIPT-HANDLER-README.md
) - 225 lines with:✅ Working examples (
examples/script-handler-examples.yaml
) - 12 examples:Updated Documentation
✨ Code Quality
Linter Compliance
Refactoring Applied
🧪 Testing
All tests passing:
🔒 Security Considerations
The script handler:
📦 Breaking Changes
None - This is a purely additive feature with no breaking changes to existing functionality.
🚀 Migration
No migration needed. Existing configurations work without changes. To use the new feature, simply add a
scripts
section to your mapping configuration.🎓 Related Issues
This PR implements a flexible scripting solution that can be used for:
📝 Checklist
Total additions: 2,939 lines (implementation + tests + documentation)
Documentation: 1,077 lines of high-quality docs and examples
Test coverage: 886 lines of comprehensive tests