diff --git a/core/runner/http.go b/core/runner/http.go new file mode 100644 index 0000000..4f25242 --- /dev/null +++ b/core/runner/http.go @@ -0,0 +1,135 @@ +package runner + +import ( + luajit "git.sharkk.net/Sky/LuaJIT-to-Go" +) + +// HTTPResponse represents an HTTP response from Lua +type HTTPResponse struct { + Status int `json:"status"` + Headers map[string]string `json:"headers"` + Body any `json:"body"` +} + +// NewHTTPResponse creates a default HTTP response +func NewHTTPResponse() *HTTPResponse { + return &HTTPResponse{ + Status: 200, + Headers: make(map[string]string), + } +} + +// LuaHTTPModule is the pure Lua implementation of the HTTP module +const LuaHTTPModule = ` +-- Table to store response data +__http_responses = {} + +-- HTTP module implementation +local http = { + -- Set HTTP status code + set_status = function(code) + if type(code) ~= "number" then + error("http.set_status: status code must be a number", 2) + end + + local resp = __http_responses[1] or {} + resp.status = code + __http_responses[1] = resp + end, + + -- Set HTTP header + set_header = function(name, value) + if type(name) ~= "string" or type(value) ~= "string" then + error("http.set_header: name and value must be strings", 2) + end + + local resp = __http_responses[1] or {} + resp.headers = resp.headers or {} + resp.headers[name] = value + __http_responses[1] = resp + end +} + +-- Set content type; set_header helper +http.set_content_type = function(content_type) + http.set_header("Content-Type", content_type) +end + +-- Install HTTP module +_G.http = http + +-- Override sandbox executor to clear HTTP responses +local old_execute_sandbox = __execute_sandbox +__execute_sandbox = function(bytecode, ctx) + -- Clear previous response for this thread + __http_responses[1] = nil + + -- Execute the original function + local result = old_execute_sandbox(bytecode, ctx) + + -- Return the result unchanged + return result +end + +-- Make sure the HTTP module is accessible in sandbox +if __env_system and __env_system.base_env then + __env_system.base_env.http = http +end +` + +// HTTPModuleInitFunc returns an initializer function for the HTTP module +func HTTPModuleInitFunc() StateInitFunc { + return func(state *luajit.State) error { + // Initialize pure Lua HTTP module + return state.DoString(LuaHTTPModule) + } +} + +// GetHTTPResponse extracts the HTTP response from Lua state +func GetHTTPResponse(state *luajit.State) (*HTTPResponse, bool) { + response := NewHTTPResponse() + + // Get response table + state.GetGlobal("__http_responses") + if state.IsNil(-1) { + state.Pop(1) + return response, false + } + + // Check for response at thread index + state.PushNumber(1) + state.GetTable(-2) + if state.IsNil(-1) { + state.Pop(2) + return response, false + } + + // Get status + state.GetField(-1, "status") + if state.IsNumber(-1) { + response.Status = int(state.ToNumber(-1)) + } + state.Pop(1) + + // Get headers + state.GetField(-1, "headers") + if state.IsTable(-1) { + // Iterate through headers table + state.PushNil() // Start iteration + for state.Next(-2) { + // Stack has key at -2 and value at -1 + if state.IsString(-2) && state.IsString(-1) { + key := state.ToString(-2) + value := state.ToString(-1) + response.Headers[key] = value + } + state.Pop(1) // Pop value, leave key for next iteration + } + } + state.Pop(1) + + // Clean up + state.Pop(2) // Pop response table and __http_responses + + return response, true +} diff --git a/core/utils/errorpages.go b/core/utils/errorpages.go new file mode 100644 index 0000000..40fa5ae --- /dev/null +++ b/core/utils/errorpages.go @@ -0,0 +1,227 @@ +package utils + +import ( + "math/rand" + "os" + "path/filepath" +) + +// ErrorPageConfig holds configuration for generating error pages +type ErrorPageConfig struct { + OverrideDir string // Directory where override templates are stored + DebugMode bool // Whether to show debug information +} + +// ErrorType represents HTTP error types +type ErrorType int + +const ( + ErrorTypeNotFound ErrorType = 404 + ErrorTypeMethodNotAllowed ErrorType = 405 + ErrorTypeInternalError ErrorType = 500 +) + +// ErrorPage generates an HTML error page based on the error type +// It first checks for an override file, and if not found, generates a default page +func ErrorPage(config ErrorPageConfig, errorType ErrorType, url string, errMsg string) string { + // Check for override file + if config.OverrideDir != "" { + var filename string + switch errorType { + case ErrorTypeNotFound: + filename = "404.html" + case ErrorTypeMethodNotAllowed: + filename = "405.html" + case ErrorTypeInternalError: + filename = "500.html" + } + + if filename != "" { + overridePath := filepath.Join(config.OverrideDir, filename) + if content, err := os.ReadFile(overridePath); err == nil { + return string(content) + } + } + } + + // No override found, generate default page + switch errorType { + case ErrorTypeNotFound: + return generateNotFoundHTML(url) + case ErrorTypeMethodNotAllowed: + return generateMethodNotAllowedHTML(url) + case ErrorTypeInternalError: + return generateInternalErrorHTML(config.DebugMode, url, errMsg) + default: + // Fallback to internal error + return generateInternalErrorHTML(config.DebugMode, url, errMsg) + } +} + +// NotFoundPage generates a 404 Not Found error page +func NotFoundPage(config ErrorPageConfig, url string) string { + return ErrorPage(config, ErrorTypeNotFound, url, "") +} + +// MethodNotAllowedPage generates a 405 Method Not Allowed error page +func MethodNotAllowedPage(config ErrorPageConfig, url string) string { + return ErrorPage(config, ErrorTypeMethodNotAllowed, url, "") +} + +// InternalErrorPage generates a 500 Internal Server Error page +func InternalErrorPage(config ErrorPageConfig, url string, errMsg string) string { + return ErrorPage(config, ErrorTypeInternalError, url, errMsg) +} + +// generateInternalErrorHTML creates a 500 Internal Server Error page +func generateInternalErrorHTML(debugMode bool, url string, errMsg string) string { + errorMessages := []string{ + "Oops! Something went wrong", + "Oh no! The server choked", + "Well, this is embarrassing...", + "Houston, we have a problem", + "Gremlins in the system", + "The server is taking a coffee break", + "Moonshark encountered a lunar eclipse", + "Our code monkeys are working on it", + "The server is feeling under the weather", + "500 Brain Not Found", + } + + randomMessage := errorMessages[rand.Intn(len(errorMessages))] + codeContent := url + if errMsg != "" { + codeContent = url + " → " + errMsg + } + return generateErrorHTML("500", randomMessage, "Internal Server Error", debugMode, codeContent) +} + +// generateNotFoundHTML creates a 404 Not Found error page +func generateNotFoundHTML(url string) string { + errorMessages := []string{ + "Nothing to see here", + "This page is on vacation", + "The page is missing in action", + "This page has left the building", + "This page is in another castle", + "Sorry, we can't find that", + "The page you're looking for doesn't exist", + "Lost in space", + "That's a 404", + "Page not found", + } + + randomMessage := errorMessages[rand.Intn(len(errorMessages))] + return generateErrorHTML("404", randomMessage, "Page Not Found", false, url) +} + +// generateMethodNotAllowedHTML creates a 405 Method Not Allowed error page +func generateMethodNotAllowedHTML(url string) string { + errorMessages := []string{ + "That's not how this works", + "Method not allowed", + "Wrong way!", + "This method is not supported", + "You can't do that here", + "Sorry, wrong door", + "That method won't work here", + "Try a different approach", + "Access denied for this method", + "Method mismatch", + } + + randomMessage := errorMessages[rand.Intn(len(errorMessages))] + return generateErrorHTML("405", randomMessage, "Method Not Allowed", false, url) +} + +// generateErrorHTML creates the common HTML structure for error pages +func generateErrorHTML(errorCode, mainMessage, subMessage string, showDebugInfo bool, codeContent string) string { + errorHTML := ` + +
+` + mainMessage + `
+ ` + + if codeContent != "" { + errorHTML += ` +` + codeContent + `
`
+ }
+
+ // Add a note for debug mode
+ if showDebugInfo {
+ errorHTML += `
+
+ An error occurred while processing your request.
+ Please check the server logs for details.
+