├── .flake8 ├── .github └── workflows │ ├── ci-alpine.yml │ ├── ci-linux.yml │ └── ci-macos.yml ├── .gitignore ├── COPYING ├── contrib ├── angel.conf ├── core.lua ├── core__cached_html.lua ├── core__xsendfile.lua ├── create-mimetypes.conf.pl ├── default.html ├── lighttpd.conf ├── meson.build ├── mimetypes.conf ├── secdownload.lua ├── secdownload__secdownload.lua ├── service │ ├── log │ │ └── run │ └── run └── systemd │ └── lighttpd2.service ├── diff-dist-git.sh ├── doc ├── .gitignore ├── bootstrap-theme.min.css ├── bootstrap.min.css ├── bootstrap.min.js ├── compile.rb ├── core_config.xml ├── core_config_angel.xml ├── core_fetch.xml ├── core_introduction.xml ├── core_lua.xml ├── core_pattern.xml ├── core_regex.xml ├── doc_schema.xsd ├── jquery-1.10.1.min.js ├── lighttpd2-worker.8 ├── lighttpd2.8 ├── meson.build ├── mod_access.xml ├── mod_accesslog.xml ├── mod_auth.xml ├── mod_balance.xml ├── mod_cache_disk_etag.xml ├── mod_core.lua.xml ├── mod_debug.xml ├── mod_deflate.xml ├── mod_dirlist.xml ├── mod_expire.xml ├── mod_fastcgi.xml ├── mod_flv.xml ├── mod_fortune.xml ├── mod_gnutls.xml ├── mod_limit.xml ├── mod_lua.xml ├── mod_memcached.xml ├── mod_openssl.xml ├── mod_progress.xml ├── mod_proxy.xml ├── mod_redirect.xml ├── mod_rewrite.xml ├── mod_scgi.xml ├── mod_secdownload.lua.xml ├── mod_status.xml ├── mod_throttle.xml ├── mod_userdir.xml ├── mod_vhost.xml ├── plugin_core.xml └── style.css ├── include ├── lighttpd │ ├── actions.h │ ├── actions_lua.h │ ├── angel.h │ ├── angel_base.h │ ├── angel_config_parser.h │ ├── angel_connection.h │ ├── angel_data.h │ ├── angel_log.h │ ├── angel_plugin.h │ ├── angel_plugin_core.h │ ├── angel_proc.h │ ├── angel_server.h │ ├── angel_typedefs.h │ ├── backends.h │ ├── base.h │ ├── base_lua.h │ ├── buffer.h │ ├── chunk.h │ ├── chunk_parser.h │ ├── collect.h │ ├── condition.h │ ├── condition_lua.h │ ├── config_lua.h │ ├── config_parser.h │ ├── connection.h │ ├── core_lua.h │ ├── encoding.h │ ├── environment.h │ ├── etag.h │ ├── events.h │ ├── fetch.h │ ├── filter.h │ ├── filter_buffer_on_disk.h │ ├── filter_chunked.h │ ├── hedley.h │ ├── http_headers.h │ ├── http_range_parser.h │ ├── http_request_parser.h │ ├── http_response_parser.h │ ├── idlist.h │ ├── ip_parsers.h │ ├── jobqueue.h │ ├── lighttpd-glue.h │ ├── log.h │ ├── memcached.h │ ├── mempool.h │ ├── meson.build │ ├── mimetype.h │ ├── module.h │ ├── network.h │ ├── options.h │ ├── pattern.h │ ├── plugin.h │ ├── plugin_core.h │ ├── profiler.h │ ├── radix.h │ ├── request.h │ ├── response.h │ ├── server.h │ ├── settings.h │ ├── stat_cache.h │ ├── stream.h │ ├── stream_http_response.h │ ├── sys_memory.h │ ├── sys_socket.h │ ├── tasklet.h │ ├── throttle.h │ ├── typedefs.h │ ├── url_parser.h │ ├── utils.h │ ├── value.h │ ├── value_lua.h │ ├── version.h │ ├── virtualrequest.h │ ├── waitqueue.h │ └── worker.h └── meson.build ├── meson.build ├── meson_options.txt ├── packdist.sh ├── pyproject.toml ├── src ├── angel │ ├── angel_config_parser.rl │ ├── angel_log.c │ ├── angel_main.c │ ├── angel_plugin.c │ ├── angel_plugin_core.c │ ├── angel_proc.c │ ├── angel_server.c │ ├── angel_value.c │ └── meson.build ├── common │ ├── angel_connection.c │ ├── angel_data.c │ ├── buffer.c │ ├── encoding.c │ ├── events.c │ ├── fetch.c │ ├── idlist.c │ ├── ip_parsers.rl │ ├── jobqueue.c │ ├── memcached.c │ ├── mempool.c │ ├── meson.build │ ├── module.c │ ├── profiler.c │ ├── radix.c │ ├── sys_memory.c │ ├── sys_socket.c │ ├── tasklet.c │ ├── utils.c │ ├── value.c │ ├── value_impl.c │ └── waitqueue.c ├── lighttpd2.pc.in ├── main │ ├── actions.c │ ├── actions_lua.c │ ├── angel.c │ ├── angel_fake.c │ ├── backends.c │ ├── base_lua.c │ ├── chunk.c │ ├── chunk_lua.c │ ├── chunk_parser.c │ ├── collect.c │ ├── condition.c │ ├── condition_lua.c │ ├── config_lua.c │ ├── config_parser.rl │ ├── connection.c │ ├── core_lua.c │ ├── environment.c │ ├── environment_lua.c │ ├── etag.c │ ├── filter.c │ ├── filter_buffer_on_disk.c │ ├── filter_chunked.c │ ├── filters_lua.c │ ├── http_headers.c │ ├── http_headers_lua.c │ ├── http_range_parser.rl │ ├── http_request_parser.rl │ ├── http_response_parser.rl │ ├── lighttpd_glue.c │ ├── lighttpd_worker.c │ ├── log.c │ ├── meson.build │ ├── mimetype.c │ ├── network.c │ ├── network_sendfile.c │ ├── network_write.c │ ├── network_writev.c │ ├── options.c │ ├── pattern.c │ ├── physical_lua.c │ ├── plugin.c │ ├── plugin_core.c │ ├── request.c │ ├── request_lua.c │ ├── response.c │ ├── response_lua.c │ ├── server.c │ ├── stat_cache.c │ ├── stat_lua.c │ ├── stream.c │ ├── stream_http_response.c │ ├── stream_simple_socket.c │ ├── subrequest_lua.c │ ├── throttle.c │ ├── url_parser.rl │ ├── value.c │ ├── value_lua.c │ ├── virtualrequest.c │ ├── virtualrequest_lua.c │ └── worker.c ├── meson.build ├── modules │ ├── fastcgi_stream.c │ ├── fastcgi_stream.h │ ├── gnutls_filter.c │ ├── gnutls_filter.h │ ├── gnutls_ocsp.c │ ├── gnutls_ocsp.h │ ├── meson.build │ ├── mod_access.c │ ├── mod_accesslog.c │ ├── mod_auth.c │ ├── mod_balance.c │ ├── mod_cache_disk_etag.c │ ├── mod_debug.c │ ├── mod_deflate.c │ ├── mod_dirlist.c │ ├── mod_expire.c │ ├── mod_fastcgi.c │ ├── mod_flv.c │ ├── mod_fortune.c │ ├── mod_gnutls.c │ ├── mod_limit.c │ ├── mod_lua.c │ ├── mod_memcached.c │ ├── mod_openssl.c │ ├── mod_progress.c │ ├── mod_proxy.c │ ├── mod_redirect.c │ ├── mod_rewrite.c │ ├── mod_scgi.c │ ├── mod_status.c │ ├── mod_throttle.c │ ├── mod_userdir.c │ ├── mod_vhost.c │ ├── openssl_filter.c │ ├── openssl_filter.h │ ├── ssl-session-db.h │ └── ssl_client_hello_parser.h └── unittests │ ├── meson.build │ ├── test-chunk.c │ ├── test-http-request-parser.c │ ├── test-ip-parser.c │ ├── test-radix.c │ ├── test-range-parser.c │ └── test-utils.c └── tests ├── .gitignore ├── CONTRIBUTE.md ├── autowrapper.sh ├── ca ├── ca.crt ├── ca.key ├── ca.pub ├── ca.template ├── client1.crt ├── client1.key ├── client1.pub ├── client1.template ├── client_ca.crt ├── client_ca.key ├── client_ca.pub ├── client_ca.template ├── createca.sh ├── intermediate.crt ├── intermediate.key ├── intermediate.pub ├── intermediate.template ├── server.key ├── server.pub ├── server_test1.ssl.crt ├── server_test1.ssl.pem ├── server_test1.ssl.template ├── server_test2.ssl.crt ├── server_test2.ssl.pem └── server_test2.ssl.template ├── meson.build ├── pylt ├── __init__.py ├── base.py ├── fastcgi.py ├── logfile.py ├── requests.py ├── run.py ├── service.py └── tests │ ├── __init__.py │ ├── t-alias.py │ ├── t-basic-docroot.py │ ├── t-basic-gets.py │ ├── t-cgi.py │ ├── t-deflate.py │ ├── t-dirlist.py │ ├── t-env-set.py │ ├── t-etag.py │ ├── t-gnutls.py │ ├── t-header-modify.py │ ├── t-lua.py │ ├── t-map-cidr.py │ ├── t-memcached.py │ ├── t-mime-type.py │ ├── t-mod-auth.py │ ├── t-mod-lua.py │ ├── t-mod-proxy.py │ ├── t-openssl.py │ ├── t-redirect.py │ ├── t-rewrite.py │ ├── t-scgi.py │ └── t-secdownload.py ├── run-fcgi-cgi.py ├── run-memcached.py ├── run-scgi-envcheck.py └── runtests.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | # E266 too many leading '#' for block comment [ I like marking disabled code blocks with '### ' ] 3 | # E402 module level import not at top of file [ usually on purpose. might use individual overrides instead? ] 4 | # E713 test for membership should be ‘not in’ [ disagree: want `not a in x` ] 5 | # E714 test for object identity should be 'is not' [ disagree: want `not a is x` ] 6 | # W503 line break before binary operator [ gotta pick one way ] 7 | extend-ignore = E266,E402,E713,E714,W503 8 | max-line-length = 120 9 | -------------------------------------------------------------------------------- /.github/workflows/ci-alpine.yml: -------------------------------------------------------------------------------- 1 | name: "Checks (alpine, many platforms)" 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.head_ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | linux-alpine: 11 | runs-on: ubuntu-latest 12 | name: linux-alpine-${{ matrix.platform }} 13 | # abort if x86_64 fails 14 | continue-on-error: ${{ matrix.platform != 'x86_64' }} 15 | strategy: 16 | fail-fast: true 17 | matrix: 18 | platform: ['x86_64','x86','armhf','armv7','aarch64','ppc64le','riscv64','s390x'] 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: jirutka/setup-alpine@v1 22 | with: 23 | # riscv64 currently requires 'edge' 24 | branch: edge 25 | arch: ${{ matrix.platform }} 26 | packages: > 27 | build-base 28 | meson 29 | libev-dev 30 | ragel 31 | glib-dev 32 | lua5.1-dev 33 | zlib-dev 34 | bzip2-dev 35 | pkgconf 36 | openssl-dev 37 | gnutls-dev 38 | libidn-dev 39 | libunwind-dev 40 | python3 41 | py3-curl 42 | - name: meson setup 43 | shell: alpine.sh {0} 44 | run: meson setup mesonbuilddir 45 | - name: meson compile 46 | shell: alpine.sh {0} 47 | run: meson compile -C mesonbuilddir 48 | - name: meson test 49 | shell: alpine.sh {0} 50 | run: meson test -C mesonbuilddir -v 51 | -------------------------------------------------------------------------------- /.github/workflows/ci-linux.yml: -------------------------------------------------------------------------------- 1 | name: "Checks (Ubuntu: gcc+clang)" 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{github.workflow}}-${{github.head_ref}} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | linux-build-docs: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Install dependencies 15 | run: | 16 | pkgs=( 17 | ruby 18 | ruby-nokogiri 19 | ruby-kramdown 20 | libxml2-utils 21 | ) 22 | sudo apt-get install "${pkgs[@]}" 23 | - name: Build docs 24 | run: | 25 | mkdir -p out 26 | ruby doc/compile.rb out 27 | cp doc/*.css doc/*.js out 28 | - uses: actions/upload-artifact@v4 29 | with: 30 | name: lighttpd2-docs 31 | path: out 32 | 33 | linux-ubuntu: 34 | runs-on: ubuntu-latest 35 | name: linux-ubuntu-${{ matrix.compiler }} 36 | strategy: 37 | matrix: 38 | compiler: ['gcc', 'clang'] 39 | steps: 40 | - uses: actions/checkout@v4 41 | - if: ${{ matrix.compiler == 'clang' }} 42 | uses: egor-tensin/setup-clang@v1 43 | - name: Install dependencies 44 | run: | 45 | pkgs=( 46 | meson 47 | libev-dev 48 | ragel 49 | libglib2.0-dev 50 | liblua5.1-dev 51 | zlib1g-dev 52 | libbz2-dev 53 | pkg-config 54 | libssl-dev 55 | libgnutls28-dev 56 | libidn-dev 57 | libunwind8-dev 58 | python3 59 | python3-pycurl 60 | ) 61 | sudo apt-get install "${pkgs[@]}" 62 | - name: meson setup 63 | run: meson setup mesonbuilddir 64 | - name: meson compile 65 | run: meson compile -C mesonbuilddir 66 | - name: meson test 67 | run: meson test -C mesonbuilddir -v 68 | -------------------------------------------------------------------------------- /.github/workflows/ci-macos.yml: -------------------------------------------------------------------------------- 1 | name: "Checks (macOS)" 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{github.workflow}}-${{github.head_ref}} 7 | cancel-in-progress: true 8 | 9 | env: 10 | C_INCLUDE_PATH: /opt/homebrew/opt/libev/include 11 | LIBRARY_PATH: /opt/homebrew/opt/libev/lib 12 | 13 | jobs: 14 | macOS: 15 | runs-on: macos-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Install dependencies 19 | run: | 20 | pkgs=( 21 | meson 22 | libev 23 | ragel 24 | glib 25 | lua@5.4 26 | # zlib # keg-only 27 | # bzip2 # keg-only 28 | openssl@3 29 | gnutls 30 | libidn 31 | python3 32 | md5sha1sum 33 | ) 34 | brew install "${pkgs[@]}" 35 | - name: python venv setup 36 | run: | 37 | python3 -m venv venv 38 | venv/bin/pip3 install pycurl 39 | venv/bin/python3 -c 'import pycurl' 40 | - name: meson setup 41 | run: meson setup -D unwind=false mesonbuilddir 42 | - name: meson compile 43 | run: meson compile -C mesonbuilddir 44 | - name: prepare environment for tests 45 | run: | 46 | sudo ifconfig lo0 alias 127.0.0.2 up 47 | 48 | # try to create a tmpdir with a short relative path (for shorter unix socket paths) 49 | NEWTMPDIR=~/tmp 50 | ln -sf "${TMPDIR}" "${NEWTMPDIR}" 51 | echo "TMPDIR=$NEWTMPDIR" >> "$GITHUB_ENV" 52 | echo "PATH=$(brew --prefix python)/libexec/bin:$PATH" >> "$GITHUB_ENV" 53 | 54 | if [ ! -f $(brew --prefix python)/libexec/bin/python3 ]; then 55 | # the brew path only provides "python", not "python3"... 56 | ln -s python $(brew --prefix python)/libexec/bin/python3 57 | fi 58 | - name: meson test 59 | run: | 60 | source venv/bin/activate 61 | meson test -C mesonbuilddir -v 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *build/ 2 | .vscode 3 | .mypy_cache 4 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | The MIT License 3 | 4 | Copyright (c) 2004-2008 Jan Kneschke 5 | Copyright (c) 2008-2010 Stefan Bühler 6 | Copyright (c) 2008-2010 Thomas Porzelt 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | 28 | 29 | The doc/ directory contains some assets by 3rd parties: 30 | From doc/bootstrap* (Apache License 2.0): 31 | Copyright 2013 Twitter, Inc. 32 | Licensed under https://www.apache.org/licenses/LICENSE-2.0 33 | From doc/jquery-1.10.1.min.js (MIT License): 34 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license 35 | 36 | Also include/lighttpd/hedley.h is licensed as CC0-1.0 by Evan Nemerson. 37 | -------------------------------------------------------------------------------- /contrib/angel.conf: -------------------------------------------------------------------------------- 1 | user "www-data"; 2 | max_open_files 16384; 3 | 4 | copy_env [ "PATH" ]; 5 | # env [ "G_SLICE=always-malloc", "G_DEBUG=gc-friendly,fatal_criticals" ]; 6 | # wrapper [ "/usr/bin/valgrind", "--leak-check=full", "--show-reachable=yes", "--leak-resolution=high" ]; 7 | 8 | # need separate statements for IPv4 and IPv6. if none are configured, allow 9 | # TCP port 80 and 443 on all IPv4 and IPv6. 10 | # no port means 80 and 443 are allowed: 11 | # allow_listen "0.0.0.0/0"; 12 | # allow_listen "[::/0]"; 13 | 14 | # allow_listen "0.0.0.0/0:8080"; 15 | # allow_listen "[::/0]:8080"; 16 | -------------------------------------------------------------------------------- /contrib/core__cached_html.lua: -------------------------------------------------------------------------------- 1 | local function try_cached_html(vr) 2 | local p = vr.phys.path 3 | if p:sub(-1) == '/' then 4 | p = p:sub(0, -2) .. '.html' 5 | else 6 | p = p .. '.html' 7 | end 8 | st, res = vr:stat(p) 9 | if st and st.is_file then 10 | -- found the file 11 | vr.phys.path = p 12 | elseif res == lighty.HANDLER_WAIT_FOR_EVENT then 13 | return lighty.HANDLER_WAIT_FOR_EVENT 14 | end 15 | -- ignore other errors 16 | end 17 | 18 | actions = try_cached_html 19 | -------------------------------------------------------------------------------- /contrib/core__xsendfile.lua: -------------------------------------------------------------------------------- 1 | 2 | local filename, args = ... 3 | 4 | local docroot = args 5 | 6 | -- create new class XFilterDrop 7 | local XFilterDrop = { } 8 | XFilterDrop.__index = XFilterDrop 9 | 10 | -- "classmethod" to create new instance 11 | function XFilterDrop:new(vr) 12 | -- vr:debug("New XSendfile instance") 13 | local o = { } 14 | setmetatable(o, self) 15 | return o 16 | end 17 | 18 | -- normal method to handle content 19 | function XFilterDrop:handle(vr, outq, inq) 20 | -- drop input, close it 21 | if nil ~= inq then 22 | inq.is_closed = true 23 | inq:skip_all() 24 | end 25 | return lighty.HANDLER_GO_ON 26 | end 27 | 28 | -- create a new filter which drops all input and adds it to the vrequest 29 | -- returns the filter object so you can insert your own content in f.out (it is already closed) 30 | local function add_drop_filter(vr) 31 | local f = vr:add_filter_out(XFilterDrop:new()) 32 | local inq = f['in'] 33 | if nil ~= inq then 34 | inq.is_closed = true 35 | inq:skip_all() 36 | end 37 | f.out.is_closed = true 38 | return f 39 | end 40 | 41 | local function handle_x_sendfile(vr) 42 | -- vr:debug("handle x-sendfile") 43 | -- wait for response 44 | if not vr.has_response then 45 | if vr.is_handled then 46 | -- vr:debug("x-sendfile: waiting for response headers") 47 | return lighty.HANDLER_WAIT_FOR_EVENT 48 | else 49 | -- vr:debug("No response handler yet, cannot handle X-Sendfile") 50 | return lighty.HANDLER_GO_ON 51 | end 52 | end 53 | -- vr:debug("handle x-sendfile: headers available") 54 | -- add filter if x-sendfile header is not empty 55 | local xs = vr.resp.headers["X-Sendfile"] 56 | if xs and xs ~= "" then 57 | xs = lighty.path_simplify(xs) 58 | if docroot and xs:sub(docroot.len()) ~= docroot then 59 | vr:error("x-sendfile: File '".. xs .. "'not in required docroot '" .. docroot .. "'") 60 | return lighty.HANDLER_GO_ON 61 | end 62 | 63 | -- make sure to drop all other content from the backend 64 | local f = add_drop_filter(vr) 65 | 66 | vr.resp.headers["X-Sendfile"] = nil -- remove header from response 67 | 68 | -- Add checks for the pathname here 69 | 70 | vr:debug("XSendfile:handle: pushing file '" .. xs .. "' as content") 71 | f.out:add_file(xs) 72 | end 73 | end 74 | 75 | actions = handle_x_sendfile 76 | -------------------------------------------------------------------------------- /contrib/lighttpd.conf: -------------------------------------------------------------------------------- 1 | 2 | setup { 3 | 4 | module_load [ 5 | "mod_accesslog", 6 | "mod_dirlist" 7 | ]; 8 | 9 | listen "0.0.0.0:80"; 10 | listen "[::]:80"; 11 | 12 | log ["debug" => "", default => "/var/log/lighttpd2/error.log"]; 13 | accesslog "/var/log/lighttpd2/access.log"; 14 | accesslog.format "%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""; 15 | 16 | static.exclude_extensions [ ".php", ".pl", ".fcgi", "~", ".inc" ]; 17 | 18 | } 19 | 20 | include "/etc/lighttpd2/mimetypes.conf"; 21 | 22 | docroot "/var/www"; 23 | index [ "index.php", "index.html", "index.htm", "default.htm", "index.lighttpd.html" ]; 24 | dirlist; 25 | static; 26 | -------------------------------------------------------------------------------- /contrib/meson.build: -------------------------------------------------------------------------------- 1 | if get_option('lua') 2 | install_data( 3 | 'core__cached_html.lua', 4 | 'core.lua', 5 | 'core__xsendfile.lua', 6 | 'secdownload.lua', 7 | 'secdownload__secdownload.lua', 8 | install_dir: lua_dir, 9 | install_tag: 'runtime', 10 | ) 11 | endif 12 | -------------------------------------------------------------------------------- /contrib/secdownload.lua: -------------------------------------------------------------------------------- 1 | 2 | -- secdownload.lua 3 | 4 | -- usage: 5 | -- a) load mod_lua and this plugin: 6 | -- setup { 7 | -- module_load "mod_lua"; 8 | -- lua.plugin "/path/secdownload.lua"; 9 | -- } 10 | -- b) activate it anywhere you want: 11 | -- secdownload [ "prefix" => "/sec/", "document-root" => "/secret/path", "secret" => "abc", "timeout" => 600 ]; 12 | 13 | local filename, args = ... 14 | 15 | -- basepath for loading sub handlers with lua.handler 16 | -- this allows us to have lua actions that don't use the global lock 17 | local basepath = filename:gsub("(.*/)(.*)", "%1") 18 | 19 | local function secdownload(options) 20 | if type(options) ~= "table" then 21 | lighty.error("secdownload expects a hashtable as parameter") 22 | return nil 23 | end 24 | 25 | local uri_prefix = options["prefix"] 26 | local doc_root = options["document-root"] 27 | local secret = options["secret"] 28 | local timeout = tonumber(options["timeout"]) or 60 29 | 30 | if secret == nil then 31 | lighty.error("secdownload: need secret in options") 32 | return nil 33 | end 34 | 35 | if doc_root == nil then 36 | lighty.error("secdownload: need doc_root in options") 37 | return nil 38 | end 39 | 40 | if doc_root:sub(-1) ~= "/" then 41 | doc_root = doc_root .. "/" 42 | end 43 | 44 | if uri_prefix == nil then 45 | uri_prefix = "/" 46 | end 47 | 48 | local args = { ["prefix"] = uri_prefix, ["document-root"] = doc_root, ["secret"] = secret, ["timeout"] = timeout } 49 | 50 | local handle_secdownload = action.lua.handler(basepath .. 'secdownload__secdownload.lua', nil, args) 51 | 52 | return handle_secdownload 53 | end 54 | 55 | actions = { 56 | ["secdownload"] = secdownload 57 | } 58 | -------------------------------------------------------------------------------- /contrib/secdownload__secdownload.lua: -------------------------------------------------------------------------------- 1 | 2 | local filename, args = ... 3 | 4 | local uri_prefix = args["prefix"] 5 | local doc_root = args["document-root"] 6 | local secret = args["secret"] 7 | local timeout = tonumber(args["timeout"]) 8 | 9 | local function deny_access(vr, status) 10 | vr:handle_direct() 11 | vr.resp.status = status 12 | end 13 | 14 | local function handle_secdownload(vr) 15 | local path = vr.req.uri.path 16 | local prefix_len = uri_prefix:len() 17 | if path:sub(1, prefix_len) == uri_prefix and not vr.is_handled then 18 | local md5str = path:sub(prefix_len + 1, prefix_len + 32) 19 | 20 | if md5str:len() ~= 32 then return deny_access(vr, 403) end 21 | 22 | if path:sub(prefix_len + 33, prefix_len + 33) ~= "/" then return deny_access(vr, 403) end 23 | 24 | local slash = path:find("/", prefix_len + 34) 25 | if slash == nil then return deny_access(vr, 403) end 26 | 27 | local ts_string = path:sub(prefix_len + 34, slash - 1) 28 | local timestamp = tonumber(ts_string, 16) 29 | if timestamp == nil then return deny_access(vr, 403) end 30 | 31 | path = path:sub(slash) 32 | 33 | local ts = os.time() 34 | if (timestamp < ts - timeout) or (timestamp > ts + timeout) then 35 | -- Gone, not Timeout (don't retry later) 36 | return deny_access(vr, 410) 37 | end 38 | 39 | -- modify md5content as you wish :) 40 | local md5content = secret .. path .. ts_string 41 | -- vr:debug("checking md5: '" .. md5content .. "', md5: " .. md5str) 42 | if md5str ~= lighty.md5(secret .. path .. ts_string) then 43 | return deny_access(vr, 403) 44 | end 45 | 46 | -- rewrite physical paths 47 | vr.phys.doc_root = doc_root 48 | vr.phys.path = doc_root .. path 49 | end 50 | end 51 | 52 | actions = handle_secdownload 53 | -------------------------------------------------------------------------------- /contrib/service/log/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | LOG=/var/log/lighttpd2/sv.log 5 | 6 | test -d "$LOG" || mkdir -p -m2750 "$LOG" && chown root:adm "$LOG" 7 | exec svlogd -tt "$LOG" 8 | -------------------------------------------------------------------------------- /contrib/service/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec 2>&1 3 | echo 'lighttpd2 starting.' 4 | 5 | LANG=C LC_ALL=C \ 6 | exec /usr/sbin/lighttpd2 -c /etc/lighttpd2/angel.conf 7 | -------------------------------------------------------------------------------- /contrib/systemd/lighttpd2.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Lighttpd2 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/sbin/lighttpd2 -c /etc/lighttpd2/angel.conf 8 | ExecReload=/bin/kill -HUP $MAINPID 9 | Restart=on-failure 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /diff-dist-git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -f "$1" ]; then 6 | ( 7 | echo "Syntax: $0 [path-to-dist.tar.gz]" 8 | echo 9 | echo "Build such file with ./autogen.sh; ./configure; make dist-gzip" 10 | echo "This tool can then be used to check the differences between the git" 11 | echo "repository and the tar; it might show added files for autotools" 12 | echo "(compile, configure, Makefile.in, m4, ...) and should remove" 13 | echo ".gitignore files and some helper scripts (packdist.sh and this file)" 14 | ) >&2 15 | exit 1 16 | fi 17 | 18 | tmpdir=$(mktemp --tmpdir -d diff-dist-tar-git-XXXXXXX) 19 | trap 'rm -rf "${tmpdir}"' EXIT 20 | 21 | git archive --format tar.gz -o "${tmpdir}/lighttpd.tar.gz" --prefix "lighttpd-2.0.0/" HEAD 22 | tardiff --modified --autoskip "${tmpdir}/lighttpd.tar.gz" "$1" 23 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /doc/core_fetch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The Fetch API provides a common interface between lighttpd modules to lookup entries in a database. Both lookup key and data are simple (binary) strings. 5 | 6 | So far only a [fetch.files_static](plugin_core.html#plugin_core__setup_fetch-files_static) is providing a database, and only [gnutls](mod_gnutls.html#mod_gnutls__setup_gnutls) is using it to lookup SNI certificates. 7 | 8 | 9 | -------------------------------------------------------------------------------- /doc/core_introduction.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 22 |
23 | 24 |
25 | -------------------------------------------------------------------------------- /doc/core_regex.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | lighttpd2 uses the "Perl-compatible regular expressions" implementation from GLib, see their [Regular expression syntax](https://developer.gnome.org/glib/stable/glib-regex-syntax.html) documentation. 5 | 6 | The config format has different ways to provide strings (you can quote with either `'` or `"`; the character used to quote has to be escaped with `\` if used inside the string). 7 | The simple (standard) way `"text"` has the following escape rules: 8 | 9 | * `"\n"` is a newline, `"\r"` a carriage return, `"\t"` a tab stop 10 | * `"\\"` is one `\`, `"\""` is one double quote `"` and `"\'"` a single quote `'` 11 | * escaping single/double quote is optional if the symbol is not used to terminate the string, i.e. `'\"'` = `'"'` and `"\'"` = `"'"` 12 | * `"\xNN"`: NN must be hexadecimal characters, and the string is replaced with the decoded 8-bit value as a single byte 13 | * All other `\` occurences are **not** removed from the string. 14 | 15 | This way is the preferred one for regular expressions; only to actually match a `\` you have to do additional escaping (`"\\\\"`; `"\x5C"` = `"\\"` is not working), and `\\` is usually not doing what you wanted (matching a digit: `"\\d"` = `"\d"`). All other escape rules are compatible with what pcre is doing. 16 | 17 | The second way is to place an `e` before the string like this: `e"text"`. It has the same rules like the normal string, but does not allow unknown escape sequences (the last rule above). 18 | To match a digit with pcre this way you'd have to write `e"\\d"` instead of `"\d"`. 19 | 20 | 21 | -------------------------------------------------------------------------------- /doc/lighttpd2-worker.8: -------------------------------------------------------------------------------- 1 | .TH lighttpd2-worker "8" "2010-08-24" "" "" 2 | . 3 | .SH NAME 4 | lighttpd \- a fast, secure and flexible web server 5 | . 6 | .SH SYNOPSIS 7 | \fBlighttpd2-worker\fP [\fB\-ltvh\fP] \fB\-c\fP \fIconfigfile\fP [\fB\-m\fP \fImoduledir\fP] 8 | . 9 | .SH DESCRIPTION 10 | \fBlighttpd\fP (pronounced 'lighty') is an advanced HTTP daemon that aims 11 | to be secure, fast, compliant and very flexible. It has been optimized for 12 | high performance. Its feature set includes, but is not limited to, FastCGI, 13 | CGI, basic and digest HTTP authentication, output compression, URL rewriting. 14 | .PP 15 | This manual page only lists the command line arguments. For details 16 | on how to configure \fBlighttpd\fP and its modules see the online documentation: 17 | https://redmine.lighttpd.net/projects/lighttpd2/wiki 18 | .PP 19 | You probably want to use the angel instead of this application: lighttpd2(8) 20 | . 21 | .SH OPTIONS 22 | The following options are supported: 23 | .TP 8 24 | \fB\-c, --config\ \fP \fIconfigfile\fP 25 | Load configuration file \fIconfigfile\fP. 26 | .TP 8 27 | \fB\-l, --lua\fP 28 | Interpret the configuration file as lua script. 29 | .TP 8 30 | \fB\-m, --module-dir\ \fP \fImoduledir\fP 31 | Use 32 | \fImoduledir\fP 33 | as the directory that contains modules, instead of the default. 34 | .TP 8 35 | \fB\-t, --test\fP 36 | Test the configuration file for errors and exit. 37 | .TP 8 38 | \fB\-v, --version\fP 39 | Show version and exit. 40 | .TP 8 41 | \fB\-h, --help, -?\fP 42 | Show a brief help message and exit. 43 | . 44 | .SH SEE ALSO 45 | Online Documentation: https://redmine.lighttpd.net/projects/lighttpd2/wiki 46 | .PP 47 | spawn-fcgi(1), lighttpd2(8) 48 | . 49 | .SH AUTHOR 50 | Jan Kneschke 51 | .PP 52 | Stefan Buehler 53 | .PP 54 | Thomas Porzelt 55 | -------------------------------------------------------------------------------- /doc/lighttpd2.8: -------------------------------------------------------------------------------- 1 | .TH lighttpd2 "8" "2010-08-24" "" "" 2 | . 3 | .SH NAME 4 | lighttpd \- a fast, secure and flexible web server 5 | . 6 | .SH SYNOPSIS 7 | \fBlighttpd2\fP [\fB\-nvh\fP] \fB\-c\fP \fIconfigfile\fP [\fB\-m\fP \fImoduledir\fP] [\fB\--pid-file\fP \fIpidfile\fP] 8 | . 9 | .SH DESCRIPTION 10 | \fBlighttpd\fP (pronounced 'lighty') is an advanced HTTP daemon that aims 11 | to be secure, fast, compliant and very flexible. It has been optimized for 12 | high performance. Its feature set includes, but is not limited to, FastCGI, 13 | CGI, basic and digest HTTP authentication, output compression, URL rewriting. 14 | .PP 15 | The angel is used to keep the worker running and for other tasks where root 16 | permissions are needed (opening network sockets, ...) 17 | .PP 18 | This manual page only lists the command line arguments. For details 19 | on how to configure \fBlighttpd\fP and its modules see the online documentation: 20 | https://redmine.lighttpd.net/projects/lighttpd2/wiki 21 | . 22 | .SH OPTIONS 23 | The following options are supported: 24 | .TP 8 25 | \fB\-c, --config\ \fP \fIconfigfile\fP 26 | Load configuration file \fIconfigfile\fP. 27 | .TP 8 28 | \fB\-m, --module-dir\ \fP \fImoduledir\fP 29 | Use 30 | \fImoduledir\fP 31 | as the directory that contains modules, instead of the default. 32 | .TP 8 33 | \fB\--pid-file\ \fP \fIpidfile\fP 34 | Location of the pid file (only valid in daemon mode). 35 | .TP 8 36 | \fB\-v, --version\fP 37 | Show version and exit. 38 | .TP 8 39 | \fB\-h, --help, -?\fP 40 | Show a brief help message and exit. 41 | . 42 | .SH SEE ALSO 43 | Online Documentation: https://redmine.lighttpd.net/projects/lighttpd2/wiki 44 | .PP 45 | lighttpd2-worker(8) 46 | . 47 | .SH AUTHOR 48 | Jan Kneschke 49 | .PP 50 | Stefan Buehler 51 | .PP 52 | Thomas Porzelt 53 | -------------------------------------------------------------------------------- /doc/meson.build: -------------------------------------------------------------------------------- 1 | install_man( 2 | 'lighttpd2.8', 3 | 'lighttpd2-worker.8', 4 | ) 5 | -------------------------------------------------------------------------------- /doc/mod_access.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | lets you filter clients by IP address. 5 | 6 | 7 | 8 | denies access by returning a 403 status code 9 | 10 | 11 | 12 | allows or denies access based on client IP address 13 | 14 | A key value list mapping "access" and/or "deny" keys to a list of CIDR addresses or "all". 15 | 16 | 17 | Checks the client IP address against the rules. Default is to deny all addresses. The most precise matching rule defines the result ("192.168.100.0/24" takes precedence over "192.168.0.0/16"; similar to routing tables); if the same CIDR is in both lists the second action is taken. "all" is a synonym for "0.0.0.0/0" and "::/0", matching all IPv4 and IPv6 addresses. 18 | 19 | 20 | 21 | Limit access to clients from the local network. The deny rule isn't strictly required, as the default is to deny anyway. The smaller CIDR strings for the local networks override the global deny rule. 22 | 23 | 24 | setup { 25 | module_load "mod_access"; 26 | } 27 | 28 | access.check ( 29 | "allow" => ("127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"), 30 | "deny" => ("all") 31 | ); 32 | 33 | 34 | 35 | 36 | Limit access to clients from "192.168.10.0/24", but deny access to "192.168.10.1". As "192.168.10.1" (equivalent to "192.168.10.1/32") is a more precise match it overwrites the allow rule for the subnet "192.168.10.0/24" containing it. 37 | 38 | 39 | setup { 40 | module_load "mod_access"; 41 | } 42 | 43 | access.check ( 44 | "allow" => ("192.168.10.0/24"), 45 | "deny" => ("192.168.10.1") 46 | ); 47 | 48 | 49 | 50 | 51 | 59 | 60 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /doc/mod_balance.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | balances between different backends. 4 | 5 | 6 | Using an action from mod_balance also activates a backlog: lighttpd2 will then put requests in a backlog if no backend is available and try again later. 7 | 8 | Be careful: the referenced actions may get executed more than once (until one is successful!), so don't loop rewrites in them or something similar. 9 | 10 | 11 | 12 | balance between actions (list or single action) with Round-Robin 13 | 14 | the actions to balance between 15 | 16 | 17 | Round-Robin (rr) the requests are distributed equally over all backends. 18 | 19 | 20 | 21 | balance.rr { fastcgi "127.0.0.1:9090"; }; 22 | 23 | 24 | 25 | 26 | balance.rr ({ fastcgi "127.0.0.1:9090"; }, { fastcgi "127.0.0.1:9091"; }); 27 | 28 | 29 | 30 | 31 | 32 | balance between actions (list or single action) with SQF 33 | 34 | the actions to balance between 35 | 36 | 37 | Shortest-Queue-First (sqf) is similar to Round-Robin and prefers the backend with the shortest wait-queue. 38 | 39 | 40 | 41 | balance.sqf { fastcgi "127.0.0.1:9090"; }; 42 | 43 | 44 | 45 | 46 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /doc/mod_cache_disk_etag.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | caches generated content on disk if an etag response header is set; if the backend sends an already cached etag, the backend is closed and the file is sent directly. 4 | 5 | 6 | Please note: This will not skip the backend, as it will need at least the response headers. 7 | 8 | **Hint:** 9 | Use a cron-job like the following to remove old cached data, e.g. in crontab daily: 10 | 11 | ``` 12 | find /var/cache/lighttpd/cache_etag/ -type f -mtime +2 -exec rm -r {} \; 13 | ``` 14 | 15 | **Hint:** 16 | Have a look at [mod_deflate](mod_deflate.html#mod_deflate) to see this module in action. 17 | 18 | 19 | 20 | cache responses based on the ETag response header 21 | 22 | directory to store the cached results in 23 | 24 | 25 | This blocks action progress until the response headers are done (i.e. there has to be a content generator before it (like fastcgi/dirlist/static file). 26 | You could insert it multiple times of course (e.g. before and after deflate). 27 | 28 | 29 | 30 | 31 | setup { 32 | module_load "mod_cache_disk_etag"; 33 | } 34 | 35 | cache.disk.etag "/var/lib/lighttpd/cache_etag" 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /doc/mod_debug.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | offers various utilities to aid you debug a problem. 5 | 6 | 7 | 8 | shows a page similar to the one from mod_status, listing all active connections 9 | 10 | By specifying one or more "connection ids" via querystring (parameter "con"), one can request additional debug output for specific connections. 11 | 12 | 13 | 14 | if req.path == "/debug/connections" { debug.show_connections; } 15 | 16 | 17 | 18 | 19 | 20 | dumps all allocated memory to the profiler output file if profiling enabled 21 | 22 | 23 | lighttpd2 needs to be compiled with profiler support, and profiling has to be enabled by setting the environment variable `LIGHTY_PROFILE_MEM` to the path of a target log file. 24 | 25 | 26 | 27 | 28 | shows a plain text list of all events 29 | 30 | this is a very low level debug tool for developers. 31 | 32 | 33 | 34 | if req.path == "/debug/events" { debug.show_events; } 35 | 36 | 37 | 38 | 39 | 40 | time in seconds after start of shutdown to log remaining active events 41 | 42 | timeout after which to display events (default: disabled) 43 | 44 | 45 | timeout after which to repeat displaying events (default: disabled) 46 | 47 | 48 | 49 | this is a very low level debug tool for developers; it shows which event listeners keep lighttpd2 alive when it should stop. 50 | 51 | 52 | 53 | 54 | setup { debug.show_events_after_shutdown 5; } 55 | setup { debug.show_events_after_shutdown 5, 15; } 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /doc/mod_dirlist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | lists files inside a directory. The output can be customized in various ways from style via css to excluding certain entries. 4 | 5 | 6 | lists file in a directory 7 | 8 | 9 | 10 | string: url to external stylesheet (default: inline internal css) 11 | 12 | 13 | boolean: hide entries beginning with a dot (default: true) 14 | 15 | 16 | boolean: hide entries ending with a tilde (~), often used for backups (default: true) 17 | 18 | 19 | boolean: hide directories from the directory listing (default: false) 20 | 21 | 22 | boolean: include HEADER.txt above the directory listing (default: false) 23 | 24 | 25 | boolean: hide HEADER.txt from the directory listing (default: false) 26 | 27 | 28 | boolean: html-encode HEADER.txt (if included), set to false if it contains real HTML (default: true) 29 | 30 | 31 | boolean: include README.txt below the directory listing (default: true) 32 | 33 | 34 | boolean: hide README.txt from the directory listing (default: false) 35 | 36 | 37 | boolean: html-encode README.txt (if included), set to false if it contains real HTML (default: true) 38 | 39 | 40 | list of strings: hide entries that end with one of the strings supplied (default: empty list) 41 | 42 | 43 | list of strings: hide entries that begin with one of the strings supplied (default: empty list) 44 | 45 | 46 | boolean: output debug information to log (default: false) 47 | 48 | 49 | string: content-type to return in HTTP response headers (default: "text/html; charset=utf-8") 50 | 51 |
52 |
53 | 54 | 55 | 56 | shows a directory listing including the content of HEADER.txt above the list and hiding itself from it; also hides all files ending in ".bak" 57 | 58 | 59 | setup { 60 | module_load ("mod_dirlist"); 61 | } 62 | 63 | if req.path =^ "/files/" { 64 | dirlist ("include-header" => true, "hide-header" => true, "hide->suffix" => (".bak")); 65 | } 66 | 67 | 68 |
69 | 70 |
71 | 72 | -------------------------------------------------------------------------------- /doc/mod_expire.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | add "Expires" and "Cache-Control" headers to the response 4 | 5 | ` if your content changes in specific intervals like every 15 minutes. 11 | ]]> 12 | 13 | 14 | adds an "Expires" header to the response 15 | 16 | the rule to calculate the "Expires" header value with 17 | 18 | [plus] ( )+ 22 | 23 | * `` is one of "access", "now" or "modification"; "now" being equivalent to "access". 24 | * "`plus`" is optional and does nothing. 25 | * `` is any positive integer. 26 | * `` is one of "seconds", "minutes", "hours", "days", "weeks", "months" or "years". 27 | 28 | The trailing "s" in `` is optional. 29 | 30 | If you use "modification" as `` and the file does not exist or cannot be accessed, mod_expire will do nothing and request processing will go on. 31 | 32 | The expire action will overwrite any existing "Expires" header. 33 | It will append the max-age value to any existing "Cache-Control" header. 34 | ]]> 35 | 36 | 37 | 38 | Cache image, css, txt and js files for 1 week. 39 | 40 | 41 | setup { 42 | module_load "mod_expire"; 43 | } 44 | 45 | if req.path =~ "\.(jpe?g|png|gif|txt|css|js)$" { 46 | expire "access plus 1 week"; 47 | } 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /doc/mod_fastcgi.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | connect to FastCGI backends for generating response content 4 | 5 | 6 | connect to FastCGI backend 7 | 8 | socket to connect to, either "ip:port" or "unix:/path" 9 | 10 | 11 | Don't confuse FastCGI with CGI! Not all CGI backends can be used as FastCGI backends (but you can use [fcgi-cgi](https://redmine.lighttpd.net/projects/fcgi-cgi/wiki) to run CGI backends with lighttpd2). 12 | 13 | 14 | 15 | fastcgi "127.0.0.1:9090" 16 | 17 | 18 | 19 | 20 | Start php for example with spawn-fcgi: `spawn-fcgi -n -s /var/run/lighttpd2/php.sock -- /usr/bin/php5-cgi` 21 | 22 | 23 | setup { 24 | module_load "mod_fastcgi"; 25 | } 26 | 27 | if phys.path =$ ".php" and phys.is_file { 28 | fastcgi "unix:/var/run/lighttpd2/php.sock"; 29 | } 30 | 31 | 32 | 33 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /doc/mod_flv.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | provides flash pseudo streaming 4 | 5 | 6 | pseudo stream the current file as flash 7 | 8 | Lets flash players seek with the "start" query string parameter to an (byte) offset in the file, and prepends a simple flash header before streaming the file from that offset. 9 | 10 | Uses "video/x-flv" as hard-coded content type. 11 | 12 | 13 | 14 | if phys.path =$ ".flv" { 15 | flv; 16 | } 17 | 18 | 19 | 20 | 21 | Use caching and bandwidth throttling to save traffic. Use a small burst threshold to prevent the player from buffering at the beginning. 22 | 23 | This config will make browsers cache videos for 1 month and limit bandwidth to 150 kilobyte/s after 500 kilobytes. 24 | 25 | 26 | if phys.path =$ ".flv" { 27 | expire "access 1 month"; 28 | io.throttle 500kbyte => 150kbyte; 29 | flv; 30 | } 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /doc/mod_fortune.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | loads quotes (aka fortune cookies) from a file and provides actions to add a random quote as response header (X-fortune) or display it as a page. 4 | 5 | 6 | loads cookies from a file, can be called multiple times to load data from multiple files 7 | 8 | the file to load the cookies from 9 | 10 | 11 | 12 | 13 | adds a random quote as response header "X-fortune". 14 | 15 | 16 | 17 | shows a random cookie as response text 18 | 19 | 20 | 21 | 22 | setup { 23 | module_load "mod_fortune"; 24 | fortune.load "/var/www/fortunes.txt"; 25 | } 26 | 27 | if req.path == "/fortune" { 28 | fortune.page; 29 | } else { 30 | fortune.header; 31 | } 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /doc/mod_proxy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | connect to HTTP backends for generating response content 4 | 5 | 6 | connect to HTTP backend 7 | 8 | socket to connect to, either "ip:port" or "unix:/path" 9 | 10 | 13 | 14 | 15 | setup { 16 | module_load "mod_proxy"; 17 | } 18 | 19 | proxy "127.0.0.1:8080"; 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /doc/mod_redirect.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | redirect clients by sending a http status code 301 plus Location header 4 | 5 | 6 | It supports matching [regular expressions](core_regex.html#core_regex) and [substitution](core_pattern.html#core_pattern) with captured substrings as well as other placeholders. 7 | 8 | 9 | 10 | redirect clients 11 | 12 | a simple target string or one rule, mapping a regular expression to a target string, or a list of rules. 13 | 14 | 15 | * The target string is a [pattern](core_pattern.html#core_pattern). 16 | * The [regular expressions](core_regex.html#core_regex) are used to match the request path (always starts with "/"!) 17 | * If a list of rules is given, redirect stops on the first match. 18 | * By default the target string is interpreted as absolute uri; if it starts with '?' it will replace the query string, if it starts with '/' it will replace the current path, and if it starts with './' it is interpreted relative to the current "directory" in the path. 19 | 20 | 21 | 22 | setup { 23 | module_load "mod_redirect"; 24 | } 25 | if request.scheme == "http" { 26 | if request.query == "" { 27 | redirect "https://%{request.host}%{enc:request.path}"; 28 | } else { 29 | redirect "https://%{request.host}%{enc:request.path}?%{request.query}"; 30 | } 31 | } 32 | 33 | 34 | 35 | 36 | setup { 37 | module_load "mod_redirect"; 38 | } 39 | if request.host !~ "^www\.(.*)$" { 40 | if request.query == "" { 41 | redirect "http://www.%{request.host}%{enc:request.path}"; 42 | } else { 43 | redirect "http://www.%{request.host}%{enc:request.path}?%{request.query}"; 44 | } 45 | } 46 | 47 | 48 | 49 | 50 | setup { 51 | module_load "mod_redirect"; 52 | } 53 | redirect "^/old_url$" => "http://new.example.tld/url" 54 | 55 | 56 | 57 | 58 | 59 | redirect all non www. requests. for example: foo.tld/bar?x=y to www.foo.tld/bar?x=y 60 | 61 | 62 | if request.host !~ "^www\.(.*)$" { 63 | redirect "." => "http://www.%1/$0?%{request.query}"; 64 | } 65 | 66 | 67 | 68 | 69 | 73 | 74 | -------------------------------------------------------------------------------- /doc/mod_scgi.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | connect to SCGI backends for generating response content 4 | 5 | 6 | connect to SCGI backend 7 | 8 | socket to connect to, either "ip:port" or "unix:/path" 9 | 10 | 11 | 12 | setup { 13 | module_load "mod_scgi"; 14 | } 15 | 16 | if req.path =^ "/RPC2" { 17 | scgi "127.0.0.1:5000"; 18 | } 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /doc/mod_status.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | displays a page with internal statistics like amount of requests (total or per second), active connections etc. 4 | 5 | 15 | 16 | 17 | returns the status page to the client 18 | 19 | (optional) "short" 20 | 21 | 22 | The "short" mode removes connection and runtime details (recommended for "public" status). 23 | 24 | The status page accepts the following query-string parameters: 25 | 26 | * `?mode=runtime`: shows the runtime details 27 | * `format=plain`: shows the "short" stats in plain text format 28 | 29 | 30 | 31 | If /server-status is requested, a page with lighttpd statistics is displayed. 32 | 33 | 34 | setup { 35 | module_load "mod_status"; 36 | } 37 | 38 | if req.path == "/server-status" { 39 | status.info; 40 | } 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /doc/mod_throttle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | limits outgoing bandwidth usage 4 | 5 | 6 | All rates are in bytes/sec. The magazines are filled up in fixed intervals (compile time constant; defaults to 200ms). 7 | 8 | 9 | 10 | set the outgoing throttle limits for current connection 11 | 12 | bytes/sec limit 13 | 14 | 15 | optional, defaults to 2*rate 16 | 17 | 18 | `burst` is the initial and maximum value for the `magazine`; doing IO drains the `magazine`, which fills up again over time with the specified `rate`. 19 | 20 | 21 | 22 | 23 | adds the current connection to a throttle pool for outgoing limits 24 | 25 | bytes/sec limit 26 | 27 | 28 | all connections in the same pool are limited as whole. Each `io.throttle_pool` action creates its own pool. 29 | 30 | 31 | 32 | Using the same pool in more than one place: 33 | 34 | 35 | setup { 36 | module_load "mod_throttle"; 37 | } 38 | downloadLimit = { 39 | io.throttle_pool 1mbyte; 40 | } 41 | # now use it wherever you need it... 42 | downloadLimit; 43 | 44 | 45 | 46 | 47 | 48 | adds the current connection to an IP-based throttle pool for outgoing limits 49 | 50 | bytes/sec limit 51 | 52 | 53 | all connections from the same IP address in the same pool are limited as whole. Each `io.throttle_ip` action creates its own pool. 54 | 55 | 56 | 57 | Using the same pool in more than one place: 58 | 59 | 60 | setup { 61 | module_load "mod_throttle"; 62 | } 63 | downloadLimit = { 64 | io.throttle_ip 200kbyte; 65 | } 66 | # now use it wherever you need it... 67 | downloadLimit; 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /doc/mod_userdir.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | allows you to have user-specific document roots being accessed through http://domain/~user/ 4 | 5 | 9 | 10 | 11 | builds the document root by replacing certain placeholders in path with (parts of) the username. 12 | 13 | the path to build the document root with 14 | 15 | 38 | 39 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /include/lighttpd/actions_lua.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ACTIONS_LUA_H_ 2 | #define _LIGHTTPD_ACTIONS_LUA_H_ 3 | 4 | #include 5 | #include 6 | 7 | LI_API void li_lua_init_action_mt(liServer *srv, lua_State *L); 8 | 9 | LI_API liAction* li_lua_get_action(lua_State *L, int ndx); 10 | LI_API int li_lua_push_action(liServer *srv, lua_State *L, liAction *a); 11 | 12 | /* create new action from lua function */ 13 | LI_API liAction* li_lua_make_action(lua_State *L, int ndx); 14 | 15 | /* either acquire a action ref or makes a new action from a lua function */ 16 | LI_API liAction* li_lua_get_action_ref(lua_State *L, int ndx); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/lighttpd/angel.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_H_ 2 | #define _LIGHTTPD_ANGEL_H_ 3 | 4 | typedef void (*liAngelListenCB)(liServer *srv, int fd, gpointer data); 5 | 6 | typedef void (*liAngelLogOpen)(liServer *srv, int fd, gpointer data); 7 | 8 | /* interface to the angel; implementation needs to work without angel too */ 9 | LI_API void li_angel_setup(liServer *srv); 10 | 11 | /* listen to a socket (mainloop context) */ 12 | LI_API void li_angel_listen(liServer *srv, GString *str, liAngelListenCB cb, gpointer data); 13 | 14 | /* send log messages during startup to angel, frees the string */ 15 | LI_API void li_angel_log(liServer *srv, GString *str); 16 | 17 | LI_API void li_angel_log_open_file(liServer *srv, liEventLoop *loop, GString *filename, liAngelLogOpen, gpointer data); 18 | 19 | /* angle_fake definitions, only for internal use */ 20 | int li_angel_fake_listen(liServer *srv, GString *str); 21 | gboolean li_angel_fake_log(liServer *srv, GString *str); 22 | int li_angel_fake_log_open_file(liServer *srv, GString *filename); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/lighttpd/angel_base.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_BASE_H_ 2 | #define _LIGHTTPD_ANGEL_BASE_H_ 3 | 4 | #ifdef _LIGHTTPD_BASE_H_ 5 | #error Do not mix lighty with angel code 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/lighttpd/angel_config_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_CONFIG_PARSER_H_ 2 | #define _LIGHTTPD_ANGEL_CONFIG_PARSER_H_ 3 | 4 | /* error handling */ 5 | #define LI_ANGEL_CONFIG_PARSER_ERROR li_angel_config_parser_error_quark() 6 | LI_API GQuark li_angel_config_parser_error_quark(void); 7 | 8 | typedef enum { 9 | LI_ANGEL_CONFIG_PARSER_ERROR_PARSE, /* parse error */ 10 | } liAngelConfigParserError; 11 | 12 | LI_API gboolean li_angel_config_parse_file(liServer *srv, const gchar *filename, GError **err); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/lighttpd/angel_data.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_DATA_H_ 2 | #define _LIGHTTPD_ANGEL_DATA_H_ 3 | 4 | /* write/read data from/to a buffer (GString) (binary) 5 | * this is not meant to be the most performant way to do this, 6 | * as communication with the angel shouldn't happen to often anyway. 7 | * 8 | * Please never send "user" data to the angel (i.e. do not implement 9 | * something like a mod_cgi via sending the request data to the angel; 10 | * instead use the angel to spawn a fastcgi backend (or something similar) 11 | * and send the request via a socket to the backend directly. 12 | */ 13 | 14 | /* angel obviously doesn't work across platforms, so we don't need 15 | * to care about endianness 16 | */ 17 | 18 | /* The buffer may be bigger of course, but a single string should not 19 | * exceed this length: */ 20 | #define LI_ANGEL_DATA_MAX_STR_LEN 1024 /* must fit into a gint32 */ 21 | 22 | /* Needed for reading data */ 23 | typedef struct liAngelBuffer liAngelBuffer; 24 | struct liAngelBuffer { 25 | GString *data; 26 | gsize pos; 27 | }; 28 | 29 | /* error handling */ 30 | #define LI_ANGEL_DATA_ERROR li_angel_data_error_quark() 31 | LI_API GQuark li_angel_data_error_quark(void); 32 | 33 | typedef enum { 34 | LI_ANGEL_DATA_ERROR_EOF, /* not enough data to read value */ 35 | LI_ANGEL_DATA_ERROR_INVALID_STRING_LENGTH, /* invalid string length read from buffer (< 0 || > max-str-len) */ 36 | LI_ANGEL_DATA_ERROR_STRING_TOO_LONG /* string too long (len > max-str-len) */ 37 | } liAngelDataError; 38 | 39 | /* write */ 40 | LI_API gboolean li_angel_data_write_int32(GString *buf, gint32 i, GError **err); 41 | LI_API gboolean li_angel_data_write_int64(GString *buf, gint64 i, GError **err); 42 | LI_API gboolean li_angel_data_write_char (GString *buf, gchar c, GError **err); 43 | LI_API gboolean li_angel_data_write_str (GString *buf, const GString *str, GError **err); 44 | LI_API gboolean li_angel_data_write_cstr (GString *buf, const gchar *str, gsize len, GError **err); 45 | 46 | /* read: 47 | * - if the val pointer is NULL, the data will be discarded 48 | * - reading strings: if *val != NULL *val will be reused; 49 | * otherwise a new GString* will be created 50 | * - *val will only be modified if no error is returned 51 | */ 52 | LI_API gboolean li_angel_data_read_int32(liAngelBuffer *buf, gint32 *val, GError **err); 53 | LI_API gboolean li_angel_data_read_int64(liAngelBuffer *buf, gint64 *val, GError **err); 54 | LI_API gboolean li_angel_data_read_char (liAngelBuffer *buf, gchar *val, GError **err); 55 | LI_API gboolean li_angel_data_read_str (liAngelBuffer *buf, GString **val, GError **err); 56 | LI_API gboolean li_angel_data_read_mem (liAngelBuffer *buf, GString **val, gsize len, GError **err); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/lighttpd/angel_log.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_LOG_H_ 2 | #define _LIGHTTPD_ANGEL_LOG_H_ 3 | 4 | #ifndef _LIGHTTPD_ANGEL_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | #define SEGFAULT(srv, fmt, ...) \ 9 | do { \ 10 | li_log_write_(srv, LI_LOG_LEVEL_ABORT, LI_LOG_FLAG_TIMESTAMP, "(crashing) %s:%d: "fmt, LI_REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__); \ 11 | li_print_backtrace_stderr(); \ 12 | abort(); \ 13 | } while(0) 14 | 15 | #define ERROR(srv, fmt, ...) \ 16 | li_log_write(srv, LI_LOG_LEVEL_ERROR, LI_LOG_FLAG_TIMESTAMP, "error (%s:%d): "fmt, LI_REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__) 17 | 18 | #define WARNING(srv, fmt, ...) \ 19 | li_log_write(srv, LI_LOG_LEVEL_WARNING, LI_LOG_FLAG_TIMESTAMP, "warning (%s:%d): "fmt, LI_REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__) 20 | 21 | #define INFO(srv, fmt, ...) \ 22 | li_log_write(srv, LI_LOG_LEVEL_INFO, LI_LOG_FLAG_TIMESTAMP, "info (%s:%d): "fmt, LI_REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__) 23 | 24 | #define DEBUG(srv, fmt, ...) \ 25 | li_log_write(srv, LI_LOG_LEVEL_DEBUG, LI_LOG_FLAG_TIMESTAMP, "debug (%s:%d): "fmt, LI_REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__) 26 | 27 | /* log messages from lighty always as ERROR */ 28 | #define INSTANCE(srv, inst, msg) \ 29 | li_log_write(srv, LI_LOG_LEVEL_ERROR, LI_LOG_FLAG_TIMESTAMP, "lighttpd[%d]: %s", (int) inst->pid, msg) 30 | 31 | #define GERROR(srv, error, fmt, ...) \ 32 | li_log_write(srv, LI_LOG_LEVEL_ERROR, LI_LOG_FLAG_TIMESTAMP, "error (%s:%d): " fmt "\n %s", LI_REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__, error ? error->message : "Empty GError") 33 | 34 | #define BACKEND_LINES(srv, txt, ...) \ 35 | li_log_split_lines_(srv, LI_LOG_LEVEL_INFO, LI_LOG_FLAG_TIMESTAMP, txt, __VA_ARGS__) 36 | 37 | typedef enum { 38 | LI_LOG_LEVEL_DEBUG, 39 | LI_LOG_LEVEL_INFO, 40 | LI_LOG_LEVEL_WARNING, 41 | LI_LOG_LEVEL_ERROR, 42 | LI_LOG_LEVEL_ABORT 43 | } liLogLevel; 44 | 45 | #define LI_LOG_LEVEL_COUNT (LI_LOG_LEVEL_ABORT+1) 46 | 47 | typedef enum { 48 | LI_LOG_TYPE_STDERR, 49 | LI_LOG_TYPE_FILE, 50 | LI_LOG_TYPE_PIPE, 51 | LI_LOG_TYPE_SYSLOG, 52 | LI_LOG_TYPE_NONE 53 | } liLogType; 54 | 55 | #define LI_LOG_FLAG_NONE (0x0) /* default flag */ 56 | #define LI_LOG_FLAG_TIMESTAMP (0x1) /* prepend a timestamp to the log message */ 57 | 58 | typedef struct liLog liLog; 59 | 60 | struct liLog { 61 | liLogType type; 62 | gboolean levels[LI_LOG_LEVEL_COUNT]; 63 | GString *path; 64 | gint fd; 65 | 66 | time_t last_ts; 67 | GString *ts_cache; 68 | 69 | GString *log_line; 70 | }; 71 | 72 | LI_API void li_log_init(liServer *srv); 73 | LI_API void li_log_clean(liServer *srv); 74 | 75 | LI_API void li_log_write(liServer *srv, liLogLevel log_level, guint flags, const gchar *fmt, ...) HEDLEY_PRINTF_FORMAT(4, 5); 76 | 77 | /* replaces '\r' and '\n' with '\0' */ 78 | LI_API void li_log_split_lines(liServer *srv, liLogLevel log_level, guint flags, gchar *txt, const gchar *prefix); 79 | LI_API void li_log_split_lines_(liServer *srv, liLogLevel log_level, guint flags, gchar *txt, const gchar *fmt, ...) HEDLEY_PRINTF_FORMAT(5, 6); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /include/lighttpd/angel_plugin_core.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_PLUGIN_CORE_H_ 2 | #define _LIGHTTPD_ANGEL_PLUGIN_CORE_H_ 3 | 4 | #include 5 | 6 | LI_API gboolean li_plugin_core_init(liServer *srv); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/lighttpd/angel_proc.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_PROC_H_ 2 | #define _LIGHTTPD_ANGEL_PROC_H_ 3 | 4 | #ifndef _LIGHTTPD_ANGEL_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | /* The callback is not allowed to close the epipe */ 9 | typedef void (*liErrorPipeCB)(liServer *srv, liErrorPipe *epipe, GString *msg); 10 | 11 | typedef void (*liProcSetupCB)(gpointer ctx); 12 | 13 | struct liErrorPipe { 14 | liServer *srv; 15 | gpointer ctx; 16 | liErrorPipeCB cb; 17 | 18 | int fds[2]; 19 | liEventIO fd_watcher; 20 | }; 21 | 22 | struct liProc { 23 | liServer *srv; 24 | 25 | pid_t child_pid; 26 | liErrorPipe *epipe; 27 | gchar *appname; 28 | }; 29 | 30 | LI_API liErrorPipe* li_error_pipe_new(liServer *srv, liErrorPipeCB cb, gpointer ctx); 31 | LI_API void li_error_pipe_free(liErrorPipe *epipe); 32 | 33 | /** closes out-fd */ 34 | LI_API void li_error_pipe_activate(liErrorPipe *epipe); 35 | 36 | /** closes in-fd, moves out-fd to dest_fd */ 37 | LI_API void li_error_pipe_use(liErrorPipe *epipe, int dest_fd); 38 | 39 | /** read remaining data from in-fd */ 40 | LI_API void li_error_pipe_flush(liErrorPipe *epipe); 41 | 42 | LI_API liProc* li_proc_new(liServer *srv, gchar **args, gchar **env, uid_t uid, gid_t gid, gchar *username, gint64 rlim_core, gint64 rlim_nofile, liProcSetupCB cb, gpointer ctx); 43 | LI_API void li_proc_free(liProc *proc); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /include/lighttpd/angel_server.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_SERVER_H_ 2 | #define _LIGHTTPD_ANGEL_SERVER_H_ 3 | 4 | #ifndef _LIGHTTPD_ANGEL_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | #ifndef LIGHTTPD_ANGEL_MAGIC 9 | #define LIGHTTPD_ANGEL_MAGIC ((guint)0x3e14ac65) 10 | #endif 11 | 12 | typedef void (*liInstanceResourceFreeCB) (liServer *srv, liInstance *i, liPlugin *p, liInstanceResource *res); 13 | 14 | struct liInstanceConf { 15 | gint refcount; 16 | 17 | gchar **cmd; 18 | gchar **env; 19 | GString *username; 20 | uid_t uid; 21 | gid_t gid; 22 | 23 | gint64 rlim_core, rlim_nofile; /* < 0: don't change, G_MAXINT64: unlimited */ 24 | }; 25 | 26 | struct liInstance { 27 | gint refcount; 28 | 29 | liServer *srv; 30 | liInstanceConf *ic; 31 | 32 | int pid; /** < remember PID for process as instance ID, even if process is already gone */ 33 | liProc *proc; 34 | liEventChild child_watcher; 35 | 36 | liInstanceState s_cur, s_dest; 37 | 38 | liInstance *replace, *replace_by; 39 | 40 | liAngelConnection *acon; 41 | 42 | GPtrArray *resources; 43 | }; 44 | 45 | struct liServer { 46 | guint32 magic; /** server magic version, check against LIGHTTPD_ANGEL_MAGIC in plugins */ 47 | 48 | liEventLoop loop; 49 | liEventSignal 50 | sig_w_INT, 51 | sig_w_TERM, 52 | sig_w_PIPE; 53 | 54 | liPlugins plugins; 55 | 56 | liLog log; 57 | 58 | gboolean one_shot; /* don't restart instance if it goes down */ 59 | }; 60 | 61 | struct liInstanceResource { 62 | liInstanceResourceFreeCB free_cb; 63 | liPlugin *plugin; /* may be NULL - we don't care about that */ 64 | guint ndx; /* internal array index */ 65 | 66 | gpointer data; 67 | }; 68 | 69 | LI_API liServer* li_server_new(const gchar *module_dir, gboolean module_resident); 70 | LI_API void li_server_free(liServer* srv); 71 | 72 | LI_API void li_server_stop(liServer *srv); 73 | 74 | LI_API liInstance* li_server_new_instance(liServer *srv, liInstanceConf *ic); 75 | LI_API gboolean li_instance_replace(liInstance *oldi, liInstance *newi); 76 | LI_API void li_instance_set_state(liInstance *i, liInstanceState s); 77 | LI_API void li_instance_state_reached(liInstance *i, liInstanceState s); 78 | 79 | LI_API liInstanceConf* li_instance_conf_new(gchar **cmd, gchar **env, GString *username, uid_t uid, gid_t gid, gint64 rlim_core, gint64 rlim_nofile); 80 | LI_API void li_instance_conf_release(liInstanceConf *ic); 81 | LI_API void li_instance_conf_acquire(liInstanceConf *ic); 82 | 83 | LI_API void li_instance_release(liInstance *i); 84 | LI_API void li_instance_acquire(liInstance *i); 85 | 86 | LI_API void li_instance_add_resource(liInstance *i, liInstanceResource *res, liInstanceResourceFreeCB free_cb, liPlugin *p, gpointer data); 87 | LI_API void li_instance_rem_resource(liInstance *i, liInstanceResource *res); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /include/lighttpd/angel_typedefs.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ANGEL_TYPEDEFS_H_ 2 | #define _LIGHTTPD_ANGEL_TYPEDEFS_H_ 3 | 4 | /* angel_proc.h */ 5 | 6 | typedef struct liErrorPipe liErrorPipe; 7 | typedef struct liProc liProc; 8 | 9 | /* angel_server.h */ 10 | 11 | typedef enum { 12 | LI_INSTANCE_DOWN, /* not started yet */ 13 | LI_INSTANCE_SUSPENDED, /* inactive, neither accept nor logs, handle remaining connections */ 14 | LI_INSTANCE_WARMUP, /* only accept(), no logging: waiting for another instance to suspend */ 15 | LI_INSTANCE_RUNNING, /* everything running */ 16 | LI_INSTANCE_SUSPENDING, /* suspended accept(), still logging, handle remaining connections */ 17 | LI_INSTANCE_FINISHED /* not running */ 18 | } liInstanceState; 19 | 20 | typedef struct liServer liServer; 21 | typedef struct liInstance liInstance; 22 | typedef struct liInstanceConf liInstanceConf; 23 | typedef struct liInstanceResource liInstanceResource; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/lighttpd/base.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_BASE_H_ 2 | #define _LIGHTTPD_BASE_H_ 3 | 4 | #ifdef _LIGHTTPD_ANGEL_BASE_H_ 5 | #error Do not mix lighty with angel code 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #define SERVER_VERSION ((guint) 0x01FF0000) 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/lighttpd/base_lua.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_BASE_LUA_H_ 2 | #define _LIGHTTPD_BASE_LUA_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | /* this file defines lighttpd <-> lua glue which is always active, even if compiled without lua */ 9 | 10 | struct lua_State; 11 | 12 | struct liLuaState { 13 | struct lua_State* L; /** NULL if compiled without Lua */ 14 | GStaticRecMutex lualock; 15 | int li_env_ref; 16 | int li_env_default_metatable_ref; 17 | }; 18 | 19 | LI_API void li_lua_init(liLuaState* LL, liServer* srv, liWorker* wrk); 20 | LI_API void li_lua_clear(liLuaState* LL); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/lighttpd/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_BUFFER_H_ 2 | #define _LIGHTTPD_BUFFER_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | typedef struct liBuffer liBuffer; 9 | struct liBuffer { 10 | gchar *addr; 11 | gsize alloc_size; 12 | gsize used; 13 | gint refcount; 14 | liMempoolPtr mptr; 15 | }; 16 | 17 | /* shared buffer; free memory after last reference is released */ 18 | 19 | /** create new buffer: optimized for short-term buffers which will be released soon, uses mempool */ 20 | LI_API liBuffer* li_buffer_new(gsize max_size); 21 | /** create new buffer; optimized for long-term buffers, uses g_slice_alloc */ 22 | LI_API liBuffer* li_buffer_new_slice(gsize max_size); 23 | 24 | LI_API void li_buffer_acquire(liBuffer *buf); 25 | LI_API void li_buffer_release(liBuffer *buf); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/lighttpd/chunk_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_CHUNK_PARSER_H_ 2 | #define _LIGHTTPD_CHUNK_PARSER_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | struct liChunkParserCtx { 9 | liChunkQueue *cq; 10 | 11 | goffset bytes_in; 12 | 13 | /* current position 14 | * buf is curi[start..start+length) 15 | */ 16 | liChunkIter curi; 17 | off_t start, length; 18 | char *buf; 19 | 20 | int cs; 21 | }; 22 | 23 | struct liChunkParserMark { 24 | liChunkIter ci; 25 | off_t pos, abs_pos; 26 | }; 27 | 28 | LI_API void li_chunk_parser_init(liChunkParserCtx *ctx, liChunkQueue *cq); 29 | LI_API void li_chunk_parser_reset(liChunkParserCtx *ctx); 30 | LI_API liHandlerResult li_chunk_parser_prepare(liChunkParserCtx *ctx); 31 | LI_API liHandlerResult li_chunk_parser_next(liChunkParserCtx *ctx, char **p, char **pe, GError **err); 32 | LI_API void li_chunk_parser_done(liChunkParserCtx *ctx, goffset len); 33 | 34 | /* extract [from..to) */ 35 | LI_API gboolean li_chunk_extract_to(liChunkParserMark from, liChunkParserMark to, GString *dest, GError **err); 36 | LI_API GString* li_chunk_extract(liChunkParserMark from, liChunkParserMark to, GError **err); 37 | 38 | INLINE liChunkParserMark li_chunk_parser_getmark(liChunkParserCtx *ctx, const char *fpc); 39 | 40 | /******************** 41 | * Inline functions * 42 | ********************/ 43 | 44 | INLINE liChunkParserMark li_chunk_parser_getmark(liChunkParserCtx *ctx, const char *fpc) { 45 | liChunkParserMark m; 46 | m.ci = ctx->curi; 47 | m.pos = ctx->start + fpc - ctx->buf; 48 | m.abs_pos = ctx->bytes_in + fpc - ctx->buf; 49 | return m; 50 | } 51 | 52 | #define LI_GETMARK(FPC) (li_chunk_parser_getmark(&ctx->chunk_ctx, FPC)) 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/lighttpd/collect.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_COLLECT_H_ 2 | #define _LIGHTTPD_COLLECT_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | /* executes a function in each worker context */ 9 | 10 | /** CollectFunc: the type of functions to execute in each workers context 11 | * - wrk: the current worker 12 | * - fdata: optional user data 13 | * the return value will be placed in the GArray 14 | */ 15 | typedef gpointer (*liCollectFuncCB)(liWorker *wrk, gpointer fdata); 16 | 17 | /** CollectCallback: the type of functions to call after a function was called in each workers context 18 | * - cbdata: optional callback data 19 | * depending on the data you should only use it when complete == TRUE 20 | * - fdata : the data the CollectFunc got (this data must be valid until cb is called) 21 | * - result: the return values 22 | * - complete: determines if cbdata is still valid 23 | * if this is FALSE, it may be called from another context than li_collect_start was called 24 | */ 25 | typedef void (*liCollectCB)(gpointer cbdata, gpointer fdata, GPtrArray *result, gboolean complete); 26 | 27 | typedef struct liCollectInfo liCollectInfo; 28 | 29 | /** li_collect_start returns NULL if the callback was called directly (e.g. for only one worker and ctx = wrk) */ 30 | LI_API liCollectInfo* li_collect_start(liWorker *ctx, liCollectFuncCB func, gpointer fdata, liCollectCB cb, gpointer cbdata); 31 | /** li_collect_start_global uses srv->main_worker to call cb(), and never returns directly */ 32 | LI_API liCollectInfo* li_collect_start_global(liServer *srv, liCollectFuncCB func, gpointer fdata, liCollectCB cb, gpointer cbdata); 33 | LI_API void li_collect_break(liCollectInfo* ci); /** this will result in complete == FALSE in the callback; call it if cbdata gets invalid */ 34 | 35 | /* internal functions */ 36 | LI_API void li_collect_watcher_cb(liEventBase *watcher, int events); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/lighttpd/condition_lua.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_CONDITION_LUA_H_ 2 | #define _LIGHTTPD_CONDITION_LUA_H_ 3 | 4 | #include 5 | #include 6 | 7 | LI_API void li_lua_init_condition_mt(liServer *srv, lua_State *L); 8 | 9 | LI_API liCondition* li_lua_get_condition(lua_State *L, int ndx); 10 | LI_API int li_lua_push_condition(liServer *srv, lua_State *L, liCondition *c); 11 | 12 | LI_API void li_lua_set_global_condition_lvalues(liServer *srv, lua_State *L); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/lighttpd/config_lua.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_CONFIG_LUA_H_ 2 | #define _LIGHTTPD_CONFIG_LUA_H_ 3 | 4 | #include 5 | #include 6 | 7 | LI_API gboolean li_config_lua_load(liLuaState *LL, liServer *srv, liWorker *wrk, const gchar *filename, liAction **pact, gboolean allow_setup, liValue *args); 8 | 9 | LI_API void li_lua_push_action_table(liServer *srv, liWorker *wrk, lua_State *L); 10 | LI_API void li_lua_push_setup_table(liServer *srv, liWorker *wrk, lua_State *L); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/lighttpd/config_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_CONFIGPARSER_H_ 2 | #define _LIGHTTPD_CONFIGPARSER_H_ 3 | 4 | #include 5 | 6 | #define LI_CONFIG_ERROR li_config_error_quark() 7 | LI_API GQuark li_config_error_quark(void); 8 | 9 | LI_API gboolean li_config_parse(liServer *srv, const gchar *config_path); 10 | 11 | /* parse more config snippets at runtime. does not support includes and modifying global vars */ 12 | LI_API liAction* li_config_parse_live(liWorker *wrk, const gchar *sourcename, const char *source, gsize sourcelen, GError **error); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/lighttpd/encoding.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ENCODING_H_ 2 | #define _LIGHTTPD_ENCODING_H_ 3 | 4 | #include 5 | 6 | typedef enum { 7 | LI_ENCODING_HEX, /* a => 61 */ 8 | LI_ENCODING_HTML, /* HTML special chars. & => & e.g. */ 9 | LI_ENCODING_URI /* relative URI */ 10 | } liEncoding; 11 | 12 | 13 | /* encodes special characters in a string and returns the new string */ 14 | LI_API GString *li_string_encode_append(const gchar *str, GString *dest, liEncoding encoding); 15 | LI_API GString *li_string_encode(const gchar *str, GString *dest, liEncoding encoding); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/lighttpd/environment.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ENVIRONMENT_H_ 2 | #define _LIGHTTPD_ENVIRONMENT_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | typedef struct liEnvironment liEnvironment; 9 | 10 | typedef struct liEnvironmentDup liEnvironmentDup; 11 | 12 | struct liEnvironment { 13 | GHashTable *table; 14 | }; 15 | 16 | /* read only duplicate of a real environment: use it to remember which 17 | env vars you already sent (mod_fastcgi) */ 18 | struct liEnvironmentDup { 19 | GHashTable *table; 20 | }; 21 | 22 | LI_API void li_environment_init(liEnvironment *env); /* create table */ 23 | LI_API void li_environment_reset(liEnvironment *env); /* remove all entries */ 24 | LI_API void li_environment_clear(liEnvironment *env); /* destroy table */ 25 | 26 | /* overwrite previous value */ 27 | LI_API void li_environment_set(liEnvironment *env, const gchar *key, size_t keylen, const gchar *val, size_t valuelen); 28 | /* do not overwrite */ 29 | LI_API void li_environment_insert(liEnvironment *env, const gchar *key, size_t keylen, const gchar *val, size_t valuelen); 30 | LI_API void li_environment_remove(liEnvironment *env, const gchar *key, size_t keylen); 31 | LI_API GString* li_environment_get(liEnvironment *env, const gchar *key, size_t keylen); 32 | 33 | 34 | /* create (data) read only copy of a environment; don't modify the real environment 35 | while using the duplicate */ 36 | LI_API liEnvironmentDup* li_environment_make_dup(liEnvironment *env); 37 | LI_API void li_environment_dup_free(liEnvironmentDup *envdup); 38 | /* remove an entry (this is allowed - it doesn't modify anything in the original environment); 39 | you must not modify the returned GString */ 40 | LI_API GString* li_environment_dup_pop(liEnvironmentDup *envdup, const gchar *key, size_t keylen); 41 | 42 | typedef void (*liAddEnvironmentCB)(gpointer param, const gchar *key, size_t keylen, const gchar *val, size_t valuelen); 43 | /* calls callback for various CGI environment variables to add; if the variable is also present 44 | in envdup, the value from envdup is used instead for the callback and it is popped from envdup. 45 | Also adds all remaining values from envdup via callback, and then frees envdup. */ 46 | LI_API void li_environment_dup2cgi(liVRequest *vr, liEnvironmentDup *envdup, liAddEnvironmentCB callback, gpointer param); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/lighttpd/etag.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_ETAG_H_ 2 | #define _LIGHTTPD_ETAG_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | LI_API liTristate li_http_response_handle_cachable_etag(liVRequest *vr, GString *etag); 9 | LI_API liTristate li_http_response_handle_cachable_modified(liVRequest *vr, GString *last_modified); 10 | LI_API gboolean li_http_response_handle_cachable(liVRequest *vr); 11 | 12 | /* mut maybe the same as etag */ 13 | LI_API void li_etag_mutate(GString *mut, GString *etag); 14 | LI_API void li_etag_set_header(liVRequest *vr, struct stat *st, gboolean *cachable); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/lighttpd/filter.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_FILTER_H_ 2 | #define _LIGHTTPD_FILTER_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | typedef liHandlerResult (*liFilterHandlerCB)(liVRequest *vr, liFilter *f); 9 | typedef void (*liFilterFreeCB)(liVRequest *vr, liFilter *f); 10 | typedef void (*liFilterEventCB)(liVRequest *vr, liFilter *f, liStreamEvent event); 11 | 12 | struct liFilter { 13 | liStream stream; 14 | 15 | liChunkQueue *in, *out; 16 | 17 | /* if the handler wasn't able to handle all "in" data it must call li_stream_again(&f->stream) to trigger a new call to handle_data 18 | * vr, in and out can be NULL if the associated vrequest/stream was destroyed 19 | * in handle_data out is never NULL 20 | */ 21 | liFilterHandlerCB handle_data; 22 | liFilterFreeCB handle_free; 23 | liFilterEventCB handle_event; 24 | gpointer param; 25 | 26 | liVRequest *vr; 27 | guint filter_ndx; 28 | }; 29 | 30 | LI_API liFilter* li_filter_new(liVRequest *vr, liFilterHandlerCB handle_data, liFilterFreeCB handle_free, liFilterEventCB handle_event, gpointer param); 31 | 32 | LI_API liFilter* li_vrequest_add_filter_in(liVRequest *vr, liFilterHandlerCB handle_data, liFilterFreeCB handle_free, liFilterEventCB handle_event, gpointer param); 33 | LI_API liFilter* li_vrequest_add_filter_out(liVRequest *vr, liFilterHandlerCB handle_data, liFilterFreeCB handle_free, liFilterEventCB handle_event, gpointer param); 34 | 35 | LI_API void li_vrequest_filters_init(liVRequest *vr); 36 | LI_API void li_vrequest_filters_clear(liVRequest *vr); 37 | LI_API void li_vrequest_filters_reset(liVRequest *vr); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/lighttpd/filter_buffer_on_disk.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_FILTER_BUFFER_ON_DISK_H_ 2 | #define _LIGHTTPD_FILTER_BUFFER_ON_DISK_H_ 3 | 4 | #include 5 | 6 | /* flush_limit: -1: wait for end-of-stream, n >= 0: if more than n bytes have been written, the next part of the file gets forwarded to out */ 7 | /* split_on_file_chunks: start a new file on FILE_CHUNK (those are not written to the file) */ 8 | LI_API liStream* li_filter_buffer_on_disk(liVRequest *vr, goffset flush_limit, gboolean split_on_file_chunks); 9 | LI_API void li_filter_buffer_on_disk_stop(liStream *stream); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /include/lighttpd/filter_chunked.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_FILTER_CHUNKED_H_ 2 | #define _LIGHTTPD_FILTER_CHUNKED_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | /* initialize with zero */ 9 | typedef struct { 10 | int parse_state; 11 | goffset cur_chunklen; 12 | } liFilterChunkedDecodeState; 13 | 14 | LI_API liHandlerResult li_filter_chunked_encode(liVRequest *vr, liChunkQueue *out, liChunkQueue *in); 15 | LI_API gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueue *in, liFilterChunkedDecodeState *state); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/lighttpd/http_range_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_HTTP_RANGE_PARSER_H_ 2 | #define _LIGHTTPD_HTTP_RANGE_PARSER_H_ 3 | 4 | #include 5 | 6 | typedef struct { 7 | /* public */ 8 | gboolean last_range; 9 | goffset range_start, range_length, range_end; /* length = end - start + 1; */ 10 | 11 | /* private */ 12 | GString *data; 13 | goffset limit; /* "file size" */ 14 | gboolean found_valid_range; 15 | 16 | int cs; 17 | gchar *data_pos; 18 | } liParseHttpRangeState; 19 | 20 | typedef enum { 21 | LI_PARSE_HTTP_RANGE_OK, 22 | LI_PARSE_HTTP_RANGE_DONE, 23 | LI_PARSE_HTTP_RANGE_INVALID, 24 | LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE 25 | } liParseHttpRangeResult; 26 | 27 | LI_API void li_parse_http_range_init(liParseHttpRangeState* s, const GString *range_str, goffset limit); 28 | LI_API liParseHttpRangeResult li_parse_http_range_next(liParseHttpRangeState* s); 29 | LI_API void li_parse_http_range_clear(liParseHttpRangeState* s); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/lighttpd/http_request_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_HTTP_REQUEST_PARSER_H_ 2 | #define _LIGHTTPD_HTTP_REQUEST_PARSER_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | struct liHttpRequestCtx { 9 | liChunkParserCtx chunk_ctx; 10 | liRequest *request; 11 | 12 | liChunkParserMark mark; 13 | GString *h_key, *h_value; 14 | }; 15 | 16 | LI_API void li_http_request_parser_init(liHttpRequestCtx* ctx, liRequest *req, liChunkQueue *cq); 17 | LI_API void li_http_request_parser_reset(liHttpRequestCtx* ctx); 18 | LI_API void li_http_request_parser_clear(liHttpRequestCtx *ctx); 19 | 20 | LI_API liHandlerResult li_http_request_parse(liVRequest *vr, liHttpRequestCtx *ctx); 21 | 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/lighttpd/http_response_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_HTTP_RESPONSE_PARSER_H_ 2 | #define _LIGHTTPD_HTTP_RESPONSE_PARSER_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | struct liHttpResponseCtx { 9 | liChunkParserCtx chunk_ctx; 10 | liResponse *response; 11 | 12 | gboolean accept_cgi, accept_nph; 13 | gboolean drop_header; /* for 1xx responses */ 14 | 15 | liHttpVersion http_version; 16 | 17 | liChunkParserMark mark; 18 | GString *h_key, *h_value; 19 | }; 20 | 21 | LI_API void li_http_response_parser_init(liHttpResponseCtx* ctx, liResponse *req, liChunkQueue *cq, gboolean accept_cgi, gboolean accept_nph); 22 | LI_API void li_http_response_parser_reset(liHttpResponseCtx* ctx); 23 | LI_API void li_http_response_parser_clear(liHttpResponseCtx *ctx); 24 | 25 | LI_API liHandlerResult li_http_response_parse(liVRequest *vr, liHttpResponseCtx *ctx); 26 | 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/lighttpd/idlist.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_IDLIST_H_ 2 | #define _LIGHTTPD_IDLIST_H_ 3 | 4 | #include 5 | 6 | typedef struct liIDList liIDList; 7 | 8 | struct liIDList { 9 | /* used ids are marked with a "1" in the bitvector (represented as array of gulong) */ 10 | GArray *bitvector; 11 | 12 | /* all ids are in the range [0, max_ids[, i.e. 0 <= id < max_ids 13 | * although the type is guint, it has to fit in a gint too, as we 14 | * use gint for the ids in the interface, so we can use -1 as a special value. 15 | */ 16 | guint max_ids; 17 | 18 | /* if all ids in [0, used_ids-1] are used, next_free_id is -1 19 | * if not, then all available ids are >= next_free_id, 20 | * so we can start at next_free_id for searching the next free id 21 | */ 22 | gint next_free_id; 23 | guint used_ids; 24 | }; 25 | 26 | /* create new idlist; the parameter max_ids is "signed" on purpose */ 27 | LI_API liIDList* li_idlist_new(gint max_ids); 28 | 29 | /* free idlist */ 30 | LI_API void li_idlist_free(liIDList *l); 31 | 32 | /* request new id; return -1 if no id is available, valid ids are always > 0 */ 33 | LI_API gint li_idlist_get(liIDList *l); 34 | 35 | /* check whether an id is in use and can be "_put" */ 36 | LI_API gboolean li_idlist_is_used(liIDList *l, gint id); 37 | 38 | /* release id. never release an id more than once! */ 39 | LI_API void li_idlist_put(liIDList *l, gint id); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /include/lighttpd/ip_parsers.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_IP_PARSERS_H_ 2 | #define _LIGHTTPD_IP_PARSERS_H_ 3 | 4 | #include 5 | 6 | /** optional parameters are set to default values (netmask all bits, port 0) */ 7 | /** parse an IPv4 (if netmask is not NULL with optional cidr netmask, if port is not NULL with optional port) */ 8 | LI_API gboolean li_parse_ipv4(const char *str, guint32 *ip, guint32 *netmask, guint16 *port); 9 | /** parse an IPv6 (if network is not NULL with optional cidr network, if port is not NULL with optional port if the ip/cidr part is in [...]) */ 10 | LI_API gboolean li_parse_ipv6(const char *str, guint8 *ip, guint *network, guint16 *port); 11 | /** print the ip into dest, return dest */ 12 | LI_API GString* li_ipv6_tostring(GString *dest, const guint8 ip[16]); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/lighttpd/jobqueue.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_JOBQUEUE_H_ 2 | #define _LIGHTTPD_JOBQUEUE_H_ 3 | 4 | #ifndef _LIGHTTPD_EVENTS_H_ 5 | #error Include lighttpd/events.h instead 6 | #endif 7 | 8 | typedef struct liJob liJob; 9 | typedef struct liJobRef liJobRef; 10 | typedef struct liJobQueue liJobQueue; 11 | 12 | typedef void (*liJobCB)(liJob *job); 13 | 14 | /* All data here is private; use the functions to interact with the job-queue */ 15 | 16 | struct liJob { 17 | /* prevent running callback in a loop (delay if job generation == queue generation) */ 18 | guint generation; 19 | GList link; 20 | liJobCB callback; 21 | liJobRef *ref; 22 | }; 23 | 24 | struct liJobRef { 25 | gint refcount; 26 | liJob *job; 27 | liJobQueue *queue; 28 | }; 29 | 30 | struct liJobQueue { 31 | guint generation; 32 | 33 | liEventPrepare prepare_watcher; 34 | 35 | GQueue queue; 36 | liEventTimer queue_watcher; 37 | 38 | GAsyncQueue *async_queue; 39 | liEventAsync async_queue_watcher; 40 | }; 41 | 42 | LI_API void li_job_queue_init(liJobQueue *jq, liEventLoop *loop); 43 | LI_API void li_job_queue_clear(liJobQueue *jq); /* runs until all jobs are done */ 44 | 45 | LI_API void li_job_init(liJob *job, liJobCB callback); 46 | LI_API void li_job_reset(liJob *job); 47 | /* remove job from queue if active and detach existing references, but doesn't reset loop detection */ 48 | LI_API void li_job_stop(liJob *job); 49 | LI_API void li_job_clear(liJob *job); 50 | 51 | /* marks the job for later execution */ 52 | LI_API void li_job_later(liJobQueue *jq, liJob *job); 53 | LI_API void li_job_later_ref(liJobRef *jobref); /* NOT thread-safe! */ 54 | /* if the job didn't run in this generation yet, run it now; otherwise mark it for later execution */ 55 | LI_API void li_job_now(liJobQueue *jq, liJob *job); 56 | LI_API void li_job_now_ref(liJobRef *jobref); /* NOT thread-safe! */ 57 | 58 | LI_API void li_job_async(liJobRef *jobref); 59 | /* marks the job for later execution; this is the only threadsafe way to push a job to the queue */ 60 | 61 | LI_API liJobRef* li_job_ref(liJobQueue *jq, liJob *job); 62 | LI_API void li_job_ref_release(liJobRef *jobref); 63 | LI_API void li_job_ref_acquire(liJobRef *jobref); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/lighttpd/lighttpd-glue.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_LIGHTTPD_GLUE_H_ 2 | #define _LIGHTTPD_LIGHTTPD_GLUE_H_ 3 | 4 | /* returns the description for a given http status code and sets the len to the length of the returned string */ 5 | LI_API gchar *li_http_status_string(guint status_code, guint *len); 6 | /* returns the liHttpMethod enum entry matching the given string */ 7 | LI_API liHttpMethod li_http_method_from_string(const gchar *method_str, gssize len); 8 | /* returns the http method as a string and sets len to the length of the returned string */ 9 | LI_API gchar *li_http_method_string(liHttpMethod method, guint *len); 10 | /* returns the http version as a string and sets len to the length of the returned string */ 11 | LI_API gchar *li_http_version_string(liHttpVersion method, guint *len); 12 | /* converts a given 3 digit http status code to a gchar[3] string. e.g. 403 to {'4','0','3'} */ 13 | LI_API void li_http_status_to_str(gint status_code, gchar status_str[]); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/lighttpd/memcached.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_MEMCACHED_H_ 2 | #define _LIGHTTPD_MEMCACHED_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct liMemcachedCon liMemcachedCon; 9 | typedef struct liMemcachedItem liMemcachedItem; 10 | typedef struct liMemcachedRequest liMemcachedRequest; 11 | typedef enum { 12 | LI_MEMCACHED_OK, /* STORED, VALUE, DELETED */ 13 | LI_MEMCACHED_NOT_STORED, 14 | LI_MEMCACHED_EXISTS, 15 | LI_MEMCACHED_NOT_FOUND, 16 | LI_MEMCACHED_RESULT_ERROR /* some error occurred */ 17 | } liMemcachedResult; 18 | 19 | typedef void (*liMemcachedCB)(liMemcachedRequest *request, liMemcachedResult result, liMemcachedItem *item, GError **err); 20 | 21 | struct liMemcachedItem { 22 | GString *key; 23 | guint32 flags; 24 | li_tstamp ttl; 25 | guint64 cas; 26 | liBuffer *data; 27 | }; 28 | 29 | struct liMemcachedRequest { 30 | liMemcachedCB callback; 31 | gpointer cb_data; 32 | }; 33 | 34 | /* error handling */ 35 | #define LI_MEMCACHED_ERROR li_memcached_error_quark() 36 | LI_API GQuark li_memcached_error_quark(void); 37 | 38 | typedef enum { 39 | LI_MEMCACHED_CONNECTION, 40 | LI_MEMCACHED_BAD_KEY, 41 | LI_MEMCACHED_DISABLED, /* disabled right now */ 42 | LI_MEMCACHED_UNKNOWN = 0xff 43 | } liMemcachedError; 44 | 45 | LI_API liMemcachedCon* li_memcached_con_new(liEventLoop *loop, liSocketAddress addr); 46 | LI_API void li_memcached_con_acquire(liMemcachedCon* con); 47 | LI_API void li_memcached_con_release(liMemcachedCon* con); /* thread-safe */ 48 | 49 | /* these functions are not thread-safe, i.e. must be called in the same context as "loop" from li_memcached_con_new */ 50 | LI_API liMemcachedRequest* li_memcached_get(liMemcachedCon *con, GString *key, liMemcachedCB callback, gpointer cb_data, GError **err); 51 | LI_API liMemcachedRequest* li_memcached_set(liMemcachedCon *con, GString *key, guint32 flags, li_tstamp ttl, liBuffer *data, liMemcachedCB callback, gpointer cb_data, GError **err); 52 | 53 | /* if length(key) <= 250 and all chars x: 0x20 < x < 0x7f the key 54 | * remains untouched; otherwise it gets replaced with its sha1hex hash 55 | * so in most cases the key stays readable, and we have a good fallback 56 | */ 57 | LI_API void li_memcached_mutate_key(GString *key); 58 | LI_API gboolean li_memcached_is_key_valid(GString *key); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /include/lighttpd/mempool.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_MEMPOOL_H_ 2 | #define _LIGHTTPD_MEMPOOL_H_ 3 | 4 | #include 5 | 6 | typedef struct liMempoolPtr liMempoolPtr; 7 | struct liMempoolPtr { 8 | void *priv_data; /* private data for internal management */ 9 | void *data; /* real pointer (result of alloc) */ 10 | }; 11 | 12 | LI_API gsize li_mempool_align_page_size(gsize size); 13 | LI_API liMempoolPtr li_mempool_alloc(gsize size); 14 | 15 | /* you cannot release parts from an allocated chunk; so you _have_ to remember the size from the alloc */ 16 | LI_API void li_mempool_free(liMempoolPtr ptr, gsize size); 17 | 18 | LI_API void li_mempool_cleanup(void); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/lighttpd/meson.build: -------------------------------------------------------------------------------- 1 | configure_file(output: 'config.h', configuration: conf_data) 2 | -------------------------------------------------------------------------------- /include/lighttpd/mimetype.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_MIMETYPE_H_ 2 | #define _LIGHTTPD_MIMETYPE_H_ 3 | 4 | struct liMimetypeNode { 5 | guchar cmin; 6 | guchar cmax; 7 | gpointer *children; /* array of either liMimetypeNode* or GString* */ 8 | GString *mimetype; 9 | }; 10 | typedef struct liMimetypeNode liMimetypeNode; 11 | 12 | LI_API liMimetypeNode *li_mimetype_node_new(void); 13 | LI_API void li_mimetype_node_free(liMimetypeNode *node); 14 | LI_API void li_mimetype_insert(liMimetypeNode *node, GString *suffix, GString *mimetype); 15 | 16 | /* looks up the mimetype for a filename by comparing suffixes. longest match is returned. do not free the result */ 17 | LI_API GString *li_mimetype_get(liVRequest *vr, GString *filename); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/lighttpd/network.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_NETWORK_H_ 2 | #define _LIGHTTPD_NETWORK_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | #if defined(USE_LINUX_SENDFILE) || defined(USE_FREEBSD_SENDFILE) || defined(USE_SOLARIS_SENDFILEV) || defined(USE_OSX_SENDFILE) 9 | # define USE_SENDFILE 10 | #endif 11 | 12 | #define LI_NETWORK_ERROR li_network_error_quark() 13 | LI_API GQuark li_network_error_quark(void); 14 | 15 | /** repeats write after EINTR */ 16 | LI_API ssize_t li_net_write(int fd, void *buf, ssize_t nbyte); 17 | 18 | /** repeats read after EINTR */ 19 | LI_API ssize_t li_net_read(int fd, void *buf, ssize_t nbyte); 20 | 21 | LI_API liNetworkStatus li_network_write(int fd, liChunkQueue *cq, goffset write_max, GError **err); 22 | LI_API liNetworkStatus li_network_read(int fd, liChunkQueue *cq, goffset read_max, liBuffer **buffer, GError **err); 23 | 24 | /* use writev for mem chunks, buffered read/write for files */ 25 | LI_API liNetworkStatus li_network_write_writev(int fd, liChunkQueue *cq, goffset *write_max, GError **err); 26 | 27 | #ifdef USE_SENDFILE 28 | /* use sendfile for files, writev for mem chunks */ 29 | LI_API liNetworkStatus li_network_write_sendfile(int fd, liChunkQueue *cq, goffset *write_max, GError **err); 30 | #endif 31 | 32 | /* write backends */ 33 | LI_API liNetworkStatus li_network_backend_write(int fd, liChunkQueue *cq, goffset *write_max, GError **err); 34 | LI_API liNetworkStatus li_network_backend_writev(int fd, liChunkQueue *cq, goffset *write_max, GError **err); 35 | 36 | #define LI_NETWORK_FALLBACK(f, write_max) do { \ 37 | liNetworkStatus res; \ 38 | switch(res = f(fd, cq, write_max, err)) { \ 39 | case LI_NETWORK_STATUS_SUCCESS: \ 40 | break; \ 41 | default: \ 42 | return res; \ 43 | } \ 44 | } while(0) 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/lighttpd/options.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_OPTIONS_H_ 2 | #define _LIGHTTPD_OPTIONS_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | union liOptionValue { 9 | gint64 number; 10 | gboolean boolean; 11 | }; 12 | 13 | struct liOptionPtrValue { 14 | gint refcount; 15 | 16 | union { 17 | gpointer ptr; 18 | 19 | /* some common pointer types */ 20 | GString *string; 21 | GPtrArray *list; 22 | GHashTable *hash; 23 | liAction *action; 24 | liCondition *cond; 25 | } data; 26 | 27 | liServerOptionPtr *sopt; 28 | }; 29 | 30 | struct liOptionSet { 31 | size_t ndx; 32 | liOptionValue value; 33 | }; 34 | 35 | struct liOptionPtrSet { 36 | size_t ndx; 37 | liOptionPtrValue *value; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/lighttpd/pattern.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_PATTERN_H_ 2 | #define _LIGHTTPD_PATTERN_H_ 3 | 4 | /* liPattern are a parsed representation of a string that can contain various placeholders like $n, %n, %{var} or {enc:var} */ 5 | 6 | /* liPattern is a GArray in disguise */ 7 | typedef GArray liPattern; 8 | 9 | /* a pattern callback receives an integer index range [from-to] and a data pointer (usually an array) and must return a GString* which gets inserted into the pattern result 10 | * "from" doesn't have to be smaller than "to" (allows reverse ranges)! 11 | */ 12 | typedef void (*liPatternCB) (GString *pattern_result, guint from, guint to, gpointer data); 13 | 14 | /* constructs a new liPattern* by parsing the given string, returns NULL on error */ 15 | LI_API liPattern *li_pattern_new(liServer *srv, const gchar* str); 16 | LI_API void li_pattern_free(liPattern *pattern); 17 | 18 | /* appends the result to "dest". use (and truncate) vr->wrk->tmp_str as "dest" if possible */ 19 | LI_API void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatternCB nth_callback, gpointer nth_data, liPatternCB nth_prev_callback, gpointer nth_prev_data); 20 | 21 | /* default array callback, expects a GArray* containing GString* elements */ 22 | LI_API void li_pattern_array_cb(GString *pattern_result, guint from, guint to, gpointer data); 23 | /* default regex callback, expects a GMatchInfo* */ 24 | LI_API void li_pattern_regex_cb(GString *pattern_result, guint from, guint to, gpointer data); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/lighttpd/plugin_core.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_PLUGIN_CORE_H_ 2 | #define _LIGHTTPD_PLUGIN_CORE_H_ 3 | 4 | #include 5 | 6 | typedef enum { LI_ETAG_USE_INODE = 1, LI_ETAG_USE_MTIME = 2, LI_ETAG_USE_SIZE = 4 } liETagFlags; 7 | 8 | enum liCoreOptions { 9 | LI_CORE_OPTION_DEBUG_REQUEST_HANDLING = 0, 10 | 11 | LI_CORE_OPTION_STATIC_RANGE_REQUESTS, 12 | 13 | LI_CORE_OPTION_MAX_KEEP_ALIVE_IDLE, 14 | LI_CORE_OPTION_MAX_KEEP_ALIVE_REQUESTS, 15 | 16 | LI_CORE_OPTION_ETAG_FLAGS, 17 | 18 | LI_CORE_OPTION_ASYNC_STAT, 19 | 20 | LI_CORE_OPTION_BUFFER_ON_DISK_REQUEST_BODY, 21 | 22 | LI_CORE_OPTION_STRICT_POST_CONTENT_LENGTH, 23 | }; 24 | 25 | enum liCoreOptionPtrs { 26 | LI_CORE_OPTION_STATIC_FILE_EXCLUDE_EXTENSIONS = 0, 27 | 28 | LI_CORE_OPTION_SERVER_NAME, 29 | LI_CORE_OPTION_SERVER_TAG, 30 | 31 | LI_CORE_OPTION_MIME_TYPES, 32 | }; 33 | 34 | /* the core plugin always has base index 0, as it is the first plugin loaded */ 35 | #define CORE_OPTION(idx) _CORE_OPTION(vr, idx) 36 | #define _CORE_OPTION(vr, idx) _OPTION_ABS(vr, idx) 37 | #define CORE_OPTIONPTR(idx) _CORE_OPTIONPTR(vr, idx) 38 | #define _CORE_OPTIONPTR(vr, idx) _OPTIONPTR_ABS(vr, idx) 39 | 40 | LI_API void li_plugin_core_init(liServer *srv, liPlugin *p, gpointer userdata); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/lighttpd/profiler.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_PROFILER_H_ 2 | #define _LIGHTTPD_PROFILER_H_ 3 | 4 | extern gboolean li_profiler_enabled; /* read only */ 5 | 6 | typedef struct liProfilerMem liProfilerMem; 7 | 8 | struct liProfilerMem { 9 | guint64 inuse_bytes; 10 | guint64 alloc_times; 11 | guint64 alloc_bytes; 12 | guint64 calloc_times; 13 | guint64 calloc_bytes; 14 | guint64 realloc_times; 15 | guint64 realloc_bytes; 16 | guint64 free_times; 17 | guint64 free_bytes; 18 | }; 19 | 20 | LI_API void li_profiler_enable(gchar *output_path); /* enables the profiler */ 21 | LI_API void li_profiler_finish(void); 22 | LI_API void li_profiler_dump(gint minsize); /* dumps memory statistics to file specified in LI_PROFILE_MEM env var */ 23 | LI_API void li_profiler_hashtable_insert(const gpointer addr, gsize size); /* registers an allocated block with the profiler */ 24 | LI_API void li_profiler_hashtable_remove(const gpointer addr); /* deregisters an allocated block with the profiler */ 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/lighttpd/radix.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_RADIX_H_ 2 | #define _LIGHTTPD_RADIX_H_ 3 | 4 | #include 5 | 6 | typedef struct liRadixTree liRadixTree; 7 | 8 | LI_API liRadixTree* li_radixtree_new(void); 9 | LI_API void li_radixtree_free(liRadixTree *tree, GFunc free_func, gpointer free_userdata); 10 | 11 | LI_API gpointer li_radixtree_insert(liRadixTree *tree, const void *key, guint32 bits, gpointer data); /* returns old data after overwrite */ 12 | LI_API gpointer li_radixtree_remove(liRadixTree *tree, const void *key, guint32 bits); /* returns data from removed node */ 13 | LI_API gpointer li_radixtree_lookup(liRadixTree *tree, const void *key, guint32 bits); /* longest matching prefix */ 14 | LI_API gpointer li_radixtree_lookup_exact(liRadixTree *tree, const void *key, guint32 bits); 15 | 16 | LI_API void li_radixtree_foreach(liRadixTree *tree, GFunc func, gpointer userdata); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/lighttpd/request.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_REQUEST_H_ 2 | #define _LIGHTTPD_REQUEST_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | struct liRequestUri { 9 | GString *raw; /* may include scheme and authority before path_raw */ 10 | GString *raw_path, *raw_orig_path; /* not decoded path with querystring */ 11 | 12 | GString *scheme; 13 | GString *authority; /* authority: may include auth and ports and hostname trailing dots */ 14 | GString *path; 15 | GString *query; 16 | 17 | GString *host; /* without userinfo and port and trailing dots */ 18 | }; 19 | 20 | struct liPhysical { 21 | GString *path; 22 | GString *doc_root; 23 | GString *pathinfo; 24 | }; 25 | 26 | struct liRequest { 27 | liHttpMethod http_method; 28 | GString *http_method_str; 29 | liHttpVersion http_version; 30 | 31 | liRequestUri uri; 32 | 33 | liHttpHeaders *headers; 34 | /* Parsed headers: */ 35 | goffset content_length; /* -1 if not specified; implies chunked transfer-encoding */ 36 | }; 37 | 38 | LI_API void li_request_init(liRequest *req); 39 | LI_API void li_request_reset(liRequest *req); 40 | LI_API void li_request_clear(liRequest *req); 41 | 42 | LI_API void li_request_copy(liRequest *dest, const liRequest *src); 43 | 44 | LI_API gboolean li_request_validate_header(liConnection *con); 45 | 46 | LI_API void li_physical_init(liPhysical *phys); 47 | LI_API void li_physical_reset(liPhysical *phys); 48 | LI_API void li_physical_clear(liPhysical *phys); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /include/lighttpd/response.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_RESPONSE_H_ 2 | #define _LIGHTTPD_RESPONSE_H_ 3 | 4 | #ifndef _LIGHTTPD_BASE_H_ 5 | #error Please include instead of this file 6 | #endif 7 | 8 | struct liResponse { 9 | liHttpHeaders *headers; 10 | gint http_status; 11 | liTransferEncoding transfer_encoding; 12 | }; 13 | 14 | LI_API void li_response_init(liResponse *resp); 15 | LI_API void li_response_reset(liResponse *resp); 16 | LI_API void li_response_clear(liResponse *resp); 17 | 18 | LI_API void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueue *response_body, gboolean upgraded); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/lighttpd/stream_http_response.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_STREAM_HTTP_RESPONSE_H_ 2 | #define _LIGHTTPD_STREAM_HTTP_RESPONSE_H_ 3 | 4 | #include 5 | 6 | LI_API liStream* li_stream_http_response_handle(liStream *http_in, liVRequest *vr, gboolean accept_cgi, gboolean accept_nph, gboolean keepalive); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/lighttpd/sys_memory.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_SYS_MEMORY_H_ 2 | #define _LIGHTTPD_SYS_MEMORY_H_ 3 | 4 | /* returns the currently used memory (RSS, resident set size) in bytes or 0 on failure */ 5 | LI_API gsize li_memory_usage(void); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /include/lighttpd/tasklet.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_TASKLET_H_ 2 | #define _LIGHTTPD_TASKLET_H_ 3 | 4 | #include 5 | #include 6 | 7 | typedef struct liTaskletPool liTaskletPool; 8 | 9 | typedef void (*liTaskletFinishedCB)(gpointer data); 10 | typedef void (*liTaskletRunCB)(gpointer data); 11 | 12 | /* if threads = 0: all run callbacks are executed immediately in li_tasklet_push (but finished_cb is delayed) 13 | * if threads < 0: a shared GThreadPool is used 14 | * if threads > 0: a exclusive GThreadPool is used with the specified numbers of threads 15 | */ 16 | 17 | /* we do not keep the loop alive! */ 18 | LI_API liTaskletPool* li_tasklet_pool_new(liEventLoop *loop, gint threads); 19 | 20 | /* blocks until all tasks are done; calls all finished callbacks; 21 | * you are allowed to call this from finish callbacks, but not more than once! 22 | */ 23 | LI_API void li_tasklet_pool_free(liTaskletPool *pool); 24 | 25 | /* this may stop the old pool and wait for all jobs to be finished; doesn't call finished callbacks */ 26 | LI_API void li_tasklet_pool_set_threads(liTaskletPool *pool, gint threads); 27 | 28 | LI_API gint li_tasklet_pool_get_threads(liTaskletPool *pool); 29 | 30 | /* the finished callback is executed in the same thread context as the pool lives in; 31 | * it will either be called from li_tasklet_pool_free or the ev-loop handler, 32 | * never from li_tasklet_push 33 | * all tasklets will be executed, you can *not* cancel them! 34 | */ 35 | LI_API void li_tasklet_push(liTaskletPool *pool, liTaskletRunCB run, liTaskletFinishedCB finished, gpointer data); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/lighttpd/throttle.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_THROTTLE_H_ 2 | #define _LIGHTTPD_THROTTLE_H_ 3 | 4 | #include 5 | 6 | #define LI_THROTTLE_GRANULARITY 200 /* defines how frequently (in milliseconds) a magazine is refilled */ 7 | 8 | typedef void (*liThrottleNotifyCB)(liThrottleState *state, gpointer data); 9 | 10 | LI_API liThrottleState* li_throttle_new(void); 11 | LI_API void li_throttle_set(liWorker *wrk, liThrottleState *state, guint rate, guint burst); 12 | LI_API void li_throttle_free(liWorker *wrk, liThrottleState *state); 13 | 14 | LI_API guint li_throttle_query(liWorker *wrk, liThrottleState *state, guint interested, liThrottleNotifyCB notify_callback, gpointer data); 15 | LI_API void li_throttle_update(liThrottleState *state, guint used); 16 | 17 | LI_API liThrottlePool* li_throttle_pool_new(liServer *srv, guint rate, guint burst); 18 | LI_API void li_throttle_pool_acquire(liThrottlePool *pool); 19 | LI_API void li_throttle_pool_release(liThrottlePool *pool, liServer *srv); 20 | 21 | /* returns whether pool was actually added (otherwise it already was added) */ 22 | LI_API gboolean li_throttle_add_pool(liWorker *wrk, liThrottleState *state, liThrottlePool *pool); 23 | LI_API void li_throttle_remove_pool(liWorker *wrk, liThrottleState *state, liThrottlePool *pool); 24 | 25 | /* internal for worker waitqueue setup */ 26 | LI_API void li_throttle_waitqueue_cb(liWaitQueue *wq, gpointer data); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/lighttpd/url_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_URL_PARSER_H_ 2 | #define _LIGHTTPD_URL_PARSER_H_ 3 | 4 | #include 5 | 6 | /* parses uri->raw into all components, which have to be reset/initialized before */ 7 | LI_API gboolean li_parse_raw_url(liRequestUri *uri); 8 | 9 | /* parse input into uri->path, uri->raw_path and uri->query, which get truncated before. 10 | * also decodes and simplifies path on success 11 | */ 12 | LI_API gboolean li_parse_raw_path(liRequestUri *uri, GString *input); 13 | 14 | LI_API gboolean li_parse_hostname(liRequestUri *uri); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/lighttpd/value_lua.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_OPTIONS_LUA_H_ 2 | #define _LIGHTTPD_OPTIONS_LUA_H_ 3 | 4 | #include 5 | #include 6 | 7 | LI_API void li_lua_init_value_mt(lua_State *L); 8 | 9 | /* converts the top of the stack into an value 10 | * and pops the value 11 | * returns NULL if it couldn't convert the value (still pops it) 12 | */ 13 | LI_API liValue* li_value_from_lua(liServer *srv, lua_State *L); 14 | 15 | /* always returns 1, pushes nil on error */ 16 | LI_API int li_lua_push_value(lua_State *L, liValue *value); 17 | 18 | LI_API GString* li_lua_togstring(lua_State *L, int ndx); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/lighttpd/version.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_VERSION_H_ 2 | #define _LIGHTTPD_VERSION_H_ 3 | 4 | #ifdef HAVE_VERSION_H 5 | #include 6 | #else 7 | #define REPO_VERSION "" 8 | #endif 9 | 10 | #define PACKAGE_DESC PACKAGE_NAME "/" PACKAGE_VERSION REPO_VERSION 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/lighttpd/waitqueue.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_WAITQUEUE_H_ 2 | #define _LIGHTTPD_WAITQUEUE_H_ 3 | 4 | #include 5 | #include 6 | 7 | typedef struct liWaitQueueElem liWaitQueueElem; 8 | typedef struct liWaitQueue liWaitQueue; 9 | typedef void (*liWaitQueueCB) (liWaitQueue *wq, gpointer data); 10 | 11 | struct liWaitQueueElem { 12 | gboolean queued; 13 | li_tstamp ts; 14 | liWaitQueueElem *prev; 15 | liWaitQueueElem *next; 16 | gpointer data; 17 | }; 18 | 19 | struct liWaitQueue { 20 | liWaitQueueElem *head; 21 | liWaitQueueElem *tail; 22 | liEventTimer timer; 23 | gdouble delay; 24 | 25 | liWaitQueueCB callback; 26 | gpointer data; 27 | guint length; 28 | }; 29 | 30 | /* 31 | * waitqueues are queues used to implement delays for certain tasks in a lightweight, non-blocking way 32 | * they are used for io timeouts or throttling for example 33 | * li_waitqueue_push, li_waitqueue_pop and li_waitqueue_remove have O(1) complexity 34 | */ 35 | 36 | /* initializes a waitqueue by creating the timer and initializing the queue. precision is sub-seconds */ 37 | LI_API void li_waitqueue_init(liWaitQueue *queue, liEventLoop *loop, const char *waitqueue_name, liWaitQueueCB callback, gdouble delay, gpointer data); 38 | 39 | /* stops the waitqueue. to restart it, simply call li_waitqueue_update */ 40 | LI_API void li_waitqueue_stop(liWaitQueue *queue); 41 | 42 | /* updates the delay of the timer. if timer is active, it is stopped and restarted */ 43 | LI_API void li_waitqueue_set_delay(liWaitQueue *queue, gdouble delay); 44 | 45 | /* updates the timeout of the waitqueue, you should always call this at the end of your callback */ 46 | LI_API void li_waitqueue_update(liWaitQueue *queue); 47 | 48 | /* moves the element to the end of the queue if already queued, appends it to the end otherwise */ 49 | LI_API void li_waitqueue_push(liWaitQueue *queue, liWaitQueueElem *elem); 50 | 51 | /* pops the first ready! element from the queue or NULL if none ready yet. this should be called in your callback */ 52 | LI_API liWaitQueueElem *li_waitqueue_pop(liWaitQueue *queue); 53 | 54 | /* pops the first element from the queue or NULL if empty. use it to clean your queue */ 55 | LI_API liWaitQueueElem *li_waitqueue_pop_force(liWaitQueue *queue); 56 | 57 | /* pops all elements from the queue that are ready or NULL of none ready yet. returns number of elements pop()ed and saves old head in '*head' */ 58 | LI_API guint li_waitqueue_pop_ready(liWaitQueue *queue, liWaitQueueElem **head); 59 | 60 | /* removes an element from the queue */ 61 | LI_API void li_waitqueue_remove(liWaitQueue *queue, liWaitQueueElem *elem); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /include/meson.build: -------------------------------------------------------------------------------- 1 | inc_dir = include_directories('.', is_system: true) 2 | 3 | subdir('lighttpd') 4 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('lua', type : 'boolean', value : true, description : 'Build with lua; extends core and other modules, and builds mod_lua') 2 | option('ipv6', type : 'boolean', value : true, description : 'Build with IPv6 support') 3 | option('config-parser', type : 'boolean', value : true, description : 'Build with standard config parser') 4 | option('unwind', type : 'boolean', value : true, description : 'Build with (lib)unwind support in asserts to print backtraces') 5 | option('openssl', type : 'boolean', value : true, description : 'Build mod_openssl') 6 | option('gnutls', type : 'boolean', value : true, description : 'Build mod_gnutls') 7 | option('sni', type : 'boolean', value : true, description : 'Build mod_openssl/mod_gnutls with SNI support') 8 | option('bzip2', type : 'boolean', value : true, description : 'Build mod_deflate with bzip2 support') 9 | option('deflate', type : 'boolean', value : true, description : 'Build mod_deflate with zlib (deflate) support') 10 | option('extra-warnings', type : 'boolean', value : true, description : 'Build with extra warnings enabled') 11 | # option('static', type : 'boolean', value : false, description : 'Build static lighttpd with all modules included') 12 | option('profiler', type : 'boolean', value : false, description : 'Build with memory profiler') 13 | 14 | option('search-lib', type : 'array', value : [], description: 'Search libs in additional paths') 15 | option('search-inc', type : 'array', value : [], description: 'Search includes in additional paths') 16 | -------------------------------------------------------------------------------- /packdist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # only builds snapshots for now 4 | # tarball name contains date and git short id 5 | 6 | SRCTEST=src/main/lighttpd_worker.c 7 | PACKAGE=lighttpd 8 | REV=${REV} 9 | BASEDOWNLOADURL="https://download.lighttpd.net/lighttpd/snapshots-2.0.x" 10 | 11 | if [ ! -f ${SRCTEST} ]; then 12 | echo "Current directory is not the source directory" 13 | exit 1 14 | fi 15 | 16 | dopack=1 17 | if [ "$1" = "--nopack" ]; then 18 | dopack=0 19 | shift 20 | fi 21 | 22 | append="$1" 23 | 24 | function force() { 25 | "$@" || { 26 | echo "Command failed: $*" 27 | exit 1 28 | } 29 | } 30 | 31 | if [ ${dopack} = "1" ]; then 32 | force ./autogen.sh 33 | 34 | if [ -d distbuild ]; then 35 | # make distcheck may leave readonly files 36 | chmod u+w -R distbuild 37 | rm -rf distbuild 38 | fi 39 | 40 | force mkdir distbuild 41 | force cd distbuild 42 | 43 | force ../configure --prefix=/usr 44 | 45 | # force make 46 | # force make check 47 | 48 | force make distcheck 49 | else 50 | force cd distbuild 51 | fi 52 | 53 | version=`./config.status -V | head -n 1 | cut -d' ' -f3` 54 | name="${PACKAGE}-${version}" 55 | append="-snap-$(date '+%Y%m%d')${REV}-g$(git rev-list --abbrev-commit --abbrev=6 HEAD^..HEAD)" 56 | if [ -n "${append}" ]; then 57 | cp "${name}.tar.gz" "${name}${append}.tar.gz" 58 | cp "${name}.tar.bz2" "${name}${append}.tar.bz2" 59 | name="${name}${append}" 60 | fi 61 | 62 | force sha256sum "${name}.tar."{gz,bz2} > "${name}.sha256sum" 63 | 64 | echo wget "${BASEDOWNLOADURL}/${name}".'{tar.gz,tar.bz2,sha256sum}; sha256sum -c '${name}'.sha256sum' 65 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pyright] 2 | include = ["tests"] 3 | 4 | [[tool.pyright.executionEnvironments]] 5 | root = "tests" 6 | 7 | [tool.mypy] 8 | warn_unused_configs = true 9 | mypy_path = "tests" 10 | files = "tests" 11 | implicit_reexport = false 12 | show_error_codes = true 13 | follow_imports = "silent" 14 | -------------------------------------------------------------------------------- /src/angel/angel_log.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | void li_log_init(liServer *srv) { 5 | srv->log.type = LI_LOG_TYPE_STDERR; 6 | 7 | srv->log.levels[LI_LOG_LEVEL_ABORT] = TRUE; 8 | srv->log.levels[LI_LOG_LEVEL_ERROR] = TRUE; 9 | srv->log.levels[LI_LOG_LEVEL_WARNING] = TRUE; 10 | 11 | srv->log.levels[LI_LOG_LEVEL_INFO] = TRUE; /* TODO: remove debug levels */ 12 | srv->log.levels[LI_LOG_LEVEL_DEBUG] = TRUE; 13 | 14 | srv->log.fd = -1; 15 | srv->log.ts_cache = g_string_sized_new(0); 16 | srv->log.log_line = g_string_sized_new(0); 17 | } 18 | 19 | void li_log_clean(liServer *srv) { 20 | g_string_free(srv->log.ts_cache, TRUE); 21 | g_string_free(srv->log.log_line, TRUE); 22 | } 23 | 24 | void li_log_write(liServer *srv, liLogLevel log_level, guint flags, const gchar *fmt, ...) { 25 | va_list ap; 26 | GString *log_line = srv->log.log_line; 27 | 28 | if (!srv->log.levels[log_level]) return; 29 | 30 | g_string_truncate(log_line, 0); 31 | 32 | /* for normal error messages, we prepend a timestamp */ 33 | if (flags & LI_LOG_FLAG_TIMESTAMP) { 34 | GString *log_ts = srv->log.ts_cache; 35 | time_t li_cur_ts; 36 | 37 | li_cur_ts = (time_t) (li_event_now(&srv->loop)); 38 | 39 | if (li_cur_ts != srv->log.last_ts) { 40 | gsize s; 41 | struct tm tm; 42 | 43 | g_string_set_size(log_ts, 255); 44 | #ifdef HAVE_LOCALTIME_R 45 | s = strftime(log_ts->str, log_ts->allocated_len, "%Y-%m-%d %H:%M:%S %Z: ", localtime_r(&li_cur_ts, &tm)); 46 | #else 47 | s = strftime(log_ts->str, log_ts->allocated_len, "%Y-%m-%d %H:%M:%S %Z: ", localtime(&li_cur_ts)); 48 | #endif 49 | 50 | g_string_set_size(log_ts, s); 51 | 52 | srv->log.last_ts = li_cur_ts; 53 | } 54 | 55 | li_g_string_append_len(log_line, GSTR_LEN(log_ts)); 56 | } 57 | 58 | va_start(ap, fmt); 59 | g_string_append_vprintf(log_line, fmt, ap); 60 | va_end(ap); 61 | 62 | li_g_string_append_len(log_line, CONST_STR_LEN("\n")); 63 | 64 | fprintf(stderr, "%s", log_line->str); 65 | } 66 | 67 | void li_log_split_lines(liServer *srv, liLogLevel log_level, guint flags, gchar *txt, const gchar *prefix) { 68 | gchar *start; 69 | 70 | start = txt; 71 | while ('\0' != *txt) { 72 | if ('\r' == *txt || '\n' == *txt) { 73 | *txt = '\0'; 74 | if (txt - start > 1) { /* skip empty lines*/ 75 | li_log_write(srv, log_level, flags, "%s%s", prefix, start); 76 | } 77 | txt++; 78 | while (*txt == '\n' || *txt == '\r') txt++; 79 | start = txt; 80 | } else { 81 | txt++; 82 | } 83 | } 84 | if (txt - start > 1) { /* skip empty lines*/ 85 | li_log_write(srv, log_level, flags, "%s%s", prefix, start); 86 | } 87 | } 88 | 89 | void li_log_split_lines_(liServer *srv, liLogLevel log_level, guint flags, gchar *txt, const gchar *fmt, ...) { 90 | va_list ap; 91 | GString *prefix; 92 | 93 | prefix = g_string_sized_new(0); 94 | va_start(ap, fmt); 95 | g_string_vprintf(prefix, fmt, ap); 96 | va_end(ap); 97 | 98 | li_log_split_lines(srv, log_level, flags, txt, prefix->str); 99 | 100 | g_string_free(prefix, TRUE); 101 | } 102 | -------------------------------------------------------------------------------- /src/angel/angel_value.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../common/value_impl.c" 4 | 5 | liValue* li_value_copy(liValue* val) { 6 | return li_common_value_copy_(val); 7 | } 8 | 9 | void li_value_clear(liValue *val) { 10 | li_common_value_clear_(val); 11 | } 12 | 13 | const char* li_valuetype_string(liValueType type) { 14 | return li_common_valuetype_string_(type); 15 | } 16 | 17 | GString *li_value_to_string(liValue *val) { 18 | return li_common_value_to_string_(val); 19 | } 20 | 21 | gpointer li_value_extract_ptr(liValue *val) { 22 | return li_common_value_extract_ptr_(val); 23 | } 24 | -------------------------------------------------------------------------------- /src/angel/meson.build: -------------------------------------------------------------------------------- 1 | src_angel_shared = [ 2 | 'angel_log.c', 3 | 'angel_plugin.c', 4 | 'angel_plugin_core.c', 5 | 'angel_proc.c', 6 | 'angel_server.c', 7 | 'angel_value.c', 8 | ] + ragel_gen.process( 9 | 'angel_config_parser.rl', 10 | ) 11 | 12 | lib_shared_angel = library( 13 | 'lighttpd2-sharedangel-' + meson.project_version(), 14 | src_angel_shared, 15 | include_directories: [inc_dir] + search_includes, 16 | dependencies: [ 17 | main_deps, 18 | ], 19 | link_with: lib_common, 20 | install: true, 21 | ) 22 | 23 | bin_angel = executable( 24 | 'lighttpd2', 25 | 'angel_main.c', 26 | include_directories: [inc_dir] + search_includes, 27 | dependencies: [ 28 | main_deps, 29 | ], 30 | link_with: [ 31 | lib_common, 32 | lib_shared_angel, 33 | ], 34 | install: true, 35 | install_dir: get_option('sbindir') 36 | ) 37 | -------------------------------------------------------------------------------- /src/common/buffer.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | static void _buffer_init(liBuffer *buf, gsize alloc_size) { 6 | buf->alloc_size = alloc_size; 7 | buf->used = 0; 8 | buf->mptr = li_mempool_alloc(alloc_size); 9 | buf->addr = buf->mptr.data; 10 | } 11 | 12 | static void _buffer_init_slice(liBuffer *buf, gsize alloc_size) { 13 | buf->alloc_size = alloc_size; 14 | buf->used = 0; 15 | buf->mptr.data = NULL; 16 | buf->addr = g_slice_alloc(alloc_size); 17 | } 18 | 19 | static void _buffer_destroy(liBuffer *buf) { 20 | if (!buf || NULL == buf->addr) return; 21 | 22 | if (NULL == buf->mptr.data) { 23 | g_slice_free1(buf->alloc_size, buf->addr); 24 | } else { 25 | li_mempool_free(buf->mptr, buf->alloc_size); 26 | buf->addr = NULL; 27 | buf->mptr.data = NULL; buf->mptr.priv_data = NULL; 28 | buf->used = buf->alloc_size = 0; 29 | } 30 | 31 | g_slice_free(liBuffer, buf); 32 | } 33 | 34 | 35 | liBuffer* li_buffer_new(gsize max_size) { 36 | liBuffer *buf = g_slice_new0(liBuffer); 37 | _buffer_init(buf, li_mempool_align_page_size(max_size)); 38 | buf->refcount = 1; 39 | return buf; 40 | } 41 | 42 | liBuffer* li_buffer_new_slice(gsize max_size) { 43 | liBuffer *buf = g_slice_new0(liBuffer); 44 | _buffer_init_slice(buf, max_size); 45 | buf->refcount = 1; 46 | return buf; 47 | } 48 | 49 | void li_buffer_release(liBuffer *buf) { 50 | if (!buf) return; 51 | LI_FORCE_ASSERT(g_atomic_int_get(&buf->refcount) > 0); 52 | if (g_atomic_int_dec_and_test(&buf->refcount)) { 53 | _buffer_destroy(buf); 54 | } 55 | } 56 | 57 | void li_buffer_acquire(liBuffer *buf) { 58 | LI_FORCE_ASSERT(g_atomic_int_get(&buf->refcount) > 0); 59 | g_atomic_int_inc(&buf->refcount); 60 | } 61 | -------------------------------------------------------------------------------- /src/common/meson.build: -------------------------------------------------------------------------------- 1 | src_common = [ 2 | 'angel_connection.c', 3 | 'angel_data.c', 4 | 'buffer.c', 5 | 'encoding.c', 6 | 'events.c', 7 | 'fetch.c', 8 | 'idlist.c', 9 | 'jobqueue.c', 10 | 'memcached.c', 11 | 'mempool.c', 12 | 'module.c', 13 | 'radix.c', 14 | 'sys_memory.c', 15 | 'sys_socket.c', 16 | 'tasklet.c', 17 | 'utils.c', 18 | 'value.c', 19 | # 'value_impl.c', -- "templated", gets included in main and angel 20 | 'waitqueue.c', 21 | ] + ragel_gen.process('ip_parsers.rl') 22 | 23 | if get_option('profiler') 24 | src_common += ['profiler.c'] 25 | endif 26 | 27 | lib_common = library( 28 | 'lighttpd2-common-' + meson.project_version(), 29 | src_common, 30 | include_directories: [inc_dir] + search_includes, 31 | dependencies: [ 32 | main_deps, 33 | opt_dep_unwind, 34 | lib_crypt, 35 | lib_kvm, 36 | ], 37 | install: true, 38 | ) 39 | -------------------------------------------------------------------------------- /src/common/sys_memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | #if defined(LIGHTY_OS_LINUX) 5 | #include 6 | 7 | gsize li_memory_usage(void) { 8 | /* parse /proc/self/stat */ 9 | int d; 10 | gchar s[PATH_MAX]; 11 | gchar c; 12 | unsigned int u; 13 | long unsigned int lu; 14 | long int ld; 15 | long long unsigned int llu; 16 | gsize rss; 17 | FILE *f; 18 | 19 | f = fopen("/proc/self/stat", "r"); 20 | if (!f) 21 | return 0; 22 | 23 | /* fields according to proc(5) */ 24 | d = fscanf( 25 | f, 26 | "%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu" 27 | " %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld", 28 | &d, s, &c, &d, &d, &d, &d, &d, &u, &lu, &lu, &lu, &lu, &lu, &lu, &ld, &ld, &ld, &ld, &ld, &ld, &lu, &lu, 29 | (long int*)&rss, &lu, &lu, &lu, &lu, &lu, &lu, &lu, &lu, &lu, &lu, &lu, &lu, &lu, &d, &d, &u, &u, &llu, &lu, &ld 30 | ); 31 | 32 | fclose(f); 33 | 34 | /* rss is the 24th field */ 35 | if (d < 24) 36 | return 0; 37 | 38 | return rss * getpagesize(); 39 | } 40 | 41 | #elif defined(LIGHTY_OS_FREEBSD) 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | gsize li_memory_usage(void) { 50 | kvm_t *kvm; 51 | struct kinfo_proc *ki_proc; 52 | gint cnt; 53 | gsize rss; 54 | 55 | kvm = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); 56 | if (!kvm) 57 | return 0; 58 | 59 | ki_proc = kvm_getprocs(kvm, KERN_PROC_PID, getpid(), &cnt); 60 | if (!ki_proc) { 61 | kvm_close(kvm); 62 | return 0; 63 | } 64 | 65 | rss = ki_proc->ki_rssize * getpagesize(); 66 | 67 | kvm_close(kvm); 68 | 69 | return rss; 70 | } 71 | #elif defined(LIGHTY_OS_MACOSX) 72 | #include 73 | #include 74 | 75 | gsize li_memory_usage(void) { 76 | /* info from https://miknight.blogspot.com/2005/11/resident-set-size-in-mac-os-x.html */ 77 | struct task_basic_info tbinfo; 78 | mach_msg_type_number_t cnt = TASK_BASIC_INFO_COUNT; 79 | 80 | if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t) &tbinfo, &cnt)) 81 | return 0; 82 | 83 | return tbinfo.resident_size; 84 | } 85 | 86 | #elif defined(LIGHTY_OS_SOLARIS) 87 | 88 | gsize li_memory_usage(void) { 89 | /* /proc/$pid/psinfo: http://docs.sun.com/app/docs/doc/816-5174/proc-4?l=en&a=view */ 90 | gchar path[64]; 91 | psinfo_t psinfo; 92 | FILE *f; 93 | 94 | sprintf(path, "/proc/%d/psinfo", getpid()); 95 | 96 | f = fopen(path, "r"); 97 | if (!f) 98 | return 0; 99 | 100 | if (fread(&psinfo, sizeof(psinfo_t), 1, f) != 1) { 101 | fclose(f); 102 | return 0; 103 | } 104 | 105 | return psinfo.pr_rssize * 1024; 106 | } 107 | 108 | #else 109 | 110 | gsize li_memory_usage(void) { 111 | /* unsupported OS */ 112 | return 0; 113 | } 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /src/common/sys_socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef HAVE_INET_ATON 5 | /* win32 has inet_addr instead if inet_aton */ 6 | # ifdef HAVE_INET_ADDR 7 | int inet_aton(const char *cp, struct in_addr *inp) { 8 | struct in_addr a; 9 | 10 | a.s_addr = inet_addr(cp); 11 | 12 | if (INADDR_NONE == a.s_addr) { 13 | return 0; 14 | } 15 | 16 | inp->s_addr = a.s_addr; 17 | 18 | return 1; 19 | } 20 | # else 21 | # error no inet_aton emulation found 22 | # endif 23 | 24 | #endif 25 | 26 | #ifdef _WIN32 27 | 28 | #include 29 | 30 | /* windows doesn't have inet_ntop */ 31 | 32 | /* 33 | I have to look into this more. WSAAddressToString takes a sockaddr structure, which includes 34 | the port number, so I must first test this stuff more carefully. For now, no IPV6 on windows. 35 | You will notice that HAVE_IPV6 is never true for win32. 36 | */ 37 | 38 | const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) 39 | { 40 | /* WSAAddressToString takes a full sockaddr, while inet_ntop only takes the address */ 41 | struct sockaddr_in sock4; 42 | struct sockaddr_in6 sock6; 43 | DWORD addrLen = cnt; 44 | int err = 0; 45 | 46 | /* src is either an in_addr or an in6_addr. */ 47 | const struct in_addr *src4 = (const struct in_addr*) src; 48 | const struct in6_addr *src6 = (const struct in6_addr*) src; 49 | 50 | int ipv6 = af == AF_INET6; 51 | 52 | /* DebugBreak(); */ 53 | 54 | if ( ipv6 ) 55 | { 56 | sock6.sin6_family = AF_INET6; 57 | sock6.sin6_port = 0; 58 | sock6.sin6_addr = *src6; 59 | } 60 | else 61 | { 62 | sock4.sin_family = AF_INET; 63 | sock4.sin_port = 0; 64 | sock4.sin_addr = *src4; 65 | } 66 | 67 | err = WSAAddressToStringA( 68 | ipv6 ? (LPSOCKADDR) &sock6 : (LPSOCKADDR) &sock4, 69 | ipv6 ? sizeof(sock6) : sizeof(sock4), 70 | NULL, 71 | dst, &addrLen ); 72 | return err == 0 ? dst : NULL; 73 | } 74 | 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/lighttpd2.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | version=@VERSION@ 6 | 7 | INSTALL_MOD=@pkglibdir@ 8 | 9 | Name: lighttpd2 10 | Description: modules for lighttpd2 11 | Version: ${version} 12 | Requires: glib-2.0 13 | Libs: -module -export-dynamic -avoid-version -no-undefined -L${libdir} -llighttpd2-common-${version} -llighttpd2-shared-${version} -lev 14 | Cflags: -I${includedir} 15 | -------------------------------------------------------------------------------- /src/main/base_lua.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #ifdef HAVE_LUA_H 5 | # include 6 | # include 7 | #endif 8 | 9 | #ifdef HAVE_LUA_H 10 | 11 | void li_lua_init(liLuaState* LL, liServer* srv, liWorker* wrk) { 12 | lua_State *L = LL->L = luaL_newstate(); 13 | 14 | lua_pushlightuserdata(L, LL); 15 | lua_setfield(L, LUA_REGISTRYINDEX, LI_LUA_REGISTRY_STATE); 16 | 17 | luaL_openlibs(LL->L); 18 | li_lua_init2(LL, srv, wrk); 19 | 20 | g_static_rec_mutex_init(&LL->lualock); 21 | } 22 | 23 | void li_lua_clear(liLuaState* LL) { 24 | lua_close(LL->L); 25 | LL->L = NULL; 26 | 27 | g_static_rec_mutex_free(&LL->lualock); 28 | } 29 | 30 | #else 31 | 32 | void li_lua_init(liLuaState* LL, liServer* srv, liWorker* wrk) { 33 | UNUSED(srv); 34 | UNUSED(wrk); 35 | 36 | LL->L = NULL; 37 | g_static_rec_mutex_init(&LL->lualock); 38 | } 39 | 40 | void li_lua_clear(liLuaState* LL) { 41 | g_static_rec_mutex_free(&LL->lualock); 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/main/http_range_parser.rl: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | %%{ 8 | machine http_range_parser; 9 | variable cs s->cs; 10 | variable p s->data_pos; 11 | 12 | SP = ' '; 13 | HT = '\t'; 14 | 15 | ws = SP | HT; 16 | 17 | action int_start { 18 | tmp = 0; 19 | } 20 | action int_step { 21 | int d = fc - '0'; 22 | if (tmp > (G_MAXOFFSET-10)/10) { 23 | s->cs = http_range_parser_error; 24 | return LI_PARSE_HTTP_RANGE_INVALID; 25 | } 26 | tmp = 10*tmp + d; 27 | } 28 | 29 | int = (digit digit**) >int_start $int_step ; 30 | 31 | action first_byte { 32 | s->range_start = tmp; 33 | } 34 | action last_byte { 35 | s->range_end = tmp; 36 | found = TRUE; 37 | } 38 | action last_byte_empty { 39 | s->range_end = s->limit - 1; 40 | found = TRUE; 41 | } 42 | action suffix_range { 43 | s->range_end = s->limit - 1; 44 | s->range_start = s->limit - tmp; 45 | found = TRUE; 46 | } 47 | action range_complete { 48 | fbreak; 49 | } 50 | 51 | range = (int %first_byte ws* "-" ws** (int %last_byte | "" %last_byte_empty) | "-" ws* int %suffix_range) ; 52 | 53 | main := ws* "bytes" ws* "=" (ws | ",")* range ( ws* "," >range_complete (ws | ",")* range)** (ws | ",")*; 54 | 55 | write data nofinal; 56 | }%% 57 | 58 | liParseHttpRangeResult li_parse_http_range_next(liParseHttpRangeState* s) { 59 | const char *pe, *eof; 60 | goffset tmp = 0; 61 | 62 | if (s->cs == http_range_parser_error) { 63 | return LI_PARSE_HTTP_RANGE_INVALID; 64 | } 65 | 66 | eof = pe = s->data->str + s->data->len; 67 | 68 | for ( ;; ) { 69 | gboolean found = FALSE; 70 | 71 | if (s->data_pos >= eof) { 72 | return s->found_valid_range ? LI_PARSE_HTTP_RANGE_DONE : LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE; 73 | } 74 | 75 | %% write exec; 76 | 77 | if (s->cs == http_range_parser_error) { 78 | return LI_PARSE_HTTP_RANGE_INVALID; 79 | } 80 | 81 | if (s->data_pos >= eof) { 82 | s->last_range = TRUE; 83 | } 84 | 85 | if (!found) { 86 | return s->found_valid_range ? LI_PARSE_HTTP_RANGE_DONE : LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE; 87 | } 88 | 89 | if (s->range_end >= s->limit) { 90 | s->range_end = s->limit - 1; 91 | } 92 | if (s->range_start < 0) { 93 | s->range_start = 0; 94 | } 95 | 96 | if (s->range_start <= s->range_end) { 97 | s->found_valid_range = TRUE; 98 | s->range_length = s->range_end - s->range_start + 1; 99 | return LI_PARSE_HTTP_RANGE_OK; 100 | } 101 | } 102 | } 103 | 104 | void li_parse_http_range_init(liParseHttpRangeState* s, const GString *range_str, goffset limit) { 105 | s->data = g_string_new_len(GSTR_LEN(range_str)); 106 | s->data_pos = s->data->str; 107 | s->limit = limit; 108 | s->last_range = FALSE; 109 | s->found_valid_range = FALSE; 110 | 111 | (void) http_range_parser_en_main; 112 | %% write init; 113 | } 114 | 115 | void li_parse_http_range_clear(liParseHttpRangeState* s) { 116 | if (s->data) { 117 | g_string_free(s->data, TRUE); 118 | s->data = NULL; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/meson.build: -------------------------------------------------------------------------------- 1 | src_shared = [ 2 | 'angel.c', 3 | 'angel_fake.c', 4 | 'actions.c', 5 | 'base_lua.c', 6 | 'backends.c', 7 | 'chunk.c', 8 | 'chunk_parser.c', 9 | 'collect.c', 10 | 'condition.c', 11 | 'connection.c', 12 | 'environment.c', 13 | 'etag.c', 14 | 'filter.c', 15 | 'filter_chunked.c', 16 | 'filter_buffer_on_disk.c', 17 | 'http_headers.c', 18 | 'lighttpd_glue.c', 19 | 'log.c', 20 | 'mimetype.c', 21 | 'network.c', 22 | 'network_write.c', 23 | 'network_writev.c', 24 | 'network_sendfile.c', 25 | 'options.c', 26 | 'pattern.c', 27 | 'plugin.c', 28 | 'request.c', 29 | 'response.c', 30 | 'server.c', 31 | 'stat_cache.c', 32 | 'stream.c', 33 | 'stream_http_response.c', 34 | 'stream_simple_socket.c', 35 | 'throttle.c', 36 | 'value.c', 37 | 'virtualrequest.c', 38 | 'worker.c', 39 | 'plugin_core.c', 40 | ] + ragel_gen.process( 41 | 'http_range_parser.rl', 42 | 'http_request_parser.rl', 43 | 'http_response_parser.rl', 44 | 'url_parser.rl', 45 | ) 46 | 47 | if not get_option('config-parser') 48 | conf_data.set10('WITHOUT_CONFIG_PARSER', true) 49 | else 50 | src_shared += ragel_gen_t0.process('config_parser.rl') 51 | endif 52 | 53 | if get_option('lua') 54 | src_shared += [ 55 | 'actions_lua.c', 56 | 'condition_lua.c', 57 | 'config_lua.c', 58 | 'value_lua.c', 59 | 'chunk_lua.c', 60 | 'core_lua.c', 61 | 'environment_lua.c', 62 | 'filters_lua.c', 63 | 'http_headers_lua.c', 64 | 'physical_lua.c', 65 | 'request_lua.c', 66 | 'response_lua.c', 67 | 'stat_lua.c', 68 | # 'subrequest_lua.c', 69 | 'virtualrequest_lua.c', 70 | ] 71 | endif 72 | 73 | lib_shared = library( 74 | 'lighttpd2-shared-' + meson.project_version(), 75 | src_shared, 76 | include_directories: [inc_dir] + search_includes, 77 | dependencies: [ 78 | main_deps, 79 | opt_dep_lua, 80 | lib_m, # TODO: fmod in throttle.c 81 | ], 82 | link_with: lib_common, 83 | install: true, 84 | ) 85 | 86 | bin_worker = executable( 87 | 'lighttpd2-worker', 88 | 'lighttpd_worker.c', 89 | include_directories: [inc_dir] + search_includes, 90 | dependencies: [ 91 | main_deps, 92 | opt_dep_lua, 93 | ], 94 | link_with: [ 95 | lib_shared, 96 | lib_common, 97 | ], 98 | install: true, 99 | install_dir: libexec_dir, 100 | ) 101 | -------------------------------------------------------------------------------- /src/main/network_write.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | liNetworkStatus li_network_backend_write(int fd, liChunkQueue *cq, goffset *write_max, GError **err) { 5 | const ssize_t blocksize = 16*1024; /* 16k */ 6 | char *block_data; 7 | off_t block_len; 8 | ssize_t r; 9 | gboolean did_write_something = FALSE; 10 | liChunkIter ci; 11 | 12 | do { 13 | if (0 == cq->length) 14 | return did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_FATAL_ERROR; 15 | 16 | ci = li_chunkqueue_iter(cq); 17 | switch (li_chunkiter_read(ci, 0, blocksize, &block_data, &block_len, err)) { 18 | case LI_HANDLER_GO_ON: 19 | break; 20 | case LI_HANDLER_ERROR: 21 | default: 22 | return LI_NETWORK_STATUS_FATAL_ERROR; 23 | } 24 | 25 | if (-1 == (r = li_net_write(fd, block_data, block_len))) { 26 | switch (errno) { 27 | case EAGAIN: 28 | #if EWOULDBLOCK != EAGAIN 29 | case EWOULDBLOCK: 30 | #endif 31 | return did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT; 32 | case ECONNRESET: 33 | case EPIPE: 34 | case ETIMEDOUT: 35 | return LI_NETWORK_STATUS_CONNECTION_CLOSE; 36 | default: 37 | g_set_error(err, LI_NETWORK_ERROR, 0, "li_network_backend_write: oops, write to fd=%d failed: %s", fd, g_strerror(errno)); 38 | return LI_NETWORK_STATUS_FATAL_ERROR; 39 | } 40 | } else if (0 == r) { 41 | return did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT; 42 | } 43 | 44 | li_chunkqueue_skip(cq, r); 45 | did_write_something = TRUE; 46 | *write_max -= r; 47 | } while (r == block_len && *write_max > 0); 48 | 49 | return LI_NETWORK_STATUS_SUCCESS; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/options.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | subdir('common') 2 | subdir('angel') 3 | subdir('main') 4 | subdir('modules') 5 | subdir('unittests') 6 | -------------------------------------------------------------------------------- /src/modules/fastcgi_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_FASTCGI_STREAM_H_ 2 | #define _LIGHTTPD_FASTCGI_STREAM_H_ 3 | 4 | #include 5 | #include 6 | 7 | typedef struct liFastCGIBackendCallbacks liFastCGIBackendCallbacks; 8 | typedef struct liFastCGIBackendWait liFastCGIBackendWait; 9 | typedef struct liFastCGIBackendConnection liFastCGIBackendConnection; 10 | typedef struct liFastCGIBackendPool liFastCGIBackendPool; 11 | typedef struct liFastCGIBackendConfig liFastCGIBackendConfig; 12 | 13 | typedef void (*liFastCGIBackendConnectionResetCB)(liVRequest *vr, liFastCGIBackendPool *pool, liFastCGIBackendConnection *bcon); 14 | typedef void (*liFastCGIBackendConnectionEndRequestCB)(liVRequest *vr, liFastCGIBackendPool *pool, liFastCGIBackendConnection *bcon, guint32 appStatus); 15 | typedef void (*liFastCGIBackendConnectionStderrCB)(liVRequest *vr, liFastCGIBackendPool *pool, liFastCGIBackendConnection *bcon, GString *message); 16 | 17 | 18 | struct liFastCGIBackendConnection { 19 | gpointer data; 20 | }; 21 | 22 | struct liFastCGIBackendCallbacks { 23 | liFastCGIBackendConnectionResetCB reset_cb; 24 | liFastCGIBackendConnectionEndRequestCB end_request_cb; 25 | liFastCGIBackendConnectionStderrCB fastcgi_stderr_cb; 26 | }; 27 | 28 | struct liFastCGIBackendPool { 29 | liBackendPool *subpool; 30 | }; 31 | 32 | struct liFastCGIBackendConfig { 33 | const liFastCGIBackendCallbacks *callbacks; 34 | 35 | /* see liBackendConfig */ 36 | liSocketAddress sock_addr; 37 | int max_connections; 38 | guint idle_timeout; 39 | guint connect_timeout; 40 | guint wait_timeout; 41 | guint disable_time; 42 | int max_requests; 43 | }; 44 | 45 | /* config gets copied, can be freed after this call */ 46 | LI_API liFastCGIBackendPool* li_fastcgi_backend_pool_new(const liFastCGIBackendConfig *config); 47 | LI_API void li_fastcgi_backend_pool_free(liFastCGIBackendPool *bpool); 48 | 49 | LI_API liBackendResult li_fastcgi_backend_get(liVRequest *vr, liFastCGIBackendPool *bpool, liFastCGIBackendConnection **pbcon, liFastCGIBackendWait **pbwait); 50 | LI_API void li_fastcgi_backend_wait_stop(liVRequest *vr, liFastCGIBackendPool *bpool, liFastCGIBackendWait **pbwait); 51 | 52 | /* only call from reset or end_request callbacks */ 53 | LI_API void li_fastcgi_backend_put(liFastCGIBackendConnection *bcon); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/modules/gnutls_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_GNUTLS_FILTER_H_ 2 | #define _LIGHTTPD_GNUTLS_FILTER_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | typedef struct liGnuTLSFilter liGnuTLSFilter; 9 | 10 | typedef void (*liGnuTLSFilterHandshakeCB)(liGnuTLSFilter *f, gpointer data, liStream *plain_source, liStream *plain_drain); 11 | typedef void (*liGnuTLSFilterClosedCB)(liGnuTLSFilter *f, gpointer data); 12 | typedef int (*liGnuTLSFilterPostClientHelloCB)(liGnuTLSFilter *f, gpointer data); 13 | 14 | typedef struct liGnuTLSFilterCallbacks liGnuTLSFilterCallbacks; 15 | struct liGnuTLSFilterCallbacks { 16 | liGnuTLSFilterHandshakeCB handshake_cb; /* called after initial handshake is done */ 17 | liGnuTLSFilterClosedCB closed_cb; 18 | liGnuTLSFilterPostClientHelloCB post_client_hello_cb; 19 | }; 20 | 21 | LI_API liGnuTLSFilter* li_gnutls_filter_new( 22 | liServer *srv, liWorker *wrk, 23 | const liGnuTLSFilterCallbacks *callbacks, gpointer data, 24 | gnutls_session_t session, liStream *crypt_source, liStream *crypt_drain); 25 | 26 | /* doesn't call closed_cb; but you can call this from closed_cb */ 27 | LI_API void li_gnutls_filter_free(liGnuTLSFilter *f); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/modules/gnutls_ocsp.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_GNUTLS_OCSP_H_ 2 | #define _LIGHTTPD_GNUTLS_OCSP_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | typedef struct liGnuTLSOCSP liGnuTLSOCSP; 9 | 10 | LI_API liGnuTLSOCSP* li_gnutls_ocsp_new(void); 11 | 12 | /* doesn't call closed_cb; but you can call this from closed_cb */ 13 | LI_API void li_gnutls_ocsp_free(liGnuTLSOCSP *ocsp); 14 | 15 | LI_API void li_gnutls_ocsp_use(liGnuTLSOCSP *ocsp, gnutls_certificate_credentials_t creds); 16 | 17 | /* load DER or PEM ("OCSP RESPONSE") encoded ocsp response */ 18 | LI_API gboolean li_gnutls_ocsp_add(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename); 19 | 20 | /* search in PEM file for a OCSP RESPONSE block and add it if there is one; 21 | * returns only FALSE if a block was found which COULDN'T be loaded 22 | */ 23 | LI_API gboolean li_gnutls_ocsp_search(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename); 24 | 25 | /* search in PEM datum for a OCSP RESPONSE block and add it if there is one; 26 | * returns only FALSE if a block was found which COULDN'T be loaded 27 | */ 28 | LI_API gboolean li_gnutls_ocsp_search_datum(liServer *srv, liGnuTLSOCSP *ocsp, gnutls_datum_t const* file); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/modules/meson.build: -------------------------------------------------------------------------------- 1 | modules = { 2 | 'access': { 3 | 'sources': ['mod_access.c'], 4 | }, 5 | 'accesslog': { 6 | 'sources': ['mod_accesslog.c'], 7 | }, 8 | 'auth': { 9 | 'sources': ['mod_auth.c'], 10 | }, 11 | 'balance': { 12 | 'sources': ['mod_balance.c'], 13 | }, 14 | 'cache_disk_etag': { 15 | 'sources': ['mod_cache_disk_etag.c'], 16 | }, 17 | 'debug': { 18 | 'sources': ['mod_debug.c'], 19 | }, 20 | 'deflate': { 21 | 'sources': ['mod_deflate.c'], 22 | 'dependencies': [dep_deflate], 23 | }, 24 | 'dirlist': { 25 | 'sources': ['mod_dirlist.c'], 26 | }, 27 | 'expire': { 28 | 'sources': ['mod_expire.c'], 29 | }, 30 | 'fastcgi': { 31 | 'sources': ['mod_fastcgi.c', 'fastcgi_stream.c'], 32 | }, 33 | 'flv': { 34 | 'sources': ['mod_flv.c'], 35 | }, 36 | 'fortune': { 37 | 'sources': ['mod_fortune.c'], 38 | }, 39 | 'gnutls': { 40 | 'sources': ['mod_gnutls.c', 'gnutls_filter.c', 'gnutls_ocsp.c'], 41 | 'dependencies': [dep_gnutls, opt_dep_idn], 42 | }, 43 | 'limit': { 44 | 'sources': ['mod_limit.c'], 45 | }, 46 | 'lua': { 47 | 'sources': ['mod_lua.c'], 48 | 'dependencies': [dep_lua], 49 | }, 50 | 'memcached': { 51 | 'sources': ['mod_memcached.c'], 52 | 'dependencies': [opt_dep_lua], 53 | }, 54 | 'openssl': { 55 | 'sources': ['mod_openssl.c', 'openssl_filter.c'], 56 | 'dependencies': [dep_openssl, opt_dep_idn], 57 | }, 58 | 'progress': { 59 | 'sources': ['mod_progress.c'], 60 | }, 61 | 'proxy': { 62 | 'sources': ['mod_proxy.c'], 63 | }, 64 | 'redirect': { 65 | 'sources': ['mod_redirect.c'], 66 | }, 67 | 'rewrite': { 68 | 'sources': ['mod_rewrite.c'], 69 | }, 70 | 'scgi': { 71 | 'sources': ['mod_scgi.c'], 72 | }, 73 | 'status': { 74 | 'sources': ['mod_status.c'], 75 | }, 76 | 'throttle': { 77 | 'sources': ['mod_throttle.c'], 78 | }, 79 | 'userdir': { 80 | 'sources': ['mod_userdir.c'], 81 | }, 82 | 'vhost': { 83 | 'sources': ['mod_vhost.c'], 84 | }, 85 | } 86 | 87 | modules_build_dir = meson.current_build_dir() 88 | 89 | all_modules = [] 90 | enabled_modules = [] 91 | 92 | foreach name, def: modules 93 | mod_deps = def.get('dependencies', []) 94 | expect_mod = true 95 | foreach dep: mod_deps 96 | if not dep.found() 97 | expect_mod = false 98 | endif 99 | endforeach 100 | mod = shared_module( 101 | 'mod_' + name, 102 | def['sources'], 103 | dependencies: main_deps + mod_deps, 104 | c_args: def.get('c_args', []), 105 | include_directories: [inc_dir] + search_includes, 106 | install: true, 107 | install_dir: modules_dir, 108 | ) 109 | all_modules += mod 110 | if expect_mod 111 | enabled_modules += mod 112 | endif 113 | endforeach 114 | -------------------------------------------------------------------------------- /src/modules/openssl_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIGHTTPD_OPENSSL_FILTER_H_ 2 | #define _LIGHTTPD_OPENSSL_FILTER_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | typedef struct liOpenSSLFilter liOpenSSLFilter; 9 | 10 | typedef void (*liOpenSSLFilterHandshakeCB)(liOpenSSLFilter *f, gpointer data, liStream *plain_source, liStream *plain_drain); 11 | typedef void (*liOpenSSLFilterClosedCB)(liOpenSSLFilter *f, gpointer data); 12 | 13 | typedef struct liOpenSSLFilterCallbacks liOpenSSLFilterCallbacks; 14 | struct liOpenSSLFilterCallbacks { 15 | liOpenSSLFilterHandshakeCB handshake_cb; /* called after initial handshake is done */ 16 | liOpenSSLFilterClosedCB closed_cb; 17 | }; 18 | 19 | LI_API liOpenSSLFilter* li_openssl_filter_new( 20 | liServer *srv, liWorker *wrk, 21 | const liOpenSSLFilterCallbacks *callbacks, gpointer data, 22 | SSL_CTX *ssl_ctx, liStream *crypt_source, liStream *crypt_drain); 23 | 24 | /* doesn't call closed_cb; but you can call this from closed_cb */ 25 | LI_API void li_openssl_filter_free(liOpenSSLFilter *f); 26 | 27 | LI_API SSL* li_openssl_filter_ssl(liOpenSSLFilter *f); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/unittests/meson.build: -------------------------------------------------------------------------------- 1 | unittests = { 2 | 'Chunk-UnitTest': { 3 | 'binary': 'test-chunk', 4 | 'sources': ['test-chunk.c'], 5 | }, 6 | 'HttpRequestParser-UnitTest': { 7 | 'binary': 'test-http-request-parser', 8 | 'sources': ['test-http-request-parser.c'], 9 | }, 10 | 'IpParser-UnitTest': { 11 | 'binary': 'test-ip-parser', 12 | 'sources': ['test-ip-parser.c'], 13 | }, 14 | 'Radix-UnitTest': { 15 | 'binary': 'test-radix', 16 | 'sources': ['test-radix.c'], 17 | }, 18 | 'RangeParser-UnitTest': { 19 | 'binary': 'test-range-parser', 20 | 'sources': ['test-range-parser.c'], 21 | }, 22 | 'Utils-UnitTest': { 23 | 'binary': 'test-utils', 24 | 'sources': ['test-utils.c'], 25 | }, 26 | } 27 | 28 | foreach name, def: unittests 29 | test_bin = executable( 30 | def['binary'], 31 | def['sources'], 32 | include_directories: [inc_dir] + search_includes, 33 | dependencies: main_deps + def.get('dependencies', []), 34 | link_with: [ 35 | lib_shared, 36 | lib_common, 37 | ], 38 | build_by_default: false, 39 | ) 40 | test( 41 | name, 42 | test_bin, 43 | protocol: 'tap', 44 | ) 45 | endforeach 46 | -------------------------------------------------------------------------------- /src/unittests/test-chunk.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define perror(msg) g_error("(%s:%i) %s failed: %s", __FILE__, __LINE__, msg, g_strerror(errno)) 5 | 6 | #if 0 7 | static liChunkQueue* cq_from_str(const gchar *s, size_t len) { 8 | liChunkQueue *cq = li_chunkqueue_new(); 9 | li_chunkqueue_append_mem(cq, s, len); 10 | return cq; 11 | } 12 | #endif 13 | 14 | static void cq_load_str(liChunkQueue *cq, const gchar *s, size_t len) { 15 | li_chunkqueue_reset(cq); 16 | li_chunkqueue_append_mem(cq, s, len); 17 | } 18 | 19 | static void cq_assert_eq(liChunkQueue *cq, const gchar *s, size_t len) { 20 | GString *buf = g_string_sized_new(cq->length); 21 | g_assert(li_chunkqueue_extract_to(cq, cq->length, buf, NULL)); 22 | g_assert(0 == memcmp(s, buf->str, len)); 23 | g_string_free(buf, TRUE); 24 | } 25 | 26 | 27 | static void test_filter_chunked_decode(void) { 28 | liChunkQueue *cq = li_chunkqueue_new(), *cq2 = li_chunkqueue_new(); 29 | liFilterChunkedDecodeState decode_state; 30 | 31 | cq_load_str(cq, CONST_STR_LEN( 32 | "14\r\n" 33 | "01234567890123456789" "\r\n" 34 | "0\r\nrandom foo: xx\r\n\r\n" 35 | "xxx" /* next message */ 36 | )); 37 | cq->is_closed = TRUE; 38 | memset(&decode_state, 0, sizeof(decode_state)); 39 | li_chunkqueue_reset(cq2); 40 | g_assert(li_filter_chunked_decode(NULL, cq2, cq, &decode_state)); 41 | cq_assert_eq(cq2, CONST_STR_LEN( 42 | "01234567890123456789" 43 | )); 44 | g_assert(cq2->is_closed); 45 | cq_assert_eq(cq, CONST_STR_LEN( 46 | "xxx" 47 | )); 48 | 49 | li_chunkqueue_free(cq); 50 | li_chunkqueue_free(cq2); 51 | } 52 | 53 | int main(int argc, char **argv) { 54 | g_test_init(&argc, &argv, NULL); 55 | 56 | g_test_add_func("/chunk/filter_chunked_decode", test_filter_chunked_decode); 57 | 58 | return g_test_run(); 59 | } 60 | -------------------------------------------------------------------------------- /src/unittests/test-http-request-parser.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | static void test_crlf_newlines(void) { 6 | liRequest req; 7 | liHttpRequestCtx http_req_ctx; 8 | liChunkQueue* cq = li_chunkqueue_new(); 9 | liHandlerResult res; 10 | 11 | li_chunkqueue_append_mem(cq, CONST_STR_LEN( 12 | "GET / HTTP/1.0\r\n" 13 | "Host: www.example.com\r\n" 14 | "\r\n" 15 | "\ntrash")); 16 | li_request_init(&req); 17 | li_http_request_parser_init(&http_req_ctx, &req, cq); 18 | 19 | res = li_http_request_parse(NULL, &http_req_ctx); 20 | if (LI_HANDLER_GO_ON != res) g_error("li_http_request_parse didn't finish parsing or failed: %i", res); 21 | 22 | g_assert(6 == cq->length); 23 | g_assert(li_http_header_is(req.headers, CONST_STR_LEN("host"), CONST_STR_LEN("www.example.com"))); 24 | 25 | li_chunkqueue_free(cq); 26 | li_http_request_parser_clear(&http_req_ctx); 27 | li_request_clear(&req); 28 | } 29 | 30 | static void test_lf_newlines(void) { 31 | liRequest req; 32 | liHttpRequestCtx http_req_ctx; 33 | liChunkQueue* cq = li_chunkqueue_new(); 34 | liHandlerResult res; 35 | 36 | li_chunkqueue_append_mem(cq, CONST_STR_LEN( 37 | "GET / HTTP/1.0\n" 38 | "Host: www.example.com\n" 39 | "\n" 40 | "\rtrash")); 41 | li_request_init(&req); 42 | li_http_request_parser_init(&http_req_ctx, &req, cq); 43 | 44 | res = li_http_request_parse(NULL, &http_req_ctx); 45 | if (LI_HANDLER_GO_ON != res) g_error("li_http_request_parse didn't finish parsing or failed: %i", res); 46 | 47 | g_assert(6 == cq->length); 48 | g_assert(li_http_header_is(req.headers, CONST_STR_LEN("host"), CONST_STR_LEN("www.example.com"))); 49 | 50 | li_chunkqueue_free(cq); 51 | li_http_request_parser_clear(&http_req_ctx); 52 | li_request_clear(&req); 53 | } 54 | 55 | int main(int argc, char **argv) { 56 | g_test_init(&argc, &argv, NULL); 57 | 58 | g_test_add_func("/http-request-parser/crlf_newlines", test_crlf_newlines); 59 | g_test_add_func("/http-request-parser/lf_newlines", test_lf_newlines); 60 | 61 | return g_test_run(); 62 | } 63 | -------------------------------------------------------------------------------- /src/unittests/test-ip-parser.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define perror(msg) g_error("(%s:%i) %s failed: %s", __FILE__, __LINE__, msg, g_strerror(errno)) 5 | 6 | 7 | typedef struct { 8 | struct { 9 | guint32 addr; 10 | guint32 networkmask; 11 | guint16 port; 12 | } ipv4; 13 | struct { 14 | guint8 addr[16]; 15 | guint network; 16 | guint16 port; 17 | } ipv6; 18 | struct { 19 | GString *path; 20 | } unix_socket; 21 | } netrange; 22 | 23 | static void test_ipv4_net1(void) { 24 | netrange range; 25 | liSocketAddress addr; 26 | const char str0[] = "0.0.0.0/0:80"; 27 | GString str1 = li_const_gstring(CONST_STR_LEN("127.0.0.1")); 28 | struct sockaddr_in *ipv4; 29 | 30 | g_assert(!li_parse_ipv6(str0, range.ipv6.addr, &range.ipv6.network, &range.ipv6.port)); 31 | g_assert(li_parse_ipv4(str0, &range.ipv4.addr, &range.ipv4.networkmask, &range.ipv4.port)); 32 | 33 | g_assert_cmpuint(range.ipv4.addr, ==, 0); 34 | g_assert_cmpuint(range.ipv4.networkmask, ==, 0); 35 | g_assert_cmpuint(range.ipv4.port, ==, 80); 36 | 37 | addr = li_sockaddr_from_string(&str1, 80); 38 | g_assert(addr.addr_up.raw); 39 | 40 | g_assert_cmpuint(AF_INET, ==, addr.addr_up.plain->sa_family); 41 | ipv4 = addr.addr_up.ipv4; 42 | 43 | g_assert_cmpuint(ipv4->sin_addr.s_addr, ==, htonl(0x7f000001u)); 44 | 45 | g_assert(li_ipv4_in_ipv4_net(ipv4->sin_addr.s_addr, range.ipv4.addr, range.ipv4.networkmask)); 46 | g_assert_cmpuint(ipv4->sin_port, ==, htons(range.ipv4.port)); 47 | 48 | li_sockaddr_clear(&addr); 49 | } 50 | 51 | static void test_ipv6_net1(void) { 52 | netrange range; 53 | liSocketAddress addr; 54 | const char str0[] = "[::/0]:80"; 55 | GString str1 = li_const_gstring(CONST_STR_LEN("::1")); 56 | struct sockaddr_in6 *ipv6; 57 | 58 | g_assert(!li_parse_ipv4(str0, &range.ipv4.addr, &range.ipv4.networkmask, &range.ipv4.port)); 59 | g_assert(li_parse_ipv6(str0, range.ipv6.addr, &range.ipv6.network, &range.ipv6.port)); 60 | 61 | g_assert_cmpuint(range.ipv6.network, ==, 0); 62 | g_assert_cmpuint(range.ipv6.port, ==, 80); 63 | 64 | addr = li_sockaddr_from_string(&str1, 80); 65 | g_assert(addr.addr_up.raw); 66 | 67 | g_assert_cmpuint(AF_INET6, ==, addr.addr_up.plain->sa_family); 68 | ipv6 = addr.addr_up.ipv6; 69 | 70 | g_assert(li_ipv6_in_ipv6_net(ipv6->sin6_addr.s6_addr, range.ipv6.addr, range.ipv6.network)); 71 | g_assert_cmpuint(ipv6->sin6_port, ==, htons(range.ipv6.port)); 72 | 73 | li_sockaddr_clear(&addr); 74 | } 75 | 76 | int main(int argc, char **argv) { 77 | g_test_init(&argc, &argv, NULL); 78 | 79 | g_test_add_func("/ip-parser/test-localhost-in-all-ipv4-net", test_ipv4_net1); 80 | g_test_add_func("/ip-parser/test-localhost-in-all-ipv6-net", test_ipv6_net1); 81 | 82 | return g_test_run(); 83 | } 84 | -------------------------------------------------------------------------------- /src/unittests/test-radix.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | static void test_radix_insert_lookup(void) { 5 | static const guint magic1 = 4235; 6 | guint value; 7 | 8 | /* 127.0.0.1 */ 9 | in_addr_t ip1 = htonl(INADDR_LOOPBACK); 10 | liRadixTree *rd = li_radixtree_new(); 11 | 12 | li_radixtree_insert(rd, &ip1, 32, GUINT_TO_POINTER(magic1)); 13 | 14 | value = GPOINTER_TO_UINT(li_radixtree_lookup_exact(rd, &ip1, 32)); 15 | 16 | g_assert_cmpuint(magic1, ==, value); 17 | 18 | li_radixtree_free(rd, NULL, NULL); 19 | } 20 | 21 | static void test_radix_insert_insert_lookup(void) { 22 | static const guint magic1 = 4235 /* 0x108b */, magic2 = 59234 /* 0xe762 */; 23 | guint value; 24 | 25 | /* 127.0.0.1, 192.168.0.125 */ 26 | /* as guint32: 2130706433, 3232235645 */ 27 | in_addr_t ip1 = htonl(INADDR_LOOPBACK), ip2 = htonl( (in_addr_t) 0xC0A8007D ); 28 | liRadixTree *rd = li_radixtree_new(); 29 | 30 | li_radixtree_insert(rd, &ip1, 32, GUINT_TO_POINTER(magic1)); 31 | li_radixtree_insert(rd, &ip2, 32, GUINT_TO_POINTER(magic2)); 32 | 33 | value = GPOINTER_TO_UINT(li_radixtree_lookup_exact(rd, &ip1, 32)); 34 | 35 | g_assert_cmpuint(magic1, ==, value); 36 | 37 | value = GPOINTER_TO_UINT(li_radixtree_lookup_exact(rd, &ip2, 32)); 38 | 39 | g_assert_cmpuint(magic2, ==, value); 40 | 41 | li_radixtree_free(rd, NULL, NULL); 42 | } 43 | 44 | static void test_radix_insert_insert_del_lookup(void) { 45 | static const guint magic1 = 4235 /* 0x108b */, magic2 = 59234 /* 0xe762 */; 46 | guint value; 47 | 48 | /* 127.0.0.1, 192.168.0.125 */ 49 | /* as guint32: 2130706433, 3232235645 */ 50 | in_addr_t ip1 = htonl(INADDR_LOOPBACK), ip2 = htonl( (in_addr_t) 0xC0A8007D ); 51 | liRadixTree *rd = li_radixtree_new(); 52 | 53 | li_radixtree_insert(rd, &ip1, 32, GUINT_TO_POINTER(magic1)); 54 | li_radixtree_insert(rd, &ip2, 32, GUINT_TO_POINTER(magic2)); 55 | li_radixtree_remove(rd, &ip2, 32); 56 | 57 | value = GPOINTER_TO_UINT(li_radixtree_lookup_exact(rd, &ip1, 32)); 58 | 59 | g_assert_cmpuint(magic1, ==, value); 60 | 61 | li_radixtree_free(rd, NULL, NULL); 62 | } 63 | 64 | int main(int argc, char **argv) { 65 | g_test_init(&argc, &argv, NULL); 66 | 67 | g_test_add_func("/radix/insert-lookup", test_radix_insert_lookup); 68 | g_test_add_func("/radix/insert-insert-lookup", test_radix_insert_insert_lookup); 69 | g_test_add_func("/radix/insert-insert-del-lookup", test_radix_insert_insert_del_lookup); 70 | 71 | return g_test_run(); 72 | } 73 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | tmp* 3 | -------------------------------------------------------------------------------- /tests/CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | Please run `mypy` and `flake8 tests` in project root and fix errors/lints. 2 | -------------------------------------------------------------------------------- /tests/autowrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TESTSRC="$1" 4 | BUILD="$2" 5 | 6 | ARGS="${RUNTEST_ARGS}" 7 | 8 | exec "${TESTSRC}/runtests.py" --angel "${BUILD}/src/angel/lighttpd2" --worker "${BUILD}/src/main/lighttpd2-worker" --plugindir "${BUILD}/src/modules/.libs" $ARGS "$@" 9 | -------------------------------------------------------------------------------- /tests/ca/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICAzCCAYqgAwIBAgIBATAKBggqhkjOPQQDBDBLMScwJQYDVQQDEx5saWdodHRw 3 | ZDIgdGVzdCBzdWl0ZSBBdXRob3JpdHkxIDAeBgNVBAsTF2xpZ2h0dHBkMiB0ZXN0 4 | IHN1aXRlIENBMB4XDTI1MDEwMTEzMzIyNloXDTM0MTIzMDEzMzIyNlowSzEnMCUG 5 | A1UEAxMebGlnaHR0cGQyIHRlc3Qgc3VpdGUgQXV0aG9yaXR5MSAwHgYDVQQLExds 6 | aWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABELi 7 | eTNiaScZ2eF/OkuczjC2L2iHw3KtzsiLNxMiBlxhBH0/Lsw96s1pf/FZ5+NR+CTe 8 | 5AnHCizcIdOK7ZUgs4de3jXNsEmuADvc5WAgIhAMewLsbROkCDdMwmN9/8Xnu6NC 9 | MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMEz 10 | tottTkpfEMdgJyKaXwKTAIJrMAoGCCqGSM49BAMEA2cAMGQCMEOSIX2o5l64UiJ1 11 | desOcopAzZYEcQJvEIXwn0OAPLZ2eYBJ7n2tJMpBJALT6gV70wIwd3kFAXMbyC9J 12 | B2kD93IzgHJUaHTUCOCgixVCyuuhcEkH+sosx/3g05oV0yV1zSGR 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /tests/ca/ca.key: -------------------------------------------------------------------------------- 1 | Public Key Info: 2 | Public Key Algorithm: EC/ECDSA 3 | Key Security Level: Ultra (384 bits) 4 | 5 | curve: SECP384R1 6 | private key: 7 | 5e:ca:7f:3a:1f:16:b3:1e:7d:e9:e3:b5:cc:0e:fa:74 8 | 8b:2d:46:e7:f1:93:84:73:b7:dd:19:d5:71:ef:b2:89 9 | 06:16:22:b9:73:35:ff:f3:0e:82:4e:56:fd:99:df:31 10 | 11 | 12 | x: 13 | 42:e2:79:33:62:69:27:19:d9:e1:7f:3a:4b:9c:ce:30 14 | b6:2f:68:87:c3:72:ad:ce:c8:8b:37:13:22:06:5c:61 15 | 04:7d:3f:2e:cc:3d:ea:cd:69:7f:f1:59:e7:e3:51:f8 16 | 17 | 18 | y: 19 | 24:de:e4:09:c7:0a:2c:dc:21:d3:8a:ed:95:20:b3:87 20 | 5e:de:35:cd:b0:49:ae:00:3b:dc:e5:60:20:22:10:0c 21 | 7b:02:ec:6d:13:a4:08:37:4c:c2:63:7d:ff:c5:e7:bb 22 | 23 | 24 | 25 | Public Key PIN: 26 | pin-sha256:lMWzBgixjgubOuTReiygfOlYjtIjm+GpneEegi92kAM= 27 | Public Key ID: 28 | sha256:94c5b30608b18e0b9b3ae4d17a2ca07ce9588ed2239be1a99de11e822f769003 29 | sha1:c133b68b6d4e4a5f10c76027229a5f029300826b 30 | 31 | -----BEGIN EC PRIVATE KEY----- 32 | MIGkAgEBBDBeyn86HxazHn3p47XMDvp0iy1G5/GThHO33RnVce+yiQYWIrlzNf/z 33 | DoJOVv2Z3zGgBwYFK4EEACKhZANiAARC4nkzYmknGdnhfzpLnM4wti9oh8Nyrc7I 34 | izcTIgZcYQR9Py7MPerNaX/xWefjUfgk3uQJxwos3CHTiu2VILOHXt41zbBJrgA7 35 | 3OVgICIQDHsC7G0TpAg3TMJjff/F57s= 36 | -----END EC PRIVATE KEY----- 37 | -------------------------------------------------------------------------------- /tests/ca/ca.pub: -------------------------------------------------------------------------------- 1 | Public Key Information: 2 | Public Key Algorithm: EC/ECDSA 3 | Algorithm Security Level: Ultra (384 bits) 4 | Curve: SECP384R1 5 | X: 6 | 42:e2:79:33:62:69:27:19:d9:e1:7f:3a:4b:9c:ce:30 7 | b6:2f:68:87:c3:72:ad:ce:c8:8b:37:13:22:06:5c:61 8 | 04:7d:3f:2e:cc:3d:ea:cd:69:7f:f1:59:e7:e3:51:f8 9 | Y: 10 | 24:de:e4:09:c7:0a:2c:dc:21:d3:8a:ed:95:20:b3:87 11 | 5e:de:35:cd:b0:49:ae:00:3b:dc:e5:60:20:22:10:0c 12 | 7b:02:ec:6d:13:a4:08:37:4c:c2:63:7d:ff:c5:e7:bb 13 | 14 | Public Key ID: 15 | sha1:c133b68b6d4e4a5f10c76027229a5f029300826b 16 | sha256:94c5b30608b18e0b9b3ae4d17a2ca07ce9588ed2239be1a99de11e822f769003 17 | Public Key PIN: 18 | pin-sha256:lMWzBgixjgubOuTReiygfOlYjtIjm+GpneEegi92kAM= 19 | 20 | 21 | -----BEGIN PUBLIC KEY----- 22 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEQuJ5M2JpJxnZ4X86S5zOMLYvaIfDcq3O 23 | yIs3EyIGXGEEfT8uzD3qzWl/8Vnn41H4JN7kCccKLNwh04rtlSCzh17eNc2wSa4A 24 | O9zlYCAiEAx7AuxtE6QIN0zCY33/xee7 25 | -----END PUBLIC KEY----- 26 | -------------------------------------------------------------------------------- /tests/ca/ca.template: -------------------------------------------------------------------------------- 1 | 2 | dn = "OU=lighttpd2 test suite CA,CN=lighttpd2 test suite Authority" 3 | 4 | serial = 01 5 | 6 | expiration_days = 3650 7 | 8 | ca 9 | cert_signing_key 10 | crl_signing_key 11 | 12 | -------------------------------------------------------------------------------- /tests/ca/client1.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICRzCCAc6gAwIBAgIUTXuH5LA/Ee7GxPU8psTxrsOB3nMwCgYIKoZIzj0EAwQw 3 | WTEuMCwGA1UEAxMlbGlnaHR0cGQyIHRlc3Qgc3VpdGUgQ2xpZW50IEF1dGhvcml0 4 | eTEnMCUGA1UECxMebGlnaHR0cGQyIHRlc3Qgc3VpdGUgQ2xpZW50IENBMB4XDTI1 5 | MDEwMTEzMzIyNloXDTM0MTIzMDEzMzIyNlowOzEQMA4GA1UEAxMHY2xpZW50MTEn 6 | MCUGA1UECxMebGlnaHR0cGQyIHRlc3Qgc3VpdGUgQ2xpZW50IENBMHYwEAYHKoZI 7 | zj0CAQYFK4EEACIDYgAED0VUvV0QW1TY0qxDrkTsln6WGJcifQdFB+uLXzBCfTwV 8 | SbCXhdY0EdowT2uVZjlDI2xHTAkSdqfSGcrkPJDhSpJYIanX8MbiNSMxQ3kY3ZyF 9 | oGpZ40PhD5O97jX3uvRLo3UwczAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG 10 | AQUFBwMCMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQU5L/1A0rcCn/DHaFHJnPl 11 | 8hgX8GcwHwYDVR0jBBgwFoAU2hsHrFdo+I7ndox2VLpzaSW3Ws4wCgYIKoZIzj0E 12 | AwQDZwAwZAIwBf9uuyS+5V0f3EROHCcXc73lPHv8BhmPt+u/5yJJhl7PqzRuFRsT 13 | t8BwnRbkm95tAjBpStZOKGgl10T4nk0VBvap7Y8rjcIzVgM5g2ppLkaLUMwmI+tu 14 | aSuQ6gOp73Yshes= 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /tests/ca/client1.key: -------------------------------------------------------------------------------- 1 | Public Key Info: 2 | Public Key Algorithm: EC/ECDSA 3 | Key Security Level: Ultra (384 bits) 4 | 5 | curve: SECP384R1 6 | private key: 7 | 00:a9:ce:10:df:bc:99:a8:5b:89:00:e6:16:5c:cc:52 8 | 09:3f:49:07:74:95:21:35:4d:00:25:b8:a4:64:0e:64 9 | 15:f6:52:29:fa:39:77:27:a9:65:90:e7:ec:de:a7:3e 10 | 00: 11 | 12 | x: 13 | 0f:45:54:bd:5d:10:5b:54:d8:d2:ac:43:ae:44:ec:96 14 | 7e:96:18:97:22:7d:07:45:07:eb:8b:5f:30:42:7d:3c 15 | 15:49:b0:97:85:d6:34:11:da:30:4f:6b:95:66:39:43 16 | 17 | 18 | y: 19 | 23:6c:47:4c:09:12:76:a7:d2:19:ca:e4:3c:90:e1:4a 20 | 92:58:21:a9:d7:f0:c6:e2:35:23:31:43:79:18:dd:9c 21 | 85:a0:6a:59:e3:43:e1:0f:93:bd:ee:35:f7:ba:f4:4b 22 | 23 | 24 | 25 | Public Key PIN: 26 | pin-sha256:tiP++hy/M1YPIiNHrKyqWySy8mpf+mrPCwGyI/HOTrs= 27 | Public Key ID: 28 | sha256:b623fefa1cbf33560f222347acacaa5b24b2f26a5ffa6acf0b01b223f1ce4ebb 29 | sha1:e4bff5034adc0a7fc31da1472673e5f21817f067 30 | 31 | -----BEGIN EC PRIVATE KEY----- 32 | MIGlAgEBBDEAqc4Q37yZqFuJAOYWXMxSCT9JB3SVITVNACW4pGQOZBX2Uin6OXcn 33 | qWWQ5+zepz4AoAcGBSuBBAAioWQDYgAED0VUvV0QW1TY0qxDrkTsln6WGJcifQdF 34 | B+uLXzBCfTwVSbCXhdY0EdowT2uVZjlDI2xHTAkSdqfSGcrkPJDhSpJYIanX8Mbi 35 | NSMxQ3kY3ZyFoGpZ40PhD5O97jX3uvRL 36 | -----END EC PRIVATE KEY----- 37 | -------------------------------------------------------------------------------- /tests/ca/client1.pub: -------------------------------------------------------------------------------- 1 | Public Key Information: 2 | Public Key Algorithm: EC/ECDSA 3 | Algorithm Security Level: Ultra (384 bits) 4 | Curve: SECP384R1 5 | X: 6 | 0f:45:54:bd:5d:10:5b:54:d8:d2:ac:43:ae:44:ec:96 7 | 7e:96:18:97:22:7d:07:45:07:eb:8b:5f:30:42:7d:3c 8 | 15:49:b0:97:85:d6:34:11:da:30:4f:6b:95:66:39:43 9 | Y: 10 | 23:6c:47:4c:09:12:76:a7:d2:19:ca:e4:3c:90:e1:4a 11 | 92:58:21:a9:d7:f0:c6:e2:35:23:31:43:79:18:dd:9c 12 | 85:a0:6a:59:e3:43:e1:0f:93:bd:ee:35:f7:ba:f4:4b 13 | 14 | Public Key ID: 15 | sha1:e4bff5034adc0a7fc31da1472673e5f21817f067 16 | sha256:b623fefa1cbf33560f222347acacaa5b24b2f26a5ffa6acf0b01b223f1ce4ebb 17 | Public Key PIN: 18 | pin-sha256:tiP++hy/M1YPIiNHrKyqWySy8mpf+mrPCwGyI/HOTrs= 19 | 20 | 21 | -----BEGIN PUBLIC KEY----- 22 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAED0VUvV0QW1TY0qxDrkTsln6WGJcifQdF 23 | B+uLXzBCfTwVSbCXhdY0EdowT2uVZjlDI2xHTAkSdqfSGcrkPJDhSpJYIanX8Mbi 24 | NSMxQ3kY3ZyFoGpZ40PhD5O97jX3uvRL 25 | -----END PUBLIC KEY----- 26 | -------------------------------------------------------------------------------- /tests/ca/client1.template: -------------------------------------------------------------------------------- 1 | 2 | dn = "OU=lighttpd2 test suite Client CA,CN=client1" 3 | 4 | expiration_days = 3650 5 | 6 | tls_www_client 7 | signing_key 8 | -------------------------------------------------------------------------------- /tests/ca/client_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICIDCCAaagAwIBAgIBATAKBggqhkjOPQQDBDBZMS4wLAYDVQQDEyVsaWdodHRw 3 | ZDIgdGVzdCBzdWl0ZSBDbGllbnQgQXV0aG9yaXR5MScwJQYDVQQLEx5saWdodHRw 4 | ZDIgdGVzdCBzdWl0ZSBDbGllbnQgQ0EwHhcNMjUwMTAxMTMzMjI2WhcNMzQxMjMw 5 | MTMzMjI2WjBZMS4wLAYDVQQDEyVsaWdodHRwZDIgdGVzdCBzdWl0ZSBDbGllbnQg 6 | QXV0aG9yaXR5MScwJQYDVQQLEx5saWdodHRwZDIgdGVzdCBzdWl0ZSBDbGllbnQg 7 | Q0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASS3dTmmtjhKfTQw33jA8vGC9LJkGh1 8 | hMH5N2kmE52C46cZFZwH6wZMVkcLmp3EpyASiCfuOi3RvIzUPRkrlXbvY7aL3NkT 9 | /7WhZZtn7cwFaD/NiBO5YWN5zbA6ERatOF6jQjBAMA8GA1UdEwEB/wQFMAMBAf8w 10 | DgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTaGwesV2j4jud2jHZUunNpJbdazjAK 11 | BggqhkjOPQQDBANoADBlAjAsUDPUAQQTNXMCNmxIQ3qba9ZrJSfMIVhZlDO/mUSJ 12 | Vp7nku/3lUdg6VOCCyEnv1kCMQCONCYvNaTMsn7wn6I5Euxyn++S/zwZ+eY3NrCD 13 | xSj8IeL4vDXzU39pNc9Z0nYWKzs= 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /tests/ca/client_ca.key: -------------------------------------------------------------------------------- 1 | Public Key Info: 2 | Public Key Algorithm: EC/ECDSA 3 | Key Security Level: Ultra (384 bits) 4 | 5 | curve: SECP384R1 6 | private key: 7 | 48:92:1f:d3:2a:81:6b:7e:08:c7:de:99:26:6a:c2:c2 8 | f5:28:53:01:c5:9d:ae:b7:6a:2d:db:3c:a9:b7:04:f1 9 | 20:5a:0a:35:11:09:90:eb:0c:e3:6a:45:f7:3a:f1:dd 10 | 11 | 12 | x: 13 | 00:92:dd:d4:e6:9a:d8:e1:29:f4:d0:c3:7d:e3:03:cb 14 | c6:0b:d2:c9:90:68:75:84:c1:f9:37:69:26:13:9d:82 15 | e3:a7:19:15:9c:07:eb:06:4c:56:47:0b:9a:9d:c4:a7 16 | 20: 17 | 18 | y: 19 | 12:88:27:ee:3a:2d:d1:bc:8c:d4:3d:19:2b:95:76:ef 20 | 63:b6:8b:dc:d9:13:ff:b5:a1:65:9b:67:ed:cc:05:68 21 | 3f:cd:88:13:b9:61:63:79:cd:b0:3a:11:16:ad:38:5e 22 | 23 | 24 | 25 | Public Key PIN: 26 | pin-sha256:ClC41VsR43G8CaiRcPuncIR9mdMdf8aTr3b2aZKV5Ec= 27 | Public Key ID: 28 | sha256:0a50b8d55b11e371bc09a89170fba770847d99d31d7fc693af76f6699295e447 29 | sha1:da1b07ac5768f88ee7768c7654ba736925b75ace 30 | 31 | -----BEGIN EC PRIVATE KEY----- 32 | MIGkAgEBBDBIkh/TKoFrfgjH3pkmasLC9ShTAcWdrrdqLds8qbcE8SBaCjURCZDr 33 | DONqRfc68d2gBwYFK4EEACKhZANiAASS3dTmmtjhKfTQw33jA8vGC9LJkGh1hMH5 34 | N2kmE52C46cZFZwH6wZMVkcLmp3EpyASiCfuOi3RvIzUPRkrlXbvY7aL3NkT/7Wh 35 | ZZtn7cwFaD/NiBO5YWN5zbA6ERatOF4= 36 | -----END EC PRIVATE KEY----- 37 | -------------------------------------------------------------------------------- /tests/ca/client_ca.pub: -------------------------------------------------------------------------------- 1 | Public Key Information: 2 | Public Key Algorithm: EC/ECDSA 3 | Algorithm Security Level: Ultra (384 bits) 4 | Curve: SECP384R1 5 | X: 6 | 00:92:dd:d4:e6:9a:d8:e1:29:f4:d0:c3:7d:e3:03:cb 7 | c6:0b:d2:c9:90:68:75:84:c1:f9:37:69:26:13:9d:82 8 | e3:a7:19:15:9c:07:eb:06:4c:56:47:0b:9a:9d:c4:a7 9 | 20 10 | Y: 11 | 12:88:27:ee:3a:2d:d1:bc:8c:d4:3d:19:2b:95:76:ef 12 | 63:b6:8b:dc:d9:13:ff:b5:a1:65:9b:67:ed:cc:05:68 13 | 3f:cd:88:13:b9:61:63:79:cd:b0:3a:11:16:ad:38:5e 14 | 15 | Public Key ID: 16 | sha1:da1b07ac5768f88ee7768c7654ba736925b75ace 17 | sha256:0a50b8d55b11e371bc09a89170fba770847d99d31d7fc693af76f6699295e447 18 | Public Key PIN: 19 | pin-sha256:ClC41VsR43G8CaiRcPuncIR9mdMdf8aTr3b2aZKV5Ec= 20 | 21 | 22 | -----BEGIN PUBLIC KEY----- 23 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEkt3U5prY4Sn00MN94wPLxgvSyZBodYTB 24 | +TdpJhOdguOnGRWcB+sGTFZHC5qdxKcgEogn7jot0byM1D0ZK5V272O2i9zZE/+1 25 | oWWbZ+3MBWg/zYgTuWFjec2wOhEWrThe 26 | -----END PUBLIC KEY----- 27 | -------------------------------------------------------------------------------- /tests/ca/client_ca.template: -------------------------------------------------------------------------------- 1 | 2 | dn = "OU=lighttpd2 test suite Client CA,CN=lighttpd2 test suite Client Authority" 3 | 4 | serial = 01 5 | 6 | expiration_days = 3650 7 | 8 | ca 9 | cert_signing_key 10 | crl_signing_key 11 | 12 | -------------------------------------------------------------------------------- /tests/ca/createca.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # (requires gnutls >= 3.2.7 (or >= 3.1.17 and < 3.2.0)) 6 | 7 | KEY_TYPE="${KEY_TYPE:-ecc}" 8 | HASH_ALG="${HASH_ALG:-SHA512}" 9 | 10 | function gen_rsa_key() { 11 | local name="$1" 12 | local security="${2:-high}" 13 | local secparam=(--sec-param "${security}") 14 | 15 | echo "Generate RSA key into ${name}.key and ${name}.pub" 16 | certtool -p --rsa --outfile "${name}.key" "${secparam[@]}" 17 | certtool --load-privkey "${name}.key" --pubkey-info --outfile "${name}.pub" 18 | } 19 | 20 | function gen_ecc_key() { 21 | local name="$1" 22 | local security="${2:-ultra}" 23 | local secparam=(--sec-param "${security}") 24 | 25 | echo "Generate ECC key into ${name}.key and ${name}.pub" 26 | certtool -p --ecc --outfile "${name}.key" "${secparam[@]}" 27 | certtool --load-privkey "${name}.key" --pubkey-info --outfile "${name}.pub" 28 | } 29 | 30 | function gen_key() { 31 | case "${KEY_TYPE}" in 32 | rsa) gen_rsa_key "$@" ;; 33 | ecc) gen_ecc_key "$@" ;; 34 | *) echo >&2 "Unknown key type: ${KEY_TYPE}"; exit 1 ;; 35 | esac 36 | } 37 | 38 | function ca_sign_self() { 39 | local ca_name="$1" 40 | 41 | echo "Self signing ${ca_name}" 42 | certtool -s "--hash=${HASH_ALG}" --load-privkey "${ca_name}.key" --outfile "${ca_name}.crt" --template "${ca_name}.template" 43 | } 44 | 45 | function ca_sign() { 46 | local ca_name="$1" 47 | local subject_name="$2" 48 | local key_name="${3:-${subject_name}}" 49 | 50 | echo "Signing ${subject_name} (key ${key_name}) with ${ca_name}" 51 | certtool -c "--hash=${HASH_ALG}" --load-ca-certificate "${ca_name}.crt" --load-ca-privkey "${ca_name}.key" --load-pubkey "${key_name}.pub" --outfile "${subject_name}.crt" --template "${subject_name}.template" 52 | } 53 | 54 | # gen keys 55 | gen_key "ca" 56 | gen_key "intermediate" 57 | gen_key "server" 58 | gen_key "client_ca" 59 | gen_key "client1" 60 | 61 | ca_sign_self "ca" 62 | ca_sign "ca" "intermediate" 63 | ca_sign_self "client_ca" 64 | 65 | for name in test1.ssl test2.ssl; do 66 | ca_sign "intermediate" "server_${name}" "server" 67 | 68 | echo "Generate server_${name}.pem" 69 | cat "server.key" "server_${name}.crt" "intermediate.crt" > "server_${name}.pem" 70 | done 71 | 72 | ca_sign "client_ca" "client1" 73 | -------------------------------------------------------------------------------- /tests/ca/intermediate.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICPDCCAcOgAwIBAgIURcjH78CmuAoNZVwLZka0+BmM17wwCgYIKoZIzj0EAwQw 3 | SzEnMCUGA1UEAxMebGlnaHR0cGQyIHRlc3Qgc3VpdGUgQXV0aG9yaXR5MSAwHgYD 4 | VQQLExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTAeFw0yNTAxMDExMzMyMjZaFw0z 5 | NDEyMzAxMzMyMjZaMFAxLDAqBgNVBAMTI2xpZ2h0dHBkMiB0ZXN0IHN1aXRlIElu 6 | dGVybWVkaWF0ZSAxMSAwHgYDVQQLExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2 7 | MBAGByqGSM49AgEGBSuBBAAiA2IABGAMLbh3AitDqsKwtO3b1qOwve2RzGWGs7z8 8 | qsi4Nye9+r9l6VQTnK0TUxrr/WdYOLdNmeMVtMl3tXb+m6XKnBVH442q8pipYbM7 9 | xSRLTuPDRSpKoJqZTTJcoLC/uhpLu6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNV 10 | HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFIdYZEGTpLS+h8WsSGr4m/7tnnetMB8GA1Ud 11 | IwQYMBaAFMEztottTkpfEMdgJyKaXwKTAIJrMAoGCCqGSM49BAMEA2cAMGQCMDUJ 12 | y41eYkjLnj1x9T1xvy4LPLjhQE4ZdpJ2BwNNnn/+lHgfZtxf8thQtoXbx9uHMgIw 13 | H8cWKviP+rrBLUW4JdnSvv+8voRJYpNswi+EJoee53hShIWsETixdwGM1hRGQtdB 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /tests/ca/intermediate.key: -------------------------------------------------------------------------------- 1 | Public Key Info: 2 | Public Key Algorithm: EC/ECDSA 3 | Key Security Level: Ultra (384 bits) 4 | 5 | curve: SECP384R1 6 | private key: 7 | 6c:80:9c:eb:20:30:f0:e5:2e:22:73:0b:32:6f:46:95 8 | 64:9e:fd:14:b6:32:c4:3d:58:24:0f:2c:cf:1f:98:ad 9 | f1:6c:ac:a0:ff:f0:e4:20:66:81:35:fa:f3:f9:50:95 10 | 11 | 12 | x: 13 | 60:0c:2d:b8:77:02:2b:43:aa:c2:b0:b4:ed:db:d6:a3 14 | b0:bd:ed:91:cc:65:86:b3:bc:fc:aa:c8:b8:37:27:bd 15 | fa:bf:65:e9:54:13:9c:ad:13:53:1a:eb:fd:67:58:38 16 | 17 | 18 | y: 19 | 00:b7:4d:99:e3:15:b4:c9:77:b5:76:fe:9b:a5:ca:9c 20 | 15:47:e3:8d:aa:f2:98:a9:61:b3:3b:c5:24:4b:4e:e3 21 | c3:45:2a:4a:a0:9a:99:4d:32:5c:a0:b0:bf:ba:1a:4b 22 | bb: 23 | 24 | 25 | Public Key PIN: 26 | pin-sha256:81OEBDrqLrlq9D3S34OiNpfVp9N9tQyR0O+6KPeXiFs= 27 | Public Key ID: 28 | sha256:f35384043aea2eb96af43dd2df83a23697d5a7d37db50c91d0efba28f797885b 29 | sha1:8758644193a4b4be87c5ac486af89bfeed9e77ad 30 | 31 | -----BEGIN EC PRIVATE KEY----- 32 | MIGkAgEBBDBsgJzrIDDw5S4icwsyb0aVZJ79FLYyxD1YJA8szx+YrfFsrKD/8OQg 33 | ZoE1+vP5UJWgBwYFK4EEACKhZANiAARgDC24dwIrQ6rCsLTt29ajsL3tkcxlhrO8 34 | /KrIuDcnvfq/ZelUE5ytE1Ma6/1nWDi3TZnjFbTJd7V2/pulypwVR+ONqvKYqWGz 35 | O8UkS07jw0UqSqCamU0yXKCwv7oaS7s= 36 | -----END EC PRIVATE KEY----- 37 | -------------------------------------------------------------------------------- /tests/ca/intermediate.pub: -------------------------------------------------------------------------------- 1 | Public Key Information: 2 | Public Key Algorithm: EC/ECDSA 3 | Algorithm Security Level: Ultra (384 bits) 4 | Curve: SECP384R1 5 | X: 6 | 60:0c:2d:b8:77:02:2b:43:aa:c2:b0:b4:ed:db:d6:a3 7 | b0:bd:ed:91:cc:65:86:b3:bc:fc:aa:c8:b8:37:27:bd 8 | fa:bf:65:e9:54:13:9c:ad:13:53:1a:eb:fd:67:58:38 9 | Y: 10 | 00:b7:4d:99:e3:15:b4:c9:77:b5:76:fe:9b:a5:ca:9c 11 | 15:47:e3:8d:aa:f2:98:a9:61:b3:3b:c5:24:4b:4e:e3 12 | c3:45:2a:4a:a0:9a:99:4d:32:5c:a0:b0:bf:ba:1a:4b 13 | bb 14 | 15 | Public Key ID: 16 | sha1:8758644193a4b4be87c5ac486af89bfeed9e77ad 17 | sha256:f35384043aea2eb96af43dd2df83a23697d5a7d37db50c91d0efba28f797885b 18 | Public Key PIN: 19 | pin-sha256:81OEBDrqLrlq9D3S34OiNpfVp9N9tQyR0O+6KPeXiFs= 20 | 21 | 22 | -----BEGIN PUBLIC KEY----- 23 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYAwtuHcCK0OqwrC07dvWo7C97ZHMZYaz 24 | vPyqyLg3J736v2XpVBOcrRNTGuv9Z1g4t02Z4xW0yXe1dv6bpcqcFUfjjarymKlh 25 | szvFJEtO48NFKkqgmplNMlygsL+6Gku7 26 | -----END PUBLIC KEY----- 27 | -------------------------------------------------------------------------------- /tests/ca/intermediate.template: -------------------------------------------------------------------------------- 1 | 2 | dn = "OU=lighttpd2 test suite CA,CN=lighttpd2 test suite Intermediate 1" 3 | 4 | expiration_days = 3650 5 | 6 | ca 7 | cert_signing_key 8 | crl_signing_key 9 | -------------------------------------------------------------------------------- /tests/ca/server.key: -------------------------------------------------------------------------------- 1 | Public Key Info: 2 | Public Key Algorithm: EC/ECDSA 3 | Key Security Level: Ultra (384 bits) 4 | 5 | curve: SECP384R1 6 | private key: 7 | 00:c9:a2:25:88:02:2c:62:f6:11:76:bd:b1:bf:ff:1c 8 | b8:19:5e:49:a3:df:72:d9:d0:2a:83:cc:b0:90:5e:13 9 | f2:29:59:b0:66:07:32:dd:a1:67:12:8d:91:84:9f:64 10 | ca: 11 | 12 | x: 13 | 00:fb:88:39:71:73:b5:7d:c1:17:c5:a3:17:9c:a0:48 14 | af:97:14:92:2e:9b:ff:fb:1b:16:7b:d6:d1:54:54:3e 15 | 49:be:6a:89:3a:44:ef:13:33:6b:ca:84:02:67:6d:65 16 | 16: 17 | 18 | y: 19 | 00:a9:8e:fd:01:17:a6:b7:2c:53:04:e4:d0:99:01:ce 20 | f2:50:43:43:00:66:50:e9:7d:95:c9:0d:55:54:14:93 21 | 7f:63:b8:b7:c1:a5:d9:e4:b9:57:8b:93:96:78:5c:cd 22 | b0: 23 | 24 | 25 | Public Key PIN: 26 | pin-sha256:7nnoskGn5ykPxCscQsd3tPYd2VWvmjZ5Da6yN4hLiBY= 27 | Public Key ID: 28 | sha256:ee79e8b241a7e7290fc42b1c42c777b4f61dd955af9a36790daeb237884b8816 29 | sha1:34ffccbf6c5e5904d8534af3da79f201fddc733d 30 | 31 | -----BEGIN EC PRIVATE KEY----- 32 | MIGlAgEBBDEAyaIliAIsYvYRdr2xv/8cuBleSaPfctnQKoPMsJBeE/IpWbBmBzLd 33 | oWcSjZGEn2TKoAcGBSuBBAAioWQDYgAE+4g5cXO1fcEXxaMXnKBIr5cUki6b//sb 34 | FnvW0VRUPkm+aok6RO8TM2vKhAJnbWUWqY79ARemtyxTBOTQmQHO8lBDQwBmUOl9 35 | lckNVVQUk39juLfBpdnkuVeLk5Z4XM2w 36 | -----END EC PRIVATE KEY----- 37 | -------------------------------------------------------------------------------- /tests/ca/server.pub: -------------------------------------------------------------------------------- 1 | Public Key Information: 2 | Public Key Algorithm: EC/ECDSA 3 | Algorithm Security Level: Ultra (384 bits) 4 | Curve: SECP384R1 5 | X: 6 | 00:fb:88:39:71:73:b5:7d:c1:17:c5:a3:17:9c:a0:48 7 | af:97:14:92:2e:9b:ff:fb:1b:16:7b:d6:d1:54:54:3e 8 | 49:be:6a:89:3a:44:ef:13:33:6b:ca:84:02:67:6d:65 9 | 16 10 | Y: 11 | 00:a9:8e:fd:01:17:a6:b7:2c:53:04:e4:d0:99:01:ce 12 | f2:50:43:43:00:66:50:e9:7d:95:c9:0d:55:54:14:93 13 | 7f:63:b8:b7:c1:a5:d9:e4:b9:57:8b:93:96:78:5c:cd 14 | b0 15 | 16 | Public Key ID: 17 | sha1:34ffccbf6c5e5904d8534af3da79f201fddc733d 18 | sha256:ee79e8b241a7e7290fc42b1c42c777b4f61dd955af9a36790daeb237884b8816 19 | Public Key PIN: 20 | pin-sha256:7nnoskGn5ykPxCscQsd3tPYd2VWvmjZ5Da6yN4hLiBY= 21 | 22 | 23 | -----BEGIN PUBLIC KEY----- 24 | MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+4g5cXO1fcEXxaMXnKBIr5cUki6b//sb 25 | FnvW0VRUPkm+aok6RO8TM2vKhAJnbWUWqY79ARemtyxTBOTQmQHO8lBDQwBmUOl9 26 | lckNVVQUk39juLfBpdnkuVeLk5Z4XM2w 27 | -----END PUBLIC KEY----- 28 | -------------------------------------------------------------------------------- /tests/ca/server_test1.ssl.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICdTCCAfygAwIBAgIUcrQcpa5JXvNxkPgRtiMk6atyQJ4wCgYIKoZIzj0EAwQw 3 | UDEsMCoGA1UEAxMjbGlnaHR0cGQyIHRlc3Qgc3VpdGUgSW50ZXJtZWRpYXRlIDEx 4 | IDAeBgNVBAsTF2xpZ2h0dHBkMiB0ZXN0IHN1aXRlIENBMB4XDTI1MDEwMTEzMzIy 5 | NloXDTM0MTIzMDEzMzIyNlowNjESMBAGA1UEAxMJdGVzdDEuc3NsMSAwHgYDVQQL 6 | ExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IA 7 | BPuIOXFztX3BF8WjF5ygSK+XFJIum//7GxZ71tFUVD5JvmqJOkTvEzNryoQCZ21l 8 | FqmO/QEXprcsUwTk0JkBzvJQQ0MAZlDpfZXJDVVUFJN/Y7i3waXZ5LlXi5OWeFzN 9 | sKOBsDCBrTAMBgNVHRMBAf8EAjAAMDgGA1UdEQQxMC+CDnRlc3QxLnNzbC50ZXN0 10 | gg4qLm9wZW5zc2wudGVzdIINKi5nbnV0bHMudGVzdDATBgNVHSUEDDAKBggrBgEF 11 | BQcDATAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFDT/zL9sXlkE2FNK89p58gH9 12 | 3HM9MB8GA1UdIwQYMBaAFIdYZEGTpLS+h8WsSGr4m/7tnnetMAoGCCqGSM49BAME 13 | A2cAMGQCMECcU3y8OD+uImStj6IRi7iZPp3bEPdyN2zJOnxwG3kbxguGEZTPZoMe 14 | DMsLdiQSAwIwNc60UE7OKhJJaiNgPnuMybTNvEyksakAP2LfFDpK6/XQyBOnP91J 15 | UZM4P74V6PwR 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /tests/ca/server_test1.ssl.pem: -------------------------------------------------------------------------------- 1 | Public Key Info: 2 | Public Key Algorithm: EC/ECDSA 3 | Key Security Level: Ultra (384 bits) 4 | 5 | curve: SECP384R1 6 | private key: 7 | 00:c9:a2:25:88:02:2c:62:f6:11:76:bd:b1:bf:ff:1c 8 | b8:19:5e:49:a3:df:72:d9:d0:2a:83:cc:b0:90:5e:13 9 | f2:29:59:b0:66:07:32:dd:a1:67:12:8d:91:84:9f:64 10 | ca: 11 | 12 | x: 13 | 00:fb:88:39:71:73:b5:7d:c1:17:c5:a3:17:9c:a0:48 14 | af:97:14:92:2e:9b:ff:fb:1b:16:7b:d6:d1:54:54:3e 15 | 49:be:6a:89:3a:44:ef:13:33:6b:ca:84:02:67:6d:65 16 | 16: 17 | 18 | y: 19 | 00:a9:8e:fd:01:17:a6:b7:2c:53:04:e4:d0:99:01:ce 20 | f2:50:43:43:00:66:50:e9:7d:95:c9:0d:55:54:14:93 21 | 7f:63:b8:b7:c1:a5:d9:e4:b9:57:8b:93:96:78:5c:cd 22 | b0: 23 | 24 | 25 | Public Key PIN: 26 | pin-sha256:7nnoskGn5ykPxCscQsd3tPYd2VWvmjZ5Da6yN4hLiBY= 27 | Public Key ID: 28 | sha256:ee79e8b241a7e7290fc42b1c42c777b4f61dd955af9a36790daeb237884b8816 29 | sha1:34ffccbf6c5e5904d8534af3da79f201fddc733d 30 | 31 | -----BEGIN EC PRIVATE KEY----- 32 | MIGlAgEBBDEAyaIliAIsYvYRdr2xv/8cuBleSaPfctnQKoPMsJBeE/IpWbBmBzLd 33 | oWcSjZGEn2TKoAcGBSuBBAAioWQDYgAE+4g5cXO1fcEXxaMXnKBIr5cUki6b//sb 34 | FnvW0VRUPkm+aok6RO8TM2vKhAJnbWUWqY79ARemtyxTBOTQmQHO8lBDQwBmUOl9 35 | lckNVVQUk39juLfBpdnkuVeLk5Z4XM2w 36 | -----END EC PRIVATE KEY----- 37 | -----BEGIN CERTIFICATE----- 38 | MIICdTCCAfygAwIBAgIUcrQcpa5JXvNxkPgRtiMk6atyQJ4wCgYIKoZIzj0EAwQw 39 | UDEsMCoGA1UEAxMjbGlnaHR0cGQyIHRlc3Qgc3VpdGUgSW50ZXJtZWRpYXRlIDEx 40 | IDAeBgNVBAsTF2xpZ2h0dHBkMiB0ZXN0IHN1aXRlIENBMB4XDTI1MDEwMTEzMzIy 41 | NloXDTM0MTIzMDEzMzIyNlowNjESMBAGA1UEAxMJdGVzdDEuc3NsMSAwHgYDVQQL 42 | ExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IA 43 | BPuIOXFztX3BF8WjF5ygSK+XFJIum//7GxZ71tFUVD5JvmqJOkTvEzNryoQCZ21l 44 | FqmO/QEXprcsUwTk0JkBzvJQQ0MAZlDpfZXJDVVUFJN/Y7i3waXZ5LlXi5OWeFzN 45 | sKOBsDCBrTAMBgNVHRMBAf8EAjAAMDgGA1UdEQQxMC+CDnRlc3QxLnNzbC50ZXN0 46 | gg4qLm9wZW5zc2wudGVzdIINKi5nbnV0bHMudGVzdDATBgNVHSUEDDAKBggrBgEF 47 | BQcDATAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFDT/zL9sXlkE2FNK89p58gH9 48 | 3HM9MB8GA1UdIwQYMBaAFIdYZEGTpLS+h8WsSGr4m/7tnnetMAoGCCqGSM49BAME 49 | A2cAMGQCMECcU3y8OD+uImStj6IRi7iZPp3bEPdyN2zJOnxwG3kbxguGEZTPZoMe 50 | DMsLdiQSAwIwNc60UE7OKhJJaiNgPnuMybTNvEyksakAP2LfFDpK6/XQyBOnP91J 51 | UZM4P74V6PwR 52 | -----END CERTIFICATE----- 53 | -----BEGIN CERTIFICATE----- 54 | MIICPDCCAcOgAwIBAgIURcjH78CmuAoNZVwLZka0+BmM17wwCgYIKoZIzj0EAwQw 55 | SzEnMCUGA1UEAxMebGlnaHR0cGQyIHRlc3Qgc3VpdGUgQXV0aG9yaXR5MSAwHgYD 56 | VQQLExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTAeFw0yNTAxMDExMzMyMjZaFw0z 57 | NDEyMzAxMzMyMjZaMFAxLDAqBgNVBAMTI2xpZ2h0dHBkMiB0ZXN0IHN1aXRlIElu 58 | dGVybWVkaWF0ZSAxMSAwHgYDVQQLExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2 59 | MBAGByqGSM49AgEGBSuBBAAiA2IABGAMLbh3AitDqsKwtO3b1qOwve2RzGWGs7z8 60 | qsi4Nye9+r9l6VQTnK0TUxrr/WdYOLdNmeMVtMl3tXb+m6XKnBVH442q8pipYbM7 61 | xSRLTuPDRSpKoJqZTTJcoLC/uhpLu6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNV 62 | HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFIdYZEGTpLS+h8WsSGr4m/7tnnetMB8GA1Ud 63 | IwQYMBaAFMEztottTkpfEMdgJyKaXwKTAIJrMAoGCCqGSM49BAMEA2cAMGQCMDUJ 64 | y41eYkjLnj1x9T1xvy4LPLjhQE4ZdpJ2BwNNnn/+lHgfZtxf8thQtoXbx9uHMgIw 65 | H8cWKviP+rrBLUW4JdnSvv+8voRJYpNswi+EJoee53hShIWsETixdwGM1hRGQtdB 66 | -----END CERTIFICATE----- 67 | -------------------------------------------------------------------------------- /tests/ca/server_test1.ssl.template: -------------------------------------------------------------------------------- 1 | 2 | dn = "OU=lighttpd2 test suite CA,CN=test1.ssl" 3 | dns_name = "test1.ssl.test" 4 | dns_name = "*.openssl.test" 5 | dns_name = "*.gnutls.test" 6 | 7 | expiration_days = 3650 8 | 9 | tls_www_server 10 | signing_key 11 | encryption_key 12 | -------------------------------------------------------------------------------- /tests/ca/server_test2.ssl.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICdjCCAfygAwIBAgIUSthFkk2qsTj4RNfm/uyiQpsDdzAwCgYIKoZIzj0EAwQw 3 | UDEsMCoGA1UEAxMjbGlnaHR0cGQyIHRlc3Qgc3VpdGUgSW50ZXJtZWRpYXRlIDEx 4 | IDAeBgNVBAsTF2xpZ2h0dHBkMiB0ZXN0IHN1aXRlIENBMB4XDTI1MDEwMTEzMzIy 5 | NloXDTM0MTIzMDEzMzIyNlowNjESMBAGA1UEAxMJdGVzdDIuc3NsMSAwHgYDVQQL 6 | ExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IA 7 | BPuIOXFztX3BF8WjF5ygSK+XFJIum//7GxZ71tFUVD5JvmqJOkTvEzNryoQCZ21l 8 | FqmO/QEXprcsUwTk0JkBzvJQQ0MAZlDpfZXJDVVUFJN/Y7i3waXZ5LlXi5OWeFzN 9 | sKOBsDCBrTAMBgNVHRMBAf8EAjAAMDgGA1UdEQQxMC+CDnRlc3QyLnNzbC50ZXN0 10 | gg4qLm9wZW5zc2wudGVzdIINKi5nbnV0bHMudGVzdDATBgNVHSUEDDAKBggrBgEF 11 | BQcDATAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFDT/zL9sXlkE2FNK89p58gH9 12 | 3HM9MB8GA1UdIwQYMBaAFIdYZEGTpLS+h8WsSGr4m/7tnnetMAoGCCqGSM49BAME 13 | A2gAMGUCMQDTOUcupCV7GYVaet4uIql6Qgbb5Y7sF8fQ4z9B6nBCde+u54HaijSd 14 | X9vzCKY5UqoCMDzkFiNc0AFrMHvaKUK+7mA2/8P+qiCI/de/UA/Ke7PzVWmLEDEJ 15 | daygZ2t/xp12vw== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /tests/ca/server_test2.ssl.pem: -------------------------------------------------------------------------------- 1 | Public Key Info: 2 | Public Key Algorithm: EC/ECDSA 3 | Key Security Level: Ultra (384 bits) 4 | 5 | curve: SECP384R1 6 | private key: 7 | 00:c9:a2:25:88:02:2c:62:f6:11:76:bd:b1:bf:ff:1c 8 | b8:19:5e:49:a3:df:72:d9:d0:2a:83:cc:b0:90:5e:13 9 | f2:29:59:b0:66:07:32:dd:a1:67:12:8d:91:84:9f:64 10 | ca: 11 | 12 | x: 13 | 00:fb:88:39:71:73:b5:7d:c1:17:c5:a3:17:9c:a0:48 14 | af:97:14:92:2e:9b:ff:fb:1b:16:7b:d6:d1:54:54:3e 15 | 49:be:6a:89:3a:44:ef:13:33:6b:ca:84:02:67:6d:65 16 | 16: 17 | 18 | y: 19 | 00:a9:8e:fd:01:17:a6:b7:2c:53:04:e4:d0:99:01:ce 20 | f2:50:43:43:00:66:50:e9:7d:95:c9:0d:55:54:14:93 21 | 7f:63:b8:b7:c1:a5:d9:e4:b9:57:8b:93:96:78:5c:cd 22 | b0: 23 | 24 | 25 | Public Key PIN: 26 | pin-sha256:7nnoskGn5ykPxCscQsd3tPYd2VWvmjZ5Da6yN4hLiBY= 27 | Public Key ID: 28 | sha256:ee79e8b241a7e7290fc42b1c42c777b4f61dd955af9a36790daeb237884b8816 29 | sha1:34ffccbf6c5e5904d8534af3da79f201fddc733d 30 | 31 | -----BEGIN EC PRIVATE KEY----- 32 | MIGlAgEBBDEAyaIliAIsYvYRdr2xv/8cuBleSaPfctnQKoPMsJBeE/IpWbBmBzLd 33 | oWcSjZGEn2TKoAcGBSuBBAAioWQDYgAE+4g5cXO1fcEXxaMXnKBIr5cUki6b//sb 34 | FnvW0VRUPkm+aok6RO8TM2vKhAJnbWUWqY79ARemtyxTBOTQmQHO8lBDQwBmUOl9 35 | lckNVVQUk39juLfBpdnkuVeLk5Z4XM2w 36 | -----END EC PRIVATE KEY----- 37 | -----BEGIN CERTIFICATE----- 38 | MIICdjCCAfygAwIBAgIUSthFkk2qsTj4RNfm/uyiQpsDdzAwCgYIKoZIzj0EAwQw 39 | UDEsMCoGA1UEAxMjbGlnaHR0cGQyIHRlc3Qgc3VpdGUgSW50ZXJtZWRpYXRlIDEx 40 | IDAeBgNVBAsTF2xpZ2h0dHBkMiB0ZXN0IHN1aXRlIENBMB4XDTI1MDEwMTEzMzIy 41 | NloXDTM0MTIzMDEzMzIyNlowNjESMBAGA1UEAxMJdGVzdDIuc3NsMSAwHgYDVQQL 42 | ExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IA 43 | BPuIOXFztX3BF8WjF5ygSK+XFJIum//7GxZ71tFUVD5JvmqJOkTvEzNryoQCZ21l 44 | FqmO/QEXprcsUwTk0JkBzvJQQ0MAZlDpfZXJDVVUFJN/Y7i3waXZ5LlXi5OWeFzN 45 | sKOBsDCBrTAMBgNVHRMBAf8EAjAAMDgGA1UdEQQxMC+CDnRlc3QyLnNzbC50ZXN0 46 | gg4qLm9wZW5zc2wudGVzdIINKi5nbnV0bHMudGVzdDATBgNVHSUEDDAKBggrBgEF 47 | BQcDATAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFDT/zL9sXlkE2FNK89p58gH9 48 | 3HM9MB8GA1UdIwQYMBaAFIdYZEGTpLS+h8WsSGr4m/7tnnetMAoGCCqGSM49BAME 49 | A2gAMGUCMQDTOUcupCV7GYVaet4uIql6Qgbb5Y7sF8fQ4z9B6nBCde+u54HaijSd 50 | X9vzCKY5UqoCMDzkFiNc0AFrMHvaKUK+7mA2/8P+qiCI/de/UA/Ke7PzVWmLEDEJ 51 | daygZ2t/xp12vw== 52 | -----END CERTIFICATE----- 53 | -----BEGIN CERTIFICATE----- 54 | MIICPDCCAcOgAwIBAgIURcjH78CmuAoNZVwLZka0+BmM17wwCgYIKoZIzj0EAwQw 55 | SzEnMCUGA1UEAxMebGlnaHR0cGQyIHRlc3Qgc3VpdGUgQXV0aG9yaXR5MSAwHgYD 56 | VQQLExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTAeFw0yNTAxMDExMzMyMjZaFw0z 57 | NDEyMzAxMzMyMjZaMFAxLDAqBgNVBAMTI2xpZ2h0dHBkMiB0ZXN0IHN1aXRlIElu 58 | dGVybWVkaWF0ZSAxMSAwHgYDVQQLExdsaWdodHRwZDIgdGVzdCBzdWl0ZSBDQTB2 59 | MBAGByqGSM49AgEGBSuBBAAiA2IABGAMLbh3AitDqsKwtO3b1qOwve2RzGWGs7z8 60 | qsi4Nye9+r9l6VQTnK0TUxrr/WdYOLdNmeMVtMl3tXb+m6XKnBVH442q8pipYbM7 61 | xSRLTuPDRSpKoJqZTTJcoLC/uhpLu6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNV 62 | HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFIdYZEGTpLS+h8WsSGr4m/7tnnetMB8GA1Ud 63 | IwQYMBaAFMEztottTkpfEMdgJyKaXwKTAIJrMAoGCCqGSM49BAMEA2cAMGQCMDUJ 64 | y41eYkjLnj1x9T1xvy4LPLjhQE4ZdpJ2BwNNnn/+lHgfZtxf8thQtoXbx9uHMgIw 65 | H8cWKviP+rrBLUW4JdnSvv+8voRJYpNswi+EJoee53hShIWsETixdwGM1hRGQtdB 66 | -----END CERTIFICATE----- 67 | -------------------------------------------------------------------------------- /tests/ca/server_test2.ssl.template: -------------------------------------------------------------------------------- 1 | 2 | dn = "OU=lighttpd2 test suite CA,CN=test2.ssl" 3 | dns_name = "test2.ssl.test" 4 | dns_name = "*.openssl.test" 5 | dns_name = "*.gnutls.test" 6 | 7 | expiration_days = 3650 8 | 9 | tls_www_server 10 | signing_key 11 | encryption_key 12 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | runtest_file = files('runtests.py') 2 | 3 | test( 4 | 'http', 5 | runtest_file, 6 | args: [ 7 | '--angel', bin_angel.full_path(), 8 | '--worker', bin_worker.full_path(), 9 | '--plugindir', modules_build_dir, 10 | ], 11 | depends: [ 12 | bin_angel, 13 | bin_worker, 14 | enabled_modules, 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /tests/pylt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lighttpd/lighttpd2/c6d66b2084a60324f9a4d48960c8d268a0fc3b31/tests/pylt/__init__.py -------------------------------------------------------------------------------- /tests/pylt/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lighttpd/lighttpd2/c6d66b2084a60324f9a4d48960c8d268a0fc3b31/tests/pylt/tests/__init__.py -------------------------------------------------------------------------------- /tests/pylt/tests/t-alias.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.requests import CurlRequest, TEST_TXT 4 | 5 | 6 | class TestAlias1(CurlRequest): 7 | URL = "/alias1" 8 | EXPECT_RESPONSE_BODY = TEST_TXT 9 | EXPECT_RESPONSE_CODE = 200 10 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 11 | config = """ 12 | alias "/alias1" => var.default_docroot + "/test.txt"; 13 | """ 14 | 15 | 16 | class TestAlias2(CurlRequest): 17 | URL = "/alias2" 18 | EXPECT_RESPONSE_BODY = TEST_TXT 19 | EXPECT_RESPONSE_CODE = 200 20 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 21 | config = """ 22 | alias "/alias1" => "/nothing", "/alias2" => var.default_docroot + "/test.txt"; 23 | """ 24 | 25 | 26 | class TestAlias3(CurlRequest): 27 | URL = "/alias3/test.txt" 28 | EXPECT_RESPONSE_BODY = TEST_TXT 29 | EXPECT_RESPONSE_CODE = 200 30 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 31 | config = """ 32 | alias "/alias3" => var.default_docroot + "/"; 33 | """ 34 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-basic-docroot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest 4 | from pylt.requests import CurlRequest 5 | 6 | 7 | class TestSimple(CurlRequest): 8 | vhost = "xyz.abc.basic-docroot" 9 | URL = "/?simple" 10 | EXPECT_RESPONSE_BODY = "/var/www/xyz.abc.basic-docroot/htdocs" 11 | EXPECT_RESPONSE_CODE = 200 12 | 13 | 14 | class TestSubdir(CurlRequest): 15 | vhost = "xyz.abc.basic-docroot" 16 | URL = "/?subdir" 17 | EXPECT_RESPONSE_BODY = "/var/www/basic-docroot/xyz.abc.basic-docroot/htdocs" 18 | EXPECT_RESPONSE_CODE = 200 19 | 20 | 21 | class TestSubdirOpenRange(CurlRequest): 22 | vhost = "test.xyz.abc.basic-docroot" 23 | URL = "/?subdir-open-range" 24 | EXPECT_RESPONSE_BODY = "/var/www/basic-docroot/test.xyz.abc/htdocs" 25 | EXPECT_RESPONSE_CODE = 200 26 | 27 | 28 | class TestSubdirFixedRange(CurlRequest): 29 | vhost = "test.xyz.abc.basic-docroot" 30 | URL = "/?subdir-fixed-range" 31 | EXPECT_RESPONSE_BODY = "/var/www/basic-docroot/xyz.abc/htdocs" 32 | EXPECT_RESPONSE_CODE = 200 33 | 34 | 35 | class TestCascade(CurlRequest): 36 | URL = "/?cascade" 37 | EXPECT_RESPONSE_BODY = "/" 38 | EXPECT_RESPONSE_CODE = 200 39 | 40 | 41 | class TestCascadeFallback(CurlRequest): 42 | URL = "/?cascade-fallback" 43 | EXPECT_RESPONSE_BODY = "/var/www/fallback/htdocs" 44 | EXPECT_RESPONSE_CODE = 200 45 | 46 | 47 | class Test(ModuleTest): 48 | vhost = "basic-docroot" 49 | subdomains = True 50 | config = """ 51 | 52 | if req.query == "simple" { 53 | docroot "/var/www/$0/htdocs"; 54 | } else if req.query == "subdir" { 55 | docroot "/var/www/$[1]/$[0]/htdocs"; 56 | } else if req.query == "subdir-open-range" { 57 | docroot "/var/www/$1/$[2-]/htdocs"; 58 | } else if req.query == "subdir-fixed-range" { 59 | docroot "/var/www/$1/$[2-3]/htdocs"; 60 | } else if req.query == "cascade" { 61 | docroot ("/","/var/www/fallback/htdocs"); 62 | } else if req.query == "cascade-fallback" { 63 | docroot ("_","/var/www/fallback/htdocs"); 64 | } 65 | 66 | env.set "INFO" => "%{phys.docroot}"; 67 | show_env_info; 68 | 69 | """ 70 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-deflate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest 4 | from pylt.requests import CurlRequest, TEST_TXT 5 | 6 | 7 | class DeflateRequest(CurlRequest): 8 | _NO_REGISTER = True # used in metaclass 9 | 10 | URL = "/test.txt" 11 | EXPECT_RESPONSE_BODY = TEST_TXT 12 | EXPECT_RESPONSE_CODE = 200 13 | 14 | EXPECT_RESPONSE_HEADERS = [("Vary", "Accept-Encoding")] 15 | 16 | def prepare_test(self) -> None: 17 | self.EXPECT_RESPONSE_HEADERS = ( 18 | self.EXPECT_RESPONSE_HEADERS 19 | + [("Content-Encoding", self.ACCEPT_ENCODING)] 20 | ) 21 | 22 | 23 | class TestGzip(DeflateRequest): 24 | ACCEPT_ENCODING = 'gzip' 25 | 26 | 27 | class TestXGzip(DeflateRequest): 28 | ACCEPT_ENCODING = 'x-gzip' 29 | 30 | 31 | class TestDeflate(DeflateRequest): 32 | ACCEPT_ENCODING = 'deflate' 33 | 34 | 35 | # not supported 36 | ### class TestCompress(DeflateRequest): 37 | ### ACCEPT_ENCODING = 'compress' 38 | 39 | 40 | class TestBzip2(DeflateRequest): 41 | ACCEPT_ENCODING = 'bzip2' 42 | 43 | 44 | class TestXBzip2(DeflateRequest): 45 | ACCEPT_ENCODING = 'x-bzip2' 46 | 47 | 48 | class TestDisableDeflate(CurlRequest): 49 | URL = "/test.txt?nodeflate" 50 | EXPECT_RESPONSE_BODY = TEST_TXT 51 | EXPECT_RESPONSE_CODE = 200 52 | 53 | EXPECT_RESPONSE_HEADERS = [("Content-Encoding", None)] 54 | 55 | 56 | class Test(ModuleTest): 57 | def prepare_test(self) -> None: 58 | # deflate is enabled global too; force it here anyway 59 | self.config = """ 60 | defaultaction; 61 | if req.query == "nodeflate" { req_header.remove "Accept-Encoding"; } static; do_deflate; 62 | """ 63 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-dirlist.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest 4 | from pylt.requests import CurlRequest 5 | 6 | 7 | class TestDirlist(CurlRequest): 8 | URL = "/foo/" 9 | EXPECT_RESPONSE_CODE = 200 10 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/html; charset=utf-8")] 11 | 12 | 13 | class TestRedirectDir(CurlRequest): 14 | URL = "/foo" 15 | EXPECT_RESPONSE_CODE = 301 16 | EXPECT_RESPONSE_HEADERS = [("Location", "http://dirlist.test/foo/")] 17 | 18 | 19 | class TestRedirectDirWithQuery(CurlRequest): 20 | URL = "/foo?bar=baz" 21 | EXPECT_RESPONSE_CODE = 301 22 | EXPECT_RESPONSE_HEADERS = [("Location", "http://dirlist.test/foo/?bar=baz")] 23 | 24 | 25 | class TestRedirectDirWithQueryAndSpecialChars(CurlRequest): 26 | URL = "/f%3f%20o?bar=baz" 27 | EXPECT_RESPONSE_CODE = 301 28 | EXPECT_RESPONSE_HEADERS = [("Location", "http://dirlist.test/f%3f%20o/?bar=baz")] 29 | 30 | 31 | class Test(ModuleTest): 32 | config = """ 33 | setup { module_load "mod_dirlist"; } 34 | dirlist; 35 | """ 36 | 37 | def prepare_test(self) -> None: 38 | self.prepare_dir("www/vhosts/dirlist.test/foo") 39 | self.prepare_file("www/vhosts/dirlist.test/foo/test.txt", "abc") 40 | self.prepare_dir("www/vhosts/dirlist.test/f? o") 41 | self.prepare_file("www/vhosts/dirlist.test/f? o/test.txt", "abc") 42 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-env-set.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.requests import CurlRequest 4 | 5 | 6 | class TestPatternCapture(CurlRequest): 7 | # use capture from previous regex conditional 8 | config = """ 9 | if req.path =~ "(.*)" { 10 | env.set "INFO" => "%1"; 11 | show_env_info; 12 | } 13 | """ 14 | URL = "/path/?a_simple_query" 15 | EXPECT_RESPONSE_BODY = "/path/" 16 | EXPECT_RESPONSE_CODE = 200 17 | 18 | 19 | class TestPatternCaptureRange(CurlRequest): 20 | # use capture from previous regex conditional 21 | config = """ 22 | if req.path =~ "/([^/]*)/(.*)" { 23 | env.set "INFO" => "%[1-2]"; 24 | show_env_info; 25 | } 26 | """ 27 | URL = "/path/xyz" 28 | EXPECT_RESPONSE_BODY = "pathxyz" 29 | EXPECT_RESPONSE_CODE = 200 30 | 31 | 32 | class TestPatternCaptureRevRange(CurlRequest): 33 | # use capture from previous regex conditional 34 | config = """ 35 | if req.path =~ "/([^/]*)/(.*)" { 36 | env.set "INFO" => "%[2-1]"; 37 | show_env_info; 38 | } 39 | """ 40 | URL = "/path/xyz" 41 | EXPECT_RESPONSE_BODY = "xyzpath" 42 | EXPECT_RESPONSE_CODE = 200 43 | 44 | 45 | class TestPatternEncodingPath(CurlRequest): 46 | # encoding path 47 | config = """ 48 | env.set "INFO" => "%{enc:req.path}"; 49 | show_env_info; 50 | """ 51 | URL = "/complicated%3fpath%3d%20%24" 52 | EXPECT_RESPONSE_BODY = "/complicated%3fpath%3d%20%24" 53 | EXPECT_RESPONSE_CODE = 200 54 | 55 | 56 | class TestPatternCombine(CurlRequest): 57 | # combine several pieces 58 | config = """ 59 | env.set "INFO" => "Abc:%{enc:req.path}:%{req.query}:%{req.host}"; 60 | show_env_info; 61 | """ 62 | URL = "/complicated%3fpath%3d%20%24?a_simple_query" 63 | EXPECT_RESPONSE_CODE = 200 64 | 65 | def prepare_test(self): 66 | self.EXPECT_RESPONSE_BODY = "Abc:/complicated%3fpath%3d%20%24:a_simple_query:" + self.vhost 67 | 68 | 69 | class TestPatternEscape(CurlRequest): 70 | config = r""" 71 | env.set "INFO" => "\\%\\?\\$\\%{req.path}"; 72 | show_env_info; 73 | """ 74 | URL = "/abc" 75 | EXPECT_RESPONSE_BODY = "%?$%{req.path}" 76 | EXPECT_RESPONSE_CODE = 200 77 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-gnutls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.requests import CurlRequest, TEST_TXT 4 | 5 | 6 | class TestSimpleRequest(CurlRequest): 7 | PORT_OFFSET = 1 8 | SCHEME = "https" 9 | URL = "/test.txt" 10 | EXPECT_RESPONSE_BODY = TEST_TXT 11 | EXPECT_RESPONSE_CODE = 200 12 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 13 | vhost = "test1.ssl.test" 14 | 15 | 16 | class TestSNI(CurlRequest): 17 | PORT_OFFSET = 1 18 | SCHEME = "https" 19 | URL = "/test.txt" 20 | EXPECT_RESPONSE_BODY = TEST_TXT 21 | EXPECT_RESPONSE_CODE = 200 22 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 23 | vhost = "test2.ssl.test" 24 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-header-modify.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import eprint, log 4 | from pylt.requests import CurlRequest 5 | 6 | 7 | class TestHeaderAdd(CurlRequest): 8 | # use capture from previous regex conditional 9 | config = """ 10 | header.add "Test-Header" => "%{req.query}"; 11 | header.add "Test-Header" => "%{req.path}"; 12 | respond; 13 | """ 14 | URL = "/path?simple_query" 15 | 16 | def CheckResponse(self) -> bool: 17 | h = self.response.get_all_headers("Test-Header") 18 | if len(h) != 2 or h[0] != "simple_query" or h[1] != "/path": 19 | eprint(repr(h)) 20 | raise Exception("Unexpected headers 'Test-Header'") 21 | return True 22 | 23 | 24 | class TestHeaderAppend(CurlRequest): 25 | # use capture from previous regex conditional 26 | config = """ 27 | header.append "Test-Header" => "%{req.query}"; 28 | header.append "Test-Header" => "%{req.path}"; 29 | respond; 30 | """ 31 | URL = "/path?simple_query" 32 | 33 | def CheckResponse(self) -> bool: 34 | h = self.response.get_all_headers("Test-Header") 35 | if len(h) != 1 or h[0] != "simple_query, /path": 36 | log(repr(h)) 37 | raise Exception("Unexpected headers 'Test-Header'") 38 | return True 39 | 40 | 41 | class TestHeaderOverwrite(CurlRequest): 42 | # use capture from previous regex conditional 43 | config = """ 44 | header.overwrite "Test-Header" => "%{req.query}"; 45 | header.overwrite "Test-Header" => "%{req.path}"; 46 | respond; 47 | """ 48 | URL = "/path?simple_query" 49 | 50 | def CheckResponse(self) -> bool: 51 | h = self.response.get_all_headers("Test-Header") 52 | if len(h) != 1 or h[0] != "/path": 53 | log(repr(h)) 54 | raise Exception("Unexpected headers 'Test-Header'") 55 | return True 56 | 57 | 58 | class TestHeaderRemove(CurlRequest): 59 | # use capture from previous regex conditional 60 | config = """ 61 | header.add "Test-Header" => "%{req.query}"; 62 | header.remove "Test-Header"; 63 | respond; 64 | """ 65 | URL = "/path?simple_query" 66 | 67 | def CheckResponse(self) -> bool: 68 | h = self.response.get_all_headers("Test-Header") 69 | if len(h) != 0: 70 | log(repr(h)) 71 | raise Exception("Unexpected headers 'Test-Header'") 72 | return True 73 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-lua.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest, TestBase 4 | from pylt.requests import CurlRequest 5 | 6 | 7 | LUA_STATE_ENV_INFO = """ 8 | -- globals should be reset after loading 9 | info = "global info" 10 | 11 | -- tostring(custom_obj) should throw an error, but log functions should handle it 12 | local custom_obj = {["data"] = "test string"} 13 | setmetatable(custom_obj, {["__tostring"] = function(obj) return nil .. obj["data"] end}) 14 | 15 | print("bar", "custom_obj=", custom_obj) 16 | 17 | local function extract_info(vr) 18 | vr:error("extract_info: info=" .. (info or ""), "nil=", nil, "custom_obj=", custom_obj) 19 | -- simple globals should be "per handler" (and request) 20 | info = (info or "") .. "handler global" 21 | -- special `REQ` allows request-global state across handlers 22 | REQ.info = (REQ.info or "") .. "request global:" .. vr.env["INFO"] 23 | end 24 | 25 | local function show_info(vr) 26 | lighty.error("show_info: info=" .. (info or "") .. "; REQ.info=" .. (REQ.info or "")) 27 | if vr:handle_direct() then 28 | vr.resp.status = 200 29 | vr.resp.headers["Content-Type"] = "text/plain" 30 | vr.out:add((info or "") .. (REQ.info or "")) 31 | end 32 | end 33 | 34 | actions = action.list({extract_info, show_info}) 35 | """ 36 | 37 | 38 | class TestLuaStateInfo1(CurlRequest): 39 | URL = "/?a_simple_query" 40 | EXPECT_RESPONSE_BODY = "request global:a_simple_query" 41 | EXPECT_RESPONSE_CODE = 200 42 | 43 | config = """ 44 | env.set "INFO" => "%{req.query}"; 45 | lua_state_env_info; 46 | """ 47 | 48 | class TestLuaStateInfo2(CurlRequest): 49 | URL = "/?b_simple_query" 50 | EXPECT_RESPONSE_BODY = "request global:b_simple_query" 51 | EXPECT_RESPONSE_CODE = 200 52 | 53 | config = """ 54 | env.set "INFO" => "%{req.query}"; 55 | lua_state_env_info; 56 | """ 57 | 58 | class TestLuaWorkerStateInfo1(CurlRequest): 59 | URL = "/?a_simple_query" 60 | EXPECT_RESPONSE_BODY = "request global:a_simple_query" 61 | EXPECT_RESPONSE_CODE = 200 62 | 63 | config = """ 64 | env.set "INFO" => "%{req.query}"; 65 | worker_lua_state_env_info; 66 | """ 67 | 68 | class TestLuaWorkerStateInfo2(CurlRequest): 69 | URL = "/?b_simple_query" 70 | EXPECT_RESPONSE_BODY = "request global:b_simple_query" 71 | EXPECT_RESPONSE_CODE = 200 72 | 73 | config = """ 74 | env.set "INFO" => "%{req.query}"; 75 | worker_lua_state_env_info; 76 | """ 77 | 78 | class Test(ModuleTest): 79 | def prepare_test(self) -> None: 80 | show_env_info_lua = self.prepare_file("lua/lua_state_env_info.lua", LUA_STATE_ENV_INFO) 81 | self.plain_config = f""" 82 | lua_state_env_info = {{ 83 | include_lua "{show_env_info_lua}"; 84 | }}; 85 | worker_lua_state_env_info = {{ 86 | lua.handler "{show_env_info_lua}"; 87 | }}; 88 | """ 89 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-map-cidr.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.requests import CurlRequest 4 | 5 | 6 | class TestSimpleCidrMap(CurlRequest): 7 | URL = "/test.txt" 8 | EXPECT_RESPONSE_BODY = "abc" 9 | EXPECT_RESPONSE_CODE = 200 10 | config = """ 11 | map_cidr [ 12 | "127.0.0.0/8" => { 13 | }, 14 | default => { 15 | respond 402 => ""; 16 | }, 17 | ]; 18 | respond 200 => "abc"; 19 | """ 20 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-memcached.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import socket 4 | import os 5 | import time 6 | 7 | from pylt import base 8 | from pylt.requests import CurlRequest 9 | from pylt.service import Service 10 | 11 | 12 | class Memcached(Service): 13 | name = "memcached" 14 | binary: list[str] = [] 15 | 16 | def __init__(self, *, tests: base.Tests) -> None: 17 | super().__init__(tests=tests) 18 | self.sockfile = os.path.join(self.tests.env.dir, "tmp", "sockets", self.name + ".sock") 19 | self.binary = [os.path.join(self.tests.env.sourcedir, "tests", "run-memcached.py")] 20 | 21 | def prepare_service(self) -> None: 22 | assert self.tests 23 | self.tests.install_dir(os.path.join("tmp", "sockets")) 24 | sock = self.bind_unix_socket(sockfile=self.sockfile) 25 | self.fork(*self.binary, inp=sock) 26 | 27 | def cleanup_service(self) -> None: 28 | assert self.tests 29 | try: 30 | os.remove(self.sockfile) 31 | except Exception as e: 32 | base.eprint(f"Couldn't delete socket {self.sockfile!r}: {e}") 33 | self.tests.remove_dir(os.path.join("tmp", "sockets")) 34 | 35 | 36 | class TestStore1(CurlRequest): 37 | URL = "/" 38 | EXPECT_RESPONSE_BODY = "Hello World!" 39 | EXPECT_RESPONSE_CODE = 200 40 | EXPECT_RESPONSE_HEADERS = [("X-Memcached-Hit", "false")] 41 | 42 | 43 | class TestLookup1(CurlRequest): 44 | URL = "/" 45 | EXPECT_RESPONSE_BODY = "Hello World!" 46 | EXPECT_RESPONSE_CODE = 200 47 | EXPECT_RESPONSE_HEADERS = [("X-Memcached-Hit", "true")] 48 | 49 | def run_test(self) -> bool: 50 | # storing might take some time: only after the request is actually 51 | # finished does lighttpd start the memcache connection to store it 52 | time.sleep(0.2) 53 | return super().run_test() 54 | 55 | 56 | class Test(base.ModuleTest): 57 | config = """ 58 | memcache; 59 | """ 60 | 61 | def __init__(self, *, tests: base.Tests) -> None: 62 | super().__init__(tests=tests) 63 | 64 | memcached = Memcached(tests=self.tests) 65 | self.plain_config = f""" 66 | setup {{ module_load "mod_memcached"; }} 67 | 68 | memcache = {{ 69 | memcached.lookup (( "server" => "unix:{memcached.sockfile}" ), {{ 70 | header.add "X-Memcached-Hit" => "true"; 71 | }}, {{ 72 | header.add "X-Memcached-Hit" => "false"; 73 | respond 200 => "Hello World!"; 74 | memcached.store ( "server" => "unix:{memcached.sockfile}" ); 75 | }}); 76 | }}; 77 | """ 78 | self.tests.add_service(memcached) 79 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-mime-type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest 4 | from pylt.requests import CurlRequest 5 | 6 | 7 | class TestMimeType1(CurlRequest): 8 | URL = "/test.txt" 9 | EXPECT_RESPONSE_BODY = "" 10 | EXPECT_RESPONSE_CODE = 200 11 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 12 | 13 | 14 | class TestMimeType2(CurlRequest): 15 | URL = "/test.xt" 16 | EXPECT_RESPONSE_BODY = "" 17 | EXPECT_RESPONSE_CODE = 200 18 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain")] 19 | 20 | 21 | class TestMimeType3(CurlRequest): 22 | URL = "/test.rxt" 23 | EXPECT_RESPONSE_BODY = "" 24 | EXPECT_RESPONSE_CODE = 200 25 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/strange")] 26 | 27 | 28 | class Test(ModuleTest): 29 | def prepare_test(self) -> None: 30 | self.prepare_vhost_file("test.txt", "") 31 | self.prepare_vhost_file("test.xt", "") 32 | self.prepare_vhost_file("test.rxt", "") 33 | self.config = """ 34 | mime_types ( 35 | ".txt" => "text/plain; charset=utf-8", 36 | ".xt" => "text/plain", 37 | ".rxt" => "text/strange", 38 | "xt" => "should-not-trigger" 39 | ); 40 | """ 41 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-mod-lua.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest 4 | from pylt.requests import CurlRequest 5 | 6 | 7 | LUA_TEST_OPTIONS = """ 8 | 9 | local function settag(tag) 10 | setup["server.tag"](tag) 11 | end 12 | 13 | local function changetag(tag) 14 | return action["server.tag"](tag) 15 | end 16 | 17 | actions = { 18 | ["lua.changetag"] = changetag 19 | } 20 | setups = { 21 | ["lua.settag"] = settag 22 | } 23 | 24 | """ 25 | 26 | 27 | class TestSetupOption(CurlRequest): 28 | URL = "/" 29 | EXPECT_RESPONSE_HEADERS = [("Server", "lighttpd 2.0 with lua")] 30 | 31 | 32 | class TestChangeOption(CurlRequest): 33 | URL = "/?change" 34 | EXPECT_RESPONSE_HEADERS = [("Server", "lighttpd 2.0 with modified lua")] 35 | 36 | 37 | class Test(ModuleTest): 38 | def prepare_test(self) -> None: 39 | test_options_lua = self.prepare_file("lua/test_options.lua", LUA_TEST_OPTIONS) 40 | self.plain_config = f""" 41 | setup {{ 42 | lua.plugin "{test_options_lua}"; 43 | lua.settag "lighttpd 2.0 with lua"; 44 | }} 45 | """ 46 | self.config = """ 47 | if req.query == "change" { 48 | lua.changetag "lighttpd 2.0 with modified lua"; 49 | } 50 | """ 51 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-openssl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.requests import CurlRequest, TEST_TXT 4 | 5 | 6 | class TestSimpleRequest(CurlRequest): 7 | PORT_OFFSET = 2 8 | SCHEME = "https" 9 | URL = "/test.txt" 10 | EXPECT_RESPONSE_BODY = TEST_TXT 11 | EXPECT_RESPONSE_CODE = 200 12 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 13 | vhost = "test1.ssl.test" 14 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-redirect.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest 4 | from pylt.requests import CurlRequest, TEST_TXT 5 | 6 | 7 | class TestRewrite1(CurlRequest): 8 | URL = "/somefile" 9 | EXPECT_RESPONSE_BODY = TEST_TXT 10 | EXPECT_RESPONSE_CODE = 200 11 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 12 | config = """ 13 | rewrite "^/somefile$" => "/test.txt"; 14 | defaultaction; 15 | """ 16 | 17 | 18 | class TestRewrite2(CurlRequest): 19 | URL = "/somefile" 20 | EXPECT_RESPONSE_BODY = TEST_TXT 21 | EXPECT_RESPONSE_CODE = 200 22 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 23 | config = """ 24 | rewrite "/somethingelse" => "/nothing", "^/somefile$" => "/test.txt"; 25 | defaultaction; 26 | """ 27 | 28 | 29 | class Test(ModuleTest): 30 | plain_config = """ 31 | setup { module_load "mod_rewrite"; } 32 | """ 33 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-rewrite.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pylt.base import ModuleTest 4 | from pylt.requests import CurlRequest, TEST_TXT 5 | 6 | 7 | class TestRewrite1(CurlRequest): 8 | URL = "/somefile" 9 | EXPECT_RESPONSE_BODY = TEST_TXT 10 | EXPECT_RESPONSE_CODE = 200 11 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 12 | config = """ 13 | rewrite "^/somefile$" => "/test.txt"; 14 | defaultaction; 15 | """ 16 | 17 | 18 | class TestRewrite2(CurlRequest): 19 | URL = "/somefile" 20 | EXPECT_RESPONSE_BODY = TEST_TXT 21 | EXPECT_RESPONSE_CODE = 200 22 | EXPECT_RESPONSE_HEADERS = [("Content-Type", "text/plain; charset=utf-8")] 23 | config = """ 24 | rewrite "/somethingelse" => "/nothing", "^/somefile$" => "/test.txt"; 25 | defaultaction; 26 | """ 27 | 28 | 29 | # match decoded and simplified paths by default 30 | class TestRewrite3(CurlRequest): 31 | URL = "/http://some%2Ffile" 32 | EXPECT_RESPONSE_BODY = "/dest/file" 33 | EXPECT_RESPONSE_CODE = 200 34 | config = """ 35 | rewrite "/http:/some(/.*)" => "/dest$1"; 36 | respond 200 => "%{req.path}"; 37 | """ 38 | 39 | 40 | # match raw paths and simplify path 41 | class TestRewrite4(CurlRequest): 42 | URL = "/http://some%2Ffile" 43 | EXPECT_RESPONSE_BODY = "/dest/http:/some/file" 44 | EXPECT_RESPONSE_CODE = 200 45 | config = """ 46 | rewrite_raw "(/http://some%2F.*)" => "/dest$1"; 47 | respond 200 => "%{req.path}"; 48 | """ 49 | 50 | 51 | # match and write raw paths 52 | class TestRewrite5(CurlRequest): 53 | URL = "/http://some%2Ffile" 54 | EXPECT_RESPONSE_BODY = "/dest/http://some%2Ffile" 55 | EXPECT_RESPONSE_CODE = 200 56 | config = """ 57 | rewrite_raw "(/http://some%2F.*)" => "/dest$1"; 58 | respond 200 => "%{req.raw_path}"; 59 | """ 60 | 61 | 62 | # raw match and write query string 63 | class TestRewrite6(CurlRequest): 64 | URL = "/http://some%2Ffile" 65 | EXPECT_RESPONSE_BODY = "/http://some%2Ffile" 66 | EXPECT_RESPONSE_CODE = 200 67 | config = """ 68 | rewrite_raw "(/http://some%2F.*)" => "/dest?$1"; 69 | respond 200 => "%{req.query}"; 70 | """ 71 | 72 | 73 | class Test(ModuleTest): 74 | plain_config = """ 75 | setup { module_load "mod_rewrite"; } 76 | """ 77 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-scgi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import socket 4 | import os 5 | 6 | from pylt import base 7 | from pylt.requests import CurlRequest 8 | from pylt.service import Service 9 | 10 | 11 | class SCGI(Service): 12 | name = "scgi" 13 | binary: list[str] = [] 14 | 15 | def __init__(self, *, tests: base.Tests) -> None: 16 | super().__init__(tests=tests) 17 | self.sockfile = os.path.join(self.tests.env.dir, "tmp", "sockets", self.name + ".sock") 18 | self.binary = [os.path.join(self.tests.env.sourcedir, "tests", "run-scgi-envcheck.py")] 19 | 20 | def prepare_service(self) -> None: 21 | assert self.tests 22 | self.tests.install_dir(os.path.join("tmp", "sockets")) 23 | sock = self.bind_unix_socket(sockfile=self.sockfile) 24 | self.fork(*self.binary, inp=sock) 25 | 26 | def cleanup_service(self) -> None: 27 | assert self.tests 28 | try: 29 | os.remove(self.sockfile) 30 | except Exception as e: 31 | base.eprint(f"Couldn't delete socket {self.sockfile}: {e}") 32 | self.tests.remove_dir(os.path.join("tmp", "sockets")) 33 | 34 | 35 | class TestPathInfo1(CurlRequest): 36 | URL = "/scgi/abc/xyz?PATH_INFO" 37 | EXPECT_RESPONSE_BODY = "/abc/xyz" 38 | EXPECT_RESPONSE_CODE = 200 39 | 40 | 41 | class TestRequestUri1(CurlRequest): 42 | URL = "/scgi/abc/xyz?REQUEST_URI" 43 | EXPECT_RESPONSE_BODY = "/scgi/abc/xyz?REQUEST_URI" 44 | EXPECT_RESPONSE_CODE = 200 45 | 46 | 47 | class Test(base.ModuleTest): 48 | config = """ 49 | run_scgi; 50 | """ 51 | 52 | def __init__(self, *, tests: base.Tests) -> None: 53 | super().__init__(tests=tests) 54 | 55 | scgi = SCGI(tests=self.tests) 56 | self.plain_config = f""" 57 | setup {{ module_load "mod_scgi"; }} 58 | 59 | run_scgi = {{ 60 | core.wsgi ( "/scgi", {{ scgi "unix:{scgi.sockfile}"; }} ); 61 | }}; 62 | """ 63 | self.tests.add_service(scgi) 64 | -------------------------------------------------------------------------------- /tests/pylt/tests/t-secdownload.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import time 4 | import typing 5 | from hashlib import md5 6 | 7 | from pylt.base import ModuleTest 8 | from pylt.requests import CurlRequest, TEST_TXT 9 | 10 | 11 | def securl(prefix: str, path: str, secret: str, tstamp: typing.Optional[float] = None) -> str: 12 | if tstamp is None: 13 | tstamp = time.time() 14 | hex_tstamp = f'{int(tstamp):x}' 15 | md5content = secret + path + hex_tstamp 16 | if prefix[-1] != '/': 17 | prefix += '/' 18 | return prefix + md5(md5content.encode()).hexdigest() + '/' + hex_tstamp + path 19 | 20 | 21 | class SecdownloadFail(CurlRequest): 22 | URL = "/test.txt" 23 | EXPECT_RESPONSE_CODE = 403 24 | 25 | 26 | class SecdownloadSuccess(CurlRequest): 27 | EXPECT_RESPONSE_BODY = TEST_TXT 28 | EXPECT_RESPONSE_CODE = 200 29 | 30 | def run_test(self) -> bool: 31 | self.URL = securl('/', '/test.txt', 'abc') 32 | return super().run_test() 33 | 34 | 35 | class SecdownloadGone(CurlRequest): 36 | EXPECT_RESPONSE_CODE = 410 37 | 38 | def run_test(self) -> bool: 39 | self.URL = securl('/', '/test.txt', 'abc', time.time() - 800) 40 | return super().run_test() 41 | 42 | 43 | class Test(ModuleTest): 44 | config = """ 45 | secdownload ( "prefix" => "/", "document-root" => var.default_docroot, "secret" => "abc", "timeout" => 600 ); 46 | """ 47 | no_docroot = True 48 | -------------------------------------------------------------------------------- /tests/run-scgi-envcheck.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import asyncio 5 | import dataclasses 6 | import socket 7 | import traceback 8 | 9 | 10 | @dataclasses.dataclass 11 | class ScgiRequest: 12 | headers: dict[bytes, bytes] 13 | body: bytes 14 | 15 | 16 | async def parse_scgi_request(reader: asyncio.StreamReader) -> ScgiRequest: 17 | hlen = int((await reader.readuntil(b':'))[:-1]) 18 | header_raw = await reader.readexactly(hlen + 1) 19 | assert len(header_raw) >= 16, "invalid request: too short (< 16)" 20 | assert header_raw[-2:] == b'\0,', \ 21 | fr"Invalid request: missing header/netstring terminator '\x00,', got {header_raw[-2:]!r}" 22 | header_list = header_raw[:-2].split(b'\0') 23 | assert len(header_list) % 2 == 0, \ 24 | f"Invalid request: odd numbers of header entries (must be pairs), got {len(header_list)}" 25 | assert header_list[0] == b'CONTENT_LENGTH', \ 26 | f"Invalid request: first header entry must be 'CONTENT_LENGTH', got {header_list[0]!r}" 27 | clen = int(header_list[1]) 28 | headers = {} 29 | i = 0 30 | while i < len(header_list): 31 | key = header_list[i] 32 | value = header_list[i+1] 33 | i += 2 34 | assert not key in headers, f"Invalid request: duplicate header key {key!r}" 35 | headers[key] = value 36 | assert headers.get(b'SCGI') == b'1', "Invalid request: missing SCGI=1 header" 37 | body = await reader.readexactly(clen) 38 | return ScgiRequest(headers=headers, body=body) 39 | 40 | 41 | async def handle_scgi(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None: 42 | print("scgi-envcheck: Incoming connection", flush=True) 43 | try: 44 | req = await parse_scgi_request(reader) 45 | envvar = req.headers[b'QUERY_STRING'] 46 | result = req.headers[envvar] 47 | except KeyboardInterrupt: 48 | raise 49 | except Exception as e: 50 | print(traceback.format_exc()) 51 | writer.write(b"Status: 500\r\nContent-Type: text/plain\r\n\r\n" + str(e).encode()) 52 | else: 53 | writer.write(b"Status: 200\r\nContent-Type: text/plain\r\n\r\n" + result) 54 | await writer.drain() 55 | writer.close() 56 | await writer.wait_closed() 57 | 58 | 59 | async def main() -> None: 60 | sock = socket.socket(fileno=0) 61 | 62 | if sock.type == socket.AF_UNIX: 63 | server = await asyncio.start_unix_server(handle_scgi, sock=sock, start_serving=False) 64 | else: 65 | server = await asyncio.start_server(handle_scgi, sock=sock, start_serving=False) 66 | 67 | addr = server.sockets[0].getsockname() 68 | print(f'Serving on {addr}', flush=True) 69 | 70 | async with server: 71 | await server.serve_forever() 72 | 73 | 74 | try: 75 | asyncio.run(main()) 76 | except KeyboardInterrupt: 77 | pass 78 | -------------------------------------------------------------------------------- /tests/runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os.path 5 | import sys 6 | 7 | sys.path.append(os.path.dirname(__file__)) 8 | 9 | import pylt.run 10 | 11 | pylt.run.main() 12 | --------------------------------------------------------------------------------