2
0
Fork 0

Compare commits

...

114 Commits

Author SHA1 Message Date
Stefan Bühler b033a4fcb4 [test] enable TestRedirectDirWithQueryAndSpecialChars
Change-Id: Ic649c1e4ea158853c362836f31f6ec7e250413cd
2021-05-03 23:26:51 +02:00
Stefan Bühler 140a3c9018 fix refcount issue: don't drop rc in li_iostream_reset
... so li_stream_simple_socket_close doesn't need to acquire (when the rc might already be 0).

Instead call li_iostream_reset in all places that didn't acquire before,
and drop the acquire.

Change-Id: I347f49eb57989738ed811a1f3a31b8942ff32881
2021-05-03 23:24:48 +02:00
Stefan Bühler 850aea7a5d [build] enable all "main" features by default
Change-Id: I9bf8ee3f4bfc6736f5fc5aea6b3958ee298c65d6
2021-02-28 20:02:44 +01:00
Stefan Bühler 21865cbbd2 [autobuild] install and use glib tap helpers, replaces gtester
Change-Id: I889eba5be61a1c2b89f9f53b38abbba201625b6b
2021-02-28 19:53:38 +01:00
Stefan Bühler 2a4744a677 [autobuild] raise autoconf min version, fix various deprecated macros
Change-Id: Ic7ae8f7bb0720ba4e6b965f1951f091cf03f6b83
2021-02-28 19:35:24 +01:00
Stefan Bühler c8bf635551 [tests] fix memcached and scgi-envcheck for python3; use asyncio
Change-Id: If3be7d98701d90c0374f625d1571140a100009df
2021-02-28 16:24:14 +01:00
Stefan Bühler 98ea1dc7de [contrib] add overrides for new mime types, regenerate mimetypes.conf
Change-Id: I6e0116b1f227c96eb98a6e45300030bc5cde72fd
2021-02-28 13:58:45 +01:00
Stefan Bühler 424e1a37f8 [core] Reset con->out (response body) queue counters (used by mod_accesslog) for keep-alive
Change-Id: Id644a71c808c8a3c73e476fddb6022177de8dd5f
2021-02-28 12:38:33 +00:00
Stefan Bühler d544ee105d [core] docs say empty log targets request not logging; actually implement that (went to stderr)
Change-Id: If71dc0b309c4e8221ecef877202d9a1a571ad5c6
2021-02-28 13:28:00 +01:00
Stefan Bühler 7a343d4765 [mod_acceslog]: fix log target reference in docs
Change-Id: If390d666db5e4952a64d545eb77ddf6e8f7ed9df
2021-02-28 11:45:03 +01:00
Stefan Bühler a5d3e11c1f [tests] add tests for mod_dirlist
Change-Id: I5d6ea970cd5dca7e202c8bd888142e44ef8f4211
2020-06-22 20:38:47 +02:00
Stefan Bühler 76fccefc84 [core] encode path in li_vrequest_redirect_directory
To handle whitespace and ? (and other special chars).

Change-Id: Ie597c1d784d42dba70dd21650f5fc9770e9e6547
2020-06-22 20:36:58 +02:00
Stefan Bühler 51a7fd577c [core]: fix query string handling in li_vrequest_redirect_directory (dirlist, index)
- use decoded path instead of orig raw path (which includes the query
  string); the decoded path should be safe, and we also really don't
  need to support any "raw" handling - we're at the filesystem level
  anyway.

Change-Id: Ic9a5b362bea9813873631b18aaa908c59f2bb0a6
2020-06-22 20:27:41 +02:00
Stefan Bühler 92681fcde4 [tests] fix PrepareDir
Change-Id: Idb26de002ad1586101c9e9bdb0256170b8e287b9
2020-06-22 19:59:43 +02:00
Stefan Bühler 505bfb053f [core] move CGI environment creation to core
Change-Id: Ia826381365a04352249321097fda57f704984821
2019-08-31 13:48:17 +02:00
Stefan Bühler 8989ca32d4 [mod_{fast,s}cgi] support REQUEST_SCHEME cgi variable
Change-Id: I4750086962ee50a530694309ec8a6165eddabcf3
2019-08-31 13:08:11 +02:00
Stefan Bühler afaf285bfb [core] fix chunk decoding bug: reused stale context after forwarding chunk data
Change-Id: Ie37e3c2d605ae23dcb7a6b74ff6b5f0699074ac5
2019-08-26 00:36:59 +02:00
Stefan Bühler 7078063491 [core] fix parsing of chunk digits
Change-Id: I8fd5b916172c432b63329078e261a8ed5eff23dc
2019-08-26 00:15:27 +02:00
Stefan Bühler a41b92eb8a [core] fix segfault in http response streaming
Change-Id: I61a0167d8a7fdede96075960f5099b33c0a8ab02
2019-08-25 19:25:02 +02:00
Stefan Bühler d8bd405a19 [core] fix crash when HTTP backend "Upgrade"s
Change-Id: If7143f49baa2efa8d1fb3f78c6390969730c678e
2019-08-25 13:07:49 +02:00
Stefan Bühler bffe96f5d0 [build] sync extra warning flags between cmake and autobuild
Change-Id: I4165d22ef944b959f9acd8f57c3d7658049b39f0
2019-08-25 12:16:19 +02:00
Stefan Bühler d167e6e416 [core/mod_proxy] support http backends trying to run keep-alive
Even if they shouldn't (due to HTTP/1.0 or Connection; close) some
backends send HTTP/1.1 without Connection: close, and use Content-Length
to signal end of response (and don't close the connection, as they wait
for another request).

Now Content-Length is used to find the end of the response (chunked
transfer-encoding was already supported).

mod_proxy now signals HTTP/1.1, but also sends "Connection: close": it
doesn't reuse the connection yet.

Change-Id: Ica0c9b3b7da79899412a746f21e7348ccd3d23ee
2019-08-25 11:45:18 +02:00
Stefan Bühler 52566836a0 [core] fix fallthrough annotation for gcc
Change-Id: I3130f87b0709055d1f496961f7bd8167329e2c46
2019-08-25 11:45:18 +02:00
Stefan Bühler 16ccf1d307 [tests] show error log of failed tests
Change-Id: Iaee7559a0526cdb4a8d337d003a79062ff912dce
2019-08-25 11:45:18 +02:00
Stefan Bühler f60b53bd44 [tests] build custom http backend server, require python3 for socketserver
Change-Id: Id4deaad74799a8cc2454869e941e7a00106d25cd
2019-08-25 01:22:55 +02:00
Stefan Bühler 9263deaeef [tests] python3 compability
Change-Id: I09c8eaf0cbe4a74b795aeb2a3b04f0abc39383c8
2019-08-24 23:35:45 +02:00
Stefan Bühler 8643cafa22 [core] fix log level of connection: (backend) response header parsing
Change-Id: Idd8263ccf19bf60df10cf22fa8c7037615171c35
2019-08-24 23:35:40 +02:00
Stefan Bühler f66defba93 [doc] mod_fastcgi: extend php fastcgi example with physical file test
Change-Id: I061278e2bd588d27f57be8b6711d470161a955cc
2019-06-27 21:19:07 +02:00
Stefan Bühler bda1a90f27 [core] add strict.post_content_length option
Change-Id: Ie9d67eceed7e957b667554925d562018a3217209
2018-09-08 23:12:22 +02:00
Stefan Bühler bc6b256c34 [core] use readdir instead of readdir_r
readdir_r is deprecated in glibc due to serious memory handling issues
in the API: one cannot pass the size of the allocated dirent.

glibc authors claims readdir is thread-safe in modern implementations,
and expect POSIX to require it in a future version.

No way to check whether readdir is thread-safe though :(

("thread-safe" in this context means different directory streams, which
is good enough.)

Also remove li_dirent_buf_size.

Change-Id: Ia5eae3327e97dc4b0751fb2604ea21c0ce09a5f9
2017-07-29 15:25:55 +02:00
Stefan Bühler b3dcc9662e [mod_openssl] fix C90 mixed declarations and code warning
Change-Id: I04f5e5dba87b7174eb5e93d18bddb5fb0ba717e9
2017-07-29 15:06:39 +02:00
Stefan Bühler 04e54a4388 [mod_openssl] fix crash with libressl or openssl < 1.1
Change-Id: I920ab1e4bc36df8396dcbf3d5777af32ae273a8b
2017-07-29 14:59:50 +02:00
Stefan Bühler 8c68b120da [mod_gnutls] support OCSP responses in sni backends
Change-Id: I7ec08bf6e414140b53019885eb906bdfe3251a2e
2017-07-26 09:46:13 +02:00
Stefan Bühler e0e96ae377 [plugin_core] fix segfault in fetch.files_static init
Change-Id: Icbc8e1440ea35b80a25e600e4a1bd913cafc72f1
2017-07-26 08:16:36 +02:00
Stefan Bühler 808cdf301b handle ENAMETOOLONG in various places
- ENAMETOOLONG:
  - static, flv: return 414
  - dirlist, pathinfo: treat as not-existing (i.e. no handling)
- also return 500 instead of closing the connection when stat/open
  fails an unhandled error
- explicit return instead of switch-case fallthrough (no semantic
  change) in actions.c

Change-Id: I1e2dd721dd18544500b4436ada843cb6e7f2db72
2017-05-20 15:48:36 +02:00
Stefan Bühler 863b433aa6 [doc build] remove markdown support
- bluecloth is dead, and was dropped from debian
- markdown wasn't used in any doc

Change-Id: I61f51724a5abbae6cc0e4cbf993873b59b6e563d
2017-05-20 14:04:21 +02:00
Stefan Bühler f6990a9d8a [plugin_core] support patterns in alias target
Change-Id: Ic77080dc5d7e1821832d10b56366cc79d0ff8a6a
2017-05-12 13:32:32 +02:00
Stefan Bühler ca446911ef [mimetypes.conf] add text/markdown to utf-8 list, regenerate mimetypes.conf
Change-Id: I653b49a49f348224f6b29d76194cf1061968b494
2017-01-02 17:09:39 +01:00
Stefan Bühler c8b27d7462 [mod_openssl] fix warnings and compile breaks with openssl 1.1.0
Change-Id: Ia69e8192004208a9e55246196b5b64d39cd53a66
2017-01-02 17:06:34 +01:00
Simon Lundström 64096982fa [mod_openssl/doc] Update links to man pages
Change-Id: I4f6fc1bca8ac7a4145b792fe7cb6fd57e4edcff6
2016-11-30 20:29:06 +01:00
Simon Lundström a4804cbd93 Add NO_TLSv1_1 and higher options
Change-Id: I69b675a8b41f84b9e786bc2ce1b9661fbd76cbd4
2016-11-30 19:42:18 +01:00
Stefan Bühler 3d2880258d [mod_gnutls] workaround gnutls API breakage, and prepare for future ones
Change-Id: I1b97aa31fd1a7adb0107761d05bf81a4509e9fc9
2016-10-21 20:08:41 +02:00
Stefan Bühler 9926bef92e [mod_gnutls/doc] more details which certificates are needed for OCSP
Change-Id: I1f7004bf2182f8023f19c0e3d2e3f5dee4968a9b
2016-09-02 12:08:32 +02:00
Stefan Bühler 85f3a1da1a [doc] decode HTML entities in code/markdown/textile blocks
- simulates an implicit "<![CDATA[ ... ]]>" mode
- if the blocks consists of a single CDATA node entities are not decoded;
  instead the CDATA content is used directly.

Change-Id: Ifc25d4ae49518d6d219a70d5760c214cafe0ed46
2016-09-02 12:07:13 +02:00
Stefan Bühler 98a14017a5 [mod_gnutls/docs] ocsptool is the ocsp tool
Change-Id: Iffd5e1488131c99877971e935c798a2371fe3209
2016-08-28 01:11:12 +02:00
Stefan Bühler fc0119720e [mod_gnutls] use gnutls_pem_base64_decode_alloc instead of gnutls_pem_base64_decode2 (compat name for old versions)
Change-Id: Id07d9d38ac809b4116b04debba41cd7351ebd40b
2016-08-27 20:19:53 +02:00
Stefan Bühler dca42093b1 [core] disable strict-alias warnings for libev wrappers
Change-Id: I6bea24eb35838e91cc3c3cc6a6f94664c7446533
2016-08-27 20:00:09 +02:00
Stefan Bühler d72a3c2940 [mod_gnutls] add basic OCSP response stapling support
Change-Id: I700b2afd0e0fc60ce4f864e77166e3fa2e36aaae
2016-08-27 19:55:25 +02:00
Stefan Bühler 7fb0148348 [mod_gnutls] use only pin callback, don't pass pin as parameter
- also check for number of attempts instead of flags,
  the same the gnutls internal callback is doing when a
  password is passed

Change-Id: I84f5a0c7a4e3aea6f55b7b28c2f57019128351c7
2016-08-27 14:00:43 +02:00
Stefan Bühler 6a0e57ec8f [parsers] fix unused variable warnings
Change-Id: Ifc0210235ada102e2dbcba23e1179e87b973510f
2016-08-27 11:32:23 +02:00
Stefan Bühler f17a221cd8 use _DEFAULT_SOURCE instead of _BSD_SOURCE
Change-Id: Iecd6f2b4d31cd8f64f1c7ed491650a9b2a35be28
2016-08-27 11:32:19 +02:00
Stefan Bühler 130f14a3ea [config] expect exactly one comma between list entries
- the parser didn't require a comma before, and accepted multiple ones
- comma at end of list still optional

Change-Id: I4bb07ceed9aaceb43a14f7eeb5d364d6718e7acc
2015-12-04 14:17:29 +01:00
Stefan Bühler a1340123be [cmake] handle include directories separately
- update cmake required version to 3.0
- should workaround cmake CodeBlocks generator (-> qtcreator) bug with not finding the include paths

Change-Id: I95a6741ff9fc6cc75ae62774ad53d438bfd67b90
2015-12-04 14:17:29 +01:00
Stefan Bühler 3a4698d6b9 [pattern] fix all calls of li_pattern_eval to provide latest regex match
Change-Id: I7aeaf908eab9da3fe24e9871f648e3fd58fddbe1
2015-12-04 13:36:27 +01:00
Stefan Bühler d2c6a28d97 use g_assert instead of g_assert_true for older glib2 versions
Change-Id: Ie2a4e7b723d7a123f08c9c79503425b40a76a5b7
2015-08-10 11:52:11 +02:00
Stefan Bühler c9a3764f12 [ssl] don't exit early when there is no data to write; otherwise EOS stream handling is not triggered
Change-Id: Ie49fa1e7062f91c5708ae72db48c8e2144570f20
2015-08-10 11:41:47 +02:00
Stefan Bühler 909626691e ignore CMakeLists.txt.user (qtcreator)
Change-Id: If42de6186888a301e1276c48182bd5bbb7e826bb
2015-08-09 10:13:08 +02:00
Stefan Bühler f527a16138 [debug] provide more unique event names for waitqueues
Change-Id: Iddf9e10b3902fbfe4fa7e97b7a172018d0d70b54
2015-08-09 10:12:21 +02:00
Stefan Bühler 535f6b4afb [streams] improve stream handling, hopefully fixes bug with lingering backend connections
- reset backend_source before backend_drain; first tell the backend we
  are not interested in data anymore, then force closing the outgoing
  data
- as backend_drain might get reopened for a Connection: upgrade, the
  backend cannot rely on backend_drain->out.is_closed, but waits for a
  disconnect (which will also release the last reference).  the same is
  done for backend_source, so a backend can just wait for both reference
  counts to drop to zero before actually closing the backend connection
  (unless it keeps the streams alive itself).

Change-Id: Ibfe7985debd71580dbb78b985abaf946f59e3024
2015-08-08 16:12:59 +02:00
Stefan Bühler 4375aba174 [request parser] adapt whitespace removal from response parser, fixes bug removing last character if CR was missing
Change-Id: Idd39363b460f1141aa2bebde187f3ca10a7bb344
2015-08-06 23:19:37 +02:00
Stefan Bühler f6def17999 [common] format IPv6 addresses with port as [addr]:port (added the square brackets)
Change-Id: Ic5246c6cf9e1762b8e8ea4c020983289c4e071c6
2015-08-06 21:49:06 +02:00
Stefan Bühler 3cfc0aa82c [automake] add missing files to dist tar
Change-Id: I2c53b97415301931ca9530ac8d54701daef59ec8
2015-08-06 00:29:36 +02:00
Stefan Bühler 811e13cfcd [common] fix out-of-bounds read in li_sockaddr_to_string
Change-Id: I8984b6b170a43eb8c0f648c31957c5ae441cf93f
2015-08-06 00:05:09 +02:00
Stefan Bühler 78ac96cc04 [mod_debug] show more details for events
Change-Id: I245cff8a95839fa961ec32a8a8b2f3da39edb777
2015-08-05 21:57:12 +02:00
Stefan Bühler 3f7de3b51a mimetypes.conf: add some new mime types, remove .dat, .sha1, .md5, update .vcf
- create-mimetypes.conf.pl: also parse lines with upper case characters
- rerun create-mimetypes.conf.pl with debian mime-support 3.58

Change-Id: I8a6e91b8cc879158d5cd8931630a67db4d5a0dd2
2015-07-11 14:04:34 +02:00
Stefan Bühler b37d95facf [lua] handle filter->vr == NULL in lua filters, handle filter->in == NULL in xsendfile
Change-Id: I5326ce72f1b45bf0b64194e6ec8935b6261d18d2
2015-06-13 22:57:54 +02:00
Stefan Bühler 6b22f2f104 [mod_debug] debug events
Change-Id: Ib776e950902a36f13ed766a78a92f6971310e87d
2015-01-25 13:27:59 +01:00
Stefan Bühler 66bd6b22a2 track event "names" for debugging
Change-Id: Ib8f2b589a6087de2355906a87bd2cd0c84bafcba
2015-01-17 15:16:29 +01:00
Marcus Rückert 17d066bc57 Add systemd unit file
Change-Id: I680ab136870a8474e4979517fe46f2c9eff8ce34
2015-01-06 16:33:06 +01:00
Stefan Bühler a5886b3a81 [mod_openssl] various fixes, fix error handling
- update docs with default options
- always set "session_id_context"
- load all algorithms
- cleanup error handling (abort on fatal errors, not the other way
  round, log non fatal errors in debug log-level)

Change-Id: I2b6028bbe97a237ab94ad00d58c7773d9d3d8830
2014-12-22 15:12:48 +01:00
Stefan Bühler 10305546cb [core] close out stream nicely on regular shutdown (fixes openssl session problem)
Change-Id: Iaac73fa64c03225751c4492b5c690094f3d6e97f
2014-12-22 15:11:31 +01:00
Stefan Bühler 93d04a3514 [tests] use gnutls certtool to create test ca
also supports generating ecc (ecdsa) keys and selecting a hash

Change-Id: Iafa6557a4f0a97885a3300861b842289b73ea1de
2014-11-09 11:26:15 +01:00
Stefan Bühler f144349c72 [mod_openssl] allow all authentication methods in default cipher string
The details depend on the server key anyway; with the new default string
ecdsa keys work out of the box.

Change-Id: I7229f899ffd8dca5740767f9832980198b4f7bee
2014-11-09 11:14:03 +01:00
Stefan Bühler fc02dcf9e2 [mod_gnutls] improve alert handling
* print alerts with numerical value
* show non-fatal alerts
* use log level info for all alerts
* use log level warning for non-fatal "unknown" errors

Change-Id: Ibaa33743bfe809579981fdeb121955ef5c6d0ab2
2014-11-09 08:45:43 +01:00
Stefan Bühler f7155d5972 [doc] mod_gnutls: add comment that reusing keys in DH is not recommended anyway
Change-Id: I392b2913ca5f7fdc88bf773032b27d2417211a58
2014-11-08 20:02:42 +01:00
Stefan Bühler 6954c4563b [doc] improve section about DH/DSA parameters in mod_gnutls
Change-Id: I9665691ff3c6dbb24defc02ae3f9a2f8efe8264d
2014-11-08 15:33:15 +01:00
Nikos Mavrogiannopoulos 91060ebe91 [mod_gnutls] allow pemfile to accept a key-value list of a certificate and key
In addition, this patch adds the ability to specify a PIN,
to be used to decrypt an encrypted key, or to login to a PKCS #11
module.

Change-Id: Iff36879926236d07be6baaa1736985a58c54d0cc
2014-11-02 08:27:20 +01:00
Nikos Mavrogiannopoulos 344e918f05 [mod_gnutls] when an alert is received print its actual value
Change-Id: I89b12cb5e53cbd0d36d9b30d9a7e99aa70836717
2014-11-01 12:25:03 +01:00
Stefan Bühler aa44c63f59 [doc] clarify "physical.exists" semantic
Change-Id: I8212e4e66c2666579a8726c35f6840e3eddf4366
2014-08-24 18:32:50 +02:00
Stefan Bühler 912b460145 [autobuild] fix configure to search for openssl in the specified paths 2014-08-17 09:45:52 +02:00
Stefan Bühler 72011fbede [mox_rewrite,mod_proxy,docs] fix request.raw_path handling (includes query-string) 2014-06-06 13:41:30 +02:00
Stefan Bühler 2fe2af2022 [docs] generate link back to index in sub pages 2014-06-06 12:18:50 +02:00
Stefan Bühler ab60804b9c [docs] fix typo 2014-06-06 12:18:46 +02:00
Stefan Bühler b5eac15433 [mod_proxy] use raw_path instead of re-encoded path 2014-06-06 12:08:16 +02:00
Stefan Bühler 6e2ca4b80c [tests] small cleanups: use req_header.overwrite, no_docroot option 2014-06-06 11:57:41 +02:00
Stefan Bühler 76c12e4077 [mod_rewrite] rewrite_raw result gets decoded 2014-06-06 11:43:40 +02:00
Stefan Bühler 4c741ce853 [core] expose request.raw_path as condition variable 2014-06-06 11:16:22 +02:00
Stefan Bühler e168e0653c [docs] fix typos, add some details, add rewrite_raw action 2014-05-27 20:07:12 +02:00
Stefan Bühler 9acfe515f7 [docs] fix xml error 2014-05-26 15:26:10 +02:00
Stefan Bühler 4741120b74 [docs] fix links, add index page, add small introduction, add lua config / api 2014-05-26 15:04:46 +02:00
Stefan Bühler 5c9a9bbaba [docs] some small fixes 2014-05-23 16:03:54 +02:00
Stefan Bühler dd9f052c57 [mod_gnutls] remove broken include 2014-05-23 11:24:43 +02:00
Stefan Bühler 47c1cf1c20 [tests] increase timeout for https requests 2014-05-22 18:31:33 +02:00
Stefan Bühler d58d98a62d [value] move value functions that depend on angel / main implementations into separate file, included in the other implementations
this fixes building with --no-undefined. also link with libm in cmake.
2014-05-22 14:58:26 +02:00
Stefan Bühler 55042a89a9 [tests] add a 0.2 second sleep before testing memcache lookup after store 2014-05-22 13:02:25 +02:00
Stefan Bühler 928d5dcb5e [angel] don't load angel core plugin for shut down, so the core plugin doesn't respawn a worker with default settings 2014-05-22 12:44:48 +02:00
Stefan Bühler c30060e0eb [angel] check setgid/setuid/... return values and abort on failure 2014-05-16 17:23:27 +02:00
Stefan Bühler 09002ad827 [mod_openssl] fix default cipher string 2014-05-16 17:22:16 +02:00
Stefan Bühler 5e30919291 [mod_openssl] wrap all options in #ifdef 2014-05-16 08:33:24 +02:00
Stefan Bühler 8596f874a7 [autobuild] fix duplicate config.h install and remove config.h from dist tarball 2014-05-16 08:17:03 +02:00
Stefan Bühler 72d4578e70 [doc] add script to generate mimetypes.conf 2014-05-16 08:16:08 +02:00
Stefan Bühler b6ee9241ad [angel_fake] use li_sockaddr_from_string in li_angel_fake_listen 2014-04-15 13:05:52 +02:00
Stefan Bühler 5f4019359e [config parser] copy fixes from angel config parser 2014-04-15 12:41:09 +02:00
Stefan Bühler 63f4b78353 fix some socket address handlings - limit unix socket path names to struct size 2014-04-15 12:06:11 +02:00
Stefan Bühler 5b706ac033 [simple-stream] fix double assignment 2014-04-15 11:30:36 +02:00
Stefan Bühler 21e18176f2 fix wrong operator to check for set bit in events 2014-04-15 11:24:51 +02:00
Stefan Bühler ed7e70d0b4 [angel] fix missing break in switch statement in config parser for casting string to int 2014-04-15 11:22:15 +02:00
Stefan Bühler 3ad9e4cb01 [actions] fix dereference before null check 2014-04-15 11:13:20 +02:00
Stefan Bühler acd2967534 assert many previously unchecked return values, handle some explicitly, remove FD_CLOEXEC in worker - mustn't fork 2014-04-15 11:11:51 +02:00
Stefan Bühler 582a8585ff [fastcgi] assert pointer is not NULL before dereference before NULL check in a loop 2014-04-15 11:07:39 +02:00
Stefan Bühler 118844573c [angel] fix memory leak on error in angel config parser 2014-04-15 10:39:14 +02:00
Stefan Bühler 176d6099aa [liValue] fix missing break in switch statement 2014-04-15 10:33:48 +02:00
Stefan Bühler e5e37b8369 [idlist] fix bad shift operations 2014-04-14 17:51:47 +02:00
Stefan Bühler e0ed289c1a [liValue] fix dereference after NULL check 2014-04-14 17:49:41 +02:00
159 changed files with 7213 additions and 2101 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ include/lighttpd/config.h.in~
install-sh
ltmain.sh
missing
CMakeLists.txt.user

View File

@ -1,4 +1,4 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6.4 FATAL_ERROR)
CMAKE_MINIMUM_REQUIRED(VERSION 3.0 FATAL_ERROR)
PROJECT(lighttpd C)
SET(PACKAGE_NAME ${CMAKE_PROJECT_NAME})

View File

@ -1,6 +1,6 @@
SUBDIRS=contrib doc src include tests
ACLOCAL_AMFLAGS=-I m4
EXTRA_DIST=autogen.sh CMakeLists.txt cmake
EXTRA_DIST=autogen.sh CMakeLists.txt cmake build-helpers
DISTCHECK_CONFIGURE_FLAGS=--with-lua --with-openssl --with-kerberos5 --with-gnutls --with-zlib --with-bzip2

136
build-helpers/glib-tap.mk Normal file
View File

@ -0,0 +1,136 @@
# GLIB - Library of useful C routines
AM_TESTS_ENVIRONMENT= \
G_TEST_SRCDIR="$(abs_srcdir)" \
G_TEST_BUILDDIR="$(abs_builddir)" \
G_DEBUG=gc-friendly \
MALLOC_CHECK_=2 \
MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-helpers/tap-driver.sh
LOG_COMPILER = $(top_srcdir)/build-helpers/tap-test
NULL =
# initialize variables for unconditional += appending
BUILT_SOURCES =
BUILT_EXTRA_DIST =
CLEANFILES = *.log *.trs
DISTCLEANFILES =
MAINTAINERCLEANFILES =
EXTRA_DIST =
TESTS =
installed_test_LTLIBRARIES =
installed_test_PROGRAMS =
installed_test_SCRIPTS =
nobase_installed_test_DATA =
noinst_LTLIBRARIES =
noinst_PROGRAMS =
noinst_SCRIPTS =
noinst_DATA =
check_LTLIBRARIES =
check_PROGRAMS =
check_SCRIPTS =
check_DATA =
# We support a fairly large range of possible variables. It is expected that all types of files in a test suite
# will belong in exactly one of the following variables.
#
# First, we support the usual automake suffixes, but in lowercase, with the customary meaning:
#
# test_programs, test_scripts, test_data, test_ltlibraries
#
# The above are used to list files that are involved in both uninstalled and installed testing. The
# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite.
# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data is
# installed in the same way as it appears in the package layout.
#
# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled',
# like so:
#
# installed_test_programs, uninstalled_test_programs
# installed_test_scripts, uninstalled_test_scripts
# installed_test_data, uninstalled_test_data
# installed_test_ltlibraries, uninstalled_test_ltlibraries
#
# Additionally, we support 'extra' infixes for programs and scripts. This is used for support programs/scripts
# that should not themselves be run as testcases (but exist to be used from other testcases):
#
# test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs
# test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts
#
# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or data
# file automatically end up in the tarball.
#
# dist_test_scripts, dist_test_data, dist_test_extra_scripts
# dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts
# dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts
#
# Note that no file is automatically disted unless it appears in one of the dist_ variables. This follows the
# standard automake convention of not disting programs scripts or data by default.
#
# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their disted
# variants) will be run as part of the in-tree 'make check'. These are all assumed to be runnable under
# gtester. That's a bit strange for scripts, but it's possible.
TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \
$(dist_test_scripts) $(dist_uninstalled_test_scripts)
# Note: build even the installed-only targets during 'make check' to ensure that they still work.
# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to
# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were
# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'.
all_test_programs = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \
$(test_extra_programs) $(uninstalled_test_extra_programs) $(installed_test_extra_programs)
all_test_scripts = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \
$(test_extra_scripts) $(uninstalled_test_extra_scripts) $(installed_test_extra_scripts)
all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) \
$(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) $(dist_installed_test_extra_scripts)
all_test_scripts += $(all_dist_test_scripts)
EXTRA_DIST += $(all_dist_test_scripts)
all_test_data = $(test_data) $(uninstalled_test_data) $(installed_test_data)
all_dist_test_data = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data)
all_test_data += $(all_dist_test_data)
EXTRA_DIST += $(all_dist_test_data)
all_test_ltlibs = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries)
if ENABLE_ALWAYS_BUILD_TESTS
noinst_LTLIBRARIES += $(all_test_ltlibs)
noinst_PROGRAMS += $(all_test_programs)
noinst_SCRIPTS += $(all_test_scripts)
noinst_DATA += $(all_test_data)
else
check_LTLIBRARIES += $(all_test_ltlibs)
check_PROGRAMS += $(all_test_programs)
check_SCRIPTS += $(all_test_scripts)
check_DATA += $(all_test_data)
endif
if ENABLE_INSTALLED_TESTS
installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \
$(test_extra_programs) $(installed_test_extra_programs)
installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \
$(test_extra_scripts) $(installed_test_extra_scripts)
installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \
$(dist_installed_test_scripts) $(dist_installed_test_extra_scripts)
nobase_installed_test_DATA += $(test_data) $(installed_test_data)
nobase_installed_test_DATA += $(dist_test_data) $(dist_installed_test_data)
installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries)
installed_testcases = $(test_programs) $(installed_test_programs) \
$(test_scripts) $(installed_test_scripts) \
$(dist_test_scripts) $(dist_installed_test_scripts)
installed_test_meta_DATA = $(installed_testcases:=.test)
%.test: %$(EXEEXT) Makefile
$(AM_V_GEN) ($(MKDIR_P) $(@D); \
echo '[Test]' > $@.tmp; \
echo 'Type=session' >> $@.tmp; \
echo 'Exec=$(installed_testdir)/$(notdir $<) --tap' >> $@.tmp; \
echo 'Output=TAP' >> $@.tmp; \
mv $@.tmp $@)
CLEANFILES += $(installed_test_meta_DATA)
endif

View File

@ -0,0 +1,31 @@
# Increment this whenever this file is changed.
#serial 1
dnl GLIB_TESTS
dnl
AC_DEFUN([GLIB_TESTS],
[
AC_ARG_ENABLE(installed-tests,
AS_HELP_STRING([--enable-installed-tests],
[Enable installation of some test cases]),
[case ${enableval} in
yes) ENABLE_INSTALLED_TESTS="1" ;;
no) ENABLE_INSTALLED_TESTS="" ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-installed-tests]) ;;
esac])
AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], test "$ENABLE_INSTALLED_TESTS" = "1")
AC_ARG_ENABLE(always-build-tests,
AS_HELP_STRING([--enable-always-build-tests],
[Enable always building tests during 'make all']),
[case ${enableval} in
yes) ENABLE_ALWAYS_BUILD_TESTS="1" ;;
no) ENABLE_ALWAYS_BUILD_TESTS="" ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-always-build-tests]) ;;
esac])
AM_CONDITIONAL([ENABLE_ALWAYS_BUILD_TESTS], test "$ENABLE_ALWAYS_BUILD_TESTS" = "1")
if test "$ENABLE_INSTALLED_TESTS" = "1"; then
AC_SUBST(installed_test_metadir, [${datadir}/installed-tests/]AC_PACKAGE_NAME)
AC_SUBST(installed_testdir, [${libexecdir}/installed-tests/]AC_PACKAGE_NAME)
fi
])

652
build-helpers/tap-driver.sh Normal file
View File

@ -0,0 +1,652 @@
#! /bin/sh
# Copyright (C) 2011-2013 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
scriptversion=2011-12-27.17; # UTC
# Make unconditional expansion of undefined variables an error. This
# helps a lot in preventing typo-related bugs.
set -u
me=tap-driver.sh
fatal ()
{
echo "$me: fatal: $*" >&2
exit 1
}
usage_error ()
{
echo "$me: $*" >&2
print_usage >&2
exit 2
}
print_usage ()
{
cat <<END
Usage:
tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH
[--expect-failure={yes|no}] [--color-tests={yes|no}]
[--enable-hard-errors={yes|no}] [--ignore-exit]
[--diagnostic-string=STRING] [--merge|--no-merge]
[--comments|--no-comments] [--] TEST-COMMAND
The \`--test-name', \`--log-file' and \`--trs-file' options are mandatory.
END
}
# TODO: better error handling in option parsing (in particular, ensure
# TODO: $log_file, $trs_file and $test_name are defined).
test_name= # Used for reporting.
log_file= # Where to save the result and output of the test script.
trs_file= # Where to save the metadata of the test run.
expect_failure=0
color_tests=0
merge=0
ignore_exit=0
comments=0
diag_string='#'
while test $# -gt 0; do
case $1 in
--help) print_usage; exit $?;;
--version) echo "$me $scriptversion"; exit $?;;
--test-name) test_name=$2; shift;;
--log-file) log_file=$2; shift;;
--trs-file) trs_file=$2; shift;;
--color-tests) color_tests=$2; shift;;
--expect-failure) expect_failure=$2; shift;;
--enable-hard-errors) shift;; # No-op.
--merge) merge=1;;
--no-merge) merge=0;;
--ignore-exit) ignore_exit=1;;
--comments) comments=1;;
--no-comments) comments=0;;
--diagnostic-string) diag_string=$2; shift;;
--) shift; break;;
-*) usage_error "invalid option: '$1'";;
esac
shift
done
test $# -gt 0 || usage_error "missing test command"
case $expect_failure in
yes) expect_failure=1;;
*) expect_failure=0;;
esac
if test $color_tests = yes; then
init_colors='
color_map["red"]="" # Red.
color_map["grn"]="" # Green.
color_map["lgn"]="" # Light green.
color_map["blu"]="" # Blue.
color_map["mgn"]="" # Magenta.
color_map["std"]="" # No color.
color_for_result["ERROR"] = "mgn"
color_for_result["PASS"] = "grn"
color_for_result["XPASS"] = "red"
color_for_result["FAIL"] = "red"
color_for_result["XFAIL"] = "lgn"
color_for_result["SKIP"] = "blu"'
else
init_colors=''
fi
# :; is there to work around a bug in bash 3.2 (and earlier) which
# does not always set '$?' properly on redirection failure.
# See the Autoconf manual for more details.
:;{
(
# Ignore common signals (in this subshell only!), to avoid potential
# problems with Korn shells. Some Korn shells are known to propagate
# to themselves signals that have killed a child process they were
# waiting for; this is done at least for SIGINT (and usually only for
# it, in truth). Without the `trap' below, such a behaviour could
# cause a premature exit in the current subshell, e.g., in case the
# test command it runs gets terminated by a SIGINT. Thus, the awk
# script we are piping into would never seen the exit status it
# expects on its last input line (which is displayed below by the
# last `echo $?' statement), and would thus die reporting an internal
# error.
# For more information, see the Autoconf manual and the threads:
# <http://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html>
# <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html>
trap : 1 3 2 13 15
if test $merge -gt 0; then
exec 2>&1
else
exec 2>&3
fi
"$@"
echo $?
) | LC_ALL=C ${AM_TAP_AWK-awk} \
-v me="$me" \
-v test_script_name="$test_name" \
-v log_file="$log_file" \
-v trs_file="$trs_file" \
-v expect_failure="$expect_failure" \
-v merge="$merge" \
-v ignore_exit="$ignore_exit" \
-v comments="$comments" \
-v diag_string="$diag_string" \
'
# FIXME: the usages of "cat >&3" below could be optimized when using
# FIXME: GNU awk, and/or on systems that supports /dev/fd/.
# Implementation note: in what follows, `result_obj` will be an
# associative array that (partly) simulates a TAP result object
# from the `TAP::Parser` perl module.
## ----------- ##
## FUNCTIONS ##
## ----------- ##
function fatal(msg)
{
print me ": " msg | "cat >&2"
exit 1
}
function abort(where)
{
fatal("internal error " where)
}
# Convert a boolean to a "yes"/"no" string.
function yn(bool)
{
return bool ? "yes" : "no";
}
function add_test_result(result)
{
if (!test_results_index)
test_results_index = 0
test_results_list[test_results_index] = result
test_results_index += 1
test_results_seen[result] = 1;
}
# Whether the test script should be re-run by "make recheck".
function must_recheck()
{
for (k in test_results_seen)
if (k != "XFAIL" && k != "PASS" && k != "SKIP")
return 1
return 0
}
# Whether the content of the log file associated to this test should
# be copied into the "global" test-suite.log.
function copy_in_global_log()
{
for (k in test_results_seen)
if (k != "PASS")
return 1
return 0
}
# FIXME: this can certainly be improved ...
function get_global_test_result()
{
if ("ERROR" in test_results_seen)
return "ERROR"
if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
return "FAIL"
all_skipped = 1
for (k in test_results_seen)
if (k != "SKIP")
all_skipped = 0
if (all_skipped)
return "SKIP"
return "PASS";
}
function stringify_result_obj(result_obj)
{
if (result_obj["is_unplanned"] || result_obj["number"] != testno)
return "ERROR"
if (plan_seen == LATE_PLAN)
return "ERROR"
if (result_obj["directive"] == "TODO")
return result_obj["is_ok"] ? "XPASS" : "XFAIL"
if (result_obj["directive"] == "SKIP")
return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL;
if (length(result_obj["directive"]))
abort("in function stringify_result_obj()")
return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
}
function decorate_result(result)
{
color_name = color_for_result[result]
if (color_name)
return color_map[color_name] "" result "" color_map["std"]
# If we are not using colorized output, or if we do not know how
# to colorize the given result, we should return it unchanged.
return result
}
function report(result, details)
{
if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/)
{
msg = ": " test_script_name
add_test_result(result)
}
else if (result == "#")
{
msg = " " test_script_name ":"
}
else
{
abort("in function report()")
}
if (length(details))
msg = msg " " details
# Output on console might be colorized.
print decorate_result(result) msg
# Log the result in the log file too, to help debugging (this is
# especially true when said result is a TAP error or "Bail out!").
print result msg | "cat >&3";
}
function testsuite_error(error_message)
{
report("ERROR", "- " error_message)
}
function handle_tap_result()
{
details = result_obj["number"];
if (length(result_obj["description"]))
details = details " " result_obj["description"]
if (plan_seen == LATE_PLAN)
{
details = details " # AFTER LATE PLAN";
}
else if (result_obj["is_unplanned"])
{
details = details " # UNPLANNED";
}
else if (result_obj["number"] != testno)
{
details = sprintf("%s # OUT-OF-ORDER (expecting %d)",
details, testno);
}
else if (result_obj["directive"])
{
details = details " # " result_obj["directive"];
if (length(result_obj["explanation"]))
details = details " " result_obj["explanation"]
}
report(stringify_result_obj(result_obj), details)
}
# `skip_reason` should be empty whenever planned > 0.
function handle_tap_plan(planned, skip_reason)
{
planned += 0 # Avoid getting confused if, say, `planned` is "00"
if (length(skip_reason) && planned > 0)
abort("in function handle_tap_plan()")
if (plan_seen)
{
# Error, only one plan per stream is acceptable.
testsuite_error("multiple test plans")
return;
}
planned_tests = planned
# The TAP plan can come before or after *all* the TAP results; we speak
# respectively of an "early" or a "late" plan. If we see the plan line
# after at least one TAP result has been seen, assume we have a late
# plan; in this case, any further test result seen after the plan will
# be flagged as an error.
plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN)
# If testno > 0, we have an error ("too many tests run") that will be
# automatically dealt with later, so do not worry about it here. If
# $plan_seen is true, we have an error due to a repeated plan, and that
# has already been dealt with above. Otherwise, we have a valid "plan
# with SKIP" specification, and should report it as a particular kind
# of SKIP result.
if (planned == 0 && testno == 0)
{
if (length(skip_reason))
skip_reason = "- " skip_reason;
report("SKIP", skip_reason);
}
}
function extract_tap_comment(line)
{
if (index(line, diag_string) == 1)
{
# Strip leading `diag_string` from `line`.
line = substr(line, length(diag_string) + 1)
# And strip any leading and trailing whitespace left.
sub("^[ \t]*", "", line)
sub("[ \t]*$", "", line)
# Return what is left (if any).
return line;
}
return "";
}
# When this function is called, we know that line is a TAP result line,
# so that it matches the (perl) RE "^(not )?ok\b".
function setup_result_obj(line)
{
# Get the result, and remove it from the line.
result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0)
sub("^(not )?ok[ \t]*", "", line)
# If the result has an explicit number, get it and strip it; otherwise,
# automatically assing the next progresive number to it.
if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/)
{
match(line, "^[0-9]+")
# The final `+ 0` is to normalize numbers with leading zeros.
result_obj["number"] = substr(line, 1, RLENGTH) + 0
line = substr(line, RLENGTH + 1)
}
else
{
result_obj["number"] = testno
}
if (plan_seen == LATE_PLAN)
# No further test results are acceptable after a "late" TAP plan
# has been seen.
result_obj["is_unplanned"] = 1
else if (plan_seen && testno > planned_tests)
result_obj["is_unplanned"] = 1
else
result_obj["is_unplanned"] = 0
# Strip trailing and leading whitespace.
sub("^[ \t]*", "", line)
sub("[ \t]*$", "", line)
# This will have to be corrected if we have a "TODO"/"SKIP" directive.
result_obj["description"] = line
result_obj["directive"] = ""
result_obj["explanation"] = ""
if (index(line, "#") == 0)
return # No possible directive, nothing more to do.
# Directives are case-insensitive.
rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*"
# See whether we have the directive, and if yes, where.
pos = match(line, rx "$")
if (!pos)
pos = match(line, rx "[^a-zA-Z0-9_]")
# If there was no TAP directive, we have nothing more to do.
if (!pos)
return
# Let`s now see if the TAP directive has been escaped. For example:
# escaped: ok \# SKIP
# not escaped: ok \\# SKIP
# escaped: ok \\\\\# SKIP
# not escaped: ok \ # SKIP
if (substr(line, pos, 1) == "#")
{
bslash_count = 0
for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--)
bslash_count += 1
if (bslash_count % 2)
return # Directive was escaped.
}
# Strip the directive and its explanation (if any) from the test
# description.
result_obj["description"] = substr(line, 1, pos - 1)
# Now remove the test description from the line, that has been dealt
# with already.
line = substr(line, pos)
# Strip the directive, and save its value (normalized to upper case).
sub("^[ \t]*#[ \t]*", "", line)
result_obj["directive"] = toupper(substr(line, 1, 4))
line = substr(line, 5)
# Now get the explanation for the directive (if any), with leading
# and trailing whitespace removed.
sub("^[ \t]*", "", line)
sub("[ \t]*$", "", line)
result_obj["explanation"] = line
}
function get_test_exit_message(status)
{
if (status == 0)
return ""
if (status !~ /^[1-9][0-9]*$/)
abort("getting exit status")
if (status < 127)
exit_details = ""
else if (status == 127)
exit_details = " (command not found?)"
else if (status >= 128 && status <= 255)
exit_details = sprintf(" (terminated by signal %d?)", status - 128)
else if (status > 256 && status <= 384)
# We used to report an "abnormal termination" here, but some Korn
# shells, when a child process die due to signal number n, can leave
# in $? an exit status of 256+n instead of the more standard 128+n.
# Apparently, both behaviours are allowed by POSIX (2008), so be
# prepared to handle them both. See also Austing Group report ID
# 0000051 <http://www.austingroupbugs.net/view.php?id=51>
exit_details = sprintf(" (terminated by signal %d?)", status - 256)
else
# Never seen in practice.
exit_details = " (abnormal termination)"
return sprintf("exited with status %d%s", status, exit_details)
}
function write_test_results()
{
print ":global-test-result: " get_global_test_result() > trs_file
print ":recheck: " yn(must_recheck()) > trs_file
print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file
for (i = 0; i < test_results_index; i += 1)
print ":test-result: " test_results_list[i] > trs_file
close(trs_file);
}
BEGIN {
## ------- ##
## SETUP ##
## ------- ##
'"$init_colors"'
# Properly initialized once the TAP plan is seen.
planned_tests = 0
COOKED_PASS = expect_failure ? "XPASS": "PASS";
COOKED_FAIL = expect_failure ? "XFAIL": "FAIL";
# Enumeration-like constants to remember which kind of plan (if any)
# has been seen. It is important that NO_PLAN evaluates "false" as
# a boolean.
NO_PLAN = 0
EARLY_PLAN = 1
LATE_PLAN = 2
testno = 0 # Number of test results seen so far.
bailed_out = 0 # Whether a "Bail out!" directive has been seen.
# Whether the TAP plan has been seen or not, and if yes, which kind
# it is ("early" is seen before any test result, "late" otherwise).
plan_seen = NO_PLAN
## --------- ##
## PARSING ##
## --------- ##
is_first_read = 1
while (1)
{
# Involutions required so that we are able to read the exit status
# from the last input line.
st = getline
if (st < 0) # I/O error.
fatal("I/O error while reading from input stream")
else if (st == 0) # End-of-input
{
if (is_first_read)
abort("in input loop: only one input line")
break
}
if (is_first_read)
{
is_first_read = 0
nextline = $0
continue
}
else
{
curline = nextline
nextline = $0
$0 = curline
}
# Copy any input line verbatim into the log file.
print | "cat >&3"
# Parsing of TAP input should stop after a "Bail out!" directive.
if (bailed_out)
continue
# TAP test result.
if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/)
{
testno += 1
setup_result_obj($0)
handle_tap_result()
}
# TAP plan (normal or "SKIP" without explanation).
else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/)
{
# The next two lines will put the number of planned tests in $0.
sub("^1\\.\\.", "")
sub("[^0-9]*$", "")
handle_tap_plan($0, "")
continue
}
# TAP "SKIP" plan, with an explanation.
else if ($0 ~ /^1\.\.0+[ \t]*#/)
{
# The next lines will put the skip explanation in $0, stripping
# any leading and trailing whitespace. This is a little more
# tricky in truth, since we want to also strip a potential leading
# "SKIP" string from the message.
sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "")
sub("[ \t]*$", "");
handle_tap_plan(0, $0)
}
# "Bail out!" magic.
# Older versions of prove and TAP::Harness (e.g., 3.17) did not
# recognize a "Bail out!" directive when preceded by leading
# whitespace, but more modern versions (e.g., 3.23) do. So we
# emulate the latter, "more modern" behaviour.
else if ($0 ~ /^[ \t]*Bail out!/)
{
bailed_out = 1
# Get the bailout message (if any), with leading and trailing
# whitespace stripped. The message remains stored in `$0`.
sub("^[ \t]*Bail out![ \t]*", "");
sub("[ \t]*$", "");
# Format the error message for the
bailout_message = "Bail out!"
if (length($0))
bailout_message = bailout_message " " $0
testsuite_error(bailout_message)
}
# Maybe we have too look for dianogtic comments too.
else if (comments != 0)
{
comment = extract_tap_comment($0);
if (length(comment))
report("#", comment);
}
}
## -------- ##
## FINISH ##
## -------- ##
# A "Bail out!" directive should cause us to ignore any following TAP
# error, as well as a non-zero exit status from the TAP producer.
if (!bailed_out)
{
if (!plan_seen)
{
testsuite_error("missing test plan")
}
else if (planned_tests != testno)
{
bad_amount = testno > planned_tests ? "many" : "few"
testsuite_error(sprintf("too %s tests run (expected %d, got %d)",
bad_amount, planned_tests, testno))
}
if (!ignore_exit)
{
# Fetch exit status from the last line.
exit_message = get_test_exit_message(nextline)
if (exit_message)
testsuite_error(exit_message)
}
}
write_test_results()
exit 0
} # End of "BEGIN" block.
'
# TODO: document that we consume the file descriptor 3 :-(
} 3>"$log_file"
test $? -eq 0 || fatal "I/O or internal error"
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

5
build-helpers/tap-test Normal file
View File

@ -0,0 +1,5 @@
#! /bin/sh
# run a GTest in tap mode. The test binary is passed as $1
$1 -k --tap

View File

@ -31,6 +31,8 @@ IF(NOT LIBEV_FOUND)
IF(HAVE_LIBEV)
SET(LIBEV_LIBRARIES ev CACHE INTERNAL "")
SET(LIBEV_CFLAGS "" CACHE INTERNAL "")
SET(LIBEV_CFLAGS_OTHER "" CACHE INTERNAL "")
SET(LIBEV_INCLUDE_DIRS "" CACHE INTERNAL "")
SET(LIBEV_LDFLAGS "-L${LIBEV_LIBDIR} -lev" CACHE INTERNAL "")
SET(LIBEV_FOUND TRUE CACHE INTERNAL "Found libev" FORCE)
ELSE(HAVE_LIBEV)
@ -49,6 +51,8 @@ IF(NOT LIBEV_FOUND)
CHECK_LIBRARY_EXISTS(ev ev_time "" HAVE_LIBEV)
IF(HAVE_LIBEV)
SET(LIBEV_CFLAGS "" CACHE INTERNAL "")
SET(LIBEV_CFLAGS_OTHER "" CACHE INTERNAL "")
SET(LIBEV_INCLUDE_DIRS "" CACHE INTERNAL "")
SET(LIBEV_LDFLAGS "-lev" CACHE INTERNAL "")
SET(LIBEV_FOUND TRUE CACHE INTERNAL "Found libev" FORCE)
ELSE(HAVE_LIBEV)

View File

@ -11,6 +11,7 @@ MACRO(ADD_AND_INSTALL_LIBRARY LIBNAME SRCFILES)
ADD_TARGET_PROPERTIES(${LIBNAME} LINK_FLAGS ${COMMON_LDFLAGS})
ADD_TARGET_PROPERTIES(${LIBNAME} COMPILE_FLAGS ${COMMON_CFLAGS})
SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
TARGET_INCLUDE_DIRECTORIES(${LIBNAME} PRIVATE ${COMMON_INCLUDE_DIRECTORIES})
TARGET_LINK_LIBRARIES(${LIBNAME} lighttpd-${PACKAGE_VERSION}-common lighttpd-${PACKAGE_VERSION}-shared)
@ -26,7 +27,7 @@ MACRO(ADD_TARGET_PROPERTIES _target _name)
SET(_properties "${_properties} ${_prop}")
ENDFOREACH(_prop)
GET_TARGET_PROPERTY(_old_properties ${_target} ${_name})
MESSAGE(STATUS "adding property to ${_target} ${_name}:" ${_properties})
#MESSAGE(STATUS "adding property to ${_target} ${_name}:" ${_properties})
IF(NOT _old_properties)
# in case it's NOTFOUND
SET(_old_properties)

View File

@ -1,23 +1,16 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.61])
AC_INIT([lighttpd],[2.0.0],[contact@lighttpd.net])
AC_PREREQ([2.69])
AC_INIT([lighttpd],[2.0.0],[],[],[https://redmine.lighttpd.net/projects/lighttpd2/wiki])
AC_CONFIG_SRCDIR([src/main/lighttpd_worker.c])
AC_CONFIG_HEADER([include/lighttpd/config.h])
AC_CONFIG_HEADERS([include/lighttpd/config.h])
AC_CONFIG_MACRO_DIR([m4])
dnl HOW FUCKING BROKEN IS AUTOMAKE...
dnl serial-tests is not recognized before 1.12, and required for serial tests with 1.13
dnl if you don't use the default (symlinked) automake on your box, set AUTOMAKE to the path you're using
m4_define([serial_tests], [
m4_esyscmd([case `${AUTOMAKE:-automake} --version | head -n 1` in
*1.11.*|*1.10.*|*1.9.*);;
*) echo serial-tests;;
esac])
])
AM_INIT_AUTOMAKE([-Wall -Wno-portability -Wno-override -Werror foreign dist-bzip2 tar-ustar] serial-tests)
AM_INIT_AUTOMAKE([-Wall -Wno-portability -Wno-override -Werror foreign dist-bzip2 tar-ustar] serial_tests)
m4_include([build-helpers/glibtests.m4])
GLIB_TESTS
dnl check environment
AC_USE_SYSTEM_EXTENSIONS
@ -35,18 +28,13 @@ if test "x$RAGEL" = "x"; then
fi
AC_SUBST([RAGEL])
dnl libtool
AC_DISABLE_STATIC
AC_ENABLE_SHARED
m4_ifndef([PKG_PROG_PKG_CONFIG], [m4_fatal([pkg-config not installed])])
m4_ifndef([AC_PROG_LIBTOOL], [m4_fatal([libtool not installed])])
m4_ifndef([LT_INIT], [m4_fatal([libtool not installed])])
AC_PROG_LIBTOOL
LT_INIT([shared disable-static])
# Checks for header files.
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([ \
unistd.h \
@ -126,8 +114,8 @@ if test x$lfs = xtrue; then
fi
dnl Check for gnutls
AC_ARG_WITH([gnutls], [AS_HELP_STRING([--with-gnutls],[gnutls library for ssl/tls])],
[WITH_GNUTLS=$withval],[WITH_GNUTLS=no])
AC_ARG_WITH([gnutls], [AS_HELP_STRING([--with-gnutls],[gnutls library for ssl/tls (default)])],
[WITH_GNUTLS=$withval],[WITH_GNUTLS=yes])
if test "$WITH_GNUTLS" != "no"; then
PKG_CHECK_MODULES([GNUTLS], [gnutls],[],[
@ -143,8 +131,8 @@ AM_CONDITIONAL([USE_GNUTLS], [test "$USE_GNUTLS" = "true"])
dnl Check for libidn, needed to decode SNI names
AC_ARG_WITH([sni], [AS_HELP_STRING([--with-sni],[SNI support for gnutls/openssl, needs libidn])],
[WITH_SNI=$withval],[WITH_SNI=no])
AC_ARG_WITH([sni], [AS_HELP_STRING([--with-sni],[SNI support for gnutls/openssl, needs libidn (default)])],
[WITH_SNI=$withval],[WITH_SNI=yes])
if test "$WITH_SNI" != "no"; then
PKG_CHECK_MODULES([IDN], [libidn],[],[
@ -162,8 +150,8 @@ AM_CONDITIONAL([USE_SNI], [test "$USE_SNI" = "true"])
dnl Check for lua
AC_MSG_CHECKING([for lua])
AC_ARG_WITH([lua], [AS_HELP_STRING([--with-lua],[lua engine (recommended)])],
[WITH_LUA=$withval],[WITH_LUA=no])
AC_ARG_WITH([lua], [AS_HELP_STRING([--with-lua],[lua engine (default)])],
[WITH_LUA=$withval],[WITH_LUA=yes])
AC_MSG_RESULT([$WITH_LUA])
if test "$WITH_LUA" != "no"; then
@ -201,13 +189,11 @@ fi
AM_CONDITIONAL([USE_LUA], [test "$USE_LUA" = "true"])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_UID_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_HEADER_TIME
## solaris needs -lsocket -lnsl
AC_SEARCH_LIBS([socket],[socket])
@ -255,6 +241,8 @@ if test x$ipv6 = xtrue; then
if test "$ac_cv_ipv6_support" = yes; then
AC_DEFINE([HAVE_IPV6],[1],[Whether to enable IPv6 support])
else
AC_MSG_ERROR([IPv6 not supported. Use --disable-ipv6 if this is acceptable.])
fi
fi
@ -271,8 +259,8 @@ fi
dnl Checking for libunwind
AC_MSG_CHECKING(for libunwind)
AC_ARG_WITH(libunwind,
AC_HELP_STRING([--with-libunwind],[Include libunwind support for backtraces on assert failures]),
[WITH_LIBUNWIND=$withval],[WITH_LIBUNWIND=no])
AS_HELP_STRING([--with-libunwind],[Include libunwind support for backtraces on assert failures (default)]),
[WITH_LIBUNWIND=$withval],[WITH_LIBUNWIND=yes])
if test "$WITH_LIBUNWIND" != "no"; then
have_libunwind=no
@ -313,8 +301,8 @@ fi
dnl Check for openssl
AC_MSG_CHECKING([for OpenSSL])
AC_ARG_WITH([openssl],
AS_HELP_STRING([--with-openssl@<:@=DIR@:>@],[Include openssl support (default no)]),
[WITH_OPENSSL=$withval],[WITH_OPENSSL=no])
AS_HELP_STRING([--with-openssl@<:@=DIR@:>@],[Include openssl support (default)]),
[WITH_OPENSSL=$withval],[WITH_OPENSSL=yes])
OPENSSL_CFLAGS=""
OPENSSL_LIBS=""
@ -331,27 +319,32 @@ fi
AC_MSG_RESULT([$use_openssl])
AC_ARG_WITH([openssl-includes],
[AS_HELP_STRING([--with-openssl-includes=DIR],[OpenSSL includes])],
[AS_HELP_STRING([--with-openssl-includes=DIR],[Custom OpenSSL include path])],
[ use_openssl=yes OPENSSL_CFLAGS="-I$withval" ]
)
AC_ARG_WITH([openssl-libs],
[AS_HELP_STRING([--with-openssl-libs=DIR],[OpenSSL libraries])],
[AS_HELP_STRING([--with-openssl-libs=DIR],[Custom OpenSSL library path])],
[ use_openssl=yes OPENSSL_LIBS="-L$withval" ]
)
AC_ARG_WITH([kerberos5],
[AS_HELP_STRING([--with-kerberos5],[use Kerberos5 support with OpenSSL])],
[ use_kerberos=yes ], [use_kerberos=no]
[ use_kerberos=$withval ], [use_kerberos=no]
)
if test "x$use_openssl" = "xyes"; then
if test "x$use_kerberos" != "xyes"; then
if test "$use_openssl" = "yes"; then
if test "$use_kerberos" = "no"; then
OPENSSL_CFLAGS="$OPENSSL_CFLAGS -DOPENSSL_NO_KRB5"
fi
OLD_LIBS="$LIBS"
OLD_LDFLAGS="$LDFLAGS"
OLD_CPPFLAGS="$CPPFLAGS"
LDFLAGS="$LDFLAGS $OPENSSL_LIBS"
CPPFLAGS="$CPPFLAGS $OPENSSL_CFLAGS"
AC_CHECK_HEADERS([openssl/ssl.h])
OLDLIBS="$LIBS"
AC_CHECK_LIB([crypto], [BIO_f_base64], [
AC_CHECK_LIB([ssl], [SSL_new], [
OPENSSL_LIBS="$OPENSSL_LIBS -lssl -lcrypto"
@ -359,7 +352,10 @@ if test "x$use_openssl" = "xyes"; then
AC_DEFINE([HAVE_OPENSSL], [], [Have openssl])
], [], [ -lcrypto "$DL_LIB" ])
], [], [])
LIBS="$OLDLIBS"
LIBS="$OLD_LIBS"
LDFLAGS="$OLD_LDFLAGS"
CPPFLAGS="$OLD_CPPFLAGS"
if test "x$have_openssl" != "xyes"; then
AC_MSG_ERROR([Couldn't find openssl])
@ -377,7 +373,7 @@ use_mod_deflate=no
# check for zlib
AC_MSG_CHECKING([for zlib support])
AC_ARG_WITH([zlib], [AS_HELP_STRING([--with-zlib],[Enable zlib support for mod_deflate])],
AC_ARG_WITH([zlib], [AS_HELP_STRING([--with-zlib],[Enable zlib support for mod_deflate (default)])],
[WITH_ZLIB=$withval],[WITH_ZLIB=yes])
AC_MSG_RESULT([$WITH_ZLIB])
@ -395,7 +391,7 @@ AC_SUBST([Z_LIB])
# check for bzip2
AC_MSG_CHECKING([for bzip2 support])
AC_ARG_WITH([bzip2], [AS_HELP_STRING([--with-bzip2],[Enable bzip2 support for mod_deflate])],
AC_ARG_WITH([bzip2], [AS_HELP_STRING([--with-bzip2],[Enable bzip2 support for mod_deflate (default)])],
[WITH_BZIP2=$withval],[WITH_BZIP2=yes])
AC_MSG_RESULT([$WITH_BZIP2])
@ -458,9 +454,10 @@ AC_ARG_ENABLE([extra-warnings],
esac],[extrawarnings=false])
if test x$extrawarnings = xtrue; then
CFLAGS="${CFLAGS} -g -O2 -g2 -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wno-pointer-sign -Wcast-align -Wsign-compare -Wnested-externs -Wpointer-arith -Wl,--as-needed -Wformat-security"
CFLAGS="${CFLAGS} -g -O2 -g2 -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wcast-align -Wsign-compare -Wnested-externs -Wpointer-arith -Wmissing-prototypes -Wshadow -Wno-pointer-sign -Wformat-security -Wl,--as-needed -Wl,--no-undefined"
fi
AC_CONFIG_FILES([Makefile \
contrib/Makefile \
doc/Makefile \

View File

@ -1,5 +1,5 @@
EXTRA_DIST=angel.conf lighttpd.conf mimetypes.conf service \
default.html
EXTRA_DIST=angel.conf lighttpd.conf mimetypes.conf service systemd \
default.html create-mimetypes.conf.pl
# see src/modules/Makefile.am
luadir = $(datarootdir)/lighttpd2/lua

View File

@ -17,8 +17,11 @@ end
-- normal method to handle content
function XFilterDrop:handle(vr, outq, inq)
-- drop further input (we closed it already)
inq:skip_all()
-- drop input, close it
if nil ~= inq then
inq.is_closed = true
inq:skip_all()
end
return lighty.HANDLER_GO_ON
end
@ -26,8 +29,11 @@ end
-- returns the filter object so you can insert your own content in f.out (it is already closed)
local function add_drop_filter(vr)
local f = vr:add_filter_out(XFilterDrop:new())
f['in'].is_closed = true
f['in']:skip_all()
local inq = f['in']
if nil ~= inq then
inq.is_closed = true
inq:skip_all()
end
f.out.is_closed = true
return f
end

200
contrib/create-mimetypes.conf.pl Executable file
View File

@ -0,0 +1,200 @@
#!/usr/bin/perl -w
# Based on create-mime.assign.pl in debian lighttpd (1.4.x) package
# Creates an example mimetypes.conf from /etc/mime.types
use strict;
# text/* subtypes to serve as "text/...; charset=utf-8"
# text/html IS NOT INCLUDED: html has its own method for defining charset
# (<meta>), but the standards specify that content-type in HTTP wins over
# the setting in the html document.
# text/markdown doesn't have an official default charset, but requires
# one being specified - it seems reasonable to hardcode it to UTF-8
my %text_utf8 = map { $_ => 1 } qw(
css
csv
markdown
plain
x-bibtex
x-boo
x-c++hdr
x-c++src
x-chdr
x-csh
x-csrc
x-dsrc
x-diff
x-haskell
x-java
x-lilypond
x-literate-haskell
x-makefile
x-moc
x-pascal
x-perl
x-python
x-scala
x-sh
x-tcl
x-tex
);
# map extension to hash which maps types to the type they should be replaced with
my %manual_conflicts_resolve = (
'.asn' => {
'chemical/x-ncbi-asn1-spec' => 'application/octet-stream',
'chemical/x-ncbi-asn1' => 'application/octet-stream',
},
'.otf' => {
'application/font-sfnt' => 'font/otf',
'font/sfnt' => 'font/otf',
'font/ttf' => 'font/otf',
},
'.pcx' => {
'image/vnd.zbrush.pcx' => 'image/pcx',
},
'.png' => {
'image/vnd.mozilla.apng' => 'image/png',
},
'.ra' => {
'audio/x-pn-realaudio' => 'audio/x-realaudio',
},
'.ttf' => {
'application/font-sfnt' => 'font/ttf',
'font/sfnt' => 'font/ttf',
'font/otf' => 'font/ttf',
},
'.woff' => {
'application/font-woff' => 'font/woff',
},
);
open MIMETYPES, "/etc/mime.types" or die "Can't open mime.types: $!";
my %extensions;
sub set {
my ($extension, $mimetype) = @_;
$extensions{$extension} = $mimetype;
}
sub add {
my ($extension, $mimetype) = @_;
my $have = $extensions{$extension};
my $r = $manual_conflicts_resolve{$extension};
# update @_ too for calls to set
$_[1] = $mimetype = $r->{$mimetype} if $r && $r->{$mimetype};
# mime.types can have same extension for different mime types
if ($have) {
# application/octet-stream means we couldn't resolve another conflict
return if $have eq $mimetype || $have eq 'application/octet-stream';
my ($have_type, $have_subtype) = split /\//, $have, 2;
my ($type, $subtype) = split /\//, $mimetype, 2;
my $have_x = ($have_type =~ /^x-/ || $have_subtype =~ /^x-/);
my $x = ($type =~ /^x-/ || $subtype =~ /^x-/);
# entries without x- prefix in type/subtype win:
if ($have_x && !$x) {
return set @_; # overwrite
} elsif ($x && !$have_x) {
return; # ignore
}
# text/ wins over application/ for same subtype
if ($subtype eq $have_subtype) {
if ($type eq "text" && $have_type eq "application") {
return set @_; # overwrite
} elsif ($have_type eq "text" && $type eq "application") {
return; # ignore
}
}
print STDERR "Duplicate mimetype: '${extension}' => '${mimetype}' (already have '${have}'), merging to 'application/octet-stream'\n";
set ($extension, 'application/octet-stream');
} else {
set @_;
}
}
sub print_type {
my ($extension, $mimetype) = @_;
if ($mimetype =~ /^text\/(.*)$/) {
$mimetype .= "; charset=utf-8" if $text_utf8{$1};
}
print "\t\t\"${extension}\" => \"${mimetype}\",\n";
}
while (<MIMETYPES>) {
chomp;
s/\#.*//;
next if /^\w*$/;
if (/^([a-z0-9\/+-.]+)\s+((?:[a-z0-9.+-]+[ ]?)+)$/i) {
my $mimetype = $1;
my @extensions = split / /, $2;
foreach my $ext (@extensions) {
add(".${ext}", $mimetype);
}
}
}
# missing in /etc/mime.types;
# from http://www.iana.org/assignments/media-types/media-types.xhtml
add(".dtd", "application/xml-dtd");
print <<EOF;
# created by create-mimetypes.conf.pl
setup {
mime_types [
# /etc/mime.types
# and from http://www.iana.org/assignments/media-types/media-types.xhtml
EOF
# sort "x-" and "vnd." prefixed names after everything else
sub mimecmpvalue {
my ($mimetype) = @_;
$mimetype =~ s/(^|\/)(x-|vnd\.)/~$1$2/g;
return $mimetype;
}
for my $ext (sort { mimecmpvalue($extensions{$a}) cmp mimecmpvalue($extensions{$b}) || $a cmp $b } keys(%extensions)) {
print_type($ext, $extensions{$ext});
}
# array instead of hash to keep order as given here
my @useful = (
".tar.gz" , "application/x-gtar-compressed",
".gz" , "application/x-gzip",
".tbz" , "application/x-gtar-compressed",
".tar.bz2" , "application/x-gtar-compressed",
".bz2" , "application/x-bzip",
".log" , "text/plain",
".conf" , "text/plain",
".spec" , "text/plain",
"README" , "text/plain",
"Makefile" , "text/x-makefile",
);
print <<EOF;
# other useful mappings
EOF
while (my ($ext, $mimetype) = splice(@useful, 0, 2)) {
print_type($ext, $mimetype) unless $extensions{$ext};
}
print <<EOF;
# custom - put your own entries here (overwriting mappings above)
];
}
EOF

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
[Unit]
Description=Lighttpd2
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/lighttpd2 -c /etc/lighttpd2/angel.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@ -7,7 +7,10 @@ EXTRA_DIST=\
style.css \
doc_schema.xsd \
core_config.xml \
core_config_angel.xml \
core_fetch.xml \
core_introduction.xml \
core_lua.xml \
core_pattern.xml \
core_regex.xml \
mod_accesslog.xml \

View File

@ -3,9 +3,15 @@
require 'rubygems'
require 'nokogiri'
require 'bluecloth'
require 'redcloth'
require 'cgi'
# find all options, actions, setups in the c modules - can be used to check list for completeness (although it doesn't scan the lua modules):
# awk '/static const liPluginOption/, /;/ {print; }' src/*/*.c | grep '"' | cut -d'"' -f2 | perl -e 'print sort <>;'
# awk '/static const liPluginAction/, /;/ {print; }' src/*/*.c | grep '"' | cut -d'"' -f2 | perl -e 'print sort <>;'
# awk '/static const liPluginSetup/, /;/ {print; }' src/*/*.c | grep '"' | cut -d'"' -f2 | perl -e 'print sort <>;'
HTML_TEMPLATE='''
<!DOCTYPE html>
<html>
@ -50,6 +56,8 @@ class Documentation
@actions = []
@setups = []
@options = []
@sub_pages = []
end
def render_main
@ -66,7 +74,7 @@ class Documentation
def title=(value)
@title = value
@html_doc.xpath('/html/head/title')[0].inner_html = value
@html_doc.xpath('/html/head/title')[0].content = value ? 'lighttpd2 - ' + value : 'lighttpd2'
end
def to_html_fragment
@ -93,9 +101,18 @@ class Documentation
@options
end
def sub_pages
@sub_pages
end
def _store_toc(html, toc, rootToc = false)
return unless toc.length > 0
html.ul(:class => rootToc ? "nav bs-sidenav" : "nav" ) {
if rootToc and @basename != 'index' and @basename != 'all'
html.li(:class => 'index') {
html.a({:href => 'index.html'}, 'Index')
}
end
toc.each do |anchor, title, subtoc, cls|
html.li(:class => cls || '') {
html.a({:href => '#' + anchor}, title)
@ -177,7 +194,20 @@ class Documentation
end
return line[p..-1]
end
def _format_code(code)
def _get_cdata(xml)
if xml.cdata?
return xml.content
else
c = xml.children
if 1 == c.length and c[0].cdata?
return c[0].content
else
return CGI::unescapeHTML(xml.inner_html)
end
end
end
def _format_code(xml)
code = _get_cdata(xml)
lines = code.rstrip.lines
real_lines = lines.grep(/\S/)
return '' if real_lines.length == 0
@ -188,18 +218,12 @@ class Documentation
code.gsub(/\A\n+/, "").gsub(/\n\n+/, "\n\n").gsub(/\n+\Z/, "\n")
end
def _parse_code(xml)
@html.pre { @html.code { @html << _format_code(xml.inner_html) } }
end
def _parse_markdown(xml)
md = _format_code(xml.inner_html)
@html << BlueCloth.new(md).to_html
@html.pre { @html.code { @html << _format_code(xml) } }
end
def _parse_textile(xml)
tx = _format_code(xml.inner_html)
tx = _format_code(xml)
@html << RedCloth.new(tx).to_html
end
@ -215,7 +239,7 @@ class Documentation
xml.children.each do |child|
if child.text?
@html.p child.content.strip
elsif ['html','textile','markdown'].include? child.name
elsif ['html','textile'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid description element ' + child.name
@ -402,7 +426,7 @@ class ModuleDocumentation < GenericModuleDocumentation
if child.text?
text = child.content.strip
@html.p text if text.length > 0
elsif ['action','setup','option','html','textile','markdown','example','section'].include? child.name
elsif ['action','setup','option','html','textile','example','section'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid section element ' + child.name
@ -456,7 +480,7 @@ class AngelModuleDocumentation < GenericModuleDocumentation
if child.text?
text = child.content.strip
@html.p text if text.length > 0
elsif ['item','html','textile','markdown','example','section'].include? child.name
elsif ['item','html','textile','example','section'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid section element ' + child.name
@ -472,10 +496,12 @@ class AngelModuleDocumentation < GenericModuleDocumentation
self.ordername = xml['order']
nest(title, '', 'angel-module') {
@html.p {
@html.text (basename + ' ')
@short = _parse_short(xml, false)
}
if 'core_config_angel' != basename then
@html.p {
@html.text (basename + ' ')
@short = _parse_short(xml, false)
}
end
_parse_description(xml)
xml.element_children.each do |child|
@ -518,7 +544,7 @@ class ChapterDocumentation < Documentation
if child.text?
text = child.content.strip
@html.p text if text.length > 0
elsif ['html','textile','markdown','example','section'].include? child.name
elsif ['html','textile','example','section'].include? child.name
self.send('_parse_' + child.name, child)
else
raise 'invalid section element ' + child.name
@ -599,6 +625,8 @@ class ModuleIndex < Documentation
def initialize(modules)
super('index_modules')
@sub_pages = modules
actions = []
setups = []
options = []
@ -609,7 +637,7 @@ class ModuleIndex < Documentation
end
render_main do
nest('Modules overview', '', 'index_modules') {
nest('Module index', '', 'index_modules') {
modules_table(modules)
aso_html_table('action', actions.sort)
aso_html_table('setup', setups.sort)
@ -617,7 +645,41 @@ class ModuleIndex < Documentation
}
end
self.title = "lighttpd2 - all in one"
self.title = "Module index"
store_toc
end
end
class IndexPage < Documentation
def list(pages)
return if pages.empty?
@html.ul do
pages.each do |page|
@html.li do
@html.a({:href => page.filename}, page.title)
list(page.sub_pages)
end
end
end
end
def initialize(pages)
super('index')
self.title = "Index"
render_main do
nest(self.title, '', 'index') do
@html.p do
@html << "The documentation is also available as a "
@html.a({:href => "all.html"}, "single HTML page")
@html << "."
end
list(pages)
end
end
store_toc
end
end
@ -630,26 +692,29 @@ class AllPage < Documentation
a['href'] = m[2] if m && @href_map[m[1]]
end
def append(pages)
pages.each do |page|
@href_map[page.filename] = true
@html << page.to_html_fragment
@toc += page.toc
append(page.sub_pages)
end
end
def initialize(pages)
super('all')
@href_map = {}
pages.each do |page|
@href_map[page.filename] = true
end
render_main do
pages.each do |page|
@html << page.to_html_fragment
@toc += page.toc
end
append(pages)
end
@html_doc.xpath('//a').each do |a|
fix_link(a)
end
self.title = "lighttpd2 - all in one"
self.title = "all in one"
store_toc
end
end
@ -686,11 +751,28 @@ if __FILE__ == $0
end
pages.sort!
pages << ModuleIndex.new(pages)
normal_pages = []
module_pages = []
pages.each do |page|
if page.is_a? ModuleDocumentation
module_pages << page
else
normal_pages << page
end
end
module_index_page = ModuleIndex.new(module_pages)
pages << module_index_page
normal_pages << module_index_page
pages.sort!
pages << AllPage.new(pages)
all_page = AllPage.new(normal_pages)
index_page = IndexPage.new(normal_pages)
pages << all_page << index_page
pages.sort!
pages.each { |page| page.write_disk(output_directory) }

View File

@ -158,7 +158,10 @@
By default variables assignment overwrites an existing variable (in its previous scope) or, if it doesn't exist, creates a new one in the local scope (i.e. it will only be available in the current scope and nested descendants).
You can explicitly create a new variable in the local scope (hiding variables in parent scopes with the same name) by prefixing the assignment with @local@:
@local wwwpath = "/var/www/example.com";
<pre>
local wwwpath = "/var/www/example.com";
</pre>
You can also create variables in the global scope by prefixing the assignment with @global@.
The main config already is in a nested scope (i.e. *not* the global scope). The global scope is not destroyed after config loading, and can be used in delayed config loading (say from SQL in the future).
@ -222,14 +225,14 @@
The actions, setups and options are provided by the "modules":index_modules.html#index_modules.
]]></textile>
<section title="Includes">
<section title="Includes" anchor="includes">
<textile><![CDATA[
Includes are similar to function calls in the syntax, but are directly handled by the config parser. They are only allowed in action context, as they insert a reference to an action block at the point they are used.
There are three types of includes:
* @include "/etc/lighttpd/vhosts/*.conf";@: include files like the main config itself; the path can contain wildcards
* @include_shell "/etc/lighttpd/config_generator.sh";@: runs the specified command, and parses the output of it as config file
* @include_lua "/etc/lighttpd/complex.lua"@: includes a Lua config file
* @include_lua "/etc/lighttpd/complex.lua"@: includes a Lua config file. The single action to be executed must be returned in the global @actions@ variable (or leave it empty to do nothing). See also "@lua.handler@":mod_lua.html#mod_lua__action_lua-handler
Includes also create a new nested scope.
]]></textile>
@ -291,30 +294,31 @@
table(table table-striped).
|_. variable |_. description |
| req.localip | ip address of the listing socket, the client connected to (filename for unix sockets) |
| req.localport | port number of the listening socket, -1 for unix sockets |
| req.remoteip | ip address of the client |
| req.remoteport | port number of the client, -1 for unix sockets |
| req.path | the _path_ part of the requested url. not including the querystring. |
| req.host | requested hostname |
| req.scheme | scheme of the request. "http" or "https" |
| req.query | the _querystring_ of the requested url |
| req.method | method of the request. "GET", "POST", "HEAD" etc. |
| req.length | integer. length of the content for e.g. POST methods |
| req.header["name"] | request header _name_ e.g. req.header["referer"] |
| req.is_handled | boolean condition, does request already have a handler (static, fastcgi..) |
| req.env["name"] | (short for req.environment["name"]) CGI environment |
| request.localip | ip address of the listing socket, the client connected to (filename for unix sockets) |
| request.localport | port number of the listening socket, -1 for unix sockets |
| request.remoteip | ip address of the client |
| request.remoteport | port number of the client, -1 for unix sockets |
| request.path | the _path_ part of the requested url. not including the querystring. |
| request.raw_path | the raw _path_ (not urldecoded, not simplified) of the requested url, including the querystring. |
| request.host | requested hostname |
| request.scheme | scheme of the request. "http" or "https" |
| request.query | the _querystring_ of the requested url |
| request.method | method of the request. "GET", "POST", "HEAD" etc. |
| request.length | integer. length of the content for e.g. POST methods |
| request.header["name"] | request header _name_ e.g. request.header["referer"] |
| request.is_handled | boolean condition, does request already have a handler (static, fastcgi..) |
| request.environment["name"] | (or short request.env["name"]) CGI environment |
| | |
| phys.path | physical path of the file to be served. e.g. document root + path |
| phys.exists | boolean condition, indicates whether the requested file exist |
| phys.size | integer. size of the requested file. -1 if file doesn't exist |
| phys.is_dir | boolean condition, indicates whether the requested file is a directory |
| phys.is_file | boolean condition, indicates whether the requested file is a normal file (e.g. no unix socket etc) |
| phys.docroot | document root |
| phys.pathinfo | pathinfo |
| physical.path | physical path of the file to be served. e.g. document root + path |
| physical.exists | boolean condition, indicates whether the requested file (normal file, directory or even a special file) exists |
| physical.size | integer. size of the requested file. -1 if file doesn't exist |
| physical.is_dir | boolean condition, indicates whether the requested file is a directory |
| physical.is_file | boolean condition, indicates whether the requested file is a normal file (e.g. no unix socket etc) |
| physical.docroot | document root |
| physical.pathinfo | pathinfo |
| | |
| resp.status | response status code (blocks request until response header is available) |
| resp.header["name"] | response header (blocks request until response header is available) |
| response.status | response status code (blocks request until response header is available) |
| response.header["name"] | response header (blocks request until response header is available) |
]]></textile>
</section>

View File

@ -3,7 +3,7 @@
<description>
<textile>
lighttpd2 consists of two main binaries: the angel (@lighttpd2@) and the worker (@lighttpd2-worker@). The "main configuration":core_config.html#core_config is used by the worker, and this chapter describes the configuration for the angel.
A standard distribution should install a angel config with reasonable defaults which should work for most basic setups.
A standard distribution should install a angel config in @/etc/lighttpd2/angel.conf@ with reasonable defaults which should work for most basic setups.
</textile>
</description>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Fetch API">
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Fetch API" order="core_regex_after_01_fetch">
<description>
<textile>
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.

22
doc/core_introduction.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Introduction" order="a01_introduction">
<section title="Running lighttpd2">
<textile><![CDATA[
You need two config files for lighttpd, which are usually in the following two locations:
* "@/etc/lighttpd2/lighttpd.conf@":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/lighttpd.conf: the main config, see "Main Configuration":core_config.html#core_config. It can be split into multiple files, see "Includes":core_config.html#core_config__includes.
* "@/etc/lighttpd2/angel.conf@":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/angel.conf: the angel config, see "Angel Configuration":core_config_angel.html#core_config_angel.
The "@contrib/@":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib directory in the sources includes example config files.
Then start lighttpd2 with:
<pre>/usr/sbin/lighttpd2 -c /etc/lighttpd2/angel.conf</pre>
The process will not fork into background, you need to do that yourself if you want that.
Our recommended way to run lighttpd2 is "runit":http://smarden.org/runit/useinit.html (have a look at the "@contrib/service@":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/service directory).
]]></textile>
</section>
</chapter>

372
doc/core_lua.xml Normal file
View File

@ -0,0 +1,372 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="urn:lighttpd.net:lighttpd2/doc1" title="Lua API" order="core_regex_after_02_lua">
<description>
<textile>
Lua can be used to generate configs (like a shortcut to "@include_shell@":core_config.html#core_config__includes) or to write actual response handlers.
Using Lua to generate configs doesn't have any performance impact; in this case Lua is only run at startup to generate the config, and there is no Lua involved for processing requests.
As a @lua_State@ itself is not thread-safe, you have two ways to use Lua configs:
* "@include_lua@":core_config.html#core_config__includes and "@lua.plugin@":mod_lua.html#mod_lua__setup_lua-plugin : using a global server lock, but with sharing the same @lua_State@ in all workers
* "@lua_handler@":mod_lua.html#mod_lua__action_lua-handler: without locking, and every worker has its own @lua_State@ (and they cannot share their global context).
</textile>
</description>
<section title="Lua Config">
<textile>
This section describe how to translate concepts from the main config to Lua. You can write the whole config in Lua or only parts and include them (for example with "@include_lua@":core_config.html#core_config__includes).
</textile>
<example title="Example - debug.lua">
<description><textile><![CDATA[
The following Lua snippet saved as "debug.lua" could for example be included with @include_lua "debug.lua"@.
]]></textile></description>
<config><![CDATA[
function mydebug(vr)
local url_fields = { "raw", "raw_path", "raw_orig_path", "scheme", "authority", "path", "query", "host" }
local phys_fields = { "path", "doc_root", "pathinfo" }
if vr:handle_direct() then
vr.resp.status = 200
vr.resp.headers["Content-Type"] = "text/plain"
vr.out:add("Hello World!\n\n")
vr.out:add("http method: " .. vr.req.http_method .. "\n")
vr.out:add("http version: " .. vr.req.http_version .. "\n")
for k, v in vr.env:pairs() do
vr.out:add("Env['" .. k .. "'] = '" .. v .. "'\n")
end
vr.out:add("\n")
for k, v in pairs(url_fields) do
vr.out:add("vr.req.uri['" .. v .. "'] = '" .. vr.req.uri[v] .. "'\n")
end
vr.out:add("\n")
for k, v in pairs(phys_fields) do
vr.out:add("vr.phys['" .. v .. "'] = '" .. vr.phys[v] .. "'\n")
end
vr.out:add("\n")
for k, v in vr.req.headers:pairs() do
vr.out:add("vr.req.headers['" .. k .. "'] = '" .. v .. "'\n")
end
end
end
actions = mydebug
]]></config>
</example>
<section title="Values">
<textile><![CDATA[
* Boolean: Lua supports @true@ and @false@ directly
* Integers: Lua has its own number type (usually a "@double@":http://en.wikipedia.org/wiki/Double-precision_floating-point_format), and doesn't know any of the suffixes.
* Strings: Lua supports strings directly. Check the Lua reference for the various quoting styles.
* Lists and Key-Value-Lists: Lua has a "table" type; it can contain sequential lists and associative mappings. Use @{1, 2, 3}@ to create simple lists, @{a=1, b=2}@ to create unique mappings (which get converted to Key-Value-Lists) or @{{"a",1},{"a",2}}@ to explicitly create Key-Value-Lists (where a key can be used more than once and the order matters).
Don't mix sequential lists and associative mappings.
If you get a List (possible a Key-Value-List) value from lighttpd it is represented as sequential list but has a special @__index@ meta-table method supporting strings and @nil@ as lookup parameter, i.e. you can treat a Key-Value-List like an associative mapping in Lua (see for example the options handling in "contrib/secdownload.lua":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/secdownload.lua).
* Expressions and variables just are the usual Lua things; there is no direct access to the lighttpd config variables (yet).
* Action blocks: you can make an action from a list of actions using the "list action":plugin_core.html#plugin_core__action_list (@act = action.list(act1, act2)@)
]]></textile>
</section>
<section title="Function calls">
<textile><![CDATA[
Action context is given by prefixing the function name with @action.@, and setup context by prefixing with @setup.@. Don't try to call setups in request handling.
Also each Lua function can act as an action (see the debug.lua example above), taking a virtual request object as parameter.
Includes are not supported, neither is the debug @__print@ (there are other logging methods available).
]]></textile>
</section>
<section title="Conditions">
<textile><![CDATA[
Conditions are the ugliest part: there is no way translating native Lua if statements into the lighttpd config, so they need to be constructed manually.
Only the long names of the condition variables are available in Lua. The condition operators are all given names and appended to the condition variable, and then called with the value to compare with.
table(table table-striped).
|_. op |_. Lua name |_. op |_. Lua name |
| <notextile>==</notextile> | @:eq@ | != | @:ne@ |
| <= | @:le@ | < | @:lt@ |
| >= | @:ge@ | > | @:gt@ |
| =~ | @:match@ | !~ | @:nomatch@ |
| =^ | @:prefix@ | !^ | @:notprefix@ |
| =$ | @:suffix@ | !$ | @:notsuffix@ |
| =/ | @:ip@ | !/ | @:notip@ |
Boolean condition variables are called with @:is()@ or @:isnot()@.
The result of such call (a "condition") is then passed as first parameter to "@action.when@":plugin_core#plugin_core__action_when.
]]></textile>
<example title="Example - admin only">
<description><textile>
Translating @if req.env["REMOTE_USER"] != "admin" { auth.deny; }@ to Lua:
</textile></description>
<config><![CDATA[
actions = action.when(request.environment["REMOTE_USER"]:ne("admin"), action.auth.deny())
]]></config>
</example>
<example title="Example - physical files only">
<description><textile>
Translating @if !phys.exists { auth.deny; }@ to Lua:
</textile></description>
<config><![CDATA[
actions = action.when(physical.exists:isnot(), action.auth.deny())
]]></config>
</example>
</section>
</section>
<section title="API">
<textile><![CDATA[
This section documents the object types you need to handle requests; you will probably start from the Virtual Request object you get as parameter in your handler.
Object fields should be accessed with @.field@ or @["field"]@, for example:
<pre>
e = vr.env
e["XXX"] = "abc"
</pre>
Fields tagged with (ro) are read only; that does not mean the fields value can't be modified, you only cannot overwrite the field with another object. Readonly string / number properties are really read only though.
Call object methods with @:method(...)@:
<pre>
vr:print("Hello World")
</pre>
*Note*:
The @obj:method(par1, par2, ...)@ syntax is just another way to say @obj["method"](obj, par1, par2, ...)@ (but @obj@ is only evaluated once), so field and method names live in the same namespace.
This means that our container types cannot provide access to fields which have the same names as the methods (and the methods starting with "__" are not listed here), so you have to use explicit access methods to read generic fields in such containers (write is not a problem as we don't allow writing methods).
All container types should provide a @get@ and a @set@ method to provide "clean" access to the container contents.
h3. pairs()
Some objects may provide a @:pairs()@ method to loop through the fields (not the methods); this works for simple things like
<pre>
for k, v in vr.env:pairs() do
vr:print("env['" .. k .. "'] = '" .. v .. "'")
end
</pre>
lua expects that the @:pairs@ method returns a @next, obj, startkey@ tuple and loops through the list with @k = startkey; while k, v = next(obj, k) do ... end@; but the @next()@ method is supposed to use @k@ as previous key and to return the next one.
Our @next@ methods will keep the current position in an internal object (associated with the @next@ function as upvalue), and will advance on every call ignoring the @obj@ and @k@ parameter.
]]></textile>
<section title="Global constants">
<textile><![CDATA[
@liHandlerResult@ enumeration values:
* @lighty.HANDLER_GO_ON@
* @lighty.HANDLER_COMEBACK@
* @lighty.HANDLER_WAIT_FOR_EVENT@
* @lighty.HANDLER_ERROR@
]]></textile>
</section>
<section title="Global methods">
<textile><![CDATA[
* @lighty.print@ (and @lighty.error@ and @print@): print parameters via lua "tostring" method as ERROR in global server context
* @lighty.warning@: print parameters via lua "tostring" method as WARNING in global server context
* @lighty.info@: print parameters via lua "tostring" method as INFO in global server context
* @lighty.debug@: print parameters via lua "tostring" method as DEBUG in global server context
* @lighty.filter_in(class)@: creates a new action, which adds a incoming filter from @class:new(vr)@ if called at runtime
* @lighty.filter_out(class)@: creates a new action, which adds a outgoing filter from @class:new(vr)@ if called at runtime
* @lighty.md5(str)@: calculates the md5 checksum of the string @str@ (returns the digest as string in hexadecimal)
* @lighty.sha1(str)@: calculates the sha1 checksum of the string @str@ (returns the digest as string in hexadecimal)
* @lighty.sha256(str)@: calculates the sha256 checksum of the string @str@ (returns the digest as string in hexadecimal)
]]></textile>
<example>
<config><![CDATA[
lighty.print("Hello World!")
]]></config>
</example>
<example>
<config><![CDATA[
local MyFilterclass = { }
MyFilterClass.__index = MyFilterClass
function MyFilterClass:new(vr)
local o = { }
setmetatable(o, self)
return o -- return nil if you want to skip the filter this time
end
function MyFilterClass:handle(vr, outq, inq) ... end
actions = lighty.filter_out(MyFilterClass)
]]></config>
</example>
</section>
<section title="Virtual Request">
<textile><![CDATA[
Fields:
* @con@(ro): Connection
* @in@(ro): Chunk Queue, read request post content
* @out@(ro): Chunk Queue, write response content
* @env@(ro): Environment, (fast)cgi environment
* @req@(ro): Request, data from request header
* @resp@(ro): Response, response header data
* @phys@(ro): Physical, paths and filenames
* @is_handled@(ro): whether vrequest is already handled
* @has_response@(ro): whether the response headers (and status) is available
Methods:
* @print(...)@: print parameters via lua @tostring@ method as ERROR in Virtual Request context
* @warning(...)@: print parameters via lua @tostring@ method as WARNING in Virtual Request context
* @info(...)@: print parameters via lua @tostring@ method as INFO in Virtual Request context
* @debug(...)@: print parameters via lua @tostring@ method as DEBUG in Virtual Request context
* @handle_direct()@: handle vrequest (i.e. provide headers and body); returns true if not already handled.
* @enter_action(act)@: push a new action on the action stack (return HANDLER_WAIT_FOR_EVENT to rerun after the pushed actions are done, HANDLER_GO_ON if you are done)
* @st, res, errno, msg = stat(filename)@: async stat(filename). Following results are possible
** st is the stat result, res == HANDLER_GO_ON, if the file was found. errno and msg are NIL. In all other cases st is NIL and res != HANDLER_GO_ON.
** res == HANDLER_WAIT_FOR_EVENT: stat() is in progress, just try again later (and return HANDLER_WAIT_FOR_EVENT in the meantime)
** res == HANDLER_ERROR: if stat() failed, errno contains the errno and msg the error message for the errno code.
* @add_filter_in(obj)@: adds @obj@ as lua incoming filter (needs to respond to @obj:handle(vr, outq, inq)@ and optionally @obj:finished()@); returns a Filter object
* @add_filter_out(obj)@: adds @obj@ as lua outgoing filter (needs to respond to @obj:handle(vr, outq, inq)@ and optionally @obj:finished()@); returns a Filter object
]]></textile>
</section>
<section title="Connection">
<textile><![CDATA[
* @local@: address of local socket
* @remote@: address of remote host
]]></textile>
</section>
<section title="Environment">
<textile><![CDATA[
Fields are the keys in the environment, so it behaves like a lua table; if you use keys starting with "__" or keys with the name of one of the methods below, you have to use the @get@ method to read them, for example:
<pre>
x = env["set"] -- doesn't work, returns the set method instead
x = env:get("set") -- use this instead
x = env[y] -- don't do this, as y may be a special key like "set"
x = env:get(y) -- just do it the safe way if you are not sure
</pre>
Methods:
* @get(k)@: safe way for @env[k]@
* @set(k, v)@: safe way for @env[k] = v@
* @unset(k)@: safe way for @env[k] = nil@
* @weak_set(k, v)@: don't override old value, safe way for @env[k] = env[k] or v@
* @pairs()@: use to loop through keys: @for k, v in env:pairs() do ... end@
* @clear()@: remove all entries
]]></textile>
</section>
<section title="Chunk Queue">
<textile><![CDATA[
Fields:
* @is_closed@: whether the ChunkQueue is closed
Methods:
* @add(s)@: appends a string to the queue
* @add({filename="/..."})@: appends a file to the queue (only regular files allowed)
* @reset()@: removes all chunks, resets counters
* @steal_all(from)@: steal all chunks from another queue (useful in a filter if you decide to pass all data through it)
* @skip_all()@: skips all chunks (removes all chunks but does *not* reset counters)
]]></textile>
</section>
<section title="Request">
<textile><![CDATA[
Fields:
* @headers@(ro): HTTP Headers
* @http_method@(ro): HTTP method string ("GET", "POST", "HEAD", ...)
* @http_version@(ro): HTTP version string ("HTTP/1.0", "HTTP/1.1")
* @content_length@(ro): Numeric value of Content-Length header (not updated automatically if someone changes the header value), -1 if not specified
* @uri@: Request URI
]]></textile>
</section>
<section title="Request URI">
<textile><![CDATA[
Fields:
* @raw@: Request uri as it was in the HTTP Request Line (or a rewrite result)
* @raw_path@: not decoded path with querystring (will be the same as @raw@ for most requests, unless someone does something like @GET http://example.com/test?abc HTTP/1.1@)
* @raw_orig_path@: same as raw_path, but saved before any rewrite happened
* @scheme@: "http" or "https"
* @authority@: complete host name header (or authority in an absolute url), e.g. "user@www.example.com.:8080"
* @path@: decoded and simplified path name, without authority, scheme, query-string; e.g. "/index.php"
* @host@: simple hostname, without auth information, without port, without trailing dot; e.g. "www.example.com"
* @query@: The querystring, e.g. "a=1&b=2"
]]></textile>
</section>
<section title="Response">
<textile><![CDATA[
Fields:
* @headers@(ro): HTTP Headers
* @status@: HTTP status code
]]></textile>
</section>
<section title="Physical">
<textile><![CDATA[
Fields:
* @path@: physical path
* @doc_root@: document root
* @pathinfo@: pathinfo
]]></textile>
</section>
<section title="HTTP Headers">
<textile><![CDATA[
Same restriction as Environment for fields.
Methods:
* @get(k)@: joins all header values for the key @k@ with ", " (as the rfc allows it)
* @set(k, v)@: removes all headers with key @k@ and, if v is not nil, appends new "k: v" header
* @append(k, v)@: appends ", v" to last header value with key k if it already exists, @insert(k, v)@ otherwise
* @insert(k, v)@: appends new "k: v" header to list
* @unset(k)@: removes all headers with key @k@
* @pairs()@: loops through all headers. Please note that the keys are not unique!
* @list(k)@: loops through all headers with key @k@
* @clear()@: remove all headers
]]></textile>
</section>
<section title="Filter">
<textile><![CDATA[
Represents a "liFilter".
Fields:
* @in@(ro): Chunk Queue, incoming stream
* @out@(ro): Chunk Queue, outgoing stream
]]></textile>
</section>
<section title="Stat struct">
<textile><![CDATA[
Represents "struct stat". Most fields should be self explaining (@man stat@ if you don't know them).
Fields:
* @is_file@(ro): S_ISREG(mode)
* @is_dir@(ro): S_ISDIR(mode)
* @is_char@(ro): S_ISCHR(mode)
* @is_block@(ro): S_ISBLK(mode)
* @is_socket@(ro): S_ISSOCK(mode)
* @is_link@(ro): S_ISLNK(mode)
* @is_fifo@(ro): S_ISFIFO(mode)
* @mode@(ro)
* @mtime@(ro)
* @ctime@(ro)
* @atime@(ro)
* @uid@(ro)
* @gid@(ro)
* @size@(ro)
* @ino@(ro)
* @ino@(ro)
]]></textile>
</section>
</section>
</chapter>

View File

@ -14,7 +14,7 @@
* simple text. can contain special characters $ and % only when they are escaped with \ - remember, that the \ has to be escaped too for the config, so you'll probably have to use \\ to escape. You are allowed to escape ? too (used for special "split" in rewrite).
* "%" capture references (previous matching regular expression conditional); either followed by a single digit, or a range (see below for range syntax)
* "$" capture references (depends on action); either followed by a single digit, or a range (see below for range syntax)
* "%" references to "condition variables":core_config.html#core_connfig__condition_vars, for example: @%{req.path}@; the conditional can be prefixed with "enc:" (@%{enc:req.path}@), in which case the value will be urlencoded.
* "%" references to "condition variables":core_config.html#core_config__condition_vars, for example: @%{req.path}@; the conditional can be prefixed with "enc:" (@%{enc:req.path}@), in which case the value will be urlencoded.
</textile>
</section>

View File

@ -96,7 +96,6 @@
<choice minOccurs="0" maxOccurs="unbounded">
<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
</choice>
</sequence>
</complexType>
@ -121,7 +120,6 @@
<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
<element name="example" type="d:ExampleType" />
<element name="section" type="d:ModuleSectionType" />
</choice>
@ -157,7 +155,6 @@
<choice minOccurs="0" maxOccurs="unbounded">
<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
<element name="example" type="d:ExampleType" />
<element name="section" type="d:ChapterSectionType" />
</choice>
@ -208,7 +205,6 @@
<element name="html" type="anyType" />
<element name="textile" type="anyType" />
<element name="markdown" type="anyType" />
<element name="example" type="d:ExampleType" />
<element name="section" type="d:AngelModuleSectionType" />
</choice>

View File

@ -54,7 +54,7 @@
<parameter name="target" />
<default><text>logging disabled</text></default>
<description>
<html>Enable logging by setting a log target. Supports the same log targets as <a href="plugin_clore.html#plugin_core__action_log">log</a>.</html>
<html>Enable logging by setting a log target. Supports the same log targets as <a href="plugin_core.html#plugin_core__action_log">log</a>.</html>
</description>
<example>
<config>

View File

@ -124,7 +124,7 @@
<example>
<description>
<textile>
You can use @auth.require_user@ from the mod_lua plugin "contrib/core.lua":http://git.lighttpd.net/lighttpd/lighttpd2/tree/contrib/core.lua for the REMOTE_USER check too:
You can use @auth.require_user@ from the mod_lua plugin "contrib/core.lua":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/core.lua for the REMOTE_USER check too:
</textile>
</description>

View File

@ -8,12 +8,13 @@
*Hint:*
Use a cron-job like the following to remove old cached data, e.g. in crontab daily:
<pre>
find /var/cache/lighttpd/cache_etag/ -type f -mtime +2 -exec rm -r {} \;
find /var/cache/lighttpd/cache_etag/ -type f -mtime +2 -exec rm -r {} \;
</pre>
*Hint:*
Have a look at mod_deflate to see this module in action.
Have a look at "mod_deflate":mod_deflate.html#mod_deflate to see this module in action.
</textile>
</description>

View File

@ -25,4 +25,33 @@
</textile>
</description>
</action>
<action name="debug.show_events">
<short>shows a plain text list of all events</short>
<description>
this is a very low level debug tool for developers.
</description>
<example>
<config>
if req.path == "/debug/events" { debug.show_events; }
</config>
</example>
</action>
<setup name="debug.show_events_after_shutdown">
<short>time in seconds after start of shutdown to log remaining active events</short>
<parameter name="timeout">
<short>timeout after which to display events (default: disabled)</short>
</parameter>
<description>
this is a very low level debug tool for developers; it shows which event listeners keep lighttpd2 alive when it should stop.
</description>
<example>
<config>
setup { debug.show_events_after_shutdown 5; }
</config>
</example>
</setup>
</module>

View File

@ -39,7 +39,7 @@
<section title="Notes">
<textile>
*Important*: As deflate; waits for the response headers, you must handle the request before it (see below how to check whether the request is handled).
*Important*: As @deflate;@ waits for the response headers, you must handle the request before it (see below how to check whether the request is handled).
If the request is not handled, you will get a "500 - Internal error" and a message in the error.log.
Does not compress:

View File

@ -28,10 +28,15 @@
module_load "mod_fastcgi";
}
if phys.path =$ ".php" {
if phys.path =$ ".php" and phys.is_file {
fastcgi "unix:/var/run/lighttpd2/php.sock";
}
</config>
</example>
</action>
<option name="fastcgi.log_plain_errors">
<short>whether to prepend timestamp and other info to FastCGI stderr lines in the "backend" log</short>
<default><value>false</value></default>
</option>
</module>

View File

@ -10,7 +10,11 @@
<short>(mandatory) the socket address to listen on (same as "listen":plugin_core.html#plugin_core__setup_listen), can be specified more than once to setup multiple sockets with the same options</short>
</entry>
<entry name="pemfile">
<short>(mandatory) file containing the private key, certificate and intermediate certificates (the root certificate is usually not included)</short>
<short>(mandatory) file containing the private key, certificate, intermediate certificates (the root certificate is usually not
included) and an OCSP response; alternatively it can be a key-value list with a "key" and a "cert" entry, and optionally a "ocsp" entry.</short>
</entry>
<entry name="pin">
<short>the PIN (or password) to use when using PKCS #11 modules or encrypted keys. The pin is kept in memory.</short>
</entry>
<entry name="priority">
<short>GnuTLS priority string, specifying ciphers and other GnuTLS options (default: "NORMAL")</short>
@ -28,7 +32,7 @@
<short>"fetch" backend name to search certificates in with the SNI servername as key (only available if SNI in lighttpd2 was enabled)</short>
</entry>
<entry name="sni-fallback-pemfile">
<short>certificate to use if request contained SNI servername, but the sni-backend didn't find anything; if request didn't contain SNI the standard "pemfile"(s) are used</short>
<short>certificate to use if request contained SNI servername, but the sni-backend didn't find anything; if request didn't contain SNI the standard "pemfile"(s) are used; similarly with "pemfile" it can also be a key-value list with a "key" and a "cert" entry.</short>
</entry>
</table>
</parameter>
@ -83,6 +87,60 @@
}
</config>
</example>
<example title="Simple TLS on IPv4 and IPv6 with separate files for key and certificate and encrypted key">
<config>
setup {
module_load "mod_gnutls";
gnutls (
"priority" => "PFS:-3DES-CBC:-ARCFOUR-128:-VERS-SSL3.0:-SHA1:+SHA1:+RSA:%SERVER_PRECEDENCE",
"listen" => "0.0.0.0:443",
"listen" => "[::]:443",
"pin" => "passwordForEncryptedKey",
"pemfile" => (
"key" => "/etc/certs/lighttpd_key.pem",
"cert" => "/etc/certs/lighttpd_cert.pem"
)
);
}
</config>
</example>
<example title="Simple TLS on IPv4 and IPv6 with SoftHSM">
<config>
setup {
module_load "mod_gnutls";
gnutls (
"priority" => "PFS:-3DES-CBC:-ARCFOUR-128:-VERS-SSL3.0:-SHA1:+SHA1:+RSA:%SERVER_PRECEDENCE",
"listen" => "0.0.0.0:443",
"listen" => "[::]:443",
"pin" => "SoftHSM-pin",
"pemfile" => (
"key" => "pkcs11:model=SoftHSM;manufacturer=SoftHSM;serial=1;token=master-key;id=%ac%d5%52%69%16%09%2c%0c%9c%b0%ec%6c%3d%3b%c6%4d%55%4c%40%49;object=my-key;object-type=private",
"cert" => "pkcs11:model=SoftHSM;manufacturer=SoftHSM;serial=1;token=master-key;id=%ac%d5%52%69%16%09%2c%0c%9c%b0%ec%6c%3d%3b%c6%4d%55%4c%40%49;object=my-key;object-type=cert"
)
);
}
</config>
</example>
<example title="Simple TLS on IPv4 and IPv6 with OCSP stapling">
<config>
setup {
module_load "mod_gnutls";
gnutls (
"priority" => "PFS:-3DES-CBC:-ARCFOUR-128:-VERS-SSL3.0:-SHA1:+SHA1:+RSA:%SERVER_PRECEDENCE",
"listen" => "0.0.0.0:443",
"listen" => "[::]:443",
"pemfile" => (
"key" => "/etc/certs/lighttpd.pem",
"cert" => "/etc/certs/lighttpd.pem",
"ocsp" => "/etc/certs/lighttpd-ocsp.der",
)
);
}
</config>
</example>
</setup>
<section title="Server Name Indication (SNI)">
@ -125,6 +183,44 @@
</textile>
</section>
<section title="OCSP stapling">
<textile>
"OCSP stapling":https://en.wikipedia.org/wiki/OCSP_stapling is used to assure a client the certificate is still valid (i.e. not revoked); you can put an OCSP response into the certificate file in an "OCSP RESPONSE"-PEM block (there is probably no standard for this, juse base64-encode the DER-response you have), or specify the (DER or PEM formatted) OCSP response as separate file using "ocsp" in a "pemfile" block.
Server Name Indication (SNI) should work fine, as an OCSP response is only used if it matches the certificate in use for the connection.
The fetch backends do support OCSP stapling if the OCSP response is appended as PEM block.
Lighttpd does NOT automatically reload OCSP responses; you have to restart to load new OCSP responses (a cron job is probably the right way to do it).
If you have your certificate and the issuer-certificate (the one that signed yours) in separate files you can request an OCSP response like that (using the GnuTLS "ocsptool"):
<pre>
ocsptool --ask --load-issuer issuer.pem --load-cert cert.pem --outfile ocsp.der
</pre>
Converting into PEM format can be done like this:
<pre>
(echo "-----BEGIN OCSP RESPONSE-----"; base64 --wrap=64 ocsp.der; echo "-----END OCSP RESPONSE-----") > ocsp.pem
</pre>
If you have trouble identifying which certificates you need, here the more detailed explanation:
You usually have a list of certificates in the PEM file you pass to lighttpd. The first certificate usually has a "Subject" pointing to your server name (CN), like: "Subject: CN=lighttpd.net". It also has a "Issuer" attribute (like "C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3"). The issuer certificate needs a "Subject" matching that "Issuer", and should be the second certificate in the PEM file (unless it already is the root CA, in which case it is usually omitted).
@ocsptool@ will always use the first certificate in a file and ignore the others, so you can use the normal PEM file you pass to lighttpd as argument after @--load-cert@, but you need to extract the issuer certificate if you don't have it in a separate file. The following @awk@ script extracts the second PEM block from a file:
<pre>
awk '
BEGIN { block = 0 }
/^-----BEGIN / { ++block; }
{ if (block > 1) print; }
' "certs.pem" > "issuer.pem"
</pre>
</textile>
</section>
<section title="protect-against-beast">
<textile>
"BEAST":http://en.wikipedia.org/wiki/Transport_Layer_Security#BEAST_attack is considered mitigated on clients by many now, so this workaround is no longer recommended and disabled by default.
@ -135,9 +231,9 @@
<section title="DH parameters">
<textile>
The @DHE_RSA@ key exchange requires parameters (similar to the curves used for @ECDHE_RSA@); the parameters specify a prime @p@ and a group generator @g@ of the multiplicative group of integers modulo @p@ (i.e. for all @x@ in @1..p-1@ exists an @e@ with @g^e = x@); sometimes @g@ might only create a sufficiently large subgroup (for example of size @(p-1)/2@ for Sophie Germain primes).
The @DHE_RSA@ key exchange requires parameters (similar to the curves used for @ECDHE_RSA@); the parameters specify a prime @p@ and a group generator @g@ of the multiplicative group of integers modulo @p@ (i.e. for all @x@ in @1..p-1@ exists an @e@ with @g^e = x@); sometimes @g@ might only create a sufficiently large subgroup (for example of size @(p-1)/2@).
The security of the DH key exchange depends (among other things) on the bit length of @p@; therefore lighttpd includes default 4096-bit parameters (provided by the GnuTLS certtool with @certtool --get-dh-params --bits=4096@ in version 3.0.22), and these should be safe to use.
The security of the DH key exchange depends (among other things) on the bit length of @p@; therefore lighttpd includes default 4096-bit parameters (provided by the GnuTLS certtool with @certtool --get-dh-params --bits=4096@ in version 3.0.22), and these should be safe to use (key lengths > 1024 are not supported by some older clients; if you need to support those you either have to disable DH key exchange or specify 1024-bit parameters).
You can use either GnuTLS or openssl to generate your own parameters:
* @certtool --generate-dh-params --bits=2048@
@ -145,11 +241,14 @@
* @openssl dhparam -2 2048@
* @openssl dhparam -5 2048@
The GnuTLS @certtool@ only generates "dsaparam" style parameters (any prime with a large generator), while @openssl@ can generate parameters with a generator of 2 or 5 combined with a Sophie Germain prime @p@ (i.e. (p-1)/2 is a prime too); such strong parameters take a lot more time to generate.
("dsaparam" style parameters should not be reused, i.e. one should generate a new private key for each connection.)
The GnuTLS @certtool@ only generates "DSA" parameters (any prime with a large generator), while @openssl@ can generate "DH" parameters with a generator of 2 or 5 combined with a Sophie Germain prime @(p-1)/2@ (i.e. both @p@ and @(p-1)/2@ are primes) or "DSA" parameters using the @-dsaparam@ option.
"DH" parameters take a lot more time to generate, but you can reuse keys for those (although it is not recommended, search for @SSL_OP_SINGLE_DH_USE@ in the openssl manual on @SSL_CTX_set_tmp_dh_callback@). Keys for "DSA" parameters should not be reused, i.e. one should generate a new private key for each connection, which is the case in mod_gnutls. The default parameters provided by lighttpd are "DH" parameters.
See also:
* "Diffie-Hellman(-Merkle) key exchange":http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
* "OpenSSL: Documents, dhparam(1)":https://www.openssl.org/docs/apps/dhparam.html
* "OpenSSL: Documents, SSL_CTX_set_tmp_dh_callback(3)":https://www.openssl.org/docs/ssl/SSL_CTX_set_tmp_dh_callback.html#NOTES
</textile>
</section>
</module>

View File

@ -2,6 +2,12 @@
<module xmlns="urn:lighttpd.net:lighttpd2/doc1">
<short>load lua plugins and actions</short>
<description>
<textile><![CDATA[
Also see "Lua API":core_lua.html#core_lua.
]]></textile>
</description>
<setup name="lua.plugin">
<short>load file as lua plugin</short>
<parameter name="filename">
@ -30,11 +36,12 @@
</example>
</setup>
<section title="Example plugin" anchor="#">
<textile>
(see "contrib/core.lua":http://git.lighttpd.net/lighttpd/lighttpd2/tree/contrib/core.lua for a real example)
<example title="Example plugin" anchor="#">
<description><textile>
(see "contrib/core.lua":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/core.lua for a real example)
</textile></description>
<pre>
<config>
local filename, args = ...
-- args are from the lua.plugin line
@ -49,9 +56,8 @@
actions = {
["simple"] = simple,
}
</pre>
</textile>
</section>
</config>
</example>
<action name="lua.handler">
<short>load file as lua config</short>
@ -70,14 +76,14 @@
</parameter>
<description>
<textile>
lua.handler is basically the same as "include_lua":plugin_core.html#plugin_core__action_include_lua with the following differences:
lua.handler is basically the same as "include_lua":core_config.html#core_config__includes with the following differences:
* each worker loads the lua file itself
* it isn't loaded before it is used, so you won't see errors in the script at load time
* it cannot call setup functions
* it supports arguments to the script (@local filename, args = ...@)
* doesn't lock the global lua lock, so it performs better when you use multiple workers
See "contrib/core.lua":http://git.lighttpd.net/lighttpd/lighttpd2/tree/contrib/core.lua for how we load some external actions like "contrib/core__xsendfile.lua":http://git.lighttpd.net/lighttpd/lighttpd2/tree/contrib/core__xsendfile.lua
See "contrib/core.lua":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/core.lua for how we load some external actions like "contrib/core__xsendfile.lua":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/core__xsendfile.lua
</textile>
</description>
<example>

View File

@ -3,7 +3,7 @@
<short>caches content on memcached servers</short>
<description>
<textile>
<textile><![CDATA[
@lookup@ tries to find data associated with the key, and returns it as http body with status 200 if it finds something.
@store@ stores a http body (generated by another backend) in memcached.
@ -14,7 +14,7 @@
The other way is to purge the keys in your dynamic backend; you can set the memcached content from your backend too, which probably is faster than @memcached.store@.
If the key is longer than 255 bytes or contains characters outside the range 0x21 - 0x7e we will use a hash of it instead (for now sha1, but that may change).
</textile>
]]></textile>
</description>
<action name="memcached.lookup">
@ -41,7 +41,7 @@
</action>
<action name="memcached.store">
<short>searches the content in a memcached database</short>
<short>stores the generated respone in a memcached database</short>
<parameter name="options">
<table>
<entry name="server">
@ -67,7 +67,7 @@
</action>
<example>
<config>
<config><![CDATA[
setup {
module_load "mod_memcached";
}
@ -81,12 +81,12 @@
static;
memcached.store ["key" => "%{req.scheme}://%{req.host}%{req.path}"];
});
</config>
]]></config>
</example>
<section title="lua API" anchor="#">
<textile>
Exports a lua api to per-worker luaStates too (for use in lua.handler):
<section title="Lua API" anchor="#">
<textile><![CDATA[
mod_memcached exports a Lua API to per-worker @luaState@s too (for use in lua.handler):
@memcached.new(address)@ creates a new connection; a connection provides:
* @req = con:get(key, cb | vr)@
@ -103,6 +103,6 @@
* @ttl@
* @cas@
* @data@
</textile>
]]></textile>
</section>
</module>

View File

@ -16,7 +16,7 @@
<short>file containing the intermediate certificates</short>
</entry>
<entry name="ciphers">
<short>OpenSSL ciphers string</short>
<short>OpenSSL ciphers string (default: "HIGH !aNULL !3DES +kEDH +kRSA !kSRP !kPSK")</short>
</entry>
<entry name="dh-params">
<short>filename with generated dh-params (default: fixed 4096-bit parameters)</short>
@ -25,7 +25,7 @@
<short>OpenSSL ecdh-curve name</short>
</entry>
<entry name="options">
<short>list of OpenSSL options (default: NO_SSLv2, CIPHER_SERVER_PREFERENCE, NO_COMPRESSION)</short>
<short>list of OpenSSL options (default: NO_SSLv2, NO_SSLv3, CIPHER_SERVER_PREFERENCE, NO_COMPRESSION, SINGLE_DH_USE, SINGLE_ECDH_USE)</short>
</entry>
<entry name="verify">
<short>enable client certificate verification (default: false)</short>
@ -47,9 +47,9 @@
<description>
<textile>
For @ciphers@ see OpenSSL "ciphers":http://www.openssl.org/docs/apps/ciphers.html string
For @ciphers@ see OpenSSL "ciphers":https://www.openssl.org/docs/manmaster/man1/ciphers.html string
For @options@ see "options":https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html. Explicitly specify the reverse flag by toggling the "NO_" prefix to override defaults.
For @options@ see "options":https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html. Explicitly specify the reverse flag by toggling the "NO_" prefix to override defaults.
</textile>
</description>
@ -61,7 +61,7 @@
"listen" => "0.0.0.0:443",
"listen" => "[::]:443",
"pemfile" => "/etc/certs/lighttpd.pem",
"options" => ["NO_SSLv3"],
"options" => ["ALL", "NO_TICKET"],
];
}
</config>

View File

@ -3,7 +3,7 @@
<short>track connection progress (state) via a unique identifier</short>
<description>
<textile>
<textile><![CDATA[
mod_progress lets you track connection progress (or rather state) using a lookup table in which connections are registered via a random unique identifier specified with the request.
It is most commonly used to implement progress bars for file uploads.
@ -13,7 +13,7 @@
A live demonstration of a progress bar implementation can be seen at http://demo.lighttpd.net/progress/
Check the sourcecode there for further insight.
</textile>
]]></textile>
</description>
@ -29,12 +29,12 @@
<parameter name="methods" />
<default><value>("POST")</value></default>
<example>
<config>
<config><![CDATA[
setup {
module_load "mod_progress";
progress.methods ("PUT", "POST");
}
</config>
]]></config>
</example>
</option>
@ -48,7 +48,7 @@
<short>(optional) output format, one of "legacy", "json" or "jsonp". Defaults to "json".</short>
</parameter>
<description>
<textile>
<textile><![CDATA[
Output formats:
* legacy: @new Object({"state": "running"", "received": 123456, "sent": 0, "request_size": 200000, "response_size": 0})@
* json: @{"state": "running", "received": 123456, "sent": 0, "request_size": 200000, "response_size": 0}@
@ -65,18 +65,18 @@
@received@, @request_size@, @sent@ and @response_size@ are only available if @state@ is @"running"@ or @"done"@.
@status@ is only available if @state@ is @"error"@.
</textile>
]]></textile>
</description>
<example>
<config>
<config><![CDATA[
setup {
module_load "mod_progress";
}
if req.path == "/upload.php" { progress.track; }
if req.path == "/progress" { progress.show; }
</config>
]]></config>
</example>
</action>

View File

@ -7,6 +7,11 @@
<parameter name="socket">
<short>socket to connect to, either "ip:port" or "unix:/path"</short>
</parameter>
<description>
<textile><![CDATA[
proxy uses @request.raw_path@ for the URL (including the query string) to send to the backend.
]]></textile>
</description>
<example>
<config>
setup {

View File

@ -75,6 +75,22 @@
</example>
</action>
<action name="rewrite_raw">
<short>modify request path and querystring, matching and writing raw path</short>
<parameter name="rule">
<short>a simple target string or one rule, mapping a regular expression to a target string, or a list of rules.</short>
</parameter>
<description>
<textile>
Similar to "@rewrite@":mod_rewrite.html#mod_rewrite__action_rewrite, but matches the raw path (i.e. the path before URL decoding and sanitizing) and the result is decoded again.
"@rewrite@":mod_rewrite.html#mod_rewrite__action_rewrite writes the result to @request.path@ and possibly @request.query@ and uses URL encoding to generate @request.raw_path@ from those.
"@rewrite_raw@":mod_rewrite.html#mod_rewrite__action_rewrite_raw writes @request.raw_path@ and decodes it into @request.path@ and @request.query@; this means the query string is always overwritten.
In both cases @request.path@ gets simplified afterwards.
</textile>
</description>
</action>
<option name="rewrite.debug">
<short>enable debug output</short>
<default><value>false</value></default>

View File

@ -3,13 +3,13 @@
<short>protects files with a time limited code</short>
<section title="Install">
<textile>
<textile><![CDATA[
By default distributions (and @make install@) should provide the necessary files; but you can always find them in the "contrib":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib folder:
* @secdownload.lua@
* @secdownload__secdownload.lua@
That way you can modify them for your own needs if you have to (although it is recommended to change the names of the files and the actions, so you don't get conflicts).
</textile>
]]></textile>
</section>
<action name="secdownload">
@ -31,19 +31,19 @@
</table>
</parameter>
<description>
<textile>
The @prefix@ is not used to build the filename; include it manually in the @document-root@ (works like @"alias":plugin_core.html#plugin_core__action_alias "/prefix" => "/docroot").
<textile><![CDATA[
The @prefix@ is not used to build the filename; include it manually in the @document-root@ (works like @alias "/prefix" => "/docroot"@, see "@alias@":plugin_core.html#plugin_core__action_alias).
secdownload doesn't actually handle the (valid) request, it just provides the mapping to a filename (and rejects invalid requests).
</textile>
]]></textile>
</description>
<example>
<config>
<config><![CDATA[
setup {
module_load "mod_lua";
lua.plugin "secdownload.lua";
}
secdownload [ "prefix" => "/sec/", "document-root" => "/secret/path", "secret" => "abc", "timeout" => 600 ];
</config>
]]></config>
</example>
</action>

View File

@ -3,10 +3,10 @@
<short>allows you to have user-specific document roots being accessed through http://domain/~user/</short>
<description>
<textile>
<textile><![CDATA[
The document root can be built by using the home directory of a user which is specified by /~username/ at the beginning of the request path.
Alternatively, mod_userdir can build the docroot from a pattern similar to @vhost.pattern@ but using the username instead of the hostname.
</textile>
]]></textile>
</description>
<action name="userdir">
@ -20,8 +20,8 @@
Otherwise the @path@ specifies the absolute docroot to be used.
Placeholders are:
* @*@ replaced by the complete username
* $1-9 replaced by the n-th letter of the username, e.g. $2 is the second letter
* @*@ is replaced by the complete username
* @$1@-@$9@ are replaced by the n-th letter of the username, e.g. @$2@ is the second letter
Examples:
@ -38,13 +38,13 @@
]]></textile>
</description>
<example>
<config>
<config><![CDATA[
setup {
module_load "mod_userdir";
}
userdir "public_html";
</config>
]]></config>
</example>
</action>
</module>

View File

@ -100,6 +100,15 @@
</textile>
</description>
</option>
<option name="strict.post_content_length">
<short>require Content-Length for POST requests</short>
<default><value>true</value></default>
<description>
<textile>
Some clients don't send Content-Length for POST requests with empty body; they should send @Content-Length: 0@. When this check is enabled they'll get a @411 Length required@ error.
</textile>
</description>
</option>
<option name="static.exclude_extensions">
<short>don't deliver static files with one of the listed extensions</short>
@ -142,6 +151,8 @@
<description>
<textile>
Default MIME type is "application/octet-stream". The sources contain a "mimetypes example config":http://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/mimetypes.conf with many standard mappings.
The longest matching suffix is used (@".tar.gz"@ always wins over @".gz"@), and in case of duplicate entries the last one is used.
</textile>
</description>
<example>
@ -204,6 +215,8 @@
<textile>
The prefix is removed from the url path before it is appended to the base location.
You'll want the @docroot@ action *before* @alias@ actions!
"Patterns":core_pattern.html#core_pattern are supported for alias targets as in @docroot@. As only one pattern per prefix can be given @alias@ does not check whether the target exists.
</textile>
</description>
<example>
@ -212,6 +225,7 @@
alias [
"/phpmyadmin/" => "/usr/share/phpmyadmin",
"/pma/" => "/usr/share/phpmyadmin",
"/.well-known/openpgpkey/" => "/var/lib/gnupg/wks/$0/",
];
alias "/favicon.ico" => "/var/www/favicon.ico";
</config>

View File

@ -1,5 +1,5 @@
headerfiles=$(filter-out config.h,$(wildcard $(srcdir)/*.h))
headerfiles=$(filter-out $(srcdir)/config.h,$(wildcard $(srcdir)/*.h))
EXTRA_DIST=$(headerfiles)

View File

@ -42,6 +42,7 @@ typedef enum {
LI_COMP_REQUEST_REMOTEIP,
LI_COMP_REQUEST_REMOTEPORT,
LI_COMP_REQUEST_PATH,
LI_COMP_REQUEST_RAW_PATH,
LI_COMP_REQUEST_HOST,
LI_COMP_REQUEST_SCHEME,
LI_COMP_REQUEST_QUERY_STRING,

View File

@ -1,7 +1,9 @@
#ifndef _LIGHTTPD_ENVIRONMENT_H_
#define _LIGHTTPD_ENVIRONMENT_H_
#include <lighttpd/settings.h>
#ifndef _LIGHTTPD_BASE_H_
#error Please include <lighttpd/base.h> instead of this file
#endif
typedef struct liEnvironment liEnvironment;
@ -37,5 +39,10 @@ LI_API void li_environment_dup_free(liEnvironmentDup *envdup);
you must not modify the returned GString */
LI_API GString* li_environment_dup_pop(liEnvironmentDup *envdup, const gchar *key, size_t keylen);
typedef void (*liAddEnvironmentCB)(gpointer param, const gchar *key, size_t keylen, const gchar *val, size_t valuelen);
/* calls callback for various CGI environment variables to add; if the variable is also present
in envdup, the value from envdup is used instead for the callback and it is popped from envdup.
Also adds all remaining values from envdup via callback, and then frees envdup. */
LI_API void li_environment_dup2cgi(liVRequest *vr, liEnvironmentDup *envdup, liAddEnvironmentCB callback, gpointer param);
#endif

View File

@ -39,6 +39,7 @@ struct liEventBase {
liEventType type;
unsigned int keep_loop_alive:1, active: 1;
GList link_watchers; /* data points to loop */
const char *event_name; /* track what the event is used for */
liEventCallback callback;
};
@ -151,7 +152,7 @@ INLINE void li_event_set_callback_(liEventBase *base, liEventCallback callback);
#define li_event_set_callback(watcher, callback) (li_event_set_callback_(&(watcher)->base, callback))
/* defaults to keep_loop_alive = TRUE */
LI_API void li_event_io_init(liEventLoop *loop, liEventIO *io, liEventCallback callback, int fd, int events);
LI_API void li_event_io_init(liEventLoop *loop, const char *event_name, liEventIO *io, liEventCallback callback, int fd, int events);
LI_API void li_event_io_set_fd(liEventIO *io, int fd);
INLINE int li_event_io_fd(liEventIO *io);
LI_API void li_event_io_set_events(liEventIO *io, int events);
@ -161,37 +162,42 @@ INLINE liEventIO* li_event_io_from(liEventBase *base);
/* defaults to keep_loop_alive = TRUE */
/* timer will always stop when it triggers */
LI_API void li_event_timer_init(liEventLoop *loop, liEventTimer *timer, liEventCallback callback);
LI_API void li_event_timer_init(liEventLoop *loop, const char *event_name, liEventTimer *timer, liEventCallback callback);
INLINE void li_event_timer_once(liEventTimer *timer, li_tstamp timeout); /* also starts the watcher */
INLINE liEventTimer* li_event_timer_from(liEventBase *base);
/* defaults to keep_loop_alive = FALSE, starts immediately */
LI_API void li_event_async_init(liEventLoop *loop, liEventAsync *async, liEventCallback callback);
LI_API void li_event_async_init(liEventLoop *loop, const char *event_name, liEventAsync *async, liEventCallback callback);
INLINE void li_event_async_send(liEventAsync *async);
INLINE liEventAsync* li_event_async_from(liEventBase *base);
/* defaults to keep_loop_alive = TRUE, starts immediately */
LI_API void li_event_child_init(liEventLoop *loop, liEventChild *child, liEventCallback callback, int pid);
LI_API void li_event_child_init(liEventLoop *loop, const char *event_name, liEventChild *child, liEventCallback callback, int pid);
INLINE int li_event_child_pid(liEventChild *child);
INLINE int li_event_child_status(liEventChild *child);
INLINE liEventChild* li_event_child_from(liEventBase *base);
/* defaults to keep_loop_alive = FALSE, starts immediately */
LI_API void li_event_signal_init(liEventLoop *loop, liEventSignal *signal, liEventCallback callback, int signum);
LI_API void li_event_signal_init(liEventLoop *loop, const char *event_name, liEventSignal *signal, liEventCallback callback, int signum);
INLINE int li_event_signal_signum(liEventSignal *signal);
INLINE liEventSignal* li_event_signal_from(liEventBase *base);
/* defaults to keep_loop_alive = FALSE, starts immediately */
LI_API void li_event_prepare_init(liEventLoop *loop, liEventPrepare *prepare, liEventCallback callback);
LI_API void li_event_prepare_init(liEventLoop *loop, const char *event_name, liEventPrepare *prepare, liEventCallback callback);
INLINE liEventPrepare* li_event_prepare_from(liEventBase *base);
/* defaults to keep_loop_alive = FALSE, starts immediately */
LI_API void li_event_check_init(liEventLoop *loop, liEventCheck *check, liEventCallback callback);
LI_API void li_event_check_init(liEventLoop *loop, const char *event_name, liEventCheck *check, liEventCallback callback);
INLINE liEventCheck* li_event_check_from(liEventBase *base);
LI_API const char* li_event_type_string(liEventType type);
/* inline implementations */
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
INLINE li_tstamp li_event_now(liEventLoop *loop) {
return ev_now(loop->loop);
}
@ -530,5 +536,6 @@ INLINE liEventCheck* li_event_check_from(liEventBase *base) {
return LI_CONTAINER_OF(base, liEventCheck, base);
}
#pragma clang diagnostic pop
#endif

View File

@ -12,6 +12,8 @@ struct liHttpResponseCtx {
gboolean accept_cgi, accept_nph;
gboolean drop_header; /* for 1xx responses */
liHttpVersion http_version;
liChunkParserMark mark;
GString *h_key, *h_value;
};

View File

@ -16,8 +16,8 @@
* Logs are sent once per event loop iteration to the logging thread in order to reduce syscalls and lock contention.
*/
/* at least one of srv and wrk must not be NULL. log_map may be NULL. */
#define _SEGFAULT(srv, wrk, log_map, fmt, ...) \
/* at least one of srv and wrk must not be NULL. ctx may be NULL. */
#define _SEGFAULT(srv, wrk, ctx, fmt, ...) \
do { \
li_log_write(srv, NULL, NULL, LI_LOG_LEVEL_ABORT, LI_LOG_FLAG_TIMESTAMP, "(crashing) %s:%d: %s " fmt, LI_REMOVE_PATH(__FILE__), __LINE__, G_STRFUNC, __VA_ARGS__); \
li_print_backtrace_stderr(); \

View File

@ -17,7 +17,9 @@ enum liCoreOptions {
LI_CORE_OPTION_ASYNC_STAT,
LI_CORE_OPTION_BUFFER_ON_DISK_REQUEST_BODY
LI_CORE_OPTION_BUFFER_ON_DISK_REQUEST_BODY,
LI_CORE_OPTION_STRICT_POST_CONTENT_LENGTH,
};
enum liCoreOptionPtrs {

View File

@ -38,8 +38,8 @@
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
# ifndef _BSD_SOURCE
# define _BSD_SOURCE 1
# ifndef _DEFAULT_SOURCE
# define _DEFAULT_SOURCE 1
# endif
#endif

View File

@ -3,6 +3,6 @@
#include <lighttpd/base.h>
LI_API liStream* li_stream_http_response_handle(liStream *http_in, liVRequest *vr, gboolean accept_cgi, gboolean accept_nph);
LI_API liStream* li_stream_http_response_handle(liStream *http_in, liVRequest *vr, gboolean accept_cgi, gboolean accept_nph, gboolean keepalive);
#endif

View File

@ -3,7 +3,14 @@
#include <lighttpd/base.h>
/* parses uri->raw into all components, which have to be reset/initialized before */
LI_API gboolean li_parse_raw_url(liRequestUri *uri);
/* parse input into uri->path, uri->raw_path and uri->query, which get truncated before.
* also decodes and simplifies path on success
*/
LI_API gboolean li_parse_raw_path(liRequestUri *uri, GString *input);
LI_API gboolean li_parse_hostname(liRequestUri *uri);
#endif

View File

@ -21,6 +21,7 @@ LI_API void li_print_backtrace_stderr(void);
LI_API void li_fd_init(int fd);
LI_API void li_fd_no_block(int fd);
LI_API void li_fd_block(int fd);
LI_API void li_fd_close_on_exec(int fd);
#ifndef LIGHTY_OS_WINDOWS
/* return -2 for EAGAIN, -1 for some other error, 0 for success */
@ -80,8 +81,6 @@ LI_API gboolean li_string_suffix(const GString *str, const gchar *s, gsize len);
LI_API void li_string_append_int(GString *dest, gint64 val);
LI_API gsize li_dirent_buf_size(DIR * dirp);
LI_API void li_apr_sha1_base64(GString *dest, const GString *passwd);
LI_API void li_apr_md5_crypt(GString *dest, const GString *password, const GString *salt);

View File

@ -159,6 +159,7 @@ INLINE void li_value_list_set(liValue *val, guint ndx, liValue *entry) {
GPtrArray *list;
if (NULL == val || LI_VALUE_LIST != val->type) {
li_value_free(entry);
return;
}
list = val->data.list;
if (ndx <= list->len) {

View File

@ -34,7 +34,7 @@ struct liWaitQueue {
*/
/* initializes a waitqueue by creating the timer and initializing the queue. precision is sub-seconds */
LI_API void li_waitqueue_init(liWaitQueue *queue, liEventLoop *loop, liWaitQueueCB callback, gdouble delay, gpointer data);
LI_API void li_waitqueue_init(liWaitQueue *queue, liEventLoop *loop, const char *waitqueue_name, liWaitQueueCB callback, gdouble delay, gpointer data);
/* stops the waitqueue. to restart it, simply call li_waitqueue_update */
LI_API void li_waitqueue_stop(liWaitQueue *queue);

View File

@ -19,14 +19,14 @@ ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES)
OPTION(WITH_LUA "with lua 5.1 for lua-configfile [default: on]" ON)
OPTION(WITHOUT_CONFIG_PARSER "without standard config parser [default: off]" OFF)
OPTION(WITH_UNWIND "with (lib)unwind support in asserts to print backtraces [default: off]" OFF)
OPTION(WITH_OPENSSL "with openssl support [default: off]")
OPTION(WITH_GNUTLS "with gnutls support [default: off]")
OPTION(WITH_SNI "with SNI support for gnutls/openssl, needs libidn [default: off]")
OPTION(WITH_UNWIND "with (lib)unwind support in asserts to print backtraces [default: on]" ON)
OPTION(WITH_OPENSSL "with openssl support [default: on]" ON)
OPTION(WITH_GNUTLS "with gnutls support [default: on]" ON)
OPTION(WITH_SNI "with SNI support for gnutls/openssl, needs libidn [default: on]" ON)
OPTION(BUILD_STATIC "build a static lighttpd with all modules added")
OPTION(BUILD_EXTRA_WARNINGS "extra warnings")
OPTION(WITH_BZIP "with bzip2 support for mod_deflate")
OPTION(WITH_ZLIB "with deflate support for mod_deflate")
OPTION(BUILD_EXTRA_WARNINGS "extra warnings [default: on]" ON)
OPTION(WITH_BZIP "with bzip2 support for mod_deflate [default: on]" ON)
OPTION(WITH_ZLIB "with deflate support for mod_deflate [default: on]" ON)
OPTION(WITH_PROFILER "with memory profiler")
OPTION(BUILD_UNIT_TESTS "build unit tests for testing")
@ -337,8 +337,8 @@ TARGET_LINK_LIBRARIES(lighttpd2 lighttpd-${PACKAGE_VERSION}-common lighttpd-${PA
SET(L_INSTALL_TARGETS ${L_INSTALL_TARGETS} lighttpd2-worker lighttpd2 lighttpd-${PACKAGE_VERSION}-common lighttpd-${PACKAGE_VERSION}-shared lighttpd-${PACKAGE_VERSION}-sharedangel)
IF(BUILD_EXTRA_WARNINGS)
SET(WARN_CFLAGS " -g -O2 -g2 -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wcast-align -Wsign-compare -Wnested-externs -Wpointer-arith -Wmissing-prototypes -Wshadow")
SET(WARN_LDFLAGS " -g -O2 -g2 -Wall -Wl,--as-needed")
SET(WARN_CFLAGS " -g -O2 -g2 -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wcast-align -Wsign-compare -Wnested-externs -Wpointer-arith -Wmissing-prototypes -Wshadow -Wno-pointer-sign -Wformat-security")
SET(WARN_LDFLAGS " -g -O2 -g2 -Wall -Wl,--as-needed -Wl,--no-undefined")
# -Werror
ELSE(BUILD_EXTRA_WARNINGS)
SET(WARN_CFLAGS "")
@ -346,7 +346,8 @@ ELSE(BUILD_EXTRA_WARNINGS)
ENDIF(BUILD_EXTRA_WARNINGS)
SET(COMMON_LDFLAGS "${LUA_LDFLAGS} ${LIBEV_LDFLAGS} ${GTHREAD_LDFLAGS} ${GMODULE_LDFLAGS}${WARN_LDFLAGS}")
SET(COMMON_CFLAGS "${LUA_CFLAGS} ${LIBEV_CFLAGS} ${GTHREAD_CFLAGS} ${GMODULE_CFLAGS}${WARN_CFLAGS}")
SET(COMMON_CFLAGS "${LUA_CFLAGS_OTHER} ${LIBEV_CFLAGS_OTHER} ${GTHREAD_CFLAGS_OTHER} ${GMODULE_CFLAGS_OTHER}${WARN_CFLAGS}")
SET(COMMON_INCLUDE_DIRECTORIES ${LUA_INCLUDE_DIRS} ${LIBEV_INCLUDE_DIRS} ${GTHREAD_INCLUDE_DIRS} ${GMODULE_INCLUDE_DIRS})
ADD_AND_INSTALL_LIBRARY(mod_access "modules/mod_access.c")
ADD_AND_INSTALL_LIBRARY(mod_accesslog "modules/mod_accesslog.c")
@ -383,7 +384,7 @@ IF(WITH_LUA)
ENDIF(WITH_LUA)
IF(WITH_GNUTLS)
ADD_AND_INSTALL_LIBRARY(mod_gnutls "modules/mod_gnutls.c;modules/gnutls_filter.c")
ADD_AND_INSTALL_LIBRARY(mod_gnutls "modules/mod_gnutls.c;modules/gnutls_filter.c;modules/gnutls_ocsp.c")
TARGET_LINK_LIBRARIES(mod_gnutls ${GNUTLS_LDFLAGS} ${IDN_LDFLAGS})
ADD_TARGET_PROPERTIES(mod_gnutls COMPILE_FLAGS ${GNUTLS_CFLAGS} ${IDN_CFLAGS})
ENDIF(WITH_GNUTLS)
@ -396,18 +397,23 @@ ENDIF(WITH_OPENSSL)
TARGET_LINK_LIBRARIES(lighttpd-${PACKAGE_VERSION}-common ${COMMON_LDFLAGS} ${UNWIND_LDFLAGS})
ADD_TARGET_PROPERTIES(lighttpd-${PACKAGE_VERSION}-common COMPILE_FLAGS ${COMMON_CFLAGS} ${UNWIND_CFLAGS})
TARGET_INCLUDE_DIRECTORIES(lighttpd-${PACKAGE_VERSION}-common PUBLIC ${COMMON_INCLUDE_DIRECTORIES})
TARGET_LINK_LIBRARIES(lighttpd-${PACKAGE_VERSION}-shared ${COMMON_LDFLAGS})
TARGET_LINK_LIBRARIES(lighttpd-${PACKAGE_VERSION}-shared ${COMMON_LDFLAGS} m)
ADD_TARGET_PROPERTIES(lighttpd-${PACKAGE_VERSION}-shared COMPILE_FLAGS ${COMMON_CFLAGS})
TARGET_INCLUDE_DIRECTORIES(lighttpd-${PACKAGE_VERSION}-shared PUBLIC ${COMMON_INCLUDE_DIRECTORIES})
TARGET_LINK_LIBRARIES(lighttpd-${PACKAGE_VERSION}-sharedangel ${COMMON_LDFLAGS})
ADD_TARGET_PROPERTIES(lighttpd-${PACKAGE_VERSION}-sharedangel COMPILE_FLAGS ${COMMON_CFLAGS})
TARGET_INCLUDE_DIRECTORIES(lighttpd-${PACKAGE_VERSION}-sharedangel PUBLIC ${COMMON_INCLUDE_DIRECTORIES})
TARGET_LINK_LIBRARIES(lighttpd2-worker ${COMMON_LDFLAGS})
ADD_TARGET_PROPERTIES(lighttpd2-worker COMPILE_FLAGS ${COMMON_CFLAGS})
TARGET_INCLUDE_DIRECTORIES(lighttpd2-worker PUBLIC ${COMMON_INCLUDE_DIRECTORIES})
TARGET_LINK_LIBRARIES(lighttpd2 ${COMMON_LDFLAGS})
ADD_TARGET_PROPERTIES(lighttpd2 COMPILE_FLAGS ${COMMON_CFLAGS})
TARGET_INCLUDE_DIRECTORIES(lighttpd2 PUBLIC ${COMMON_INCLUDE_DIRECTORIES})
IF(HAVE_LIBCRYPT)
TARGET_LINK_LIBRARIES(lighttpd-${PACKAGE_VERSION}-common crypt)
@ -460,6 +466,7 @@ IF(BUILD_UNIT_TESTS)
TARGET_LINK_LIBRARIES(${EXENAME} ${COMMON_LDFLAGS})
ADD_TARGET_PROPERTIES(${EXENAME} COMPILE_FLAGS ${COMMON_CFLAGS})
TARGET_INCLUDE_DIRECTORIES(${EXENAME} PUBLIC ${COMMON_INCLUDE_DIRECTORIES})
TARGET_LINK_LIBRARIES(${EXENAME} lighttpd-${PACKAGE_VERSION}-common lighttpd-${PACKAGE_VERSION}-shared)
@ -467,6 +474,7 @@ IF(BUILD_UNIT_TESTS)
ENDMACRO(ADD_TEST_BINARY)
ADD_TEST_BINARY(Chunk-UnitTest test-chunk unittests/test-chunk.c)
ADD_TEST_BINARY(HttpRequestParser-UnitTest test-http-request-parser unittests/test-http-request-parser.c)
ADD_TEST_BINARY(IpParser-UnitTest test-ip-parser unittests/test-ip-parser.c)
ADD_TEST_BINARY(Radix-UnitTest test-radix unittests/test-radix.c)
ADD_TEST_BINARY(RangeParser-UnitTest test-range-parser unittests/test-range-parser.c)

View File

@ -328,7 +328,7 @@ GQuark li_angel_config_parser_error_quark(void) {
( endoffile | keyword | name | cast | single_operator | operator | integer | string );
}%%
%% write data;
%% write data noerror nofinal;
static void set_config_error(liConfigTokenizerContext *ctx, GError **error, const char *fmt, va_list ap) {
GString *msg = g_string_sized_new(127);
@ -399,6 +399,7 @@ static liConfigToken tokenizer_next(liConfigTokenizerContext *ctx, GError **erro
ctx->token_line = ctx->line;
ctx->token_line_start = ctx->line_start;
(void) config_tokenizer_en_token;
%% write init;
%% write exec;
@ -724,7 +725,7 @@ static gboolean p_value_list(gint *key_value_nesting, liValue **result, gboolean
if (!key_value_list && TK_ASSOCICATE == token) {
key_value_list = TRUE;
if (li_value_list_len(list) > 0) {
return parse_error(ctx, error, "unexpected '=>'");
parse_error(ctx, error, "unexpected '=>'");
goto error;
}
}
@ -839,6 +840,7 @@ static gboolean p_value(gint *key_value_nesting, liValue **value, liConfigToken
li_value_free(v);
v = li_value_new_number(i);
}
break;
default:
parse_error(ctx, error, "cast(int) from %s not supported yet", li_value_type_string(v));
li_value_free(v);

View File

@ -135,17 +135,19 @@ gboolean li_plugins_config_load(liServer *srv, const gchar *filename) {
GError *error = NULL;
guint i;
if (!li_plugins_load_module(srv, NULL)) {
ERROR(srv, "%s", "failed loading core plugins");
li_plugins_config_clean(srv);
return FALSE;
}
if (filename) {
if (!li_plugins_load_module(srv, NULL)) {
ERROR(srv, "%s", "failed loading core plugins");
li_plugins_config_clean(srv);
return FALSE;
}
if (filename && !li_angel_config_parse_file(srv, filename, &error)) {
ERROR(srv, "failed to parse config file: %s", error->message);
g_error_free(error);
li_plugins_config_clean(srv);
return FALSE;
if (!li_angel_config_parse_file(srv, filename, &error)) {
ERROR(srv, "failed to parse config file: %s", error->message);
g_error_free(error);
li_plugins_config_clean(srv);
return FALSE;
}
}
/* check new config */

View File

@ -978,7 +978,7 @@ static gboolean core_init(liServer *srv, liPlugin *p) {
li_angel_plugin_add_angel_cb(p, "reached-state", core_reached_state);
li_angel_plugin_add_angel_cb(p, "log-open-file", core_log_open_file);
li_event_signal_init(&srv->loop, &config->sig_hup, core_handle_sig_hup, SIGHUP);
li_event_signal_init(&srv->loop, "angel SIGHUP", &config->sig_hup, core_handle_sig_hup, SIGHUP);
return TRUE;
}

View File

@ -88,7 +88,7 @@ liErrorPipe* li_error_pipe_new(liServer *srv, liErrorPipeCB cb, gpointer ctx) {
epipe->srv = srv;
epipe->cb = cb;
epipe->ctx = ctx;
li_event_io_init(&srv->loop, &epipe->fd_watcher, error_pipe_cb, fds[0], LI_EV_READ);
li_event_io_init(&srv->loop, "angel error-pipe", &epipe->fd_watcher, error_pipe_cb, fds[0], LI_EV_READ);
epipe->fds[0] = fds[0];
epipe->fds[1] = fds[1];
@ -170,15 +170,27 @@ liProc* li_proc_new(liServer *srv, gchar **args, gchar **env, uid_t uid, gid_t g
#endif
if (gid != (gid_t) -1) {
setgid(gid);
setgroups(0, NULL);
if (username) initgroups(username, gid);
if (-1 == setgid(gid)) {
ERROR(srv, "setgid(%i) failed: %s", (int) gid, g_strerror(errno));
abort();
}
if (-1 == setgroups(0, NULL)) {
ERROR(srv, "setgroups failed: %s", g_strerror(errno));
abort();
}
if (username && -1 == initgroups(username, gid)) {
ERROR(srv, "initgroups('%s', %i) failed: %s", username, (int) gid, g_strerror(errno));
abort();
}
}
if (cb) cb(ctx);
if (uid != (uid_t) -1) {
setuid(uid);
if (-1 == setuid(uid)) {
ERROR(srv, "setuid(%i) failed: %s", (int) uid, g_strerror(errno));
abort();
}
}
if (NULL == env)

View File

@ -18,9 +18,9 @@ liServer* li_server_new(const gchar *module_dir, gboolean module_resident) {
li_event_loop_init(&srv->loop, ev_default_loop(0));
li_event_signal_init(&srv->loop, &srv->sig_w_INT, sigint_cb, SIGINT);
li_event_signal_init(&srv->loop, &srv->sig_w_TERM, sigint_cb, SIGTERM);
li_event_signal_init(&srv->loop, &srv->sig_w_PIPE, sigpipe_cb, SIGPIPE);
li_event_signal_init(&srv->loop, "angel SIGINT", &srv->sig_w_INT, sigint_cb, SIGINT);
li_event_signal_init(&srv->loop, "angel SIGTERM", &srv->sig_w_TERM, sigint_cb, SIGTERM);
li_event_signal_init(&srv->loop, "angel SIGPIPE", &srv->sig_w_PIPE, sigpipe_cb, SIGPIPE);
li_log_init(srv);
li_plugins_init(srv, module_dir, module_resident);
@ -178,7 +178,7 @@ static void instance_spawn(liInstance *i) {
close(confd[1]);
li_event_clear(&i->child_watcher);
li_event_child_init(&i->srv->loop, &i->child_watcher, instance_child_cb, i->proc->child_pid);
li_event_child_init(&i->srv->loop, "lighttpd2-worker", &i->child_watcher, instance_child_cb, i->proc->child_pid);
i->s_cur = LI_INSTANCE_DOWN;
li_instance_acquire(i);
DEBUG(i->srv, "Instance (%i) spawned: %s", i->proc->child_pid, i->ic->cmd[0]);

View File

@ -1,5 +1,7 @@
#include <lighttpd/angel_base.h>
#include "../common/value_impl.c"
liValue* li_value_copy(liValue* val) {
return li_common_value_copy_(val);
}

View File

@ -32,7 +32,7 @@ EXTRA_liblighttpd2_common_la_SOURCES=profiler.c
BUILT_SOURCES=$(parsers)
CLEANFILES=$(parsers)
EXTRA_DIST=ip_parsers.rl
EXTRA_DIST=ip_parsers.rl value_impl.c
ip_parsers.c: ip_parsers.rl
$(RAGEL) -C -T1 -o $@ $<

View File

@ -291,7 +291,7 @@ static gboolean angel_connection_read(liAngelConnection *acon, GError **err) {
static void angel_connection_io_cb(liEventBase *watcher, int events) {
liAngelConnection *acon = LI_CONTAINER_OF(li_event_io_from(watcher), liAngelConnection, fd_watcher);
if (events | LI_EV_WRITE) {
if (events & LI_EV_WRITE) {
GString *out_str;
int i;
ssize_t written, len;
@ -368,7 +368,7 @@ write_eagain:
if (out_queue_empty) li_event_io_rem_events(&acon->fd_watcher, LI_EV_WRITE);
}
if (events | LI_EV_READ) {
if (events & LI_EV_READ) {
GError *err = NULL;
if (!angel_connection_read(acon, &err)) {
li_event_clear(&acon->out_notify_watcher);
@ -395,11 +395,11 @@ liAngelConnection* li_angel_connection_new(liEventLoop *loop, int fd, gpointer d
acon->call_id_list = li_idlist_new(65535);
acon->call_table = g_ptr_array_new();
li_event_io_init(loop, &acon->fd_watcher, angel_connection_io_cb, fd, LI_EV_READ);
li_event_io_init(loop, "angel connection", &acon->fd_watcher, angel_connection_io_cb, fd, LI_EV_READ);
li_event_set_keep_loop_alive(&acon->fd_watcher, FALSE);
li_event_start(&acon->fd_watcher);
li_event_async_init(loop, &acon->out_notify_watcher, angel_connection_out_notify_cb);
li_event_async_init(loop, "angel out-notify", &acon->out_notify_watcher, angel_connection_out_notify_cb);
acon->out = g_queue_new();
acon->in.data = g_string_sized_new(1024);
@ -504,9 +504,9 @@ liAngelCall *li_angel_call_new(liEventLoop *loop, liAngelCallCB callback, li_tst
g_assert(NULL != callback);
call->callback = callback;
li_event_timer_init(loop, &call->timeout_watcher, angel_call_timeout_cb);
li_event_timer_init(loop, "angel call timeout", &call->timeout_watcher, angel_call_timeout_cb);
li_event_timer_once(&call->timeout_watcher, timeout);
li_event_async_init(loop, &call->result_watcher, angel_call_result_cb);
li_event_async_init(loop, "angel call result", &call->result_watcher, angel_call_result_cb);
call->id = -1;
return call;

View File

@ -171,10 +171,11 @@ static int io_events_to_libev(int events) {
return revents;
}
void li_event_io_init(liEventLoop *loop, liEventIO *io, liEventCallback callback, int fd, int events) {
void li_event_io_init(liEventLoop *loop, const char *event_name, liEventIO *io, liEventCallback callback, int fd, int events) {
memset(io, 0, sizeof(*io));
io->base.type = LI_EVT_IO;
io->base.keep_loop_alive = 1;
io->base.event_name = event_name;
io->base.callback = callback;
io->events = events;
ev_init(&io->libevmess.w, NULL);
@ -251,10 +252,11 @@ static void event_timer_cb(struct ev_loop *loop, ev_timer *w, int revents) {
timer->base.callback(&timer->base, LI_EV_WAKEUP);
}
void li_event_timer_init(liEventLoop *loop, liEventTimer *timer, liEventCallback callback) {
void li_event_timer_init(liEventLoop *loop, const char *event_name, liEventTimer *timer, liEventCallback callback) {
memset(timer, 0, sizeof(*timer));
timer->base.type = LI_EVT_TIMER;
timer->base.keep_loop_alive = 1;
timer->base.event_name = event_name;
timer->base.callback = callback;
ev_init(&timer->libevmess.w, NULL);
ev_set_cb(&timer->libevmess.timer, event_timer_cb);
@ -273,10 +275,11 @@ static void event_async_cb(struct ev_loop *loop, ev_async *w, int revents) {
async->base.callback(&async->base, LI_EV_WAKEUP);
}
void li_event_async_init(liEventLoop *loop, liEventAsync *async, liEventCallback callback) {
void li_event_async_init(liEventLoop *loop, const char *event_name, liEventAsync *async, liEventCallback callback) {
memset(async, 0, sizeof(*async));
async->base.type = LI_EVT_ASYNC;
async->base.keep_loop_alive = 0;
async->base.event_name = event_name;
async->base.callback = callback;
ev_init(&async->libevmess.w, NULL);
ev_set_cb(&async->libevmess.async, event_async_cb);
@ -302,10 +305,11 @@ static void event_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
child->base.callback(&child->base, LI_EV_WAKEUP);
}
void li_event_child_init(liEventLoop *loop, liEventChild *child, liEventCallback callback, int pid) {
void li_event_child_init(liEventLoop *loop, const char *event_name, liEventChild *child, liEventCallback callback, int pid) {
memset(child, 0, sizeof(*child));
child->base.type = LI_EVT_CHILD;
child->base.keep_loop_alive = 1;
child->base.event_name = event_name;
child->base.callback = callback;
ev_init(&child->libevmess.w, NULL);
ev_child_set(&child->libevmess.child, pid, 0);
@ -326,10 +330,11 @@ static void event_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
sig->base.callback(&sig->base, LI_EV_WAKEUP);
}
void li_event_signal_init(liEventLoop *loop, liEventSignal *sig, liEventCallback callback, int signum) {
void li_event_signal_init(liEventLoop *loop, const char *event_name, liEventSignal *sig, liEventCallback callback, int signum) {
memset(sig, 0, sizeof(*sig));
sig->base.type = LI_EVT_SIGNAL;
sig->base.keep_loop_alive = 0;
sig->base.event_name = event_name;
sig->base.callback = callback;
ev_init(&sig->libevmess.w, NULL);
ev_signal_set(&sig->libevmess.sig, signum);
@ -350,10 +355,11 @@ static void event_prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) {
prepare->base.callback(&prepare->base, LI_EV_WAKEUP);
}
void li_event_prepare_init(liEventLoop *loop, liEventPrepare *prepare, liEventCallback callback) {
void li_event_prepare_init(liEventLoop *loop, const char *event_name, liEventPrepare *prepare, liEventCallback callback) {
memset(prepare, 0, sizeof(*prepare));
prepare->base.type = LI_EVT_PREPARE;
prepare->base.keep_loop_alive = 0;
prepare->base.event_name = event_name;
prepare->base.callback = callback;
ev_init(&prepare->libevmess.w, NULL);
ev_set_cb(&prepare->libevmess.prepare, event_prepare_cb);
@ -373,10 +379,11 @@ static void event_check_cb(struct ev_loop *loop, ev_check *w, int revents) {
check->base.callback(&check->base, LI_EV_WAKEUP);
}
void li_event_check_init(liEventLoop *loop, liEventCheck *check, liEventCallback callback) {
void li_event_check_init(liEventLoop *loop, const char *event_name, liEventCheck *check, liEventCallback callback) {
memset(check, 0, sizeof(*check));
check->base.type = LI_EVT_CHECK;
check->base.keep_loop_alive = 0;
check->base.event_name = event_name;
check->base.callback = callback;
ev_init(&check->libevmess.w, NULL);
ev_set_cb(&check->libevmess.check, event_check_cb);
@ -384,3 +391,25 @@ void li_event_check_init(liEventLoop *loop, liEventCheck *check, liEventCallback
if (NULL != loop) li_event_attach(loop, check);
li_event_start(check);
}
const char* li_event_type_string(liEventType type) {
switch (type) {
case LI_EVT_NONE:
return "none";
case LI_EVT_IO:
return "io";
case LI_EVT_TIMER:
return "timer";
case LI_EVT_ASYNC:
return "async";
case LI_EVT_CHILD:
return "child";
case LI_EVT_SIGNAL:
return "signal";
case LI_EVT_PREPARE:
return "prepare";
case LI_EVT_CHECK:
return "check";
}
return "INVALID";
}

View File

@ -27,7 +27,7 @@ void li_idlist_free(liIDList *l) {
static void mark_bit(GArray *a, gint id) {
guint ndx = id / UL_BITS, bndx = id % UL_BITS;
gulong bmask = 1 << bndx;
gulong bmask = 1ul << bndx;
g_assert(id >= 0 && ndx < a->len);
g_assert(0 == (g_array_index(a, gulong, ndx) & (bmask))); /* bit musn't be set */
@ -36,7 +36,7 @@ static void mark_bit(GArray *a, gint id) {
static void clear_bit(GArray *a, gint id) {
guint ndx = id / UL_BITS, bndx = id % UL_BITS;
gulong bmask = 1 << bndx;
gulong bmask = 1ul << bndx;
g_assert(id >= 0 && ndx < a->len);
g_assert(0 != (g_array_index(a, gulong, ndx) & (bmask))); /* bit must be set */
@ -100,7 +100,7 @@ gint li_idlist_get(liIDList *l) {
gboolean li_idlist_is_used(liIDList *l, gint id) {
GArray *a = l->bitvector;
guint ndx = id / UL_BITS, bndx = id % UL_BITS;
gulong bmask = 1 << bndx;
gulong bmask = 1ul << bndx;
if (id < 0 || ndx >= a->len) return FALSE;
return (0 != (g_array_index(a, gulong, ndx) & (bmask)));

View File

@ -29,7 +29,7 @@
ipv4_socket := ipv4_data port? end;
ipv4_socket_cidr := ipv4_data netmask? port? end;
write data;
write data noerror;
}%%
gboolean li_parse_ipv4(const char *str, guint32 *ip, guint32 *netmask, guint16 *port) {
@ -41,6 +41,7 @@ gboolean li_parse_ipv4(const char *str, guint32 *ip, guint32 *netmask, guint16 *
if (netmask) *netmask = 0xffffffffu;
if (port) *port = 0;
(void) ipv4_parser_start;
%% write init nocs;
cs = netmask
@ -110,7 +111,7 @@ gboolean li_parse_ipv4(const char *str, guint32 *ip, guint32 *netmask, guint16 *
ipv6_socket := ( ( ipv6_data ) | ( "[" ipv6_data "]" port? ) ) end;
ipv6_socket_cidr := ( ( ipv6_data network? ) | ( ipv6_bracket_cidr port?) ) end;
write data;
write data noerror;
}%%
gboolean li_parse_ipv6(const char *str, guint8 *ip, guint *network, guint16 *port) {
@ -124,6 +125,7 @@ gboolean li_parse_ipv6(const char *str, guint8 *ip, guint *network, guint16 *por
if (network) *network = 128;
if (port) *port = 0;
(void) ipv6_parser_start;
%% write init nocs;
cs = network

View File

@ -61,9 +61,9 @@ static void job_async_queue_cb(liEventBase *watcher, int events) {
void li_job_queue_init(liJobQueue* jq, liEventLoop *loop) {
li_event_prepare_init(loop, &jq->prepare_watcher, job_queue_prepare_cb);
li_event_async_init(loop, &jq->async_queue_watcher, job_async_queue_cb);
li_event_timer_init(loop, &jq->queue_watcher, job_queue_watcher_cb);
li_event_prepare_init(loop, "jobqueue", &jq->prepare_watcher, job_queue_prepare_cb);
li_event_async_init(loop, "jobqueue", &jq->async_queue_watcher, job_async_queue_cb);
li_event_timer_init(loop, "jobqueue", &jq->queue_watcher, job_queue_watcher_cb);
/* job queue */
g_queue_init(&jq->queue);

View File

@ -330,7 +330,9 @@ static void memcached_connect(liMemcachedCon *con) {
int err;
len = sizeof(err);
#ifdef SO_ERROR
getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&err, &len);
if (-1 == getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&err, &len)) {
err = errno;
}
#else
{
char ch;
@ -735,7 +737,7 @@ static void memcached_io_cb(liEventBase *watcher, int events) {
li_memcached_con_acquire(con); /* make sure con isn't freed in the middle of something */
if (events | LI_EV_WRITE) {
if (events & LI_EV_WRITE) {
int i;
ssize_t written, len;
gchar *data;
@ -779,7 +781,7 @@ write_eagain:
send_queue_clean(&con->out);
}
if (events | LI_EV_READ) {
if (events & LI_EV_READ) {
do {
handle_read(con);
} while (con->remaining && con->remaining->used > 0);
@ -799,7 +801,7 @@ liMemcachedCon* li_memcached_con_new(liEventLoop *loop, liSocketAddress addr) {
con->tmpstr = g_string_sized_new(511);
con->fd = -1;
li_event_io_init(loop, &con->con_watcher, memcached_io_cb, -1, 0);
li_event_io_init(loop, "memcached", &con->con_watcher, memcached_io_cb, -1, 0);
li_event_set_keep_loop_alive(&con->con_watcher, FALSE);
memcached_connect(con);

View File

@ -57,7 +57,7 @@ static void run_tasklet(gpointer data, gpointer userdata) {
liTaskletPool* li_tasklet_pool_new(liEventLoop *loop, gint threads) {
liTaskletPool *pool = g_slice_new0(liTaskletPool);
li_event_async_init(loop, &pool->finished_watcher, finished_watcher_cb);
li_event_async_init(loop, "tasklet pool", &pool->finished_watcher, finished_watcher_cb);
pool->finished = g_async_queue_new();

View File

@ -81,7 +81,10 @@ void li_fatal(const char *filename, unsigned int line, const char *function, con
void li_fd_no_block(int fd) {
#ifdef O_NONBLOCK
fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
int flags = fcntl(fd, F_GETFL, 0);
if (0 == (flags & O_NONBLOCK)) {
LI_FORCE_ASSERT(-1 != fcntl(fd, F_SETFL, flags | O_NONBLOCK));
}
#elif defined _WIN32
int i = 1;
ioctlsocket(fd, FIONBIO, &i);
@ -92,7 +95,10 @@ void li_fd_no_block(int fd) {
void li_fd_block(int fd) {
#ifdef O_NONBLOCK
fcntl(fd, F_SETFL, O_RDWR);
int flags = fcntl(fd, F_GETFL, 0);
if (0 != (flags & O_NONBLOCK)) {
LI_FORCE_ASSERT(-1 != fcntl(fd, F_SETFL, flags & ~O_NONBLOCK));
}
#elif defined _WIN32
int i = 0;
ioctlsocket(fd, FIONBIO, &i);
@ -101,11 +107,20 @@ void li_fd_block(int fd) {
#endif
}
void li_fd_init(int fd) {
void li_fd_close_on_exec(int fd) {
#ifdef FD_CLOEXEC
/* close fd on exec (cgi) */
fcntl(fd, F_SETFD, FD_CLOEXEC);
/* close fd on exec */
int flags = fcntl(fd, F_GETFD, 0);
if (0 == (flags & FD_CLOEXEC)) {
LI_FORCE_ASSERT(-1 != fcntl(fd, F_SETFD, flags | FD_CLOEXEC));
}
#else
UNUSED(fd);
#endif
}
void li_fd_init(int fd) {
li_fd_close_on_exec(fd);
li_fd_no_block(fd);
}
@ -559,6 +574,7 @@ GString *li_sockaddr_to_string(liSocketAddress addr, GString *dest, gboolean sho
guint i;
if (!saddr) {
if (!dest) dest = g_string_sized_new(6);
li_string_assign_len(dest, CONST_STR_LEN("<null>"));
return dest;
}
@ -600,10 +616,13 @@ GString *li_sockaddr_to_string(liSocketAddress addr, GString *dest, gboolean sho
case AF_INET6:
/* ipv6 - not yet implemented with own function */
if (!dest)
dest = g_string_sized_new(INET6_ADDRSTRLEN+6);
dest = g_string_sized_new(INET6_ADDRSTRLEN+8);
li_ipv6_tostring(dest, saddr->ipv6.sin6_addr.s6_addr);
if (showport) g_string_append_printf(dest, ":%u", (unsigned int) ntohs(saddr->ipv6.sin6_port));
if (showport) {
g_string_prepend_c(dest, '[');
g_string_append_printf(dest, "]:%u", (unsigned int) ntohs(saddr->ipv6.sin6_port));
}
break;
#endif
#ifdef HAVE_SYS_UN_H
@ -613,7 +632,12 @@ GString *li_sockaddr_to_string(liSocketAddress addr, GString *dest, gboolean sho
else
g_string_truncate(dest, 0);
g_string_append_len(dest, CONST_STR_LEN("unix:"));
g_string_append(dest, saddr->un.sun_path);
{
const char* path_start = saddr->un.sun_path;
const char* path_end = ((const char*)saddr) + addr.len;
size_t path_len = path_end - path_start;
g_string_append_len(dest, path_start, strnlen(path_start, path_len));
}
break;
#endif
default:
@ -636,12 +660,12 @@ liSocketAddress li_sockaddr_from_string(const GString *str, guint tcp_default_po
#ifdef HAVE_SYS_UN_H
if (0 == strncmp(str->str, "unix:/", 6)) {
/* try to support larger unix socket names than what fits in the default sockaddr_un struct */
saddr.len = str->len + 1 - 5 + sizeof(saddr.addr->un) - sizeof(saddr.addr->un.sun_path);
if (saddr.len < sizeof(saddr.addr->un)) saddr.len = sizeof(saddr.addr->un);
saddr.addr = (liSockAddr*) g_slice_alloc0(saddr.len);
saddr.addr->un.sun_family = AF_UNIX;
strcpy(saddr.addr->un.sun_path, str->str + 5);
if (str->len + 1 - 5 <= sizeof(saddr.addr->un.sun_path)) {
saddr.len = sizeof(saddr.addr->un);
saddr.addr = (liSockAddr*) g_slice_alloc0(saddr.len);
saddr.addr->un.sun_family = AF_UNIX;
memcpy(saddr.addr->un.sun_path, str->str + 5, str->len + 1 - 5);
}
} else
#endif
if (li_parse_ipv4(str->str, &ipv4, NULL, &port)) {
@ -677,9 +701,11 @@ liSocketAddress li_sockaddr_local_from_socket(gint fd) {
saddr.addr = (liSockAddr*) g_slice_alloc0(l);
saddr.len = l;
if (l <= sizeof(sa)) {
memcpy(saddr.addr, &sa.plain, l);
memcpy(saddr.addr, &sa, l);
} else {
getsockname(fd, (struct sockaddr*) saddr.addr, &l);
if (-1 == getsockname(fd, (struct sockaddr*) saddr.addr, &l)) {
li_sockaddr_clear(&saddr);
}
}
return saddr;
@ -697,9 +723,11 @@ liSocketAddress li_sockaddr_remote_from_socket(gint fd) {
saddr.addr = (liSockAddr*) g_slice_alloc0(l);
saddr.len = l;
if (l <= sizeof(sa)) {
memcpy(saddr.addr, &sa.plain, l);
memcpy(saddr.addr, &sa, l);
} else {
getpeername(fd, (struct sockaddr*) saddr.addr, &l);
if (-1 == getpeername(fd, (struct sockaddr*) saddr.addr, &l)) {
li_sockaddr_clear(&saddr);
}
}
return saddr;
@ -823,36 +851,6 @@ void li_string_append_int(GString *dest, gint64 v) {
dest->len = len;
}
/* http://womble.decadentplace.org.uk/readdir_r-advisory.html */
gsize li_dirent_buf_size(DIR * dirp) {
glong name_max;
gsize name_end;
# if !defined(HAVE_DIRFD)
UNUSED(dirp);
# endif
# if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
if (name_max == -1)
# if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
# else
return (gsize)(-1);
# endif
# else
# if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
# else
# error "buffer size for readdir_r cannot be determined"
# endif
# endif
name_end = (gsize)offsetof(struct dirent, d_name) + name_max + 1;
return (name_end > sizeof(struct dirent) ? name_end : sizeof(struct dirent));
}
const char *li_remove_path(const char *path) {
char *p = strrchr(path, G_DIR_SEPARATOR);
if (NULL != p && *(p) != '\0') {

View File

@ -37,20 +37,6 @@ liValue* li_value_new_list(void) {
return v;
}
static void _value_hash_free_key(gpointer data) {
g_string_free((GString*) data, TRUE);
}
static void _value_hash_free_value(gpointer data) {
li_value_free((liValue*) data);
}
GHashTable *li_value_new_hashtable(void) {
return g_hash_table_new_full(
(GHashFunc) g_string_hash, (GEqualFunc) g_string_equal,
_value_hash_free_key, _value_hash_free_value);
}
void li_value_list_append(liValue *list, liValue *item) {
LI_FORCE_ASSERT(LI_VALUE_LIST == list->type);
g_ptr_array_add(list->data.list, item);
@ -66,64 +52,11 @@ void li_value_wrap_in_list(liValue *val) {
g_ptr_array_add(val->data.list, item);
}
liValue* li_common_value_copy_(liValue* val) {
liValue *n;
if (NULL == val) return NULL;
switch (val->type) {
case LI_VALUE_NONE: return li_value_new_none();
case LI_VALUE_BOOLEAN: return li_value_new_bool(val->data.boolean);
case LI_VALUE_NUMBER: return li_value_new_number(val->data.number);
case LI_VALUE_STRING: return li_value_new_string(g_string_new_len(GSTR_LEN(val->data.string)));
/* list: we have to copy every value in the list! */
case LI_VALUE_LIST:
n = li_value_new_list();
g_ptr_array_set_size(n->data.list, val->data.list->len);
for (guint i = 0; i < val->data.list->len; i++) {
g_ptr_array_index(n->data.list, i) = li_value_copy(g_ptr_array_index(val->data.list, i));
}
return n;
}
return NULL;
}
static void _li_value_clear(liValue *val) {
memset(val, 0, sizeof(*val));
val->type = LI_VALUE_NONE;
}
void li_common_value_clear_(liValue *val) {
if (NULL == val) return;
switch (val->type) {
case LI_VALUE_NONE:
case LI_VALUE_BOOLEAN:
case LI_VALUE_NUMBER:
/* Nothing to free */
break;
case LI_VALUE_STRING:
g_string_free(val->data.string, TRUE);
break;
case LI_VALUE_LIST:
li_value_list_free(val->data.list);
break;
}
_li_value_clear(val);
}
void li_value_free(liValue* val) {
if (NULL == val) return;
li_value_clear(val);
g_slice_free(liValue, val);
}
void li_value_move(liValue *dest, liValue *src) {
LI_FORCE_ASSERT(NULL != dest && NULL != src && dest != src);
li_value_clear(dest);
*dest = *src;
_li_value_clear(src);
}
const char* li_common_valuetype_string_(liValueType type) {
switch(type) {
case LI_VALUE_NONE:
@ -140,52 +73,6 @@ const char* li_common_valuetype_string_(liValueType type) {
return "<unknown>";
}
void li_value_list_free(GPtrArray *vallist) {
if (NULL == vallist) return;
for (gsize i = 0; i < vallist->len; i++) {
li_value_free(g_ptr_array_index(vallist, i));
}
g_ptr_array_free(vallist, TRUE);
}
GString *li_common_value_to_string_(liValue *val) {
GString *str = NULL;
switch (val->type) {
case LI_VALUE_NONE:
str = g_string_new("null");
case LI_VALUE_BOOLEAN:
str = g_string_new(val->data.boolean ? "true" : "false");
break;
case LI_VALUE_NUMBER:
str = g_string_sized_new(0);
g_string_printf(str, "%" G_GINT64_FORMAT, val->data.number);
break;
case LI_VALUE_STRING:
str = g_string_new_len(CONST_STR_LEN("\""));
g_string_append_len(str, GSTR_LEN(val->data.string));
g_string_append_c(str, '"');
break;
case LI_VALUE_LIST:
str = g_string_new_len(CONST_STR_LEN("("));
if (val->data.list->len) {
GString *tmp = li_value_to_string(g_ptr_array_index(val->data.list, 0));
g_string_append(str, tmp->str);
g_string_free(tmp, TRUE);
for (guint i = 1; i < val->data.list->len; i++) {
tmp = li_value_to_string(g_ptr_array_index(val->data.list, i));
g_string_append_len(str, CONST_STR_LEN(", "));
g_string_append(str, tmp->str);
g_string_free(tmp, TRUE);
}
}
g_string_append_c(str, ')');
break;
}
return str;
}
gpointer li_common_value_extract_ptr_(liValue *val) {
gpointer ptr = NULL;

132
src/common/value_impl.c Normal file
View File

@ -0,0 +1,132 @@
/* include from angel / main;
* as some "common" functions need to call the actual implementations
* in angel / main they cannot live in "common"
*/
static void _li_value_clear(liValue *val) {
memset(val, 0, sizeof(*val));
val->type = LI_VALUE_NONE;
}
liValue* li_common_value_copy_(liValue* val) {
liValue *n;
if (NULL == val) return NULL;
switch (val->type) {
case LI_VALUE_NONE: return li_value_new_none();
case LI_VALUE_BOOLEAN: return li_value_new_bool(val->data.boolean);
case LI_VALUE_NUMBER: return li_value_new_number(val->data.number);
case LI_VALUE_STRING: return li_value_new_string(g_string_new_len(GSTR_LEN(val->data.string)));
/* list: we have to copy every value in the list! */
case LI_VALUE_LIST:
n = li_value_new_list();
g_ptr_array_set_size(n->data.list, val->data.list->len);
for (guint i = 0; i < val->data.list->len; i++) {
g_ptr_array_index(n->data.list, i) = li_value_copy(g_ptr_array_index(val->data.list, i));
}
return n;
default:
/* other cases need to be handled by li_value_copy */
break;
}
return NULL;
}
GString *li_common_value_to_string_(liValue *val) {
GString *str = NULL;
switch (val->type) {
case LI_VALUE_NONE:
str = g_string_new("null");
break;
case LI_VALUE_BOOLEAN:
str = g_string_new(val->data.boolean ? "true" : "false");
break;
case LI_VALUE_NUMBER:
str = g_string_sized_new(0);
g_string_printf(str, "%" G_GINT64_FORMAT, val->data.number);
break;
case LI_VALUE_STRING:
str = g_string_new_len(CONST_STR_LEN("\""));
g_string_append_len(str, GSTR_LEN(val->data.string));
g_string_append_c(str, '"');
break;
case LI_VALUE_LIST:
str = g_string_new_len(CONST_STR_LEN("("));
if (val->data.list->len) {
GString *tmp = li_value_to_string(g_ptr_array_index(val->data.list, 0));
g_string_append(str, tmp->str);
g_string_free(tmp, TRUE);
for (guint i = 1; i < val->data.list->len; i++) {
tmp = li_value_to_string(g_ptr_array_index(val->data.list, i));
g_string_append_len(str, CONST_STR_LEN(", "));
g_string_append(str, tmp->str);
g_string_free(tmp, TRUE);
}
}
g_string_append_c(str, ')');
break;
default:
/* other cases need to be handled by li_value_to_string */
break;
}
return str;
}
void li_value_free(liValue* val) {
if (NULL == val) return;
li_value_clear(val);
g_slice_free(liValue, val);
}
void li_value_move(liValue *dest, liValue *src) {
LI_FORCE_ASSERT(NULL != dest && NULL != src && dest != src);
li_value_clear(dest);
*dest = *src;
_li_value_clear(src);
}
static void _value_hash_free_key(gpointer data) {
g_string_free((GString*) data, TRUE);
}
static void _value_hash_free_value(gpointer data) {
li_value_free((liValue*) data);
}
GHashTable *li_value_new_hashtable(void) {
return g_hash_table_new_full(
(GHashFunc) g_string_hash, (GEqualFunc) g_string_equal,
_value_hash_free_key, _value_hash_free_value);
}
void li_value_list_free(GPtrArray *vallist) {
if (NULL == vallist) return;
for (gsize i = 0; i < vallist->len; i++) {
li_value_free(g_ptr_array_index(vallist, i));
}
g_ptr_array_free(vallist, TRUE);
}
void li_common_value_clear_(liValue *val) {
if (NULL == val) return;
switch (val->type) {
case LI_VALUE_NONE:
case LI_VALUE_BOOLEAN:
case LI_VALUE_NUMBER:
/* Nothing to free */
break;
case LI_VALUE_STRING:
g_string_free(val->data.string, TRUE);
break;
case LI_VALUE_LIST:
li_value_list_free(val->data.list);
break;
default:
/* other cases need to be handled by li_value_clear */
break;
}
_li_value_clear(val);
}

View File

@ -8,8 +8,8 @@ static void wq_cb(liEventBase *watcher, int events) {
queue->callback(queue, queue->data);
}
void li_waitqueue_init(liWaitQueue *queue, liEventLoop *loop, liWaitQueueCB callback, gdouble delay, gpointer data) {
li_event_timer_init(loop, &queue->timer, wq_cb);
void li_waitqueue_init(liWaitQueue *queue, liEventLoop *loop, const char *waitqueue_name, liWaitQueueCB callback, gdouble delay, gpointer data) {
li_event_timer_init(loop, waitqueue_name, &queue->timer, wq_cb);
queue->head = queue->tail = NULL;
queue->delay = delay;

View File

@ -23,8 +23,6 @@
#cmakedefine HAVE_SYS_UIO_H
#cmakedefine HAVE_SYS_UN_H
#cmakedefine HAVE_SYS_WAIT_H
#cmakedefine HAVE_SYS_TIME_H
#cmakedefine HAVE_TIME_H
#cmakedefine HAVE_UNISTD_H
#cmakedefine HAVE_PTHREAD_H
#cmakedefine HAVE_INET_ATON

View File

@ -162,9 +162,9 @@ void li_action_append_inplace(liAction *list, liAction *element) {
}
static void action_stack_element_release(liServer *srv, liVRequest *vr, action_stack_element *ase) {
liAction *a = ase->act;
liAction *a;
if (!ase || !a) return;
if (NULL == ase || NULL == (a = ase->act)) return;
switch (a->type) {
case LI_ACTION_TNOTHING:
@ -320,6 +320,7 @@ liHandlerResult li_action_execute(liVRequest *vr) {
break;
case LI_HANDLER_ERROR:
li_action_stack_reset(vr, as);
return res;
case LI_HANDLER_COMEBACK:
case LI_HANDLER_WAIT_FOR_EVENT:
return res;
@ -395,6 +396,7 @@ liHandlerResult li_action_execute(liVRequest *vr) {
break;
case LI_HANDLER_ERROR:
li_action_stack_reset(vr, as);
return res;
case LI_HANDLER_COMEBACK:
case LI_HANDLER_WAIT_FOR_EVENT:
return res;
@ -422,6 +424,7 @@ liHandlerResult li_action_execute(liVRequest *vr) {
break;
case LI_HANDLER_ERROR:
li_action_stack_reset(vr, as);
return res;
case LI_HANDLER_COMEBACK:
case LI_HANDLER_WAIT_FOR_EVENT:
return res;

View File

@ -7,143 +7,104 @@
/* listen to a socket */
int li_angel_fake_listen(liServer *srv, GString *str) {
guint32 ipv4;
#ifdef HAVE_IPV6
guint8 ipv6[16];
#endif
guint16 port;
liSocketAddress addr = li_sockaddr_from_string(str, 80);
liSockAddr *saddr = addr.addr;
GString *tmpstr;
int s, v;
if (NULL == saddr) {
ERROR(srv, "Invalid socket address: '%s'", str->str);
return -1;
}
tmpstr = li_sockaddr_to_string(addr, NULL, TRUE);
switch (saddr->plain.sa_family) {
#ifdef HAVE_SYS_UN_H
if (0 == strncmp(str->str, "unix:/", 6)) {
int s;
struct sockaddr_un *un;
socklen_t slen = str->len + 1 - 5 + sizeof(un->sun_family);
un = g_malloc0(slen);
un->sun_family = AF_UNIX;
strcpy(un->sun_path, str->str + 5);
if (-1 == unlink(un->sun_path)) {
case AF_UNIX:
if (-1 == unlink(saddr->un.sun_path)) {
switch (errno) {
case ENOENT:
break;
default:
ERROR(srv, "removing old socket '%s' failed: %s\n", str->str, g_strerror(errno));
g_free(un);
return -1;
goto error;
}
}
if (-1 == (s = socket(AF_UNIX, SOCK_STREAM, 0))) {
if (-1 == (s = socket(saddr->plain.sa_family, SOCK_STREAM, 0))) {
ERROR(srv, "Couldn't open socket: %s", g_strerror(errno));
g_free(un);
return -1;
goto error;
}
if (-1 == bind(s, (struct sockaddr*) un, slen)) {
if (-1 == bind(s, &saddr->plain, addr.len)) {
ERROR(srv, "Couldn't bind socket to '%s': %s", tmpstr->str, g_strerror(errno));
close(s);
ERROR(srv, "Couldn't bind socket to '%s': %s", str->str, g_strerror(errno));
g_free(un);
return -1;
goto error;
}
if (-1 == listen(s, 1000)) {
ERROR(srv, "Couldn't listen on '%s': %s", tmpstr->str, g_strerror(errno));
close(s);
ERROR(srv, "Couldn't listen on '%s': %s", str->str, g_strerror(errno));
g_free(un);
return -1;
goto error;
}
g_free(un);
DEBUG(srv, "listen to unix socket: '%s'", str->str);
return s;
} else
DEBUG(srv, "listen to unix socket: '%s'", tmpstr->str);
break;
#endif
if (li_parse_ipv4(str->str, &ipv4, NULL, &port)) {
int s, v;
struct sockaddr_in addr;
if (!port) port = 80;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ipv4;
addr.sin_port = htons(port);
if (-1 == (s = socket(AF_INET, SOCK_STREAM, 0))) {
ERROR(srv, "Couldn't open socket: %s", g_strerror(errno));
return -1;
}
v = 1;
if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v))) {
close(s);
ERROR(srv, "Couldn't setsockopt(SO_REUSEADDR): %s", g_strerror(errno));
return -1;
}
if (-1 == bind(s, (struct sockaddr*)&addr, sizeof(addr))) {
close(s);
ERROR(srv, "Couldn't bind socket to '%s': %s", inet_ntoa(addr.sin_addr), g_strerror(errno));
return -1;
}
#ifdef TCP_FASTOPEN
v = 1000;
setsockopt(s, SOL_TCP, TCP_FASTOPEN, &v, sizeof(v));
#endif
if (-1 == listen(s, 1000)) {
close(s);
ERROR(srv, "Couldn't listen on '%s': %s", inet_ntoa(addr.sin_addr), g_strerror(errno));
return -1;
}
DEBUG(srv, "listen to ipv4: '%s' port: %d", inet_ntoa(addr.sin_addr), port);
return s;
case AF_INET:
#ifdef HAVE_IPV6
} else if (li_parse_ipv6(str->str, ipv6, NULL, &port)) {
GString *ipv6_str = g_string_sized_new(0);
int s, v;
struct sockaddr_in6 addr;
li_ipv6_tostring(ipv6_str, ipv6);
if (!port) port = 80;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, ipv6, 16);
addr.sin6_port = htons(port);
if (-1 == (s = socket(AF_INET6, SOCK_STREAM, 0))) {
case AF_INET6:
#endif
if (-1 == (s = socket(saddr->plain.sa_family, SOCK_STREAM, 0))) {
ERROR(srv, "Couldn't open socket: %s", g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
goto error;
}
v = 1;
if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v))) {
close(s);
ERROR(srv, "Couldn't setsockopt(SO_REUSEADDR): %s", g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
}
if (-1 == setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v))) {
close(s);
goto error;
}
#ifdef HAVE_IPV6
if (AF_INET6 == saddr->plain.sa_family && -1 == setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v))) {
ERROR(srv, "Couldn't setsockopt(IPV6_V6ONLY): %s", g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
}
if (-1 == bind(s, (struct sockaddr*)&addr, sizeof(addr))) {
close(s);
ERROR(srv, "Couldn't bind socket to '%s': %s", ipv6_str->str, g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
goto error;
}
#endif
if (-1 == bind(s, &saddr->plain, addr.len)) {
ERROR(srv, "Couldn't bind socket to '%s': %s", tmpstr->str, g_strerror(errno));
close(s);
goto error;
}
#ifdef TCP_FASTOPEN
v = 1000;
setsockopt(s, SOL_TCP, TCP_FASTOPEN, &v, sizeof(v));
#endif
if (-1 == listen(s, 1000)) {
ERROR(srv, "Couldn't listen on '%s': %s", tmpstr->str, g_strerror(errno));
close(s);
ERROR(srv, "Couldn't listen on '%s': %s", ipv6_str->str, g_strerror(errno));
g_string_free(ipv6_str, TRUE);
return -1;
goto error;
}
DEBUG(srv, "listen to ipv6: '%s' port: %d", ipv6_str->str, port);
g_string_free(ipv6_str, TRUE);
return s;
#ifdef HAVE_IPV6
if (AF_INET6 == saddr->plain.sa_family) {
DEBUG(srv, "listen to ipv6: '%s'", tmpstr->str);
} else
#endif
} else {
ERROR(srv, "Invalid ip: '%s'", str->str);
return -1;
{
DEBUG(srv, "listen to ipv4: '%s'", tmpstr->str);
}
break;
default:
ERROR(srv, "Unknown address family for '%s'", tmpstr->str);
goto error;
}
g_string_free(tmpstr, TRUE);
li_sockaddr_clear(&addr);
return s;
error:
g_string_free(tmpstr, TRUE);
li_sockaddr_clear(&addr);
return -1;
}
/* print log messages during startup to stderr */

View File

@ -240,7 +240,7 @@ static void S_backend_pool_worker_insert_connected(liBackendWorkerPool *wpool, i
liBackendConnection_p *con = backend_connection_new(wpool);
liBackendPool_p *pool = wpool->pool;
li_event_io_init(&wpool->wrk->loop, &con->public.watcher, NULL, fd, 0);
li_event_io_init(&wpool->wrk->loop, "backend connection", &con->public.watcher, NULL, fd, 0);
li_event_set_keep_loop_alive(&con->public.watcher, FALSE);
BACKEND_THREAD_CB(new, pool, wpool->wrk, con);
@ -337,7 +337,9 @@ static void backend_con_watch_connect_cb(liEventBase *watcher, int events) {
int err;
len = sizeof(err);
#ifdef SO_ERROR
getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&err, &len);
if (-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&err, &len)) {
err = errno;
}
#else
{
char ch;
@ -384,7 +386,7 @@ static void backend_con_watch_connect_cb(liEventBase *watcher, int events) {
static void S_backend_pool_worker_insert_pending(liBackendWorkerPool *wpool, int fd) {
liBackendConnection_p *con = backend_connection_new(wpool);
li_event_io_init(&wpool->wrk->loop, &con->public.watcher, backend_con_watch_connect_cb, fd, LI_EV_READ | LI_EV_WRITE);
li_event_io_init(&wpool->wrk->loop, "backend connection", &con->public.watcher, backend_con_watch_connect_cb, fd, LI_EV_READ | LI_EV_WRITE);
li_event_set_keep_loop_alive(&con->public.watcher, FALSE);
li_event_start(&con->public.watcher);
@ -409,7 +411,7 @@ static gboolean S_backend_connection_connect(liBackendWorkerPool *wpool) {
ERROR(srv, "Couldn't open socket: %s", g_strerror(errno));
return FALSE;
}
li_fd_init(fd);
li_fd_no_block(fd);
if (-1 == connect(fd, &config->sock_addr.addr->plain, config->sock_addr.len)) {
switch (errno) {
@ -773,12 +775,12 @@ static gpointer backend_pool_worker_init(liWorker *wrk, gpointer fdata) {
if (wpool->initialized) return NULL;
li_event_async_init(&wrk->loop, &wpool->wakeup, backend_pool_worker_run);
li_event_async_init(&wrk->loop, "backend manager", &wpool->wakeup, backend_pool_worker_run);
if (idle_timeout < 1) idle_timeout = 5;
li_waitqueue_init(&wpool->idle_queue, &wrk->loop, backend_pool_worker_idle_timeout, idle_timeout, wpool);
li_waitqueue_init(&wpool->connect_queue, &wrk->loop, backend_pool_worker_connect_timeout, pool->public.config->connect_timeout, wpool);
li_waitqueue_init(&wpool->idle_queue, &wrk->loop, "backend idle queue", backend_pool_worker_idle_timeout, idle_timeout, wpool);
li_waitqueue_init(&wpool->connect_queue, &wrk->loop, "backend connect queue", backend_pool_worker_connect_timeout, pool->public.config->connect_timeout, wpool);
li_event_timer_init(&wrk->loop, &wpool->wait_queue_timer, backend_pool_wait_queue_timeout);
li_event_timer_init(&wrk->loop, "backend wait timeout", &wpool->wait_queue_timer, backend_pool_wait_queue_timeout);
li_event_set_keep_loop_alive(&wpool->wait_queue_timer, FALSE);
wpool->initialized = TRUE;

View File

@ -63,9 +63,6 @@ liHandlerResult li_chunkfile_open(liChunkFile *cf, GError **err) {
g_set_error(err, LI_CHUNK_ERROR, 0, "li_chunkfile_open: Couldn't open file '%s': %s", GSTR_SAFE_STR(cf->name), g_strerror(errno));
return LI_HANDLER_ERROR;
}
#ifdef FD_CLOEXEC
fcntl(cf->fd, F_SETFD, FD_CLOEXEC);
#endif
#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_SEQUENTIAL)
/* tell the kernel that we want to stream the file */
if (-1 == posix_fadvise(cf->fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {

View File

@ -7,6 +7,7 @@ static const liConditionValueType cond_value_hints[] = {
/* LI_COMP_REQUEST_REMOTEIP: */ LI_COND_VALUE_HINT_SOCKADDR,
/* LI_COMP_REQUEST_REMOTEPORT: */ LI_COND_VALUE_HINT_NUMBER,
/* LI_COMP_REQUEST_PATH: */ LI_COND_VALUE_HINT_STRING,
/* LI_COMP_REQUEST_RAW_PATH: */ LI_COND_VALUE_HINT_STRING,
/* LI_COMP_REQUEST_HOST: */ LI_COND_VALUE_HINT_ANY,
/* LI_COMP_REQUEST_SCHEME: */ LI_COND_VALUE_HINT_STRING,
/* LI_COMP_REQUEST_QUERY_STRING: */ LI_COND_VALUE_HINT_ANY,
@ -95,6 +96,10 @@ liHandlerResult li_condition_get_value(GString *tmpstr, liVRequest *vr, liCondit
res->match_type = LI_COND_VALUE_HINT_STRING;
res->data.str = vr->request.uri.path->str;
break;
case LI_COMP_REQUEST_RAW_PATH:
res->match_type = LI_COND_VALUE_HINT_STRING;
res->data.str = vr->request.uri.raw_path->str;
break;
case LI_COMP_REQUEST_HOST:
res->match_type = LI_COND_VALUE_HINT_STRING;
res->data.str = vr->request.uri.host->str;
@ -465,6 +470,7 @@ const char* li_cond_lvalue_to_string(liCondLValue t) {
case LI_COMP_REQUEST_REMOTEIP: return "request.remoteip";
case LI_COMP_REQUEST_REMOTEPORT: return "request.remoteport";
case LI_COMP_REQUEST_PATH: return "request.path";
case LI_COMP_REQUEST_RAW_PATH: return "request.raw_path";
case LI_COMP_REQUEST_HOST: return "request.host";
case LI_COMP_REQUEST_SCHEME: return "request.scheme";
case LI_COMP_REQUEST_QUERY_STRING: return "request.query";
@ -512,6 +518,8 @@ liCondLValue li_cond_lvalue_from_string(const gchar *str, guint len) {
return LI_COMP_REQUEST_REMOTEPORT;
else if (strncmp(c, "path", len) == 0)
return LI_COMP_REQUEST_PATH;
else if (strncmp(c, "raw_path", len) == 0)
return LI_COMP_REQUEST_RAW_PATH;
else if (strncmp(c, "host", len) == 0)
return LI_COMP_REQUEST_HOST;
else if (strncmp(c, "scheme", len) == 0)

View File

@ -400,7 +400,7 @@ GQuark li_config_error_quark(void) {
( endoffile | keyword | name | cast | single_operator | operator | integer | string );
}%%
%% write data;
%% write data noerror nofinal;
static void set_config_error(liConfigTokenizerContext *ctx, GError **error, const char *fmt, va_list ap) {
GString *msg = g_string_sized_new(127);
@ -481,6 +481,7 @@ static liConfigToken tokenizer_next(liConfigTokenizerContext *ctx, GError **erro
ctx->token_line = ctx->line;
ctx->token_line_start = ctx->line_start;
(void) config_tokenizer_en_token;
%% write init;
%% write exec;
@ -1244,7 +1245,10 @@ static gboolean p_value_list(gint *key_value_nesting, liValue **result, gboolean
if (NULL == pre_value) {
NEXT(token);
if (end == token) break;
if (TK_COMMA == token) continue;
if (TK_COMMA == token) {
parse_error(ctx, error, "unexpected ',', expected value or list end");
goto error;
}
REMEMBER(token);
if (!p_value(&sub_kv_nesting, &value, TK_ERROR, ctx, error)) goto error;
@ -1258,7 +1262,7 @@ static gboolean p_value_list(gint *key_value_nesting, liValue **result, gboolean
if (!key_value_list && TK_ASSOCICATE == token) {
key_value_list = TRUE;
if (li_value_list_len(list) > 0) {
return parse_error(ctx, error, "unexpected '=>'");
parse_error(ctx, error, "unexpected '=>', not a key-value list");
goto error;
}
}
@ -1278,11 +1282,17 @@ static gboolean p_value_list(gint *key_value_nesting, liValue **result, gboolean
li_value_list_append(pair, value);
li_value_list_append(list, pair);
value = key = NULL;
NEXT(token);
} else {
REMEMBER(token);
li_value_list_append(list, value);
value = NULL;
}
if (end == token) break;
if (TK_COMMA != token) {
parse_error(ctx, error, "expected ',' or list end");
goto error;
}
}
if (key_value_list) *key_value_nesting = 0;
@ -1372,6 +1382,7 @@ static gboolean p_value(gint *key_value_nesting, liValue **value, liConfigToken
li_value_free(v);
v = li_value_new_number(i);
}
break;
default:
parse_error(ctx, error, "cast(int) from %s not supported yet", li_value_type_string(v));
li_value_free(v);

View File

@ -165,6 +165,11 @@ static void con_iostream_close(liConnection *con) { /* force close */
LI_FORCE_ASSERT(NULL == con->con_sock.data);
}
static void con_iostream_shutdown(liConnection *con) { /* (try) regular shutdown */
if (NULL != con->con_sock.raw_out) {
con->con_sock.raw_out->out->is_closed = TRUE;
li_stream_notify(con->con_sock.raw_out);
}
if (con->con_sock.callbacks) {
con->con_sock.callbacks->finish(con, FALSE);
}
@ -701,7 +706,7 @@ liConnection* li_connection_new(liWorker *wrk) {
con->keep_alive_data.link = NULL;
con->keep_alive_data.timeout = 0;
con->keep_alive_data.max_idle = 0;
li_event_timer_init(&wrk->loop, &con->keep_alive_data.watcher, connection_keepalive_cb);
li_event_timer_init(&wrk->loop, "connection keep-alive timeout", &con->keep_alive_data.watcher, connection_keepalive_cb);
con->io_timeout_elem.data = con;
@ -840,6 +845,9 @@ static void li_connection_reset_keep_alive(liConnection *con) {
li_stream_disconnect(&con->out);
li_stream_disconnect_dest(&con->in);
con->out.out->is_closed = FALSE;
/* reset con->out (== vr->coninfo->resp) counters. con->in is reset on keep-alive "reopen" */
con->out.out->bytes_out = 0;
con->out.out->bytes_in = con->out.out->length;
memset(&con->in_chunked_decode_state, 0, sizeof(con->in_chunked_decode_state));

View File

@ -1,5 +1,6 @@
#include <lighttpd/environment.h>
#include <lighttpd/base.h>
#include <lighttpd/plugin_core.h>
#include <lighttpd/utils.h>
static void _hash_free_gstring(gpointer data) {
@ -72,3 +73,135 @@ GString* li_environment_dup_pop(liEnvironmentDup *envdup, const gchar *key, size
return sval;
}
static void add_env_var(liEnvironmentDup *envdup, liAddEnvironmentCB callback, gpointer param, const gchar *key, size_t keylen, const gchar *val, size_t valuelen) {
GString *sval;
if (NULL != (sval = li_environment_dup_pop(envdup, key, keylen))) {
callback(param, key, keylen, GSTR_LEN(sval));
} else {
callback(param, key, keylen, val, valuelen);
}
}
static void cgi_fix_header_name(GString *str) {
guint i, len = str->len;
gchar *s = str->str;
for (i = 0; i < len; i++) {
if (g_ascii_isalpha(s[i])) {
s[i] = g_ascii_toupper(s[i]);
} else if (!g_ascii_isdigit(s[i])) {
s[i] = '_';
}
}
}
void li_environment_dup2cgi(liVRequest *vr, liEnvironmentDup *envdup, liAddEnvironmentCB callback, gpointer param) {
liConInfo *coninfo = vr->coninfo;
GString *tmp = vr->wrk->tmp_str;
/* SCGI needs this as first variable */
if (vr->request.content_length >= 0) {
g_string_printf(tmp, "%" LI_GOFFSET_MODIFIER "i", vr->request.content_length);
add_env_var(envdup, callback, param, CONST_STR_LEN("CONTENT_LENGTH"), GSTR_LEN(tmp));
}
add_env_var(envdup, callback, param, CONST_STR_LEN("SERVER_SOFTWARE"), GSTR_LEN(CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_TAG).string));
add_env_var(envdup, callback, param, CONST_STR_LEN("SERVER_NAME"), GSTR_LEN(vr->request.uri.host));
add_env_var(envdup, callback, param, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
{
guint port = 0;
switch (coninfo->local_addr.addr->plain.sa_family) {
case AF_INET: port = coninfo->local_addr.addr->ipv4.sin_port; break;
#ifdef HAVE_IPV6
case AF_INET6: port = coninfo->local_addr.addr->ipv6.sin6_port; break;
#endif
}
if (port) {
g_string_printf(tmp, "%u", htons(port));
add_env_var(envdup, callback, param, CONST_STR_LEN("SERVER_PORT"), GSTR_LEN(tmp));
}
}
add_env_var(envdup, callback, param, CONST_STR_LEN("SERVER_ADDR"), GSTR_LEN(coninfo->local_addr_str));
{
guint port = 0;
switch (coninfo->remote_addr.addr->plain.sa_family) {
case AF_INET: port = coninfo->remote_addr.addr->ipv4.sin_port; break;
#ifdef HAVE_IPV6
case AF_INET6: port = coninfo->remote_addr.addr->ipv6.sin6_port; break;
#endif
}
if (port) {
g_string_printf(tmp, "%u", htons(port));
add_env_var(envdup, callback, param, CONST_STR_LEN("REMOTE_PORT"), GSTR_LEN(tmp));
}
}
add_env_var(envdup, callback, param, CONST_STR_LEN("REMOTE_ADDR"), GSTR_LEN(coninfo->remote_addr_str));
add_env_var(envdup, callback, param, CONST_STR_LEN("SCRIPT_NAME"), GSTR_LEN(vr->request.uri.path));
add_env_var(envdup, callback, param, CONST_STR_LEN("PATH_INFO"), GSTR_LEN(vr->physical.pathinfo));
if (vr->physical.pathinfo->len) {
g_string_truncate(tmp, 0);
g_string_append_len(tmp, GSTR_LEN(vr->physical.doc_root)); /* TODO: perhaps an option for alternative doc-root? */
g_string_append_len(tmp, GSTR_LEN(vr->physical.pathinfo));
add_env_var(envdup, callback, param, CONST_STR_LEN("PATH_TRANSLATED"), GSTR_LEN(tmp));
}
add_env_var(envdup, callback, param, CONST_STR_LEN("SCRIPT_FILENAME"), GSTR_LEN(vr->physical.path));
add_env_var(envdup, callback, param, CONST_STR_LEN("DOCUMENT_ROOT"), GSTR_LEN(vr->physical.doc_root));
add_env_var(envdup, callback, param, CONST_STR_LEN("REQUEST_URI"), GSTR_LEN(vr->request.uri.raw_orig_path));
if (!g_string_equal(vr->request.uri.raw_orig_path, vr->request.uri.raw_path)) {
add_env_var(envdup, callback, param, CONST_STR_LEN("REDIRECT_URI"), GSTR_LEN(vr->request.uri.raw_path));
}
add_env_var(envdup, callback, param, CONST_STR_LEN("QUERY_STRING"), GSTR_LEN(vr->request.uri.query));
add_env_var(envdup, callback, param, CONST_STR_LEN("REQUEST_METHOD"), GSTR_LEN(vr->request.http_method_str));
add_env_var(envdup, callback, param, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */
switch (vr->request.http_version) {
case LI_HTTP_VERSION_1_1:
add_env_var(envdup, callback, param, CONST_STR_LEN("SERVER_PROTOCOL"), CONST_STR_LEN("HTTP/1.1"));
break;
case LI_HTTP_VERSION_1_0:
default:
add_env_var(envdup, callback, param, CONST_STR_LEN("SERVER_PROTOCOL"), CONST_STR_LEN("HTTP/1.0"));
break;
}
if (coninfo->is_ssl) {
add_env_var(envdup, callback, param, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
add_env_var(envdup, callback, param, CONST_STR_LEN("REQUEST_SCHEME"), CONST_STR_LEN("https"));
} else {
add_env_var(envdup, callback, param, CONST_STR_LEN("REQUEST_SCHEME"), CONST_STR_LEN("http"));
}
{
GList *i;
for (i = vr->request.headers->entries.head; NULL != i; i = i->next) {
liHttpHeader *h = (liHttpHeader*) i->data;
const GString hkey = li_const_gstring(h->data->str, h->keylen);
g_string_truncate(tmp, 0);
if (!li_strncase_equal(&hkey, CONST_STR_LEN("CONTENT-TYPE"))) {
g_string_append_len(tmp, CONST_STR_LEN("HTTP_"));
}
g_string_append_len(tmp, h->data->str, h->keylen);
cgi_fix_header_name(tmp);
add_env_var(envdup, callback, param, GSTR_LEN(tmp), h->data->str + h->keylen+2, h->data->len - (h->keylen+2));
}
}
{
GHashTableIter i;
gpointer key, val;
g_hash_table_iter_init(&i, envdup->table);
while (g_hash_table_iter_next(&i, &key, &val)) {
callback(param, GSTR_LEN((GString*) key), GSTR_LEN((GString*) val));
}
}
li_environment_dup_free(envdup);
}

View File

@ -139,12 +139,9 @@ liFilter* li_filter_new(liVRequest* vr, liFilterHandlerCB handle_data, liFilterF
static void li_filter_stop(liFilter *filter) {
liVRequest *vr = filter->vr;
if (NULL == vr) return;
filter->vr = NULL;
/* remove from vr filters list */
LI_FORCE_ASSERT(vr->filters->len > 0);
LI_FORCE_ASSERT(g_ptr_array_index(vr->filters, filter->filter_ndx) == filter);
if (vr->filters->len - 1 != filter->filter_ndx) {
/* not the last filter, swap: */
liFilter *last = g_ptr_array_index(vr->filters, vr->filters->len - 1);
@ -153,8 +150,7 @@ static void li_filter_stop(liFilter *filter) {
}
g_ptr_array_set_size(vr->filters, vr->filters->len - 1);
li_stream_again(&filter->stream);
filter->vr = NULL;
li_stream_release(&filter->stream);
}

View File

@ -69,7 +69,6 @@ gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueu
li_chunk_parser_init(&ctx, in);
li_chunk_parser_prepare(&ctx);
for (;;) {
/* 0: start new chunklen, 1: reading chunklen, 2: found \r, 3: copying content, 4: found \r,
* 10: wait for \r\n\r\n, 11: wait for \n\r\n, 12: wait for \r\n, 13: wait for \n, 14: eof,
@ -78,7 +77,6 @@ gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueu
switch (state->parse_state) {
case 0:
state->cur_chunklen = -1;
li_chunk_parser_prepare(&ctx);
state->parse_state = 1;
break;
case 1:
@ -89,7 +87,7 @@ gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueu
digit = c - '0';
} else if (c >= 'a' && c <= 'f') {
digit = c - 'a' + 10;
} else if (c >= 'A' && c >= 'F') {
} else if (c >= 'A' && c <= 'F') {
digit = c - 'A' + 10;
} else if (c == '\r') {
if (state->cur_chunklen == -1) {
@ -117,10 +115,10 @@ gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueu
li_chunk_parser_done(&ctx, 1);
if (c == '\n') {
li_chunkqueue_skip(in, ctx.bytes_in);
li_chunk_parser_reset(&ctx); p = NULL;
if (state->cur_chunklen > 0) {
state->parse_state = 3;
} else {
li_chunk_parser_reset(&ctx); p = NULL;
li_chunk_parser_prepare(&ctx);
state->parse_state = 12;
}
@ -133,6 +131,7 @@ gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueu
state->cur_chunklen -= li_chunkqueue_steal_len(out, in, state->cur_chunklen);
}
if (state->cur_chunklen == 0) {
li_chunk_parser_reset(&ctx); p = NULL;
li_chunk_parser_prepare(&ctx);
read_char(c);
li_chunk_parser_done(&ctx, 1);
@ -152,6 +151,7 @@ gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueu
if (c == '\n') {
li_chunkqueue_skip(in, ctx.bytes_in);
li_chunk_parser_reset(&ctx); p = NULL;
li_chunk_parser_prepare(&ctx);
state->parse_state = 0;
} else {
state->parse_state = 20;

View File

@ -202,7 +202,7 @@ static filter_lua_state* filter_lua_state_new(liVRequest *vr, filter_lua_config
}
static void filter_lua_state_free(liVRequest *vr, filter_lua_state *state) {
liServer *srv = vr->wrk->srv;
liServer *srv = NULL != vr ? vr->wrk->srv : NULL;
lua_State *L = state->LL->L;
li_lua_lock(state->LL);
@ -225,6 +225,7 @@ static void filter_lua_free(liVRequest *vr, liFilter *f) {
}
static liHandlerResult filter_lua_handle(liVRequest *vr, liFilter *f) {
liServer *srv = NULL != vr ? vr->wrk->srv : NULL;
filter_lua_state *state = (filter_lua_state*) f->param;
lua_State *L = state->LL->L;
liHandlerResult res;
@ -235,7 +236,7 @@ static liHandlerResult filter_lua_handle(liVRequest *vr, liFilter *f) {
li_lua_push_vrequest(L, vr); /* +1 */
li_lua_push_chunkqueue(L, f->out); /* +1 */
li_lua_push_chunkqueue(L, f->in); /* +1 */
if (li_lua_call_object(NULL, vr, L, "handle", 4, 1, FALSE)) { /* -4, +1 on success */
if (li_lua_call_object(srv, vr, L, "handle", 4, 1, FALSE)) { /* -4, +1 on success */
res = LI_HANDLER_GO_ON;
if (!lua_isnil(L, -1)) {
int rc = lua_tointeger(L, -1);

View File

@ -266,7 +266,8 @@ gboolean li_http_header_tokenizer_next(liHttpHeaderTokenizer *tokenizer, GString
case '\\':
++pos;
if (pos >= len) return FALSE; /* no character after backslash */
/* fall through, append whatever comes */
/* append whatever comes */
/* fallthrough */
default:
g_string_append_c(token, str[pos]);
break;
@ -283,7 +284,8 @@ quoted:
case '\\':
++pos;
if (pos >= len) return FALSE; /* no character after backslash */
/* fall through, append whatever comes */
/* append whatever comes */
/* fallthrough */
default:
g_string_append_c(token, str[pos]);
break;

View File

@ -52,7 +52,7 @@
main := ws* "bytes" ws* "=" (ws | ",")* range ( ws* "," >range_complete (ws | ",")* range)** (ws | ",")*;
write data;
write data nofinal;
}%%
liParseHttpRangeResult li_parse_http_range_next(liParseHttpRangeState* s) {
@ -108,6 +108,7 @@ void li_parse_http_range_init(liParseHttpRangeState* s, const GString *range_str
s->last_range = FALSE;
s->found_valid_range = FALSE;
(void) http_range_parser_en_main;
%% write init;
}

View File

@ -13,7 +13,6 @@
%%{
machine li_http_request_parser;
variable cs ctx->chunk_ctx.cs;
@ -31,15 +30,19 @@
li_g_string_clear(ctx->h_value);
}
action header_value {
guint i;
/* strip whitespace */
getStringTo(fpc, ctx->h_value);
/* Remove CRLF */
if (ctx->h_value->len > 2) {
ctx->h_value->len -= 2;
ctx->h_value->str[ctx->h_value->len] = '\0';
/* g_string_truncate(ctx->h_value, ctx->h_value->len - 2); */
} else {
li_g_string_clear(ctx->h_value);
for (i = ctx->h_value->len; i-- > 0; ) {
switch (ctx->h_value->str[i]) {
case '\r':
case '\n':
case ' ':
continue;
}
break;
}
g_string_truncate(ctx->h_value, i+1);
}
action header {
li_http_header_insert(ctx->request->headers, GSTR_LEN(ctx->h_key), GSTR_LEN(ctx->h_value));
@ -124,6 +127,7 @@ void li_http_request_parser_init(liHttpRequestCtx* ctx, liRequest *req, liChunkQ
ctx->h_key = g_string_sized_new(0);
ctx->h_value = g_string_sized_new(0);
(void) li_http_request_parser_en_main;
%% write init;
}

View File

@ -14,7 +14,6 @@
%%{
machine li_http_response_parser;
variable cs ctx->chunk_ctx.cs;
@ -96,13 +95,13 @@
Quoted_String = DQUOTE ( QDText | Quoted_Pair )* DQUOTE;
HTTP_Version = (
"HTTP/1.0" %{ ctx->response->http_version = LI_HTTP_VERSION_1_0; }
| "HTTP/1.1" %{ ctx->response->http_version = LI_HTTP_VERSION_1_1; }
| "HTTP" "/" DIGIT+ "." DIGIT+ ) >{ ctx->response->http_version = LI_HTTP_VERSION_UNSET; };
"HTTP/1.0" %{ ctx->http_version = LI_HTTP_VERSION_1_0; }
| "HTTP/1.1" %{ ctx->http_version = LI_HTTP_VERSION_1_1; }
| "HTTP" "/" DIGIT+ "." DIGIT+ ) >{ ctx->http_version = LI_HTTP_VERSION_UNSET; };
#HTTP_URL = "http:" "//" Host ( ":" Port )? ( abs_path ( "?" query )? )?;
Status = (digit digit digit) >mark %status;
Response_Line = "HTTP/" digit+ "." digit+ SP Status SP (any - CTL - CR - LF)* CRLF;
Response_Line = HTTP_Version SP Status SP (any - CTL - CR - LF)* CRLF;
# Field_Content = ( TEXT+ | ( Token | Separators | Quoted_String )+ );
Field_Content = ( (OCTET - CTL - DQUOTE) | SP | HT | Quoted_String )+;
@ -128,9 +127,11 @@ void li_http_response_parser_init(liHttpResponseCtx* ctx, liResponse *req, liChu
ctx->accept_cgi = accept_cgi;
ctx->accept_nph = accept_nph;
ctx->drop_header = FALSE;
ctx->http_version = LI_HTTP_VERSION_UNSET;
ctx->h_key = g_string_sized_new(0);
ctx->h_value = g_string_sized_new(0);
(void) li_http_response_parser_en_main;
%% write init;
}

View File

@ -108,9 +108,9 @@ static void log_close_cb(liWaitQueue *wq, gpointer data) {
void li_log_init(liServer *srv) {
li_event_loop_init(&srv->logs.loop, ev_loop_new(EVFLAG_AUTO));
li_event_async_init(&srv->logs.loop, &srv->logs.watcher, log_watcher_cb);
li_event_async_init(&srv->logs.loop, "log", &srv->logs.watcher, log_watcher_cb);
srv->logs.targets = li_radixtree_new();
li_waitqueue_init(&srv->logs.close_queue, &srv->logs.loop, log_close_cb, LOG_DEFAULT_TTL, srv);
li_waitqueue_init(&srv->logs.close_queue, &srv->logs.loop, "log close queue", log_close_cb, LOG_DEFAULT_TTL, srv);
srv->logs.timestamp.format = g_string_new_len(CONST_STR_LEN(LOG_DEFAULT_TS_FORMAT));
srv->logs.timestamp.cached = g_string_sized_new(255);
srv->logs.timestamp.last_ts = 0;
@ -193,6 +193,11 @@ void li_log_context_set(liLogContext *context, liLogMap *log_map) {
gboolean li_log_write_direct(liServer *srv, liWorker *wrk, GString *path, GString *msg) {
liLogEntry *log_entry;
if (!path || path->len == 0) {
/* ignore empty log targets */
return TRUE;
}
log_entry = g_slice_new(liLogEntry);
log_entry->path = g_string_new_len(GSTR_LEN(path));
log_entry->level = 0;
@ -231,20 +236,20 @@ gboolean li_log_write(liServer *srv, liWorker *wrk, liLogContext *context, liLog
if (log_map != NULL && log_level < LI_LOG_LEVEL_COUNT) {
path = log_map->targets[log_level];
} else {
/* no log map or invalid log level */
return FALSE;
}
if (!path || path->len == 0) {
/* log-level is ignored */
return TRUE;
}
log_line = g_string_sized_new(63);
va_start(ap, fmt);
g_string_vprintf(log_line, fmt, ap);
va_end(ap);
if (!path) {
li_log_write_stderr(srv, log_line->str, TRUE);
g_string_free(log_line, TRUE);
return TRUE;
}
switch (g_atomic_int_get(&srv->state)) {
case LI_SERVER_INIT:
case LI_SERVER_LOADING:
@ -350,7 +355,10 @@ static void log_watcher_cb(liEventBase *watcher, int events) {
log = log_open(srv, log_entry->path);
if (NULL == log || -1 == log->fd) {
if (NULL == log) {
/* explicit empty target, ignore */
} else if (-1 == log->fd) {
/* failed opening log file */
li_log_write_stderr(srv, msg->str, TRUE);
goto next;
}

View File

@ -276,14 +276,22 @@ static liAction* core_docroot(liServer *srv, liWorker *wrk, liPlugin* p, liValue
}
typedef struct {
GString *prefix, *path;
GString *prefix;
liPattern *path;
} core_alias_config;
static liHandlerResult core_handle_alias(liVRequest *vr, gpointer _param, gpointer *context) {
GArray *param = _param;
guint i;
docroot_split dsplit = { vr->request.uri.host, NULL, 0 };
GMatchInfo *match_info = NULL;
UNUSED(context);
if (vr->action_stack.regex_stack->len) {
GArray *rs = vr->action_stack.regex_stack;
match_info = g_array_index(rs, liActionRegexStackElement, rs->len - 1).match_info;
}
for (i = 0; i < param->len; i++) {
core_alias_config ac = g_array_index(param, core_alias_config, i);
gsize preflen = ac.prefix->len;
@ -298,17 +306,17 @@ static liHandlerResult core_handle_alias(liVRequest *vr, gpointer _param, gpoint
/* check if url has the form "prefix" or "prefix/.*" */
if (isdir && vr->request.uri.path->str[preflen] != '\0' && vr->request.uri.path->str[preflen] != '/') continue;
g_string_truncate(vr->physical.doc_root, 0);
li_pattern_eval(vr, vr->physical.doc_root, ac.path, core_docroot_nth_cb, &dsplit, li_pattern_regex_cb, match_info);
/* prefix matched */
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "alias path: %s", ac.path->str);
VR_DEBUG(vr, "alias path: %s", vr->physical.doc_root->str);
}
g_string_truncate(vr->physical.doc_root, 0);
g_string_append_len(vr->physical.doc_root, GSTR_LEN(ac.path));
/* build physical path: docroot + uri.path */
g_string_truncate(vr->physical.path, 0);
g_string_append_len(vr->physical.path, GSTR_LEN(ac.path));
g_string_append_len(vr->physical.path, GSTR_LEN(vr->physical.doc_root));
g_string_append_len(vr->physical.path, vr->request.uri.path->str + preflen, vr->request.uri.path->len - preflen);
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
@ -332,7 +340,7 @@ static void core_alias_free(liServer *srv, gpointer _param) {
for (i = 0; i < param->len; i++) {
core_alias_config ac = g_array_index(param, core_alias_config, i);
g_string_free(ac.prefix, TRUE);
g_string_free(ac.path, TRUE);
li_pattern_free(ac.path);
}
g_array_free(param, TRUE);
}
@ -354,9 +362,12 @@ static liAction* core_alias(liServer *srv, liWorker *wrk, liPlugin* p, liValue *
if (LI_VALUE_STRING == li_value_type(vPrefix) && LI_VALUE_STRING == li_value_type(vPath)) {
core_alias_config ac;
a = g_array_sized_new(FALSE, TRUE, sizeof(core_alias_config), 1);
ac.path = li_pattern_new(srv, vPath->data.string->str);
if (NULL == ac.path) goto fail;
ac.prefix = li_value_extract_string(vPrefix);
ac.path = li_value_extract_string(vPath);
a = g_array_sized_new(FALSE, TRUE, sizeof(core_alias_config), 1);
g_array_append_val(a, ac);
}
}
@ -370,10 +381,13 @@ static liAction* core_alias(liServer *srv, liWorker *wrk, liPlugin* p, liValue *
if (!li_value_list_has_len(entry, 2)) goto fail;
vPrefix = li_value_list_at(entry, 0);
vPath = li_value_list_at(entry, 1);
if (LI_VALUE_STRING != li_value_type(vPrefix) || LI_VALUE_STRING != li_value_type(vPath)) goto fail;
ac.path = li_pattern_new(srv, vPath->data.string->str);
if (NULL == ac.path) goto fail;
ac.prefix = li_value_extract_string(vPrefix);
ac.path = li_value_extract_string(vPath);
g_array_append_val(a, ac);
LI_VALUE_END_FOREACH()
}
@ -611,9 +625,13 @@ static liHandlerResult core_handle_static(liVRequest *vr, gpointer param, gpoint
case EACCES:
vr->response.http_status = 403;
return LI_HANDLER_GO_ON;
case ENAMETOOLONG:
vr->response.http_status = 414;
return LI_HANDLER_GO_ON;
default:
VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err));
return LI_HANDLER_ERROR;
vr->response.http_status = 500;
return LI_HANDLER_GO_ON;
}
} else if (S_ISDIR(st.st_mode)) {
if (fd != -1)
@ -642,10 +660,6 @@ static liHandlerResult core_handle_static(liVRequest *vr, gpointer param, gpoint
liChunkFile *cf;
static const GString default_mime_str = { CONST_STR_LEN("application/octet-stream"), 0 };
#ifdef FD_CLOEXEC
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
if (!li_vrequest_handle_direct(vr)) {
close(fd);
return LI_HANDLER_ERROR;
@ -805,13 +819,15 @@ next_round:
VR_DEBUG(vr, "physical path: %s", vr->physical.path->str);
}
goto next_round;
case ENAMETOOLONG:
case ENOENT:
return LI_HANDLER_GO_ON;
case EACCES:
return LI_HANDLER_GO_ON;
default:
VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err));
return LI_HANDLER_ERROR;
if (!li_vrequest_handle_direct(vr)) return LI_HANDLER_ERROR;
vr->response.http_status = 500;
return LI_HANDLER_GO_ON;
}
return LI_HANDLER_GO_ON;
@ -919,12 +935,18 @@ static void core_respond_free(liServer *srv, gpointer param) {
static liHandlerResult core_handle_respond(liVRequest *vr, gpointer param, gpointer *context) {
respond_param *rp = param;
GMatchInfo *match_info = NULL;
UNUSED(context);
if (!li_vrequest_handle_direct(vr))
return LI_HANDLER_GO_ON;
if (vr->action_stack.regex_stack->len) {
GArray *rs = vr->action_stack.regex_stack;
match_info = g_array_index(rs, liActionRegexStackElement, rs->len - 1).match_info;
}
vr->response.http_status = rp->status_code;
if (!li_http_header_lookup(vr->response.headers, CONST_STR_LEN("content-type")))
@ -932,7 +954,7 @@ static liHandlerResult core_handle_respond(liVRequest *vr, gpointer param, gpoin
if (rp->pattern) {
g_string_truncate(vr->wrk->tmp_str, 0);
li_pattern_eval(vr, vr->wrk->tmp_str, rp->pattern, NULL, NULL, NULL, NULL);
li_pattern_eval(vr, vr->wrk->tmp_str, rp->pattern, NULL, NULL, li_pattern_regex_cb, match_info);
li_chunkqueue_append_mem(vr->direct_out, GSTR_LEN(vr->wrk->tmp_str));
}
@ -1774,11 +1796,17 @@ static void core_map_free(liServer *srv, gpointer param) {
static liHandlerResult core_handle_map(liVRequest *vr, gpointer param, gpointer *context) {
liValue *v;
core_map_data *md = param;
GMatchInfo *match_info = NULL;
UNUSED(context);
g_string_truncate(vr->wrk->tmp_str, 0);
li_pattern_eval(vr, vr->wrk->tmp_str, md->pattern, NULL, NULL, NULL, NULL);
if (vr->action_stack.regex_stack->len) {
GArray *rs = vr->action_stack.regex_stack;
match_info = g_array_index(rs, liActionRegexStackElement, rs->len - 1).match_info;
}
li_pattern_eval(vr, vr->wrk->tmp_str, md->pattern, NULL, NULL, li_pattern_regex_cb, match_info);
v = g_hash_table_lookup(md->hash, vr->wrk->tmp_str);
if (NULL != v) {
li_action_enter(vr, v->data.val_action.action);
@ -1998,7 +2026,7 @@ static gboolean core_register_fetch_files_static(liServer *srv, liPlugin* p, liV
out:
if (NULL != basedir) g_string_free(basedir, TRUE);
if (NULL != subfile) g_string_free(basedir, TRUE);
if (NULL != subfile) g_string_free(subfile, TRUE);
if (NULL != prefix) g_string_free(prefix, TRUE);
if (NULL != suffix) g_string_free(suffix, TRUE);
if (NULL != filename) g_string_free(filename, TRUE);
@ -2051,6 +2079,8 @@ static const liPluginOption options[] = {
{ "buffer_request_body", LI_VALUE_BOOLEAN, TRUE, NULL },
{ "strict.post_content_length", LI_VALUE_BOOLEAN, TRUE, NULL },
{ NULL, 0, 0, NULL }
};

View File

@ -1,5 +1,6 @@
#include <lighttpd/base.h>
#include <lighttpd/plugin_core.h>
#include <lighttpd/url_parser.h>
void li_request_init(liRequest *req) {
@ -292,10 +293,14 @@ gboolean li_request_validate_header(liConnection *con) {
/* content-length or chunked encoding is required for them */
if (con->mainvr->request.content_length == -1 && !transfer_encoding_chunked) {
/* content-length is missing */
VR_ERROR(con->mainvr, "%s", "POST-request, but content-length missing -> 411");
if (_CORE_OPTION(con->mainvr, LI_CORE_OPTION_STRICT_POST_CONTENT_LENGTH).boolean) {
VR_ERROR(con->mainvr, "%s", "POST-request, but content-length missing -> 411");
bad_request(con, 411); /* Length Required */
return FALSE;
bad_request(con, 411); /* Length Required */
return FALSE;
} else {
con->mainvr->request.content_length = 0;
}
}
break;
default:

View File

@ -21,8 +21,8 @@ static liServerSocket* server_socket_new(liServer *srv, int fd) {
sock->local_addr = li_sockaddr_local_from_socket(fd);
sock->refcount = 1;
li_fd_init(fd);
li_event_io_init(&srv->main_worker->loop, &sock->watcher, li_server_listen_cb, fd, LI_EV_READ);
li_fd_no_block(fd);
li_event_io_init(&srv->main_worker->loop, "server socket", &sock->watcher, li_server_listen_cb, fd, LI_EV_READ);
return sock;
}
@ -287,15 +287,15 @@ void li_server_loop_init(liServer *srv) {
loop = &srv->main_worker->loop;
li_event_async_init(loop, &srv->state_ready_watcher, state_ready_cb);
li_event_async_init(loop, "server state ready", &srv->state_ready_watcher, state_ready_cb);
li_event_timer_init(loop, &srv->srv_1sec_timer, li_server_1sec_timer);
li_event_timer_init(loop, "server 1sec", &srv->srv_1sec_timer, li_server_1sec_timer);
li_event_set_keep_loop_alive(&srv->srv_1sec_timer, FALSE);
li_event_timer_once(&srv->srv_1sec_timer, 1);
li_event_signal_init(loop, &srv->sig_w_INT, sigint_cb, SIGINT);
li_event_signal_init(loop, &srv->sig_w_TERM, sigint_cb, SIGTERM);
li_event_signal_init(loop, &srv->sig_w_PIPE, sigpipe_cb, SIGPIPE);
li_event_signal_init(loop, "server SIGINT", &srv->sig_w_INT, sigint_cb, SIGINT);
li_event_signal_init(loop, "server SIGTERM", &srv->sig_w_TERM, sigint_cb, SIGTERM);
li_event_signal_init(loop, "server SIGPIPE", &srv->sig_w_PIPE, sigpipe_cb, SIGPIPE);
li_log_init(srv);
}
@ -419,7 +419,7 @@ static void li_server_listen_cb(liEventBase *watcher, int events) {
if (l <= sizeof(sa)) {
remote_addr.addr = g_slice_alloc(l);
remote_addr.len = l;
memcpy(remote_addr.addr, &sa.plain, l);
memcpy(remote_addr.addr, &sa, l);
} else {
remote_addr = li_sockaddr_remote_from_socket(s);
}

View File

@ -25,7 +25,7 @@ liStatCache* li_stat_cache_new(liWorker *wrk, gdouble ttl) {
sc->entries = g_hash_table_new_full((GHashFunc)g_string_hash, (GEqualFunc)g_string_equal, NULL, NULL);
sc->dirlists = g_hash_table_new_full((GHashFunc)g_string_hash, (GEqualFunc)g_string_equal, NULL, NULL);
li_waitqueue_init(&sc->delete_queue, &wrk->loop, stat_cache_delete_cb, ttl, sc);
li_waitqueue_init(&sc->delete_queue, &wrk->loop, "stat cache delete queue", stat_cache_delete_cb, ttl, sc);
return sc;
}
@ -108,10 +108,7 @@ static void stat_cache_run(gpointer data) {
if (!sce->data.failed && sce->type == STAT_CACHE_ENTRY_DIR) {
/* dirlisting */
DIR *dirp;
gsize size;
struct dirent *entry;
struct dirent *result;
gint error;
liStatCacheEntryData sced;
GString *str;
@ -120,16 +117,19 @@ static void stat_cache_run(gpointer data) {
sce->data.failed = TRUE;
sce->data.err = errno;
} else {
size = li_dirent_buf_size(dirp);
LI_FORCE_ASSERT(size != (gsize)-1);
entry = g_slice_alloc(size);
sce->dirlist = g_array_sized_new(FALSE, FALSE, sizeof(liStatCacheEntryData), 32);
str = g_string_sized_new(sce->data.path->len + 64);
g_string_append_len(str, GSTR_LEN(sce->data.path));
while ((error = readdir_r(dirp, entry, &result)) == 0 && result != NULL) {
/* glibc claims modern readdir are thread-safe, and
* readdir_r has issues. no way to check readdir is actually
* safe, hope for the best.
*/
for (;;) {
errno = 0; /* readdir may not reset errno */
if (NULL == (result = readdir(dirp))) break;
/* hide "." and ".." */
if (result->d_name[0] == '.' && (result->d_name[1] == '\0' ||
(result->d_name[1] == '.' && result->d_name[2] == '\0'))) {
@ -155,13 +155,12 @@ static void stat_cache_run(gpointer data) {
g_array_append_val(sce->dirlist, sced);
}
if (error) {
if (errno) {
sce->data.failed = TRUE;
sce->data.err = error;
sce->data.err = errno;
}
g_string_free(str, TRUE);
g_slice_free1(size, entry);
closedir(dirp);
}
}

View File

@ -487,7 +487,7 @@ liIOStream* li_iostream_new(liWorker *wrk, int fd, liIOStreamCB cb, gpointer dat
iostream->write_timeout_queue = NULL;
li_event_io_init(&wrk->loop, &iostream->io_watcher, iostream_io_cb, fd, LI_EV_READ);
li_event_io_init(&wrk->loop, "iostream", &iostream->io_watcher, iostream_io_cb, fd, LI_EV_READ);
iostream->in_closed = iostream->out_closed = iostream->can_read = FALSE;
iostream->can_write = TRUE;
@ -506,6 +506,7 @@ void li_iostream_acquire(liIOStream* iostream) {
}
void li_iostream_release(liIOStream* iostream) {
if (iostream == NULL) return;
li_stream_release(&iostream->stream_in);
li_stream_release(&iostream->stream_out);
}
@ -526,8 +527,6 @@ int li_iostream_reset(liIOStream *iostream) {
li_stream_disconnect(&iostream->stream_out);
li_stream_disconnect_dest(&iostream->stream_in);
li_iostream_release(iostream);
return fd;
}

View File

@ -7,7 +7,8 @@ struct liStreamHttpResponse {
liStream stream;
liVRequest *vr;
gboolean response_headers_finished, transfer_encoding_chunked;
gboolean keepalive, response_headers_finished, transfer_encoding_chunked, wait_for_close;
goffset content_length;
liFilterChunkedDecodeState chunked_decode_state;
};
@ -16,6 +17,9 @@ static void check_response_header(liStreamHttpResponse* shr) {
GList *l;
shr->transfer_encoding_chunked = FALSE;
/* if protocol doesn't support keep-alive just wait for stream end */
shr->wait_for_close = !shr->keepalive;
shr->content_length = -1;
/* Transfer-Encoding: chunked */
l = li_http_header_find_first(resp->headers, CONST_STR_LEN("transfer-encoding"));
@ -65,8 +69,8 @@ static void check_response_header(liStreamHttpResponse* shr) {
token = g_string_sized_new(15);
li_http_header_tokenizer_start(&header_tokenizer, resp->headers, CONST_STR_LEN("Connection"));
while (li_http_header_tokenizer_next(&header_tokenizer, token)) {
VR_ERROR(shr->vr, "Parsing header '%s'", ((liHttpHeader*)header_tokenizer.cur->data)->data->str);
VR_ERROR(shr->vr, "Connection token '%s'", token->str);
VR_DEBUG(shr->vr, "Parsing header '%s'", ((liHttpHeader*)header_tokenizer.cur->data)->data->str);
VR_DEBUG(shr->vr, "Connection token '%s'", token->str);
if (0 == g_ascii_strcasecmp(token->str, "Upgrade")) {
have_connection_upgrade = TRUE;
break;
@ -78,12 +82,86 @@ static void check_response_header(liStreamHttpResponse* shr) {
li_vrequest_error(shr->vr);
return;
}
shr->wait_for_close = TRUE; /* forward all data until stream closes */
shr->response_headers_finished = TRUE;
shr->vr->backend_drain->out->is_closed = FALSE;
li_vrequest_connection_upgrade(shr->vr, shr->vr->backend_drain, &shr->stream);
{
/* li_vrequest_connection_upgrade releases vr->backend_drain; keep our own reference */
liStream *backend_drain = shr->vr->backend_drain;
shr->vr->backend_drain = NULL;
li_vrequest_connection_upgrade(shr->vr, backend_drain, &shr->stream);
li_stream_release(backend_drain);
}
return;
}
if (!shr->transfer_encoding_chunked && shr->keepalive) {
/**
* if protocol has HTTP "keepalive" concept and encoding isn't chunked,
* we need to check for content-length or "connection: close" indications.
* otherwise we won't know when the response is done
*/
liHttpHeader *hh;
switch (shr->parse_response_ctx.http_version) {
case LI_HTTP_VERSION_1_0:
if (!li_http_header_is(shr->vr->response.headers, CONST_STR_LEN("connection"), CONST_STR_LEN("keep-alive")))
shr->wait_for_close = TRUE;
break;
case LI_HTTP_VERSION_1_1:
if (li_http_header_is(shr->vr->response.headers, CONST_STR_LEN("connection"), CONST_STR_LEN("close")))
shr->wait_for_close = TRUE;
break;
case LI_HTTP_VERSION_UNSET:
break;
}
/* content-length */
hh = li_http_header_lookup(shr->vr->response.headers, CONST_STR_LEN("content-length"));
if (hh) {
const gchar *val = LI_HEADER_VALUE(hh);
gint64 r;
char *err;
r = g_ascii_strtoll(val, &err, 10);
if (*err != '\0') {
VR_ERROR(shr->vr, "Backend response: content-length is not a number: %s", err);
li_vrequest_error(shr->vr);
return;
}
/**
* negative content-length is not supported
* and is a bad request
*/
if (r < 0) {
VR_ERROR(shr->vr, "%s", "Backend response: content-length is negative");
li_vrequest_error(shr->vr);
return;
}
/**
* check if we had a over- or underrun in the string conversion
*/
if (r == G_MININT64 || r == G_MAXINT64) {
if (errno == ERANGE) {
VR_ERROR(shr->vr, "%s", "Backend response: content-length overflow");
li_vrequest_error(shr->vr);
return;
}
}
shr->content_length = r;
shr->wait_for_close = FALSE;
}
if (!shr->wait_for_close && shr->content_length < 0) {
VR_ERROR(shr->vr, "%s", "Backend: need chunked transfer-encoding or content-length for keepalive connections");
li_vrequest_error(shr->vr);
return;
}
}
shr->response_headers_finished = TRUE;
li_vrequest_indirect_headers_ready(shr->vr);
@ -122,13 +200,23 @@ static void stream_http_response_data(liStreamHttpResponse* shr) {
} else {
li_stream_reset(&shr->stream);
}
} else if (shr->stream.source->out->is_closed) {
li_stream_disconnect(&shr->stream);
}
} else if (shr->wait_for_close) {
li_chunkqueue_steal_all(shr->stream.out, shr->stream.source->out);
if (shr->stream.source->out->is_closed) {
shr->stream.out->is_closed = TRUE;
li_stream_disconnect(&shr->stream);
}
} else {
li_chunkqueue_steal_all(shr->stream.out, shr->stream.source->out);
if (shr->stream.source->out->is_closed) {
g_assert(shr->content_length >= 0);
if (shr->content_length > 0) {
goffset moved;
moved = li_chunkqueue_steal_len(shr->stream.out, shr->stream.source->out, shr->content_length);
shr->content_length -= moved;
}
if (shr->content_length == 0) {
shr->stream.out->is_closed = TRUE;
li_stream_disconnect(&shr->stream);
}
@ -164,8 +252,9 @@ static void stream_http_response_cb(liStream *stream, liStreamEvent event) {
}
}
LI_API liStream* li_stream_http_response_handle(liStream *http_in, liVRequest *vr, gboolean accept_cgi, gboolean accept_nph) {
LI_API liStream* li_stream_http_response_handle(liStream *http_in, liVRequest *vr, gboolean accept_cgi, gboolean accept_nph, gboolean keepalive) {
liStreamHttpResponse *shr = g_slice_new0(liStreamHttpResponse);
shr->keepalive = keepalive;
shr->response_headers_finished = FALSE;
shr->vr = vr;
li_stream_init(&shr->stream, &vr->wrk->loop, stream_http_response_cb);

View File

@ -19,7 +19,6 @@ void li_stream_simple_socket_close(liIOStream *stream, gboolean aborted) {
}
if (aborted || stream->in_closed) {
li_iostream_acquire(stream);
fd = li_iostream_reset(stream);
if (-1 != fd) {
shutdown(fd, SHUT_RDWR);
@ -189,7 +188,7 @@ void li_stream_simple_socket_io_cb_with_context(liIOStream *stream, liIOStreamEv
void li_stream_simple_socket_flush(liIOStream *stream) {
int val = 1;
int fd = fd = li_event_io_fd(&stream->io_watcher);
int fd = li_event_io_fd(&stream->io_watcher);
if (-1 != fd) {
/* setting TCP_NODELAY should flush the socket. if it fails it probably isn't a TCP socket,
* so no need to disable TCP_NODELAY */

View File

@ -77,9 +77,10 @@
URI = (scheme >mark %save_scheme) "://" (authority >mark %save_authority) URI_path;
parse_URI := URI | ("*" >mark %save_path) | URI_path;
parse_URI_path := URI_path;
parse_Hostname := (host >mark_host %save_host) ( ":" port )?;
write data;
write data noerror;
}%%
gboolean li_parse_raw_url(liRequestUri *uri) {
@ -90,6 +91,7 @@ gboolean li_parse_raw_url(liRequestUri *uri) {
p = uri->raw->str;
eof = pe = uri->raw->str + uri->raw->len;
(void) url_parser_start;
%% write init nocs;
cs = url_parser_en_parse_URI;
@ -98,6 +100,32 @@ gboolean li_parse_raw_url(liRequestUri *uri) {
return (cs >= url_parser_first_final);
}
gboolean li_parse_raw_path(liRequestUri *uri, GString *input) {
const char *p, *pe, *eof;
const char *mark = NULL, *host_mark = NULL;
int cs;
p = input->str;
eof = pe = input->str + input->len;
g_string_truncate(uri->path, 0);
g_string_truncate(uri->raw_path, 0);
g_string_truncate(uri->query, 0);
(void) url_parser_start;
%% write init nocs;
cs = url_parser_en_parse_URI_path;
%% write exec;
if (cs >= url_parser_first_final) {
li_url_decode(uri->path);
li_path_simplify(uri->path);
}
return (cs >= url_parser_first_final);
}
gboolean li_parse_hostname(liRequestUri *uri) {
const char *p, *pe, *eof;
const char *mark = NULL, *host_mark = NULL;
@ -107,6 +135,7 @@ gboolean li_parse_hostname(liRequestUri *uri) {
p = uri->authority->str;
eof = pe = uri->authority->str + uri->authority->len;
(void) url_parser_start;
%% write init nocs;
cs = url_parser_en_parse_Hostname;

View File

@ -1,5 +1,7 @@
#include <lighttpd/base.h>
#include "../common/value_impl.c"
liValue* li_value_new_action(liServer *srv, liAction *a) {
liValue *v = g_slice_new0(liValue);
v->data.val_action.srv = srv;
@ -35,11 +37,6 @@ liValue* li_value_copy(liValue* val) {
return NULL;
}
static void _li_value_clear(liValue *val) {
memset(val, 0, sizeof(*val));
val->type = LI_VALUE_NONE;
}
void li_value_clear(liValue *val) {
if (NULL == val) return;

View File

@ -1,5 +1,6 @@
#include <lighttpd/base.h>
#include <lighttpd/encoding.h>
#include <lighttpd/plugin_core.h>
#include <lighttpd/filter_buffer_on_disk.h>
@ -53,9 +54,9 @@ liVRequest* li_vrequest_new(liWorker *wrk, liConInfo *coninfo) {
void li_vrequest_free(liVRequest* vr) {
liServer *srv = vr->wrk->srv;
li_stream_safe_reset_and_release(&vr->backend_drain);
vr->direct_out = NULL;
li_stream_safe_reset_and_release(&vr->backend_source);
li_stream_safe_reset_and_release(&vr->backend_drain);
li_filter_buffer_on_disk_stop(vr->in_buffer_on_disk_stream);
li_stream_safe_reset_and_release(&vr->in_buffer_on_disk_stream);
@ -102,21 +103,9 @@ void li_vrequest_free(liVRequest* vr) {
void li_vrequest_reset(liVRequest *vr, gboolean keepalive) {
liServer *srv = vr->wrk->srv;
if (NULL != vr->backend_drain) {
li_stream_disconnect(vr->backend_drain);
li_stream_release(vr->backend_drain);
vr->backend_drain = NULL;
}
if (NULL != vr->backend_source) {
if (NULL == vr->backend_source->dest) {
/* wasn't connected: disconnect source */
li_stream_disconnect(vr->backend_source);
}
li_stream_disconnect_dest(vr->backend_source);
li_stream_release(vr->backend_source);
vr->backend_source = NULL;
vr->direct_out = NULL;
}
vr->direct_out = NULL;
li_stream_safe_reset_and_release(&vr->backend_source);
li_stream_safe_reset_and_release(&vr->backend_drain);
li_filter_buffer_on_disk_stop(vr->in_buffer_on_disk_stream);
li_stream_safe_reset_and_release(&vr->in_buffer_on_disk_stream);
@ -430,7 +419,7 @@ static liHandlerResult vrequest_do_handle_actions(liVRequest *vr) {
case LI_HANDLER_GO_ON:
if (vr->state == LI_VRS_HANDLE_REQUEST_HEADERS) {
/* request not handled */
li_vrequest_handle_direct(vr);
LI_FORCE_ASSERT(li_vrequest_handle_direct(vr));
if (vr->request.http_method == LI_HTTP_METHOD_OPTIONS) {
vr->response.http_status = 200;
li_http_header_append(vr->response.headers, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));
@ -575,7 +564,7 @@ gboolean li_vrequest_redirect_directory(liVRequest *vr) {
} else {
g_string_append_len(uri, GSTR_LEN(vr->coninfo->local_addr_str));
}
g_string_append_len(uri, GSTR_LEN(vr->request.uri.raw_orig_path));
li_string_encode_append(vr->request.uri.path->str, uri, LI_ENCODING_URI);
g_string_append_c(uri, '/');
if (vr->request.uri.query->len) {
g_string_append_c(uri, '?');

View File

@ -285,7 +285,7 @@ liWorker* li_worker_new(liServer *srv, struct ev_loop *loop) {
li_lua_init(&wrk->LL, srv, wrk);
g_queue_init(&wrk->keep_alive_queue);
li_event_timer_init(&wrk->loop, &wrk->keep_alive_timer, worker_keepalive_cb);
li_event_timer_init(&wrk->loop, "worker connection keep-alive", &wrk->keep_alive_timer, worker_keepalive_cb);
wrk->connections_active = 0;
wrk->connections = g_array_new(FALSE, TRUE, sizeof(liConnection*));
@ -307,27 +307,27 @@ liWorker* li_worker_new(liServer *srv, struct ev_loop *loop) {
g_array_index(wrk->timestamps_local, liWorkerTS, i).str = g_string_sized_new(255);
}
li_event_prepare_init(&wrk->loop, &wrk->loop_prepare, li_worker_prepare_cb);
li_event_async_init(&wrk->loop, &wrk->worker_stop_watcher, li_worker_stop_cb);
li_event_async_init(&wrk->loop, &wrk->worker_stopping_watcher, li_worker_stopping_cb);
li_event_async_init(&wrk->loop, &wrk->worker_exit_watcher, li_worker_exit_cb);
li_event_async_init(&wrk->loop, &wrk->worker_suspend_watcher, li_worker_suspend_cb);
li_event_prepare_init(&wrk->loop, "worker flush logs", &wrk->loop_prepare, li_worker_prepare_cb);
li_event_async_init(&wrk->loop, "worker stop", &wrk->worker_stop_watcher, li_worker_stop_cb);
li_event_async_init(&wrk->loop, "worker stopping", &wrk->worker_stopping_watcher, li_worker_stopping_cb);
li_event_async_init(&wrk->loop, "worker exit", &wrk->worker_exit_watcher, li_worker_exit_cb);
li_event_async_init(&wrk->loop, "worker suspend", &wrk->worker_suspend_watcher, li_worker_suspend_cb);
li_event_async_init(&wrk->loop, &wrk->new_con_watcher, li_worker_new_con_cb);
li_event_async_init(&wrk->loop, "worker new connection", &wrk->new_con_watcher, li_worker_new_con_cb);
wrk->new_con_queue = g_async_queue_new();
li_event_timer_init(&wrk->loop, &wrk->stats_watcher, worker_stats_watcher_cb);
li_event_timer_init(&wrk->loop, "worker stats update", &wrk->stats_watcher, worker_stats_watcher_cb);
li_event_set_keep_loop_alive(&wrk->stats_watcher, FALSE);
li_event_timer_once(&wrk->stats_watcher, 1);
li_event_async_init(&wrk->loop, &wrk->collect_watcher, li_collect_watcher_cb);
li_event_async_init(&wrk->loop, "worker collect", &wrk->collect_watcher, li_collect_watcher_cb);
wrk->collect_queue = g_async_queue_new();
/* io timeout timer */
li_waitqueue_init(&wrk->io_timeout_queue, &wrk->loop, worker_io_timeout_cb, srv->io_timeout, wrk);
li_waitqueue_init(&wrk->io_timeout_queue, &wrk->loop, "io timeout queue", worker_io_timeout_cb, srv->io_timeout, wrk);
/* throttling */
li_waitqueue_init(&wrk->throttle_queue, &wrk->loop, li_throttle_waitqueue_cb, ((gdouble)LI_THROTTLE_GRANULARITY) / 1000, wrk);
li_waitqueue_init(&wrk->throttle_queue, &wrk->loop, "throttle queue", li_throttle_waitqueue_cb, ((gdouble)LI_THROTTLE_GRANULARITY) / 1000, wrk);
wrk->tasklets = li_tasklet_pool_new(&wrk->loop, srv->tasklet_pool_threads);

Some files were not shown because too many files have changed in this diff Show More