mod_lua for beginners Eric Covener, IBM November 2011
About Me Eric Covener Apache HTTP Server and WebSphere Application Server Development at IBM. Contributor to Apache HTTP Server, Apache Portable Runtime, and Apache OpenWebBeans. Frequent supporter on freenode #httpd and the HTTPD users mailing list
Agenda Introduction to Lua Introduction to mod_lua mod_lua recipes for familiar problems The future Wrap-up
What is Lua? “Lua is a powerful, fast, lightweight, embeddable scripting language” Basic syntax familiar to occasional users of C or interpreted languages such as JavaScript, PHP, PERL, … #!/usr/bin/lua io.write("What's your name? ") local name = io.read("*line") print("Hello, ", name)
More on Lua Lua is used as the scripting engine many popular large applications World of Warcraft, VLC, Wireshark, RPM Extensions allow Lua scripts to exploit popular libraries such as memcached, mysql, GD, curl, expat, sockets. The programs embedding Lua are expected to provide useful routines. Basic Features: Simple syntax, C-like control structures, I/O, basic regex Advanced Features: Co-routines, closures, OOP
What is mod_lua? mod_lua allows users to write Lua scripts to extend and modify the webserver Handling the response in a Lua script Changing request processing metadata (think SetEnvIf, RequestHeader, Header, and RewriteRule) Implementation of Authentication, Authorization, Access Control Much like a compiled Apache HTTP Server module, or scripts written in mod_perl.
Getting mod_lua Part of Apache HTTP Server 2.3.x (and later) Must be enabled during build (explicitly, or via – enable-mods-shared=[really]all) Lua interpreter (and –dev counterpart) must be available at build time Packaged with most Linux distributions Source tarball is 200k and has no dependencies (doesn’t even use autoconf)
A simple mod_lua handler The simplest way a traditional module (or a mod_lua script) can participate in a request is by acting as the “handler”, or generator, for the response. 1 function handle(r) 2 r.content_type = "text/html" 3 local query = r:parseargs() 4 if query.name == nil then 5 r:puts("What's your name? “, 6 “ ") 7 else 8 r:puts("Hello, ", r:escape_html(query.name)); 9 end 10 end
Invoking a Lua handler A few different ways to teach mod_lua about our script AddHandler lua-script.lua LuaMapHandler LuaMapHandler /hello /path/to/luascripts/hello.lua LuaQuickHandler No external script file required, lives in httpd.conf
Handler performance SourceRequests/second static4300 mod_php2900 mod_lua2400 CGI 600 Crude performance test of a short “Hello, world” page.
Apache Module Primer Traditional Apache modules can do more than just generate the response The participate in designated “hooks” with other modules Mapping a URI to the filesystem Performing authentication Setting mime-types mod_lua allows Lua scripts to participate the same way
Noteworthy hooks quick_handler translate_name URI -> filename access_checker user-independent access control check_userid authentication auth_checker authorization fixups
Redirect to SSL 1 require "apache2" 2 function redirect_ssl(r) 3 local https = r:ssl_var_lookup("HTTPS") 4 if (https == nil or https == "" or https == "off") then 5 r.err_headers_out['Location'] = string.gsub(r:construct_url(r.uri), " " 6 return apache2.HTTP_MOVED_TEMPORARILY 7 end 8 return apache2.DECLINED 9 end
Maintenance page 1 require "apache2“ 2 require “posix" 3 4 function maint(r) 5 if posix.stat(“/tmp/maintenance”) ~= nil then 6 r:puts(“site temporarily unavailable") 7 r.status = 503; 8 return apache2.OK 9 else 10 return apache2.DECLINED 11 end 12 end
Rewrite based on Query 1 function rewrite_query(r) 2 local query = r:parseargs() 3 if query.foo ~= nil and 4 query.bar ~= nil then 5 r.uri = r.uri.. 6 "/".. query.foo.. 7 "/".. query.bar 8 end 9 return apache2.DECLINED 10 end
mod_lua in place of mod_rewrite Pros Proper programming language with control structures, subroutines Less baggage Custom logging Easier to unit test Easier/less fragile to extend Cons Only basic regular expression by default Less source material on the web No htaccess for most recipes
Serving pre-compressed files LuaHookMapToStorage... 1 require "apache2" 2 require "posix" 3 function gz(r) 4 local acceptEncoding = 5 r.headers_in['Accept-Encoding'] 6 if (r.filename and acceptEncoding and 7 string.find(acceptEncoding, "gzip")) then 8 if posix.stat(r.filename.. ".gz") then 9 r.filename = r.filename.. ".gz" 10 r.headers_out['Vary'] = "Accept-Encoding" 11 end 12 end 13 return apache2.DECLINED 14 end
Check Authorization LuaHookAuthChecker /path/to/authz.lua authz … 1 require "apache2" 2 require "posix" 3 4 function authz(r) 5 if r.user == "bob" then 6 local hour = tonumber(os.date("%H")) 7 if hour > 8 and hour < 17 then 8 return end 10 end 11 return apache2.DECLINED 12 end
Basic-if-no-cert LuaHookAuthCheckUserID /path/to/auth.lua \ authn early 1 function authn(r) 2 local user = 3 r:ssl_var_lookup("SSL_CLIENT_S_DN_CN") 4 if user == nil or user == "" then 5 return apache2.DECLINED 6 end 7 r.user = user 8 return apache2.OK 9 end
mod_lua's future HTTP Request/Response filters More Apache API access Configuration in mod_lua More mod_lua examples Join in and help decide!
Conclusion Flexible alternative to mod_setenvif/mod_rewrite/mod_headers recipes. Lowers barriers to quickly interact with HTTP Server API in what would be short modules Lightweight scripting language for handlers Input needed from users, tell us what you want to write in Lua!
Sources mod_lua manual – mod_lua.html The Apache Modules Book – Programming in Lua –
Contact Eric Covener