diff --git a/CMakeLists.txt b/CMakeLists.txt
index c8e933345d54c4739ae1c6ae670c3bc7790f7b2b..0a206c37a8c68b1738dfaa43b358319f02d38bcf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -110,6 +110,7 @@ option(LWS_WITH_GCOV "Build with gcc gcov coverage instrumentation" OFF)
 option(LWS_WITH_EXPORT_LWSTARGETS "Export libwebsockets CMake targets.  Disable if they conflict with an outer cmake project." ON)
 option(LWS_REPRODUCIBLE "Build libwebsockets reproducible. It removes the build user and hostname from the build" ON)
 option(LWS_WITH_MINIMAL_EXAMPLES "Also build the normally standalone minimal examples, for QA" OFF)
+option(LWS_WITH_LWSAC "lwsac Chunk Allocation api" ON)
 #
 # End of user settings
 #
@@ -149,6 +150,10 @@ if (WIN32 OR LWS_WITH_ESP32)
 	set(LWS_UNIX_SOCK 0)
 endif()
 
+if (LWS_WITH_ESP32)
+	set(LWS_WITH_LWSAC 0)
+endif()
+
 project(libwebsockets C)
 
 set(PACKAGE "libwebsockets")
@@ -875,6 +880,12 @@ if (LWS_WITH_PEER_LIMITS)
 		lib/misc/peer-limits.c)
 endif()
 
+if (LWS_WITH_LWSAC)
+	list(APPEND SOURCES
+		lib/misc/lwsac/lwsac.c
+		lib/misc/lwsac/cached-file.c)
+endif()
+
 if (NOT LWS_WITHOUT_CLIENT)
 	list(APPEND SOURCES
 		lib/core/connect.c
diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in
index 6c6d1e3ba4dbd5ab426abccbfc092e2af03a3a53..343ec95cb7daa6e34bc138657187bc3310741ae8 100644
--- a/cmake/lws_config.h.in
+++ b/cmake/lws_config.h.in
@@ -15,6 +15,8 @@
 #cmakedefine LWS_ROLE_CGI
 #cmakedefine LWS_ROLE_DBUS
 
+#cmakedefine LWS_WITH_LWSAC
+
 /* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
  * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
 #cmakedefine USE_WOLFSSL
diff --git a/doc-assets/lwsac.svg b/doc-assets/lwsac.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1a8ba874d6ad2ca7680fe30673fbfaaec48327ed
--- /dev/null
+++ b/doc-assets/lwsac.svg
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="187.8mm" height="80.752mm" version="1.1" viewBox="0 0 187.79663 80.752144" xmlns="http://www.w3.org/2000/svg">
+	<defs>
+		<filter id="a" x="-.014418" y="-.034864" width="1.0288" height="1.0697" color-interpolation-filters="sRGB">
+			<feGaussianBlur stdDeviation="1.5466839"/>
+		</filter>
+	</defs>
+	<g transform="translate(-161.61 156.88)">
+		<g>
+			<g stroke="#666" stroke-linejoin="round">
+				<rect transform="matrix(.709 0 0 .709 167.83 -486.38)" x="-5.0609" y="468.46" width="257.45" height="106.47" ry="0" filter="url(#a)" stroke-width=".24551"/>
+				<rect x="163.71" y="-154.85" width="182.53" height="75.489" ry="0" fill="#f2f2f2" stroke-width=".17406"/>
+				<rect x="171.7" y="-140.36" width="30.952" height="50.082" fill="#808080" stroke-width=".40058"/>
+				<rect x="172.68" y="-139.06" width="28.784" height="6.4996" fill="#a00" stroke-width=".11698"/>
+				<rect x="172.74" y="-131.83" width="28.784" height="40.39" fill="#c4c8b7" stroke-width=".11698"/>
+			</g>
+			<text x="181.33067" y="-136.13423" fill="#ffffff" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="181.33067" y="-136.13423" fill="#ffffff" font-family="'Open Sans'" font-size="2.8222px" stroke-width=".39677">struct lwsac</tspan></text>
+			<text x="184.53554" y="-92.850395" fill="#000000" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="184.53554" y="-92.850395" font-family="'Open Sans'" stroke-width=".39677">allocated area</tspan></text>
+			<path d="m206.96-138.6 1.5352 0.74171-1.4119 0.76078-0.0812-0.34638-6.239-0.0174c0.0635-0.40246-0.0214-0.51522 0.0725-0.82588l6.136 5e-3z" fill="#2a7fff"/>
+			<rect x="172" y="-150.67" width="26.156" height="4.4025" fill="#500" stroke="#666" stroke-linejoin="round" stroke-width=".091779"/>
+			<text x="185.17738" y="-147.67433" fill="#ffffff" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="185.17738" y="-147.67433" fill="#ffffff" font-family="'Open Sans'" font-size="2.8222px" stroke-width=".39677">struct lwsac *head</tspan></text>
+			<text x="198.1916" y="-137.38612" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="198.1916" y="-137.38612">next</tspan><tspan x="198.1916" y="-135.51024">head</tspan><tspan x="198.1916" y="-133.63435">curr</tspan></text>
+			<path d="m174.55-138.71-0.74171 2.0619-0.76078-1.8964 0.34638-0.10906 0.0174-8.3796c0.40246 0.0853 0.51522-0.0288 0.82587 0.0974l-5e-3 8.2413z" fill="#2a7fff"/>
+			<text x="188.84824" y="-135.46999" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="188.84824" y="-135.46999" text-align="start">ofs</tspan><tspan x="188.84824" y="-133.5941" text-align="start">alloc_size</tspan></text>
+			<path d="m195.91-144.71-0.7417-2.0619-0.76078 1.8964 0.34637 0.10906-0.0412 9.1651c0.24984 0.0323 0.88518-0.0192 1.5658-0.0156 0.0711-0.0968 0.0691-0.46205 0.015-0.82619-0.45397 2e-3 -0.78488-5e-3 -0.69634-0.0411l-5e-3 -8.2413z" fill="#2a7fff"/>
+		</g>
+		<path d="m201.26-131.89-2.3876 2.1223-0.13265 35.947 2.7855 2.255" fill="none" stroke="#000" stroke-dasharray="0.5627649, 0.1875883" stroke-width=".18759"/>
+		<path d="m191.97-133.09 6.8975 7.6934" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<path d="m174.12-132.51-1.5352 0.7417 1.4119 0.76078 0.0812-0.34638 2.127 0.0157c-0.0635-0.40245 0.0214-0.54838-0.0725-0.85903l-2.024 5e-3z"/>
+		<path d="m176.09-131.63 5.7037 0.0664 6.2343-3.913 0.66323-0.0995" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<g>
+			<rect x="220.5" y="-140.05" width="30.952" height="50.082" fill="#808080" stroke="#666" stroke-linejoin="round" stroke-width=".40058"/>
+			<rect x="221.48" y="-138.74" width="28.784" height="6.4996" fill="#a00" stroke="#666" stroke-linejoin="round" stroke-width=".11698"/>
+			<text x="226.27528" y="-118.77273" fill="#000000" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="226.27528" y="-118.77273" font-family="'Open Sans'" stroke-width=".18759">ptr aligned</tspan></text>
+			<rect x="221.54" y="-131.51" width="28.784" height="40.39" fill="#c4c8b7" stroke="#666" stroke-linejoin="round" stroke-width=".11698"/>
+			<text x="230.13133" y="-135.82219" fill="#ffffff" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="230.13133" y="-135.82219" fill="#ffffff" font-family="'Open Sans'" font-size="2.8222px" stroke-width=".39677">struct lwsac</tspan></text>
+			<text x="233.3362" y="-92.538383" fill="#000000" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="233.3362" y="-92.538383" font-family="'Open Sans'" stroke-width=".39677">allocated area</tspan></text>
+			<path d="m255.76-138.29 1.5352 0.74171-1.4119 0.76078-0.0812-0.34638-6.239-0.0174c0.0635-0.40246-0.0214-0.51522 0.0725-0.82588l6.136 5e-3z" fill="#2a7fff"/>
+			<rect x="220.8" y="-150.36" width="26.156" height="4.4025" fill="#500" stroke="#666" stroke-linejoin="round" stroke-width=".091779"/>
+			<text x="233.97806" y="-147.36229" fill="#ffffff" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="233.97806" y="-147.36229" fill="#ffffff" font-family="'Open Sans'" font-size="2.8222px" stroke-width=".39677">struct lwsac *head</tspan></text>
+			<text x="246.99228" y="-137.07408" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="246.99228" y="-137.07408">next</tspan><tspan x="246.99228" y="-135.1982">head</tspan><tspan x="246.99228" y="-133.32231">curr</tspan></text>
+			<path d="m223.35-138.4-0.74171 2.0619-0.76078-1.8964 0.34638-0.10905 0.0174-8.3796c0.40246 0.0853 0.51522-0.0288 0.82588 0.0974l-5e-3 8.2413z" fill="#2a7fff"/>
+			<text x="237.64891" y="-135.15791" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="237.64891" y="-135.15791" text-align="start">ofs</tspan><tspan x="237.64891" y="-133.28203" text-align="start">alloc_size</tspan></text>
+			<path d="m244.71-144.39-0.74171-2.0619-0.76078 1.8964 0.34638 0.10906-0.0412 9.1651c0.24984 0.0323 0.88518-0.0192 1.5658-0.0156 0.0711-0.0968 0.0691-0.46205 0.015-0.82618-0.45397 2e-3 -0.78488-5e-3 -0.69634-0.0411l-5e-3 -8.2413z" fill="#2a7fff"/>
+			<rect x="221.98" y="-130.85" width="27.712" height="7.4313" fill="#ff8080" stroke="#666" stroke-linejoin="round" stroke-width=".14463"/>
+			<rect x="221.97" y="-121.59" width="27.712" height="7.4313" fill="#ff8080" stroke="#666" stroke-linejoin="round" stroke-width=".14463"/>
+		</g>
+		<path d="m250.06-131.58-2.3876 2.1223-0.13265 35.947 2.7855 2.255" fill="none" stroke="#000" stroke-dasharray="0.56276491, 0.1875883" stroke-width=".18759"/>
+		<path d="m240.78-132.77 6.8975 7.6934" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<text x="235.9016" y="-126.22051" fill="#ffffff" font-family="'Open Sans'" font-size="2.5582px" letter-spacing="0px" stroke-width=".31977" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="235.9016" y="-126.22051" fill="#ffffff" font-family="'Open Sans'" stroke-width=".31977">lwsac_use area</tspan></text>
+		<path d="m223.49-112.72-1.5352 0.7417 1.4119 0.76079 0.0812-0.34638 2.127 0.0157c-0.0635-0.40245 0.0214-0.54838-0.0725-0.85903l-2.024 5e-3z"/>
+		<path d="m225.29-112.13 8.31 0.0664 3.0298-2.9076 0.20032-20.194 0.66323-0.0995" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<g>
+			<text x="177.42035" y="-129.76131" fill="#000000" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="177.42035" y="-129.76131" font-family="'Open Sans'" stroke-width=".18759">ptr aligned</tspan></text>
+			<text x="235.79298" y="-122.17254" fill="#000000" font-family="'Open Sans'" font-size="1.0364px" letter-spacing="0px" stroke-width=".12956" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="235.79298" y="-122.17254" font-family="'Open Sans'" stroke-width=".12956">alignment padding</tspan></text>
+			<path d="m220.64-131.26 1.5352 0.7417-1.4119 0.76079-0.0812-0.34638-2.127 0.0157c0.0635-0.40246-0.0214-0.54838 0.0725-0.85903l2.024 5e-3z"/>
+			<g font-family="'Open Sans'" letter-spacing="0px" text-anchor="middle" word-spacing="0px">
+				<text x="235.89856" y="-116.96035" fill="#ffffff" font-size="2.5582px" stroke-width=".31977" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="235.89856" y="-116.96035" fill="#ffffff" font-family="'Open Sans'" stroke-width=".31977">lwsac_use area</tspan></text>
+				<text x="226.27226" y="-109.51257" fill="#000000" font-size="1.5007px" stroke-width=".18759" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="226.27226" y="-109.51257" font-family="'Open Sans'" stroke-width=".18759">ptr aligned</tspan></text>
+				<text x="235.78995" y="-112.91241" fill="#000000" font-size="1.0364px" stroke-width=".12956" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="235.78995" y="-112.91241" font-family="'Open Sans'" stroke-width=".12956">alignment padding</tspan></text>
+			</g>
+			<path d="m220.64-122 1.5352 0.7417-1.4119 0.76079-0.0812-0.34638-2.127 0.0157c0.0635-0.40245-0.0214-0.54838 0.0725-0.85903l2.024 5e-3z"/>
+			<rect x="268.33" y="-140.45" width="30.952" height="50.082" fill="#808080" stroke="#666" stroke-linejoin="round" stroke-width=".40058"/>
+			<rect x="269.31" y="-139.14" width="28.784" height="6.4996" fill="#a00" stroke="#666" stroke-linejoin="round" stroke-width=".11698"/>
+			<text x="274.10483" y="-119.17068" fill="#000000" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="274.10483" y="-119.17068" font-family="'Open Sans'" stroke-width=".18759">ptr aligned</tspan></text>
+			<rect x="269.37" y="-131.91" width="28.784" height="40.39" fill="#c4c8b7" stroke="#666" stroke-linejoin="round" stroke-width=".11698"/>
+			<text x="277.96088" y="-136.22011" fill="#ffffff" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="277.96088" y="-136.22011" fill="#ffffff" font-family="'Open Sans'" font-size="2.8222px" stroke-width=".39677">struct lwsac</tspan></text>
+			<text x="281.16574" y="-92.936333" fill="#000000" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="281.16574" y="-92.936333" font-family="'Open Sans'" stroke-width=".39677">allocated area</tspan></text>
+			<rect x="302.47" y="-140.62" width="30.952" height="50.082" fill="#808080" stroke="#666" stroke-linejoin="round" stroke-width=".40058"/>
+			<rect x="303.45" y="-139.31" width="28.784" height="6.4996" fill="#a00" stroke="#666" stroke-linejoin="round" stroke-width=".11698"/>
+			<path d="m303.59-138.69 1.5352 0.7417-1.4119 0.76078-0.0812-0.34638-6.239-0.0174c0.0635-0.40245-0.0214-0.51522 0.0725-0.82587l6.136 5e-3z" fill="#2a7fff"/>
+			<rect x="268.63" y="-150.76" width="26.156" height="4.4025" fill="#500" stroke="#666" stroke-linejoin="round" stroke-width=".091779"/>
+			<text x="281.80762" y="-147.76024" fill="#ffffff" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="281.80762" y="-147.76024" fill="#ffffff" font-family="'Open Sans'" font-size="2.8222px" stroke-width=".39677">struct lwsac *head</tspan></text>
+			<text x="294.82181" y="-137.47203" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="294.82181" y="-137.47203">next</tspan><tspan x="294.82181" y="-135.59615">head</tspan><tspan x="294.82181" y="-133.72026">curr</tspan></text>
+			<path d="m271.18-138.8-0.74171 2.0619-0.76078-1.8964 0.34638-0.10906 0.0174-8.3796c0.40246 0.0853 0.51522-0.0288 0.82587 0.0974l-5e-3 8.2413z" fill="#2a7fff"/>
+			<text x="285.47845" y="-135.55586" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="285.47845" y="-135.55586" text-align="start">ofs</tspan><tspan x="285.47845" y="-133.67998" text-align="start">alloc_size</tspan></text>
+			<path d="m292.54-144.79-0.74171-2.0619-0.76078 1.8964 0.34638 0.10906-0.0412 9.1651c0.24984 0.0323 0.88517-0.0192 1.5658-0.0156 0.0711-0.0968 0.0691-0.46206 0.015-0.82619-0.45397 2e-3 -0.78487-5e-3 -0.69633-0.0411l-5e-3 -8.2413z" fill="#2a7fff"/>
+			<rect x="269.81" y="-131.25" width="27.712" height="7.4313" fill="#ff8080" stroke="#666" stroke-linejoin="round" stroke-width=".14463"/>
+			<rect x="269.8" y="-121.99" width="27.712" height="7.4313" fill="#ff8080" stroke="#666" stroke-linejoin="round" stroke-width=".14463"/>
+		</g>
+		<path d="m297.89-131.98-2.3876 2.1223-0.13264 35.947 2.7855 2.255" fill="none" stroke="#000" stroke-dasharray="0.56276492, 0.1875883" stroke-width=".18759"/>
+		<path d="m288.61-133.17 6.8975 7.6934" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<text x="283.73114" y="-126.61846" fill="#ffffff" font-family="'Open Sans'" font-size="2.5582px" letter-spacing="0px" stroke-width=".31977" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="283.73114" y="-126.61846" fill="#ffffff" font-family="'Open Sans'" stroke-width=".31977">lwsac_use area</tspan></text>
+		<path d="m271.31-113.11-1.5352 0.7417 1.4119 0.76079 0.0812-0.34638 2.127 0.0157c-0.0635-0.40246 0.0214-0.54838-0.0725-0.85904l-2.024 5e-3z"/>
+		<path d="m273.12-112.52 8.31 0.0664 3.0298-2.9076 0.20032-20.194 0.66323-0.0995" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<g>
+			<text x="283.62253" y="-122.57049" fill="#000000" font-family="'Open Sans'" font-size="1.0364px" letter-spacing="0px" stroke-width=".12956" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="283.62253" y="-122.57049" font-family="'Open Sans'" stroke-width=".12956">alignment padding</tspan></text>
+			<path d="m268.47-131.66 1.5352 0.74171-1.4119 0.76078-0.0812-0.34638-2.127 0.0157c0.0635-0.40245-0.0214-0.54838 0.0725-0.85903l2.024 5e-3z"/>
+			<g font-family="'Open Sans'" letter-spacing="0px" text-anchor="middle" word-spacing="0px">
+				<text x="283.72812" y="-117.3583" fill="#ffffff" font-size="2.5582px" stroke-width=".31977" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="283.72812" y="-117.3583" fill="#ffffff" font-family="'Open Sans'" stroke-width=".31977">lwsac_use area</tspan></text>
+				<text x="274.10181" y="-109.91051" fill="#000000" font-size="1.5007px" stroke-width=".18759" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="274.10181" y="-109.91051" font-family="'Open Sans'" stroke-width=".18759">ptr aligned</tspan></text>
+				<text x="283.61948" y="-113.31036" fill="#000000" font-size="1.0364px" stroke-width=".12956" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="283.61948" y="-113.31036" font-family="'Open Sans'" stroke-width=".12956">alignment padding</tspan></text>
+			</g>
+			<path d="m268.47-122.4 1.5352 0.7417-1.4119 0.76079-0.0812-0.34638-2.127 0.0157c0.0635-0.40246-0.0214-0.54838 0.0725-0.85904l2.024 5e-3z"/>
+			<rect x="303.52" y="-132.08" width="28.784" height="40.39" fill="#c4c8b7" stroke="#666" stroke-linejoin="round" stroke-width=".11698"/>
+			<text x="312.10593" y="-136.39232" fill="#ffffff" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="312.10593" y="-136.39232" fill="#ffffff" font-family="'Open Sans'" font-size="2.8222px" stroke-width=".39677">struct lwsac</tspan></text>
+			<text x="315.31079" y="-93.108513" fill="#000000" font-family="'Open Sans'" font-size="3.1742px" letter-spacing="0px" stroke-width=".39677" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="315.31079" y="-93.108513" font-family="'Open Sans'" stroke-width=".39677">allocated area</tspan></text>
+			<path d="m337.74-138.86 1.5352 0.74171-1.4119 0.76078-0.0812-0.34638-6.239-0.0174c0.0635-0.40245-0.0214-0.51522 0.0725-0.82587l6.136 5e-3z" fill="#2a7fff"/>
+			<text x="328.96686" y="-137.64424" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="328.96686" y="-137.64424">next</tspan><tspan x="328.96686" y="-135.76836">head</tspan><tspan x="328.96686" y="-133.89247">curr</tspan></text>
+			<text x="319.6235" y="-135.72807" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" stroke-width=".18759" text-align="center" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="319.6235" y="-135.72807" text-align="start">ofs</tspan><tspan x="319.6235" y="-133.85219" text-align="start">alloc_size</tspan></text>
+			<rect x="304.15" y="-131.34" width="27.572" height="28.913" fill="#ff8080" stroke="#666" stroke-linejoin="round" stroke-width=".28455"/>
+		</g>
+		<path d="m332.04-132.15-2.3876 2.1223-0.13264 35.947 2.7855 2.255" fill="none" stroke="#000" stroke-dasharray="0.56276491, 0.1875883" stroke-width=".18759"/>
+		<path d="m322.75-133.34 6.8975 7.6934" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<path d="m292.68-145.32 0.74171-1.5352 0.76078 1.4119-0.34638 0.0812 0.0259 2.071 28.542 0.041 2.225 2.8639 0.0875 2.9524 0.64641 0.65493 1.5316 2e-3 0.0488 0.76904-2.1195-0.0297-1.142-1.1761-0.0569-2.9726-2.1165-2.253c-0.40246-0.0635-28.205 0.0547-28.516-0.0391l5e-3 -2.8532z" fill="#2a7fff"/>
+		<path d="m303.21-134.9 1.5352 0.74171-1.4119 0.76078-0.0812-0.34638-6.239-0.0174c0.0635-0.40245-0.0214-0.51522 0.0725-0.82587l6.136 5e-3z" fill="#2a7fff"/>
+		<path d="m307.26-100.45 1.4591 0.0664 3.06-3.7843-9e-3 -23.607 7.6934-8.0582" fill="none" stroke="#000" stroke-dasharray="0.37517659, 0.1875883" stroke-width=".18759"/>
+		<g>
+			<path d="m305.59-100.91-1.5352 0.74171 1.4119 0.76078 0.0812-0.34638 2.127 0.01569c-0.0635-0.40245 0.0214-0.54838-0.0725-0.85903l-2.024 5e-3z"/>
+			<g font-family="'Open Sans'" letter-spacing="0px" text-anchor="middle" word-spacing="0px">
+				<text x="318.0058" y="-105.15496" fill="#ffffff" font-size="2.5582px" stroke-width=".31977" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="318.0058" y="-105.15496" fill="#ffffff" font-family="'Open Sans'" stroke-width=".31977">lwsac_use area</tspan></text>
+				<text x="308.37949" y="-97.707207" fill="#000000" font-size="1.5007px" stroke-width=".18759" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="308.37949" y="-97.707207" font-family="'Open Sans'" stroke-width=".18759">ptr aligned</tspan></text>
+				<text x="317.89719" y="-101.10705" fill="#000000" font-size="1.0364px" stroke-width=".12956" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="317.89719" y="-101.10705" font-family="'Open Sans'" stroke-width=".12956">alignment padding</tspan></text>
+			</g>
+			<path d="m302.48-132.08 1.5352 0.74171-1.4119 0.76078-0.0812-0.34638-2.127 0.0157c0.0635-0.40245-0.0214-0.54838 0.0725-0.85903l2.024 5e-3z"/>
+			<g stroke-width=".18759">
+				<rect x="204.83" y="-139.17" width="5.018" height="2.6731" ry="1.3366" fill="#808080" stroke="#666" stroke-linejoin="round"/>
+				<text x="207.36176" y="-137.33116" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="207.36176" y="-137.33116" fill="#ffffff" font-family="'Open Sans'" stroke-width=".18759">NULL</tspan></text>
+				<rect x="253.14" y="-139.13" width="5.018" height="2.6731" ry="1.3366" fill="#808080" stroke="#666" stroke-linejoin="round"/>
+				<text x="255.6705" y="-137.29219" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="255.6705" y="-137.29219" fill="#ffffff" font-family="'Open Sans'" stroke-width=".18759">NULL</tspan></text>
+				<rect x="336.04" y="-139.46" width="5.018" height="2.6731" ry="1.3366" fill="#808080" stroke="#666" stroke-linejoin="round"/>
+				<text x="338.57361" y="-137.62383" fill="#ffffff" font-family="'Open Sans'" font-size="1.5007px" letter-spacing="0px" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="338.57361" y="-137.62383" fill="#ffffff" font-family="'Open Sans'" stroke-width=".18759">NULL</tspan></text>
+			</g>
+		</g>
+		<g fill="#0000ff" font-family="'Open Sans'" font-size="3.2649px" letter-spacing="0px" stroke-width=".40811" text-anchor="middle" word-spacing="0px">
+			<text x="187.11359" y="-84.657211" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="187.11359" y="-84.657211" fill="#0000ff" font-family="'Open Sans'" stroke-width=".40811">empty, generic lwsac</tspan></text>
+			<text x="235.71112" y="-84.353561" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="235.71112" y="-84.353561" fill="#0000ff" font-family="'Open Sans'" stroke-width=".40811">lwsac with 2 "uses"</tspan></text>
+			<text x="300.70877" y="-83.844711" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="300.70877" y="-83.844711" fill="#0000ff" font-family="'Open Sans'" stroke-width=".40811">lwsac with 2 "uses", 3rd requires a new one</tspan></text>
+		</g>
+	</g>
+</svg>
diff --git a/include/libwebsockets.h b/include/libwebsockets.h
index c4fc84ad85063a3ce6da296b728b628a7cce0af6..3d63dffe1634d02102ec850b556583effe82f0d2 100644
--- a/include/libwebsockets.h
+++ b/include/libwebsockets.h
@@ -409,6 +409,7 @@ struct lws;
 #include <libwebsockets/lws-stats.h>
 #include <libwebsockets/lws-threadpool.h>
 #include <libwebsockets/lws-tokenize.h>
+#include <libwebsockets/lws-lwsac.h>
 
 #if defined(LWS_WITH_TLS)
 
diff --git a/include/libwebsockets/lws-lwsac.h b/include/libwebsockets/lws-lwsac.h
new file mode 100644
index 0000000000000000000000000000000000000000..1f914b66a650f2beb9994748dc833370e9fb6628
--- /dev/null
+++ b/include/libwebsockets/lws-lwsac.h
@@ -0,0 +1,191 @@
+/*
+ * libwebsockets - lws alloc chunk
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ *
+ * included from libwebsockets.h
+ */
+
+/** \defgroup log lwsac
+ *
+ * ##Allocated Chunks
+ *
+ * If you know you will be allocating a large, unknown number of same or
+ * differently sized objects, it's certainly possible to do it with libc
+ * malloc.  However the allocation cost in time and memory overhead can
+ * add up, and deallocation means walking the structure of every object and
+ * freeing them in turn.
+ *
+ * lwsac (LWS Allocated Chunks) allocates chunks intended to be larger
+ * than your objects (4000 bytes by default) which you linearly allocate from
+ * using lwsac_use().
+ *
+ * If your next request won't fit in the current chunk, a new chunk is added
+ * to the chain of chunks and the allocaton done from there.  If the request
+ * is larger than the chunk size, an oversize chunk is created to satisfy it.
+ *
+ * When you are finished with the allocations, you call lwsac_free() and
+ * free all the *chunks*.  So you may have thousands of objects in the chunks,
+ * but they are all destroyed with the chunks without having to deallocate them
+ * one by one pointlessly.
+ */
+///@{
+
+struct lwsac;
+typedef unsigned char * lwsac_cached_file_t;
+
+
+#define lws_list_ptr_container(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
+
+/*
+ * linked-list helper that's commonly useful to manage lists of things
+ * allocated using lwsac.
+ *
+ * These lists point to their corresponding "next" member in the target, NOT
+ * the original containing struct.  To get the containing struct, you must use
+ * lws_list_ptr_container() to convert.
+ *
+ * It's like that because it means we no longer have to have the next pointer
+ * at the start of the struct, and we can have the same struct on multiple
+ * linked-lists with everything held in the struct itself.
+ */
+typedef void * lws_list_ptr;
+
+/*
+ * optional sorting callback called by lws_list_ptr_insert() to sort the right
+ * things inside the opqaue struct being sorted / inserted on the list.
+ */
+typedef int (*lws_list_ptr_sort_func_t)(lws_list_ptr a, lws_list_ptr b);
+
+#define lws_list_ptr_advance(_lp) _lp = *((void **)_lp)
+
+/* sort may be NULL if you don't care about order */
+LWS_VISIBLE LWS_EXTERN void
+lws_list_ptr_insert(lws_list_ptr *phead, lws_list_ptr *add,
+		    lws_list_ptr_sort_func_t sort);
+
+
+/**
+ * lwsac_use - allocate / use some memory from a lwsac
+ *
+ * \param head: pointer to the lwsac list object
+ * \param ensure: the number of bytes we want to use
+ * \param chunk_size: 0, or the size of the chunk to (over)allocate if
+ *			what we want won't fit in the current tail chunk.  If
+ *			0, the default value of 4000 is used. If ensure is
+ *			larger, it is used instead.
+ *
+ * This also serves to init the lwsac if *head is NULL.  Basically it does
+ * whatever is necessary to return you a pointer to ensure bytes of memory
+ * reserved for the caller.
+ *
+ * Returns NULL if OOM.
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size);
+
+/**
+ * lwsac_free - deallocate all chunks in the lwsac and set head NULL
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * This deallocates all chunks in the lwsac, then sets *head to NULL.  All
+ * lwsac_use() pointers are invalidated in one hit without individual frees.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_free(struct lwsac **head);
+
+/*
+ * Optional helpers useful for where consumers may need to defer destruction
+ * until all consumers are finished with the lwsac
+ */
+
+/**
+ * lwsac_detach() - destroy an lwsac unless somebody else is referencing it
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * The creator of the lwsac can all this instead of lwsac_free() when it itself
+ * has finished with the lwsac, but other code may be consuming it.
+ *
+ * If there are no other references, the lwsac is destroyed, *head is set to
+ * NULL and that's the end; however if something else has called
+ * lwsac_reference() on the lwsac, it simply returns.  When lws_unreference()
+ * is called and no references are left, it will be destroyed then.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_detach(struct lwsac **head);
+
+/**
+ * lwsac_reference() - increase the lwsac reference count
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * Increment the reference count on the lwsac to defer destruction.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_reference(struct lwsac *head);
+
+/**
+ * lwsac_reference() - increase the lwsac reference count
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * Decrement the reference count on the lwsac... if it reached 0 on a detached
+ * lwsac then the lwsac is immediately destroyed and *head set to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_unreference(struct lwsac **head);
+
+
+/* helpers to keep a file cached in memory */
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_use_cached_file_start(lwsac_cached_file_t cache);
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_use_cached_file_end(lwsac_cached_file_t *cache);
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_use_cached_file_detach(lwsac_cached_file_t *cache);
+
+LWS_VISIBLE LWS_EXTERN int
+lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache,
+		  size_t *len);
+
+/* more advanced helpers */
+
+LWS_VISIBLE LWS_EXTERN size_t
+lwsac_sizeof(void);
+
+LWS_VISIBLE LWS_EXTERN size_t
+lwsac_get_tail_pos(struct lwsac *lac);
+
+LWS_VISIBLE LWS_EXTERN struct lwsac *
+lwsac_get_next(struct lwsac *lac);
+
+LWS_VISIBLE LWS_EXTERN size_t
+lwsac_align(size_t length);
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_info(struct lwsac *head);
+
+LWS_VISIBLE LWS_EXTERN uint64_t
+lwsac_total_alloc(struct lwsac *head);
+
+///@}
diff --git a/lib/misc/lwsac/README.md b/lib/misc/lwsac/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..33462a7430703172859285a7562c555d0118e1b3
--- /dev/null
+++ b/lib/misc/lwsac/README.md
@@ -0,0 +1,63 @@
+## LWS Allocated Chunks
+
+![lwsac flow](/doc-assets/lwsac.svg)
+
+These apis provide a way to manage a linked-list of allocated chunks...
+
+[ HEAD alloc ] -> [ next alloc ] -> [ next alloc ] -> [ curr alloc ]
+
+... and sub-allocate trivially inside the chunks.  These sub-allocations are
+not tracked by lwsac at all, there is a "used" high-water mark for each chunk
+that's simply advanced by the amount sub-allocated.  If the allocation size
+matches the platform pointer alignment, there is zero overhead to sub-allocate
+(otherwise the allocation is padded to the next platform pointer alignment
+automatically).
+
+If you have an unknown amount of relatively little things to allocate, including
+strings or other unstructured data, lwsac is significantly more efficient than
+individual allocations using malloc or so.
+
+## lwsac_use() api
+
+When you make an sub-allocation using `lwsac_use()`, you can either
+set the `chunk_size` arg to zero, defaulting to 4000, or a specific chunk size.
+In the event the requested sub-allocation exceeds the chunk size, the chunk
+size is increated to match it automatically for this allocation only.
+
+Subsequent `lwsac_use()` calls will advance internal pointers to use up the
+remaining space inside the current chunk if possible; if not enough remaining
+space it is skipped, a new allocation is chained on and the request pointed to
+there.
+
+Lwsac does not store information about sub-allocations.  There is really zero
+overhead for individual sub-allocations (unless their size is not
+pointer-aligned, in which case the actual amount sub-allocated is rounded up to
+the next pointer alignment automatically).  For structs, which are pointer-
+aligned naturally, and a chunk size relatively large for the sub-allocation
+size, lwsac is extremely efficient even for huge numbers of small allocations.
+
+This makes lwsac very effective when the total amount of allocation needed is
+not known at the start and may be large... it will simply add on chunks to cope
+with whatever happens.
+
+## lwsac_free() api
+
+When you are finished with the lwsac, you simply free the chain of allocated
+chunks using lwsac_free() on the lwsac head.  There's no tracking or individual
+destruction of suballocations - the whole chain of chunks the suballocations
+live in are freed and invalidated all together.
+
+If the structs stored in the lwsac allocated things **outside** the lwsac, then the
+user must unwind through them and perform the frees.  But the idea of lwsac is
+things stored in the lwsac also suballocate into the lwsac, and point into the
+lwsac if they need to, avoiding any need to visit them during destroy.  It's
+like clearing up after a kids' party by gathering up a disposable tablecloth:
+no matter what was left on the table, it's all gone in one step.
+
+## lws_list_ptr helpers
+
+A common pattern needed with sub-allocated structs is they are on one or more
+linked-list.  To make that simple to do cleanly, lws_list... apis are provided
+along with a generic insertion function that can take a sort callback.  These
+allow a struct to participate on multiple linked-lists simultaneously.
+
diff --git a/lib/misc/lwsac/cached-file.c b/lib/misc/lwsac/cached-file.c
new file mode 100644
index 0000000000000000000000000000000000000000..f5f64d21669f9f4b30404451679ea691faa4fad1
--- /dev/null
+++ b/lib/misc/lwsac/cached-file.c
@@ -0,0 +1,202 @@
+/*
+ * libwebsockets - lws alloc chunk live file caching
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+#include "misc/lwsac/private.h"
+
+/*
+ * Helper for caching a file in memory in a lac, but also to check at intervals
+ * no less than 5s if the file is still fresh.
+ *
+ * Set *cache to NULL the first time before calling.
+ *
+ * You should call this each time before using the cache... if it's
+ *
+ *  - less than 5s since the last freshness check, and
+ *  - the file is already in memory
+ *
+ * it just returns with *cache left alone; this costs very little.  You should
+ * call `lwsac_use_cached_file_start()` and `lwsac_use_cached_file_end()` to lock
+ * the cache against deletion while you are using it.
+ *
+ * If it's
+ *
+ *  - at least 5s since the last freshness check, and
+ *  - the file timestamp has changed
+ *
+ * then
+ *
+ *  - the file is reloaded into a new lac and *cache set to that
+ *
+ *  - the old cache lac, if any, is detached (so it will be freed when its
+ *    reference count reaches zero, or immediately if nobody has it)
+ *
+ * Note the call can fail due to OOM or filesystem issue at any time.
+ *
+ *
+ * After the LAC header there is stored a `struct cached_file_info` and then
+ * the raw file contents.  *
+ *
+ *  [LAC header]
+ *  [struct cached_file_info]
+ *  [file contents]  <--- *cache is set to here
+ *
+ * The api returns a lwsac_cached_file_t type offset to point to the file
+ * contents.  Helpers for reference counting and freeing are also provided
+ * that take that type and know how to correct it back to operate on the LAC.
+ */
+
+#define cache_file_to_lac(c) ((struct lwsac *)((char *)c - \
+			      sizeof(struct cached_file_info) - \
+			      sizeof(struct lwsac)))
+
+void
+lwsac_use_cached_file_start(lwsac_cached_file_t cache)
+{
+	struct lwsac *lac = cache_file_to_lac(cache);
+
+	lac->refcount++;
+	// lwsl_debug("%s: html refcount: %d\n", __func__, lac->refcount);
+}
+
+void
+lwsac_use_cached_file_end(lwsac_cached_file_t *cache)
+{
+	struct lwsac *lac;
+
+	if (!cache || !*cache)
+		return;
+
+	lac = cache_file_to_lac(*cache);
+
+	if (!lac->refcount)
+		lwsl_err("%s: html refcount zero on entry\n", __func__);
+
+	if (lac->refcount && !--lac->refcount && lac->detached) {
+		*cache = NULL; /* not usable any more */
+		lwsac_free(&lac);
+	}
+}
+
+void
+lwsac_use_cached_file_detach(lwsac_cached_file_t *cache)
+{
+	struct lwsac *lac = cache_file_to_lac(*cache);
+
+	lac->detached = 1;
+	if (lac->refcount)
+		return;
+
+	*cache = NULL;
+	lwsac_free(&lac);
+}
+
+int
+lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, size_t *len)
+{
+	struct cached_file_info *info = NULL;
+	lwsac_cached_file_t old = *cache;
+	struct lwsac *lac = NULL;
+	time_t t = time(NULL);
+	unsigned char *a;
+	struct stat s;
+	size_t all;
+	ssize_t rd;
+	int fd;
+
+	if (old) { /* we already have a cached copy of it */
+
+		info = (struct cached_file_info *)((*cache) - sizeof(*info));
+
+		if (t - info->last_confirm < 5)
+			/* we checked it as fresh less than 5s ago, use old */
+			return 0;
+	}
+
+	/*
+	 * ...it's been 5s, we should check again on the filesystem
+	 * that the file hasn't changed
+	 */
+
+	fd = open(filepath, O_RDONLY);
+	if (fd < 0) {
+		lwsl_err("%s: cannot open %s\n", __func__, filepath);
+
+		return 1;
+	}
+
+	if (fstat(fd, &s)) {
+		lwsl_err("%s: cannot stat %s\n", __func__, filepath);
+
+		goto bail;
+	}
+
+	if (old && s.st_mtime == info->s.st_mtime) {
+		/* it still seems to be the same as our cached one */
+		info->last_confirm = t;
+
+		close(fd);
+
+		return 0;
+	}
+
+	/*
+	 * we either didn't cache it yet, or it has changed since we cached
+	 * it... reload in a new lac and then detach the old lac.
+	 */
+
+	all = sizeof(*info) + s.st_size + 1;
+
+	info = lwsac_use(&lac, all, all);
+	if (!info)
+		goto bail;
+
+	info->s = s;
+	info->last_confirm = t;
+
+	a = (unsigned char *)(info + 1);
+
+	*len = s.st_size;
+	a[s.st_size] = '\0';
+
+	rd = read(fd, a, s.st_size);
+	if (rd != s.st_size) {
+		lwsl_err("%s: cannot read %s (%d)\n", __func__, filepath,
+			 (int)rd);
+		goto bail1;
+	}
+
+	close(fd);
+
+	*cache = (lwsac_cached_file_t)a;
+	if (old)
+		lwsac_use_cached_file_detach(&old);
+
+	return 0;
+
+bail1:
+	lwsac_free(&lac);
+
+bail:
+	close(fd);
+
+	return 1;
+}
diff --git a/lib/misc/lwsac/lwsac.c b/lib/misc/lwsac/lwsac.c
new file mode 100644
index 0000000000000000000000000000000000000000..abde1a832ba54d63c09c2c1ac6ec279e5d9706c6
--- /dev/null
+++ b/lib/misc/lwsac/lwsac.c
@@ -0,0 +1,182 @@
+/*
+ * libwebsockets - lws alloc chunk
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "core/private.h"
+#include "misc/lwsac/private.h"
+
+void
+lws_list_ptr_insert(lws_list_ptr *head, lws_list_ptr *add,
+		    lws_list_ptr_sort_func_t sort_func)
+{
+	while (sort_func && *head) {
+		if (sort_func(add, *head) <= 0)
+			break;
+
+		head = *head;
+	}
+
+	*add = *head;
+	*head = add;
+}
+
+size_t
+lwsac_align(size_t length)
+{
+	size_t align = sizeof(int *);
+
+	if (length & (align - 1))
+		length += align - (length & (align - 1));
+
+	return length;
+}
+
+size_t
+lwsac_sizeof(void)
+{
+	return sizeof(struct lwsac);
+}
+
+size_t
+lwsac_get_tail_pos(struct lwsac *lac)
+{
+	return lac->ofs;
+}
+
+struct lwsac *
+lwsac_get_next(struct lwsac *lac)
+{
+	return lac->next;
+}
+
+void *
+lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size)
+{
+	struct lwsac *chunk;
+	size_t ofs, alloc;
+
+	/* ensure there's a chunk and enough space in it for this name */
+
+	if (!*head || (*head)->curr->alloc_size - (*head)->curr->ofs < ensure) {
+
+		if (!chunk_size)
+			alloc = LWSAC_CHUNK_SIZE + sizeof(*chunk);
+		else
+			alloc = chunk_size + sizeof(*chunk);
+
+		/*
+		 * If we get asked for something outside our expectation,
+		 * allocate to meet it
+		 */
+
+		if (ensure >= alloc - sizeof(*chunk))
+			alloc = ensure + sizeof(*chunk);
+
+		chunk = malloc(alloc);
+		if (!chunk) {
+			lwsl_err("%s: OOM trying to alloc %llud\n", __func__,
+					(unsigned long long)alloc);
+			return NULL;
+		}
+
+		if (!*head) {
+			*head = chunk;
+			chunk->total_alloc_size = 0;
+			chunk->total_blocks = 0;
+		}
+		else
+			(*head)->curr->next = chunk;
+
+		(*head)->curr = chunk;
+		(*head)->curr->head = *head;
+
+		chunk->next = NULL;
+		chunk->alloc_size = alloc;
+		chunk->detached = 0;
+		chunk->refcount = 0;
+
+		(*head)->total_alloc_size += alloc;
+		(*head)->total_blocks++;
+
+		/*
+		 * belabouring the point... ofs is aligned to the platform's
+		 * generic struct alignment at the start then
+		 */
+		(*head)->curr->ofs = sizeof(*chunk);
+	}
+
+	ofs = (*head)->curr->ofs;
+
+	(*head)->curr->ofs += lwsac_align(ensure);
+	if ((*head)->curr->ofs >= (*head)->curr->alloc_size)
+		(*head)->curr->ofs = (*head)->curr->alloc_size;
+
+	return (char *)(*head)->curr + ofs;
+}
+
+void
+lwsac_free(struct lwsac **head)
+{
+	struct lwsac *it = *head;
+
+	while (it) {
+		struct lwsac *tmp = it->next;
+
+		free(it);
+		it = tmp;
+	}
+
+	*head = NULL;
+}
+
+void
+lwsac_info(struct lwsac *head)
+{
+	lwsl_notice("%s: lac %p: %dKiB in %d blocks\n", __func__, head,
+		    (int)(head->total_alloc_size >> 10), head->total_blocks);
+}
+
+uint64_t
+lwsac_total_alloc(struct lwsac *head)
+{
+	return head->total_alloc_size;
+}
+
+void
+lwsac_reference(struct lwsac *head)
+{
+	head->refcount++;
+}
+
+void
+lwsac_unreference(struct lwsac **head)
+{
+	(*head)->refcount--;
+	if ((*head)->detached && !(*head)->refcount)
+		lwsac_free(head);
+}
+
+void
+lwsac_detach(struct lwsac **head)
+{
+	(*head)->detached = 1;
+	if (!(*head)->refcount)
+		lwsac_free(head);
+}
diff --git a/lib/misc/lwsac/private.h b/lib/misc/lwsac/private.h
new file mode 100644
index 0000000000000000000000000000000000000000..efefeb85af782454e12f995b7baa8bbe79744c9d
--- /dev/null
+++ b/lib/misc/lwsac/private.h
@@ -0,0 +1,48 @@
+/*
+ * libwebsockets - lws alloc chunk
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <sys/stat.h>
+
+/* under page size of 4096 to allow overhead */
+#define LWSAC_CHUNK_SIZE 4000
+
+/*
+ * the chunk list members all point back to the head themselves so the list
+ * can be detached from the formal head and free itself when its reference
+ * count reaches zero.
+ */
+
+struct lwsac {
+	struct lwsac *next;
+	struct lwsac *head; /* pointer back to the first chunk */
+	struct lwsac *curr; /* applies to head chunk only */
+	size_t total_alloc_size; /* applies to head chunk only */
+	size_t alloc_size;
+	size_t ofs; /* next writeable position inside chunk */
+	int refcount; /* applies to head chunk only */
+	int total_blocks; /* applies to head chunk only */
+	char detached; /* if our refcount gets to zero, free the chunk list */
+};
+
+struct cached_file_info {
+	struct stat s;
+	time_t last_confirm;
+};
diff --git a/minimal-examples/api-tests/README.md b/minimal-examples/api-tests/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b047af52f43633bc03fa6377a002eb51b5828e91
--- /dev/null
+++ b/minimal-examples/api-tests/README.md
@@ -0,0 +1,5 @@
+|name|tests|
+---|---
+api-test-lwsac|LWS Allocated Chunks
+api-test-lws_tokenize|Generic secure string tokenizer
+
diff --git a/minimal-examples/api-tests/api-test-lwsac/CMakeLists.txt b/minimal-examples/api-tests/api-test-lwsac/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a73c6807e9c8669ed78910d801c8e0926fb8ab3a
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-lwsac/CMakeLists.txt
@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-api-test-lwsac)
+set(SRCS main.c)
+
+# If we are being built as part of lws, confirm current build config supports
+# reqconfig, else skip building ourselves.
+#
+# If we are being built externally, confirm installed lws was configured to
+# support reqconfig, else error out with a helpful message about the problem.
+#
+MACRO(require_lws_config reqconfig _val result)
+
+	if (DEFINED ${reqconfig})
+	if (${reqconfig})
+		set (rq 1)
+	else()
+		set (rq 0)
+	endif()
+	else()
+		set(rq 0)
+	endif()
+
+	if (${_val} EQUAL ${rq})
+		set(SAME 1)
+	else()
+		set(SAME 0)
+	endif()
+
+	if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
+		if (${_val})
+			message("${SAMP}: skipping as lws being built without ${reqconfig}")
+		else()
+			message("${SAMP}: skipping as lws built with ${reqconfig}")
+		endif()
+		set(${result} 0)
+	else()
+		if (LWS_WITH_MINIMAL_EXAMPLES)
+			set(MET ${SAME})
+		else()
+			CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
+			if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
+				set(HAS_${reqconfig} 0)
+			else()
+				set(HAS_${reqconfig} 1)
+			endif()
+			if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
+				set(MET 1)
+			else()
+				set(MET 0)
+			endif()
+		endif()
+		if (NOT MET)
+			if (${_val})
+				message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
+			else()
+				message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
+			endif()
+		endif()
+	endif()
+ENDMACRO()
+
+
+
+	add_executable(${SAMP} ${SRCS})
+
+	if (websockets_shared)
+		target_link_libraries(${SAMP} websockets_shared)
+		add_dependencies(${SAMP} websockets_shared)
+	else()
+		target_link_libraries(${SAMP} websockets)
+	endif()
diff --git a/minimal-examples/api-tests/api-test-lwsac/README.md b/minimal-examples/api-tests/api-test-lwsac/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..74034c793f330199d851a6248a5bec836f71cb90
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-lwsac/README.md
@@ -0,0 +1,22 @@
+# lws api test lwsac
+
+Demonstrates how to use and performs selftests for lwsac
+
+## build
+
+```
+ $ cmake . && make
+```
+
+## usage
+
+Commandline option|Meaning
+---|---
+-d <loglevel>|Debug verbosity in decimal, eg, -d15
+
+```
+ $ ./lws-api-test-lwsac
+[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac
+[2018/10/09 09:14:17:4835] USER: Completed: PASS
+```
+
diff --git a/minimal-examples/api-tests/api-test-lwsac/main.c b/minimal-examples/api-tests/api-test-lwsac/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..854e0adc24f7c9feeb72ccd92cd87a410a888dd0
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-lwsac/main.c
@@ -0,0 +1,81 @@
+/*
+ * lws-api-test-lwsac
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ */
+
+#include <libwebsockets.h>
+
+struct mytest {
+	int payload;
+	/* notice doesn't have to be at start of struct */
+	lws_list_ptr list_next;
+	/* a struct can appear on multiple lists too... */
+};
+
+/* converts a ptr to struct mytest .list_next to a ptr to struct mytest */
+#define list_to_mytest(p) lws_list_ptr_container(p, struct mytest, list_next)
+
+int main(int argc, const char **argv)
+{
+	int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, acc;
+	lws_list_ptr list_head = NULL, iter;
+	struct lwsac *lwsac = NULL;
+	struct mytest *m;
+	const char *p;
+
+	if ((p = lws_cmdline_option(argc, argv, "-d")))
+		logs = atoi(p);
+
+	lws_set_log_level(logs, NULL);
+	lwsl_user("LWS API selftest: lwsac\n");
+
+	/*
+	 * 1) allocate and create 1000 struct mytest in a linked-list
+	 */
+
+	for (n = 0; n < 1000; n++) {
+		m = lwsac_use(&lwsac, sizeof(*m), 0);
+		m->payload = n;
+
+		lws_list_ptr_insert(&list_head, &m->list_next, NULL);
+	}
+
+	/*
+	 * 2) report some debug info about the lwsac state... those 1000
+	 * allocations actually only required 4 mallocs
+	 */
+
+	lwsac_info(lwsac);
+
+	/* 3) iterate the list, accumulating the payloads */
+
+	acc = 0;
+	iter = list_head;
+	while (iter) {
+		m = list_to_mytest(iter);
+		acc += m->payload;
+
+		lws_list_ptr_advance(iter);
+	}
+
+	if (acc != 499500) {
+		lwsl_err("%s: FAIL acc %d\n", __func__, acc);
+
+		return 1;
+	}
+
+	/*
+	 * 4) deallocate everything (lwsac is also set to NULL).  It just
+	 *    deallocates the 4 mallocs, everything in there is gone accordingly
+	 */
+
+	lwsac_free(&lwsac);
+
+	lwsl_user("Completed: PASS\n");
+
+	return 0;
+}
diff --git a/minimal-examples/api-tests/api-test-lwsac/selftest.sh b/minimal-examples/api-tests/api-test-lwsac/selftest.sh
new file mode 100755
index 0000000000000000000000000000000000000000..16d1e2e8e463dec610c1cc4c2c7cc22f3cf0b14f
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-lwsac/selftest.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# $1: path to minimal example binaries...
+#     if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1
+#     that will be ./bin from your build dir
+#
+# $2: path for logs and results.  The results will go
+#     in a subdir named after the directory this script
+#     is in
+#
+# $3: offset for test index count
+#
+# $4: total test count
+#
+# $5: path to ./minimal-examples dir in lws
+#
+# Test return code 0: OK, 254: timed out, other: error indication
+
+. $5/selftests-library.sh
+
+COUNT_TESTS=1
+
+dotest $1 $2 apiselftest
+exit $FAILS