├── .drone.yml
├── .gitignore
├── .rancher-pipeline.yml
├── README.md
├── index.lua
├── init.lua
├── libs
├── Response.lua
├── ansicolors.lua
├── cookie.lua
├── etlua.lua
├── fse.lua
├── helpers.lua
├── mime.lua
├── resty-template.lua
└── template
│ ├── 403.html
│ ├── 404.html
│ ├── 500.html
│ └── directory.html
├── main.lua
├── package.json
├── package.lua
└── test
├── init.lua
└── views
├── footer.html
├── header.html
├── index.elua
└── index.html
/.drone.yml:
--------------------------------------------------------------------------------
1 | kind: pipeline
2 | name: default
3 |
4 | steps:
5 | - name: Install Luvit
6 | image: ubuntu
7 | commands:
8 | - apt install curl
9 | - echo "Hello world!"
10 | - curl -L https://github.com/luvit/lit/raw/master/get-lit.sh | sh
11 | - mv luvit /usr/local/bin && mv lit /usr/local/bin
12 | - luvit test
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /deps/
2 | mooncake
3 | test/cache.html
4 |
--------------------------------------------------------------------------------
/.rancher-pipeline.yml:
--------------------------------------------------------------------------------
1 | stages:
2 | - name: compile
3 | steps:
4 | - runScriptConfig:
5 | image: ubuntu
6 | shellScript: |-
7 | apt-get update
8 | apt-get install curl -y
9 | curl -L https://github.com/luvit/lit/raw/master/get-lit.sh | sh
10 | ./luvit test
11 | timeout: 60
12 | notification: {}
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mooncake
2 |
3 | [](https://gitter.im/cyrilis/luvit-mooncake?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4 |
5 | **A web framework powered by luvit.**
6 |
7 | Mooncake is a web framework powered by [luvit](https://luvit.io/). inspired by [expressjs](http://expressjs.com/) for nodejs.
8 |
9 | ## Install
10 |
11 | Install with [lit](https://luvit.io/lit.html)
12 |
13 | ```bash
14 | lit install cyrilis/mooncake
15 | ```
16 |
17 | ## Usage
18 |
19 | ### Getting start
20 |
21 | ```lua
22 | local MoonCake = require("mooncake")
23 | local server = MoonCake:new()
24 |
25 | -- route your application
26 | server:get("/", function(req, res)
27 | local content = "
Hello world from MoonCake
"
28 | res:send(content, 200)
29 | end)
30 |
31 | server:start(8080)
32 | ```
33 |
34 | ### Server
35 |
36 | - #### create server
37 |
38 | - create a http server:
39 |
40 | ```lua
41 | Mooncake = require("mooncake")
42 | local sever = Mooncake:new()
43 | ```
44 |
45 | - or create a https server:
46 |
47 | ```lua
48 | Mooncake = require("mooncake")
49 | local server = Mooncake:new({
50 | isHttps = true
51 | keyPath = "/path/to/key"
52 | })
53 | -- will read ssl key and cert file at "/path/to/key/key.pem" and "path/to/key/cert.pem"
54 | ```
55 |
56 | - #### server:use(func)
57 |
58 | Use the given middleware function,
59 |
60 | Example:
61 |
62 | ```lua
63 | server:use(function(req, res, next)
64 | res:locals({first = true})
65 | next()
66 | end)
67 | ```
68 |
69 | - #### server:static(fileDir, options)
70 |
71 | - fileDir: string, directory path, **required**, eg: "public/files"
72 |
73 | - options:
74 |
75 | - root: string, mount path, eg: `"/static"`
76 |
77 | - maxAge: number, cache option for maxAge, default is `15552000` (half a year).
78 |
79 | eg:
80 |
81 | ```lua
82 | server:static(path.resolve(module.dir, "../public/"), {
83 | root = "/static/",
84 | maxAge = 31536000 -- one year
85 | })
86 | ```
87 |
88 | - #### server:start(port [, address])
89 |
90 | - port: number, optional, default to 8080
91 | - address: string, optional, default to "127.0.0.1"
92 |
93 | Start your server:
94 |
95 | ```lua
96 | server:start(8080)
97 | ```
98 |
99 | ### Route
100 |
101 | #### You can route your app easily with below methods:
102 |
103 | `:get` `:post` `:put` `:delete` `:all`
104 |
105 | Example:
106 |
107 | ```lua
108 | -- :all
109 | server:all("/hello", function(req, res, next)
110 | res:send("HELLO!")
111 | end)
112 |
113 | -- :get
114 | server:get("/admin/:page", function(req, res,next)
115 | --- ...
116 | -- login check func
117 | if isLogin then
118 | next()
119 | else
120 | res:status(403):redirect("/login")
121 | end
122 | end)
123 |
124 | server:get("/admin/dashboard", function(req, res, next)
125 | --- ...
126 | res:render("../views/dashbaord", data)
127 | end)
128 |
129 | -- :post
130 | server:post("/posts", function(req, res)
131 | p(req.body) -- print post data;
132 | ... -- create a new post
133 | end)
134 |
135 | -- :put
136 | server:put("/posts/:id", function(req, res)
137 | p(req.params.id) -- print id params in request
138 | ... -- update post with id = `req.params.id`
139 | end)
140 |
141 | -- :delete
142 | server:delete("/posts/:id", function(req, res)
143 | ... -- delete post with id = "req.params.id"
144 | end)
145 | ```
146 |
147 | Or you can use `server:route()` to match route if your use custom method:
148 |
149 | ```lua
150 | server:route("custom-method", "/posts/:id", function(req, res)
151 | ... -- delete post with id = "req.params.id"
152 | end)
153 | ```
154 |
155 | ### Request
156 |
157 | - #### req.params
158 |
159 | This property is an array containing properties mapped to the named route “parameters”. For example if you have the route /user/:name, then the “name” property is available to you as req.params.name. This object defaults to {}.
160 |
161 | eg:
162 |
163 | ```lua
164 | -- GET "/user/cyrilis"
165 | server:put("/user/:name", function(req, res)
166 | p(req.params.name) -- output user name `cyrilis`
167 | end)
168 | ```
169 |
170 | - #### req.query
171 |
172 | This property is an object containing the parsed query-string, defaulting to {}.
173 |
174 | ```lua
175 | -- GET /search?q=tobi+ferret
176 | req.query.q
177 | -- => "tobi ferret"
178 |
179 | -- GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
180 | req.query.order
181 | -- => "desc"
182 |
183 | req.query.shoe.color
184 | -- => "blue"
185 |
186 | req.query.shoe.type
187 | -- => "converse"
188 | ```
189 |
190 | - #### req.body
191 |
192 | This property is an object containing the parsed request body. This property defaults to {}.
193 |
194 | ```lua
195 | -- POST user[name]=tobi&user[email]=tobi@learnboost.com
196 | req.body.user.name
197 | -- => "tobi"
198 |
199 | req.body.user.email
200 | -- => "tobi@learnboost.com"
201 |
202 | // POST { "name": "tobi" }
203 | req.body.name
204 | -- => "tobi"
205 | ```
206 |
207 | You can get post data via req.body, then save to db:
208 |
209 | ```lua
210 | server:post("/posts/new", function(req,res)
211 | if req.body.title and req.body.content then
212 | print("new post")
213 | -- Save to DB:
214 | -- DB.save("post", {title = req.body.title, content = req.body.content})
215 | res.redirect("/posts")
216 | end
217 | end)
218 | ```
219 |
220 | - #### req.method
221 |
222 | return request method, eg: `"GET"`
223 |
224 | - #### req.cookie
225 |
226 | This object return parsed cookies sent by the user-agent. If no cookies are sent, it defaults to {}.
227 |
228 | ```lua
229 | -- Cookie: name=cyrilis
230 | req.cookies.name
231 | -- => "cyrilis"
232 | ```
233 |
234 | - #### req.headers
235 |
236 | return headers of request.
237 |
238 | - #### req.url
239 |
240 | return the url of request.
241 |
242 | ### Response
243 |
244 | `ServerResponse` has several extra methods:
245 |
246 | - #### res:send (data, code, header)
247 |
248 | This method performs a myriad of useful tasks for simple non-streaming responses such as automatically assigning the Content-Length unless previously defined and providing automatic `HEAD` and `HTTP` cache freshness support.
249 |
250 | - `data`: string, content string to render, **required**, eg: "Hello World!"
251 | - `code`: number, status code, eg: 200
252 | - `header`: table, Custom header, eg: `{['Content-Type'] = 'text/plain', ["Content-Length"] = 1000}`
253 |
254 | Example:
255 |
256 | ```lua
257 | server:get("/abc", function(req, res)
258 | res:send("")
259 | end)
260 | ```
261 |
262 | - #### res:render(tpl, data)
263 |
264 | There are two render engines build in, default is [lua-resty-template](https://github.com/bungle/lua-resty-template/), and the other one is [etlua](https://github.com/leafo/etlua), if you prefer `etlua` as your default render engine, you can add below code in your project:
265 |
266 | ```lua
267 | local env = require("env")
268 | env.set("viewEngine", "etlua")
269 | ```
270 |
271 | — Or just use .elua as template file extension name
272 |
273 | ```lua
274 | res:render("./views/index.elua", {title = "Hello world!"})
275 | ```
276 |
277 | Render engine accept the first argument as template file path or template content, the second arguments as render data. you can visit [lua-resty-template ](https://github.com/bungle/lua-resty-template/) and [etlua](https://github.com/leafo/etlua) project home page for template syntax.
278 |
279 | - `tpl`: string, path to template file, **required**,eg: `"views/post.html"`
280 | - `data`: table, render data, **required**, eg: `{["status" = "success"]}`
281 |
282 | Example of [lua-resty-template ](https://github.com/bungle/lua-resty-template/):
283 |
284 | ```lua
285 | server:get("/posts", function(q,s)
286 | -- get post list, render template.
287 | s:render("./view/post-list.html", {posts = DB.find("posts")})
288 | end)
289 | ```
290 |
291 | `./view/post-list.html`
292 |
293 | ```html
294 |
295 | {% for _, post in ipairs(posts) do %}
296 | - {{post.title}}
297 | {% end %}
298 |
299 | ```
300 |
301 | Example of [etlua](https://github.com/leafo/etlua)
302 |
303 | ```lua
304 | server:get("/posts", function(q,s)
305 | -- get post list, render template.
306 | s:render("./view/post-list.elua", {posts = DB.find("posts")})
307 | end)
308 | ```
309 |
310 | `./view/post-list.elua`
311 |
312 | ```ejs
313 |
314 | <% for _, name in ipairs(names) do %>
315 | - <%= name %>
316 | <% end %>
317 |
318 | ```
319 |
320 | #### Render Result:
321 |
322 | ```html
323 |
324 | - Post 1
325 | - Post 2
326 | - Post 3
327 |
328 | ```
329 |
330 |
331 | - #### res:redirect(url, code)
332 |
333 | Redirect to the given url with optional status code defaulting to 302 “Found”.
334 |
335 | - `url`: string, url for redirect, **required**, eg: "/404"
336 | - `code`: number, status code, eg: 200
337 |
338 | Example:
339 |
340 | ```lua
341 | server:get("/post-not-exist", function(req, res)
342 | -- if user not login then
343 | res:redirect("/page-not-found", 302)
344 | end)
345 | server:get("/page-not-found", function(req, res)
346 | res:render("404.html", 404)
347 | end)
348 | ```
349 |
350 | - #### res:status(statusCode)
351 |
352 | Chainable alias of luvit `res.statusCode=`.
353 |
354 | - `statusCode`: number, required, status code, eg: 403
355 |
356 | Example:
357 |
358 | ```lua
359 | server:get("/page-not-exist", function(req, res)
360 | res:status(404):render("404.html")
361 | end)
362 | ```
363 |
364 | - #### res:sendFile(filePath, headers)
365 |
366 | - `filePath`: string, **required**, send file directly. eg: `res:sendFile("files/preview.pdf")`
367 | - `headers`: table, Custom header, eg: `{['Content-Type'] = 'text/plain', ["Content-Length"] = 1000}`
368 |
369 | Example:
370 |
371 | ```lua
372 | server:get("/files/:file", function(req,res)
373 | res:sendFile("/public/files/".. req.params.file)
374 | end)
375 | ```
376 |
377 | - #### res:json(obj, code, headers)
378 |
379 | Send a JSON response. When an Table is given mooncake will respond with the JSON representation:
380 |
381 | - `obj`: table, **required**, data for render. eg: `{["status"]="OK"}`
382 | - `code`: number, status code, eg: 200
383 | - `headers`: table, Custom header, eg: `{['Content-Type'] = 'text/plain', ["Content-Length"] = 1000}`
384 |
385 | Example:
386 |
387 | ```lua
388 | server:get("/api.json", function(req, res)
389 | posts = DB:find("*")
390 | res:json(posts, 200)
391 | end)
392 | ```
393 |
394 | - #### res:flash(type, flash)
395 |
396 | `flash` method like in rails.
397 |
398 | - `type`: string, required, flash type: eg: "success"
399 | - `flash`: string, required, flash content: eg: "You've successfully login, welcome back."
400 |
401 | - #### res:locals(data)
402 |
403 | Store data as local data for render.
404 |
405 | Example:
406 |
407 | ```lua
408 | server:use(function(req, res, next)
409 | if req.query.page ~= 1 then
410 | res:locals({isFirstPage = false})
411 | end
412 | next()
413 | end)
414 | server:get("/post", function()
415 | render("views/post-list.html")
416 | end)
417 | ```
418 |
419 | ## Contribute
420 |
421 | Feel free to open issues and submit pull request :)
422 |
423 | ## Licence
424 |
425 | (The MIT License)
426 |
427 | Copyright (c) 2015 Cyril Hou
428 |
429 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
430 |
431 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
432 |
433 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
434 |
--------------------------------------------------------------------------------
/index.lua:
--------------------------------------------------------------------------------
1 | require("./libs/Response")
2 | local http = require("http")
3 | local https = require("https")
4 | local path = require("path")
5 | local fs = require('fs')
6 | local mime = require('./libs/mime')
7 | local helpers = require('./libs/helpers')
8 | local querystring = require('querystring')
9 | local JSON = require("json")
10 | local Cookie = require("./libs/cookie")
11 | require("./libs/ansicolors")
12 |
13 | d((" Hello "):bluebg(), (" World "):redbg(), (" from MoonCake "):yellowbg(), (" ! "):greenbg())
14 |
15 | local Emitter = require("core").Emitter
16 |
17 | local _routes = {}
18 |
19 | local getQueryFromUrl = function(url)
20 | local params = string.match(url, "?(.*)") or ""
21 | return querystring.parse(params)
22 | end
23 |
24 | local MoonCake = Emitter:extend()
25 |
26 | function MoonCake:initialize(options)
27 | options = options or {}
28 | self.options = options
29 | self.notAuthorizedRequest = function(req, res, next)
30 | res:status(403):render("./libs/template/403.html")
31 | end
32 | self.indexDirectory = function(filePath, routePath, req, res)
33 | local fileList = fs.readdirSync(filePath)
34 | local parentPathList = helpers.split2(routePath, "/")
35 | if routePath:sub(#routePath) == "/" then
36 | table.remove(parentPathList)
37 | end
38 | table.remove(parentPathList)
39 | local parentPath = table.concat(parentPathList, "/")
40 | if parentPath == "" then parentPath = "/" end
41 | local data = {parentPath = parentPath, currentPath = routePath, files = {}}
42 | for idx, v in pairs(fileList) do
43 | local fstat = fs.statSync(path.join(filePath, v))
44 | local vfilePath = path.join(routePath, v)
45 | if fstat.type ~= "file" then
46 | vfilePath = vfilePath .. "/"
47 | end
48 | local lastModifiedTime = os.date("%c", fstat.mtime.sec)
49 | data.files[idx] = {
50 | name = v,
51 | path = vfilePath,
52 | lastModified = lastModifiedTime,
53 | type = fstat.type,
54 | size = fstat.size
55 | }
56 | end
57 | res:render(path.resolve(module.dir, "./libs/template/directory.html"), data)
58 | end
59 | self.isHttps = false
60 | if self.options.isHttps == true then
61 | self.isHttps = true
62 | self.keyPath = self.options.keyPath
63 | end
64 | return self
65 | end
66 |
67 | function MoonCake:listen (port)
68 | return self:start(port)
69 | end
70 |
71 | function MoonCake:start (port, host)
72 | host = host or "127.0.0.1"
73 | if port == nil then
74 | port = 8080
75 | end
76 | local fn = function(req, res)
77 | self:handleRequest(req, res)
78 | end
79 | if self.isHttps == true then
80 | local keyConfig = {
81 | key = fs:readFileSync(path.join(self.keyPath, "key.pem")),
82 | cert = fs:readFileSync(path.join(self.keyPath, "cert.pem"))
83 | }
84 | self.server = https.createServer(keyConfig, fn):listen(port, host)
85 | else
86 | --- Export server instance
87 | self.server = http.createServer(fn):listen(port, host)
88 | end
89 | d(("Moon"):redbg(),("Cake"):yellowbg()," Server Listening at http".. (self.isHttps and "s" or "") .."://".. host .. ":" .. tostring(port) .. "/")
90 | end
91 |
92 | function MoonCake:handleRequest(req, res)
93 | local url = req.url
94 | local method = string.lower(req.method)
95 | res.req = req
96 | local querys = getQueryFromUrl(url)
97 | req.query = querys
98 | req.start_time = helpers.getTime()
99 | res:on("finish", function()
100 | helpers.log(req, res)
101 | end)
102 | if req.headers.cookie then
103 | local cookie = Cookie:parse(req.headers.cookie)
104 | req.cookie = cookie or {}
105 | else
106 | req.cookie = {}
107 | end
108 | if method ~= "get" then
109 | local body = ""
110 | local fileData = ""
111 | req:on("data", function(chunk)
112 | if req.headers['Content-Type'] then
113 | if string.find(req.headers['Content-Type'], "multipart/form-data", 1, true) then
114 | fileData = fileData..chunk
115 | else
116 | body = body..chunk
117 | end
118 | else
119 | body = body..chunk
120 | if #body > 0 then
121 | res:status(400):json({
122 | status = "failed",
123 | success = false,
124 | code = 400,
125 | message = "Request is not valid, 'Content-Type' should be specified in header if request body exist."
126 | })
127 | end
128 | end
129 | end)
130 | req:on("end", function()
131 |
132 | local contentType = req.headers['Content-Type']
133 | if contentType and string.find(contentType, "multipart/form-data", 1, true) then
134 | local boundary = string.match(fileData, "^([^\r?\n?]+)\n?\r?")
135 | local fileArray = helpers.split2(fileData,boundary)
136 | table.remove(fileArray)
137 | table.remove(fileArray, 1)
138 | req.files = {}
139 | req.body = {}
140 | for _, fileString in pairs(fileArray) do
141 | local header, headers = string.match(fileString, "^\r?\n(.-\r?\n\r?\n)"), {}
142 | local content = ""
143 | string.gsub(fileString, "^\r?\n(.-\r?\n\r?\n)(.*)", function(_,b)
144 | if b:sub(#b-1):find("\r?\n") then
145 | local _, n = b:sub(#b-1):find("\r?\n")
146 | content = b:sub(0,#b-n)
147 | end
148 | end)
149 | string.gsub(header, '%s?([^%:?%=?]+)%:?%s?%=?%"?([^%"?%;?%c?]+)%"?%;?%c?', function(k,v)
150 | headers[k] = v
151 | end)
152 | if headers["filename"] then
153 | local tempname = os.tmpname()
154 | fs.writeFileSync(tempname, content)
155 | req.files[headers["name"]] = {path = tempname, name = headers["filename"], ["Content-Type"] = headers["Content-Type"] }
156 | else
157 | req.body[headers["name"]] = content
158 | end
159 | end
160 | else
161 | local bodyObj
162 | if contentType then
163 | if req.headers["Content-Type"]:sub(1,16) == 'application/json' then
164 | -- is this request JSON?
165 | bodyObj = JSON.parse(body)
166 | elseif req.headers["Content-Type"]:sub(1, 33) == "application/x-www-form-urlencoded" then
167 | -- normal form
168 | bodyObj = querystring.parse(body)
169 | else
170 | -- content-type: text/xml
171 | bodyObj = body
172 | end
173 | else
174 | if #body > 0 then
175 | res:status(400):json({
176 | status = "failed",
177 | success = false,
178 | code = 400,
179 | message = "Bad Request, 'Content-Type' in request headers should be specified if request body exist."
180 | })
181 | end
182 | end
183 | req.body = bodyObj or {}
184 | if req.body._method then
185 | req._method = req.body._method:lower()
186 | end
187 | end
188 |
189 | req.body = req.body or {}
190 | req.files = req.files or {}
191 |
192 | self:execute(req, res)
193 | end)
194 | else
195 | req.body = {}
196 | self:execute(req, res)
197 | end
198 | end
199 |
200 | function MoonCake.notFound(req, res, err)
201 | if(err) then
202 | MoonCake.serverError(req, res, err)
203 | else
204 | p("404 - Not Found!")
205 | res:status(404):render("./libs/template/404.html")
206 | end
207 | end
208 |
209 | function MoonCake.serverError (req, res, err)
210 | d(("MoonCake: Server Error"):redbg():white())
211 | p(err)
212 | res:status(500):render("./libs/template/500.html")
213 | end
214 |
215 | function MoonCake:execute(req, res)
216 | function go (i, error, req, res)
217 | local success, err = pcall(function ()
218 | i = i or 1
219 | local next = function(error)
220 | if(error)then
221 | MoonCake.serverError(req, res, error)
222 | else
223 | if i < #_routes then
224 | return go(i + 1, error, req, res)
225 | else
226 | MoonCake.notFound(req, res)
227 | end
228 | end
229 | end
230 |
231 | return _routes[i](req, res, next)
232 | end)
233 | if not success then
234 | p(err)
235 | MoonCake.serverError(req, res, err)
236 | end
237 | end
238 | go(1, nil, req, res);
239 | end
240 |
241 | function MoonCake:use(fn)
242 | table.insert(_routes, fn)
243 | return self
244 | end
245 |
246 | function MoonCake:clear()
247 | _routes = {}
248 | end
249 |
250 | local quotepattern = '(['..("%^$().[]*+-?"):gsub("(.)", "%%%1")..'])'
251 |
252 | local function escape(str)
253 | return str:gsub(quotepattern, "%%%1")
254 | end
255 |
256 | local function compileRoute(route)
257 | local parts = {"^"}
258 | local names = {}
259 | for a, b, c, d in route:gmatch("([^:]*):([_%a][_%w]*)(:?)([^:]*)") do
260 | if #a > 0 then
261 | parts[#parts + 1] = escape(a)
262 | end
263 | if #c > 0 then
264 | parts[#parts + 1] = "(.*)"
265 | else
266 | parts[#parts + 1] = "([^/]*)"
267 | end
268 | names[#names + 1] = b
269 | if #d > 0 then
270 | parts[#parts + 1] = escape(d)
271 | end
272 | end
273 | if #parts == 1 then
274 | return function (string)
275 | if string == route then
276 | return {}
277 | else
278 | if route == string:gsub("%/$", "") then
279 | return {}
280 | else
281 | if route:gsub("%/$", "") == string then
282 | return {}
283 | end
284 | end
285 | end
286 | end
287 | end
288 |
289 | if #parts > 1 and not(parts[#parts]:match("%*%)")) then
290 | local lastComp = parts[#parts]
291 | if lastComp:sub(#lastComp) == "/" then
292 | lastComp = lastComp:sub(1, #lastComp - 1)
293 | end
294 | parts[#parts] = lastComp .. "%/?"
295 | end
296 |
297 | parts[#parts + 1] = "$"
298 | local pattern = table.concat(parts)
299 | return function (string)
300 | local matches = {string:match(pattern)}
301 | if #matches > 0 then
302 | local results = {}
303 | for i = 1, #matches do
304 | results[i] = matches[i]
305 | results[names[i]] = matches[i]
306 | end
307 | return results
308 | end
309 | end
310 | end
311 |
312 | function MoonCake:route(method, path, fn)
313 | local _path = path and compileRoute(path)
314 | self:use(function (req, res, next)
315 | if method:lower() ~= (req.method):lower() and method:lower() ~= "all" and method:lower() ~= (req._method or ""):lower() then
316 | return next()
317 | end
318 | if req._method and method:lower() ~= (req._method or ""):lower() then
319 | return next()
320 | end
321 | local params
322 | if _path then
323 | local pathname, query = req.url:match("^([^?]*)%??(.*)");
324 | params = _path(pathname)
325 | if not params then return next() end
326 | end
327 | req.params = params or {}
328 | return fn(req, res, next)
329 | end)
330 | return self
331 | end
332 |
333 | function MoonCake:get(path, fn)
334 | self:route("get", path, fn)
335 | end
336 | function MoonCake:post(path, fn)
337 | self:route("post", path, fn)
338 | end
339 | function MoonCake:put(path, fn)
340 | self:route("put", path, fn)
341 | end
342 | function MoonCake:delete(path, fn)
343 | self:route("delete", path, fn)
344 | end
345 | function MoonCake:patch(path, fn)
346 | self:route("patch", path, fn)
347 | end
348 | function MoonCake:all(path, fn)
349 | self:route("all", path, fn)
350 | end
351 |
352 | function MoonCake:static (fileDir, options)
353 | if type(options) == "string" then
354 | options = {
355 | root = options
356 | }
357 | end
358 |
359 | if type(options) == "number" then
360 | options = {
361 | maxAge = options
362 | }
363 | end
364 | options = options or {}
365 | options.root = options.root or "/"
366 | options.index = options.index or false
367 | print("Serving Directory:" .. fileDir)
368 | local headers = {}
369 | local maxAge = options.maxAge or options.age or 15552000 -- half a year
370 | headers["Cache-Control"] = "public, max-age=" .. tostring(maxAge)
371 | local routePath = path.join(options.root, ":file:")
372 | self:get(routePath, function(req, res, next)
373 | local trimdPath = req.params.file:match("([^?]*)(?*)(.*)")
374 | local filePath = path.resolve(fileDir, trimdPath)
375 | local trimedRoutePath = path.resolve(options.root, trimdPath)
376 | if fs.existsSync(filePath) then
377 | if fs.statSync(filePath).type == "file" then
378 | res:sendFile(filePath, headers)
379 | else
380 | if options.index then
381 | self.indexDirectory(filePath, trimedRoutePath, req, res)
382 | else
383 | self.notAuthorizedRequest(req, res, next)
384 | end
385 |
386 | end
387 | else
388 | next()
389 | end
390 | end)
391 | return self
392 | end
393 |
394 | return MoonCake
395 |
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | return require("./index.lua")
2 |
--------------------------------------------------------------------------------
/libs/Response.lua:
--------------------------------------------------------------------------------
1 | local ServerResponse = require("http").ServerResponse
2 | ServerResponse.flashData = {}
3 | local template = require("./resty-template")
4 | local etlua = require("./etlua")
5 | local path = require("path")
6 | local env = require("env")
7 | local fs = require("fs")
8 | local JSON = require("json")
9 | local Cookie = require("./cookie")
10 | local mime = require('./mime')
11 | local helpers = require('./helpers')
12 |
13 | local extend = function(obj, with_obj)
14 | for k, v in pairs(with_obj) do
15 | obj[k] = v
16 | end
17 | return obj
18 | end
19 |
20 | local function copy(a, b)
21 | a = a or {}
22 | b = b or {}
23 | local obj = {}
24 | for key, value in pairs(b) do
25 | obj[key] = value
26 | end
27 | for key, value in pairs(a) do
28 | obj[key] = value
29 | end
30 | return obj
31 | end
32 |
33 | function ServerResponse:send (data, code, header)
34 | if self._headerSent then
35 | return true
36 | end
37 | self:sendHead(code, header, data)
38 | if data then
39 | self:write(data)
40 | end
41 | self:finish()
42 | collectgarbage()
43 | end
44 |
45 | function ServerResponse:sendHead(code, header, data)
46 | if self._headerSent then
47 | p("------------------------------")
48 | p("Error", "Header Has Been Sent.")
49 | p("------------------------------")
50 | return true
51 | end
52 | self._headerSent = true
53 | code = code or self.statusCode or 200
54 | self:status(code)
55 | header = copy(copy(header, self.headers), {
56 | ["Connection"] = "keep-alive",
57 | ["Content-Type"] = "text/html; charset=utf-8",
58 | ["X-Served-By"] = "MoonCake",
59 | ["Content-Length"] = data and #data or 0
60 | })
61 | self:writeHead(self.statusCode, header)
62 | end
63 |
64 | function ServerResponse:setCookie(name, value, options)
65 | options = options or {}
66 | if type(options.httpOnly) == "nil" then
67 | options.httpOnly = true
68 | end
69 | local cookieStr = Cookie:serialize (name, value, options)
70 | self:setHeader("Set-Cookie", cookieStr)
71 | return self
72 | end
73 |
74 | function ServerResponse:deleteCookie(name)
75 | local options = {
76 | expires = 0,
77 | path = "/"
78 | }
79 | self:setCookie(name, "" , options)
80 | return self
81 | end
82 |
83 | ServerResponse.removeCookie = ServerResponse.deleteCookie
84 |
85 | function ServerResponse:render(tpl, data)
86 | local callerSource = debug.getinfo(2).source
87 |
88 | if callerSource:sub(1,1) == "@" then
89 | callerSource = callerSource:sub(2)
90 | elseif callerSource:sub(1, 7) == "bundle:" then
91 | callerSource = callerSource
92 | end
93 |
94 | local filePath = path.resolve(path.dirname(callerSource), tpl)
95 | local viewEngine = env.get("viewEngine")
96 | local renderer = viewEngine == "etlua" and etlua or template
97 | local key = "no-cache"
98 | if env.get("PROD") == "TRUE" then
99 | key = nil
100 | end
101 | local localData = self._local or {}
102 |
103 | local flashData = { flash = {}}
104 | if self.req.session and self.req.session.sid then
105 | local sid = self.req.session.sid
106 | flashData = {flash = ServerResponse.flashData[sid] or {} }
107 | ServerResponse.flashData[sid] = nil
108 | end
109 | local renderData = extend(extend(localData, data or {}), flashData)
110 | if viewEngine == "etlua" or path.extname(filePath) == ".etlua" then
111 | local templateString = fs.readFileSync(filePath)
112 | local include = function(fpath, data)
113 | local fpath = path.resolve(path.dirname(filePath), fpath)
114 | local tplString = fs.readFileSync(fpath)
115 | if not tplString then
116 | p("[Error]: File " .. fpath .. " Not Found.")
117 | return "File: `".. fpath .. "` not found.
"
118 | end
119 | local renderData = extend(extend(localData or {}, {currentPath = fpath, include = include }),data or {})
120 | local tplResult, err = etlua.render(tplString, renderData)
121 | if tplResult then
122 | return tplResult
123 | else
124 | p("[Error Rendering HTML](:include) ", err)
125 | return "Internal Error
Error while render template :(
"
126 | end
127 | end
128 | renderData = extend(localData, {
129 | currentPath = filePath,
130 | include = include
131 | })
132 | if not templateString then
133 | templateString = tpl
134 | end
135 | local result, error = etlua.render(templateString, renderData)
136 | if not result then
137 | p("[Error Rendering HTML] ", error)
138 | self:status(500):render("./template/500.html")
139 | else
140 | self:send(result)
141 | end
142 | else
143 | local status, result = pcall(function() return template.render(filePath, renderData, key) end)
144 | if status then
145 | self:send(result)
146 | else
147 | p("[Error Rendering HTML] ",result)
148 | self:fail("Internal Error")
149 | end
150 | end
151 | end
152 |
153 | function ServerResponse:renderToFile(tpl, data, file, continueOnError)
154 | local callerSource = debug.getinfo(2).source
155 |
156 | if callerSource:sub(1,1) == "@" then
157 | callerSource = callerSource:sub(2)
158 | elseif callerSource:sub(1, 7) == "bundle:" then
159 | callerSource = callerSource
160 | end
161 |
162 | local filePath = path.resolve(path.dirname(callerSource), tpl)
163 | local viewEngine = env.get("viewEngine")
164 | local renderer = viewEngine == "etlua" and etlua or template
165 | local key = "no-cache"
166 | if env.get("PROD") == "TRUE" then
167 | key = nil
168 | end
169 | local localData = self._local or {}
170 |
171 | local flashData = { flash = nil}
172 | if self.req.session and self.req.session.sid then
173 | local sid = self.req.session.sid
174 | flashData = {flash = ServerResponse.flashData[sid] or {} }
175 | ServerResponse.flashData[sid] = nil
176 | end
177 | local renderData = extend(extend(localData, data or {}), flashData)
178 |
179 | local status, result = pcall(function() return template.render(filePath, renderData, key) end)
180 | if status then
181 | fs.writeFileSync(file, result)
182 | else
183 | p("[Error Rendering HTML] ",result)
184 | if (continueOnError) then
185 | fs.writeFileSync(file, result)
186 | end
187 | end
188 | end
189 |
190 | function ServerResponse:status (statusCode)
191 | self.statusCode = statusCode
192 | return self
193 | end
194 |
195 | function ServerResponse:sendFile(filePath, headers)
196 | headers = headers or {}
197 | local callerSource = debug.getinfo(2).source
198 |
199 | if callerSource:sub(1,1) == "@" then
200 | callerSource = callerSource:sub(2)
201 | elseif callerSource:sub(1, 7) == "bundle:" then
202 | callerSource = callerSource
203 | end
204 |
205 | filePath = path.resolve(path.dirname(callerSource), filePath)
206 | local stat = fs.statSync(filePath)
207 | if not(stat) then
208 | return self:send("Can't get "..filePath .. "
", 404)
209 | end
210 | local fileType = mime.guess(filePath) or "application/octet-stream: charset=utf8"
211 | local etag = helpers.calcEtag(stat)
212 | local lastModified = os.date("%a, %d %b %Y %H:%M:%S GMT", stat.mtime.sec)
213 | local header = extend({
214 | ["Content-Type"] = fileType,
215 | ["Content-Length"] = stat.size,
216 | ['ETag'] = etag,
217 | ['Last-Modified'] = lastModified
218 | }, headers or {})
219 | local statusCode = 200
220 | local content = fs.readFileSync(filePath)
221 | if self.req.headers["if-none-match"] == etag or self.req.headers["if-modified-since"] == lastModified then
222 | statusCode = 304
223 | content = nil
224 | end
225 | if self._headerSent then
226 | return true
227 | end
228 | self:sendHead(statusCode, header, nil)
229 | fs.createReadStream(filePath):pipe(self)
230 | end
231 |
232 | function ServerResponse:redirect(url, code)
233 | code = code or 302
234 | self:status(code):setHeader("Location", url)
235 | self:send()
236 | return self
237 | end
238 |
239 | function ServerResponse:json(obj, code, headers)
240 | headers = copy(headers, {
241 | ["Content-Type"] = "application/json"
242 | })
243 | self:send(JSON.stringify(obj), code, headers)
244 | end
245 |
246 | function ServerResponse:flash(type, flash)
247 | local sid
248 | if self.req.session and self.req.session.sid then
249 | sid = self.req.session.sid
250 | ServerResponse.flashData[sid] = ServerResponse.flashData[sid] or {}
251 | ServerResponse.flashData[sid][type] = flash
252 | end
253 | end
254 |
255 | function ServerResponse:locals(data)
256 | local localData = self._local or {}
257 | self._local = extend(localData, data)
258 | end
259 |
260 | function ServerResponse:fail(reason)
261 | self:status(500):send(reason)
262 | end
263 |
264 | function ServerResponse:not_modified(header)
265 | self:send(nil, 304, header)
266 | end
267 |
--------------------------------------------------------------------------------
/libs/ansicolors.lua:
--------------------------------------------------------------------------------
1 | -- ansicolors.lua v1.0.2 (2012-08)
2 |
3 | -- Copyright (c) 2009 Rob Hoelz
4 | -- Copyright (c) 2011 Enrique García Cota
5 | --
6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy
7 | -- of this software and associated documentation files (the "Software"), to deal
8 | -- in the Software without restriction, including without limitation the rights
9 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | -- copies of the Software, and to permit persons to whom the Software is
11 | -- furnished to do so, subject to the following conditions:
12 | --
13 | -- The above copyright notice and this permission notice shall be included in
14 | -- all copies or substantial portions of the Software.
15 | --
16 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | -- THE SOFTWARE.
23 |
24 |
25 | -- support detection
26 | local function isWindows()
27 | return type(package) == 'table' and type(package.config) == 'string' and package.config:sub(1,1) == '\\'
28 | end
29 |
30 | local supported = not isWindows()
31 | if isWindows() then supported = os.getenv("ANSICON") end
32 |
33 | local keys = {
34 | -- reset
35 | reset = 0,
36 |
37 | -- misc
38 | bright = 1,
39 | dim = 2,
40 | italic = 3,
41 | underline = 4,
42 | blink = 5,
43 | invert = 7,
44 | hidden = 8,
45 |
46 | -- foreground colors
47 | black = 30,
48 | red = 31,
49 | green = 32,
50 | yellow = 33,
51 | blue = 34,
52 | magenta = 35,
53 | cyan = 36,
54 | white = 37,
55 |
56 | -- background colors
57 | blackbg = 40,
58 | redbg = 41,
59 | greenbg = 42,
60 | yellowbg = 43,
61 | bluebg = 44,
62 | magentabg = 45,
63 | cyanbg = 46,
64 | whitebg = 47
65 | }
66 |
67 | local escapeString = string.char(27) .. '[%dm'
68 | local function escapeNumber(number)
69 | return escapeString:format(number)
70 | end
71 |
72 | local function escapeKeys(str)
73 |
74 | if not supported then return "" end
75 |
76 | local buffer = {}
77 | local number
78 | for word in str:gmatch("%w+") do
79 | number = keys[word]
80 | assert(number, "Unknown key: " .. word)
81 | table.insert(buffer, escapeNumber(number) )
82 | end
83 |
84 | return table.concat(buffer)
85 | end
86 |
87 | local function replaceCodes(str)
88 | str = string.gsub(str,"(%%{(.-)})", function(_, str) return escapeKeys(str) end )
89 | return str
90 | end
91 |
92 | -- public
93 |
94 | local function ansicolors( str )
95 | str = tostring(str or '')
96 |
97 | return replaceCodes('%{reset}' .. str .. '%{reset}')
98 | end
99 |
100 |
101 | -- function print(...)
102 | -- io.write(table.concat({...}))
103 | -- io.flush()
104 | -- end
105 |
106 |
107 | for key, value in pairs(keys) do
108 | string[key] = function(self, str)
109 | if self:find("%%{(.-)}", 1) then
110 | str = self:gsub("%%{(.-)}", function(str)
111 | return "%{"..key.." "..str.."}"
112 | end,1)
113 | if self:sub(-8) == "%{reset}" then
114 | self = self .. "%{reset}"
115 | end
116 | else
117 | str = "%{"..key.."}".. self .. "%{reset}"
118 | end
119 | return str
120 | end
121 | end
122 |
123 | function string:print()
124 | print(self)
125 | end
126 |
127 | old_print = print
128 | _G.d = function(...)
129 | local allString = true
130 | local _temp = ...
131 | if _temp == nil then
132 | return old_print(...)
133 | end
134 | for _, value in pairs({...}) do
135 | if type(value) ~= "string" then
136 | allString = false
137 | end
138 | end
139 | if not allString then
140 | old_print(...)
141 | else
142 | old_print(ansicolors(table.concat({...})))
143 | end
144 | end
145 |
--------------------------------------------------------------------------------
/libs/cookie.lua:
--------------------------------------------------------------------------------
1 | -- Code From: https://github.com/voronianski/luvit-connect/blob/master/lib/cookie.lua
2 |
3 | -- Cookies module
4 | -- TO DO: extract to separate luvit module
5 |
6 | local os = require('os')
7 | local qs = require('querystring')
8 | local table = require('table')
9 | local openssl = require("openssl")
10 | local json = require('json')
11 | local Object = require('core').Object
12 | local helpers = require('./helpers')
13 |
14 | local Cookie = Object:extend()
15 |
16 | function Cookie.meta.__tostring ()
17 | return ''
18 | end
19 |
20 | local function trim(str, what)
21 | if what == nil then
22 | what = '%s+'
23 | end
24 | str = string.gsub(str, '^' .. what, '')
25 | str = string.gsub(str, what .. '$', '')
26 | return str
27 | end
28 |
29 | -- Serialize a "name-value" pair into a cookie string suitable for http headers.
30 | -- An optional options table specifies cookie parameters.
31 | -- @param {String} name
32 | -- @param {String} value
33 | -- @param {Table} options
34 | -- @return {String}
35 |
36 | function Cookie:serialize (name, value, options)
37 | options = options or {}
38 | options.encode = options.encode or qs.urlencode
39 |
40 | local cookiePairs = { name .. '=' .. options.encode(value) }
41 |
42 | if options.maxAge then table.insert(cookiePairs, 'Max-Age=' .. options.maxAge) end
43 | if options.domain then table.insert(cookiePairs, 'Domain=' .. options.domain) end
44 | if options.path then table.insert(cookiePairs, 'Path=' .. options.path) end
45 | if options.expires then table.insert(cookiePairs, 'Expires=' .. os.date('!%a, %d %b %Y %H:%M:%S GMT', options.expires)) end
46 | if options.httpOnly then table.insert(cookiePairs, 'HttpOnly') end
47 | if options.secure then table.insert(cookiePairs, 'Secure') end
48 |
49 | return table.concat(cookiePairs, '; ')
50 | end
51 |
52 | -- Parse the given cookie header string into a table
53 | -- @param {String} str
54 | -- @return {Table}
55 |
56 | function Cookie:parse (str, options)
57 | options = options or {}
58 | options.decode = options.decode or qs.urldecode
59 |
60 | local tbl = {}
61 | local cookiePairs = helpers.split(str, '%;%,')
62 |
63 | table.foreach(cookiePairs, function (index, pair)
64 | local eqIndex = helpers.indexOf(pair, '=')
65 |
66 | if not eqIndex then return end
67 |
68 | local key = trim(pair:sub(1, eqIndex-1))
69 | local val = pair:sub(eqIndex+1, #pair)
70 |
71 | -- quoted values
72 | if val:find('%"', 1) == 1 then
73 | val = val:sub(2, #val-1)
74 | end
75 |
76 | if not tbl[key] then
77 | tbl[key] = options.decode(val)
78 | end
79 | end)
80 |
81 | return tbl
82 | end
83 |
84 | -- Sign value with secret.
85 | -- @param {String} value
86 | -- @param {String} secret
87 | -- @return {String}
88 |
89 | function Cookie:sign (value, secret)
90 | if type(value) ~= 'string' then error('cookie required') end
91 | if type(secret) ~= 'string' then error('secret required') end
92 |
93 | local sha256 = openssl.digest.get("sha256")
94 | local d = openssl.digest.new(sha256)
95 | d:update(secret)
96 | d:update(value)
97 | local signed = value .. '.' .. d:final():gsub('%=+$', '')
98 | return signed
99 | end
100 |
101 | -- Unsign and decode value with secret, returns 'false' if signature is invalid.
102 | -- @param {String} value
103 | -- @param {String} secret
104 | -- @return {String}
105 |
106 | function Cookie:unsign (value, secret)
107 | if type(value) ~= 'string' then error('cookie required') end
108 | if type(secret) ~= 'string' then error('secret required') end
109 |
110 | local str = value:sub(1, helpers.lastIndexOf(value, '%.') - 1)
111 |
112 | if self:sign(str, secret) == value then
113 | return str
114 | end
115 |
116 | return false
117 | end
118 |
119 | -- Parse JSON values in cookies
120 | -- @param {Table} tbl
121 | -- return {Table}
122 |
123 | function Cookie:parseJSONCookies (tbl)
124 | table.foreach(tbl, function (index, str)
125 | if helpers.indexOf(str, 'j:') == 1 then
126 | local parseStatus, result = pcall(json.parse, str:sub(3, #str))
127 |
128 | if not parseStatus then return nil end
129 |
130 | tbl[index] = result
131 | end
132 | end)
133 |
134 | return tbl
135 | end
136 |
137 | -- Parse signed cookies.
138 | -- Returns a table containing the decoded key/value pairs, while removing the signed key from 'tbl'.
139 | -- @param {Table} tbl
140 | -- @param {String} secret
141 | -- return {Table}
142 |
143 | function Cookie:parseSignedCookies (tbl, secret)
144 | local result
145 |
146 | table.foreach(tbl, function (index, str)
147 | if helpers.indexOf(str, 's:') == 1 then
148 | local value = self.unsign(str:sub(3, #str), secret)
149 | if value then
150 | result[index] = value
151 | tbl[index] = nil
152 | end
153 | end
154 | end)
155 |
156 | return result
157 | end
158 |
159 | return Cookie
160 |
--------------------------------------------------------------------------------
/libs/etlua.lua:
--------------------------------------------------------------------------------
1 | -- Copyright :
2 | -- https://github.com/leafo/etlua/blob/master/etlua.lua
3 |
4 | local VERSION = "1.0.2"
5 | local insert, concat
6 | do
7 | local _obj_0 = table
8 | insert, concat = _obj_0.insert, _obj_0.concat
9 | end
10 | local load, setfenv, assert, type, error, tostring, tonumber, setmetatable
11 | do
12 | local _obj_0 = _G
13 | load, setfenv, assert, type, error, tostring, tonumber, setmetatable = _obj_0.load, _obj_0.setfenv, _obj_0.assert, _obj_0.type, _obj_0.error, _obj_0.tostring, _obj_0.tonumber, _obj_0.setmetatable
14 | end
15 | setfenv = setfenv or function(fn, env)
16 | local name
17 | local i = 1
18 | while true do
19 | name = debug.getupvalue(fn, i)
20 | if not name or name == "_ENV" then
21 | break
22 | end
23 | i = i + 1
24 | end
25 | if name then
26 | debug.upvaluejoin(fn, i, (function()
27 | return env
28 | end), 1)
29 | end
30 | return fn
31 | end
32 | local html_escape_entities = {
33 | ['&'] = '&',
34 | ['<'] = '<',
35 | ['>'] = '>',
36 | ['"'] = '"',
37 | ["'"] = '''
38 | }
39 | local html_escape
40 | html_escape = function(str)
41 | return (str:gsub([=[["><'&]]=], html_escape_entities))
42 | end
43 | local get_line
44 | get_line = function(str, line_num)
45 | for line in str:gmatch("([^\n]*)\n?") do
46 | if line_num == 1 then
47 | return line
48 | end
49 | line_num = line_num - 1
50 | end
51 | end
52 | local pos_to_line
53 | pos_to_line = function(str, pos)
54 | local line = 1
55 | for _ in str:sub(1, pos):gmatch("\n") do
56 | line = line + 1
57 | end
58 | return line
59 | end
60 | local Compiler
61 | do
62 | local _base_0 = {
63 | render = function(self)
64 | return table.concat(self.buffer)
65 | end,
66 | push = function(self, str, ...)
67 | local i = self.i + 1
68 | self.buffer[i] = str
69 | self.i = i
70 | if ... then
71 | return self:push(...)
72 | end
73 | end,
74 | header = function(self)
75 | return self:push("local _tostring, _escape, _b, _b_i = ...\n")
76 | end,
77 | footer = function(self)
78 | return self:push("return _b")
79 | end,
80 | increment = function(self)
81 | return self:push("_b_i = _b_i + 1\n")
82 | end,
83 | mark = function(self, pos)
84 | return self:push("--[[", tostring(pos), "]] ")
85 | end,
86 | assign = function(self, ...)
87 | self:push("_b[_b_i] = ", ...)
88 | if ... then
89 | return self:push("\n")
90 | end
91 | end
92 | }
93 | _base_0.__index = _base_0
94 | local _class_0 = setmetatable({
95 | __init = function(self)
96 | self.buffer = { }
97 | self.i = 0
98 | end,
99 | __base = _base_0,
100 | __name = "Compiler"
101 | }, {
102 | __index = _base_0,
103 | __call = function(cls, ...)
104 | local _self_0 = setmetatable({}, _base_0)
105 | cls.__init(_self_0, ...)
106 | return _self_0
107 | end
108 | })
109 | _base_0.__class = _class_0
110 | Compiler = _class_0
111 | end
112 | local Parser
113 | do
114 | local _base_0 = {
115 | open_tag = "<%",
116 | close_tag = "%>",
117 | modifiers = "^[=-]",
118 | html_escape = true,
119 | next_tag = function(self)
120 | local start, stop = self.str:find(self.open_tag, self.pos, true)
121 | if not (start) then
122 | self:push_raw(self.pos, #self.str)
123 | return false
124 | end
125 | if not (start == self.pos) then
126 | self:push_raw(self.pos, start - 1)
127 | end
128 | self.pos = stop + 1
129 | local modifier
130 | if self.str:match(self.modifiers, self.pos) then
131 | do
132 | local _with_0 = self.str:sub(self.pos, self.pos)
133 | self.pos = self.pos + 1
134 | modifier = _with_0
135 | end
136 | end
137 | local close_start, close_stop = self.str:find(self.close_tag, self.pos, true)
138 | if not (close_start) then
139 | return nil, self:error_for_pos(start, "failed to find closing tag")
140 | end
141 | while self:in_string(self.pos, close_start) do
142 | close_start, close_stop = self.str:find(self.close_tag, close_stop, true)
143 | end
144 | local trim_newline
145 | if "-" == self.str:sub(close_start - 1, close_start - 1) then
146 | close_start = close_start - 1
147 | trim_newline = true
148 | end
149 | self:push_code(modifier or "code", self.pos, close_start - 1)
150 | self.pos = close_stop + 1
151 | if trim_newline then
152 | do
153 | local match = self.str:match("^\n", self.pos)
154 | if match then
155 | self.pos = self.pos + #match
156 | end
157 | end
158 | end
159 | return true
160 | end,
161 | in_string = function(self, start, stop)
162 | local in_string = false
163 | local end_delim = nil
164 | local escape = false
165 | local pos = 0
166 | local skip_until = nil
167 | local chunk = self.str:sub(start, stop)
168 | for char in chunk:gmatch(".") do
169 | local _continue_0 = false
170 | repeat
171 | pos = pos + 1
172 | if skip_until then
173 | if pos <= skip_until then
174 | _continue_0 = true
175 | break
176 | end
177 | skip_until = nil
178 | end
179 | if end_delim then
180 | if end_delim == char and not escape then
181 | in_string = false
182 | end_delim = nil
183 | end
184 | else
185 | if char == "'" or char == '"' then
186 | end_delim = char
187 | in_string = true
188 | end
189 | if char == "[" then
190 | do
191 | local lstring = chunk:match("^%[=*%[", pos)
192 | if lstring then
193 | local lstring_end = lstring:gsub("%[", "]")
194 | local lstring_p1, lstring_p2 = chunk:find(lstring_end, pos, true)
195 | if not (lstring_p1) then
196 | return true
197 | end
198 | skip_until = lstring_p2
199 | end
200 | end
201 | end
202 | end
203 | escape = char == "\\"
204 | _continue_0 = true
205 | until true
206 | if not _continue_0 then
207 | break
208 | end
209 | end
210 | return in_string
211 | end,
212 | push_raw = function(self, start, stop)
213 | return insert(self.chunks, self.str:sub(start, stop))
214 | end,
215 | push_code = function(self, kind, start, stop)
216 | return insert(self.chunks, {
217 | kind,
218 | self.str:sub(start, stop),
219 | start
220 | })
221 | end,
222 | compile = function(self, str)
223 | local success, err = self:parse(str)
224 | if not (success) then
225 | return nil, err
226 | end
227 | local fn
228 | fn, err = self:load(self:chunks_to_lua())
229 | if not (fn) then
230 | return nil, err
231 | end
232 | return function(...)
233 | local success, result
234 | local args = ...
235 | success, result = pcall(function()
236 | local result, err = self:run(fn, args)
237 | if result then
238 | return result
239 | else
240 | return error(err)
241 | end
242 | end)
243 | if success then
244 | return concat(result)
245 | else
246 | return nil, result
247 | end
248 | end
249 | end,
250 | parse = function(self, str)
251 | self.str = str
252 | assert(type(self.str) == "string", "expecting string for parse")
253 | self.pos = 1
254 | self.chunks = { }
255 | while true do
256 | local found, err = self:next_tag()
257 | if err then
258 | return nil, err
259 | end
260 | if not (found) then
261 | break
262 | end
263 | end
264 | return true
265 | end,
266 | parse_error = function(self, err, code)
267 | local line_no, err_msg = err:match("%[.-%]:(%d+): (.*)$")
268 | line_no = tonumber(line_no)
269 | if not (line_no) then
270 | return
271 | end
272 | local line = get_line(code, line_no)
273 | local source_pos = tonumber(line:match("^%-%-%[%[(%d+)%]%]"))
274 | if not (source_pos) then
275 | return
276 | end
277 | return self:error_for_pos(source_pos, err_msg)
278 | end,
279 | error_for_pos = function(self, source_pos, err_msg)
280 | local source_line_no = pos_to_line(self.str, source_pos)
281 | local source_line = get_line(self.str, source_line_no)
282 | return tostring(err_msg) .. " [" .. tostring(source_line_no) .. "]: " .. tostring(source_line)
283 | end,
284 | load = function(self, code, name)
285 | if name == nil then
286 | name = "etlua"
287 | end
288 | local code_fn
289 | do
290 | local code_ref = code
291 | code_fn = function()
292 | do
293 | local ret = code_ref
294 | code_ref = nil
295 | return ret
296 | end
297 | end
298 | end
299 | local fn, err = load(code_fn, name)
300 | if not (fn) then
301 | do
302 | local err_msg = self:parse_error(err, code)
303 | if err_msg then
304 | return nil, err_msg
305 | end
306 | end
307 | return nil, err
308 | end
309 | return fn
310 | end,
311 | run = function(self, fn, env, buffer, i, ...)
312 | if env == nil then
313 | env = { }
314 | end
315 | local combined_env = setmetatable({ }, {
316 | __index = function(self, name)
317 | local val = env[name]
318 | if val == nil then
319 | val = _G[name]
320 | end
321 | return val
322 | end
323 | })
324 | if not (buffer) then
325 | buffer = { }
326 | i = 0
327 | end
328 | setfenv(fn, combined_env)
329 | return fn(tostring, html_escape, buffer, i, ...)
330 | end,
331 | compile_to_lua = function(self, str, ...)
332 | local success, err = self:parse(str)
333 | if not (success) then
334 | return nil, err
335 | end
336 | return self:chunks_to_lua(...)
337 | end,
338 | chunks_to_lua = function(self, compiler_cls)
339 | if compiler_cls == nil then
340 | compiler_cls = Compiler
341 | end
342 | local r = compiler_cls()
343 | r:header()
344 | local _list_0 = self.chunks
345 | for _index_0 = 1, #_list_0 do
346 | local chunk = _list_0[_index_0]
347 | local t = type(chunk)
348 | if t == "table" then
349 | t = chunk[1]
350 | end
351 | local _exp_0 = t
352 | if "string" == _exp_0 then
353 | r:increment()
354 | r:assign(("%q"):format(chunk))
355 | elseif "code" == _exp_0 then
356 | r:mark(chunk[3])
357 | r:push(chunk[2], "\n")
358 | elseif "=" == _exp_0 or "-" == _exp_0 then
359 | r:increment()
360 | r:mark(chunk[3])
361 | r:assign()
362 | if t == "=" and self.html_escape then
363 | r:push("_escape(_tostring(", chunk[2], "))\n")
364 | else
365 | r:push("_tostring(", chunk[2], ")\n")
366 | end
367 | else
368 | error("unknown type " .. tostring(t))
369 | end
370 | end
371 | r:footer()
372 | return r:render()
373 | end
374 | }
375 | _base_0.__index = _base_0
376 | local _class_0 = setmetatable({
377 | __init = function() end,
378 | __base = _base_0,
379 | __name = "Parser"
380 | }, {
381 | __index = _base_0,
382 | __call = function(cls, ...)
383 | local _self_0 = setmetatable({}, _base_0)
384 | cls.__init(_self_0, ...)
385 | return _self_0
386 | end
387 | })
388 | _base_0.__class = _class_0
389 | Parser = _class_0
390 | end
391 | local compile
392 | do
393 | local _base_0 = Parser()
394 | local _fn_0 = _base_0.compile
395 | compile = function(...)
396 | return _fn_0(_base_0, ...)
397 | end
398 | end
399 | local render
400 | render = function(str, ...)
401 | local fn, err = compile(str)
402 | if fn then
403 | return fn(...)
404 | else
405 | return nil, err
406 | end
407 | end
408 | return {
409 | compile = compile,
410 | render = render,
411 | Parser = Parser,
412 | Compiler = Compiler,
413 | _version = VERSION
414 | }
--------------------------------------------------------------------------------
/libs/fse.lua:
--------------------------------------------------------------------------------
1 | local fs = require("fs");
2 | local pathJoin = require('luvi').path.join
3 |
4 | function getDirData (dir, files, rootDir)
5 | local dirFiles = fs.readdirSync(dir)
6 | files = files or {}
7 | for _, file in pairs(dirFiles) do
8 | local objPath = pathJoin(dir, file)
9 | local stat = fs.statSync(objPath)
10 | if stat.type == "file" then
11 | local filePath = file
12 | if rootDir and rootDir ~= dir then
13 | filePath = string.gsub(objPath, rootDir, "")
14 | end
15 | files[filePath] = stat
16 | else
17 | getDirData(objPath, files, rootDir or dir)
18 | end
19 | end
20 | return files
21 | end
22 |
23 | function readDirFile (dir, options)
24 | return getDirData(dir, nil, dir)
25 | end
26 |
27 | return readDirFile
28 |
--------------------------------------------------------------------------------
/libs/helpers.lua:
--------------------------------------------------------------------------------
1 | -- Some code from: https://github.com/voronianski/luvit-connect/blob/master/lib/helpers.lua
2 |
3 | local floor = require('math').floor
4 | local http = require('http')
5 | local table = require('table')
6 | local string = require('string')
7 | local math = require('math')
8 | require("./ansicolors")
9 |
10 | local digits = {
11 | "0", "1", "2", "3", "4", "5", "6", "7",
12 | "8", "9", "A", "B", "C", "D", "E", "F",
13 | "G", "H", "I", "J", "K", "L", "M", "N",
14 | "O", "P", "Q", "R", "S", "T", "U", "V",
15 | "W", "X", "Y", "Z", "a", "b", "c", "d",
16 | "e", "f", "g", "h", "i", "j", "k", "l",
17 | "m", "n", "o", "p", "q", "r", "s", "t",
18 | "u", "v", "w", "x", "y", "z", "_", "$"
19 | }
20 |
21 | local function numToBase(num, base)
22 | local parts = {}
23 | repeat
24 | table.insert(parts, digits[(num % base) + 1])
25 | num = floor(num / base)
26 | until num == 0
27 | return table.concat(parts)
28 | end
29 |
30 | -- microsecond precision
31 | local ffi = require("ffi")
32 |
33 | local getTime;
34 | do
35 | if jit.os == "Windows" then
36 | ffi.cdef [[
37 | typedef unsigned long DWORD, *PDWORD, *LPDWORD;
38 | typedef struct _FILETIME {
39 | DWORD dwLowDateTime;
40 | DWORD dwHighDateTime;
41 | } FILETIME, *PFILETIME;
42 |
43 | void GetSystemTimeAsFileTime ( FILETIME* );
44 | ]]
45 | local ft = ffi.new ( "FILETIME[1]" )
46 | getTime = function ( ) -- As found in luasocket's timeout.c
47 | ffi.C.GetSystemTimeAsFileTime ( ft )
48 | local t = tonumber ( ft[0].dwLowDateTime ) / 1e7 + tonumber ( ft[0].dwHighDateTime ) * ( 2^32 / 1e7 )
49 | -- Convert to Unix Epoch time (time since January 1, 1970 (UTC))
50 | t = t - 11644473600
51 | return math.floor(t * 1000 + 0.5)
52 | end
53 | else -- Assume posix
54 |
55 | if pcall(ffi.typeof, "struct timeval") then
56 | else
57 | ffi.cdef[[
58 | typedef long time_t;
59 |
60 | typedef struct timeval {
61 | time_t tv_sec;
62 | time_t tv_usec;
63 | } timeval;
64 |
65 | int gettimeofday(struct timeval* t, void* tzp);
66 | ]]
67 | end
68 |
69 | local gettimeofday_struct = ffi.new("timeval")
70 | getTime = function ()
71 | ffi.C.gettimeofday(gettimeofday_struct, nil)
72 | return tonumber(gettimeofday_struct.tv_sec) * 1000 + tonumber(gettimeofday_struct.tv_usec / 1000)
73 | end
74 | end
75 | end
76 |
77 | local function log (req, res)
78 | local currentDate = os.date("[%Y-%m-%d %H:%M:%S]"):dim()
79 | local statusCode = res.statusCode
80 | local stCode = ""
81 | if 100 <= statusCode and statusCode < 200 then
82 | stCode = tostring(statusCode):red()
83 | elseif 200 <= statusCode and statusCode < 300 then
84 | stCode = tostring(statusCode):green()
85 | elseif 300 <= statusCode and statusCode < 400 then
86 | stCode = tostring(statusCode):blue()
87 | elseif 400 <= statusCode and statusCode < 500 then
88 | stCode = tostring(statusCode):yellow()
89 | else
90 | stCode = tostring(statusCode):red()
91 | end
92 | local timeCosted = getTime() - req.start_time
93 | d(currentDate:dim(), " - [", stCode, "]", (" " .. tostring(req.method) .. " "):yellow(), (tostring(timeCosted) .. "ms "):cyan(), req.url:blue(), (" UserAgent: "):magenta(), req.headers["user-agent"])
94 | end
95 |
96 | local function calcEtag(stat)
97 | return (not stat.type == "file" and 'W/' or '') ..
98 | '"' .. numToBase(stat.ino or 0, 64) ..
99 | '-' .. numToBase(stat.size, 64) ..
100 | '-' .. numToBase(stat.mtime.sec, 64) .. '"'
101 | end
102 |
103 | local function copy(obj, seen)
104 | if type(obj) ~= 'table' then return obj end
105 | if seen and seen[obj] then return seen[obj] end
106 | local s = seen or {}
107 | local res = setmetatable({}, getmetatable(obj))
108 | s[obj] = res
109 | for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
110 | return res
111 | end
112 |
113 | -- nil, boolean, number, string, userdata, function, thread, and table.
114 | function colorise (anything)
115 | typeStr = type(anything)
116 | str = tostring(anything)
117 | if typeStr == "nil" then
118 | return str:dim()
119 | elseif typeStr == "boolean" then
120 | return str:cyan()
121 | elseif typeStr == "number" then
122 | return str:red()
123 | elseif typeStr == "string" then
124 | return str:yellow()
125 | elseif typeStr == "userdata" then
126 | return str:cyan()
127 | elseif typeStr == "function" then
128 | return str:blue()
129 | elseif typeStr == "thread" then
130 | return str:white()
131 | elseif typeStr == "table" then
132 | return str:magenta()
133 | end
134 | end
135 |
136 | -- pretty print of tables to console
137 | function tprint (tbl, indent)
138 | indent = indent or 0
139 | if indent >= 5 then return false end
140 | for key, value in pairs(tbl) do
141 |
142 | key = colorise(key)
143 |
144 | formatting = string.rep(' ', indent) .. key .. ': '
145 |
146 | if type(value) == 'table' then
147 | print(formatting)
148 | tprint(value, indent + 1)
149 | else
150 | print(formatting .. colorise(value))
151 | end
152 | end
153 | end
154 |
155 | -- filter values from table
156 | function filter (tbl, fn)
157 | local result = {}
158 |
159 | if not tbl or type(tbl) ~= 'table' then return result end
160 |
161 | for key, value in pairs(tbl) do
162 | if fn(value, key, tbl) then
163 | table.insert(result, value)
164 | end
165 | end
166 |
167 | return result
168 | end
169 |
170 | -- round number to decimals, defaults to 2 decimals
171 | function roundToDecimals (num, decimals)
172 | decimals = decimals or 2
173 | local shift = 10 ^ decimals
174 | local result = math.floor(num * shift + 0.5) / shift
175 | return result
176 | end
177 |
178 | -- merge table2 into table1
179 | function merge (table1, table2)
180 | for key, value in pairs(table2) do
181 | table1[key] = value
182 | end
183 | return table1
184 | end
185 |
186 | -- get index of field in table or character in string
187 | function indexOf (target, field)
188 | if type(target) == 'string' then
189 | return target:find(field, 1, true)
190 | end
191 |
192 | for index, value in pairs(target) do
193 | if value == field then
194 | return index
195 | end
196 | end
197 |
198 | return nil
199 | end
200 |
201 | -- last index of an element in string
202 | function lastIndexOf (str, elem)
203 | if type(str) ~= 'string' then error('string required') end
204 | if type(str) ~= 'string' then error('elem required') end
205 |
206 | local index = str:match('.*' .. elem .. '()')
207 | if not index then
208 | return nil
209 | else
210 | return index - 1
211 | end
212 | end
213 |
214 | -- split string
215 | function split (str, sep)
216 | sep = sep or '%s+'
217 |
218 | local result = {}
219 | local i = 1
220 |
221 | for value in str:gmatch('([^' .. sep .. ']+)') do
222 | result[i] = value
223 | i = i + 1
224 | end
225 |
226 | return result
227 | end
228 |
229 | local find , sub= string.find, string.sub
230 | function split2(str, sep, nmax)
231 | if sep == nil then
232 | sep = '%s+'
233 | end
234 | local r = { }
235 | if #str <= 0 then
236 | return r
237 | end
238 | local plain = false
239 | nmax = nmax or -1
240 | local nf = 1
241 | local ns = 1
242 | local nfr, nl = find(str, sep, ns, plain)
243 | while nfr and nmax ~= 0 do
244 | r[nf] = sub(str, ns, nfr - 1)
245 | nf = nf + 1
246 | ns = nl + 1
247 | nmax = nmax - 1
248 | nfr, nl = find(str, sep, ns, plain)
249 | end
250 | r[nf] = sub(str, ns)
251 | return r
252 | end
253 |
254 | -- create an error table to throw
255 | function throwError (code, msg)
256 | return {
257 | status = code,
258 | msg = msg or http.STATUS_CODES[code]
259 | }
260 | end
261 |
262 | return {
263 | merge = merge,
264 | tprint = tprint,
265 | filter = filter,
266 | split = split,
267 | split2 = split2,
268 | indexOf = indexOf,
269 | lastIndexOf = lastIndexOf,
270 | mime = mime,
271 | throwError = throwError,
272 | roundToDecimals = roundToDecimals,
273 | supportMethod = supportMethod,
274 | hasBody = hasBody,
275 | copy = copy,
276 | calcEtag = calcEtag,
277 | log = log,
278 | getTime = getTime
279 | }
280 |
--------------------------------------------------------------------------------
/libs/mime.lua:
--------------------------------------------------------------------------------
1 | -- mimetypes.lua
2 | -- Version 1.0.0
3 |
4 | --[[
5 | Copyright (c) 2011 Matthew "LeafStorm" Frazier
6 |
7 | Permission is hereby granted, free of charge, to any person
8 | obtaining a copy of this software and associated documentation
9 | files (the "Software"), to deal in the Software without
10 | restriction, including without limitation the rights to use,
11 | copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the
13 | Software is furnished to do so, subject to the following
14 | conditions:
15 |
16 | The above copyright notice and this permission notice shall be
17 | included in all copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 | OTHER DEALINGS IN THE SOFTWARE.
27 |
28 | ======
29 |
30 | In addition, the MIME types contained in the Software were
31 | originally obtained from the Apache HTTP Server available under the
32 | Apache Software License, Version 2.0 license
33 | (http://directory.fsf.org/wiki/License:Apache2.0)
34 | ]]
35 |
36 | -- This table is the one that actually contains the exported functions.
37 |
38 | local mimetypes = {}
39 |
40 | mimetypes.version = '1.0.0'
41 |
42 |
43 | -- Extracts the extension from a filename and returns it.
44 | -- The extension must be at the end of the string, and preceded by a dot and
45 | -- at least one other character. Only the last part will be returned (so
46 | -- "package-1.2.tar.gz" will return "gz").
47 | -- If there is no extension, this function will return nil.
48 |
49 | local function extension (filename)
50 | return filename:match(".+%.([%a%d]+)$")
51 | end
52 |
53 |
54 | -- Creates a deep copy of the given table.
55 |
56 | local function copy (tbl)
57 | local ntbl = {}
58 | for key, value in pairs(tbl) do
59 | if type(value) == 'table' then
60 | ntbl[key] = copy(value)
61 | else
62 | ntbl[key] = value
63 | end
64 | end
65 | return ntbl
66 | end
67 |
68 |
69 | -- This is the default MIME type database.
70 | -- It is a table with two members - "extensions" and "filenames".
71 | -- The filenames table maps complete file names (like README) to MIME types.
72 | -- The extensions just maps the files' extensions (like jpg) to types.
73 |
74 | local defaultdb = {
75 | -- The MIME types. Remember to not include the dot on the extension.
76 | extensions = {
77 | ['mods'] = 'application/mods+xml',
78 | ['hps'] = 'application/vnd.hp-hps',
79 | ['sfv'] = 'text/x-sfv',
80 | ['pcl'] = 'application/vnd.hp-pcl',
81 | ['oth'] = 'application/vnd.oasis.opendocument.text-web',
82 | ['arc'] = 'application/x-freearc',
83 | ['txd'] = 'application/vnd.genomatix.tuxedo',
84 | ['tcl'] = 'application/x-tcl',
85 | ['apr'] = 'application/vnd.lotus-approach',
86 | ['viv'] = 'video/vnd.vivo',
87 | ['wmlsc'] = 'application/vnd.wap.wmlscriptc',
88 | ['inkml'] = 'application/inkml+xml',
89 | ['sdc'] = 'application/vnd.stardivision.calc',
90 | ['dll'] = 'application/x-msdownload',
91 | ['sdkd'] = 'application/vnd.solent.sdkm+xml',
92 | ['kpr'] = 'application/vnd.kde.kpresenter',
93 | ['uvd'] = 'application/vnd.dece.data',
94 | ['gtw'] = 'model/vnd.gtw',
95 | ['svd'] = 'application/vnd.svd',
96 | ['m14'] = 'application/x-msmediaview',
97 | ['spp'] = 'application/scvp-vp-response',
98 | ['qt'] = 'video/quicktime',
99 | ['c4g'] = 'application/vnd.clonk.c4group',
100 | ['dp'] = 'application/vnd.osgi.dp',
101 | ['oda'] = 'application/oda',
102 | ['log'] = 'text/plain',
103 | ['gre'] = 'application/vnd.geometry-explorer',
104 | ['sda'] = 'application/vnd.stardivision.draw',
105 | ['rms'] = 'application/vnd.jcp.javame.midlet-rms',
106 | ['ico'] = 'image/x-icon',
107 | ['cab'] = 'application/vnd.ms-cab-compressed',
108 | ['p7c'] = 'application/pkcs7-mime',
109 | ['cb7'] = 'application/x-cbr',
110 | ['src'] = 'application/x-wais-source',
111 | ['uvf'] = 'application/vnd.dece.data',
112 | ['dms'] = 'application/octet-stream',
113 | ['ccxml'] = 'application/ccxml+xml',
114 | ['uvvd'] = 'application/vnd.dece.data',
115 | ['abw'] = 'application/x-abiword',
116 | ['gex'] = 'application/vnd.geometry-explorer',
117 | ['es3'] = 'application/vnd.eszigno3+xml',
118 | ['mmr'] = 'image/vnd.fujixerox.edmics-mmr',
119 | ['wgt'] = 'application/widget',
120 | ['mp4a'] = 'audio/mp4',
121 | ['gram'] = 'application/srgs',
122 | ['p7m'] = 'application/pkcs7-mime',
123 | ['org'] = 'application/vnd.lotus-organizer',
124 | ['silo'] = 'model/mesh',
125 | ['scq'] = 'application/scvp-cv-request',
126 | ['cxx'] = 'text/x-c',
127 | ['ots'] = 'application/vnd.oasis.opendocument.spreadsheet-template',
128 | ['fhc'] = 'image/x-freehand',
129 | ['uvvf'] = 'application/vnd.dece.data',
130 | ['jpeg'] = 'image/jpeg',
131 | ['ma'] = 'application/mathematica',
132 | ['odm'] = 'application/vnd.oasis.opendocument.text-master',
133 | ['uvvh'] = 'video/vnd.dece.hd',
134 | ['bed'] = 'application/vnd.realvnc.bed',
135 | ['doc'] = 'application/msword',
136 | ['ice'] = 'x-conference/x-cooltalk',
137 | ['fg5'] = 'application/vnd.fujitsu.oasysgp',
138 | ['ustar'] = 'application/x-ustar',
139 | ['mesh'] = 'model/mesh',
140 | ['smv'] = 'video/x-smv',
141 | ['imp'] = 'application/vnd.accpac.simply.imp',
142 | ['movie'] = 'video/x-sgi-movie',
143 | ['cmp'] = 'application/vnd.yellowriver-custom-menu',
144 | ['avi'] = 'video/x-msvideo',
145 | ['cpt'] = 'application/mac-compactpro',
146 | ['lha'] = 'application/x-lzh-compressed',
147 | ['cmdf'] = 'chemical/x-cmdf',
148 | ['wvx'] = 'video/x-ms-wvx',
149 | ['prf'] = 'application/pics-rules',
150 | ['wmx'] = 'video/x-ms-wmx',
151 | ['wmv'] = 'video/x-ms-wmv',
152 | ['ggt'] = 'application/vnd.geogebra.tool',
153 | ['kpt'] = 'application/vnd.kde.kpresenter',
154 | ['kmz'] = 'application/vnd.google-earth.kmz',
155 | ['mpt'] = 'application/vnd.ms-project',
156 | ['caf'] = 'audio/x-caf',
157 | ['mp4'] = 'video/mp4',
158 | ['dcr'] = 'application/x-director',
159 | ['wm'] = 'video/x-ms-wm',
160 | ['ai'] = 'application/postscript',
161 | ['lrf'] = 'application/octet-stream',
162 | ['qbo'] = 'application/vnd.intu.qbo',
163 | ['vob'] = 'video/x-ms-vob',
164 | ['nml'] = 'application/vnd.enliven',
165 | ['xlf'] = 'application/x-xliff+xml',
166 | ['dcurl'] = 'text/vnd.curl.dcurl',
167 | ['hpid'] = 'application/vnd.hp-hpid',
168 | ['kwd'] = 'application/vnd.kde.kword',
169 | ['asx'] = 'video/x-ms-asf',
170 | ['asf'] = 'video/x-ms-asf',
171 | ['acu'] = 'application/vnd.acucobol',
172 | ['fm'] = 'application/vnd.framemaker',
173 | ['plf'] = 'application/vnd.pocketlearn',
174 | ['mng'] = 'video/x-mng',
175 | ['gramps'] = 'application/x-gramps-xml',
176 | ['mks'] = 'video/x-matroska',
177 | ['vcard'] = 'text/vcard',
178 | ['sgml'] = 'text/sgml',
179 | ['mvb'] = 'application/x-msmediaview',
180 | ['rp9'] = 'application/vnd.cloanto.rp9',
181 | ['mkv'] = 'video/x-matroska',
182 | ['xhtml'] = 'application/xhtml+xml',
183 | ['dssc'] = 'application/dssc+der',
184 | ['m4v'] = 'video/x-m4v',
185 | ['xml'] = 'application/xml',
186 | ['fli'] = 'video/x-fli',
187 | ['mpm'] = 'application/vnd.blueice.multipass',
188 | ['pskcxml'] = 'application/pskc+xml',
189 | ['webm'] = 'video/webm',
190 | ['pml'] = 'application/vnd.ctc-posml',
191 | ['cu'] = 'application/cu-seeme',
192 | ['rm'] = 'application/vnd.rn-realmedia',
193 | ['spx'] = 'audio/ogg',
194 | ['dic'] = 'text/x-c',
195 | ['esa'] = 'application/vnd.osgi.subsystem',
196 | ['odi'] = 'application/vnd.oasis.opendocument.image',
197 | ['irm'] = 'application/vnd.ibm.rights-management',
198 | ['m4u'] = 'video/vnd.mpegurl',
199 | ['cdmid'] = 'application/cdmi-domain',
200 | ['elc'] = 'application/octet-stream',
201 | ['mxu'] = 'video/vnd.mpegurl',
202 | ['mdi'] = 'image/vnd.ms-modi',
203 | ['rld'] = 'application/resource-lists-diff+xml',
204 | ['sdw'] = 'application/vnd.stardivision.writer',
205 | ['fvt'] = 'video/vnd.fvt',
206 | ['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
207 | ['dvb'] = 'video/vnd.dvb.file',
208 | ['book'] = 'application/vnd.framemaker',
209 | ['pfx'] = 'application/x-pkcs12',
210 | ['qwd'] = 'application/vnd.quark.quarkxpress',
211 | ['nbp'] = 'application/vnd.wolfram.player',
212 | ['vcx'] = 'application/vnd.vcx',
213 | ['gpx'] = 'application/gpx+xml',
214 | ['uvvs'] = 'video/vnd.dece.sd',
215 | ['shf'] = 'application/shf+xml',
216 | ['n-gage'] = 'application/vnd.nokia.n-gage.symbian.install',
217 | ['hvd'] = 'application/vnd.yamaha.hv-dic',
218 | ['lostxml'] = 'application/lost+xml',
219 | ['knp'] = 'application/vnd.kinar',
220 | ['dvi'] = 'application/x-dvi',
221 | ['uvvp'] = 'video/vnd.dece.pd',
222 | ['psb'] = 'application/vnd.3gpp.pic-bw-small',
223 | ['asc'] = 'application/pgp-signature',
224 | ['wsdl'] = 'application/wsdl+xml',
225 | ['stk'] = 'application/hyperstudio',
226 | ['x3dbz'] = 'model/x3d+binary',
227 | ['uvvm'] = 'video/vnd.dece.mobile',
228 | ['3gp'] = 'video/3gpp',
229 | ['uvm'] = 'video/vnd.dece.mobile',
230 | ['sfd-hdstx'] = 'application/vnd.hydrostatix.sof-data',
231 | ['mpeg'] = 'video/mpeg',
232 | ['uvh'] = 'video/vnd.dece.hd',
233 | ['mov'] = 'video/quicktime',
234 | ['cla'] = 'application/vnd.claymore',
235 | ['ogv'] = 'video/ogg',
236 | ['m2v'] = 'video/mpeg',
237 | ['sse'] = 'application/vnd.kodak-descriptor',
238 | ['atomsvc'] = 'application/atomsvc+xml',
239 | ['djv'] = 'image/vnd.djvu',
240 | ['fe_launch'] = 'application/vnd.denovo.fcselayout-link',
241 | ['mpe'] = 'video/mpeg',
242 | ['x3dz'] = 'model/x3d+xml',
243 | ['et3'] = 'application/vnd.eszigno3+xml',
244 | ['u32'] = 'application/x-authorware-bin',
245 | ['mpg'] = 'video/mpeg',
246 | ['mpg4'] = 'video/mp4',
247 | ['mp4v'] = 'video/mp4',
248 | ['nsf'] = 'application/vnd.lotus-notes',
249 | ['dwg'] = 'image/vnd.dwg',
250 | ['teicorpus'] = 'application/tei+xml',
251 | ['sus'] = 'application/vnd.sus-calendar',
252 | ['uvvz'] = 'application/vnd.dece.zip',
253 | ['uvvx'] = 'application/vnd.dece.unspecified',
254 | ['psd'] = 'image/vnd.adobe.photoshop',
255 | ['ami'] = 'application/vnd.amiga.ami',
256 | ['nb'] = 'application/mathematica',
257 | ['nzb'] = 'application/x-nzb',
258 | ['tsd'] = 'application/timestamped-data',
259 | ['f'] = 'text/x-fortran',
260 | ['c11amc'] = 'application/vnd.cluetrust.cartomobile-config',
261 | ['torrent'] = 'application/x-bittorrent',
262 | ['cdkey'] = 'application/vnd.mediastation.cdkey',
263 | ['mj2'] = 'video/mj2',
264 | ['nns'] = 'application/vnd.noblenet-sealer',
265 | ['jpgm'] = 'video/jpm',
266 | ['jpm'] = 'video/jpm',
267 | ['jpgv'] = 'video/jpeg',
268 | ['h264'] = 'video/h264',
269 | ['mus'] = 'application/vnd.musician',
270 | ['ppsm'] = 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
271 | ['appcache'] = 'text/cache-manifest',
272 | ['rpss'] = 'application/vnd.nokia.radio-presets',
273 | ['smi'] = 'application/smil+xml',
274 | ['h263'] = 'video/h263',
275 | ['h261'] = 'video/h261',
276 | ['3g2'] = 'video/3gpp2',
277 | ['rtx'] = 'text/richtext',
278 | ['vcf'] = 'text/x-vcard',
279 | ['texi'] = 'application/x-texinfo',
280 | ['cdbcmsg'] = 'application/vnd.contact.cmsg',
281 | ['uu'] = 'text/x-uuencode',
282 | ['nsc'] = 'application/x-conference',
283 | ['pic'] = 'image/x-pict',
284 | ['cil'] = 'application/vnd.ms-artgalry',
285 | ['t'] = 'text/troff',
286 | ['psf'] = 'application/x-font-linux-psf',
287 | ['p'] = 'text/x-pascal',
288 | ['vsf'] = 'application/vnd.vsf',
289 | ['odc'] = 'application/vnd.oasis.opendocument.chart',
290 | ['lbe'] = 'application/vnd.llamagraphics.life-balance.exchange+xml',
291 | ['java'] = 'text/x-java-source',
292 | ['tfm'] = 'application/x-tex-tfm',
293 | ['cpio'] = 'application/x-cpio',
294 | ['gnumeric'] = 'application/x-gnumeric',
295 | ['for'] = 'text/x-fortran',
296 | ['uvu'] = 'video/vnd.uvvu.mp4',
297 | ['qxt'] = 'application/vnd.quark.quarkxpress',
298 | ['fxpl'] = 'application/vnd.adobe.fxp',
299 | ['hh'] = 'text/x-c',
300 | ['uvi'] = 'image/vnd.dece.graphic',
301 | ['cpp'] = 'text/x-c',
302 | ['x3db'] = 'model/x3d+binary',
303 | ['obd'] = 'application/x-msbinder',
304 | ['cc'] = 'text/x-c',
305 | ['rtf'] = 'application/rtf',
306 | ['ddd'] = 'application/vnd.fujixerox.ddd',
307 | ['ttf'] = 'application/x-font-ttf',
308 | ['iif'] = 'application/vnd.shana.informed.interchange',
309 | ['tao'] = 'application/vnd.tao.intent-module-archive',
310 | ['potm'] = 'application/vnd.ms-powerpoint.template.macroenabled.12',
311 | ['oxt'] = 'application/vnd.openofficeorg.extension',
312 | ['mif'] = 'application/vnd.mif',
313 | ['mk3d'] = 'video/x-matroska',
314 | ['mrc'] = 'application/marc',
315 | ['cxt'] = 'application/x-director',
316 | ['thmx'] = 'application/vnd.ms-officetheme',
317 | ['ext'] = 'application/vnd.novadigm.ext',
318 | ['tr'] = 'text/troff',
319 | ['gxt'] = 'application/vnd.geonext',
320 | ['rcprofile'] = 'application/vnd.ipunplugged.rcprofile',
321 | ['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
322 | ['wmls'] = 'text/vnd.wap.wmlscript',
323 | ['wml'] = 'text/vnd.wap.wml',
324 | ['dpg'] = 'application/vnd.dpgraph',
325 | ['wri'] = 'application/x-mswrite',
326 | ['cif'] = 'chemical/x-cif',
327 | ['wks'] = 'application/vnd.ms-works',
328 | ['ods'] = 'application/vnd.oasis.opendocument.spreadsheet',
329 | ['lasxml'] = 'application/vnd.las.las+xml',
330 | ['jpg'] = 'image/jpeg',
331 | ['ltf'] = 'application/vnd.frogans.ltf',
332 | ['spot'] = 'text/vnd.in3d.spot',
333 | ['3dml'] = 'text/vnd.in3d.3dml',
334 | ['curl'] = 'text/vnd.curl',
335 | ['xdp'] = 'application/vnd.adobe.xdp+xml',
336 | ['flx'] = 'text/vnd.fmi.flexstor',
337 | ['fly'] = 'text/vnd.fly',
338 | ['uvva'] = 'audio/vnd.dece.audio',
339 | ['p7r'] = 'application/x-pkcs7-certreqresp',
340 | ['cryptonote'] = 'application/vnd.rig.cryptonote',
341 | ['cdmio'] = 'application/cdmi-object',
342 | ['igx'] = 'application/vnd.micrografx.igx',
343 | ['smzip'] = 'application/vnd.stepmania.package',
344 | ['ogx'] = 'application/ogg',
345 | ['scs'] = 'application/scvp-cv-response',
346 | ['gv'] = 'text/vnd.graphviz',
347 | ['urls'] = 'text/uri-list',
348 | ['otc'] = 'application/vnd.oasis.opendocument.chart-template',
349 | ['png'] = 'image/png',
350 | ['uri'] = 'text/uri-list',
351 | ['ttl'] = 'text/turtle',
352 | ['metalink'] = 'application/metalink+xml',
353 | ['cmc'] = 'application/vnd.cosmocaller',
354 | ['ahead'] = 'application/vnd.ahead.space',
355 | ['taglet'] = 'application/vnd.mynfc',
356 | ['list'] = 'text/plain',
357 | ['dae'] = 'model/vnd.collada+xml',
358 | ['me'] = 'text/troff',
359 | ['srt'] = 'application/x-subrip',
360 | ['fgd'] = 'application/x-director',
361 | ['man'] = 'text/troff',
362 | ['jpe'] = 'image/jpeg',
363 | ['clkw'] = 'application/vnd.crick.clicker.wordbank',
364 | ['mfm'] = 'application/vnd.mfmp',
365 | ['pfb'] = 'application/x-font-type1',
366 | ['roff'] = 'text/troff',
367 | ['dtd'] = 'application/xml-dtd',
368 | ['ifm'] = 'application/vnd.shana.informed.formdata',
369 | ['pas'] = 'text/x-pascal',
370 | ['bz'] = 'application/x-bzip',
371 | ['ras'] = 'image/x-cmu-raster',
372 | ['car'] = 'application/vnd.curl.car',
373 | ['list3820'] = 'application/vnd.ibm.modcap',
374 | ['mgz'] = 'application/vnd.proteus.magazine',
375 | ['crt'] = 'application/x-x509-ca-cert',
376 | ['sbml'] = 'application/sbml+xml',
377 | ['pcurl'] = 'application/vnd.curl.pcurl',
378 | ['dbk'] = 'application/docbook+xml',
379 | ['sgm'] = 'text/sgml',
380 | ['ac'] = 'application/pkix-attr-cert',
381 | ['opf'] = 'application/oebps-package+xml',
382 | ['mar'] = 'application/octet-stream',
383 | ['asm'] = 'text/x-asm',
384 | ['wav'] = 'audio/x-wav',
385 | ['x3dvz'] = 'model/x3d+vrml',
386 | ['bpk'] = 'application/octet-stream',
387 | ['dtb'] = 'application/x-dtbook+xml',
388 | ['dsc'] = 'text/prs.lines.tag',
389 | ['wpd'] = 'application/vnd.wordperfect',
390 | ['xsl'] = 'application/xml',
391 | ['in'] = 'text/plain',
392 | ['stc'] = 'application/vnd.sun.xml.calc.template',
393 | ['musicxml'] = 'application/vnd.recordare.musicxml+xml',
394 | ['sc'] = 'application/vnd.ibm.secure-container',
395 | ['def'] = 'text/plain',
396 | ['uvs'] = 'video/vnd.dece.sd',
397 | ['wma'] = 'audio/x-ms-wma',
398 | ['text'] = 'text/plain',
399 | ['obj'] = 'application/x-tgif',
400 | ['cap'] = 'application/vnd.tcpdump.pcap',
401 | ['lvp'] = 'audio/vnd.lucent.voice',
402 | ['kfo'] = 'application/vnd.kde.kformula',
403 | ['sitx'] = 'application/x-stuffitx',
404 | ['jnlp'] = 'application/x-java-jnlp-file',
405 | ['rmvb'] = 'application/vnd.rn-realmedia-vbr',
406 | ['blb'] = 'application/x-blorb',
407 | ['evy'] = 'application/x-envoy',
408 | ['htm'] = 'text/html',
409 | ['html'] = 'text/html',
410 | ['sv4cpio'] = 'application/x-sv4cpio',
411 | ['mpkg'] = 'application/vnd.apple.installer+xml',
412 | ['xer'] = 'application/patch-ops-error+xml',
413 | ['cdmic'] = 'application/cdmi-container',
414 | ['csv'] = 'text/csv',
415 | ['aso'] = 'application/vnd.accpac.simply.aso',
416 | ['css'] = 'text/css',
417 | ['pki'] = 'application/pkixcmp',
418 | ['sdkm'] = 'application/vnd.solent.sdkm+xml',
419 | ['ifb'] = 'text/calendar',
420 | ['mcd'] = 'application/vnd.mcd',
421 | ['nnd'] = 'application/vnd.noblenet-directory',
422 | ['pbm'] = 'image/x-portable-bitmap',
423 | ['srx'] = 'application/sparql-results+xml',
424 | ['cdmiq'] = 'application/cdmi-queue',
425 | ['qxl'] = 'application/vnd.quark.quarkxpress',
426 | ['ics'] = 'text/calendar',
427 | ['icm'] = 'application/vnd.iccprofile',
428 | ['x3dv'] = 'model/x3d+vrml',
429 | ['vrml'] = 'model/vrml',
430 | ['xbm'] = 'image/x-xbitmap',
431 | ['spl'] = 'application/x-futuresplash',
432 | ['gtm'] = 'application/vnd.groove-tool-message',
433 | ['paw'] = 'application/vnd.pawaafile',
434 | ['csp'] = 'application/vnd.commonspace',
435 | ['jlt'] = 'application/vnd.hp-jlyt',
436 | ['rip'] = 'audio/vnd.rip',
437 | ['mxl'] = 'application/vnd.recordare.musicxml',
438 | ['rep'] = 'application/vnd.businessobjects',
439 | ['mts'] = 'model/vnd.mts',
440 | ['mads'] = 'application/mads+xml',
441 | ['gdl'] = 'model/vnd.gdl',
442 | ['ksp'] = 'application/vnd.kde.kspread',
443 | ['dwf'] = 'model/vnd.dwf',
444 | ['ms'] = 'text/troff',
445 | ['udeb'] = 'application/x-debian-package',
446 | ['iges'] = 'model/iges',
447 | ['fcdt'] = 'application/vnd.adobe.formscentral.fcdt',
448 | ['xo'] = 'application/vnd.olpc-sugar',
449 | ['xlt'] = 'application/vnd.ms-excel',
450 | ['gtar'] = 'application/x-gtar',
451 | ['p12'] = 'application/x-pkcs12',
452 | ['gqf'] = 'application/vnd.grafeq',
453 | ['wbmp'] = 'image/vnd.wap.wbmp',
454 | ['mime'] = 'message/rfc822',
455 | ['sv4crc'] = 'application/x-sv4crc',
456 | ['xwd'] = 'image/x-xwindowdump',
457 | ['qxb'] = 'application/vnd.quark.quarkxpress',
458 | ['xpm'] = 'image/x-xpixmap',
459 | ['tga'] = 'image/x-tga',
460 | ['seed'] = 'application/vnd.fdsn.seed',
461 | ['rss'] = 'application/rss+xml',
462 | ['ppm'] = 'image/x-portable-pixmap',
463 | ['fsc'] = 'application/vnd.fsc.weblaunch',
464 | ['cat'] = 'application/vnd.ms-pki.seccat',
465 | ['mwf'] = 'application/vnd.mfer',
466 | ['pnm'] = 'image/x-portable-anymap',
467 | ['pct'] = 'image/x-pict',
468 | ['pcx'] = 'image/x-pcx',
469 | ['gph'] = 'application/vnd.flographit',
470 | ['sid'] = 'image/x-mrsid-image',
471 | ['class'] = 'application/java-vm',
472 | ['c4p'] = 'application/vnd.clonk.c4group',
473 | ['mp3'] = 'audio/mpeg',
474 | ['uvvi'] = 'image/vnd.dece.graphic',
475 | ['fh4'] = 'image/x-freehand',
476 | ['rgb'] = 'image/x-rgb',
477 | ['vcg'] = 'application/vnd.groove-vcard',
478 | ['cmx'] = 'image/x-cmx',
479 | ['3ds'] = 'image/x-3ds',
480 | ['cdy'] = 'application/vnd.cinderella',
481 | ['webp'] = 'image/webp',
482 | ['xif'] = 'image/vnd.xiff',
483 | ['p10'] = 'application/pkcs10',
484 | ['wax'] = 'audio/x-ms-wax',
485 | ['npx'] = 'image/vnd.net-fpx',
486 | ['ufdl'] = 'application/vnd.ufdl',
487 | ['rlc'] = 'image/vnd.fujixerox.edmics-rlc',
488 | ['fst'] = 'image/vnd.fst',
489 | ['setreg'] = 'application/set-registration-initiation',
490 | ['mbox'] = 'application/mbox',
491 | ['kpxx'] = 'application/vnd.ds-keypoint',
492 | ['fbs'] = 'image/vnd.fastbidsheet',
493 | ['z1'] = 'application/x-zmachine',
494 | ['weba'] = 'audio/webm',
495 | ['clkt'] = 'application/vnd.crick.clicker.template',
496 | ['m13'] = 'application/x-msmediaview',
497 | ['cww'] = 'application/prs.cww',
498 | ['sxd'] = 'application/vnd.sun.xml.draw',
499 | ['xpw'] = 'application/vnd.intercon.formnet',
500 | ['dd2'] = 'application/vnd.oma.dd2+xml',
501 | ['odf'] = 'application/vnd.oasis.opendocument.formula',
502 | ['fzs'] = 'application/vnd.fuzzysheet',
503 | ['portpkg'] = 'application/vnd.macports.portpkg',
504 | ['oti'] = 'application/vnd.oasis.opendocument.image-template',
505 | ['hlp'] = 'application/winhlp',
506 | ['cst'] = 'application/x-director',
507 | ['mpn'] = 'application/vnd.mophun.application',
508 | ['xvm'] = 'application/xv+xml',
509 | ['cgm'] = 'image/cgm',
510 | ['fh5'] = 'image/x-freehand',
511 | ['h'] = 'text/x-c',
512 | ['xar'] = 'application/vnd.xara',
513 | ['vis'] = 'application/vnd.visionary',
514 | ['wqd'] = 'application/vnd.wqd',
515 | ['cbr'] = 'application/x-cbr',
516 | ['svgz'] = 'image/svg+xml',
517 | ['wpl'] = 'application/vnd.ms-wpl',
518 | ['lwp'] = 'application/vnd.lotus-wordpro',
519 | ['igm'] = 'application/vnd.insors.igm',
520 | ['flo'] = 'application/vnd.micrografx.flo',
521 | ['zirz'] = 'application/vnd.zul',
522 | ['svg'] = 'image/svg+xml',
523 | ['m3u8'] = 'application/vnd.apple.mpegurl',
524 | ['dra'] = 'audio/vnd.dra',
525 | ['oprc'] = 'application/vnd.palm',
526 | ['vxml'] = 'application/voicexml+xml',
527 | ['dis'] = 'application/vnd.mobius.dis',
528 | ['qps'] = 'application/vnd.publishare-delta-tree',
529 | ['vsw'] = 'application/vnd.visio',
530 | ['ntf'] = 'application/vnd.nitf',
531 | ['btif'] = 'image/prs.btif',
532 | ['uris'] = 'text/uri-list',
533 | ['mxf'] = 'application/mxf',
534 | ['wps'] = 'application/vnd.ms-works',
535 | ['aas'] = 'application/x-authorware-seg',
536 | ['c4d'] = 'application/vnd.clonk.c4group',
537 | ['zip'] = 'application/zip',
538 | ['gif'] = 'image/gif',
539 | ['susp'] = 'application/vnd.sus-calendar',
540 | ['air'] = 'application/vnd.adobe.air-application-installer-package+zip',
541 | ['ris'] = 'application/x-research-info-systems',
542 | ['pgn'] = 'application/x-chess-pgn',
543 | ['gxf'] = 'application/gxf',
544 | ['g3'] = 'image/g3fax',
545 | ['sdd'] = 'application/vnd.stardivision.impress',
546 | ['bmp'] = 'image/bmp',
547 | ['otg'] = 'application/vnd.oasis.opendocument.graphics-template',
548 | ['eps'] = 'application/postscript',
549 | ['xyz'] = 'chemical/x-xyz',
550 | ['sema'] = 'application/vnd.sema',
551 | ['csml'] = 'chemical/x-csml',
552 | ['swf'] = 'application/x-shockwave-flash',
553 | ['qxd'] = 'application/vnd.quark.quarkxpress',
554 | ['cdx'] = 'chemical/x-cdx',
555 | ['123'] = 'application/vnd.lotus-1-2-3',
556 | ['dotx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
557 | ['xm'] = 'audio/xm',
558 | ['spq'] = 'application/scvp-vp-request',
559 | ['jisp'] = 'application/vnd.jisp',
560 | ['rmp'] = 'audio/x-pn-realaudio-plugin',
561 | ['tex'] = 'application/x-tex',
562 | ['xlm'] = 'application/vnd.ms-excel',
563 | ['ppam'] = 'application/vnd.ms-powerpoint.addin.macroenabled.12',
564 | ['xht'] = 'application/xhtml+xml',
565 | ['odb'] = 'application/vnd.oasis.opendocument.database',
566 | ['ram'] = 'audio/x-pn-realaudio',
567 | ['xfdl'] = 'application/vnd.xfdl',
568 | ['mka'] = 'audio/x-matroska',
569 | ['flac'] = 'audio/x-flac',
570 | ['aifc'] = 'audio/x-aiff',
571 | ['sfs'] = 'application/vnd.spotfire.sfs',
572 | ['rl'] = 'application/resource-lists+xml',
573 | ['wdb'] = 'application/vnd.ms-works',
574 | ['tra'] = 'application/vnd.trueapp',
575 | ['dfac'] = 'application/vnd.dreamfactory',
576 | ['aif'] = 'audio/x-aiff',
577 | ['mp2'] = 'audio/mpeg',
578 | ['vtu'] = 'model/vnd.vtu',
579 | ['mny'] = 'application/x-msmoney',
580 | ['ecelp7470'] = 'audio/vnd.nuera.ecelp7470',
581 | ['ppsx'] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
582 | ['kon'] = 'application/vnd.kde.kontour',
583 | ['odp'] = 'application/vnd.oasis.opendocument.presentation',
584 | ['twds'] = 'application/vnd.simtech-mindmapper',
585 | ['dtshd'] = 'audio/vnd.dts.hd',
586 | ['dts'] = 'audio/vnd.dts',
587 | ['sit'] = 'application/x-stuffit',
588 | ['ipfix'] = 'application/ipfix',
589 | ['sgi'] = 'image/sgi',
590 | ['application'] = 'application/x-ms-application',
591 | ['roa'] = 'application/rpki-roa',
592 | ['xaml'] = 'application/xaml+xml',
593 | ['sil'] = 'audio/silk',
594 | ['smil'] = 'application/smil+xml',
595 | ['s3m'] = 'audio/s3m',
596 | ['ogg'] = 'audio/ogg',
597 | ['chm'] = 'application/vnd.ms-htmlhelp',
598 | ['mscml'] = 'application/mediaservercontrol+xml',
599 | ['atom'] = 'application/atom+xml',
600 | ['eva'] = 'application/x-eva',
601 | ['oga'] = 'audio/ogg',
602 | ['prc'] = 'application/x-mobipocket-ebook',
603 | ['sldm'] = 'application/vnd.ms-powerpoint.slide.macroenabled.12',
604 | ['m2a'] = 'audio/mpeg',
605 | ['frame'] = 'application/vnd.framemaker',
606 | ['fh7'] = 'image/x-freehand',
607 | ['mp2a'] = 'audio/mpeg',
608 | ['aac'] = 'audio/x-aac',
609 | ['mpga'] = 'audio/mpeg',
610 | ['rmi'] = 'audio/midi',
611 | ['m21'] = 'application/mp21',
612 | ['kar'] = 'audio/midi',
613 | ['p7b'] = 'application/x-pkcs7-certificates',
614 | ['gac'] = 'application/vnd.groove-account',
615 | ['midi'] = 'audio/midi',
616 | ['mid'] = 'audio/midi',
617 | ['ott'] = 'application/vnd.oasis.opendocument.text-template',
618 | ['rif'] = 'application/reginfo+xml',
619 | ['cfs'] = 'application/x-cfs-compressed',
620 | ['au'] = 'audio/basic',
621 | ['adp'] = 'audio/adpcm',
622 | ['pfr'] = 'application/font-tdpfr',
623 | ['ief'] = 'image/ief',
624 | ['xul'] = 'application/vnd.mozilla.xul+xml',
625 | ['dart'] = 'application/vnd.dart',
626 | ['yin'] = 'application/yin+xml',
627 | ['pya'] = 'audio/vnd.ms-playready.media.pya',
628 | ['uvg'] = 'image/vnd.dece.graphic',
629 | ['str'] = 'application/vnd.pg.format',
630 | ['tiff'] = 'image/tiff',
631 | ['crd'] = 'application/x-mscardfile',
632 | ['c4f'] = 'application/vnd.clonk.c4group',
633 | ['xhvml'] = 'application/xv+xml',
634 | ['ssdl'] = 'application/ssdl+xml',
635 | ['mxml'] = 'application/xv+xml',
636 | ['g2w'] = 'application/vnd.geoplan',
637 | ['xslt'] = 'application/xslt+xml',
638 | ['nnw'] = 'application/vnd.noblenet-web',
639 | ['xop'] = 'application/xop+xml',
640 | ['flv'] = 'video/x-flv',
641 | ['ra'] = 'audio/x-pn-realaudio',
642 | ['tpt'] = 'application/vnd.trid.tpt',
643 | ['bdm'] = 'application/vnd.syncml.dm+wbxml',
644 | ['davmount'] = 'application/davmount+xml',
645 | ['z6'] = 'application/x-zmachine',
646 | ['xenc'] = 'application/xenc+xml',
647 | ['yang'] = 'application/yang',
648 | ['uva'] = 'audio/vnd.dece.audio',
649 | ['ez2'] = 'application/vnd.ezpix-album',
650 | ['dmp'] = 'application/vnd.tcpdump.pcap',
651 | ['mets'] = 'application/mets+xml',
652 | ['z8'] = 'application/x-zmachine',
653 | ['xfdf'] = 'application/vnd.adobe.xfdf',
654 | ['fcs'] = 'application/vnd.isac.fcs',
655 | ['z7'] = 'application/x-zmachine',
656 | ['xdssc'] = 'application/dssc+xml',
657 | ['z5'] = 'application/x-zmachine',
658 | ['z4'] = 'application/x-zmachine',
659 | ['daf'] = 'application/vnd.mobius.daf',
660 | ['sql'] = 'application/x-sql',
661 | ['z3'] = 'application/x-zmachine',
662 | ['z2'] = 'application/x-zmachine',
663 | ['uvvt'] = 'application/vnd.dece.ttml+xml',
664 | ['xz'] = 'application/x-xz',
665 | ['xdm'] = 'application/vnd.syncml.dm+xml',
666 | ['xpi'] = 'application/x-xpinstall',
667 | ['fig'] = 'application/x-xfig',
668 | ['der'] = 'application/x-x509-ca-cert',
669 | ['chrt'] = 'application/vnd.kde.kchart',
670 | ['vcs'] = 'text/x-vcalendar',
671 | ['pgp'] = 'application/pgp-encrypted',
672 | ['onetmp'] = 'application/onenote',
673 | ['mjp2'] = 'video/mj2',
674 | ['pvb'] = 'application/vnd.3gpp.pic-bw-var',
675 | ['f90'] = 'text/x-fortran',
676 | ['tar'] = 'application/x-tar',
677 | ['hvp'] = 'application/vnd.yamaha.hv-voice',
678 | ['afm'] = 'application/x-font-type1',
679 | ['semd'] = 'application/vnd.semd',
680 | ['mp4s'] = 'application/mp4',
681 | ['eml'] = 'message/rfc822',
682 | ['otp'] = 'application/vnd.oasis.opendocument.presentation-template',
683 | ['gml'] = 'application/gml+xml',
684 | ['itp'] = 'application/vnd.shana.informed.formtemplate',
685 | ['st'] = 'application/vnd.sailingtracker.track',
686 | ['svc'] = 'application/vnd.dvb.service',
687 | ['cml'] = 'chemical/x-cml',
688 | ['pkipath'] = 'application/pkix-pkipath',
689 | ['sh'] = 'application/x-sh',
690 | ['spf'] = 'application/vnd.yamaha.smaf-phrase',
691 | ['mcurl'] = 'text/vnd.curl.mcurl',
692 | ['spc'] = 'application/x-pkcs7-certificates',
693 | ['rq'] = 'application/sparql-query',
694 | ['uvvv'] = 'video/vnd.dece.video',
695 | ['cdf'] = 'application/x-netcdf',
696 | ['atomcat'] = 'application/atomcat+xml',
697 | ['jad'] = 'text/vnd.sun.j2me.app-descriptor',
698 | ['gqs'] = 'application/vnd.grafeq',
699 | ['bin'] = 'application/octet-stream',
700 | ['m3u'] = 'audio/x-mpegurl',
701 | ['scd'] = 'application/x-msschedule',
702 | ['pub'] = 'application/x-mspublisher',
703 | ['sm'] = 'application/vnd.stepmania.stepchart',
704 | ['ecelp9600'] = 'audio/vnd.nuera.ecelp9600',
705 | ['mpy'] = 'application/vnd.ibm.minipay',
706 | ['xlam'] = 'application/vnd.ms-excel.addin.macroenabled.12',
707 | ['vox'] = 'application/x-authorware-bin',
708 | ['emf'] = 'application/x-msmetafile',
709 | ['mc1'] = 'application/vnd.medcalcdata',
710 | ['zmm'] = 'application/vnd.handheld-entertainment+xml',
711 | ['mxs'] = 'application/vnd.triscape.mxs',
712 | ['msi'] = 'application/x-msdownload',
713 | ['mseq'] = 'application/vnd.mseq',
714 | ['emma'] = 'application/emma+xml',
715 | ['bat'] = 'application/x-msdownload',
716 | ['com'] = 'application/x-msdownload',
717 | ['exe'] = 'application/x-msdownload',
718 | ['xpx'] = 'application/vnd.intercon.formnet',
719 | ['twd'] = 'application/vnd.simtech-mindmapper',
720 | ['clp'] = 'application/x-msclip',
721 | ['mdb'] = 'application/x-msaccess',
722 | ['xbap'] = 'application/x-ms-xbap',
723 | ['xlc'] = 'application/vnd.ms-excel',
724 | ['fpx'] = 'image/vnd.fpx',
725 | ['wmd'] = 'application/x-ms-wmd',
726 | ['lnk'] = 'application/x-ms-shortcut',
727 | ['lrm'] = 'application/vnd.ms-lrm',
728 | ['latex'] = 'application/x-latex',
729 | ['xdw'] = 'application/vnd.fujixerox.docuworks',
730 | ['dump'] = 'application/octet-stream',
731 | ['plc'] = 'application/vnd.mobius.plc',
732 | ['cdxml'] = 'application/vnd.chemdraw+xml',
733 | ['umj'] = 'application/vnd.umajin',
734 | ['osfpvg'] = 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
735 | ['mobi'] = 'application/x-mobipocket-ebook',
736 | ['dxr'] = 'application/x-director',
737 | ['mie'] = 'application/x-mie',
738 | ['ei6'] = 'application/vnd.pg.osasli',
739 | ['lzh'] = 'application/x-lzh-compressed',
740 | ['dxp'] = 'application/vnd.spotfire.dxp',
741 | ['iso'] = 'application/x-iso9660-image',
742 | ['install'] = 'application/x-install-instructions',
743 | ['hdf'] = 'application/x-hdf',
744 | ['igs'] = 'model/iges',
745 | ['ims'] = 'application/vnd.ms-ims',
746 | ['dist'] = 'application/octet-stream',
747 | ['efif'] = 'application/vnd.picsel',
748 | ['wrl'] = 'model/vrml',
749 | ['esf'] = 'application/vnd.epson.esf',
750 | ['link66'] = 'application/vnd.route66.link66+xml',
751 | ['woff'] = 'application/font-woff',
752 | ['gim'] = 'application/vnd.groove-identity-message',
753 | ['t3'] = 'application/x-t3vm-image',
754 | ['mp21'] = 'application/mp21',
755 | ['xla'] = 'application/vnd.ms-excel',
756 | ['odft'] = 'application/vnd.oasis.opendocument.formula-template',
757 | ['msf'] = 'application/vnd.epson.msf',
758 | ['ktr'] = 'application/vnd.kahootz',
759 | ['box'] = 'application/vnd.previewsystems.box',
760 | ['crl'] = 'application/pkix-crl',
761 | ['skp'] = 'application/vnd.koan',
762 | ['ttc'] = 'application/x-font-ttf',
763 | ['osf'] = 'application/vnd.yamaha.openscoreformat',
764 | ['oa2'] = 'application/vnd.fujitsu.oasys2',
765 | ['pcf'] = 'application/x-font-pcf',
766 | ['fxp'] = 'application/vnd.adobe.fxp',
767 | ['trm'] = 'application/x-msterminal',
768 | ['oxps'] = 'application/oxps',
769 | ['gsf'] = 'application/x-font-ghostscript',
770 | ['swa'] = 'application/x-director',
771 | ['mb'] = 'application/mathematica',
772 | ['bdf'] = 'application/x-font-bdf',
773 | ['mmf'] = 'application/vnd.smaf',
774 | ['ufd'] = 'application/vnd.ufdl',
775 | ['n3'] = 'text/n3',
776 | ['rdz'] = 'application/vnd.data-vision.rdz',
777 | ['pot'] = 'application/vnd.ms-powerpoint',
778 | ['ncx'] = 'application/x-dtbncx+xml',
779 | ['wad'] = 'application/x-doom',
780 | ['w3d'] = 'application/x-director',
781 | ['mpc'] = 'application/vnd.mophun.certificate',
782 | ['ktz'] = 'application/vnd.kahootz',
783 | ['cct'] = 'application/x-director',
784 | ['dir'] = 'application/x-director',
785 | ['distz'] = 'application/octet-stream',
786 | ['rpst'] = 'application/vnd.nokia.radio-preset',
787 | ['csh'] = 'application/x-csh',
788 | ['skd'] = 'application/vnd.koan',
789 | ['jsonml'] = 'application/jsonml+json',
790 | ['sisx'] = 'application/vnd.symbian.install',
791 | ['boz'] = 'application/x-bzip2',
792 | ['deploy'] = 'application/octet-stream',
793 | ['msh'] = 'model/mesh',
794 | ['epub'] = 'application/epub+zip',
795 | ['smf'] = 'application/vnd.stardivision.math',
796 | ['dmg'] = 'application/x-apple-diskimage',
797 | ['deb'] = 'application/x-debian-package',
798 | ['f77'] = 'text/x-fortran',
799 | ['ssf'] = 'application/vnd.epson.ssf',
800 | ['etx'] = 'text/x-setext',
801 | ['chat'] = 'application/x-chat',
802 | ['shar'] = 'application/x-shar',
803 | ['wbxml'] = 'application/vnd.wap.wbxml',
804 | ['atx'] = 'application/vnd.antix.game-component',
805 | ['unityweb'] = 'application/vnd.unity',
806 | ['cbz'] = 'application/x-cbr',
807 | ['cbt'] = 'application/x-cbr',
808 | ['ait'] = 'application/vnd.dvb.ait',
809 | ['cba'] = 'application/x-cbr',
810 | ['bz2'] = 'application/x-bzip2',
811 | ['blorb'] = 'application/x-blorb',
812 | ['txt'] = 'text/plain',
813 | ['bcpio'] = 'application/x-bcpio',
814 | ['ktx'] = 'image/ktx',
815 | ['scm'] = 'application/vnd.lotus-screencam',
816 | ['lbd'] = 'application/vnd.llamagraphics.life-balance.desktop',
817 | ['mmd'] = 'application/vnd.chipnuts.karaoke-mmd',
818 | ['x32'] = 'application/x-authorware-bin',
819 | ['ecma'] = 'application/ecmascript',
820 | ['pbd'] = 'application/vnd.powerbuilder6',
821 | ['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
822 | ['stf'] = 'application/vnd.wt.stf',
823 | ['hal'] = 'application/vnd.hal+xml',
824 | ['aab'] = 'application/x-authorware-bin',
825 | ['json'] = 'application/json',
826 | ['uvt'] = 'application/vnd.dece.ttml+xml',
827 | ['pfa'] = 'application/x-font-type1',
828 | ['xbd'] = 'application/vnd.fujixerox.docuworks.binder',
829 | ['7z'] = 'application/x-7z-compressed',
830 | ['wspolicy'] = 'application/wspolicy+xml',
831 | ['zaz'] = 'application/vnd.zzazz.deck+xml',
832 | ['aam'] = 'application/x-authorware-map',
833 | ['rar'] = 'application/x-rar-compressed',
834 | ['saf'] = 'application/vnd.yamaha.smaf-audio',
835 | ['exi'] = 'application/exi',
836 | ['azf'] = 'application/vnd.airzip.filesecure.azf',
837 | ['msty'] = 'application/vnd.muvee.style',
838 | ['gam'] = 'application/x-tads',
839 | ['grxml'] = 'application/srgs+xml',
840 | ['ghf'] = 'application/vnd.groove-help',
841 | ['conf'] = 'text/plain',
842 | ['otf'] = 'application/x-font-otf',
843 | ['hqx'] = 'application/mac-binhex40',
844 | ['ssml'] = 'application/ssml+xml',
845 | ['tif'] = 'image/tiff',
846 | ['xvml'] = 'application/xv+xml',
847 | ['pkg'] = 'application/octet-stream',
848 | ['qam'] = 'application/vnd.epson.quickanime',
849 | ['bmi'] = 'application/vnd.bmi',
850 | ['pcap'] = 'application/vnd.tcpdump.pcap',
851 | ['ftc'] = 'application/vnd.fluxtime.clip',
852 | ['wmlc'] = 'application/vnd.wap.wmlc',
853 | ['vss'] = 'application/vnd.visio',
854 | ['std'] = 'application/vnd.sun.xml.draw.template',
855 | ['vst'] = 'application/vnd.visio',
856 | ['vsd'] = 'application/vnd.visio',
857 | ['msl'] = 'application/vnd.mobius.msl',
858 | ['uvv'] = 'video/vnd.dece.video',
859 | ['uoml'] = 'application/vnd.uoml+xml',
860 | ['sig'] = 'application/pgp-signature',
861 | ['utz'] = 'application/vnd.uiq.theme',
862 | ['wdp'] = 'image/vnd.ms-photo',
863 | ['tmo'] = 'application/vnd.tmobile-livetv',
864 | ['mqy'] = 'application/vnd.mobius.mqy',
865 | ['skt'] = 'application/vnd.koan',
866 | ['sdp'] = 'application/sdp',
867 | ['vor'] = 'application/vnd.stardivision.writer',
868 | ['geo'] = 'application/vnd.dynageo',
869 | ['sru'] = 'application/sru+xml',
870 | ['tei'] = 'application/tei+xml',
871 | ['xap'] = 'application/x-silverlight-app',
872 | ['c'] = 'text/x-c',
873 | ['ez'] = 'application/andrew-inset',
874 | ['ink'] = 'application/inkml+xml',
875 | ['edm'] = 'application/vnd.novadigm.edm',
876 | ['gca'] = 'application/x-gca-compressed',
877 | ['cii'] = 'application/vnd.anser-web-certificate-issue-initiation',
878 | ['oas'] = 'application/vnd.fujitsu.oasys',
879 | ['onetoc'] = 'application/onenote',
880 | ['oa3'] = 'application/vnd.fujitsu.oasys3',
881 | ['pptm'] = 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
882 | ['wtb'] = 'application/vnd.webturbo',
883 | ['xlw'] = 'application/vnd.ms-excel',
884 | ['xsm'] = 'application/vnd.syncml+xml',
885 | ['dataless'] = 'application/vnd.fdsn.seed',
886 | ['iota'] = 'application/vnd.astraea-software.iota',
887 | ['dotm'] = 'application/vnd.ms-word.template.macroenabled.12',
888 | ['odt'] = 'application/vnd.oasis.opendocument.text',
889 | ['sxi'] = 'application/vnd.sun.xml.impress',
890 | ['uvp'] = 'video/vnd.dece.pd',
891 | ['pfm'] = 'application/x-font-type1',
892 | ['gbr'] = 'application/rpki-ghostbusters',
893 | ['pyv'] = 'video/vnd.ms-playready.media.pyv',
894 | ['f4v'] = 'video/x-f4v',
895 | ['uvvg'] = 'image/vnd.dece.graphic',
896 | ['pdf'] = 'application/pdf',
897 | ['apk'] = 'application/vnd.android.package-archive',
898 | ['swi'] = 'application/vnd.aristanetworks.swi',
899 | ['nc'] = 'application/x-netcdf',
900 | ['mrcx'] = 'application/marcxml+xml',
901 | ['meta4'] = 'application/metalink4+xml',
902 | ['ulx'] = 'application/x-glulx',
903 | ['rs'] = 'application/rls-services+xml',
904 | ['tcap'] = 'application/vnd.3gpp2.tcap',
905 | ['ipk'] = 'application/vnd.shana.informed.package',
906 | ['acc'] = 'application/vnd.americandynamics.acc',
907 | ['potx'] = 'application/vnd.openxmlformats-officedocument.presentationml.template',
908 | ['bh2'] = 'application/vnd.fujitsu.oasysprs',
909 | ['nfo'] = 'text/x-nfo',
910 | ['ppd'] = 'application/vnd.cups-ppd',
911 | ['mathml'] = 'application/mathml+xml',
912 | ['onepkg'] = 'application/onenote',
913 | ['azs'] = 'application/vnd.airzip.filesecure.azs',
914 | ['icc'] = 'application/vnd.iccprofile',
915 | ['eol'] = 'audio/vnd.digital-winds',
916 | ['fti'] = 'application/vnd.anser-web-funds-transfer-initiation',
917 | ['joda'] = 'application/vnd.joost.joda-archive',
918 | ['ivp'] = 'application/vnd.immervision-ivp',
919 | ['acutc'] = 'application/vnd.acucorp',
920 | ['uvvu'] = 'video/vnd.uvvu.mp4',
921 | ['ser'] = 'application/java-serialized-object',
922 | ['fnc'] = 'application/vnd.frogans.fnc',
923 | ['wg'] = 'application/vnd.pmi.widget',
924 | ['irp'] = 'application/vnd.irepository.package+xml',
925 | ['karbon'] = 'application/vnd.kde.karbon',
926 | ['p7s'] = 'application/pkcs7-signature',
927 | ['mseed'] = 'application/vnd.fdsn.mseed',
928 | ['pre'] = 'application/vnd.lotus-freelance',
929 | ['pwn'] = 'application/vnd.3m.post-it-notes',
930 | ['semf'] = 'application/vnd.semf',
931 | ['nitf'] = 'application/vnd.nitf',
932 | ['m1v'] = 'video/mpeg',
933 | ['aw'] = 'application/applixware',
934 | ['jar'] = 'application/java-archive',
935 | ['hvs'] = 'application/vnd.yamaha.hv-script',
936 | ['kia'] = 'application/vnd.kidspiration',
937 | ['flw'] = 'application/vnd.kde.kivio',
938 | ['ace'] = 'application/x-ace-compressed',
939 | ['vcd'] = 'application/x-cdlink',
940 | ['s'] = 'text/x-asm',
941 | ['plb'] = 'application/vnd.3gpp.pic-bw-large',
942 | ['i2g'] = 'application/vnd.intergeo',
943 | ['pclxl'] = 'application/vnd.hp-pclxl',
944 | ['qfx'] = 'application/vnd.intu.qfx',
945 | ['qwt'] = 'application/vnd.quark.quarkxpress',
946 | ['snd'] = 'audio/basic',
947 | ['xls'] = 'application/vnd.ms-excel',
948 | ['listafp'] = 'application/vnd.ibm.modcap',
949 | ['mag'] = 'application/vnd.ecowin.chart',
950 | ['slt'] = 'application/vnd.epson.salt',
951 | ['tfi'] = 'application/thraud+xml',
952 | ['x3d'] = 'model/x3d+xml',
953 | ['sxm'] = 'application/vnd.sun.xml.math',
954 | ['teacher'] = 'application/vnd.smart.teacher',
955 | ['pls'] = 'application/pls+xml',
956 | ['rsd'] = 'application/rsd+xml',
957 | ['ps'] = 'application/postscript',
958 | ['so'] = 'application/octet-stream',
959 | ['wmz'] = 'application/x-msmetafile',
960 | ['aiff'] = 'audio/x-aiff',
961 | ['nlu'] = 'application/vnd.neurolanguage.nlu',
962 | ['les'] = 'application/vnd.hhe.lesson-player',
963 | ['grv'] = 'application/vnd.groove-injector',
964 | ['wcm'] = 'application/vnd.ms-works',
965 | ['omdoc'] = 'application/omdoc+xml',
966 | ['hpgl'] = 'application/vnd.hp-hpgl',
967 | ['kwt'] = 'application/vnd.kde.kword',
968 | ['res'] = 'application/x-dtbresource+xml',
969 | ['cer'] = 'application/pkix-cert',
970 | ['ecelp4800'] = 'audio/vnd.nuera.ecelp4800',
971 | ['ggb'] = 'application/vnd.geogebra.file',
972 | ['edx'] = 'application/vnd.novadigm.edx',
973 | ['xltm'] = 'application/vnd.ms-excel.template.macroenabled.12',
974 | ['ivu'] = 'application/vnd.immervision-ivu',
975 | ['djvu'] = 'image/vnd.djvu',
976 | ['ngdat'] = 'application/vnd.nokia.n-gage.data',
977 | ['dna'] = 'application/vnd.dna',
978 | ['snf'] = 'application/x-font-snf',
979 | ['js'] = 'application/javascript',
980 | ['xpr'] = 'application/vnd.is-xpr',
981 | ['jam'] = 'application/vnd.jam',
982 | ['stl'] = 'application/vnd.ms-pki.stl',
983 | ['afp'] = 'application/vnd.ibm.modcap',
984 | ['p8'] = 'application/pkcs8',
985 | ['kne'] = 'application/vnd.kinar',
986 | ['xltx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
987 | ['ez3'] = 'application/vnd.ezpix-package',
988 | ['dgc'] = 'application/x-dgc-compressed',
989 | ['sxc'] = 'application/vnd.sun.xml.calc',
990 | ['clkk'] = 'application/vnd.crick.clicker.keyboard',
991 | ['c11amz'] = 'application/vnd.cluetrust.cartomobile-config-pkg',
992 | ['hbci'] = 'application/vnd.hbci',
993 | ['clkp'] = 'application/vnd.crick.clicker.palette',
994 | ['skm'] = 'application/vnd.koan',
995 | ['zir'] = 'application/vnd.zul',
996 | ['atc'] = 'application/vnd.acucorp',
997 | ['sxg'] = 'application/vnd.sun.xml.writer.global',
998 | ['wmf'] = 'application/x-msmetafile',
999 | ['texinfo'] = 'application/x-texinfo',
1000 | ['mbk'] = 'application/vnd.mobius.mbk',
1001 | ['wbs'] = 'application/vnd.criticaltools.wbs+xml',
1002 | ['g3w'] = 'application/vnd.geospace',
1003 | ['aep'] = 'application/vnd.audiograph',
1004 | ['sxw'] = 'application/vnd.sun.xml.writer',
1005 | ['emz'] = 'application/x-msmetafile',
1006 | ['mlp'] = 'application/vnd.dolby.mlp',
1007 | ['odg'] = 'application/vnd.oasis.opendocument.graphics',
1008 | ['xlsb'] = 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
1009 | ['ptid'] = 'application/vnd.pvi.ptid1',
1010 | ['xlsm'] = 'application/vnd.ms-excel.sheet.macroenabled.12',
1011 | ['setpay'] = 'application/set-payment-initiation',
1012 | ['maker'] = 'application/vnd.framemaker',
1013 | ['igl'] = 'application/vnd.igloader',
1014 | ['dot'] = 'application/msword',
1015 | ['eot'] = 'application/vnd.ms-fontobject',
1016 | ['fdf'] = 'application/vnd.fdf',
1017 | ['ppt'] = 'application/vnd.ms-powerpoint',
1018 | ['uvx'] = 'application/vnd.dece.unspecified',
1019 | ['sgl'] = 'application/vnd.stardivision.writer-global',
1020 | ['rdf'] = 'application/rdf+xml',
1021 | ['m3a'] = 'audio/mpeg',
1022 | ['mft'] = 'application/rpki-manifest',
1023 | ['mpp'] = 'application/vnd.ms-project',
1024 | ['docm'] = 'application/vnd.ms-word.document.macroenabled.12',
1025 | ['tsv'] = 'text/tab-separated-values',
1026 | ['tpl'] = 'application/vnd.groove-tool-template',
1027 | ['rnc'] = 'application/relax-ng-compact-syntax',
1028 | ['onetoc2'] = 'application/onenote',
1029 | ['clkx'] = 'application/vnd.crick.clicker',
1030 | ['xpl'] = 'application/xproc+xml',
1031 | ['c4u'] = 'application/vnd.clonk.c4group',
1032 | ['opml'] = 'text/x-opml',
1033 | ['cdmia'] = 'application/cdmi-capability',
1034 | ['xdf'] = 'application/xcap-diff+xml',
1035 | ['gmx'] = 'application/vnd.gmx',
1036 | ['pgm'] = 'image/x-portable-graymap',
1037 | ['sub'] = 'text/vnd.dvb.subtitle',
1038 | ['sldx'] = 'application/vnd.openxmlformats-officedocument.presentationml.slide',
1039 | ['pps'] = 'application/vnd.ms-powerpoint',
1040 | ['txf'] = 'application/vnd.mobius.txf',
1041 | ['mgp'] = 'application/vnd.osgeo.mapguide.package',
1042 | ['pdb'] = 'application/vnd.palm',
1043 | ['pqa'] = 'application/vnd.palm',
1044 | ['xspf'] = 'application/xspf+xml',
1045 | ['cod'] = 'application/vnd.rim.cod',
1046 | ['htke'] = 'application/vnd.kenameaapp',
1047 | ['xps'] = 'application/vnd.ms-xpsdocument',
1048 | ['kml'] = 'application/vnd.google-earth.kml+xml',
1049 | ['scurl'] = 'text/vnd.curl.scurl',
1050 | ['uvz'] = 'application/vnd.dece.zip',
1051 | ['fh'] = 'image/x-freehand',
1052 | ['sis'] = 'application/vnd.symbian.install',
1053 | ['azw'] = 'application/vnd.amazon.ebook',
1054 | ['see'] = 'application/vnd.seemail',
1055 | ['stw'] = 'application/vnd.sun.xml.writer.template',
1056 | ['dxf'] = 'image/vnd.dxf',
1057 | ['sti'] = 'application/vnd.sun.xml.impress.template',
1058 |
1059 | -- aditional extensions
1060 |
1061 | ['vtt'] = 'text/vtt',
1062 | ['crx'] = 'application/x-chrome-extension',
1063 | ['htc'] = 'text/x-component',
1064 | ['manifest'] = 'text/cache-manifest',
1065 | ['buffer'] = 'application/octet-stream',
1066 | ['m4p'] = 'application/mp4',
1067 | ['m4a'] = 'audio/mp4',
1068 | ['ts'] = 'video/MP2T',
1069 | ['webapp'] = 'application/x-web-app-manifest+json',
1070 | ['lua'] = 'text/x-lua',
1071 | ['luac'] = 'application/x-lua-bytecode',
1072 | ['markdown'] = 'text/x-markdown',
1073 | ['md'] = 'text/x-markdown',
1074 | ['mkd'] = 'text/x-markdown',
1075 | ['ini'] = 'text/plain',
1076 | ['mdp'] = 'application/dash+xml',
1077 | ['map'] = 'application/json',
1078 | ['xsd'] = 'application/xml',
1079 | ['opus'] = 'audio/ogg',
1080 | ['gz'] = 'application/x-gzip'
1081 | },
1082 |
1083 | -- This contains filename overrides for certain files, like README files.
1084 | -- Sort them in the same order as extensions.
1085 |
1086 | filenames = {
1087 | ['COPYING'] = 'text/plain',
1088 | ['LICENSE'] = 'text/plain',
1089 | ['Makefile'] = 'text/x-makefile',
1090 | ['README'] = 'text/plain'
1091 | }
1092 | }
1093 |
1094 |
1095 | -- Creates a copy of the MIME types database for customization.
1096 |
1097 | function mimetypes.copy (db)
1098 | db = db or defaultdb
1099 | return copy(db)
1100 | end
1101 |
1102 |
1103 | -- Guesses the MIME type of the file with the given name.
1104 | -- It is returned as a string. If the type cannot be guessed, then nil is
1105 | -- returned.
1106 |
1107 | function mimetypes.guess (filename, db)
1108 | local filename = filename:lower()
1109 | db = db or defaultdb
1110 | if db.filenames[filename] then
1111 | return db.filenames[filename]
1112 | end
1113 | local ext = extension(filename)
1114 | if ext then
1115 | return db.extensions[ext]
1116 | end
1117 | return nil
1118 | end
1119 |
1120 | return mimetypes
1121 |
--------------------------------------------------------------------------------
/libs/resty-template.lua:
--------------------------------------------------------------------------------
1 | local setmetatable = setmetatable
2 | local tostring = tostring
3 | local setfenv = setfenv
4 | local concat = table.concat
5 | local assert = assert
6 | local open = io.open
7 | local load = load
8 | local type = type
9 | local dump = string.dump
10 | local find = string.find
11 | local gsub = string.gsub
12 | local byte = string.byte
13 | local sub = string.sub
14 |
15 | local lpath = require("path")
16 |
17 | local HTML_ENTITIES = {
18 | ["&"] = "&",
19 | ["<"] = "<",
20 | [">"] = ">",
21 | ['"'] = """,
22 | ["'"] = "'",
23 | ["/"] = "/"
24 | }
25 |
26 | local CODE_ENTITIES = {
27 | ["{"] = "{",
28 | ["}"] = "}",
29 | ["&"] = "&",
30 | ["<"] = "<",
31 | [">"] = ">",
32 | ['"'] = """,
33 | ["'"] = "'",
34 | ["/"] = "/"
35 | }
36 |
37 | local ok, newtab = pcall(require, "table.new")
38 | if not ok then newtab = function() return {} end end
39 |
40 | local caching = true
41 | local template = newtab(0, 13);
42 |
43 | template._VERSION = "1.5"
44 | template.cache = {}
45 | template.concat = concat
46 |
47 | local function rpos(view, s)
48 | while s > 0 do
49 | local c = sub(view, s, s)
50 | if c == " " or c == "\t" or c == "\0" or c == "\x0B" then
51 | s = s - 1
52 | else
53 | break;
54 | end
55 | end
56 | return s
57 | end
58 |
59 | local function read_file(path)
60 | local file = open(path, "rb")
61 | if not file then return nil end
62 | local content = file:read "*a"
63 | file:close()
64 | return content
65 | end
66 |
67 | local function load_lua(path)
68 | return read_file(path) or path
69 | end
70 |
71 |
72 | template.load = load_lua
73 |
74 | local load_chunk
75 |
76 | if _VERSION == "Lua 5.1" then
77 | local context = { __index = function(t, k)
78 | return t.context[k] or t.template[k] or _G[k]
79 | end }
80 | if jit then
81 | load_chunk = function(view)
82 | return assert(load(view, nil, "tb", setmetatable({ template = template }, context)))
83 | end
84 | else
85 | load_chunk = function(view)
86 | local func = assert(loadstring(view))
87 | setfenv(func, setmetatable({ template = template }, context))
88 | return func
89 | end
90 | end
91 | else
92 | local context = { __index = function(t, k)
93 | return t.context[k] or t.template[k] or _ENV[k]
94 | end }
95 | load_chunk = function(view)
96 | return assert(load(view, nil, "tb", setmetatable({ template = template }, context)))
97 | end
98 | end
99 |
100 | function template.caching(enable)
101 | if enable ~= nil then caching = enable == true end
102 | return caching
103 | end
104 |
105 | function template.output(s)
106 | if s == nil then return "" end
107 | if type(s) == "function" then return template.output(s()) end
108 | return tostring(s)
109 | end
110 |
111 | function template.escape(s, c)
112 | if type(s) == "string" then
113 | if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end
114 | return gsub(s, "[\">/<'&]", HTML_ENTITIES)
115 | end
116 | return template.output(s)
117 | end
118 |
119 | function template.new(view, layout)
120 | assert(view, "view was not provided for template.new(view, layout).")
121 | local render, compile = template.render, template.compile
122 | if layout then
123 | return setmetatable({ render = function(self, context)
124 | local context = context or self
125 | context.blocks = context.blocks or {}
126 | context.view = compile(view)(context)
127 | return render(layout, context)
128 | end }, { __tostring = function(self)
129 | local context = context or self
130 | context.blocks = context.blocks or {}
131 | context.view = compile(view)(context)
132 | return compile(layout)(context)
133 | end })
134 | end
135 | return setmetatable({ render = function(self, context)
136 | return render(view, context or self)
137 | end }, { __tostring = function(self)
138 | return compile(view)(context or self)
139 | end })
140 | end
141 |
142 | function template.precompile(view, path, strip)
143 | local chunk = dump(template.compile(view), strip ~= false)
144 | if path then
145 | local file = open(path, "wb")
146 | file:write(chunk)
147 | file:close()
148 | end
149 | return chunk
150 | end
151 |
152 | function template.compile(view, key, plain)
153 | assert(view, "view was not provided for template.compile(view, key, plain).")
154 | if key == "no-cache" then
155 | return load_chunk(template.parse(view, plain, key)), false
156 | end
157 | key = key or view
158 | local cache = template.cache
159 | if cache[key] then return cache[key], true end
160 | local func = load_chunk(template.parse(view, plain, key))
161 | if caching then cache[key] = func end
162 | return func, false
163 | end
164 |
165 | function template.parse(view, plain, key)
166 | assert(view, "view was not provided for template.parse(view, plain).")
167 | local concat, rpos, find, byte, sub = concat, rpos, find, byte, sub
168 | if not plain then
169 | filePath = view
170 | baseDir = lpath.dirname(filePath)
171 | view = template.load(view)
172 | if byte(sub(view, 1, 1)) == 27 then return view end
173 | end
174 | local c = {[[
175 | context=(...) or {}
176 | local function include(v, c, key)
177 | return template.compile(v, key)(c or context)
178 | end
179 | local ___,blocks,layout={},blocks or {}
180 | ]]}
181 | local i, s = 1, find(view, "{", 1, true)
182 | while s do
183 | local t, p, d, z, r = sub(view, s + 1, s + 1), s + 2
184 | if t == "{" then
185 | local e = find(view, "}}", p, true)
186 | if e then
187 | d = concat{"___[#___+1]=template.escape(", sub(view, p, e - 1), ")\n" }
188 | z = e + 1
189 | end
190 | elseif t == "*" then
191 | local e = (find(view, "*}", p, true))
192 | if e then
193 | d = concat{"___[#___+1]=template.output(", sub(view, p, e - 1), ")\n" }
194 | z = e + 1
195 | end
196 | elseif t == "%" then
197 | local e = find(view, "%}", p, true)
198 | if e then
199 | local n = e + 2
200 | if sub(view, n, n) == "\n" then
201 | n = n + 1
202 | end
203 | d = concat{sub(view, p, e - 1), "\n" }
204 | z, r = n - 1, true
205 | end
206 | elseif t == "(" then
207 | local e = find(view, ")}", p, true)
208 | if e then
209 | local f = sub(view, p, e - 1)
210 | local x = (find(f, ",", 2, true))
211 | if x then
212 | f = f:gsub(" ", function()return "" end)
213 | local x = (find(f, ",", 2, true))
214 | local fpt = lpath.resolve(baseDir,f:sub(0,f:find(',',2, true)-1))
215 | d = concat{"___[#___+1]=include([=[", fpt, "]=],", sub(f, x + 1), ",'",key,"'", ")\n"}
216 | else
217 | d = concat{"___[#___+1]=include([=[", lpath.resolve(baseDir,f), "]=], nil, '", key, "')\n" }
218 | end
219 | z = e + 1
220 | end
221 | elseif t == "[" then
222 | local e = find(view, "]}", p, true)
223 | if e then
224 | d = concat{"___[#___+1]=include(", sub(view, p, e - 1), ")\n" }
225 | z = e + 1
226 | end
227 | elseif t == "-" then
228 | local e = find(view, "-}", p, true)
229 | if e then
230 | local x, y = find(view, sub(view, s, e + 1), e + 2, true)
231 | if x then
232 | y = y + 1
233 | x = x - 1
234 | if sub(view, y, y) == "\n" then
235 | y = y + 1
236 | end
237 | local b = sub(view, p, e - 1)
238 | if b == "verbatim" or b == "raw" then
239 | d = concat{"___[#___+1]=[=[", sub(view, e + 2, x), "]=]\n" }
240 | z, r = y - 1, false
241 | else
242 | if sub(view, x, x) == "\n" then
243 | x = x - 1
244 | end
245 | d = concat{'blocks["', b, '"]=include[=[', sub(view, e + 2, x), "]=]\n" }
246 | z, r = y - 1, true
247 | end
248 |
249 | end
250 | end
251 | elseif t == "#" then
252 | local e = find(view, "#}", p, true)
253 | if e then
254 | e = e + 2
255 | if sub(view, e, e) == "\n" then
256 | e = e + 1
257 | end
258 | d = ""
259 | z, r = e - 1, true
260 | end
261 | end
262 | if d then
263 | c[#c+1] = concat{"___[#___+1]=[=[\n", sub(view, i, r and rpos(view, s - 1) or s - 1), "]=]\n" }
264 | if d ~= "" then
265 | c[#c+1] = d
266 | end
267 | s, i = z, z + 1
268 | end
269 | s = find(view, "{", s + 1, true)
270 | end
271 | c[#c+1] = concat{"___[#___+1]=[=[\n", sub(view, i), "]=]\n"}
272 | c[#c+1] = "return layout and include(layout,setmetatable({view=template.concat(___),blocks=blocks},{__index=context})) or template.concat(___)"
273 | return concat(c)
274 | end
275 |
276 | function template.render(view, context, key, plain)
277 | assert(view, "view was not provided for template.render(view, context, key, plain).")
278 | return template.compile(view, key, plain)(context)
279 | end
280 |
281 | return template
282 |
--------------------------------------------------------------------------------
/libs/template/403.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 403
6 |
27 |
28 |
29 | 403
30 | Your are not authorized to view this page.
31 |
32 |
33 |
--------------------------------------------------------------------------------
/libs/template/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 404
6 |
27 |
28 |
29 | 404
30 | The page your are looking for doesn't exist.
31 |
32 |
33 |
--------------------------------------------------------------------------------
/libs/template/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 500
6 |
27 |
28 |
29 | 500
30 | Internal Server Error.
31 |
32 |
33 |
--------------------------------------------------------------------------------
/libs/template/directory.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Index of {{ currentPath }}
6 |
7 |
8 | Index of {{ currentPath }}
9 |
10 |
11 |
12 | Type |
13 | Name |
14 | Size |
15 | Description |
16 | Last Modified |
17 |
18 |
19 |
20 |
|
21 |
22 |
23 |
24 | |
25 |
26 | __Parent Directory__
27 | |
28 |
29 | {% for idx, file in pairs(files) do %}
30 |
31 | {% if file.type == "file" then %}
32 |
33 | {% else %}
34 |
35 | {% end %}
36 | |
37 | {{ file.name }} |
38 | {{ file.size }} |
39 | - |
40 | {{ file.lastModified }} |
41 |
42 | {% end %}
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/main.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- Created by: Cyril.
3 | -- Created at: 15/11/21 下午4:19
4 | -- Email: houshoushuai@gmail.com
5 | --
6 |
7 | require("./init")
8 |
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mooncake",
3 | "version": "0.0.10",
4 | "description": "Express like web framework but powered by luvit.",
5 | "main": "index.lua",
6 | "scripts": {
7 | "test": "luvit test"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/cyrilis/mooncake.git"
12 | },
13 | "keywords": [
14 | "luvit",
15 | "lua",
16 | "expressjs",
17 | "mooncake",
18 | "web",
19 | "framework",
20 | "server",
21 | "moonscript"
22 | ],
23 | "author": "Cyril Hou (http://cyrilis.com/)",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/cyrilis/mooncake/issues"
27 | },
28 | "homepage": "https://github.com/cyrilis/mooncake"
29 | }
30 |
--------------------------------------------------------------------------------
/package.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- Created by Cyril Hou
3 | -- Date: 15/9/28
4 | -- Time: 2015-09-28 16:24:56
5 | --
6 | return {
7 | name = "cyrilis/mooncake",
8 | version = "0.1.11",
9 | homepage = "https://github.com/cyrilis/luvit-mooncake",
10 | description = "Web framework for Luvit lang.",
11 | tags = {"luvit", "web framework", "web", "application", "express", "mooncake", "framework"},
12 | license = "MIT",
13 | author = { name = "Cyril Hou", email = "houshoushuai@gmail.com"},
14 | dependencies = {
15 | "luvit/require",
16 | "luvit/pretty-print",
17 | "luvit/http",
18 | "luvit/https",
19 | "luvit/path",
20 | "luvit/fs",
21 | "luvit/json"
22 | },
23 | files = {
24 | "**.lua",
25 | "**.md",
26 | "**.html",
27 | "**.elua"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/init.lua:
--------------------------------------------------------------------------------
1 | local MoonCake = require("../")
2 | local server = MoonCake:new()
3 | local env = require("env")
4 |
5 | server:use(function(req, res, next)
6 | next()
7 | end)
8 |
9 | server:route("get", "/", function(req, res)
10 | res:render("./views/index.html", {
11 | title= "Hello world from MoonCake!",
12 | address = req.socket:address(),
13 | message = "You are welcome!",
14 | names = {"Tom", "Jerry", "Wof"},
15 | jquery = ''
16 | })
17 | end)
18 |
19 | server:get("/etlua", function(req, res)
20 | res:render("./views/index.elua", {
21 | title= "Hello world from MoonCake!",
22 | message = "You are welcome!",
23 | names = {"Tom", "Jerry", "Wof"},
24 | jquery = ''
25 | })
26 | end)
27 |
28 | server:route("get", "/users/:id", function(q, s)
29 | s:send("List User in Databases => " .. q.params.id)
30 | end)
31 |
32 | server:get("/setCookie", function(req, res)
33 | res:setCookie("WTF", "Test", {
34 | path = "/",
35 | httpOnly = true
36 | }):send("Set Cookie Test.")
37 | end)
38 |
39 | server:get("/removeCookie", function(req, res)
40 | res:removeCookie("WTF"):send("RemoveCookie")
41 | end)
42 |
43 | server:all("/hello.test", function(q, s)
44 | s:send("HELLO!")
45 | end)
46 |
47 | server:all("/hello.hello/*", function(q, s)
48 | s:send("Splat!")
49 | end)
50 |
51 | server:get("/WTF", function(q, s)
52 | s:redirect("/hello")
53 | end)
54 |
55 | server:get("/posts", function(q,s)
56 | s:send("Post list: ...")
57 | end)
58 |
59 | server:post("/posts/new", function(q,s)
60 | if q.body.title and q.body.content then
61 | p("new post")
62 | -- Save to DB:
63 | -- DB.save("post", {title = q.body.title, content = q.body.content})
64 | s:redirect("/posts")
65 | end
66 | end)
67 |
68 | server:post("/", function(req, res)
69 | p(req.files)
70 | res:json(req.files)
71 | end)
72 |
73 | server:static("./libs/", {
74 | root = "/static/",
75 | maxAge = 31536000 -- one year
76 | })
77 |
78 | -- Test render to file : renderToFile
79 | server:get("/cache", function (req, res, next)
80 | p("Cache test....")
81 | res:renderToFile("./views/index.html", {
82 | title= "Hello world from MoonCake!",
83 | message = "You are welcome!",
84 | names = {"Tom", "Jerry", "Wof"},
85 | jquery = ''
86 | }, "./cache.html");
87 | res:sendFile("./cache.html")
88 | end)
89 |
90 | server:get("/testError", function (req, res, next)
91 | next("ERROR: SOMETHING HAPPEND.")
92 | -- next()
93 | end)
94 |
95 | MoonCake.serverError = function (req, res, error)
96 | res:status(500):json(error)
97 | end
98 |
99 | server:start(8081)
100 |
--------------------------------------------------------------------------------
/test/views/footer.html:
--------------------------------------------------------------------------------
1 |