├── LICENSE
├── README.md
├── examples
└── sailor
│ ├── controllers
│ └── remy_demo.lua
│ └── views
│ └── remy_demo
│ └── index.lp
├── rockspecs
├── remy-0.2.10-1.rockspec
├── remy-0.2.11-1.rockspec
├── remy-0.2.12-1.rockspec
├── remy-0.2.13-1.rockspec
├── remy-0.2.14-1.rockspec
└── remy-current-1.rockspec
└── src
├── remy.lua
└── remy
├── cgilua.lua
├── file_obj.lua
├── lwan.lua
├── mod_magnet.lua
├── mod_plua.lua
└── nginx.lua
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Felipe Daragon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Remy #
2 |
3 | Remy (the R emulator) is a simple mod_lua emulator, allowing to run web applications built for mod_lua in alternative environments that allow to run server-side Lua code. As such, it is able to support and emulate the mod_lua API, the request_rec structure and some of its built-in functions.
4 |
5 | This is a work in progress, an may not be suitable for production environments yet.
6 |
7 | Remy was developed as part of [Sailor](https://github.com/Etiene/sailor), a Lua-based MVC framework which originally uses mod_lua, and is already able to run mod_lua apps in a variety of environments (listed below).
8 |
9 | ## Supported Environments #
10 |
11 | * Any web server with [CGILua](https://github.com/keplerproject/cgilua) Tested with:
12 | * Apache
13 | * [Civetweb](https://github.com/bel2125/civetweb)
14 | * [Mongoose](https://github.com/cesanta/mongoose)
15 | * Untested: IIS
16 | * Apache with [mod_lua](http://www.modlua.org/)
17 | * Apache with [mod_plua](https://github.com/Humbedooh/mod_pLua)
18 | * Lighttp with [mod_magnet](http://redmine.lighttpd.net/projects/1/wiki/Docs_ModMagnet)
19 | * [Lwan Web Server](http://lwan.ws/)
20 | * Nginx with [ngx_lua](https://github.com/nginx/nginx) (HttpLuaModule)
21 |
22 | ## Planned Environments #
23 |
24 | * IIS with [LuaScript](http://na-s.jp/LuaScript/)
25 |
26 | ## Usage #
27 |
28 | ``` lua
29 | local remy = require "remy"
30 |
31 | function handle(r)
32 | r.content_type = "text/plain"
33 |
34 | if r.method == 'GET' then
35 | r:puts("Hello Lua World!\n")
36 | for k, v in pairs( r:parseargs() ) do
37 | r:puts( string.format("%s: %s\n", k, v) )
38 | end
39 | elseif r.method == 'POST' then
40 | r:puts("Hello Lua World!\n")
41 | for k, v in pairs( r:parsebody() ) do
42 | r:puts( string.format("%s: %s\n", k, v) )
43 | end
44 | end
45 | return apache2.OK
46 | end
47 |
48 | remy.init()
49 | remy.contentheader("text/plain")
50 | remy.run(handle)
51 | ```
52 |
53 | ## License #
54 |
55 | Remy is licensed under the MIT license (http://opensource.org/licenses/MIT)
56 |
57 | (c) Felipe Daragon, 2014-2015
--------------------------------------------------------------------------------
/examples/sailor/controllers/remy_demo.lua:
--------------------------------------------------------------------------------
1 | local remy_demo = {}
2 |
3 | -- This page will work with mod_lua, mod_pLua & CGILua
4 | function remy_demo.index(page)
5 | local t = {}
6 | t.server = page.r.banner
7 | t.ip = page.r.useragent_ip
8 | page:render('index',{server=t.server,ip=t.ip})
9 | end
10 |
11 | -- This one will work only with CGILua
12 | function remy_demo.cgi(page)
13 | local t = {}
14 | t.server = cgilua.servervariable('SERVER_SOFTWARE')
15 | t.ip = cgilua.servervariable('REMOTE_ADDR')
16 | page:render('index',{server=t.server,ip=t.ip})
17 | end
18 |
19 | return remy_demo
--------------------------------------------------------------------------------
/examples/sailor/views/remy_demo/index.lp:
--------------------------------------------------------------------------------
1 |
2 | This server is running: =server?>
3 |
4 | Your IP address is: =ip?>
5 |
--------------------------------------------------------------------------------
/rockspecs/remy-0.2.10-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "remy"
2 | version = "0.2.10-1"
3 | source = {
4 | url = "git://github.com/sailorproject/remy",
5 | tag = "v0.2.10-alpha"
6 | }
7 | description = {
8 | summary = "An abstraction of web servers supporting Lua",
9 | detailed = [[
10 | Remy (the R emulator) is a simple mod_lua emulator, allowing to run web applications built for mod_lua in alternative environments that allow to run server-side Lua code
11 | ]],
12 | license = "MIT"
13 | }
14 | dependencies = {
15 | }
16 | build = {
17 | type = "builtin",
18 | modules = {
19 | remy = "src/remy.lua",
20 | ['remy.cgilua'] = "src/remy/cgilua.lua",
21 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua",
22 | ['remy.mod_plua'] = "src/remy/mod_plua.lua",
23 | ['remy.nginx'] = "src/remy/nginx.lua",
24 | ['remy.lwan'] = "src/remy/lwan.lua",
25 | ['remy.nginx'] = "src/remy/nginx.lua",
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/rockspecs/remy-0.2.11-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "remy"
2 | version = "0.2.11-1"
3 | source = {
4 | url = "git://github.com/sailorproject/remy",
5 | tag = "v0.2.11-alpha"
6 | }
7 | description = {
8 | summary = "An abstraction of web servers supporting Lua",
9 | detailed = [[
10 | Remy (the R emulator) is a simple mod_lua emulator, allowing to run web applications built for mod_lua in alternative environments that allow to run server-side Lua code
11 | ]],
12 | license = "MIT"
13 | }
14 | dependencies = {
15 | }
16 | build = {
17 | type = "builtin",
18 | modules = {
19 | remy = "src/remy.lua",
20 | ['remy.cgilua'] = "src/remy/cgilua.lua",
21 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua",
22 | ['remy.mod_plua'] = "src/remy/mod_plua.lua",
23 | ['remy.nginx'] = "src/remy/nginx.lua",
24 | ['remy.lwan'] = "src/remy/lwan.lua",
25 | ['remy.nginx'] = "src/remy/nginx.lua",
26 | ['remy.file_obj'] = "src/remy/file_obj.lua",
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/rockspecs/remy-0.2.12-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "remy"
2 | version = "0.2.12-1"
3 | source = {
4 | url = "git://github.com/sailorproject/remy",
5 | tag = "v0.2.12-alpha"
6 | }
7 | description = {
8 | summary = "An abstraction of web servers supporting Lua",
9 | detailed = [[
10 | Remy (the R emulator) is a simple mod_lua emulator, allowing to run web applications built for mod_lua in alternative environments that allow to run server-side Lua code
11 | ]],
12 | license = "MIT"
13 | }
14 | dependencies = {
15 | }
16 | build = {
17 | type = "builtin",
18 | modules = {
19 | remy = "src/remy.lua",
20 | ['remy.cgilua'] = "src/remy/cgilua.lua",
21 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua",
22 | ['remy.mod_plua'] = "src/remy/mod_plua.lua",
23 | ['remy.nginx'] = "src/remy/nginx.lua",
24 | ['remy.lwan'] = "src/remy/lwan.lua",
25 | ['remy.nginx'] = "src/remy/nginx.lua",
26 | ['remy.file_obj'] = "src/remy/file_obj.lua",
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/rockspecs/remy-0.2.13-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "remy"
2 | version = "0.2.13-1"
3 | source = {
4 | url = "git://github.com/sailorproject/remy",
5 | tag = "v0.2.13-alpha"
6 | }
7 | description = {
8 | summary = "An abstraction of web servers supporting Lua",
9 | detailed = [[
10 | Remy (the R emulator) is a simple mod_lua emulator, allowing to run web applications built for mod_lua in alternative environments that allow to run server-side Lua code
11 | ]],
12 | license = "MIT"
13 | }
14 | dependencies = {
15 | }
16 | build = {
17 | type = "builtin",
18 | modules = {
19 | remy = "src/remy.lua",
20 | ['remy.cgilua'] = "src/remy/cgilua.lua",
21 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua",
22 | ['remy.mod_plua'] = "src/remy/mod_plua.lua",
23 | ['remy.nginx'] = "src/remy/nginx.lua",
24 | ['remy.lwan'] = "src/remy/lwan.lua",
25 | ['remy.nginx'] = "src/remy/nginx.lua",
26 | ['remy.file_obj'] = "src/remy/file_obj.lua",
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/rockspecs/remy-0.2.14-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "remy"
2 | version = "0.2.14-1"
3 | source = {
4 | url = "git://github.com/sailorproject/remy",
5 | tag = "v0.2.14-alpha"
6 | }
7 | description = {
8 | summary = "An abstraction of web servers supporting Lua",
9 | detailed = [[
10 | Remy (the R emulator) is a simple mod_lua emulator, allowing to run web applications built for mod_lua in alternative environments that allow to run server-side Lua code
11 | ]],
12 | license = "MIT"
13 | }
14 | dependencies = {
15 | }
16 | build = {
17 | type = "builtin",
18 | modules = {
19 | remy = "src/remy.lua",
20 | ['remy.cgilua'] = "src/remy/cgilua.lua",
21 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua",
22 | ['remy.mod_plua'] = "src/remy/mod_plua.lua",
23 | ['remy.nginx'] = "src/remy/nginx.lua",
24 | ['remy.lwan'] = "src/remy/lwan.lua",
25 | ['remy.nginx'] = "src/remy/nginx.lua",
26 | ['remy.file_obj'] = "src/remy/file_obj.lua",
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/rockspecs/remy-current-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "remy"
2 | version = "current-1"
3 | source = {
4 | url = "git://github.com/sailorproject/remy",
5 | }
6 | description = {
7 | summary = "An abstraction of web servers supporting Lua",
8 | detailed = [[
9 | Remy (the R emulator) is a simple mod_lua emulator, allowing to run web applications built for mod_lua in alternative environments that allow to run server-side Lua code
10 | ]],
11 | license = "MIT"
12 | }
13 | dependencies = {
14 | }
15 | build = {
16 | type = "builtin",
17 | modules = {
18 | remy = "src/remy.lua",
19 | ['remy.cgilua'] = "src/remy/cgilua.lua",
20 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua",
21 | ['remy.mod_plua'] = "src/remy/mod_plua.lua",
22 | ['remy.nginx'] = "src/remy/nginx.lua",
23 | ['remy.lwan'] = "src/remy/lwan.lua",
24 | ['remy.nginx'] = "src/remy/nginx.lua",
25 | ['remy.file_obj'] = "src/remy/file_obj.lua",
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/remy.lua:
--------------------------------------------------------------------------------
1 | -- Remy 0.2.11
2 | -- Copyright (c) 2014-2015 Felipe Daragon
3 | -- License: MIT (http://opensource.org/licenses/mit-license.php)
4 | --
5 | -- Remy runs Lua-based web applications in alternative web server
6 | -- environments that allow to run Lua code.
7 |
8 | local remy = {
9 | MODE_AUTODETECT = nil,
10 | MODE_CGILUA = 0,
11 | MODE_MOD_PLUA = 1,
12 | MODE_NGINX = 2,
13 | MODE_LWAN = 3,
14 | MODE_LIGHTTPD = 4
15 | }
16 |
17 | local forced_mode = nil
18 |
19 | local emu = {}
20 |
21 | -- The values below will be updated during runtime
22 | remy.config = {
23 | banner = "Apache/2.4.7 (Unix)",
24 | hostname = "localhost",
25 | uri = "/index.lua"
26 | }
27 |
28 | remy.responsetext = nil
29 |
30 | -- HTTPd Package constants
31 | remy.httpd = {
32 | -- Internal constants from include/httpd.h
33 | OK = 0,
34 | DECLINED = -1,
35 | DONE = -2,
36 | version = remy.config.banner,
37 | -- Other HTTP status codes are not yet implemented in mod_lua
38 | HTTP_MOVED_TEMPORARILY = 302,
39 | -- Internal constants used by mod_proxy
40 | PROXYREQ_NONE = 0,
41 | PROXYREQ_PROXY = 1,
42 | PROXYREQ_REVERSE = 2,
43 | PROXYREQ_RESPONSE = 3,
44 | -- Internal constants used by mod_authz_core
45 | AUTHZ_DENIED = 0,
46 | AUTHZ_GRANTED = 1,
47 | AUTHZ_NEUTRAL = 2,
48 | AUTHZ_GENERAL_ERROR = 3,
49 | AUTHZ_DENIED_NO_USER = 4
50 | }
51 |
52 | -- mod_lua's request_rec
53 | -- The values below will be updated during runtime
54 | local request_rec_fields = {
55 | allowoverrides = " ",
56 | ap_auth_type = nil,
57 | args = nil,
58 | assbackwards = false,
59 | auth_name = "",
60 | banner = remy.config.banner,
61 | basic_auth_pw = "",
62 | canonical_filename = nil,
63 | content_encoding = nil,
64 | content_type = nil,
65 | context_prefix = nil,
66 | context_document_root = nil,
67 | document_root = nil,
68 | err_headers_out = {},
69 | filename = nil,
70 | handler = "lua-script",
71 | headers_in = {}, -- request headers
72 | headers_out = {}, -- response headers
73 | hostname = remy.config.hostname,
74 | is_https = false,
75 | is_initial_req = true,
76 | limit_req_body = 0,
77 | log_id = nil,
78 | method = "GET",
79 | notes = {},
80 | options = "Indexes FollowSymLinks ",
81 | path_info = "",
82 | port = 80,
83 | protocol = "HTTP/1.1",
84 | proxyreq = "PROXYREQ_NONE",
85 | range = nil,
86 | remaining = 0,
87 | server_built = "Nov 26 2013 15:46:56",
88 | server_name = remy.config.hostname,
89 | some_auth_required = false,
90 | subprocess_env = {},
91 | started = 1393508507,
92 | status = 200,
93 | the_request = "GET "..remy.config.uri.." HTTP/1.1",
94 | unparsed_uri = remy.config.uri,
95 | uri = remy.config.uri,
96 | user = nil,
97 | useragent_ip = "127.0.0.1"
98 | }
99 |
100 | function remy.init(mode, native_request)
101 | remy.responsetext = nil
102 | if mode == remy.MODE_AUTODETECT then
103 | mode = remy.detect(native_request)
104 | end
105 | if mode == remy.MODE_CGILUA then
106 | emu = require "remy.cgilua"
107 | elseif mode == remy.MODE_NGINX then
108 | emu = require "remy.nginx"
109 | elseif mode == remy.MODE_MOD_PLUA then
110 | emu = require "remy.mod_plua"
111 | elseif mode == remy.MODE_LIGHTTPD then
112 | emu = require "remy.mod_magnet"
113 | elseif mode == remy.MODE_LWAN then
114 | emu = require "remy.lwan"
115 | end
116 | apache2 = remy.httpd
117 | emu.init(native_request)
118 | return mode
119 | end
120 |
121 | -- Sets the value of the Content Type header field
122 | function remy.contentheader(content_type)
123 | emu.contentheader(content_type)
124 | end
125 |
126 | -- Detects the Lua environment
127 | function remy.detect(native_request)
128 | local mode = nil
129 | if forced_mode then return forced_mode end
130 | if package.loaded.cgilua ~= nil then
131 | mode = remy.MODE_CGILUA
132 | elseif package.loaded.ngx ~= nil then
133 | mode = remy.MODE_NGINX
134 | elseif getEnv ~= nil then
135 | local env = getEnv()
136 | if env["pLua-Version"] ~= nil then
137 | mode = remy.MODE_MOD_PLUA
138 | end
139 | -- Note: lighty is not a package so package.loaded.lighty ~= nil will not work
140 | elseif lighty ~= nil then
141 | mode = remy.MODE_LIGHTTPD
142 | elseif native_request ~= nil and type(native_request.query_param) == "function" then
143 | mode = remy.MODE_LWAN
144 | end
145 | return mode
146 | end
147 |
148 | -- Handles the return code
149 | function remy.finish(code)
150 | emu.finish(code)
151 | end
152 |
153 | -- Load the default request_rec fields
154 | function remy.loadrequestrec(r)
155 | for k,v in pairs(request_rec_fields) do r[k] = v end
156 | return r
157 | end
158 |
159 | -- Temporarily stores the printed content (needed by CGILua mode)
160 | function remy.print(str)
161 | remy.responsetext = remy.responsetext or ""
162 | remy.responsetext = remy.responsetext..str
163 | end
164 |
165 | -- Runs the mod_lua handle function
166 | function remy.run(handlefunc)
167 | local code = handlefunc(emu.request)
168 | remy.finish(code)
169 | end
170 |
171 | function remy.splitstring(s, delimiter)
172 | local result = {}
173 | for match in (s..delimiter):gmatch("(.-)"..delimiter) do
174 | table.insert(result, match)
175 | end
176 | return result
177 | end
178 |
179 | function remy.sha1(str)
180 | local sha1 = require "sha1"
181 | return sha1(str)
182 | end
183 |
184 | return remy
185 |
--------------------------------------------------------------------------------
/src/remy/cgilua.lua:
--------------------------------------------------------------------------------
1 | -- Remy - CGI-Lua compatibility
2 | -- Copyright (c) 2014 Felipe Daragon
3 | -- License: MIT
4 |
5 | local base64 = require "base64"
6 | local cgilua = require "cgilua"
7 | local remy = require "remy"
8 |
9 | -- TODO: implement all functions from mod_lua's request_rec
10 | local request = {
11 | -- ENCODING/DECODING FUNCTIONS
12 | base64_decode = function(_,...) return base64.decode(...) end,
13 | base64_encode = function(_,...) return base64.encode(...) end,
14 | escape = function(_,...) return cgilua.urlcode.escape(...) end,
15 | unescape = function(_,...) return cgilua.urlcode.unescape(...) end,
16 | sha1 = function(_,...) return remy.sha1(...) end,
17 | -- REQUEST PARSING FUNCTIONS
18 | parseargs = function(_) return cgilua.QUERY, {} end,
19 | parsebody = function(_) return cgilua.POST, {} end,
20 | -- REQUEST RESPONSE FUNCTIONS
21 | --puts = function(_,...) cgilua.put(...) end,
22 | --write = function(_,...) cgilua.print(...) end
23 | puts = function(_,...) remy.print(...) end,
24 | write = function(_,...) remy.print(...) end
25 | }
26 |
27 | local M = {
28 | mode = "cgilua",
29 | request = request
30 | }
31 |
32 | function M.init()
33 | local r = request
34 | local query = cgilua.servervariable("QUERY_STRING")
35 | local port = cgilua.servervariable("SERVER_PORT")
36 | local server_name = cgilua.servervariable("SERVER_NAME")
37 | local path_info = M.getpathinfo()
38 | apache2.version = cgilua.servervariable("SERVER_SOFTWARE")
39 | r = remy.loadrequestrec(r)
40 | r.ap_auth_type = cgilua.servervariable("AUTH_TYPE")
41 | if query ~= nil and query ~= '' then
42 | r.args = query
43 | end
44 | r.banner = apache2.version
45 | r.canonical_filename = cgilua.script_path
46 | r.content_type = "text/html" -- CGILua needs a default content_type
47 | r.context_document_root = cgilua.script_pdir
48 | r.document_root = cgilua.script_pdir
49 | r.filename = cgilua.script_path
50 | r.hostname = server_name
51 | r.method = cgilua.servervariable("REQUEST_METHOD")
52 | r.path_info = path_info
53 | if port ~= nil then
54 | r.port = tonumber(port)
55 | if r.port == 443 then
56 | r.is_https = true
57 | end
58 | end
59 | r.protocol = cgilua.servervariable("SERVER_PROTOCOL")
60 | r.range = cgilua.servervariable("HTTP_RANGE")
61 | r.server_name = server_name
62 | r.started = os.time()
63 | r.the_request = r.method..' '..M.getunparseduri()..' '..r.protocol
64 | r.unparsed_uri = M.getunparseduri()
65 | r.uri = path_info
66 | r.user = cgilua.servervariable("REMOTE_USER")
67 | r.useragent_ip = cgilua.servervariable("REMOTE_ADDR")
68 | end
69 |
70 | function M.getpathinfo()
71 | local p = cgilua.servervariable("PATH_INFO")
72 | --Xavante compatibility fix (Etiene)
73 | if cgilua.servervariable("SERVER_SOFTWARE"):match('^Xavante')then
74 | p = cgilua.urlpath
75 | end
76 | if p == nil then
77 | p = cgilua.servervariable("SCRIPT_NAME")
78 | end
79 | return p
80 | end
81 |
82 | function M.getunparseduri()
83 | local uri = M.getpathinfo()
84 | local query = cgilua.servervariable("QUERY_STRING")
85 | if query ~= nil and query ~= '' then
86 | uri = uri..'?'..query
87 | end
88 | return uri
89 | end
90 |
91 | function M.contentheader(content_type)
92 | local r = request
93 | r.content_type = content_type
94 | end
95 |
96 | -- TODO: better handle the return code
97 | function M.finish(code)
98 | local r = request
99 |
100 | -- Handle page redirect
101 | local location = r.headers_out['Location']
102 | if location ~= nil and r.status == 302 then
103 | if location:match('^https?://') then
104 | cgilua.redirect(location)
105 | else
106 | -- CGILua needs a full URL
107 | if r.is_https then
108 | location = 'https://'..cgilua.servervariable("HTTP_HOST")..location
109 | else
110 | location = 'http://'..cgilua.servervariable("HTTP_HOST")..location
111 | end
112 | cgilua.redirect(location)
113 | end
114 | end
115 |
116 | -- add support for custom headers
117 | for header, value in pairs(r.headers_out) do
118 | -- skip Location header.
119 | if not header == "Location" then
120 | cgilua.header(header, value)
121 | end
122 | end
123 |
124 | -- Prints the response text (if any)
125 | if r.content_type == "text/html" then
126 | cgilua.htmlheader()
127 | else
128 | local header_sep = "/"
129 | local header_type = remy.splitstring(r.content_type,header_sep)[1]
130 | local header_subtype = remy.splitstring(r.content_type,header_sep)[2]
131 | cgilua.contentheader(header_type,header_subtype)
132 | end
133 |
134 | if remy.responsetext ~= nil then
135 | cgilua.print(remy.responsetext)
136 | end
137 | end
138 |
139 | return M
140 |
--------------------------------------------------------------------------------
/src/remy/file_obj.lua:
--------------------------------------------------------------------------------
1 | -- Remy - File object
2 | -- By m1cr0man
3 | -- License: MIT
4 |
5 | local File = {
6 | name = nil,
7 | path = nil,
8 | content_type = nil,
9 | move_to = nil,
10 | handle = nil
11 | }
12 |
13 | -- Efficiency function for saving the temp file
14 | -- somewhere else instead of reading & writing it
15 | function File:move_to(path)
16 | local success, err = os.rename(self.path, path)
17 | if not success then return success, err end
18 | self.path = path
19 | return true
20 | end
21 |
22 | function File.new(name, content_type)
23 | local new_file = {
24 | name = name,
25 | path = os.tmpname(),
26 | content_type = content_type
27 | }
28 |
29 | -- We will want a handle to write to
30 | -- when we create the file
31 | new_file.handle = io.open(new_file.path, "w")
32 | setmetatable(new_file, {__index = File})
33 |
34 | return new_file
35 | end
36 |
37 | return File
38 |
--------------------------------------------------------------------------------
/src/remy/lwan.lua:
--------------------------------------------------------------------------------
1 | -- Remy - Lwan compatibility
2 | -- Copyright (c) 2014 Leandro Pereira
3 | -- License: MIT
4 |
5 | local base64 = require "base64"
6 | local remy = require "remy"
7 |
8 | local lwan = {}
9 | lwan.query_param_table = function(req)
10 | return setmetatable({}, {
11 | __index = function(tbl, key)
12 | return req.native_request:query_param(key)
13 | end
14 | })
15 | end
16 |
17 | lwan.post_param_table = function(req)
18 | return setmetatable({}, {
19 | __index = function (tbl, key)
20 | return req.native_request:post_param(key)
21 | end
22 | })
23 | end
24 |
25 | local request = {
26 | -- ENCODING/DECODING FUNCTIONS
27 | base64_decode = function(_,...) return base64.decode(...) end,
28 | base64_encode = function(_,...) return base64.encode(...) end,
29 | -- REQUEST PARSING FUNCTIONS
30 | parseargs = function(...) return lwan.query_param_table(...), {} end,
31 | parsebody = function(...) return lwan.post_param_table(...), {} end,
32 | requestbody = function(_,...) return nil end,
33 | -- REQUEST RESPONSE FUNCTIONS
34 | puts = function(req, ...) req.native_request:say(...) end,
35 | write = function(req, ...) req.native_request:say(...) end,
36 | }
37 |
38 | local M = {
39 | mode = "lwan",
40 | request = request
41 | }
42 |
43 | function M.init(native_request)
44 | -- FIXME: Most of these constants are hardcoded to possibly wrong values.
45 | local filename = "./index.lua"
46 | local uri = "/index.lua"
47 | apache2.version = M.mode
48 |
49 | local r = request
50 | r = remy.loadrequestrec(r)
51 |
52 | r.native_request = native_request
53 |
54 | r.headers_out = {}
55 | r.headers_in = {}
56 |
57 | r.banner = M.mode
58 | r.server_name = r.hostname
59 |
60 | r.started = 0
61 | r.args = {}
62 |
63 | r.canonical_filename = filename
64 | r.context_document_root = "/"
65 | r.document_root = "/"
66 | r.filename = filename
67 |
68 | r.hostname = "127.0.0.1"
69 | r.useragent_ip = "127.0.0.1"
70 | r.port = "8080"
71 |
72 | r.range = nil
73 |
74 | r.method = "GET"
75 | r.protocol = "HTTP/1.1"
76 | r.the_request = "GET / HTTP/1.1"
77 |
78 | r.unparsed_uri = uri
79 | r.uri = uri
80 |
81 | -- This is handled by Lwan itself and is not exposed to the API
82 | r.user = nil
83 | r.basic_auth_pw = nil
84 | end
85 |
86 | function M.contentheader(content_type)
87 | request.content_type = content_type
88 | end
89 |
90 | function M.finish(code)
91 | request.native_request:set_headers(request.headers_out)
92 | end
93 |
94 | return M
95 |
--------------------------------------------------------------------------------
/src/remy/mod_magnet.lua:
--------------------------------------------------------------------------------
1 | -- Remy - Lighttpd's mod_magnet compatibility
2 | -- Copyright (c) 2015 Felipe Daragon
3 | -- License: MIT
4 |
5 | local base64 = require "base64"
6 | local remy = require "remy"
7 |
8 | local utils = {}
9 |
10 | utils.parseargs = function()
11 | local get = {}
12 | if (lighty.env["uri.query"]) then
13 | -- split the query-string
14 | for k, v in string.gmatch(lighty.env["uri.query"], "(%w+)=(.+)") do
15 | get[k] = v
16 | end
17 | end
18 | return get
19 | end
20 |
21 | -- TODO: implement all functions from mod_lua's request_rec
22 | local request = {
23 | -- ENCODING/DECODING FUNCTIONS
24 | base64_decode = function(_,...) return base64.decode(...) end,
25 | base64_encode = function(_,...) return base64.encode(...) end,
26 | sha1 = function(_,...) return remy.sha1(...) end,
27 | -- REQUEST PARSING FUNCTIONS
28 | parseargs = function(_) return utils.parseargs(), {} end,
29 | parsebody = function(_) return {}, {} end,
30 | -- REQUEST RESPONSE FUNCTIONS
31 | puts = function(_,...) remy.print(...) end,
32 | write = function(_,...) remy.print(...) end
33 | }
34 |
35 | local M = {
36 | mode = "lighttpd",
37 | request = request
38 | }
39 |
40 | function M.init()
41 | local r = request
42 | local filename = lighty.env["physical.path"]
43 | local uri = lighty.env["uri.path"]
44 | apache2.version = M.mode
45 | r = remy.loadrequestrec(r)
46 | r.headers_out = lighty.header
47 | r.headers_in = lighty.request
48 | local auth = base64.decode((r.headers_in["Authorization"] or ""):sub(7))
49 | local _,_,user,pass = auth:find("([^:]+)%:([^:]+)")
50 | r.method = lighty.env["request.method"]
51 | r.args = lighty.env["uri.query"]
52 | --r.args = remy.splitstring(lighty.env['request.uri'],'?')
53 | r.banner = M.mode
54 | r.basic_auth_pw = pass
55 | r.canonical_filename = filename
56 | r.context_document_root = lighty.env["physical.doc-root"]
57 | r.document_root = lighty.env["physical.doc-root"]
58 | r.filename = filename
59 | r.hostname = lighty.env["uri.authority"]
60 | --r.port = ???
61 | r.protocol = lighty.env["request.protocol"]
62 | r.range = r.headers_in["Range"]
63 | r.server_name = r.hostname
64 | r.the_request = r.method.." "..lighty.env["request.uri"].." "..r.protocol
65 | r.unparsed_uri = lighty.env["request.orig-uri"]
66 | r.uri = uri
67 | r.user = user
68 | r.useragent_ip = lighty.env['request.remote-ip']
69 | end
70 |
71 | function M.contentheader(content_type)
72 | request.content_type = content_type
73 | lighty.header["Content-Type"] = content_type
74 | end
75 |
76 | function M.finish(code)
77 | lighty.content = { remy.responsetext }
78 | end
79 |
80 | return M
81 |
--------------------------------------------------------------------------------
/src/remy/mod_plua.lua:
--------------------------------------------------------------------------------
1 | -- Remy - mod_pLua compatibility
2 | -- Copyright (c) 2014 Felipe Daragon
3 | -- License: MIT
4 |
5 | local remy = require "remy"
6 |
7 | -- TODO: implement all functions from mod_lua's request_rec
8 | local request = {
9 | -- ENCODING/DECODING FUNCTIONS
10 | base64_decode = function(_,...) return string.decode64(...) end,
11 | base64_encode = function(_,...) return string.encode64(...) end,
12 | md5 = function(_,...) return string.md5(...) end,
13 | -- REQUEST PARSING FUNCTIONS
14 | parseargs = function(_) return parseGet(), {} end,
15 | parsebody = function(_) return parsePost(), {} end,
16 | requestbody = function(_,...) return getRequestBody(...) end,
17 | -- REQUEST RESPONSE FUNCTIONS
18 | sendfile = function(_,...) return file.send(...) end,
19 | puts = function(_,...) echo(...) end,
20 | write = function(_,...) echo(...) end
21 | }
22 |
23 | local M = {
24 | mode = "mod_plua",
25 | request = request
26 | }
27 |
28 | function M.init()
29 | local env = getEnv()
30 | local r = request
31 | local auth = string.decode64((env["Authorization"] or ""):sub(7))
32 | local _,_,user,pass = auth:find("([^:]+)%:([^:]+)")
33 | local filename = env["Filename"]
34 | apache2.version = env["Server-Banner"]
35 | r = remy.loadrequestrec(r)
36 | r.method = env["Request-Method"]
37 | r.args = remy.splitstring(env["Unparsed-URI"],'?')
38 | r.banner = apache2.version
39 | r.basic_auth_pw = pass
40 | r.canonical_filename = filename
41 | r.context_document_root = env["Working-Directory"]
42 | r.document_root = r.context_document_root
43 | r.filename = filename
44 | r.hostname = env["Host"]
45 | r.path_info = env["Path-Info"]
46 | r.range = env["Range"]
47 | r.server_name = r.hostname
48 | r.the_request = env["Request"]
49 | r.unparsed_uri = env["Unparsed-URI"]
50 | r.uri = env["URI"]
51 | r.user = user
52 | r.useragent_ip = env["Remote-Address"]
53 | end
54 |
55 | function M.contentheader(content_type)
56 | request.content_type = content_type
57 | setContentType(content_type)
58 | end
59 |
60 | function M.finish(code)
61 | -- mod_pLua uses text/html as default content type
62 | if request.content_type ~= nil then
63 | setContentType(request.content_type)
64 | end
65 | setReturnCode(code)
66 | end
67 |
68 | return M
69 |
--------------------------------------------------------------------------------
/src/remy/nginx.lua:
--------------------------------------------------------------------------------
1 | -- Remy - Nginx compatibility
2 | -- Copyright (c) 2014 Felipe Daragon
3 | -- License: MIT
4 |
5 | local ngx = require "ngx"
6 | local remy = require "remy"
7 | local file = require "remy.file_obj"
8 | local output_buffer = {}
9 | local files = {}
10 |
11 | -- Settings for streaming multipart/form-data
12 | local chunk_size = 16384 -- Bytes
13 | local socket_timeout = 5000 -- Milliseconds
14 |
15 | -- Buffer output to allow changing the header up until M.finish is called
16 | -- See https://github.com/openresty/lua-nginx-module#ngxheaderheader
17 | local function buffered_print(_, ...)
18 | table.insert(output_buffer, {...})
19 | end
20 |
21 | -- Manually parses the request body to handle files
22 | -- Buffers any files to disk
23 | local function load_req_body()
24 |
25 | -- Nginx lower cases all the keys
26 | local header = ngx.req.get_headers()
27 |
28 | -- Check the header for a boundary
29 | local boundary = (header["content-type"] or ""):match("boundary=(.+)$")
30 |
31 | -- If there's no boundary, don't worry about using a socket
32 | -- Parse the body the normal way
33 | if not boundary then
34 | ngx.req.read_body()
35 | return ngx.req.get_post_args(), {}
36 | end
37 |
38 | -- Otherwise try to open a socket
39 | local socket, err = ngx.req.socket()
40 | if not socket then
41 | ngx.say("Failed to open socket: ", err)
42 | ngx.exit(500)
43 | end
44 | socket:settimeout(socket_timeout)
45 |
46 | -- CRLF is the standard for HTTP requests
47 | local stream, err = socket:receiveuntil("\r\n--" .. boundary)
48 | if not stream then
49 | ngx.say("Failed to open stream: ", err)
50 | ngx.exit(500)
51 | end
52 |
53 | local POST = {}
54 |
55 | -- Read the data
56 | while true do
57 |
58 | -- Read a line
59 | local header = socket:receive()
60 | if not header then
61 | break
62 | end
63 | local key = header:match("name=\"(.-)\"")
64 |
65 | -- If this is just a boundary, it will ignore it
66 | if key then
67 | local value = ""
68 |
69 | -- Check if it's a file
70 | local filename = header:match("filename=\"(.-)\"")
71 | if filename then
72 | local content_type = socket:receive():match("Content%-Type: ?(.+)$")
73 |
74 | -- Build the file object
75 | value = file.new(filename, content_type)
76 | end
77 |
78 | -- Stream the data, skipping the blank line
79 | socket:receive()
80 | while true do
81 | local data, err = stream(chunk_size)
82 | if err then
83 | ngx.say("Failed to read data stream: ", err)
84 | ngx.exit(500)
85 | elseif not data then
86 | break
87 | end
88 |
89 | -- Write to file, or save to variable
90 | if type(value) == "table" then
91 |
92 | -- Convert the line endings respective to host OS
93 | value.handle:write(value.path:match("/") and data:gsub("\r\n", "\n") or data)
94 | else
95 | value = value .. data
96 | end
97 | end
98 |
99 | -- Close the file
100 | if type(value) == "table" then
101 | value.handle:close()
102 | value.handle = nil
103 | end
104 |
105 | -- Append POST & files list if necessary
106 | -- Mimics behaviour of ngx.req.get_post_args()
107 | if POST[key] and (type(POST[key]) ~= "table" or POST[key].name) then
108 | POST[key] = {POST[key], value}
109 | elseif type(POST[key]) == "table" then
110 | table.insert(POST[key], value)
111 | else
112 | POST[key] = value
113 | end
114 | if type(value) == "table" then table.insert(files, value) end
115 | end
116 | end
117 |
118 | return POST, {}
119 | end
120 |
121 | -- TODO: implement all functions from mod_lua's request_rec
122 | local request = {
123 | -- ENCODING/DECODING FUNCTIONS
124 | base64_decode = function(_,...) return ngx.decode_base64(...) end,
125 | base64_encode = function(_,...) return ngx.encode_base64(...) end,
126 | escape = function(_,...) return ngx.escape_uri(...) end,
127 | unescape = function(_,...) return ngx.unescape_uri(...) end,
128 | md5 = function(_,...) return ngx.md5(...) end,
129 | -- REQUEST PARSING FUNCTIONS
130 | parseargs = function(_) return ngx.req.get_uri_args(), {} end,
131 | parsebody = load_req_body,
132 | requestbody = function(_,...)
133 | -- Make sure the request body has been read
134 | ngx.req.read_body()
135 | return ngx.req.get_body_data()
136 | end,
137 | -- REQUEST RESPONSE FUNCTIONS
138 | puts = buffered_print,
139 | write = buffered_print
140 | }
141 |
142 | local M = {
143 | mode = "nginx",
144 | request = request
145 | }
146 |
147 | function M.init()
148 | local r = request
149 | local filename = debug.getinfo(4).source
150 | local uri = ngx.var.uri
151 | apache2.version = M.mode.."/"..ngx.var.nginx_version
152 | r = remy.loadrequestrec(r)
153 | r.headers_out = ngx.resp.get_headers()
154 | r.headers_in = ngx.req.get_headers()
155 | local auth = ngx.decode_base64((r.headers_in["Authorization"] or ""):sub(7))
156 | local _,_,user,pass = auth:find("([^:]+)%:([^:]+)")
157 | r.started = ngx.req.start_time
158 | r.method = ngx.var.request_method
159 | r.args = remy.splitstring(ngx.var.request_uri,'?')
160 | r.banner = M.mode.."/"..ngx.var.nginx_version
161 | r.basic_auth_pw = pass
162 | r.canonical_filename = filename
163 | r.context_document_root = ngx.var.document_root
164 | r.document_root = r.context_document_root
165 | r.filename = filename
166 | r.hostname = ngx.var.hostname
167 | r.port = ngx.var.server_port
168 | r.protocol = ngx.var.server_protocol
169 | r.range = r.headers_in["Range"]
170 | r.server_name = r.hostname
171 | r.the_request = r.method.." "..ngx.var.request_uri.." "..r.protocol
172 | r.unparsed_uri = uri
173 | r.uri = uri
174 | r.user = user
175 | r.useragent_ip = ngx.var.remote_addr
176 | end
177 |
178 | function M.contentheader(content_type)
179 | request.content_type = content_type
180 | ngx.header.content_type = content_type
181 | end
182 |
183 | function M.finish(code)
184 | -- Set status code
185 | ngx.status = code
186 |
187 | -- Set the headers
188 | if request.content_type and not ngx.header.content_type then
189 | ngx.header["Content-Type"] = request.content_type
190 | end
191 | for k, v in pairs(request.headers_out) do
192 | ngx.header[k] = v
193 | end
194 |
195 | -- Print the data & Clear the buffer
196 | ngx.print(output_buffer)
197 | output_buffer = {}
198 |
199 | -- Delete temporary files
200 | for _, file in pairs(files) do
201 | os.remove(file.path)
202 | end
203 | files = {}
204 |
205 | -- TODO: translate request_rec's exit code and call ngx.exit(code)
206 | end
207 |
208 | return M
209 |
--------------------------------------------------------------------------------