[
  {
    "path": ".gitignore",
    "content": "**/*.o\n**/*.so\n**/*.log\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"deps/libuv\"]\n\tpath = deps/libuv\n\turl = https://github.com/libuv/libuv.git\n[submodule \"deps/luajit-2.0\"]\n\tpath = deps/luajit-2.0\n\turl = http://luajit.org/git/luajit-2.0.git\n[submodule \"deps/lua-PUC-Rio\"]\n\tpath = deps/lua-PUC-Rio\n\turl = https://github.com/lua/lua.git\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "# Makefile for building Luaw\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\nexport UVDIR= deps/libuv\nexport UVLIB= deps/libuv/.libs/libuv.a\n\nifeq ($(LUAVM),luajit)\n    export LUADIR= deps/luajit-2.0\n    export LUALIB= deps/luajit-2.0/src/libluajit.a\n    export OSXLDFLAGS= \"-Wl,-pagezero_size,10000 -Wl,-image_base,100000000\"\nelse\n    export LUADIR= deps/lua-PUC-Rio\n    export LUALIB= deps/lua-PUC-Rio/src/liblua.a\n    export OSXLDFLAGS=\nendif\n\n# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======\n\n# Supported platforms\nPLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris\nALL= all\n\n# Targets start here.\n\nall:\n\t@echo \"Please do 'make PLATFORM' where PLATFORM is one of these:\"\n\t@echo \"   $(PLATS)\"\n\n$(UVLIB): $(UVDIR)/Makefile\n\t$(MAKE) -C $(UVDIR)\n\n$(UVDIR)/Makefile: $(UVDIR)/configure\n\tcd $(UVDIR) && ./configure\n\n$(UVDIR)/configure: $(UVDIR)/autogen.sh\n\tcd $(UVDIR) && sh autogen.sh\n\n$(LUADIR)/src/libluajit.a:\n\t$(MAKE) -C $(LUADIR)\n\n$(LUADIR)/src/liblua.a:\n\t$(MAKE) -C $(LUADIR) $(TARGET)\n\nluaw:\n\t$(MAKE) -C src $(ALL) SYSLDFLAGS=$(SYSLDFLAGS) CC=$(CC)\n\n# Convenience targets for popular platforms\n\naix: TARGET= aix\naix: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL) CC=\"xlc\" CFLAGS=\"-O2\" SYSLIBS=\"-ldl\" SYSLDFLAGS=\"-brtl -bexpall\"\n\nansi: TARGET= ansi\nansi: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL)\n\nbsd: TARGET= bsd\nbsd: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL) SYSLIBS=\"-Wl,-E\"\n\nfreebsd: TARGET= freebsd\nfreebsd: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL) SYSLIBS=\"-Wl,-E\"\n\nlinux: TARGET= linux\nlinux: SYSLIBS= -Wl,-E -ldl\nlinux: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL) SYSLIBS=\"-lrt -Wl,-E -ldl\"\n\nmacosx: TARGET= macosx\nmacosx: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL) CC=\"cc\" SYSLDFLAGS=$(OSXLDFLAGS)\n\nposix: TARGET= posix\nposix: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL)\n\nsolaris: TARGET= solaris\nsolaris: $(UVLIB) $(LUALIB)\n\t$(MAKE) -C src $(ALL) SYSLIBS=\"-ldl\"\n\n#build objects management\n\ninstall:\n\t$(MAKE) -C src install\n\ninstall-sample:\n\t$(MAKE) -C src install-sample\n\nuninstall:\n\t$(MAKE) -C src uninstall\n\nclean: $(UVDIR)/Makefile\n\t$(MAKE) -C deps/luajit-2.0 clean\n\t$(MAKE) -C deps/lua-PUC-Rio clean\n\t$(MAKE) -C $(UVDIR) distclean\n\t$(MAKE) -C src clean\n\n# list targets that do not create files (but not all makes understand .PHONY)\n.PHONY: all check_plat $(LUALIB) $(PLATS) luaw install uninstall clean $(LUADIR)/src/libluajit.a $(LUADIR)/src/liblua.a\n\n"
  },
  {
    "path": "README.md",
    "content": "Luaw - Lua meets Node.js\n========================\n***\n\nLuaw 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 :)\n\nLuaw 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.\n\n\n##Features\n\n1. Full HTTP 1.1 support with persistent/keep-alive connections and HTTP pipelining with configurable connect and read timeouts.\n2. Two ways to read and parse incoming HTTP requests,\n    - Reading whole request in memory and then parsing it which is suitable for majority of applications.\n    - 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.\n3. Similarly on the output side it supports,\n    - Buffering entire content in memory before writing it out to client with the correct \"Content-Length\" header, or\n    - Streaming response continuously as it is generated using HTTP 1.1 chunked encoding to keep server memory pressure minimum.\n4. Fully asynchronous DNS and HTTP client for making remote HTTP calls from server\n5. [Sinatra](http://www.sinatrarb.com/) like web application framework that supports mapping URLs to REST resources with full support for path and query parameters.\n6. 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.\n7. Support for spawning user threads that can hook into Luaw's async IO machinery for calling multiple backend services from Luaw server in parallel.\n8. Support for user timers for implementing periodic cron like jobs inside Luaw server.\n9. Log4j like logging framework with configurable log levels, log file size limits and automatic log rotation that integrates with syslog out of the box.\n10. [MessagePack](http://msgpack.org/) like library to efficiently serialize/deserialize arbitrarily complex data into compact and portable binary format for remote web service calls.\n11. Built in multipart file upload support.\n\n\n##How To Build\n***\n1. 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. \n\n        sudo apt-get install make\n        sudo apt-get install autotools-dev\n        sudo apt-get install autoconf\n        sudo apt-get install build-essential libtool\n        \n\n2. Clone Luaw repository\n\n        git clone --recursive https://github.com/raksoras/luaw.git\n        \n3. Build Luaw to use standard [PUC-Rio Lua VM](http://www.lua.org/)\n\n        cd luaw\n        make linux\n        \n    or, build Luaw to use [LuaJit VM](http://luajit.org/)\n\n        cd luaw\n        make LUAVM=luajit linux\n        \n    While building on mac OS use target \"macosx\". For example\n\n        cd luaw\n        make LUAVM=luajit macosx\n        \n4. 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\n\n        make INSTALL_ROOT=~/luawsample install\n        \n    To install binaries along with the sample app provided\n\n        make INSTALL_ROOT=~/luawsample install-sample\n        \n    \n\n##Luaw directory structure\n\nIn 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`\n\n```\n~/luawsample\n |\n |\n +--- bin              Directory that holds Luaw server binary we built\n |                     along with all necessary Lua libraries\n |\n +--- conf             Directory for server configuration\n |   |\n │   +--- server.cfg   Sample server configuration file, to be used as a starting point\n |\n +--- lib              Directory to install any third party or user supplied Lua\n |                     libraries that application may depend on.\n |\n +--- logs             Directory for server logs\n |\n +--- webapps          Directory to install Luaw webapps\n```\n\n##License\n\nLuaw uses [MIT license](http://opensource.org/licenses/mit-license.html) for all parts of Luaw that are not externally\nmaintained libraries like libuv.\n\n## Documentation\n\nPlease refer to the [project wiki](https://github.com/raksoras/luaw/wiki) for documentation regarding how to build, configure, start and program using Luaw.\n\n## Getting in touch\n\nPlease post your questions/comments on Google Group [Luaw](https://groups.google.com/forum/#!forum/luaw) and follow [@raksoras](https://twitter.com/raksoras) on Twitter\n"
  },
  {
    "path": "conf/server.cfg",
    "content": "luaw_server_config = {\n    server_ip = \"0.0.0.0\",\n    server_port = 7001,\n    connect_timeout = 4000,\n    read_timeout = 8000,\n    write_timeout = 8000\n}\n\nluaw_log_config = {\n    log_dir = \"./logs\",\n    log_file_basename = \"luaw-log\",\n    log_file_size_limit = 1024*1024,\n    log_file_count_limit = 9,\n    log_filename_timestamp_format = '%Y%m%d',\n    log_lines_buffer_count = 16,\n    syslog_server = \"127.0.0.1\",\n    syslog_port = 514,\n}\n\nluaw_webapp_config = {\n    base_dir = \"./webapps\"\n}\n\n"
  },
  {
    "path": "docs/Contents.md",
    "content": "#Contents\n\n1. Introduction\n2. Getting started: building and installing Luaw\n3. Luaw configuration\n4. Your first Luaw webapp - \"Hello world!\"\n5. Your second Luaw webapp - \"Hello `<user name>`!\"\n6. Luaw template views\n7. Your third webapp -   with template views\n8. Using Luaw logging framework\n9. Using Luaw response object\n10. Using Luaw async HTTP Client\n11. Using Luaw threads and background jobs\n12. Using Luaw timers\n13. Using custom HTTP handler for HTTP stream parsing\n14. Using custom scripts on command line at Luaw start up\n"
  },
  {
    "path": "docs/Introduction.md",
    "content": "#Introduction\n\nLuaw 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 :)\n\nLuaw 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.\n\n\n##Features\n\n1. Full HTTP 1.1 support with persistent/keep-alive connections and HTTP pipelining with configurable connect and read timeouts.\n2. Two ways to read and parse incoming HTTP requests,\n    - Reading whole request in memory and then parsing it which is suitable for majority of applications.\n    - 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.\n3. Similarly on the output side it supports,\n    - Buffering entire content in memory before writing it out to client with the correct \"Content-Length\" header.\n    - Streaming response continuously as it is generated using HTTP 1.1 chunked encoding to keep server memory pressure minimum.\n4. Fully asynchronous DNS and HTTP client for making remote HTTP calls from server\n5. [Sinatra](http://www.sinatrarb.com/) like web application framework that supports mapping URLs to REST resources with full support for path and query parameters.\n6. 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.\n7. Support for spawning user threads that can hook into Luaw's async IO machinery for calling multiple backend services from Luaw in parallel.\n8. Support for user timers for implementing periodic cron like jobs inside Luaw server.\n9. Log4j like logging framework with configurable log levels, log file size limits and automatic log rotation that integrates with syslog out of the box.\n10. [MessagePack](http://msgpack.org/) like library to efficiently serialize/deserialize arbitrarily complex data into compact and portable binary format for remote web service calls.\n"
  },
  {
    "path": "docs/cmd-line.md",
    "content": "#14. Advanced Topic II - Using custom scripts on command line at start up\n\nIn 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,\n\n1. Loading any in memory caches required by application in Luaw's memory\n2. 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\n3. Set up sandboxes in which to execute certain request or resource handlers."
  },
  {
    "path": "docs/configuration.md",
    "content": "#3. Configuring Luaw\n\nBefore 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.\n\nLike 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.\n\n`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.\n\n##Server configuration\n\nHere is a sample server.cfg\n\n```lua\nluaw_server_config = {\n    server_ip = \"0.0.0.0\",\n    server_port = 7001,\n    connect_timeout = 4000,\n    read_timeout = 8000,\n    write_timeout = 8000\n}\n\nluaw_log_config = {\n    log_dir = \"/apps/luaw/sample/log\",\n    log_file_basename = \"luaw-log\",\n    log_file_size_limit = 1024*1024,\n    log_file_count_limit = 9,\n    log_filename_timestamp_format = '%Y%m%d',\n    log_lines_buffer_count = 16,\n    syslog_server = \"127.0.0.1\",\n    syslog_port = 514,\n}\n\nluaw_webapp_config = {\n    base_dir = \"/apps/luaw/sample/webapps\"\n}\n```\nluaw_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.\n\nluaw_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.\n\nFinally, 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.\n\n## webapp configuration\n\nLike 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:\n\n```\nluaw_root_dir\n |\n +--- bin\n |\n +--- conf\n |     |\n │     +--- server.cfg\n |\n +--- lib\n |\n +--- logs\n |\n +--- webapps\n       |\n       +--- myapp1\n       |     |\n       |     +---web.lua\n       |\n       +--- myapp2\n             |\n             +---web.lua\n```\n\nEach 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.\n\n##Sample web.lua:\n\n```lua\nluaw_webapp = {\n    resourcePattern = \"handler%-.*%.lua\",\n\tviews = {\n\t\t\"user/address-view.lua\",\n\t\t\"account/account-view.lua\"\n\t}\n}\n\nLuaw.logging.file {\n    name = \"root\",\n    level = Luaw.logging.ERROR,\n}\n\nLuaw.logging.file {\n    name = \"com.luaw\",\n    level = Luaw.logging.INFO,\n}\n\nLuaw.logging.syslog {\n    name = \"com.luaw\",\n    level = Luaw.logging.WARNING,\n}\n```\n\nluaw_webapp section specifies resources (request handlers) and views (templates) that make up the web application. These can be specified in two different ways:\n\n1. **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.\n2. **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.\n\nYou 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.\n\nRest 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.\n"
  },
  {
    "path": "docs/first-webapp.md",
    "content": "#4. Your First Luaw Webapp - \"Hello world!\"\n\nNow that we are familiar with Luaw's directory layout and configuration, we are ready to write our first webapp - \"Hello world\" but of course.\n\nLuaw 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.\n\n##Writing Luaw Request Handler\n\n1. 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.\n\n\t\tcd luaw_root_dir/webapps\n\t\tmkdir myapp\n\t\tcd myapp\n\n2. create a filed called web.lua in `luaw_root_dir/webapps/myapp` and put following content in it\n```lua\nluaw_webapp = {\n\t\tresourcePattern = \"handler%-.*%.lua\",\n}\n```\nThis 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.\n\n3. 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.\nNext put following code in \"handler-helloworld.lua\":\n```lua\n    GET 'helloworld' {\n        function(req, resp, pathParams)\n            return \"Hello World!\"\n        end\n    }\n```\n\n\tIn 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.\nthe 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.\n\n\tFinally, 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.\n\n    export LD_LIBRARY_PATH=/usr/local/lib/\n\n4. Now we are ready to start the server. To do this switch to `luaw_root_dir` and run Luaw server like this:\n\t\tcd luaw_root_dir\n\t\t./bin/luaw_server ./conf/server.cfg\n\n\tFirst 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:\n\n```\n******************** Starting webapp myapp *******************************\n\t.Loading resource ./webapps/myapp/handlers/handler-helloworld.lua\n\t#Loaded total 1 resources\n\n\t#Compiled total 0 views\n *********************** Webapp myapp started ****************************\nstarting server on port 7001 ...\n```\n\nNow point your browser to http://localhost:7001/myapp/helloworld and greet the brave new world!\n\nCongratulations, you have successfully written your first Luaw webapp!"
  },
  {
    "path": "docs/getting-started.md",
    "content": "#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 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.\n\n##Building Lua 5.2\nLua 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:\n\n    curl -R -O http://www.lua.org/ftp/lua-5.2.3.tar.gz\n    tar zxf lua-5.2.3.tar.gz\n    cd lua-5.2.3\n    make linux test\n    sudo make linux install\n\nTo 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,\n\n    make macosx test\n    sudo make linux install\n\nTo see what OSes are supported run ./lua-5.2.3/src/Makefile targets\n\n\n##Building libuv\nLuaw uses node.js library libuv to do asynchronous, event based IO in a portable, cross platform  manner. To build libuv:\n\n1. first clone libuv repository\n        git clone https://github.com/joyent/libuv.git\n2. 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.\n        cd libuv\n        git checkout tags/v1.0.0\n3. Build libuv. This may require you to install autotools. Detailed instructions are [here](https://github.com/joyent/libuv#build-instructions)\n        sh autogen.sh\n        ./configure\n        make\n        make check\n        sudo make install\n\n## Building Luaw\nWith all dependencies built, now we are ready to build Luaw itself.\n\n1. Clone Luaw repository\n        git clone https://github.com/raksoras/luaw.git\n2. Build Luaw\n        cd luaw/src\n        make linux\n3. Install Luaw binary - luaw_server - in directory of your choice\n\t\tmake INSTALL_ROOT=<luaw_root_dir> install\n4. Note: On Mac running Yosemite version of Mac OS you may have to run,\n\t\tmake SYSCFLAGS=-I/usr/local/include SYSLDFLAGS=-L/usr/local/lib macosx\n        make INSTALL_ROOT=<luaw_root_dir> install\n\n\n##Luaw directory structure\n\nIn 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`\n\n```\nluaw_root_dir\n |\n |\n +--- bin              Directory that holds Luaw server binary we built\n |                     along with all necessary Lua libraries\n |\n +--- conf             Directory for server configuration\n |   |\n │   +--- server.cfg   Sample server configuration file, to be used as a starting point\n |\n +--- lib              Directory to install any third party or user supplied Lua\n |                     libraries that application may depend on.\n |\n +--- logs             Directory for server logs\n |\n +--- webapps          Directory to install Luaw webapps\n```\n\nThis directory structure and its usage is further explained in the next chapter \"Configuring Luaw\"\n\n"
  },
  {
    "path": "docs/http-client.md",
    "content": "#10. Async HTTP Client\n\nLuaw 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.\n\nThe Luaw HTTP client is modeled by two objects: clientHttpRequest and clientHttpResponse. Here is a small example of Luaw's HTTP client's usage:\n\n```lua\n-- set up HTTP request\nlocal clientReq = Luaw.newClientHttpRequest()\nclientReq.hostName = \"www.google.com\"\n-- OR alternatively,\nclientReq.hostIP = \"74.125.25.106\"\nclientReq.method = 'GET'\nclientReq.url = \"/\"\n\nclientReq.headers = { Host = \"www.google.com\" }\n-- OR alternatively\nclientReq:addHeader(\"Host\", \"www.google.com\")\n\n-- execute the HTTP request and read the response back.\nlocal clientResp = clientReq:execute()\n\n-- Get the respone headers and body from the client response object returned\nlocal respBody = clientResp.body\nlocal respHeaders = clientResp.headers\n```\n\nIn 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.\n\n\n\n"
  },
  {
    "path": "docs/logging.md",
    "content": "#8. Luaw logging framework\n\nLuaw 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\nruntime properties\n\nLuaw's logging is configured at two levels\n\n##Logging configuration for the whole server\nConfiguration 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\n```lua\nluaw_log_config = {\n\tlog_dir = \"/apps/luaw/sample/log\",\n\tlog_file_basename = \"luaw-log\",\n\tlog_filename_timestamp_format = '%Y%m%d',\n\tlog_file_size_limit = 1024*1024,\n\tlog_file_count_limit = 9,\n    log_lines_buffer_count = 16,\n\tsyslog_server = \"127.0.0.1\",\n\tsyslog_port = 514\n}\n```\n\nExample above configures,\n\n1. Log directory where the files should go: /apps/luaw/sample/log\n2. Log file base name: luaw-log\n3. [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\n4. File size limit for an individual log file: 1MB\n5. Total number of log files to keep: 9\n6. How many log lines to buffer in memory before flushing them to log file: 16\n7. syslog server address: 127.0.0.1\n8. syslog server port: 514\n\nYou could omit either file system related configuration or syslog configuration but at least one must be present to use Luaw logging.\n\n\n##Logging configuration per webapp\n\nConfiguration related to individual webapps' logging is configured in `web.lua` config file of each webapp. Here are some example entries from sample \"web.lua\":\n```lua\nLuaw.logging.file {\n    name = \"root\",\n    level = Luaw.logging.ERROR\n}\n```\n\nAbove 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.\n\n```lua\nLuaw.logging.file {\n    name = \"com.myapp\",\n    level = Luaw.logging.INFO\n}\n```\n\nOverrides 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.\n\nTo actually log a line you use code like following:\n\n```lua\nlocal log = require(\"luaw_logging\")\n\nlocal logger = log.getLogger(\"com.myapp\")\nlogger.error(\"some error occurred\")\nlogger.warning(\"this is a warning\")\nlogger.info(\"some information\")\nlogger.debug(\"lowest level debug\")\n```\n\nHere are different log levels and Luaw logger functions corresponding to them in a decreasing order of severity:\n\n|Log Level  | Logging function   |\n|--------------------------------\n| EMERGENCY | logger.emergency() |\n| ALERT     | logger.alert()     |\n| CRITICAL  | logger.critical()  |\n| ERROR     | logger.error()     |\n| WARNING   | logger.warning()   |\n| NOTICE    | logger.notice()    |\n| INFO      | logger.info()      |\n| DEBUG     | logger.debug()     |\n\n\nAll 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\n\n```lua\nlogger.infof(\"User name %s, User Id %d\", name, id)\n```\n\nThis form is useful to avoid unnecessary string concatenation beforehand when the configured logger level may actually end up filtering the log line anyways.\n\nFinally, 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:\n\n```lua\nLuaw.logging.syslog {\n    name = \"com.myapp.system\",\n    level = Luaw.logging.WARNING\n}\n```\n\nsyslog sever and port to use is specified in server.cfg"
  },
  {
    "path": "docs/proxy-http.md",
    "content": "#13. custom HTTP handler for proxying request\n\nSo 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.\n\nHowever, 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.\n\nA 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.\n\n```\n+------+ ----HTTP request---> +-------------+ --- Proxy HTTP request ---> +-------+\n|Client|                      |Reverse Proxy|                             |Backend|\n+------+ <---HTTP response--- +-------------+ <-- Proxy HTTP response --- +-------+\n```\n\nBelow 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.\n\n```lua\n--[[\n    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.\n    ]]\n\n    Luaw.request_handler =  function(conn)\n        conn:startReading()\n\n        -- loop to support HTTP 1.1 persistent (keep-alive) connections\n        while true do\n            local req = Luaw.newServerHttpRequest(conn)\n            local resp = Luaw.newServerHttpResponse(conn)\n\n            -- read and parse full request\n            local eof = req:readFull()\n            if (eof) then\n                conn:close()\n                return \"connection reset by peer\"\n            end\n\n            local reqHeaders = req.headers\n            local beHost =  reqHeaders['backend-host']\n            local beURL = reqHeaders['backend-url']\n\n            if (beHost and beURL) then\n                local backendReq = Luaw.newClientHttpRequest()\n                backendReq.hostName = beHost\n                backendReq.url = beURL\n                backendReq.method = 'GET'\n                backendReq.headers = { Host = beHost }\n\n                local status, backendResp = pcall(backendReq.execute, backendReq)\n                if (status) then\n                    resp:setStatus(backendResp:getStatus())\n                    resp:appendBody(backendResp:getBody())\n                    local beHeaders = backendResp.headers\n                    for k,v in pairs(beHeaders) do\n                        if ((k ~= 'Transfer-Encoding')and(k ~= 'Content-Length')) then\n                            resp:addHeader(k,v)\n                        end\n                    end\n                    backendResp:close()\n                else\n                    resp:setStatus(500)\n                    resp:appendBody(\"connection to backend server failed\")\n                end\n            else\n                resp:setStatus(400)\n                resp:appendBody(\"Request must contain headers backend-host and backend-url\")\n            end\n\n            local status, mesg = pcall(resp.flush, resp)\n            if (not status) then\n                conn:close()\n                return error(mesg)\n            end\n\n            if (req:shouldCloseConnection() or resp:shouldCloseConnection()) then\n                conn:close()\n                return \"connection reset by peer\"\n            end\n        end\n    end\n```\n\nThe 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:\n```\n./bin/luaw_server ./conf/server.cfg\n```\nWhere `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.\n\nSo 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:\n\n    ./bin/luaw_server ./conf/server.cfg ./bin/proxy-handler.lua\n\n`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.\n\nNow test your shiny new proxy server by running following tests:\n\n## Test 1 - Missing required headers\n\n    $ curl -v http://127.0.0.1:7001/\n    *   Trying 127.0.0.1...\n    * Connected to 127.0.0.1 (127.0.0.1) port 7001 (#0)\n    > GET / HTTP/1.1\n    > User-Agent: curl/7.37.1\n    > Host: 127.0.0.1:7001\n    > Accept: */*\n    >\n    < HTTP/1.1 400 Bad Request\n    < Content-Length: 50\n    <\n    Headers proxy-host and proxy-url must be present\n\n## Test 2 - With correct headers\n    $ curl -v -H\"proxy-host: www.google.com\" -H\"proxy-url: /\" http://127.0.0.1:7001/\n    *   Trying 127.0.0.1...\n    * Connected to 127.0.0.1 (127.0.0.1) port 7001 (#0)\n    > GET / HTTP/1.1\n    > User-Agent: curl/7.37.1\n    > Host: 127.0.0.1:7001\n    > Accept: */*\n    > proxy-host: www.google.com\n    > proxy-url: /\n    >\n    < HTTP/1.1 200 OK\n    < Content-Type: text/html; charset=ISO-8859-1\n    < Transfer-Encoding: chunked\n    * Server gws is not blacklisted\n    < Server: gws\n\n    (.. followed by the body of the home page at www.google.com)\n"
  },
  {
    "path": "docs/resp-obj.md",
    "content": "#9. Response Object\n\nSo far we have seen two ways to return response body from resource handler:\n\n1. Returning a string from resource handler function which Luaw returns in turn as a whole response body to the client, and\n2. Using Luaw template views that generate response body programmatically in a fashion similar to JSP or ASP\n\nHowever, 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:\n\n```lua\nGET '/user/:username' {\n\tfunction(req, resp, pathParams)\n\t\treturn \"Hello \"..pathParams.username..\"!\"\n\tend\n}\n```\n\nFiner grained control over response generation is achieved by invoking various methods on the response object as described below.\n\n1. `resp:setStatus(status)`: You can set HTTP status code to be returned to the client - 200, 404 etc. - using this method\n\n2. `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.\n\n3. `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.\n\n3. `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.\n\n4. `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.\n\n5. `resp:close()`: Finally, call this method to actually close underlying connection to client and release all the associated resources.\n"
  },
  {
    "path": "docs/second-webapp.md",
    "content": "#5. Your Second Luaw webapp - \"Hello `username`!\"\n\nLuaw 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']`\n\nLuaw 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:\n```lua\nGET '/user/:username' {\n\tfunction(req, resp, pathParams)\n\t\treturn \"Hello \"..pathParams.username..\"!\"\n\tend\n}\n```\nHere 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']`\n\nNow 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!\n\nIn 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,\n\n```lua\nGET '/user/:username/#count' {\n\tfunction(req, resp, pathParams)\n\t\treturn \"Hello \"..pathParams.username..\"! You are user number \"..pathParams.count..\" to visit this site.\"\n\tend\n}\n```\n\nand then point your browser to \"http://localhost:7001/myapp/user/raksoras/9\" to get\n```\nHello raksoras! You are user number 9 to visit this site.\n```\nin your browser window."
  },
  {
    "path": "docs/template-view.md",
    "content": "#6. Luaw Template Views\n\nLuaw 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.\n\nIn 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!\n\nWithout further ado, here is a sample Luaw template view file - view-address.lua:\n```lua\nBEGIN 'html'\n    BEGIN 'head'\n        BEGIN 'title'\n            TEXT 'Address'\n        END 'title'\n    END 'head'\n    BEGIN 'body'\n        BEGIN 'div' {class='address'}\n            BEGIN 'h1'\n                TEXT(model.title)\n            END 'h1'\n            BEGIN 'table' {border=\"1\", margin=\"1px\"}\n                BEGIN 'tr'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT 'City'\n                    END 'td'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT(model.city)\n                    END 'td'\n                END 'tr'\n                if (model.zip == 94086) then\n                    BEGIN 'tr'\n                        BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                            TEXT 'County'\n                        END 'td'\n                        BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                            TEXT 'Santa Clara'\n                        END 'td'\n                    END 'tr'\n                end\n                BEGIN 'tr'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT 'Zip'\n                    END 'td'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT(model.zip)\n                    END 'td'\n                END 'tr'\n            END 'table'\n        END 'div'\n    END 'body'\nEND 'html'\n```\n\nEach Luaw template view has access to following implicitly defined variables: `req`, `resp`,\n`pathParams` and `model` passed from resource handler that invoked the view\n\nIt also defines three syntax sugar extentions to generate markeup `BEGIN`, `TEXT` and `END`.\n\n1. You use `BEGIN tag_name` to open any HTML or XML tag of type tag_name\n2. If the tag has any attributes you follow the `BEGIN tag_name`  with set of attributes defined like this `{name1=value1, name2=value2 ...}`\n3. You close opne tag with `END tag_name`\n4. 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.\n\nThat'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.\n\nIn the next chapter we will see how to use Luaw template views with resource handlers.\n\n**_NOTE:_**\n\nYou 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:\n\n```lua\n-- Reusable markup generating function AKA tag library\nfunction generateHeader(model, BEGIN, TEXT, END)\n\tBEGIN 'div' {class='header')\n    \t--- generate HTML here using BEGIN, TEXT and END\n    END 'div'\nend\n```\n\n```lua\n-- Using common markup generating function from Luaw template view\nlocal ssi = require(\"common_markup.lua\")\n\n-- Just call function to include the markup at write place\nssi.generateHeader(model, BEGIN, TEXT, END)\n\nBEGIN 'div' {class=\"body\"}\n\t--- generate page specific page markup here\nEND 'div'\n\nssi.generateFooter(model, BEGIN, TEXT, END)\n```"
  },
  {
    "path": "docs/third-webapp.md",
    "content": "#7. Your third webapp - with Luaw template view\n\nIn 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.\n\n##Luaw template view\nCreate 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:\n\n```lua\nBEGIN 'html'\n    BEGIN 'head'\n        BEGIN 'title'\n            TEXT 'Address'\n        END 'title'\n    END 'head'\n    BEGIN 'body'\n        BEGIN 'div' {class='address'}\n            BEGIN 'h1'\n                TEXT(model.title)\n            END 'h1'\n            BEGIN 'table' {border=\"1\", margin=\"1px\"}\n                BEGIN 'tr'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT 'City'\n                    END 'td'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT(model.city)\n                    END 'td'\n                END 'tr'\n                if (model.zip == 94086) then\n                    BEGIN 'tr'\n                        BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                            TEXT 'County'\n                        END 'td'\n                        BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                            TEXT 'Santa Clara'\n                        END 'td'\n                    END 'tr'\n                end\n                BEGIN 'tr'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT 'Zip'\n                    END 'td'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT(model.zip)\n                    END 'td'\n                END 'tr'\n            END 'table'\n        END 'div'\n    END 'body'\nEND 'html'\n```\n\n##REST resource handler\nAdd a file `handler-address.lua` to `luaw_root_dir/webapps/myapp/handlers` containing following code:\n```lua\n    GET 'address/:city/#zip' {\n        function(req, resp, pathParams)\n            address = {\n                city = pathParams.city,\n                zip = pathParams.zip\n            }\n            return '/views/view-address.lua', address\n        end\n    }\n```\n\nThis 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 '#')\n\nMost interesting line in the handler above is the following return statement from the function handler:\n\n\t\treturn '/views/view-address.lua', address\n\nSo 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`\n\n##Modified web.lua\nModify *<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\n\n    luaw_webapp = {\n        resourcePattern = \"handler%-.*%.lua\",\n        viewPattern = \"view%-.*%.lua\",\n    }\n\n##Test your work\nFinally, restart your luaw server by running\n\n    cd luaw_root_dir\n    ./bin/luaw_server ./conf/server.cfg\n\nYou should see console output similar to this:\n\n```\n********************* Starting webapp myapp ****************************\n  .Loading resource ./webapps/myapp/handlers/handler-address.lua\n  .Loading resource ./webapps/myapp/handlers/handler-hellouser.lua\n  .Loading resource ./webapps/myapp/handlers/handler-helloworld.lua\n  #Loaded total 3 resources\n\n  .Loading view /views/view-address.lua\n  #Compiled total 1 views\n *********** ********* Webapp myapp started ****************************\n```\n\nNote the \"loading view\" part.\n\nNow point your browser to http://127.0.0.1:7001/myapp/address/Sunnyvale/94085 and see the output in your browser.\n\nTo 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.\n"
  },
  {
    "path": "docs/user-threads.md",
    "content": "#11. User Threads\n\nLuaw 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.\n\nHere 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\n\n```lua\nlocal function parallelHttpRequest(host, url)\n    local clientReq = Luaw.newClientHttpRequest()\n    clientReq.hostName = host\n    clientReq.method = 'GET'\n    clientReq.url = url\n    clientReq:addHeader(\"Host\", host)\n    local clientResp = clientReq:execute()\n    return clientResp\nend\n\nlocal scheduler = Luaw.scheduler\n-- do two HTTP request in parallel\nlocal threadCtx1 = scheduler.startUserThread(parallelHttpRequest, \"www.google.com\", \"/\")\nlocal threadCtx2 = scheduler.startUserThread(parallelHttpRequest, \"www.facebook.com\", \"/\")\n\n-- wait on both threads to be donw\nscheduler.join(threadCtx1, threadCtx2)\n\n-- Retrieve the responses received\nlocal clientResp1 = threadCtx1.result\nlocal clientResp2 = threadCtx2.result\n```\n\n1. 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.\n\n2. 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.\n\n3. 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.\n\n4. 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.\n"
  },
  {
    "path": "docs/user-timers.md",
    "content": "#12. Luaw Timers\n\nLuaw supports user defined timers. Here is an example:\n\n```lua\nlocal timer = Luaw.newTimer()\ntimer:start(1000)\ndoSomeStuff()\nprint(\"waiting till it's time...\")\ntimer:wait()\nprint('done waiting!')\n-- call timer: delete() to free timer resources immediately. If not delete() is not called\n-- timer resources hang around till Lua's VM garbage collects them\ntimer:delete()\n```\n\n1. You create a new timer using Luaw.newTimer()\n2. You start it with some timeout - specified in milliseconds using timer:start(timeout)\n3.  and finally you wait on it using timer:wait()\n\nThat's basically it!\n\nThere 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:\n\n```lua\nlocal timer = Luaw.newTimer()\ntimer:sleep(1000)\nprint('done waiting!')\n```\n\nLuaw 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."
  },
  {
    "path": "lib/luapack.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal luaw_constants = require(\"luaw_constants\")\n\nlocal lpackMT = getmetatable(luaw_lpack_lib.newLPackParser())\n\nlpackMT.INT_RANGES = {\n    lpackMT.UINT_8,\n    lpackMT.UINT_16,\n    lpackMT.UINT_32,\n    lpackMT.INT_8,\n    lpackMT.INT_16,\n    lpackMT.INT_32,\n    lpackMT.INT_64,\n    lpackMT.FLOAT,\n    lpackMT.DOUBLE\n}\n\nlpackMT.FLOAT_RANGES = {\n    lpackMT.FLOAT,\n    lpackMT.DOUBLE\n}\n\nlpackMT.STRING_RANGES = {\n    lpackMT.STRING,\n    lpackMT.BIG_STRING,\n    lpackMT.HUGE_STRING\n}\n\nlpackMT.DICT_ENTRY_RANGES = {\n    lpackMT.DICT_ENTRY,\n    lpackMT.BIG_DICT_ENTRY\n}\n\nlocal function findMinRange(num, ranges)\n    for i, range in ipairs(ranges) do\n        if ((num >= range[4])and(num <= range[5])) then\n            return range\n        end\n    end\n    error(\"Number \"..num..\" outside supported max range\")\nend\n\n-- read functions\n\nlocal function readNextBuffer(lpack)\n    if not lpack.EOF then\n        local newBuffer = lpack:readFn()\n        if not newBuffer then\n            lpack.EOF = true\n        else\n            local buffer = lpack.buffer\n            local offset = lpack.offset\n            if ((buffer)and(#buffer > offset)) then\n                lpack.buffer = string.sub(buffer, offset)..newBuffer\n            else\n                lpack.buffer = newBuffer\n            end\n            lpack.offset = 0\n        end\n    end\nend\n\nlocal function done(lpack)\n    return ((lpack.EOF)and(lpack.offset >= #lpack.buffer))\nend\n\nlocal function readNumber(lpack, numType)\n    while (not lpack:done()) do\n        local offset = lpack.offset\n        local readLen, value = lpack.read_number(numType, lpack.buffer, offset)\n        if (readLen < 0) then\n            error(\"Error while reading number at byte# \"..tostring(offset)..\" in buffer: \"..tostring(lpack.buffer))\n        end\n        if (readLen > 0) then\n            lpack.offset = offset + readLen\n            return value\n        end\n        readNextBuffer(lpack);\n    end\nend\n\nlocal function readMarker(lpack)\n    return readNumber(lpack, lpack.TYPE_MARKER[2])\nend\n\nlocal function readString(lpack, desiredLen)\n    local accm\n    while ((desiredLen > 0)and(not lpack:done())) do\n        local offset = lpack.offset\n        local buffer = lpack.buffer\n        local readLen, value = lpack.read_string(desiredLen, buffer, offset)\n        if (readLen > 0) then\n            lpack.offset = offset + readLen\n            desiredLen = desiredLen - readLen\n\n            if (desiredLen == 0) then\n                if accm then\n                    table.insert(accm, value)\n                    return table.concat(accm)\n                end\n                return value\n            end\n\n            if not accm then accm = {} end\n            table.insert(accm, value)\n        end\n        readNextBuffer(lpack);\n    end\nend\n\nlocal function deserialize(lpack, container, isMap)\n    local key, val, len, t\n    local isKey = true\n    local dictionary = lpack.dictionary\n\n    while not lpack:done() do\n        t = readMarker(lpack)\n\n        if t == lpack.NIL[2] then\n            val = nil\n\n        elseif t == lpack.BOOL_TRUE[2] then\n            val = true\n\n        elseif t == lpack.BOOL_FALSE[2] then\n            val = false\n\n        elseif t == lpack.STRING[2] then\n            len = readNumber(lpack, lpack.UINT_8[2])\n            val = readString(lpack, len)\n\n        elseif t == lpack.BIG_STRING[2] then\n            len = readNumber(lpack, lpack.UINT_16[2])\n            val = readString(lpack, len)\n\n        elseif t == lpack.HUGE_STRING[2] then\n            len = readNumber(lpack, lpack.UINT_32[2])\n            val = readString(lpack, len)\n\n        elseif t == lpack.MAP_START[2] then\n            val = deserialize(lpack, luaw_lpack_lib.createDict(0, 16), true)\n\n        elseif t == lpack.ARRAY_START[2] then\n            val = deserialize(lpack, luaw_lpack_lib.createDict(16, 0), false)\n\n        elseif t == lpack.RECORD_END[2] then\n            if ((isMap)and(not isKey)) then\n                error(\"Unbalanced table, key without corresponding value found\")\n            end\n            return container\n\n        elseif t == lpack.DICT_ENTRY[2] then\n            local dw = readNumber(lpack, lpack.UINT_8[2])\n            assert(dictionary, \"Missing dictionary\")\n            val = assert(dictionary[dw], \"Entry missing in dictionary: \"..dw)\n\n        elseif t == lpack.BIG_DICT_ENTRY[2] then\n            local dw = readNumber(lpack, lpack.UINT_16[2])\n            assert(dictionary, \"Missing dictionary\")\n            val = assert(dictionary[dw], \"Entry missing in dictionary\")\n\n        elseif t == lpack.DICT_START[2] then\n            dictionary = deserialize(lpack, luaw_lpack_lib.createDict(64, 0), false)\n            lpack.dictionary = dictionary\n            debugDump(dictionary)\n\n        else\n            -- everything else is a number\n            val = readNumber(lpack, t)\n        end\n\n        if container then\n            if isMap then\n                if isKey then\n                    key = val\n                    isKey = false\n                else\n                    container[key] = val\n                    isKey = true\n                end\n            else\n                -- is array\n                table.insert(container, val)\n            end\n        else\n            if (t ~= lpack.DICT_START[2]) then\n                -- single, standalone value\n                return val\n            end\n        end\n    end\n\n    return val\nend\n\nlocal function read(lpack)\n    readNextBuffer(lpack)\n    val = deserialize(lpack, nil, false)\n    return val\nend\n\nlocal function newLPackReader()\n    local lpackReader = luaw_lpack_lib.newLPackParser();\n    lpackReader.EOF = false\n    lpackReader.buffer = ''\n    lpackReader.offset = 0\n    lpackReader.done = done\n    lpackReader.read = read\n    return lpackReader\nend\n\nlocal function newLPackStringReader(str)\n    assert(str, \"String cannot be null\")\n    local lpackReader = newLPackReader()\n    local eof = false\n    lpackReader.readFn = function()\n        if (not eof) then\n            eof = true\n            return str\n        end\n    end\n    return lpackReader\nend\n\nlocal function newLPackFileReader(file)\n    assert(file, \"File cannot be null\")\n    local lpackReader = newLPackReader()\n    lpackReader.readFn = function()\n        return file:read(1024)\n    end\n    return lpackReader\nend\n\nlocal function newLPackReqReader(req)\n    assert(req, \"Request cannot be null\")\n    local lpackReader = newLPackReader()\n    lpackReader.readFn = function()\n        if ((not req.EOF)and(not req.luaw_mesg_done)) then\n            req:readAndParse()\n            local str =  req:consumeBodyChunkParsed()\n            if (not str) then\n                debugDump(req)\n            end\n            return str\n        end\n    end\n    return lpackReader\nend\n\n-- Write functions\n\nlocal function flush(lpack)\n    local writeQ = lpack.writeQ\n    local count = #writeQ\n    if count then\n        local str = lpack.serialize_write_Q(writeQ, lpack.writeQsize)\n        if str then\n            lpack:writeFn(str)\n            lpack.writeQsize = 0\n            for i=1,count do\n                writeQ[i] = nil\n            end\n        end\n    end\nend\n\nlocal function qstore(lpack, val, size)\n    if not val then\n        error(\"nil string passed to write(), use writeNil() instead\")\n    end\n\n    local writeQ = lpack.writeQ\n    table.insert(writeQ, val);\n    lpack.writeQsize = lpack.writeQsize + size;\n\n    if (lpack.writeQsize >= lpack.flushLimit) then\n        flush(lpack)\n    end\nend\n\nlocal function writeMarker(lpack, marker)\n    if ((marker[2] < lpack.TYPE_MARKER[2])or(marker[2] > lpack.HUGE_STRING[2])) then\n        error(\"Invalid marker \"..marker..\" specified\")\n    end\n    qstore(lpack, marker[2], 1)\nend\n\nlocal function startMap(lpack)\n    writeMarker(lpack, lpack.MAP_START)\nend\n\nlocal function startArray(lpack)\n    writeMarker(lpack, lpack.ARRAY_START)\nend\n\nlocal function startDict(lpack)\n    writeMarker(lpack, lpack.DICT_START)\nend\n\nlocal function endCollection(lpack)\n    writeMarker(lpack, lpack.RECORD_END)\nend\n\nlocal function writeBoolean(lpack, value)\n    if (value) then\n        writeMarker(lpack, lpack.BOOL_TRUE)\n    else\n        writeMarker(lpack, lpack.BOOL_FALSE)\n    end\nend\n\nlocal function writeNil(lpack)\n    writeMarker(lpack, lpack.NIL)\nend\n\nlocal function writeNumber(lpack, num)\n    local range\n\n    if (num % 1 == 0) then\n        -- integer\n        range = findMinRange(num, lpack.INT_RANGES)\n    else\n        -- float\n        range = findMinRange(num, lpack.FLOAT_RANGES)\n    end\n    qstore(lpack, range[2], 1);\n    qstore(lpack, num, range[3]);\nend\n\nlocal function writeString(lpack, str)\n    local dw, len, range\n    local dict = lpack.dictionary\n    if dict then\n        dw = dict[str]\n    end\n\n    if dw then\n        range = findMinRange(dw, lpack.DICT_ENTRY_RANGES)\n        str = dw\n        len = range[3]\n    else\n        len = #str\n        range = findMinRange(len, lpack.STRING_RANGES)\n    end\n\n    qstore(lpack, range[2], 1) -- marker\n    qstore(lpack, str, len) -- actual string value/dictionary entry\nend\n\nlocal function serialize(lpack, val)\n    local t = type(val)\n\n    if t == 'nil' then\n        writeNil(lpack)\n        return;\n    end\n\n    if t == 'boolean' then\n        writeBoolean(lpack, val)\n        return\n    end\n\n    if t == 'number' then\n        writeNumber(lpack, val)\n        return\n    end\n\n    if t == 'string' then\n        writeString(lpack, val)\n        return\n    end\n\n    if t == 'table' then\n        if (#val > 0) then\n            writeMarker(lpack, lpack.ARRAY_START)\n            for i, v in ipairs(val) do\n                serialize(lpack, v)\n            end\n            endCollection(lpack)\n        else\n            writeMarker(lpack, lpack.MAP_START)\n            for k, v in pairs(val) do\n                serialize(lpack, k)\n                serialize(lpack, v)\n            end\n            endCollection(lpack)\n        end\n    end\nend\n\nlocal function write(lpack, val)\n    serialize(lpack, val)\n    flush(lpack)\nend\n\nlocal function setDictionaryForWrite(lpack, dict)\n    assert((type(dict) == 'table'), \"Please provide valid dictionary table\")\n    local dictionary = luaw_lpack_lib.createDict(#dict, 0)\n    writeMarker(lpack, lpack.DICT_START)\n    for i, dw in ipairs(dict) do\n        writeString(lpack, dw)\n        dictionary[dw] = i\n    end\n    writeMarker(lpack, lpack.RECORD_END)\n    lpack.dictionary = dictionary\nend\n\nlocal function newLPackWriter(limit)\n    local lpackWriter = luaw_lpack_lib.newLPackParser()\n    lpackWriter.writeQ = {}\n    lpackWriter.writeQsize = 0\n    lpackWriter.flushLimit = limit or luaw_constants.CONN_BUFFER_SIZE\n    lpackWriter.useDictionary = setDictionaryForWrite\n    lpackWriter.write = write\n    return lpackWriter\nend\n\nlocal function newLPackFileWriter(file, limit)\n    assert(file, \"File can not be nil\")\n    local lpackWriter = newLPackWriter(limit)\n    lpackWriter.writeFn = function(lpack, str)\n        file:write(str)\n    end\n    return lpackWriter\nend\n\nlocal function newLPackBufferWriter(buff, limit)\n    assert(buff, \"buffer can not be nil\")\n    local lpackWriter = newLPackWriter(limit)\n    lpackWriter.writeFn = function(lpack, str)\n        table.insert(buff, str)\n    end\n    return lpackWriter\nend\n\nlocal function newLPackRespWriter(resp, limit)\n    assert(resp, \"response can not be nil\")\n    local lpackWriter = newLPackWriter(limit)\n    resp.headers['Content-Type'] = 'application/luapack'\n    resp:startStreaming()\n    lpackWriter.writeFn = function(lpack, str)\n        resp:appendBody(str)\n    end\n    return lpackWriter\nend\n\nluaw_lpack_lib.newLPackFileReader = newLPackFileReader\nluaw_lpack_lib.newLPackStringReader = newLPackStringReader\nluaw_lpack_lib.newLPackReqReader = newLPackReqReader\nluaw_lpack_lib.newLPackFileWriter = newLPackFileWriter\nluaw_lpack_lib.newLPackBufferWriter = newLPackBufferWriter\nluaw_lpack_lib.newLPackRespWriter = newLPackRespWriter\n\nreturn luaw_lpack_lib\n\n\n"
  },
  {
    "path": "lib/luaw_constants.lua",
    "content": "local constMT = {\n    __newindex = function(table, key, value)\n        error(\"constant \"..table.name..\" cannot be changed\")\n    end,\n\n    __tostring = function(table)\n        return table.name\n    end,\n\n    __concat = function(op1, op2)\n        return tostring(op1)..tostring(op2)\n    end,\n\n    __metatable = \"Luaw constant\"\n}\n\nlocal function luaw_constant(value)\n    local c = {name = value}\n    setmetatable(c, constMT)\n    return c\nend\n\nreturn {\n    -- scheduler constants\n    TS_RUNNABLE = luaw_constant(\"RUNNABLE\"),\n    TS_DONE = luaw_constant(\"DONE\"),\n    TS_BLOCKED_EVENT = luaw_constant(\"BLOCKED_ON_EVENT\"),\n    TS_BLOCKED_THREAD = luaw_constant(\"BLOCKED_ON_THREAD\"),\n    END_OF_CALL = luaw_constant(\"END_OF_CALL\"),\n    END_OF_THREAD = luaw_constant(\"END_OF_THREAD\"),\n\n    -- TCP constants\n    DEFAULT_CONNECT_TIMEOUT = luaw_server_config.connect_timeout or 8000,\n    DEFAULT_READ_TIMEOUT = luaw_server_config.read_timeout or 3000,\n    DEFAULT_WRITE_TIMEOUT = luaw_server_config.write_timeout or 3000,\n    CONN_BUFFER_SIZE = luaw_server_config.connection_buffer_size or 4096,\n\n    -- HTTP parser constants\n    EOF = 0,\n    CRLF = '\\r\\n',\n    MULTIPART_BEGIN = luaw_constant(\"MULTIPART_BEGIN\"),\n    PART_BEGIN = luaw_constant(\"PART_BEGIN\"),\n    PART_DATA = luaw_constant(\"PART_DATA\"),\n    PART_END = luaw_constant(\"PART_END\"),\n    MULTIPART_END = luaw_constant(\"MULTIPART_END\")\n\n}"
  },
  {
    "path": "lib/luaw_data_structs_lib.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal module = {}\n\n--\n-- Registry\n---\n\n-- reg[0] stores head of the free list\nlocal function ref(reg, obj)\n    if not obj then return -1 end\n\n    local ref = reg[0];\n    if ref then\n        reg[0] = reg[ref]\n    else\n        ref = #reg + 1\n    end\n\n    reg[ref] = obj\n    reg.size = reg.size + 1\n    return ref\nend\n\nlocal function unref(reg, ref)\n    if ref >= 0 then\n        reg[ref] = reg[0]\n        reg[0] = ref\n        reg.size = reg.size -1\n    end\nend\n\nfunction module.newRegistry(size)\n    local reg = {}\n    reg.ref = ref;\n    reg.unref = unref;\n    reg.size = 0\n    return reg;\nend\n\n--\n-- Ring buffers\n--\n\nlocal function offer(rb, obj)\n    local size = rb.size\n    local filled = rb.filled\n    local writer = rb.writer\n\n    if (filled < size) then\n        rb[writer] = obj\n        rb.filled = filled + 1\n        if (writer == size) then\n            rb.writer = 1\n        else\n            rb.writer = writer + 1\n        end\n        return true;\n    end\n    return false;\nend\n\nlocal function offerWithWait(rb, obj)\n    local added = offer(rb, obj)\n    while not added do\n        coroutine.yield()\n        added = offer(rb, obj)\n    end\n    return added\nend\n\nlocal function take(rb)\n    local size = rb.size\n    local filled = rb.filled\n    local reader = rb.reader\n    local obj = nil\n\n    if (filled > 0) then\n        obj = rb[reader];\n        rb[reader] = nil;\n        rb.filled = filled - 1\n        if (reader == size) then\n            rb.reader = 1\n        else\n            rb.reader = reader + 1\n        end\n    end\n    return obj\nend\n\nlocal function takeWithWait(rb)\n    local obj = take(rb)\n    while not obj do\n        coroutine.yield()\n        obj = take(rb)\n    end\n    return obj\nend\n\nlocal function offerWithOverwrite(rb, obj)\n    local added = offer(rb, obj)\n    if added then return true end\n\n    -- overwrite oldest item\n    local overwrittenObj = take(rb)\n    offer(rb, obj)\n    return false, overwrittenObj\nend\n\n\nfunction module.newRingBuffer(size)\n    local rb = {}\n    rb.reader = 1\n    rb.writer = 1\n    rb.filled = 0\n    rb.size = size\n    rb.offer = offer\n    rb.take = take\n    return rb\nend\n\nfunction module.newOverwrittingRingBuffer(size)\n    local rb = {}\n    rb.reader = 1\n    rb.writer = 1\n    rb.filled = 0\n    rb.size = size\n    rb.offer = offerWithOverwrite\n    rb.take = take\n    return rb\nend\n\nfunction module.newBlockingRingBuffer(size)\n    local rb = {}\n    rb.reader = 1\n    rb.writer = 1\n    rb.filled = 0\n    rb.size = size\n    rb.offer = offerWithWait\n    rb.take = takeWithWait\n    return rb\nend\n\nreturn module\n"
  },
  {
    "path": "lib/luaw_http.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal constants = require('luaw_constants')\nlocal luaw_tcp_lib = require('luaw_tcp')\n\nlocal TS_BLOCKED_EVENT = constants.TS_BLOCKED_EVENT\nlocal TS_RUNNABLE = constants.TS_RUNNABLE\n\nlocal CONN_BUFFER_SIZE = constants.CONN_BUFFER_SIZE\n\nlocal EOF = constants.EOF\nlocal CRLF = constants.CRLF\n\nlocal MULTIPART_BEGIN = constants.MULTIPART_BEGIN\nlocal PART_BEGIN = constants.PART_BEGIN\nlocal PART_DATA = constants.PART_DATA\nlocal PART_END = constants.PART_END\nlocal MULTIPART_END = constants.MULTIPART_END\n\nlocal http_status_codes = {\n    [100] = \"Continue\",\n    [101] = \"Switching Protocols\",\n    [200] = \"OK\",\n    [201] = \"Created\",\n    [202] = \"Accepted\",\n    [203] = \"Non-Authoritative Information\",\n    [204] = \"No Content\",\n    [205] = \"Reset Content\",\n    [206] = \"Partial Content\",\n    [300] = \"Multiple Choices\",\n    [301] = \"Moved Permanently\",\n    [302] = \"Found\",\n    [303] = \"See Other\",\n    [304] = \"Not Modified\",\n    [305] = \"Use Proxy\",\n    [307] = \"Temporary Redirect\",\n    [400] = \"Bad Request\",\n    [401] = \"Unauthorized\",\n    [402] = \"Payment Required\",\n    [403] = \"Forbidden\",\n    [404] = \"Not Found\",\n    [405] = \"Method Not Allowed\",\n    [406] = \"Not Acceptable\",\n    [407] = \"Proxy Authentication Required\",\n    [408] = \"Request Timeout\",\n    [409] = \"Conflict\",\n    [410] = \"Gone\",\n    [411] = \"Length Required\",\n    [412] = \"Precondition Failed\",\n    [413] = \"Request Entity Too Large\",\n    [414] = \"Request-URI Too Long\",\n    [416] = \"Requested Range Not Satisfiable\",\n    [417] = \"Expectation Failed\",\n    [500] = \"Internal Server Error\",\n    [501] = \"Not Implemented\",\n    [502] = \"Bad Gateway\",\n    [503] = \"Service Unavailable\",\n    [504] = \"Gateway Timeout\",\n    [505] = \"HTTP Version Not Supported\"\n}\n\nsetmetatable(http_status_codes, {\n    __index = function(status)\n        return \"User Defined Status\"\n    end\n})\n\n-- write buffer implementation\n\nlocal function clearArrayPart(t)\n    local len = #t\n    for i=1,len do\n        t[i] = nil\n    end\nend\n\nlocal function reset(buffer)\n    clearArrayPart(buffer)\n    buffer.len = 0\nend\n\nlocal function concat(buffer)\n    return table.concat(buffer)\nend\n\nlocal function append(buffer, str)\n    local len = buffer.len\n    if (str) then\n        table.insert(buffer, str)\n        len = len + #str\n        buffer.len = len\n    end\n    return len\nend\n\nlocal function newBuffer()\n    return {\n        len = 0,\n        reset = reset,\n        concat = concat,\n        append = append\n    }\nend\n\n\nfunction luaw_http_lib.storeHttpParam(params, name , value)\n\toldValue = params[name]\n    if (oldValue) then\n\t\t-- handle multi-valued param names\n        if (type(oldValue) == 'table') then\n        \ttable.insert(oldValue, value)\n        else\n        \t-- single param value already stored against the same param name\n\t\t\t-- convert it to table and store multiple values in it\n\t\t\tparams[name] = {oldValue, value}\n\t\tend\n\telse\n\t\tparams[name] = value\n\tend\nend\n\nlocal parserMT = getmetatable(luaw_http_lib.newHttpRequestParser())\n\n--[[ HTTP parser we use can invoke callback for the same HTTP field (status, URL, header\nname/value etc.) multiple times, each time passing only few characters for the current\nongoing field. This can happen because we are reading HTTP request or response body in\nmultiple chuncks - either of a fixed byte buffer size of by new lines. For this reason we\n\"accumulate\" http header name and value in a hidden request table fields (_acc_header_name_\n/_acc_header_value_) and then store full header value against full header name when the\nparser issues callback for a next HTTP field. Other fields like URL, status etc. are\naccumulated by concatenating them \"in place\".\n]]\n\n\nlocal function accumulateChunkedValue(req, name, chunk)\n\tlocal accValue = rawget(req, name)\n\tif accValue then\n\t\trawset(req, name, accValue .. chunk)\n\telse\n\t\trawset(req, name, chunk)\n\tend\nend\n\nlocal function addHeader(req, hName, hValue)\n\tif (hName and hValue) then\n\t\tlocal headers = req.headers\n\t\tlocal currValues = headers[hName]\n\n        if currValues then\n            -- handle multi-valued headers\n            if (type(currValues) == 'table') then\n                table.insert(currValues, hValue)\n            else\n                -- single string header value already stored against the same header name\n                -- convert it to table and store multiple values in it\n                headers[hName] = {currValues, hValue}\n            end\n        else\n            headers[hName] = hValue\n        end\n        return true\n\tend\n\treturn false\nend\n\nlocal function handleAccHttpHeader(req)\n\tlocal hName = rawget(req, '_acc_header_name_')\n\tlocal hValue = rawget(req, '_acc_header_value_')\n\tlocal added = addHeader(req, hName, hValue)\n\tif (added) then\n        rawset(req, '_acc_header_name_', nil)\n        rawset(req, '_acc_header_value_', nil)\n\tend\nend\n\nlocal function handleKeepAlive(req, keepAlive)\n    if not keepAlive then\n        req.headers['Connection'] = 'close'\n        req.EOF = true\n    end\nend\n\nlocal function onNone(req, cbType)\nend\n\nlocal function onMesgBegin(req, cbtype, remaining)\n    req:reset()\nend\n\nlocal function onStatus(req, cbtype, remaining ,status)\n\taccumulateChunkedValue(req, 'statusMesg', status)\nend\n\nlocal function onURL(req, cbtype, remaining, url)\n\taccumulateChunkedValue(req, 'url', url)\nend\n\nlocal function onHeaderName(req, cbtype, remaining, hName)\n\thandleAccHttpHeader(req)\n    accumulateChunkedValue(req, '_acc_header_name_', hName)\nend\n\nlocal function onHeaderValue(req, cbtype, remaining, hValue)\n\tif not hValue then hValue = '' end -- empty header value\n\taccumulateChunkedValue(req, '_acc_header_value_', hValue)\nend\n\nlocal function onHeadersComplete(req, cbtype, remaining, keepAlive, httpMajor, httpMinor, method, status)\n\thandleAccHttpHeader(req)\n\thandleKeepAlive(req, keepAlive)\n\treq.major_version = httpMajor\n\treq.minor_version = httpMinor\n    req.method = method\n    req.status = status\n\n    -- parse URL\n    local url = req.url\n    local parsedURL\n    if url then\n        local method = req.method\n        parsedURL = luaw_http_lib.parseURL(url, ((method) and (string.upper(method) == \"CONNECT\")))\n    else\n        parsedURL = {}\n    end\n    req.parsedURL = parsedURL\n\n    -- GET query params\n    local params = {}\n    local queryString = parsedURL.queryString\n    if queryString then\n        assert(luaw_http_lib:urlDecode(queryString, params))\n    end\n    req.params = params\n\n    req.luaw_headers_done = true\nend\n\nlocal function onBody(req, cbtype, remaining, chunk)\n    req.bodyParts:append(chunk)\nend\n\nlocal function onMesgComplete(req, cbtype, remaining, keepAlive)\n\t-- for the rare boundary case of chunked transfer encoding, where headers may continue\n\t-- after the last body chunk\n\thandleAccHttpHeader(req)\n\thandleKeepAlive(req, keepAlive)\n\tlocal luaw_parser = req.luaw_parser\n    if (luaw_parser) then\n        luaw_parser:initHttpParser()\n    end\n\n    -- store body\n    local bodyParts = req.bodyParts\n    req.body = bodyParts:concat()\n    bodyParts:reset()\n\n    -- POST form params\n    local params = req.params\n    local contentType = req.headers['Content-Type']\n    if ((contentType) and (contentType:lower() == 'application/x-www-form-urlencoded')) then\n        assert(luaw_http_lib:urlDecode(req.body, params))\n    end\n\n    req.luaw_mesg_done = true\nend\n\n-- Order is important and must match C enum http_parser_cb_type\nlocal http_callbacks_lua = {\n    onNone,\n    onMesgBegin,\n    onStatus,\n    onURL,\n    onHeaderName,\n    onHeaderValue,\n    onHeadersComplete,\n    onBody,\n    onMesgComplete\n}\n\nlocal function parseHttpFragment(req, conn, parser, content, offset)\n    -- matched against most number of return results possible. Actual variable names\n    -- are meaningless without the context of correct callback, misleading even!\n    local cbtype, offset, keepAlive, httpMajor, httpMinor, method, status = parser:parseHttp(content, offset)\n    if (not cbtype) then\n        conn:close()\n        return error(offset) -- offset carries error message in this case\n    end\n\n    local callback = http_callbacks_lua[cbtype]\n    if (not callback) then\n        conn:close()\n        return error(\"Invalid HTTP parser callback# \"..tostring(cbtype)..\" requested\")\n    end\n\n    callback(req, cbtype, remaining, keepAlive, httpMajor, httpMinor, method, status)\n    return  cbtype, offset\nend\n\nlocal function hasContent(content, offset)\n    return (content)and(offset)and(offset < #content)\nend\n\nlocal function readAndParse(req)\n    local conn = req.luaw_conn\n    local parser = req.luaw_parser\n    local content = req.luaw_read_content\n    local offset = req.luaw_read_offset\n    local httpcb, status\n\n    if (not hasContent(content, offset)) then\n        -- read new content from socket\n        status, content = conn:read(req.readTimeout)\n        if (not status) then\n            if (content == 'EOF') then\n                req:addHeader('Connection', 'close')\n                req.luaw_read_content = nil\n                req.luaw_read_offset = nil\n                req.luaw_headers_done = true\n                req.luaw_mesg_done = true\n                req.EOF = true\n                return\n            else\n                return error(content)\n            end\n        end\n        offset = 0 -- offset is for C, therefore zero based\n    end\n\n    httpcb, offset = parseHttpFragment(req, conn, parser, content, offset)\n\n    if (hasContent(content,offset)) then\n        -- store back remaining content in request object for next HTTP request parsing\n        req.luaw_read_content = content\n        req.luaw_read_offset = offset\n    else\n        req.luaw_read_content = nil\n        req.luaw_read_offset = nil\n    end\nend\n\nlocal function consumeTill(input, search, offset)\n    local start, stop = string.find(input, search, offset, true)\n    if (start and stop) then\n        return stop+1, string.sub(input, offset, start-1)\n    end\nend\n\nlocal function getMultipartBoundary(req)\n    local header = req.headers['Content-Type']\n    if (header) then\n        local offset, contentType = consumeTill(header, \";\", 1)\n        if (contentType == \"multipart/form-data\") then\n            local boundary\n            offset, boundary = consumeTill(header, \"=\", offset)\n            if ((boundary)and(string.find(boundary, \"boundary\", 1, true))) then\n                return '--'..string.sub(header, offset)\n            end\n        end\n    end\nend\n\nlocal function isMultipart(req)\n    if (req.luaw_multipart_boundary) then\n        return true\n    end\n\n    local boundary = getMultipartBoundary(req)\n    if (boundary) then\n        req.luaw_multipart_boundary = boundary\n        req.luaw_multipart_end = boundary .. '--'\n        return true\n    end\nend\n\nlocal function isLuaPackMesg(req)\n    local contentType = req.headers['Content-Type']\n    if ('application/luapack' == contentType) then\n        return true\n    end\nend\n\nlocal function readFull(req)\n    -- first parse till headers are done\n    while (not req.luaw_headers_done) do\n        req:readAndParse()\n    end\n\n    if ((isMultipart(req))or(isLuaPackMesg(req))) then\n        -- multipart (file upload) HTTP requests and LuaPack requests are forced to be streaming to conserve memory\n        return\n    end\n\n    while (not req.luaw_mesg_done) do\n        req:readAndParse()\n    end\nend\n\nlocal function consumeBodyChunkParsed(req)\n    local bodyChunk = req.body\n    if (bodyChunk) then\n        req.body = nil\n    else\n        local bodyParts = req.bodyParts\n        if (bodyParts.len > 0) then\n            bodyChunk = bodyParts:concat()\n            bodyParts:reset()\n        end\n    end\n    return bodyChunk\nend\n\nlocal function bufferedConsume(req, search, content, offset)\n    assert(search, \"search pattern cannot be nil\")\n\n    while (true) do\n        if ((content)and(#content > offset)) then\n            local matchPos, matchStr  = consumeTill(content, search, offset)\n            if (matchPos) then\n                -- found match\n                if (matchPos < #content) then\n                    -- there is content remaining\n                    return matchStr, content, matchPos\n                end\n                -- content fully consumed\n                return matchStr, nil, nil\n            end\n\n            if (offset > 1) then\n                content = string.sub(content, offset)\n                offset = 1\n            end\n        end\n\n        -- match not found, read more\n        req:readAndParse()\n        if (req.luaw_mesg_done) then\n            error(\"premature HTTP message end\")\n        end\n\n        local readStr = req:consumeBodyChunkParsed()\n        if (readStr) then\n            if (content) then\n                content = content..readStr\n            else\n                content = readStr\n                offset = 1\n            end\n        end\n    end\nend\n\nlocal function saveState(req, content, offset)\n    req.luaw_multipart_content = content\n    req.luaw_multipart_offset = offset\nend\n\nfunction fetchNextPart(req, state)\n    local content = req.luaw_multipart_content\n    local offset = req.luaw_multipart_offset or 1\n    local boundary = req.luaw_multipart_boundary\n    local matchStr\n\n    if (state == MULTIPART_BEGIN) then\n        -- read beginning boundary\n        matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)\n        assert(matchedStr == boundary, \"Missing multi-part boundary at the beginning of the part\")\n        state = PART_END\n    end\n\n    if (state == PART_END) then\n        -- read \"Content-Disposition\" line\n        matchedStr, content, offset = bufferedConsume(req, \": \", content, offset)\n        assert(matchedStr == 'Content-Disposition',\"Missing 'Content-Disposition' header\")\n\n        matchedStr, content, offset = bufferedConsume(req, \"; \", content, offset)\n        assert(string.find(matchedStr, 'form-data', 1, true), \"Wrong Content-Disposition\")\n\n        matchedStr, content, offset = bufferedConsume(req, '\"', content, offset)\n        assert(string.find(matchedStr, \"name\", 1, true), \"form field name missing\")\n\n        local fieldName\n        fieldName, content, offset = bufferedConsume(req, '\"', content, offset)\n        assert(#fieldName > 0, \"form field name missing\")\n\n        matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)\n        local _, filenamePos = string.find(matchedStr, 'filename=\"', 1, true)\n        local fileName\n        if (filenamePos) then\n            fileName = string.sub(matchedStr, filenamePos+1, #matchedStr-1)\n        end\n\n        local contentType\n        if (fileName) then\n            -- read \"Content-Type\" line\n            matchedStr, content, offset = bufferedConsume(req, \": \", content, offset)\n            assert(matchedStr == 'Content-Type', \"Missing 'Content-Type' header\")\n\n            contentType, content, offset = bufferedConsume(req, CRLF, content, offset)\n            assert(#contentType, \"Content-Type value missing\")\n        end\n\n        -- read next blank line\n        matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)\n        assert(#matchedStr == 0, \"Missing line separating content headers and actual content\")\n\n        saveState(req, content, offset)\n        return PART_BEGIN, fieldName, fileName, contentType\n    end\n\n    while ((state == PART_BEGIN)or(state == PART_DATA)) do\n        local matchedStr, content, offset = bufferedConsume(req, CRLF, content, offset)\n        if (matchedStr) then\n            if (matchedStr == boundary) then\n                saveState(req, content, offset)\n                return PART_END\n            end\n\n            local lastBoundary = req.luaw_multipart_end\n            if (matchedStr == lastBoundary) then\n                saveState(req, content, offset)\n                return MULTIPART_END\n            end\n\n            saveState(req, content, offset)\n            return PART_DATA, matchedStr\n        else\n            local content = req.luaw_multipart_content\n            local offset = req.luaw_consumed_till or 0\n\n            if ((content)and(#content-offset) > #boundary+3) then\n                saveState(req, nil, 1)\n                return PART_DATA, content\n            end\n        end\n    end\nend\n\nlocal function multiPartIterator(req)\n    if (isMultipart(req)) then\n        return fetchNextPart, req, MULTIPART_BEGIN\n    end\nend\n\nlocal function shouldCloseConnection(req)\n    if req and req.EOF then\n        return true\n    end\nend\n\nlocal function  toURLSafeChar(ch)\n    if (ch == \" \") then return \"+\" end\n    return string.format(\"%%%02X\", string.byte(ch))\nend\n\nlocal function urlEncode(str)\n    str = string.gsub(str, \"([^a-zA-Z0-9%.%*%-_])\", toURLSafeChar)\n    return str\nend\n\nlocal function urlEncodeParams(params)\n    if params then\n        local encodedParams = {}\n        local contentLength = 0\n\n        for key, val in pairs(params) do\n            local ueKey = urlEncode(key)\n            table.insert(encodedParams, ueKey)\n            contentLength = contentLength + #ueKey\n\n            table.insert(encodedParams, \"=\")\n            contentLength = contentLength + 1\n\n            local ueVal = urlEncode(val)\n            table.insert(encodedParams, ueVal)\n            contentLength = contentLength + #ueVal\n\n            table.insert(encodedParams, \"&\")\n            contentLength = contentLength + 1\n        end\n\n        if (#encodedParams > 0) then\n            table.remove(encodedParams) -- remove the last extra \"&\"\n            contentLength = contentLength - 1\n            return encodedParams, contentLength\n        end\n    end\n\n    return nil, 0\nend\n\nfunction buildURL(req)\n    if (req.method == 'GET') then\n        local encodedParams = urlEncodeParams(req.params)\n        if encodedParams then\n            table.insert(encodedParams, 1, \"?\")\n            table.insert(encodedParams, 1, req.url)\n            return table.concat(encodedParams)\n        end\n    end\n    return req.url\nend\n\nlocal function setStatus(resp, status)\n    resp.status = status\n    resp.statusMesg = http_status_codes[status]\nend\n\nlocal function getStatus(resp)\n    return resp.status\nend\n\nlocal function getBody(resp)\n    return resp.body\nend\n\nlocal function firstResponseLine(resp)\n    local line = {\"HTTP/\", resp.major_version, \".\", resp.minor_version,\n        \" \", resp.status, \" \", resp.statusMesg, CRLF}\n    return table.concat(line)\nend\n\nlocal function firstRequestLine(req)\n    local line = {req.method, \" \", req:buildURL(), \" HTTP/\", req.major_version,\n         \".\", req.minor_version, CRLF}\n    return table.concat(line)\nend\n\nlocal function sendBuffer(buffer, conn, writeTimeout, isChunked)\n\tlocal chunk = table.concat(buffer)\n\tbuffer:reset()\n    if (isChunked) then\n        chunk = string.format(\"%x\\r\\n\", #chunk)..chunk..CRLF\n    end\n    conn:write(chunk, writeTimeout)\nend\n\nlocal function bufferHeader(buffer, name, value)\n    buffer:append(tostring(name))\n    buffer:append(\": \")\n    buffer:append(tostring(value))\n    buffer:append(CRLF)\nend\n\nlocal function bufferHeaders(headers, buffer)\n    if (headers) then\n        for name,value in pairs(headers) do\n            if (type(value) == 'table') then\n                for i,v in ipairs(value) do\n                    bufferHeader(buffer, name, v)\n                end\n            else\n                bufferHeader(buffer, name, value)\n            end\n            headers[name] = nil\n        end\n    end\n    buffer:append(CRLF)\nend\n\nlocal function startStreaming(resp)\n    resp.luaw_is_chunked = true\n    resp:addHeader('Transfer-Encoding', 'chunked')\n\n    local headers = resp.headers\n    local conn = resp.luaw_conn\n    local writeTimeout = resp.writeTimeout\n\n    -- use separate buffer from \"bodyParts\" to serialize headers\n    local headersBuffer = newBuffer()\n    headersBuffer:append(resp:firstLine())\n    bufferHeaders(headers, headersBuffer)\n\n    -- flush up to HTTP headers end without chunked encoding before actual body starts\n    sendBuffer(headersBuffer, conn, writeTimeout, false)\nend\n\nlocal function appendBody(resp, bodyPart)\n    if not bodyPart then\n        return\n    end\n\n    local bodyBuffer = resp.bodyParts\n    local len = bodyBuffer:append(bodyPart)\n\n    if ((resp.luaw_is_chunked)and(len >= CONN_BUFFER_SIZE)) then\n        local conn = resp.luaw_conn\n        local writeTimeout = resp.writeTimeout\n        sendBuffer(bodyBuffer, conn, writeTimeout, true)\n    end\nend\n\nlocal function writeFullBody(resp)\n    local conn = resp.luaw_conn\n    local writeTimeout = resp.writeTimeout\n    local bodyBuffer = resp.bodyParts\n\n    if (resp.method == 'POST') then\n        local encodedParams, contentLength = urlEncodeParams(resp.params)\n        if encodedParams then\n            resp:addHeader('Content-Type', 'application/x-www-form-urlencoded')\n            bodyBuffer:append(encodedParams)\n        end\n    end\n\n    resp:addHeader('Content-Length', bodyBuffer.len)\n\n    -- first write up to HTTP headers end\n    local headersBuffer = newBuffer()\n    headersBuffer:append(resp:firstLine())\n    bufferHeaders(resp.headers, headersBuffer)\n    sendBuffer(headersBuffer, conn, writeTimeout, false)\n\n    -- now write body\n    sendBuffer(bodyBuffer, conn, writeTimeout, false)\nend\n\nlocal function endStreaming(resp)\n    local conn = resp.luaw_conn\n    local writeTimeout = resp.writeTimeout\n    local bodyBuffer = resp.bodyParts\n\n    -- flush whatever is remaining in write buffer\n    sendBuffer(bodyBuffer, conn, writeTimeout, true)\n\n    -- add last chunk encoding trailer\n    conn:write(\"0\\r\\n\\r\\n\", writeTimeout)\nend\n\nlocal function flush(resp)\n    if resp.luaw_is_chunked then\n        endStreaming(resp)\n    else\n        writeFullBody(resp)\n    end\nend\n\nlocal function close(req)\n    local conn = req.luaw_conn;\n    if conn then\n        conn:close()\n        req.luaw_conn = nil\n    end\nend\n\nlocal function reset(req)\n    req.headers = {}\n    req[\"_acc_header_name_\"] = nil\n    req[\"_acc_header_value_\"] = nil\n    req.url = nil\n    req.bodyParts:reset()\n    req.body = nil\n    req.luaw_mesg_done = nil\n    req.luaw_headers_done = nil\n    req.params = nil\n    req.parsedURL = nil\n    req.status = nil\n    req.statusMesg = nil\nend\n\nluaw_http_lib.newServerHttpRequest = function(conn)\n\tlocal req = {\n\t    luaw_mesg_type = 'sreq',\n\t    luaw_conn = conn,\n\t    headers = {},\n\t\tbodyParts = newBuffer(),\n\t    luaw_parser = luaw_http_lib:newHttpRequestParser(),\n\t    addHeader = addHeader,\n\t    shouldCloseConnection = shouldCloseConnection,\n\t    isComplete = isComplete,\n\t    readAndParse = readAndParse,\n\t    readFull = readFull,\n\t    isMultipart = isMultipart,\n\t    multiPartIterator = multiPartIterator,\n\t    getBody = getBody,\n\t    reset = reset,\n\t    consumeBodyChunkParsed = consumeBodyChunkParsed,\n\t    close = close\n\t}\n    return req;\nend\n\nluaw_http_lib.newServerHttpResponse = function(conn)\n    local resp = {\n        luaw_mesg_type = 'sresp',\n        luaw_conn = conn,\n        major_version = 1,\n        minor_version = 1,\n        contentLength = 0,\n        headers = {},\n        bodyParts = newBuffer(),\n        addHeader = addHeader,\n        shouldCloseConnection = shouldCloseConnection,\n        setStatus = setStatus,\n        firstLine = firstResponseLine,\n        startStreaming = startStreaming,\n        appendBody = appendBody,\n        flush = flush,\n        reset = reset,\n        close = close\n    }\n    return resp;\nend\n\nlocal function newClientHttpResponse(conn)\n\tlocal resp = {\n\t    luaw_mesg_type = 'cresp',\n\t    luaw_conn = conn,\n\t    headers = {},\n\t\tbodyParts = newBuffer(),\n\t    luaw_parser = luaw_http_lib:newHttpResponseParser(),\n\t    addHeader = addHeader,\n\t    shouldCloseConnection = shouldCloseConnection,\n\t    readAndParse = readAndParse,\n\t    getBody = getBody,\n\t    getStatus = getStatus,\n\t    readFull = readFull,\n\t    consumeBodyChunkParsed = consumeBodyChunkParsed,\n\t    reset = reset,\n\t    close = close\n\t}\n\treturn resp;\nend\n\nlocal function connect(req)\n    local conn = assert(luaw_tcp_lib.connect(req.hostIP, req.hostName, req.port, req.connectTimeout))\n    return conn\nend\n\nlocal function connectReq(req)\n    conn = connect(req)\n    conn:startReading()\n    req.luaw_conn = conn\n    local resp = newClientHttpResponse(conn)\n    resp.readTimeout = req.readTimeout\n    resp.writeTimeout = req.writeTimeout\n    return resp\nend\n\nlocal function execute(req)\n    local resp = req:connect()\n    req:flush()\n    resp:readFull()\n    if resp:shouldCloseConnection() then\n        resp:close()\n    end\n    return resp\nend\n\n\nluaw_http_lib.newClientHttpRequest = function()\n    local req = {\n        luaw_mesg_type = 'creq',\n        port = 80,\n        major_version = 1,\n        minor_version = 1,\n        method = 'GET',\n        contentLength = 0,\n        headers = {},\n\t\tbodyParts = newBuffer(),\n        addHeader = addHeader,\n        connect = connectReq,\n        execute = execute,\n        shouldCloseConnection = shouldCloseConnection,\n        buildURL = buildURL,\n        firstLine = firstRequestLine,\n        startStreaming = startStreaming,\n        appendBody = appendBody,\n        flush = flush,\n        reset = reset,\n        close = close\n    }\n\treturn req;\nend\n\n\nreturn luaw_http_lib\n"
  },
  {
    "path": "lib/luaw_init.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nluaw_utils = require(\"luaw_utils\")\nluaw_logging = require(\"luaw_logging\")\nluaw_lpack = require(\"luapack\")\nluaw_scheduler = require(\"luaw_scheduler\")\nluaw_tcp = require(\"luaw_tcp\")\nluaw_timer = require(\"luaw_timer\")\nluaw_http = require(\"luaw_http\")\nluaw_webapp = require(\"luaw_webapp\")\n\nluaw_webapp.init()"
  },
  {
    "path": "lib/luaw_logging.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal ds_lib = require('luaw_data_structs_lib')\nlocal luaw_utils_lib = require(\"luaw_utils\")\nlocal luapack_lib = require('luapack')\n\nlocal log_module = {}\n\nlocal PATH_SEPARATOR = string.match (package.config, \"[^\\n]+\")\n\n-- Log file states\nlocal LOG_NOT_OPEN = 0\nlocal OPENING_LOG = 1\nlocal LOG_IS_OPEN = 2\n\n-- Log levels\nlocal EMERGENCY = 0\nlocal ALERT = 1\nlocal CRITICAL = 2\nlocal ERROR = 3\nlocal WARNING = 4\nlocal NOTICE = 5\nlocal INFO = 6\nlocal DEBUG = 7\n\nlog_module.EMERGENCY = EMERGENCY\nlog_module.ALERT = ALERT\nlog_module.CRITICAL = CRITICAL\nlog_module.ERROR = ERROR\nlog_module.WARNING = WARNING\nlog_module.NOTICE = NOTICE\nlog_module.INFO = INFO\nlog_module.DEBUG = DEBUG\n\n-- Log appender types\nlocal FILE_LOG = \"FILE\"\nlocal SYS_LOG = \"SYSLOG\"\n\nlog_module.SYSLOG_FACILITY_USER = 1\nlog_module.SYSLOG_FACILITY_AUTH = 10\nlog_module.SYSLOG_FACILITY_AUDIT = 13\nlog_module.SYSLOG_FACILITY_ALERT = 14\nlog_module.SYSLOG_FACILITY_LOCAL0 = 16\nlog_module.SYSLOG_FACILITY_LOCAL1 = 17\nlog_module.SYSLOG_FACILITY_LOCAL2 = 18\nlog_module.SYSLOG_FACILITY_LOCAL3 = 19\nlog_module.SYSLOG_FACILITY_LOCAL4 = 20\nlog_module.SYSLOG_FACILITY_LOCAL5 = 21\nlog_module.SYSLOG_FACILITY_LOCAL6 = 22\nlog_module.SYSLOG_FACILITY_LOCAL7 = 23\n\nlocal logRoot = { }\n\nlocal logDir = assert(luaw_log_config.log_dir, \"Invalid log directory specified\")\nlocal noOfLogLinesToBuffer = luaw_log_config.log_lines_buffer_count or 100\nlocal logfileBaseName = luaw_log_config.log_file_basename or \"luaw-log\"\nlocal logfileSizeLimit = luaw_log_config.log_file_size_limit or (1024 * 1024 * 10) -- 10MB\nlocal logfileCountLimit = luaw_log_config.log_file_count_limit or 99\nlocal logLineTimeFormat = luaw_log_config.log_line_timestamp_format or \"%x %X\"\nlocal logFileNameTimeFormat = luaw_log_config.log_filename_timestamp_format or '%Y%m%d-%H%M%S'\n\nlocal syslogTag = luaw_log_config.syslog_tag or 'luaw'\nlocal syslogPresent = luaw_logging_lib.syslogConnect(luaw_log_config.syslog_server, luaw_log_config.syslog_port)\nlogRoot.facility = luaw_log_config.syslog_facility or log_module.SYSLOG_FACILITY_LOCAL7\nlocal hostname = luaw_logging_lib.hostname()\n\n\nlocal logSequenceNum = 0\nlocal logSize = 0\nlocal logBuffer = ds_lib.newOverwrittingRingBuffer(noOfLogLinesToBuffer + 32)\nlocal noOfLogLinesDropped = 0\n\nlocal currentTimeStr\nlocal syslogTimeStr\n\nlog_module.updateCurrentTime = function(currentTime)\n    currentTimeStr = os.date(logLineTimeFormat, currentTime)\n    if syslogPresent then\n        syslogTimeStr = os.date(\"%b %d %X\", currentTime)\n    end\nend\n\nlocal function nextLogSequenceNum()\n    if logSequenceNum > logfileCountLimit then logSequenceNum = 0 end\n    logSequenceNum = logSequenceNum + 1\n    return logSequenceNum\nend\n\nlocal function concatLogLines()\n    local temp = luapack_lib.createDict(logBuffer.filled+1, 0)\n    local i = 1\n    local logLine = logBuffer:take()\n    while logLine do\n        temp[i] = logLine\n        i = i+1\n        logLine = logBuffer:take()\n    end\n    temp[i] = '' -- for the last newline\n    return table.concat(temp, '\\n')\nend\n\nlocal function logToFile(logLine)\n    local added = logBuffer:offer(currentTimeStr..' '..logLine)\n    if not added then noOfLogLinesDropped = noOfLogLinesDropped +1 end\n\n    local state = luaw_logging_lib.logState()\n\n    if ((state == LOG_IS_OPEN)and(logBuffer.filled >= noOfLogLinesToBuffer)) then\n        local logBatch = concatLogLines()\n        logSize = logSize + string.len(logBatch)\n        local rotateLog = (logSize >= logfileSizeLimit)\n        state = luaw_logging_lib.writeLog(logBatch, rotateLog)\n    end\n\n    if (state == LOG_NOT_OPEN) then\n        logSize = 0\n        local ts = os.date(logFileNameTimeFormat, os.time())\n        local fileName = logDir..PATH_SEPARATOR..logfileBaseName..'-'..ts..'-'..nextLogSequenceNum()..'.log'\n        luaw_logging_lib.openLog(fileName)\n    end\nend\n\nlocal function syslog(priority, facility, mesg)\n    local pri = priority + (facility * 8)\n    local logLine = string.format(\"<%d>%s %s %s: %s\", pri, syslogTimeStr, hostname, syslogTag, mesg)\n    luaw_logging_lib.syslogSend(logLine);\nend\n\nlocal nameIterator = luaw_utils_lib.splitter('.')\nlocal function splitName(name)\n    if not name then return luaw_utils_lib.nilFn end\n    return nameIterator, name, 0\nend\n\nlocal function logInternal(logLevel, fileLevel, syslogLevel, syslogFacility, mesg)\n    if (logLevel <= fileLevel) then\n        logToFile(mesg)\n    end\n    if ((syslogPresent)and(logLevel <= syslogLevel)) then\n        syslog(logLevel, syslogFacility, mesg)\n    end\nend\n\nlocal function log(logger, logLevel, mesg)\n    local fileLevel = logger[FILE_LOG] or ERROR\n    local syslogLevel = logger[SYS_LOG] or ERROR\n    logInternal(logLevel, fileLevel, syslogLevel, logger.facility, mesg)\nend\n\nlocal function logf(logger, logLevel, mesgFormat, ...)\n    local fileLevel = logger[FILE_LOG] or ERROR\n    local syslogLevel = logger[SYS_LOG] or ERROR\n    if ((logLevel <= fileLevel)or(logLevel <= syslogLevel)) then\n        local mesg = string.format(mesgFormat, ...)\n        logInternal(logLevel, fileLevel, syslogLevel, logger.facility, mesg)\n    end\nend\n\nlogRoot.log = log\n\nlogRoot.logf = logf\n\nlogRoot.emergency = function(logger, mesg)\n    log(logger, EMERGENCY, mesg)\nend\n\nlogRoot.alert = function(logger, mesg)\n    log(logger, ALERT, mesg)\nend\n\nlogRoot.critical = function(logger, mesg)\n    log(logger, CRITICAL, mesg)\nend\n\nlogRoot.error = function(logger, mesg)\n    log(logger, ERROR, mesg)\nend\n\nlogRoot.warning = function(logger, mesg)\n    log(logger, WARNING, mesg)\nend\n\nlogRoot.notice = function(logger, mesg)\n    log(logger, NOTICE, mesg)\nend\n\nlogRoot.info = function(logger, mesg)\n    log(logger, INFO, mesg)\nend\n\nlogRoot.debug = function(logger, mesg)\n    log(logger, DEBUG, mesg)\nend\n\nlogRoot.emergencyf = function(logger, mesgFormat, ...)\n    logf(logger, EMERGENCY, mesgFormat, ...)\nend\n\nlogRoot.alertf = function(logger, mesgFormat, ...)\n    logf(logger, ALERT, mesgFormat, ...)\nend\n\nlogRoot.criticalf = function(logger, mesgFormat, ...)\n    logf(logger, CRITICAL, mesgFormat, ...)\nend\n\nlogRoot.errorf = function(logger, mesgFormat, ...)\n    logf(logger, ERROR, mesgFormat, ...)\nend\n\nlogRoot.warningf = function(logger, mesgFormat, ...)\n    logf(logger, WARNING, mesgFormat, ...)\nend\n\nlogRoot.noticef = function(logger, mesgFormat, ...)\n    logf(logger, NOTICE, mesgFormat, ...)\nend\n\nlogRoot.infof = function(logger, mesgFormat, ...)\n    logf(logger, INFO, mesgFormat, ...)\nend\n\nlogRoot.debugf = function(logger, mesgFormat, ...)\n    logf(logger, DEBUG, mesgFormat, ...)\nend\n\nlocal function getLogger(name)\n    local logger = logRoot\n    if (name == 'root') then return logger end\n\n    for idx, namePart in splitName(name) do\n        local child = logger[namePart]\n        if not child then\n            child = {}\n            setmetatable(child, {__index = logger})\n            logger[namePart] = child\n        end\n        logger = child\n    end\n    return logger\nend\n\nlog_module.getLogger = getLogger\n\nlocal function configureLogger(logCfg, logType)\n    local loggerName = assert(logCfg.name, \"Logger name missing\")\n    local logLevel = assert(logCfg.level, \"Logger level missing\")\n    local logger = assert(getLogger(loggerName), \"Could not find logger \"..loggerName)\n    logger[logType] = logLevel\n    return logger\nend\n\nlog_module.file = function(logCfg)\n    configureLogger(logCfg, FILE_LOG)\nend\n\nlog_module.syslog = function(logCfg)\n    local logger = configureLogger(logCfg, SYS_LOG)\n    logger.facility = logCfg.facility\nend\n\nreturn log_module"
  },
  {
    "path": "lib/luaw_scheduler.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal constants = require('luaw_constants')\nlocal ds_lib = require('luaw_data_structs_lib')\nlocal logging = require('luaw_logging')\n\n-- Scheduler object\nlocal scheduler = {}\n\n-- Constants\nlocal TS_RUNNABLE = constants.RUNNABLE\nlocal TS_DONE = constants.DONE\nlocal TS_BLOCKED_EVENT = constants.BLOCKED_ON_EVENT\nlocal TS_BLOCKED_THREAD = constants.BLOCKED_ON_THREAD\nlocal END_OF_CALL = constants.END_OF_CALL\nlocal END_OF_THREAD = constants.END_OF_THREAD\n\nlocal UPDATE_TIME_COUNTER_LIMIT = 10\n\n-- scheduler state\nlocal threadRegistry = ds_lib.newRegistry(luaw_server_config.thread_pool_size or 1024)\nlocal threadPool = ds_lib.newRingBuffer(luaw_server_config.thread_pool_size or 1024)\nlocal timesReuseThread = luaw_server_config.thread_reuse_limit or 1024\nlocal runQueueLen = 0\nlocal runQueueHead = nil\nlocal runQueueTail = nil\nlocal currentRunningThreadCtx = nil\nlocal updateTimeCyclingCounter = 0\nlocal currentTime\n\n\nscheduler.updateCurrentTime = function ()\n    updateTimeCyclingCounter = updateTimeCyclingCounter + 1\n    if (updateTimeCyclingCounter >= UPDATE_TIME_COUNTER_LIMIT) then\n        currentTime = os.time()\n        logging.updateCurrentTime(currentTime)\n        updateTimeCyclingCounter = 0\n    end\nend\n\nscheduler.time = function()\n    return currentTime\nend\n\n-- returns current running thread's id\nscheduler.tid = function()\n    if currentRunningThreadCtx then\n        return currentRunningThreadCtx.tid\n    end\n    return nil\nend\n\nlocal function threadRunLoop(fn, arg1, arg2, arg3, arg4)\n    local i = 0\n    while i <= timesReuseThread do\n        fn, arg1, arg2, arg3, arg4 = coroutine.yield(END_OF_CALL, fn(arg1, arg2, arg3, arg4))\n        i = i+1\n    end\n    return END_OF_THREAD, fn(arg1, arg2, arg3, arg4)\nend\n\nlocal function userThreadRunner(userThreadFn, ...)\n    -- We have captured user thread function along with its arguments on a coroutine stack.\n    -- Yield now so that scheduler can add this thread in run queue for \"bottom half\"\n    -- processing later and original calling thread can resume.\n    coroutine.yield(TS_RUNNABLE)\n    -- At this point we have been resumed by thread scheduler during the \"bottom half\" run\n    -- queue processing bu the scheduler so run the actual user thread function.\n    return userThreadFn(...)\nend\n\nlocal function addToRunQueue(threadCtx)\n    if not runQueueTail then\n        runQueueHead = threadCtx\n        runQueueTail = threadCtx\n    else\n        runQueueTail.nextThread = threadCtx\n        runQueueTail = threadCtx\n    end\n    runQueueLen = runQueueLen + 1\n    threadCtx.state = TS_RUNNABLE\nend\n\nlocal function newThread()\n    local t = threadPool:take()\n    if not t then\n        t = coroutine.create(threadRunLoop)\n    end\n\n    local threadCtx = { thread = t, requestCtx = {} }\n    -- anchor thread in registry to prevent GC\n    local ref = threadRegistry:ref(threadCtx)\n    threadCtx.tid = ref\n    return threadCtx\nend\n\nlocal function unblockJoinedThreadIfAny(threadCtx, status, retVal)\n    local joinedTC = threadCtx.joinedBy\n    if joinedTC then\n        local count = joinedTC.joinCount\n        count = count -1\n        joinedTC.joinCount = count\n        if (count <= 0) then\n            addToRunQueue(joinedTC)\n        end\n    end\nend\n\nlocal function afterResume(threadCtx, state, retVal)\n    threadCtx.state, threadCtx.result = state, retVal\n    currentRunningThreadCtx = nil\n    if (state == TS_DONE) then\n        return true, retVal\n    end\n    return false, retVal\nend\n\nlocal function resumeThread(threadCtx, ...)\n    currentRunningThreadCtx = threadCtx\n    local t = threadCtx.thread\n    local tid = threadCtx.tid\n\n    scheduler.updateCurrentTime()\n\n    context = threadCtx.requestCtx  -- TLS, per thread context\n    local status, state, retVal = coroutine.resume(t, ...)\n    context = nil -- reset TLS context\n\n    if not status then\n        -- thread ran into error\n        print(\"Error: \"..tostring(state))\n        state = END_OF_THREAD\n        -- thread has blown its stack so let it get garbage collected\n        t = nil\n    end\n\n    if ((state == END_OF_THREAD)or(state == END_OF_CALL)) then\n        threadRegistry:unref(tid)\n        threadCtx.thread = nil\n        threadCtx.requestCtx = nil\n        if ((state == END_OF_CALL) and (t)) then\n            -- thread is still alive, return it to free pool if possible\n            threadPool:offer(t)\n        end\n        unblockJoinedThreadIfAny(threadCtx, status, retVal)\n        return afterResume(threadCtx, TS_DONE, retVal)\n    end\n\n    if ((state == TS_BLOCKED_EVENT)or(state == TS_BLOCKED_THREAD)) then\n        -- thread will later be resumed by libuv call back\n        return afterResume(threadCtx, state, retVal)\n    end\n\n    -- Thread yielded, but is still runnable. Add it back to the run queue\n    addToRunQueue(threadCtx)\n    return afterResume(threadCtx, TS_RUNNABLE, retVal)\nend\n\nfunction resumeThreadId(tid, ...)\n    local threadCtx = threadRegistry[tid]\n    if not threadCtx then error(\"Invalid thread Id \"..tostring(tid)) end\n    return resumeThread(threadCtx, ...)\nend\n\nscheduler.resumeThreadId = resumeThreadId\n\nfunction startSystemThread(serviceFn, conn, ...)\n    local threadCtx = newThread()\n    threadCtx.state = TS_RUNNABLE\n    local isDone = resumeThread(threadCtx, serviceFn, conn, ...)\n    return isDone, threadCtx.tid\nend\n\nscheduler.startSystemThread = startSystemThread\n\n-- Scheduler object methods\n\nscheduler.startUserThread = function(userThreadFn, ...)\n    local backgroundThreadCtx = newThread()\n    coroutine.resume(backgroundThreadCtx.thread, userThreadRunner, userThreadFn, ...)\n    addToRunQueue(backgroundThreadCtx)\n    return backgroundThreadCtx;\nend\n\nscheduler.join = function(...)\n    local joiningTC = currentRunningThreadCtx\n    if (joininTC) then\n        local joinedThreads = table.pack(...)\n        local numOfThreads = #joinedThreads\n\n        local count = 0\n\n        for i, joinedTC in ipairs(joinedThreads) do\n            if ((joinedTC)and(joinedTC.state)and(joinedTC.state ~= TS_DONE)) then\n                count = count + 1\n                joinedTC.joinedBy = joiningTC\n            end\n        end\n\n        joiningTC.joinCount = count\n        while (joiningTC.joinCount > 0) do\n            coroutine.yield(TS_BLOCKED_THREAD)\n        end\n    end\nend\n\nscheduler.runQueueSize = function()\n    return runQueueLen\nend\n\nlocal runNextFromRunQueue = function()\n    local threadCtx = runQueueHead\n    if threadCtx then\n        runQueueHead = threadCtx.nextThread\n        if not runQueueHead then\n            runQueueTail = nil\n        end\n\n        threadCtx.nextThread = nil\n\n        runQueueLen = runQueueLen -1\n        if (runQueueLen < 0) then\n            runQueueLen = 0\n        end\n\n        if (threadCtx.state == TS_DONE) then\n            -- This can happen when thread is added to the run queue but is woken up by libuv\n            -- event and then runs to completion before the run queue scheduler gets chance\n            -- to resume it\n            return\n        end\n\n        return resumeThread(threadCtx)\n    end\nend\n\nscheduler.runReadyThreads = function(limit)\n    local runnableCount = runQueueLen\n    if ((limit)and(limit < runnableCount)) then\n        runnableCount = limit\n    end\n\n    for i=1, runnableCount do\n        runNextFromRunQueue()\n        end\n\n    -- about to block on libuv event loop, next resumeThread should update current time\n    -- as it may have spent significant time blocked on a event loop.\n    updateTimeCyclingCounter = UPDATE_TIME_COUNTER_LIMIT\n\n    return runnableCount\nend\n\nscheduler.updateCurrentTime()\n\nreturn scheduler\n"
  },
  {
    "path": "lib/luaw_tcp.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal constants = require('luaw_constants')\nlocal scheduler = require('luaw_scheduler')\n\nlocal DEFAULT_CONNECT_TIMEOUT = constants.DEFAULT_CONNECT_TIMEOUT\nlocal DEFAULT_READ_TIMEOUT = constants.DEFAULT_READ_TIMEOUT\nlocal DEFAULT_WRITE_TIMEOUT = constants.DEFAULT_WRITE_TIMEOUT\nlocal CONN_BUFFER_SIZE = constants.CONN_BUFFER_SIZE\n\nlocal conn = luaw_tcp_lib.newConnection();\nlocal connMT = getmetatable(conn)\nconn:close()\nlocal startReadingInternal = connMT.startReading\nlocal readInternal = connMT.read\nlocal writeInternal = connMT.write\n\nconnMT.startReading = function(self)\n    local status, mesg = startReadingInternal(self)\n    assert(status, mesg)\nend\n\nconnMT.read = function(self, readTimeout)\n    local status, str = readInternal(self, scheduler.tid(), readTimeout or DEFAULT_READ_TIMEOUT)\n    if ((status)and(not str)) then\n        -- nothing in buffer, wait for libuv on_read callback\n        status, str = coroutine.yield(TS_BLOCKED_EVENT)\n    end\n    return status, str\nend\n\nconnMT.write = function(self, str, writeTimeout)\n    local status, nwritten = writeInternal(self, scheduler.tid(), str, writeTimeout  or DEFAULT_WRITE_TIMEOUT)\n    if ((status)and(nwritten > 0)) then\n        -- there is something to write, yield for libuv callback\n        status, nwritten = coroutine.yield(TS_BLOCKED_EVENT)\n    end\n    assert(status, nwritten)\n    return nwritten\nend\n\nlocal connectInternal = luaw_tcp_lib.connect\n\nlocal function connect(hostIP, hostName, port, connectTimeout)\n    assert((hostName or hostIP), \"Either hostName or hostIP must be specified in request\")\n    local threadId = scheduler.tid()\n    if not hostIP then\n        local status, mesg = luaw_tcp_lib.resolveDNS(hostName, threadId)\n        assert(status, mesg)\n        status, mesg = coroutine.yield(TS_BLOCKED_EVENT)\n        assert(status, mesg)\n        hostIP = mesg\n    end\n\n    local connectTimeout = connectTimeout or DEFAULT_CONNECT_TIMEOUT\n    local conn, mesg = connectInternal(hostIP, port, threadId, connectTimeout)\n\n    -- initial connect_req succeeded, block for libuv callback\n    assert(coroutine.yield(TS_BLOCKED_EVENT))\n    return conn, mesg\nend\n\nluaw_tcp_lib.connect = connect\n\nreturn luaw_tcp_lib"
  },
  {
    "path": "lib/luaw_timer.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal constants = require('luaw_constants')\nlocal TS_BLOCKED_EVENT = constants.TS_BLOCKED_EVENT\n\n\nlocal timerMT = getmetatable(luaw_timer_lib.newTimer())\nlocal waitInternal = timerMT.wait\n\ntimerMT.wait = function(timer)\n    local status, elapsed = waitInternal(timer, scheduler.tid())\n    if ((status) and (not elapsed)) then\n        -- timer not yet elapsed, wait for libuv on_timeout callback\n        status, elapsed = coroutine.yield(TS_BLOCKED_EVENT)\n    end\n    return status, elapsed\nend\n\ntimerMT.sleep = function(timer, timeout)\n    assert(timer:start(timeout))\n    timer:wait()\nend\n\nreturn luaw_timer_lib"
  },
  {
    "path": "lib/luaw_utils.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal luaw_util_lib = {}\n\nlocal function tprint(tbl, indent, tab)\n  for k, v in pairs(tbl) do\n    if type(v) == \"table\" then\n\t\tprint(string.rep(tab, indent) .. tostring(k) .. \": {\")\n\t\ttprint(v, indent+1, tab)\n\t\tprint(string.rep(tab, indent) .. \"}\")\n    else\n\t\tprint(string.rep(tab, indent) .. tostring(k) .. \": \" .. tostring(v))\n    end\n  end\nend\n\n-- Print contents of `tbl`, with indentation.\n-- `indent` sets the initial level of indentation.\nluaw_util_lib.debugDump = function(tbl, indent, tab)\n    indent = indent or 0\n    tab = tab or \"  \"\n  \tprint(string.rep(tab, indent) .. \"{\")\n\ttprint(tbl, indent+1, tab)\n\tprint(string.rep(tab, indent) .. \"}\")\nend\n\nluaw_util_lib.steplight = function(mesg)\n    local tid = tostring(Luaw.scheduler.tid())\n    print(\"Thread-\"..tid..\"> \"..tostring(mesg))\nend\n\nluaw_util_lib.step = function(mesg, level)\n    local tid = tostring(Luaw.scheduler.tid())\n\n    local lvl = level or 2\n    if (lvl < 0) then lvl = lvl * -1 end\n\n    local dc = debug.getinfo(lvl, \"nSl\")\n\n    local str = \"\"\n    if type(mesg) == 'table' then\n        for k,v in pairs(mesg) do\n            str = str..\", \"..tostring(k)..\"=\"..tostring(v)\n        end\n    else\n        str = tostring(mesg)\n    end\n\n    print('Thread '..tid..'> line# '..tostring(dc.linedefined)..' in function '..tostring(dc.name)..' in file '..tostring(dc.source)..': '..str)\n\n    if ((level)and(level < 0)) then\n        print(debug.traceback())\n    end\nend\n\nluaw_util_lib.run = function(codeblock)\n    if (codeblock) then\n        local try = codeblock.try\n        if (try) then\n            local catch = codeblock.catch\n            local finally = codeblock.finally\n\n            local status, err = pcall(try, codeblock)\n            if ((not status)and(catch)) then\n                status, err = pcall(catch, codeblock, err)\n            end\n\n            if (finally) then\n                finally(codeblock)\n            end\n\n            if (not status) then\n                error(err)\n            end\n        end\n    end\nend\n\nluaw_util_lib.clearArrayPart = function(t)\n    local len = #t\n    for i=1,len do\n        t[i] = nil\n    end\nend\n\nluaw_util_lib.splitter = function(splitCh)\n    local separator = string.byte(splitCh, 1, 1)\n    local byte = string.byte\n\n    return function (str, pos)\n        pos = pos + 1\n        local start = pos\n        local len = #str\n        while pos <= len do\n            local ch = byte(str, pos, pos)\n            if (ch == separator) then\n                if (pos > start) then\n                    return pos, string.sub(str, start, pos-1)\n                end\n                start = pos + 1\n            end\n            pos = pos + 1\n        end\n        if (pos > start) then return pos, string.sub(str, start, pos) end\n    end\nend\n\nluaw_util_lib.nilFn = function()\n    return nil\nend\n\nluaw_util_lib.formattedLine = function(str, lineSize, paddingCh, beginCh, endCh)\n    lineSize = lineSize or 0\n    paddingCh = paddingCh or ''\n    beginCh = beginCh or ''\n    endCh = endCh or ''\n    paddingWidth = (lineSize - #str -2)/2\n    local padding = ''\n    if paddingWidth > 0 then\n        padding = string.rep(paddingCh, paddingWidth)\n    end\n    print(string.format(\"%s %s %s %s %s\", beginCh, padding, str, padding, endCh))\nend\n\nreturn luaw_util_lib\n"
  },
  {
    "path": "lib/luaw_webapp.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal luaw_utils_lib = require(\"luaw_utils\")\nlocal luaw_http_lib = require(\"luaw_http\")\n\nlocal HTTP_METHODS = {\n    GET = \"GET\",\n    POST = \"POST\",\n    PUT = \"PUT\",\n    DELETE = \"DELETE\",\n    HEAD = \"HEAD\",\n    OPTIONS = \"OPTIONS\",\n    TRACE = \"TRACE\",\n    CONNECT = \"CONNECT\",\n    SERVICE = \"SERVICE\"\n}\n\nlocal registeredWebApps = {}\n\nlocal DIR_SEPARATOR = string.match (package.config, \"[^\\n]+\")\nlocal STRING_PATH_PARAM = { start = string.byte(\":\"), valueOf = tostring }\nlocal NUM_PATH_PARAM = { start = string.byte(\"#\"), valueOf = tonumber }\n\nTAB = '    '\n\nlocal function findFiles(path, pattern, matches)\n    if (path and pattern) then\n        for file in lfs.dir(path) do\n            if (file ~= '.' and file ~= '..') then\n                local f = path..DIR_SEPARATOR..file\n                local attrs = lfs.attributes(f)\n                if attrs then\n                    local mode = attrs.mode\n                    if mode == 'file' then\n                        if (string.match(f, pattern)) then\n                            table.insert(matches, f)\n                        end\n                    elseif mode == 'directory' then\n                        findFiles(f, pattern, matches)\n                    end\n                end\n            end\n        end\n    end\n    return matches\nend\n\nlocal pathIterator = luaw_utils_lib.splitter('/')\nlocal function splitPath(path)\n    if not path then return luaw_util_lib.nilFn end\n    return pathIterator, path, 0\nend\n\nlocal function findAction(method, path)\n    assert(method, \"HTTP method may not be nil\")\n    assert(method, \"HTTP request path may not be nil\")\n\n    local webApp = nil\n    local route = nil\n    local pathParams = {}\n\n    for idx, pseg in splitPath(path) do\n        -- first iteration only\n        if not webApp then\n            webApp = registeredWebApps[pseg]\n            if not webApp then\n                -- fallback to root path\n                webApp = registeredWebApps['/']\n            end\n            if not webApp then\n                return nil\n            end\n            route = webApp.root\n        else\n            local nextRoute = route.childRoutes[pseg]\n            if not nextRoute then\n                -- may be it's a path param\n                nextRoute = route.childRoutes[\"_path_param_\"]\n                if nextRoute then\n                    pathParams[nextRoute.pathParam] = nextRoute.pathParamType.valueOf(pseg)\n                end\n            end\n            if not nextRoute then return nil end\n            route = nextRoute\n        end\n    end\n\n    if (route) then\n        local action = route[method]\n        if not action then\n            -- try catch call action as a fallback\n            action = route['SERVICE']\n        end\n        return webApp, action, pathParams\n    end\nend\n\nlocal function renderView(req, resp, pathParams, model, view)\n    local isTagOpen = false\n    local indent = 0\n\n    local attributes = function(attrs)\n        if attrs then\n            for k,v in pairs(attrs) do\n                resp:appendBody(' ')\n                resp:appendBody(k)\n                resp:appendBody('=\"')\n                resp:appendBody(tostring(v))\n                resp:appendBody('\"')\n            end\n        end\n    end\n\n    local BEGIN = function(tag)\n        if (isTagOpen) then\n            resp:appendBody('>\\n')\n        end\n        for i=1, indent do\n            resp:appendBody(TAB)\n        end\n        resp:appendBody('<')\n        resp:appendBody(tag)\n        isTagOpen = true;\n        indent = indent+1\n        return attributes\n    end\n\n    local END = function(tag)\n        if (isTagOpen) then\n            resp:appendBody('>\\n')\n            isTagOpen = false;\n        end\n\n        indent = indent - 1\n        if indent > 0 then\n            indent = indent\n        else\n            indent = 0\n        end\n        for i=1, indent do\n            resp:appendBody(TAB)\n        end\n\n        resp:appendBody('</')\n        resp:appendBody(tag)\n        resp:appendBody('>\\n')\n    end\n\n    local TEXT = function(...)\n        if (isTagOpen) then\n            resp:appendBody('>\\n')\n            isTagOpen = false;\n        end\n        for i=1, indent do\n            resp:appendBody(TAB)\n        end\n        local values = {...}\n        for i,v in ipairs(values) do\n            resp:appendBody(tostring(v))\n            resp:appendBody(' ')\n        end\n        resp:appendBody('\\n')\n    end\n\n    -- render view\n    if ((req.major_version >= 1)and(req.minor_version >= 1)) then\n        resp:startStreaming()\n    end\n    view(req, resp, pathParams, model, BEGIN, TEXT, END)\nend\n\nlocal function dispatchAction(req, resp)\n    assert(req, \"HTTP request may not be nil\")\n    local parsedURL = req.parsedURL\n\n    if not(req.method and  parsedURL) then\n        -- EOF in case of persistent connections\n        return\n    end\n\n    local webApp, action, pathParams = findAction(req.method, parsedURL.path)\n    assert(action, \"No action found for path \"..parsedURL.path..\" for method \"..req.method)\n\n    if req:shouldCloseConnection() then\n        resp.headers['Connection'] = 'close'\n    else\n        resp.headers['Connection'] = 'Keep-Alive'\n    end\n\n    v1, v2 = action.handler(req, resp, pathParams)\n\n    -- handle action returned response, if any and if resp is not closed\n    if v1 and resp.luaw_conn then\n        if (type(v1) == 'number') then\n            -- v1 is HTTP status\n            resp:setStatus(v1)\n            --resp:startStreaming()\n            if v2 then\n                -- v2 is body content\n                resp:appendBody(tostring(v2))\n            end\n        else\n            if not resp.statusCode then\n                resp:setStatus(200)\n            end\n            --resp:startStreaming()\n            if ((type(v1) == 'string')and(v2)) then\n                -- v1 is view path, v2 is view model\n                local compiledView = webApp.compiledViews[v1]\n                if not compiledView then\n                    error(\"View '\"..tostring(v1)..\"' is not defined\")\n                end\n                renderView(req, resp, pathParams, v2, compiledView)\n            else\n                -- v1 is the body content itself\n                resp:appendBody(tostring(v1))\n            end\n        end\n    end\n\n    -- flush in case resp is not closed\n    if resp.luaw_conn then resp:flush() end\nend\n\nlocal function registerResource(resource)\n    local route = assert(webapp.root, \"webapp root not defined\")\n    local path = assert(resource.path , \"Handler definition is missing value for 'path'\")\n    local handlerFn = assert(resource.handler, \"Handler definition is missing 'handler' function\")\n    local method = resource.method or 'SERVICE'\n    if(not HTTP_METHODS[method]) then\n        error(method..\" is not a valid HTTP method\")\n    end\n\n\n    for idx, pseg in splitPath(path) do\n        local firstChar = string.byte(pseg)\n        local pathParam = nil\n        if ((firstChar == STRING_PATH_PARAM.start)or(firstChar == NUM_PATH_PARAM.start)) then\n            pathParam = pseg:sub(2)\n            pseg = \"_path_param_\"\n        end\n\n        local nextRoute = route.childRoutes[pseg]\n        if not nextRoute then\n            nextRoute = { childRoutes = {} }\n            if pathParam then\n                nextRoute.pathParam = pathParam\n                if (firstChar == NUM_PATH_PARAM.start) then\n                    nextRoute.pathParamType = NUM_PATH_PARAM\n                else\n                    nextRoute.pathParamType = STRING_PATH_PARAM\n                end\n            end\n            route.childRoutes[pseg] = nextRoute\n        end\n        route = nextRoute\n    end\n\n    assert(route, \"Could not register handler for path \"..path)\n    assert((not route[method]), 'Handler already registered for '..method..' for path \"/'..webapp.path..'/'..path..'\"')\n    route[method] = {handler = handlerFn}\nend\n\nlocal function serviceHTTP(conn)\n    conn:startReading()\n\n    -- loop to support HTTP 1.1 persistent (keep-alive) connections\n    while true do\n        local req = luaw_http_lib.newServerHttpRequest(conn)\n\n        -- read and parse full request\n        local status, errmesg = pcall(req.readFull, req)\n        if ((not status)or(req.EOF == true)) then\n            conn:close()\n            if (status) then\n                return \"read time out\"\n            end\n            print(\"Error: \", errmesg, debug.traceback())\n            return \"connection reset by peer\"\n        end\n\n        local resp = luaw_http_lib.newServerHttpResponse(conn)\n        local status, errMesg = pcall(dispatchAction, req, resp)\n\n        if (not status) then\n            -- send HTTP error response\n            resp:setStatus(500)\n            resp:addHeader('Connection', 'close')\n            pcall(resp.appendBody, resp, errMesg)\n            pcall(resp.flush, resp)\n            conn:close()\n            error(errMesg)\n        end\n\n        if (req:shouldCloseConnection() or resp:shouldCloseConnection()) then\n            conn:close()\n            return \"connection reset by peer\"\n        end\n    end\nend\n\nlocal function toFullPath(appRoot, files)\n    local fullPaths = {}\n    if files then\n        for i, file in ipairs(files) do\n            table.insert(fullPaths, appRoot..'/'..file)\n        end\n    end\n    return fullPaths\nend\n\nlocal function loadWebApp(appName, appDir)\n    local app = {}\n\n    app.path = assert(appName, \"Missing mandatory configuration property 'path'\")\n    app.appRoot = assert(appDir, \"Missing mandatory configuration property 'root_dir'\")\n\n    if (registeredWebApps[app.path]) then\n        error('Anothe web app is already registered for path '..app.path)\n    end\n    registeredWebApps[app.path] = app\n\n    app.root = { childRoutes = {} }\n\n    -- Load resource handlers\n    local resources = toFullPath(app.appRoot, luaw_webapp.resources)\n    if luaw_webapp.resourcePattern then\n        resources = findFiles(app.appRoot, luaw_webapp.resourcePattern, resources)\n    end\n    assert((resources and (#resources > 0)), \"Either 'resources' or 'resourcePattern' must be specified in a web app configuration\")\n    app.resources = resources\n\n    -- Load view files if any\n    local views = toFullPath(app.appRoot, luaw_webapp.views)\n    if luaw_webapp.viewPattern then\n        views = findFiles(app.appRoot, luaw_webapp.viewPattern, views)\n    end\n    app.views = views\n\n    return app\nend\n\nlocal function loadView(viewPath, buff)\n    local firstLine = true\n    local lastLine = false\n    local viewLines = io.lines(viewPath)\n\n    return function()\n        local line = nil\n\n        if firstLine then\n            firstLine = false\n            line = \"return function(req, resp, pathParams, model, BEGIN, TEXT, END)\"\n        else\n            if (not lastLine) then\n                local vl = viewLines()\n                if (not vl) then\n                    lastLine = true\n                    line = \"end\"\n                else\n                    line = vl\n                end\n            end\n        end\n        if line then\n            table.insert(buff, line)\n            return (line .. '\\n')\n        end\n    end\nend\n\nlocal function startWebApp(app)\n    -- register resources\n    local resources = app.resources\n    for i,resource in ipairs(resources) do\n        luaw_utils_lib.formattedLine(\".Loading resource \"..resource)\n        -- declare globals (registerHandler and webapp) for the duration of the loadfile(resource)\n        registerHandler = registerResource\n        webapp = app\n        local routeDefn = assert(loadfile(resource), string.format(\"Could not load resource %s\", resource))\n        routeDefn()\n    end\n    luaw_utils_lib.formattedLine(\"#Loaded total \"..#resources..\" resources\\n\")\n\n    -- compile views\n    local views = app.views\n    local compiledViews = {}\n    local appRootLen = string.len(app.appRoot) + 1\n    for i,view in ipairs(views) do\n        local relativeViewPath = string.sub(view, appRootLen)\n        luaw_utils_lib.formattedLine(\".Loading view \"..relativeViewPath)\n        local viewBuff = {}\n        local viewDefn = loadView(view, viewBuff)\n        local compiledView, errMesg = load(viewDefn, relativeViewPath)\n        if (not compiledView) then\n            luaw_utils_lib.formattedLine(\"\\nError while compiling view: \"..view)\n            luaw_utils_lib.formattedLine(\"<SOURCE>\")\n            for i, line in ipairs(viewBuff) do\n                print(tostring(i)..\":\\t\"..tostring(line))\n            end\n            luaw_utils_lib.formattedLine(\"<SOURCE>\")\n            error(errMesg)\n        end\n        compiledViews[relativeViewPath] = compiledView()\n    end\n    app.views = nil\n    app.compiledViews = compiledViews\n    luaw_utils_lib.formattedLine(\"#Compiled total \"..#views..\" views\")\nend\n\nlocal function init()\n    if ((luaw_webapp_config)and(luaw_webapp_config.base_dir)) then\n        local root = luaw_webapp_config.base_dir\n        for webappName in lfs.dir(root) do\n            if (webappName ~= '.' and webappName ~= '..') then\n                local webappDir = root..DIR_SEPARATOR..webappName\n                local attrs = lfs.attributes(webappDir)\n                if ((attrs)and(attrs.mode == 'directory')) then\n                    local webappCfgFile = webappDir..DIR_SEPARATOR..'web.lua'\n                    if (lfs.attributes(webappCfgFile, 'mode') == 'file') then\n                        luaw_utils_lib.formattedLine('Starting webapp '..webappName, 120, '*', '\\n')\n                        dofile(webappCfgFile) -- defines global variable luaw_webapp\n                        local app = loadWebApp(webappName, webappDir)\n                        startWebApp(app)\n                        luaw_utils_lib.formattedLine('Webapp '..webappName..' started', 120, '*')\n                        webapp = nil  -- reset global variable\n                    end\n                end\n            end\n        end\n    end\nend\n\n-- install REST HTTP app handler as a default request handler\nluaw_http_lib.request_handler = serviceHTTP\n\nreturn {\n    init = init,\n    dispatchAction = dispatchAction,\n    serviceHTTP = serviceHTTP\n}\n"
  },
  {
    "path": "lib/unit_testing.lua",
    "content": "--[[\nCopyright (c) 2015 raksoras\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n]]\n\nlocal module = {total_run = 0, total_failed = 0}\n\nfunction module.assertTrue(expr)\n\tif not expr then\n\t\terror(\"Assert true failed!\", 2)\n\tend\nend\n\nfunction module.assertFalse(expr)\n\tif expr then\n\t\terror(\"Assert false failed!\", 2)\n\tend\nend\n\nfunction module.assertNotNil(expr)\n\tif not expr then\n\t\terror(\"Assert not nil failed!\", 2)\n\tend\nend\n\nfunction module.assertNil(expr)\n\tif expr then\n\t\terror(\"Assert nil failed!\", 2)\n\tend\nend\n\nfunction module.assertEqual(actual, expected)\n\tif (actual ~= expected) then\n\t\terror(string.format(\"Assert equal failed! Actual: [%s], Expected: [%s]\", actual, expected), 2)\n\tend\nend\n\nfunction module.assertNotEqual(actual, expected)\n\tif (actual == expected) then\n\t\terror(string.format(\"Assert not equal failed! Actual: [%s], Expected: [%s]\", actual, expected), 2)\n\tend\nend\n\nlocal function tprint(tbl, indent, tab)\n  for k, v in pairs(tbl) do\n    if type(v) == \"table\" then\n\t\tprint(string.rep(tab, indent) .. tostring(k) .. \": {\")\n\t\ttprint(v, indent+1, tab)\n\t\tprint(string.rep(tab, indent) .. \"}\")\n    else\n\t\tprint(string.rep(tab, indent) .. tostring(k) .. \": \" .. tostring(v))\n    end\n  end\nend\n\n-- Print contents of `tbl`, with indentation.\n-- `indent` sets the initial level of indentation.\nfunction module.printTable (tbl, indent, tab)\n    indent = indent or 0\n    tab = tab or \"  \"\n  \tprint(string.rep(tab, indent) .. \"{\")\n\ttprint(tbl, indent+1, tab)\n\tprint(string.rep(tab, indent) .. \"}\")\nend\n\nfunction module:runTests()\n\tnumber_run = 0\n\tnumber_failed = 0\n\n\tfor name, func in pairs(self) do\n\t\tif (string.find(name, \"test\") == 1) then\n\t\t\tresult, mesg = pcall(func)\n\t\t\tnumber_run = number_run + 1\n\t\t\tif (string.find(name, \"testError\") == 1) then\n\t\t\t\t-- negative test\n\t\t\t\tif not result then\n\t\t\t\t\tprint(string.format(\"    %-40s [OK]\", name));\n\t\t\t\telse\n\t\t\t\t\tnumber_failed = number_failed + 1\n\t\t\t\t\tprint(string.format(\"    %-30s [FAILED! Expected to throw error]\", name));\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif result then\n\t\t\t\t\tprint(string.format(\"    %-40s [OK]\", name));\n\t\t\t\telse\n\t\t\t\t\tnumber_failed = number_failed + 1\n\t\t\t\t\tprint(string.format(\"    %-30s [FAILED! %s]\", name, mesg));\n\t\t\t\tend\n\t\t\tend\n\t\t\tself[name] = nil\n\t\tend\n\tend\n\n\tself.total_run = self.total_run + number_run\n\tself.total_failed = self.total_failed + number_failed\n\n\tlocal info = debug.getinfo(2, \"S\")\n\tprint('----------------------------------------------------------------------------')\n\tprint(string.format(\"%s:  Total# %d, Failed# %d\", info.source, number_run, number_failed));\n\tprint('----------------------------------------------------------------------------')\nend\n\nfunction printOverallSummary()\n\tprint('\\n*****************************************************************************')\n\tprint(string.format(\"Overall Summary:  Total# %d, Failed# %d\", module.total_run, module.total_failed));\n\tprint('*****************************************************************************\\n')\n\nend\n\nreturn module;"
  },
  {
    "path": "sample/conf/server.cfg",
    "content": "luaw_server_config = {\n    server_ip = \"0.0.0.0\",\n    server_port = 7001,\n    connect_timeout = 4000,\n    read_timeout = 8000,\n    write_timeout = 8000\n}\n\nluaw_log_config = {\n    log_dir = \"./logs\",\n    log_file_basename = \"luaw-log\",\n    log_file_size_limit = 1024*1024,\n    log_file_count_limit = 9,\n    log_filename_timestamp_format = '%Y%m%d',\n    log_lines_buffer_count = 16,\n    syslog_server = \"127.0.0.1\",\n    syslog_port = 514,\n}\n\nluaw_webapp_config = {\n    base_dir = \"./webapps\"\n}\n\n"
  },
  {
    "path": "sample/proxy_handler.lua",
    "content": "local http_lib = require('luaw_http')\n--[[\n    Luaw allows you to replace it's default MVC/REST request handler with your own custom HTTP\nrequest handler implementation. To override the default HTTP request handler just set Luaw object's\nrequest_handler property to your custom Lua function. This function is passed in a low level connection\nobject for each incoming request instead of the normal request and response objects passed to REST handler.\nThe function is called on its own separate Luaw coroutine for each HTTP request so you don't have to worry\nabout multithreaded access to shared state inside the function.\n]]\n\nhttp_lib.request_handler =  function(conn)\n    conn:startReading()\n\n    -- loop to support HTTP 1.1 persistent (keep-alive) connections\n    while true do\n        local req = http_lib.newServerHttpRequest(conn)\n        local resp = http_lib.newServerHttpResponse(conn)\n\n        -- read and parse full request\n        req:readFull()\n        if (req.EOF) then\n            conn:close()\n            return \"connection reset by peer\"\n        end\n\n        local reqHeaders = req.headers\n        local beHost =  reqHeaders['backend-host']\n        local beURL = reqHeaders['backend-url']\n\n        if (beHost and beURL) then\n           local backendReq = http_lib.newClientHttpRequest()\n           backendReq.hostName = beHost\n           backendReq.url = beURL\n           backendReq.method = 'GET'\n           backendReq.headers = { Host = beHost }\n\n           local status, backendResp = pcall(backendReq.execute, backendReq)\n           if (status) then\n               resp:setStatus(backendResp:getStatus())\n               resp:appendBody(backendResp:getBody())\n               local beHeaders = backendResp.headers\n               for k,v in pairs(beHeaders) do\n                   if ((k ~= 'Transfer-Encoding')and(k ~= 'Content-Length')) then\n                       resp:addHeader(k,v)\n                   end\n               end\n               backendResp:close()\n            else\n               resp:setStatus(500)\n               resp:appendBody(\"connection to backend server failed\")\n           end\n        else\n            resp:setStatus(400)\n            resp:appendBody(\"Request must contain headers backend-host and backend-url\\n\")\n        end\n\n        local status, mesg = pcall(resp.flush, resp)\n        if (not status) then\n            conn:close()\n            return error(mesg)\n        end\n\n        if (req:shouldCloseConnection() or resp:shouldCloseConnection()) then\n            conn:close()\n            return \"connection reset by peer\"\n        end\n    end\nend\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-address.lua",
    "content": "registerHandler {\n    method = 'GET',\n    path = 'address/:city/#zip',\n\n    handler = function(req, resp, pathParams)\n        address = {\n            city = pathParams.city,\n            zip = pathParams.zip\n        }\n        return '/views/view-address.lua', address\n    end\n}\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-fileupload-display.lua",
    "content": "registerHandler {\n    method = 'GET',\n    path = 'showform',\n\n\thandler = function(req, resp, pathParams)\n\t    return [[\n            <!DOCTYPE html>\n            <html lang=\"en\">\n                <head>\n                    <meta charset=\"utf-8\"/>\n                    <title>upload</title>\n                </head>\n                <body>\n                    <form action=\"/myapp/filesupload\" method=\"post\" enctype=\"multipart/form-data\">\n                        <p><input type=\"text\" name=\"text1\" value=\"text default\">\n                        <p><input type=\"text\" name=\"text2\" value=\"ABCD\">\n                        <p><input type=\"file\" name=\"file1\">\n                        <p><input type=\"file\" name=\"file2\">\n                        <p><button type=\"submit\">Submit</button>\n                    </form>\n                </body>\n            </html>\n        ]]\n\tend\n}\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-fileupload-process.lua",
    "content": "local function append(buffer, str)\n    if (str) then\n        table.insert(buffer, str)\n        table.insert(buffer, \", \")\n    end\nend\n\n\nregisterHandler {\n    method = 'POST',\n    path = 'filesupload',\n\n\thandler = function(req, resp, pathParams)\n\t    if (req:isMultipart()) then\n            local token, fieldName, fileName, contentType\n            local buffer = {}\n            for token, fieldName, fileName, contentType in req:multiPartIterator() do\n                append(buffer, tostring(token))\n                append(buffer, fieldName)\n                append(buffer, fileName)\n                append(buffer, contentType)\n                table.insert(buffer,\"\\n\")\n            end\n            return table.concat(buffer)\n\t    end\n\t    return \"Not a multi-part file upload\"\n\tend\n}\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-hellouser.lua",
    "content": "registerHandler {\n    method = 'GET',\n    path = '/user/:username/#count',\n\n\thandler = function(req, resp, pathParams)\n\t\treturn \"Hello \"..pathParams.username..\"! You are user number \"..pathParams.count..\" to visit this site.\"\n\tend\n}\n\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-helloworld.lua",
    "content": "registerHandler {\n    method = 'GET',\n    path = 'helloworld',\n\n    handler = function(req, resp, pathParams)\n        return \"Hello World!\"\n    end\n}\n\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-read-lpack.lua",
    "content": "local utils_lib = require('luaw_utils')\nlocal http_lib = require('luaw_http')\nlocal lpack = require('luapack')\n\nregisterHandler {\n    method = 'GET',\n    path = 'readlpack',\n\n\thandler = function(req, resp, pathParams)\n        local backendReq = http_lib.newClientHttpRequest()\n        backendReq.hostName = 'localhost'\n        backendReq.port = 7001\n        backendReq.url = '/myapp/genlpack'\n        backendReq.method = 'GET'\n        backendReq.headers = { Host = 'localhost' }\n        local backendResp = backendReq:execute()\n        local lpackReader = lpack.newLPackReqReader(backendResp)\n        local mesg = lpackReader:read()\n\n        print('\\n================================\\n')\n        utils_lib.debugDump(backendResp.headers)\n        print('\\n================================\\n')\n        utils_lib.debugDump(mesg)\n        print('\\n================================\\n')\n        return \"OK\"\n\tend\n}\n"
  },
  {
    "path": "sample/webapps/myapp/handlers/handler-write-lpack.lua",
    "content": "local utils_lib = require('luaw_utils')\nlocal http_lib = require('luaw_http')\nlocal lpack = require('luapack')\n\nlocal big_str = \"abcdefghijklmnopqrstuvwxyz0123456789\"\nwhile (#big_str < 4096) do\n    big_str = big_str ..'-'..big_str\nend\n\nlocal mesg = {\n    name = \"Homer Simpson\",\n    gender = \"M\",\n    uint8 = 255,\n    uint16 = 256,\n    uint32 = 4294967295,\n    int8_neg = -128,\n    int8 = 127,\n    int16_neg = -1000,\n    int16 = 20000,\n    int32 = 32456,\n    int64= 17179869184,\n    int64_neg= -17179869184,\n    float = 0.0012,\n    float_neg = - 112.8,\n    double = 8589934592.13,\n    double_neg = -8589934592.28,\n    str = \"ABCD\",\n    bigstr = big_str,\n    positive = true,\n    negative = false,\n    kids = {\n        {\n            name = \"Lisa\",\n            gender = \"F\",\n            uint8 = 255,\n            uint16 = 256,\n            uint32 = 4294967295,\n            int8_neg = -128,\n            int8 = 127,\n            int16_neg = -1000,\n            int16 = 20000,\n            int32 = 32456,\n            int64= 17179869184,\n            int64_neg= -17179869184,\n            float = 0.0012,\n            float_neg = - 112.8,\n            double = 8589934592.13,\n            double_neg = -8589934592.28,\n            str = \"ABCD\",\n            bigstr = big_str,\n            positive = true,\n            negative = false\n          },\n        {\n            name = \"Bart\",\n            gender = \"M\",\n            uint8 = 255,\n            uint16 = 256,\n            uint32 = 4294967295,\n            int8_neg = -128,\n            int8 = 127,\n            int16_neg = -1000,\n            int16 = 20000,\n            int32 = 32456,\n            int64= 17179869184,\n            int64_neg= -17179869184,\n            float = 0.0012,\n            float_neg = - 112.8,\n            double = 8589934592.13,\n            double_neg = -8589934592.28,\n            str = \"ABCD\",\n            bigstr = big_str,\n            positive = true,\n            negative = false\n          },\n        {\n            name = \"Maggy\",\n            gender = \"?\"\n        }\n    }\n}\n\nlocal dict = {\n    \"name\",\n    \"gender\",\n    \"uint8\",\n    \"uint16\",\n    \"uint32\",\n    \"int8_neg\",\n    \"int8\",\n    \"int16_neg\",\n    \"int16\",\n    \"int32\",\n    \"int64\",\n    \"int64_neg\",\n    \"float\",\n    \"float_neg\",\n    \"double\",\n    \"double_neg\",\n    \"str\",\n    \"bigstr\",\n    \"positive\",\n    \"negative\",\n    big_str\n}\n\nregisterHandler {\n    method = 'GET',\n    path = 'genlpack',\n\n\thandler = function(req, resp, pathParams)\n\t    resp:setStatus(200)\n        local lpackWriter = lpack.newLPackRespWriter(resp)\n        if (req.params['dict'] == 'true') then\n            lpackWriter:useDictionary(dict)\n        end\n        lpackWriter:write(mesg)\n\tend\n}\n"
  },
  {
    "path": "sample/webapps/myapp/views/view-address.lua",
    "content": "BEGIN 'html'\n    BEGIN 'head'\n        BEGIN 'title'\n            TEXT 'Address'\n        END 'title'\n    END 'head'\n    BEGIN 'body'\n        BEGIN 'div' {class='address'}\n            BEGIN 'h1'\n                TEXT(model.title)\n            END 'h1'\n            BEGIN 'table' {border=\"1\", margin=\"1px\"}\n                BEGIN 'tr'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT 'City'\n                    END 'td'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT(model.city)\n                    END 'td'\n                END 'tr'\n                if (model.zip == 94086) then\n                    BEGIN 'tr'\n                        BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                            TEXT 'County'\n                        END 'td'\n                        BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                            TEXT 'Santa Clara'\n                        END 'td'\n                    END 'tr'\n                end\n                BEGIN 'tr'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT 'Zip'\n                    END 'td'\n                    BEGIN 'td' {style=\"padding: 3px 3px 3px 3px\"}\n                        TEXT(model.zip)\n                    END 'td'\n                END 'tr'\n            END 'table'\n        END 'div'\n    END 'body'\nEND 'html'\n\n"
  },
  {
    "path": "sample/webapps/myapp/web.lua",
    "content": "luaw_webapp = {\n    resourcePattern = \"handler%-.*%.lua\",\n    viewPattern = \"view%-.*%.lua\",\n}\n"
  },
  {
    "path": "src/Makefile",
    "content": "# Makefile for building Luaw\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\nCC?= gcc\nCFLAGS?= -O2 -g -Wall\nCFLAGS+= $(SYSCFLAGS) $(MYCFLAGS) -I../$(UVDIR)/include -I../$(LUADIR)/src\nLDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS)\nLIBS= ../$(UVLIB) -lpthread ../$(LUALIB) -lm $(SYSLIBS) $(MYLIBS)\n\n# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======\n\n# Build artifacts\nLUAW_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\nLUAW_BIN= luaw_server\nLUAW_CONF= server.cfg\nLUAW_SCRIPTS= luapack.lua luaw_init.lua luaw_logging.lua luaw_data_structs_lib.lua luaw_utils.lua \\\nluaw_scheduler.lua luaw_webapp.lua luaw_timer.lua luaw_tcp.lua luaw_http.lua luaw_constants.lua\n\n# How to install. If your install program does not support \"-p\", then\n# you may have to run ranlib on the installed liblua.a.\nINSTALL= install\nINSTALL_EXEC= $(INSTALL) -p -m 0755\nINSTALL_DATA= $(INSTALL) -p -m 0644\n\n#\n# If you don't have \"install\" you can use \"cp\" instead.\n# INSTALL= cp -p\n# INSTALL_EXEC= $(INSTALL)\n# INSTALL_DATA= $(INSTALL)\n\n# Other utilities.\nMKDIR= mkdir -p\nRM= rm -f\nRMDIR= rm -rf\n\n#where to install\nINSTALL_BIN=$(INSTALL_ROOT)/bin\nINSTALL_LIB=$(INSTALL_ROOT)/lib\nINSTALL_CONF=$(INSTALL_ROOT)/conf\nINSTALL_LOGS=$(INSTALL_ROOT)/logs\nINSTALL_WEBAPP=$(INSTALL_ROOT)/webapps\n\n# Targets start here.\n\nall: $(LUAW_BIN)\n\n$(LUAW_BIN): $(LUAW_OBJS)\n\t$(CC) -o $@ $(LDFLAGS) $(LUAW_OBJS) $(LIBS)\n\ninstall: check_install_root\n\t$(MKDIR) $(INSTALL_BIN)\n\t$(MKDIR) $(INSTALL_LIB)\n\t$(MKDIR) $(INSTALL_CONF)\n\t$(MKDIR) $(INSTALL_LOGS)\n\t$(MKDIR) $(INSTALL_WEBAPP)\n\t$(INSTALL_EXEC) $(LUAW_BIN) $(INSTALL_BIN)\n\tcd ../lib && $(INSTALL_DATA) $(LUAW_SCRIPTS) $(INSTALL_BIN)\n\tcd ../conf && $(INSTALL_DATA) $(LUAW_CONF) $(INSTALL_CONF)\n\ninstall-sample: install\n\tcd ../sample && cp -r * $(INSTALL_ROOT)\n\nuninstall: check_install_root\n\t$(RMDIR) $(INSTALL_ROOT)/*\n\ncheck_install_root:\nifndef INSTALL_ROOT\n\t$(error INSTALL_ROOT is undefined)\nendif\n\nclean:\n\t$(RM) $(LUAW_BIN) $(LUAW_OBJS)\n\necho:\n\t@echo \"CC= $(CC)\"\n\t@echo \"CFLAGS= $(CFLAGS)\"\n\t@echo \"LDFLAGS= $(SYSLDFLAGS)\"\n\t@echo \"LIBS= $(LIBS)\"\n\t@echo \"RM= $(RM)\"\n\n# list targets that do not create files (but not all makes understand .PHONY)\n.PHONY: default install install-sample uninstall check_install_root clean echo\n\n# Luaw object files\nhttp_parser.o: http_parser.c http_parser.h\nlua_lpack.o: lua_lpack.c lua_lpack.h luaw_common.h\nluaw_logging.o: luaw_logging.c luaw_logging.h luaw_common.h\nluaw_common.o: luaw_common.c luaw_common.h luaw_tcp.h luaw_http_parser.h luaw_timer.h lua_lpack.h\nluaw_http_parser.o: luaw_http_parser.c luaw_http_parser.h luaw_common.h luaw_tcp.h lfs.h\nluaw_server.o: luaw_server.c luaw_common.h luaw_tcp.h luaw_logging.h\nluaw_tcp.o: luaw_tcp.c luaw_tcp.h luaw_common.h http_parser.h luaw_http_parser.h luaw_tcp.h\nluaw_timer.o: luaw_timer.c luaw_timer.h luaw_common.h\nlfs.o: lfs.c lfs.h\n\n"
  },
  {
    "path": "src/http_parser.c",
    "content": "/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev\n *\n * Additional changes are licensed under the same terms as NGINX and\n * copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#include \"http_parser.h\"\n#include <assert.h>\n#include <stddef.h>\n#include <ctype.h>\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n#ifndef ULLONG_MAX\n# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */\n#endif\n\n#ifndef MIN\n# define MIN(a,b) ((a) < (b) ? (a) : (b))\n#endif\n\n#ifndef ARRAY_SIZE\n# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))\n#endif\n\n#ifndef BIT_AT\n# define BIT_AT(a, i)                                                \\\n  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \\\n   (1 << ((unsigned int) (i) & 7))))\n#endif\n\n#ifndef ELEM_AT\n# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))\n#endif\n\n#define SET_ERRNO(e)                                                 \\\ndo {                                                                 \\\n  parser->http_errno = (e);                                          \\\n} while(0)\n\n\n/* Run the notify callback FOR, returning ER if it fails */\n#define CALLBACK_NOTIFY_(FOR, ER)                                    \\\ndo {                                                                 \\\n  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \\\n                                                                     \\\n  if (settings->on_##FOR) {                                          \\\n    if (0 != settings->on_##FOR(parser)) {                           \\\n      SET_ERRNO(HPE_CB_##FOR);                                       \\\n    }                                                                \\\n                                                                     \\\n    /* We either errored above or got paused; get out */             \\\n    if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                       \\\n      return (ER);                                                   \\\n    }                                                                \\\n  }                                                                  \\\n} while (0)\n\n/* Run the notify callback FOR and consume the current byte */\n#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)\n\n/* Run the notify callback FOR and don't consume the current byte */\n#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)\n\n/* Run data callback FOR with LEN bytes, returning ER if it fails */\n#define CALLBACK_DATA_(FOR, LEN, ER)                                 \\\ndo {                                                                 \\\n  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \\\n                                                                     \\\n  if (FOR##_mark) {                                                  \\\n    if (settings->on_##FOR) {                                        \\\n      if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) {      \\\n        SET_ERRNO(HPE_CB_##FOR);                                     \\\n      }                                                              \\\n                                                                     \\\n      /* We either errored above or got paused; get out */           \\\n      if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                     \\\n        return (ER);                                                 \\\n      }                                                              \\\n    }                                                                \\\n    FOR##_mark = NULL;                                               \\\n  }                                                                  \\\n} while (0)\n  \n/* Run the data callback FOR and consume the current byte */\n#define CALLBACK_DATA(FOR)                                           \\\n    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)\n\n/* Run the data callback FOR and don't consume the current byte */\n#define CALLBACK_DATA_NOADVANCE(FOR)                                 \\\n    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)\n\n/* Set the mark FOR; non-destructive if mark is already set */\n#define MARK(FOR)                                                    \\\ndo {                                                                 \\\n  if (!FOR##_mark) {                                                 \\\n    FOR##_mark = p;                                                  \\\n  }                                                                  \\\n} while (0)\n\n\n#define PROXY_CONNECTION \"proxy-connection\"\n#define CONNECTION \"connection\"\n#define CONTENT_LENGTH \"content-length\"\n#define TRANSFER_ENCODING \"transfer-encoding\"\n#define UPGRADE \"upgrade\"\n#define CHUNKED \"chunked\"\n#define KEEP_ALIVE \"keep-alive\"\n#define CLOSE \"close\"\n\n\nstatic const char *method_strings[] =\n  {\n#define XX(num, name, string) #string,\n  HTTP_METHOD_MAP(XX)\n#undef XX\n  };\n\n\n/* Tokens as defined by rfc 2616. Also lowercases them.\n *        token       = 1*<any CHAR except CTLs or separators>\n *     separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *                    | \",\" | \";\" | \":\" | \"\\\" | <\">\n *                    | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *                    | \"{\" | \"}\" | SP | HT\n */\nstatic const char tokens[256] = {\n/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n        0,      '!',      0,      '#',     '$',     '%',     '&',    '\\'',\n/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n        0,       0,      '*',     '+',      0,      '-',     '.',      0,\n/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',\n/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n       '8',     '9',      0,       0,       0,       0,       0,       0,\n/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',\n/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',\n/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n/*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n       'x',     'y',     'z',      0,       0,       0,      '^',     '_',\n/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',\n/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',\n/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };\n\n\nstatic const int8_t unhex[256] =\n  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1\n  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  };\n\n\n#if HTTP_PARSER_STRICT\n# define T(v) 0\n#else\n# define T(v) v\n#endif\n\n\nstatic const uint8_t normal_url_char[32] = {\n/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */\n        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,\n/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,\n/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,\n/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };\n\n#undef T\n\nenum state\n  { s_dead = 1 /* important that this is > 0 */\n\n  , s_start_req_or_res\n  , s_res_or_resp_H\n  , s_start_res\n  , s_res_H\n  , s_res_HT\n  , s_res_HTT\n  , s_res_HTTP\n  , s_res_first_http_major\n  , s_res_http_major\n  , s_res_first_http_minor\n  , s_res_http_minor\n  , s_res_first_status_code\n  , s_res_status_code\n  , s_res_status_start\n  , s_res_status\n  , s_res_line_almost_done\n\n  , s_start_req\n\n  , s_req_method\n  , s_req_spaces_before_url\n  , s_req_schema\n  , s_req_schema_slash\n  , s_req_schema_slash_slash\n  , s_req_server_start\n  , s_req_server\n  , s_req_server_with_at\n  , s_req_path\n  , s_req_query_string_start\n  , s_req_query_string\n  , s_req_fragment_start\n  , s_req_fragment\n  , s_req_http_start\n  , s_req_http_H\n  , s_req_http_HT\n  , s_req_http_HTT\n  , s_req_http_HTTP\n  , s_req_first_http_major\n  , s_req_http_major\n  , s_req_first_http_minor\n  , s_req_http_minor\n  , s_req_line_almost_done\n\n  , s_header_field_start\n  , s_header_field\n  , s_header_value_start\n  , s_header_value\n  , s_header_value_lws\n\n  , s_header_almost_done\n\n  , s_chunk_size_start\n  , s_chunk_size\n  , s_chunk_parameters\n  , s_chunk_size_almost_done\n\n  , s_headers_almost_done\n  , s_headers_done\n\n  /* Important: 's_headers_done' must be the last 'header' state. All\n   * states beyond this must be 'body' states. It is used for overflow\n   * checking. See the PARSING_HEADER() macro.\n   */\n\n  , s_chunk_data\n  , s_chunk_data_almost_done\n  , s_chunk_data_done\n\n  , s_body_identity\n  , s_body_identity_eof\n\n  , s_message_done\n  };\n\n\n#define PARSING_HEADER(state) (state <= s_headers_done)\n\n\nenum header_states\n  { h_general = 0\n  , h_C\n  , h_CO\n  , h_CON\n\n  , h_matching_connection\n  , h_matching_proxy_connection\n  , h_matching_content_length\n  , h_matching_transfer_encoding\n  , h_matching_upgrade\n\n  , h_connection\n  , h_content_length\n  , h_transfer_encoding\n  , h_upgrade\n\n  , h_matching_transfer_encoding_chunked\n  , h_matching_connection_keep_alive\n  , h_matching_connection_close\n\n  , h_transfer_encoding_chunked\n  , h_connection_keep_alive\n  , h_connection_close\n  };\n\nenum http_host_state\n  {\n    s_http_host_dead = 1\n  , s_http_userinfo_start\n  , s_http_userinfo\n  , s_http_host_start\n  , s_http_host_v6_start\n  , s_http_host\n  , s_http_host_v6\n  , s_http_host_v6_end\n  , s_http_host_port_start\n  , s_http_host_port\n};\n\n/* Macros for character classes; depends on strict-mode  */\n#define CR                  '\\r'\n#define LF                  '\\n'\n#define LOWER(c)            (unsigned char)(c | 0x20)\n#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')\n#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')\n#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))\n#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))\n#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \\\n  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\\'' || (c) == '(' || \\\n  (c) == ')')\n#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \\\n  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \\\n  (c) == '$' || (c) == ',')\n\n#if HTTP_PARSER_STRICT\n#define TOKEN(c)            (tokens[(unsigned char)c])\n#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))\n#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')\n#else\n#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])\n#define IS_URL_CHAR(c)                                                         \\\n  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))\n#define IS_HOST_CHAR(c)                                                        \\\n  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')\n#endif\n\n\n#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)\n\n\n#if HTTP_PARSER_STRICT\n# define STRICT_CHECK(cond)                                          \\\ndo {                                                                 \\\n  if (cond) {                                                        \\\n    SET_ERRNO(HPE_STRICT);                                           \\\n    goto error;                                                      \\\n  }                                                                  \\\n} while (0)\n# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)\n#else\n# define STRICT_CHECK(cond)\n# define NEW_MESSAGE() start_state\n#endif\n\n\n/* Map errno values to strings for human-readable output */\n#define HTTP_STRERROR_GEN(n, s) { \"HPE_\" #n, s },\nstatic struct {\n  const char *name;\n  const char *description;\n} http_strerror_tab[] = {\n  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)\n};\n#undef HTTP_STRERROR_GEN\n\nint http_message_needs_eof(const http_parser *parser);\n\n/* Our URL parser.\n *\n * This is designed to be shared by http_parser_execute() for URL validation,\n * hence it has a state transition + byte-for-byte interface. In addition, it\n * is meant to be embedded in http_parser_parse_url(), which does the dirty\n * work of turning state transitions URL components for its API.\n *\n * This function should only be invoked with non-space characters. It is\n * assumed that the caller cares about (and can detect) the transition between\n * URL and non-URL states by looking for these.\n */\nstatic enum state\nparse_url_char(enum state s, const char ch)\n{\n  if (ch == ' ' || ch == '\\r' || ch == '\\n') {\n    return s_dead;\n  }\n\n#if HTTP_PARSER_STRICT\n  if (ch == '\\t' || ch == '\\f') {\n    return s_dead;\n  }\n#endif\n\n  switch (s) {\n    case s_req_spaces_before_url:\n      /* Proxied requests are followed by scheme of an absolute URI (alpha).\n       * All methods except CONNECT are followed by '/' or '*'.\n       */\n\n      if (ch == '/' || ch == '*') {\n        return s_req_path;\n      }\n\n      if (IS_ALPHA(ch)) {\n        return s_req_schema;\n      }\n\n      break;\n\n    case s_req_schema:\n      if (IS_ALPHA(ch)) {\n        return s;\n      }\n\n      if (ch == ':') {\n        return s_req_schema_slash;\n      }\n\n      break;\n\n    case s_req_schema_slash:\n      if (ch == '/') {\n        return s_req_schema_slash_slash;\n      }\n\n      break;\n\n    case s_req_schema_slash_slash:\n      if (ch == '/') {\n        return s_req_server_start;\n      }\n\n      break;\n\n    case s_req_server_with_at:\n      if (ch == '@') {\n        return s_dead;\n      }\n\n    /* FALLTHROUGH */\n    case s_req_server_start:\n    case s_req_server:\n      if (ch == '/') {\n        return s_req_path;\n      }\n\n      if (ch == '?') {\n        return s_req_query_string_start;\n      }\n\n      if (ch == '@') {\n        return s_req_server_with_at;\n      }\n\n      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {\n        return s_req_server;\n      }\n\n      break;\n\n    case s_req_path:\n      if (IS_URL_CHAR(ch)) {\n        return s;\n      }\n\n      switch (ch) {\n        case '?':\n          return s_req_query_string_start;\n\n        case '#':\n          return s_req_fragment_start;\n      }\n\n      break;\n\n    case s_req_query_string_start:\n    case s_req_query_string:\n      if (IS_URL_CHAR(ch)) {\n        return s_req_query_string;\n      }\n\n      switch (ch) {\n        case '?':\n          /* allow extra '?' in query string */\n          return s_req_query_string;\n\n        case '#':\n          return s_req_fragment_start;\n      }\n\n      break;\n\n    case s_req_fragment_start:\n      if (IS_URL_CHAR(ch)) {\n        return s_req_fragment;\n      }\n\n      switch (ch) {\n        case '?':\n          return s_req_fragment;\n\n        case '#':\n          return s;\n      }\n\n      break;\n\n    case s_req_fragment:\n      if (IS_URL_CHAR(ch)) {\n        return s;\n      }\n\n      switch (ch) {\n        case '?':\n        case '#':\n          return s;\n      }\n\n      break;\n\n    default:\n      break;\n  }\n\n  /* We should never fall out of the switch above unless there's an error */\n  return s_dead;\n}\n\nsize_t http_parser_execute (http_parser *parser,\n                            const http_parser_settings *settings,\n                            const char *data,\n                            size_t len)\n{\n  char c, ch;\n  int8_t unhex_val;\n  const char *p = data;\n  const char *header_field_mark = 0;\n  const char *header_value_mark = 0;\n  const char *url_mark = 0;\n  const char *body_mark = 0;\n  const char *status_mark = 0;\n\n  /* We're in an error state. Don't bother doing anything. */\n  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {\n    return 0;\n  }\n\n  if (len == 0) {\n    switch (parser->state) {\n      case s_body_identity_eof:\n        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if\n         * we got paused.\n         */\n        CALLBACK_NOTIFY_NOADVANCE(message_complete);\n        return 0;\n\n      case s_dead:\n      case s_start_req_or_res:\n      case s_start_res:\n      case s_start_req:\n        return 0;\n\n      default:\n        SET_ERRNO(HPE_INVALID_EOF_STATE);\n        return 1;\n    }\n  }\n\n\n  if (parser->state == s_header_field)\n    header_field_mark = data;\n  if (parser->state == s_header_value)\n    header_value_mark = data;\n  switch (parser->state) {\n  case s_req_path:\n  case s_req_schema:\n  case s_req_schema_slash:\n  case s_req_schema_slash_slash:\n  case s_req_server_start:\n  case s_req_server:\n  case s_req_server_with_at:\n  case s_req_query_string_start:\n  case s_req_query_string:\n  case s_req_fragment_start:\n  case s_req_fragment:\n    url_mark = data;\n    break;\n  case s_res_status:\n    status_mark = data;\n    break;\n  }\n\n  for (p=data; p != data + len; p++) {\n    ch = *p;\n\n    if (PARSING_HEADER(parser->state)) {\n      ++parser->nread;\n      /* Don't allow the total size of the HTTP headers (including the status\n       * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect\n       * embedders against denial-of-service attacks where the attacker feeds\n       * us a never-ending header that the embedder keeps buffering.\n       *\n       * This check is arguably the responsibility of embedders but we're doing\n       * it on the embedder's behalf because most won't bother and this way we\n       * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger\n       * than any reasonable request or response so this should never affect\n       * day-to-day operation.\n       */\n      if (parser->nread > HTTP_MAX_HEADER_SIZE) {\n        SET_ERRNO(HPE_HEADER_OVERFLOW);\n        goto error;\n      }\n    }\n\n    reexecute_byte:\n    switch (parser->state) {\n\n      case s_dead:\n        /* this state is used after a 'Connection: close' message\n         * the parser will error out if it reads another message\n         */\n        if (ch == CR || ch == LF)\n          break;\n\n        SET_ERRNO(HPE_CLOSED_CONNECTION);\n        goto error;\n\n      case s_start_req_or_res:\n      {\n        if (ch == CR || ch == LF)\n          break;\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        if (ch == 'H') {\n          parser->state = s_res_or_resp_H;\n\n          CALLBACK_NOTIFY(message_begin);\n        } else {\n          parser->type = HTTP_REQUEST;\n          parser->state = s_start_req;\n          goto reexecute_byte;\n        }\n\n        break;\n      }\n\n      case s_res_or_resp_H:\n        if (ch == 'T') {\n          parser->type = HTTP_RESPONSE;\n          parser->state = s_res_HT;\n        } else {\n          if (ch != 'E') {\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n          }\n\n          parser->type = HTTP_REQUEST;\n          parser->method = HTTP_HEAD;\n          parser->index = 2;\n          parser->state = s_req_method;\n        }\n        break;\n\n      case s_start_res:\n      {\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        switch (ch) {\n          case 'H':\n            parser->state = s_res_H;\n            break;\n\n          case CR:\n          case LF:\n            break;\n\n          default:\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n        }\n\n        CALLBACK_NOTIFY(message_begin);\n        break;\n      }\n\n      case s_res_H:\n        STRICT_CHECK(ch != 'T');\n        parser->state = s_res_HT;\n        break;\n\n      case s_res_HT:\n        STRICT_CHECK(ch != 'T');\n        parser->state = s_res_HTT;\n        break;\n\n      case s_res_HTT:\n        STRICT_CHECK(ch != 'P');\n        parser->state = s_res_HTTP;\n        break;\n\n      case s_res_HTTP:\n        STRICT_CHECK(ch != '/');\n        parser->state = s_res_first_http_major;\n        break;\n\n      case s_res_first_http_major:\n        if (ch < '0' || ch > '9') {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        parser->state = s_res_http_major;\n        break;\n\n      /* major HTTP version or dot */\n      case s_res_http_major:\n      {\n        if (ch == '.') {\n          parser->state = s_res_first_http_minor;\n          break;\n        }\n\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major *= 10;\n        parser->http_major += ch - '0';\n\n        if (parser->http_major > 999) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* first digit of minor HTTP version */\n      case s_res_first_http_minor:\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        parser->state = s_res_http_minor;\n        break;\n\n      /* minor HTTP version or end of request line */\n      case s_res_http_minor:\n      {\n        if (ch == ' ') {\n          parser->state = s_res_first_status_code;\n          break;\n        }\n\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor *= 10;\n        parser->http_minor += ch - '0';\n\n        if (parser->http_minor > 999) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_res_first_status_code:\n      {\n        if (!IS_NUM(ch)) {\n          if (ch == ' ') {\n            break;\n          }\n\n          SET_ERRNO(HPE_INVALID_STATUS);\n          goto error;\n        }\n        parser->status_code = ch - '0';\n        parser->state = s_res_status_code;\n        break;\n      }\n\n      case s_res_status_code:\n      {\n        if (!IS_NUM(ch)) {\n          switch (ch) {\n            case ' ':\n              parser->state = s_res_status_start;\n              break;\n            case CR:\n              parser->state = s_res_line_almost_done;\n              break;\n            case LF:\n              parser->state = s_header_field_start;\n              break;\n            default:\n              SET_ERRNO(HPE_INVALID_STATUS);\n              goto error;\n          }\n          break;\n        }\n\n        parser->status_code *= 10;\n        parser->status_code += ch - '0';\n\n        if (parser->status_code > 999) {\n          SET_ERRNO(HPE_INVALID_STATUS);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_res_status_start:\n      {\n        if (ch == CR) {\n          parser->state = s_res_line_almost_done;\n          break;\n        }\n\n        if (ch == LF) {\n          parser->state = s_header_field_start;\n          break;\n        }\n\n        MARK(status);\n        parser->state = s_res_status;\n        parser->index = 0;\n        break;\n      }\n\n      case s_res_status:\n        if (ch == CR) {\n          parser->state = s_res_line_almost_done;\n          CALLBACK_DATA(status);\n          break;\n        }\n\n        if (ch == LF) {\n          parser->state = s_header_field_start;\n          CALLBACK_DATA(status);\n          break;\n        }\n\n        break;\n\n      case s_res_line_almost_done:\n        STRICT_CHECK(ch != LF);\n        parser->state = s_header_field_start;\n        break;\n\n      case s_start_req:\n      {\n        if (ch == CR || ch == LF)\n          break;\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        if (!IS_ALPHA(ch)) {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        parser->method = (enum http_method) 0;\n        parser->index = 1;\n        switch (ch) {\n          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;\n          case 'D': parser->method = HTTP_DELETE; break;\n          case 'G': parser->method = HTTP_GET; break;\n          case 'H': parser->method = HTTP_HEAD; break;\n          case 'L': parser->method = HTTP_LOCK; break;\n          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;\n          case 'N': parser->method = HTTP_NOTIFY; break;\n          case 'O': parser->method = HTTP_OPTIONS; break;\n          case 'P': parser->method = HTTP_POST;\n            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */\n            break;\n          case 'R': parser->method = HTTP_REPORT; break;\n          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;\n          case 'T': parser->method = HTTP_TRACE; break;\n          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;\n          default:\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n        }\n        parser->state = s_req_method;\n\n        CALLBACK_NOTIFY(message_begin);\n\n        break;\n      }\n\n      case s_req_method:\n      {\n        const char *matcher;\n        if (ch == '\\0') {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        matcher = method_strings[parser->method];\n        if (ch == ' ' && matcher[parser->index] == '\\0') {\n          parser->state = s_req_spaces_before_url;\n        } else if (ch == matcher[parser->index]) {\n          ; /* nada */\n        } else if (parser->method == HTTP_CONNECT) {\n          if (parser->index == 1 && ch == 'H') {\n            parser->method = HTTP_CHECKOUT;\n          } else if (parser->index == 2  && ch == 'P') {\n            parser->method = HTTP_COPY;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->method == HTTP_MKCOL) {\n          if (parser->index == 1 && ch == 'O') {\n            parser->method = HTTP_MOVE;\n          } else if (parser->index == 1 && ch == 'E') {\n            parser->method = HTTP_MERGE;\n          } else if (parser->index == 1 && ch == '-') {\n            parser->method = HTTP_MSEARCH;\n          } else if (parser->index == 2 && ch == 'A') {\n            parser->method = HTTP_MKACTIVITY;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->method == HTTP_SUBSCRIBE) {\n          if (parser->index == 1 && ch == 'E') {\n            parser->method = HTTP_SEARCH;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 1 && parser->method == HTTP_POST) {\n          if (ch == 'R') {\n            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */\n          } else if (ch == 'U') {\n            parser->method = HTTP_PUT; /* or HTTP_PURGE */\n          } else if (ch == 'A') {\n            parser->method = HTTP_PATCH;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 2) {\n          if (parser->method == HTTP_PUT) {\n            if (ch == 'R') {\n              parser->method = HTTP_PURGE;\n            } else {\n              SET_ERRNO(HPE_INVALID_METHOD);\n              goto error;\n            }\n          } else if (parser->method == HTTP_UNLOCK) {\n            if (ch == 'S') {\n              parser->method = HTTP_UNSUBSCRIBE;\n            } else {\n              SET_ERRNO(HPE_INVALID_METHOD);\n              goto error;\n            }\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {\n          parser->method = HTTP_PROPPATCH;\n        } else {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        ++parser->index;\n        break;\n      }\n\n      case s_req_spaces_before_url:\n      {\n        if (ch == ' ') break;\n\n        MARK(url);\n        if (parser->method == HTTP_CONNECT) {\n          parser->state = s_req_server_start;\n        }\n\n        parser->state = parse_url_char((enum state)parser->state, ch);\n        if (parser->state == s_dead) {\n          SET_ERRNO(HPE_INVALID_URL);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_req_schema:\n      case s_req_schema_slash:\n      case s_req_schema_slash_slash:\n      case s_req_server_start:\n      {\n        switch (ch) {\n          /* No whitespace allowed here */\n          case ' ':\n          case CR:\n          case LF:\n            SET_ERRNO(HPE_INVALID_URL);\n            goto error;\n          default:\n            parser->state = parse_url_char((enum state)parser->state, ch);\n            if (parser->state == s_dead) {\n              SET_ERRNO(HPE_INVALID_URL);\n              goto error;\n            }\n        }\n\n        break;\n      }\n\n      case s_req_server:\n      case s_req_server_with_at:\n      case s_req_path:\n      case s_req_query_string_start:\n      case s_req_query_string:\n      case s_req_fragment_start:\n      case s_req_fragment:\n      {\n        switch (ch) {\n          case ' ':\n            parser->state = s_req_http_start;\n            CALLBACK_DATA(url);\n            break;\n          case CR:\n          case LF:\n            parser->http_major = 0;\n            parser->http_minor = 9;\n            parser->state = (ch == CR) ?\n              s_req_line_almost_done :\n              s_header_field_start;\n            CALLBACK_DATA(url);\n            break;\n          default:\n            parser->state = parse_url_char((enum state)parser->state, ch);\n            if (parser->state == s_dead) {\n              SET_ERRNO(HPE_INVALID_URL);\n              goto error;\n            }\n        }\n        break;\n      }\n\n      case s_req_http_start:\n        switch (ch) {\n          case 'H':\n            parser->state = s_req_http_H;\n            break;\n          case ' ':\n            break;\n          default:\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n        }\n        break;\n\n      case s_req_http_H:\n        STRICT_CHECK(ch != 'T');\n        parser->state = s_req_http_HT;\n        break;\n\n      case s_req_http_HT:\n        STRICT_CHECK(ch != 'T');\n        parser->state = s_req_http_HTT;\n        break;\n\n      case s_req_http_HTT:\n        STRICT_CHECK(ch != 'P');\n        parser->state = s_req_http_HTTP;\n        break;\n\n      case s_req_http_HTTP:\n        STRICT_CHECK(ch != '/');\n        parser->state = s_req_first_http_major;\n        break;\n\n      /* first digit of major HTTP version */\n      case s_req_first_http_major:\n        if (ch < '1' || ch > '9') {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        parser->state = s_req_http_major;\n        break;\n\n      /* major HTTP version or dot */\n      case s_req_http_major:\n      {\n        if (ch == '.') {\n          parser->state = s_req_first_http_minor;\n          break;\n        }\n\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major *= 10;\n        parser->http_major += ch - '0';\n\n        if (parser->http_major > 999) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* first digit of minor HTTP version */\n      case s_req_first_http_minor:\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        parser->state = s_req_http_minor;\n        break;\n\n      /* minor HTTP version or end of request line */\n      case s_req_http_minor:\n      {\n        if (ch == CR) {\n          parser->state = s_req_line_almost_done;\n          break;\n        }\n\n        if (ch == LF) {\n          parser->state = s_header_field_start;\n          break;\n        }\n\n        /* XXX allow spaces after digit? */\n\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor *= 10;\n        parser->http_minor += ch - '0';\n\n        if (parser->http_minor > 999) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* end of request line */\n      case s_req_line_almost_done:\n      {\n        if (ch != LF) {\n          SET_ERRNO(HPE_LF_EXPECTED);\n          goto error;\n        }\n\n        parser->state = s_header_field_start;\n        break;\n      }\n\n      case s_header_field_start:\n      {\n        if (ch == CR) {\n          parser->state = s_headers_almost_done;\n          break;\n        }\n\n        if (ch == LF) {\n          /* they might be just sending \\n instead of \\r\\n so this would be\n           * the second \\n to denote the end of headers*/\n          parser->state = s_headers_almost_done;\n          goto reexecute_byte;\n        }\n\n        c = TOKEN(ch);\n\n        if (!c) {\n          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n          goto error;\n        }\n\n        MARK(header_field);\n\n        parser->index = 0;\n        parser->state = s_header_field;\n\n        switch (c) {\n          case 'c':\n            parser->header_state = h_C;\n            break;\n\n          case 'p':\n            parser->header_state = h_matching_proxy_connection;\n            break;\n\n          case 't':\n            parser->header_state = h_matching_transfer_encoding;\n            break;\n\n          case 'u':\n            parser->header_state = h_matching_upgrade;\n            break;\n\n          default:\n            parser->header_state = h_general;\n            break;\n        }\n        break;\n      }\n\n      case s_header_field:\n      {\n        c = TOKEN(ch);\n\n        if (c) {\n          switch (parser->header_state) {\n            case h_general:\n              break;\n\n            case h_C:\n              parser->index++;\n              parser->header_state = (c == 'o' ? h_CO : h_general);\n              break;\n\n            case h_CO:\n              parser->index++;\n              parser->header_state = (c == 'n' ? h_CON : h_general);\n              break;\n\n            case h_CON:\n              parser->index++;\n              switch (c) {\n                case 'n':\n                  parser->header_state = h_matching_connection;\n                  break;\n                case 't':\n                  parser->header_state = h_matching_content_length;\n                  break;\n                default:\n                  parser->header_state = h_general;\n                  break;\n              }\n              break;\n\n            /* connection */\n\n            case h_matching_connection:\n              parser->index++;\n              if (parser->index > sizeof(CONNECTION)-1\n                  || c != CONNECTION[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(CONNECTION)-2) {\n                parser->header_state = h_connection;\n              }\n              break;\n\n            /* proxy-connection */\n\n            case h_matching_proxy_connection:\n              parser->index++;\n              if (parser->index > sizeof(PROXY_CONNECTION)-1\n                  || c != PROXY_CONNECTION[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {\n                parser->header_state = h_connection;\n              }\n              break;\n\n            /* content-length */\n\n            case h_matching_content_length:\n              parser->index++;\n              if (parser->index > sizeof(CONTENT_LENGTH)-1\n                  || c != CONTENT_LENGTH[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {\n                parser->header_state = h_content_length;\n              }\n              break;\n\n            /* transfer-encoding */\n\n            case h_matching_transfer_encoding:\n              parser->index++;\n              if (parser->index > sizeof(TRANSFER_ENCODING)-1\n                  || c != TRANSFER_ENCODING[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {\n                parser->header_state = h_transfer_encoding;\n              }\n              break;\n\n            /* upgrade */\n\n            case h_matching_upgrade:\n              parser->index++;\n              if (parser->index > sizeof(UPGRADE)-1\n                  || c != UPGRADE[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(UPGRADE)-2) {\n                parser->header_state = h_upgrade;\n              }\n              break;\n\n            case h_connection:\n            case h_content_length:\n            case h_transfer_encoding:\n            case h_upgrade:\n              if (ch != ' ') parser->header_state = h_general;\n              break;\n\n            default:\n              assert(0 && \"Unknown header_state\");\n              break;\n          }\n          break;\n        }\n\n        if (ch == ':') {\n          parser->state = s_header_value_start;\n          CALLBACK_DATA(header_field);\n          break;\n        }\n\n        if (ch == CR) {\n          parser->state = s_header_almost_done;\n          CALLBACK_DATA(header_field);\n          break;\n        }\n\n        if (ch == LF) {\n          parser->state = s_header_field_start;\n          CALLBACK_DATA(header_field);\n          break;\n        }\n\n        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n        goto error;\n      }\n\n      case s_header_value_start:\n      {\n        if (ch == ' ' || ch == '\\t') break;\n\n        MARK(header_value);\n\n        parser->state = s_header_value;\n        parser->index = 0;\n\n        if (ch == CR) {\n          parser->header_state = h_general;\n          parser->state = s_header_almost_done;\n          CALLBACK_DATA(header_value);\n          break;\n        }\n\n        if (ch == LF) {\n          parser->state = s_header_field_start;\n          CALLBACK_DATA(header_value);\n          break;\n        }\n\n        c = LOWER(ch);\n\n        switch (parser->header_state) {\n          case h_upgrade:\n            parser->flags |= F_UPGRADE;\n            parser->header_state = h_general;\n            break;\n\n          case h_transfer_encoding:\n            /* looking for 'Transfer-Encoding: chunked' */\n            if ('c' == c) {\n              parser->header_state = h_matching_transfer_encoding_chunked;\n            } else {\n              parser->header_state = h_general;\n            }\n            break;\n\n          case h_content_length:\n            if (!IS_NUM(ch)) {\n              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n              goto error;\n            }\n\n            parser->content_length = ch - '0';\n            break;\n\n          case h_connection:\n            /* looking for 'Connection: keep-alive' */\n            if (c == 'k') {\n              parser->header_state = h_matching_connection_keep_alive;\n            /* looking for 'Connection: close' */\n            } else if (c == 'c') {\n              parser->header_state = h_matching_connection_close;\n            } else {\n              parser->header_state = h_general;\n            }\n            break;\n\n          default:\n            parser->header_state = h_general;\n            break;\n        }\n        break;\n      }\n\n      case s_header_value:\n      {\n\n        if (ch == CR) {\n          parser->state = s_header_almost_done;\n          CALLBACK_DATA(header_value);\n          break;\n        }\n\n        if (ch == LF) {\n          parser->state = s_header_almost_done;\n          CALLBACK_DATA_NOADVANCE(header_value);\n          goto reexecute_byte;\n        }\n\n        c = LOWER(ch);\n\n        switch (parser->header_state) {\n          case h_general:\n            break;\n\n          case h_connection:\n          case h_transfer_encoding:\n            assert(0 && \"Shouldn't get here.\");\n            break;\n\n          case h_content_length:\n          {\n            uint64_t t;\n\n            if (ch == ' ') break;\n\n            if (!IS_NUM(ch)) {\n              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n              goto error;\n            }\n\n            t = parser->content_length;\n            t *= 10;\n            t += ch - '0';\n\n            /* Overflow? Test against a conservative limit for simplicity. */\n            if ((ULLONG_MAX - 10) / 10 < parser->content_length) {\n              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n              goto error;\n            }\n\n            parser->content_length = t;\n            break;\n          }\n\n          /* Transfer-Encoding: chunked */\n          case h_matching_transfer_encoding_chunked:\n            parser->index++;\n            if (parser->index > sizeof(CHUNKED)-1\n                || c != CHUNKED[parser->index]) {\n              parser->header_state = h_general;\n            } else if (parser->index == sizeof(CHUNKED)-2) {\n              parser->header_state = h_transfer_encoding_chunked;\n            }\n            break;\n\n          /* looking for 'Connection: keep-alive' */\n          case h_matching_connection_keep_alive:\n            parser->index++;\n            if (parser->index > sizeof(KEEP_ALIVE)-1\n                || c != KEEP_ALIVE[parser->index]) {\n              parser->header_state = h_general;\n            } else if (parser->index == sizeof(KEEP_ALIVE)-2) {\n              parser->header_state = h_connection_keep_alive;\n            }\n            break;\n\n          /* looking for 'Connection: close' */\n          case h_matching_connection_close:\n            parser->index++;\n            if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {\n              parser->header_state = h_general;\n            } else if (parser->index == sizeof(CLOSE)-2) {\n              parser->header_state = h_connection_close;\n            }\n            break;\n\n          case h_transfer_encoding_chunked:\n          case h_connection_keep_alive:\n          case h_connection_close:\n            if (ch != ' ') parser->header_state = h_general;\n            break;\n\n          default:\n            parser->state = s_header_value;\n            parser->header_state = h_general;\n            break;\n        }\n        break;\n      }\n\n      case s_header_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        parser->state = s_header_value_lws;\n\n        switch (parser->header_state) {\n          case h_connection_keep_alive:\n            parser->flags |= F_CONNECTION_KEEP_ALIVE;\n            break;\n          case h_connection_close:\n            parser->flags |= F_CONNECTION_CLOSE;\n            break;\n          case h_transfer_encoding_chunked:\n            parser->flags |= F_CHUNKED;\n            break;\n          default:\n            break;\n        }\n\n        break;\n      }\n\n      case s_header_value_lws:\n      {\n        if (ch == ' ' || ch == '\\t')\n          parser->state = s_header_value_start;\n        else\n        {\n          parser->state = s_header_field_start;\n          goto reexecute_byte;\n        }\n        break;\n      }\n\n      case s_headers_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        if (parser->flags & F_TRAILING) {\n          /* End of a chunked request */\n          parser->state = NEW_MESSAGE();\n          CALLBACK_NOTIFY(message_complete);\n          break;\n        }\n\n        parser->state = s_headers_done;\n\n        /* Set this here so that on_headers_complete() callbacks can see it */\n        parser->upgrade =\n          (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);\n\n        /* Here we call the headers_complete callback. This is somewhat\n         * different than other callbacks because if the user returns 1, we\n         * will interpret that as saying that this message has no body. This\n         * is needed for the annoying case of recieving a response to a HEAD\n         * request.\n         *\n         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so\n         * we have to simulate it by handling a change in errno below.\n         */\n        if (settings->on_headers_complete) {\n          switch (settings->on_headers_complete(parser)) {\n            case 0:\n              break;\n\n            case 1:\n              parser->flags |= F_SKIPBODY;\n              break;\n\n            default:\n              SET_ERRNO(HPE_CB_headers_complete);\n              return p - data; /* Error */\n          }\n        }\n\n        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {\n          return p - data;\n        }\n\n        goto reexecute_byte;\n      }\n\n      case s_headers_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        parser->nread = 0;\n\n        /* Exit, the rest of the connect is in a different protocol. */\n        if (parser->upgrade) {\n          parser->state = NEW_MESSAGE();\n          CALLBACK_NOTIFY(message_complete);\n          return (p - data) + 1;\n        }\n\n        if (parser->flags & F_SKIPBODY) {\n          parser->state = NEW_MESSAGE();\n          CALLBACK_NOTIFY(message_complete);\n        } else if (parser->flags & F_CHUNKED) {\n          /* chunked encoding - ignore Content-Length header */\n          parser->state = s_chunk_size_start;\n        } else {\n          if (parser->content_length == 0) {\n            /* Content-Length header given but zero: Content-Length: 0\\r\\n */\n            parser->state = NEW_MESSAGE();\n            CALLBACK_NOTIFY(message_complete);\n          } else if (parser->content_length != ULLONG_MAX) {\n            /* Content-Length header given and non-zero */\n            parser->state = s_body_identity;\n          } else {\n            if (parser->type == HTTP_REQUEST ||\n                !http_message_needs_eof(parser)) {\n              /* Assume content-length 0 - read the next */\n              parser->state = NEW_MESSAGE();\n              CALLBACK_NOTIFY(message_complete);\n            } else {\n              /* Read body until EOF */\n              parser->state = s_body_identity_eof;\n            }\n          }\n        }\n\n        break;\n      }\n\n      case s_body_identity:\n      {\n        uint64_t to_read = MIN(parser->content_length,\n                               (uint64_t) ((data + len) - p));\n\n        assert(parser->content_length != 0\n            && parser->content_length != ULLONG_MAX);\n\n        /* The difference between advancing content_length and p is because\n         * the latter will automaticaly advance on the next loop iteration.\n         * Further, if content_length ends up at 0, we want to see the last\n         * byte again for our message complete callback.\n         */\n        MARK(body);\n        parser->content_length -= to_read;\n        p += to_read - 1;\n\n        if (parser->content_length == 0) {\n          parser->state = s_message_done;\n\n          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.\n           *\n           * The alternative to doing this is to wait for the next byte to\n           * trigger the data callback, just as in every other case. The\n           * problem with this is that this makes it difficult for the test\n           * harness to distinguish between complete-on-EOF and\n           * complete-on-length. It's not clear that this distinction is\n           * important for applications, but let's keep it for now.\n           */\n          CALLBACK_DATA_(body, p - body_mark + 1, p - data);\n          goto reexecute_byte;\n        }\n\n        break;\n      }\n\n      /* read until EOF */\n      case s_body_identity_eof:\n        MARK(body);\n        p = data + len - 1;\n\n        break;\n\n      case s_message_done:\n        parser->state = NEW_MESSAGE();\n        CALLBACK_NOTIFY(message_complete);\n        break;\n\n      case s_chunk_size_start:\n      {\n        assert(parser->nread == 1);\n        assert(parser->flags & F_CHUNKED);\n\n        unhex_val = unhex[(unsigned char)ch];\n        if (unhex_val == -1) {\n          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);\n          goto error;\n        }\n\n        parser->content_length = unhex_val;\n        parser->state = s_chunk_size;\n        break;\n      }\n\n      case s_chunk_size:\n      {\n        uint64_t t;\n\n        assert(parser->flags & F_CHUNKED);\n\n        if (ch == CR) {\n          parser->state = s_chunk_size_almost_done;\n          break;\n        }\n\n        unhex_val = unhex[(unsigned char)ch];\n\n        if (unhex_val == -1) {\n          if (ch == ';' || ch == ' ') {\n            parser->state = s_chunk_parameters;\n            break;\n          }\n\n          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);\n          goto error;\n        }\n\n        t = parser->content_length;\n        t *= 16;\n        t += unhex_val;\n\n        /* Overflow? Test against a conservative limit for simplicity. */\n        if ((ULLONG_MAX - 16) / 16 < parser->content_length) {\n          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n          goto error;\n        }\n\n        parser->content_length = t;\n        break;\n      }\n\n      case s_chunk_parameters:\n      {\n        assert(parser->flags & F_CHUNKED);\n        /* just ignore this shit. TODO check for overflow */\n        if (ch == CR) {\n          parser->state = s_chunk_size_almost_done;\n          break;\n        }\n        break;\n      }\n\n      case s_chunk_size_almost_done:\n      {\n        assert(parser->flags & F_CHUNKED);\n        STRICT_CHECK(ch != LF);\n\n        parser->nread = 0;\n\n        if (parser->content_length == 0) {\n          parser->flags |= F_TRAILING;\n          parser->state = s_header_field_start;\n        } else {\n          parser->state = s_chunk_data;\n        }\n        break;\n      }\n\n      case s_chunk_data:\n      {\n        uint64_t to_read = MIN(parser->content_length,\n                               (uint64_t) ((data + len) - p));\n\n        assert(parser->flags & F_CHUNKED);\n        assert(parser->content_length != 0\n            && parser->content_length != ULLONG_MAX);\n\n        /* See the explanation in s_body_identity for why the content\n         * length and data pointers are managed this way.\n         */\n        MARK(body);\n        parser->content_length -= to_read;\n        p += to_read - 1;\n\n        if (parser->content_length == 0) {\n          parser->state = s_chunk_data_almost_done;\n        }\n\n        break;\n      }\n\n      case s_chunk_data_almost_done:\n        assert(parser->flags & F_CHUNKED);\n        assert(parser->content_length == 0);\n        STRICT_CHECK(ch != CR);\n        parser->state = s_chunk_data_done;\n        CALLBACK_DATA(body);\n        break;\n\n      case s_chunk_data_done:\n        assert(parser->flags & F_CHUNKED);\n        STRICT_CHECK(ch != LF);\n        parser->nread = 0;\n        parser->state = s_chunk_size_start;\n        break;\n\n      default:\n        assert(0 && \"unhandled state\");\n        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);\n        goto error;\n    }\n  }\n\n  /* Run callbacks for any marks that we have leftover after we ran our of\n   * bytes. There should be at most one of these set, so it's OK to invoke\n   * them in series (unset marks will not result in callbacks).\n   *\n   * We use the NOADVANCE() variety of callbacks here because 'p' has already\n   * overflowed 'data' and this allows us to correct for the off-by-one that\n   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'\n   * value that's in-bounds).\n   */\n\n  assert(((header_field_mark ? 1 : 0) +\n          (header_value_mark ? 1 : 0) +\n          (url_mark ? 1 : 0)  +\n          (body_mark ? 1 : 0) +\n          (status_mark ? 1 : 0)) <= 1);\n\n  CALLBACK_DATA_NOADVANCE(header_field);\n  CALLBACK_DATA_NOADVANCE(header_value);\n  CALLBACK_DATA_NOADVANCE(url);\n  CALLBACK_DATA_NOADVANCE(body);\n  CALLBACK_DATA_NOADVANCE(status);\n\n  return len;\n\nerror:\n  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {\n    SET_ERRNO(HPE_UNKNOWN);\n  }\n\n  return (p - data);\n}\n\n\n/* Does the parser need to see an EOF to find the end of the message? */\nint\nhttp_message_needs_eof (const http_parser *parser)\n{\n  if (parser->type == HTTP_REQUEST) {\n    return 0;\n  }\n\n  /* See RFC 2616 section 4.4 */\n  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */\n      parser->status_code == 204 ||     /* No Content */\n      parser->status_code == 304 ||     /* Not Modified */\n      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */\n    return 0;\n  }\n\n  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {\n    return 0;\n  }\n\n  return 1;\n}\n\n\nint\nhttp_should_keep_alive (const http_parser *parser)\n{\n  if (parser->http_major > 0 && parser->http_minor > 0) {\n    /* HTTP/1.1 */\n    if (parser->flags & F_CONNECTION_CLOSE) {\n      return 0;\n    }\n  } else {\n    /* HTTP/1.0 or earlier */\n    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {\n      return 0;\n    }\n  }\n\n  return !http_message_needs_eof(parser);\n}\n\n\nconst char *\nhttp_method_str (enum http_method m)\n{\n  return ELEM_AT(method_strings, m, \"<unknown>\");\n}\n\n\nvoid\nhttp_parser_init (http_parser *parser, enum http_parser_type t)\n{\n  void *data = parser->data; /* preserve application data */\n  memset(parser, 0, sizeof(*parser));\n  parser->data = data;\n  parser->type = t;\n  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));\n  parser->http_errno = HPE_OK;\n}\n\nconst char *\nhttp_errno_name(enum http_errno err) {\n  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));\n  return http_strerror_tab[err].name;\n}\n\nconst char *\nhttp_errno_description(enum http_errno err) {\n  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));\n  return http_strerror_tab[err].description;\n}\n\nstatic enum http_host_state\nhttp_parse_host_char(enum http_host_state s, const char ch) {\n  switch(s) {\n    case s_http_userinfo:\n    case s_http_userinfo_start:\n      if (ch == '@') {\n        return s_http_host_start;\n      }\n\n      if (IS_USERINFO_CHAR(ch)) {\n        return s_http_userinfo;\n      }\n      break;\n\n    case s_http_host_start:\n      if (ch == '[') {\n        return s_http_host_v6_start;\n      }\n\n      if (IS_HOST_CHAR(ch)) {\n        return s_http_host;\n      }\n\n      break;\n\n    case s_http_host:\n      if (IS_HOST_CHAR(ch)) {\n        return s_http_host;\n      }\n\n    /* FALLTHROUGH */\n    case s_http_host_v6_end:\n      if (ch == ':') {\n        return s_http_host_port_start;\n      }\n\n      break;\n\n    case s_http_host_v6:\n      if (ch == ']') {\n        return s_http_host_v6_end;\n      }\n\n    /* FALLTHROUGH */\n    case s_http_host_v6_start:\n      if (IS_HEX(ch) || ch == ':' || ch == '.') {\n        return s_http_host_v6;\n      }\n\n      break;\n\n    case s_http_host_port:\n    case s_http_host_port_start:\n      if (IS_NUM(ch)) {\n        return s_http_host_port;\n      }\n\n      break;\n\n    default:\n      break;\n  }\n  return s_http_host_dead;\n}\n\nstatic int\nhttp_parse_host(const char * buf, struct http_parser_url *u, int found_at) {\n  enum http_host_state s;\n\n  const char *p;\n  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;\n\n  u->field_data[UF_HOST].len = 0;\n\n  s = found_at ? s_http_userinfo_start : s_http_host_start;\n\n  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {\n    enum http_host_state new_s = http_parse_host_char(s, *p);\n\n    if (new_s == s_http_host_dead) {\n      return 1;\n    }\n\n    switch(new_s) {\n      case s_http_host:\n        if (s != s_http_host) {\n          u->field_data[UF_HOST].off = p - buf;\n        }\n        u->field_data[UF_HOST].len++;\n        break;\n\n      case s_http_host_v6:\n        if (s != s_http_host_v6) {\n          u->field_data[UF_HOST].off = p - buf;\n        }\n        u->field_data[UF_HOST].len++;\n        break;\n\n      case s_http_host_port:\n        if (s != s_http_host_port) {\n          u->field_data[UF_PORT].off = p - buf;\n          u->field_data[UF_PORT].len = 0;\n          u->field_set |= (1 << UF_PORT);\n        }\n        u->field_data[UF_PORT].len++;\n        break;\n\n      case s_http_userinfo:\n        if (s != s_http_userinfo) {\n          u->field_data[UF_USERINFO].off = p - buf ;\n          u->field_data[UF_USERINFO].len = 0;\n          u->field_set |= (1 << UF_USERINFO);\n        }\n        u->field_data[UF_USERINFO].len++;\n        break;\n\n      default:\n        break;\n    }\n    s = new_s;\n  }\n\n  /* Make sure we don't end somewhere unexpected */\n  switch (s) {\n    case s_http_host_start:\n    case s_http_host_v6_start:\n    case s_http_host_v6:\n    case s_http_host_port_start:\n    case s_http_userinfo:\n    case s_http_userinfo_start:\n      return 1;\n    default:\n      break;\n  }\n\n  return 0;\n}\n\nint\nhttp_parser_parse_url(const char *buf, size_t buflen, int is_connect,\n                      struct http_parser_url *u)\n{\n  enum state s;\n  const char *p;\n  enum http_parser_url_fields uf, old_uf;\n  int found_at = 0;\n\n  u->port = u->field_set = 0;\n  s = is_connect ? s_req_server_start : s_req_spaces_before_url;\n  uf = old_uf = UF_MAX;\n\n  for (p = buf; p < buf + buflen; p++) {\n    s = parse_url_char(s, *p);\n\n    /* Figure out the next field that we're operating on */\n    switch (s) {\n      case s_dead:\n        return 1;\n\n      /* Skip delimeters */\n      case s_req_schema_slash:\n      case s_req_schema_slash_slash:\n      case s_req_server_start:\n      case s_req_query_string_start:\n      case s_req_fragment_start:\n        continue;\n\n      case s_req_schema:\n        uf = UF_SCHEMA;\n        break;\n\n      case s_req_server_with_at:\n        found_at = 1;\n\n      /* FALLTROUGH */\n      case s_req_server:\n        uf = UF_HOST;\n        break;\n\n      case s_req_path:\n        uf = UF_PATH;\n        break;\n\n      case s_req_query_string:\n        uf = UF_QUERY;\n        break;\n\n      case s_req_fragment:\n        uf = UF_FRAGMENT;\n        break;\n\n      default:\n        assert(!\"Unexpected state\");\n        return 1;\n    }\n\n    /* Nothing's changed; soldier on */\n    if (uf == old_uf) {\n      u->field_data[uf].len++;\n      continue;\n    }\n\n    u->field_data[uf].off = p - buf;\n    u->field_data[uf].len = 1;\n\n    u->field_set |= (1 << uf);\n    old_uf = uf;\n  }\n\n  /* host must be present if there is a schema */\n  /* parsing http:///toto will fail */\n  if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {\n    if (http_parse_host(buf, u, found_at) != 0) {\n      return 1;\n    }\n  }\n\n  /* CONNECT requests can only contain \"hostname:port\" */\n  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {\n    return 1;\n  }\n\n  if (u->field_set & (1 << UF_PORT)) {\n    /* Don't bother with endp; we've already validated the string */\n    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);\n\n    /* Ports have a max value of 2^16 */\n    if (v > 0xffff) {\n      return 1;\n    }\n\n    u->port = (uint16_t) v;\n  }\n\n  return 0;\n}\n\nvoid\nhttp_parser_pause(http_parser *parser, int paused) {\n  /* Users should only be pausing/unpausing a parser that is not in an error\n   * state. In non-debug builds, there's not much that we can do about this\n   * other than ignore it.\n   */\n  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||\n      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {\n    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);\n  } else {\n    assert(0 && \"Attempting to pause parser in error state\");\n  }\n}\n\nint\nhttp_body_is_final(const struct http_parser *parser) {\n    return parser->state == s_message_done;\n}\n\nunsigned long\nhttp_parser_version(void) {\n  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |\n         HTTP_PARSER_VERSION_MINOR * 0x00100 |\n         HTTP_PARSER_VERSION_PATCH * 0x00001;\n}\n"
  },
  {
    "path": "src/http_parser.h",
    "content": "/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#ifndef http_parser_h\n#define http_parser_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Also update SONAME in the Makefile whenever you change these. */\n#define HTTP_PARSER_VERSION_MAJOR 2\n#define HTTP_PARSER_VERSION_MINOR 2\n#define HTTP_PARSER_VERSION_PATCH 0\n\n#include <sys/types.h>\n#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)\n#include <BaseTsd.h>\n#include <stddef.h>\ntypedef __int8 int8_t;\ntypedef unsigned __int8 uint8_t;\ntypedef __int16 int16_t;\ntypedef unsigned __int16 uint16_t;\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n#else\n#include <stdint.h>\n#endif\n\n/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run\n * faster\n */\n#ifndef HTTP_PARSER_STRICT\n# define HTTP_PARSER_STRICT 1\n#endif\n\n/* Maximium header size allowed */\n#define HTTP_MAX_HEADER_SIZE (80*1024)\n\n\ntypedef struct http_parser http_parser;\ntypedef struct http_parser_settings http_parser_settings;\n\n\n/* Callbacks should return non-zero to indicate an error. The parser will\n * then halt execution.\n *\n * The one exception is on_headers_complete. In a HTTP_RESPONSE parser\n * returning '1' from on_headers_complete will tell the parser that it\n * should not expect a body. This is used when receiving a response to a\n * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:\n * chunked' headers that indicate the presence of a body.\n *\n * http_data_cb does not return data chunks. It will be call arbitrarally\n * many times for each string. E.G. you might get 10 callbacks for \"on_url\"\n * each providing just a few characters more data.\n */\ntypedef int (*http_data_cb) (http_parser*, const char *at, size_t length);\ntypedef int (*http_cb) (http_parser*);\n\n\n/* Request Methods */\n#define HTTP_METHOD_MAP(XX)         \\\n  XX(0,  DELETE,      DELETE)       \\\n  XX(1,  GET,         GET)          \\\n  XX(2,  HEAD,        HEAD)         \\\n  XX(3,  POST,        POST)         \\\n  XX(4,  PUT,         PUT)          \\\n  /* pathological */                \\\n  XX(5,  CONNECT,     CONNECT)      \\\n  XX(6,  OPTIONS,     OPTIONS)      \\\n  XX(7,  TRACE,       TRACE)        \\\n  /* webdav */                      \\\n  XX(8,  COPY,        COPY)         \\\n  XX(9,  LOCK,        LOCK)         \\\n  XX(10, MKCOL,       MKCOL)        \\\n  XX(11, MOVE,        MOVE)         \\\n  XX(12, PROPFIND,    PROPFIND)     \\\n  XX(13, PROPPATCH,   PROPPATCH)    \\\n  XX(14, SEARCH,      SEARCH)       \\\n  XX(15, UNLOCK,      UNLOCK)       \\\n  /* subversion */                  \\\n  XX(16, REPORT,      REPORT)       \\\n  XX(17, MKACTIVITY,  MKACTIVITY)   \\\n  XX(18, CHECKOUT,    CHECKOUT)     \\\n  XX(19, MERGE,       MERGE)        \\\n  /* upnp */                        \\\n  XX(20, MSEARCH,     M-SEARCH)     \\\n  XX(21, NOTIFY,      NOTIFY)       \\\n  XX(22, SUBSCRIBE,   SUBSCRIBE)    \\\n  XX(23, UNSUBSCRIBE, UNSUBSCRIBE)  \\\n  /* RFC-5789 */                    \\\n  XX(24, PATCH,       PATCH)        \\\n  XX(25, PURGE,       PURGE)        \\\n\nenum http_method\n  {\n#define XX(num, name, string) HTTP_##name = num,\n  HTTP_METHOD_MAP(XX)\n#undef XX\n  };\n\n\nenum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };\n\n\n/* Flag values for http_parser.flags field */\nenum flags\n  { F_CHUNKED               = 1 << 0\n  , F_CONNECTION_KEEP_ALIVE = 1 << 1\n  , F_CONNECTION_CLOSE      = 1 << 2\n  , F_TRAILING              = 1 << 3\n  , F_UPGRADE               = 1 << 4\n  , F_SKIPBODY              = 1 << 5\n  };\n\n\n/* Map for errno-related constants\n * \n * The provided argument should be a macro that takes 2 arguments.\n */\n#define HTTP_ERRNO_MAP(XX)                                           \\\n  /* No error */                                                     \\\n  XX(OK, \"success\")                                                  \\\n                                                                     \\\n  /* Callback-related errors */                                      \\\n  XX(CB_message_begin, \"the on_message_begin callback failed\")       \\\n  XX(CB_url, \"the on_url callback failed\")                           \\\n  XX(CB_header_field, \"the on_header_field callback failed\")         \\\n  XX(CB_header_value, \"the on_header_value callback failed\")         \\\n  XX(CB_headers_complete, \"the on_headers_complete callback failed\") \\\n  XX(CB_body, \"the on_body callback failed\")                         \\\n  XX(CB_message_complete, \"the on_message_complete callback failed\") \\\n  XX(CB_status, \"the on_status callback failed\")                     \\\n                                                                     \\\n  /* Parsing-related errors */                                       \\\n  XX(INVALID_EOF_STATE, \"stream ended at an unexpected time\")        \\\n  XX(HEADER_OVERFLOW,                                                \\\n     \"too many header bytes seen; overflow detected\")                \\\n  XX(CLOSED_CONNECTION,                                              \\\n     \"data received after completed connection: close message\")      \\\n  XX(INVALID_VERSION, \"invalid HTTP version\")                        \\\n  XX(INVALID_STATUS, \"invalid HTTP status code\")                     \\\n  XX(INVALID_METHOD, \"invalid HTTP method\")                          \\\n  XX(INVALID_URL, \"invalid URL\")                                     \\\n  XX(INVALID_HOST, \"invalid host\")                                   \\\n  XX(INVALID_PORT, \"invalid port\")                                   \\\n  XX(INVALID_PATH, \"invalid path\")                                   \\\n  XX(INVALID_QUERY_STRING, \"invalid query string\")                   \\\n  XX(INVALID_FRAGMENT, \"invalid fragment\")                           \\\n  XX(LF_EXPECTED, \"LF character expected\")                           \\\n  XX(INVALID_HEADER_TOKEN, \"invalid character in header\")            \\\n  XX(INVALID_CONTENT_LENGTH,                                         \\\n     \"invalid character in content-length header\")                   \\\n  XX(INVALID_CHUNK_SIZE,                                             \\\n     \"invalid character in chunk size header\")                       \\\n  XX(INVALID_CONSTANT, \"invalid constant string\")                    \\\n  XX(INVALID_INTERNAL_STATE, \"encountered unexpected internal state\")\\\n  XX(STRICT, \"strict mode assertion failed\")                         \\\n  XX(PAUSED, \"parser is paused\")                                     \\\n  XX(UNKNOWN, \"an unknown error occurred\")\n\n\n/* Define HPE_* values for each errno value above */\n#define HTTP_ERRNO_GEN(n, s) HPE_##n,\nenum http_errno {\n  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)\n};\n#undef HTTP_ERRNO_GEN\n\n\n/* Get an http_errno value from an http_parser */\n#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)\n\n\nstruct http_parser {\n  /** PRIVATE **/\n  unsigned int type : 2;         /* enum http_parser_type */\n  unsigned int flags : 6;        /* F_* values from 'flags' enum; semi-public */\n  unsigned int state : 8;        /* enum state from http_parser.c */\n  unsigned int header_state : 8; /* enum header_state from http_parser.c */\n  unsigned int index : 8;        /* index into current matcher */\n\n  uint32_t nread;          /* # bytes read in various scenarios */\n  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */\n\n  /** READ-ONLY **/\n  unsigned short http_major;\n  unsigned short http_minor;\n  unsigned int status_code : 16; /* responses only */\n  unsigned int method : 8;       /* requests only */\n  unsigned int http_errno : 7;\n\n  /* 1 = Upgrade header was present and the parser has exited because of that.\n   * 0 = No upgrade header present.\n   * Should be checked when http_parser_execute() returns in addition to\n   * error checking.\n   */\n  unsigned int upgrade : 1;\n\n  /** PUBLIC **/\n  void *data; /* A pointer to get hook to the \"connection\" or \"socket\" object */\n};\n\n\nstruct http_parser_settings {\n  http_cb      on_message_begin;\n  http_data_cb on_url;\n  http_data_cb on_status;\n  http_data_cb on_header_field;\n  http_data_cb on_header_value;\n  http_cb      on_headers_complete;\n  http_data_cb on_body;\n  http_cb      on_message_complete;\n};\n\n\nenum http_parser_url_fields\n  { UF_SCHEMA           = 0\n  , UF_HOST             = 1\n  , UF_PORT             = 2\n  , UF_PATH             = 3\n  , UF_QUERY            = 4\n  , UF_FRAGMENT         = 5\n  , UF_USERINFO         = 6\n  , UF_MAX              = 7\n  };\n\n\n/* Result structure for http_parser_parse_url().\n *\n * Callers should index into field_data[] with UF_* values iff field_set\n * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and\n * because we probably have padding left over), we convert any port to\n * a uint16_t.\n */\nstruct http_parser_url {\n  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */\n  uint16_t port;                /* Converted UF_PORT string */\n\n  struct {\n    uint16_t off;               /* Offset into buffer in which field starts */\n    uint16_t len;               /* Length of run in buffer */\n  } field_data[UF_MAX];\n};\n\n\n/* Returns the library version. Bits 16-23 contain the major version number,\n * bits 8-15 the minor version number and bits 0-7 the patch level.\n * Usage example:\n *\n *   unsigned long version = http_parser_version();\n *   unsigned major = (version >> 16) & 255;\n *   unsigned minor = (version >> 8) & 255;\n *   unsigned patch = version & 255;\n *   printf(\"http_parser v%u.%u.%u\\n\", major, minor, version);\n */\nunsigned long http_parser_version(void);\n\nvoid http_parser_init(http_parser *parser, enum http_parser_type type);\n\n\nsize_t http_parser_execute(http_parser *parser,\n                           const http_parser_settings *settings,\n                           const char *data,\n                           size_t len);\n\n\n/* If http_should_keep_alive() in the on_headers_complete or\n * on_message_complete callback returns 0, then this should be\n * the last message on the connection.\n * If you are the server, respond with the \"Connection: close\" header.\n * If you are the client, close the connection.\n */\nint http_should_keep_alive(const http_parser *parser);\n\n/* Returns a string version of the HTTP method. */\nconst char *http_method_str(enum http_method m);\n\n/* Return a string name of the given error */\nconst char *http_errno_name(enum http_errno err);\n\n/* Return a string description of the given error */\nconst char *http_errno_description(enum http_errno err);\n\n/* Parse a URL; return nonzero on failure */\nint http_parser_parse_url(const char *buf, size_t buflen,\n                          int is_connect,\n                          struct http_parser_url *u);\n\n/* Pause or un-pause the parser; a nonzero value pauses */\nvoid http_parser_pause(http_parser *parser, int paused);\n\n/* Checks if this is the final chunk of the body. */\nint http_body_is_final(const http_parser *parser);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "src/lfs.c",
    "content": "/*\n** LuaFileSystem\n** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)\n**\n** File system manipulation library.\n** This library offers these functions:\n**   lfs.attributes (filepath [, attributename])\n**   lfs.chdir (path)\n**   lfs.currentdir ()\n**   lfs.dir (path)\n**   lfs.lock (fh, mode)\n**   lfs.lock_dir (path)\n**   lfs.mkdir (path)\n**   lfs.rmdir (path)\n**   lfs.setmode (filepath, mode)\n**   lfs.symlinkattributes (filepath [, attributename]) -- thanks to Sam Roberts\n**   lfs.touch (filepath [, atime [, mtime]])\n**   lfs.unlock (fh)\n**\n** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $\n*/\n\n#ifndef LFS_DO_NOT_USE_LARGE_FILE\n#ifndef _WIN32\n#ifndef _AIX\n#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */\n#else\n#define _LARGE_FILES 1 /* AIX */\n#endif\n#endif\n#endif\n\n#ifndef LFS_DO_NOT_USE_LARGE_FILE\n#define _LARGEFILE64_SOURCE\n#endif\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <time.h>\n#include <sys/stat.h>\n\n#ifdef _WIN32\n#include <direct.h>\n#include <windows.h>\n#include <io.h>\n#include <sys/locking.h>\n#ifdef __BORLANDC__\n #include <utime.h>\n#else\n #include <sys/utime.h>\n#endif\n#include <fcntl.h>\n#else\n#include <unistd.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <utime.h>\n#endif\n\n#include <lua.h>\n#include <lauxlib.h>\n#include <lualib.h>\n\n#include \"lfs.h\"\n\n#define LFS_VERSION \"1.6.2\"\n#define LFS_LIBNAME \"lfs\"\n\n#if LUA_VERSION_NUM < 502\n#  define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l))\n#endif\n\n/* Define 'strerror' for systems that do not implement it */\n#ifdef NO_STRERROR\n#define strerror(_)     \"System unable to describe the error\"\n#endif\n\n/* Define 'getcwd' for systems that do not implement it */\n#ifdef NO_GETCWD\n#define getcwd(p,s)     NULL\n#define getcwd_error    \"Function 'getcwd' not provided by system\"\n#else\n#define getcwd_error    strerror(errno)\n  #ifdef _WIN32\n\t /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */\n    #define LFS_MAXPATHLEN MAX_PATH\n  #else\n\t/* For MAXPATHLEN: */\n    #include <sys/param.h>\n    #define LFS_MAXPATHLEN MAXPATHLEN\n  #endif\n#endif\n\n#define DIR_METATABLE \"directory metatable\"\ntypedef struct dir_data {\n        int  closed;\n#ifdef _WIN32\n        intptr_t hFile;\n        char pattern[MAX_PATH+1];\n#else\n        DIR *dir;\n#endif\n} dir_data;\n\n#define LOCK_METATABLE \"lock metatable\"\n\n#ifdef _WIN32\n #ifdef __BORLANDC__\n  #define lfs_setmode(L,file,m)   ((void)L, setmode(_fileno(file), m))\n  #define STAT_STRUCT struct stati64\n #else\n  #define lfs_setmode(L,file,m)   ((void)L, _setmode(_fileno(file), m))\n  #define STAT_STRUCT struct _stati64\n #endif\n#define STAT_FUNC _stati64\n#define LSTAT_FUNC STAT_FUNC\n#else\n#define _O_TEXT               0\n#define _O_BINARY             0\n#define lfs_setmode(L,file,m)   ((void)L, (void)file, (void)m, 0)\n#define STAT_STRUCT struct stat\n#define STAT_FUNC stat\n#define LSTAT_FUNC lstat\n#endif\n\n/*\n** Utility functions\n*/\nstatic int pusherror(lua_State *L, const char *info)\n{\n        lua_pushnil(L);\n        if (info==NULL)\n                lua_pushstring(L, strerror(errno));\n        else\n                lua_pushfstring(L, \"%s: %s\", info, strerror(errno));\n        lua_pushinteger(L, errno);\n        return 3;\n}\n\nstatic int pushresult(lua_State *L, int i, const char *info)\n{\n        if (i==-1)\n                return pusherror(L, info);\n        lua_pushinteger(L, i);\n        return 1;\n}\n\n\n/*\n** This function changes the working (current) directory\n*/\nstatic int change_dir (lua_State *L) {\n        const char *path = luaL_checkstring(L, 1);\n        if (chdir(path)) {\n                lua_pushnil (L);\n                lua_pushfstring (L,\"Unable to change working directory to '%s'\\n%s\\n\",\n                                path, chdir_error);\n                return 2;\n        } else {\n                lua_pushboolean (L, 1);\n                return 1;\n        }\n}\n\n/*\n** This function returns the current directory\n** If unable to get the current directory, it returns nil\n**  and a string describing the error\n*/\nstatic int get_dir (lua_State *L) {\n  char *path;\n  /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */\n  char buf[LFS_MAXPATHLEN];\n  if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) {\n    lua_pushnil(L);\n    lua_pushstring(L, getcwd_error);\n    return 2;\n  }\n  else {\n    lua_pushstring(L, path);\n    return 1;\n  }\n}\n\n/*\n** Check if the given element on the stack is a file and returns it.\n*/\nstatic FILE *check_file (lua_State *L, int idx, const char *funcname) {\n        FILE **fh = (FILE **)luaL_checkudata (L, idx, \"FILE*\");\n        if (fh == NULL) {\n                luaL_error (L, \"%s: not a file\", funcname);\n                return 0;\n        } else if (*fh == NULL) {\n                luaL_error (L, \"%s: closed file\", funcname);\n                return 0;\n        } else\n                return *fh;\n}\n\n\n/*\n**\n*/\nstatic int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) {\n        int code;\n#ifdef _WIN32\n        /* lkmode valid values are:\n           LK_LOCK    Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error.\n           LK_NBLCK   Locks the specified bytes. If the bytes cannot be locked, the constant returns an error.\n           LK_NBRLCK  Same as _LK_NBLCK.\n           LK_RLCK    Same as _LK_LOCK.\n           LK_UNLCK   Unlocks the specified bytes, which must have been previously locked.\n\n           Regions should be locked only briefly and should be unlocked before closing a file or exiting the program.\n\n           http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp\n        */\n        int lkmode;\n        switch (*mode) {\n                case 'r': lkmode = LK_NBLCK; break;\n                case 'w': lkmode = LK_NBLCK; break;\n                case 'u': lkmode = LK_UNLCK; break;\n                default : return luaL_error (L, \"%s: invalid mode\", funcname);\n        }\n        if (!len) {\n                fseek (fh, 0L, SEEK_END);\n                len = ftell (fh);\n        }\n        fseek (fh, start, SEEK_SET);\n#ifdef __BORLANDC__\n        code = locking (fileno(fh), lkmode, len);\n#else\n        code = _locking (fileno(fh), lkmode, len);\n#endif\n#else\n        struct flock f;\n        switch (*mode) {\n                case 'w': f.l_type = F_WRLCK; break;\n                case 'r': f.l_type = F_RDLCK; break;\n                case 'u': f.l_type = F_UNLCK; break;\n                default : return luaL_error (L, \"%s: invalid mode\", funcname);\n        }\n        f.l_whence = SEEK_SET;\n        f.l_start = (off_t)start;\n        f.l_len = (off_t)len;\n        code = fcntl (fileno(fh), F_SETLK, &f);\n#endif\n        return (code != -1);\n}\n\n#ifdef _WIN32\ntypedef struct lfs_Lock {\n  HANDLE fd;\n} lfs_Lock;\nstatic int lfs_lock_dir(lua_State *L) {\n  size_t pathl; HANDLE fd;\n  lfs_Lock *lock;\n  char *ln;\n  const char *lockfile = \"/lockfile.lfs\";\n  const char *path = luaL_checklstring(L, 1, &pathl);\n  ln = (char*)malloc(pathl + strlen(lockfile) + 1);\n  if(!ln) {\n    lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;\n  }\n  strcpy(ln, path); strcat(ln, lockfile);\n  if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW,\n                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) {\n        int en = GetLastError();\n        free(ln); lua_pushnil(L);\n        if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION)\n                lua_pushstring(L, \"File exists\");\n        else\n                lua_pushstring(L, strerror(en));\n        return 2;\n  }\n  free(ln);\n  lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));\n  lock->fd = fd;\n  luaL_getmetatable (L, LOCK_METATABLE);\n  lua_setmetatable (L, -2);\n  return 1;\n}\nstatic int lfs_unlock_dir(lua_State *L) {\n  lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE);\n  if(lock->fd != INVALID_HANDLE_VALUE) {\n    CloseHandle(lock->fd);\n    lock->fd=INVALID_HANDLE_VALUE;\n  }\n  return 0;\n}\n#else\ntypedef struct lfs_Lock {\n  char *ln;\n} lfs_Lock;\nstatic int lfs_lock_dir(lua_State *L) {\n  lfs_Lock *lock;\n  size_t pathl;\n  char *ln;\n  const char *lockfile = \"/lockfile.lfs\";\n  const char *path = luaL_checklstring(L, 1, &pathl);\n  lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));\n  ln = (char*)malloc(pathl + strlen(lockfile) + 1);\n  if(!ln) {\n    lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;\n  }\n  strcpy(ln, path); strcat(ln, lockfile);\n  if(symlink(\"lock\", ln) == -1) {\n    free(ln); lua_pushnil(L);\n    lua_pushstring(L, strerror(errno)); return 2;\n  }\n  lock->ln = ln;\n  luaL_getmetatable (L, LOCK_METATABLE);\n  lua_setmetatable (L, -2);\n  return 1;\n}\nstatic int lfs_unlock_dir(lua_State *L) {\n  lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE);\n  if(lock->ln) {\n    unlink(lock->ln);\n    free(lock->ln);\n    lock->ln = NULL;\n  }\n  return 0;\n}\n#endif\n\nstatic int lfs_g_setmode (lua_State *L, FILE *f, int arg) {\n  static const int mode[] = {_O_BINARY, _O_TEXT};\n  static const char *const modenames[] = {\"binary\", \"text\", NULL};\n  int op = luaL_checkoption(L, arg, NULL, modenames);\n  int res = lfs_setmode(L, f, mode[op]);\n  if (res != -1) {\n    int i;\n    lua_pushboolean(L, 1);\n    for (i = 0; modenames[i] != NULL; i++) {\n      if (mode[i] == res) {\n        lua_pushstring(L, modenames[i]);\n        goto exit;\n      }\n    }\n    lua_pushnil(L);\n  exit:\n    return 2;\n  } else {\n    int en = errno;\n    lua_pushnil(L);\n    lua_pushfstring(L, \"%s\", strerror(en));\n    lua_pushinteger(L, en);\n    return 3;\n  }\n}\n\nstatic int lfs_f_setmode(lua_State *L) {\n  return lfs_g_setmode(L, check_file(L, 1, \"setmode\"), 2);\n}\n\n/*\n** Locks a file.\n** @param #1 File handle.\n** @param #2 String with lock mode ('w'rite, 'r'ead).\n** @param #3 Number with start position (optional).\n** @param #4 Number with length (optional).\n*/\nstatic int file_lock (lua_State *L) {\n        FILE *fh = check_file (L, 1, \"lock\");\n        const char *mode = luaL_checkstring (L, 2);\n        const long start = (long)luaL_optinteger(L, 3, 0);\n        long len = (long)luaL_optinteger(L, 4, 0);\n        if (_file_lock (L, fh, mode, start, len, \"lock\")) {\n                lua_pushboolean (L, 1);\n                return 1;\n        } else {\n                lua_pushnil (L);\n                lua_pushfstring (L, \"%s\", strerror(errno));\n                return 2;\n        }\n}\n\n\n/*\n** Unlocks a file.\n** @param #1 File handle.\n** @param #2 Number with start position (optional).\n** @param #3 Number with length (optional).\n*/\nstatic int file_unlock (lua_State *L) {\n        FILE *fh = check_file (L, 1, \"unlock\");\n        const long start = (long)luaL_optinteger(L, 2, 0);\n        long len = (long)luaL_optinteger(L, 3, 0);\n        if (_file_lock (L, fh, \"u\", start, len, \"unlock\")) {\n                lua_pushboolean (L, 1);\n                return 1;\n        } else {\n                lua_pushnil (L);\n                lua_pushfstring (L, \"%s\", strerror(errno));\n                return 2;\n        }\n}\n\n\n/*\n** Creates a link.\n** @param #1 Object to link to.\n** @param #2 Name of link.\n** @param #3 True if link is symbolic (optional).\n*/\nstatic int make_link(lua_State *L)\n{\n#ifndef _WIN32\n        const char *oldpath = luaL_checkstring(L, 1);\n        const char *newpath = luaL_checkstring(L, 2);\n        return pushresult(L,\n                (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL);\n#else\n        return pusherror(L, \"make_link is not supported on Windows\");\n#endif\n}\n\n\n/*\n** Creates a directory.\n** @param #1 Directory path.\n*/\nstatic int make_dir (lua_State *L) {\n        const char *path = luaL_checkstring (L, 1);\n        int fail;\n#ifdef _WIN32\n        fail = _mkdir (path);\n#else\n        fail =  mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |\n                             S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH );\n#endif\n        if (fail) {\n                lua_pushnil (L);\n        lua_pushfstring (L, \"%s\", strerror(errno));\n                return 2;\n        }\n        lua_pushboolean (L, 1);\n        return 1;\n}\n\n\n/*\n** Removes a directory.\n** @param #1 Directory path.\n*/\nstatic int remove_dir (lua_State *L) {\n        const char *path = luaL_checkstring (L, 1);\n        int fail;\n\n        fail = rmdir (path);\n\n        if (fail) {\n                lua_pushnil (L);\n                lua_pushfstring (L, \"%s\", strerror(errno));\n                return 2;\n        }\n        lua_pushboolean (L, 1);\n        return 1;\n}\n\n\n/*\n** Directory iterator\n*/\nstatic int dir_iter (lua_State *L) {\n#ifdef _WIN32\n        struct _finddata_t c_file;\n#else\n        struct dirent *entry;\n#endif\n        dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE);\n        luaL_argcheck (L, d->closed == 0, 1, \"closed directory\");\n#ifdef _WIN32\n        if (d->hFile == 0L) { /* first entry */\n                if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) {\n                        lua_pushnil (L);\n                        lua_pushstring (L, strerror (errno));\n                        d->closed = 1;\n                        return 2;\n                } else {\n                        lua_pushstring (L, c_file.name);\n                        return 1;\n                }\n        } else { /* next entry */\n                if (_findnext (d->hFile, &c_file) == -1L) {\n                        /* no more entries => close directory */\n                        _findclose (d->hFile);\n                        d->closed = 1;\n                        return 0;\n                } else {\n                        lua_pushstring (L, c_file.name);\n                        return 1;\n                }\n        }\n#else\n        if ((entry = readdir (d->dir)) != NULL) {\n                lua_pushstring (L, entry->d_name);\n                return 1;\n        } else {\n                /* no more entries => close directory */\n                closedir (d->dir);\n                d->closed = 1;\n                return 0;\n        }\n#endif\n}\n\n\n/*\n** Closes directory iterators\n*/\nstatic int dir_close (lua_State *L) {\n        dir_data *d = (dir_data *)lua_touserdata (L, 1);\n#ifdef _WIN32\n        if (!d->closed && d->hFile) {\n                _findclose (d->hFile);\n        }\n#else\n        if (!d->closed && d->dir) {\n                closedir (d->dir);\n        }\n#endif\n        d->closed = 1;\n        return 0;\n}\n\n\n/*\n** Factory of directory iterators\n*/\nstatic int dir_iter_factory (lua_State *L) {\n        const char *path = luaL_checkstring (L, 1);\n        dir_data *d;\n        lua_pushcfunction (L, dir_iter);\n        d = (dir_data *) lua_newuserdata (L, sizeof(dir_data));\n        luaL_getmetatable (L, DIR_METATABLE);\n        lua_setmetatable (L, -2);\n        d->closed = 0;\n#ifdef _WIN32\n        d->hFile = 0L;\n        if (strlen(path) > MAX_PATH-2)\n          luaL_error (L, \"path too long: %s\", path);\n        else\n          sprintf (d->pattern, \"%s/*\", path);\n#else\n        d->dir = opendir (path);\n        if (d->dir == NULL)\n          luaL_error (L, \"cannot open %s: %s\", path, strerror (errno));\n#endif\n        return 2;\n}\n\n\n/*\n** Creates directory metatable.\n*/\nstatic int dir_create_meta (lua_State *L) {\n        luaL_newmetatable (L, DIR_METATABLE);\n\n        /* Method table */\n        lua_newtable(L);\n        lua_pushcfunction (L, dir_iter);\n        lua_setfield(L, -2, \"next\");\n        lua_pushcfunction (L, dir_close);\n        lua_setfield(L, -2, \"close\");\n\n        /* Metamethods */\n        lua_setfield(L, -2, \"__index\");\n        lua_pushcfunction (L, dir_close);\n        lua_setfield (L, -2, \"__gc\");\n        return 1;\n}\n\n\n/*\n** Creates lock metatable.\n*/\nstatic int lock_create_meta (lua_State *L) {\n        luaL_newmetatable (L, LOCK_METATABLE);\n\n        /* Method table */\n        lua_newtable(L);\n        lua_pushcfunction(L, lfs_unlock_dir);\n        lua_setfield(L, -2, \"free\");\n\n        /* Metamethods */\n        lua_setfield(L, -2, \"__index\");\n        lua_pushcfunction(L, lfs_unlock_dir);\n        lua_setfield(L, -2, \"__gc\");\n        return 1;\n}\n\n\n#ifdef _WIN32\n #ifndef S_ISDIR\n   #define S_ISDIR(mode)  (mode&_S_IFDIR)\n #endif\n #ifndef S_ISREG\n   #define S_ISREG(mode)  (mode&_S_IFREG)\n #endif\n #ifndef S_ISLNK\n   #define S_ISLNK(mode)  (0)\n #endif\n #ifndef S_ISSOCK\n   #define S_ISSOCK(mode)  (0)\n #endif\n #ifndef S_ISFIFO\n   #define S_ISFIFO(mode)  (0)\n #endif\n #ifndef S_ISCHR\n   #define S_ISCHR(mode)  (mode&_S_IFCHR)\n #endif\n #ifndef S_ISBLK\n   #define S_ISBLK(mode)  (0)\n #endif\n#endif\n/*\n** Convert the inode protection mode to a string.\n*/\n#ifdef _WIN32\nstatic const char *mode2string (unsigned short mode) {\n#else\nstatic const char *mode2string (mode_t mode) {\n#endif\n  if ( S_ISREG(mode) )\n    return \"file\";\n  else if ( S_ISDIR(mode) )\n    return \"directory\";\n  else if ( S_ISLNK(mode) )\n        return \"link\";\n  else if ( S_ISSOCK(mode) )\n    return \"socket\";\n  else if ( S_ISFIFO(mode) )\n        return \"named pipe\";\n  else if ( S_ISCHR(mode) )\n        return \"char device\";\n  else if ( S_ISBLK(mode) )\n        return \"block device\";\n  else\n        return \"other\";\n}\n\n\n/*\n** Set access time and modification values for file\n*/\nstatic int file_utime (lua_State *L) {\n        const char *file = luaL_checkstring (L, 1);\n        struct utimbuf utb, *buf;\n\n        if (lua_gettop (L) == 1) /* set to current date/time */\n                buf = NULL;\n        else {\n                utb.actime = (time_t)luaL_optnumber (L, 2, 0);\n                utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime);\n                buf = &utb;\n        }\n        if (utime (file, buf)) {\n                lua_pushnil (L);\n                lua_pushfstring (L, \"%s\", strerror (errno));\n                return 2;\n        }\n        lua_pushboolean (L, 1);\n        return 1;\n}\n\n\n/* inode protection mode */\nstatic void push_st_mode (lua_State *L, STAT_STRUCT *info) {\n        lua_pushstring (L, mode2string (info->st_mode));\n}\n/* device inode resides on */\nstatic void push_st_dev (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_dev);\n}\n/* inode's number */\nstatic void push_st_ino (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_ino);\n}\n/* number of hard links to the file */\nstatic void push_st_nlink (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_nlink);\n}\n/* user-id of owner */\nstatic void push_st_uid (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_uid);\n}\n/* group-id of owner */\nstatic void push_st_gid (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_gid);\n}\n/* device type, for special file inode */\nstatic void push_st_rdev (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_rdev);\n}\n/* time of last access */\nstatic void push_st_atime (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, info->st_atime);\n}\n/* time of last data modification */\nstatic void push_st_mtime (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, info->st_mtime);\n}\n/* time of last file status change */\nstatic void push_st_ctime (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, info->st_ctime);\n}\n/* file size, in bytes */\nstatic void push_st_size (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_size);\n}\n#ifndef _WIN32\n/* blocks allocated for file */\nstatic void push_st_blocks (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_blocks);\n}\n/* optimal file system I/O blocksize */\nstatic void push_st_blksize (lua_State *L, STAT_STRUCT *info) {\n        lua_pushnumber (L, (lua_Number)info->st_blksize);\n}\n#endif\n\n /*\n** Convert the inode protection mode to a permission list.\n*/\n\n#ifdef _WIN32\nstatic const char *perm2string (unsigned short mode) {\n  static char perms[10] = \"---------\\0\";\n  int i;\n  for (i=0;i<9;i++) perms[i]='-';\n  if (mode  & _S_IREAD)\n   { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; }\n  if (mode  & _S_IWRITE)\n   { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; }\n  if (mode  & _S_IEXEC)\n   { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; }\n  return perms;\n}\n#else\nstatic const char *perm2string (mode_t mode) {\n  static char perms[10] = \"---------\\0\";\n  int i;\n  for (i=0;i<9;i++) perms[i]='-';\n  if (mode & S_IRUSR) perms[0] = 'r';\n  if (mode & S_IWUSR) perms[1] = 'w';\n  if (mode & S_IXUSR) perms[2] = 'x';\n  if (mode & S_IRGRP) perms[3] = 'r';\n  if (mode & S_IWGRP) perms[4] = 'w';\n  if (mode & S_IXGRP) perms[5] = 'x';\n  if (mode & S_IROTH) perms[6] = 'r';\n  if (mode & S_IWOTH) perms[7] = 'w';\n  if (mode & S_IXOTH) perms[8] = 'x';\n  return perms;\n}\n#endif\n\n/* permssions string */\nstatic void push_st_perm (lua_State *L, STAT_STRUCT *info) {\n    lua_pushstring (L, perm2string (info->st_mode));\n}\n\ntypedef void (*_push_function) (lua_State *L, STAT_STRUCT *info);\n\nstruct _stat_members {\n        const char *name;\n        _push_function push;\n};\n\nstruct _stat_members members[] = {\n        { \"mode\",         push_st_mode },\n        { \"dev\",          push_st_dev },\n        { \"ino\",          push_st_ino },\n        { \"nlink\",        push_st_nlink },\n        { \"uid\",          push_st_uid },\n        { \"gid\",          push_st_gid },\n        { \"rdev\",         push_st_rdev },\n        { \"access\",       push_st_atime },\n        { \"modification\", push_st_mtime },\n        { \"change\",       push_st_ctime },\n        { \"size\",         push_st_size },\n        { \"permissions\",  push_st_perm },\n#ifndef _WIN32\n        { \"blocks\",       push_st_blocks },\n        { \"blksize\",      push_st_blksize },\n#endif\n        { NULL, NULL }\n};\n\n/*\n** Get file or symbolic link information\n*/\nstatic int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {\n        STAT_STRUCT info;\n        const char *file = luaL_checkstring (L, 1);\n        int i;\n\n        if (st(file, &info)) {\n                lua_pushnil (L);\n                lua_pushfstring (L, \"cannot obtain information from file `%s'\", file);\n                return 2;\n        }\n        if (lua_isstring (L, 2)) {\n                const char *member = lua_tostring (L, 2);\n                for (i = 0; members[i].name; i++) {\n                        if (strcmp(members[i].name, member) == 0) {\n                                /* push member value and return */\n                                members[i].push (L, &info);\n                                return 1;\n                        }\n                }\n                /* member not found */\n                return luaL_error(L, \"invalid attribute name\");\n        }\n        /* creates a table if none is given */\n        if (!lua_istable (L, 2)) {\n                lua_newtable (L);\n        }\n        /* stores all members in table on top of the stack */\n        for (i = 0; members[i].name; i++) {\n                lua_pushstring (L, members[i].name);\n                members[i].push (L, &info);\n                lua_rawset (L, -3);\n        }\n        return 1;\n}\n\n\n/*\n** Get file information using stat.\n*/\nstatic int file_info (lua_State *L) {\n        return _file_info_ (L, STAT_FUNC);\n}\n\n\n/*\n** Get symbolic link information using lstat.\n*/\nstatic int link_info (lua_State *L) {\n        return _file_info_ (L, LSTAT_FUNC);\n}\n\n\n/*\n** Assumes the table is on top of the stack.\n*/\nstatic void set_info (lua_State *L) {\n        lua_pushliteral (L, \"_COPYRIGHT\");\n        lua_pushliteral (L, \"Copyright (C) 2003-2012 Kepler Project\");\n        lua_settable (L, -3);\n        lua_pushliteral (L, \"_DESCRIPTION\");\n        lua_pushliteral (L, \"LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution\");\n        lua_settable (L, -3);\n        lua_pushliteral (L, \"_VERSION\");\n        lua_pushliteral (L, \"LuaFileSystem \"LFS_VERSION);\n        lua_settable (L, -3);\n}\n\n\nstatic const struct luaL_Reg fslib[] = {\n        {\"attributes\", file_info},\n        {\"chdir\", change_dir},\n        {\"currentdir\", get_dir},\n        {\"dir\", dir_iter_factory},\n        {\"link\", make_link},\n        {\"lock\", file_lock},\n        {\"mkdir\", make_dir},\n        {\"rmdir\", remove_dir},\n        {\"symlinkattributes\", link_info},\n        {\"setmode\", lfs_f_setmode},\n        {\"touch\", file_utime},\n        {\"unlock\", file_unlock},\n        {\"lock_dir\", lfs_lock_dir},\n        {NULL, NULL},\n};\n\nint luaopen_lfs (lua_State *L) {\n        dir_create_meta (L);\n        lock_create_meta (L);\n        luaL_newlib (L, fslib);\n        lua_pushvalue(L, -1);\n        lua_setglobal(L, LFS_LIBNAME);\n        set_info (L);\n        return 1;\n}"
  },
  {
    "path": "src/lfs.h",
    "content": "/*\n** LuaFileSystem\n** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)\n**\n** $Id: lfs.h,v 1.5 2008/02/19 20:08:23 mascarenhas Exp $\n*/\n\n/* Define 'chdir' for systems that do not implement it */\n#ifdef NO_CHDIR\n#define chdir(p)\t(-1)\n#define chdir_error\t\"Function 'chdir' not provided by system\"\n#else\n#define chdir_error\tstrerror(errno)\n#endif\n\n\nextern int luaopen_lfs (lua_State *L);"
  },
  {
    "path": "src/lua_lpack.c",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include <stdint.h>\n#include <stddef.h>\n#include <string.h>\n#include <stdlib.h>\n#include <float.h>\n#include <lua.h>\n#include <lauxlib.h>\n\n#include \"uv.h\"\n#include \"luaw_common.h\"\n#include \"lua_lpack.h\"\n\n\nstatic void be16(const char* in, char * out) {\n    if (is_bigendian()) {\n        out[0] = in[0];\n        out[1] = in[1];\n    } else {\n        out[0] = in[1];\n        out[1] = in[0];\n    }\n}\n\nstatic void be32(const char* in, char* out) {\n    if (is_bigendian()) {\n        out[0] = in[0];\n        out[1] = in[1];\n        out[2] = in[2];\n        out[3] = in[3];\n    } else {\n        out[0] = in[3];\n        out[3] = in[0];\n        out[1] = in[2];\n        out[2] = in[1];\n    }\n}\n\nstatic void be64(const char* in, char* out) {\n    if (is_bigendian()) {\n        out[0] = in[0];\n        out[1] = in[1];\n\n        out[2] = in[2];\n        out[3] = in[3];\n\n        out[4] = in[4];\n        out[5] = in[5];\n\n        out[6] = in[6];\n        out[7] = in[7];\n    } else {\n        out[0] = in[7];\n        out[7] = in[0];\n\n        out[1] = in[6];\n        out[6] = in[1];\n\n        out[2] = in[5];\n        out[5] = in[2];\n\n        out[3] = in[4];\n        out[4] = in[3];\n    }\n}\n\nstatic int number_to_lua(lua_State* L, int read_len, double value) {\n    lua_pushinteger(L, read_len);\n    lua_pushnumber(L, value);\n    return 2;\n}\n\nLUA_LIB_METHOD int read_number(lua_State* L) {\n    int num_type = luaL_checkinteger(L, 1);\n    size_t len;\n    const char* buff = luaL_checklstring(L, 2, &len);\n    size_t offset = luaL_checkinteger(L, 3);\n\n    int remaining = len - offset;\n    if (remaining < 0) {\n        luaL_error(L, \"Buffer underflow while reading number\");\n    }\n\n    buff = buff + offset;\n    size_t sz;\n    switch(num_type) {\n        case TYPE_MARKER:\n        case UINT_8:\n        case STRING:\n        case DICT_ENTRY:\n            if (remaining < 1) return number_to_lua(L, 0, 0);\n            uint8_t u = buff[0];\n            return number_to_lua(L, 1, u);\n\n        case UINT_16:\n        case BIG_STRING:\n        case BIG_DICT_ENTRY:\n            if (remaining < 2) return number_to_lua(L, 0, 0);\n            uint16_t u16;\n            be16(buff, (char *)&u16);\n            return number_to_lua(L, 2, u16);\n\n        case UINT_32:\n        case HUGE_STRING:\n            if (remaining < 4) return number_to_lua(L, 0, 0);\n            uint32_t u32;\n            be32(buff, (char *)&u32);\n            return number_to_lua(L, 4, u32);\n\n        case INT_8:\n            if (remaining < 1) return number_to_lua(L, 0, 0);\n            int8_t i = buff[0];\n            return number_to_lua(L, 1, i);\n\n        case INT_16:\n            if (remaining < 2) return number_to_lua(L, 0, 0);\n            int16_t i16;\n            be16(buff, (char *)&i16);\n            return number_to_lua(L, 2, i16);\n\n        case INT_32:\n            if (remaining < 4) return number_to_lua(L, 0, 0);\n            int32_t i32;\n            be32(buff, (char *)&i32);\n            return number_to_lua(L, 4, i32);\n\n        case INT_64:\n            if (remaining < 8) return number_to_lua(L, 0, 0);\n            int64_t i64;\n            be64(buff, (char *)&i64);\n            return number_to_lua(L, 8, i64);\n\n        case FLOAT:\n            sz = sizeof(float);\n            if (remaining < sz) return number_to_lua(L, 0, 0);\n            float f;\n            be32(buff, (char *)&f);\n            return number_to_lua(L, sz, f);\n\n        case DOUBLE:\n            sz = sizeof(double);\n            if (remaining < sz) return number_to_lua(L, 0, 0);\n            double d;\n            be64(buff, (char *)&d);\n            return number_to_lua(L, sz, d);\n\n        default:\n            return luaL_error(L, \"Invalid marker %d specified\", num_type);\n    }\n}\n\nstatic int string_to_lua(lua_State* L, const char* buff, size_t len) {\n    lua_pushinteger(L, len);\n    lua_pushlstring(L, buff, len);\n    return 2;\n}\n\nLUA_LIB_METHOD int read_string(lua_State* L) {\n    int desired = luaL_checkinteger(L, 1);\n    size_t len;\n    const char* buff = luaL_checklstring(L, 2, &len);\n    size_t offset = luaL_checkinteger(L, 3);\n\n    int remaining = len - offset;\n    if (remaining < 0) {\n        luaL_error(L, \"Buffer underflow while reading string\");\n    }\n\n    buff = buff + offset;\n    if (remaining >= desired) {\n        return string_to_lua(L, buff, desired);\n    } else {\n        return string_to_lua(L, buff, remaining);\n    }\n}\n\nstatic double fetch_next_as_integer(lua_State* L, int* idx, const char* s) {\n    lua_rawgeti(L, 1, *idx);\n    if (!lua_isnumber(L, -1)) {\n        const char* dbg_val = lua_tostring(L, -1);\n        luaL_error(L, \"Invalid value %s found where %s was expected\",dbg_val, s);\n    }\n    int i = lua_tointeger(L, -1);\n    *idx = *idx + 1;\n    lua_pop(L, 1);\n    return i;\n}\n\nstatic double fetch_next_as_double(lua_State* L, int* idx, const char* s) {\n    lua_rawgeti(L, 1, *idx);\n    if (!lua_tonumber(L, -1)) {\n        const char* dbg_val = lua_tostring(L, -1);\n        luaL_error(L, \"Invalid value %s found where %s was expected\", dbg_val, s);\n    }\n    double d = lua_tonumber(L, -1);\n    *idx = *idx + 1;\n    lua_pop(L, 1);\n    return d;\n}\n\nstatic const char* fetch_next_as_string(lua_State* L, size_t *len, int* idx, const char* s) {\n    lua_rawgeti(L, 1, *idx);\n    const char* val = lua_tolstring(L, -1, len);\n    if (val == NULL) {\n        luaL_error(L, \"Invalid value found where %s was expected\", s);\n    }\n    *idx = *idx + 1;\n    lua_pop(L, 1);\n    return val;\n}\n\n#define CHECK_ROOM(L, pos, need, size)  \\\n    if ((pos+need) > size) {            \\\n       return luaL_error(L, \"Run out of buffer in serialize_write_Q, wrong expected size supplied\"); \\\n    }\n\nLUA_LIB_METHOD int serialize_write_Q(lua_State* L) {\n    if (lua_istable(L, 1) == 0) {\n        return luaL_error(L, \"Invalid WriteQ, not a table\");\n    }\n\n    size_t len = lua_tointeger(L, 2);\n    if (len == 0) {\n        return luaL_error(L, \"Invalid write buffer length specified\");\n    }\n\n    const int buffsize = len + 64;\n    char* buff = (char*) malloc(buffsize);\n    if (buff == NULL) {\n        return luaL_error(L, \"Could not allocate memory for serialize_write_Q\");\n    }\n    int pos = 0;\n\n    int ival;\n    double dval;\n    uint16_t u16;\n    uint32_t u32;\n    int16_t i16;\n    int32_t i32;\n    int64_t i64;\n    const char *sval;\n    int qLen = lua_rawlen(L, 1);\n    int idx = 1;\n    int datasize = 0;\n\n    while (idx <= qLen) {\n        // write marker\n        int marker = fetch_next_as_integer(L, &idx, \"marker\");\n        CHECK_ROOM(L, pos, 1, buffsize)\n        buff[pos] = (char)marker;\n        pos++;\n\n        switch(marker) {\n            case MAP_START:\n            case ARRAY_START:\n            case DICT_START:\n            case RECORD_END:\n            case NIL:\n            case BOOL_TRUE:\n            case BOOL_FALSE:\n                break; //No value, single byte markers\n\n            case UINT_8:\n            case DICT_ENTRY:\n                datasize = sizeof(uint8_t);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                ival = fetch_next_as_integer(L, &idx, \"UINT_8\");\n                buff[pos] = (uint8_t)ival;\n                pos += datasize;\n                break;\n\n            case UINT_16:\n            case BIG_DICT_ENTRY:\n                datasize = sizeof(uint16_t);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                ival = fetch_next_as_integer(L, &idx, \"UINT_16 or BIG_DICT_ENTRY\");\n                u16 = (uint16_t)ival;\n                be16((char *)&u16, buff+pos);\n                pos += datasize;\n                break;\n\n            case UINT_32:\n                datasize = sizeof(uint32_t);\n                ival = fetch_next_as_integer(L, &idx, \"UINT_32\");\n                u32 = (uint32_t)ival;\n                be32((char *)&u32, buff+pos);\n                pos += datasize;\n                break;\n\n            case INT_8:\n                datasize = sizeof(int8_t);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                ival = fetch_next_as_integer(L, &idx, \"INT_8\");\n                buff[pos] = (int8_t)ival;\n                pos += datasize;\n                break;\n\n            case INT_16:\n                datasize = sizeof(int16_t);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                ival = fetch_next_as_integer(L, &idx, \"INT_16\");\n                i16 = (int16_t)ival;\n                be16((char *)&i16, buff+pos);\n                pos += datasize;\n                break;\n\n            case INT_32:\n                datasize = sizeof(int32_t);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                ival = fetch_next_as_integer(L, &idx, \"INT_32\");\n                i32 = (int32_t)ival;\n                be32((char *)&i32, buff+pos);\n                pos += datasize;\n                break;\n\n            case INT_64:\n                datasize = sizeof(int64_t);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                dval = fetch_next_as_double(L, &idx, \"INT_64\");\n                i64 = (int64_t)dval;\n                be64((char *)&i64, buff+pos);\n                pos += datasize;\n                break;\n\n            case FLOAT:\n                datasize = sizeof(float);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                dval = fetch_next_as_double(L, &idx, \"FLOAT\");\n                float f = (float)dval;\n                be32((char *)&f, buff+pos);\n                pos += datasize;\n                break;\n\n            case DOUBLE:\n                datasize = sizeof(double);\n                CHECK_ROOM(L, pos, datasize, buffsize)\n                dval = fetch_next_as_double(L, &idx, \"DOUBLE\");\n                be64((char *)&dval, buff+pos);\n                pos += datasize;\n                break;\n\n            case STRING:\n                datasize = sizeof(uint8_t);\n                sval = fetch_next_as_string(L, &len, &idx, \"STRING\");\n                CHECK_ROOM(L, pos, datasize+len, buffsize)\n                buff[pos] = (uint8_t)len;\n                pos += datasize;\n                memcpy(buff+pos, sval, len);\n                pos += len;\n                break;\n\n            case BIG_STRING:\n                datasize = sizeof(uint16_t);\n                sval = fetch_next_as_string(L, &len, &idx, \"BIG_STRING\");\n                CHECK_ROOM(L, pos, datasize+len, buffsize)\n                u16 = (uint16_t)len;\n                be16((char *)&u16, buff+pos);\n                pos += datasize;\n                memcpy(buff+pos, sval, len);\n                pos += len;\n                break;\n\n            case HUGE_STRING:\n                datasize = sizeof(uint32_t);\n                sval = fetch_next_as_string(L, &len, &idx, \"HUGE_STRING\");\n                CHECK_ROOM(L, pos, datasize+len, buffsize)\n                u32 = (uint32_t)len;\n                be32((char *)&u32, buff+pos);\n                pos += datasize;\n                memcpy(buff+pos, sval, len);\n                pos += len;\n                break;\n\n            default:\n                return luaL_error(L, \"Invalid marker %d encountered\", marker);\n        }\n    }\n\n    lua_pushlstring(L, buff, pos);\n    free(buff);\n    return 1;\n}\n\n/* Type enum definition in Lua: lpack.INT_8 = {\"INT_8\", 14, 1, -128, 127} */\nstatic void define_lpack_enum(lua_State* L, const char* name, type_tag tval, size_t size, double min, double max) {\n    lua_pushstring(L, name);\n    lua_createtable(L, 5, 0);\n\n    lua_pushstring(L, name);\n    lua_rawseti(L, -2, 1);\n\n    lua_pushinteger(L, tval);\n    lua_rawseti(L, -2, 2);\n\n    lua_pushinteger(L, size);\n    lua_rawseti(L, -2, 3);\n\n    lua_pushnumber(L, min);\n    lua_rawseti(L, -2, 4);\n\n    lua_pushnumber(L, max);\n    lua_rawseti(L, -2, 5);\n\n    lua_rawset(L, -3);\n}\n\n#define LPACK_ENUM_DEF(L, e, size, min, max) define_lpack_enum(L, #e, e, size, min, max)\n\nstatic void define_lapck_types(lua_State* L) {\n    LPACK_ENUM_DEF(L, TYPE_MARKER, 0, -1, -1);\n\n    LPACK_ENUM_DEF(L, MAP_START, sizeof(int8_t), MAP_START, MAP_START);\n    LPACK_ENUM_DEF(L, ARRAY_START, sizeof(int8_t), ARRAY_START, ARRAY_START);\n    LPACK_ENUM_DEF(L, DICT_START, sizeof(int8_t), DICT_START, DICT_START);\n    LPACK_ENUM_DEF(L, RECORD_END, sizeof(int8_t), RECORD_END, RECORD_END);\n\n    LPACK_ENUM_DEF(L, NIL, sizeof(int8_t), NIL, NIL);\n    LPACK_ENUM_DEF(L, BOOL_TRUE, sizeof(int8_t), BOOL_TRUE, BOOL_TRUE);\n    LPACK_ENUM_DEF(L, BOOL_FALSE, sizeof(int8_t), BOOL_FALSE, BOOL_FALSE);\n\n    LPACK_ENUM_DEF(L, UINT_8, sizeof(uint8_t), 0, UINT8_MAX);\n    LPACK_ENUM_DEF(L, DICT_ENTRY, sizeof(uint8_t), 0, UINT8_MAX);\n    LPACK_ENUM_DEF(L, UINT_16, sizeof(uint16_t), 0, UINT16_MAX);\n    LPACK_ENUM_DEF(L, BIG_DICT_ENTRY, sizeof(uint16_t), 0, UINT16_MAX);\n    LPACK_ENUM_DEF(L, UINT_32, sizeof(uint32_t), 0, UINT32_MAX);\n    LPACK_ENUM_DEF(L, INT_8, sizeof(int8_t), INT8_MIN, INT8_MAX);\n    LPACK_ENUM_DEF(L, INT_16, sizeof(int16_t), INT16_MIN, INT16_MAX);\n    LPACK_ENUM_DEF(L, INT_32, sizeof(int32_t), INT32_MIN, INT32_MAX);\n    LPACK_ENUM_DEF(L, INT_64, sizeof(int64_t), INT64_MIN, INT64_MAX);\n    LPACK_ENUM_DEF(L, FLOAT, sizeof(float), -FLT_MAX, FLT_MAX);\n    LPACK_ENUM_DEF(L, DOUBLE, sizeof(double), -DBL_MAX, DBL_MAX);\n\n    LPACK_ENUM_DEF(L, STRING, sizeof(uint8_t), 0, UINT8_MAX);\n    LPACK_ENUM_DEF(L, BIG_STRING, sizeof(uint16_t), 0, UINT16_MAX);\n    LPACK_ENUM_DEF(L, HUGE_STRING, sizeof(uint32_t), 0, UINT32_MAX);\n}\n\nLUA_LIB_METHOD static int new_lpack_parser(lua_State* L) {\n    lua_createtable(L, 0, 16);\n    luaL_setmetatable(L, LUA_LPACK_META_TABLE);\n    return 1;\n}\n\nLUA_LIB_METHOD static int create_dict(lua_State* L) {\n    int narr = luaL_checkinteger(L, 1);\n    int nrec = luaL_checkinteger(L, 2);\n    lua_createtable(L, narr, nrec);\n    return 1;\n}\n\nLUA_LIB_METHOD static int to_hex(lua_State* L) {\n    int num = luaL_checkinteger(L, -1);\n    if (num > 65536) {\n        raise_lua_error(L, \"toHex called with input %d, which is larger than acceptable limit\", num);\n    }\n\n    char hex[5];\n    sprintf(hex, \"%x\", num);\n    lua_pushstring(L, hex);\n    return 1;\n}\n\nstatic const struct luaL_Reg luaw_lpack_methods[] = {\n\t{\"read_number\", read_number},\n\t{\"read_string\", read_string},\n\t{\"serialize_write_Q\", serialize_write_Q},\n    {NULL, NULL}  /* sentinel */\n};\n\nstatic const struct luaL_Reg luaw_lpack_lib[] = {\n\t{\"newLPackParser\", new_lpack_parser},\n    {\"createDict\", create_dict},\n    {\"toHex\", to_hex},\n    {NULL, NULL}  /* sentinel */\n};\n\nvoid luaw_init_lpack_lib (lua_State *L) {\n\tmake_metatable(L, LUA_LPACK_META_TABLE, luaw_lpack_methods);\n    define_lapck_types(L);\n    luaL_newlib(L, luaw_lpack_lib);\n    lua_setglobal(L, \"luaw_lpack_lib\");\n}\n\n\n"
  },
  {
    "path": "src/lua_lpack.h",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#define LUA_LPACK_META_TABLE \"_luaw_lpack_MT_\"\n\nstatic const int32_t _i = 1;\n\n#define is_bigendian() ((*(char *)&_i) == 0)\n\ntypedef enum {\n    /* meta type for all marker tags, should never itself occur in read stream */\n    TYPE_MARKER = 0,\n\n    /* single byte marker tags */\n    MAP_START,\n    ARRAY_START,\n    DICT_START,\n    RECORD_END,\n\n    /* single byte value markers */\n    NIL,\n    BOOL_TRUE,\n    BOOL_FALSE,\n\n    /* fix width types */\n    UINT_8,\n    DICT_ENTRY,\n    UINT_16,\n    BIG_DICT_ENTRY,\n    UINT_32,\n    INT_8,\n    INT_16,\n    INT_32,\n    INT_64,\n    FLOAT,\n    DOUBLE,\n\n    /* variable length types */\n    STRING,\n    BIG_STRING,\n    HUGE_STRING\n}\ntype_tag;\n\nextern void luaw_init_lpack_lib (lua_State *L);\n"
  },
  {
    "path": "src/luaw_common.c",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\n#include <lua.h>\n#include <lauxlib.h>\n\n#include \"uv.h\"\n#include \"luaw_common.h\"\n#include \"luaw_logging.h\"\n#include \"luaw_tcp.h\"\n#include \"luaw_http_parser.h\"\n#include \"luaw_timer.h\"\n#include \"lua_lpack.h\"\n\n/* globals */\nlua_State* l_global = NULL; //main global Lua state that spawns all other coroutines\nint resume_thread_fn_ref;   //Resume thread lua function\n\n\nvoid resume_lua_thread(lua_State* L, int nargs, int nresults, int errHandler) {\n    int tid = lua_tointeger(L, (0 - nargs));\n    if (tid) {\n        int rc = lua_pcall(L, nargs, nresults, errHandler);\n        if (rc != 0) {\n            fprintf(stderr, \"******** Error resuming Lua thread# %d: %s (%d) *********\\n\", tid, lua_tostring(L, -1), rc);\n        }\n    }\n}\n\n/* sets up error code and error message on the Lua stack to be returned by the original C\n   function called from Lua. */\nint error_to_lua(lua_State* L, const char* fmt, ...) {\n    lua_settop(L, 0); //remove success status and params table from stack\n    lua_pushboolean(L, 0);  //set status to false in case of error\n    va_list argp;\n    va_start(argp, fmt);\n    lua_pushvfstring(L, fmt, argp);\n    va_end(argp);\n    return 2; //two values status and err_mesg pushed onto stack.\n}\n\nint raise_lua_error (lua_State *L, const char *fmt, ...) {\n    va_list argp;\n    va_start(argp, fmt);\n    lua_pushvfstring(L, fmt, argp);\n    va_end(argp);\n    return lua_error(L);\n}\n\nvoid close_if_active(uv_handle_t* handle, uv_close_cb close_cb) {\n    if ((handle != NULL)&&(!uv_is_closing(handle))) {\n        uv_close(handle, close_cb);\n    }\n}\n\n/* Minimal LuaJIT compatibility layer adopted from https://github.com/keplerproject/lua-compat-5.2 */\n#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501\n\nvoid luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {\n  luaL_checkstack(L, nup+1, \"too many upvalues\");\n  for (; l->name != NULL; l++) {  /* fill the table with given functions */\n    int i;\n    lua_pushstring(L, l->name);\n    for (i = 0; i < nup; i++)  /* copy upvalues to the top */\n      lua_pushvalue(L, -(nup + 1));\n    lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */\n    lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */\n  }\n  lua_pop(L, nup);  /* remove upvalues */\n}\n\n\nvoid luaL_setmetatable (lua_State *L, const char *tname) {\n  luaL_checkstack(L, 1, \"not enough stack slots\");\n  luaL_getmetatable(L, tname);\n  lua_setmetatable(L, -2);\n}\n\n#endif\n\nvoid make_metatable(lua_State *L, const char* mt_name, const luaL_Reg* mt_funcs) {\n\tluaL_newmetatable(L, mt_name);\n\tluaL_setfuncs(L, mt_funcs, 0);\n\t/* client metatable.__index = client metatable */\n\tlua_pushstring(L, \"__index\");\n\tlua_pushvalue(L,-2);\n\tlua_rawset(L, -3);\n}\n\nLUA_LIB_METHOD void luaw_init_libs (lua_State *L) {\n    luaw_init_logging_lib(L);\n    luaw_init_tcp_lib(L);\n    luaw_init_http_lib(L);\n    luaw_init_timer_lib(L);\n    luaw_init_lpack_lib(L);\n}\n\n/*********************************************************************\n* This file contains parts of Lua 5.2's source code:\n*\n* Copyright (C) 1994-2013 Lua.org, PUC-Rio.\n*\n* Permission is hereby granted, free of charge, to any person obtaining\n* a copy of this software and associated documentation files (the\n* \"Software\"), to deal in the Software without restriction, including\n* without limitation the rights to use, copy, modify, merge, publish,\n* distribute, sublicense, and/or sell copies of the Software, and to\n* permit persons to whom the Software is furnished to do so, subject to\n* the following conditions:\n*\n* The above copyright notice and this permission notice shall be\n* included in all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*********************************************************************/\n"
  },
  {
    "path": "src/luaw_common.h",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#ifndef LUAW_COMMON_H\n\n#define LUAW_COMMON_H\n\ntypedef enum {\n\tfalse = 0,\n\ttrue\n}\nbool;\n\n/* marker macros for documenting code */\n#define LIBUV_CALLBACK\n#define LIBUV_API\n#define LUA_OBJ_METHOD\n#define LUA_LIB_METHOD\n#define HTTP_PARSER_CALLBACK\n\n#define Q(x) #x\n#define QUOTE(x) Q(x)\n\n#define INCR_REF_COUNT(s) if (s != NULL) s->ref_count++;\n\n#define DECR_REF_COUNT(s) if (s != NULL) s->ref_count--;\n\n#define GC_REF(s)                           \\\n    if (s != NULL) {                        \\\n        s->ref_count--;                     \\\n        if (s->ref_count <= 0) free(s);     \\\n    }\n\n\n#define step  fprintf(stdout, \"At line# %d function(%s) in file %s\\n\", __LINE__, __FUNCTION__, __FILE__);\n#define debug_i(s) fprintf(stdout, #s \"= %d at line# %d function(%s) in file %s\\n\", s, __LINE__, __FUNCTION__, __FILE__);\n#define debug_l(s) fprintf(stdout, #s \"= %ld at line# %d function(%s) in file %s\\n\", s, __LINE__, __FUNCTION__, __FILE__);\n#define debug_s(s) fprintf(stdout, #s \"= %s at line# %d function(%s) in file %s\\n\", s, __LINE__, __FUNCTION__, __FILE__);\n#define debug_sl(s, l) fprintf(stdout, #s \"= %.*s at line# %d function(%s) in file %s\\n\", l, s, __LINE__, __FUNCTION__, __FILE__);\n#define debug_p(s) fprintf(stdout, #s \"= %p at line# %d function(%s) in file %s\\n\", s, __LINE__, __FUNCTION__, __FILE__);\n#define debug_break(s) fprintf(stdout, \"%s\\n\", s);\n\n/* global state */\nextern lua_State* l_global;\nextern int resume_thread_fn_ref;\n\nextern int error_to_lua(lua_State* L, const char* fmt, ...);\nextern int raise_lua_error (lua_State *L, const char *fmt, ...);\nextern void make_metatable(lua_State *L, const char* mt_name, const luaL_Reg* mt_funcs);\nextern void luaw_init_libs(lua_State *L);\nextern void close_if_active(uv_handle_t* handle, uv_close_cb close_cb);\nextern void resume_lua_thread(lua_State* L, int nargs, int nresults, int errHandler);\n\n\n\n/* Minimal LuaJIT compatibility layer adopted from https://github.com/keplerproject/lua-compat-5.2 */\n\n#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501\n/* Lua 5.1 */\n\n/* PUC-Rio Lua uses lconfig_h as include guard for luaconf.h,\n * LuaJIT uses luaconf_h. If you use PUC-Rio's include files\n * but LuaJIT's library, you will need to define the macro\n * COMPAT52_IS_LUAJIT yourself! */\n#if !defined(COMPAT52_IS_LUAJIT) && defined(luaconf_h)\n#define COMPAT52_IS_LUAJIT\n#endif\n\n/* LuaJIT doesn't define these unofficial macros ... */\n#if !defined(LUAI_INT32)\n#include <limits.h>\n#if INT_MAX-20 < 32760\n#define LUAI_INT32  long\n#define LUAI_UINT32 unsigned long\n#elif INT_MAX > 2147483640L\n#define LUAI_INT32  int\n#define LUAI_UINT32 unsigned int\n#else\n#error \"could not detect suitable lua_Unsigned datatype\"\n#endif\n#endif\n\n#define lua_rawlen(L, i) lua_objlen(L, i)\n\n#endif /* Lua 5.1 */\n\n#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501\n/* Lua 5.0 *or* 5.1 */\n\n#define LUA_OK 0\n\n#define lua_pushglobaltable(L) \\\n  lua_pushvalue(L, LUA_GLOBALSINDEX)\n\n#define luaL_newlib(L, l) \\\n  (lua_newtable((L)),luaL_setfuncs((L), (l), 0))\n\nvoid luaL_checkversion (lua_State *L);\n\n#endif /* Lua 5.0 *or* 5.1 */\n\nvoid luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);\nvoid luaL_setmetatable (lua_State *L, const char *tname);\n\n\n#endif\n"
  },
  {
    "path": "src/luaw_http_parser.c",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n#include <lua.h>\n#include <lauxlib.h>\n\n#include \"uv.h\"\n#include \"luaw_common.h\"\n#include \"luaw_http_parser.h\"\n#include \"luaw_tcp.h\"\n\ntypedef enum {\n\tPARSE_HTTP_PARSER_IDX = 1,\n\tPARSE_HTTP_BUFF_IDX,\n\tPARSE_HTTP_REQ_IDX,\n\tPARSE_HTTP_LENGTH_IDX,\n\tPARSE_HTTP_CONN_IDX\n}\nparse_http_lua_stack_index;\n\nstatic int decode_hex_str(const char* str, int len) {\n\tchar *read_ptr = (char *)str;\n\tchar *write_ptr = (char *)str;\n\tint hex_ch;\n\tchar ch;\n\n\twhile (len) {\n\t\tch = *read_ptr;\n\t\tif ((ch == '%')&&(len > 2)) {\n\t\t\tsscanf((read_ptr+1),\"%2x\", &hex_ch);\n\t\t\tch = (char)hex_ch;\n\t\t\tread_ptr += 3; len -= 3;\n\t\t}\n\t\telse {\n\t\t\tread_ptr++;\tlen--;\n\t\t}\n\n\t\t*write_ptr = ch;\n\t\twrite_ptr++;\n\t}\n\treturn ((const unsigned char*)write_ptr - (const unsigned char*)str);\n}\n\nstatic int handle_name_value_pair(lua_State* L, const char* name, int name_len, bool hex_name, const char* value, int value_len, bool hex_value) {\n\tif ((name != NULL)&&(name_len > 0)&&(value != NULL)&&(value_len > 0)) {\n\t\tif (hex_name) {\n\t\t\tname_len = decode_hex_str(name, name_len);\n\t\t}\n\t\tif (hex_value) {\n\t\t\tvalue_len = decode_hex_str(value, value_len);\n\t\t}\n\n\t\tlua_getfield(L, 1, \"storeHttpParam\");\n\t\tif (lua_isfunction(L, -1) != 1) {\n\t\t\traise_lua_error(L, \"Missing lua function storeHttpParam()\");\n        }\n\t\tlua_pushvalue(L, 3);\n\t\tlua_pushlstring(L, name, name_len);\n\t\tlua_pushlstring(L, value, value_len) ;\n\t\tlua_call(L, 3, 0);\n\t}\n\telse if((name_len == 0)&&(value_len > 0)) {\n\t    error_to_lua(L, \"400 Bad URL encoding: empty parameter name, non empty parameter value: %s \", value);\n\t    return false;\n\t}\n\treturn true; //param name without value is ok, e.g. &foo=\n}\n\n/* Lua call spec:\n success: params table, nil = http_lib:url_decode(url encoded string, params)\n success: status(false), error message = http_lib:url_decode(url encoded string, params)\n*/\nLUA_LIB_METHOD static int luaw_url_decode(lua_State *L) {\n\tif (!lua_istable(L, 1)) {\n\t\treturn raise_lua_error(L, \"Luaw HTTP lib table is missing\");\n\t}\n\tsize_t length = 0;\n\tconst char* data = lua_tolstring(L, 2, &length);\n\tchar *read_ptr = (char *)data;\n\tchar *name = NULL;\n\tchar *value = NULL;\n\tbool hex_name = false, hex_value = false;\n\tint name_len = 0, value_len = 0;\n\tdecoder_state ds = starting_name;\n\n\twhile (length--) {\n\t\tchar ch = *read_ptr;\n\n\t\tswitch(ds) {\n\t\t\tcase starting_name:\n                if (!handle_name_value_pair(L, name, name_len, hex_name, value, value_len, hex_value)) return 2; //err_code, err_mesg\n\t\t\t\tname_len = 0;\n\t\t\t\thex_name = false;\n\t\t\t\tvalue_len = 0;\n\t\t\t\thex_value = false;\n\n\t\t\t\tswitch(ch) {\n\t\t\t\t\tcase '&':\n\t\t\t\t\tcase '=':\n                \t    return error_to_lua(L, \"400 Bad URL encoding: Error while expecting start of param name at: %s\\n\", read_ptr);\n\t\t\t\t\tcase '%':\n\t\t\t\t\t\thex_name = true;\n\t\t\t\t}\n\t\t\t\tds = in_name;\n\t\t\t\tname = read_ptr;\n\t\t\t\tname_len = 1;\n\t\t\t\tbreak;\n\n\t\t\tcase in_name:\n\t\t\t\tswitch(ch) {\n\t\t\t\t\tcase '&':\n\t\t\t    \t    return error_to_lua(L, \"400 Bad URL encoding: Error while parsing param name at: %s\\n\", read_ptr);\n\t\t\t\t\tcase '=':\n\t\t\t\t\t\tds = starting_value;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '%':\n\t\t\t\t\t\thex_name = true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tname_len++;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase starting_value:\n\t\t\t\tswitch(ch) {\n\t\t\t\t\tcase '&':\n\t\t\t\t\tcase '=':\n\t\t\t    \t    return error_to_lua(L, \"400 Bad URL encoding: Error while expecting start of param value at: %s\\n\", read_ptr);\n\t\t\t\t\tcase '%':\n\t\t\t\t\t\thex_value = true;\n\t\t\t\t}\n\t\t\t\tds = in_value;\n\t\t\t\tvalue = read_ptr;\n\t\t\t\tvalue_len = 1;\n\t\t\t\tbreak;\n\n\t\t\tcase in_value:\n\t\t\t\tswitch(ch) {\n\t\t\t\t\tcase '&':\n\t\t\t\t\t\tds = starting_name;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '=':\n\t\t\t    \t    return error_to_lua(L, \"400 Bad URL encoding: Error while parsing param value at: %s\\n\", read_ptr);\n\t\t\t\t\tcase '%':\n\t\t\t\t\t\thex_value = true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tvalue_len++;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t}\n\t\tif (ch == '+') *read_ptr =' ';\n\t\tread_ptr++;\n\t}\n    return handle_name_value_pair(L, name, name_len, hex_name, value, value_len, hex_value) ? 1 : 2;\n}\n\nstatic int new_lhttp_parser(lua_State *L, enum http_parser_type parser_type) {\n\tluaw_http_parser_t* lhttp_parser = lua_newuserdata(L, sizeof(luaw_http_parser_t));\n\tif (lhttp_parser == NULL) {\n\t\treturn raise_lua_error(L, \"Failed to allocate memory for new http_parser.\");\n\t}\n\tluaL_setmetatable(L, LUA_HTTP_PARSER_META_TABLE);\n\thttp_parser_init(&lhttp_parser->parser, parser_type);\n\tlhttp_parser->parser.data = lhttp_parser;\n\treturn 1;\n}\n\nLUA_LIB_METHOD static int luaw_new_http_request_parser(lua_State* L) {\n    return new_lhttp_parser(L, HTTP_REQUEST);\n}\n\nLUA_LIB_METHOD static int luaw_new_http_response_parser(lua_State* L) {\n    return new_lhttp_parser(L, HTTP_RESPONSE);\n}\n\nLUA_LIB_METHOD static int luaw_init_http_parser(lua_State* L) {\n\tluaw_http_parser_t* lhttp_parser = luaL_checkudata(L, 1, LUA_HTTP_PARSER_META_TABLE);\n\thttp_parser* parser = &lhttp_parser->parser;\n\thttp_parser_init(parser, parser->type);\n    return 0;\n}\n\nstatic int handle_http_callback(http_parser *parser, http_parser_cb_type cb, const char* start, size_t len) {\n    luaw_http_parser_t* lhttp_parser = (luaw_http_parser_t*) parser->data;\n    lhttp_parser->http_cb = cb;\n    lhttp_parser->start = (char*)start;\n    lhttp_parser->len = len;\n    http_parser_pause(parser, 1);\n    return 0;\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_message_begin(http_parser *parser) {\n\treturn handle_http_callback(parser, http_cb_on_message_begin, NULL, 0);\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_url(http_parser *parser, const char* start, size_t len) {\n\treturn handle_http_callback(parser, http_cb_on_url, start, len);\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_status(http_parser *parser, const char* start, size_t len) {\n\treturn handle_http_callback(parser, http_cb_on_status, start, len);\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_header_name(http_parser *parser, const char* start, size_t len) {\n\treturn handle_http_callback(parser, http_cb_on_header_field, start, len);\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_header_value(http_parser *parser, const char* start, size_t len) {\n\treturn handle_http_callback(parser, http_cb_on_header_value, start, len);\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_headers_complete(http_parser *parser) {\n    return handle_http_callback(parser, http_cb_on_headers_complete, NULL, 0);\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_body(http_parser *parser, const char* start, size_t len) {\n\treturn handle_http_callback(parser, http_cb_on_body, start, len);\n}\n\nHTTP_PARSER_CALLBACK static int http_parser_on_message_complete(http_parser *parser) {\n    return handle_http_callback(parser, http_cb_on_mesg_complete, NULL, 0);\n}\n\nstatic const http_parser_settings parser_settings = {\n\t.on_message_begin = http_parser_on_message_begin,\n\t.on_status = http_parser_on_status,\n\t.on_url = http_parser_on_url,\n\t.on_header_field = http_parser_on_header_name,\n\t.on_header_value = http_parser_on_header_value,\n\t.on_headers_complete = http_parser_on_headers_complete,\n\t.on_body = http_parser_on_body,\n\t.on_message_complete = http_parser_on_message_complete\n};\n\n/* Lua call spec:\n* All failures:\n*       false, error message = parser:parseHttp(str, offset)\n*\n* Successes:\n*\n*   http_parser_on_headers_complete:\n*       http_cb_type, new offset, http_should_keep_alive, major_version, minor_version, http method, http status code = parseHttp(conn)\n*\n*   http_parser_on_message_complete:\n*       http_cb_type, new offset, http_should_keep_alive = parser:parseHttp(str, offset)\n*\n*   All other callbacks:\n*       http_cb_type, new offset, parsed value = parser:parseHttp(conn, str, offset)\n*\n*/\nstatic int parse_http(lua_State *L) {\n    lua_settop(L, 3);\n\n\tluaw_http_parser_t* lhttp_parser = luaL_checkudata(L, 1, LUA_HTTP_PARSER_META_TABLE);\n\thttp_parser* parser = &lhttp_parser->parser;\n\n    size_t len = 0;\n    const char* buff = lua_tolstring(L, 2, &len);\n\n    int offset = lua_tointeger(L, 3);\n    if (offset > len) {\n        lua_pushboolean(L, 0);\n        lua_pushfstring(L, \"Wrong offset into HTTP string: len=%d, offset=%d, content=%s\\n\", len, offset, buff);\n        return 2;\n    }\n\n\t/* every http_parser_execute() does not necessarily cause callback to be invoked, we need to know if it\n\t   did call the callback */\n\tlhttp_parser->http_cb = http_cb_none;\n\tconst int nparsed = http_parser_execute(parser, &parser_settings, (buff+offset), (len-offset));\n\toffset += nparsed;\n\tconst int remaining = len - offset;\n\n    if ((remaining > 0)&&(parser->http_errno != HPE_PAUSED)) {\n        lua_pushboolean(L, 0);\n        lua_pushfstring(L, \"Error parsing HTTP fragment: errorCode=%d, total=%d, parsed=%d, content=%s\\n\", parser->http_errno, len, nparsed, buff);\n        return 2;\n    }\n\n    lua_pushinteger(L, lhttp_parser->http_cb);\n    lua_pushinteger(L, offset);\n    int nresults = 3;\n\n    switch(lhttp_parser->http_cb) {\n        case http_cb_on_headers_complete:\n            lua_pushboolean(L, http_should_keep_alive(parser));\n\t        lua_pushinteger(L, parser->http_major);\n        \tlua_pushinteger(L, parser->http_minor);\n            if (parser->type == HTTP_REQUEST) {\n                lua_pushstring(L, http_methods[parser->method]);\n            } else {\n                lua_pushnil(L);\n            }\n            if (parser->type == HTTP_RESPONSE) {\n                lua_pushinteger(L, parser->status_code);\n            } else {\n                lua_pushnil(L);\n            }\n            nresults = 7;\n            break;\n\n        case http_cb_none:\n        case http_cb_on_mesg_complete:\n            lua_pushboolean(L, http_should_keep_alive(parser));\n            break;\n\n        default:\n            lua_pushlstring(L, lhttp_parser->start, lhttp_parser->len);\n    }\n\n    /* un-pause parser */\n    http_parser_pause(parser, 0);\n    return nresults;\n}\n\n\nconst char* url_field_names[] = { \"schema\", \"host\", \"port\", \"path\", \"queryString\", \"fragment\", \"userInfo\" };\n\nstatic void push_url_part(lua_State *L, const char* buff, struct http_parser_url* parsed_url, enum http_parser_url_fields url_field) {\n\tif ((parsed_url->field_set) & (1 << url_field)) {\n\t\tlua_pushlstring(L, (buff + parsed_url->field_data[url_field].off), parsed_url->field_data[url_field].len);\n\t\tlua_setfield(L, -2, url_field_names[url_field]);\n\t}\n}\n\n/* Lua call spec:\n* Success: url parts table = http_lib.parseURL(url_string, is_connect)\n* Failure: nil = http_lib.parseURL(url_string, is_connect)\n*/\nLUA_LIB_METHOD static int luaw_parse_url(lua_State *L) {\n\tsize_t len = 0;\n\tconst char* buff = luaL_checklstring(L, 1, &len);\n\tint is_connect = lua_toboolean(L, 2);\n\n\tstruct http_parser_url* parsed_url = (struct http_parser_url*) malloc(sizeof(struct http_parser_url));\n\tif (parsed_url == NULL) {\n\t\treturn raise_lua_error(L, \"Could not allocate memory for URL struct\");\n\t}\n\n\tint result = http_parser_parse_url(buff, len, is_connect, parsed_url);\n\tif (result) {\n\t    lua_pushnil(L);\n\t    return 1;\n\t}\n\n\tlua_createtable(L, 0 , 7);\n\tpush_url_part(L, buff, parsed_url, UF_SCHEMA);\n\tpush_url_part(L, buff, parsed_url, UF_HOST);\n\tpush_url_part(L, buff, parsed_url, UF_PORT);\n\tpush_url_part(L, buff, parsed_url, UF_PATH);\n\tpush_url_part(L, buff, parsed_url, UF_QUERY);\n\tpush_url_part(L, buff, parsed_url, UF_FRAGMENT);\n\tpush_url_part(L, buff, parsed_url, UF_USERINFO);\n\n\tfree(parsed_url);\n\treturn 1;\n}\n\nstatic const struct luaL_Reg luaw_http_lib[] = {\n\t{\"urlDecode\", luaw_url_decode},\n\t{\"newHttpRequestParser\", luaw_new_http_request_parser},\n\t{\"newHttpResponseParser\", luaw_new_http_response_parser},\n\t{\"parseURL\", luaw_parse_url},\n    {NULL, NULL}  /* sentinel */\n};\n\nstatic const struct luaL_Reg http_parser_methods[] = {\n\t{\"parseHttp\", parse_http},\n\t{\"initHttpParser\", luaw_init_http_parser},\n\t{NULL, NULL}  /* sentinel */\n};\n\nvoid luaw_init_http_lib (lua_State *L) {\n    make_metatable(L, LUA_HTTP_PARSER_META_TABLE, http_parser_methods);\n    luaL_newlib(L, luaw_http_lib);\n    lua_setglobal(L, \"luaw_http_lib\");\n}\n"
  },
  {
    "path": "src/luaw_http_parser.h",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include \"http_parser.h\"\n#include \"uv.h\"\n\n#ifndef LUAW_HTTP_PARSER_H\n#define LUAW_HTTP_PARSER_H\n\n#define LUA_HTTP_PARSER_META_TABLE \"__luaw_HTTP_parser_MT__\"\n\ntypedef enum {\n\tstarting_name = 0,\n\tin_name,\n\tstarting_value,\n\tin_value\n}\ndecoder_state;\n\ntypedef enum {\n    http_cb_none = 1,  //Used to denote invocations of http_parser_execute() that don't cause callback\n    http_cb_on_message_begin,\n    http_cb_on_status,\n    http_cb_on_url,\n    http_cb_on_header_field,\n    http_cb_on_header_value,\n    http_cb_on_headers_complete,\n    http_cb_on_body,\n    http_cb_on_mesg_complete\n} http_parser_cb_type;\n\n/* imported from http_parser.h */\nstatic const char *http_methods[] =\n  {\n#define XX(num, name, string) #string,\n  HTTP_METHOD_MAP(XX)\n#undef XX\n  };\n\n\ntypedef struct {\n    http_parser parser;\n    http_parser_cb_type http_cb;\n    char* start;\n    size_t len;\n}\nluaw_http_parser_t;\n\nextern void luaw_init_http_lib(lua_State *L);\n\n#endif\n"
  },
  {
    "path": "src/luaw_logging.c",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <unistd.h>\n\n#include <lua.h>\n#include <lauxlib.h>\n\n#include \"uv.h\"\n#include \"luaw_common.h\"\n#include \"luaw_logging.h\"\n\nstatic char hostname[512] = {'\\0'};\nstatic logfile_sate log_state = LOG_NOT_OPEN;\nstatic uv_file logfile;\nstatic struct addrinfo *log_server_addr = NULL;\nstatic int log_sock_fd = -1;\n\n\nstatic const char* get_hostname() {\n    if (hostname[0] == '\\0') {\n        int rc = gethostname(hostname, 511);\n        if (rc < 0) {\n            strcpy(hostname, \"localhost\");\n        }\n    }\n    return hostname;\n}\n\nLUA_LIB_METHOD static int get_hostname_lua(lua_State *L) {\n    lua_pushstring(L, get_hostname());\n    return 1;\n}\n\nLUA_LIB_METHOD static int get_logging_state(lua_State* l_thread) {\n    lua_pushinteger(l_thread, log_state);\n    return 1;\n}\n\nLIBUV_CALLBACK static void on_log_open(uv_fs_t* req) {\n    logfile = req->result;\n    log_state = LOG_IS_OPEN;\n    uv_fs_req_cleanup(req);\n    free(req);\n}\n\nLUA_LIB_METHOD static int open_log_file(lua_State* l_thread) {\n    if (log_state == LOG_NOT_OPEN) {\n        const char* filename = lua_tostring(l_thread, 1);\n        if (filename) {\n            uv_fs_t* open_req = (uv_fs_t*)malloc(sizeof(uv_fs_t));\n            if (open_req) {\n                uv_loop_t* loop = uv_default_loop();\n                int rc = uv_fs_open(loop, open_req, filename, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP, on_log_open);\n                if (rc == 0) {\n                    log_state = OPENING_LOG;\n                }\n            }\n        }\n    }\n    return 0;\n}\n\nLIBUV_CALLBACK static void on_log_close(uv_fs_t* req) {\n    uv_fs_req_cleanup(req);\n    free(req);\n}\n\nLIBUV_CALLBACK static void close_log(uv_fs_t* req) {\n    uv_file f = *((int*)req->data);\n    uv_fs_req_cleanup(req);\n    uv_fs_close(uv_default_loop(), req, f, on_log_close);\n}\n\nLIBUV_CALLBACK static void on_log_write(uv_fs_t* req) {\n    if (req->result >= 0) {\n        uv_fs_req_cleanup(req);\n        free(req);\n    } else { /* error */\n        log_state = LOG_NOT_OPEN;\n        close_log(req);\n    }\n}\n\nLUA_LIB_METHOD static int write_log(lua_State* l_thread) {\n    if (log_state == LOG_IS_OPEN) {\n        size_t len = 0;\n        const char* str = lua_tolstring(l_thread, 1, &len);\n\n        if ((str != NULL)||(len > 0)) {\n            char* log_mesg = malloc(len * sizeof(char));\n            if (log_mesg != NULL) {\n                memcpy(log_mesg, str, len);\n                uv_fs_t* write_req = (uv_fs_t*)malloc(sizeof(uv_fs_t));\n\n                if (write_req) {\n                    write_req->data = &logfile;\n                    uv_buf_t buff = uv_buf_init(log_mesg, len);\n                    uv_loop_t* loop = uv_default_loop();\n\n                    int rotate_log = lua_toboolean(l_thread, 2);\n                    if (rotate_log == 0) {\n                        int rc = uv_fs_write(loop, write_req, logfile, &buff, 1, -1, on_log_write);\n                        if (rc != 0) {\n                            log_state = LOG_NOT_OPEN;\n                            close_log(write_req);\n                        }\n                    } else {\n                        log_state = LOG_NOT_OPEN;\n                        uv_fs_write(loop, write_req, logfile, &buff, 1, -1, close_log);\n                    }\n                } else {\n                    free(log_mesg);\n                }\n            }\n        }\n    }\n\n    lua_pushinteger(l_thread, log_state);\n    return 1;\n}\n\nvoid close_syslog() {\n\tuv_freeaddrinfo(log_server_addr);\n}\n\nLUA_LIB_METHOD static int connect_to_syslog(lua_State *L) {\n    int rc = -1;\n    int flags = 1;\n    const char* log_server_ip = lua_tostring(L, 1);\n    const char* log_server_port = lua_tostring(L, 2);\n\n    if (log_server_ip && log_server_port) {\n        struct addrinfo hints;\n\n        memset(&hints, 0, sizeof(struct addrinfo));\n        hints.ai_family = AF_INET;\n        hints.ai_socktype = SOCK_DGRAM;\n        hints.ai_flags = 0;\n        hints.ai_protocol = 0;\n\n        rc = getaddrinfo(log_server_ip, log_server_port, &hints, &log_server_addr);\n        if (rc != 0) {\n            fprintf(stderr,\"failed to get address for sys log server :%s\\n\",gai_strerror(rc));\n        } else {\n            rc = socket(log_server_addr->ai_family, log_server_addr->ai_socktype, log_server_addr->ai_protocol);\n            if (rc < 0) {\n                fprintf(stderr, \"could not connect to sys log server\\n\");\n            } else {\n                log_sock_fd = rc;\n                #if defined(O_NONBLOCK)\n                    if (-1 == (flags = fcntl(log_sock_fd, F_GETFL, 0))) flags = 0;\n                    rc = fcntl(log_sock_fd, F_SETFL, flags | O_NONBLOCK);\n                #else\n                    rc = ioctl(log_sock_fd, FIONBIO, &flags);\n                #endif\n                if (rc < 0) {\n                    fprintf(stderr, \"Error putting syslog connection in non blocking mode\\n\");\n                }\n            }\n        }\n    }\n\n    lua_pushboolean(L, (rc < 0) ? 0 : 1);\n    return 1;\n}\n\nLUA_LIB_METHOD static int send_to_syslog(lua_State *L) {\n    if (log_sock_fd > 0) {\n        size_t len = 0;\n        const char* mesg = lua_tolstring(L, 1, &len);\n        if ((mesg)&&(len > 0)) {\n            sendto(log_sock_fd, mesg, len, MSG_DONTWAIT,log_server_addr->ai_addr, log_server_addr->ai_addrlen);\n        }\n    }\n    return 0;\n}\n\n\n\nstatic const struct luaL_Reg luaw_logging_lib[] = {\n\t{\"logState\", get_logging_state},\n\t{\"openLog\", open_log_file},\n\t{\"writeLog\", write_log},\n    {\"syslogConnect\", connect_to_syslog},\n\t{\"syslogSend\", send_to_syslog},\n    {\"hostname\", get_hostname_lua},\n    {NULL, NULL}  /* sentinel */\n};\n\nint luaw_init_logging_lib (lua_State *L) {\n    luaL_newlib(L, luaw_logging_lib);\n    lua_setglobal(L, \"luaw_logging_lib\");\n\treturn 1;\n}\n"
  },
  {
    "path": "src/luaw_logging.h",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#ifndef LUAW_LOGGING_H\n\n#define LUAW_LOGGING_H\n\ntypedef enum {\n    LOG_NOT_OPEN = 0,\n    OPENING_LOG,\n    LOG_IS_OPEN,\n} logfile_sate;\n\nextern void close_syslog();\nextern int luaw_init_logging_lib (lua_State *L);\n\n#endif\n"
  },
  {
    "path": "src/luaw_server.c",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <errno.h>\n#include <unistd.h>\n\n#include <lua.h>\n#include <lauxlib.h>\n#include <lualib.h>\n\n#include \"uv.h\"\n#include \"luaw_common.h\"\n#include \"luaw_logging.h\"\n#include \"luaw_tcp.h\"\n#include \"lfs.h\"\n\nstatic char* server_ip = \"0.0.0.0\";\nstatic int server_port = 80;\nstatic uv_tcp_t server;\nstatic uv_loop_t* event_loop;\nstatic uv_signal_t shutdown_signal;\n\nstatic int service_http_fn_ref;\nstatic int start_thread_fn_ref;\nstatic int run_ready_threads_fn_ref;\n\n#define LUA_LOAD_FILE_BUFF_SIZE 1024\n\ntypedef struct {\n    FILE *file;                             /* file being read */\n    char buff[LUA_LOAD_FILE_BUFF_SIZE];     /* area for reading file */\n    char* epilogue;\n} lua_load_buffer_t;\n\nuv_prepare_t user_thread_runner;\n\nstatic const char* lua_file_reader(lua_State* L, void* data, size_t* size) {\n    lua_load_buffer_t *lb = (lua_load_buffer_t*)data;\n\n    if (lb->file == NULL) return NULL;\n\n    if (feof(lb->file)) {\n        fclose(lb->file);\n        lb->file = NULL;\n        *size = (lb->epilogue != NULL) ? strlen(lb->epilogue) : 0;\n        return lb->epilogue;\n    }\n\n    *size = fread(lb->buff, 1, sizeof(lb->buff), lb->file);  /* read block */\n    return lb->buff;\n}\n\nstatic void handle_shutdown_req(uv_signal_t* handle, int signum) {\n    if (signum == SIGHUP) {\n        fprintf(stderr, \"shutdown request received\\n\");\n        uv_signal_stop(handle);\n        uv_stop(event_loop);\n    }\n}\n\nvoid init_luaw_server(lua_State* L) {\n    lua_getglobal(L, \"luaw_http_lib\");\n    if (!lua_istable(L, -1)) {\n        fprintf(stderr, \"Luaw HTTP library not initialized\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    lua_getfield(L, -1, \"request_handler\");\n    if (!lua_isfunction(L, -1)) {\n        fprintf(stderr, \"Main HTTP request handler function (Luaw.request_handler) not set\\n\");\n        exit(EXIT_FAILURE);\n    }\n    service_http_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX);\n    lua_pop(L, 1);\n\n    lua_getglobal(L, \"luaw_scheduler\");\n    if (!lua_istable(L, -1)) {\n        fprintf(stderr, \"Luaw scheduler not initialized\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    lua_getfield(L, -1, \"resumeThreadId\");\n    if (lua_isfunction(L, -1)) {\n        resume_thread_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX);\n    } else {\n        fprintf(stderr, \"resumeThreadId function not found in luaw scheduler\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    lua_getfield(L, -1, \"startSystemThread\");\n    if (lua_isfunction(L, -1)) {\n        start_thread_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX);\n    } else {\n        fprintf(stderr, \"startSystemThread function not found in luaw scheduler\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    lua_getfield(L, -1, \"runReadyThreads\");\n    if (lua_isfunction(L, -1)) {\n        run_ready_threads_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX);\n    } else {\n        fprintf(stderr, \"runReadyThreads function not found in luaw scheduler\\n\");\n        exit(EXIT_FAILURE);\n    }\n    lua_pop(L, 1);\n\n    lua_getglobal(L, \"luaw_server_config\");\n    if (lua_istable(L, -1)) {\n        lua_getfield(L, -1, \"server_ip\");\n        if (lua_isstring(L, -1)) {\n            server_ip = (char *)lua_tostring(L, -1);\n            lua_pop(L, 1);\n        }\n\n        lua_getfield(L, -1, \"server_port\");\n        if (lua_isnumber(L, -1)) {\n            server_port = lua_tointeger(L, -1);\n            lua_pop(L, 1);\n        }\n\n        lua_pop(L, 1);  //pop luaw_server_config object\n    }\n\n    lua_pushnumber(L, CONN_BUFFER_SIZE);\n    lua_setglobal(L, \"CONN_BUFFER_SIZE\");\n\n    event_loop = uv_default_loop();\n}\n\n/* create a new lua coroutine to service this conn, anchor it in \"all active coroutines\"\n*  global table to prevent it from being garbage collected and start the coroutine\n*/\nLIBUV_CALLBACK static void on_server_connect(uv_stream_t* server, int status) {\n    if (status) {\n        raise_lua_error(l_global, \"Error in on_server_connect callback: %s\\n\", uv_strerror(status));\n        return;\n    }\n\n    lua_rawgeti(l_global, LUA_REGISTRYINDEX, start_thread_fn_ref);\n    assert(lua_isfunction(l_global, -1));\n\n    lua_rawgeti(l_global, LUA_REGISTRYINDEX, service_http_fn_ref);\n    assert(lua_isfunction(l_global, -1));\n\n    connection_t * conn = new_connection(l_global);\n    status = uv_accept(server, (uv_stream_t*)&conn->handle);\n    if (status) {\n        close_connection(conn, status);\n        fprintf(stderr, \"Error accepting incoming conn: %s\\n\", uv_strerror(status));\n        return;\n    }\n\n    status = lua_pcall(l_global, 2, 2, 0);\n    if (status) {\n        fprintf(stderr, \"**** Error starting new client connect thread: %s (%d) ****\\n\", lua_tostring(l_global, -1), status);\n    }\n}\n\nvoid start_server(lua_State *L) {\n    fprintf(stderr, \"starting server on port %d ...\\n\", server_port);\n\n    struct sockaddr_in addr;\n    int err_code = uv_ip4_addr(server_ip, server_port, &addr);\n    if (err_code) {\n        fprintf(stderr, \"Error initializing socket address: %s\\n\", uv_strerror(err_code));\n        exit(EXIT_FAILURE);\n    }\n\n    uv_tcp_init(event_loop, &server);\n\n    err_code = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0);\n    if (err_code) {\n        fprintf(stderr, \"Error binding to port %d : %s\\n\", server_port, uv_strerror(err_code));\n        exit(EXIT_FAILURE);\n    }\n\n    err_code = uv_listen((uv_stream_t*)&server, 128, on_server_connect);\n    if (err_code) {\n        fprintf(stderr, \"Error listening on port %d : %s\\n\", server_port, uv_strerror(err_code));\n        exit(EXIT_FAILURE);\n    }\n\n    uv_signal_init(event_loop, &shutdown_signal);\n    uv_signal_start(&shutdown_signal, handle_shutdown_req, SIGHUP);\n}\n\nstatic void run_user_threads(uv_prepare_t* handle) {\n    /* do the bottom half processing, run ready user threads that are waiting */\n    lua_rawgeti(l_global, LUA_REGISTRYINDEX, run_ready_threads_fn_ref);\n    int status = lua_pcall(l_global, 0, 1, 0);\n\n    if (status != LUA_OK) {\n        fprintf(stderr,\"Error while running user threads for bottom half processing: %s\\n\", lua_tostring(l_global, -1));\n        uv_stop(event_loop);\n    }\n\n    lua_settop(l_global, 0);\n}\n\nstatic void close_walk_cb(uv_handle_t* handle, void* arg) {\n\tif (!uv_is_closing(handle)) {\n\t\tuv_close(handle, NULL);\n\t}\n}\n\nstatic int server_loop(lua_State *L) {\n    uv_prepare_init(event_loop, &user_thread_runner);\n    uv_prepare_start(&user_thread_runner, run_user_threads);\n\n    int status = uv_run(event_loop, UV_RUN_DEFAULT);\n\n    /* clean up resources used by the event loop and Lua */\n    uv_walk(event_loop, close_walk_cb, NULL);\n    uv_run(event_loop, UV_RUN_ONCE);\n    uv_loop_delete(event_loop);\n    lua_close(L);\n\tclose_syslog();\n\n    return status;\n}\n\nstatic void run_lua_file(const char* filename, char* epilogue) {\n    lua_load_buffer_t lb;\n\n    lb.file = fopen(filename, \"r\");\n    if (lb.file == NULL) {\n        fprintf(stderr, \"Could not open file %s for reading\\n\", filename);\n        exit(EXIT_FAILURE);\n    }\n    lb.epilogue = epilogue;\n\n    #ifdef COMPAT52_IS_LUAJIT\n        int status = lua_load(l_global, lua_file_reader, &lb, filename);\n    #else\n        int status = lua_load(l_global, lua_file_reader, &lb, filename, \"t\");\n    #endif\n\n    if (status != LUA_OK) {\n        fprintf(stderr, \"Error while loading file: %s\\n\", filename);\n        fprintf(stderr, \"%s\\n\", lua_tostring(l_global, -1));\n        exit(EXIT_FAILURE);\n    }\n\n    status = lua_pcall(l_global, 0, 0, 0);\n    if (status != LUA_OK) {\n        fprintf(stderr, \"Error while executing file: %s\\n\", filename);\n        fprintf(stderr, \"%s\\n\", lua_tostring(l_global, -1));\n        fprintf(stderr, \"\\n\\t* Try running luaw_server from directory that contains \\\"bin\\\" directory containing the binary \\\"luaw_server\\\"\\n\");\n        fprintf(stderr, \"\\t* For example: ./bin/luaw_server <server_config_file>\\n\\n\");\n        exit(EXIT_FAILURE);\n    }\n}\n\nstatic void set_lua_path(lua_State* L) {\n    lua_getglobal( L, \"package\" );\n    lua_pushliteral(L, \"?;?.lua;./bin/?;./bin/?.lua;./lib/?;./lib/?.lua\");\n    lua_setfield( L, -2, \"path\" );\n    lua_pop(L, 1);\n}\n\nint main (int argc, char* argv[]) {\n\tif (argc < 2) {\n\t\tfprintf(stderr, \"Usage: %s <luaw config file >\\n\", argv[0]);\n\t\texit(EXIT_FAILURE);\n\t}\n\n    l_global = luaL_newstate();\n\tif (!l_global) {\n\t\tfprintf(stderr, \"Could not create new Lua state\\n\");\n\t\texit(EXIT_FAILURE);\n\t}\n\n    lua_gc(l_global, LUA_GCSTOP, 0);  /* stop collector during initialization */\n    luaL_openlibs(l_global);  /* open libraries */\n    luaw_init_libs(l_global);\n    luaopen_lfs(l_global);\n    lua_gc(l_global, LUA_GCRESTART, 0);\n\n    /* load config file, mandatory */\n    set_lua_path(l_global);\n    run_lua_file(argv[1], \"\\ninit = require(\\\"luaw_init\\\")\\n\");\n\n    /* run other lua on startup script passed on the command line, if any */\n    int i = 2;\n    for (; i < argc; i++) {\n        fprintf(stderr, \"## Running %s \\n\", argv[i]);\n        run_lua_file(argv[i], NULL);\n    }\n\n    init_luaw_server(l_global);\n    start_server(l_global);\n    int status = server_loop(l_global);\n\n    exit(status);\n}\n"
  },
  {
    "path": "src/luaw_tcp.c",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <errno.h>\n#include <unistd.h>\n\n#include <lua.h>\n#include <lauxlib.h>\n\n#include \"uv.h\"\n#include \"luaw_common.h\"\n#include \"http_parser.h\"\n#include \"luaw_http_parser.h\"\n#include \"luaw_tcp.h\"\n#include \"lfs.h\"\n\n\n\nconnection_t* new_connection(lua_State* L) {\n    connection_t* conn = (connection_t*)calloc(1, sizeof(connection_t));\n    if (conn == NULL) {\n        raise_lua_error(L, \"Could not allocate memory for client connection\");\n        return NULL;\n    }\n\n    connection_t** lua_ref = lua_newuserdata(L, sizeof(connection_t*));\n    if (lua_ref == NULL) {\n        free(conn);\n        raise_lua_error(L, \"Could not allocate memory for client connection Lua reference\");\n        return NULL;\n    }\n\n    /* link C side connection reference and Lua's full userdata that represents it to each other */\n    luaL_setmetatable(L, LUA_CONNECTION_META_TABLE);\n    *lua_ref = conn;\n    INCR_REF_COUNT(conn)\n    conn->lua_ref = lua_ref;\n\n    /* init libuv artifacts */\n    uv_tcp_init(uv_default_loop(), &conn->handle);\n    conn->handle.data = conn;\n    INCR_REF_COUNT(conn)\n\n    uv_timer_init(uv_default_loop(), &conn->read_timer);\n    conn->read_timer.data = conn;\n    INCR_REF_COUNT(conn)\n    conn->read_len = 0;\n    conn->lua_reader_tid = 0;\n\n    uv_timer_init(uv_default_loop(), &conn->write_timer);\n    conn->write_timer.data = conn;\n\tINCR_REF_COUNT(conn)\n    conn->lua_writer_tid = 0;\n\n    return conn;\n}\n\nLUA_LIB_METHOD static int new_connection_lua(lua_State* L) {\n    new_connection(L);\n    return 1;\n}\n\nstatic void free_timer(uv_handle_t* handle) {\n    connection_t* conn = GET_CONN_OR_RETURN(handle);\n    handle->data = NULL;\n    GC_REF(conn)\n}\n\nstatic void free_tcp_handle(uv_handle_t* handle) {\n    connection_t* conn = GET_CONN_OR_RETURN(handle);\n    handle->data = NULL;\n    GC_REF(conn)\n}\n\nvoid close_connection(connection_t* conn, const int status) {\n    /* conn->lua_ref == NULL also acts as a flag to mark that this conn has been closed */\n    if ((conn == NULL)||(conn->lua_ref == NULL)) return;\n\n    *(conn->lua_ref) = NULL;  //delink from Lua's userdata\n    conn->lua_ref = NULL;\n    DECR_REF_COUNT(conn);\n\n    uv_timer_stop(&conn->read_timer);\n    close_if_active((uv_handle_t*)&conn->read_timer, (uv_close_cb)free_timer);\n\n    uv_timer_stop(&conn->write_timer);\n    close_if_active((uv_handle_t*)&conn->write_timer, (uv_close_cb)free_timer);\n\n    close_if_active((uv_handle_t*)&conn->handle, (uv_close_cb)free_tcp_handle);\n\n\n    /* unblock reader thread */\n    if (conn->lua_reader_tid) {\n        lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n        lua_pushinteger(l_global, conn->lua_reader_tid);\n        if ((status == 0)||(status == UV_EOF)) {\n            lua_pushboolean(l_global, 0);\n            lua_pushliteral(l_global, \"EOF\");\n        } else {\n            /* error */\n            lua_pushboolean(l_global, 0);\n            lua_pushstring(l_global, uv_strerror(status));\n        }\n\n        conn->lua_reader_tid = 0;\n        resume_lua_thread(l_global, 3, 2, 0);\n    }\n\n    /* unblock writer thread */\n    if (conn->lua_writer_tid) {\n        lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n        lua_pushinteger(l_global, conn->lua_writer_tid);\n        if ((status == 0)||(status == UV_EOF)) {\n            lua_pushboolean(l_global, 1);\n            lua_pushinteger(l_global, 0);\n        } else {\n            /* error */\n            lua_pushboolean(l_global, 0);\n            lua_pushstring(l_global, uv_strerror(status));\n        }\n\n        conn->lua_writer_tid = 0;\n        resume_lua_thread(l_global, 3, 2, 0);\n    }\n}\n\nLIBUV_CALLBACK static void on_conn_timeout(uv_timer_t* timer) {\n    /* Either connect,read or write timed out, close the connection */\n    connection_t* conn = GET_CONN_OR_RETURN(timer);\n    close_connection(conn, UV_ECANCELED);\n}\n\nstatic int start_timer(uv_timer_t* timer, int timeout) {\n    if ((timeout > 0)&&(!uv_is_active((uv_handle_t*) timer))) {\n        return uv_timer_start(timer, on_conn_timeout, timeout, 0);\n    }\n    return 0;\n}\n\nstatic void stop_timer(uv_timer_t* timer) {\n    if (uv_is_active((uv_handle_t*) timer)) {\n        uv_timer_stop(timer);\n    }\n}\n\nLUA_OBJ_METHOD static int close_connection_lua(lua_State* l_thread) {\n    LUA_GET_CONN_OR_RETURN(l_thread, 1, conn);\n\n    /* being called from lua, no reason to resume thread */\n    conn->lua_reader_tid = 0;\n    conn->lua_writer_tid = 0;\n    close_connection(conn, UV_EOF);\n    return 0;\n}\n\nLUA_OBJ_METHOD static int connection_gc(lua_State *L) {\n    LUA_GET_CONN_OR_RETURN(L, 1, conn);\n\n    /* if we reached here, there is a connection that has not been closed */\n    fprintf(stderr, \"Luaw closed unclosed connection\\n\");\n\n    /* being called from lua, no reason to resume thread */\n    conn->lua_reader_tid = 0;\n    conn->lua_writer_tid = 0;\n    close_connection(conn, UV_ECANCELED);\n    return 0;\n}\n\n/* reuse the buffer attached with this conn to minimize memory allocation. Each on_read()\n*  resets the buffer to empty after sending all the bytes read to the coroutine servicing this\n*  conn. If we get called before on_read() has had chance to empty the buffer, we return\n*  0 which means on_read() will be called with nread=UV_ENOBUFS next which we must handle.\n*/\nLIBUV_CALLBACK static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {\n    buf->base = NULL;\n    buf->len = 0;\n\n    connection_t* conn = GET_CONN_OR_RETURN(handle);\n    if(conn->read_buffer) {\n        size_t free_space = CONN_BUFFER_SIZE - conn->read_len;\n        if (free_space > 0) {\n            buf->base = conn->read_buffer + conn->read_len;\n            buf->len = free_space;\n        }\n    }\n}\n\n/* Returns\n* 1. tid\n* 2. status: true = succesfull read, false = error\n* 3. string = read string for succesfull read, error message for failure\n*/\nLIBUV_API static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {\n    connection_t* conn = GET_CONN_OR_RETURN(stream);\n    stop_timer(&conn->read_timer); //clear read timeout if any\n\n    if ((nread == 0)||(nread == UV_ENOBUFS)) {\n        /* either no data was read or no buffer was available to read the data. Anyway there\n           is nothing to do so no need to wake conn coroutine. Let this callback pass through\n           as  NOOP */\n        return;\n    }\n\n    if (nread > 0) {\n        /* success: send read bytes to coroutine if one is waiting */\n        lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n        lua_pushinteger(l_global, conn->lua_reader_tid);\n        lua_pushboolean(l_global, 1);\n        lua_pushlstring(l_global, conn->read_buffer, (conn->read_len + nread));\n        conn->lua_reader_tid = 0;\n        conn->read_len = 0L;\n        resume_lua_thread(l_global, 3, 2, 0);\n        return;\n    }\n\n    /* either EOF or read error, in either case close connection */\n    close_connection(conn, nread);\n}\n\n/* lua call spec:\nSuccess:  status(true), nil = conn:start_reading()\nFailure:  status(false), error message = conn:start_reading()\n*/\nLUA_OBJ_METHOD static int start_reading(lua_State *l_thread) {\n    LUA_GET_CONN_OR_ERROR(l_thread, 1, conn);\n\n    conn->lua_reader_tid = 0;\n    int err_code = uv_read_start((uv_stream_t*)&conn->handle, on_alloc, on_read);\n    if (err_code) {\n        lua_pushboolean(l_thread, 0);\n        lua_pushstring(l_thread,  uv_strerror(err_code));\n        close_connection(conn, err_code);\n        return 2;\n    }\n\n    lua_pushboolean(l_thread, 1);\n    return 1;\n}\n\n/* Returns\n* 1. status: true for success or no data, false for error\n* 2. str: read string for successful read, NULL if no data, error message for failure\n*/\nLUA_OBJ_METHOD static int read_check(lua_State* l_thread) {\n    LUA_GET_CONN_OR_ERROR(l_thread, 1, conn);\n\n    if (!uv_is_active((uv_handle_t*)&conn->handle)) {\n        close_connection(conn, UV_EAI_BADFLAGS);\n       return error_to_lua(l_thread, \"read() called on conn that is not registered to receive read events\");\n    }\n\n    if (conn->read_len == 0) {\n        /* empty buffer, record reader tid and block (yield) in lua */\n        int lua_reader_tid = lua_tointeger(l_thread, 2);\n        if (lua_reader_tid == 0) {\n            return error_to_lua(l_thread, \"read() specified invalid thread id\");\n        }\n\n        conn->lua_reader_tid = lua_reader_tid;\n        int readTimeout = lua_tointeger(l_thread, 3);\n        start_timer(&conn->read_timer, readTimeout);\n\n        lua_pushboolean(l_thread, 1);\n        lua_pushnil(l_thread);\n        return 2;\n    }\n\n    /* data available in buffer */\n    lua_pushboolean(l_thread, 1);\n    lua_pushlstring(l_thread, conn->read_buffer, conn->read_len);\n    return 2;\n}\n\nLIBUV_API static void on_write(uv_write_t* req, int status) {\n    connection_t* conn = TO_CONN(req);\n    if(conn) {\n        req->data = NULL;\n        if (status) {\n            close_connection(conn, status);\n        } else {\n            stop_timer(&conn->write_timer); //clear write timeout if any\n            lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n            lua_pushinteger(l_global, conn->lua_writer_tid);\n            conn->lua_writer_tid = 0;\n            lua_pushboolean(l_global, 1);\n            lua_pushinteger(l_global, 1);\n            resume_lua_thread(l_global, 3, 2, 0);\n        }\n        /* Unlike libuv handles, libuv requests do not support uv_close(). Therefore we increment\n        reference count every time request starts and decrement it as soon as it completes */\n        GC_REF(conn)\n    }\n}\n\n/* lua call spec: conn:write(tid, str, writeTimeout)\nSuccess: status(true), nwritten\nFailure: status(false), error message\n*/\nLUA_OBJ_METHOD static int write_buffer(lua_State* l_thread) {\n    LUA_GET_CONN_OR_ERROR(l_thread, 1, conn);\n\n    int lua_writer_tid = lua_tointeger(l_thread, 2);\n    if (lua_writer_tid == 0) {\n        return error_to_lua(l_thread, \"write() specified invalid thread id\");\n    }\n\n    size_t len = 0;\n    const char* buff = lua_tolstring(l_thread, 3, &len);\n\n    if (len > 0) {\n        /* non empty write buffer. Send write request, record writer tid and block in lua */\n        int writeTimeout = lua_tointeger(l_thread, 4);\n\n        uv_buf_t write_buff;\n        write_buff.base = (char*) buff;\n        write_buff.len = len;\n\n        int err_code = uv_write(&conn->write_req, (uv_stream_t*)&conn->handle, &write_buff, 1, on_write);\n        if (err_code) {\n            close_connection(conn, err_code);\n            lua_pushboolean(l_thread, 0);\n            lua_pushstring(l_thread,  uv_strerror(err_code));\n            return 2;\n        }\n\n        conn->write_req.data = conn;\n        INCR_REF_COUNT(conn)\n        conn->lua_writer_tid = lua_writer_tid;\n        start_timer(&conn->write_timer, writeTimeout);\n    }\n\n    lua_pushboolean(l_thread, 1);\n    lua_pushinteger(l_thread, len);\n    return 2;\n}\n\nLIBUV_CALLBACK static void on_client_connect(uv_connect_t* connect_req, int status) {\n    connection_t* conn = GET_CONN_OR_RETURN(connect_req);\n\n    stop_timer(&conn->write_timer); //clear connect timeout if any\n    free(connect_req);\n\n    lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n    lua_pushinteger(l_global, conn->lua_writer_tid);\n\n    if (status) {\n        close_connection(conn, status);\n        lua_pushboolean(l_global, 0);\n        lua_pushstring(l_global, uv_strerror(status));\n    } else {\n        lua_pushboolean(l_global, 1); //status to be returned\n        lua_pushnil(l_global);\n    }\n\n    resume_lua_thread(l_global, 3, 2, 0);\n}\n\n/* lua call spec: luaw_lib.connect(ip4_addr, port, tid, connectTimeout)\nSuccess: conn\nFailure: false, error message\n*/\nLUA_LIB_METHOD static int client_connect(lua_State* l_thread) {\n    const char* ip4 = luaL_checkstring(l_thread, 1);\n\n    int port = lua_tointeger(l_thread, 2);\n    if (port == 0) {\n        return error_to_lua(l_thread, \"Invalid port specified in client_connect\");\n    }\n\n    int tid = lua_tointeger(l_thread, 3);\n    if (tid == 0) {\n        return error_to_lua(l_thread, \"Invalid thread id specified in client_connect\");\n    }\n\n    int connectTimeout = lua_tointeger(l_thread, 4);\n\n    struct sockaddr_in addr;\n    if(uv_ip4_addr(ip4, port, &addr)) {\n        return error_to_lua(l_thread, \"Invalid ip address %s and port %d combination specified in client_connect\", ip4, port);\n    }\n\n    connection_t* conn = new_connection(l_thread);\n\n    uv_connect_t* connect_req = (uv_connect_t*)malloc(sizeof(uv_connect_t));\n    if (connect_req == NULL) {\n        close_connection(conn, UV_ENOMEM);\n        return error_to_lua(l_thread, \"Could not allocate memory for connect request\");\n    }\n    connect_req->data = conn;\n\n    int status = uv_tcp_connect(connect_req, &conn->handle, (const struct sockaddr*) &addr, on_client_connect);\n    if (status) {\n        free(connect_req);\n        close_connection(conn, status);\n        return error_to_lua(l_thread, \"tcp connect failed: %s\", uv_strerror(status));\n    }\n\n    conn->lua_writer_tid = tid;\n    start_timer(&conn->write_timer, connectTimeout);\n    return 1;\n}\n\nLIBUV_CALLBACK static void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) {\n    int lua_tid = *((int *)resolver->data);\n    free(resolver->data);\n    free(resolver);\n\n    char ip_str[17] = {'\\0'};\n    if ((status == 0)&&(res != NULL)) {\n        struct sockaddr_in addr = *(struct sockaddr_in*) res->ai_addr;\n        status = uv_ip4_name(&addr, (char*)ip_str, sizeof(ip_str));\n        uv_freeaddrinfo(res);\n    }\n\n    lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n    lua_pushinteger(l_global, lua_tid);\n\n    if ((status)||(res == NULL)) {\n        lua_pushboolean(l_global, 0);\n        lua_pushfstring(l_global, \"DNS resolution failed: %s\", uv_strerror(status));\n    } else {\n        lua_pushboolean(l_global, 1);       //status to be returned\n        lua_pushstring(l_global, ip_str);   //IP address string\n    }\n    resume_lua_thread(l_global, 3, 2, 0);\n}\n\nLUA_LIB_METHOD static int dns_resolve(lua_State* l_thread) {\n    const char* hostname = luaL_checkstring(l_thread, 1);\n\n    int lua_tid = lua_tointeger(l_thread, 2);\n    if (lua_tid == 0) {\n        return error_to_lua(l_thread, \"Invalid thread id specified in dns_resolve\");\n    }\n\n    struct addrinfo hints;\n    hints.ai_family = PF_INET;\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_protocol = IPPROTO_TCP;\n    hints.ai_flags = 0;\n\n    uv_getaddrinfo_t* resolver = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t));\n    if (resolver == NULL) {\n        return error_to_lua(l_thread, \"Could not allocate memory for DNS resolver\");\n    }\n\n    int* tid = (int*)malloc(sizeof(int));\n    if (tid == NULL) {\n        free(resolver);\n        return error_to_lua(l_thread, \"Could not allocate memory thread id\");\n    }\n    *tid = lua_tid;\n    resolver->data = tid;\n\n    int status = uv_getaddrinfo(uv_default_loop(), resolver, on_resolved, hostname,  NULL, &hints);\n    if (status) {\n        free(resolver);\n        free(tid);\n        return error_to_lua(l_thread, \"DNS resolve failed: %s\", uv_strerror(status));\n    }\n\n    //success\n    lua_pushboolean(l_thread, 1);\n    return 1;\n}\n\n\nstatic const struct luaL_Reg luaw_connection_methods[] = {\n\t{\"startReading\", start_reading},\n\t{\"read\", read_check},\n\t{\"write\", write_buffer},\n\t{\"close\", close_connection_lua},\n\t{\"__gc\", connection_gc},\n\t{NULL, NULL}  /* sentinel */\n};\n\nstatic const struct luaL_Reg luaw_tcp_lib[] = {\n\t{\"newConnection\", new_connection_lua},\n\t{\"connect\", client_connect},\n\t{\"resolveDNS\", dns_resolve},\n    {NULL, NULL}  /* sentinel */\n};\n\n\nvoid luaw_init_tcp_lib (lua_State *L) {\n\tmake_metatable(L, LUA_CONNECTION_META_TABLE, luaw_connection_methods);\n    luaL_newlib(L, luaw_tcp_lib);\n    lua_setglobal(L, \"luaw_tcp_lib\");\n}\n\n"
  },
  {
    "path": "src/luaw_tcp.h",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#ifndef LUAW_TCP_H\n\n#define LUAW_TCP_H\n\n#define LUA_CONNECTION_META_TABLE \"_luaw_connection_MT_\"\n#define CONN_BUFFER_SIZE 4096\n\ntypedef struct connection_s connection_t;\n\n/* client connection's state: socket connection, coroutines servicing the connection\n and read/write buffers for the connection */\nstruct connection_s {\n    uv_tcp_t handle;                        /* connected socket */\n\n    /* read section */\n    int lua_reader_tid;                     /* ID of the reading coroutine */\n\tsize_t read_len;\t\t\t            /* read length */\n    uv_timer_t read_timer;                  /* for read timeout */\n\n    /* write section */\n    int lua_writer_tid;                     /* ID of the writing coroutine */\n    uv_timer_t write_timer;                 /* for write/connect timeout */\n    uv_write_t write_req;                   /* write request */\n\n    /* memory management */\n    int ref_count;                          /* reference count */\n    connection_t** lua_ref;                 /* back reference to Lua's full userdata pointing to this conn */\n\n    /* read buffer */\n    char read_buffer[CONN_BUFFER_SIZE];     /* buffer to read into */\n};\n\n#define MAX_CONNECTION_BUFF_SIZE 65536  //16^4\n\n#define TO_CONN(h) (connection_t*)h->data\n\n#define GET_CONN_OR_RETURN(h)   \\\n    (connection_t*)h->data;     \\\n    if(!h->data) return\n\n#define LUA_GET_CONN_OR_RETURN(L, i, c)                                     \\\n    connection_t** cr = luaL_checkudata(L, i, LUA_CONNECTION_META_TABLE);   \\\n    if (cr == NULL) return 0;                                               \\\n    connection_t* c = *cr;                                                  \\\n    if (c == NULL) return 0;\n\n#define LUA_GET_CONN_OR_ERROR(L, i, c)                                      \\\n    connection_t** cr = luaL_checkudata(L, i, LUA_CONNECTION_META_TABLE);   \\\n    if (cr == NULL) return error_to_lua(L, \"Connection missing\");           \\\n    connection_t* c = *cr;                                                  \\\n    if (c == NULL) return error_to_lua(L, \"Connection closed\");\n\n#define TO_TIMER(h) (luaw_timer_t*)h->data\n\n\n/* TCP lib methods to be exported */\nextern connection_t* new_connection(lua_State* L);\nextern void close_connection(connection_t* conn, const int status);\nextern void luaw_init_tcp_lib (lua_State *L);\n\n#endif\n"
  },
  {
    "path": "src/luaw_timer.c",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <errno.h>\n#include <unistd.h>\n\n#include <lua.h>\n#include <lauxlib.h>\n\n#include \"uv.h\"\n#include \"luaw_common.h\"\n#include \"luaw_timer.h\"\n\n\nstatic void clear_user_timer(luaw_timer_t* timer) {\n    timer->state = INIT;\n    timer->lua_tid = 0;\n}\n\nstatic void free_user_timer(uv_handle_t* handle) {\n    luaw_timer_t* timer = GET_TIMER_OR_RETURN(handle);\n    handle->data = NULL;\n    GC_REF(timer)\n}\n\nLUA_OBJ_METHOD static void close_timer(luaw_timer_t* timer) {\n    /* timer->lua_ref == NULL also acts as a flag to mark that this timer has been closed */\n    if ((timer == NULL)||(timer->lua_ref == NULL)) return;\n\n    *(timer->lua_ref) = NULL;  //delink from Lua's userdata\n    timer->lua_ref = NULL;\n    DECR_REF_COUNT(timer);\n\n    /* unblock waiting thread */\n    if (timer->lua_tid) {\n        lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n        lua_pushinteger(l_global, timer->lua_tid);\n        lua_pushboolean(l_global, 0);                           //status\n        lua_pushstring(l_global, uv_strerror(UV_ECANCELED));    //error message\n        timer->lua_tid = 0;\n        resume_lua_thread(l_global, 3, 2, 0);\n    }\n\n    close_if_active((uv_handle_t*)&timer->handle, free_user_timer);\n}\n\nLUA_OBJ_METHOD static int close_timer_lua(lua_State* l_thread) {\n    LUA_GET_TIMER_OR_RETURN(l_thread, 1, timer);\n    close_timer(timer);\n    return 0;\n}\n\nLUA_OBJ_METHOD static int timer_gc(lua_State *L) {\n    LUA_GET_TIMER_OR_RETURN(L, 1, timer);\n    close_timer(timer);\n    return 0;\n}\n\nLUA_LIB_METHOD static int new_user_timer(lua_State* l_thread) {\n    luaw_timer_t* timer = (luaw_timer_t*)calloc(1, sizeof(luaw_timer_t));\n    if (timer == NULL) {\n        return raise_lua_error(l_thread, \"Could not allocate memory for user timer\");\n    }\n\n    luaw_timer_t** lua_ref = lua_newuserdata(l_thread, sizeof(luaw_timer_t*));\n    if (lua_ref == NULL) {\n        free(timer);\n        return raise_lua_error(l_thread, \"Could not allocate memory for user timer Lua reference\");\n    }\n\n    /* link C side connection reference and Lua's full userdata that represents it to each other */\n    luaL_setmetatable(l_thread, LUA_USER_TIMER_META_TABLE);\n    *lua_ref = timer;\n    INCR_REF_COUNT(timer)\n    timer->lua_ref = lua_ref;\n\n    /* init libuv artifacts */\n    uv_timer_init(uv_default_loop(), &timer->handle);\n    timer->handle.data = timer;\n    INCR_REF_COUNT(timer)\n    clear_user_timer(timer);\n\n    return 1;\n}\n\n/* lua call spec: status, timer_elapsed = timer:wait(tid)\nSuccess, timer elapsed:  status = true, timer_elapsed = true\nSuccess, timer not elapsed:  status = true, timer_elapsed = false\nFailure:  status = false, error message\n*/\nLIBUV_CALLBACK static void on_user_timer_timeout(uv_timer_t* handle) {\n    luaw_timer_t* timer = GET_TIMER_OR_RETURN(handle);\n    if(timer->lua_tid) {\n        lua_rawgeti(l_global, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n        lua_pushinteger(l_global, timer->lua_tid);\n        clear_user_timer(timer);\n        lua_pushboolean(l_global, 1);   //status\n        lua_pushboolean(l_global, 1);    //elapsed\n        resume_lua_thread(l_global, 3, 2, 0);\n    } else {\n        timer->state = ELAPSED;\n    }\n}\n\n/* lua call spec:\nSuccess:  status(true), nil = timer:start(timeout)\nFailure:  status(false), error message = timer:start(timeout)\n*/\nstatic int start_user_timer(lua_State* l_thread) {\n    LUA_GET_TIMER_OR_ERROR(l_thread, 1, timer);\n\n    if (timer->state != INIT) {\n        /* Timer is already started by another lua thread */\n        return error_to_lua(l_thread, \"Timer already in use by another thread\");\n    }\n\n    int timeout = lua_tointeger(l_thread, 2);\n    if (timeout <= 0) {\n        return error_to_lua(l_thread, \"Invalid timeout value %d specified\", timeout);\n    }\n\n    int rc = uv_timer_start(&timer->handle, on_user_timer_timeout, timeout, 0);\n    if (rc) {\n        close_timer(timer);\n        return error_to_lua(l_thread, \"Error starting timer: %s\", uv_strerror(rc));\n    }\n\n    timer->state = TICKING;\n    lua_pushboolean(l_thread, 1);\n    return 1;\n}\n\n\n/* lua call spec: status, timer_elapsed = timer:wait(tid)\nSuccess, timer elapsed:  status = true, timer_elapsed = true\nSuccess, timer not elapsed:  status = true, timer_elapsed = false\nFailure:  status = false, error message\n*/\nLUA_OBJ_METHOD static int wait_user_timer(lua_State* l_thread) {\n    LUA_GET_TIMER_OR_ERROR(l_thread, 1, timer);\n\n    if (timer->state == ELAPSED) {\n        clear_user_timer(timer);\n        lua_pushboolean(l_thread, 1);   //status\n        lua_pushboolean(l_thread, 1);   //elasped\n    } else {\n        if (timer->state != TICKING) {\n            return error_to_lua(l_thread, \"Attempt to wait on timer that is not started, timer state: %d\", timer->state);\n        }\n        if (timer->lua_tid) {\n            return error_to_lua(l_thread, \"Timer already is in use by thread %d\", timer->lua_tid);\n        }\n        int tid = lua_tointeger(l_thread, 2);\n        if (tid <= 0) {\n            return error_to_lua(l_thread, \"Invalid thread id %d specified\", tid);\n        }\n        timer->lua_tid = tid;\n        lua_pushboolean(l_thread, 1);\n        lua_pushboolean(l_thread, 0);\n    }\n    return 2;\n}\n\n/* lua call spec: timer:stop() */\nLUA_OBJ_METHOD static int stop_user_timer(lua_State* l_thread) {\n    LUA_GET_TIMER_OR_ERROR(l_thread, 1, timer);\n    if (timer->state == TICKING) {\n        if (timer->lua_tid) {\n            lua_rawgeti(l_thread, LUA_REGISTRYINDEX, resume_thread_fn_ref);\n            lua_pushinteger(l_thread, timer->lua_tid);\n            lua_pushboolean(l_thread, 0);                           //status\n            lua_pushstring(l_thread, uv_strerror(UV_ECANCELED));    //error message\n            resume_lua_thread(l_thread, 3, 2, 0);\n        }\n        clear_user_timer(timer);\n        uv_timer_stop(&timer->handle);\n    }\n    return 0;\n}\n\n\nstatic const struct luaL_Reg luaw_user_timer_methods[] = {\n\t{\"start\", start_user_timer},\n\t{\"stop\", stop_user_timer},\n\t{\"wait\", wait_user_timer},\n\t{\"delete\", close_timer_lua},\n\t{\"__gc\", timer_gc},\n\t{NULL, NULL}  /* sentinel */\n};\n\nstatic const struct luaL_Reg luaw_timer_lib[] = {\n\t{\"newTimer\", new_user_timer},\n    {NULL, NULL}  /* sentinel */\n};\n\n\nvoid luaw_init_timer_lib (lua_State *L) {\n\tmake_metatable(L, LUA_USER_TIMER_META_TABLE, luaw_user_timer_methods);\n    luaL_newlib(L, luaw_timer_lib);\n    lua_setglobal(L, \"luaw_timer_lib\");\n}\n"
  },
  {
    "path": "src/luaw_timer.h",
    "content": "/*\n* Copyright (c) 2015 raksoras\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the \"Software\"), to deal\n* in the Software without restriction, including without limitation the rights\n* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n* copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in all\n* copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n* SOFTWARE.\n*/\n\n#ifndef LUAW_TIMER_H\n\n#define LUAW_TIMER_H\n\n#define LUA_USER_TIMER_META_TABLE \"_luaw_user_timer_MT_\"\n\ntypedef enum {\n\tINIT = 0,\n\tTICKING,\n\tELAPSED\n}\ntimer_state;\n\ntypedef struct luaw_timer_s luaw_timer_t;\n\nstruct luaw_timer_s {\n    uv_timer_t handle;              /* timer handler */\n    timer_state state;              /* timer state */\n    int lua_tid;                    /* id of a lua thread waiting on this timer */\n\n    /* memory management */\n    int ref_count;                  /* reference count */\n    luaw_timer_t** lua_ref;         /* back reference to Lua's full userdata pointing to this conn */\n};\n\n\n#define TO_TIMER(h) (luaw_timer_t*)h->data\n\n#define GET_TIMER_OR_RETURN(h)  \\\n    (luaw_timer_t*)h->data;     \\\n    if(!h->data) return\n\n#define LUA_GET_TIMER_OR_RETURN(L, i, t)                                    \\\n    luaw_timer_t** tr = luaL_checkudata(L, i, LUA_USER_TIMER_META_TABLE);   \\\n    if (tr == NULL) return 0;                                               \\\n    luaw_timer_t* t = *tr;                                                  \\\n    if (t == NULL) return 0;\n\n#define LUA_GET_TIMER_OR_ERROR(L, i, t)                                     \\\n    luaw_timer_t** tr = luaL_checkudata(L, i, LUA_USER_TIMER_META_TABLE);   \\\n    if (tr == NULL) return error_to_lua(L, \"Timer missing\");                \\\n    luaw_timer_t* t = *tr;                                                  \\\n    if (t == NULL) return error_to_lua(L, \"Timer closed\");\n\n\nextern void luaw_init_timer_lib(lua_State *L);\n\n#endif"
  }
]