├── .gitignore
├── LICENSE
├── README.md
├── example
├── app.nim
├── conf
│ ├── Makefile
│ └── mongrel2.conf
├── fortunes_tmpl.nim
├── hellocookies.nim
├── helloworld.nim
├── model.nim
├── model_postgre.nim
├── model_redis.nim
├── nim.cfg
├── prepare_postgresql_database.sql
└── prepare_redis_database.sh
├── jdump.nim
├── nawak.babel
├── nawak_mongrel.nim
└── private
├── jesterpatterns.nim
├── jesterutils.nim
├── nawak_common.nim
├── nawak_mg.nim
├── netstrings.nim
├── optim_strange_loop.nim
└── tuple_index_setter.nim
/.gitignore:
--------------------------------------------------------------------------------
1 | nimcache
2 | .nimcache
3 | example/conf/run
4 | example/conf/logs
5 | example/conf/tmp
6 | example/conf/config.sqlite
7 | example/helloworld
8 | example/app_postgresql
9 | example/app_redis
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Erwan Ameil
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nawak
2 |
3 | A web micro-framework in Nimrod, heavily inspired by jester, flask and the like.
4 | It is only compatible with the `Mongrel2` server for now.
5 |
6 | ## Minimal example
7 | ```nimrod
8 | # helloworld.nim
9 | import nawak_mongrel, strutils
10 |
11 | get "/":
12 | return response("Hello World!")
13 |
14 | get "/user/@username/":
15 | return response("Hello $1!" % url_params.username)
16 |
17 | run()
18 | ```
19 |
20 | ## Installation
21 | Install `Mongrel2` v1.8.1 (`download page `_) by following the instructions in the [manual](http://mongrel2.org/manual/book-finalch3.html). The bindings for `ZeroMQ` may only work with `ZeroMQ` version 4.
22 |
23 | Start `Mongrel2` either with the provided Makefile in the `example/conf/` folder, or manually:
24 |
25 | $ cd example/conf
26 | $ mkdir -p run logs tmp
27 | $ m2sh load
28 | $ sudo m2sh start -every
29 |
30 | Please check that you use a recent compiler version of `Nimrod`/`Nim`. *nawak* only works with a fresh Nimrod compiler.
31 |
32 | You can now compile and execute the examples from the `example` folder:
33 |
34 | $ cd example
35 | $ nimrod c -d:release helloworld.nim
36 | $ ./helloworld
37 | $ firefox http://localhost:6767/
38 |
39 | The [nawak_app.nim](https://github.com/idlewan/nawak/blob/master/example/nawak_app.nim) example answers the requirements of the [web framework benchmarks](http://www.techempower.com/benchmarks/). You will want to install PostgreSQL and create the [database and tables](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/config) to test it out.
40 |
41 | ## Performance test
42 | If you want to make changes and see how it performs, you can use `wrk` to have a preview of the performance.
43 | The command-line options you will want to use are (from the web framework benchmarks):
44 |
45 | $ wrk -H 'Host: localhost' -H 'Accept: application/json,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7' -H 'Connection: keep-alive' -d 15 -c 256 -t 1 http://localhost:6767/json
46 |
--------------------------------------------------------------------------------
/example/app.nim:
--------------------------------------------------------------------------------
1 | import strtabs, strutils, math, algorithm
2 | import nawak_mongrel, jdump
3 | import model, fortunes_tmpl
4 | when not defined(postgre_model) xor defined(redis_model):
5 | {.error: "please pass either -d:postgre_model or -d:redis_model to the compiler".}
6 | when defined(postgre_model):
7 | import model_postgre
8 | when defined(redis_model):
9 | import model_redis
10 |
11 | get "/json":
12 | var j: THello
13 | j.message = "Hello, World!"
14 | # jdump serialize the tuples of the model as json
15 | return response(jdump(j), "application/json")
16 |
17 | get "/plaintext":
18 | return response("Hello, World!", "text/plain")
19 |
20 | get "/db":
21 | let w = getWorld(random(10_000)+1)
22 | return response(jdump(w), "application/json")
23 |
24 | get "/fortunes":
25 | var fortunes = getAllFortunes()
26 | let new_fortune: TFortune =
27 | (id: 0,
28 | message: "Additional fortune added at request time.")
29 | fortunes.add new_fortune
30 | sort(fortunes, proc(x, y: TFortune): int =
31 | return cmp(x.message, y.message))
32 |
33 | return response(fortunes_tmpl(fortunes), "text/html; charset=utf-8")
34 |
35 |
36 | proc limit_queries(query_params: PStringTable): int =
37 | result = 1
38 | if query_params.hasKey("queries"):
39 | try:
40 | result = parseInt(query_params["queries"])
41 | except EInvalidValue: discard
42 | # clamp the number of queries
43 | if result < 1: result = 1
44 | elif result > 500: result = 500
45 |
46 | get "/queries":
47 | let queries = limit_queries(request.query)
48 |
49 | var world: seq[TWorld]
50 | world.newSeq(queries)
51 | for i in 0..
70 | Here's a 404 Page Not Found error for you.""")
71 |
72 | run(init=init_db, nb_threads=256)
73 |
--------------------------------------------------------------------------------
/example/conf/Makefile:
--------------------------------------------------------------------------------
1 |
2 | all: run
3 |
4 | run:
5 | mkdir -p run logs tmp
6 | m2sh load
7 | sudo m2sh start -every
8 |
9 | .PHONY: run
10 |
--------------------------------------------------------------------------------
/example/conf/mongrel2.conf:
--------------------------------------------------------------------------------
1 | # this is the minimal config required for mongrel2
2 | micro_framework = Handler(
3 | send_spec='tcp://127.0.0.1:9999',
4 | #send_spec='ipc:///tmp/requests.mongrel.ipc',
5 | send_ident='0138a43-micro-nimrod',
6 | recv_spec='tcp://127.0.0.1:9998',
7 | #recv_spec='ipc:///tmp/responses.mongrel.ipc',
8 | #protocol='tnetstring', # default to json
9 | recv_ident='')
10 |
11 | main = Server(
12 | uuid="bd81667c-505a-41da-952c-1e672338db8a",
13 | access_log="/logs/access.log",
14 | error_log="/logs/error.log",
15 | chroot="./",
16 | default_host="localhost",
17 | name="test",
18 | pid_file="/run/mongrel2.pid",
19 | port=6767,
20 | hosts = [
21 | Host(name="localhost", routes={
22 | '/': micro_framework
23 | })
24 | ]
25 | )
26 |
27 | settings = {"zeromq.threads": 1,
28 | "disable.access_logging": 1,
29 | "limits.min_ping": 0,
30 | "limits.min_write_rate": 0,
31 | "limits.min_read_rate": 0,
32 | "limits.kill_limit": 2}
33 |
34 | servers = [main]
35 |
--------------------------------------------------------------------------------
/example/fortunes_tmpl.nim:
--------------------------------------------------------------------------------
1 | #! stdtmpl | standard
2 | #from xmltree import escape
3 | #import model
4 | #proc fortunes_tmpl*(fortunes: openArray[TFortune]): string =
5 | # result = ""
6 |
7 |
8 | Fortunes
9 |
10 |
11 |
id
message
12 | #for fortune in items(fortunes):
13 |
${fortune.id}
${escape(fortune.message)}
14 | #end for
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/example/hellocookies.nim:
--------------------------------------------------------------------------------
1 | import strtabs, times
2 | import nawak_mongrel, cookies
3 |
4 |
5 | get "/mmm_cookies":
6 | var headers = {:}.newStringTable
7 | headers.addCookie("a_cookie", "simple session cookie")
8 | headers.addCookie("another_cookie", "Persistent cookie that will stay a week",
9 | 7.daysFromNow)
10 | headers.addCookie("other_cookie",
11 | "Only sent back on https and unaccessible from javascript",
12 | secure=true, httpOnly=true)
13 |
14 | echo headers
15 | return response("Hi! I gave you cookies. Go back.", headers)
16 |
17 | get "/":
18 | var msg = ""
19 | if request.cookies.hasKey("a_cookie"):
20 | # Here is how you get the value of a cookie
21 | msg = "Ha! I see you already got something for me (like a " &
22 | request.cookies["a_cookie"] & ").
"
23 |
24 | return response(msg & "Here are the cookies you sent me: " & $request.cookies &
25 | """