Annotation of embedaddon/lighttpd/doc/outdated/magnet.txt, revision 1.1
1.1 ! misho 1: {{{
! 2: #!rst
! 3: ==============
! 4: a power-magnet
! 5: ==============
! 6:
! 7: ------------------
! 8: Module: mod_magnet
! 9: ------------------
! 10:
! 11:
! 12:
! 13: .. contents:: Table of Contents
! 14:
! 15: Requirements
! 16: ============
! 17:
! 18: :Version: lighttpd 1.4.12 or higher
! 19: :Packages: lua >= 5.1
! 20:
! 21: Overview
! 22: ========
! 23:
! 24: mod_magnet is a module to control the request handling in lighty.
! 25:
! 26: .. note::
! 27:
! 28: Keep in mind that the magnet is executed in the core of lighty. EVERY long-running operation is blocking
! 29: ALL connections in the server. You are warned. For time-consuming or blocking scripts use mod_fastcgi and friends.
! 30:
! 31: For performance reasons mod_magnet caches the compiled script. For each script-run the script itself is checked for
! 32: freshness and recompile if neccesary.
! 33:
! 34:
! 35: Installation
! 36: ============
! 37:
! 38: mod_magnet needs a lighty which is compiled with the lua-support ( --with-lua). Lua 5.1 or higher are required by
! 39: the module. Use "--with-lua=lua5.1" to install on Debian and friends. ::
! 40:
! 41: server.modules = ( ..., "mod_magnet", ... )
! 42:
! 43: Options
! 44: =======
! 45:
! 46: mod_magnet can attract a request in several stages in the request-handling.
! 47:
! 48: * either at the same level as mod_rewrite, before any parsing of the URL is done
! 49: * or at a later stage, when the doc-root is known and the physical-path is already setup
! 50:
! 51: It depends on the purpose of the script which stage you want to intercept. Usually you want to use
! 52: the 2nd stage where the physical-path which relates to your request is known. At this level you
! 53: can run checks against lighty.env["physical.path"].
! 54:
! 55: ::
! 56:
! 57: magnet.attract-raw-url-to = ( ... )
! 58: magnet.attract-physical-path-to = ( ... )
! 59:
! 60: You can define multiple scripts when separated by a semicolon. The scripts are executed in the specified
! 61: order. If one of them a returning a status-code, the following scripts will not be executed.
! 62:
! 63: Tables
! 64: ======
! 65:
! 66: Most of the interaction between between mod_magnet and lighty is done through tables. Tables in lua are hashes (Perl), dictionaries (Java), arrays (PHP), ...
! 67:
! 68: Request-Environment
! 69: -------------------
! 70:
! 71: Lighttpd has its internal variables which are exported as read/write to the magnet.
! 72:
! 73: If "http://example.org/search.php?q=lighty" is requested this results in a request like ::
! 74:
! 75: GET /search.php?q=lighty HTTP/1.1
! 76: Host: example.org
! 77:
! 78: When you are using ``attract-raw-url-to`` you can access the following variables:
! 79:
! 80: * parts of the request-line
! 81:
! 82: * lighty.env["request.uri"] = "/search.php?q=lighty"
! 83:
! 84: * HTTP request-headers
! 85:
! 86: * lighty.request["Host"] = "example.org"
! 87:
! 88: Later in the request-handling, the URL is splitted, cleaned up and turned into a physical path name:
! 89:
! 90: * parts of the URI
! 91:
! 92: * lighty.env["uri.path"] = "/search.php"
! 93: * lighty.env["uri.path-raw"] = "/search.php"
! 94: * lighty.env["uri.scheme"] = "http"
! 95: * lighty.env["uri.authority"] = "example.org"
! 96: * lighty.env["uri.query"] = "q=lighty"
! 97:
! 98: * filenames, pathnames
! 99:
! 100: * lighty.env["physical.path"] = "/my-docroot/search.php"
! 101: * lighty.env["physical.rel-path"] = "/search.php"
! 102: * lighty.env["physical.doc-root"] = "/my-docroot"
! 103:
! 104: All of them are readable, not all of the are writable (or don't have an effect if you write to them).
! 105:
! 106: As a start, you might want to use those variables for writing: ::
! 107:
! 108: -- 1. simple rewriting is done via the request.uri
! 109: lighty.env["request.uri"] = ...
! 110: return lighty.RESTART_REQUEST
! 111:
! 112: -- 2. changing the physical-path
! 113: lighty.env["physical.path"] = ...
! 114:
! 115: -- 3. changing the query-string
! 116: lighty.env["uri.query"] = ...
! 117:
! 118: Response Headers
! 119: ----------------
! 120:
! 121: If you want to set a response header for your request, you can add a field to the lighty.header[] table: ::
! 122:
! 123: lighty.header["Content-Type"] = "text/html"
! 124:
! 125: Sending Content
! 126: ===============
! 127:
! 128: You can generate your own content and send it out to the clients. ::
! 129:
! 130: lighty.content = { "<pre>", { filename = "/etc/passwd" }, "</pre>" }
! 131: lighty.header["Content-Type"] = "text/html"
! 132:
! 133: return 200
! 134:
! 135: The lighty.content[] table is executed when the script is finished. The elements of the array are processed left to right and the elements can either be a string or a table. Strings are included AS IS into the output of the request.
! 136:
! 137: * Strings
! 138:
! 139: * are included as is
! 140:
! 141: * Tables
! 142:
! 143: * filename = "<absolute-path>" is required
! 144: * offset = <number> [default: 0]
! 145: * length = <number> [default: size of the file - offset]
! 146:
! 147: Internally lighty will use the sendfile() call to send out the static files at full speed.
! 148:
! 149: Status Codes
! 150: ============
! 151:
! 152: You might have seen it already in other examples: In case you are handling the request completly in the magnet you
! 153: can return your own status-codes. Examples are: Redirected, Input Validation, ... ::
! 154:
! 155: if (lighty.env["uri.scheme"] == "http") then
! 156: lighty.header["Location"] = "https://" .. lighty.env["uri.authority"] .. lighty.env["request.uri"]
! 157: return 302
! 158: end
! 159:
! 160: You every number above and equal to 100 is taken as final status code and finishes the request. No other modules are
! 161: executed after this return.
! 162:
! 163: A special return-code is lighty.RESTART_REQUEST (currently equal to 99) which is usually used in combination with
! 164: changing the request.uri in a rewrite. It restarts the splitting of the request-uri again.
! 165:
! 166: If you return nothing (or nil) the request-handling just continues.
! 167:
! 168: Debugging
! 169: =========
! 170:
! 171: To easy debugging we overloaded the print()-function in lua and redirect the output of print() to the error-log. ::
! 172:
! 173: print("Host: " .. lighty.request["Host"])
! 174: print("Request-URI: " .. lighty.env["request.uri"])
! 175:
! 176:
! 177: Examples
! 178: ========
! 179:
! 180: Sending text-files as HTML
! 181: --------------------------
! 182:
! 183: This is a bit simplistic, but it illustrates the idea: Take a text-file and cover it in a <pre> tag.
! 184:
! 185: Config-file ::
! 186:
! 187: magnet.attract-physical-path-to = server.docroot + "/readme.lua"
! 188:
! 189: readme.lua ::
! 190:
! 191: lighty.content = { "<pre>", { filename = "/README" }, "</pre>" }
! 192: lighty.header["Content-Type"] = "text/html"
! 193:
! 194: return 200
! 195:
! 196: Maintainance pages
! 197: ------------------
! 198:
! 199: Your side might be on maintainance from time to time. Instead of shutting down the server confusing all
! 200: users, you can just send a maintainance page.
! 201:
! 202: Config-file ::
! 203:
! 204: magnet.attract-physical-path-to = server.docroot + "/maintainance.lua"
! 205:
! 206: maintainance.lua ::
! 207:
! 208: require "lfs"
! 209:
! 210: if (nil == lfs.attributes(lighty.env["physical.doc-root"] .. "/maintainance.html")) then
! 211: lighty.content = ( lighty.env["physical.doc-root"] .. "/maintainance.html" )
! 212:
! 213: lighty.header["Content-Type"] = "text/html"
! 214:
! 215: return 200
! 216: end
! 217:
! 218: mod_flv_streaming
! 219: -----------------
! 220:
! 221: Config-file ::
! 222:
! 223: magnet.attract-physical-path-to = server.docroot + "/flv-streaming.lua"
! 224:
! 225: flv-streaming.lua::
! 226:
! 227: if (lighty.env["uri.query"]) then
! 228: -- split the query-string
! 229: get = {}
! 230: for k, v in string.gmatch(lighty.env["uri.query"], "(%w+)=(%w+)") do
! 231: get[k] = v
! 232: end
! 233:
! 234: if (get["start"]) then
! 235: -- missing: check if start is numeric and positive
! 236:
! 237: -- send te FLV header + a seek into the file
! 238: lighty.content = { "FLV\x1\x1\0\0\0\x9\0\0\0\x9",
! 239: { filename = lighty.env["physical.path"], offset = get["start"] } }
! 240: lighty.header["Content-Type"] = "video/x-flv"
! 241:
! 242: return 200
! 243: end
! 244: end
! 245:
! 246:
! 247: selecting a random file from a directory
! 248: ----------------------------------------
! 249:
! 250: Say, you want to send a random file (ad-content) from a directory.
! 251:
! 252: To simplify the code and to improve the performance we define:
! 253:
! 254: * all images have the same format (e.g. image/png)
! 255: * all images use increasing numbers starting from 1
! 256: * a special index-file names the highest number
! 257:
! 258: Config ::
! 259:
! 260: server.modules += ( "mod_magnet" )
! 261: magnet.attract-physical-path-to = "random.lua"
! 262:
! 263: random.lua ::
! 264:
! 265: dir = lighty.env["physical.path"]
! 266:
! 267: f = assert(io.open(dir .. "/index", "r"))
! 268: maxndx = f:read("*all")
! 269: f:close()
! 270:
! 271: ndx = math.random(maxndx)
! 272:
! 273: lighty.content = { { filename = dir .. "/" .. ndx }}
! 274: lighty.header["Content-Type"] = "image/png"
! 275:
! 276: return 200
! 277:
! 278: denying illegal character sequences in the URL
! 279: ----------------------------------------------
! 280:
! 281: Instead of implementing mod_security, you might just want to apply filters on the content
! 282: and deny special sequences that look like SQL injection.
! 283:
! 284: A common injection is using UNION to extend a query with another SELECT query.
! 285:
! 286: ::
! 287:
! 288: if (string.find(lighty.env["request.uri"], "UNION%s")) then
! 289: return 400
! 290: end
! 291:
! 292: Traffic Quotas
! 293: --------------
! 294:
! 295: If you only allow your virtual hosts a certain amount for traffic each month and want to
! 296: disable them if the traffic is reached, perhaps this helps: ::
! 297:
! 298: host_blacklist = { ["www.example.org"] = 0 }
! 299:
! 300: if (host_blacklist[lighty.request["Host"]]) then
! 301: return 404
! 302: end
! 303:
! 304: Just add the hosts you want to blacklist into the blacklist table in the shown way.
! 305:
! 306: Complex rewrites
! 307: ----------------
! 308:
! 309: If you want to implement caching on your document-root and only want to regenerate
! 310: content if the requested file doesn't exist, you can attract the physical.path: ::
! 311:
! 312: magnet.attract-physical-path-to = ( server.document-root + "/rewrite.lua" )
! 313:
! 314: rewrite.lua ::
! 315:
! 316: require "lfs"
! 317:
! 318: attr = lfs.attributes(lighty.env["physical.path"])
! 319:
! 320: if (not attr) then
! 321: -- we couldn't stat() the file for some reason
! 322: -- let the backend generate it
! 323:
! 324: lighty.env["uri.path"] = "/dispatch.fcgi"
! 325: lighty.env["physical.rel-path"] = lighty.env["uri.path"]
! 326: lighty.env["physical.path"] = lighty.env["physical.doc-root"] .. lighty.env["physical.rel-path"]
! 327: fi
! 328:
! 329: luafilesystem
! 330: +++++++++++++
! 331:
! 332: We are requiring the lua-module 'lfs' (http://www.keplerproject.org/luafilesystem/).
! 333:
! 334: I had to compile lfs myself for lua-5.1 which required a minor patch as compat-5.1 is not needed::
! 335:
! 336: $ wget http://luaforge.net/frs/download.php/1487/luafilesystem-1.2.tar.gz
! 337: $ wget http://www.lighttpd.net/download/luafilesystem-1.2-lua51.diff
! 338: $ gzip -cd luafilesystem-1.2.tar.gz | tar xf -
! 339: $ cd luafilesystem-1.2
! 340: $ patch -ls -p1 < ../luafilesystem-1.2-lua51.diff
! 341: $ make install
! 342:
! 343: It will install lfs.so into /usr/lib/lua/5.1/ which is where lua expects the extensions on my system.
! 344:
! 345: SuSE and Gentoo are known to have their own lfs packages and don't require a compile.
! 346:
! 347: Usertracking
! 348: ------------
! 349:
! 350: ... or how to store data globally in the script-context:
! 351:
! 352: Each script has its own script-context. When the script is started it only contains the lua-functions
! 353: and the special lighty.* name-space. If you want to save data between script runs, you can use the global-script
! 354: context:
! 355:
! 356: ::
! 357:
! 358: if (nil == _G["usertrack"]) then
! 359: _G["usertrack"] = {}
! 360: end
! 361: if (nil == _G["usertrack"][lighty.request["Cookie"]]) then
! 362: _G["usertrack"][lighty.request["Cookie"]]
! 363: else
! 364: _G["usertrack"][lighty.request["Cookie"]] = _G["usertrack"][lighty.request["Cookie"]] + 1
! 365: end
! 366:
! 367: print _G["usertrack"][lighty.request["Cookie"]]
! 368:
! 369: The global-context is per script. If you update the script without restarting the server, the context will still be maintained.
! 370:
! 371: Counters
! 372: --------
! 373:
! 374: mod_status support a global statistics page and mod_magnet allows to add and update values in the status page:
! 375:
! 376: Config ::
! 377:
! 378: status.statistics-url = "/server-counters"
! 379: magnet.attract-raw-url-to = server.docroot + "/counter.lua"
! 380:
! 381: counter.lua ::
! 382:
! 383: lighty.status["core.connections"] = lighty.status["core.connections"] + 1
! 384:
! 385: Result::
! 386:
! 387: core.connections: 7
! 388: fastcgi.backend.php-foo.0.connected: 0
! 389: fastcgi.backend.php-foo.0.died: 0
! 390: fastcgi.backend.php-foo.0.disabled: 0
! 391: fastcgi.backend.php-foo.0.load: 0
! 392: fastcgi.backend.php-foo.0.overloaded: 0
! 393: fastcgi.backend.php-foo.1.connected: 0
! 394: fastcgi.backend.php-foo.1.died: 0
! 395: fastcgi.backend.php-foo.1.disabled: 0
! 396: fastcgi.backend.php-foo.1.load: 0
! 397: fastcgi.backend.php-foo.1.overloaded: 0
! 398: fastcgi.backend.php-foo.load: 0
! 399:
! 400: Porting mod_cml scripts
! 401: -----------------------
! 402:
! 403: mod_cml got replaced by mod_magnet.
! 404:
! 405: A CACHE_HIT in mod_cml::
! 406:
! 407: output_include = { "file1", "file2" }
! 408:
! 409: return CACHE_HIT
! 410:
! 411: becomes::
! 412:
! 413: content = { { filename = "/path/to/file1" }, { filename = "/path/to/file2"} }
! 414:
! 415: return 200
! 416:
! 417: while a CACHE_MISS like (CML) ::
! 418:
! 419: trigger_handler = "/index.php"
! 420:
! 421: return CACHE_MISS
! 422:
! 423: becomes (magnet) ::
! 424:
! 425: lighty.env["request.uri"] = "/index.php"
! 426:
! 427: return lighty.RESTART_REQUEST
! 428:
! 429: }}}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>