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>