-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New makefile + new view template implementation
- Loading branch information
Showing
23 changed files
with
1,105 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#Contents | ||
|
||
1. Introduction | ||
2. Getting started: building and installing Luaw | ||
3. Luaw configuration | ||
4. Your first Luaw webapp - "Hello world!" | ||
5. Your second Luaw webapp - "Hello `<user name>`!" | ||
6. Luaw template views | ||
7. Your third webapp - with template views | ||
8. Using Luaw logging framework | ||
9. Using Luaw response object | ||
10. Using Luaw async HTTP Client | ||
11. Using Luaw threads and background jobs | ||
12. Using Luaw timers | ||
13. Using custom HTTP handler for HTTP stream parsing | ||
14. Using custom scripts on command line at Luaw start up |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#1. Introduction | ||
|
||
Luaw stands for "Lua web server". It is a happy coincidence that it also matches abbreviation for an air traffic controller command "line up and wait" that closely resembles the way it handles multiple requests using event loop :) | ||
|
||
Luaw is an event driven, non blocking IO based server inspired by node.js. It uses node.js's excellent async library libuv to do non-blocking IO but instead of Javascript it uses [Lua](http://www.lua.org/) as its language for application development. Luaw takes advantage of Lua's first class coroutine support to avoid [callback hell](http://callbackhell.com/). This makes writing async IO code as simple as writing sequential code while all the heavy-lifting of application state machine management that is usually associated with async IO is transparently handled by Lua coroutines. This mean a Luaw application developer gets best of both worlds - [scale](http://www.kegel.com/c10k.html) of event driven IO and simplicity of blocking IO like code. | ||
|
||
|
||
##Features | ||
|
||
1. Full HTTP 1.1 support with persistent/keep-alive connections and HTTP pipelining with configurable connect and read timeouts. | ||
2. Two ways to read and parse incoming HTTP requests, | ||
- Reading whole request in memory and then parsing it which is suitable for majority of applications. | ||
- Reading request as a stream and parsing it as it arrives in parts to minimize latency and server memory footprint for high traffic, high performance applications like HTTP proxies or HTTP load balancers. | ||
3. Similarly on the output side it supports, | ||
- Buffering entire content in memory before writing it out to client with the correct "Content-Length" header. | ||
- Streaming response continuously as it is generated using HTTP 1.1 chunked encoding to keep server memory pressure minimum. | ||
4. Fully asynchronous DNS and HTTP client for making remote HTTP calls from server | ||
5. [Sinatra](http://www.sinatrarb.com/) like web application framework that supports mapping URLs to REST resources with full support for path and query parameters. | ||
6. Luaw template views for server side dynamic content generation . Luaw template views use streaming output with chunked encoding described above to eliminate need for huge server side memory buffers to buffer output generated by templates. | ||
7. Support for spawning user threads that can hook into Luaw's async IO machinery for calling multiple backend services from Luaw in parallel. | ||
8. Support for user timers for implementing periodic cron like jobs inside Luaw server. | ||
9. Log4j like logging framework with configurable log levels, log file size limits and automatic log rotation that integrates with syslog out of the box. | ||
10. MessagePack like library to efficiently serialize/deserialize arbitrarily complex data into compact and portable binary format for remote web service calls. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#14. Advanced Topic II - Using custom scripts on command line at start up | ||
|
||
In the last topic we saw that we can specify any custom Lua script(s) on the command line after the server configuration file at start up. All these script are executed sequentially in order by Luaw using the same Lua VM and global environment. These scripts are executed after all the core infrastructure machinery of Luaw is initialized. This means these custom scripts have full access to initialized Luaw container before it starts to server any traffic. This is a very flexible and powerful trick to customize Luaw server and modify the global environment in which all subsequent request handlers will run. We have already used this trick to override Luaw's default HTTP request handler with our own custom request handler optimized for proxying network traffic. You can use this trick to do many essential, common tasks at server start up time. For example, | ||
|
||
1. Loading any in memory caches required by application in Luaw's memory | ||
2. Start any crontab like tasks that you would like to be run periodically inside your Luaw server. To do this you would typically spawn a new user thread per task and use a timer:sleep() method to sleep for desired time and then execute whichever action you want in a forever loop | ||
3. Set up sandboxes in which to execute certain request or resource handlers. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
#3. Configuring Luaw | ||
|
||
Before we can write our first Luaw webapp we need to configure our Luaw server with some basic settings -- TCP port on which Luaw server will listen for incoming HTTP connections, for example. These settings are specified in a text file which is named server.cfg by convention and is put under `luaw_root_dir/conf` directory. This file is Luaw's counterpart of Apache web server's httpd.conf or Tomcat's server.xml file. All Luaw configuration files use [Lua object notation syntax](http://www.lua.org/pil/10.1.html#DataDesc) and server.cfg is no exception to this rule. | ||
|
||
Like Tomcat, Luaw allows deploying multiple web apps in a single Luaw server. Settings configured in server.cfg apply to the whole server, that is, they apply to all webapps that are deployed in that server. Settings specific to each webapps are configured using "web.lua" file per webapp. | ||
|
||
`make INSTALL_ROOT=luaw_root_dir install` step from the last chapter should have created the standard directory structure for you and put sample server.cfg file under `luaw_root_dir/conf` directory. You can use this server.cfg as a starting point for defining your configuration. Open your conf/server.cfg and take a look. | ||
|
||
##Server configuration | ||
|
||
Here is a sample server.cfg | ||
|
||
```lua | ||
luaw_server_config = { | ||
server_ip = "0.0.0.0", | ||
server_port = 7001, | ||
connect_timeout = 4000, | ||
read_timeout = 8000, | ||
write_timeout = 8000 | ||
} | ||
|
||
luaw_log_config = { | ||
log_dir = "/apps/luaw/sample/log", | ||
log_file_basename = "luaw-log", | ||
log_file_size_limit = 1024*1024, | ||
log_file_count_limit = 9, | ||
log_filename_timestamp_format = '%Y%m%d', | ||
log_lines_buffer_count = 16, | ||
syslog_server = "127.0.0.1", | ||
syslog_port = 514, | ||
} | ||
|
||
luaw_webapp_config = { | ||
base_dir = "/apps/luaw/sample/webapps" | ||
} | ||
``` | ||
luaw_server_config section specifies listening port and read/connection timeout defaults for TCP socket connections. "server_ip" setting's value "0.0.0.0" tells server to accept connections coming in on any of the host's ip addresses. Some hosts have more than one IP address assigned to them. In such case "server_ip" can be used to restrict Luaw server to accept incoming connections on only one of the multiple IP addresses of the host. | ||
|
||
luaw_log_config section sets up parameters for Luaw's log4j like logging subsystem - log file name pattern, size limit for a single log file after which Luaw should open new log file, how many of such past log files to keep around (log rotation) etc. Luaw logging framework can send messages to syslog daemon as well and this section can be used to specify target syslog server's ip address and port. | ||
|
||
Finally, luaw_webapp_config section specifies location of directory that houses all the webapps that this Luaw server will load and run. By convention this directory is named "webapps" and is placed directly under Luaw server's root folder but you can place it anywhere you like using this section, should your build/deploy procedure requires you to choose another location. | ||
|
||
## webapp configuration | ||
|
||
Like Tomcat, Luaw allows deploying multiple webapps in a single Luaw server. These webapps are deployed under `luaw_root_dir/webapps`. Here is a sample layout for a Luaw server that has two webapps - `myapp1` and `myapp2` - deployed in it: | ||
|
||
``` | ||
luaw_root_dir | ||
| | ||
+--- bin | ||
| | ||
+--- conf | ||
| | | ||
│ +--- server.cfg | ||
| | ||
+--- lib | ||
| | ||
+--- logs | ||
| | ||
+--- webapps | ||
| | ||
+--- myapp1 | ||
| | | ||
| +---web.lua | ||
| | ||
+--- myapp2 | ||
| | ||
+---web.lua | ||
``` | ||
|
||
Each webapp contains file named web.lua that specifies settings specific to that particular webapp. The same directory (`/myapp1` and `/myapp2` in the example above) also contains Lua code for the webapp - REST handlers, views etc. We will visit application code in the next chapter. In this chapter we will focus on just the configuration piece. | ||
|
||
##Sample web.lua: | ||
|
||
```lua | ||
luaw_webapp = { | ||
resourcePattern = "handler%-.*%.lua", | ||
views = { | ||
"user/address-view.lua", | ||
"account/account-view.lua" | ||
} | ||
} | ||
|
||
Luaw.logging.file { | ||
name = "root", | ||
level = Luaw.logging.ERROR, | ||
} | ||
|
||
Luaw.logging.file { | ||
name = "com.luaw", | ||
level = Luaw.logging.INFO, | ||
} | ||
|
||
Luaw.logging.syslog { | ||
name = "com.luaw", | ||
level = Luaw.logging.WARNING, | ||
} | ||
``` | ||
|
||
luaw_webapp section specifies resources (request handlers) and views (templates) that make up the web application. These can be specified in two different ways: | ||
|
||
1. **Using pattern **: You can use configuration elements *resourcePattern* and *viewPattern* to specify name pattern for request handlers and views. Luaw will traverse all directories under current webapp recursively to load all files that match these patterns. The patterns are specified using standard [Lua regular expressions](http://www.lua.org/pil/20.2.html). These are somewhat different than usual POSIX or PERL regular expressions so be sure to read the documentation linked above before you use them. | ||
2. **Listing them one by one by exact name and path **: You can also use configuration elements *resources* and *views* to list exact resources and views by name to load them. Each entry should contain a path that is relative to the root webapp directory (**myapp1** and **myapp2** in the example above) ending in the filename of the file to load. | ||
|
||
You can mix and match both these approach. For example you can use *resourcePattern* to specify resources and *views* for specifying exact list of views. You can even use both the ways together. That is, you can use both *resourcePattern* and *resources* in the same section and Luaw will load all the files under the given webapp's folder that either match the *resourcePattern* or match the path and file name from the *resources* list (union operation). This could be useful if most of your resource files follow certain naming convention or a pattern but you also have few one off files that don't follow those conventions that you'd like to load nonetheless. | ||
|
||
Rest of the file specifies logging level and logging target (file vs syslog) for different Luaw packages. Logging settings in web.lua specify log levels that are specific to that webapp while overall logging settings like log file name and size limit are determined by server.cfg settings at a global server level. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#4. Your First Luaw Webapp - "Hello world!" | ||
|
||
Now that we are familiar with Luaw's directory layout and configuration, we are ready to write our first webapp - "Hello world" but of course. | ||
|
||
Luaw comes equipped with [Ruby's Sinatra](http://www.sinatrarb.com/) like web framework that allows mapping URLs to request handlers (routes in Sinatra's terminology). It has full support for REST path and query parameters. | ||
|
||
##Writing Luaw Request Handler | ||
|
||
1. Switch to directory `luaw_root_dir/webapps` that was created in chapter "Getting started" and create a directory called `myapp` under it. This is our first webapp. | ||
|
||
cd luaw_root_dir/webapps | ||
mkdir myapp | ||
cd myapp | ||
|
||
2. create a filed called web.lua in `luaw_root_dir/webapps/myapp` and put following content in it | ||
```lua | ||
luaw_webapp = { | ||
resourcePattern = "handler%-.*%.lua", | ||
} | ||
``` | ||
This is a bare minimum webapp configuration that basically tells Luaw to load any file matching Lua regex pattern "handler%-.*%.lua" as a URL (or in REST terminology Resource) handler. | ||
|
||
3. Now we will write our first resource handler. create a directory `handlers` under `luaw_root_dir/webapps/myapp` and create a file named `handler-helloworld.lua` under it. The choice of the directory name "handlers" is purely arbitrary. All that matters is that handler's file name matches the pattern `handler%-.*%.lua` that we have specified in web.lua. Luaw will traverse all folders under `luaw_root_dir/webapps/myapp` looking for handler files to load that match the pattern. This means we could have placed handler-helloworld.lua directly under luaw_root_dir/webapps/myapp and it would have still worked. It's probably a better practice to put them in their own directory like "handlers" from the point of view of clean code organization though. | ||
Next put following code in "handler-helloworld.lua": | ||
```lua | ||
GET 'helloworld' { | ||
function(req, resp, pathParams) | ||
return "Hello World!" | ||
end | ||
} | ||
``` | ||
|
||
In the code above GET identifies the HTTP method this handler will service. Other methods available are POST, PUT, DELETE, HEAD, OPTIONS, TRACE and CONNECT corresponding to respective HTTP methods. There is also a catch-all, uber method called SERVICE that you can use in case you want to handle any HTTP request irrespective of its method. | ||
the string 'helloworld' specifies the URL path this handler will match. It is analogus to Sinatra's route. It means this handler will be invoked for all GET methods made on `/myapp/helloworld` URL. | ||
|
||
Finally, the function in the above example is the actual code run whenever `/myapp/helloworld` is requested. The function is passed incoming HTTP request, HTTP response to write to and any REST path parameters defined on the resources. We will see how to used all three of these objects in subsequent chapters. For now, we just want to follow the age old rite of passage and say "Hello World!" to the browser. Simply returning the string "Hello World!" from our function is sufficient to achieve this. Later we will see more sophisticated ways of forming and returning response using response object and Luaw template views. | ||
|
||
4. Now we are ready to start the server. To do this switch to `luaw_root_dir` and run Luaw server like this: | ||
cd luaw_root_dir | ||
./bin/luaw_server ./conf/server.cfg | ||
|
||
First argument to the luaw_server is always the server configuration file. If you have followed all the steps so far correctly, you should see following output in your console window: | ||
|
||
``` | ||
******************** Starting webapp myapp ******************************* | ||
.Loading resource ./webapps/myapp/handlers/handler-helloworld.lua | ||
#Loaded total 1 resources | ||
#Compiled total 0 views | ||
*********************** Webapp myapp started **************************** | ||
starting server on port 7001 ... | ||
``` | ||
|
||
Now point your browser to http://localhost:7001/myapp/helloworld and greet the brave new world! | ||
|
||
Congratulations, you have successfully written your first Luaw webapp! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#2. Getting started | ||
|
||
Let's build Luaw from its sources. Luaw depends on, | ||
|
||
1. Lua 5.2(+) | ||
2. libuv (v1.0.0 ) | ||
|
||
We will first build these dependencies from their sources and then finally build Luaw itself. To build these artifacts you would need [Git](http://git-scm.com/), [Make](http://www.gnu.org/software/make/) and [autotools](http://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html) setup on your machine. | ||
|
||
##Building Lua 5.2 | ||
Lua sources can be downloaded from [here](http://www.lua.org/download.html). Here are the steps to download Lua 5.2 sources and build it for Linux: | ||
|
||
curl -R -O http://www.lua.org/ftp/lua-5.2.3.tar.gz | ||
tar zxf lua-5.2.3.tar.gz | ||
cd lua-5.2.3 | ||
make linux test | ||
sudo make linux install | ||
|
||
To build for other OSes replace "linux" from the last two make commands with the OS you are building for. For example for when building for Mac OS run, | ||
|
||
make macosx test | ||
sudo make linux install | ||
|
||
To see what OSes are supported run ./lua-5.2.3/src/Makefile targets | ||
|
||
|
||
##Building libuv | ||
Luaw uses node.js library libuv to do asynchronous, event based IO in a portable, cross platform manner. To build libuv: | ||
|
||
1. first clone libuv repository | ||
git clone https://github.com/joyent/libuv.git | ||
2. Checkout latest stable release of libuv from the cloned local repository. As of this writing the latest stable release is v1.0.0 and Luaw is verified to compile and run successfully with this release of libuv. | ||
cd libuv | ||
git checkout tags/v1.0.0 | ||
3. Build libuv. Detailed instructions are [here](https://github.com/joyent/libuv#build-instructions) | ||
sh autogen.sh | ||
./configure | ||
make | ||
make check | ||
sudo make install | ||
|
||
## Building Luaw | ||
With all dependencies built, now we are ready to build Luaw itself. | ||
|
||
1. Clone Luaw repository | ||
git clone https://github.com/raksoras/luaw.git | ||
2. Build Luaw | ||
cd luaw/src | ||
make linux | ||
3. Install Luaw binary - luaw_server - in directory of your choice | ||
make INSTALL_ROOT=<luaw_root_dir> install | ||
4. Note: On Mac running Yosemite version of Mac OS you may have to run, | ||
make SYSCFLAGS=-I/usr/local/include SYSLDFLAGS=-L/usr/local/lib macosx | ||
make INSTALL_ROOT=<luaw_root_dir> install | ||
|
||
|
||
##Luaw directory structure | ||
|
||
In the tree diagram below `luaw_root_dir` is a directory that you chose in the `make intsall` step above. It will act as a root for Luaw server's directory structure. The `make install` step will create following directory structure under `luaw_root_dir` | ||
|
||
``` | ||
luaw_root_dir | ||
| | ||
| | ||
+--- bin Directory that holds Luaw server binary we built | ||
| along with all necessary Lua libraries | ||
| | ||
+--- conf Directory for server configuration | ||
| | | ||
│ +--- server.cfg Sample server configuration file, to be used as a starting point | ||
| | ||
+--- lib Directory to install any third party or user supplied Lua | ||
| libraries that application may depend on. | ||
| | ||
+--- logs Directory for server logs | ||
| | ||
+--- webapps Directory to install Luaw webapps | ||
``` | ||
|
||
This directory structure and its usage is further explained in the next chapter "Configuring Luaw" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#10. Async HTTP Client | ||
|
||
Luaw comes equipped with curl like async HTTP client. It is fully async, in that both DNS lookup for the hostname as well as actual connect/read/write on the socket are done in a non blocking fashion. Due to this you can use this client safely in your Luaw webapp, from your request thread to make HTTP requests to other backend servers. Luaw will transparently suspend your running request thread (Lua coroutine) when the HTTP client is waiting on DNS lookup, connect, read or write. | ||
|
||
The Luaw HTTP client is modeled by two objects: clientHttpRequest and clientHttpResponse. Here is a small example of Luaw's HTTP client's usage: | ||
|
||
```lua | ||
-- set up HTTP request | ||
local clientReq = Luaw.newClientHttpRequest() | ||
clientReq.hostName = "www.google.com" | ||
-- OR alternatively, | ||
clientReq.hostIP = "74.125.25.106" | ||
clientReq.method = 'GET' | ||
clientReq.url = "/" | ||
|
||
clientReq.headers = { Host = "www.google.com" } | ||
-- OR alternatively | ||
clientReq:addHeader("Host", "www.google.com") | ||
|
||
-- execute the HTTP request and read the response back. | ||
local clientResp = clientReq:execute() | ||
|
||
-- Get the respone headers and body from the client response object returned | ||
local respBody = clientResp.body | ||
local respHeaders = clientResp.headers | ||
``` | ||
|
||
In fact, Luaw's built in HTTP client allows even more fine grained control over various stages of HTTP request execution and parsing of the HTTP response received from the server, similar to what we saw in the chapter "Advanced Topic I - Using Response Object" which was about server's HTTP response. We learn will how to use some of these methods in the last chapter where we put together all the things we have learned so far to develop a streaming request/response handler for a high performance proxy web server. | ||
|
||
|
||
|
Oops, something went wrong.