Full Code of raksoras/luaw for AI

master 036601d6a76b cached
62 files
349.9 KB
91.7k tokens
204 symbols
1 requests
Download .txt
Showing preview only (369K chars total). Download the full file or copy to clipboard to get everything.
Repository: raksoras/luaw
Branch: master
Commit: 036601d6a76b
Files: 62
Total size: 349.9 KB

Directory structure:
gitextract_wtxp3faz/

├── .gitignore
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
├── conf/
│   └── server.cfg
├── docs/
│   ├── Contents.md
│   ├── Introduction.md
│   ├── cmd-line.md
│   ├── configuration.md
│   ├── first-webapp.md
│   ├── getting-started.md
│   ├── http-client.md
│   ├── logging.md
│   ├── proxy-http.md
│   ├── resp-obj.md
│   ├── second-webapp.md
│   ├── template-view.md
│   ├── third-webapp.md
│   ├── user-threads.md
│   └── user-timers.md
├── lib/
│   ├── luapack.lua
│   ├── luaw_constants.lua
│   ├── luaw_data_structs_lib.lua
│   ├── luaw_http.lua
│   ├── luaw_init.lua
│   ├── luaw_logging.lua
│   ├── luaw_scheduler.lua
│   ├── luaw_tcp.lua
│   ├── luaw_timer.lua
│   ├── luaw_utils.lua
│   ├── luaw_webapp.lua
│   └── unit_testing.lua
├── sample/
│   ├── conf/
│   │   └── server.cfg
│   ├── proxy_handler.lua
│   └── webapps/
│       └── myapp/
│           ├── handlers/
│           │   ├── handler-address.lua
│           │   ├── handler-fileupload-display.lua
│           │   ├── handler-fileupload-process.lua
│           │   ├── handler-hellouser.lua
│           │   ├── handler-helloworld.lua
│           │   ├── handler-read-lpack.lua
│           │   └── handler-write-lpack.lua
│           ├── views/
│           │   └── view-address.lua
│           └── web.lua
└── src/
    ├── Makefile
    ├── http_parser.c
    ├── http_parser.h
    ├── lfs.c
    ├── lfs.h
    ├── lua_lpack.c
    ├── lua_lpack.h
    ├── luaw_common.c
    ├── luaw_common.h
    ├── luaw_http_parser.c
    ├── luaw_http_parser.h
    ├── luaw_logging.c
    ├── luaw_logging.h
    ├── luaw_server.c
    ├── luaw_tcp.c
    ├── luaw_tcp.h
    ├── luaw_timer.c
    └── luaw_timer.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
**/*.o
**/*.so
**/*.log


================================================
FILE: .gitmodules
================================================
[submodule "deps/libuv"]
	path = deps/libuv
	url = https://github.com/libuv/libuv.git
[submodule "deps/luajit-2.0"]
	path = deps/luajit-2.0
	url = http://luajit.org/git/luajit-2.0.git
[submodule "deps/lua-PUC-Rio"]
	path = deps/lua-PUC-Rio
	url = https://github.com/lua/lua.git


================================================
FILE: LICENSE
================================================
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.


================================================
FILE: Makefile
================================================
# Makefile for building Luaw
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================

export UVDIR= deps/libuv
export UVLIB= deps/libuv/.libs/libuv.a

ifeq ($(LUAVM),luajit)
    export LUADIR= deps/luajit-2.0
    export LUALIB= deps/luajit-2.0/src/libluajit.a
    export OSXLDFLAGS= "-Wl,-pagezero_size,10000 -Wl,-image_base,100000000"
else
    export LUADIR= deps/lua-PUC-Rio
    export LUALIB= deps/lua-PUC-Rio/src/liblua.a
    export OSXLDFLAGS=
endif

# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======

# Supported platforms
PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
ALL= all

# Targets start here.

all:
	@echo "Please do 'make PLATFORM' where PLATFORM is one of these:"
	@echo "   $(PLATS)"

$(UVLIB): $(UVDIR)/Makefile
	$(MAKE) -C $(UVDIR)

$(UVDIR)/Makefile: $(UVDIR)/configure
	cd $(UVDIR) && ./configure

$(UVDIR)/configure: $(UVDIR)/autogen.sh
	cd $(UVDIR) && sh autogen.sh

$(LUADIR)/src/libluajit.a:
	$(MAKE) -C $(LUADIR)

$(LUADIR)/src/liblua.a:
	$(MAKE) -C $(LUADIR) $(TARGET)

luaw:
	$(MAKE) -C src $(ALL) SYSLDFLAGS=$(SYSLDFLAGS) CC=$(CC)

# Convenience targets for popular platforms

aix: TARGET= aix
aix: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL) CC="xlc" CFLAGS="-O2" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall"

ansi: TARGET= ansi
ansi: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL)

bsd: TARGET= bsd
bsd: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL) SYSLIBS="-Wl,-E"

freebsd: TARGET= freebsd
freebsd: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL) SYSLIBS="-Wl,-E"

linux: TARGET= linux
linux: SYSLIBS= -Wl,-E -ldl
linux: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL) SYSLIBS="-lrt -Wl,-E -ldl"

macosx: TARGET= macosx
macosx: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL) CC="cc" SYSLDFLAGS=$(OSXLDFLAGS)

posix: TARGET= posix
posix: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL)

solaris: TARGET= solaris
solaris: $(UVLIB) $(LUALIB)
	$(MAKE) -C src $(ALL) SYSLIBS="-ldl"

#build objects management

install:
	$(MAKE) -C src install

install-sample:
	$(MAKE) -C src install-sample

uninstall:
	$(MAKE) -C src uninstall

clean: $(UVDIR)/Makefile
	$(MAKE) -C deps/luajit-2.0 clean
	$(MAKE) -C deps/lua-PUC-Rio clean
	$(MAKE) -C $(UVDIR) distclean
	$(MAKE) -C src clean

# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: all check_plat $(LUALIB) $(PLATS) luaw install uninstall clean $(LUADIR)/src/libluajit.a $(LUADIR)/src/liblua.a



================================================
FILE: README.md
================================================
Luaw - Lua meets Node.js
========================
***

Luaw stands for "Lua web server". 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 HTTP application 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 primary language for application development. Luaw takes advantage of Lua's first class coroutine support to avoid [callback spaghetti problem](http://callbackhell.com/). This makes writing async IO code as straight forward as writing sequential code while all the heavy-lifting of application state management 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 code simplicity of blocking IO.


##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, or
    - 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 the templates.
7. Support for spawning user threads that can hook into Luaw's async IO machinery for calling multiple backend services from Luaw server 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](http://msgpack.org/) like library to efficiently serialize/deserialize arbitrarily complex data into compact and portable binary format for remote web service calls.
11. Built in multipart file upload support.


##How To Build
***
1. Get necessary build tools. libuv, one of the Luaw dependencies uses autotools, autoconf and libtoolize in its build system. If your machine is not already setup with these, you can use following steps to get these tools. 

        sudo apt-get install make
        sudo apt-get install autotools-dev
        sudo apt-get install autoconf
        sudo apt-get install build-essential libtool
        

2. Clone Luaw repository

        git clone --recursive https://github.com/raksoras/luaw.git
        
3. Build Luaw to use standard [PUC-Rio Lua VM](http://www.lua.org/)

        cd luaw
        make linux
        
    or, build Luaw to use [LuaJit VM](http://luajit.org/)

        cd luaw
        make LUAVM=luajit linux
        
    While building on mac OS use target "macosx". For example

        cd luaw
        make LUAVM=luajit macosx
        
4. Install Luaw binary - luaw_server - in directory of your choice. We will use `~/luawsample` in all our examples going forward as a directory of choice for Luaw installation

        make INSTALL_ROOT=~/luawsample install
        
    To install binaries along with the sample app provided

        make INSTALL_ROOT=~/luawsample install-sample
        
    

##Luaw directory structure

In the tree diagram below `~/luawsample` 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 `~/luawsample`

```
~/luawsample
 |
 |
 +--- 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
```

##License

Luaw uses [MIT license](http://opensource.org/licenses/mit-license.html) for all parts of Luaw that are not externally
maintained libraries like libuv.

## Documentation

Please refer to the [project wiki](https://github.com/raksoras/luaw/wiki) for documentation regarding how to build, configure, start and program using Luaw.

## Getting in touch

Please post your questions/comments on Google Group [Luaw](https://groups.google.com/forum/#!forum/luaw) and follow [@raksoras](https://twitter.com/raksoras) on Twitter


================================================
FILE: conf/server.cfg
================================================
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 = "./logs",
    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 = "./webapps"
}



================================================
FILE: docs/Contents.md
================================================
#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


================================================
FILE: docs/Introduction.md
================================================
#Introduction

Luaw stands for "Lua web server". 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 HTTP application 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 spaghetti problem](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](http://msgpack.org/) like library to efficiently serialize/deserialize arbitrarily complex data into compact and portable binary format for remote web service calls.


================================================
FILE: docs/cmd-line.md
================================================
#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.

================================================
FILE: docs/configuration.md
================================================
#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.


================================================
FILE: docs/first-webapp.md
================================================
#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.

    export LD_LIBRARY_PATH=/usr/local/lib/

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!

================================================
FILE: docs/getting-started.md
================================================
#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. This may require you to install autotools. 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"



================================================
FILE: docs/http-client.md
================================================
#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.





================================================
FILE: docs/logging.md
================================================
#8. Luaw logging framework

Luaw comes equipped with logging framework that's modeled after Java's popular log4j logging framework. Like log4j, it allows different destinations for logs (either file system or syslog) and different log levels per package to allow enabling/disabling logging by setting
runtime properties

Luaw's logging is configured at two levels

##Logging configuration for the whole server
Configuration related to log destinations is configured in `conf/server.cfg` and applies to all web applications deployed in that server. luaw_log_config section contains this configuration in server.cfg
```lua
luaw_log_config = {
	log_dir = "/apps/luaw/sample/log",
	log_file_basename = "luaw-log",
	log_filename_timestamp_format = '%Y%m%d',
	log_file_size_limit = 1024*1024,
	log_file_count_limit = 9,
    log_lines_buffer_count = 16,
	syslog_server = "127.0.0.1",
	syslog_port = 514
}
```

Example above configures,

1. Log directory where the files should go: /apps/luaw/sample/log
2. Log file base name: luaw-log
3. [Timestamp format](http://www.lua.org/pil/22.1.html) used along with log file base name to generate full log file name for versioned log files: %Y%m%d
4. File size limit for an individual log file: 1MB
5. Total number of log files to keep: 9
6. How many log lines to buffer in memory before flushing them to log file: 16
7. syslog server address: 127.0.0.1
8. syslog server port: 514

You could omit either file system related configuration or syslog configuration but at least one must be present to use Luaw logging.


##Logging configuration per webapp

Configuration related to individual webapps' logging is configured in `web.lua` config file of each webapp. Here are some example entries from sample "web.lua":
```lua
Luaw.logging.file {
    name = "root",
    level = Luaw.logging.ERROR
}
```

Above entry configures root level logging ("root" is a special word) for the entire webapp that applies to all loggers used by the webapp by default unless overridden by more specific logger. It configures all logging to go to log file - whose destination and name is configured in server.cfg - and states only logs with log level ERROR and above should be logged.

```lua
Luaw.logging.file {
    name = "com.myapp",
    level = Luaw.logging.INFO
}
```

Overrides default ("root") logging for the logger "com.myapp" and sets its level to INFO which means for any logger with name starting with "com.myapp", log lines with the log level INFO and above will get logged to the file instead of just ERROR as dictated by the default (root) configuration above. "com.myapp" is a logger name. You can use any name separated by periods. Each part of the name delimited by a period forms logger hierarchy just like log4j. For example, the logger named "com" is a parent to logger name "com.myapp" and any logging configuration defined for "com" is inherited by "com.myapp" unless specififcally overriden on "com.myapp". This makes it easy to fine tune logging levels of different parts of code using loggers arranged in a logical hierarchy.

To actually log a line you use code like following:

```lua
local log = require("luaw_logging")

local logger = log.getLogger("com.myapp")
logger.error("some error occurred")
logger.warning("this is a warning")
logger.info("some information")
logger.debug("lowest level debug")
```

Here are different log levels and Luaw logger functions corresponding to them in a decreasing order of severity:

|Log Level  | Logging function   |
|--------------------------------
| EMERGENCY | logger.emergency() |
| ALERT     | logger.alert()     |
| CRITICAL  | logger.critical()  |
| ERROR     | logger.error()     |
| WARNING   | logger.warning()   |
| NOTICE    | logger.notice()    |
| INFO      | logger.info()      |
| DEBUG     | logger.debug()     |


All these functions also have a counterpart ending in "f" - `logger.errorf()`, `logger.warningf()`, `logger.debugf()` etc. - that take a [lua format string](http://lua-users.org/wiki/StringLibraryTutorial) followed by variable number of arguments that are used to substitute place holderes in the format string like below

```lua
logger.infof("User name %s, User Id %d", name, id)
```

This form is useful to avoid unnecessary string concatenation beforehand when the configured logger level may actually end up filtering the log line anyways.

Finally, here is an example of configuring syslog as a log destination for logger "com.myapp.system" and setting it to the log level WARNING in web.lua:

```lua
Luaw.logging.syslog {
    name = "com.myapp.system",
    level = Luaw.logging.WARNING
}
```

syslog sever and port to use is specified in server.cfg

================================================
FILE: docs/proxy-http.md
================================================
#13. custom HTTP handler for proxying request

So far we have used default HTTP requests handler that ships with Luaw in all our examples. For most purposes it is indeed sufficient, even ideal HTTP handler to use. It handles all low level details of HTTP protocol parsing and routes incoming requests to REST resource by matching URL to user specified matching rules.

However, if you need it, Luaw also allows you to specify your own custom HTTP request handler - which is top level HTTP request router really comparable to Strut's main servlet or Spring MVC servlet counterpart in Java world - which will replace Luaw's default request handler. We will use this Luaw facility to develop a toy HTTP buffering reverse proxy server in this section.

A typical reverse proxy server accepts a HTTP request from a client, inspects its contents and then forwards it to one of many backend HTTP servers it is balancing load for depending upon the results of the request content inspection. On the return path it receives the response generated by the backend server and forwards it to the connected client. Buffering reverse proxy adds, well, buffering to the standard reverse proxy functionality. It buffers incoming HTTP client request till it is complete before it sends it to one of the backend servers. Similarly on the return path it buffers full response generated by the backend server before it starts returning it back to the original client. This buffering is important in order to defend backend servers' limited resoucres against slow client. Without buffering both consumption of input HTTP request and sending of HTTP response will proceed at "line speed" dictated by the slow client which will tie backend server's resources for a long time.

```
+------+ ----HTTP request---> +-------------+ --- Proxy HTTP request ---> +-------+
|Client|                      |Reverse Proxy|                             |Backend|
+------+ <---HTTP response--- +-------------+ <-- Proxy HTTP response --- +-------+
```

Below is the code for a toy buffering reverse proxy with inline comments explaining what's going on. Our toy proxy server uses a protocol where by client sends the backend host and URL it wants to connect through the proxy server in its HTTP request's "proxy-host" and "proxy-url" headers respectively. If any of these headers are missing our proxy server responds with 400 error. It also proxies only HTTP GET requests but its easy to see how it can be modified to proxy other HTTP methods too.

```lua
--[[
    Luaw allows you to replace it's default MVC/REST request handler with your own custom HTTP request handler implementation. To override the default HTTP request handler just set Luaw object's request_handler property to your custom Lua function. This function is passed in a low level connection object for each incoming request instead of the normal request and response objects. The function is called on its own separate Luaw coroutine for each HTTP request so you don't have to worry about multithreaded access to same state inside the function.
    ]]

    Luaw.request_handler =  function(conn)
        conn:startReading()

        -- loop to support HTTP 1.1 persistent (keep-alive) connections
        while true do
            local req = Luaw.newServerHttpRequest(conn)
            local resp = Luaw.newServerHttpResponse(conn)

            -- read and parse full request
            local eof = req:readFull()
            if (eof) then
                conn:close()
                return "connection reset by peer"
            end

            local reqHeaders = req.headers
            local beHost =  reqHeaders['backend-host']
            local beURL = reqHeaders['backend-url']

            if (beHost and beURL) then
                local backendReq = Luaw.newClientHttpRequest()
                backendReq.hostName = beHost
                backendReq.url = beURL
                backendReq.method = 'GET'
                backendReq.headers = { Host = beHost }

                local status, backendResp = pcall(backendReq.execute, backendReq)
                if (status) then
                    resp:setStatus(backendResp:getStatus())
                    resp:appendBody(backendResp:getBody())
                    local beHeaders = backendResp.headers
                    for k,v in pairs(beHeaders) do
                        if ((k ~= 'Transfer-Encoding')and(k ~= 'Content-Length')) then
                            resp:addHeader(k,v)
                        end
                    end
                    backendResp:close()
                else
                    resp:setStatus(500)
                    resp:appendBody("connection to backend server failed")
                end
            else
                resp:setStatus(400)
                resp:appendBody("Request must contain headers backend-host and backend-url")
            end

            local status, mesg = pcall(resp.flush, resp)
            if (not status) then
                conn:close()
                return error(mesg)
            end

            if (req:shouldCloseConnection() or resp:shouldCloseConnection()) then
                conn:close()
                return "connection reset by peer"
            end
        end
    end
```

The last bit of puzzle remaining is how do we actually load this new, shiny custom HTTP handler of ours into Luaw server? To this we use a simple trick that is quite flexible and powerful in practice. So far we have been starting our Luaw server with following command in luaw_roo_dir:
```
./bin/luaw_server ./conf/server.cfg
```
Where `server.cfg` is a Luaw server configuration file. In reality `luaw_server` binary will read and execute any number of Lua script files specified as series of command line arguments. The very first one is assumed to be server configuration file and is mandatory. Any number - and any kind - of Lua script files can follow the configuration file and are executed by `luaw_server` in the same order as they are specified on the start up commmand line using the same Lua VM and global environment. We can use this handy trick to load any functions we want into Luaw as well as hook up into Luaw's internal machinery using any of the public hook up points that Luaw advertises.

So for our case, just create a file called proxy-handler.lua under `luaw_roo_dir/bin` folder and put the above code in it. Then from your command prompt run Luaw like this:

    ./bin/luaw_server ./conf/server.cfg ./bin/proxy-handler.lua

`luaw_server` will run your proxy-handler.lua after it has initialized itself. The script in proxy-handler.lua then takes care of replacing Luaw's default HTTP request handler with the custom one by assigning the custom handler function to `Luaw.request_handler` property.

Now test your shiny new proxy server by running following tests:

## Test 1 - Missing required headers

    $ curl -v http://127.0.0.1:7001/
    *   Trying 127.0.0.1...
    * Connected to 127.0.0.1 (127.0.0.1) port 7001 (#0)
    > GET / HTTP/1.1
    > User-Agent: curl/7.37.1
    > Host: 127.0.0.1:7001
    > Accept: */*
    >
    < HTTP/1.1 400 Bad Request
    < Content-Length: 50
    <
    Headers proxy-host and proxy-url must be present

## Test 2 - With correct headers
    $ curl -v -H"proxy-host: www.google.com" -H"proxy-url: /" http://127.0.0.1:7001/
    *   Trying 127.0.0.1...
    * Connected to 127.0.0.1 (127.0.0.1) port 7001 (#0)
    > GET / HTTP/1.1
    > User-Agent: curl/7.37.1
    > Host: 127.0.0.1:7001
    > Accept: */*
    > proxy-host: www.google.com
    > proxy-url: /
    >
    < HTTP/1.1 200 OK
    < Content-Type: text/html; charset=ISO-8859-1
    < Transfer-Encoding: chunked
    * Server gws is not blacklisted
    < Server: gws

    (.. followed by the body of the home page at www.google.com)


================================================
FILE: docs/resp-obj.md
================================================
#9. Response Object

So far we have seen two ways to return response body from resource handler:

1. Returning a string from resource handler function which Luaw returns in turn as a whole response body to the client, and
2. Using Luaw template views that generate response body programmatically in a fashion similar to JSP or ASP

However, Luaw does offer finer grain control over response generation from resource handler should you need it. Remember the resource handler function is passed in both request and response object like this:

```lua
GET '/user/:username' {
	function(req, resp, pathParams)
		return "Hello "..pathParams.username.."!"
	end
}
```

Finer grained control over response generation is achieved by invoking various methods on the response object as described below.

1. `resp:setStatus(status)`: You can set HTTP status code to be returned to the client - 200, 404 etc. - using this method

2. `resp:addHeader(name, value)`: You can add arbitrary HTTP headers to the response using this method. All the headers must be added before you start adding body content.

3. `resp:startStreaming()`: Calling this method activates a special [HTTP 1.1 chunked transfer mode](http://en.wikipedia.org/wiki/Chunked_transfer_encoding) which causes Luaw to stream response to the connected client instead of buffering it in memory till end and then sending it in a single shot. In this mode, any body content added to the response is buffered till it reaches a certain , relatively small buffer size threshold - 2K by default whihch is configurable using property "connection_buffer_size" in server.cfg's luaw_server_config section - and then is sent to the client as a HTTP 1.1 compliant body chunk. This means server does not have to buffer the entire response body in its memory to calculate "Content-Length" header value before it can send it to the client. Thus, this mode improves overall server memory footprint and also client's response time to the first byte received. Luaw template views use this mode by default to generate content. HTTP status and all the HTTP headers must be added to response before resp:startStreaming() is called.

3. `resp:appendBody(content)`: You can use this method to add content to the response body in piecemeal fashion. Depending upon whether the response is in default HTTP 1.1 mode or put in HTTP 1.1 chunked transfer mode by calling resp:startStreaming(); the content is either buffered till resp:flush() is called or streamed to the client in HTTP 1.1 chunks whenever buffered content reaches size limit specified by connection_buffer_size.

4. `resp:flush()`: Causes the response to be flushed to the client. In default (HTTP 1.1) mode this causes Luaw to calculate correct "Content-Length" header value for the whole response buffered so far in the memory and then send it to the client along with the "Content-Length" header. In case the response object was put in the HTTP 1.1 chunked transfer mode by calling resp:startStreaming() this causes Luaw to send the last HTTP chunk followed by the terminating chunk as required by the HTTP 1.1 specification.

5. `resp:close()`: Finally, call this method to actually close underlying connection to client and release all the associated resources.


================================================
FILE: docs/second-webapp.md
================================================
#5. Your Second Luaw webapp - "Hello `username`!"

Luaw handlers can accept and process HTTP request parameters (query parameters as well as form parameters) using the request object that is passed to the resource handler function. These parameters are available as `req.params`. For example to access HTTP parameter 'username' - either passed as a query parameter like `?username=raksoras` or POSTed as a form field - you can do either `req.params.username` or `req.params['username']`

Luaw also supports mapping parts of URL paths to REST path parameters. We will use this method to receive username. Let's say we want to use URL format '/user/raksoras' where raksoras is the input user name. To do this create a new handler named "handler-hellouser.lua" under `luaw_root_dir/webapps/myapp/handlers` that we created in previous chapter and put following code in it:
```lua
GET '/user/:username' {
	function(req, resp, pathParams)
		return "Hello "..pathParams.username.."!"
	end
}
```
Here colon in `:username` in the URL path `user/:username` identifies it as a REST path parameter. Luaw will parse it from the URL path at runtime and will make it available on the third paramter - pathParams - passed to the handler function. Inside the function you may refer to it as either `pathParams.username` or `pathParams['username']`

Now restart your Luaw server and then point your browser to http://localhost:7001/myapp/user/your_name and Luaw should greet you this time in a more personal manner!

In fact, the preceding ":" identifies the path parameter as a string path parameter. You can use preceding "#" to identify a path parameter as a numerical path parameter instead and Luaw will automatically parse it as a number. For example, you can change the handler-hellouser.lua code as follows,

```lua
GET '/user/:username/#count' {
	function(req, resp, pathParams)
		return "Hello "..pathParams.username.."! You are user number "..pathParams.count.." to visit this site."
	end
}
```

and then point your browser to "http://localhost:7001/myapp/user/raksoras/9" to get
```
Hello raksoras! You are user number 9 to visit this site.
```
in your browser window.

================================================
FILE: docs/template-view.md
================================================
#6. Luaw Template Views

Luaw comes equipped with a Luaw template views for generating server-side dynamic content. Luaw template views serve the same need as served by solutions like JSP, ASP and PHP.

In reality, a Luaw template view is a normal, plain Lua code with little bit of syntactical sugar added for generating HTML/XHTML/XML markup. This allows developer to use full power of Lua - including if-then-else conditional checks, for/while loops, local variables and functions etc. - within the template view while still using concise, readable notation for generating markup in output. Of course this power can be abused by writing complex logic - which should belong to a well defined, separate business logic tier - inside a template view but we trust that you will do no such thing :) Remember with great power comes great responsibility!

Without further ado, here is a sample Luaw template view file - view-address.lua:
```lua
BEGIN 'html'
    BEGIN 'head'
        BEGIN 'title'
            TEXT 'Address'
        END 'title'
    END 'head'
    BEGIN 'body'
        BEGIN 'div' {class='address'}
            BEGIN 'h1'
                TEXT(model.title)
            END 'h1'
            BEGIN 'table' {border="1", margin="1px"}
                BEGIN 'tr'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT 'City'
                    END 'td'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT(model.city)
                    END 'td'
                END 'tr'
                if (model.zip == 94086) then
                    BEGIN 'tr'
                        BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                            TEXT 'County'
                        END 'td'
                        BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                            TEXT 'Santa Clara'
                        END 'td'
                    END 'tr'
                end
                BEGIN 'tr'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT 'Zip'
                    END 'td'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT(model.zip)
                    END 'td'
                END 'tr'
            END 'table'
        END 'div'
    END 'body'
END 'html'
```

Each Luaw template view has access to following implicitly defined variables: `req`, `resp`,
`pathParams` and `model` passed from resource handler that invoked the view

It also defines three syntax sugar extentions to generate markeup `BEGIN`, `TEXT` and `END`.

1. You use `BEGIN tag_name` to open any HTML or XML tag of type tag_name
2. If the tag has any attributes you follow the `BEGIN tag_name`  with set of attributes defined like this `{name1=value1, name2=value2 ...}`
3. You close opne tag with `END tag_name`
4. If you want to emit any content in the body of the response you are generating you can use `TEXT()` to do so. `TEXT()` can take multiple, variable number of arguments of different types. It will include them in the output response in the same order by calling tostring() on each of the arguments. Consequtive arguments are separated by a single space in the response. As a special case if you want to emit a literal string ('Hello world!' for example) you can do so without using parenthesis like this: `TEXT 'Hello world!'`. There is nothing special about this syntax. Lua as a language offers this convenient notation for calling any Lua function with a single, literal string argument.

That's it! You can mix your normal Lua code - if/else, loops etc. - along with the markup to be  genertated easily. Take a look at the check for zipcode in the sample code above to see an example of this.

In the next chapter we will see how to use Luaw template views with resource handlers.

**_NOTE:_**

You can write resusable templates to generate markup that is common across many pages - web site header and footer, for example - by writing Lua functions that generate this common markup and then simply invoking these where you want to include the markup. This is very similar to JSP tag libraries or other server side include technologies. The only tricky part is making syntax sugar extentions like `BEGIN`, `TEXT` and `END` available to normal Lua functions outside Lua template views. This is actually very easy. Behind the scene these three extentions are really closures bound to current request/response scope. This means you can pass them to any normal Lua functions - even functions defined in separate .lua files that themselves are not Lua template views - like this:

```lua
-- Reusable markup generating function AKA tag library
function generateHeader(model, BEGIN, TEXT, END)
	BEGIN 'div' {class='header')
    	--- generate HTML here using BEGIN, TEXT and END
    END 'div'
end
```

```lua
-- Using common markup generating function from Luaw template view
local ssi = require("common_markup.lua")

-- Just call function to include the markup at write place
ssi.generateHeader(model, BEGIN, TEXT, END)

BEGIN 'div' {class="body"}
	--- generate page specific page markup here
END 'div'

ssi.generateFooter(model, BEGIN, TEXT, END)
```

================================================
FILE: docs/third-webapp.md
================================================
#7. Your third webapp - with Luaw template view

In this chapter we will put together all the pieces we have learned about so far - resource handler reading REST path parameters + Luaw template view - to build a toy but nevertheless complete MVC solution with Luaw.

##Luaw template view
Create a new directory `views` under `luaw_root_dir/webapps/myapp` and add a file named "view-address.lua" to the `views` directory, containing following piece of Luaw template view code from the last chapter:

```lua
BEGIN 'html'
    BEGIN 'head'
        BEGIN 'title'
            TEXT 'Address'
        END 'title'
    END 'head'
    BEGIN 'body'
        BEGIN 'div' {class='address'}
            BEGIN 'h1'
                TEXT(model.title)
            END 'h1'
            BEGIN 'table' {border="1", margin="1px"}
                BEGIN 'tr'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT 'City'
                    END 'td'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT(model.city)
                    END 'td'
                END 'tr'
                if (model.zip == 94086) then
                    BEGIN 'tr'
                        BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                            TEXT 'County'
                        END 'td'
                        BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                            TEXT 'Santa Clara'
                        END 'td'
                    END 'tr'
                end
                BEGIN 'tr'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT 'Zip'
                    END 'td'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT(model.zip)
                    END 'td'
                END 'tr'
            END 'table'
        END 'div'
    END 'body'
END 'html'
```

##REST resource handler
Add a file `handler-address.lua` to `luaw_root_dir/webapps/myapp/handlers` containing following code:
```lua
    GET 'address/:city/#zip' {
        function(req, resp, pathParams)
            address = {
                city = pathParams.city,
                zip = pathParams.zip
            }
            return '/views/view-address.lua', address
        end
    }
```

This resource handler handles GET request made to URL path "address/_city_/_zip_" with two path parameters - "city" defined as string parameter (denoted by preceding ':') and "zip" defined as numeric (denoted by preceding '#')

Most interesting line in the handler above is the following return statement from the function handler:

		return '/views/view-address.lua', address

So far we have been returning a single string from resource handler function (return "Hello World", for example) which Luaw took as a whole response body. This is a second, alternative form. In this form we return two values from the handler function - Lua as a language allows returning multiple values from a function which is very handy - a string and any other value. Whenever this form is used Luaw automatically interpretes first string returned as a relative path to a Luaw template view and the second value to be a "model" that is to be passed to the "view" defined by the Luaw template view. The Luaw template view path is always relative to the application root (`luaw_root_dir/webapps/myapp` in case of our example here) and always starts with a "/". The second value returned - the "model" - can be of any type - number, string, boolean or a Lua table. Our example resource handler above reads values for city and zip code from its REST path parameters and puts them in a single Lua table which it then returns as a model. Our Luaw template view in the step 1 above - view-address.lua - gets access to this model passed from the resource handler using variable `model`

##Modified web.lua
Modify *<luaw_root_dir>*/webapps/myapp/web.lua to include the "viewPattern" element that defines a view pattern so Luaw can load any Luaw template view definitions found under "myapp" directory

    luaw_webapp = {
        resourcePattern = "handler%-.*%.lua",
        viewPattern = "view%-.*%.lua",
    }

##Test your work
Finally, restart your luaw server by running

    cd luaw_root_dir
    ./bin/luaw_server ./conf/server.cfg

You should see console output similar to this:

```
********************* Starting webapp myapp ****************************
  .Loading resource ./webapps/myapp/handlers/handler-address.lua
  .Loading resource ./webapps/myapp/handlers/handler-hellouser.lua
  .Loading resource ./webapps/myapp/handlers/handler-helloworld.lua
  #Loaded total 3 resources

  .Loading view /views/view-address.lua
  #Compiled total 1 views
 *********** ********* Webapp myapp started ****************************
```

Note the "loading view" part.

Now point your browser to http://127.0.0.1:7001/myapp/address/Sunnyvale/94085 and see the output in your browser.

To verify that the Lua conditional logic embedded in view-address.lua is working properly, point your browser to http://127.0.0.1:7001/myapp/address/Sunnyvale/94086 and see the output. It should include one additional row for the county now.


================================================
FILE: docs/user-threads.md
================================================
#11. User Threads

Luaw allows developer to spawn user threads to execute multiple tasks in parallel. Luaw user threads are implemented using Lua's coroutines and hence are very lightweight compared to real OS threads. In addition to this Luaw also pools and reuses Lua coroutines underlying user threads to make spawning user threads even cheaper. As a result you should be able to spawn thousands of them without having to worry about using up all your system resources.

Here is a small example of user threads' usage. It uses async HTTP client introduced in last chapter to do two HTTP calls in parallel using user threads, waits till both of them return and then processes both the responses received. It is an example of common scatter/gather pattern frequently encountered in service oriented architecture and illustrates how Luaw's user threads nicely complement Luaw's async HTTP client's functionality

```lua
local function parallelHttpRequest(host, url)
    local clientReq = Luaw.newClientHttpRequest()
    clientReq.hostName = host
    clientReq.method = 'GET'
    clientReq.url = url
    clientReq:addHeader("Host", host)
    local clientResp = clientReq:execute()
    return clientResp
end

local scheduler = Luaw.scheduler
-- do two HTTP request in parallel
local threadCtx1 = scheduler.startUserThread(parallelHttpRequest, "www.google.com", "/")
local threadCtx2 = scheduler.startUserThread(parallelHttpRequest, "www.facebook.com", "/")

-- wait on both threads to be donw
scheduler.join(threadCtx1, threadCtx2)

-- Retrieve the responses received
local clientResp1 = threadCtx1.result
local clientResp2 = threadCtx2.result
```

1. You use Luaw.scheduler.startUserThread(function, ...) to start a new user thread. First argument to this method must be a "thread function" that is to be run by the thread being spawn. This function argument may be followed by variable number of arguments which are passed to the thread function as its argument. In case of the example above our thread function is "parallelHttpRequest" which takes two arguments - a hostname and a URL. These two arguments are passed in Luaw.scheduler.startUserThread() after the thread function in the same order. Luaw.scheduler.startUserThread() re-uses internally pooled coroutine - if one is available - to run the thread function provided so this call is really cheap.

2. Luaw.scheduler.startUserThread() returns a thread context which you can pass to scheduler.join() to wait on the thread to complete. scheduler.join() accepts variable number of thread contexts so you can wait on more than one thread in a single call. scheduler.join() doesn't return till all the threads represented by thread contexts passed into it have finished executing.

3. Value returned by the thread function ("parallelHttpRequest" in our case) can be retrieved as threadCtx.result. Thread function should return only single value (Lua allows functions to return multiple values). If there is a need to return multiple values from the thread function, the function can stuff them all inside a single Lua table with different keys (i.e. property names) and return the Lua table instead.

4. Finally, in all other aspects user threads are semantically similar to system threads spawned by Luaw server itself to server incoming HTTP requests. That is, they can use async calls like HTTP client's execute or Timer methods (explained in the next chapter) and Luaw will automatically suspend them when they are waiting for the async calls to return. They are fully hooked into Luaw's internal async callback mechanism.


================================================
FILE: docs/user-timers.md
================================================
#12. Luaw Timers

Luaw supports user defined timers. Here is an example:

```lua
local timer = Luaw.newTimer()
timer:start(1000)
doSomeStuff()
print("waiting till it's time...")
timer:wait()
print('done waiting!')
-- call timer: delete() to free timer resources immediately. If not delete() is not called
-- timer resources hang around till Lua's VM garbage collects them
timer:delete()
```

1. You create a new timer using Luaw.newTimer()
2. You start it with some timeout - specified in milliseconds using timer:start(timeout)
3.  and finally you wait on it using timer:wait()

That's basically it!

There is one more call - `timer:sleep(timeout)` - that combines `timer:start()` and `timer:wait()` in a single function call. The example above can be rewritten as follows provided we did not have to call doSomeStuff() in between:

```lua
local timer = Luaw.newTimer()
timer:sleep(1000)
print('done waiting!')
```

Luaw timers are fully hooked into Luaw's async machinert. Just like any other async call - HTTP client's execute(), for example - timer:wait() or timer:sleep() suspend current Luaw thread till the time is up.

================================================
FILE: lib/luapack.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local luaw_constants = require("luaw_constants")

local lpackMT = getmetatable(luaw_lpack_lib.newLPackParser())

lpackMT.INT_RANGES = {
    lpackMT.UINT_8,
    lpackMT.UINT_16,
    lpackMT.UINT_32,
    lpackMT.INT_8,
    lpackMT.INT_16,
    lpackMT.INT_32,
    lpackMT.INT_64,
    lpackMT.FLOAT,
    lpackMT.DOUBLE
}

lpackMT.FLOAT_RANGES = {
    lpackMT.FLOAT,
    lpackMT.DOUBLE
}

lpackMT.STRING_RANGES = {
    lpackMT.STRING,
    lpackMT.BIG_STRING,
    lpackMT.HUGE_STRING
}

lpackMT.DICT_ENTRY_RANGES = {
    lpackMT.DICT_ENTRY,
    lpackMT.BIG_DICT_ENTRY
}

local function findMinRange(num, ranges)
    for i, range in ipairs(ranges) do
        if ((num >= range[4])and(num <= range[5])) then
            return range
        end
    end
    error("Number "..num.." outside supported max range")
end

-- read functions

local function readNextBuffer(lpack)
    if not lpack.EOF then
        local newBuffer = lpack:readFn()
        if not newBuffer then
            lpack.EOF = true
        else
            local buffer = lpack.buffer
            local offset = lpack.offset
            if ((buffer)and(#buffer > offset)) then
                lpack.buffer = string.sub(buffer, offset)..newBuffer
            else
                lpack.buffer = newBuffer
            end
            lpack.offset = 0
        end
    end
end

local function done(lpack)
    return ((lpack.EOF)and(lpack.offset >= #lpack.buffer))
end

local function readNumber(lpack, numType)
    while (not lpack:done()) do
        local offset = lpack.offset
        local readLen, value = lpack.read_number(numType, lpack.buffer, offset)
        if (readLen < 0) then
            error("Error while reading number at byte# "..tostring(offset).." in buffer: "..tostring(lpack.buffer))
        end
        if (readLen > 0) then
            lpack.offset = offset + readLen
            return value
        end
        readNextBuffer(lpack);
    end
end

local function readMarker(lpack)
    return readNumber(lpack, lpack.TYPE_MARKER[2])
end

local function readString(lpack, desiredLen)
    local accm
    while ((desiredLen > 0)and(not lpack:done())) do
        local offset = lpack.offset
        local buffer = lpack.buffer
        local readLen, value = lpack.read_string(desiredLen, buffer, offset)
        if (readLen > 0) then
            lpack.offset = offset + readLen
            desiredLen = desiredLen - readLen

            if (desiredLen == 0) then
                if accm then
                    table.insert(accm, value)
                    return table.concat(accm)
                end
                return value
            end

            if not accm then accm = {} end
            table.insert(accm, value)
        end
        readNextBuffer(lpack);
    end
end

local function deserialize(lpack, container, isMap)
    local key, val, len, t
    local isKey = true
    local dictionary = lpack.dictionary

    while not lpack:done() do
        t = readMarker(lpack)

        if t == lpack.NIL[2] then
            val = nil

        elseif t == lpack.BOOL_TRUE[2] then
            val = true

        elseif t == lpack.BOOL_FALSE[2] then
            val = false

        elseif t == lpack.STRING[2] then
            len = readNumber(lpack, lpack.UINT_8[2])
            val = readString(lpack, len)

        elseif t == lpack.BIG_STRING[2] then
            len = readNumber(lpack, lpack.UINT_16[2])
            val = readString(lpack, len)

        elseif t == lpack.HUGE_STRING[2] then
            len = readNumber(lpack, lpack.UINT_32[2])
            val = readString(lpack, len)

        elseif t == lpack.MAP_START[2] then
            val = deserialize(lpack, luaw_lpack_lib.createDict(0, 16), true)

        elseif t == lpack.ARRAY_START[2] then
            val = deserialize(lpack, luaw_lpack_lib.createDict(16, 0), false)

        elseif t == lpack.RECORD_END[2] then
            if ((isMap)and(not isKey)) then
                error("Unbalanced table, key without corresponding value found")
            end
            return container

        elseif t == lpack.DICT_ENTRY[2] then
            local dw = readNumber(lpack, lpack.UINT_8[2])
            assert(dictionary, "Missing dictionary")
            val = assert(dictionary[dw], "Entry missing in dictionary: "..dw)

        elseif t == lpack.BIG_DICT_ENTRY[2] then
            local dw = readNumber(lpack, lpack.UINT_16[2])
            assert(dictionary, "Missing dictionary")
            val = assert(dictionary[dw], "Entry missing in dictionary")

        elseif t == lpack.DICT_START[2] then
            dictionary = deserialize(lpack, luaw_lpack_lib.createDict(64, 0), false)
            lpack.dictionary = dictionary
            debugDump(dictionary)

        else
            -- everything else is a number
            val = readNumber(lpack, t)
        end

        if container then
            if isMap then
                if isKey then
                    key = val
                    isKey = false
                else
                    container[key] = val
                    isKey = true
                end
            else
                -- is array
                table.insert(container, val)
            end
        else
            if (t ~= lpack.DICT_START[2]) then
                -- single, standalone value
                return val
            end
        end
    end

    return val
end

local function read(lpack)
    readNextBuffer(lpack)
    val = deserialize(lpack, nil, false)
    return val
end

local function newLPackReader()
    local lpackReader = luaw_lpack_lib.newLPackParser();
    lpackReader.EOF = false
    lpackReader.buffer = ''
    lpackReader.offset = 0
    lpackReader.done = done
    lpackReader.read = read
    return lpackReader
end

local function newLPackStringReader(str)
    assert(str, "String cannot be null")
    local lpackReader = newLPackReader()
    local eof = false
    lpackReader.readFn = function()
        if (not eof) then
            eof = true
            return str
        end
    end
    return lpackReader
end

local function newLPackFileReader(file)
    assert(file, "File cannot be null")
    local lpackReader = newLPackReader()
    lpackReader.readFn = function()
        return file:read(1024)
    end
    return lpackReader
end

local function newLPackReqReader(req)
    assert(req, "Request cannot be null")
    local lpackReader = newLPackReader()
    lpackReader.readFn = function()
        if ((not req.EOF)and(not req.luaw_mesg_done)) then
            req:readAndParse()
            local str =  req:consumeBodyChunkParsed()
            if (not str) then
                debugDump(req)
            end
            return str
        end
    end
    return lpackReader
end

-- Write functions

local function flush(lpack)
    local writeQ = lpack.writeQ
    local count = #writeQ
    if count then
        local str = lpack.serialize_write_Q(writeQ, lpack.writeQsize)
        if str then
            lpack:writeFn(str)
            lpack.writeQsize = 0
            for i=1,count do
                writeQ[i] = nil
            end
        end
    end
end

local function qstore(lpack, val, size)
    if not val then
        error("nil string passed to write(), use writeNil() instead")
    end

    local writeQ = lpack.writeQ
    table.insert(writeQ, val);
    lpack.writeQsize = lpack.writeQsize + size;

    if (lpack.writeQsize >= lpack.flushLimit) then
        flush(lpack)
    end
end

local function writeMarker(lpack, marker)
    if ((marker[2] < lpack.TYPE_MARKER[2])or(marker[2] > lpack.HUGE_STRING[2])) then
        error("Invalid marker "..marker.." specified")
    end
    qstore(lpack, marker[2], 1)
end

local function startMap(lpack)
    writeMarker(lpack, lpack.MAP_START)
end

local function startArray(lpack)
    writeMarker(lpack, lpack.ARRAY_START)
end

local function startDict(lpack)
    writeMarker(lpack, lpack.DICT_START)
end

local function endCollection(lpack)
    writeMarker(lpack, lpack.RECORD_END)
end

local function writeBoolean(lpack, value)
    if (value) then
        writeMarker(lpack, lpack.BOOL_TRUE)
    else
        writeMarker(lpack, lpack.BOOL_FALSE)
    end
end

local function writeNil(lpack)
    writeMarker(lpack, lpack.NIL)
end

local function writeNumber(lpack, num)
    local range

    if (num % 1 == 0) then
        -- integer
        range = findMinRange(num, lpack.INT_RANGES)
    else
        -- float
        range = findMinRange(num, lpack.FLOAT_RANGES)
    end
    qstore(lpack, range[2], 1);
    qstore(lpack, num, range[3]);
end

local function writeString(lpack, str)
    local dw, len, range
    local dict = lpack.dictionary
    if dict then
        dw = dict[str]
    end

    if dw then
        range = findMinRange(dw, lpack.DICT_ENTRY_RANGES)
        str = dw
        len = range[3]
    else
        len = #str
        range = findMinRange(len, lpack.STRING_RANGES)
    end

    qstore(lpack, range[2], 1) -- marker
    qstore(lpack, str, len) -- actual string value/dictionary entry
end

local function serialize(lpack, val)
    local t = type(val)

    if t == 'nil' then
        writeNil(lpack)
        return;
    end

    if t == 'boolean' then
        writeBoolean(lpack, val)
        return
    end

    if t == 'number' then
        writeNumber(lpack, val)
        return
    end

    if t == 'string' then
        writeString(lpack, val)
        return
    end

    if t == 'table' then
        if (#val > 0) then
            writeMarker(lpack, lpack.ARRAY_START)
            for i, v in ipairs(val) do
                serialize(lpack, v)
            end
            endCollection(lpack)
        else
            writeMarker(lpack, lpack.MAP_START)
            for k, v in pairs(val) do
                serialize(lpack, k)
                serialize(lpack, v)
            end
            endCollection(lpack)
        end
    end
end

local function write(lpack, val)
    serialize(lpack, val)
    flush(lpack)
end

local function setDictionaryForWrite(lpack, dict)
    assert((type(dict) == 'table'), "Please provide valid dictionary table")
    local dictionary = luaw_lpack_lib.createDict(#dict, 0)
    writeMarker(lpack, lpack.DICT_START)
    for i, dw in ipairs(dict) do
        writeString(lpack, dw)
        dictionary[dw] = i
    end
    writeMarker(lpack, lpack.RECORD_END)
    lpack.dictionary = dictionary
end

local function newLPackWriter(limit)
    local lpackWriter = luaw_lpack_lib.newLPackParser()
    lpackWriter.writeQ = {}
    lpackWriter.writeQsize = 0
    lpackWriter.flushLimit = limit or luaw_constants.CONN_BUFFER_SIZE
    lpackWriter.useDictionary = setDictionaryForWrite
    lpackWriter.write = write
    return lpackWriter
end

local function newLPackFileWriter(file, limit)
    assert(file, "File can not be nil")
    local lpackWriter = newLPackWriter(limit)
    lpackWriter.writeFn = function(lpack, str)
        file:write(str)
    end
    return lpackWriter
end

local function newLPackBufferWriter(buff, limit)
    assert(buff, "buffer can not be nil")
    local lpackWriter = newLPackWriter(limit)
    lpackWriter.writeFn = function(lpack, str)
        table.insert(buff, str)
    end
    return lpackWriter
end

local function newLPackRespWriter(resp, limit)
    assert(resp, "response can not be nil")
    local lpackWriter = newLPackWriter(limit)
    resp.headers['Content-Type'] = 'application/luapack'
    resp:startStreaming()
    lpackWriter.writeFn = function(lpack, str)
        resp:appendBody(str)
    end
    return lpackWriter
end

luaw_lpack_lib.newLPackFileReader = newLPackFileReader
luaw_lpack_lib.newLPackStringReader = newLPackStringReader
luaw_lpack_lib.newLPackReqReader = newLPackReqReader
luaw_lpack_lib.newLPackFileWriter = newLPackFileWriter
luaw_lpack_lib.newLPackBufferWriter = newLPackBufferWriter
luaw_lpack_lib.newLPackRespWriter = newLPackRespWriter

return luaw_lpack_lib




================================================
FILE: lib/luaw_constants.lua
================================================
local constMT = {
    __newindex = function(table, key, value)
        error("constant "..table.name.." cannot be changed")
    end,

    __tostring = function(table)
        return table.name
    end,

    __concat = function(op1, op2)
        return tostring(op1)..tostring(op2)
    end,

    __metatable = "Luaw constant"
}

local function luaw_constant(value)
    local c = {name = value}
    setmetatable(c, constMT)
    return c
end

return {
    -- scheduler constants
    TS_RUNNABLE = luaw_constant("RUNNABLE"),
    TS_DONE = luaw_constant("DONE"),
    TS_BLOCKED_EVENT = luaw_constant("BLOCKED_ON_EVENT"),
    TS_BLOCKED_THREAD = luaw_constant("BLOCKED_ON_THREAD"),
    END_OF_CALL = luaw_constant("END_OF_CALL"),
    END_OF_THREAD = luaw_constant("END_OF_THREAD"),

    -- TCP constants
    DEFAULT_CONNECT_TIMEOUT = luaw_server_config.connect_timeout or 8000,
    DEFAULT_READ_TIMEOUT = luaw_server_config.read_timeout or 3000,
    DEFAULT_WRITE_TIMEOUT = luaw_server_config.write_timeout or 3000,
    CONN_BUFFER_SIZE = luaw_server_config.connection_buffer_size or 4096,

    -- HTTP parser constants
    EOF = 0,
    CRLF = '\r\n',
    MULTIPART_BEGIN = luaw_constant("MULTIPART_BEGIN"),
    PART_BEGIN = luaw_constant("PART_BEGIN"),
    PART_DATA = luaw_constant("PART_DATA"),
    PART_END = luaw_constant("PART_END"),
    MULTIPART_END = luaw_constant("MULTIPART_END")

}

================================================
FILE: lib/luaw_data_structs_lib.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local module = {}

--
-- Registry
---

-- reg[0] stores head of the free list
local function ref(reg, obj)
    if not obj then return -1 end

    local ref = reg[0];
    if ref then
        reg[0] = reg[ref]
    else
        ref = #reg + 1
    end

    reg[ref] = obj
    reg.size = reg.size + 1
    return ref
end

local function unref(reg, ref)
    if ref >= 0 then
        reg[ref] = reg[0]
        reg[0] = ref
        reg.size = reg.size -1
    end
end

function module.newRegistry(size)
    local reg = {}
    reg.ref = ref;
    reg.unref = unref;
    reg.size = 0
    return reg;
end

--
-- Ring buffers
--

local function offer(rb, obj)
    local size = rb.size
    local filled = rb.filled
    local writer = rb.writer

    if (filled < size) then
        rb[writer] = obj
        rb.filled = filled + 1
        if (writer == size) then
            rb.writer = 1
        else
            rb.writer = writer + 1
        end
        return true;
    end
    return false;
end

local function offerWithWait(rb, obj)
    local added = offer(rb, obj)
    while not added do
        coroutine.yield()
        added = offer(rb, obj)
    end
    return added
end

local function take(rb)
    local size = rb.size
    local filled = rb.filled
    local reader = rb.reader
    local obj = nil

    if (filled > 0) then
        obj = rb[reader];
        rb[reader] = nil;
        rb.filled = filled - 1
        if (reader == size) then
            rb.reader = 1
        else
            rb.reader = reader + 1
        end
    end
    return obj
end

local function takeWithWait(rb)
    local obj = take(rb)
    while not obj do
        coroutine.yield()
        obj = take(rb)
    end
    return obj
end

local function offerWithOverwrite(rb, obj)
    local added = offer(rb, obj)
    if added then return true end

    -- overwrite oldest item
    local overwrittenObj = take(rb)
    offer(rb, obj)
    return false, overwrittenObj
end


function module.newRingBuffer(size)
    local rb = {}
    rb.reader = 1
    rb.writer = 1
    rb.filled = 0
    rb.size = size
    rb.offer = offer
    rb.take = take
    return rb
end

function module.newOverwrittingRingBuffer(size)
    local rb = {}
    rb.reader = 1
    rb.writer = 1
    rb.filled = 0
    rb.size = size
    rb.offer = offerWithOverwrite
    rb.take = take
    return rb
end

function module.newBlockingRingBuffer(size)
    local rb = {}
    rb.reader = 1
    rb.writer = 1
    rb.filled = 0
    rb.size = size
    rb.offer = offerWithWait
    rb.take = takeWithWait
    return rb
end

return module


================================================
FILE: lib/luaw_http.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local constants = require('luaw_constants')
local luaw_tcp_lib = require('luaw_tcp')

local TS_BLOCKED_EVENT = constants.TS_BLOCKED_EVENT
local TS_RUNNABLE = constants.TS_RUNNABLE

local CONN_BUFFER_SIZE = constants.CONN_BUFFER_SIZE

local EOF = constants.EOF
local CRLF = constants.CRLF

local MULTIPART_BEGIN = constants.MULTIPART_BEGIN
local PART_BEGIN = constants.PART_BEGIN
local PART_DATA = constants.PART_DATA
local PART_END = constants.PART_END
local MULTIPART_END = constants.MULTIPART_END

local http_status_codes = {
    [100] = "Continue",
    [101] = "Switching Protocols",
    [200] = "OK",
    [201] = "Created",
    [202] = "Accepted",
    [203] = "Non-Authoritative Information",
    [204] = "No Content",
    [205] = "Reset Content",
    [206] = "Partial Content",
    [300] = "Multiple Choices",
    [301] = "Moved Permanently",
    [302] = "Found",
    [303] = "See Other",
    [304] = "Not Modified",
    [305] = "Use Proxy",
    [307] = "Temporary Redirect",
    [400] = "Bad Request",
    [401] = "Unauthorized",
    [402] = "Payment Required",
    [403] = "Forbidden",
    [404] = "Not Found",
    [405] = "Method Not Allowed",
    [406] = "Not Acceptable",
    [407] = "Proxy Authentication Required",
    [408] = "Request Timeout",
    [409] = "Conflict",
    [410] = "Gone",
    [411] = "Length Required",
    [412] = "Precondition Failed",
    [413] = "Request Entity Too Large",
    [414] = "Request-URI Too Long",
    [416] = "Requested Range Not Satisfiable",
    [417] = "Expectation Failed",
    [500] = "Internal Server Error",
    [501] = "Not Implemented",
    [502] = "Bad Gateway",
    [503] = "Service Unavailable",
    [504] = "Gateway Timeout",
    [505] = "HTTP Version Not Supported"
}

setmetatable(http_status_codes, {
    __index = function(status)
        return "User Defined Status"
    end
})

-- write buffer implementation

local function clearArrayPart(t)
    local len = #t
    for i=1,len do
        t[i] = nil
    end
end

local function reset(buffer)
    clearArrayPart(buffer)
    buffer.len = 0
end

local function concat(buffer)
    return table.concat(buffer)
end

local function append(buffer, str)
    local len = buffer.len
    if (str) then
        table.insert(buffer, str)
        len = len + #str
        buffer.len = len
    end
    return len
end

local function newBuffer()
    return {
        len = 0,
        reset = reset,
        concat = concat,
        append = append
    }
end


function luaw_http_lib.storeHttpParam(params, name , value)
	oldValue = params[name]
    if (oldValue) then
		-- handle multi-valued param names
        if (type(oldValue) == 'table') then
        	table.insert(oldValue, value)
        else
        	-- single param value already stored against the same param name
			-- convert it to table and store multiple values in it
			params[name] = {oldValue, value}
		end
	else
		params[name] = value
	end
end

local parserMT = getmetatable(luaw_http_lib.newHttpRequestParser())

--[[ HTTP parser we use can invoke callback for the same HTTP field (status, URL, header
name/value etc.) multiple times, each time passing only few characters for the current
ongoing field. This can happen because we are reading HTTP request or response body in
multiple chuncks - either of a fixed byte buffer size of by new lines. For this reason we
"accumulate" http header name and value in a hidden request table fields (_acc_header_name_
/_acc_header_value_) and then store full header value against full header name when the
parser issues callback for a next HTTP field. Other fields like URL, status etc. are
accumulated by concatenating them "in place".
]]


local function accumulateChunkedValue(req, name, chunk)
	local accValue = rawget(req, name)
	if accValue then
		rawset(req, name, accValue .. chunk)
	else
		rawset(req, name, chunk)
	end
end

local function addHeader(req, hName, hValue)
	if (hName and hValue) then
		local headers = req.headers
		local currValues = headers[hName]

        if currValues then
            -- handle multi-valued headers
            if (type(currValues) == 'table') then
                table.insert(currValues, hValue)
            else
                -- single string header value already stored against the same header name
                -- convert it to table and store multiple values in it
                headers[hName] = {currValues, hValue}
            end
        else
            headers[hName] = hValue
        end
        return true
	end
	return false
end

local function handleAccHttpHeader(req)
	local hName = rawget(req, '_acc_header_name_')
	local hValue = rawget(req, '_acc_header_value_')
	local added = addHeader(req, hName, hValue)
	if (added) then
        rawset(req, '_acc_header_name_', nil)
        rawset(req, '_acc_header_value_', nil)
	end
end

local function handleKeepAlive(req, keepAlive)
    if not keepAlive then
        req.headers['Connection'] = 'close'
        req.EOF = true
    end
end

local function onNone(req, cbType)
end

local function onMesgBegin(req, cbtype, remaining)
    req:reset()
end

local function onStatus(req, cbtype, remaining ,status)
	accumulateChunkedValue(req, 'statusMesg', status)
end

local function onURL(req, cbtype, remaining, url)
	accumulateChunkedValue(req, 'url', url)
end

local function onHeaderName(req, cbtype, remaining, hName)
	handleAccHttpHeader(req)
    accumulateChunkedValue(req, '_acc_header_name_', hName)
end

local function onHeaderValue(req, cbtype, remaining, hValue)
	if not hValue then hValue = '' end -- empty header value
	accumulateChunkedValue(req, '_acc_header_value_', hValue)
end

local function onHeadersComplete(req, cbtype, remaining, keepAlive, httpMajor, httpMinor, method, status)
	handleAccHttpHeader(req)
	handleKeepAlive(req, keepAlive)
	req.major_version = httpMajor
	req.minor_version = httpMinor
    req.method = method
    req.status = status

    -- parse URL
    local url = req.url
    local parsedURL
    if url then
        local method = req.method
        parsedURL = luaw_http_lib.parseURL(url, ((method) and (string.upper(method) == "CONNECT")))
    else
        parsedURL = {}
    end
    req.parsedURL = parsedURL

    -- GET query params
    local params = {}
    local queryString = parsedURL.queryString
    if queryString then
        assert(luaw_http_lib:urlDecode(queryString, params))
    end
    req.params = params

    req.luaw_headers_done = true
end

local function onBody(req, cbtype, remaining, chunk)
    req.bodyParts:append(chunk)
end

local function onMesgComplete(req, cbtype, remaining, keepAlive)
	-- for the rare boundary case of chunked transfer encoding, where headers may continue
	-- after the last body chunk
	handleAccHttpHeader(req)
	handleKeepAlive(req, keepAlive)
	local luaw_parser = req.luaw_parser
    if (luaw_parser) then
        luaw_parser:initHttpParser()
    end

    -- store body
    local bodyParts = req.bodyParts
    req.body = bodyParts:concat()
    bodyParts:reset()

    -- POST form params
    local params = req.params
    local contentType = req.headers['Content-Type']
    if ((contentType) and (contentType:lower() == 'application/x-www-form-urlencoded')) then
        assert(luaw_http_lib:urlDecode(req.body, params))
    end

    req.luaw_mesg_done = true
end

-- Order is important and must match C enum http_parser_cb_type
local http_callbacks_lua = {
    onNone,
    onMesgBegin,
    onStatus,
    onURL,
    onHeaderName,
    onHeaderValue,
    onHeadersComplete,
    onBody,
    onMesgComplete
}

local function parseHttpFragment(req, conn, parser, content, offset)
    -- matched against most number of return results possible. Actual variable names
    -- are meaningless without the context of correct callback, misleading even!
    local cbtype, offset, keepAlive, httpMajor, httpMinor, method, status = parser:parseHttp(content, offset)
    if (not cbtype) then
        conn:close()
        return error(offset) -- offset carries error message in this case
    end

    local callback = http_callbacks_lua[cbtype]
    if (not callback) then
        conn:close()
        return error("Invalid HTTP parser callback# "..tostring(cbtype).." requested")
    end

    callback(req, cbtype, remaining, keepAlive, httpMajor, httpMinor, method, status)
    return  cbtype, offset
end

local function hasContent(content, offset)
    return (content)and(offset)and(offset < #content)
end

local function readAndParse(req)
    local conn = req.luaw_conn
    local parser = req.luaw_parser
    local content = req.luaw_read_content
    local offset = req.luaw_read_offset
    local httpcb, status

    if (not hasContent(content, offset)) then
        -- read new content from socket
        status, content = conn:read(req.readTimeout)
        if (not status) then
            if (content == 'EOF') then
                req:addHeader('Connection', 'close')
                req.luaw_read_content = nil
                req.luaw_read_offset = nil
                req.luaw_headers_done = true
                req.luaw_mesg_done = true
                req.EOF = true
                return
            else
                return error(content)
            end
        end
        offset = 0 -- offset is for C, therefore zero based
    end

    httpcb, offset = parseHttpFragment(req, conn, parser, content, offset)

    if (hasContent(content,offset)) then
        -- store back remaining content in request object for next HTTP request parsing
        req.luaw_read_content = content
        req.luaw_read_offset = offset
    else
        req.luaw_read_content = nil
        req.luaw_read_offset = nil
    end
end

local function consumeTill(input, search, offset)
    local start, stop = string.find(input, search, offset, true)
    if (start and stop) then
        return stop+1, string.sub(input, offset, start-1)
    end
end

local function getMultipartBoundary(req)
    local header = req.headers['Content-Type']
    if (header) then
        local offset, contentType = consumeTill(header, ";", 1)
        if (contentType == "multipart/form-data") then
            local boundary
            offset, boundary = consumeTill(header, "=", offset)
            if ((boundary)and(string.find(boundary, "boundary", 1, true))) then
                return '--'..string.sub(header, offset)
            end
        end
    end
end

local function isMultipart(req)
    if (req.luaw_multipart_boundary) then
        return true
    end

    local boundary = getMultipartBoundary(req)
    if (boundary) then
        req.luaw_multipart_boundary = boundary
        req.luaw_multipart_end = boundary .. '--'
        return true
    end
end

local function isLuaPackMesg(req)
    local contentType = req.headers['Content-Type']
    if ('application/luapack' == contentType) then
        return true
    end
end

local function readFull(req)
    -- first parse till headers are done
    while (not req.luaw_headers_done) do
        req:readAndParse()
    end

    if ((isMultipart(req))or(isLuaPackMesg(req))) then
        -- multipart (file upload) HTTP requests and LuaPack requests are forced to be streaming to conserve memory
        return
    end

    while (not req.luaw_mesg_done) do
        req:readAndParse()
    end
end

local function consumeBodyChunkParsed(req)
    local bodyChunk = req.body
    if (bodyChunk) then
        req.body = nil
    else
        local bodyParts = req.bodyParts
        if (bodyParts.len > 0) then
            bodyChunk = bodyParts:concat()
            bodyParts:reset()
        end
    end
    return bodyChunk
end

local function bufferedConsume(req, search, content, offset)
    assert(search, "search pattern cannot be nil")

    while (true) do
        if ((content)and(#content > offset)) then
            local matchPos, matchStr  = consumeTill(content, search, offset)
            if (matchPos) then
                -- found match
                if (matchPos < #content) then
                    -- there is content remaining
                    return matchStr, content, matchPos
                end
                -- content fully consumed
                return matchStr, nil, nil
            end

            if (offset > 1) then
                content = string.sub(content, offset)
                offset = 1
            end
        end

        -- match not found, read more
        req:readAndParse()
        if (req.luaw_mesg_done) then
            error("premature HTTP message end")
        end

        local readStr = req:consumeBodyChunkParsed()
        if (readStr) then
            if (content) then
                content = content..readStr
            else
                content = readStr
                offset = 1
            end
        end
    end
end

local function saveState(req, content, offset)
    req.luaw_multipart_content = content
    req.luaw_multipart_offset = offset
end

function fetchNextPart(req, state)
    local content = req.luaw_multipart_content
    local offset = req.luaw_multipart_offset or 1
    local boundary = req.luaw_multipart_boundary
    local matchStr

    if (state == MULTIPART_BEGIN) then
        -- read beginning boundary
        matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)
        assert(matchedStr == boundary, "Missing multi-part boundary at the beginning of the part")
        state = PART_END
    end

    if (state == PART_END) then
        -- read "Content-Disposition" line
        matchedStr, content, offset = bufferedConsume(req, ": ", content, offset)
        assert(matchedStr == 'Content-Disposition',"Missing 'Content-Disposition' header")

        matchedStr, content, offset = bufferedConsume(req, "; ", content, offset)
        assert(string.find(matchedStr, 'form-data', 1, true), "Wrong Content-Disposition")

        matchedStr, content, offset = bufferedConsume(req, '"', content, offset)
        assert(string.find(matchedStr, "name", 1, true), "form field name missing")

        local fieldName
        fieldName, content, offset = bufferedConsume(req, '"', content, offset)
        assert(#fieldName > 0, "form field name missing")

        matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)
        local _, filenamePos = string.find(matchedStr, 'filename="', 1, true)
        local fileName
        if (filenamePos) then
            fileName = string.sub(matchedStr, filenamePos+1, #matchedStr-1)
        end

        local contentType
        if (fileName) then
            -- read "Content-Type" line
            matchedStr, content, offset = bufferedConsume(req, ": ", content, offset)
            assert(matchedStr == 'Content-Type', "Missing 'Content-Type' header")

            contentType, content, offset = bufferedConsume(req, CRLF, content, offset)
            assert(#contentType, "Content-Type value missing")
        end

        -- read next blank line
        matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)
        assert(#matchedStr == 0, "Missing line separating content headers and actual content")

        saveState(req, content, offset)
        return PART_BEGIN, fieldName, fileName, contentType
    end

    while ((state == PART_BEGIN)or(state == PART_DATA)) do
        local matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)
        if (matchedStr) then
            if (matchedStr == boundary) then
                saveState(req, content, offset)
                return PART_END
            end

            local lastBoundary = req.luaw_multipart_end
            if (matchedStr == lastBoundary) then
                saveState(req, content, offset)
                return MULTIPART_END
            end

            saveState(req, content, offset)
            return PART_DATA, matchedStr
        else
            local content = req.luaw_multipart_content
            local offset = req.luaw_consumed_till or 0

            if ((content)and(#content-offset) > #boundary+3) then
                saveState(req, nil, 1)
                return PART_DATA, content
            end
        end
    end
end

local function multiPartIterator(req)
    if (isMultipart(req)) then
        return fetchNextPart, req, MULTIPART_BEGIN
    end
end

local function shouldCloseConnection(req)
    if req and req.EOF then
        return true
    end
end

local function  toURLSafeChar(ch)
    if (ch == " ") then return "+" end
    return string.format("%%%02X", string.byte(ch))
end

local function urlEncode(str)
    str = string.gsub(str, "([^a-zA-Z0-9%.%*%-_])", toURLSafeChar)
    return str
end

local function urlEncodeParams(params)
    if params then
        local encodedParams = {}
        local contentLength = 0

        for key, val in pairs(params) do
            local ueKey = urlEncode(key)
            table.insert(encodedParams, ueKey)
            contentLength = contentLength + #ueKey

            table.insert(encodedParams, "=")
            contentLength = contentLength + 1

            local ueVal = urlEncode(val)
            table.insert(encodedParams, ueVal)
            contentLength = contentLength + #ueVal

            table.insert(encodedParams, "&")
            contentLength = contentLength + 1
        end

        if (#encodedParams > 0) then
            table.remove(encodedParams) -- remove the last extra "&"
            contentLength = contentLength - 1
            return encodedParams, contentLength
        end
    end

    return nil, 0
end

function buildURL(req)
    if (req.method == 'GET') then
        local encodedParams = urlEncodeParams(req.params)
        if encodedParams then
            table.insert(encodedParams, 1, "?")
            table.insert(encodedParams, 1, req.url)
            return table.concat(encodedParams)
        end
    end
    return req.url
end

local function setStatus(resp, status)
    resp.status = status
    resp.statusMesg = http_status_codes[status]
end

local function getStatus(resp)
    return resp.status
end

local function getBody(resp)
    return resp.body
end

local function firstResponseLine(resp)
    local line = {"HTTP/", resp.major_version, ".", resp.minor_version,
        " ", resp.status, " ", resp.statusMesg, CRLF}
    return table.concat(line)
end

local function firstRequestLine(req)
    local line = {req.method, " ", req:buildURL(), " HTTP/", req.major_version,
         ".", req.minor_version, CRLF}
    return table.concat(line)
end

local function sendBuffer(buffer, conn, writeTimeout, isChunked)
	local chunk = table.concat(buffer)
	buffer:reset()
    if (isChunked) then
        chunk = string.format("%x\r\n", #chunk)..chunk..CRLF
    end
    conn:write(chunk, writeTimeout)
end

local function bufferHeader(buffer, name, value)
    buffer:append(tostring(name))
    buffer:append(": ")
    buffer:append(tostring(value))
    buffer:append(CRLF)
end

local function bufferHeaders(headers, buffer)
    if (headers) then
        for name,value in pairs(headers) do
            if (type(value) == 'table') then
                for i,v in ipairs(value) do
                    bufferHeader(buffer, name, v)
                end
            else
                bufferHeader(buffer, name, value)
            end
            headers[name] = nil
        end
    end
    buffer:append(CRLF)
end

local function startStreaming(resp)
    resp.luaw_is_chunked = true
    resp:addHeader('Transfer-Encoding', 'chunked')

    local headers = resp.headers
    local conn = resp.luaw_conn
    local writeTimeout = resp.writeTimeout

    -- use separate buffer from "bodyParts" to serialize headers
    local headersBuffer = newBuffer()
    headersBuffer:append(resp:firstLine())
    bufferHeaders(headers, headersBuffer)

    -- flush up to HTTP headers end without chunked encoding before actual body starts
    sendBuffer(headersBuffer, conn, writeTimeout, false)
end

local function appendBody(resp, bodyPart)
    if not bodyPart then
        return
    end

    local bodyBuffer = resp.bodyParts
    local len = bodyBuffer:append(bodyPart)

    if ((resp.luaw_is_chunked)and(len >= CONN_BUFFER_SIZE)) then
        local conn = resp.luaw_conn
        local writeTimeout = resp.writeTimeout
        sendBuffer(bodyBuffer, conn, writeTimeout, true)
    end
end

local function writeFullBody(resp)
    local conn = resp.luaw_conn
    local writeTimeout = resp.writeTimeout
    local bodyBuffer = resp.bodyParts

    if (resp.method == 'POST') then
        local encodedParams, contentLength = urlEncodeParams(resp.params)
        if encodedParams then
            resp:addHeader('Content-Type', 'application/x-www-form-urlencoded')
            bodyBuffer:append(encodedParams)
        end
    end

    resp:addHeader('Content-Length', bodyBuffer.len)

    -- first write up to HTTP headers end
    local headersBuffer = newBuffer()
    headersBuffer:append(resp:firstLine())
    bufferHeaders(resp.headers, headersBuffer)
    sendBuffer(headersBuffer, conn, writeTimeout, false)

    -- now write body
    sendBuffer(bodyBuffer, conn, writeTimeout, false)
end

local function endStreaming(resp)
    local conn = resp.luaw_conn
    local writeTimeout = resp.writeTimeout
    local bodyBuffer = resp.bodyParts

    -- flush whatever is remaining in write buffer
    sendBuffer(bodyBuffer, conn, writeTimeout, true)

    -- add last chunk encoding trailer
    conn:write("0\r\n\r\n", writeTimeout)
end

local function flush(resp)
    if resp.luaw_is_chunked then
        endStreaming(resp)
    else
        writeFullBody(resp)
    end
end

local function close(req)
    local conn = req.luaw_conn;
    if conn then
        conn:close()
        req.luaw_conn = nil
    end
end

local function reset(req)
    req.headers = {}
    req["_acc_header_name_"] = nil
    req["_acc_header_value_"] = nil
    req.url = nil
    req.bodyParts:reset()
    req.body = nil
    req.luaw_mesg_done = nil
    req.luaw_headers_done = nil
    req.params = nil
    req.parsedURL = nil
    req.status = nil
    req.statusMesg = nil
end

luaw_http_lib.newServerHttpRequest = function(conn)
	local req = {
	    luaw_mesg_type = 'sreq',
	    luaw_conn = conn,
	    headers = {},
		bodyParts = newBuffer(),
	    luaw_parser = luaw_http_lib:newHttpRequestParser(),
	    addHeader = addHeader,
	    shouldCloseConnection = shouldCloseConnection,
	    isComplete = isComplete,
	    readAndParse = readAndParse,
	    readFull = readFull,
	    isMultipart = isMultipart,
	    multiPartIterator = multiPartIterator,
	    getBody = getBody,
	    reset = reset,
	    consumeBodyChunkParsed = consumeBodyChunkParsed,
	    close = close
	}
    return req;
end

luaw_http_lib.newServerHttpResponse = function(conn)
    local resp = {
        luaw_mesg_type = 'sresp',
        luaw_conn = conn,
        major_version = 1,
        minor_version = 1,
        contentLength = 0,
        headers = {},
        bodyParts = newBuffer(),
        addHeader = addHeader,
        shouldCloseConnection = shouldCloseConnection,
        setStatus = setStatus,
        firstLine = firstResponseLine,
        startStreaming = startStreaming,
        appendBody = appendBody,
        flush = flush,
        reset = reset,
        close = close
    }
    return resp;
end

local function newClientHttpResponse(conn)
	local resp = {
	    luaw_mesg_type = 'cresp',
	    luaw_conn = conn,
	    headers = {},
		bodyParts = newBuffer(),
	    luaw_parser = luaw_http_lib:newHttpResponseParser(),
	    addHeader = addHeader,
	    shouldCloseConnection = shouldCloseConnection,
	    readAndParse = readAndParse,
	    getBody = getBody,
	    getStatus = getStatus,
	    readFull = readFull,
	    consumeBodyChunkParsed = consumeBodyChunkParsed,
	    reset = reset,
	    close = close
	}
	return resp;
end

local function connect(req)
    local conn = assert(luaw_tcp_lib.connect(req.hostIP, req.hostName, req.port, req.connectTimeout))
    return conn
end

local function connectReq(req)
    conn = connect(req)
    conn:startReading()
    req.luaw_conn = conn
    local resp = newClientHttpResponse(conn)
    resp.readTimeout = req.readTimeout
    resp.writeTimeout = req.writeTimeout
    return resp
end

local function execute(req)
    local resp = req:connect()
    req:flush()
    resp:readFull()
    if resp:shouldCloseConnection() then
        resp:close()
    end
    return resp
end


luaw_http_lib.newClientHttpRequest = function()
    local req = {
        luaw_mesg_type = 'creq',
        port = 80,
        major_version = 1,
        minor_version = 1,
        method = 'GET',
        contentLength = 0,
        headers = {},
		bodyParts = newBuffer(),
        addHeader = addHeader,
        connect = connectReq,
        execute = execute,
        shouldCloseConnection = shouldCloseConnection,
        buildURL = buildURL,
        firstLine = firstRequestLine,
        startStreaming = startStreaming,
        appendBody = appendBody,
        flush = flush,
        reset = reset,
        close = close
    }
	return req;
end


return luaw_http_lib


================================================
FILE: lib/luaw_init.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

luaw_utils = require("luaw_utils")
luaw_logging = require("luaw_logging")
luaw_lpack = require("luapack")
luaw_scheduler = require("luaw_scheduler")
luaw_tcp = require("luaw_tcp")
luaw_timer = require("luaw_timer")
luaw_http = require("luaw_http")
luaw_webapp = require("luaw_webapp")

luaw_webapp.init()

================================================
FILE: lib/luaw_logging.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local ds_lib = require('luaw_data_structs_lib')
local luaw_utils_lib = require("luaw_utils")
local luapack_lib = require('luapack')

local log_module = {}

local PATH_SEPARATOR = string.match (package.config, "[^\n]+")

-- Log file states
local LOG_NOT_OPEN = 0
local OPENING_LOG = 1
local LOG_IS_OPEN = 2

-- Log levels
local EMERGENCY = 0
local ALERT = 1
local CRITICAL = 2
local ERROR = 3
local WARNING = 4
local NOTICE = 5
local INFO = 6
local DEBUG = 7

log_module.EMERGENCY = EMERGENCY
log_module.ALERT = ALERT
log_module.CRITICAL = CRITICAL
log_module.ERROR = ERROR
log_module.WARNING = WARNING
log_module.NOTICE = NOTICE
log_module.INFO = INFO
log_module.DEBUG = DEBUG

-- Log appender types
local FILE_LOG = "FILE"
local SYS_LOG = "SYSLOG"

log_module.SYSLOG_FACILITY_USER = 1
log_module.SYSLOG_FACILITY_AUTH = 10
log_module.SYSLOG_FACILITY_AUDIT = 13
log_module.SYSLOG_FACILITY_ALERT = 14
log_module.SYSLOG_FACILITY_LOCAL0 = 16
log_module.SYSLOG_FACILITY_LOCAL1 = 17
log_module.SYSLOG_FACILITY_LOCAL2 = 18
log_module.SYSLOG_FACILITY_LOCAL3 = 19
log_module.SYSLOG_FACILITY_LOCAL4 = 20
log_module.SYSLOG_FACILITY_LOCAL5 = 21
log_module.SYSLOG_FACILITY_LOCAL6 = 22
log_module.SYSLOG_FACILITY_LOCAL7 = 23

local logRoot = { }

local logDir = assert(luaw_log_config.log_dir, "Invalid log directory specified")
local noOfLogLinesToBuffer = luaw_log_config.log_lines_buffer_count or 100
local logfileBaseName = luaw_log_config.log_file_basename or "luaw-log"
local logfileSizeLimit = luaw_log_config.log_file_size_limit or (1024 * 1024 * 10) -- 10MB
local logfileCountLimit = luaw_log_config.log_file_count_limit or 99
local logLineTimeFormat = luaw_log_config.log_line_timestamp_format or "%x %X"
local logFileNameTimeFormat = luaw_log_config.log_filename_timestamp_format or '%Y%m%d-%H%M%S'

local syslogTag = luaw_log_config.syslog_tag or 'luaw'
local syslogPresent = luaw_logging_lib.syslogConnect(luaw_log_config.syslog_server, luaw_log_config.syslog_port)
logRoot.facility = luaw_log_config.syslog_facility or log_module.SYSLOG_FACILITY_LOCAL7
local hostname = luaw_logging_lib.hostname()


local logSequenceNum = 0
local logSize = 0
local logBuffer = ds_lib.newOverwrittingRingBuffer(noOfLogLinesToBuffer + 32)
local noOfLogLinesDropped = 0

local currentTimeStr
local syslogTimeStr

log_module.updateCurrentTime = function(currentTime)
    currentTimeStr = os.date(logLineTimeFormat, currentTime)
    if syslogPresent then
        syslogTimeStr = os.date("%b %d %X", currentTime)
    end
end

local function nextLogSequenceNum()
    if logSequenceNum > logfileCountLimit then logSequenceNum = 0 end
    logSequenceNum = logSequenceNum + 1
    return logSequenceNum
end

local function concatLogLines()
    local temp = luapack_lib.createDict(logBuffer.filled+1, 0)
    local i = 1
    local logLine = logBuffer:take()
    while logLine do
        temp[i] = logLine
        i = i+1
        logLine = logBuffer:take()
    end
    temp[i] = '' -- for the last newline
    return table.concat(temp, '\n')
end

local function logToFile(logLine)
    local added = logBuffer:offer(currentTimeStr..' '..logLine)
    if not added then noOfLogLinesDropped = noOfLogLinesDropped +1 end

    local state = luaw_logging_lib.logState()

    if ((state == LOG_IS_OPEN)and(logBuffer.filled >= noOfLogLinesToBuffer)) then
        local logBatch = concatLogLines()
        logSize = logSize + string.len(logBatch)
        local rotateLog = (logSize >= logfileSizeLimit)
        state = luaw_logging_lib.writeLog(logBatch, rotateLog)
    end

    if (state == LOG_NOT_OPEN) then
        logSize = 0
        local ts = os.date(logFileNameTimeFormat, os.time())
        local fileName = logDir..PATH_SEPARATOR..logfileBaseName..'-'..ts..'-'..nextLogSequenceNum()..'.log'
        luaw_logging_lib.openLog(fileName)
    end
end

local function syslog(priority, facility, mesg)
    local pri = priority + (facility * 8)
    local logLine = string.format("<%d>%s %s %s: %s", pri, syslogTimeStr, hostname, syslogTag, mesg)
    luaw_logging_lib.syslogSend(logLine);
end

local nameIterator = luaw_utils_lib.splitter('.')
local function splitName(name)
    if not name then return luaw_utils_lib.nilFn end
    return nameIterator, name, 0
end

local function logInternal(logLevel, fileLevel, syslogLevel, syslogFacility, mesg)
    if (logLevel <= fileLevel) then
        logToFile(mesg)
    end
    if ((syslogPresent)and(logLevel <= syslogLevel)) then
        syslog(logLevel, syslogFacility, mesg)
    end
end

local function log(logger, logLevel, mesg)
    local fileLevel = logger[FILE_LOG] or ERROR
    local syslogLevel = logger[SYS_LOG] or ERROR
    logInternal(logLevel, fileLevel, syslogLevel, logger.facility, mesg)
end

local function logf(logger, logLevel, mesgFormat, ...)
    local fileLevel = logger[FILE_LOG] or ERROR
    local syslogLevel = logger[SYS_LOG] or ERROR
    if ((logLevel <= fileLevel)or(logLevel <= syslogLevel)) then
        local mesg = string.format(mesgFormat, ...)
        logInternal(logLevel, fileLevel, syslogLevel, logger.facility, mesg)
    end
end

logRoot.log = log

logRoot.logf = logf

logRoot.emergency = function(logger, mesg)
    log(logger, EMERGENCY, mesg)
end

logRoot.alert = function(logger, mesg)
    log(logger, ALERT, mesg)
end

logRoot.critical = function(logger, mesg)
    log(logger, CRITICAL, mesg)
end

logRoot.error = function(logger, mesg)
    log(logger, ERROR, mesg)
end

logRoot.warning = function(logger, mesg)
    log(logger, WARNING, mesg)
end

logRoot.notice = function(logger, mesg)
    log(logger, NOTICE, mesg)
end

logRoot.info = function(logger, mesg)
    log(logger, INFO, mesg)
end

logRoot.debug = function(logger, mesg)
    log(logger, DEBUG, mesg)
end

logRoot.emergencyf = function(logger, mesgFormat, ...)
    logf(logger, EMERGENCY, mesgFormat, ...)
end

logRoot.alertf = function(logger, mesgFormat, ...)
    logf(logger, ALERT, mesgFormat, ...)
end

logRoot.criticalf = function(logger, mesgFormat, ...)
    logf(logger, CRITICAL, mesgFormat, ...)
end

logRoot.errorf = function(logger, mesgFormat, ...)
    logf(logger, ERROR, mesgFormat, ...)
end

logRoot.warningf = function(logger, mesgFormat, ...)
    logf(logger, WARNING, mesgFormat, ...)
end

logRoot.noticef = function(logger, mesgFormat, ...)
    logf(logger, NOTICE, mesgFormat, ...)
end

logRoot.infof = function(logger, mesgFormat, ...)
    logf(logger, INFO, mesgFormat, ...)
end

logRoot.debugf = function(logger, mesgFormat, ...)
    logf(logger, DEBUG, mesgFormat, ...)
end

local function getLogger(name)
    local logger = logRoot
    if (name == 'root') then return logger end

    for idx, namePart in splitName(name) do
        local child = logger[namePart]
        if not child then
            child = {}
            setmetatable(child, {__index = logger})
            logger[namePart] = child
        end
        logger = child
    end
    return logger
end

log_module.getLogger = getLogger

local function configureLogger(logCfg, logType)
    local loggerName = assert(logCfg.name, "Logger name missing")
    local logLevel = assert(logCfg.level, "Logger level missing")
    local logger = assert(getLogger(loggerName), "Could not find logger "..loggerName)
    logger[logType] = logLevel
    return logger
end

log_module.file = function(logCfg)
    configureLogger(logCfg, FILE_LOG)
end

log_module.syslog = function(logCfg)
    local logger = configureLogger(logCfg, SYS_LOG)
    logger.facility = logCfg.facility
end

return log_module

================================================
FILE: lib/luaw_scheduler.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local constants = require('luaw_constants')
local ds_lib = require('luaw_data_structs_lib')
local logging = require('luaw_logging')

-- Scheduler object
local scheduler = {}

-- Constants
local TS_RUNNABLE = constants.RUNNABLE
local TS_DONE = constants.DONE
local TS_BLOCKED_EVENT = constants.BLOCKED_ON_EVENT
local TS_BLOCKED_THREAD = constants.BLOCKED_ON_THREAD
local END_OF_CALL = constants.END_OF_CALL
local END_OF_THREAD = constants.END_OF_THREAD

local UPDATE_TIME_COUNTER_LIMIT = 10

-- scheduler state
local threadRegistry = ds_lib.newRegistry(luaw_server_config.thread_pool_size or 1024)
local threadPool = ds_lib.newRingBuffer(luaw_server_config.thread_pool_size or 1024)
local timesReuseThread = luaw_server_config.thread_reuse_limit or 1024
local runQueueLen = 0
local runQueueHead = nil
local runQueueTail = nil
local currentRunningThreadCtx = nil
local updateTimeCyclingCounter = 0
local currentTime


scheduler.updateCurrentTime = function ()
    updateTimeCyclingCounter = updateTimeCyclingCounter + 1
    if (updateTimeCyclingCounter >= UPDATE_TIME_COUNTER_LIMIT) then
        currentTime = os.time()
        logging.updateCurrentTime(currentTime)
        updateTimeCyclingCounter = 0
    end
end

scheduler.time = function()
    return currentTime
end

-- returns current running thread's id
scheduler.tid = function()
    if currentRunningThreadCtx then
        return currentRunningThreadCtx.tid
    end
    return nil
end

local function threadRunLoop(fn, arg1, arg2, arg3, arg4)
    local i = 0
    while i <= timesReuseThread do
        fn, arg1, arg2, arg3, arg4 = coroutine.yield(END_OF_CALL, fn(arg1, arg2, arg3, arg4))
        i = i+1
    end
    return END_OF_THREAD, fn(arg1, arg2, arg3, arg4)
end

local function userThreadRunner(userThreadFn, ...)
    -- We have captured user thread function along with its arguments on a coroutine stack.
    -- Yield now so that scheduler can add this thread in run queue for "bottom half"
    -- processing later and original calling thread can resume.
    coroutine.yield(TS_RUNNABLE)
    -- At this point we have been resumed by thread scheduler during the "bottom half" run
    -- queue processing bu the scheduler so run the actual user thread function.
    return userThreadFn(...)
end

local function addToRunQueue(threadCtx)
    if not runQueueTail then
        runQueueHead = threadCtx
        runQueueTail = threadCtx
    else
        runQueueTail.nextThread = threadCtx
        runQueueTail = threadCtx
    end
    runQueueLen = runQueueLen + 1
    threadCtx.state = TS_RUNNABLE
end

local function newThread()
    local t = threadPool:take()
    if not t then
        t = coroutine.create(threadRunLoop)
    end

    local threadCtx = { thread = t, requestCtx = {} }
    -- anchor thread in registry to prevent GC
    local ref = threadRegistry:ref(threadCtx)
    threadCtx.tid = ref
    return threadCtx
end

local function unblockJoinedThreadIfAny(threadCtx, status, retVal)
    local joinedTC = threadCtx.joinedBy
    if joinedTC then
        local count = joinedTC.joinCount
        count = count -1
        joinedTC.joinCount = count
        if (count <= 0) then
            addToRunQueue(joinedTC)
        end
    end
end

local function afterResume(threadCtx, state, retVal)
    threadCtx.state, threadCtx.result = state, retVal
    currentRunningThreadCtx = nil
    if (state == TS_DONE) then
        return true, retVal
    end
    return false, retVal
end

local function resumeThread(threadCtx, ...)
    currentRunningThreadCtx = threadCtx
    local t = threadCtx.thread
    local tid = threadCtx.tid

    scheduler.updateCurrentTime()

    context = threadCtx.requestCtx  -- TLS, per thread context
    local status, state, retVal = coroutine.resume(t, ...)
    context = nil -- reset TLS context

    if not status then
        -- thread ran into error
        print("Error: "..tostring(state))
        state = END_OF_THREAD
        -- thread has blown its stack so let it get garbage collected
        t = nil
    end

    if ((state == END_OF_THREAD)or(state == END_OF_CALL)) then
        threadRegistry:unref(tid)
        threadCtx.thread = nil
        threadCtx.requestCtx = nil
        if ((state == END_OF_CALL) and (t)) then
            -- thread is still alive, return it to free pool if possible
            threadPool:offer(t)
        end
        unblockJoinedThreadIfAny(threadCtx, status, retVal)
        return afterResume(threadCtx, TS_DONE, retVal)
    end

    if ((state == TS_BLOCKED_EVENT)or(state == TS_BLOCKED_THREAD)) then
        -- thread will later be resumed by libuv call back
        return afterResume(threadCtx, state, retVal)
    end

    -- Thread yielded, but is still runnable. Add it back to the run queue
    addToRunQueue(threadCtx)
    return afterResume(threadCtx, TS_RUNNABLE, retVal)
end

function resumeThreadId(tid, ...)
    local threadCtx = threadRegistry[tid]
    if not threadCtx then error("Invalid thread Id "..tostring(tid)) end
    return resumeThread(threadCtx, ...)
end

scheduler.resumeThreadId = resumeThreadId

function startSystemThread(serviceFn, conn, ...)
    local threadCtx = newThread()
    threadCtx.state = TS_RUNNABLE
    local isDone = resumeThread(threadCtx, serviceFn, conn, ...)
    return isDone, threadCtx.tid
end

scheduler.startSystemThread = startSystemThread

-- Scheduler object methods

scheduler.startUserThread = function(userThreadFn, ...)
    local backgroundThreadCtx = newThread()
    coroutine.resume(backgroundThreadCtx.thread, userThreadRunner, userThreadFn, ...)
    addToRunQueue(backgroundThreadCtx)
    return backgroundThreadCtx;
end

scheduler.join = function(...)
    local joiningTC = currentRunningThreadCtx
    if (joininTC) then
        local joinedThreads = table.pack(...)
        local numOfThreads = #joinedThreads

        local count = 0

        for i, joinedTC in ipairs(joinedThreads) do
            if ((joinedTC)and(joinedTC.state)and(joinedTC.state ~= TS_DONE)) then
                count = count + 1
                joinedTC.joinedBy = joiningTC
            end
        end

        joiningTC.joinCount = count
        while (joiningTC.joinCount > 0) do
            coroutine.yield(TS_BLOCKED_THREAD)
        end
    end
end

scheduler.runQueueSize = function()
    return runQueueLen
end

local runNextFromRunQueue = function()
    local threadCtx = runQueueHead
    if threadCtx then
        runQueueHead = threadCtx.nextThread
        if not runQueueHead then
            runQueueTail = nil
        end

        threadCtx.nextThread = nil

        runQueueLen = runQueueLen -1
        if (runQueueLen < 0) then
            runQueueLen = 0
        end

        if (threadCtx.state == TS_DONE) then
            -- This can happen when thread is added to the run queue but is woken up by libuv
            -- event and then runs to completion before the run queue scheduler gets chance
            -- to resume it
            return
        end

        return resumeThread(threadCtx)
    end
end

scheduler.runReadyThreads = function(limit)
    local runnableCount = runQueueLen
    if ((limit)and(limit < runnableCount)) then
        runnableCount = limit
    end

    for i=1, runnableCount do
        runNextFromRunQueue()
        end

    -- about to block on libuv event loop, next resumeThread should update current time
    -- as it may have spent significant time blocked on a event loop.
    updateTimeCyclingCounter = UPDATE_TIME_COUNTER_LIMIT

    return runnableCount
end

scheduler.updateCurrentTime()

return scheduler


================================================
FILE: lib/luaw_tcp.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local constants = require('luaw_constants')
local scheduler = require('luaw_scheduler')

local DEFAULT_CONNECT_TIMEOUT = constants.DEFAULT_CONNECT_TIMEOUT
local DEFAULT_READ_TIMEOUT = constants.DEFAULT_READ_TIMEOUT
local DEFAULT_WRITE_TIMEOUT = constants.DEFAULT_WRITE_TIMEOUT
local CONN_BUFFER_SIZE = constants.CONN_BUFFER_SIZE

local conn = luaw_tcp_lib.newConnection();
local connMT = getmetatable(conn)
conn:close()
local startReadingInternal = connMT.startReading
local readInternal = connMT.read
local writeInternal = connMT.write

connMT.startReading = function(self)
    local status, mesg = startReadingInternal(self)
    assert(status, mesg)
end

connMT.read = function(self, readTimeout)
    local status, str = readInternal(self, scheduler.tid(), readTimeout or DEFAULT_READ_TIMEOUT)
    if ((status)and(not str)) then
        -- nothing in buffer, wait for libuv on_read callback
        status, str = coroutine.yield(TS_BLOCKED_EVENT)
    end
    return status, str
end

connMT.write = function(self, str, writeTimeout)
    local status, nwritten = writeInternal(self, scheduler.tid(), str, writeTimeout  or DEFAULT_WRITE_TIMEOUT)
    if ((status)and(nwritten > 0)) then
        -- there is something to write, yield for libuv callback
        status, nwritten = coroutine.yield(TS_BLOCKED_EVENT)
    end
    assert(status, nwritten)
    return nwritten
end

local connectInternal = luaw_tcp_lib.connect

local function connect(hostIP, hostName, port, connectTimeout)
    assert((hostName or hostIP), "Either hostName or hostIP must be specified in request")
    local threadId = scheduler.tid()
    if not hostIP then
        local status, mesg = luaw_tcp_lib.resolveDNS(hostName, threadId)
        assert(status, mesg)
        status, mesg = coroutine.yield(TS_BLOCKED_EVENT)
        assert(status, mesg)
        hostIP = mesg
    end

    local connectTimeout = connectTimeout or DEFAULT_CONNECT_TIMEOUT
    local conn, mesg = connectInternal(hostIP, port, threadId, connectTimeout)

    -- initial connect_req succeeded, block for libuv callback
    assert(coroutine.yield(TS_BLOCKED_EVENT))
    return conn, mesg
end

luaw_tcp_lib.connect = connect

return luaw_tcp_lib

================================================
FILE: lib/luaw_timer.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local constants = require('luaw_constants')
local TS_BLOCKED_EVENT = constants.TS_BLOCKED_EVENT


local timerMT = getmetatable(luaw_timer_lib.newTimer())
local waitInternal = timerMT.wait

timerMT.wait = function(timer)
    local status, elapsed = waitInternal(timer, scheduler.tid())
    if ((status) and (not elapsed)) then
        -- timer not yet elapsed, wait for libuv on_timeout callback
        status, elapsed = coroutine.yield(TS_BLOCKED_EVENT)
    end
    return status, elapsed
end

timerMT.sleep = function(timer, timeout)
    assert(timer:start(timeout))
    timer:wait()
end

return luaw_timer_lib

================================================
FILE: lib/luaw_utils.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local luaw_util_lib = {}

local function tprint(tbl, indent, tab)
  for k, v in pairs(tbl) do
    if type(v) == "table" then
		print(string.rep(tab, indent) .. tostring(k) .. ": {")
		tprint(v, indent+1, tab)
		print(string.rep(tab, indent) .. "}")
    else
		print(string.rep(tab, indent) .. tostring(k) .. ": " .. tostring(v))
    end
  end
end

-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
luaw_util_lib.debugDump = function(tbl, indent, tab)
    indent = indent or 0
    tab = tab or "  "
  	print(string.rep(tab, indent) .. "{")
	tprint(tbl, indent+1, tab)
	print(string.rep(tab, indent) .. "}")
end

luaw_util_lib.steplight = function(mesg)
    local tid = tostring(Luaw.scheduler.tid())
    print("Thread-"..tid.."> "..tostring(mesg))
end

luaw_util_lib.step = function(mesg, level)
    local tid = tostring(Luaw.scheduler.tid())

    local lvl = level or 2
    if (lvl < 0) then lvl = lvl * -1 end

    local dc = debug.getinfo(lvl, "nSl")

    local str = ""
    if type(mesg) == 'table' then
        for k,v in pairs(mesg) do
            str = str..", "..tostring(k).."="..tostring(v)
        end
    else
        str = tostring(mesg)
    end

    print('Thread '..tid..'> line# '..tostring(dc.linedefined)..' in function '..tostring(dc.name)..' in file '..tostring(dc.source)..': '..str)

    if ((level)and(level < 0)) then
        print(debug.traceback())
    end
end

luaw_util_lib.run = function(codeblock)
    if (codeblock) then
        local try = codeblock.try
        if (try) then
            local catch = codeblock.catch
            local finally = codeblock.finally

            local status, err = pcall(try, codeblock)
            if ((not status)and(catch)) then
                status, err = pcall(catch, codeblock, err)
            end

            if (finally) then
                finally(codeblock)
            end

            if (not status) then
                error(err)
            end
        end
    end
end

luaw_util_lib.clearArrayPart = function(t)
    local len = #t
    for i=1,len do
        t[i] = nil
    end
end

luaw_util_lib.splitter = function(splitCh)
    local separator = string.byte(splitCh, 1, 1)
    local byte = string.byte

    return function (str, pos)
        pos = pos + 1
        local start = pos
        local len = #str
        while pos <= len do
            local ch = byte(str, pos, pos)
            if (ch == separator) then
                if (pos > start) then
                    return pos, string.sub(str, start, pos-1)
                end
                start = pos + 1
            end
            pos = pos + 1
        end
        if (pos > start) then return pos, string.sub(str, start, pos) end
    end
end

luaw_util_lib.nilFn = function()
    return nil
end

luaw_util_lib.formattedLine = function(str, lineSize, paddingCh, beginCh, endCh)
    lineSize = lineSize or 0
    paddingCh = paddingCh or ''
    beginCh = beginCh or ''
    endCh = endCh or ''
    paddingWidth = (lineSize - #str -2)/2
    local padding = ''
    if paddingWidth > 0 then
        padding = string.rep(paddingCh, paddingWidth)
    end
    print(string.format("%s %s %s %s %s", beginCh, padding, str, padding, endCh))
end

return luaw_util_lib


================================================
FILE: lib/luaw_webapp.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local luaw_utils_lib = require("luaw_utils")
local luaw_http_lib = require("luaw_http")

local HTTP_METHODS = {
    GET = "GET",
    POST = "POST",
    PUT = "PUT",
    DELETE = "DELETE",
    HEAD = "HEAD",
    OPTIONS = "OPTIONS",
    TRACE = "TRACE",
    CONNECT = "CONNECT",
    SERVICE = "SERVICE"
}

local registeredWebApps = {}

local DIR_SEPARATOR = string.match (package.config, "[^\n]+")
local STRING_PATH_PARAM = { start = string.byte(":"), valueOf = tostring }
local NUM_PATH_PARAM = { start = string.byte("#"), valueOf = tonumber }

TAB = '    '

local function findFiles(path, pattern, matches)
    if (path and pattern) then
        for file in lfs.dir(path) do
            if (file ~= '.' and file ~= '..') then
                local f = path..DIR_SEPARATOR..file
                local attrs = lfs.attributes(f)
                if attrs then
                    local mode = attrs.mode
                    if mode == 'file' then
                        if (string.match(f, pattern)) then
                            table.insert(matches, f)
                        end
                    elseif mode == 'directory' then
                        findFiles(f, pattern, matches)
                    end
                end
            end
        end
    end
    return matches
end

local pathIterator = luaw_utils_lib.splitter('/')
local function splitPath(path)
    if not path then return luaw_util_lib.nilFn end
    return pathIterator, path, 0
end

local function findAction(method, path)
    assert(method, "HTTP method may not be nil")
    assert(method, "HTTP request path may not be nil")

    local webApp = nil
    local route = nil
    local pathParams = {}

    for idx, pseg in splitPath(path) do
        -- first iteration only
        if not webApp then
            webApp = registeredWebApps[pseg]
            if not webApp then
                -- fallback to root path
                webApp = registeredWebApps['/']
            end
            if not webApp then
                return nil
            end
            route = webApp.root
        else
            local nextRoute = route.childRoutes[pseg]
            if not nextRoute then
                -- may be it's a path param
                nextRoute = route.childRoutes["_path_param_"]
                if nextRoute then
                    pathParams[nextRoute.pathParam] = nextRoute.pathParamType.valueOf(pseg)
                end
            end
            if not nextRoute then return nil end
            route = nextRoute
        end
    end

    if (route) then
        local action = route[method]
        if not action then
            -- try catch call action as a fallback
            action = route['SERVICE']
        end
        return webApp, action, pathParams
    end
end

local function renderView(req, resp, pathParams, model, view)
    local isTagOpen = false
    local indent = 0

    local attributes = function(attrs)
        if attrs then
            for k,v in pairs(attrs) do
                resp:appendBody(' ')
                resp:appendBody(k)
                resp:appendBody('="')
                resp:appendBody(tostring(v))
                resp:appendBody('"')
            end
        end
    end

    local BEGIN = function(tag)
        if (isTagOpen) then
            resp:appendBody('>\n')
        end
        for i=1, indent do
            resp:appendBody(TAB)
        end
        resp:appendBody('<')
        resp:appendBody(tag)
        isTagOpen = true;
        indent = indent+1
        return attributes
    end

    local END = function(tag)
        if (isTagOpen) then
            resp:appendBody('>\n')
            isTagOpen = false;
        end

        indent = indent - 1
        if indent > 0 then
            indent = indent
        else
            indent = 0
        end
        for i=1, indent do
            resp:appendBody(TAB)
        end

        resp:appendBody('</')
        resp:appendBody(tag)
        resp:appendBody('>\n')
    end

    local TEXT = function(...)
        if (isTagOpen) then
            resp:appendBody('>\n')
            isTagOpen = false;
        end
        for i=1, indent do
            resp:appendBody(TAB)
        end
        local values = {...}
        for i,v in ipairs(values) do
            resp:appendBody(tostring(v))
            resp:appendBody(' ')
        end
        resp:appendBody('\n')
    end

    -- render view
    if ((req.major_version >= 1)and(req.minor_version >= 1)) then
        resp:startStreaming()
    end
    view(req, resp, pathParams, model, BEGIN, TEXT, END)
end

local function dispatchAction(req, resp)
    assert(req, "HTTP request may not be nil")
    local parsedURL = req.parsedURL

    if not(req.method and  parsedURL) then
        -- EOF in case of persistent connections
        return
    end

    local webApp, action, pathParams = findAction(req.method, parsedURL.path)
    assert(action, "No action found for path "..parsedURL.path.." for method "..req.method)

    if req:shouldCloseConnection() then
        resp.headers['Connection'] = 'close'
    else
        resp.headers['Connection'] = 'Keep-Alive'
    end

    v1, v2 = action.handler(req, resp, pathParams)

    -- handle action returned response, if any and if resp is not closed
    if v1 and resp.luaw_conn then
        if (type(v1) == 'number') then
            -- v1 is HTTP status
            resp:setStatus(v1)
            --resp:startStreaming()
            if v2 then
                -- v2 is body content
                resp:appendBody(tostring(v2))
            end
        else
            if not resp.statusCode then
                resp:setStatus(200)
            end
            --resp:startStreaming()
            if ((type(v1) == 'string')and(v2)) then
                -- v1 is view path, v2 is view model
                local compiledView = webApp.compiledViews[v1]
                if not compiledView then
                    error("View '"..tostring(v1).."' is not defined")
                end
                renderView(req, resp, pathParams, v2, compiledView)
            else
                -- v1 is the body content itself
                resp:appendBody(tostring(v1))
            end
        end
    end

    -- flush in case resp is not closed
    if resp.luaw_conn then resp:flush() end
end

local function registerResource(resource)
    local route = assert(webapp.root, "webapp root not defined")
    local path = assert(resource.path , "Handler definition is missing value for 'path'")
    local handlerFn = assert(resource.handler, "Handler definition is missing 'handler' function")
    local method = resource.method or 'SERVICE'
    if(not HTTP_METHODS[method]) then
        error(method.." is not a valid HTTP method")
    end


    for idx, pseg in splitPath(path) do
        local firstChar = string.byte(pseg)
        local pathParam = nil
        if ((firstChar == STRING_PATH_PARAM.start)or(firstChar == NUM_PATH_PARAM.start)) then
            pathParam = pseg:sub(2)
            pseg = "_path_param_"
        end

        local nextRoute = route.childRoutes[pseg]
        if not nextRoute then
            nextRoute = { childRoutes = {} }
            if pathParam then
                nextRoute.pathParam = pathParam
                if (firstChar == NUM_PATH_PARAM.start) then
                    nextRoute.pathParamType = NUM_PATH_PARAM
                else
                    nextRoute.pathParamType = STRING_PATH_PARAM
                end
            end
            route.childRoutes[pseg] = nextRoute
        end
        route = nextRoute
    end

    assert(route, "Could not register handler for path "..path)
    assert((not route[method]), 'Handler already registered for '..method..' for path "/'..webapp.path..'/'..path..'"')
    route[method] = {handler = handlerFn}
end

local function serviceHTTP(conn)
    conn:startReading()

    -- loop to support HTTP 1.1 persistent (keep-alive) connections
    while true do
        local req = luaw_http_lib.newServerHttpRequest(conn)

        -- read and parse full request
        local status, errmesg = pcall(req.readFull, req)
        if ((not status)or(req.EOF == true)) then
            conn:close()
            if (status) then
                return "read time out"
            end
            print("Error: ", errmesg, debug.traceback())
            return "connection reset by peer"
        end

        local resp = luaw_http_lib.newServerHttpResponse(conn)
        local status, errMesg = pcall(dispatchAction, req, resp)

        if (not status) then
            -- send HTTP error response
            resp:setStatus(500)
            resp:addHeader('Connection', 'close')
            pcall(resp.appendBody, resp, errMesg)
            pcall(resp.flush, resp)
            conn:close()
            error(errMesg)
        end

        if (req:shouldCloseConnection() or resp:shouldCloseConnection()) then
            conn:close()
            return "connection reset by peer"
        end
    end
end

local function toFullPath(appRoot, files)
    local fullPaths = {}
    if files then
        for i, file in ipairs(files) do
            table.insert(fullPaths, appRoot..'/'..file)
        end
    end
    return fullPaths
end

local function loadWebApp(appName, appDir)
    local app = {}

    app.path = assert(appName, "Missing mandatory configuration property 'path'")
    app.appRoot = assert(appDir, "Missing mandatory configuration property 'root_dir'")

    if (registeredWebApps[app.path]) then
        error('Anothe web app is already registered for path '..app.path)
    end
    registeredWebApps[app.path] = app

    app.root = { childRoutes = {} }

    -- Load resource handlers
    local resources = toFullPath(app.appRoot, luaw_webapp.resources)
    if luaw_webapp.resourcePattern then
        resources = findFiles(app.appRoot, luaw_webapp.resourcePattern, resources)
    end
    assert((resources and (#resources > 0)), "Either 'resources' or 'resourcePattern' must be specified in a web app configuration")
    app.resources = resources

    -- Load view files if any
    local views = toFullPath(app.appRoot, luaw_webapp.views)
    if luaw_webapp.viewPattern then
        views = findFiles(app.appRoot, luaw_webapp.viewPattern, views)
    end
    app.views = views

    return app
end

local function loadView(viewPath, buff)
    local firstLine = true
    local lastLine = false
    local viewLines = io.lines(viewPath)

    return function()
        local line = nil

        if firstLine then
            firstLine = false
            line = "return function(req, resp, pathParams, model, BEGIN, TEXT, END)"
        else
            if (not lastLine) then
                local vl = viewLines()
                if (not vl) then
                    lastLine = true
                    line = "end"
                else
                    line = vl
                end
            end
        end
        if line then
            table.insert(buff, line)
            return (line .. '\n')
        end
    end
end

local function startWebApp(app)
    -- register resources
    local resources = app.resources
    for i,resource in ipairs(resources) do
        luaw_utils_lib.formattedLine(".Loading resource "..resource)
        -- declare globals (registerHandler and webapp) for the duration of the loadfile(resource)
        registerHandler = registerResource
        webapp = app
        local routeDefn = assert(loadfile(resource), string.format("Could not load resource %s", resource))
        routeDefn()
    end
    luaw_utils_lib.formattedLine("#Loaded total "..#resources.." resources\n")

    -- compile views
    local views = app.views
    local compiledViews = {}
    local appRootLen = string.len(app.appRoot) + 1
    for i,view in ipairs(views) do
        local relativeViewPath = string.sub(view, appRootLen)
        luaw_utils_lib.formattedLine(".Loading view "..relativeViewPath)
        local viewBuff = {}
        local viewDefn = loadView(view, viewBuff)
        local compiledView, errMesg = load(viewDefn, relativeViewPath)
        if (not compiledView) then
            luaw_utils_lib.formattedLine("\nError while compiling view: "..view)
            luaw_utils_lib.formattedLine("<SOURCE>")
            for i, line in ipairs(viewBuff) do
                print(tostring(i)..":\t"..tostring(line))
            end
            luaw_utils_lib.formattedLine("<SOURCE>")
            error(errMesg)
        end
        compiledViews[relativeViewPath] = compiledView()
    end
    app.views = nil
    app.compiledViews = compiledViews
    luaw_utils_lib.formattedLine("#Compiled total "..#views.." views")
end

local function init()
    if ((luaw_webapp_config)and(luaw_webapp_config.base_dir)) then
        local root = luaw_webapp_config.base_dir
        for webappName in lfs.dir(root) do
            if (webappName ~= '.' and webappName ~= '..') then
                local webappDir = root..DIR_SEPARATOR..webappName
                local attrs = lfs.attributes(webappDir)
                if ((attrs)and(attrs.mode == 'directory')) then
                    local webappCfgFile = webappDir..DIR_SEPARATOR..'web.lua'
                    if (lfs.attributes(webappCfgFile, 'mode') == 'file') then
                        luaw_utils_lib.formattedLine('Starting webapp '..webappName, 120, '*', '\n')
                        dofile(webappCfgFile) -- defines global variable luaw_webapp
                        local app = loadWebApp(webappName, webappDir)
                        startWebApp(app)
                        luaw_utils_lib.formattedLine('Webapp '..webappName..' started', 120, '*')
                        webapp = nil  -- reset global variable
                    end
                end
            end
        end
    end
end

-- install REST HTTP app handler as a default request handler
luaw_http_lib.request_handler = serviceHTTP

return {
    init = init,
    dispatchAction = dispatchAction,
    serviceHTTP = serviceHTTP
}


================================================
FILE: lib/unit_testing.lua
================================================
--[[
Copyright (c) 2015 raksoras

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
]]

local module = {total_run = 0, total_failed = 0}

function module.assertTrue(expr)
	if not expr then
		error("Assert true failed!", 2)
	end
end

function module.assertFalse(expr)
	if expr then
		error("Assert false failed!", 2)
	end
end

function module.assertNotNil(expr)
	if not expr then
		error("Assert not nil failed!", 2)
	end
end

function module.assertNil(expr)
	if expr then
		error("Assert nil failed!", 2)
	end
end

function module.assertEqual(actual, expected)
	if (actual ~= expected) then
		error(string.format("Assert equal failed! Actual: [%s], Expected: [%s]", actual, expected), 2)
	end
end

function module.assertNotEqual(actual, expected)
	if (actual == expected) then
		error(string.format("Assert not equal failed! Actual: [%s], Expected: [%s]", actual, expected), 2)
	end
end

local function tprint(tbl, indent, tab)
  for k, v in pairs(tbl) do
    if type(v) == "table" then
		print(string.rep(tab, indent) .. tostring(k) .. ": {")
		tprint(v, indent+1, tab)
		print(string.rep(tab, indent) .. "}")
    else
		print(string.rep(tab, indent) .. tostring(k) .. ": " .. tostring(v))
    end
  end
end

-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
function module.printTable (tbl, indent, tab)
    indent = indent or 0
    tab = tab or "  "
  	print(string.rep(tab, indent) .. "{")
	tprint(tbl, indent+1, tab)
	print(string.rep(tab, indent) .. "}")
end

function module:runTests()
	number_run = 0
	number_failed = 0

	for name, func in pairs(self) do
		if (string.find(name, "test") == 1) then
			result, mesg = pcall(func)
			number_run = number_run + 1
			if (string.find(name, "testError") == 1) then
				-- negative test
				if not result then
					print(string.format("    %-40s [OK]", name));
				else
					number_failed = number_failed + 1
					print(string.format("    %-30s [FAILED! Expected to throw error]", name));
				end
			else
				if result then
					print(string.format("    %-40s [OK]", name));
				else
					number_failed = number_failed + 1
					print(string.format("    %-30s [FAILED! %s]", name, mesg));
				end
			end
			self[name] = nil
		end
	end

	self.total_run = self.total_run + number_run
	self.total_failed = self.total_failed + number_failed

	local info = debug.getinfo(2, "S")
	print('----------------------------------------------------------------------------')
	print(string.format("%s:  Total# %d, Failed# %d", info.source, number_run, number_failed));
	print('----------------------------------------------------------------------------')
end

function printOverallSummary()
	print('\n*****************************************************************************')
	print(string.format("Overall Summary:  Total# %d, Failed# %d", module.total_run, module.total_failed));
	print('*****************************************************************************\n')

end

return module;

================================================
FILE: sample/conf/server.cfg
================================================
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 = "./logs",
    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 = "./webapps"
}



================================================
FILE: sample/proxy_handler.lua
================================================
local http_lib = require('luaw_http')
--[[
    Luaw allows you to replace it's default MVC/REST request handler with your own custom HTTP
request handler implementation. To override the default HTTP request handler just set Luaw object's
request_handler property to your custom Lua function. This function is passed in a low level connection
object for each incoming request instead of the normal request and response objects passed to REST handler.
The function is called on its own separate Luaw coroutine for each HTTP request so you don't have to worry
about multithreaded access to shared state inside the function.
]]

http_lib.request_handler =  function(conn)
    conn:startReading()

    -- loop to support HTTP 1.1 persistent (keep-alive) connections
    while true do
        local req = http_lib.newServerHttpRequest(conn)
        local resp = http_lib.newServerHttpResponse(conn)

        -- read and parse full request
        req:readFull()
        if (req.EOF) then
            conn:close()
            return "connection reset by peer"
        end

        local reqHeaders = req.headers
        local beHost =  reqHeaders['backend-host']
        local beURL = reqHeaders['backend-url']

        if (beHost and beURL) then
           local backendReq = http_lib.newClientHttpRequest()
           backendReq.hostName = beHost
           backendReq.url = beURL
           backendReq.method = 'GET'
           backendReq.headers = { Host = beHost }

           local status, backendResp = pcall(backendReq.execute, backendReq)
           if (status) then
               resp:setStatus(backendResp:getStatus())
               resp:appendBody(backendResp:getBody())
               local beHeaders = backendResp.headers
               for k,v in pairs(beHeaders) do
                   if ((k ~= 'Transfer-Encoding')and(k ~= 'Content-Length')) then
                       resp:addHeader(k,v)
                   end
               end
               backendResp:close()
            else
               resp:setStatus(500)
               resp:appendBody("connection to backend server failed")
           end
        else
            resp:setStatus(400)
            resp:appendBody("Request must contain headers backend-host and backend-url\n")
        end

        local status, mesg = pcall(resp.flush, resp)
        if (not status) then
            conn:close()
            return error(mesg)
        end

        if (req:shouldCloseConnection() or resp:shouldCloseConnection()) then
            conn:close()
            return "connection reset by peer"
        end
    end
end


================================================
FILE: sample/webapps/myapp/handlers/handler-address.lua
================================================
registerHandler {
    method = 'GET',
    path = 'address/:city/#zip',

    handler = function(req, resp, pathParams)
        address = {
            city = pathParams.city,
            zip = pathParams.zip
        }
        return '/views/view-address.lua', address
    end
}


================================================
FILE: sample/webapps/myapp/handlers/handler-fileupload-display.lua
================================================
registerHandler {
    method = 'GET',
    path = 'showform',

	handler = function(req, resp, pathParams)
	    return [[
            <!DOCTYPE html>
            <html lang="en">
                <head>
                    <meta charset="utf-8"/>
                    <title>upload</title>
                </head>
                <body>
                    <form action="/myapp/filesupload" method="post" enctype="multipart/form-data">
                        <p><input type="text" name="text1" value="text default">
                        <p><input type="text" name="text2" value="ABCD">
                        <p><input type="file" name="file1">
                        <p><input type="file" name="file2">
                        <p><button type="submit">Submit</button>
                    </form>
                </body>
            </html>
        ]]
	end
}


================================================
FILE: sample/webapps/myapp/handlers/handler-fileupload-process.lua
================================================
local function append(buffer, str)
    if (str) then
        table.insert(buffer, str)
        table.insert(buffer, ", ")
    end
end


registerHandler {
    method = 'POST',
    path = 'filesupload',

	handler = function(req, resp, pathParams)
	    if (req:isMultipart()) then
            local token, fieldName, fileName, contentType
            local buffer = {}
            for token, fieldName, fileName, contentType in req:multiPartIterator() do
                append(buffer, tostring(token))
                append(buffer, fieldName)
                append(buffer, fileName)
                append(buffer, contentType)
                table.insert(buffer,"\n")
            end
            return table.concat(buffer)
	    end
	    return "Not a multi-part file upload"
	end
}


================================================
FILE: sample/webapps/myapp/handlers/handler-hellouser.lua
================================================
registerHandler {
    method = 'GET',
    path = '/user/:username/#count',

	handler = function(req, resp, pathParams)
		return "Hello "..pathParams.username.."! You are user number "..pathParams.count.." to visit this site."
	end
}



================================================
FILE: sample/webapps/myapp/handlers/handler-helloworld.lua
================================================
registerHandler {
    method = 'GET',
    path = 'helloworld',

    handler = function(req, resp, pathParams)
        return "Hello World!"
    end
}



================================================
FILE: sample/webapps/myapp/handlers/handler-read-lpack.lua
================================================
local utils_lib = require('luaw_utils')
local http_lib = require('luaw_http')
local lpack = require('luapack')

registerHandler {
    method = 'GET',
    path = 'readlpack',

	handler = function(req, resp, pathParams)
        local backendReq = http_lib.newClientHttpRequest()
        backendReq.hostName = 'localhost'
        backendReq.port = 7001
        backendReq.url = '/myapp/genlpack'
        backendReq.method = 'GET'
        backendReq.headers = { Host = 'localhost' }
        local backendResp = backendReq:execute()
        local lpackReader = lpack.newLPackReqReader(backendResp)
        local mesg = lpackReader:read()

        print('\n================================\n')
        utils_lib.debugDump(backendResp.headers)
        print('\n================================\n')
        utils_lib.debugDump(mesg)
        print('\n================================\n')
        return "OK"
	end
}


================================================
FILE: sample/webapps/myapp/handlers/handler-write-lpack.lua
================================================
local utils_lib = require('luaw_utils')
local http_lib = require('luaw_http')
local lpack = require('luapack')

local big_str = "abcdefghijklmnopqrstuvwxyz0123456789"
while (#big_str < 4096) do
    big_str = big_str ..'-'..big_str
end

local mesg = {
    name = "Homer Simpson",
    gender = "M",
    uint8 = 255,
    uint16 = 256,
    uint32 = 4294967295,
    int8_neg = -128,
    int8 = 127,
    int16_neg = -1000,
    int16 = 20000,
    int32 = 32456,
    int64= 17179869184,
    int64_neg= -17179869184,
    float = 0.0012,
    float_neg = - 112.8,
    double = 8589934592.13,
    double_neg = -8589934592.28,
    str = "ABCD",
    bigstr = big_str,
    positive = true,
    negative = false,
    kids = {
        {
            name = "Lisa",
            gender = "F",
            uint8 = 255,
            uint16 = 256,
            uint32 = 4294967295,
            int8_neg = -128,
            int8 = 127,
            int16_neg = -1000,
            int16 = 20000,
            int32 = 32456,
            int64= 17179869184,
            int64_neg= -17179869184,
            float = 0.0012,
            float_neg = - 112.8,
            double = 8589934592.13,
            double_neg = -8589934592.28,
            str = "ABCD",
            bigstr = big_str,
            positive = true,
            negative = false
          },
        {
            name = "Bart",
            gender = "M",
            uint8 = 255,
            uint16 = 256,
            uint32 = 4294967295,
            int8_neg = -128,
            int8 = 127,
            int16_neg = -1000,
            int16 = 20000,
            int32 = 32456,
            int64= 17179869184,
            int64_neg= -17179869184,
            float = 0.0012,
            float_neg = - 112.8,
            double = 8589934592.13,
            double_neg = -8589934592.28,
            str = "ABCD",
            bigstr = big_str,
            positive = true,
            negative = false
          },
        {
            name = "Maggy",
            gender = "?"
        }
    }
}

local dict = {
    "name",
    "gender",
    "uint8",
    "uint16",
    "uint32",
    "int8_neg",
    "int8",
    "int16_neg",
    "int16",
    "int32",
    "int64",
    "int64_neg",
    "float",
    "float_neg",
    "double",
    "double_neg",
    "str",
    "bigstr",
    "positive",
    "negative",
    big_str
}

registerHandler {
    method = 'GET',
    path = 'genlpack',

	handler = function(req, resp, pathParams)
	    resp:setStatus(200)
        local lpackWriter = lpack.newLPackRespWriter(resp)
        if (req.params['dict'] == 'true') then
            lpackWriter:useDictionary(dict)
        end
        lpackWriter:write(mesg)
	end
}


================================================
FILE: sample/webapps/myapp/views/view-address.lua
================================================
BEGIN 'html'
    BEGIN 'head'
        BEGIN 'title'
            TEXT 'Address'
        END 'title'
    END 'head'
    BEGIN 'body'
        BEGIN 'div' {class='address'}
            BEGIN 'h1'
                TEXT(model.title)
            END 'h1'
            BEGIN 'table' {border="1", margin="1px"}
                BEGIN 'tr'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT 'City'
                    END 'td'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT(model.city)
                    END 'td'
                END 'tr'
                if (model.zip == 94086) then
                    BEGIN 'tr'
                        BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                            TEXT 'County'
                        END 'td'
                        BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                            TEXT 'Santa Clara'
                        END 'td'
                    END 'tr'
                end
                BEGIN 'tr'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT 'Zip'
                    END 'td'
                    BEGIN 'td' {style="padding: 3px 3px 3px 3px"}
                        TEXT(model.zip)
                    END 'td'
                END 'tr'
            END 'table'
        END 'div'
    END 'body'
END 'html'



================================================
FILE: sample/webapps/myapp/web.lua
================================================
luaw_webapp = {
    resourcePattern = "handler%-.*%.lua",
    viewPattern = "view%-.*%.lua",
}


================================================
FILE: src/Makefile
================================================
# Makefile for building Luaw
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================

CC?= gcc
CFLAGS?= -O2 -g -Wall
CFLAGS+= $(SYSCFLAGS) $(MYCFLAGS) -I../$(UVDIR)/include -I../$(LUADIR)/src
LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS)
LIBS= ../$(UVLIB) -lpthread ../$(LUALIB) -lm $(SYSLIBS) $(MYLIBS)

# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======

# Build artifacts
LUAW_OBJS= http_parser.o lua_lpack.o luaw_common.o luaw_logging.o luaw_http_parser.o luaw_server.o luaw_tcp.o luaw_timer.o lfs.o
LUAW_BIN= luaw_server
LUAW_CONF= server.cfg
LUAW_SCRIPTS= luapack.lua luaw_init.lua luaw_logging.lua luaw_data_structs_lib.lua luaw_utils.lua \
luaw_scheduler.lua luaw_webapp.lua luaw_timer.lua luaw_tcp.lua luaw_http.lua luaw_constants.lua

# How to install. If your install program does not support "-p", then
# you may have to run ranlib on the installed liblua.a.
INSTALL= install
INSTALL_EXEC= $(INSTALL) -p -m 0755
INSTALL_DATA= $(INSTALL) -p -m 0644

#
# If you don't have "install" you can use "cp" instead.
# INSTALL= cp -p
# INSTALL_EXEC= $(INSTALL)
# INSTALL_DATA= $(INSTALL)

# Other utilities.
MKDIR= mkdir -p
RM= rm -f
RMDIR= rm -rf

#where to install
INSTALL_BIN=$(INSTALL_ROOT)/bin
INSTALL_LIB=$(INSTALL_ROOT)/lib
INSTALL_CONF=$(INSTALL_ROOT)/conf
INSTALL_LOGS=$(INSTALL_ROOT)/logs
INSTALL_WEBAPP=$(INSTALL_ROOT)/webapps

# Targets start here.

all: $(LUAW_BIN)

$(LUAW_BIN): $(LUAW_OBJS)
	$(CC) -o $@ $(LDFLAGS) $(LUAW_OBJS) $(LIBS)

install: check_install_root
	$(MKDIR) $(INSTALL_BIN)
	$(MKDIR) $(INSTALL_LIB)
	$(MKDIR) $(INSTALL_CONF)
	$(MKDIR) $(INSTALL_LOGS)
	$(MKDIR) $(INSTALL_WEBAPP)
	$(INSTALL_EXEC) $(LUAW_BIN) $(INSTALL_BIN)
	cd ../lib && $(INSTALL_DATA) $(LUAW_SCRIPTS) $(INSTALL_BIN)
	cd ../conf && $(INSTALL_DATA) $(LUAW_CONF) $(INSTALL_CONF)

install-sample: install
	cd ../sample && cp -r * $(INSTALL_ROOT)

uninstall: check_install_root
	$(RMDIR) $(INSTALL_ROOT)/*

check_install_root:
ifndef INSTALL_ROOT
	$(error INSTALL_ROOT is undefined)
endif

clean:
	$(RM) $(LUAW_BIN) $(LUAW_OBJS)

echo:
	@echo "CC= $(CC)"
	@echo "CFLAGS= $(CFLAGS)"
	@echo "LDFLAGS= $(SYSLDFLAGS)"
	@echo "LIBS= $(LIBS)"
	@echo "RM= $(RM)"

# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: default install install-sample uninstall check_install_root clean echo

# Luaw object files
http_parser.o: http_parser.c http_parser.h
lua_lpack.o: lua_lpack.c lua_lpack.h luaw_common.h
luaw_logging.o: luaw_logging.c luaw_logging.h luaw_common.h
luaw_common.o: luaw_common.c luaw_common.h luaw_tcp.h luaw_http_parser.h luaw_timer.h lua_lpack.h
luaw_http_parser.o: luaw_http_parser.c luaw_http_parser.h luaw_common.h luaw_tcp.h lfs.h
luaw_server.o: luaw_server.c luaw_common.h luaw_tcp.h luaw_logging.h
luaw_tcp.o: luaw_tcp.c luaw_tcp.h luaw_common.h http_parser.h luaw_http_parser.h luaw_tcp.h
luaw_timer.o: luaw_timer.c luaw_timer.h luaw_common.h
lfs.o: lfs.c lfs.h



================================================
FILE: src/http_parser.c
================================================
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
 *
 * Additional changes are licensed under the same terms as NGINX and
 * copyright Joyent, Inc. and other Node contributors. All rights reserved.
 *
 * 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:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * 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.
 */
#include "http_parser.h"
#include <assert.h>
#include <stddef.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#ifndef ULLONG_MAX
# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
#endif

#ifndef MIN
# define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

#ifndef ARRAY_SIZE
# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif

#ifndef BIT_AT
# define BIT_AT(a, i)                                                \
  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
   (1 << ((unsigned int) (i) & 7))))
#endif

#ifndef ELEM_AT
# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
#endif

#define SET_ERRNO(e)                                                 \
do {                                                                 \
  parser->http_errno = (e);                                          \
} while(0)


/* Run the notify callback FOR, returning ER if it fails */
#define CALLBACK_NOTIFY_(FOR, ER)                                    \
do {                                                                 \
  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
                                                                     \
  if (settings->on_##FOR) {                                          \
    if (0 != settings->on_##FOR(parser)) {                           \
      SET_ERRNO(HPE_CB_##FOR);                                       \
    }                                                                \
                                                                     \
    /* We either errored above or got paused; get out */             \
    if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                       \
      return (ER);                                                   \
    }                                                                \
  }                                                                  \
} while (0)

/* Run the notify callback FOR and consume the current byte */
#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)

/* Run the notify callback FOR and don't consume the current byte */
#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)

/* Run data callback FOR with LEN bytes, returning ER if it fails */
#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
do {                                                                 \
  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
                                                                     \
  if (FOR##_mark) {                                                  \
    if (settings->on_##FOR) {                                        \
      if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) {      \
        SET_ERRNO(HPE_CB_##FOR);                                     \
      }                                                              \
                                                                     \
      /* We either errored above or got paused; get out */           \
      if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                     \
        return (ER);                                                 \
      }                                                              \
    }                                                                \
    FOR##_mark = NULL;                                               \
  }                                                                  \
} while (0)
  
/* Run the data callback FOR and consume the current byte */
#define CALLBACK_DATA(FOR)                                           \
    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)

/* Run the data callback FOR and don't consume the current byte */
#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)

/* Set the mark FOR; non-destructive if mark is already set */
#define MARK(FOR)                                                    \
do {                                                                 \
  if (!FOR##_mark) {                                                 \
    FOR##_mark = p;                                                  \
  }                                                                  \
} while (0)


#define PROXY_CONNECTION "proxy-connection"
#define CONNECTION "connection"
#define CONTENT_LENGTH "content-length"
#define TRANSFER_ENCODING "transfer-encoding"
#define UPGRADE "upgrade"
#define CHUNKED "chunked"
#define KEEP_ALIVE "keep-alive"
#define CLOSE "close"


static const char *method_strings[] =
  {
#define XX(num, name, string) #string,
  HTTP_METHOD_MAP(XX)
#undef XX
  };


/* Tokens as defined by rfc 2616. Also lowercases them.
 *        token       = 1*<any CHAR except CTLs or separators>
 *     separators     = "(" | ")" | "<" | ">" | "@"
 *                    | "," | ";" | ":" | "\" | <">
 *                    | "/" | "[" | "]" | "?" | "="
 *                    | "{" | "}" | SP | HT
 */
static const char tokens[256] = {
/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
        0,       0,       0,       0,       0,       0,       0,       0,
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
        0,       0,      '*',     '+',      0,      '-',     '.',      0,
/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
       '8',     '9',      0,       0,       0,       0,       0,       0,
/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };


static const int8_t unhex[256] =
  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  };


#if HTTP_PARSER_STRICT
# define T(v) 0
#else
# define T(v) v
#endif


static const uint8_t normal_url_char[32] = {
/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };

#undef T

enum state
  { s_dead = 1 /* important that this is > 0 */

  , s_start_req_or_res
  , s_res_or_resp_H
  , s_start_res
  , s_res_H
  , s_res_HT
  , s_res_HTT
  , s_res_HTTP
  , s_res_first_http_major
  , s_res_http_major
  , s_res_first_http_minor
  , s_res_http_minor
  , s_res_first_status_code
  , s_res_status_code
  , s_res_status_start
  , s_res_status
  , s_res_line_almost_done

  , s_start_req

  , s_req_method
  , s_req_spaces_before_url
  , s_req_schema
  , s_req_schema_slash
  , s_req_schema_slash_slash
  , s_req_server_start
  , s_req_server
  , s_req_server_with_at
  , s_req_path
  , s_req_query_string_start
  , s_req_query_string
  , s_req_fragment_start
  , s_req_fragment
  , s_req_http_start
  , s_req_http_H
  , s_req_http_HT
  , s_req_http_HTT
  , s_req_http_HTTP
  , s_req_first_http_major
  , s_req_http_major
  , s_req_first_http_minor
  , s_req_http_minor
  , s_req_line_almost_done

  , s_header_field_start
  , s_header_field
  , s_header_value_start
  , s_header_value
  , s_header_value_lws

  , s_header_almost_done

  , s_chunk_size_start
  , s_chunk_size
  , s_chunk_parameters
  , s_chunk_size_almost_done

  , s_headers_almost_done
  , s_headers_done

  /* Important: 's_headers_done' must be the last 'header' state. All
   * states beyond this must be 'body' states. It is used for overflow
   * checking. See the PARSING_HEADER() macro.
   */

  , s_chunk_data
  , s_chunk_data_almost_done
  , s_chunk_data_done

  , s_body_identity
  , s_body_identity_eof

  , s_message_done
  };


#define PARSING_HEADER(state) (state <= s_headers_done)


enum header_states
  { h_general = 0
  , h_C
  , h_CO
  , h_CON

  , h_matching_connection
  , h_matching_proxy_connection
  , h_matching_content_length
  , h_matching_transfer_encoding
  , h_matching_upgrade

  , h_connection
  , h_content_length
  , h_transfer_encoding
  , h_upgrade

  , h_matching_transfer_encoding_chunked
  , h_matching_connection_keep_alive
  , h_matching_connection_close

  , h_transfer_encoding_chunked
  , h_connection_keep_alive
  , h_connection_close
  };

enum http_host_state
  {
    s_http_host_dead = 1
  , s_http_userinfo_start
  , s_http_userinfo
  , s_http_host_start
  , s_http_host_v6_start
  , s_http_host
  , s_http_host_v6
  , s_http_host_v6_end
  , s_http_host_port_start
  , s_http_host_port
};

/* Macros for character classes; depends on strict-mode  */
#define CR                  '\r'
#define LF                  '\n'
#define LOWER(c)            (unsigned char)(c | 0x20)
#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
  (c) == ')')
#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
  (c) == '$' || (c) == ',')

#if HTTP_PARSER_STRICT
#define TOKEN(c)            (tokens[(unsigned char)c])
#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
#else
#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])
#define IS_URL_CHAR(c)                                                         \
  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
#define IS_HOST_CHAR(c)                                                        \
  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif


#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)


#if HTTP_PARSER_STRICT
# define STRICT_CHECK(cond)                                          \
do {                                                                 \
  if (cond) {                                                        \
    SET_ERRNO(HPE_STRICT);                                           \
    goto error;                                                      \
  }                                                                  \
} while (0)
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
#else
# define STRICT_CHECK(cond)
# define NEW_MESSAGE() start_state
#endif


/* Map errno values to strings for human-readable output */
#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
static struct {
  const char *name;
  const char *description;
} http_strerror_tab[] = {
  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
};
#undef HTTP_STRERROR_GEN

int http_message_needs_eof(const http_parser *parser);

/* Our URL parser.
 *
 * This is designed to be shared by http_parser_execute() for URL validation,
 * hence it has a state transition + byte-for-byte interface. In addition, it
 * is meant to be embedded in http_parser_parse_url(), which does the dirty
 * work of turning state transitions URL components for its API.
 *
 * This function should only be invoked with non-space characters. It is
 * assumed that the caller cares about (and can detect) the transition between
 * URL and non-URL states by looking for these.
 */
static enum state
parse_url_char(enum state s, const char ch)
{
  if (ch == ' ' || ch == '\r' || ch == '\n') {
    return s_dead;
  }

#if HTTP_PARSER_STRICT
  if (ch == '\t' || ch == '\f') {
    return s_dead;
  }
#endif

  switch (s) {
    case s_req_spaces_before_url:
      /* Proxied requests are followed by scheme of an absolute URI (alpha).
       * All methods except CONNECT are followed by '/' or '*'.
       */

      if (ch == '/' || ch == '*') {
        return s_req_path;
      }

      if (IS_ALPHA(ch)) {
        return s_req_schema;
      }

      break;

    case s_req_schema:
      if (IS_ALPHA(ch)) {
        return s;
      }

      if (ch == ':') {
        return s_req_schema_slash;
      }

      break;

    case s_req_schema_slash:
      if (ch == '/') {
        return s_req_schema_slash_slash;
      }

      break;

    case s_req_schema_slash_slash:
      if (ch == '/') {
        return s_req_server_start;
      }

      break;

    case s_req_server_with_at:
      if (ch == '@') {
        return s_dead;
      }

    /* FALLTHROUGH */
    case s_req_server_start:
    case s_req_server:
      if (ch == '/') {
        return s_req_path;
      }

      if (ch == '?') {
        return s_req_query_string_start;
      }

      if (ch == '@') {
        return s_req_server_with_at;
      }

      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
        return s_req_server;
      }

      break;

    case s_req_path:
      if (IS_URL_CHAR(ch)) {
        return s;
      }

      switch (ch) {
        case '?':
          return s_req_query_string_start;

        case '#':
          return s_req_fragment_start;
      }

      break;

    case s_req_query_string_start:
    case s_req_query_string:
      if (IS_URL_CHAR(ch)) {
        return s_req_query_string;
      }

      switch (ch) {
        case '?':
          /* allow extra '?' in query string */
          return s_req_query_string;

        case '#':
          return s_req_fragment_start;
      }

      break;

    case s_req_fragment_start:
      if (IS_URL_CHAR(ch)) {
        return s_req_fragment;
      }

      switch (ch) {
        case '?':
          return s_req_fragment;

        case '#':
          return s;
      }

      break;

    case s_req_fragment:
      if (IS_URL_CHAR(ch)) {
        return s;
      }

      switch (ch) {
        case '?':
        case '#':
          return s;
      }

      break;

    default:
      break;
  }

  /* We should never fall out of the switch above unless there's an error */
  return s_dead;
}

size_t http_parser_execute (http_parser *parser,
                            const http_parser_settings *settings,
                            const char *data,
                            size_t len)
{
  char c, ch;
  int8_t unhex_val;
  const char *p = data;
  const char *header_field_mark = 0;
  const char *header_value_mark = 0;
  const char *url_mark = 0;
  const char *body_mark = 0;
  const char *status_mark = 0;

  /* We're in an error state. Don't bother doing anything. */
  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
    return 0;
  }

  if (len == 0) {
    switch (parser->state) {
      case s_body_identity_eof:
        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
         * we got paused.
         */
        CALLBACK_NOTIFY_NOADVANCE(message_complete);
        return 0;

      case s_dead:
      case s_start_req_or_res:
      case s_start_res:
      case s_start_req:
        return 0;

      default:
        SET_ERRNO(HPE_INVALID_EOF_STATE);
        return 1;
    }
  }


  if (parser->state == s_header_field)
    header_field_mark = data;
  if (parser->state == s_header_value)
    header_value_mark = data;
  switch (parser->state) {
  case s_req_path:
  case s_req_schema:
  case s_req_schema_slash:
  case s_req_schema_slash_slash:
  case s_req_server_start:
  case s_req_server:
  case s_req_server_with_at:
  case s_req_query_string_start:
  case s_req_query_string:
  case s_req_fragment_start:
  case s_req_fragment:
    url_mark = data;
    break;
  case s_res_status:
    status_mark = data;
    break;
  }

  for (p=data; p != data + len; p++) {
    ch = *p;

    if (PARSING_HEADER(parser->state)) {
      ++parser->nread;
      /* Don't allow the total size of the HTTP headers (including the status
       * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect
       * embedders against denial-of-service attacks where the attacker feeds
       * us a never-ending header that the embedder keeps buffering.
       *
       * This check is arguably the responsibility of embedders but we're doing
       * it on the embedder's behalf because most won't bother and this way we
       * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger
       * than any reasonable request or response so this should never affect
       * day-to-day operation.
       */
      if (parser->nread > HTTP_MAX_HEADER_SIZE) {
        SET_ERRNO(HPE_HEADER_OVERFLOW);
        goto error;
      }
    }

    reexecute_byte:
    switch (parser->state) {

      case s_dead:
        /* this state is used after a 'Connection: close' message
         * the parser will error out if it reads another message
         */
        if (ch == CR || ch == LF)
          break;

        SET_ERRNO(HPE_CLOSED_CONNECTION);
        goto error;

      case s_start_req_or_res:
      {
        if (ch == CR || ch == LF)
          break;
        parser->flags = 0;
        parser->content_length = ULLONG_MAX;

        if (ch == 'H') {
          parser->state = s_res_or_resp_H;

          CALLBACK_NOTIFY(message_begin);
        } else {
          parser->type = HTTP_REQUEST;
          parser->state = s_start_req;
          goto reexecute_byte;
        }

        break;
      }

      case s_res_or_resp_H:
        if (ch == 'T') {
          parser->type = HTTP_RESPONSE;
          parser->state = s_res_HT;
        } else {
          if (ch != 'E') {
            SET_ERRNO(HPE_INVALID_CONSTANT);
            goto error;
          }

          parser->type = HTTP_REQUEST;
          parser->method = HTTP_HEAD;
          parser->index = 2;
          parser->state = s_req_method;
        }
        break;

      case s_start_res:
      {
        parser->flags = 0;
        parser->content_length = ULLONG_MAX;

        switch (ch) {
          case 'H':
            parser->state = s_res_H;
            break;

          case CR:
          case LF:
            break;

          default:
            SET_ERRNO(HPE_INVALID_CONSTANT);
            goto error;
        }

        CALLBACK_NOTIFY(message_begin);
        break;
      }

      case s_res_H:
        STRICT_CHECK(ch != 'T');
        parser->state = s_res_HT;
        break;

      case s_res_HT:
        STRICT_CHECK(ch != 'T');
        parser->state = s_res_HTT;
        break;

      case s_res_HTT:
        STRICT_CHECK(ch != 'P');
        parser->state = s_res_HTTP;
        break;

      case s_res_HTTP:
        STRICT_CHECK(ch != '/');
        parser->state = s_res_first_http_major;
        break;

      case s_res_first_http_major:
        if (ch < '0' || ch > '9') {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_major = ch - '0';
        parser->state = s_res_http_major;
        break;

      /* major HTTP version or dot */
      case s_res_http_major:
      {
        if (ch == '.') {
          parser->state = s_res_first_http_minor;
          break;
        }

        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_major *= 10;
        parser->http_major += ch - '0';

        if (parser->http_major > 999) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        break;
      }

      /* first digit of minor HTTP version */
      case s_res_first_http_minor:
        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_minor = ch - '0';
        parser->state = s_res_http_minor;
        break;

      /* minor HTTP version or end of request line */
      case s_res_http_minor:
      {
        if (ch == ' ') {
          parser->state = s_res_first_status_code;
          break;
        }

        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_minor *= 10;
        parser->http_minor += ch - '0';

        if (parser->http_minor > 999) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        break;
      }

      case s_res_first_status_code:
      {
        if (!IS_NUM(ch)) {
          if (ch == ' ') {
            break;
          }

          SET_ERRNO(HPE_INVALID_STATUS);
          goto error;
        }
        parser->status_code = ch - '0';
        parser->state = s_res_status_code;
        break;
      }

      case s_res_status_code:
      {
        if (!IS_NUM(ch)) {
          switch (ch) {
            case ' ':
              parser->state = s_res_status_start;
              break;
            case CR:
              parser->state = s_res_line_almost_done;
              break;
            case LF:
              parser->state = s_header_field_start;
              break;
            default:
              SET_ERRNO(HPE_INVALID_STATUS);
              goto error;
          }
          break;
        }

        parser->status_code *= 10;
        parser->status_code += ch - '0';

        if (parser->status_code > 999) {
          SET_ERRNO(HPE_INVALID_STATUS);
          goto error;
        }

        break;
      }

      case s_res_status_start:
      {
        if (ch == CR) {
          parser->state = s_res_line_almost_done;
          break;
        }

        if (ch == LF) {
          parser->state = 
Download .txt
gitextract_wtxp3faz/

├── .gitignore
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
├── conf/
│   └── server.cfg
├── docs/
│   ├── Contents.md
│   ├── Introduction.md
│   ├── cmd-line.md
│   ├── configuration.md
│   ├── first-webapp.md
│   ├── getting-started.md
│   ├── http-client.md
│   ├── logging.md
│   ├── proxy-http.md
│   ├── resp-obj.md
│   ├── second-webapp.md
│   ├── template-view.md
│   ├── third-webapp.md
│   ├── user-threads.md
│   └── user-timers.md
├── lib/
│   ├── luapack.lua
│   ├── luaw_constants.lua
│   ├── luaw_data_structs_lib.lua
│   ├── luaw_http.lua
│   ├── luaw_init.lua
│   ├── luaw_logging.lua
│   ├── luaw_scheduler.lua
│   ├── luaw_tcp.lua
│   ├── luaw_timer.lua
│   ├── luaw_utils.lua
│   ├── luaw_webapp.lua
│   └── unit_testing.lua
├── sample/
│   ├── conf/
│   │   └── server.cfg
│   ├── proxy_handler.lua
│   └── webapps/
│       └── myapp/
│           ├── handlers/
│           │   ├── handler-address.lua
│           │   ├── handler-fileupload-display.lua
│           │   ├── handler-fileupload-process.lua
│           │   ├── handler-hellouser.lua
│           │   ├── handler-helloworld.lua
│           │   ├── handler-read-lpack.lua
│           │   └── handler-write-lpack.lua
│           ├── views/
│           │   └── view-address.lua
│           └── web.lua
└── src/
    ├── Makefile
    ├── http_parser.c
    ├── http_parser.h
    ├── lfs.c
    ├── lfs.h
    ├── lua_lpack.c
    ├── lua_lpack.h
    ├── luaw_common.c
    ├── luaw_common.h
    ├── luaw_http_parser.c
    ├── luaw_http_parser.h
    ├── luaw_logging.c
    ├── luaw_logging.h
    ├── luaw_server.c
    ├── luaw_tcp.c
    ├── luaw_tcp.h
    ├── luaw_timer.c
    └── luaw_timer.h
Download .txt
SYMBOL INDEX (204 symbols across 15 files)

FILE: src/http_parser.c
  type state (line 235) | enum state
  type header_states (line 316) | enum header_states
  type http_host_state (line 342) | enum http_host_state
  function parse_url_char (line 425) | static enum state
  function http_parser_execute (line 573) | size_t http_parser_execute (http_parser *parser,
  function http_message_needs_eof (line 1901) | int
  function http_should_keep_alive (line 1924) | int
  type http_method (line 1944) | enum http_method
  function http_parser_init (line 1950) | void
  type http_errno (line 1962) | enum http_errno
  type http_errno (line 1968) | enum http_errno
  function http_parse_host_char (line 1973) | static enum http_host_state
  function http_parse_host (line 2038) | static int
  function http_parser_parse_url (line 2111) | int
  function http_parser_pause (line 2210) | void
  function http_body_is_final (line 2224) | int
  function http_parser_version (line 2229) | unsigned long

FILE: src/http_parser.h
  type __int8 (line 36) | typedef __int8 int8_t;
  type __int16 (line 38) | typedef __int16 int16_t;
  type __int32 (line 40) | typedef __int32 int32_t;
  type __int64 (line 42) | typedef __int64 int64_t;
  type http_parser (line 59) | typedef struct http_parser http_parser;
  type http_parser_settings (line 60) | typedef struct http_parser_settings http_parser_settings;
  type http_method (line 114) | enum http_method
  type http_parser_type (line 122) | enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }
  type flags (line 126) | enum flags
  type http_errno (line 184) | enum http_errno {
  type http_parser (line 194) | struct http_parser {
  type http_parser_settings (line 224) | struct http_parser_settings {
  type http_parser_url_fields (line 236) | enum http_parser_url_fields
  type http_parser_url (line 255) | struct http_parser_url {
  type http_parser_type (line 278) | enum http_parser_type
  type http_method (line 296) | enum http_method
  type http_errno (line 299) | enum http_errno
  type http_errno (line 302) | enum http_errno
  type http_parser_url (line 307) | struct http_parser_url

FILE: src/lfs.c
  type dir_data (line 98) | typedef struct dir_data {
  function pusherror (line 132) | static int pusherror(lua_State *L, const char *info)
  function pushresult (line 143) | static int pushresult(lua_State *L, int i, const char *info)
  function change_dir (line 155) | static int change_dir (lua_State *L) {
  function get_dir (line 173) | static int get_dir (lua_State *L) {
  function FILE (line 191) | static FILE *check_file (lua_State *L, int idx, const char *funcname) {
  function _file_lock (line 207) | static int _file_lock (lua_State *L, FILE *fh, const char *mode, const l...
  type lfs_Lock (line 255) | typedef struct lfs_Lock {
  function lfs_lock_dir (line 258) | static int lfs_lock_dir(lua_State *L) {
  function lfs_unlock_dir (line 286) | static int lfs_unlock_dir(lua_State *L) {
  type lfs_Lock (line 295) | typedef struct lfs_Lock {
  function lfs_lock_dir (line 298) | static int lfs_lock_dir(lua_State *L) {
  function lfs_unlock_dir (line 319) | static int lfs_unlock_dir(lua_State *L) {
  function lfs_g_setmode (line 330) | static int lfs_g_setmode (lua_State *L, FILE *f, int arg) {
  function lfs_f_setmode (line 356) | static int lfs_f_setmode(lua_State *L) {
  function file_lock (line 367) | static int file_lock (lua_State *L) {
  function file_unlock (line 389) | static int file_unlock (lua_State *L) {
  function make_link (line 410) | static int make_link(lua_State *L)
  function make_dir (line 427) | static int make_dir (lua_State *L) {
  function remove_dir (line 450) | static int remove_dir (lua_State *L) {
  function dir_iter (line 469) | static int dir_iter (lua_State *L) {
  function dir_close (line 516) | static int dir_close (lua_State *L) {
  function dir_iter_factory (line 535) | static int dir_iter_factory (lua_State *L) {
  function dir_create_meta (line 561) | static int dir_create_meta (lua_State *L) {
  function lock_create_meta (line 582) | static int lock_create_meta (lua_State *L) {
  function file_utime (line 651) | static int file_utime (lua_State *L) {
  function push_st_mode (line 673) | static void push_st_mode (lua_State *L, STAT_STRUCT *info) {
  function push_st_dev (line 677) | static void push_st_dev (lua_State *L, STAT_STRUCT *info) {
  function push_st_ino (line 681) | static void push_st_ino (lua_State *L, STAT_STRUCT *info) {
  function push_st_nlink (line 685) | static void push_st_nlink (lua_State *L, STAT_STRUCT *info) {
  function push_st_uid (line 689) | static void push_st_uid (lua_State *L, STAT_STRUCT *info) {
  function push_st_gid (line 693) | static void push_st_gid (lua_State *L, STAT_STRUCT *info) {
  function push_st_rdev (line 697) | static void push_st_rdev (lua_State *L, STAT_STRUCT *info) {
  function push_st_atime (line 701) | static void push_st_atime (lua_State *L, STAT_STRUCT *info) {
  function push_st_mtime (line 705) | static void push_st_mtime (lua_State *L, STAT_STRUCT *info) {
  function push_st_ctime (line 709) | static void push_st_ctime (lua_State *L, STAT_STRUCT *info) {
  function push_st_size (line 713) | static void push_st_size (lua_State *L, STAT_STRUCT *info) {
  function push_st_blocks (line 718) | static void push_st_blocks (lua_State *L, STAT_STRUCT *info) {
  function push_st_blksize (line 722) | static void push_st_blksize (lua_State *L, STAT_STRUCT *info) {
  function push_st_perm (line 763) | static void push_st_perm (lua_State *L, STAT_STRUCT *info) {
  type _stat_members (line 769) | struct _stat_members {
  type _stat_members (line 774) | struct _stat_members
  function _file_info_ (line 797) | static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT...
  function file_info (line 836) | static int file_info (lua_State *L) {
  function link_info (line 844) | static int link_info (lua_State *L) {
  function set_info (line 852) | static void set_info (lua_State *L) {
  type luaL_Reg (line 865) | struct luaL_Reg
  function luaopen_lfs (line 882) | int luaopen_lfs (lua_State *L) {

FILE: src/lua_lpack.c
  function be16 (line 36) | static void be16(const char* in, char * out) {
  function be32 (line 46) | static void be32(const char* in, char* out) {
  function be64 (line 60) | static void be64(const char* in, char* out) {
  function number_to_lua (line 88) | static int number_to_lua(lua_State* L, int read_len, double value) {
  function LUA_LIB_METHOD (line 94) | LUA_LIB_METHOD int read_number(lua_State* L) {
  function string_to_lua (line 173) | static int string_to_lua(lua_State* L, const char* buff, size_t len) {
  function LUA_LIB_METHOD (line 179) | LUA_LIB_METHOD int read_string(lua_State* L) {
  function fetch_next_as_integer (line 198) | static double fetch_next_as_integer(lua_State* L, int* idx, const char* ...
  function fetch_next_as_double (line 210) | static double fetch_next_as_double(lua_State* L, int* idx, const char* s) {
  function LUA_LIB_METHOD (line 238) | LUA_LIB_METHOD int serialize_write_Q(lua_State* L) {
  function define_lpack_enum (line 406) | static void define_lpack_enum(lua_State* L, const char* name, type_tag t...
  function define_lapck_types (line 430) | static void define_lapck_types(lua_State* L) {
  function LUA_LIB_METHOD (line 459) | LUA_LIB_METHOD static int new_lpack_parser(lua_State* L) {
  function LUA_LIB_METHOD (line 465) | LUA_LIB_METHOD static int create_dict(lua_State* L) {
  function LUA_LIB_METHOD (line 472) | LUA_LIB_METHOD static int to_hex(lua_State* L) {
  type luaL_Reg (line 484) | struct luaL_Reg
  type luaL_Reg (line 491) | struct luaL_Reg
  function luaw_init_lpack_lib (line 498) | void luaw_init_lpack_lib (lua_State *L) {

FILE: src/lua_lpack.h
  type type_tag (line 29) | typedef enum {

FILE: src/luaw_common.c
  function resume_lua_thread (line 45) | void resume_lua_thread(lua_State* L, int nargs, int nresults, int errHan...
  function error_to_lua (line 57) | int error_to_lua(lua_State* L, const char* fmt, ...) {
  function raise_lua_error (line 67) | int raise_lua_error (lua_State *L, const char *fmt, ...) {
  function close_if_active (line 75) | void close_if_active(uv_handle_t* handle, uv_close_cb close_cb) {
  function luaL_setfuncs (line 84) | void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
  function luaL_setmetatable (line 98) | void luaL_setmetatable (lua_State *L, const char *tname) {
  function make_metatable (line 106) | void make_metatable(lua_State *L, const char* mt_name, const luaL_Reg* m...
  function LUA_LIB_METHOD (line 115) | LUA_LIB_METHOD void luaw_init_libs (lua_State *L) {

FILE: src/luaw_http_parser.c
  type parse_http_lua_stack_index (line 35) | typedef enum {
  function decode_hex_str (line 44) | static int decode_hex_str(const char* str, int len) {
  function handle_name_value_pair (line 67) | static int handle_name_value_pair(lua_State* L, const char* name, int na...
  function LUA_LIB_METHOD (line 96) | LUA_LIB_METHOD static int luaw_url_decode(lua_State *L) {
  function new_lhttp_parser (line 180) | static int new_lhttp_parser(lua_State *L, enum http_parser_type parser_t...
  function LUA_LIB_METHOD (line 191) | LUA_LIB_METHOD static int luaw_new_http_request_parser(lua_State* L) {
  function LUA_LIB_METHOD (line 195) | LUA_LIB_METHOD static int luaw_new_http_response_parser(lua_State* L) {
  function LUA_LIB_METHOD (line 199) | LUA_LIB_METHOD static int luaw_init_http_parser(lua_State* L) {
  function handle_http_callback (line 206) | static int handle_http_callback(http_parser *parser, http_parser_cb_type...
  function HTTP_PARSER_CALLBACK (line 215) | HTTP_PARSER_CALLBACK static int http_parser_on_message_begin(http_parser...
  function HTTP_PARSER_CALLBACK (line 219) | HTTP_PARSER_CALLBACK static int http_parser_on_url(http_parser *parser, ...
  function HTTP_PARSER_CALLBACK (line 223) | HTTP_PARSER_CALLBACK static int http_parser_on_status(http_parser *parse...
  function HTTP_PARSER_CALLBACK (line 227) | HTTP_PARSER_CALLBACK static int http_parser_on_header_name(http_parser *...
  function HTTP_PARSER_CALLBACK (line 231) | HTTP_PARSER_CALLBACK static int http_parser_on_header_value(http_parser ...
  function HTTP_PARSER_CALLBACK (line 235) | HTTP_PARSER_CALLBACK static int http_parser_on_headers_complete(http_par...
  function HTTP_PARSER_CALLBACK (line 239) | HTTP_PARSER_CALLBACK static int http_parser_on_body(http_parser *parser,...
  function HTTP_PARSER_CALLBACK (line 243) | HTTP_PARSER_CALLBACK static int http_parser_on_message_complete(http_par...
  function parse_http (line 274) | static int parse_http(lua_State *L) {
  function push_url_part (line 342) | static void push_url_part(lua_State *L, const char* buff, struct http_pa...
  function LUA_LIB_METHOD (line 353) | LUA_LIB_METHOD static int luaw_parse_url(lua_State *L) {
  type luaL_Reg (line 382) | struct luaL_Reg
  type luaL_Reg (line 390) | struct luaL_Reg
  function luaw_init_http_lib (line 396) | void luaw_init_http_lib (lua_State *L) {

FILE: src/luaw_http_parser.h
  type decoder_state (line 31) | typedef enum {
  type http_parser_cb_type (line 39) | typedef enum {
  type luaw_http_parser_t (line 60) | typedef struct {

FILE: src/luaw_logging.c
  type addrinfo (line 39) | struct addrinfo
  function LUA_LIB_METHOD (line 53) | LUA_LIB_METHOD static int get_hostname_lua(lua_State *L) {
  function LUA_LIB_METHOD (line 58) | LUA_LIB_METHOD static int get_logging_state(lua_State* l_thread) {
  function LIBUV_CALLBACK (line 63) | LIBUV_CALLBACK static void on_log_open(uv_fs_t* req) {
  function LUA_LIB_METHOD (line 70) | LUA_LIB_METHOD static int open_log_file(lua_State* l_thread) {
  function LIBUV_CALLBACK (line 87) | LIBUV_CALLBACK static void on_log_close(uv_fs_t* req) {
  function LIBUV_CALLBACK (line 92) | LIBUV_CALLBACK static void close_log(uv_fs_t* req) {
  function LIBUV_CALLBACK (line 98) | LIBUV_CALLBACK static void on_log_write(uv_fs_t* req) {
  function LUA_LIB_METHOD (line 108) | LUA_LIB_METHOD static int write_log(lua_State* l_thread) {
  function close_syslog (line 146) | void close_syslog() {
  function LUA_LIB_METHOD (line 150) | LUA_LIB_METHOD static int connect_to_syslog(lua_State *L) {
  function LUA_LIB_METHOD (line 191) | LUA_LIB_METHOD static int send_to_syslog(lua_State *L) {
  type luaL_Reg (line 204) | struct luaL_Reg
  function luaw_init_logging_lib (line 214) | int luaw_init_logging_lib (lua_State *L) {

FILE: src/luaw_logging.h
  type logfile_sate (line 27) | typedef enum {

FILE: src/luaw_server.c
  type lua_load_buffer_t (line 55) | typedef struct {
  function handle_shutdown_req (line 79) | static void handle_shutdown_req(uv_signal_t* handle, int signum) {
  function init_luaw_server (line 87) | void init_luaw_server(lua_State* L) {
  function LIBUV_CALLBACK (line 159) | LIBUV_CALLBACK static void on_server_connect(uv_stream_t* server, int st...
  function start_server (line 185) | void start_server(lua_State *L) {
  function run_user_threads (line 213) | static void run_user_threads(uv_prepare_t* handle) {
  function close_walk_cb (line 226) | static void close_walk_cb(uv_handle_t* handle, void* arg) {
  function server_loop (line 232) | static int server_loop(lua_State *L) {
  function run_lua_file (line 248) | static void run_lua_file(const char* filename, char* epilogue) {
  function set_lua_path (line 280) | static void set_lua_path(lua_State* L) {
  function main (line 287) | int main (int argc, char* argv[]) {

FILE: src/luaw_tcp.c
  function connection_t (line 46) | connection_t* new_connection(lua_State* L) {
  function LUA_LIB_METHOD (line 85) | LUA_LIB_METHOD static int new_connection_lua(lua_State* L) {
  function free_timer (line 90) | static void free_timer(uv_handle_t* handle) {
  function free_tcp_handle (line 96) | static void free_tcp_handle(uv_handle_t* handle) {
  function close_connection (line 102) | void close_connection(connection_t* conn, const int status) {
  function LIBUV_CALLBACK (line 154) | LIBUV_CALLBACK static void on_conn_timeout(uv_timer_t* timer) {
  function start_timer (line 160) | static int start_timer(uv_timer_t* timer, int timeout) {
  function stop_timer (line 167) | static void stop_timer(uv_timer_t* timer) {
  function LUA_OBJ_METHOD (line 173) | LUA_OBJ_METHOD static int close_connection_lua(lua_State* l_thread) {
  function LUA_OBJ_METHOD (line 183) | LUA_OBJ_METHOD static int connection_gc(lua_State *L) {
  function LIBUV_CALLBACK (line 201) | LIBUV_CALLBACK static void on_alloc(uv_handle_t* handle, size_t suggeste...
  function LIBUV_API (line 220) | LIBUV_API static void on_read(uv_stream_t* stream, ssize_t nread, const ...
  function LUA_OBJ_METHOD (line 251) | LUA_OBJ_METHOD static int start_reading(lua_State *l_thread) {
  function LUA_OBJ_METHOD (line 271) | LUA_OBJ_METHOD static int read_check(lua_State* l_thread) {
  function LIBUV_API (line 301) | LIBUV_API static void on_write(uv_write_t* req, int status) {
  function LUA_OBJ_METHOD (line 326) | LUA_OBJ_METHOD static int write_buffer(lua_State* l_thread) {
  function LIBUV_CALLBACK (line 364) | LIBUV_CALLBACK static void on_client_connect(uv_connect_t* connect_req, ...
  function LUA_LIB_METHOD (line 389) | LUA_LIB_METHOD static int client_connect(lua_State* l_thread) {
  function LIBUV_CALLBACK (line 430) | LIBUV_CALLBACK static void on_resolved(uv_getaddrinfo_t *resolver, int s...
  function LUA_LIB_METHOD (line 455) | LUA_LIB_METHOD static int dns_resolve(lua_State* l_thread) {
  type luaL_Reg (line 495) | struct luaL_Reg
  type luaL_Reg (line 504) | struct luaL_Reg
  function luaw_init_tcp_lib (line 512) | void luaw_init_tcp_lib (lua_State *L) {

FILE: src/luaw_tcp.h
  type connection_t (line 30) | typedef struct connection_s connection_t;
  type connection_s (line 34) | struct connection_s {

FILE: src/luaw_timer.c
  function clear_user_timer (line 41) | static void clear_user_timer(luaw_timer_t* timer) {
  function free_user_timer (line 46) | static void free_user_timer(uv_handle_t* handle) {
  function LUA_OBJ_METHOD (line 52) | LUA_OBJ_METHOD static void close_timer(luaw_timer_t* timer) {
  function LUA_OBJ_METHOD (line 73) | LUA_OBJ_METHOD static int close_timer_lua(lua_State* l_thread) {
  function LUA_OBJ_METHOD (line 79) | LUA_OBJ_METHOD static int timer_gc(lua_State *L) {
  function LUA_LIB_METHOD (line 85) | LUA_LIB_METHOD static int new_user_timer(lua_State* l_thread) {
  function LIBUV_CALLBACK (line 117) | LIBUV_CALLBACK static void on_user_timer_timeout(uv_timer_t* handle) {
  function start_user_timer (line 135) | static int start_user_timer(lua_State* l_thread) {
  function LUA_OBJ_METHOD (line 165) | LUA_OBJ_METHOD static int wait_user_timer(lua_State* l_thread) {
  function LUA_OBJ_METHOD (line 191) | LUA_OBJ_METHOD static int stop_user_timer(lua_State* l_thread) {
  type luaL_Reg (line 208) | struct luaL_Reg
  type luaL_Reg (line 217) | struct luaL_Reg
  function luaw_init_timer_lib (line 223) | void luaw_init_timer_lib (lua_State *L) {

FILE: src/luaw_timer.h
  type timer_state (line 29) | typedef enum {
  type luaw_timer_t (line 36) | typedef struct luaw_timer_s luaw_timer_t;
  type luaw_timer_s (line 38) | struct luaw_timer_s {
Condensed preview — 62 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (375K chars).
[
  {
    "path": ".gitignore",
    "chars": 24,
    "preview": "**/*.o\n**/*.so\n**/*.log\n"
  },
  {
    "path": ".gitmodules",
    "chars": 278,
    "preview": "[submodule \"deps/libuv\"]\n\tpath = deps/libuv\n\turl = https://github.com/libuv/libuv.git\n[submodule \"deps/luajit-2.0\"]\n\tpat"
  },
  {
    "path": "LICENSE",
    "chars": 1052,
    "preview": "Copyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this softwa"
  },
  {
    "path": "Makefile",
    "chars": 2447,
    "preview": "# Makefile for building Luaw\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\nexport UVD"
  },
  {
    "path": "README.md",
    "chars": 5551,
    "preview": "Luaw - Lua meets Node.js\n========================\n***\n\nLuaw stands for \"Lua web server\". It also matches abbreviation fo"
  },
  {
    "path": "conf/server.cfg",
    "chars": 493,
    "preview": "luaw_server_config = {\n    server_ip = \"0.0.0.0\",\n    server_port = 7001,\n    connect_timeout = 4000,\n    read_timeout ="
  },
  {
    "path": "docs/Contents.md",
    "chars": 533,
    "preview": "#Contents\n\n1. Introduction\n2. Getting started: building and installing Luaw\n3. Luaw configuration\n4. Your first Luaw web"
  },
  {
    "path": "docs/Introduction.md",
    "chars": 2859,
    "preview": "#Introduction\n\nLuaw stands for \"Lua web server\". It also matches abbreviation for an air traffic controller command \"lin"
  },
  {
    "path": "docs/cmd-line.md",
    "chars": 1372,
    "preview": "#14. Advanced Topic II - Using custom scripts on command line at start up\n\nIn the last topic we saw that we can specify "
  },
  {
    "path": "docs/configuration.md",
    "chars": 6289,
    "preview": "#3. Configuring Luaw\n\nBefore we can write our first Luaw webapp we need to configure our Luaw server with some basic set"
  },
  {
    "path": "docs/first-webapp.md",
    "chars": 3999,
    "preview": "#4. Your First Luaw Webapp - \"Hello world!\"\n\nNow that we are familiar with Luaw's directory layout and configuration, we"
  },
  {
    "path": "docs/getting-started.md",
    "chars": 3252,
    "preview": "#2. Getting started\n\nLet's build Luaw from its sources. Luaw depends on,\n\n1. Lua 5.2(+)\n2. libuv (v1.0.0 )\n\nWe will firs"
  },
  {
    "path": "docs/http-client.md",
    "chars": 1757,
    "preview": "#10. Async HTTP Client\n\nLuaw comes equipped with curl like async HTTP client. It is fully async, in that both DNS lookup"
  },
  {
    "path": "docs/logging.md",
    "chars": 4668,
    "preview": "#8. Luaw logging framework\n\nLuaw comes equipped with logging framework that's modeled after Java's popular log4j logging"
  },
  {
    "path": "docs/proxy-http.md",
    "chars": 7813,
    "preview": "#13. custom HTTP handler for proxying request\n\nSo far we have used default HTTP requests handler that ships with Luaw in"
  },
  {
    "path": "docs/resp-obj.md",
    "chars": 3252,
    "preview": "#9. Response Object\n\nSo far we have seen two ways to return response body from resource handler:\n\n1. Returning a string "
  },
  {
    "path": "docs/second-webapp.md",
    "chars": 2162,
    "preview": "#5. Your Second Luaw webapp - \"Hello `username`!\"\n\nLuaw handlers can accept and process HTTP request parameters (query p"
  },
  {
    "path": "docs/template-view.md",
    "chars": 5239,
    "preview": "#6. Luaw Template Views\n\nLuaw comes equipped with a Luaw template views for generating server-side dynamic content. Luaw"
  },
  {
    "path": "docs/third-webapp.md",
    "chars": 5229,
    "preview": "#7. Your third webapp - with Luaw template view\n\nIn this chapter we will put together all the pieces we have learned abo"
  },
  {
    "path": "docs/user-threads.md",
    "chars": 3582,
    "preview": "#11. User Threads\n\nLuaw allows developer to spawn user threads to execute multiple tasks in parallel. Luaw user threads "
  },
  {
    "path": "docs/user-timers.md",
    "chars": 1125,
    "preview": "#12. Luaw Timers\n\nLuaw supports user defined timers. Here is an example:\n\n```lua\nlocal timer = Luaw.newTimer()\ntimer:sta"
  },
  {
    "path": "lib/luapack.lua",
    "chars": 12925,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_constants.lua",
    "chars": 1387,
    "preview": "local constMT = {\n    __newindex = function(table, key, value)\n        error(\"constant \"..table.name..\" cannot be change"
  },
  {
    "path": "lib/luaw_data_structs_lib.lua",
    "chars": 3619,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_http.lua",
    "chars": 26003,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_init.lua",
    "chars": 1365,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_logging.lua",
    "chars": 8545,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_scheduler.lua",
    "chars": 8598,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_tcp.lua",
    "chars": 3249,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_timer.lua",
    "chars": 1673,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_utils.lua",
    "chars": 4314,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/luaw_webapp.lua",
    "chars": 15028,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "lib/unit_testing.lua",
    "chars": 3951,
    "preview": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "sample/conf/server.cfg",
    "chars": 493,
    "preview": "luaw_server_config = {\n    server_ip = \"0.0.0.0\",\n    server_port = 7001,\n    connect_timeout = 4000,\n    read_timeout ="
  },
  {
    "path": "sample/proxy_handler.lua",
    "chars": 2589,
    "preview": "local http_lib = require('luaw_http')\n--[[\n    Luaw allows you to replace it's default MVC/REST request handler with you"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-address.lua",
    "chars": 277,
    "preview": "registerHandler {\n    method = 'GET',\n    path = 'address/:city/#zip',\n\n    handler = function(req, resp, pathParams)\n  "
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-fileupload-display.lua",
    "chars": 861,
    "preview": "registerHandler {\n    method = 'GET',\n    path = 'showform',\n\n\thandler = function(req, resp, pathParams)\n\t    return [[\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-fileupload-process.lua",
    "chars": 784,
    "preview": "local function append(buffer, str)\n    if (str) then\n        table.insert(buffer, str)\n        table.insert(buffer, \", \""
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-hellouser.lua",
    "chars": 234,
    "preview": "registerHandler {\n    method = 'GET',\n    path = '/user/:username/#count',\n\n\thandler = function(req, resp, pathParams)\n\t"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-helloworld.lua",
    "chars": 151,
    "preview": "registerHandler {\n    method = 'GET',\n    path = 'helloworld',\n\n    handler = function(req, resp, pathParams)\n        re"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-read-lpack.lua",
    "chars": 906,
    "preview": "local utils_lib = require('luaw_utils')\nlocal http_lib = require('luaw_http')\nlocal lpack = require('luapack')\n\nregister"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-write-lpack.lua",
    "chars": 2678,
    "preview": "local utils_lib = require('luaw_utils')\nlocal http_lib = require('luaw_http')\nlocal lpack = require('luapack')\n\nlocal bi"
  },
  {
    "path": "sample/webapps/myapp/views/view-address.lua",
    "chars": 1425,
    "preview": "BEGIN 'html'\n    BEGIN 'head'\n        BEGIN 'title'\n            TEXT 'Address'\n        END 'title'\n    END 'head'\n    BE"
  },
  {
    "path": "sample/webapps/myapp/web.lua",
    "chars": 95,
    "preview": "luaw_webapp = {\n    resourcePattern = \"handler%-.*%.lua\",\n    viewPattern = \"view%-.*%.lua\",\n}\n"
  },
  {
    "path": "src/Makefile",
    "chars": 2950,
    "preview": "# Makefile for building Luaw\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\nCC?= gcc\nC"
  },
  {
    "path": "src/http_parser.c",
    "chars": 63117,
    "preview": "/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev\n *\n * Additional changes are licensed under the s"
  },
  {
    "path": "src/http_parser.h",
    "chars": 11955,
    "preview": "/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of "
  },
  {
    "path": "src/lfs.c",
    "chars": 24691,
    "preview": "/*\n** LuaFileSystem\n** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)\n**\n** File system mani"
  },
  {
    "path": "src/lfs.h",
    "chars": 410,
    "preview": "/*\n** LuaFileSystem\n** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)\n**\n** $Id: lfs.h,v 1.5"
  },
  {
    "path": "src/lua_lpack.c",
    "chars": 15598,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/lua_lpack.h",
    "chars": 1821,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_common.c",
    "chars": 5414,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_common.h",
    "chars": 4303,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_http_parser.c",
    "chars": 12950,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_http_parser.h",
    "chars": 2035,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_logging.c",
    "chars": 6891,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_logging.h",
    "chars": 1315,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_server.c",
    "chars": 10165,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_tcp.c",
    "chars": 16922,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_tcp.h",
    "chars": 3407,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_timer.c",
    "chars": 7582,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  },
  {
    "path": "src/luaw_timer.h",
    "chars": 2615,
    "preview": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of t"
  }
]

About this extraction

This page contains the full source code of the raksoras/luaw GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 62 files (349.9 KB), approximately 91.7k tokens, and a symbol index with 204 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!