[
  {
    "path": ".github/workflows/build.yml",
    "content": "name: build\n\non:\n  push:\n\njobs:\n  build:\n    runs-on: ${{ matrix.builder }}\n    strategy:\n      matrix:\n        builder: [macos-latest, ubuntu-latest]\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 0\n      - name: build\n        run: make\n"
  },
  {
    "path": ".gitignore",
    "content": "obj/\nwrk\n"
  },
  {
    "path": "CHANGES",
    "content": "master\n\n * Require OpenSSL 1.1.0+\n\nwrk 4.0.2\n\n * Send hostname using TLS SNI.\n * Add optional WITH_OPENSSL and WITH_LUAJIT to use system libs.\n * Bundle OpenSSL 1.0.2.\n * delay() can return milliseconds to delay sending next request.\n\nwrk 4.0.0\n\n * The wrk global variable is the only global defined by default.\n * wrk.init() calls the global init(), remove calls to wrk.init().\n * Add wrk.lookup(host, port) and wrk.connect(addr) functions.\n * Add setup phase that calls the global setup() for each thread.\n * Allow assignment to thread.addr to specify the server address.\n * Add thread:set(name, value), thread:get(name), and thread:stop().\n * Record latency for every request instead of random samples.\n * Latency and requests in done() are now callable, not indexable.\n * Only record timeouts when a response is actually received.\n * Remove calibration phase and record rate at fixed interval.\n * Improve correction of coordinated omission.\n"
  },
  {
    "path": "INSTALL",
    "content": "Overview\n\n  wrk should build on most UNIX-like operating systems and\n  architectures that have GNU make and are supported by LuaJIT and\n  OpenSSL. Some systems may require additional CFLAGS or LDFLAGS,\n  see the top of the Makefile for examples\n\n  In many cases simply running `make` (often `gmake` on *BSD) will\n  do the trick.\n\nDependencies\n\n  wrk requires LuaJIT and OpenSSL and is distributed with appropriate\n  versions that will be unpacked and built as necessary.\n\n  If you are building wrk packages for an OS distribution or otherwise\n  prefer to use system versions of dependencies you may specify their\n  location when invoking make with one or more of:\n\n    WITH_LUAJIT\n    WITH_OPENSSL\n\n  For example to use the system version of both libraries on Linux:\n\n    make WITH_LUAJIT=/usr WITH_OPENSSL=/usr\n\n  Or to use the Homebrew version of OpenSSL on Mac OS X:\n\n    make WITH_OPENSSL=/usr/local/opt/openssl\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                          Modified Apache 2.0 License\n                         Version 2.0.1, February 2015\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      (e) If the Derivative Work includes substantial changes to features\n          or functionality of the Work, then you must remove the name of\n          the Work, and any derivation thereof, from all copies that you\n          distribute, whether in Source or Object form, except as required\n          in copyright, patent, trademark, and attribution notices.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "Makefile",
    "content": "CFLAGS  += -std=c99 -Wall -O2 -D_REENTRANT\nLIBS    := -lm -lssl -lcrypto -lpthread\n\nTARGET  := $(shell uname -s | tr '[A-Z]' '[a-z]' 2>/dev/null || echo unknown)\n\nifeq ($(TARGET), sunos)\n\tCFLAGS += -D_PTHREADS -D_POSIX_C_SOURCE=200112L\n\tLIBS   += -lsocket\nelse ifeq ($(TARGET), darwin)\n\texport MACOSX_DEPLOYMENT_TARGET = $(shell sw_vers -productVersion)\nelse ifeq ($(TARGET), linux)\n\tCFLAGS  += -D_POSIX_C_SOURCE=200112L -D_BSD_SOURCE -D_DEFAULT_SOURCE\n\tLIBS    += -ldl\n\tLDFLAGS += -Wl,-E\nelse ifeq ($(TARGET), freebsd)\n\tCFLAGS  += -D_DECLARE_C99_LDBL_MATH\n\tLDFLAGS += -Wl,-E\nendif\n\nSRC  := wrk.c net.c ssl.c aprintf.c stats.c script.c units.c \\\n\t\tae.c zmalloc.c http_parser.c\nBIN  := wrk\nVER  ?= $(shell git describe --tags --always --dirty)\n\nODIR := obj\nOBJ  := $(patsubst %.c,$(ODIR)/%.o,$(SRC)) $(ODIR)/bytecode.o $(ODIR)/version.o\nLIBS := -lluajit-5.1 $(LIBS)\n\nDEPS    :=\nCFLAGS  += -I$(ODIR)/include\nLDFLAGS += -L$(ODIR)/lib\n\nifneq ($(WITH_LUAJIT),)\n\tCFLAGS  += -I$(WITH_LUAJIT)/include\n\tLDFLAGS += -L$(WITH_LUAJIT)/lib\nelse\n\tCFLAGS  += -I$(ODIR)/include/luajit-2.1\n\tDEPS    += $(ODIR)/lib/libluajit-5.1.a\nendif\n\nifneq ($(WITH_OPENSSL),)\n\tCFLAGS  += -I$(WITH_OPENSSL)/include\n\tLDFLAGS += -L$(WITH_OPENSSL)/lib\nelse\n\tDEPS += $(ODIR)/lib/libssl.a\nendif\n\nall: $(BIN)\n\nclean:\n\t$(RM) -rf $(BIN) obj/*\n\n$(BIN): $(OBJ)\n\t@echo LINK $(BIN)\n\t@$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)\n\n$(OBJ): config.h Makefile $(DEPS) | $(ODIR)\n\n$(ODIR):\n\t@mkdir -p $@\n\n$(ODIR)/bytecode.c: src/wrk.lua $(DEPS)\n\t@echo LUAJIT $<\n\t@$(SHELL) -c 'PATH=\"obj/bin:$(PATH)\" luajit -b \"$(CURDIR)/$<\" \"$(CURDIR)/$@\"'\n\n$(ODIR)/version.o:\n\t@echo 'const char *VERSION=\"$(VER)\";' | $(CC) -xc -c -o $@ -\n\n$(ODIR)/%.o : %.c\n\t@echo CC $<\n\t@$(CC) $(CFLAGS) -c -o $@ $<\n\n# Dependencies\n\nLUAJIT  := $(notdir $(patsubst %.zip,%,$(wildcard deps/LuaJIT*.zip)))\nOPENSSL := $(notdir $(patsubst %.tar.gz,%,$(wildcard deps/openssl*.tar.gz)))\n\nOPENSSL_OPTS = no-shared no-psk no-srp no-dtls no-idea --prefix=$(abspath $(ODIR))\n\n$(ODIR)/$(LUAJIT): deps/$(LUAJIT).zip | $(ODIR)\n\techo $(LUAJIT)\n\t@unzip -nd $(ODIR) $<\n\n$(ODIR)/$(OPENSSL): deps/$(OPENSSL).tar.gz | $(ODIR)\n\t@tar -C $(ODIR) -xf $<\n\n$(ODIR)/lib/libluajit-5.1.a: $(ODIR)/$(LUAJIT)\n\t@echo Building LuaJIT...\n\t@$(MAKE) -C $< PREFIX=$(abspath $(ODIR)) BUILDMODE=static install\n\t@cd $(ODIR)/bin && ln -s luajit-2.1.0-beta3 luajit\n\n$(ODIR)/lib/libssl.a: $(ODIR)/$(OPENSSL)\n\t@echo Building OpenSSL...\n\t@$(SHELL) -c \"cd $< && ./config $(OPENSSL_OPTS)\"\n\t@$(MAKE) -C $< depend\n\t@$(MAKE) -C $<\n\t@$(MAKE) -C $< install_sw\n\t@touch $@\n\n# ------------\n\n.PHONY: all clean\n.PHONY: $(ODIR)/version.o\n\n.SUFFIXES:\n.SUFFIXES: .c .o .lua\n\nvpath %.c   src\nvpath %.h   src\nvpath %.lua scripts\n"
  },
  {
    "path": "NOTICE",
    "content": "=========================================================================\n==  NOTICE file corresponding to section 4(d) of the Apache License,   ==\n==  Version 2.0, in this case for the wrk distribution.                ==\n=========================================================================\n\nwrk\nCopyright 2012 Will Glozer, http://glozer.net\n\n=========================================================================\n==  Redis Event Library Notice                                         ==\n=========================================================================\n\nThis product includes software developed by Salvatore Sanfilippo and\nother contributors to the redis project.\n\nCopyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>\nCopyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\nCopyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com\nCopyright (c) 2012, Joyent, Inc. All rights reserved.\n\nCopyright (c) 2006-2009, Salvatore Sanfilippo\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the *\n      distribution.\n\n    * Neither the name of Redis nor the names of its contributors may be\n      used to endorse or promote products derived from this software\n      without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n=========================================================================\n==  HTTP Parser Notice                                                 ==\n=========================================================================\n\nThis product includes software developed by Igor Sysoev, Joyent, Inc.,\nand other Node contributors.\n\nhttp_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright\nIgor Sysoev.\n\nAdditional changes are licensed under the same terms as NGINX and\ncopyright Joyent, Inc. and other Node contributors. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n\n=========================================================================\n==  LuaJIT Notice                                                      ==\n=========================================================================\n\nLuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/\n\nCopyright (C) 2005-2013 Mike Pall. All rights reserved.\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\nall copies 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\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# wrk - a HTTP benchmarking tool\n\n  wrk is a modern HTTP benchmarking tool capable of generating significant\n  load when run on a single multi-core CPU. It combines a multithreaded\n  design with scalable event notification systems such as epoll and kqueue.\n\n  An optional LuaJIT script can perform HTTP request generation, response\n  processing, and custom reporting. Details are available in SCRIPTING and\n  several examples are located in [scripts/](scripts/).\n\n## Basic Usage\n\n    wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html\n\n  This runs a benchmark for 30 seconds, using 12 threads, and keeping\n  400 HTTP connections open.\n\n  Output:\n\n    Running 30s test @ http://127.0.0.1:8080/index.html\n      12 threads and 400 connections\n      Thread Stats   Avg      Stdev     Max   +/- Stdev\n        Latency   635.91us    0.89ms  12.92ms   93.69%\n        Req/Sec    56.20k     8.07k   62.00k    86.54%\n      22464657 requests in 30.00s, 17.76GB read\n    Requests/sec: 748868.53\n    Transfer/sec:    606.33MB\n\n## Command Line Options\n\n    -c, --connections: total number of HTTP connections to keep open with\n                       each thread handling N = connections/threads\n\n    -d, --duration:    duration of the test, e.g. 2s, 2m, 2h\n\n    -t, --threads:     total number of threads to use\n\n    -s, --script:      LuaJIT script, see SCRIPTING\n\n    -H, --header:      HTTP header to add to request, e.g. \"User-Agent: wrk\"\n\n        --latency:     print detailed latency statistics\n\n        --timeout:     record a timeout if a response is not received within\n                       this amount of time.\n\n## Benchmarking Tips\n\n  The machine running wrk must have a sufficient number of ephemeral ports\n  available and closed sockets should be recycled quickly. To handle the\n  initial connection burst the server's listen(2) backlog should be greater\n  than the number of concurrent connections being tested.\n\n  A user script that only changes the HTTP method, path, adds headers or\n  a body, will have no performance impact. Per-request actions, particularly\n  building a new HTTP request, and use of response() will necessarily reduce\n  the amount of load that can be generated.\n\n## Acknowledgements\n\n  wrk contains code from a number of open source projects including the\n  'ae' event loop from redis, the nginx/joyent/node.js 'http-parser',\n  and Mike Pall's LuaJIT. Please consult the NOTICE file for licensing\n  details.\n\n## Cryptography Notice\n\n  This distribution includes cryptographic software. The country in\n  which you currently reside may have restrictions on the import,\n  possession, use, and/or re-export to another country, of encryption\n  software. BEFORE using any encryption software, please check your\n  country's laws, regulations and policies concerning the import,\n  possession, or use, and re-export of encryption software, to see if\n  this is permitted. See <http://www.wassenaar.org/> for more\n  information.\n\n  The U.S. Government Department of Commerce, Bureau of Industry and\n  Security (BIS), has classified this software as Export Commodity\n  Control Number (ECCN) 5D002.C.1, which includes information security\n  software using or performing cryptographic functions with symmetric\n  algorithms. The form and manner of this distribution makes it\n  eligible for export under the License Exception ENC Technology\n  Software Unrestricted (TSU) exception (see the BIS Export\n  Administration Regulations, Section 740.13) for both object code and\n  source code.\n"
  },
  {
    "path": "SCRIPTING",
    "content": "Overview\n\n  wrk supports executing a LuaJIT script during three distinct phases: setup,\n  running, and done. Each wrk thread has an independent scripting environment\n  and the setup & done phases execute in a separate environment which does\n  not participate in the running phase.\n\n  The public Lua API consists of a global table and a number of global\n  functions:\n\n  wrk = {\n    scheme  = \"http\",\n    host    = \"localhost\",\n    port    = nil,\n    method  = \"GET\",\n    path    = \"/\",\n    headers = {},\n    body    = nil,\n    thread  = <userdata>,\n  }\n\n  function wrk.format(method, path, headers, body)\n\n    wrk.format returns a HTTP request string containing the passed parameters\n    merged with values from the wrk table.\n\n  function wrk.lookup(host, service)\n\n    wrk.lookup returns a table containing all known addresses for the host\n    and service pair. This corresponds to the POSIX getaddrinfo() function.\n\n  function wrk.connect(addr)\n\n    wrk.connect returns true if the address can be connected to, otherwise\n    it returns false. The address must be one returned from wrk.lookup().\n\n  The following globals are optional, and if defined must be functions:\n\n    global setup    -- called during thread setup\n    global init     -- called when the thread is starting\n    global delay    -- called to get the request delay\n    global request  -- called to generate the HTTP request\n    global response -- called with HTTP response data\n    global done     -- called with results of run\n\nSetup\n\n  function setup(thread)\n\n  The setup phase begins after the target IP address has been resolved and all\n  threads have been initialized but not yet started.\n\n  setup() is called once for each thread and receives a userdata object\n  representing the thread.\n\n    thread.addr             - get or set the thread's server address\n    thread:get(name)        - get the value of a global in the thread's env\n    thread:set(name, value) - set the value of a global in the thread's env\n    thread:stop()           - stop the thread\n\n  Only boolean, nil, number, and string values or tables of the same may be\n  transfered via get()/set() and thread:stop() can only be called while the\n  thread is running.\n\nRunning\n\n  function init(args)\n  function delay()\n  function request()\n  function response(status, headers, body)\n\n  The running phase begins with a single call to init(), followed by\n  a call to request() and response() for each request cycle.\n\n  The init() function receives any extra command line arguments for the\n  script which must be separated from wrk arguments with \"--\".\n\n  delay() returns the number of milliseconds to delay sending the next\n  request.\n\n  request() returns a string containing the HTTP request. Building a new\n  request each time is expensive, when testing a high performance server\n  one solution is to pre-generate all requests in init() and do a quick\n  lookup in request().\n\n  response() is called with the HTTP response status, headers, and body.\n  Parsing the headers and body is expensive, so if the response global is\n  nil after the call to init() wrk will ignore the headers and body.\n\nDone\n\n  function done(summary, latency, requests)\n\n  The done() function receives a table containing result data, and two\n  statistics objects representing the per-request latency and per-thread\n  request rate. Duration and latency are microsecond values and rate is\n  measured in requests per second.\n\n  latency.min              -- minimum value seen\n  latency.max              -- maximum value seen\n  latency.mean             -- average value seen\n  latency.stdev            -- standard deviation\n  latency:percentile(99.0) -- 99th percentile value\n  latency(i)               -- raw value and count\n\n  summary = {\n    duration = N,  -- run duration in microseconds\n    requests = N,  -- total completed requests\n    bytes    = N,  -- total bytes received\n    errors   = {\n      connect = N, -- total socket connection errors\n      read    = N, -- total socket read errors\n      write   = N, -- total socket write errors\n      status  = N, -- total HTTP status codes > 399\n      timeout = N  -- total request timeouts\n    }\n  }\n"
  },
  {
    "path": "scripts/addr.lua",
    "content": "-- example script that demonstrates use of setup() to pass\n-- a random server address to each thread\n\nlocal addrs = nil\n\nfunction setup(thread)\n   if not addrs then\n      addrs = wrk.lookup(wrk.host, wrk.port or \"http\")\n      for i = #addrs, 1, -1 do\n         if not wrk.connect(addrs[i]) then\n            table.remove(addrs, i)\n         end\n      end\n   end\n\n   thread.addr = addrs[math.random(#addrs)]\nend\n\nfunction init(args)\n   local msg = \"thread addr: %s\"\n   print(msg:format(wrk.thread.addr))\nend\n"
  },
  {
    "path": "scripts/auth.lua",
    "content": "-- example script that demonstrates response handling and\n-- retrieving an authentication token to set on all future\n-- requests\n\ntoken = nil\npath  = \"/authenticate\"\n\nrequest = function()\n   return wrk.format(\"GET\", path)\nend\n\nresponse = function(status, headers, body)\n   if not token and status == 200 then\n      token = headers[\"X-Token\"]\n      path  = \"/resource\"\n      wrk.headers[\"X-Token\"] = token\n   end\nend\n"
  },
  {
    "path": "scripts/counter.lua",
    "content": "-- example dynamic request script which demonstrates changing\n-- the request path and a header for each request\n-------------------------------------------------------------\n-- NOTE: each wrk thread has an independent Lua scripting\n-- context and thus there will be one counter per thread\n\ncounter = 0\n\nrequest = function()\n   path = \"/\" .. counter\n   wrk.headers[\"X-Counter\"] = counter\n   counter = counter + 1\n   return wrk.format(nil, path)\nend\n"
  },
  {
    "path": "scripts/delay.lua",
    "content": "-- example script that demonstrates adding a random\n-- 10-50ms delay before each request\n\nfunction delay()\n   return math.random(10, 50)\nend\n"
  },
  {
    "path": "scripts/pipeline.lua",
    "content": "-- example script demonstrating HTTP pipelining\n\ninit = function(args)\n   local r = {}\n   r[1] = wrk.format(nil, \"/?foo\")\n   r[2] = wrk.format(nil, \"/?bar\")\n   r[3] = wrk.format(nil, \"/?baz\")\n\n   req = table.concat(r)\nend\n\nrequest = function()\n   return req\nend\n"
  },
  {
    "path": "scripts/post.lua",
    "content": "-- example HTTP POST script which demonstrates setting the\n-- HTTP method, body, and adding a header\n\nwrk.method = \"POST\"\nwrk.body   = \"foo=bar&baz=quux\"\nwrk.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\"\n"
  },
  {
    "path": "scripts/report.lua",
    "content": "-- example reporting script which demonstrates a custom\n-- done() function that prints latency percentiles as CSV\n\ndone = function(summary, latency, requests)\n   io.write(\"------------------------------\\n\")\n   for _, p in pairs({ 50, 90, 99, 99.999 }) do\n      n = latency:percentile(p)\n      io.write(string.format(\"%g%%,%d\\n\", p, n))\n   end\nend\n"
  },
  {
    "path": "scripts/setup.lua",
    "content": "-- example script that demonstrates use of setup() to pass\n-- data to and from the threads\n\nlocal counter = 1\nlocal threads = {}\n\nfunction setup(thread)\n   thread:set(\"id\", counter)\n   table.insert(threads, thread)\n   counter = counter + 1\nend\n\nfunction init(args)\n   requests  = 0\n   responses = 0\n\n   local msg = \"thread %d created\"\n   print(msg:format(id))\nend\n\nfunction request()\n   requests = requests + 1\n   return wrk.request()\nend\n\nfunction response(status, headers, body)\n   responses = responses + 1\nend\n\nfunction done(summary, latency, requests)\n   for index, thread in ipairs(threads) do\n      local id        = thread:get(\"id\")\n      local requests  = thread:get(\"requests\")\n      local responses = thread:get(\"responses\")\n      local msg = \"thread %d made %d requests and got %d responses\"\n      print(msg:format(id, requests, responses))\n   end\nend\n"
  },
  {
    "path": "scripts/stop.lua",
    "content": "-- example script that demonstrates use of thread:stop()\n\nlocal counter = 1\n\nfunction response()\n   if counter == 100 then\n      wrk.thread:stop()\n   end\n   counter = counter + 1\nend\n"
  },
  {
    "path": "src/ae.c",
    "content": "/* A simple event-driven programming library. Originally I wrote this code\n * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated\n * it in form of a library for easy reuse.\n *\n * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <poll.h>\n#include <string.h>\n#include <time.h>\n#include <errno.h>\n\n#include \"ae.h\"\n#include \"zmalloc.h\"\n#include \"config.h\"\n\n/* Include the best multiplexing layer supported by this system.\n * The following should be ordered by performances, descending. */\n#ifdef HAVE_EVPORT\n#include \"ae_evport.c\"\n#else\n    #ifdef HAVE_EPOLL\n    #include \"ae_epoll.c\"\n    #else\n        #ifdef HAVE_KQUEUE\n        #include \"ae_kqueue.c\"\n        #else\n        #include \"ae_select.c\"\n        #endif\n    #endif\n#endif\n\naeEventLoop *aeCreateEventLoop(int setsize) {\n    aeEventLoop *eventLoop;\n    int i;\n\n    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;\n    eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);\n    eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);\n    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;\n    eventLoop->setsize = setsize;\n    eventLoop->lastTime = time(NULL);\n    eventLoop->timeEventHead = NULL;\n    eventLoop->timeEventNextId = 0;\n    eventLoop->stop = 0;\n    eventLoop->maxfd = -1;\n    eventLoop->beforesleep = NULL;\n    if (aeApiCreate(eventLoop) == -1) goto err;\n    /* Events with mask == AE_NONE are not set. So let's initialize the\n     * vector with it. */\n    for (i = 0; i < setsize; i++)\n        eventLoop->events[i].mask = AE_NONE;\n    return eventLoop;\n\nerr:\n    if (eventLoop) {\n        zfree(eventLoop->events);\n        zfree(eventLoop->fired);\n        zfree(eventLoop);\n    }\n    return NULL;\n}\n\n/* Return the current set size. */\nint aeGetSetSize(aeEventLoop *eventLoop) {\n    return eventLoop->setsize;\n}\n\n/* Resize the maximum set size of the event loop.\n * If the requested set size is smaller than the current set size, but\n * there is already a file descriptor in use that is >= the requested\n * set size minus one, AE_ERR is returned and the operation is not\n * performed at all.\n *\n * Otherwise AE_OK is returned and the operation is successful. */\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {\n    int i;\n\n    if (setsize == eventLoop->setsize) return AE_OK;\n    if (eventLoop->maxfd >= setsize) return AE_ERR;\n    if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;\n\n    eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);\n    eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);\n    eventLoop->setsize = setsize;\n\n    /* Make sure that if we created new slots, they are initialized with\n     * an AE_NONE mask. */\n    for (i = eventLoop->maxfd+1; i < setsize; i++)\n        eventLoop->events[i].mask = AE_NONE;\n    return AE_OK;\n}\n\nvoid aeDeleteEventLoop(aeEventLoop *eventLoop) {\n    aeApiFree(eventLoop);\n    zfree(eventLoop->events);\n    zfree(eventLoop->fired);\n    zfree(eventLoop);\n}\n\nvoid aeStop(aeEventLoop *eventLoop) {\n    eventLoop->stop = 1;\n}\n\nint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,\n        aeFileProc *proc, void *clientData)\n{\n    if (fd >= eventLoop->setsize) {\n        errno = ERANGE;\n        return AE_ERR;\n    }\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    if (aeApiAddEvent(eventLoop, fd, mask) == -1)\n        return AE_ERR;\n    fe->mask |= mask;\n    if (mask & AE_READABLE) fe->rfileProc = proc;\n    if (mask & AE_WRITABLE) fe->wfileProc = proc;\n    fe->clientData = clientData;\n    if (fd > eventLoop->maxfd)\n        eventLoop->maxfd = fd;\n    return AE_OK;\n}\n\nvoid aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)\n{\n    if (fd >= eventLoop->setsize) return;\n    aeFileEvent *fe = &eventLoop->events[fd];\n    if (fe->mask == AE_NONE) return;\n\n    aeApiDelEvent(eventLoop, fd, mask);\n    fe->mask = fe->mask & (~mask);\n    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {\n        /* Update the max fd */\n        int j;\n\n        for (j = eventLoop->maxfd-1; j >= 0; j--)\n            if (eventLoop->events[j].mask != AE_NONE) break;\n        eventLoop->maxfd = j;\n    }\n}\n\nint aeGetFileEvents(aeEventLoop *eventLoop, int fd) {\n    if (fd >= eventLoop->setsize) return 0;\n    aeFileEvent *fe = &eventLoop->events[fd];\n\n    return fe->mask;\n}\n\nstatic void aeGetTime(long *seconds, long *milliseconds)\n{\n    struct timeval tv;\n\n    gettimeofday(&tv, NULL);\n    *seconds = tv.tv_sec;\n    *milliseconds = tv.tv_usec/1000;\n}\n\nstatic void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {\n    long cur_sec, cur_ms, when_sec, when_ms;\n\n    aeGetTime(&cur_sec, &cur_ms);\n    when_sec = cur_sec + milliseconds/1000;\n    when_ms = cur_ms + milliseconds%1000;\n    if (when_ms >= 1000) {\n        when_sec ++;\n        when_ms -= 1000;\n    }\n    *sec = when_sec;\n    *ms = when_ms;\n}\n\nlong long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,\n        aeTimeProc *proc, void *clientData,\n        aeEventFinalizerProc *finalizerProc)\n{\n    long long id = eventLoop->timeEventNextId++;\n    aeTimeEvent *te;\n\n    te = zmalloc(sizeof(*te));\n    if (te == NULL) return AE_ERR;\n    te->id = id;\n    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);\n    te->timeProc = proc;\n    te->finalizerProc = finalizerProc;\n    te->clientData = clientData;\n    te->next = eventLoop->timeEventHead;\n    eventLoop->timeEventHead = te;\n    return id;\n}\n\nint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    while(te) {\n        if (te->id == id) {\n            te->id = AE_DELETED_EVENT_ID;\n            return AE_OK;\n        }\n        te = te->next;\n    }\n    return AE_ERR; /* NO event with the specified ID found */\n}\n\n/* Search the first timer to fire.\n * This operation is useful to know how many time the select can be\n * put in sleep without to delay any event.\n * If there are no timers NULL is returned.\n *\n * Note that's O(N) since time events are unsorted.\n * Possible optimizations (not needed by Redis so far, but...):\n * 1) Insert the event in order, so that the nearest is just the head.\n *    Much better but still insertion or deletion of timers is O(N).\n * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).\n */\nstatic aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)\n{\n    aeTimeEvent *te = eventLoop->timeEventHead;\n    aeTimeEvent *nearest = NULL;\n\n    while(te) {\n        if (!nearest || te->when_sec < nearest->when_sec ||\n                (te->when_sec == nearest->when_sec &&\n                 te->when_ms < nearest->when_ms))\n            nearest = te;\n        te = te->next;\n    }\n    return nearest;\n}\n\n/* Process time events */\nstatic int processTimeEvents(aeEventLoop *eventLoop) {\n    int processed = 0;\n    aeTimeEvent *te, *prev;\n    long long maxId;\n    time_t now = time(NULL);\n\n    /* If the system clock is moved to the future, and then set back to the\n     * right value, time events may be delayed in a random way. Often this\n     * means that scheduled operations will not be performed soon enough.\n     *\n     * Here we try to detect system clock skews, and force all the time\n     * events to be processed ASAP when this happens: the idea is that\n     * processing events earlier is less dangerous than delaying them\n     * indefinitely, and practice suggests it is. */\n    if (now < eventLoop->lastTime) {\n        te = eventLoop->timeEventHead;\n        while(te) {\n            te->when_sec = 0;\n            te = te->next;\n        }\n    }\n    eventLoop->lastTime = now;\n\n    prev = NULL;\n    te = eventLoop->timeEventHead;\n    maxId = eventLoop->timeEventNextId-1;\n    while(te) {\n        long now_sec, now_ms;\n        long long id;\n\n        /* Remove events scheduled for deletion. */\n        if (te->id == AE_DELETED_EVENT_ID) {\n            aeTimeEvent *next = te->next;\n            if (prev == NULL)\n                eventLoop->timeEventHead = te->next;\n            else\n                prev->next = te->next;\n            if (te->finalizerProc)\n                te->finalizerProc(eventLoop, te->clientData);\n            zfree(te);\n            te = next;\n            continue;\n        }\n\n        /* Make sure we don't process time events created by time events in\n         * this iteration. Note that this check is currently useless: we always\n         * add new timers on the head, however if we change the implementation\n         * detail, this check may be useful again: we keep it here for future\n         * defense. */\n        if (te->id > maxId) {\n            te = te->next;\n            continue;\n        }\n        aeGetTime(&now_sec, &now_ms);\n        if (now_sec > te->when_sec ||\n            (now_sec == te->when_sec && now_ms >= te->when_ms))\n        {\n            int retval;\n\n            id = te->id;\n            retval = te->timeProc(eventLoop, id, te->clientData);\n            processed++;\n            if (retval != AE_NOMORE) {\n                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);\n            } else {\n                te->id = AE_DELETED_EVENT_ID;\n            }\n        }\n        prev = te;\n        te = te->next;\n    }\n    return processed;\n}\n\n/* Process every pending time event, then every pending file event\n * (that may be registered by time event callbacks just processed).\n * Without special flags the function sleeps until some file event\n * fires, or when the next time event occurs (if any).\n *\n * If flags is 0, the function does nothing and returns.\n * if flags has AE_ALL_EVENTS set, all the kind of events are processed.\n * if flags has AE_FILE_EVENTS set, file events are processed.\n * if flags has AE_TIME_EVENTS set, time events are processed.\n * if flags has AE_DONT_WAIT set the function returns ASAP until all\n * the events that's possible to process without to wait are processed.\n *\n * The function returns the number of events processed. */\nint aeProcessEvents(aeEventLoop *eventLoop, int flags)\n{\n    int processed = 0, numevents;\n\n    /* Nothing to do? return ASAP */\n    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;\n\n    /* Note that we want call select() even if there are no\n     * file events to process as long as we want to process time\n     * events, in order to sleep until the next time event is ready\n     * to fire. */\n    if (eventLoop->maxfd != -1 ||\n        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {\n        int j;\n        aeTimeEvent *shortest = NULL;\n        struct timeval tv, *tvp;\n\n        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))\n            shortest = aeSearchNearestTimer(eventLoop);\n        if (shortest) {\n            long now_sec, now_ms;\n\n            aeGetTime(&now_sec, &now_ms);\n            tvp = &tv;\n\n            /* How many milliseconds we need to wait for the next\n             * time event to fire? */\n            long long ms =\n                (shortest->when_sec - now_sec)*1000 +\n                shortest->when_ms - now_ms;\n\n            if (ms > 0) {\n                tvp->tv_sec = ms/1000;\n                tvp->tv_usec = (ms % 1000)*1000;\n            } else {\n                tvp->tv_sec = 0;\n                tvp->tv_usec = 0;\n            }\n        } else {\n            /* If we have to check for events but need to return\n             * ASAP because of AE_DONT_WAIT we need to set the timeout\n             * to zero */\n            if (flags & AE_DONT_WAIT) {\n                tv.tv_sec = tv.tv_usec = 0;\n                tvp = &tv;\n            } else {\n                /* Otherwise we can block */\n                tvp = NULL; /* wait forever */\n            }\n        }\n\n        numevents = aeApiPoll(eventLoop, tvp);\n        for (j = 0; j < numevents; j++) {\n            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];\n            int mask = eventLoop->fired[j].mask;\n            int fd = eventLoop->fired[j].fd;\n            int rfired = 0;\n\n\t    /* note the fe->mask & mask & ... code: maybe an already processed\n             * event removed an element that fired and we still didn't\n             * processed, so we check if the event is still valid. */\n            if (fe->mask & mask & AE_READABLE) {\n                rfired = 1;\n                fe->rfileProc(eventLoop,fd,fe->clientData,mask);\n            }\n            if (fe->mask & mask & AE_WRITABLE) {\n                if (!rfired || fe->wfileProc != fe->rfileProc)\n                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);\n            }\n            processed++;\n        }\n    }\n    /* Check time events */\n    if (flags & AE_TIME_EVENTS)\n        processed += processTimeEvents(eventLoop);\n\n    return processed; /* return the number of processed file/time events */\n}\n\n/* Wait for milliseconds until the given file descriptor becomes\n * writable/readable/exception */\nint aeWait(int fd, int mask, long long milliseconds) {\n    struct pollfd pfd;\n    int retmask = 0, retval;\n\n    memset(&pfd, 0, sizeof(pfd));\n    pfd.fd = fd;\n    if (mask & AE_READABLE) pfd.events |= POLLIN;\n    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;\n\n    if ((retval = poll(&pfd, 1, milliseconds))== 1) {\n        if (pfd.revents & POLLIN) retmask |= AE_READABLE;\n        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;\n\tif (pfd.revents & POLLERR) retmask |= AE_WRITABLE;\n        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;\n        return retmask;\n    } else {\n        return retval;\n    }\n}\n\nvoid aeMain(aeEventLoop *eventLoop) {\n    eventLoop->stop = 0;\n    while (!eventLoop->stop) {\n        if (eventLoop->beforesleep != NULL)\n            eventLoop->beforesleep(eventLoop);\n        aeProcessEvents(eventLoop, AE_ALL_EVENTS);\n    }\n}\n\nchar *aeGetApiName(void) {\n    return aeApiName();\n}\n\nvoid aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {\n    eventLoop->beforesleep = beforesleep;\n}\n"
  },
  {
    "path": "src/ae.h",
    "content": "/* A simple event-driven programming library. Originally I wrote this code\n * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated\n * it in form of a library for easy reuse.\n *\n * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __AE_H__\n#define __AE_H__\n\n#include <time.h>\n\n#define AE_OK 0\n#define AE_ERR -1\n\n#define AE_NONE 0\n#define AE_READABLE 1\n#define AE_WRITABLE 2\n\n#define AE_FILE_EVENTS 1\n#define AE_TIME_EVENTS 2\n#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)\n#define AE_DONT_WAIT 4\n\n#define AE_NOMORE -1\n#define AE_DELETED_EVENT_ID -1\n\n/* Macros */\n#define AE_NOTUSED(V) ((void) V)\n\nstruct aeEventLoop;\n\n/* Types and data structures */\ntypedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);\ntypedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);\ntypedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);\ntypedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);\n\n/* File event structure */\ntypedef struct aeFileEvent {\n    int mask; /* one of AE_(READABLE|WRITABLE) */\n    aeFileProc *rfileProc;\n    aeFileProc *wfileProc;\n    void *clientData;\n} aeFileEvent;\n\n/* Time event structure */\ntypedef struct aeTimeEvent {\n    long long id; /* time event identifier. */\n    long when_sec; /* seconds */\n    long when_ms; /* milliseconds */\n    aeTimeProc *timeProc;\n    aeEventFinalizerProc *finalizerProc;\n    void *clientData;\n    struct aeTimeEvent *next;\n} aeTimeEvent;\n\n/* A fired event */\ntypedef struct aeFiredEvent {\n    int fd;\n    int mask;\n} aeFiredEvent;\n\n/* State of an event based program */\ntypedef struct aeEventLoop {\n    int maxfd;   /* highest file descriptor currently registered */\n    int setsize; /* max number of file descriptors tracked */\n    long long timeEventNextId;\n    time_t lastTime;     /* Used to detect system clock skew */\n    aeFileEvent *events; /* Registered events */\n    aeFiredEvent *fired; /* Fired events */\n    aeTimeEvent *timeEventHead;\n    int stop;\n    void *apidata; /* This is used for polling API specific data */\n    aeBeforeSleepProc *beforesleep;\n} aeEventLoop;\n\n/* Prototypes */\naeEventLoop *aeCreateEventLoop(int setsize);\nvoid aeDeleteEventLoop(aeEventLoop *eventLoop);\nvoid aeStop(aeEventLoop *eventLoop);\nint aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,\n        aeFileProc *proc, void *clientData);\nvoid aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);\nint aeGetFileEvents(aeEventLoop *eventLoop, int fd);\nlong long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,\n        aeTimeProc *proc, void *clientData,\n        aeEventFinalizerProc *finalizerProc);\nint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);\nint aeProcessEvents(aeEventLoop *eventLoop, int flags);\nint aeWait(int fd, int mask, long long milliseconds);\nvoid aeMain(aeEventLoop *eventLoop);\nchar *aeGetApiName(void);\nvoid aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);\nint aeGetSetSize(aeEventLoop *eventLoop);\nint aeResizeSetSize(aeEventLoop *eventLoop, int setsize);\n\n#endif\n"
  },
  {
    "path": "src/ae_epoll.c",
    "content": "/* Linux epoll(2) based ae.c module\n *\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <sys/epoll.h>\n\ntypedef struct aeApiState {\n    int epfd;\n    struct epoll_event *events;\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    aeApiState *state = zmalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);\n    if (!state->events) {\n        zfree(state);\n        return -1;\n    }\n    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */\n    if (state->epfd == -1) {\n        zfree(state->events);\n        zfree(state);\n        return -1;\n    }\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    aeApiState *state = eventLoop->apidata;\n\n    state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize);\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    aeApiState *state = eventLoop->apidata;\n\n    close(state->epfd);\n    zfree(state->events);\n    zfree(state);\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    /* If the fd was already monitored for some event, we need a MOD\n     * operation. Otherwise we need an ADD operation. */\n    int op = eventLoop->events[fd].mask == AE_NONE ?\n            EPOLL_CTL_ADD : EPOLL_CTL_MOD;\n\n    ee.events = 0;\n    mask |= eventLoop->events[fd].mask; /* Merge old events */\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {\n    aeApiState *state = eventLoop->apidata;\n    struct epoll_event ee = {0}; /* avoid valgrind warning */\n    int mask = eventLoop->events[fd].mask & (~delmask);\n\n    ee.events = 0;\n    if (mask & AE_READABLE) ee.events |= EPOLLIN;\n    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;\n    ee.data.fd = fd;\n    if (mask != AE_NONE) {\n        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);\n    } else {\n        /* Note, Kernel < 2.6.9 requires a non null event pointer even for\n         * EPOLL_CTL_DEL. */\n        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, numevents = 0;\n\n    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,\n            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);\n    if (retval > 0) {\n        int j;\n\n        numevents = retval;\n        for (j = 0; j < numevents; j++) {\n            int mask = 0;\n            struct epoll_event *e = state->events+j;\n\n            if (e->events & EPOLLIN) mask |= AE_READABLE;\n            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;\n            if (e->events & EPOLLERR) mask |= AE_WRITABLE;\n            if (e->events & EPOLLHUP) mask |= AE_WRITABLE;\n            eventLoop->fired[j].fd = e->data.fd;\n            eventLoop->fired[j].mask = mask;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"epoll\";\n}\n"
  },
  {
    "path": "src/ae_evport.c",
    "content": "/* ae.c module for illumos event ports.\n *\n * Copyright (c) 2012, Joyent, Inc. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <assert.h>\n#include <errno.h>\n#include <port.h>\n#include <poll.h>\n\n#include <sys/types.h>\n#include <sys/time.h>\n\n#include <stdio.h>\n\nstatic int evport_debug = 0;\n\n/*\n * This file implements the ae API using event ports, present on Solaris-based\n * systems since Solaris 10.  Using the event port interface, we associate file\n * descriptors with the port.  Each association also includes the set of poll(2)\n * events that the consumer is interested in (e.g., POLLIN and POLLOUT).\n *\n * There's one tricky piece to this implementation: when we return events via\n * aeApiPoll, the corresponding file descriptors become dissociated from the\n * port.  This is necessary because poll events are level-triggered, so if the\n * fd didn't become dissociated, it would immediately fire another event since\n * the underlying state hasn't changed yet.  We must re-associate the file\n * descriptor, but only after we know that our caller has actually read from it.\n * The ae API does not tell us exactly when that happens, but we do know that\n * it must happen by the time aeApiPoll is called again.  Our solution is to\n * keep track of the last fds returned by aeApiPoll and re-associate them next\n * time aeApiPoll is invoked.\n *\n * To summarize, in this module, each fd association is EITHER (a) represented\n * only via the in-kernel association OR (b) represented by pending_fds and\n * pending_masks.  (b) is only true for the last fds we returned from aeApiPoll,\n * and only until we enter aeApiPoll again (at which point we restore the\n * in-kernel association).\n */\n#define MAX_EVENT_BATCHSZ 512\n\ntypedef struct aeApiState {\n    int     portfd;                             /* event port */\n    int     npending;                           /* # of pending fds */\n    int     pending_fds[MAX_EVENT_BATCHSZ];     /* pending fds */\n    int     pending_masks[MAX_EVENT_BATCHSZ];   /* pending fds' masks */\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    int i;\n    aeApiState *state = zmalloc(sizeof(aeApiState));\n    if (!state) return -1;\n\n    state->portfd = port_create();\n    if (state->portfd == -1) {\n        zfree(state);\n        return -1;\n    }\n\n    state->npending = 0;\n\n    for (i = 0; i < MAX_EVENT_BATCHSZ; i++) {\n        state->pending_fds[i] = -1;\n        state->pending_masks[i] = AE_NONE;\n    }\n\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    /* Nothing to resize here. */\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    aeApiState *state = eventLoop->apidata;\n\n    close(state->portfd);\n    zfree(state);\n}\n\nstatic int aeApiLookupPending(aeApiState *state, int fd) {\n    int i;\n\n    for (i = 0; i < state->npending; i++) {\n        if (state->pending_fds[i] == fd)\n            return (i);\n    }\n\n    return (-1);\n}\n\n/*\n * Helper function to invoke port_associate for the given fd and mask.\n */\nstatic int aeApiAssociate(const char *where, int portfd, int fd, int mask) {\n    int events = 0;\n    int rv, err;\n\n    if (mask & AE_READABLE)\n        events |= POLLIN;\n    if (mask & AE_WRITABLE)\n        events |= POLLOUT;\n\n    if (evport_debug)\n        fprintf(stderr, \"%s: port_associate(%d, 0x%x) = \", where, fd, events);\n\n    rv = port_associate(portfd, PORT_SOURCE_FD, fd, events,\n        (void *)(uintptr_t)mask);\n    err = errno;\n\n    if (evport_debug)\n        fprintf(stderr, \"%d (%s)\\n\", rv, rv == 0 ? \"no error\" : strerror(err));\n\n    if (rv == -1) {\n        fprintf(stderr, \"%s: port_associate: %s\\n\", where, strerror(err));\n\n        if (err == EAGAIN)\n            fprintf(stderr, \"aeApiAssociate: event port limit exceeded.\");\n    }\n\n    return rv;\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    int fullmask, pfd;\n\n    if (evport_debug)\n        fprintf(stderr, \"aeApiAddEvent: fd %d mask 0x%x\\n\", fd, mask);\n\n    /*\n     * Since port_associate's \"events\" argument replaces any existing events, we\n     * must be sure to include whatever events are already associated when\n     * we call port_associate() again.\n     */\n    fullmask = mask | eventLoop->events[fd].mask;\n    pfd = aeApiLookupPending(state, fd);\n\n    if (pfd != -1) {\n        /*\n         * This fd was recently returned from aeApiPoll.  It should be safe to\n         * assume that the consumer has processed that poll event, but we play\n         * it safer by simply updating pending_mask.  The fd will be\n         * re-associated as usual when aeApiPoll is called again.\n         */\n        if (evport_debug)\n            fprintf(stderr, \"aeApiAddEvent: adding to pending fd %d\\n\", fd);\n        state->pending_masks[pfd] |= fullmask;\n        return 0;\n    }\n\n    return (aeApiAssociate(\"aeApiAddEvent\", state->portfd, fd, fullmask));\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    int fullmask, pfd;\n\n    if (evport_debug)\n        fprintf(stderr, \"del fd %d mask 0x%x\\n\", fd, mask);\n\n    pfd = aeApiLookupPending(state, fd);\n\n    if (pfd != -1) {\n        if (evport_debug)\n            fprintf(stderr, \"deleting event from pending fd %d\\n\", fd);\n\n        /*\n         * This fd was just returned from aeApiPoll, so it's not currently\n         * associated with the port.  All we need to do is update\n         * pending_mask appropriately.\n         */\n        state->pending_masks[pfd] &= ~mask;\n\n        if (state->pending_masks[pfd] == AE_NONE)\n            state->pending_fds[pfd] = -1;\n\n        return;\n    }\n\n    /*\n     * The fd is currently associated with the port.  Like with the add case\n     * above, we must look at the full mask for the file descriptor before\n     * updating that association.  We don't have a good way of knowing what the\n     * events are without looking into the eventLoop state directly.  We rely on\n     * the fact that our caller has already updated the mask in the eventLoop.\n     */\n\n    fullmask = eventLoop->events[fd].mask;\n    if (fullmask == AE_NONE) {\n        /*\n         * We're removing *all* events, so use port_dissociate to remove the\n         * association completely.  Failure here indicates a bug.\n         */\n        if (evport_debug)\n            fprintf(stderr, \"aeApiDelEvent: port_dissociate(%d)\\n\", fd);\n\n        if (port_dissociate(state->portfd, PORT_SOURCE_FD, fd) != 0) {\n            perror(\"aeApiDelEvent: port_dissociate\");\n            abort(); /* will not return */\n        }\n    } else if (aeApiAssociate(\"aeApiDelEvent\", state->portfd, fd,\n        fullmask) != 0) {\n        /*\n         * ENOMEM is a potentially transient condition, but the kernel won't\n         * generally return it unless things are really bad.  EAGAIN indicates\n         * we've reached an resource limit, for which it doesn't make sense to\n         * retry (counter-intuitively).  All other errors indicate a bug.  In any\n         * of these cases, the best we can do is to abort.\n         */\n        abort(); /* will not return */\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    struct timespec timeout, *tsp;\n    int mask, i;\n    uint_t nevents;\n    port_event_t event[MAX_EVENT_BATCHSZ];\n\n    /*\n     * If we've returned fd events before, we must re-associate them with the\n     * port now, before calling port_get().  See the block comment at the top of\n     * this file for an explanation of why.\n     */\n    for (i = 0; i < state->npending; i++) {\n        if (state->pending_fds[i] == -1)\n            /* This fd has since been deleted. */\n            continue;\n\n        if (aeApiAssociate(\"aeApiPoll\", state->portfd,\n            state->pending_fds[i], state->pending_masks[i]) != 0) {\n            /* See aeApiDelEvent for why this case is fatal. */\n            abort();\n        }\n\n        state->pending_masks[i] = AE_NONE;\n        state->pending_fds[i] = -1;\n    }\n\n    state->npending = 0;\n\n    if (tvp != NULL) {\n        timeout.tv_sec = tvp->tv_sec;\n        timeout.tv_nsec = tvp->tv_usec * 1000;\n        tsp = &timeout;\n    } else {\n        tsp = NULL;\n    }\n\n    /*\n     * port_getn can return with errno == ETIME having returned some events (!).\n     * So if we get ETIME, we check nevents, too.\n     */\n    nevents = 1;\n    if (port_getn(state->portfd, event, MAX_EVENT_BATCHSZ, &nevents,\n        tsp) == -1 && (errno != ETIME || nevents == 0)) {\n        if (errno == ETIME || errno == EINTR)\n            return 0;\n\n        /* Any other error indicates a bug. */\n        perror(\"aeApiPoll: port_get\");\n        abort();\n    }\n\n    state->npending = nevents;\n\n    for (i = 0; i < nevents; i++) {\n            mask = 0;\n            if (event[i].portev_events & POLLIN)\n                mask |= AE_READABLE;\n            if (event[i].portev_events & POLLOUT)\n                mask |= AE_WRITABLE;\n\n            eventLoop->fired[i].fd = event[i].portev_object;\n            eventLoop->fired[i].mask = mask;\n\n            if (evport_debug)\n                fprintf(stderr, \"aeApiPoll: fd %d mask 0x%x\\n\",\n                    (int)event[i].portev_object, mask);\n\n            state->pending_fds[i] = event[i].portev_object;\n            state->pending_masks[i] = (uintptr_t)event[i].portev_user;\n    }\n\n    return nevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"evport\";\n}\n"
  },
  {
    "path": "src/ae_kqueue.c",
    "content": "/* Kqueue(2)-based ae.c module\n *\n * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <sys/types.h>\n#include <sys/event.h>\n#include <sys/time.h>\n\ntypedef struct aeApiState {\n    int kqfd;\n    struct kevent *events;\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    aeApiState *state = zmalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n    state->events = zmalloc(sizeof(struct kevent)*eventLoop->setsize);\n    if (!state->events) {\n        zfree(state);\n        return -1;\n    }\n    state->kqfd = kqueue();\n    if (state->kqfd == -1) {\n        zfree(state->events);\n        zfree(state);\n        return -1;\n    }\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    aeApiState *state = eventLoop->apidata;\n\n    state->events = zrealloc(state->events, sizeof(struct kevent)*setsize);\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    aeApiState *state = eventLoop->apidata;\n\n    close(state->kqfd);\n    zfree(state->events);\n    zfree(state);\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct kevent ke;\n\n    if (mask & AE_READABLE) {\n        EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);\n        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;\n    }\n    if (mask & AE_WRITABLE) {\n        EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);\n        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;\n    }\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n    struct kevent ke;\n\n    if (mask & AE_READABLE) {\n        EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);\n        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);\n    }\n    if (mask & AE_WRITABLE) {\n        EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);\n        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);\n    }\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, numevents = 0;\n\n    if (tvp != NULL) {\n        struct timespec timeout;\n        timeout.tv_sec = tvp->tv_sec;\n        timeout.tv_nsec = tvp->tv_usec * 1000;\n        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize,\n                        &timeout);\n    } else {\n        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize,\n                        NULL);\n    }\n\n    if (retval > 0) {\n        int j;\n\n        numevents = retval;\n        for(j = 0; j < numevents; j++) {\n            int mask = 0;\n            struct kevent *e = state->events+j;\n\n            if (e->filter == EVFILT_READ) mask |= AE_READABLE;\n            if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE;\n            eventLoop->fired[j].fd = e->ident;\n            eventLoop->fired[j].mask = mask;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"kqueue\";\n}\n"
  },
  {
    "path": "src/ae_select.c",
    "content": "/* Select()-based ae.c module.\n *\n * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#include <sys/select.h>\n#include <string.h>\n\ntypedef struct aeApiState {\n    fd_set rfds, wfds;\n    /* We need to have a copy of the fd sets as it's not safe to reuse\n     * FD sets after select(). */\n    fd_set _rfds, _wfds;\n} aeApiState;\n\nstatic int aeApiCreate(aeEventLoop *eventLoop) {\n    aeApiState *state = zmalloc(sizeof(aeApiState));\n\n    if (!state) return -1;\n    FD_ZERO(&state->rfds);\n    FD_ZERO(&state->wfds);\n    eventLoop->apidata = state;\n    return 0;\n}\n\nstatic int aeApiResize(aeEventLoop *eventLoop, int setsize) {\n    /* Just ensure we have enough room in the fd_set type. */\n    if (setsize >= FD_SETSIZE) return -1;\n    return 0;\n}\n\nstatic void aeApiFree(aeEventLoop *eventLoop) {\n    zfree(eventLoop->apidata);\n}\n\nstatic int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n\n    if (mask & AE_READABLE) FD_SET(fd,&state->rfds);\n    if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds);\n    return 0;\n}\n\nstatic void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {\n    aeApiState *state = eventLoop->apidata;\n\n    if (mask & AE_READABLE) FD_CLR(fd,&state->rfds);\n    if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds);\n}\n\nstatic int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {\n    aeApiState *state = eventLoop->apidata;\n    int retval, j, numevents = 0;\n\n    memcpy(&state->_rfds,&state->rfds,sizeof(fd_set));\n    memcpy(&state->_wfds,&state->wfds,sizeof(fd_set));\n\n    retval = select(eventLoop->maxfd+1,\n                &state->_rfds,&state->_wfds,NULL,tvp);\n    if (retval > 0) {\n        for (j = 0; j <= eventLoop->maxfd; j++) {\n            int mask = 0;\n            aeFileEvent *fe = &eventLoop->events[j];\n\n            if (fe->mask == AE_NONE) continue;\n            if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds))\n                mask |= AE_READABLE;\n            if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds))\n                mask |= AE_WRITABLE;\n            eventLoop->fired[numevents].fd = j;\n            eventLoop->fired[numevents].mask = mask;\n            numevents++;\n        }\n    }\n    return numevents;\n}\n\nstatic char *aeApiName(void) {\n    return \"select\";\n}\n"
  },
  {
    "path": "src/aprintf.c",
    "content": "// Copyright (C) 2012 - Will Glozer.  All rights reserved.\n\n#include <stdarg.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\nchar *aprintf(char **s, const char *fmt, ...) {\n    char *c = NULL;\n    int n, len;\n    va_list ap;\n\n    va_start(ap, fmt);\n    n = vsnprintf(NULL, 0, fmt, ap) + 1;\n    va_end(ap);\n\n    len = *s ? strlen(*s) : 0;\n\n    if ((*s = realloc(*s, (len + n) * sizeof(char)))) {\n        c = *s + len;\n        va_start(ap, fmt);\n        vsnprintf(c, n, fmt, ap);\n        va_end(ap);\n    }\n\n    return c;\n}\n"
  },
  {
    "path": "src/aprintf.h",
    "content": "#ifndef APRINTF_H\n#define APRINTF_H\n\nchar *aprintf(char **, const char *, ...);\n\n#endif /* APRINTF_H */\n"
  },
  {
    "path": "src/atomicvar.h",
    "content": "/* This file implements atomic counters using __atomic or __sync macros if\n * available, otherwise synchronizing different threads using a mutex.\n *\n * The exported interaface is composed of three macros:\n *\n * atomicIncr(var,count) -- Increment the atomic counter\n * atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter\n * atomicDecr(var,count) -- Decrement the atomic counter\n * atomicGet(var,dstvar) -- Fetch the atomic counter value\n * atomicSet(var,value)  -- Set the atomic counter value\n *\n * The variable 'var' should also have a declared mutex with the same\n * name and the \"_mutex\" postfix, for instance:\n *\n *  long myvar;\n *  pthread_mutex_t myvar_mutex;\n *  atomicSet(myvar,12345);\n *\n * If atomic primitives are availble (tested in config.h) the mutex\n * is not used.\n *\n * Never use return value from the macros, instead use the AtomicGetIncr()\n * if you need to get the current value and increment it atomically, like\n * in the followign example:\n *\n *  long oldvalue;\n *  atomicGetIncr(myvar,oldvalue,1);\n *  doSomethingWith(oldvalue);\n *\n * ----------------------------------------------------------------------------\n *\n * Copyright (c) 2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <pthread.h>\n\n#ifndef __ATOMIC_VAR_H\n#define __ATOMIC_VAR_H\n\n/* To test Redis with Helgrind (a Valgrind tool) it is useful to define\n * the following macro, so that __sync macros are used: those can be detected\n * by Helgrind (even if they are less efficient) so that no false positive\n * is reported. */\n// #define __ATOMIC_VAR_FORCE_SYNC_MACROS\n\n#if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__ATOMIC_RELAXED) && !defined(__sun) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057)\n/* Implementation using __atomic macros. */\n\n#define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \\\n} while(0)\n#define atomicDecr(var,count) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED)\n#define atomicGet(var,dstvar) do { \\\n    dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \\\n} while(0)\n#define atomicSet(var,value) __atomic_store_n(&var,value,__ATOMIC_RELAXED)\n#define REDIS_ATOMIC_API \"atomic-builtin\"\n\n#elif defined(HAVE_ATOMIC)\n/* Implementation using __sync macros. */\n\n#define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    oldvalue_var = __sync_fetch_and_add(&var,(count)); \\\n} while(0)\n#define atomicDecr(var,count) __sync_sub_and_fetch(&var,(count))\n#define atomicGet(var,dstvar) do { \\\n    dstvar = __sync_sub_and_fetch(&var,0); \\\n} while(0)\n#define atomicSet(var,value) do { \\\n    while(!__sync_bool_compare_and_swap(&var,var,value)); \\\n} while(0)\n#define REDIS_ATOMIC_API \"sync-builtin\"\n\n#else\n/* Implementation using pthread mutex. */\n\n#define atomicIncr(var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var += (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicGetIncr(var,oldvalue_var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    oldvalue_var = var; \\\n    var += (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicDecr(var,count) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var -= (count); \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicGet(var,dstvar) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    dstvar = var; \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define atomicSet(var,value) do { \\\n    pthread_mutex_lock(&var ## _mutex); \\\n    var = value; \\\n    pthread_mutex_unlock(&var ## _mutex); \\\n} while(0)\n#define REDIS_ATOMIC_API \"pthread-mutex\"\n\n#endif\n#endif /* __ATOMIC_VAR_H */\n"
  },
  {
    "path": "src/config.h",
    "content": "#ifndef CONFIG_H\n#define CONFIG_H\n\n#if defined(__FreeBSD__) || defined(__APPLE__)\n#define HAVE_KQUEUE\n#elif defined(__linux__)\n#define HAVE_EPOLL\n#elif defined (__sun)\n#define HAVE_EVPORT\n#define _XPG6\n#define __EXTENSIONS__\n#include <stropts.h>\n#include <sys/filio.h>\n#include <sys/time.h>\n#endif\n\n#endif /* CONFIG_H */\n"
  },
  {
    "path": "src/http_parser.c",
    "content": "/* Copyright Joyent, Inc. and other Node contributors.\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#define CURRENT_STATE() p_state\n#define UPDATE_STATE(V) p_state = (enum state) (V);\n#define RETURN(V)                                                    \\\ndo {                                                                 \\\n  parser->state = CURRENT_STATE();                                   \\\n  return (V);                                                        \\\n} while (0);\n#define REEXECUTE()                                                  \\\n  goto reexecute;                                                    \\\n\n\n#ifdef __GNUC__\n# define LIKELY(X) __builtin_expect(!!(X), 1)\n# define UNLIKELY(X) __builtin_expect(!!(X), 0)\n#else\n# define LIKELY(X) (X)\n# define UNLIKELY(X) (X)\n#endif\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 (LIKELY(settings->on_##FOR)) {                                  \\\n    parser->state = CURRENT_STATE();                                 \\\n    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \\\n      SET_ERRNO(HPE_CB_##FOR);                                       \\\n    }                                                                \\\n    UPDATE_STATE(parser->state);                                     \\\n                                                                     \\\n    /* We either errored above or got paused; get out */             \\\n    if (UNLIKELY(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 (LIKELY(settings->on_##FOR)) {                                \\\n      parser->state = CURRENT_STATE();                               \\\n      if (UNLIKELY(0 !=                                              \\\n                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \\\n        SET_ERRNO(HPE_CB_##FOR);                                     \\\n      }                                                              \\\n      UPDATE_STATE(parser->state);                                   \\\n                                                                     \\\n      /* We either errored above or got paused; get out */           \\\n      if (UNLIKELY(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/* 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#define COUNT_HEADER_SIZE(V)                                         \\\ndo {                                                                 \\\n  parser->nread += (V);                                              \\\n  if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) {            \\\n    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \\\n    goto error;                                                      \\\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_http_major\n  , s_res_http_dot\n  , s_res_http_minor\n  , s_res_http_end\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_http_major\n  , s_req_http_dot\n  , s_req_http_minor\n  , s_req_http_end\n  , s_req_line_almost_done\n\n  , s_header_field_start\n  , s_header_field\n  , s_header_value_discard_ws\n  , s_header_value_discard_ws_almost_done\n  , s_header_value_discard_lws\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_token_start\n  , h_matching_connection_keep_alive\n  , h_matching_connection_close\n  , h_matching_connection_upgrade\n  , h_matching_connection_token\n\n  , h_transfer_encoding_chunked\n  , h_connection_keep_alive\n  , h_connection_close\n  , h_connection_upgrade\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_v6_zone_start\n  , s_http_host_v6_zone\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#define STRICT_TOKEN(c)     (tokens[(unsigned char)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 * Verify that a char is a valid visible (printable) US-ASCII\n * character or %x80-FF\n **/\n#define IS_HEADER_CHAR(ch)                                                     \\\n  (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))\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  enum state p_state = (enum state) parser->state;\n  const unsigned int lenient = parser->lenient_http_headers;\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 (CURRENT_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 (CURRENT_STATE() == s_header_field)\n    header_field_mark = data;\n  if (CURRENT_STATE() == s_header_value)\n    header_value_mark = data;\n  switch (CURRENT_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  default:\n    break;\n  }\n\n  for (p=data; p != data + len; p++) {\n    ch = *p;\n\n    if (PARSING_HEADER(CURRENT_STATE()))\n      COUNT_HEADER_SIZE(1);\n\nreexecute:\n    switch (CURRENT_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 (LIKELY(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          UPDATE_STATE(s_res_or_resp_H);\n\n          CALLBACK_NOTIFY(message_begin);\n        } else {\n          parser->type = HTTP_REQUEST;\n          UPDATE_STATE(s_start_req);\n          REEXECUTE();\n        }\n\n        break;\n      }\n\n      case s_res_or_resp_H:\n        if (ch == 'T') {\n          parser->type = HTTP_RESPONSE;\n          UPDATE_STATE(s_res_HT);\n        } else {\n          if (UNLIKELY(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          UPDATE_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            UPDATE_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        UPDATE_STATE(s_res_HT);\n        break;\n\n      case s_res_HT:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_res_HTT);\n        break;\n\n      case s_res_HTT:\n        STRICT_CHECK(ch != 'P');\n        UPDATE_STATE(s_res_HTTP);\n        break;\n\n      case s_res_HTTP:\n        STRICT_CHECK(ch != '/');\n        UPDATE_STATE(s_res_http_major);\n        break;\n\n      case s_res_http_major:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        UPDATE_STATE(s_res_http_dot);\n        break;\n\n      case s_res_http_dot:\n      {\n        if (UNLIKELY(ch != '.')) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        UPDATE_STATE(s_res_http_minor);\n        break;\n      }\n\n      case s_res_http_minor:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        UPDATE_STATE(s_res_http_end);\n        break;\n\n      case s_res_http_end:\n      {\n        if (UNLIKELY(ch != ' ')) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        UPDATE_STATE(s_res_first_status_code);\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        UPDATE_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              UPDATE_STATE(s_res_status_start);\n              break;\n            case CR:\n            case LF:\n              UPDATE_STATE(s_res_status_start);\n              REEXECUTE();\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 (UNLIKELY(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        MARK(status);\n        UPDATE_STATE(s_res_status);\n        parser->index = 0;\n\n        if (ch == CR || ch == LF)\n          REEXECUTE();\n\n        break;\n      }\n\n      case s_res_status:\n        if (ch == CR) {\n          UPDATE_STATE(s_res_line_almost_done);\n          CALLBACK_DATA(status);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_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        UPDATE_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 (UNLIKELY(!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 'A': parser->method = HTTP_ACL; break;\n          case 'B': parser->method = HTTP_BIND; break;\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; /* or LINK */ break;\n          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ 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; /* or REBIND */ 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, UNBIND, UNLINK */ break;\n          default:\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n        }\n        UPDATE_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 (UNLIKELY(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          UPDATE_STATE(s_req_spaces_before_url);\n        } else if (ch == matcher[parser->index]) {\n          ; /* nada */\n        } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {\n\n          switch (parser->method << 16 | parser->index << 8 | ch) {\n#define XX(meth, pos, ch, new_meth) \\\n            case (HTTP_##meth << 16 | pos << 8 | ch): \\\n              parser->method = HTTP_##new_meth; break;\n\n            XX(POST,      1, 'U', PUT)\n            XX(POST,      1, 'A', PATCH)\n            XX(POST,      1, 'R', PROPFIND)\n            XX(PUT,       2, 'R', PURGE)\n            XX(CONNECT,   1, 'H', CHECKOUT)\n            XX(CONNECT,   2, 'P', COPY)\n            XX(MKCOL,     1, 'O', MOVE)\n            XX(MKCOL,     1, 'E', MERGE)\n            XX(MKCOL,     1, '-', MSEARCH)\n            XX(MKCOL,     2, 'A', MKACTIVITY)\n            XX(MKCOL,     3, 'A', MKCALENDAR)\n            XX(SUBSCRIBE, 1, 'E', SEARCH)\n            XX(REPORT,    2, 'B', REBIND)\n            XX(PROPFIND,  4, 'P', PROPPATCH)\n            XX(LOCK,      1, 'I', LINK)\n            XX(UNLOCK,    2, 'S', UNSUBSCRIBE)\n            XX(UNLOCK,    2, 'B', UNBIND)\n            XX(UNLOCK,    3, 'I', UNLINK)\n#undef XX\n            default:\n              SET_ERRNO(HPE_INVALID_METHOD);\n              goto error;\n          }\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          UPDATE_STATE(s_req_server_start);\n        }\n\n        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n        if (UNLIKELY(CURRENT_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            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n            if (UNLIKELY(CURRENT_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            UPDATE_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            UPDATE_STATE((ch == CR) ?\n              s_req_line_almost_done :\n              s_header_field_start);\n            CALLBACK_DATA(url);\n            break;\n          default:\n            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n            if (UNLIKELY(CURRENT_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            UPDATE_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        UPDATE_STATE(s_req_http_HT);\n        break;\n\n      case s_req_http_HT:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_req_http_HTT);\n        break;\n\n      case s_req_http_HTT:\n        STRICT_CHECK(ch != 'P');\n        UPDATE_STATE(s_req_http_HTTP);\n        break;\n\n      case s_req_http_HTTP:\n        STRICT_CHECK(ch != '/');\n        UPDATE_STATE(s_req_http_major);\n        break;\n\n      case s_req_http_major:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        UPDATE_STATE(s_req_http_dot);\n        break;\n\n      case s_req_http_dot:\n      {\n        if (UNLIKELY(ch != '.')) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        UPDATE_STATE(s_req_http_minor);\n        break;\n      }\n\n      case s_req_http_minor:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        UPDATE_STATE(s_req_http_end);\n        break;\n\n      case s_req_http_end:\n      {\n        if (ch == CR) {\n          UPDATE_STATE(s_req_line_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_field_start);\n          break;\n        }\n\n        SET_ERRNO(HPE_INVALID_VERSION);\n        goto error;\n        break;\n      }\n\n      /* end of request line */\n      case s_req_line_almost_done:\n      {\n        if (UNLIKELY(ch != LF)) {\n          SET_ERRNO(HPE_LF_EXPECTED);\n          goto error;\n        }\n\n        UPDATE_STATE(s_header_field_start);\n        break;\n      }\n\n      case s_header_field_start:\n      {\n        if (ch == CR) {\n          UPDATE_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          UPDATE_STATE(s_headers_almost_done);\n          REEXECUTE();\n        }\n\n        c = TOKEN(ch);\n\n        if (UNLIKELY(!c)) {\n          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n          goto error;\n        }\n\n        MARK(header_field);\n\n        parser->index = 0;\n        UPDATE_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        const char* start = p;\n        for (; p != data + len; p++) {\n          ch = *p;\n          c = TOKEN(ch);\n\n          if (!c)\n            break;\n\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        }\n\n        COUNT_HEADER_SIZE(p - start);\n\n        if (p == data + len) {\n          --p;\n          break;\n        }\n\n        if (ch == ':') {\n          UPDATE_STATE(s_header_value_discard_ws);\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_discard_ws:\n        if (ch == ' ' || ch == '\\t') break;\n\n        if (ch == CR) {\n          UPDATE_STATE(s_header_value_discard_ws_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_value_discard_lws);\n          break;\n        }\n\n        /* FALLTHROUGH */\n\n      case s_header_value_start:\n      {\n        MARK(header_value);\n\n        UPDATE_STATE(s_header_value);\n        parser->index = 0;\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 (UNLIKELY(!IS_NUM(ch))) {\n              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n              goto error;\n            }\n\n            if (parser->flags & F_CONTENTLENGTH) {\n              SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);\n              goto error;\n            }\n\n            parser->flags |= F_CONTENTLENGTH;\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 if (c == 'u') {\n              parser->header_state = h_matching_connection_upgrade;\n            } else {\n              parser->header_state = h_matching_connection_token;\n            }\n            break;\n\n          /* Multi-value `Connection` header */\n          case h_matching_connection_token_start:\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        const char* start = p;\n        enum header_states h_state = (enum header_states) parser->header_state;\n        for (; p != data + len; p++) {\n          ch = *p;\n          if (ch == CR) {\n            UPDATE_STATE(s_header_almost_done);\n            parser->header_state = h_state;\n            CALLBACK_DATA(header_value);\n            break;\n          }\n\n          if (ch == LF) {\n            UPDATE_STATE(s_header_almost_done);\n            COUNT_HEADER_SIZE(p - start);\n            parser->header_state = h_state;\n            CALLBACK_DATA_NOADVANCE(header_value);\n            REEXECUTE();\n          }\n\n          if (!lenient && !IS_HEADER_CHAR(ch)) {\n            SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n            goto error;\n          }\n\n          c = LOWER(ch);\n\n          switch (h_state) {\n            case h_general:\n            {\n              const char* p_cr;\n              const char* p_lf;\n              size_t limit = data + len - p;\n\n              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);\n\n              p_cr = (const char*) memchr(p, CR, limit);\n              p_lf = (const char*) memchr(p, LF, limit);\n              if (p_cr != NULL) {\n                if (p_lf != NULL && p_cr >= p_lf)\n                  p = p_lf;\n                else\n                  p = p_cr;\n              } else if (UNLIKELY(p_lf != NULL)) {\n                p = p_lf;\n              } else {\n                p = data + len;\n              }\n              --p;\n\n              break;\n            }\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 (UNLIKELY(!IS_NUM(ch))) {\n                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n                parser->header_state = h_state;\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 (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {\n                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n                parser->header_state = h_state;\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                h_state = h_general;\n              } else if (parser->index == sizeof(CHUNKED)-2) {\n                h_state = h_transfer_encoding_chunked;\n              }\n              break;\n\n            case h_matching_connection_token_start:\n              /* looking for 'Connection: keep-alive' */\n              if (c == 'k') {\n                h_state = h_matching_connection_keep_alive;\n              /* looking for 'Connection: close' */\n              } else if (c == 'c') {\n                h_state = h_matching_connection_close;\n              } else if (c == 'u') {\n                h_state = h_matching_connection_upgrade;\n              } else if (STRICT_TOKEN(c)) {\n                h_state = h_matching_connection_token;\n              } else if (c == ' ' || c == '\\t') {\n                /* Skip lws */\n              } else {\n                h_state = h_general;\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                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {\n                h_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                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(CLOSE)-2) {\n                h_state = h_connection_close;\n              }\n              break;\n\n            /* looking for 'Connection: upgrade' */\n            case h_matching_connection_upgrade:\n              parser->index++;\n              if (parser->index > sizeof(UPGRADE) - 1 ||\n                  c != UPGRADE[parser->index]) {\n                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(UPGRADE)-2) {\n                h_state = h_connection_upgrade;\n              }\n              break;\n\n            case h_matching_connection_token:\n              if (ch == ',') {\n                h_state = h_matching_connection_token_start;\n                parser->index = 0;\n              }\n              break;\n\n            case h_transfer_encoding_chunked:\n              if (ch != ' ') h_state = h_general;\n              break;\n\n            case h_connection_keep_alive:\n            case h_connection_close:\n            case h_connection_upgrade:\n              if (ch == ',') {\n                if (h_state == h_connection_keep_alive) {\n                  parser->flags |= F_CONNECTION_KEEP_ALIVE;\n                } else if (h_state == h_connection_close) {\n                  parser->flags |= F_CONNECTION_CLOSE;\n                } else if (h_state == h_connection_upgrade) {\n                  parser->flags |= F_CONNECTION_UPGRADE;\n                }\n                h_state = h_matching_connection_token_start;\n                parser->index = 0;\n              } else if (ch != ' ') {\n                h_state = h_matching_connection_token;\n              }\n              break;\n\n            default:\n              UPDATE_STATE(s_header_value);\n              h_state = h_general;\n              break;\n          }\n        }\n        parser->header_state = h_state;\n\n        COUNT_HEADER_SIZE(p - start);\n\n        if (p == data + len)\n          --p;\n        break;\n      }\n\n      case s_header_almost_done:\n      {\n        if (UNLIKELY(ch != LF)) {\n          SET_ERRNO(HPE_LF_EXPECTED);\n          goto error;\n        }\n\n        UPDATE_STATE(s_header_value_lws);\n        break;\n      }\n\n      case s_header_value_lws:\n      {\n        if (ch == ' ' || ch == '\\t') {\n          UPDATE_STATE(s_header_value_start);\n          REEXECUTE();\n        }\n\n        /* finished the header */\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          case h_connection_upgrade:\n            parser->flags |= F_CONNECTION_UPGRADE;\n            break;\n          default:\n            break;\n        }\n\n        UPDATE_STATE(s_header_field_start);\n        REEXECUTE();\n      }\n\n      case s_header_value_discard_ws_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n        UPDATE_STATE(s_header_value_discard_lws);\n        break;\n      }\n\n      case s_header_value_discard_lws:\n      {\n        if (ch == ' ' || ch == '\\t') {\n          UPDATE_STATE(s_header_value_discard_ws);\n          break;\n        } else {\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_connection_upgrade:\n              parser->flags |= F_CONNECTION_UPGRADE;\n              break;\n            case h_transfer_encoding_chunked:\n              parser->flags |= F_CHUNKED;\n              break;\n            default:\n              break;\n          }\n\n          /* header value was empty */\n          MARK(header_value);\n          UPDATE_STATE(s_header_field_start);\n          CALLBACK_DATA_NOADVANCE(header_value);\n          REEXECUTE();\n        }\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          UPDATE_STATE(s_message_done);\n          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);\n          REEXECUTE();\n        }\n\n        /* Cannot use chunked encoding and a content-length header together\n           per the HTTP specification. */\n        if ((parser->flags & F_CHUNKED) &&\n            (parser->flags & F_CONTENTLENGTH)) {\n          SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);\n          goto error;\n        }\n\n        UPDATE_STATE(s_headers_done);\n\n        /* Set this here so that on_headers_complete() callbacks can see it */\n        if ((parser->flags & F_UPGRADE) &&\n            (parser->flags & F_CONNECTION_UPGRADE)) {\n          /* For responses, \"Upgrade: foo\" and \"Connection: upgrade\" are\n           * mandatory only when it is a 101 Switching Protocols response,\n           * otherwise it is purely informational, to announce support.\n           */\n          parser->upgrade =\n              (parser->type == HTTP_REQUEST || parser->status_code == 101);\n        } else {\n          parser->upgrade = (parser->method == HTTP_CONNECT);\n        }\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 2:\n              parser->upgrade = 1;\n\n            /* FALLTHROUGH */\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        REEXECUTE();\n      }\n\n      case s_headers_done:\n      {\n        int hasBody;\n        STRICT_CHECK(ch != LF);\n\n        parser->nread = 0;\n\n        hasBody = parser->flags & F_CHUNKED ||\n          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);\n        if (parser->upgrade && (parser->method == HTTP_CONNECT ||\n                                (parser->flags & F_SKIPBODY) || !hasBody)) {\n          /* Exit, the rest of the message is in a different protocol. */\n          UPDATE_STATE(NEW_MESSAGE());\n          CALLBACK_NOTIFY(message_complete);\n          RETURN((p - data) + 1);\n        }\n\n        if (parser->flags & F_SKIPBODY) {\n          UPDATE_STATE(NEW_MESSAGE());\n          CALLBACK_NOTIFY(message_complete);\n        } else if (parser->flags & F_CHUNKED) {\n          /* chunked encoding - ignore Content-Length header */\n          UPDATE_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            UPDATE_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            UPDATE_STATE(s_body_identity);\n          } else {\n            if (!http_message_needs_eof(parser)) {\n              /* Assume content-length 0 - read the next */\n              UPDATE_STATE(NEW_MESSAGE());\n              CALLBACK_NOTIFY(message_complete);\n            } else {\n              /* Read body until EOF */\n              UPDATE_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          UPDATE_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          REEXECUTE();\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        UPDATE_STATE(NEW_MESSAGE());\n        CALLBACK_NOTIFY(message_complete);\n        if (parser->upgrade) {\n          /* Exit, the rest of the message is in a different protocol. */\n          RETURN((p - data) + 1);\n        }\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 (UNLIKELY(unhex_val == -1)) {\n          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);\n          goto error;\n        }\n\n        parser->content_length = unhex_val;\n        UPDATE_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          UPDATE_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            UPDATE_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 (UNLIKELY((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          UPDATE_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          UPDATE_STATE(s_header_field_start);\n        } else {\n          UPDATE_STATE(s_chunk_data);\n        }\n        CALLBACK_NOTIFY(chunk_header);\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          UPDATE_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        UPDATE_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        UPDATE_STATE(s_chunk_size_start);\n        CALLBACK_NOTIFY(chunk_complete);\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\nvoid\nhttp_parser_settings_init(http_parser_settings *settings)\n{\n  memset(settings, 0, sizeof(*settings));\n}\n\nconst char *\nhttp_errno_name(enum http_errno err) {\n  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));\n  return http_strerror_tab[err].name;\n}\n\nconst char *\nhttp_errno_description(enum http_errno err) {\n  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));\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      if (s == s_http_host_v6 && ch == '%') {\n        return s_http_host_v6_zone_start;\n      }\n      break;\n\n    case s_http_host_v6_zone:\n      if (ch == ']') {\n        return s_http_host_v6_end;\n      }\n\n    /* FALLTHROUGH */\n    case s_http_host_v6_zone_start:\n      /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */\n      if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||\n          ch == '~') {\n        return s_http_host_v6_zone;\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  assert(u->field_set & (1 << UF_HOST));\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_v6_zone_start:\n      case s_http_host_v6_zone:\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_v6_zone_start:\n    case s_http_host_v6_zone:\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\nvoid\nhttp_parser_url_init(struct http_parser_url *u) {\n  memset(u, 0, sizeof(*u));\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  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      /* FALLTHROUGH */\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)) &&\n      (u->field_set & (1 << UF_HOST)) == 0) {\n    return 1;\n  }\n\n  if (u->field_set & (1 << UF_HOST)) {\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 7\n#define HTTP_PARSER_VERSION_PATCH 1\n\n#include <stddef.h>\n#if defined(_WIN32) && !defined(__MINGW32__) && \\\n  (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)\n#include <BaseTsd.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. If the macro is not defined\n * before including this header then the default is used. To\n * change the maximum header size, define the macro in the build\n * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove\n * the effective limit on the size of the header, define the macro\n * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)\n */\n#ifndef HTTP_MAX_HEADER_SIZE\n# define HTTP_MAX_HEADER_SIZE (80*1024)\n#endif\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 * Returning `2` from on_headers_complete will tell parser that it should not\n * expect neither a body nor any futher responses on this connection. This is\n * useful for handling responses to a CONNECT request which may not contain\n * `Upgrade` or `Connection: upgrade` headers.\n *\n * http_data_cb does not return data chunks. It will be called arbitrarily\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/* Status Codes */\n#define HTTP_STATUS_MAP(XX)                                                 \\\n  XX(100, CONTINUE,                        Continue)                        \\\n  XX(101, SWITCHING_PROTOCOLS,             Switching Protocols)             \\\n  XX(102, PROCESSING,                      Processing)                      \\\n  XX(200, OK,                              OK)                              \\\n  XX(201, CREATED,                         Created)                         \\\n  XX(202, ACCEPTED,                        Accepted)                        \\\n  XX(203, NON_AUTHORITATIVE_INFORMATION,   Non-Authoritative Information)   \\\n  XX(204, NO_CONTENT,                      No Content)                      \\\n  XX(205, RESET_CONTENT,                   Reset Content)                   \\\n  XX(206, PARTIAL_CONTENT,                 Partial Content)                 \\\n  XX(207, MULTI_STATUS,                    Multi-Status)                    \\\n  XX(208, ALREADY_REPORTED,                Already Reported)                \\\n  XX(226, IM_USED,                         IM Used)                         \\\n  XX(300, MULTIPLE_CHOICES,                Multiple Choices)                \\\n  XX(301, MOVED_PERMANENTLY,               Moved Permanently)               \\\n  XX(302, FOUND,                           Found)                           \\\n  XX(303, SEE_OTHER,                       See Other)                       \\\n  XX(304, NOT_MODIFIED,                    Not Modified)                    \\\n  XX(305, USE_PROXY,                       Use Proxy)                       \\\n  XX(307, TEMPORARY_REDIRECT,              Temporary Redirect)              \\\n  XX(308, PERMANENT_REDIRECT,              Permanent Redirect)              \\\n  XX(400, BAD_REQUEST,                     Bad Request)                     \\\n  XX(401, UNAUTHORIZED,                    Unauthorized)                    \\\n  XX(402, PAYMENT_REQUIRED,                Payment Required)                \\\n  XX(403, FORBIDDEN,                       Forbidden)                       \\\n  XX(404, NOT_FOUND,                       Not Found)                       \\\n  XX(405, METHOD_NOT_ALLOWED,              Method Not Allowed)              \\\n  XX(406, NOT_ACCEPTABLE,                  Not Acceptable)                  \\\n  XX(407, PROXY_AUTHENTICATION_REQUIRED,   Proxy Authentication Required)   \\\n  XX(408, REQUEST_TIMEOUT,                 Request Timeout)                 \\\n  XX(409, CONFLICT,                        Conflict)                        \\\n  XX(410, GONE,                            Gone)                            \\\n  XX(411, LENGTH_REQUIRED,                 Length Required)                 \\\n  XX(412, PRECONDITION_FAILED,             Precondition Failed)             \\\n  XX(413, PAYLOAD_TOO_LARGE,               Payload Too Large)               \\\n  XX(414, URI_TOO_LONG,                    URI Too Long)                    \\\n  XX(415, UNSUPPORTED_MEDIA_TYPE,          Unsupported Media Type)          \\\n  XX(416, RANGE_NOT_SATISFIABLE,           Range Not Satisfiable)           \\\n  XX(417, EXPECTATION_FAILED,              Expectation Failed)              \\\n  XX(421, MISDIRECTED_REQUEST,             Misdirected Request)             \\\n  XX(422, UNPROCESSABLE_ENTITY,            Unprocessable Entity)            \\\n  XX(423, LOCKED,                          Locked)                          \\\n  XX(424, FAILED_DEPENDENCY,               Failed Dependency)               \\\n  XX(426, UPGRADE_REQUIRED,                Upgrade Required)                \\\n  XX(428, PRECONDITION_REQUIRED,           Precondition Required)           \\\n  XX(429, TOO_MANY_REQUESTS,               Too Many Requests)               \\\n  XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \\\n  XX(451, UNAVAILABLE_FOR_LEGAL_REASONS,   Unavailable For Legal Reasons)   \\\n  XX(500, INTERNAL_SERVER_ERROR,           Internal Server Error)           \\\n  XX(501, NOT_IMPLEMENTED,                 Not Implemented)                 \\\n  XX(502, BAD_GATEWAY,                     Bad Gateway)                     \\\n  XX(503, SERVICE_UNAVAILABLE,             Service Unavailable)             \\\n  XX(504, GATEWAY_TIMEOUT,                 Gateway Timeout)                 \\\n  XX(505, HTTP_VERSION_NOT_SUPPORTED,      HTTP Version Not Supported)      \\\n  XX(506, VARIANT_ALSO_NEGOTIATES,         Variant Also Negotiates)         \\\n  XX(507, INSUFFICIENT_STORAGE,            Insufficient Storage)            \\\n  XX(508, LOOP_DETECTED,                   Loop Detected)                   \\\n  XX(510, NOT_EXTENDED,                    Not Extended)                    \\\n  XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \\\n\nenum http_status\n  {\n#define XX(num, name, string) HTTP_STATUS_##name = num,\n  HTTP_STATUS_MAP(XX)\n#undef XX\n  };\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  XX(16, BIND,        BIND)         \\\n  XX(17, REBIND,      REBIND)       \\\n  XX(18, UNBIND,      UNBIND)       \\\n  XX(19, ACL,         ACL)          \\\n  /* subversion */                  \\\n  XX(20, REPORT,      REPORT)       \\\n  XX(21, MKACTIVITY,  MKACTIVITY)   \\\n  XX(22, CHECKOUT,    CHECKOUT)     \\\n  XX(23, MERGE,       MERGE)        \\\n  /* upnp */                        \\\n  XX(24, MSEARCH,     M-SEARCH)     \\\n  XX(25, NOTIFY,      NOTIFY)       \\\n  XX(26, SUBSCRIBE,   SUBSCRIBE)    \\\n  XX(27, UNSUBSCRIBE, UNSUBSCRIBE)  \\\n  /* RFC-5789 */                    \\\n  XX(28, PATCH,       PATCH)        \\\n  XX(29, PURGE,       PURGE)        \\\n  /* CalDAV */                      \\\n  XX(30, MKCALENDAR,  MKCALENDAR)   \\\n  /* RFC-2068, section 19.6.1.2 */  \\\n  XX(31, LINK,        LINK)         \\\n  XX(32, UNLINK,      UNLINK)       \\\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_CONNECTION_UPGRADE    = 1 << 3\n  , F_TRAILING              = 1 << 4\n  , F_UPGRADE               = 1 << 5\n  , F_SKIPBODY              = 1 << 6\n  , F_CONTENTLENGTH         = 1 << 7\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  XX(CB_chunk_header, \"the on_chunk_header callback failed\")         \\\n  XX(CB_chunk_complete, \"the on_chunk_complete 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(UNEXPECTED_CONTENT_LENGTH,                                      \\\n     \"unexpected 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 : 8;        /* F_* values from 'flags' enum; semi-public */\n  unsigned int state : 7;        /* enum state from http_parser.c */\n  unsigned int header_state : 7; /* enum header_state from http_parser.c */\n  unsigned int index : 7;        /* index into current matcher */\n  unsigned int lenient_http_headers : 1;\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  /* When on_chunk_header is called, the current chunk length is stored\n   * in parser->content_length.\n   */\n  http_cb      on_chunk_header;\n  http_cb      on_chunk_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, patch);\n */\nunsigned long http_parser_version(void);\n\nvoid http_parser_init(http_parser *parser, enum http_parser_type type);\n\n\n/* Initialize http_parser_settings members to 0\n */\nvoid http_parser_settings_init(http_parser_settings *settings);\n\n\n/* Executes the parser. Returns number of parsed bytes. Sets\n * `parser->http_errno` on error. */\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/* Initialize all http_parser_url members to 0 */\nvoid http_parser_url_init(struct http_parser_url *u);\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/main.h",
    "content": "#ifndef MAIN_H\n#define MAIN_H\n\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <math.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n#include <time.h>\n#include <unistd.h>\n#include <sys/time.h>\n#include <sys/uio.h>\n\n#include \"ssl.h\"\n#include \"aprintf.h\"\n#include \"stats.h\"\n#include \"units.h\"\n#include \"zmalloc.h\"\n\nstruct config;\n\nstatic void *thread_main(void *);\nstatic int connect_socket(thread *, connection *);\nstatic int reconnect_socket(thread *, connection *);\n\nstatic int record_rate(aeEventLoop *, long long, void *);\n\nstatic void socket_connected(aeEventLoop *, int, void *, int);\nstatic void socket_writeable(aeEventLoop *, int, void *, int);\nstatic void socket_readable(aeEventLoop *, int, void *, int);\n\nstatic int response_complete(http_parser *);\nstatic int header_field(http_parser *, const char *, size_t);\nstatic int header_value(http_parser *, const char *, size_t);\nstatic int response_body(http_parser *, const char *, size_t);\n\nstatic uint64_t time_us();\n\nstatic int parse_args(struct config *, char **, struct http_parser_url *, char **, int, char **);\nstatic char *copy_url_part(char *, struct http_parser_url *, enum http_parser_url_fields);\n\nstatic void print_stats_header();\nstatic void print_stats(char *, stats *, char *(*)(long double));\nstatic void print_stats_latency(stats *);\n\n#endif /* MAIN_H */\n"
  },
  {
    "path": "src/net.c",
    "content": "// Copyright (C) 2013 - Will Glozer.  All rights reserved.\n\n#include <errno.h>\n#include <unistd.h>\n#include <sys/ioctl.h>\n\n#include \"net.h\"\n\nstatus sock_connect(connection *c, char *host) {\n    return OK;\n}\n\nstatus sock_close(connection *c) {\n    return OK;\n}\n\nstatus sock_read(connection *c, size_t *n) {\n    ssize_t r = read(c->fd, c->buf, sizeof(c->buf));\n    *n = (size_t) r;\n    return r >= 0 ? OK : ERROR;\n}\n\nstatus sock_write(connection *c, char *buf, size_t len, size_t *n) {\n    ssize_t r;\n    if ((r = write(c->fd, buf, len)) == -1) {\n        switch (errno) {\n            case EAGAIN: return RETRY;\n            default:     return ERROR;\n        }\n    }\n    *n = (size_t) r;\n    return OK;\n}\n\nsize_t sock_readable(connection *c) {\n    int n, rc;\n    rc = ioctl(c->fd, FIONREAD, &n);\n    return rc == -1 ? 0 : n;\n}\n"
  },
  {
    "path": "src/net.h",
    "content": "#ifndef NET_H\n#define NET_H\n\n#include \"config.h\"\n#include <stdint.h>\n#include <openssl/ssl.h>\n#include \"wrk.h\"\n\ntypedef enum {\n    OK,\n    ERROR,\n    RETRY\n} status;\n\nstruct sock {\n    status ( *connect)(connection *, char *);\n    status (   *close)(connection *);\n    status (    *read)(connection *, size_t *);\n    status (   *write)(connection *, char *, size_t, size_t *);\n    size_t (*readable)(connection *);\n};\n\nstatus sock_connect(connection *, char *);\nstatus sock_close(connection *);\nstatus sock_read(connection *, size_t *);\nstatus sock_write(connection *, char *, size_t, size_t *);\nsize_t sock_readable(connection *);\n\n#endif /* NET_H */\n"
  },
  {
    "path": "src/script.c",
    "content": "// Copyright (C) 2013 - Will Glozer.  All rights reserved.\n\n#include <stdlib.h>\n#include <string.h>\n#include \"script.h\"\n#include \"http_parser.h\"\n#include \"zmalloc.h\"\n\ntypedef struct {\n    char *name;\n    int   type;\n    void *value;\n} table_field;\n\nstatic int script_addr_tostring(lua_State *);\nstatic int script_addr_gc(lua_State *);\nstatic int script_stats_call(lua_State *);\nstatic int script_stats_len(lua_State *);\nstatic int script_stats_index(lua_State *);\nstatic int script_thread_index(lua_State *);\nstatic int script_thread_newindex(lua_State *);\nstatic int script_wrk_lookup(lua_State *);\nstatic int script_wrk_connect(lua_State *);\n\nstatic void set_fields(lua_State *, int, const table_field *);\nstatic void set_field(lua_State *, int, char *, int);\nstatic int push_url_part(lua_State *, char *, struct http_parser_url *, enum http_parser_url_fields);\n\nstatic const struct luaL_Reg addrlib[] = {\n    { \"__tostring\", script_addr_tostring   },\n    { \"__gc\"    ,   script_addr_gc         },\n    { NULL,         NULL                   }\n};\n\nstatic const struct luaL_Reg statslib[] = {\n    { \"__call\",     script_stats_call      },\n    { \"__index\",    script_stats_index     },\n    { \"__len\",      script_stats_len       },\n    { NULL,         NULL                   }\n};\n\nstatic const struct luaL_Reg threadlib[] = {\n    { \"__index\",    script_thread_index    },\n    { \"__newindex\", script_thread_newindex },\n    { NULL,         NULL                   }\n};\n\nlua_State *script_create(char *file, char *url, char **headers) {\n    lua_State *L = luaL_newstate();\n    luaL_openlibs(L);\n    (void) luaL_dostring(L, \"wrk = require \\\"wrk\\\"\");\n\n    luaL_newmetatable(L, \"wrk.addr\");\n    luaL_register(L, NULL, addrlib);\n    luaL_newmetatable(L, \"wrk.stats\");\n    luaL_register(L, NULL, statslib);\n    luaL_newmetatable(L, \"wrk.thread\");\n    luaL_register(L, NULL, threadlib);\n\n    struct http_parser_url parts = {};\n    script_parse_url(url, &parts);\n    char *path = \"/\";\n\n    if (parts.field_set & (1 << UF_PATH)) {\n        path = &url[parts.field_data[UF_PATH].off];\n    }\n\n    const table_field fields[] = {\n        { \"lookup\",  LUA_TFUNCTION, script_wrk_lookup  },\n        { \"connect\", LUA_TFUNCTION, script_wrk_connect },\n        { \"path\",    LUA_TSTRING,   path               },\n        { NULL,      0,             NULL               },\n    };\n\n    lua_getglobal(L, \"wrk\");\n\n    set_field(L, 4, \"scheme\", push_url_part(L, url, &parts, UF_SCHEMA));\n    set_field(L, 4, \"host\",   push_url_part(L, url, &parts, UF_HOST));\n    set_field(L, 4, \"port\",   push_url_part(L, url, &parts, UF_PORT));\n    set_fields(L, 4, fields);\n\n    lua_getfield(L, 4, \"headers\");\n    for (char **h = headers; *h; h++) {\n        char *p = strchr(*h, ':');\n        if (p && p[1] == ' ') {\n            lua_pushlstring(L, *h, p - *h);\n            lua_pushstring(L, p + 2);\n            lua_settable(L, 5);\n        }\n    }\n    lua_pop(L, 5);\n\n    if (file && luaL_dofile(L, file)) {\n        const char *cause = lua_tostring(L, -1);\n        fprintf(stderr, \"%s: %s\\n\", file, cause);\n    }\n\n    return L;\n}\n\nbool script_resolve(lua_State *L, char *host, char *service) {\n    lua_getglobal(L, \"wrk\");\n\n    lua_getfield(L, -1, \"resolve\");\n    lua_pushstring(L, host);\n    lua_pushstring(L, service);\n    lua_call(L, 2, 0);\n\n    lua_getfield(L, -1, \"addrs\");\n    size_t count = lua_objlen(L, -1);\n    lua_pop(L, 2);\n    return count > 0;\n}\n\nvoid script_push_thread(lua_State *L, thread *t) {\n    thread **ptr = (thread **) lua_newuserdata(L, sizeof(thread **));\n    *ptr = t;\n    luaL_getmetatable(L, \"wrk.thread\");\n    lua_setmetatable(L, -2);\n}\n\nvoid script_init(lua_State *L, thread *t, int argc, char **argv) {\n    lua_getglobal(t->L, \"wrk\");\n\n    script_push_thread(t->L, t);\n    lua_setfield(t->L, -2, \"thread\");\n\n    lua_getglobal(L, \"wrk\");\n    lua_getfield(L, -1, \"setup\");\n    script_push_thread(L, t);\n    lua_call(L, 1, 0);\n    lua_pop(L, 1);\n\n    lua_getfield(t->L, -1, \"init\");\n    lua_newtable(t->L);\n    for (int i = 0; i < argc; i++) {\n        lua_pushstring(t->L, argv[i]);\n        lua_rawseti(t->L, -2, i);\n    }\n    lua_call(t->L, 1, 0);\n    lua_pop(t->L, 1);\n}\n\nuint64_t script_delay(lua_State *L) {\n    lua_getglobal(L, \"delay\");\n    lua_call(L, 0, 1);\n    uint64_t delay = lua_tonumber(L, -1);\n    lua_pop(L, 1);\n    return delay;\n}\n\nvoid script_request(lua_State *L, char **buf, size_t *len) {\n    int pop = 1;\n    lua_getglobal(L, \"request\");\n    if (!lua_isfunction(L, -1)) {\n        lua_getglobal(L, \"wrk\");\n        lua_getfield(L, -1, \"request\");\n        pop += 2;\n    }\n    lua_call(L, 0, 1);\n    const char *str = lua_tolstring(L, -1, len);\n    *buf = realloc(*buf, *len);\n    memcpy(*buf, str, *len);\n    lua_pop(L, pop);\n}\n\nvoid script_response(lua_State *L, int status, buffer *headers, buffer *body) {\n    lua_getglobal(L, \"response\");\n    lua_pushinteger(L, status);\n    lua_newtable(L);\n\n    for (char *c = headers->buffer; c < headers->cursor; ) {\n        c = buffer_pushlstring(L, c);\n        c = buffer_pushlstring(L, c);\n        lua_rawset(L, -3);\n    }\n\n    lua_pushlstring(L, body->buffer, body->cursor - body->buffer);\n    lua_call(L, 3, 0);\n\n    buffer_reset(headers);\n    buffer_reset(body);\n}\n\nbool script_is_function(lua_State *L, char *name) {\n    lua_getglobal(L, name);\n    bool is_function = lua_isfunction(L, -1);\n    lua_pop(L, 1);\n    return is_function;\n}\n\nbool script_is_static(lua_State *L) {\n    return !script_is_function(L, \"request\");\n}\n\nbool script_want_response(lua_State *L) {\n    return script_is_function(L, \"response\");\n}\n\nbool script_has_delay(lua_State *L) {\n    return script_is_function(L, \"delay\");\n}\n\nbool script_has_done(lua_State *L) {\n    return script_is_function(L, \"done\");\n}\n\nvoid script_header_done(lua_State *L, luaL_Buffer *buffer) {\n    luaL_pushresult(buffer);\n}\n\nvoid script_summary(lua_State *L, uint64_t duration, uint64_t requests, uint64_t bytes) {\n    const table_field fields[] = {\n        { \"duration\", LUA_TNUMBER, &duration },\n        { \"requests\", LUA_TNUMBER, &requests },\n        { \"bytes\",    LUA_TNUMBER, &bytes    },\n        { NULL,       0,           NULL      },\n    };\n    lua_newtable(L);\n    set_fields(L, 1, fields);\n}\n\nvoid script_errors(lua_State *L, errors *errors) {\n    uint64_t e[] = {\n        errors->connect,\n        errors->read,\n        errors->write,\n        errors->status,\n        errors->timeout\n    };\n    const table_field fields[] = {\n        { \"connect\", LUA_TNUMBER, &e[0] },\n        { \"read\",    LUA_TNUMBER, &e[1] },\n        { \"write\",   LUA_TNUMBER, &e[2] },\n        { \"status\",  LUA_TNUMBER, &e[3] },\n        { \"timeout\", LUA_TNUMBER, &e[4] },\n        { NULL,      0,           NULL  },\n    };\n    lua_newtable(L);\n    set_fields(L, 2, fields);\n    lua_setfield(L, 1, \"errors\");\n}\n\nvoid script_push_stats(lua_State *L, stats *s) {\n    stats **ptr = (stats **) lua_newuserdata(L, sizeof(stats **));\n    *ptr = s;\n    luaL_getmetatable(L, \"wrk.stats\");\n    lua_setmetatable(L, -2);\n}\n\nvoid script_done(lua_State *L, stats *latency, stats *requests) {\n    lua_getglobal(L, \"done\");\n    lua_pushvalue(L, 1);\n\n    script_push_stats(L, latency);\n    script_push_stats(L, requests);\n\n    lua_call(L, 3, 0);\n    lua_pop(L, 1);\n}\n\nstatic int verify_request(http_parser *parser) {\n    size_t *count = parser->data;\n    (*count)++;\n    return 0;\n}\n\nsize_t script_verify_request(lua_State *L) {\n    http_parser_settings settings = {\n        .on_message_complete = verify_request\n    };\n    http_parser parser;\n    char *request = NULL;\n    size_t len, count = 0;\n\n    script_request(L, &request, &len);\n    http_parser_init(&parser, HTTP_REQUEST);\n    parser.data = &count;\n\n    size_t parsed = http_parser_execute(&parser, &settings, request, len);\n\n    if (parsed != len || count == 0) {\n        enum http_errno err = HTTP_PARSER_ERRNO(&parser);\n        const char *desc = http_errno_description(err);\n        const char *msg = err != HPE_OK ? desc : \"incomplete request\";\n        int line = 1, column = 1;\n\n        for (char *c = request; c < request + parsed; c++) {\n            column++;\n            if (*c == '\\n') {\n                column = 1;\n                line++;\n            }\n        }\n\n        fprintf(stderr, \"%s at %d:%d\\n\", msg, line, column);\n        exit(1);\n    }\n\n    return count;\n}\n\nstatic struct addrinfo *checkaddr(lua_State *L) {\n    struct addrinfo *addr = luaL_checkudata(L, -1, \"wrk.addr\");\n    luaL_argcheck(L, addr != NULL, 1, \"`addr' expected\");\n    return addr;\n}\n\nvoid script_addr_copy(struct addrinfo *src, struct addrinfo *dst) {\n    *dst = *src;\n    dst->ai_addr = zmalloc(src->ai_addrlen);\n    memcpy(dst->ai_addr, src->ai_addr, src->ai_addrlen);\n}\n\nstruct addrinfo *script_addr_clone(lua_State *L, struct addrinfo *addr) {\n    struct addrinfo *udata = lua_newuserdata(L, sizeof(*udata));\n    luaL_getmetatable(L, \"wrk.addr\");\n    lua_setmetatable(L, -2);\n    script_addr_copy(addr, udata);\n    return udata;\n}\n\nstatic int script_addr_tostring(lua_State *L) {\n    struct addrinfo *addr = checkaddr(L);\n    char host[NI_MAXHOST];\n    char service[NI_MAXSERV];\n\n    int flags = NI_NUMERICHOST | NI_NUMERICSERV;\n    int rc = getnameinfo(addr->ai_addr, addr->ai_addrlen, host, NI_MAXHOST, service, NI_MAXSERV, flags);\n    if (rc != 0) {\n        const char *msg = gai_strerror(rc);\n        return luaL_error(L, \"addr tostring failed %s\", msg);\n    }\n\n    lua_pushfstring(L, \"%s:%s\", host, service);\n    return 1;\n}\n\nstatic int script_addr_gc(lua_State *L) {\n    struct addrinfo *addr = checkaddr(L);\n    zfree(addr->ai_addr);\n    return 0;\n}\n\nstatic stats *checkstats(lua_State *L) {\n    stats **s = luaL_checkudata(L, 1, \"wrk.stats\");\n    luaL_argcheck(L, s != NULL, 1, \"`stats' expected\");\n    return *s;\n}\n\nstatic int script_stats_percentile(lua_State *L) {\n    stats *s = checkstats(L);\n    lua_Number p = luaL_checknumber(L, 2);\n    lua_pushnumber(L, stats_percentile(s, p));\n    return 1;\n}\n\nstatic int script_stats_call(lua_State *L) {\n    stats *s = checkstats(L);\n    uint64_t index = lua_tonumber(L, 2);\n    uint64_t count;\n    lua_pushnumber(L, stats_value_at(s, index - 1, &count));\n    lua_pushnumber(L, count);\n    return 2;\n}\n\nstatic int script_stats_index(lua_State *L) {\n    stats *s = checkstats(L);\n    const char *method = lua_tostring(L, 2);\n    if (!strcmp(\"min\",   method)) lua_pushnumber(L, s->min);\n    if (!strcmp(\"max\",   method)) lua_pushnumber(L, s->max);\n    if (!strcmp(\"mean\",  method)) lua_pushnumber(L, stats_mean(s));\n    if (!strcmp(\"stdev\", method)) lua_pushnumber(L, stats_stdev(s, stats_mean(s)));\n    if (!strcmp(\"percentile\", method)) {\n        lua_pushcfunction(L, script_stats_percentile);\n    }\n    return 1;\n}\n\nstatic int script_stats_len(lua_State *L) {\n    stats *s = checkstats(L);\n    lua_pushinteger(L, stats_popcount(s));\n    return 1;\n}\n\nstatic thread *checkthread(lua_State *L) {\n    thread **t = luaL_checkudata(L, 1, \"wrk.thread\");\n    luaL_argcheck(L, t != NULL, 1, \"`thread' expected\");\n    return *t;\n}\n\nstatic int script_thread_get(lua_State *L) {\n    thread *t = checkthread(L);\n    const char *key = lua_tostring(L, -1);\n    lua_getglobal(t->L, key);\n    script_copy_value(t->L, L, -1);\n    lua_pop(t->L, 1);\n    return 1;\n}\n\nstatic int script_thread_set(lua_State *L) {\n    thread *t = checkthread(L);\n    const char *name = lua_tostring(L, -2);\n    script_copy_value(L, t->L, -1);\n    lua_setglobal(t->L, name);\n    return 0;\n}\n\nstatic int script_thread_stop(lua_State *L) {\n    thread *t = checkthread(L);\n    aeStop(t->loop);\n    return 0;\n}\n\nstatic int script_thread_index(lua_State *L) {\n    thread *t = checkthread(L);\n    const char *key = lua_tostring(L, 2);\n    if (!strcmp(\"get\",  key)) lua_pushcfunction(L, script_thread_get);\n    if (!strcmp(\"set\",  key)) lua_pushcfunction(L, script_thread_set);\n    if (!strcmp(\"stop\", key)) lua_pushcfunction(L, script_thread_stop);\n    if (!strcmp(\"addr\", key)) script_addr_clone(L, t->addr);\n    return 1;\n}\n\nstatic int script_thread_newindex(lua_State *L) {\n    thread *t = checkthread(L);\n    const char *key = lua_tostring(L, -2);\n    if (!strcmp(\"addr\", key)) {\n        struct addrinfo *addr = checkaddr(L);\n        if (t->addr) zfree(t->addr->ai_addr);\n        t->addr = zrealloc(t->addr, sizeof(*addr));\n        script_addr_copy(addr, t->addr);\n    } else {\n        luaL_error(L, \"cannot set '%s' on thread\", luaL_typename(L, -1));\n    }\n    return 0;\n}\n\nstatic int script_wrk_lookup(lua_State *L) {\n    struct addrinfo *addrs;\n    struct addrinfo hints = {\n        .ai_family   = AF_UNSPEC,\n        .ai_socktype = SOCK_STREAM\n    };\n    int rc, index = 1;\n\n    const char *host    = lua_tostring(L, -2);\n    const char *service = lua_tostring(L, -1);\n\n    if ((rc = getaddrinfo(host, service, &hints, &addrs)) != 0) {\n        const char *msg = gai_strerror(rc);\n        fprintf(stderr, \"unable to resolve %s:%s %s\\n\", host, service, msg);\n        exit(1);\n    }\n\n    lua_newtable(L);\n    for (struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next) {\n        script_addr_clone(L, addr);\n        lua_rawseti(L, -2, index++);\n    }\n\n    freeaddrinfo(addrs);\n    return 1;\n}\n\nstatic int script_wrk_connect(lua_State *L) {\n    struct addrinfo *addr = checkaddr(L);\n    int fd, connected = 0;\n    if ((fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) != -1) {\n        connected = connect(fd, addr->ai_addr, addr->ai_addrlen) == 0;\n        close(fd);\n    }\n    lua_pushboolean(L, connected);\n    return 1;\n}\n\nvoid script_copy_value(lua_State *src, lua_State *dst, int index) {\n    switch (lua_type(src, index)) {\n        case LUA_TBOOLEAN:\n            lua_pushboolean(dst, lua_toboolean(src, index));\n            break;\n        case LUA_TNIL:\n            lua_pushnil(dst);\n            break;\n        case LUA_TNUMBER:\n            lua_pushnumber(dst, lua_tonumber(src, index));\n            break;\n        case LUA_TSTRING:\n            lua_pushstring(dst, lua_tostring(src, index));\n            break;\n        case LUA_TTABLE:\n            lua_newtable(dst);\n            lua_pushnil(src);\n            while (lua_next(src, index - 1)) {\n                script_copy_value(src, dst, -2);\n                script_copy_value(src, dst, -1);\n                lua_settable(dst, -3);\n                lua_pop(src, 1);\n            }\n            lua_pop(src, 1);\n            break;\n        default:\n            luaL_error(src, \"cannot transfer '%s' to thread\", luaL_typename(src, index));\n    }\n}\n\nint script_parse_url(char *url, struct http_parser_url *parts) {\n    if (!http_parser_parse_url(url, strlen(url), 0, parts)) {\n        if (!(parts->field_set & (1 << UF_SCHEMA))) return 0;\n        if (!(parts->field_set & (1 << UF_HOST)))   return 0;\n        return 1;\n    }\n    return 0;\n}\n\nstatic int push_url_part(lua_State *L, char *url, struct http_parser_url *parts, enum http_parser_url_fields field) {\n    int type = parts->field_set & (1 << field) ? LUA_TSTRING : LUA_TNIL;\n    uint16_t off, len;\n    switch (type) {\n        case LUA_TSTRING:\n            off = parts->field_data[field].off;\n            len = parts->field_data[field].len;\n            lua_pushlstring(L, &url[off], len);\n            break;\n        case LUA_TNIL:\n            lua_pushnil(L);\n    }\n    return type;\n}\n\nstatic void set_field(lua_State *L, int index, char *field, int type) {\n    (void) type;\n    lua_setfield(L, index, field);\n}\n\nstatic void set_fields(lua_State *L, int index, const table_field *fields) {\n    for (int i = 0; fields[i].name; i++) {\n        table_field f = fields[i];\n        switch (f.value == NULL ? LUA_TNIL : f.type) {\n            case LUA_TFUNCTION:\n                lua_pushcfunction(L, (lua_CFunction) f.value);\n                break;\n            case LUA_TNUMBER:\n                lua_pushinteger(L, *((lua_Integer *) f.value));\n                break;\n            case LUA_TSTRING:\n                lua_pushstring(L, (const char *) f.value);\n                break;\n            case LUA_TNIL:\n                lua_pushnil(L);\n                break;\n        }\n        lua_setfield(L, index, f.name);\n    }\n}\n\nvoid buffer_append(buffer *b, const char *data, size_t len) {\n    size_t used = b->cursor - b->buffer;\n    while (used + len + 1 >= b->length) {\n        b->length += 1024;\n        b->buffer  = realloc(b->buffer, b->length);\n        b->cursor  = b->buffer + used;\n    }\n    memcpy(b->cursor, data, len);\n    b->cursor += len;\n}\n\nvoid buffer_reset(buffer *b) {\n    b->cursor = b->buffer;\n}\n\nchar *buffer_pushlstring(lua_State *L, char *start) {\n    char *end = strchr(start, 0);\n    lua_pushlstring(L, start, end - start);\n    return end + 1;\n}\n"
  },
  {
    "path": "src/script.h",
    "content": "#ifndef SCRIPT_H\n#define SCRIPT_H\n\n#include <stdbool.h>\n#include <lua.h>\n#include <lualib.h>\n#include <lauxlib.h>\n#include <unistd.h>\n#include \"stats.h\"\n#include \"wrk.h\"\n\nlua_State *script_create(char *, char *, char **);\n\nbool script_resolve(lua_State *, char *, char *);\nvoid script_setup(lua_State *, thread *);\nvoid script_done(lua_State *, stats *, stats *);\n\nvoid script_init(lua_State *, thread *, int, char **);\nuint64_t script_delay(lua_State *);\nvoid script_request(lua_State *, char **, size_t *);\nvoid script_response(lua_State *, int, buffer *, buffer *);\nsize_t script_verify_request(lua_State *L);\n\nbool script_is_static(lua_State *);\nbool script_want_response(lua_State *L);\nbool script_has_delay(lua_State *L);\nbool script_has_done(lua_State *L);\nvoid script_summary(lua_State *, uint64_t, uint64_t, uint64_t);\nvoid script_errors(lua_State *, errors *);\n\nvoid script_copy_value(lua_State *, lua_State *, int);\nint script_parse_url(char *, struct http_parser_url *);\n\nvoid buffer_append(buffer *, const char *, size_t);\nvoid buffer_reset(buffer *);\nchar *buffer_pushlstring(lua_State *, char *);\n\n#endif /* SCRIPT_H */\n"
  },
  {
    "path": "src/ssl.c",
    "content": "// Copyright (C) 2013 - Will Glozer.  All rights reserved.\n\n#include <pthread.h>\n\n#include <openssl/evp.h>\n#include <openssl/err.h>\n#include <openssl/ssl.h>\n\n#include \"ssl.h\"\n\nSSL_CTX *ssl_init() {\n    SSL_CTX *ctx = NULL;\n\n    SSL_load_error_strings();\n    SSL_library_init();\n    OpenSSL_add_all_algorithms();\n\n    if ((ctx = SSL_CTX_new(SSLv23_client_method()))) {\n        SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);\n        SSL_CTX_set_verify_depth(ctx, 0);\n        SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);\n        SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT);\n    }\n\n    return ctx;\n}\n\nstatus ssl_connect(connection *c, char *host) {\n    int r;\n    SSL_set_fd(c->ssl, c->fd);\n    SSL_set_tlsext_host_name(c->ssl, host);\n    if ((r = SSL_connect(c->ssl)) != 1) {\n        switch (SSL_get_error(c->ssl, r)) {\n            case SSL_ERROR_WANT_READ:  return RETRY;\n            case SSL_ERROR_WANT_WRITE: return RETRY;\n            default:                   return ERROR;\n        }\n    }\n    return OK;\n}\n\nstatus ssl_close(connection *c) {\n    SSL_shutdown(c->ssl);\n    SSL_clear(c->ssl);\n    return OK;\n}\n\nstatus ssl_read(connection *c, size_t *n) {\n    int r;\n    if ((r = SSL_read(c->ssl, c->buf, sizeof(c->buf))) <= 0) {\n        switch (SSL_get_error(c->ssl, r)) {\n            case SSL_ERROR_WANT_READ:  return RETRY;\n            case SSL_ERROR_WANT_WRITE: return RETRY;\n            default:                   return ERROR;\n        }\n    }\n    *n = (size_t) r;\n    return OK;\n}\n\nstatus ssl_write(connection *c, char *buf, size_t len, size_t *n) {\n    int r;\n    if ((r = SSL_write(c->ssl, buf, len)) <= 0) {\n        switch (SSL_get_error(c->ssl, r)) {\n            case SSL_ERROR_WANT_READ:  return RETRY;\n            case SSL_ERROR_WANT_WRITE: return RETRY;\n            default:                   return ERROR;\n        }\n    }\n    *n = (size_t) r;\n    return OK;\n}\n\nsize_t ssl_readable(connection *c) {\n    return SSL_pending(c->ssl);\n}\n"
  },
  {
    "path": "src/ssl.h",
    "content": "#ifndef SSL_H\n#define SSL_H\n\n#include \"net.h\"\n\nSSL_CTX *ssl_init();\n\nstatus ssl_connect(connection *, char *);\nstatus ssl_close(connection *);\nstatus ssl_read(connection *, size_t *);\nstatus ssl_write(connection *, char *, size_t, size_t *);\nsize_t ssl_readable(connection *);\n\n#endif /* SSL_H */\n"
  },
  {
    "path": "src/stats.c",
    "content": "// Copyright (C) 2012 - Will Glozer.  All rights reserved.\n\n#include <inttypes.h>\n#include <stdlib.h>\n#include <math.h>\n\n#include \"stats.h\"\n#include \"zmalloc.h\"\n\nstats *stats_alloc(uint64_t max) {\n    uint64_t limit = max + 1;\n    stats *s = zcalloc(sizeof(stats) + sizeof(uint64_t) * limit);\n    s->limit = limit;\n    s->min   = UINT64_MAX;\n    return s;\n}\n\nvoid stats_free(stats *stats) {\n    zfree(stats);\n}\n\nint stats_record(stats *stats, uint64_t n) {\n    if (n >= stats->limit) return 0;\n    __sync_fetch_and_add(&stats->data[n], 1);\n    __sync_fetch_and_add(&stats->count, 1);\n    uint64_t min = stats->min;\n    uint64_t max = stats->max;\n    while (n < min) min = __sync_val_compare_and_swap(&stats->min, min, n);\n    while (n > max) max = __sync_val_compare_and_swap(&stats->max, max, n);\n    return 1;\n}\n\nvoid stats_correct(stats *stats, int64_t expected) {\n    for (uint64_t n = expected * 2; n <= stats->max; n++) {\n        uint64_t count = stats->data[n];\n        int64_t m = (int64_t) n - expected;\n        while (count && m > expected) {\n            stats->data[m] += count;\n            stats->count += count;\n            m -= expected;\n        }\n    }\n}\n\nlong double stats_mean(stats *stats) {\n    if (stats->count == 0) return 0.0;\n\n    uint64_t sum = 0;\n    for (uint64_t i = stats->min; i <= stats->max; i++) {\n        sum += stats->data[i] * i;\n    }\n    return sum / (long double) stats->count;\n}\n\nlong double stats_stdev(stats *stats, long double mean) {\n    long double sum = 0.0;\n    if (stats->count < 2) return 0.0;\n    for (uint64_t i = stats->min; i <= stats->max; i++) {\n        if (stats->data[i]) {\n            sum += powl(i - mean, 2) * stats->data[i];\n        }\n    }\n    return sqrtl(sum / (stats->count - 1));\n}\n\nlong double stats_within_stdev(stats *stats, long double mean, long double stdev, uint64_t n) {\n    long double upper = mean + (stdev * n);\n    long double lower = mean - (stdev * n);\n    uint64_t sum = 0;\n\n    for (uint64_t i = stats->min; i <= stats->max; i++) {\n        if (i >= lower && i <= upper) {\n            sum += stats->data[i];\n        }\n    }\n\n    return (sum / (long double) stats->count) * 100;\n}\n\nuint64_t stats_percentile(stats *stats, long double p) {\n    uint64_t rank = round((p / 100.0) * stats->count + 0.5);\n    uint64_t total = 0;\n    for (uint64_t i = stats->min; i <= stats->max; i++) {\n        total += stats->data[i];\n        if (total >= rank) return i;\n    }\n    return 0;\n}\n\nuint64_t stats_popcount(stats *stats) {\n    uint64_t count = 0;\n    for (uint64_t i = stats->min; i <= stats->max; i++) {\n        if (stats->data[i]) count++;\n    }\n    return count;\n}\n\nuint64_t stats_value_at(stats *stats, uint64_t index, uint64_t *count) {\n    *count = 0;\n    for (uint64_t i = stats->min; i <= stats->max; i++) {\n        if (stats->data[i] && (*count)++ == index) {\n            *count = stats->data[i];\n            return i;\n        }\n    }\n    return 0;\n}\n"
  },
  {
    "path": "src/stats.h",
    "content": "#ifndef STATS_H\n#define STATS_H\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))\n#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))\n\ntypedef struct {\n    uint32_t connect;\n    uint32_t read;\n    uint32_t write;\n    uint32_t status;\n    uint32_t timeout;\n} errors;\n\ntypedef struct {\n    uint64_t count;\n    uint64_t limit;\n    uint64_t min;\n    uint64_t max;\n    uint64_t data[];\n} stats;\n\nstats *stats_alloc(uint64_t);\nvoid stats_free(stats *);\n\nint stats_record(stats *, uint64_t);\nvoid stats_correct(stats *, int64_t);\n\nlong double stats_mean(stats *);\nlong double stats_stdev(stats *stats, long double);\nlong double stats_within_stdev(stats *, long double, long double, uint64_t);\nuint64_t stats_percentile(stats *, long double);\n\nuint64_t stats_popcount(stats *);\nuint64_t stats_value_at(stats *stats, uint64_t, uint64_t *);\n\n#endif /* STATS_H */\n"
  },
  {
    "path": "src/units.c",
    "content": "// Copyright (C) 2012 - Will Glozer.  All rights reserved.\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <strings.h>\n#include <inttypes.h>\n\n#include \"units.h\"\n#include \"aprintf.h\"\n\ntypedef struct {\n    int scale;\n    char *base;\n    char *units[];\n} units;\n\nunits time_units_us = {\n    .scale = 1000,\n    .base  = \"us\",\n    .units = { \"ms\", \"s\", NULL }\n};\n\nunits time_units_s = {\n    .scale = 60,\n    .base  = \"s\",\n    .units = { \"m\", \"h\", NULL }\n};\n\nunits binary_units = {\n    .scale = 1024,\n    .base  = \"\",\n    .units = { \"K\", \"M\", \"G\", \"T\", \"P\", NULL }\n};\n\nunits metric_units = {\n    .scale = 1000,\n    .base  = \"\",\n    .units = { \"k\", \"M\", \"G\", \"T\", \"P\", NULL }\n};\n\nstatic char *format_units(long double n, units *m, int p) {\n    long double amt = n, scale;\n    char *unit = m->base;\n    char *msg = NULL;\n\n    scale = m->scale * 0.85;\n\n    for (int i = 0; m->units[i+1] && amt >= scale; i++) {\n        amt /= m->scale;\n        unit = m->units[i];\n    }\n\n    aprintf(&msg, \"%.*Lf%s\", p, amt, unit);\n\n    return msg;\n}\n\nstatic int scan_units(char *s, uint64_t *n, units *m) {\n    uint64_t base, scale = 1;\n    char unit[3] = { 0, 0, 0 };\n    int i, c;\n\n    if ((c = sscanf(s, \"%\"SCNu64\"%2s\", &base, unit)) < 1) return -1;\n\n    if (c == 2 && strncasecmp(unit, m->base, 3)) {\n        for (i = 0; m->units[i] != NULL; i++) {\n            scale *= m->scale;\n            if (!strncasecmp(unit, m->units[i], 3)) break;\n        }\n        if (m->units[i] == NULL) return -1;\n    }\n\n    *n = base * scale;\n    return 0;\n}\n\nchar *format_binary(long double n) {\n    return format_units(n, &binary_units, 2);\n}\n\nchar *format_metric(long double n) {\n    return format_units(n, &metric_units, 2);\n}\n\nchar *format_time_us(long double n) {\n    units *units = &time_units_us;\n    if (n >= 1000000.0) {\n        n /= 1000000.0;\n        units = &time_units_s;\n    }\n    return format_units(n, units, 2);\n}\n\nchar *format_time_s(long double n) {\n    return format_units(n, &time_units_s, 0);\n}\n\nint scan_metric(char *s, uint64_t *n) {\n    return scan_units(s, n, &metric_units);\n}\n\nint scan_time(char *s, uint64_t *n) {\n    return scan_units(s, n, &time_units_s);\n}\n"
  },
  {
    "path": "src/units.h",
    "content": "#ifndef UNITS_H\n#define UNITS_H\n\nchar *format_binary(long double);\nchar *format_metric(long double);\nchar *format_time_us(long double);\nchar *format_time_s(long double);\n\nint scan_metric(char *, uint64_t *);\nint scan_time(char *, uint64_t *);\n\n#endif /* UNITS_H */\n"
  },
  {
    "path": "src/wrk.c",
    "content": "// Copyright (C) 2012 - Will Glozer.  All rights reserved.\n\n#include \"wrk.h\"\n#include \"script.h\"\n#include \"main.h\"\n\nstatic struct config {\n    uint64_t connections;\n    uint64_t duration;\n    uint64_t threads;\n    uint64_t timeout;\n    uint64_t pipeline;\n    bool     delay;\n    bool     dynamic;\n    bool     latency;\n    char    *host;\n    char    *script;\n    SSL_CTX *ctx;\n} cfg;\n\nstatic struct {\n    stats *latency;\n    stats *requests;\n} statistics;\n\nstatic struct sock sock = {\n    .connect  = sock_connect,\n    .close    = sock_close,\n    .read     = sock_read,\n    .write    = sock_write,\n    .readable = sock_readable\n};\n\nstatic struct http_parser_settings parser_settings = {\n    .on_message_complete = response_complete\n};\n\nstatic volatile sig_atomic_t stop = 0;\n\nstatic void handler(int sig) {\n    stop = 1;\n}\n\nstatic void usage() {\n    printf(\"Usage: wrk <options> <url>                            \\n\"\n           \"  Options:                                            \\n\"\n           \"    -c, --connections <N>  Connections to keep open   \\n\"\n           \"    -d, --duration    <T>  Duration of test           \\n\"\n           \"    -t, --threads     <N>  Number of threads to use   \\n\"\n           \"                                                      \\n\"\n           \"    -s, --script      <S>  Load Lua script file       \\n\"\n           \"    -H, --header      <H>  Add header to request      \\n\"\n           \"        --latency          Print latency statistics   \\n\"\n           \"        --timeout     <T>  Socket/request timeout     \\n\"\n           \"    -v, --version          Print version details      \\n\"\n           \"                                                      \\n\"\n           \"  Numeric arguments may include a SI unit (1k, 1M, 1G)\\n\"\n           \"  Time arguments may include a time unit (2s, 2m, 2h)\\n\");\n}\n\nint main(int argc, char **argv) {\n    char *url, **headers = zmalloc(argc * sizeof(char *));\n    struct http_parser_url parts = {};\n\n    if (parse_args(&cfg, &url, &parts, headers, argc, argv)) {\n        usage();\n        exit(1);\n    }\n\n    char *schema  = copy_url_part(url, &parts, UF_SCHEMA);\n    char *host    = copy_url_part(url, &parts, UF_HOST);\n    char *port    = copy_url_part(url, &parts, UF_PORT);\n    char *service = port ? port : schema;\n\n    if (!strncmp(\"https\", schema, 5)) {\n        if ((cfg.ctx = ssl_init()) == NULL) {\n            fprintf(stderr, \"unable to initialize SSL\\n\");\n            ERR_print_errors_fp(stderr);\n            exit(1);\n        }\n        sock.connect  = ssl_connect;\n        sock.close    = ssl_close;\n        sock.read     = ssl_read;\n        sock.write    = ssl_write;\n        sock.readable = ssl_readable;\n    }\n\n    signal(SIGPIPE, SIG_IGN);\n    signal(SIGINT,  SIG_IGN);\n\n    statistics.latency  = stats_alloc(cfg.timeout * 1000);\n    statistics.requests = stats_alloc(MAX_THREAD_RATE_S);\n    thread *threads     = zcalloc(cfg.threads * sizeof(thread));\n\n    lua_State *L = script_create(cfg.script, url, headers);\n    if (!script_resolve(L, host, service)) {\n        char *msg = strerror(errno);\n        fprintf(stderr, \"unable to connect to %s:%s %s\\n\", host, service, msg);\n        exit(1);\n    }\n\n    cfg.host = host;\n\n    for (uint64_t i = 0; i < cfg.threads; i++) {\n        thread *t      = &threads[i];\n        t->loop        = aeCreateEventLoop(10 + cfg.connections * 3);\n        t->connections = cfg.connections / cfg.threads;\n\n        t->L = script_create(cfg.script, url, headers);\n        script_init(L, t, argc - optind, &argv[optind]);\n\n        if (i == 0) {\n            cfg.pipeline = script_verify_request(t->L);\n            cfg.dynamic  = !script_is_static(t->L);\n            cfg.delay    = script_has_delay(t->L);\n            if (script_want_response(t->L)) {\n                parser_settings.on_header_field = header_field;\n                parser_settings.on_header_value = header_value;\n                parser_settings.on_body         = response_body;\n            }\n        }\n\n        if (!t->loop || pthread_create(&t->thread, NULL, &thread_main, t)) {\n            char *msg = strerror(errno);\n            fprintf(stderr, \"unable to create thread %\"PRIu64\": %s\\n\", i, msg);\n            exit(2);\n        }\n    }\n\n    struct sigaction sa = {\n        .sa_handler = handler,\n        .sa_flags   = 0,\n    };\n    sigfillset(&sa.sa_mask);\n    sigaction(SIGINT, &sa, NULL);\n\n    char *time = format_time_s(cfg.duration);\n    printf(\"Running %s test @ %s\\n\", time, url);\n    printf(\"  %\"PRIu64\" threads and %\"PRIu64\" connections\\n\", cfg.threads, cfg.connections);\n\n    uint64_t start    = time_us();\n    uint64_t complete = 0;\n    uint64_t bytes    = 0;\n    errors errors     = { 0 };\n\n    sleep(cfg.duration);\n    stop = 1;\n\n    for (uint64_t i = 0; i < cfg.threads; i++) {\n        thread *t = &threads[i];\n        pthread_join(t->thread, NULL);\n\n        complete += t->complete;\n        bytes    += t->bytes;\n\n        errors.connect += t->errors.connect;\n        errors.read    += t->errors.read;\n        errors.write   += t->errors.write;\n        errors.timeout += t->errors.timeout;\n        errors.status  += t->errors.status;\n    }\n\n    uint64_t runtime_us = time_us() - start;\n    long double runtime_s   = runtime_us / 1000000.0;\n    long double req_per_s   = complete   / runtime_s;\n    long double bytes_per_s = bytes      / runtime_s;\n\n    if (complete / cfg.connections > 0) {\n        int64_t interval = runtime_us / (complete / cfg.connections);\n        stats_correct(statistics.latency, interval);\n    }\n\n    print_stats_header();\n    print_stats(\"Latency\", statistics.latency, format_time_us);\n    print_stats(\"Req/Sec\", statistics.requests, format_metric);\n    if (cfg.latency) print_stats_latency(statistics.latency);\n\n    char *runtime_msg = format_time_us(runtime_us);\n\n    printf(\"  %\"PRIu64\" requests in %s, %sB read\\n\", complete, runtime_msg, format_binary(bytes));\n    if (errors.connect || errors.read || errors.write || errors.timeout) {\n        printf(\"  Socket errors: connect %d, read %d, write %d, timeout %d\\n\",\n               errors.connect, errors.read, errors.write, errors.timeout);\n    }\n\n    if (errors.status) {\n        printf(\"  Non-2xx or 3xx responses: %d\\n\", errors.status);\n    }\n\n    printf(\"Requests/sec: %9.2Lf\\n\", req_per_s);\n    printf(\"Transfer/sec: %10sB\\n\", format_binary(bytes_per_s));\n\n    if (script_has_done(L)) {\n        script_summary(L, runtime_us, complete, bytes);\n        script_errors(L, &errors);\n        script_done(L, statistics.latency, statistics.requests);\n    }\n\n    return 0;\n}\n\nvoid *thread_main(void *arg) {\n    thread *thread = arg;\n\n    char *request = NULL;\n    size_t length = 0;\n\n    if (!cfg.dynamic) {\n        script_request(thread->L, &request, &length);\n    }\n\n    thread->cs = zcalloc(thread->connections * sizeof(connection));\n    connection *c = thread->cs;\n\n    for (uint64_t i = 0; i < thread->connections; i++, c++) {\n        c->thread = thread;\n        c->ssl     = cfg.ctx ? SSL_new(cfg.ctx) : NULL;\n        c->request = request;\n        c->length  = length;\n        c->delayed = cfg.delay;\n        connect_socket(thread, c);\n    }\n\n    aeEventLoop *loop = thread->loop;\n    aeCreateTimeEvent(loop, RECORD_INTERVAL_MS, record_rate, thread, NULL);\n\n    thread->start = time_us();\n    aeMain(loop);\n\n    aeDeleteEventLoop(loop);\n    zfree(thread->cs);\n\n    return NULL;\n}\n\nstatic int connect_socket(thread *thread, connection *c) {\n    struct addrinfo *addr = thread->addr;\n    struct aeEventLoop *loop = thread->loop;\n    int fd, flags;\n\n    fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);\n\n    flags = fcntl(fd, F_GETFL, 0);\n    fcntl(fd, F_SETFL, flags | O_NONBLOCK);\n\n    if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) {\n        if (errno != EINPROGRESS) goto error;\n    }\n\n    flags = 1;\n    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));\n\n    flags = AE_READABLE | AE_WRITABLE;\n    if (aeCreateFileEvent(loop, fd, flags, socket_connected, c) == AE_OK) {\n        c->parser.data = c;\n        c->fd = fd;\n        return fd;\n    }\n\n  error:\n    thread->errors.connect++;\n    close(fd);\n    return -1;\n}\n\nstatic int reconnect_socket(thread *thread, connection *c) {\n    aeDeleteFileEvent(thread->loop, c->fd, AE_WRITABLE | AE_READABLE);\n    sock.close(c);\n    close(c->fd);\n    return connect_socket(thread, c);\n}\n\nstatic int record_rate(aeEventLoop *loop, long long id, void *data) {\n    thread *thread = data;\n\n    if (thread->requests > 0) {\n        uint64_t elapsed_ms = (time_us() - thread->start) / 1000;\n        uint64_t requests = (thread->requests / (double) elapsed_ms) * 1000;\n\n        stats_record(statistics.requests, requests);\n\n        thread->requests = 0;\n        thread->start    = time_us();\n    }\n\n    if (stop) aeStop(loop);\n\n    return RECORD_INTERVAL_MS;\n}\n\nstatic int delay_request(aeEventLoop *loop, long long id, void *data) {\n    connection *c = data;\n    c->delayed = false;\n    aeCreateFileEvent(loop, c->fd, AE_WRITABLE, socket_writeable, c);\n    return AE_NOMORE;\n}\n\nstatic int header_field(http_parser *parser, const char *at, size_t len) {\n    connection *c = parser->data;\n    if (c->state == VALUE) {\n        *c->headers.cursor++ = '\\0';\n        c->state = FIELD;\n    }\n    buffer_append(&c->headers, at, len);\n    return 0;\n}\n\nstatic int header_value(http_parser *parser, const char *at, size_t len) {\n    connection *c = parser->data;\n    if (c->state == FIELD) {\n        *c->headers.cursor++ = '\\0';\n        c->state = VALUE;\n    }\n    buffer_append(&c->headers, at, len);\n    return 0;\n}\n\nstatic int response_body(http_parser *parser, const char *at, size_t len) {\n    connection *c = parser->data;\n    buffer_append(&c->body, at, len);\n    return 0;\n}\n\nstatic int response_complete(http_parser *parser) {\n    connection *c = parser->data;\n    thread *thread = c->thread;\n    uint64_t now = time_us();\n    int status = parser->status_code;\n\n    thread->complete++;\n    thread->requests++;\n\n    if (status > 399) {\n        thread->errors.status++;\n    }\n\n    if (c->headers.buffer) {\n        *c->headers.cursor++ = '\\0';\n        script_response(thread->L, status, &c->headers, &c->body);\n        c->state = FIELD;\n    }\n\n    if (--c->pending == 0) {\n        if (!stats_record(statistics.latency, now - c->start)) {\n            thread->errors.timeout++;\n        }\n        c->delayed = cfg.delay;\n        aeCreateFileEvent(thread->loop, c->fd, AE_WRITABLE, socket_writeable, c);\n    }\n\n    if (!http_should_keep_alive(parser)) {\n        reconnect_socket(thread, c);\n        goto done;\n    }\n\n    http_parser_init(parser, HTTP_RESPONSE);\n\n  done:\n    return 0;\n}\n\nstatic void socket_connected(aeEventLoop *loop, int fd, void *data, int mask) {\n    connection *c = data;\n\n    switch (sock.connect(c, cfg.host)) {\n        case OK:    break;\n        case ERROR: goto error;\n        case RETRY: return;\n    }\n\n    http_parser_init(&c->parser, HTTP_RESPONSE);\n    c->written = 0;\n\n    aeCreateFileEvent(c->thread->loop, fd, AE_READABLE, socket_readable, c);\n    aeCreateFileEvent(c->thread->loop, fd, AE_WRITABLE, socket_writeable, c);\n\n    return;\n\n  error:\n    c->thread->errors.connect++;\n    reconnect_socket(c->thread, c);\n}\n\nstatic void socket_writeable(aeEventLoop *loop, int fd, void *data, int mask) {\n    connection *c = data;\n    thread *thread = c->thread;\n\n    if (c->delayed) {\n        uint64_t delay = script_delay(thread->L);\n        aeDeleteFileEvent(loop, fd, AE_WRITABLE);\n        aeCreateTimeEvent(loop, delay, delay_request, c, NULL);\n        return;\n    }\n\n    if (!c->written) {\n        if (cfg.dynamic) {\n            script_request(thread->L, &c->request, &c->length);\n        }\n        c->start   = time_us();\n        c->pending = cfg.pipeline;\n    }\n\n    char  *buf = c->request + c->written;\n    size_t len = c->length  - c->written;\n    size_t n;\n\n    switch (sock.write(c, buf, len, &n)) {\n        case OK:    break;\n        case ERROR: goto error;\n        case RETRY: return;\n    }\n\n    c->written += n;\n    if (c->written == c->length) {\n        c->written = 0;\n        aeDeleteFileEvent(loop, fd, AE_WRITABLE);\n    }\n\n    return;\n\n  error:\n    thread->errors.write++;\n    reconnect_socket(thread, c);\n}\n\nstatic void socket_readable(aeEventLoop *loop, int fd, void *data, int mask) {\n    connection *c = data;\n    size_t n;\n\n    do {\n        switch (sock.read(c, &n)) {\n            case OK:    break;\n            case ERROR: goto error;\n            case RETRY: return;\n        }\n\n        if (http_parser_execute(&c->parser, &parser_settings, c->buf, n) != n) goto error;\n        if (n == 0 && !http_body_is_final(&c->parser)) goto error;\n\n        c->thread->bytes += n;\n    } while (n == RECVBUF && sock.readable(c) > 0);\n\n    return;\n\n  error:\n    c->thread->errors.read++;\n    reconnect_socket(c->thread, c);\n}\n\nstatic uint64_t time_us() {\n    struct timeval t;\n    gettimeofday(&t, NULL);\n    return (t.tv_sec * 1000000) + t.tv_usec;\n}\n\nstatic char *copy_url_part(char *url, struct http_parser_url *parts, enum http_parser_url_fields field) {\n    char *part = NULL;\n\n    if (parts->field_set & (1 << field)) {\n        uint16_t off = parts->field_data[field].off;\n        uint16_t len = parts->field_data[field].len;\n        part = zcalloc(len + 1 * sizeof(char));\n        memcpy(part, &url[off], len);\n    }\n\n    return part;\n}\n\nstatic struct option longopts[] = {\n    { \"connections\", required_argument, NULL, 'c' },\n    { \"duration\",    required_argument, NULL, 'd' },\n    { \"threads\",     required_argument, NULL, 't' },\n    { \"script\",      required_argument, NULL, 's' },\n    { \"header\",      required_argument, NULL, 'H' },\n    { \"latency\",     no_argument,       NULL, 'L' },\n    { \"timeout\",     required_argument, NULL, 'T' },\n    { \"help\",        no_argument,       NULL, 'h' },\n    { \"version\",     no_argument,       NULL, 'v' },\n    { NULL,          0,                 NULL,  0  }\n};\n\nstatic int parse_args(struct config *cfg, char **url, struct http_parser_url *parts, char **headers, int argc, char **argv) {\n    char **header = headers;\n    int c;\n\n    memset(cfg, 0, sizeof(struct config));\n    cfg->threads     = 2;\n    cfg->connections = 10;\n    cfg->duration    = 10;\n    cfg->timeout     = SOCKET_TIMEOUT_MS;\n\n    while ((c = getopt_long(argc, argv, \"t:c:d:s:H:T:Lrv?\", longopts, NULL)) != -1) {\n        switch (c) {\n            case 't':\n                if (scan_metric(optarg, &cfg->threads)) return -1;\n                break;\n            case 'c':\n                if (scan_metric(optarg, &cfg->connections)) return -1;\n                break;\n            case 'd':\n                if (scan_time(optarg, &cfg->duration)) return -1;\n                break;\n            case 's':\n                cfg->script = optarg;\n                break;\n            case 'H':\n                *header++ = optarg;\n                break;\n            case 'L':\n                cfg->latency = true;\n                break;\n            case 'T':\n                if (scan_time(optarg, &cfg->timeout)) return -1;\n                cfg->timeout *= 1000;\n                break;\n            case 'v':\n                printf(\"wrk %s [%s] \", VERSION, aeGetApiName());\n                printf(\"Copyright (C) 2012 Will Glozer\\n\");\n                break;\n            case 'h':\n            case '?':\n            case ':':\n            default:\n                return -1;\n        }\n    }\n\n    if (optind == argc || !cfg->threads || !cfg->duration) return -1;\n\n    if (!script_parse_url(argv[optind], parts)) {\n        fprintf(stderr, \"invalid URL: %s\\n\", argv[optind]);\n        return -1;\n    }\n\n    if (!cfg->connections || cfg->connections < cfg->threads) {\n        fprintf(stderr, \"number of connections must be >= threads\\n\");\n        return -1;\n    }\n\n    *url    = argv[optind];\n    *header = NULL;\n\n    return 0;\n}\n\nstatic void print_stats_header() {\n    printf(\"  Thread Stats%6s%11s%8s%12s\\n\", \"Avg\", \"Stdev\", \"Max\", \"+/- Stdev\");\n}\n\nstatic void print_units(long double n, char *(*fmt)(long double), int width) {\n    char *msg = fmt(n);\n    int len = strlen(msg), pad = 2;\n\n    if (isalpha(msg[len-1])) pad--;\n    if (isalpha(msg[len-2])) pad--;\n    width -= pad;\n\n    printf(\"%*.*s%.*s\", width, width, msg, pad, \"  \");\n\n    free(msg);\n}\n\nstatic void print_stats(char *name, stats *stats, char *(*fmt)(long double)) {\n    uint64_t max = stats->max;\n    long double mean  = stats_mean(stats);\n    long double stdev = stats_stdev(stats, mean);\n\n    printf(\"    %-10s\", name);\n    print_units(mean,  fmt, 8);\n    print_units(stdev, fmt, 10);\n    print_units(max,   fmt, 9);\n    printf(\"%8.2Lf%%\\n\", stats_within_stdev(stats, mean, stdev, 1));\n}\n\nstatic void print_stats_latency(stats *stats) {\n    long double percentiles[] = { 50.0, 75.0, 90.0, 99.0 };\n    printf(\"  Latency Distribution\\n\");\n    for (size_t i = 0; i < sizeof(percentiles) / sizeof(long double); i++) {\n        long double p = percentiles[i];\n        uint64_t n = stats_percentile(stats, p);\n        printf(\"%7.0Lf%%\", p);\n        print_units(n, format_time_us, 10);\n        printf(\"\\n\");\n    }\n}\n"
  },
  {
    "path": "src/wrk.h",
    "content": "#ifndef WRK_H\n#define WRK_H\n\n#include \"config.h\"\n#include <pthread.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <netdb.h>\n#include <sys/socket.h>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <lua.h>\n\n#include \"stats.h\"\n#include \"ae.h\"\n#include \"http_parser.h\"\n\n#define RECVBUF  8192\n\n#define MAX_THREAD_RATE_S   10000000\n#define SOCKET_TIMEOUT_MS   2000\n#define RECORD_INTERVAL_MS  100\n\nextern const char *VERSION;\n\ntypedef struct {\n    pthread_t thread;\n    aeEventLoop *loop;\n    struct addrinfo *addr;\n    uint64_t connections;\n    uint64_t complete;\n    uint64_t requests;\n    uint64_t bytes;\n    uint64_t start;\n    lua_State *L;\n    errors errors;\n    struct connection *cs;\n} thread;\n\ntypedef struct {\n    char  *buffer;\n    size_t length;\n    char  *cursor;\n} buffer;\n\ntypedef struct connection {\n    thread *thread;\n    http_parser parser;\n    enum {\n        FIELD, VALUE\n    } state;\n    int fd;\n    SSL *ssl;\n    bool delayed;\n    uint64_t start;\n    char *request;\n    size_t length;\n    size_t written;\n    uint64_t pending;\n    buffer headers;\n    buffer body;\n    char buf[RECVBUF];\n} connection;\n\n#endif /* WRK_H */\n"
  },
  {
    "path": "src/wrk.lua",
    "content": "local wrk = {\n   scheme  = \"http\",\n   host    = \"localhost\",\n   port    = nil,\n   method  = \"GET\",\n   path    = \"/\",\n   headers = {},\n   body    = nil,\n   thread  = nil,\n}\n\nfunction wrk.resolve(host, service)\n   local addrs = wrk.lookup(host, service)\n   for i = #addrs, 1, -1 do\n      if not wrk.connect(addrs[i]) then\n         table.remove(addrs, i)\n      end\n   end\n   wrk.addrs = addrs\nend\n\nfunction wrk.setup(thread)\n   thread.addr = wrk.addrs[1]\n   if type(setup) == \"function\" then\n      setup(thread)\n   end\nend\n\nfunction wrk.init(args)\n   if not wrk.headers[\"Host\"] then\n      local host = wrk.host\n      local port = wrk.port\n\n      host = host:find(\":\") and (\"[\" .. host .. \"]\")  or host\n      host = port           and (host .. \":\" .. port) or host\n\n      wrk.headers[\"Host\"] = host\n   end\n\n   if type(init) == \"function\" then\n      init(args)\n   end\n\n   local req = wrk.format()\n   wrk.request = function()\n      return req\n   end\nend\n\nfunction wrk.format(method, path, headers, body)\n   local method  = method  or wrk.method\n   local path    = path    or wrk.path\n   local headers = headers or wrk.headers\n   local body    = body    or wrk.body\n   local s       = {}\n\n   if not headers[\"Host\"] then\n      headers[\"Host\"] = wrk.headers[\"Host\"]\n   end\n\n   headers[\"Content-Length\"] = body and string.len(body)\n\n   s[1] = string.format(\"%s %s HTTP/1.1\", method, path)\n   for name, value in pairs(headers) do\n      s[#s+1] = string.format(\"%s: %s\", name, value)\n   end\n\n   s[#s+1] = \"\"\n   s[#s+1] = body or \"\"\n\n   return table.concat(s, \"\\r\\n\")\nend\n\nreturn wrk\n"
  },
  {
    "path": "src/zmalloc.c",
    "content": "/* zmalloc - total amount of allocated memory aware version of malloc()\n *\n * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n\n/* This function provide us access to the original libc free(). This is useful\n * for instance to free results obtained by backtrace_symbols(). We need\n * to define this function before including zmalloc.h that may shadow the\n * free implementation if we use jemalloc or another non standard allocator. */\nvoid zlibc_free(void *ptr) {\n    free(ptr);\n}\n\n#include <string.h>\n#include <pthread.h>\n#include \"config.h\"\n#include \"zmalloc.h\"\n#include \"atomicvar.h\"\n\n#ifdef HAVE_MALLOC_SIZE\n#define PREFIX_SIZE (0)\n#else\n#if defined(__sun) || defined(__sparc) || defined(__sparc__)\n#define PREFIX_SIZE (sizeof(long long))\n#else\n#define PREFIX_SIZE (sizeof(size_t))\n#endif\n#endif\n\n/* Explicitly override malloc/free etc when using tcmalloc. */\n#if defined(USE_TCMALLOC)\n#define malloc(size) tc_malloc(size)\n#define calloc(count,size) tc_calloc(count,size)\n#define realloc(ptr,size) tc_realloc(ptr,size)\n#define free(ptr) tc_free(ptr)\n#elif defined(USE_JEMALLOC)\n#define malloc(size) je_malloc(size)\n#define calloc(count,size) je_calloc(count,size)\n#define realloc(ptr,size) je_realloc(ptr,size)\n#define free(ptr) je_free(ptr)\n#define mallocx(size,flags) je_mallocx(size,flags)\n#define dallocx(ptr,flags) je_dallocx(ptr,flags)\n#endif\n\n#define update_zmalloc_stat_alloc(__n) do { \\\n    size_t _n = (__n); \\\n    if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \\\n    atomicIncr(used_memory,__n); \\\n} while(0)\n\n#define update_zmalloc_stat_free(__n) do { \\\n    size_t _n = (__n); \\\n    if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \\\n    atomicDecr(used_memory,__n); \\\n} while(0)\n\nstatic size_t used_memory = 0;\npthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;\n\nstatic void zmalloc_default_oom(size_t size) {\n    fprintf(stderr, \"zmalloc: Out of memory trying to allocate %zu bytes\\n\",\n        size);\n    fflush(stderr);\n    abort();\n}\n\nstatic void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;\n\nvoid *zmalloc(size_t size) {\n    void *ptr = malloc(size+PREFIX_SIZE);\n\n    if (!ptr) zmalloc_oom_handler(size);\n#ifdef HAVE_MALLOC_SIZE\n    update_zmalloc_stat_alloc(zmalloc_size(ptr));\n    return ptr;\n#else\n    *((size_t*)ptr) = size;\n    update_zmalloc_stat_alloc(size+PREFIX_SIZE);\n    return (char*)ptr+PREFIX_SIZE;\n#endif\n}\n\n/* Allocation and free functions that bypass the thread cache\n * and go straight to the allocator arena bins.\n * Currently implemented only for jemalloc. Used for online defragmentation. */\n#ifdef HAVE_DEFRAG\nvoid *zmalloc_no_tcache(size_t size) {\n    void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);\n    if (!ptr) zmalloc_oom_handler(size);\n    update_zmalloc_stat_alloc(zmalloc_size(ptr));\n    return ptr;\n}\n\nvoid zfree_no_tcache(void *ptr) {\n    if (ptr == NULL) return;\n    update_zmalloc_stat_free(zmalloc_size(ptr));\n    dallocx(ptr, MALLOCX_TCACHE_NONE);\n}\n#endif\n\nvoid *zcalloc(size_t size) {\n    void *ptr = calloc(1, size+PREFIX_SIZE);\n\n    if (!ptr) zmalloc_oom_handler(size);\n#ifdef HAVE_MALLOC_SIZE\n    update_zmalloc_stat_alloc(zmalloc_size(ptr));\n    return ptr;\n#else\n    *((size_t*)ptr) = size;\n    update_zmalloc_stat_alloc(size+PREFIX_SIZE);\n    return (char*)ptr+PREFIX_SIZE;\n#endif\n}\n\nvoid *zrealloc(void *ptr, size_t size) {\n#ifndef HAVE_MALLOC_SIZE\n    void *realptr;\n#endif\n    size_t oldsize;\n    void *newptr;\n\n    if (ptr == NULL) return zmalloc(size);\n#ifdef HAVE_MALLOC_SIZE\n    oldsize = zmalloc_size(ptr);\n    newptr = realloc(ptr,size);\n    if (!newptr) zmalloc_oom_handler(size);\n\n    update_zmalloc_stat_free(oldsize);\n    update_zmalloc_stat_alloc(zmalloc_size(newptr));\n    return newptr;\n#else\n    realptr = (char*)ptr-PREFIX_SIZE;\n    oldsize = *((size_t*)realptr);\n    newptr = realloc(realptr,size+PREFIX_SIZE);\n    if (!newptr) zmalloc_oom_handler(size);\n\n    *((size_t*)newptr) = size;\n    update_zmalloc_stat_free(oldsize);\n    update_zmalloc_stat_alloc(size);\n    return (char*)newptr+PREFIX_SIZE;\n#endif\n}\n\n/* Provide zmalloc_size() for systems where this function is not provided by\n * malloc itself, given that in that case we store a header with this\n * information as the first bytes of every allocation. */\n#ifndef HAVE_MALLOC_SIZE\nsize_t zmalloc_size(void *ptr) {\n    void *realptr = (char*)ptr-PREFIX_SIZE;\n    size_t size = *((size_t*)realptr);\n    /* Assume at least that all the allocations are padded at sizeof(long) by\n     * the underlying allocator. */\n    if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));\n    return size+PREFIX_SIZE;\n}\n#endif\n\nvoid zfree(void *ptr) {\n#ifndef HAVE_MALLOC_SIZE\n    void *realptr;\n    size_t oldsize;\n#endif\n\n    if (ptr == NULL) return;\n#ifdef HAVE_MALLOC_SIZE\n    update_zmalloc_stat_free(zmalloc_size(ptr));\n    free(ptr);\n#else\n    realptr = (char*)ptr-PREFIX_SIZE;\n    oldsize = *((size_t*)realptr);\n    update_zmalloc_stat_free(oldsize+PREFIX_SIZE);\n    free(realptr);\n#endif\n}\n\nchar *zstrdup(const char *s) {\n    size_t l = strlen(s)+1;\n    char *p = zmalloc(l);\n\n    memcpy(p,s,l);\n    return p;\n}\n\nsize_t zmalloc_used_memory(void) {\n    size_t um;\n    atomicGet(used_memory,um);\n    return um;\n}\n\nvoid zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {\n    zmalloc_oom_handler = oom_handler;\n}\n\n/* Get the RSS information in an OS-specific way.\n *\n * WARNING: the function zmalloc_get_rss() is not designed to be fast\n * and may not be called in the busy loops where Redis tries to release\n * memory expiring or swapping out objects.\n *\n * For this kind of \"fast RSS reporting\" usages use instead the\n * function RedisEstimateRSS() that is a much faster (and less precise)\n * version of the function. */\n\n#if defined(HAVE_PROC_STAT)\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\nsize_t zmalloc_get_rss(void) {\n    int page = sysconf(_SC_PAGESIZE);\n    size_t rss;\n    char buf[4096];\n    char filename[256];\n    int fd, count;\n    char *p, *x;\n\n    snprintf(filename,256,\"/proc/%d/stat\",getpid());\n    if ((fd = open(filename,O_RDONLY)) == -1) return 0;\n    if (read(fd,buf,4096) <= 0) {\n        close(fd);\n        return 0;\n    }\n    close(fd);\n\n    p = buf;\n    count = 23; /* RSS is the 24th field in /proc/<pid>/stat */\n    while(p && count--) {\n        p = strchr(p,' ');\n        if (p) p++;\n    }\n    if (!p) return 0;\n    x = strchr(p,' ');\n    if (!x) return 0;\n    *x = '\\0';\n\n    rss = strtoll(p,NULL,10);\n    rss *= page;\n    return rss;\n}\n#elif defined(HAVE_TASKINFO)\n#include <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n#include <mach/task.h>\n#include <mach/mach_init.h>\n\nsize_t zmalloc_get_rss(void) {\n    task_t task = MACH_PORT_NULL;\n    struct task_basic_info t_info;\n    mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;\n\n    if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)\n        return 0;\n    task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);\n\n    return t_info.resident_size;\n}\n#else\nsize_t zmalloc_get_rss(void) {\n    /* If we can't get the RSS in an OS-specific way for this system just\n     * return the memory usage we estimated in zmalloc()..\n     *\n     * Fragmentation will appear to be always 1 (no fragmentation)\n     * of course... */\n    return zmalloc_used_memory();\n}\n#endif\n\n/* Fragmentation = RSS / allocated-bytes */\nfloat zmalloc_get_fragmentation_ratio(size_t rss) {\n    return (float)rss/zmalloc_used_memory();\n}\n\n/* Get the sum of the specified field (converted form kb to bytes) in\n * /proc/self/smaps. The field must be specified with trailing \":\" as it\n * apperas in the smaps output.\n *\n * If a pid is specified, the information is extracted for such a pid,\n * otherwise if pid is -1 the information is reported is about the\n * current process.\n *\n * Example: zmalloc_get_smap_bytes_by_field(\"Rss:\",-1);\n */\n#if defined(HAVE_PROC_SMAPS)\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {\n    char line[1024];\n    size_t bytes = 0;\n    int flen = strlen(field);\n    FILE *fp;\n\n    if (pid == -1) {\n        fp = fopen(\"/proc/self/smaps\",\"r\");\n    } else {\n        char filename[128];\n        snprintf(filename,sizeof(filename),\"/proc/%ld/smaps\",pid);\n        fp = fopen(filename,\"r\");\n    }\n\n    if (!fp) return 0;\n    while(fgets(line,sizeof(line),fp) != NULL) {\n        if (strncmp(line,field,flen) == 0) {\n            char *p = strchr(line,'k');\n            if (p) {\n                *p = '\\0';\n                bytes += strtol(line+flen,NULL,10) * 1024;\n            }\n        }\n    }\n    fclose(fp);\n    return bytes;\n}\n#else\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {\n    ((void) field);\n    ((void) pid);\n    return 0;\n}\n#endif\n\nsize_t zmalloc_get_private_dirty(long pid) {\n    return zmalloc_get_smap_bytes_by_field(\"Private_Dirty:\",pid);\n}\n\n/* Returns the size of physical memory (RAM) in bytes.\n * It looks ugly, but this is the cleanest way to achive cross platform results.\n * Cleaned up from:\n *\n * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system\n *\n * Note that this function:\n * 1) Was released under the following CC attribution license:\n *    http://creativecommons.org/licenses/by/3.0/deed.en_US.\n * 2) Was originally implemented by David Robert Nadeau.\n * 3) Was modified for Redis by Matt Stancliff.\n * 4) This note exists in order to comply with the original license.\n */\nsize_t zmalloc_get_memory_size(void) {\n#if defined(__unix__) || defined(__unix) || defined(unix) || \\\n    (defined(__APPLE__) && defined(__MACH__))\n#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_MEMSIZE)\n    mib[1] = HW_MEMSIZE;            /* OSX. --------------------- */\n#elif defined(HW_PHYSMEM64)\n    mib[1] = HW_PHYSMEM64;          /* NetBSD, OpenBSD. --------- */\n#endif\n    int64_t size = 0;               /* 64-bit */\n    size_t len = sizeof(size);\n    if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n\n#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)\n    /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */\n    return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);\n\n#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))\n    /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */\n    int mib[2];\n    mib[0] = CTL_HW;\n#if defined(HW_REALMEM)\n    mib[1] = HW_REALMEM;        /* FreeBSD. ----------------- */\n#elif defined(HW_PYSMEM)\n    mib[1] = HW_PHYSMEM;        /* Others. ------------------ */\n#endif\n    unsigned int size = 0;      /* 32-bit */\n    size_t len = sizeof(size);\n    if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)\n        return (size_t)size;\n    return 0L;          /* Failed? */\n#else\n    return 0L;          /* Unknown method to get the data. */\n#endif\n#else\n    return 0L;          /* Unknown OS. */\n#endif\n}\n\n\n"
  },
  {
    "path": "src/zmalloc.h",
    "content": "/* zmalloc - total amount of allocated memory aware version of malloc()\n *\n * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __ZMALLOC_H\n#define __ZMALLOC_H\n\n/* Double expansion needed for stringification of macro values. */\n#define __xstr(s) __str(s)\n#define __str(s) #s\n\n#if defined(USE_TCMALLOC)\n#define ZMALLOC_LIB (\"tcmalloc-\" __xstr(TC_VERSION_MAJOR) \".\" __xstr(TC_VERSION_MINOR))\n#include <google/tcmalloc.h>\n#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)\n#define HAVE_MALLOC_SIZE 1\n#define zmalloc_size(p) tc_malloc_size(p)\n#else\n#error \"Newer version of tcmalloc required\"\n#endif\n\n#elif defined(USE_JEMALLOC)\n#define ZMALLOC_LIB (\"jemalloc-\" __xstr(JEMALLOC_VERSION_MAJOR) \".\" __xstr(JEMALLOC_VERSION_MINOR) \".\" __xstr(JEMALLOC_VERSION_BUGFIX))\n#include <jemalloc/jemalloc.h>\n#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)\n#define HAVE_MALLOC_SIZE 1\n#define zmalloc_size(p) je_malloc_usable_size(p)\n#else\n#error \"Newer version of jemalloc required\"\n#endif\n\n#elif defined(__APPLE__)\n#include <malloc/malloc.h>\n#define HAVE_MALLOC_SIZE 1\n#define zmalloc_size(p) malloc_size(p)\n#endif\n\n#ifndef ZMALLOC_LIB\n#define ZMALLOC_LIB \"libc\"\n#endif\n\n/* We can enable the Redis defrag capabilities only if we are using Jemalloc\n * and the version used is our special version modified for Redis having\n * the ability to return per-allocation fragmentation hints. */\n#if defined(USE_JEMALLOC) && defined(JEMALLOC_FRAG_HINT)\n#define HAVE_DEFRAG\n#endif\n\nvoid *zmalloc(size_t size);\nvoid *zcalloc(size_t size);\nvoid *zrealloc(void *ptr, size_t size);\nvoid zfree(void *ptr);\nchar *zstrdup(const char *s);\nsize_t zmalloc_used_memory(void);\nvoid zmalloc_set_oom_handler(void (*oom_handler)(size_t));\nfloat zmalloc_get_fragmentation_ratio(size_t rss);\nsize_t zmalloc_get_rss(void);\nsize_t zmalloc_get_private_dirty(long pid);\nsize_t zmalloc_get_smap_bytes_by_field(char *field, long pid);\nsize_t zmalloc_get_memory_size(void);\nvoid zlibc_free(void *ptr);\n\n#ifdef HAVE_DEFRAG\nvoid zfree_no_tcache(void *ptr);\nvoid *zmalloc_no_tcache(size_t size);\n#endif\n\n#ifndef HAVE_MALLOC_SIZE\nsize_t zmalloc_size(void *ptr);\n#endif\n\n#endif /* __ZMALLOC_H */\n"
  }
]