From 402efecf04321001cbf4b6d50b72e6dc46960cec Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 6 Jul 2026 08:03:30 +0100 Subject: [PATCH 01/44] hls: add plugin and example Basic h.264 / h.265 mkv / mp4 serving to view in browser --- lib/core-net/service.c | 8 +- lib/roles/h1/ops-h1.c | 4 + .../minimal-http-server-hls/CMakeLists.txt | 50 + .../minimal-http-server-hls/README.md | 58 ++ .../media/Big_Buck_Bunny_360_10s_1MB.mp4 | Bin 0 -> 991017 bytes .../minimal-http-server.c | 172 ++++ .../mount-origin/404.html | 13 + .../mount-origin/dir.css | 5 + .../mount-origin/favicon.ico | Bin 0 -> 1406 bytes .../mount-origin/hls.min.js | 2 + .../mount-origin/index.html | 18 + .../mount-origin/libwebsockets.org-logo.svg | 66 ++ .../mount-origin/player.css | 73 ++ .../mount-origin/player.html | 29 + .../mount-origin/player.js | 125 +++ .../mount-origin/strict-csp.svg | 53 ++ plugins/protocol_lws_hls/CMakeLists.txt | 53 ++ plugins/protocol_lws_hls/README.md | 56 ++ plugins/protocol_lws_hls/hls-av.c | 882 ++++++++++++++++++ plugins/protocol_lws_hls/hls-dir.c | 153 +++ plugins/protocol_lws_hls/private-lws-hls.h | 111 +++ plugins/protocol_lws_hls/protocol_lws_hls.c | 316 +++++++ .../protocol_lws_login/protocol_lws_login.c | 4 +- .../protocol_webtransport_shared_world.c | 4 +- .../protocol_webtransport_test.c | 4 +- 25 files changed, 2252 insertions(+), 7 deletions(-) create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/CMakeLists.txt create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/README.md create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/media/Big_Buck_Bunny_360_10s_1MB.mp4 create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/minimal-http-server.c create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/404.html create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/dir.css create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/favicon.ico create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/hls.min.js create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/index.html create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/libwebsockets.org-logo.svg create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.css create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.html create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.js create mode 100644 minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/strict-csp.svg create mode 100644 plugins/protocol_lws_hls/CMakeLists.txt create mode 100644 plugins/protocol_lws_hls/README.md create mode 100644 plugins/protocol_lws_hls/hls-av.c create mode 100644 plugins/protocol_lws_hls/hls-dir.c create mode 100644 plugins/protocol_lws_hls/private-lws-hls.h create mode 100644 plugins/protocol_lws_hls/protocol_lws_hls.c diff --git a/lib/core-net/service.c b/lib/core-net/service.c index f957f0d7b1..2d1ffbf062 100644 --- a/lib/core-net/service.c +++ b/lib/core-net/service.c @@ -676,9 +676,11 @@ lws_service_flag_pending(struct lws_context *context, int tsi) struct lws *wsi = lws_container_of(d, struct lws, dll_buflist); if (!lws_is_flowcontrolled(wsi) && - lwsi_state(wsi) != LRS_DEFERRING_ACTION && - lwsi_state(wsi) != LRS_AWAITING_FILE_READ && - lwsi_state(wsi) != LRS_AWAITING_SSL_ACCEPT) { + lwsi_state(wsi) != LRS_DEFERRING_ACTION && + lwsi_state(wsi) != LRS_ISSUING_FILE && + lwsi_state(wsi) != LRS_DOING_TRANSACTION && + lwsi_state(wsi) != LRS_AWAITING_FILE_READ && + lwsi_state(wsi) != LRS_AWAITING_SSL_ACCEPT) { forced = 1; break; } diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index e4c058d023..63beaab780 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -108,6 +108,10 @@ lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len) return lws_ptr_diff(buf, oldbuf); case LRS_ISSUING_FILE: return lws_ptr_diff(buf, oldbuf); + case LRS_DOING_TRANSACTION: + if (!lwsi_role_h1(wsi)) + break; + return lws_ptr_diff(buf, oldbuf); case LRS_DISCARD_BODY: case LRS_BODY: wsi->http.rx_content_remain = diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/CMakeLists.txt b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/CMakeLists.txt new file mode 100644 index 0000000000..5ac36e35b1 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/CMakeLists.txt @@ -0,0 +1,50 @@ +project(lws-minimal-http-server-hls C) +cmake_minimum_required(VERSION 3.10) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-minimal-http-server-hls) +set(SRCS minimal-http-server.c) + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_WITH_SERVER 1 requirements) +require_lws_config(LWS_WITH_PLUGINS 1 requirements) + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_check_modules(PC_AVFORMAT libavformat) + pkg_check_modules(PC_AVCODEC libavcodec) + pkg_check_modules(PC_AVUTIL libavutil) + pkg_check_modules(PC_SWSCALE libswscale) + + if (NOT PC_AVFORMAT_FOUND OR NOT PC_AVCODEC_FOUND OR NOT PC_AVUTIL_FOUND OR NOT PC_SWSCALE_FOUND) + set(requirements 0) + endif() +else() + set(requirements 0) +endif() + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() + + set_property(TARGET ${SAMP} APPEND PROPERTY COMPILE_DEFINITIONS + INSTALL_DATADIR="${CMAKE_INSTALL_PREFIX}/share" + ) + + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/media/" + DESTINATION share/libwebsockets-test-server/hls + COMPONENT examples) + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/mount-origin/" + DESTINATION share/libwebsockets-test-server/hls/mount-origin + COMPONENT examples) +endif() diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/README.md b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/README.md new file mode 100644 index 0000000000..966b539e51 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/README.md @@ -0,0 +1,58 @@ +# lws minimal http server HLS + +This example demonstrates how to use the `protocol_lws_hls` plugin to dynamically serve HTTP Live Streaming (HLS) content from static `.mp4` and `.mkv` files. It recursively scans the specified media directory, creates an HTML index, and chunks video files on the fly into HLS `.ts` segments. + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-http-server-hls +[2026/07/08 12:00:00:0000] USER: LWS minimal http server HLS | visit http://localhost:7681 +[2026/07/08 12:00:00:0000] USER: Media dir: /usr/local/share/libwebsockets-test-server/hls +``` + +Visit http://localhost:7681 to view the generated media library. + +## Commandline Options + +- `--media-dir `: Override the default directory containing media files (default: installed `media/` path). +- `--help`: Show built-in LWS options (e.g. `-d `). + +## lwsws configuration + +If you want to use the HLS plugin with `lwsws` (the LWS JSON-configured web server) instead of this minimal C example, you can enable and configure the plugin and mounts via your `lejp` vhost configuration file: + +```json +{ + "vhosts": [ + { + "name": "localhost", + "port": 7681, + "ws-protocols": [ + { + "lws-hls": { + "status": "ok", + "media-dir": "/path/to/your/media" + } + } + ], + "mounts": [ + { + "mountpoint": "/media", + "origin": "file:///usr/local/share/libwebsockets-test-server/hls/mount-origin", + "default": "index.html" + }, + { + "mountpoint": "/media/hls", + "origin": "callback://lws-hls" + } + ] + } + ] +} +``` diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/media/Big_Buck_Bunny_360_10s_1MB.mp4 b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/media/Big_Buck_Bunny_360_10s_1MB.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..9b6d89da00a2df9082ec980387a79a441911d88f GIT binary patch literal 991017 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

6_v7S>Q75fe)x?S0VQa({)+q@%v#$&M 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%0Aaby}!__grJ&*cq4%cihK0VAWl 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�GsBfGBf1UGu(fnKo;T;CG}nKPeKMx9t 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
qy|7nzm;jFY|0;K10Vr305kAW!Ep;b#TP|Di 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
fC0AX4!nnh8N?0R9%P26rpLuH}Mu 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

e{pR-!&6G9g4{c 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 +#include +#include + +static int interrupted; + +enum { + LWS_SW_H2_PRIOR_KNOWLEDGE, + LWS_SW_D, + LWS_SW_MEDIA_DIR, + LWS_SW_HELP, +}; + +static const struct lws_switches switches[] = { + [LWS_SW_H2_PRIOR_KNOWLEDGE] = { "--h2-prior-knowledge", "Enable --h2-prior-knowledge feature" }, + [LWS_SW_D] = { "-d", "Debug logs (e.g. -d 15)" }, + [LWS_SW_MEDIA_DIR] = { "--media-dir", "Directory containing media files (default: ./media)" }, + [LWS_SW_HELP] = { "--help", "Show this help information" }, +}; + +#ifndef INSTALL_DATADIR +#define INSTALL_DATADIR "/usr/local/share" +#endif + +static struct lws_protocol_vhost_options pvo_media = { + NULL, NULL, "media-dir", INSTALL_DATADIR "/libwebsockets-test-server/hls" +}; + +static const struct lws_protocol_vhost_options pvo_csp = { + NULL, NULL, "content-security-policy:", + "default-src 'self'; img-src 'self' data: ; " + "script-src 'self'; script-src-elem 'self'; font-src 'self'; " + "style-src 'self'; connect-src 'self' ws: wss:; " + "worker-src 'self' blob:; child-src 'self' blob:; media-src 'self' blob:; " + "frame-ancestors 'none'; base-uri 'none'; form-action 'self';" +}; + +static const struct lws_protocol_vhost_options pvo = { + NULL, &pvo_media, "lws-hls", "" +}; + +static const struct lws_http_mount mount_hls = { + .mount_next = NULL, + .mountpoint = "/hls", /* mountpoint URL */ + .origin = INSTALL_DATADIR "/libwebsockets-test-server/hls/mount-origin", + .def = "index.html", + .origin_protocol = LWSMPRO_FILE, /* serve from dir */ + .mountpoint_len = 4, /* char count */ +}; + +static const struct lws_http_mount mount_hls_live = { + .mount_next = &mount_hls, + .mountpoint = "/hls/hls", /* mountpoint URL */ + .protocol = "lws-hls", /* protocol name */ + .origin_protocol = LWSMPRO_CALLBACK, /* callback */ + .mountpoint_len = 8, /* char count */ +}; + +static const struct lws_protocol_vhost_options pvo_mime_mkv = { + NULL, NULL, ".mkv", "video/webm" +}; + +static const struct lws_protocol_vhost_options pvo_mime_mp4 = { + &pvo_mime_mkv, NULL, ".mp4", "video/mp4" +}; + +static struct lws_http_mount mount_raw_media = { + .mount_next = &mount_hls_live, + .mountpoint = "/media", /* mountpoint URL */ + .origin = NULL, /* set at runtime */ + .def = "index.html", + .origin_protocol = LWSMPRO_FILE, /* serve from dir */ + .mountpoint_len = 6, /* char count */ + .extra_mimetypes = &pvo_mime_mp4, +}; + +static const struct lws_http_mount mount = { + .mount_next = &mount_raw_media, + .mountpoint = "/", /* mountpoint URL */ + .origin = INSTALL_DATADIR "/libwebsockets-test-server/hls/mount-origin", + .def = "index.html", + .origin_protocol = LWSMPRO_FILE, /* serve from dir */ + .mountpoint_len = 1, /* char count */ +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + const char *p; + +#if defined(LWS_WITH_PLUGINS) + /* LWS searches for plugins in this array of paths */ + static const char * const plugin_dirs[] = { + "../../lib", /* For running from build/minimal-examples-lowlevel/... */ + "./lib", + NULL + }; +#endif + + if (lws_cmdline_option(argc, argv, switches[LWS_SW_HELP].sw)) { + lws_switches_print_help(argv[0], switches, LWS_ARRAY_SIZE(switches)); + return 0; + } + + if ((p = lws_cmdline_option(argc, argv, switches[LWS_SW_MEDIA_DIR].sw))) + pvo_media.value = p; + + signal(SIGINT, sigint_handler); + + lws_context_info_defaults(&info, NULL); + lws_cmdline_option_handle_builtin(argc, argv, &info); + + if ((p = lws_cmdline_option(argc, argv, "-i"))) + info.iface = p; + else + info.iface = "lo"; + + if ((p = lws_cmdline_option(argc, argv, "-b"))) + pvo_media.value = p; + + lwsl_user("LWS minimal http server HLS | visit http://localhost:7681\n"); + lwsl_user("Media dir: %s\n", pvo_media.value); + if (info.iface) + lwsl_user("Binding to interface: %s\n", info.iface); + + info.port = 7681; + + info.headers = &pvo_csp; + info.pvo = &pvo; + mount_raw_media.origin = pvo_media.value; + info.mounts = &mount; +#if defined(LWS_WITH_PLUGINS) + info.plugin_dirs = plugin_dirs; +#endif + + info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | + LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + if (lws_cmdline_option(argc, argv, switches[LWS_SW_H2_PRIOR_KNOWLEDGE].sw)) + info.options |= LWS_SERVER_OPTION_H2_PRIOR_KNOWLEDGE; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + /* We create the vhost explicitly so plugins are loaded and attached */ + struct lws_vhost *vh = lws_create_vhost(context, &info); + if (!vh) { + lwsl_err("Failed to create vhost\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 0); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/404.html b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/404.html new file mode 100644 index 0000000000..bcaf47e4d6 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/404.html @@ -0,0 +1,13 @@ + + + + + 404 + + + LWS logo +
+

404

+ Sorry, that file doesn't exist. + + diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/dir.css b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/dir.css new file mode 100644 index 0000000000..d9f91ee200 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/dir.css @@ -0,0 +1,5 @@ +body { font-family: sans-serif; background: #1a1a1a; color: #eee; } +.item { display: inline-block; margin: 10px; text-align: center; background: #333; padding: 10px; border-radius: 8px; } +.thumb { width: 320px; height: 180px; object-fit: cover; background: #000; border-radius: 4px; } +a { color: #4db8ff; text-decoration: none; } +#auth-status { float: right; margin-top: 10px; margin-right: 20px; } diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/favicon.ico b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0cc2e3dff34012ba3d4a7848a7ed17579788ec5 GIT binary patch literal 1406 zcmZQzU<5(|0R}M0U}azs1F|%L7$l?s#Ec9aKoZP=&`9i!<^REA8>%80(yxAC$j<-A zkb5S8;qL6446ipNFl>5#fuVR6L=8goC~GtXMnhmYga9MSfQgBTk&TUw5$JocUP63y z3phA97+G0a8QIy{!BT|y==xb$SQt4uIT@LmnZZ(o_~`mk`Tv1M8w?+DXJCL~kQj^& JqOtKoVgQl$ETjMc literal 0 HcmV?d00001 diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/hls.min.js b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/hls.min.js new file mode 100644 index 0000000000..77bb6b8a81 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/hls.min.js @@ -0,0 +1,2 @@ +!function t(e){var r,i;r=this,i=function(){"use strict";function r(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,i)}return r}function i(t){for(var e=1;et.length)&&(e=t.length);for(var r=0,i=new Array(e);r=t.length?{done:!0}:{done:!1,value:t[i++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function v(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var m={exports:{}};!function(t,e){var r,i,n,a,s;r=/^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/,i=/^(?=([^\/?#]*))\1([^]*)$/,n=/(?:\/|^)\.(?=\/)/g,a=/(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g,s={buildAbsoluteURL:function(t,e,r){if(r=r||{},t=t.trim(),!(e=e.trim())){if(!r.alwaysNormalize)return t;var n=s.parseURL(t);if(!n)throw new Error("Error trying to parse base URL.");return n.path=s.normalizePath(n.path),s.buildURLFromParts(n)}var a=s.parseURL(e);if(!a)throw new Error("Error trying to parse relative URL.");if(a.scheme)return r.alwaysNormalize?(a.path=s.normalizePath(a.path),s.buildURLFromParts(a)):e;var o=s.parseURL(t);if(!o)throw new Error("Error trying to parse base URL.");if(!o.netLoc&&o.path&&"/"!==o.path[0]){var l=i.exec(o.path);o.netLoc=l[1],o.path=l[2]}o.netLoc&&!o.path&&(o.path="/");var u={scheme:o.scheme,netLoc:a.netLoc,path:null,params:a.params,query:a.query,fragment:a.fragment};if(!a.netLoc&&(u.netLoc=o.netLoc,"/"!==a.path[0]))if(a.path){var h=o.path,d=h.substring(0,h.lastIndexOf("/")+1)+a.path;u.path=s.normalizePath(d)}else u.path=o.path,a.params||(u.params=o.params,a.query||(u.query=o.query));return null===u.path&&(u.path=r.alwaysNormalize?s.normalizePath(a.path):a.path),s.buildURLFromParts(u)},parseURL:function(t){var e=r.exec(t);return e?{scheme:e[1]||"",netLoc:e[2]||"",path:e[3]||"",params:e[4]||"",query:e[5]||"",fragment:e[6]||""}:null},normalizePath:function(t){for(t=t.split("").reverse().join("").replace(n,"");t.length!==(t=t.replace(a,"")).length;);return t.split("").reverse().join("")},buildURLFromParts:function(t){return t.scheme+t.netLoc+t.path+t.params+t.query+t.fragment}},t.exports=s}(m);var p=m.exports,y=Number.isFinite||function(t){return"number"==typeof t&&isFinite(t)},E=Number.isSafeInteger||function(t){return"number"==typeof t&&Math.abs(t)<=T},T=Number.MAX_SAFE_INTEGER||9007199254740991,S=function(t){return t.MEDIA_ATTACHING="hlsMediaAttaching",t.MEDIA_ATTACHED="hlsMediaAttached",t.MEDIA_DETACHING="hlsMediaDetaching",t.MEDIA_DETACHED="hlsMediaDetached",t.BUFFER_RESET="hlsBufferReset",t.BUFFER_CODECS="hlsBufferCodecs",t.BUFFER_CREATED="hlsBufferCreated",t.BUFFER_APPENDING="hlsBufferAppending",t.BUFFER_APPENDED="hlsBufferAppended",t.BUFFER_EOS="hlsBufferEos",t.BUFFER_FLUSHING="hlsBufferFlushing",t.BUFFER_FLUSHED="hlsBufferFlushed",t.MANIFEST_LOADING="hlsManifestLoading",t.MANIFEST_LOADED="hlsManifestLoaded",t.MANIFEST_PARSED="hlsManifestParsed",t.LEVEL_SWITCHING="hlsLevelSwitching",t.LEVEL_SWITCHED="hlsLevelSwitched",t.LEVEL_LOADING="hlsLevelLoading",t.LEVEL_LOADED="hlsLevelLoaded",t.LEVEL_UPDATED="hlsLevelUpdated",t.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",t.LEVELS_UPDATED="hlsLevelsUpdated",t.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",t.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",t.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",t.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",t.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",t.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",t.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",t.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",t.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",t.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",t.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",t.CUES_PARSED="hlsCuesParsed",t.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",t.INIT_PTS_FOUND="hlsInitPtsFound",t.FRAG_LOADING="hlsFragLoading",t.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",t.FRAG_LOADED="hlsFragLoaded",t.FRAG_DECRYPTED="hlsFragDecrypted",t.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",t.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",t.FRAG_PARSING_METADATA="hlsFragParsingMetadata",t.FRAG_PARSED="hlsFragParsed",t.FRAG_BUFFERED="hlsFragBuffered",t.FRAG_CHANGED="hlsFragChanged",t.FPS_DROP="hlsFpsDrop",t.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",t.MAX_AUTO_LEVEL_UPDATED="hlsMaxAutoLevelUpdated",t.ERROR="hlsError",t.DESTROYING="hlsDestroying",t.KEY_LOADING="hlsKeyLoading",t.KEY_LOADED="hlsKeyLoaded",t.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",t.BACK_BUFFER_REACHED="hlsBackBufferReached",t.STEERING_MANIFEST_LOADED="hlsSteeringManifestLoaded",t}({}),L=function(t){return t.NETWORK_ERROR="networkError",t.MEDIA_ERROR="mediaError",t.KEY_SYSTEM_ERROR="keySystemError",t.MUX_ERROR="muxError",t.OTHER_ERROR="otherError",t}({}),A=function(t){return t.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",t.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",t.KEY_SYSTEM_NO_SESSION="keySystemNoSession",t.KEY_SYSTEM_NO_CONFIGURED_LICENSE="keySystemNoConfiguredLicense",t.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",t.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED="keySystemServerCertificateRequestFailed",t.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED="keySystemServerCertificateUpdateFailed",t.KEY_SYSTEM_SESSION_UPDATE_FAILED="keySystemSessionUpdateFailed",t.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED="keySystemStatusOutputRestricted",t.KEY_SYSTEM_STATUS_INTERNAL_ERROR="keySystemStatusInternalError",t.MANIFEST_LOAD_ERROR="manifestLoadError",t.MANIFEST_LOAD_TIMEOUT="manifestLoadTimeOut",t.MANIFEST_PARSING_ERROR="manifestParsingError",t.MANIFEST_INCOMPATIBLE_CODECS_ERROR="manifestIncompatibleCodecsError",t.LEVEL_EMPTY_ERROR="levelEmptyError",t.LEVEL_LOAD_ERROR="levelLoadError",t.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",t.LEVEL_PARSING_ERROR="levelParsingError",t.LEVEL_SWITCH_ERROR="levelSwitchError",t.AUDIO_TRACK_LOAD_ERROR="audioTrackLoadError",t.AUDIO_TRACK_LOAD_TIMEOUT="audioTrackLoadTimeOut",t.SUBTITLE_LOAD_ERROR="subtitleTrackLoadError",t.SUBTITLE_TRACK_LOAD_TIMEOUT="subtitleTrackLoadTimeOut",t.FRAG_LOAD_ERROR="fragLoadError",t.FRAG_LOAD_TIMEOUT="fragLoadTimeOut",t.FRAG_DECRYPT_ERROR="fragDecryptError",t.FRAG_PARSING_ERROR="fragParsingError",t.FRAG_GAP="fragGap",t.REMUX_ALLOC_ERROR="remuxAllocError",t.KEY_LOAD_ERROR="keyLoadError",t.KEY_LOAD_TIMEOUT="keyLoadTimeOut",t.BUFFER_ADD_CODEC_ERROR="bufferAddCodecError",t.BUFFER_INCOMPATIBLE_CODECS_ERROR="bufferIncompatibleCodecsError",t.BUFFER_APPEND_ERROR="bufferAppendError",t.BUFFER_APPENDING_ERROR="bufferAppendingError",t.BUFFER_STALLED_ERROR="bufferStalledError",t.BUFFER_FULL_ERROR="bufferFullError",t.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",t.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",t.INTERNAL_EXCEPTION="internalException",t.INTERNAL_ABORTED="aborted",t.UNKNOWN="unknown",t}({}),R=function(){},k={trace:R,debug:R,log:R,warn:R,info:R,error:R},b=k;function D(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i"):R}(e)}))}function I(t,e){if("object"==typeof console&&!0===t||"object"==typeof t){D(t,"debug","log","info","warn","error");try{b.log('Debug logs enabled for "'+e+'" in hls.js version 1.5.8')}catch(t){b=k}}else b=k}var w=b,C=/^(\d+)x(\d+)$/,_=/(.+?)=(".*?"|.*?)(?:,|$)/g,x=function(){function t(e){"string"==typeof e&&(e=t.parseAttrList(e)),o(this,e)}var e=t.prototype;return e.decimalInteger=function(t){var e=parseInt(this[t],10);return e>Number.MAX_SAFE_INTEGER?1/0:e},e.hexadecimalInteger=function(t){if(this[t]){var e=(this[t]||"0x").slice(2);e=(1&e.length?"0":"")+e;for(var r=new Uint8Array(e.length/2),i=0;iNumber.MAX_SAFE_INTEGER?1/0:e},e.decimalFloatingPoint=function(t){return parseFloat(this[t])},e.optionalFloat=function(t,e){var r=this[t];return r?parseFloat(r):e},e.enumeratedString=function(t){return this[t]},e.bool=function(t){return"YES"===this[t]},e.decimalResolution=function(t){var e=C.exec(this[t]);if(null!==e)return{width:parseInt(e[1],10),height:parseInt(e[2],10)}},t.parseAttrList=function(t){var e,r={};for(_.lastIndex=0;null!==(e=_.exec(t));){var i=e[2];0===i.indexOf('"')&&i.lastIndexOf('"')===i.length-1&&(i=i.slice(1,-1)),r[e[1].trim()]=i}return r},s(t,[{key:"clientAttrs",get:function(){return Object.keys(this).filter((function(t){return"X-"===t.substring(0,2)}))}}]),t}();function P(t){return"SCTE35-OUT"===t||"SCTE35-IN"===t}var F=function(){function t(t,e){if(this.attr=void 0,this._startDate=void 0,this._endDate=void 0,this._badValueForSameId=void 0,e){var r=e.attr;for(var i in r)if(Object.prototype.hasOwnProperty.call(t,i)&&t[i]!==r[i]){w.warn('DATERANGE tag attribute: "'+i+'" does not match for tags with ID: "'+t.ID+'"'),this._badValueForSameId=i;break}t=o(new x({}),r,t)}if(this.attr=t,this._startDate=new Date(t["START-DATE"]),"END-DATE"in this.attr){var n=new Date(this.attr["END-DATE"]);y(n.getTime())&&(this._endDate=n)}}return s(t,[{key:"id",get:function(){return this.attr.ID}},{key:"class",get:function(){return this.attr.CLASS}},{key:"startDate",get:function(){return this._startDate}},{key:"endDate",get:function(){if(this._endDate)return this._endDate;var t=this.duration;return null!==t?new Date(this._startDate.getTime()+1e3*t):null}},{key:"duration",get:function(){if("DURATION"in this.attr){var t=this.attr.decimalFloatingPoint("DURATION");if(y(t))return t}else if(this._endDate)return(this._endDate.getTime()-this._startDate.getTime())/1e3;return null}},{key:"plannedDuration",get:function(){return"PLANNED-DURATION"in this.attr?this.attr.decimalFloatingPoint("PLANNED-DURATION"):null}},{key:"endOnNext",get:function(){return this.attr.bool("END-ON-NEXT")}},{key:"isValid",get:function(){return!!this.id&&!this._badValueForSameId&&y(this.startDate.getTime())&&(null===this.duration||this.duration>=0)&&(!this.endOnNext||!!this.class)}}]),t}(),M=function(){this.aborted=!1,this.loaded=0,this.retry=0,this.total=0,this.chunkCount=0,this.bwEstimate=0,this.loading={start:0,first:0,end:0},this.parsing={start:0,end:0},this.buffering={start:0,first:0,end:0}},O="audio",N="video",U="audiovideo",B=function(){function t(t){var e;this._byteRange=null,this._url=null,this.baseurl=void 0,this.relurl=void 0,this.elementaryStreams=((e={})[O]=null,e[N]=null,e[U]=null,e),this.baseurl=t}return t.prototype.setByteRange=function(t,e){var r,i=t.split("@",2);r=1===i.length?(null==e?void 0:e.byteRangeEndOffset)||0:parseInt(i[1]),this._byteRange=[r,parseInt(i[0])+r]},s(t,[{key:"byteRange",get:function(){return this._byteRange?this._byteRange:[]}},{key:"byteRangeStartOffset",get:function(){return this.byteRange[0]}},{key:"byteRangeEndOffset",get:function(){return this.byteRange[1]}},{key:"url",get:function(){return!this._url&&this.baseurl&&this.relurl&&(this._url=p.buildAbsoluteURL(this.baseurl,this.relurl,{alwaysNormalize:!0})),this._url||""},set:function(t){this._url=t}}]),t}(),G=function(t){function e(e,r){var i;return(i=t.call(this,r)||this)._decryptdata=null,i.rawProgramDateTime=null,i.programDateTime=null,i.tagList=[],i.duration=0,i.sn=0,i.levelkeys=void 0,i.type=void 0,i.loader=null,i.keyLoader=null,i.level=-1,i.cc=0,i.startPTS=void 0,i.endPTS=void 0,i.startDTS=void 0,i.endDTS=void 0,i.start=0,i.deltaPTS=void 0,i.maxStartPTS=void 0,i.minEndPTS=void 0,i.stats=new M,i.data=void 0,i.bitrateTest=!1,i.title=null,i.initSegment=null,i.endList=void 0,i.gap=void 0,i.urlId=0,i.type=e,i}l(e,t);var r=e.prototype;return r.setKeyFormat=function(t){if(this.levelkeys){var e=this.levelkeys[t];e&&!this._decryptdata&&(this._decryptdata=e.getDecryptData(this.sn))}},r.abortRequests=function(){var t,e;null==(t=this.loader)||t.abort(),null==(e=this.keyLoader)||e.abort()},r.setElementaryStreamInfo=function(t,e,r,i,n,a){void 0===a&&(a=!1);var s=this.elementaryStreams,o=s[t];o?(o.startPTS=Math.min(o.startPTS,e),o.endPTS=Math.max(o.endPTS,r),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,n)):s[t]={startPTS:e,endPTS:r,startDTS:i,endDTS:n,partial:a}},r.clearElementaryStreamInfo=function(){var t=this.elementaryStreams;t[O]=null,t[N]=null,t[U]=null},s(e,[{key:"decryptdata",get:function(){if(!this.levelkeys&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkeys&&!this.levelkeys.NONE){var t=this.levelkeys.identity;if(t)this._decryptdata=t.getDecryptData(this.sn);else{var e=Object.keys(this.levelkeys);if(1===e.length)return this._decryptdata=this.levelkeys[e[0]].getDecryptData(this.sn)}}return this._decryptdata}},{key:"end",get:function(){return this.start+this.duration}},{key:"endProgramDateTime",get:function(){if(null===this.programDateTime)return null;if(!y(this.programDateTime))return null;var t=y(this.duration)?this.duration:0;return this.programDateTime+1e3*t}},{key:"encrypted",get:function(){var t;if(null!=(t=this._decryptdata)&&t.encrypted)return!0;if(this.levelkeys){var e=Object.keys(this.levelkeys),r=e.length;if(r>1||1===r&&this.levelkeys[e[0]].encrypted)return!0}return!1}}]),e}(B),K=function(t){function e(e,r,i,n,a){var s;(s=t.call(this,i)||this).fragOffset=0,s.duration=0,s.gap=!1,s.independent=!1,s.relurl=void 0,s.fragment=void 0,s.index=void 0,s.stats=new M,s.duration=e.decimalFloatingPoint("DURATION"),s.gap=e.bool("GAP"),s.independent=e.bool("INDEPENDENT"),s.relurl=e.enumeratedString("URI"),s.fragment=r,s.index=n;var o=e.enumeratedString("BYTERANGE");return o&&s.setByteRange(o,a),a&&(s.fragOffset=a.fragOffset+a.duration),s}return l(e,t),s(e,[{key:"start",get:function(){return this.fragment.start+this.fragOffset}},{key:"end",get:function(){return this.start+this.duration}},{key:"loaded",get:function(){var t=this.elementaryStreams;return!!(t.audio||t.video||t.audiovideo)}}]),e}(B),H=function(){function t(t){this.PTSKnown=!1,this.alignedSliding=!1,this.averagetargetduration=void 0,this.endCC=0,this.endSN=0,this.fragments=void 0,this.fragmentHint=void 0,this.partList=null,this.dateRanges=void 0,this.live=!0,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!0,this.availabilityDelay=void 0,this.misses=0,this.startCC=0,this.startSN=0,this.startTimeOffset=null,this.targetduration=0,this.totalduration=0,this.type=null,this.url=void 0,this.m3u8="",this.version=null,this.canBlockReload=!1,this.canSkipUntil=0,this.canSkipDateRanges=!1,this.skippedSegments=0,this.recentlyRemovedDateranges=void 0,this.partHoldBack=0,this.holdBack=0,this.partTarget=0,this.preloadHint=void 0,this.renditionReports=void 0,this.tuneInGoal=0,this.deltaUpdateFailed=void 0,this.driftStartTime=0,this.driftEndTime=0,this.driftStart=0,this.driftEnd=0,this.encryptedFragments=void 0,this.playlistParsingError=null,this.variableList=null,this.hasVariableRefs=!1,this.fragments=[],this.encryptedFragments=[],this.dateRanges={},this.url=t}return t.prototype.reloaded=function(t){if(!t)return this.advanced=!0,void(this.updated=!0);var e=this.lastPartSn-t.lastPartSn,r=this.lastPartIndex-t.lastPartIndex;this.updated=this.endSN!==t.endSN||!!r||!!e||!this.live,this.advanced=this.endSN>t.endSN||e>0||0===e&&r>0,this.updated||this.advanced?this.misses=Math.floor(.6*t.misses):this.misses=t.misses+1,this.availabilityDelay=t.availabilityDelay},s(t,[{key:"hasProgramDateTime",get:function(){return!!this.fragments.length&&y(this.fragments[this.fragments.length-1].programDateTime)}},{key:"levelTargetDuration",get:function(){return this.averagetargetduration||this.targetduration||10}},{key:"drift",get:function(){var t=this.driftEndTime-this.driftStartTime;return t>0?1e3*(this.driftEnd-this.driftStart)/t:1}},{key:"edge",get:function(){return this.partEnd||this.fragmentEnd}},{key:"partEnd",get:function(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].end:this.fragmentEnd}},{key:"fragmentEnd",get:function(){var t;return null!=(t=this.fragments)&&t.length?this.fragments[this.fragments.length-1].end:0}},{key:"age",get:function(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}},{key:"lastPartIndex",get:function(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].index:-1}},{key:"lastPartSn",get:function(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}]),t}();function V(t){return Uint8Array.from(atob(t),(function(t){return t.charCodeAt(0)}))}function Y(t){var e,r,i=t.split(":"),n=null;if("data"===i[0]&&2===i.length){var a=i[1].split(";"),s=a[a.length-1].split(",");if(2===s.length){var o="base64"===s[0],l=s[1];o?(a.splice(-1,1),n=V(l)):(e=W(l).subarray(0,16),(r=new Uint8Array(16)).set(e,16-e.length),n=r)}}return n}function W(t){return Uint8Array.from(unescape(encodeURIComponent(t)),(function(t){return t.charCodeAt(0)}))}var j="undefined"!=typeof self?self:void 0,q={CLEARKEY:"org.w3.clearkey",FAIRPLAY:"com.apple.fps",PLAYREADY:"com.microsoft.playready",WIDEVINE:"com.widevine.alpha"},X="org.w3.clearkey",z="com.apple.streamingkeydelivery",Q="com.microsoft.playready",J="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";function $(t){switch(t){case z:return q.FAIRPLAY;case Q:return q.PLAYREADY;case J:return q.WIDEVINE;case X:return q.CLEARKEY}}var Z="edef8ba979d64acea3c827dcd51d21ed";function tt(t){switch(t){case q.FAIRPLAY:return z;case q.PLAYREADY:return Q;case q.WIDEVINE:return J;case q.CLEARKEY:return X}}function et(t){var e=t.drmSystems,r=t.widevineLicenseUrl,i=e?[q.FAIRPLAY,q.WIDEVINE,q.PLAYREADY,q.CLEARKEY].filter((function(t){return!!e[t]})):[];return!i[q.WIDEVINE]&&r&&i.push(q.WIDEVINE),i}var rt,it=null!=j&&null!=(rt=j.navigator)&&rt.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;function nt(t,e,r){return Uint8Array.prototype.slice?t.slice(e,r):new Uint8Array(Array.prototype.slice.call(t,e,r))}var at,st=function(t,e){return e+10<=t.length&&73===t[e]&&68===t[e+1]&&51===t[e+2]&&t[e+3]<255&&t[e+4]<255&&t[e+6]<128&&t[e+7]<128&&t[e+8]<128&&t[e+9]<128},ot=function(t,e){return e+10<=t.length&&51===t[e]&&68===t[e+1]&&73===t[e+2]&&t[e+3]<255&&t[e+4]<255&&t[e+6]<128&&t[e+7]<128&&t[e+8]<128&&t[e+9]<128},lt=function(t,e){for(var r=e,i=0;st(t,e);)i+=10,i+=ut(t,e+6),ot(t,e+10)&&(i+=10),e+=i;if(i>0)return t.subarray(r,r+i)},ut=function(t,e){var r=0;return r=(127&t[e])<<21,r|=(127&t[e+1])<<14,r|=(127&t[e+2])<<7,r|=127&t[e+3]},ht=function(t,e){return st(t,e)&&ut(t,e+6)+10<=t.length-e},dt=function(t){for(var e=gt(t),r=0;r>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:u+=String.fromCharCode(a);break;case 12:case 13:s=t[h++],u+=String.fromCharCode((31&a)<<6|63&s);break;case 14:s=t[h++],o=t[h++],u+=String.fromCharCode((15&a)<<12|(63&s)<<6|(63&o)<<0)}}return u};function St(){if(!navigator.userAgent.includes("PlayStation 4"))return at||void 0===self.TextDecoder||(at=new self.TextDecoder("utf-8")),at}var Lt=function(t){for(var e="",r=0;r>24,t[e+1]=r>>16&255,t[e+2]=r>>8&255,t[e+3]=255&r}function xt(t,e){var r=[];if(!e.length)return r;for(var i=t.byteLength,n=0;n1?n+a:i;if(bt(t.subarray(n+4,n+8))===e[0])if(1===e.length)r.push(t.subarray(n+8,s));else{var o=xt(t.subarray(n+8,s),e.slice(1));o.length&&Rt.apply(r,o)}n=s}return r}function Pt(t){var e=[],r=t[0],i=8,n=It(t,i);i+=4;var a=0,s=0;0===r?(a=It(t,i),s=It(t,i+4),i+=8):(a=wt(t,i),s=wt(t,i+8),i+=16),i+=2;var o=t.length+s,l=Dt(t,i);i+=2;for(var u=0;u>>31)return w.warn("SIDX has hierarchical references (not supported)"),null;var f=It(t,h);h+=4,e.push({referenceSize:c,subsegmentDuration:f,info:{duration:f/n,start:o,end:o+c-1}}),o+=c,i=h+=4}return{earliestPresentationTime:a,timescale:n,version:r,referencesCount:l,references:e}}function Ft(t){for(var e=[],r=xt(t,["moov","trak"]),n=0;n12){var h=4;if(3!==u[h++])break;h=Ot(u,h),h+=2;var d=u[h++];if(128&d&&(h+=2),64&d&&(h+=u[h++]),4!==u[h++])break;h=Ot(u,h);var c=u[h++];if(64!==c)break;if(n+="."+Nt(c),h+=12,5!==u[h++])break;h=Ot(u,h);var f=u[h++],g=(248&f)>>3;31===g&&(g+=1+((7&f)<<3)+((224&u[h])>>5)),n+="."+g}break;case"hvc1":case"hev1":var v=xt(r,["hvcC"])[0],m=v[1],p=["","A","B","C"][m>>6],y=31&m,E=It(v,2),T=(32&m)>>5?"H":"L",S=v[12],L=v.subarray(6,12);n+="."+p+y,n+="."+E.toString(16).toUpperCase(),n+="."+T+S;for(var A="",R=L.length;R--;){var k=L[R];(k||A)&&(A="."+k.toString(16).toUpperCase()+A)}n+=A;break;case"dvh1":case"dvhe":var b=xt(r,["dvcC"])[0],D=b[2]>>1&127,I=b[2]<<5&32|b[3]>>3&31;n+="."+Ut(D)+"."+Ut(I);break;case"vp09":var w=xt(r,["vpcC"])[0],C=w[4],_=w[5],x=w[6]>>4&15;n+="."+Ut(C)+"."+Ut(_)+"."+Ut(x);break;case"av01":var P=xt(r,["av1C"])[0],F=P[1]>>>5,M=31&P[1],O=P[2]>>>7?"H":"M",N=(64&P[2])>>6,U=(32&P[2])>>5,B=2===F&&N?U?12:10:N?10:8,G=(16&P[2])>>4,K=(8&P[2])>>3,H=(4&P[2])>>2,V=3&P[2];n+="."+F+"."+Ut(M)+O+"."+Ut(B)+"."+G+"."+K+H+V+"."+Ut(1)+"."+Ut(1)+"."+Ut(1)+".0"}return{codec:n,encrypted:a}}function Ot(t,e){for(var r=e+5;128&t[e++]&&e>1&63;return 39===r||40===r}return 6==(31&e)}function Yt(t,e,r,i){var n=Wt(t),a=0;a+=e;for(var s=0,o=0,l=0;a=n.length)break;s+=l=n[a++]}while(255===l);o=0;do{if(a>=n.length)break;o+=l=n[a++]}while(255===l);var u=n.length-a,h=a;if(ou){w.error("Malformed SEI payload. "+o+" is too small, only "+u+" bytes left to parse.");break}if(4===s){if(181===n[h++]){var d=Dt(n,h);if(h+=2,49===d){var c=It(n,h);if(h+=4,1195456820===c){var f=n[h++];if(3===f){var g=n[h++],v=64&g,m=v?2+3*(31&g):0,p=new Uint8Array(m);if(v){p[0]=g;for(var y=1;y16){for(var E=[],T=0;T<16;T++){var S=n[h++].toString(16);E.push(1==S.length?"0"+S:S),3!==T&&5!==T&&7!==T&&9!==T||E.push("-")}for(var L=o-16,A=new Uint8Array(L),R=0;R0?(a=new Uint8Array(4),e.length>0&&new DataView(a.buffer).setUint32(0,e.length,!1)):a=new Uint8Array;var l=new Uint8Array(4);return r&&r.byteLength>0&&new DataView(l.buffer).setUint32(0,r.byteLength,!1),function(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i>24&255,o[1]=a>>16&255,o[2]=a>>8&255,o[3]=255&a,o.set(t,4),s=0,a=8;s>8*(15-r)&255;return e}(e);return new t(this.method,this.uri,"identity",this.keyFormatVersions,r)}var i=Y(this.uri);if(i)switch(this.keyFormat){case J:this.pssh=i,i.length>=22&&(this.keyId=i.subarray(i.length-22,i.length-6));break;case Q:var n=new Uint8Array([154,4,240,121,152,64,66,134,171,146,230,91,224,136,95,149]);this.pssh=jt(n,null,i);var a=new Uint16Array(i.buffer,i.byteOffset,i.byteLength/2),s=String.fromCharCode.apply(null,Array.from(a)),o=s.substring(s.indexOf("<"),s.length),l=(new DOMParser).parseFromString(o,"text/xml").getElementsByTagName("KID")[0];if(l){var u=l.childNodes[0]?l.childNodes[0].nodeValue:l.getAttribute("VALUE");if(u){var h=V(u).subarray(0,16);!function(t){var e=function(t,e,r){var i=t[e];t[e]=t[r],t[r]=i};e(t,0,3),e(t,1,2),e(t,4,5),e(t,6,7)}(h),this.keyId=h}}break;default:var d=i.subarray(0,16);if(16!==d.length){var c=new Uint8Array(16);c.set(d,16-d.length),d=c}this.keyId=d}if(!this.keyId||16!==this.keyId.byteLength){var f=qt[this.uri];if(!f){var g=Object.keys(qt).length%Number.MAX_SAFE_INTEGER;f=new Uint8Array(16),new DataView(f.buffer,12,4).setUint32(0,g),qt[this.uri]=f}this.keyId=f}return this},t}(),zt=/\{\$([a-zA-Z0-9-_]+)\}/g;function Qt(t){return zt.test(t)}function Jt(t,e,r){if(null!==t.variableList||t.hasVariableRefs)for(var i=r.length;i--;){var n=r[i],a=e[n];a&&(e[n]=$t(t,a))}}function $t(t,e){if(null!==t.variableList||t.hasVariableRefs){var r=t.variableList;return e.replace(zt,(function(e){var i=e.substring(2,e.length-1),n=null==r?void 0:r[i];return void 0===n?(t.playlistParsingError||(t.playlistParsingError=new Error('Missing preceding EXT-X-DEFINE tag for Variable Reference: "'+i+'"')),e):n}))}return e}function Zt(t,e,r){var i,n,a=t.variableList;if(a||(t.variableList=a={}),"QUERYPARAM"in e){i=e.QUERYPARAM;try{var s=new self.URL(r).searchParams;if(!s.has(i))throw new Error('"'+i+'" does not match any query parameter in URI: "'+r+'"');n=s.get(i)}catch(e){t.playlistParsingError||(t.playlistParsingError=new Error("EXT-X-DEFINE QUERYPARAM: "+e.message))}}else i=e.NAME,n=e.VALUE;i in a?t.playlistParsingError||(t.playlistParsingError=new Error('EXT-X-DEFINE duplicate Variable Name declarations: "'+i+'"')):a[i]=n||""}function te(t,e,r){var i=e.IMPORT;if(r&&i in r){var n=t.variableList;n||(t.variableList=n={}),n[i]=r[i]}else t.playlistParsingError||(t.playlistParsingError=new Error('EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "'+i+'"'))}function ee(t){if(void 0===t&&(t=!0),"undefined"!=typeof self)return(t||!self.MediaSource)&&self.ManagedMediaSource||self.MediaSource||self.WebKitMediaSource}var re={audio:{a3ds:1,"ac-3":.95,"ac-4":1,alac:.9,alaw:1,dra1:1,"dts+":1,"dts-":1,dtsc:1,dtse:1,dtsh:1,"ec-3":.9,enca:1,fLaC:.9,flac:.9,FLAC:.9,g719:1,g726:1,m4ae:1,mha1:1,mha2:1,mhm1:1,mhm2:1,mlpa:1,mp4a:1,"raw ":1,Opus:1,opus:1,samr:1,sawb:1,sawp:1,sevc:1,sqcp:1,ssmv:1,twos:1,ulaw:1},video:{avc1:1,avc2:1,avc3:1,avc4:1,avcp:1,av01:.8,drac:1,dva1:1,dvav:1,dvh1:.7,dvhe:.7,encv:1,hev1:.75,hvc1:.75,mjp2:1,mp4v:1,mvc1:1,mvc2:1,mvc3:1,mvc4:1,resv:1,rv60:1,s263:1,svc1:1,svc2:1,"vc-1":1,vp08:1,vp09:.9},text:{stpp:1,wvtt:1}};function ie(t,e,r){return void 0===r&&(r=!0),!t.split(",").some((function(t){return!ne(t,e,r)}))}function ne(t,e,r){var i;void 0===r&&(r=!0);var n=ee(r);return null!=(i=null==n?void 0:n.isTypeSupported(ae(t,e)))&&i}function ae(t,e){return e+'/mp4;codecs="'+t+'"'}function se(t){if(t){var e=t.substring(0,4);return re.video[e]}return 2}function oe(t){return t.split(",").reduce((function(t,e){var r=re.video[e];return r?(2*r+t)/(t?3:2):(re.audio[e]+t)/(t?2:1)}),0)}var le={},ue=/flac|opus/i;function he(t,e){return void 0===e&&(e=!0),t.replace(ue,(function(t){return function(t,e){if(void 0===e&&(e=!0),le[t])return le[t];for(var r={flac:["flac","fLaC","FLAC"],opus:["opus","Opus"]}[t],i=0;i0&&a.length0&&X.bool("CAN-SKIP-DATERANGES"),h.partHoldBack=X.optionalFloat("PART-HOLD-BACK",0),h.holdBack=X.optionalFloat("HOLD-BACK",0);break;case"PART-INF":var z=new x(I);h.partTarget=z.decimalFloatingPoint("PART-TARGET");break;case"PART":var Q=h.partList;Q||(Q=h.partList=[]);var J=g>0?Q[Q.length-1]:void 0,$=g++,Z=new x(I);Jt(h,Z,["BYTERANGE","URI"]);var tt=new K(Z,E,e,$,J);Q.push(tt),E.duration+=tt.duration;break;case"PRELOAD-HINT":var et=new x(I);Jt(h,et,["URI"]),h.preloadHint=et;break;case"RENDITION-REPORT":var rt=new x(I);Jt(h,rt,["URI"]),h.renditionReports=h.renditionReports||[],h.renditionReports.push(rt);break;default:w.warn("line parsed but not handled: "+s)}}}p&&!p.relurl?(d.pop(),v-=p.duration,h.partList&&(h.fragmentHint=p)):h.partList&&(Le(E,p),E.cc=m,h.fragmentHint=E,u&&Re(E,u,h));var it=d.length,nt=d[0],at=d[it-1];if((v+=h.skippedSegments*h.targetduration)>0&&it&&at){h.averagetargetduration=v/it;var st=at.sn;h.endSN="initSegment"!==st?st:0,h.live||(at.endList=!0),nt&&(h.startCC=nt.cc)}else h.endSN=0,h.startCC=0;return h.fragmentHint&&(v+=h.fragmentHint.duration),h.totalduration=v,h.endCC=m,T>0&&function(t,e){for(var r=t[e],i=e;i--;){var n=t[i];if(!n)return;n.programDateTime=r.programDateTime-1e3*n.duration,r=n}}(d,T),h},t}();function ye(t,e,r){var i,n,a=new x(t);Jt(r,a,["KEYFORMAT","KEYFORMATVERSIONS","URI","IV","URI"]);var s=null!=(i=a.METHOD)?i:"",o=a.URI,l=a.hexadecimalInteger("IV"),u=a.KEYFORMATVERSIONS,h=null!=(n=a.KEYFORMAT)?n:"identity";o&&a.IV&&!l&&w.error("Invalid IV: "+a.IV);var d=o?pe.resolve(o,e):"",c=(u||"1").split("/").map(Number).filter(Number.isFinite);return new Xt(s,d,h,c,l)}function Ee(t){var e=new x(t).decimalFloatingPoint("TIME-OFFSET");return y(e)?e:null}function Te(t,e){var r=(t||"").split(/[ ,]+/).filter((function(t){return t}));["video","audio","text"].forEach((function(t){var i=r.filter((function(e){return function(t,e){var r=re[e];return!!r&&!!r[t.slice(0,4)]}(e,t)}));i.length&&(e[t+"Codec"]=i.join(","),r=r.filter((function(t){return-1===i.indexOf(t)})))})),e.unknownCodecs=r}function Se(t,e,r){var i=e[r];i&&(t[r]=i)}function Le(t,e){t.rawProgramDateTime?t.programDateTime=Date.parse(t.rawProgramDateTime):null!=e&&e.programDateTime&&(t.programDateTime=e.endProgramDateTime),y(t.programDateTime)||(t.programDateTime=null,t.rawProgramDateTime=null)}function Ae(t,e,r,i){t.relurl=e.URI,e.BYTERANGE&&t.setByteRange(e.BYTERANGE),t.level=r,t.sn="initSegment",i&&(t.levelkeys=i),t.initSegment=null}function Re(t,e,r){t.levelkeys=e;var i=r.encryptedFragments;i.length&&i[i.length-1].levelkeys===e||!Object.keys(e).some((function(t){return e[t].isCommonEncryption}))||i.push(t)}var ke="manifest",be="level",De="audioTrack",Ie="subtitleTrack",we="main",Ce="audio",_e="subtitle";function xe(t){switch(t.type){case De:return Ce;case Ie:return _e;default:return we}}function Pe(t,e){var r=t.url;return void 0!==r&&0!==r.indexOf("data:")||(r=e.url),r}var Fe=function(){function t(t){this.hls=void 0,this.loaders=Object.create(null),this.variableList=null,this.hls=t,this.registerListeners()}var e=t.prototype;return e.startLoad=function(t){},e.stopLoad=function(){this.destroyInternalLoaders()},e.registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.LEVEL_LOADING,this.onLevelLoading,this),t.on(S.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.on(S.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.unregisterListeners=function(){var t=this.hls;t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.LEVEL_LOADING,this.onLevelLoading,this),t.off(S.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.off(S.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)},e.createInternalLoader=function(t){var e=this.hls.config,r=e.pLoader,i=e.loader,n=new(r||i)(e);return this.loaders[t.type]=n,n},e.getInternalLoader=function(t){return this.loaders[t.type]},e.resetInternalLoader=function(t){this.loaders[t]&&delete this.loaders[t]},e.destroyInternalLoaders=function(){for(var t in this.loaders){var e=this.loaders[t];e&&e.destroy(),this.resetInternalLoader(t)}},e.destroy=function(){this.variableList=null,this.unregisterListeners(),this.destroyInternalLoaders()},e.onManifestLoading=function(t,e){var r=e.url;this.variableList=null,this.load({id:null,level:0,responseType:"text",type:ke,url:r,deliveryDirectives:null})},e.onLevelLoading=function(t,e){var r=e.id,i=e.level,n=e.pathwayId,a=e.url,s=e.deliveryDirectives;this.load({id:r,level:i,pathwayId:n,responseType:"text",type:be,url:a,deliveryDirectives:s})},e.onAudioTrackLoading=function(t,e){var r=e.id,i=e.groupId,n=e.url,a=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:De,url:n,deliveryDirectives:a})},e.onSubtitleTrackLoading=function(t,e){var r=e.id,i=e.groupId,n=e.url,a=e.deliveryDirectives;this.load({id:r,groupId:i,level:null,responseType:"text",type:Ie,url:n,deliveryDirectives:a})},e.load=function(t){var e,r,i,n=this,a=this.hls.config,s=this.getInternalLoader(t);if(s){var l=s.context;if(l&&l.url===t.url&&l.level===t.level)return void w.trace("[playlist-loader]: playlist request ongoing");w.log("[playlist-loader]: aborting previous loader for type: "+t.type),s.abort()}if(r=t.type===ke?a.manifestLoadPolicy.default:o({},a.playlistLoadPolicy.default,{timeoutRetry:null,errorRetry:null}),s=this.createInternalLoader(t),y(null==(e=t.deliveryDirectives)?void 0:e.part)&&(t.type===be&&null!==t.level?i=this.hls.levels[t.level].details:t.type===De&&null!==t.id?i=this.hls.audioTracks[t.id].details:t.type===Ie&&null!==t.id&&(i=this.hls.subtitleTracks[t.id].details),i)){var u=i.partTarget,h=i.targetduration;if(u&&h){var d=1e3*Math.max(3*u,.8*h);r=o({},r,{maxTimeToFirstByteMs:Math.min(d,r.maxTimeToFirstByteMs),maxLoadTimeMs:Math.min(d,r.maxTimeToFirstByteMs)})}}var c=r.errorRetry||r.timeoutRetry||{},f={loadPolicy:r,timeout:r.maxLoadTimeMs,maxRetry:c.maxNumRetry||0,retryDelay:c.retryDelayMs||0,maxRetryDelay:c.maxRetryDelayMs||0},g={onSuccess:function(t,e,r,i){var a=n.getInternalLoader(r);n.resetInternalLoader(r.type);var s=t.data;0===s.indexOf("#EXTM3U")?(e.parsing.start=performance.now(),pe.isMediaPlaylist(s)?n.handleTrackOrLevelPlaylist(t,e,r,i||null,a):n.handleMasterPlaylist(t,e,r,i)):n.handleManifestParsingError(t,r,new Error("no EXTM3U delimiter"),i||null,e)},onError:function(t,e,r,i){n.handleNetworkError(e,r,!1,t,i)},onTimeout:function(t,e,r){n.handleNetworkError(e,r,!0,void 0,t)}};s.load(t,f,g)},e.handleMasterPlaylist=function(t,e,r,i){var n=this.hls,a=t.data,s=Pe(t,r),o=pe.parseMasterPlaylist(a,s);if(o.playlistParsingError)this.handleManifestParsingError(t,r,o.playlistParsingError,i,e);else{var l=o.contentSteering,u=o.levels,h=o.sessionData,d=o.sessionKeys,c=o.startTimeOffset,f=o.variableList;this.variableList=f;var g=pe.parseMasterPlaylistMedia(a,s,o),v=g.AUDIO,m=void 0===v?[]:v,p=g.SUBTITLES,y=g["CLOSED-CAPTIONS"];m.length&&(m.some((function(t){return!t.url}))||!u[0].audioCodec||u[0].attrs.AUDIO||(w.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),m.unshift({type:"main",name:"main",groupId:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new x({}),bitrate:0,url:""}))),n.trigger(S.MANIFEST_LOADED,{levels:u,audioTracks:m,subtitles:p,captions:y,contentSteering:l,url:s,stats:e,networkDetails:i,sessionData:h,sessionKeys:d,startTimeOffset:c,variableList:f})}},e.handleTrackOrLevelPlaylist=function(t,e,r,i,n){var a=this.hls,s=r.id,o=r.level,l=r.type,u=Pe(t,r),h=y(o)?o:y(s)?s:0,d=xe(r),c=pe.parseLevelPlaylist(t.data,u,h,d,0,this.variableList);if(l===ke){var f={attrs:new x({}),bitrate:0,details:c,name:"",url:u};a.trigger(S.MANIFEST_LOADED,{levels:[f],audioTracks:[],url:u,stats:e,networkDetails:i,sessionData:null,sessionKeys:null,contentSteering:null,startTimeOffset:null,variableList:null})}e.parsing.end=performance.now(),r.levelDetails=c,this.handlePlaylistLoaded(c,t,e,r,i,n)},e.handleManifestParsingError=function(t,e,r,i,n){this.hls.trigger(S.ERROR,{type:L.NETWORK_ERROR,details:A.MANIFEST_PARSING_ERROR,fatal:e.type===ke,url:t.url,err:r,error:r,reason:r.message,response:t,context:e,networkDetails:i,stats:n})},e.handleNetworkError=function(t,e,r,n,a){void 0===r&&(r=!1);var s="A network "+(r?"timeout":"error"+(n?" (status "+n.code+")":""))+" occurred while loading "+t.type;t.type===be?s+=": "+t.level+" id: "+t.id:t.type!==De&&t.type!==Ie||(s+=" id: "+t.id+' group-id: "'+t.groupId+'"');var o=new Error(s);w.warn("[playlist-loader]: "+s);var l=A.UNKNOWN,u=!1,h=this.getInternalLoader(t);switch(t.type){case ke:l=r?A.MANIFEST_LOAD_TIMEOUT:A.MANIFEST_LOAD_ERROR,u=!0;break;case be:l=r?A.LEVEL_LOAD_TIMEOUT:A.LEVEL_LOAD_ERROR,u=!1;break;case De:l=r?A.AUDIO_TRACK_LOAD_TIMEOUT:A.AUDIO_TRACK_LOAD_ERROR,u=!1;break;case Ie:l=r?A.SUBTITLE_TRACK_LOAD_TIMEOUT:A.SUBTITLE_LOAD_ERROR,u=!1}h&&this.resetInternalLoader(t.type);var d={type:L.NETWORK_ERROR,details:l,fatal:u,url:t.url,loader:h,context:t,error:o,networkDetails:e,stats:a};if(n){var c=(null==e?void 0:e.url)||t.url;d.response=i({url:c,data:void 0},n)}this.hls.trigger(S.ERROR,d)},e.handlePlaylistLoaded=function(t,e,r,i,n,a){var s=this.hls,o=i.type,l=i.level,u=i.id,h=i.groupId,d=i.deliveryDirectives,c=Pe(e,i),f=xe(i),g="number"==typeof i.level&&f===we?l:void 0;if(t.fragments.length){t.targetduration||(t.playlistParsingError=new Error("Missing Target Duration"));var v=t.playlistParsingError;if(v)s.trigger(S.ERROR,{type:L.NETWORK_ERROR,details:A.LEVEL_PARSING_ERROR,fatal:!1,url:c,error:v,reason:v.message,response:e,context:i,level:g,parent:f,networkDetails:n,stats:r});else switch(t.live&&a&&(a.getCacheAge&&(t.ageHeader=a.getCacheAge()||0),a.getCacheAge&&!isNaN(t.ageHeader)||(t.ageHeader=0)),o){case ke:case be:s.trigger(S.LEVEL_LOADED,{details:t,level:g||0,id:u||0,stats:r,networkDetails:n,deliveryDirectives:d});break;case De:s.trigger(S.AUDIO_TRACK_LOADED,{details:t,id:u||0,groupId:h||"",stats:r,networkDetails:n,deliveryDirectives:d});break;case Ie:s.trigger(S.SUBTITLE_TRACK_LOADED,{details:t,id:u||0,groupId:h||"",stats:r,networkDetails:n,deliveryDirectives:d})}}else{var m=new Error("No Segments found in Playlist");s.trigger(S.ERROR,{type:L.NETWORK_ERROR,details:A.LEVEL_EMPTY_ERROR,fatal:!1,url:c,error:m,reason:m.message,response:e,context:i,level:g,parent:f,networkDetails:n,stats:r})}},t}();function Me(t,e){var r;try{r=new Event("addtrack")}catch(t){(r=document.createEvent("Event")).initEvent("addtrack",!1,!1)}r.track=t,e.dispatchEvent(r)}function Oe(t,e){var r=t.mode;if("disabled"===r&&(t.mode="hidden"),t.cues&&!t.cues.getCueById(e.id))try{if(t.addCue(e),!t.cues.getCueById(e.id))throw new Error("addCue is failed for: "+e)}catch(r){w.debug("[texttrack-utils]: "+r);try{var i=new self.TextTrackCue(e.startTime,e.endTime,e.text);i.id=e.id,t.addCue(i)}catch(t){w.debug("[texttrack-utils]: Legacy TextTrackCue fallback failed: "+t)}}"disabled"===r&&(t.mode=r)}function Ne(t){var e=t.mode;if("disabled"===e&&(t.mode="hidden"),t.cues)for(var r=t.cues.length;r--;)t.removeCue(t.cues[r]);"disabled"===e&&(t.mode=e)}function Ue(t,e,r,i){var n=t.mode;if("disabled"===n&&(t.mode="hidden"),t.cues&&t.cues.length>0)for(var a=function(t,e,r){var i=[],n=function(t,e){if(et[r].endTime)return-1;for(var i=0,n=r;i<=n;){var a=Math.floor((n+i)/2);if(et[a].startTime&&i-1)for(var a=n,s=t.length;a=e&&o.endTime<=r)i.push(o);else if(o.startTime>r)return i}return i}(t.cues,e,r),s=0;sWe&&(d=We),d-h<=0&&(d=h+.25);for(var c=0;ce.startDate&&(!t||e.startDate.05&&this.forwardBufferLength>1){var l=Math.min(2,Math.max(1,a)),u=Math.round(2/(1+Math.exp(-.75*o-this.edgeStalled))*20)/20;t.playbackRate=Math.min(l,Math.max(1,u))}else 1!==t.playbackRate&&0!==t.playbackRate&&(t.playbackRate=1)}}}}},e.estimateLiveEdge=function(){var t=this.levelDetails;return null===t?null:t.edge+t.age},e.computeLatency=function(){var t=this.estimateLiveEdge();return null===t?null:t-this.currentTime},s(t,[{key:"latency",get:function(){return this._latency||0}},{key:"maxLatency",get:function(){var t=this.config,e=this.levelDetails;return void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:e?t.liveMaxLatencyDurationCount*e.targetduration:0}},{key:"targetLatency",get:function(){var t=this.levelDetails;if(null===t)return null;var e=t.holdBack,r=t.partHoldBack,i=t.targetduration,n=this.config,a=n.liveSyncDuration,s=n.liveSyncDurationCount,o=n.lowLatencyMode,l=this.hls.userConfig,u=o&&r||e;(l.liveSyncDuration||l.liveSyncDurationCount||0===u)&&(u=void 0!==a?a:s*i);var h=i;return u+Math.min(1*this.stallCount,h)}},{key:"liveSyncPosition",get:function(){var t=this.estimateLiveEdge(),e=this.targetLatency,r=this.levelDetails;if(null===t||null===e||null===r)return null;var i=r.edge,n=t-e-this.edgeStalled,a=i-r.totalduration,s=i-(this.config.lowLatencyMode&&r.partTarget||r.targetduration);return Math.min(Math.max(a,n),s)}},{key:"drift",get:function(){var t=this.levelDetails;return null===t?1:t.drift}},{key:"edgeStalled",get:function(){var t=this.levelDetails;if(null===t)return 0;var e=3*(this.config.lowLatencyMode&&t.partTarget||t.targetduration);return Math.max(t.age-e,0)}},{key:"forwardBufferLength",get:function(){var t=this.media,e=this.levelDetails;if(!t||!e)return 0;var r=t.buffered.length;return(r?t.buffered.end(r-1):e.edge)-this.currentTime}}]),t}(),ze=["NONE","TYPE-0","TYPE-1",null],Qe=["SDR","PQ","HLG"],Je="",$e="YES",Ze="v2";function tr(t){var e=t.canSkipUntil,r=t.canSkipDateRanges,i=t.age;return e&&it.sn?(n=r-t.start,i=t):(n=t.start-r,i=e),i.duration!==n&&(i.duration=n)}else e.sn>t.sn?t.cc===e.cc&&t.minEndPTS?e.start=t.start+(t.minEndPTS-t.start):e.start=t.start+t.duration:e.start=Math.max(t.start-e.duration,0)}function ar(t,e,r,i,n,a){i-r<=0&&(w.warn("Fragment should have a positive duration",e),i=r+e.duration,a=n+e.duration);var s=r,o=i,l=e.startPTS,u=e.endPTS;if(y(l)){var h=Math.abs(l-r);y(e.deltaPTS)?e.deltaPTS=Math.max(h,e.deltaPTS):e.deltaPTS=h,s=Math.max(r,l),r=Math.min(r,l),n=Math.min(n,e.startDTS),o=Math.min(i,u),i=Math.max(i,u),a=Math.max(a,e.endDTS)}var d=r-e.start;0!==e.start&&(e.start=r),e.duration=i-e.start,e.startPTS=r,e.maxStartPTS=s,e.startDTS=n,e.endPTS=i,e.minEndPTS=o,e.endDTS=a;var c,f=e.sn;if(!t||ft.endSN)return 0;var g=f-t.startSN,v=t.fragments;for(v[g]=e,c=g;c>0;c--)nr(v[c],v[c-1]);for(c=g;c=0;n--){var a=i[n].initSegment;if(a){r=a;break}}t.fragmentHint&&delete t.fragmentHint.endPTS;var s,l,u,h,d,c=0;if(function(t,e,r){for(var i=e.skippedSegments,n=Math.max(t.startSN,e.startSN)-e.startSN,a=(t.fragmentHint?1:0)+(i?e.endSN:Math.min(t.endSN,e.endSN))-e.startSN,s=e.startSN-t.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments,u=n;u<=a;u++){var h=l[s+u],d=o[u];i&&!d&&u=i.length||lr(e,i[r].start)}function lr(t,e){if(e){for(var r=t.fragments,i=t.skippedSegments;i499)}(n)||!!r);return t.shouldRetry?t.shouldRetry(t,e,r,i,a):a}var pr=function(t,e){for(var r=0,i=t.length-1,n=null,a=null;r<=i;){var s=e(a=t[n=(r+i)/2|0]);if(s>0)r=n+1;else{if(!(s<0))return a;i=n-1}}return null};function yr(t,e,r,i){void 0===r&&(r=0),void 0===i&&(i=0);var n=null;if(t){n=e[t.sn-e[0].sn+1]||null;var a=t.endDTS-r;a>0&&a<15e-7&&(r+=15e-7)}else 0===r&&0===e[0].start&&(n=e[0]);if(n&&(!t||t.level===n.level)&&0===Er(r,i,n))return n;var s=pr(e,Er.bind(null,r,i));return!s||s===t&&n?n:s}function Er(t,e,r){if(void 0===t&&(t=0),void 0===e&&(e=0),r.start<=t&&r.start+r.duration>t)return 0;var i=Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return r.start+r.duration-i<=t?1:r.start-i>t&&r.start?-1:0}function Tr(t,e,r){var i=1e3*Math.min(e,r.duration+(r.deltaPTS?r.deltaPTS:0));return(r.endProgramDateTime||0)-i>t}var Sr=0,Lr=2,Ar=3,Rr=5,kr=0,br=1,Dr=2,Ir=function(){function t(t){this.hls=void 0,this.playlistError=0,this.penalizedRenditions={},this.log=void 0,this.warn=void 0,this.error=void 0,this.hls=t,this.log=w.log.bind(w,"[info]:"),this.warn=w.warn.bind(w,"[warning]:"),this.error=w.error.bind(w,"[error]:"),this.registerListeners()}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(S.ERROR,this.onError,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.LEVEL_UPDATED,this.onLevelUpdated,this)},e.unregisterListeners=function(){var t=this.hls;t&&(t.off(S.ERROR,this.onError,this),t.off(S.ERROR,this.onErrorOut,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.LEVEL_UPDATED,this.onLevelUpdated,this))},e.destroy=function(){this.unregisterListeners(),this.hls=null,this.penalizedRenditions={}},e.startLoad=function(t){},e.stopLoad=function(){this.playlistError=0},e.getVariantLevelIndex=function(t){return(null==t?void 0:t.type)===we?t.level:this.hls.loadLevel},e.onManifestLoading=function(){this.playlistError=0,this.penalizedRenditions={}},e.onLevelUpdated=function(){this.playlistError=0},e.onError=function(t,e){var r,i;if(!e.fatal){var n=this.hls,a=e.context;switch(e.details){case A.FRAG_LOAD_ERROR:case A.FRAG_LOAD_TIMEOUT:case A.KEY_LOAD_ERROR:case A.KEY_LOAD_TIMEOUT:return void(e.errorAction=this.getFragRetryOrSwitchAction(e));case A.FRAG_PARSING_ERROR:if(null!=(r=e.frag)&&r.gap)return void(e.errorAction={action:Sr,flags:kr});case A.FRAG_GAP:case A.FRAG_DECRYPT_ERROR:return e.errorAction=this.getFragRetryOrSwitchAction(e),void(e.errorAction.action=Lr);case A.LEVEL_EMPTY_ERROR:case A.LEVEL_PARSING_ERROR:var s,o,l=e.parent===we?e.level:n.loadLevel;return void(e.details===A.LEVEL_EMPTY_ERROR&&null!=(s=e.context)&&null!=(o=s.levelDetails)&&o.live?e.errorAction=this.getPlaylistRetryOrSwitchAction(e,l):(e.levelRetry=!1,e.errorAction=this.getLevelSwitchAction(e,l)));case A.LEVEL_LOAD_ERROR:case A.LEVEL_LOAD_TIMEOUT:return void("number"==typeof(null==a?void 0:a.level)&&(e.errorAction=this.getPlaylistRetryOrSwitchAction(e,a.level)));case A.AUDIO_TRACK_LOAD_ERROR:case A.AUDIO_TRACK_LOAD_TIMEOUT:case A.SUBTITLE_LOAD_ERROR:case A.SUBTITLE_TRACK_LOAD_TIMEOUT:if(a){var u=n.levels[n.loadLevel];if(u&&(a.type===De&&u.hasAudioGroup(a.groupId)||a.type===Ie&&u.hasSubtitleGroup(a.groupId)))return e.errorAction=this.getPlaylistRetryOrSwitchAction(e,n.loadLevel),e.errorAction.action=Lr,void(e.errorAction.flags=br)}return;case A.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:var h=n.levels[n.loadLevel],d=null==h?void 0:h.attrs["HDCP-LEVEL"];return void(d?e.errorAction={action:Lr,flags:Dr,hdcpLevel:d}:this.keySystemError(e));case A.BUFFER_ADD_CODEC_ERROR:case A.REMUX_ALLOC_ERROR:case A.BUFFER_APPEND_ERROR:return void(e.errorAction=this.getLevelSwitchAction(e,null!=(i=e.level)?i:n.loadLevel));case A.INTERNAL_EXCEPTION:case A.BUFFER_APPENDING_ERROR:case A.BUFFER_FULL_ERROR:case A.LEVEL_SWITCH_ERROR:case A.BUFFER_STALLED_ERROR:case A.BUFFER_SEEK_OVER_HOLE:case A.BUFFER_NUDGE_ON_STALL:return void(e.errorAction={action:Sr,flags:kr})}e.type===L.KEY_SYSTEM_ERROR&&this.keySystemError(e)}},e.keySystemError=function(t){var e=this.getVariantLevelIndex(t.frag);t.levelRetry=!1,t.errorAction=this.getLevelSwitchAction(t,e)},e.getPlaylistRetryOrSwitchAction=function(t,e){var r=fr(this.hls.config.playlistLoadPolicy,t),i=this.playlistError++;if(mr(r,i,cr(t),t.response))return{action:Rr,flags:kr,retryConfig:r,retryCount:i};var n=this.getLevelSwitchAction(t,e);return r&&(n.retryConfig=r,n.retryCount=i),n},e.getFragRetryOrSwitchAction=function(t){var e=this.hls,r=this.getVariantLevelIndex(t.frag),i=e.levels[r],n=e.config,a=n.fragLoadPolicy,s=n.keyLoadPolicy,o=fr(t.details.startsWith("key")?s:a,t),l=e.levels.reduce((function(t,e){return t+e.fragmentError}),0);if(i&&(t.details!==A.FRAG_GAP&&i.fragmentError++,mr(o,l,cr(t),t.response)))return{action:Rr,flags:kr,retryConfig:o,retryCount:l};var u=this.getLevelSwitchAction(t,r);return o&&(u.retryConfig=o,u.retryCount=l),u},e.getLevelSwitchAction=function(t,e){var r=this.hls;null==e&&(e=r.loadLevel);var i=this.hls.levels[e];if(i){var n,a,s=t.details;i.loadError++,s===A.BUFFER_APPEND_ERROR&&i.fragmentError++;var o=-1,l=r.levels,u=r.loadLevel,h=r.minAutoLevel,d=r.maxAutoLevel;r.autoLevelEnabled||(r.loadLevel=-1);for(var c,f=null==(n=t.frag)?void 0:n.type,g=(f===Ce&&s===A.FRAG_PARSING_ERROR||"audio"===t.sourceBufferName&&(s===A.BUFFER_ADD_CODEC_ERROR||s===A.BUFFER_APPEND_ERROR))&&l.some((function(t){var e=t.audioCodec;return i.audioCodec!==e})),v="video"===t.sourceBufferName&&(s===A.BUFFER_ADD_CODEC_ERROR||s===A.BUFFER_APPEND_ERROR)&&l.some((function(t){var e=t.codecSet,r=t.audioCodec;return i.codecSet!==e&&i.audioCodec===r})),m=null!=(a=t.context)?a:{},p=m.type,y=m.groupId,E=function(){var e=(T+u)%l.length;if(e!==u&&e>=h&&e<=d&&0===l[e].loadError){var r,n,a=l[e];if(s===A.FRAG_GAP&&t.frag){var c=l[e].details;if(c){var m=yr(t.frag,c.fragments,t.frag.start);if(null!=m&&m.gap)return 0}}else{if(p===De&&a.hasAudioGroup(y)||p===Ie&&a.hasSubtitleGroup(y))return 0;if(f===Ce&&null!=(r=i.audioGroups)&&r.some((function(t){return a.hasAudioGroup(t)}))||f===_e&&null!=(n=i.subtitleGroups)&&n.some((function(t){return a.hasSubtitleGroup(t)}))||g&&i.audioCodec===a.audioCodec||!g&&i.audioCodec!==a.audioCodec||v&&i.codecSet===a.codecSet)return 0}return o=e,1}},T=l.length;T--&&(0===(c=E())||1!==c););if(o>-1&&r.loadLevel!==o)return t.levelRetry=!0,this.playlistError=0,{action:Lr,flags:kr,nextAutoLevel:o}}return{action:Lr,flags:br}},e.onErrorOut=function(t,e){var r;switch(null==(r=e.errorAction)?void 0:r.action){case Sr:break;case Lr:this.sendAlternateToPenaltyBox(e),e.errorAction.resolved||e.details===A.FRAG_GAP?/MediaSource readyState: ended/.test(e.error.message)&&(this.warn('MediaSource ended after "'+e.sourceBufferName+'" sourceBuffer append error. Attempting to recover from media error.'),this.hls.recoverMediaError()):e.fatal=!0}e.fatal&&this.hls.stopLoad()},e.sendAlternateToPenaltyBox=function(t){var e=this.hls,r=t.errorAction;if(r){var i=r.flags,n=r.hdcpLevel,a=r.nextAutoLevel;switch(i){case kr:this.switchLevel(t,a);break;case Dr:n&&(e.maxHdcpLevel=ze[ze.indexOf(n)-1],r.resolved=!0),this.warn('Restricting playback to HDCP-LEVEL of "'+e.maxHdcpLevel+'" or lower')}r.resolved||this.switchLevel(t,a)}},e.switchLevel=function(t,e){void 0!==e&&t.errorAction&&(this.warn("switching to level "+e+" after "+t.details),this.hls.nextAutoLevel=e,t.errorAction.resolved=!0,this.hls.nextLoadLevel=this.hls.nextAutoLevel)},t}(),wr=function(){function t(t,e){this.hls=void 0,this.timer=-1,this.requestScheduled=-1,this.canLoad=!1,this.log=void 0,this.warn=void 0,this.log=w.log.bind(w,e+":"),this.warn=w.warn.bind(w,e+":"),this.hls=t}var e=t.prototype;return e.destroy=function(){this.clearTimer(),this.hls=this.log=this.warn=null},e.clearTimer=function(){-1!==this.timer&&(self.clearTimeout(this.timer),this.timer=-1)},e.startLoad=function(){this.canLoad=!0,this.requestScheduled=-1,this.loadPlaylist()},e.stopLoad=function(){this.canLoad=!1,this.clearTimer()},e.switchParams=function(t,e,r){var i=null==e?void 0:e.renditionReports;if(i){for(var n=-1,a=0;a=0&&d>e.partTarget&&(h+=1)}var c=r&&tr(r);return new er(u,h>=0?h:void 0,c)}}},e.loadPlaylist=function(t){-1===this.requestScheduled&&(this.requestScheduled=self.performance.now())},e.shouldLoadPlaylist=function(t){return this.canLoad&&!!t&&!!t.url&&(!t.details||t.details.live)},e.shouldReloadPlaylist=function(t){return-1===this.timer&&-1===this.requestScheduled&&this.shouldLoadPlaylist(t)},e.playlistLoaded=function(t,e,r){var i=this,n=e.details,a=e.stats,s=self.performance.now(),o=a.loading.first?Math.max(0,s-a.loading.first):0;if(n.advancedDateTime=Date.now()-o,n.live||null!=r&&r.live){if(n.reloaded(r),r&&this.log("live playlist "+t+" "+(n.advanced?"REFRESHED "+n.lastPartSn+"-"+n.lastPartIndex:n.updated?"UPDATED":"MISSED")),r&&n.fragments.length>0&&sr(r,n),!this.canLoad||!n.live)return;var l,u=void 0,h=void 0;if(n.canBlockReload&&n.endSN&&n.advanced){var d=this.hls.config.lowLatencyMode,c=n.lastPartSn,f=n.endSN,g=n.lastPartIndex,v=c===f;-1!==g?(u=v?f+1:c,h=v?d?0:g:g+1):u=f+1;var m=n.age,p=m+n.ageHeader,y=Math.min(p-n.partTarget,1.5*n.targetduration);if(y>0){if(r&&y>r.tuneInGoal)this.warn("CDN Tune-in goal increased from: "+r.tuneInGoal+" to: "+y+" with playlist age: "+n.age),y=0;else{var E=Math.floor(y/n.targetduration);u+=E,void 0!==h&&(h+=Math.round(y%n.targetduration/n.partTarget)),this.log("CDN Tune-in age: "+n.ageHeader+"s last advanced "+m.toFixed(2)+"s goal: "+y+" skip sn "+E+" to part "+h)}n.tuneInGoal=y}if(l=this.getDeliveryDirectives(n,e.deliveryDirectives,u,h),d||!v)return void this.loadPlaylist(l)}else(n.canBlockReload||n.canSkipUntil)&&(l=this.getDeliveryDirectives(n,e.deliveryDirectives,u,h));var T=this.hls.mainForwardBufferInfo,S=T?T.end-T.len:0,L=function(t,e){void 0===e&&(e=1/0);var r=1e3*t.targetduration;if(t.updated){var i=t.fragments;if(i.length&&4*r>e){var n=1e3*i[i.length-1].duration;nthis.requestScheduled+L&&(this.requestScheduled=a.loading.start),void 0!==u&&n.canBlockReload?this.requestScheduled=a.loading.first+L-(1e3*n.partTarget||1e3):-1===this.requestScheduled||this.requestScheduled+L=u.maxNumRetry)return!1;if(i&&null!=(d=t.context)&&d.deliveryDirectives)this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" without delivery-directives'),this.loadPlaylist();else{var c=gr(u,l);this.timer=self.setTimeout((function(){return e.loadPlaylist()}),c),this.warn("Retrying playlist loading "+(l+1)+"/"+u.maxNumRetry+' after "'+r+'" in '+c+"ms")}t.levelRetry=!0,n.resolved=!0}return h},t}(),Cr=function(){function t(t,e,r){void 0===e&&(e=0),void 0===r&&(r=0),this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=t,this.alpha_=t?Math.exp(Math.log(.5)/t):0,this.estimate_=e,this.totalWeight_=r}var e=t.prototype;return e.sample=function(t,e){var r=Math.pow(this.alpha_,t);this.estimate_=e*(1-r)+r*this.estimate_,this.totalWeight_+=t},e.getTotalWeight=function(){return this.totalWeight_},e.getEstimate=function(){if(this.alpha_){var t=1-Math.pow(this.alpha_,this.totalWeight_);if(t)return this.estimate_/t}return this.estimate_},t}(),_r=function(){function t(t,e,r,i){void 0===i&&(i=100),this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultTTFB_=void 0,this.ttfb_=void 0,this.defaultEstimate_=r,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new Cr(t),this.fast_=new Cr(e),this.defaultTTFB_=i,this.ttfb_=new Cr(t)}var e=t.prototype;return e.update=function(t,e){var r=this.slow_,i=this.fast_,n=this.ttfb_;r.halfLife!==t&&(this.slow_=new Cr(t,r.getEstimate(),r.getTotalWeight())),i.halfLife!==e&&(this.fast_=new Cr(e,i.getEstimate(),i.getTotalWeight())),n.halfLife!==t&&(this.ttfb_=new Cr(t,n.getEstimate(),n.getTotalWeight()))},e.sample=function(t,e){var r=(t=Math.max(t,this.minDelayMs_))/1e3,i=8*e/r;this.fast_.sample(r,i),this.slow_.sample(r,i)},e.sampleTTFB=function(t){var e=t/1e3,r=Math.sqrt(2)*Math.exp(-Math.pow(e,2)/2);this.ttfb_.sample(r,Math.max(t,5))},e.canEstimate=function(){return this.fast_.getTotalWeight()>=this.minWeight_},e.getEstimate=function(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_},e.getEstimateTTFB=function(){return this.ttfb_.getTotalWeight()>=this.minWeight_?this.ttfb_.getEstimate():this.defaultTTFB_},e.destroy=function(){},t}(),xr={supported:!0,configurations:[],decodingInfoResults:[{supported:!0,powerEfficient:!0,smooth:!0}]},Pr={};function Fr(t,e,r){var n=t.videoCodec,a=t.audioCodec;if(!n||!a||!r)return Promise.resolve(xr);var s={width:t.width,height:t.height,bitrate:Math.ceil(Math.max(.9*t.bitrate,t.averageBitrate)),framerate:t.frameRate||30},o=t.videoRange;"SDR"!==o&&(s.transferFunction=o.toLowerCase());var l=n.split(",").map((function(t){return{type:"media-source",video:i(i({},s),{},{contentType:ae(t,"video")})}}));return a&&t.audioGroups&&t.audioGroups.forEach((function(t){var r;t&&(null==(r=e.groups[t])||r.tracks.forEach((function(e){if(e.groupId===t){var r=e.channels||"",i=parseFloat(r);y(i)&&i>2&&l.push.apply(l,a.split(",").map((function(t){return{type:"media-source",audio:{contentType:ae(t,"audio"),channels:""+i}}})))}})))})),Promise.all(l.map((function(t){var e=function(t){var e=t.audio,r=t.video,i=r||e;if(i){var n=i.contentType.split('"')[1];if(r)return"r"+r.height+"x"+r.width+"f"+Math.ceil(r.framerate)+(r.transferFunction||"sd")+"_"+n+"_"+Math.ceil(r.bitrate/1e5);if(e)return"c"+e.channels+(e.spatialRendering?"s":"n")+"_"+n}return""}(t);return Pr[e]||(Pr[e]=r.decodingInfo(t))}))).then((function(t){return{supported:!t.some((function(t){return!t.supported})),configurations:l,decodingInfoResults:t}})).catch((function(t){return{supported:!1,configurations:l,decodingInfoResults:[],error:t}}))}function Mr(t,e){var r=!1,i=[];return t&&(r="SDR"!==t,i=[t]),e&&(i=e.allowedVideoRanges||Qe.slice(0),i=(r=void 0!==e.preferHDR?e.preferHDR:function(){if("function"==typeof matchMedia){var t=matchMedia("(dynamic-range: high)"),e=matchMedia("bad query");if(t.media!==e.media)return!0===t.matches}return!1}())?i.filter((function(t){return"SDR"!==t})):["SDR"]),{preferHDR:r,allowedVideoRanges:i}}function Or(t,e){w.log('[abr] start candidates with "'+t+'" ignored because '+e)}function Nr(t,e,r){if("attrs"in t){var i=e.indexOf(t);if(-1!==i)return i}for(var n=0;n-1,p=e.getBwEstimate(),E=i.levels,T=E[t.level],L=o.total||Math.max(o.loaded,Math.round(l*T.averageBitrate/8)),A=m?u-v:u;A<1&&m&&(A=Math.min(u,8*o.loaded/p));var R=m?1e3*o.loaded/A:0,k=R?(L-o.loaded)/R:8*L/p+c/1e3;if(!(k<=g)){var b,D=R?8*R:p,I=Number.POSITIVE_INFINITY;for(b=t.level-1;b>h;b--){var C=E[b].maxBitrate;if((I=e.getTimeToLoadFrag(c/1e3,D,l*C,!E[b].details))=k||I>10*l)){i.nextLoadLevel=i.nextAutoLevel=b,m?e.bwEstimator.sample(u-Math.min(c,v),o.loaded):e.bwEstimator.sampleTTFB(u);var _=E[b].maxBitrate;e.getBwEstimate()*e.hls.config.abrBandWidthUpFactor>_&&e.resetEstimator(_),e.clearTimer(),w.warn("[abr] Fragment "+t.sn+(r?" part "+r.index:"")+" of level "+t.level+" is loading too slowly;\n Time to underbuffer: "+g.toFixed(3)+" s\n Estimated load time for current fragment: "+k.toFixed(3)+" s\n Estimated load time for down switch fragment: "+I.toFixed(3)+" s\n TTFB estimate: "+(0|v)+" ms\n Current BW estimate: "+(y(p)?0|p:"Unknown")+" bps\n New BW estimate: "+(0|e.getBwEstimate())+" bps\n Switching to level "+b+" @ "+(0|_)+" bps"),i.trigger(S.FRAG_LOAD_EMERGENCY_ABORTED,{frag:t,part:r,stats:o})}}}}}}},this.hls=t,this.bwEstimator=this.initEstimator(),this.registerListeners()}var e=t.prototype;return e.resetEstimator=function(t){t&&(w.log("setting initial bwe to "+t),this.hls.config.abrEwmaDefaultEstimate=t),this.firstSelection=-1,this.bwEstimator=this.initEstimator()},e.initEstimator=function(){var t=this.hls.config;return new _r(t.abrEwmaSlowVoD,t.abrEwmaFastVoD,t.abrEwmaDefaultEstimate)},e.registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.FRAG_LOADING,this.onFragLoading,this),t.on(S.FRAG_LOADED,this.onFragLoaded,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this),t.on(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.on(S.LEVEL_LOADED,this.onLevelLoaded,this),t.on(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(S.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),t.on(S.ERROR,this.onError,this)},e.unregisterListeners=function(){var t=this.hls;t&&(t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.FRAG_LOADING,this.onFragLoading,this),t.off(S.FRAG_LOADED,this.onFragLoaded,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this),t.off(S.LEVEL_SWITCHING,this.onLevelSwitching,this),t.off(S.LEVEL_LOADED,this.onLevelLoaded,this),t.off(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(S.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),t.off(S.ERROR,this.onError,this))},e.destroy=function(){this.unregisterListeners(),this.clearTimer(),this.hls=this._abandonRulesCheck=null,this.fragCurrent=this.partCurrent=null},e.onManifestLoading=function(t,e){this.lastLoadedFragLevel=-1,this.firstSelection=-1,this.lastLevelLoadSec=0,this.fragCurrent=this.partCurrent=null,this.onLevelsUpdated(),this.clearTimer()},e.onLevelsUpdated=function(){this.lastLoadedFragLevel>-1&&this.fragCurrent&&(this.lastLoadedFragLevel=this.fragCurrent.level),this._nextAutoLevel=-1,this.onMaxAutoLevelUpdated(),this.codecTiers=null,this.audioTracksByGroup=null},e.onMaxAutoLevelUpdated=function(){this.firstSelection=-1,this.nextAutoLevelKey=""},e.onFragLoading=function(t,e){var r,i=e.frag;this.ignoreFragment(i)||(i.bitrateTest||(this.fragCurrent=i,this.partCurrent=null!=(r=e.part)?r:null),this.clearTimer(),this.timer=self.setInterval(this._abandonRulesCheck,100))},e.onLevelSwitching=function(t,e){this.clearTimer()},e.onError=function(t,e){if(!e.fatal)switch(e.details){case A.BUFFER_ADD_CODEC_ERROR:case A.BUFFER_APPEND_ERROR:this.lastLoadedFragLevel=-1,this.firstSelection=-1;break;case A.FRAG_LOAD_TIMEOUT:var r=e.frag,i=this.fragCurrent,n=this.partCurrent;if(r&&i&&r.sn===i.sn&&r.level===i.level){var a=performance.now(),s=n?n.stats:r.stats,o=a-s.loading.start,l=s.loading.first?s.loading.first-s.loading.start:-1;if(s.loaded&&l>-1){var u=this.bwEstimator.getEstimateTTFB();this.bwEstimator.sample(o-Math.min(u,l),s.loaded)}else this.bwEstimator.sampleTTFB(o)}}},e.getTimeToLoadFrag=function(t,e,r,i){return t+r/e+(i?this.lastLevelLoadSec:0)},e.onLevelLoaded=function(t,e){var r=this.hls.config,i=e.stats.loading,n=i.end-i.start;y(n)&&(this.lastLevelLoadSec=n/1e3),e.details.live?this.bwEstimator.update(r.abrEwmaSlowLive,r.abrEwmaFastLive):this.bwEstimator.update(r.abrEwmaSlowVoD,r.abrEwmaFastVoD)},e.onFragLoaded=function(t,e){var r=e.frag,i=e.part,n=i?i.stats:r.stats;if(r.type===we&&this.bwEstimator.sampleTTFB(n.loading.first-n.loading.start),!this.ignoreFragment(r)){if(this.clearTimer(),r.level===this._nextAutoLevel&&(this._nextAutoLevel=-1),this.firstSelection=-1,this.hls.config.abrMaxWithRealBitrate){var a=i?i.duration:r.duration,s=this.hls.levels[r.level],o=(s.loaded?s.loaded.bytes:0)+n.loaded,l=(s.loaded?s.loaded.duration:0)+a;s.loaded={bytes:o,duration:l},s.realBitrate=Math.round(8*o/l)}if(r.bitrateTest){var u={stats:n,frag:r,part:i,id:r.type};this.onFragBuffered(S.FRAG_BUFFERED,u),r.bitrateTest=!1}else this.lastLoadedFragLevel=r.level}},e.onFragBuffered=function(t,e){var r=e.frag,i=e.part,n=null!=i&&i.stats.loaded?i.stats:r.stats;if(!n.aborted&&!this.ignoreFragment(r)){var a=n.parsing.end-n.loading.start-Math.min(n.loading.first-n.loading.start,this.bwEstimator.getEstimateTTFB());this.bwEstimator.sample(a,n.loaded),n.bwEstimate=this.getBwEstimate(),r.bitrateTest?this.bitrateTestDelay=a/1e3:this.bitrateTestDelay=0}},e.ignoreFragment=function(t){return t.type!==we||"initSegment"===t.sn},e.clearTimer=function(){this.timer>-1&&(self.clearInterval(this.timer),this.timer=-1)},e.getAutoLevelKey=function(){return this.getBwEstimate()+"_"+this.getStarvationDelay().toFixed(2)},e.getNextABRAutoLevel=function(){var t=this.fragCurrent,e=this.partCurrent,r=this.hls,i=r.maxAutoLevel,n=r.config,a=r.minAutoLevel,s=e?e.duration:t?t.duration:0,o=this.getBwEstimate(),l=this.getStarvationDelay(),u=n.abrBandWidthFactor,h=n.abrBandWidthUpFactor;if(l){var d=this.findBestLevel(o,a,i,l,0,u,h);if(d>=0)return d}var c=s?Math.min(s,n.maxStarvationDelay):n.maxStarvationDelay;if(!l){var f=this.bitrateTestDelay;f&&(c=(s?Math.min(s,n.maxLoadingDelay):n.maxLoadingDelay)-f,w.info("[abr] bitrate test took "+Math.round(1e3*f)+"ms, set first fragment max fetchDuration to "+Math.round(1e3*c)+" ms"),u=h=1)}var g=this.findBestLevel(o,a,i,l,c,u,h);if(w.info("[abr] "+(l?"rebuffering expected":"buffer is empty")+", optimal quality level "+g),g>-1)return g;var v=r.levels[a],m=r.levels[r.loadLevel];return(null==v?void 0:v.bitrate)<(null==m?void 0:m.bitrate)?a:r.loadLevel},e.getStarvationDelay=function(){var t=this.hls,e=t.media;if(!e)return 1/0;var r=e&&0!==e.playbackRate?Math.abs(e.playbackRate):1,i=t.mainForwardBufferInfo;return(i?i.len:0)/r},e.getBwEstimate=function(){return this.bwEstimator.canEstimate()?this.bwEstimator.getEstimate():this.hls.config.abrEwmaDefaultEstimate},e.findBestLevel=function(t,e,r,i,n,a,s){var o,l=this,u=i+n,h=this.lastLoadedFragLevel,d=-1===h?this.hls.firstLevel:h,c=this.fragCurrent,f=this.partCurrent,g=this.hls,v=g.levels,m=g.allAudioTracks,p=g.loadLevel,E=g.config;if(1===v.length)return 0;var T,S=v[d],L=!(null==S||null==(o=S.details)||!o.live),A=-1===p||-1===h,R="SDR",k=(null==S?void 0:S.frameRate)||0,b=E.audioPreference,D=E.videoPreference,I=this.audioTracksByGroup||(this.audioTracksByGroup=function(t){return t.reduce((function(t,e){var r=t.groups[e.groupId];r||(r=t.groups[e.groupId]={tracks:[],channels:{2:0},hasDefault:!1,hasAutoSelect:!1}),r.tracks.push(e);var i=e.channels||"2";return r.channels[i]=(r.channels[i]||0)+1,r.hasDefault=r.hasDefault||e.default,r.hasAutoSelect=r.hasAutoSelect||e.autoselect,r.hasDefault&&(t.hasDefaultAudio=!0),r.hasAutoSelect&&(t.hasAutoSelectAudio=!0),t}),{hasDefaultAudio:!1,hasAutoSelectAudio:!1,groups:{}})}(m));if(A){if(-1!==this.firstSelection)return this.firstSelection;var C=this.codecTiers||(this.codecTiers=function(t,e,r,i){return t.slice(r,i+1).reduce((function(t,r){if(!r.codecSet)return t;var i=r.audioGroups,n=t[r.codecSet];n||(t[r.codecSet]=n={minBitrate:1/0,minHeight:1/0,minFramerate:1/0,maxScore:0,videoRanges:{SDR:0},channels:{2:0},hasDefaultAudio:!i,fragmentError:0}),n.minBitrate=Math.min(n.minBitrate,r.bitrate);var a=Math.min(r.height,r.width);return n.minHeight=Math.min(n.minHeight,a),n.minFramerate=Math.min(n.minFramerate,r.frameRate),n.maxScore=Math.max(n.maxScore,r.score),n.fragmentError+=r.fragmentError,n.videoRanges[r.videoRange]=(n.videoRanges[r.videoRange]||0)+1,i&&i.forEach((function(t){if(t){var r=e.groups[t];r&&(n.hasDefaultAudio=n.hasDefaultAudio||e.hasDefaultAudio?r.hasDefault:r.hasAutoSelect||!e.hasDefaultAudio&&!e.hasAutoSelectAudio,Object.keys(r.channels).forEach((function(t){n.channels[t]=(n.channels[t]||0)+r.channels[t]})))}})),t}),{})}(v,I,e,r)),_=function(t,e,r,i,n){for(var a=Object.keys(t),s=null==i?void 0:i.channels,o=null==i?void 0:i.audioCodec,l=s&&2===parseInt(s),u=!0,h=!1,d=1/0,c=1/0,f=1/0,g=0,v=[],m=Mr(e,n),p=m.preferHDR,E=m.allowedVideoRanges,T=function(){var e=t[a[S]];u=e.channels[2]>0,d=Math.min(d,e.minHeight),c=Math.min(c,e.minFramerate),f=Math.min(f,e.minBitrate);var r=E.filter((function(t){return e.videoRanges[t]>0}));r.length>0&&(h=!0,v=r)},S=a.length;S--;)T();d=y(d)?d:0,c=y(c)?c:0;var L=Math.max(1080,d),A=Math.max(30,c);return f=y(f)?f:r,r=Math.max(f,r),h||(e=void 0,v=[]),{codecSet:a.reduce((function(e,i){var n=t[i];if(i===e)return e;if(n.minBitrate>r)return Or(i,"min bitrate of "+n.minBitrate+" > current estimate of "+r),e;if(!n.hasDefaultAudio)return Or(i,"no renditions with default or auto-select sound found"),e;if(o&&i.indexOf(o.substring(0,4))%5!=0)return Or(i,'audio codec preference "'+o+'" not found'),e;if(s&&!l){if(!n.channels[s])return Or(i,"no renditions with "+s+" channel sound found (channels options: "+Object.keys(n.channels)+")"),e}else if((!o||l)&&u&&0===n.channels[2])return Or(i,"no renditions with stereo sound found"),e;return n.minHeight>L?(Or(i,"min resolution of "+n.minHeight+" > maximum of "+L),e):n.minFramerate>A?(Or(i,"min framerate of "+n.minFramerate+" > maximum of "+A),e):v.some((function(t){return n.videoRanges[t]>0}))?n.maxScore=oe(e)||n.fragmentError>t[e].fragmentError)?e:(g=n.maxScore,i):(Or(i,"no variants with VIDEO-RANGE of "+JSON.stringify(v)+" found"),e)}),void 0),videoRanges:v,preferHDR:p,minFramerate:c,minBitrate:f}}(C,R,t,b,D),x=_.codecSet,P=_.videoRanges,F=_.minFramerate,M=_.minBitrate,O=_.preferHDR;T=x,R=O?P[P.length-1]:P[0],k=F,t=Math.max(t,M),w.log("[abr] picked start tier "+JSON.stringify(_))}else T=null==S?void 0:S.codecSet,R=null==S?void 0:S.videoRange;for(var N,U=f?f.duration:c?c.duration:0,B=this.bwEstimator.getEstimateTTFB()/1e3,G=[],K=function(){var e,o=v[H],c=H>d;if(!o)return 0;if(E.useMediaCapabilities&&!o.supportedResult&&!o.supportedPromise){var g=navigator.mediaCapabilities;"function"==typeof(null==g?void 0:g.decodingInfo)&&function(t,e,r,i,n,a){var s=t.audioCodec?t.audioGroups:null,o=null==a?void 0:a.audioCodec,l=null==a?void 0:a.channels,u=l?parseInt(l):o?1/0:2,h=null;if(null!=s&&s.length)try{h=1===s.length&&s[0]?e.groups[s[0]].channels:s.reduce((function(t,r){if(r){var i=e.groups[r];if(!i)throw new Error("Audio track group "+r+" not found");Object.keys(i.channels).forEach((function(e){t[e]=(t[e]||0)+i.channels[e]}))}return t}),{2:0})}catch(t){return!0}return void 0!==t.videoCodec&&(t.width>1920&&t.height>1088||t.height>1920&&t.width>1088||t.frameRate>Math.max(i,30)||"SDR"!==t.videoRange&&t.videoRange!==r||t.bitrate>Math.max(n,8e6))||!!h&&y(u)&&Object.keys(h).some((function(t){return parseInt(t)>u}))}(o,I,R,k,t,b)?(o.supportedPromise=Fr(o,I,g),o.supportedPromise.then((function(t){if(l.hls){o.supportedResult=t;var e=l.hls.levels,r=e.indexOf(o);t.error?w.warn('[abr] MediaCapabilities decodingInfo error: "'+t.error+'" for level '+r+" "+JSON.stringify(t)):t.supported||(w.warn("[abr] Unsupported MediaCapabilities decodingInfo result for level "+r+" "+JSON.stringify(t)),r>-1&&e.length>1&&(w.log("[abr] Removing unsupported level "+r),l.hls.removeLevel(r)))}}))):o.supportedResult=xr}if(T&&o.codecSet!==T||R&&o.videoRange!==R||c&&k>o.frameRate||!c&&k>0&&k=2*U&&0===n?v[H].averageBitrate:v[H].maxBitrate,x=l.getTimeToLoadFrag(B,m,_*C,void 0===D);if(m>=_&&(H===h||0===o.loadError&&0===o.fragmentError)&&(x<=B||!y(x)||L&&!l.bitrateTestDelay||x"+H+" adjustedbw("+Math.round(m)+")-bitrate="+Math.round(m-_)+" ttfb:"+B.toFixed(1)+" avgDuration:"+C.toFixed(1)+" maxFetchDuration:"+u.toFixed(1)+" fetchDuration:"+x.toFixed(1)+" firstSelection:"+A+" codecSet:"+T+" videoRange:"+R+" hls.loadLevel:"+p)),A&&(l.firstSelection=H),{v:H}}},H=r;H>=e;H--)if(0!==(N=K())&&N)return N.v;return-1},s(t,[{key:"firstAutoLevel",get:function(){var t=this.hls,e=t.maxAutoLevel,r=t.minAutoLevel,i=this.getBwEstimate(),n=this.hls.config.maxStarvationDelay,a=this.findBestLevel(i,r,e,0,n,1,1);if(a>-1)return a;var s=this.hls.firstLevel,o=Math.min(Math.max(s,r),e);return w.warn("[abr] Could not find best starting auto level. Defaulting to first in playlist "+s+" clamped to "+o),o}},{key:"forcedAutoLevel",get:function(){return this.nextAutoLevelKey?-1:this._nextAutoLevel}},{key:"nextAutoLevel",get:function(){var t=this.forcedAutoLevel,e=this.bwEstimator.canEstimate(),r=this.lastLoadedFragLevel>-1;if(!(-1===t||e&&r&&this.nextAutoLevelKey!==this.getAutoLevelKey()))return t;var i=e&&r?this.getNextABRAutoLevel():this.firstAutoLevel;if(-1!==t){var n=this.hls.levels;if(n.length>Math.max(t,i)&&n[t].loadError<=n[i].loadError)return t}return this._nextAutoLevel=i,this.nextAutoLevelKey=this.getAutoLevelKey(),i},set:function(t){var e=this.hls,r=e.maxAutoLevel,i=e.minAutoLevel,n=Math.min(Math.max(t,i),r);this._nextAutoLevel!==n&&(this.nextAutoLevelKey="",this._nextAutoLevel=n)}}]),t}(),Hr=function(){function t(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}var e=t.prototype;return e.destroy=function(){this.onHandlerDestroying(),this.onHandlerDestroyed()},e.onHandlerDestroying=function(){this.clearNextTick(),this.clearInterval()},e.onHandlerDestroyed=function(){},e.hasInterval=function(){return!!this._tickInterval},e.hasNextTick=function(){return!!this._tickTimer},e.setInterval=function(t){return!this._tickInterval&&(this._tickCallCount=0,this._tickInterval=self.setInterval(this._boundTick,t),!0)},e.clearInterval=function(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)},e.clearNextTick=function(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)},e.tick=function(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)},e.tickImmediate=function(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)},e.doTick=function(){},t}(),Vr="NOT_LOADED",Yr="APPENDING",Wr="PARTIAL",jr="OK",qr=function(){function t(t){this.activePartLists=Object.create(null),this.endListFragments=Object.create(null),this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hasGaps=!1,this.hls=t,this._registerListeners()}var e=t.prototype;return e._registerListeners=function(){var t=this.hls;t.on(S.BUFFER_APPENDED,this.onBufferAppended,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this),t.on(S.FRAG_LOADED,this.onFragLoaded,this)},e._unregisterListeners=function(){var t=this.hls;t.off(S.BUFFER_APPENDED,this.onBufferAppended,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this),t.off(S.FRAG_LOADED,this.onFragLoaded,this)},e.destroy=function(){this._unregisterListeners(),this.fragments=this.activePartLists=this.endListFragments=this.timeRanges=null},e.getAppendedFrag=function(t,e){var r=this.activePartLists[e];if(r)for(var i=r.length;i--;){var n=r[i];if(!n)break;var a=n.end;if(n.start<=t&&null!==a&&t<=a)return n}return this.getBufferedFrag(t,e)},e.getBufferedFrag=function(t,e){for(var r=this.fragments,i=Object.keys(r),n=i.length;n--;){var a=r[i[n]];if((null==a?void 0:a.body.type)===e&&a.buffered){var s=a.body;if(s.start<=t&&t<=s.end)return s}}return null},e.detectEvictedFragments=function(t,e,r,i){var n=this;this.timeRanges&&(this.timeRanges[t]=e);var a=(null==i?void 0:i.fragment.sn)||-1;Object.keys(this.fragments).forEach((function(i){var s=n.fragments[i];if(s&&!(a>=s.body.sn))if(s.buffered||s.loaded){var o=s.range[t];o&&o.time.some((function(t){var r=!n.isTimeBuffered(t.startPTS,t.endPTS,e);return r&&n.removeFragment(s.body),r}))}else s.body.type===r&&n.removeFragment(s.body)}))},e.detectPartialFragments=function(t){var e=this,r=this.timeRanges,i=t.frag,n=t.part;if(r&&"initSegment"!==i.sn){var a=zr(i),s=this.fragments[a];if(!(!s||s.buffered&&i.gap)){var o=!i.relurl;Object.keys(r).forEach((function(t){var a=i.elementaryStreams[t];if(a){var l=r[t],u=o||!0===a.partial;s.range[t]=e.getBufferedTimes(i,n,u,l)}})),s.loaded=null,Object.keys(s.range).length?(s.buffered=!0,(s.body.endList=i.endList||s.body.endList)&&(this.endListFragments[s.body.type]=s),Xr(s)||this.removeParts(i.sn-1,i.type)):this.removeFragment(s.body)}}},e.removeParts=function(t,e){var r=this.activePartLists[e];r&&(this.activePartLists[e]=r.filter((function(e){return e.fragment.sn>=t})))},e.fragBuffered=function(t,e){var r=zr(t),i=this.fragments[r];!i&&e&&(i=this.fragments[r]={body:t,appendedPTS:null,loaded:null,buffered:!1,range:Object.create(null)},t.gap&&(this.hasGaps=!0)),i&&(i.loaded=null,i.buffered=!0)},e.getBufferedTimes=function(t,e,r,i){for(var n={time:[],partial:r},a=t.start,s=t.end,o=t.minEndPTS||s,l=t.maxStartPTS||a,u=0;u=h&&o<=d){n.time.push({startPTS:Math.max(a,i.start(u)),endPTS:Math.min(s,i.end(u))});break}if(ah){var c=Math.max(a,i.start(u)),f=Math.min(s,i.end(u));f>c&&(n.partial=!0,n.time.push({startPTS:c,endPTS:f}))}else if(s<=h)break}return n},e.getPartialFragment=function(t){var e,r,i,n=null,a=0,s=this.bufferPadding,o=this.fragments;return Object.keys(o).forEach((function(l){var u=o[l];u&&Xr(u)&&(r=u.body.start-s,i=u.body.end+s,t>=r&&t<=i&&(e=Math.min(t-r,i-t),a<=e&&(n=u.body,a=e)))})),n},e.isEndListAppended=function(t){var e=this.endListFragments[t];return void 0!==e&&(e.buffered||Xr(e))},e.getState=function(t){var e=zr(t),r=this.fragments[e];return r?r.buffered?Xr(r)?Wr:jr:Yr:Vr},e.isTimeBuffered=function(t,e,r){for(var i,n,a=0;a=i&&e<=n)return!0;if(e<=i)return!1}return!1},e.onFragLoaded=function(t,e){var r=e.frag,i=e.part;if("initSegment"!==r.sn&&!r.bitrateTest){var n=i?null:e,a=zr(r);this.fragments[a]={body:r,appendedPTS:null,loaded:n,buffered:!1,range:Object.create(null)}}},e.onBufferAppended=function(t,e){var r=this,i=e.frag,n=e.part,a=e.timeRanges;if("initSegment"!==i.sn){var s=i.type;if(n){var o=this.activePartLists[s];o||(this.activePartLists[s]=o=[]),o.push(n)}this.timeRanges=a,Object.keys(a).forEach((function(t){var e=a[t];r.detectEvictedFragments(t,e,s,n)}))}},e.onFragBuffered=function(t,e){this.detectPartialFragments(e)},e.hasFragment=function(t){var e=zr(t);return!!this.fragments[e]},e.hasParts=function(t){var e;return!(null==(e=this.activePartLists[t])||!e.length)},e.removeFragmentsInRange=function(t,e,r,i,n){var a=this;i&&!this.hasGaps||Object.keys(this.fragments).forEach((function(s){var o=a.fragments[s];if(o){var l=o.body;l.type!==r||i&&!l.gap||l.startt&&(o.buffered||n)&&a.removeFragment(l)}}))},e.removeFragment=function(t){var e=zr(t);t.stats.loaded=0,t.clearElementaryStreamInfo();var r=this.activePartLists[t.type];if(r){var i=t.sn;this.activePartLists[t.type]=r.filter((function(t){return t.fragment.sn!==i}))}delete this.fragments[e],t.endList&&delete this.endListFragments[t.type]},e.removeAllFragments=function(){this.fragments=Object.create(null),this.endListFragments=Object.create(null),this.activePartLists=Object.create(null),this.hasGaps=!1},t}();function Xr(t){var e,r,i;return t.buffered&&(t.body.gap||(null==(e=t.range.video)?void 0:e.partial)||(null==(r=t.range.audio)?void 0:r.partial)||(null==(i=t.range.audiovideo)?void 0:i.partial))}function zr(t){return t.type+"_"+t.level+"_"+t.sn}var Qr={length:0,start:function(){return 0},end:function(){return 0}},Jr=function(){function t(){}return t.isBuffered=function(e,r){try{if(e)for(var i=t.getBuffered(e),n=0;n=i.start(n)&&r<=i.end(n))return!0}catch(t){}return!1},t.bufferInfo=function(e,r,i){try{if(e){var n,a=t.getBuffered(e),s=[];for(n=0;ns&&(i[a-1].end=t[n].end):i.push(t[n])}else i.push(t[n])}else i=t;for(var o,l=0,u=e,h=e,d=0;d=c&&er.startCC||t&&t.cc>>8^255&m^99,t[f]=m,e[m]=f;var p=c[f],y=c[p],E=c[y],T=257*c[m]^16843008*m;i[f]=T<<24|T>>>8,n[f]=T<<16|T>>>16,a[f]=T<<8|T>>>24,s[f]=T,T=16843009*E^65537*y^257*p^16843008*f,l[m]=T<<24|T>>>8,u[m]=T<<16|T>>>16,h[m]=T<<8|T>>>24,d[m]=T,f?(f=p^c[c[c[E^p]]],g^=c[c[g]]):f=g=1}},e.expandKey=function(t){for(var e=this.uint8ArrayToUint32Array_(t),r=!0,i=0;is.end){var h=a>u;(a0&&null!=a&&a.key&&a.iv&&"AES-128"===a.method){var s=self.performance.now();return r.decrypter.decrypt(new Uint8Array(n),a.key.buffer,a.iv.buffer).catch((function(e){throw i.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_DECRYPT_ERROR,fatal:!1,error:e,reason:e.message,frag:t}),e})).then((function(n){var a=self.performance.now();return i.trigger(S.FRAG_DECRYPTED,{frag:t,payload:n,stats:{tstart:s,tdecrypt:a}}),e.payload=n,r.completeInitSegmentLoad(e)}))}return r.completeInitSegmentLoad(e)})).catch((function(e){r.state!==gi&&r.state!==Ai&&(r.warn(e),r.resetFragmentLoading(t))}))},r.completeInitSegmentLoad=function(t){if(!this.levels)throw new Error("init load aborted, missing levels");var e=t.frag.stats;this.state=vi,t.frag.data=new Uint8Array(t.payload),e.parsing.start=e.buffering.start=self.performance.now(),e.parsing.end=e.buffering.end=self.performance.now(),this.tick()},r.fragContextChanged=function(t){var e=this.fragCurrent;return!t||!e||t.sn!==e.sn||t.level!==e.level},r.fragBufferedComplete=function(t,e){var r,i,n,a,s=this.mediaBuffer?this.mediaBuffer:this.media;if(this.log("Buffered "+t.type+" sn: "+t.sn+(e?" part: "+e.index:"")+" of "+(this.playlistType===we?"level":"track")+" "+t.level+" (frag:["+(null!=(r=t.startPTS)?r:NaN).toFixed(3)+"-"+(null!=(i=t.endPTS)?i:NaN).toFixed(3)+"] > buffer:"+(s?fi(Jr.getBuffered(s)):"(detached)")+")"),"initSegment"!==t.sn){var o;if(t.type!==_e){var l=t.elementaryStreams;if(!Object.keys(l).some((function(t){return!!l[t]})))return void(this.state=vi)}var u=null==(o=this.levels)?void 0:o[t.level];null!=u&&u.fragmentError&&(this.log("Resetting level fragment error count of "+u.fragmentError+" on frag buffered"),u.fragmentError=0)}this.state=vi,s&&(!this.loadedmetadata&&t.type==we&&s.buffered.length&&(null==(n=this.fragCurrent)?void 0:n.sn)===(null==(a=this.fragPrevious)?void 0:a.sn)&&(this.loadedmetadata=!0,this.seekToStartPos()),this.tick())},r.seekToStartPos=function(){},r._handleFragmentLoadComplete=function(t){var e=this.transmuxer;if(e){var r=t.frag,i=t.part,n=t.partsLoaded,a=!n||0===n.length||n.some((function(t){return!t})),s=new $r(r.level,r.sn,r.stats.chunkCount+1,0,i?i.index:-1,!a);e.flush(s)}},r._handleFragmentLoadProgress=function(t){},r._doFragLoad=function(t,e,r,i){var n,a=this;void 0===r&&(r=null);var s=null==e?void 0:e.details;if(!this.levels||!s)throw new Error("frag load aborted, missing level"+(s?"":" detail")+"s");var o=null;if(!t.encrypted||null!=(n=t.decryptdata)&&n.key?!t.encrypted&&s.encryptedFragments.length&&this.keyLoader.loadClear(t,s.encryptedFragments):(this.log("Loading key for "+t.sn+" of ["+s.startSN+"-"+s.endSN+"], "+("[stream-controller]"===this.logPrefix?"level":"track")+" "+t.level),this.state=mi,this.fragCurrent=t,o=this.keyLoader.load(t).then((function(t){if(!a.fragContextChanged(t.frag))return a.hls.trigger(S.KEY_LOADED,t),a.state===mi&&(a.state=vi),t})),this.hls.trigger(S.KEY_LOADING,{frag:t}),null===this.fragCurrent&&(o=Promise.reject(new Error("frag load aborted, context changed in KEY_LOADING")))),r=Math.max(t.start,r||0),this.config.lowLatencyMode&&"initSegment"!==t.sn){var l=s.partList;if(l&&i){r>t.end&&s.fragmentHint&&(t=s.fragmentHint);var u=this.getNextPart(l,t,r);if(u>-1){var h,d=l[u];return this.log("Loading part sn: "+t.sn+" p: "+d.index+" cc: "+t.cc+" of playlist ["+s.startSN+"-"+s.endSN+"] parts [0-"+u+"-"+(l.length-1)+"] "+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),this.nextLoadPosition=d.start+d.duration,this.state=pi,h=o?o.then((function(r){return!r||a.fragContextChanged(r.frag)?null:a.doFragPartsLoad(t,d,e,i)})).catch((function(t){return a.handleFragLoadError(t)})):this.doFragPartsLoad(t,d,e,i).catch((function(t){return a.handleFragLoadError(t)})),this.hls.trigger(S.FRAG_LOADING,{frag:t,part:d,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")):h}if(!t.url||this.loadedEndOfParts(l,r))return Promise.resolve(null)}}this.log("Loading fragment "+t.sn+" cc: "+t.cc+" "+(s?"of ["+s.startSN+"-"+s.endSN+"] ":"")+("[stream-controller]"===this.logPrefix?"level":"track")+": "+t.level+", target: "+parseFloat(r.toFixed(3))),y(t.sn)&&!this.bitrateTest&&(this.nextLoadPosition=t.start+t.duration),this.state=pi;var c,f=this.config.progressive;return c=f&&o?o.then((function(e){return!e||a.fragContextChanged(null==e?void 0:e.frag)?null:a.fragmentLoader.load(t,i)})).catch((function(t){return a.handleFragLoadError(t)})):Promise.all([this.fragmentLoader.load(t,f?i:void 0),o]).then((function(t){var e=t[0];return!f&&e&&i&&i(e),e})).catch((function(t){return a.handleFragLoadError(t)})),this.hls.trigger(S.FRAG_LOADING,{frag:t,targetBufferTime:r}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")):c},r.doFragPartsLoad=function(t,e,r,i){var n=this;return new Promise((function(a,s){var o,l=[],u=null==(o=r.details)?void 0:o.partList;!function e(o){n.fragmentLoader.loadPart(t,o,i).then((function(i){l[o.index]=i;var s=i.part;n.hls.trigger(S.FRAG_LOADED,i);var h=ur(r,t.sn,o.index+1)||hr(u,t.sn,o.index+1);if(!h)return a({frag:t,part:s,partsLoaded:l});e(h)})).catch(s)}(e)}))},r.handleFragLoadError=function(t){if("data"in t){var e=t.data;t.data&&e.details===A.INTERNAL_ABORTED?this.handleFragLoadAborted(e.frag,e.part):this.hls.trigger(S.ERROR,e)}else this.hls.trigger(S.ERROR,{type:L.OTHER_ERROR,details:A.INTERNAL_EXCEPTION,err:t,error:t,fatal:!0});return null},r._handleTransmuxerFlush=function(t){var e=this.getCurrentContext(t);if(e&&this.state===Ti){var r=e.frag,i=e.part,n=e.level,a=self.performance.now();r.stats.parsing.end=a,i&&(i.stats.parsing.end=a),this.updateLevelTiming(r,i,n,t.partial)}else this.fragCurrent||this.state===gi||this.state===Ai||(this.state=vi)},r.getCurrentContext=function(t){var e=this.levels,r=this.fragCurrent,i=t.level,n=t.sn,a=t.part;if(null==e||!e[i])return this.warn("Levels object was unset while buffering fragment "+n+" of level "+i+". The current chunk will not be buffered."),null;var s=e[i],o=a>-1?ur(s,n,a):null,l=o?o.fragment:function(t,e,r){if(null==t||!t.details)return null;var i=t.details,n=i.fragments[e-i.startSN];return n||((n=i.fragmentHint)&&n.sn===e?n:ea&&this.flushMainBuffer(s,t.start)}else this.flushMainBuffer(0,t.start)},r.getFwdBufferInfo=function(t,e){var r=this.getLoadPosition();return y(r)?this.getFwdBufferInfoAtPos(t,r,e):null},r.getFwdBufferInfoAtPos=function(t,e,r){var i=this.config.maxBufferHole,n=Jr.bufferInfo(t,e,i);if(0===n.len&&void 0!==n.nextStart){var a=this.fragmentTracker.getBufferedFrag(e,r);if(a&&n.nextStart=r&&(e.maxMaxBufferLength/=2,this.warn("Reduce max buffer length to "+e.maxMaxBufferLength+"s"),!0)},r.getAppendedFrag=function(t,e){var r=this.fragmentTracker.getAppendedFrag(t,we);return r&&"fragment"in r?r.fragment:r},r.getNextFragment=function(t,e){var r=e.fragments,i=r.length;if(!i)return null;var n,a=this.config,s=r[0].start;if(e.live){var o=a.initialLiveManifestSize;if(ie},r.getNextFragmentLoopLoading=function(t,e,r,i,n){var a=t.gap,s=this.getNextFragment(this.nextLoadPosition,e);if(null===s)return s;if(t=s,a&&t&&!t.gap&&r.nextStart){var o=this.getFwdBufferInfoAtPos(this.mediaBuffer?this.mediaBuffer:this.media,r.nextStart,i);if(null!==o&&r.len+o.len>=n)return this.log('buffer full after gaps in "'+i+'" playlist starting at sn: '+t.sn),null}return t},r.mapToInitFragWhenRequired=function(t){return null==t||!t.initSegment||null!=t&&t.initSegment.data||this.bitrateTest?t:t.initSegment},r.getNextPart=function(t,e,r){for(var i=-1,n=!1,a=!0,s=0,o=t.length;s-1&&rr.start&&r.loaded},r.getInitialLiveFragment=function(t,e){var r=this.fragPrevious,i=null;if(r){if(t.hasProgramDateTime&&(this.log("Live playlist, switching playlist, load frag with same PDT: "+r.programDateTime),i=function(t,e,r){if(null===e||!Array.isArray(t)||!t.length||!y(e))return null;if(e<(t[0].programDateTime||0))return null;if(e>=(t[t.length-1].endProgramDateTime||0))return null;r=r||0;for(var i=0;i=t.startSN&&n<=t.endSN){var a=e[n-t.startSN];r.cc===a.cc&&(i=a,this.log("Live playlist, switching playlist, load frag with next SN: "+i.sn))}i||(i=function(t,e){return pr(t,(function(t){return t.cce?-1:0}))}(e,r.cc),i&&this.log("Live playlist, switching playlist, load frag with same CC: "+i.sn))}}else{var s=this.hls.liveSyncPosition;null!==s&&(i=this.getFragmentAtPosition(s,this.bitrateTest?t.fragmentEnd:t.edge,t))}return i},r.getFragmentAtPosition=function(t,e,r){var i,n=this.config,a=this.fragPrevious,s=r.fragments,o=r.endSN,l=r.fragmentHint,u=n.maxFragLookUpTolerance,h=r.partList,d=!!(n.lowLatencyMode&&null!=h&&h.length&&l);if(d&&l&&!this.bitrateTest&&(s=s.concat(l),o=l.sn),i=te-u?0:u):s[s.length-1]){var c=i.sn-r.startSN,f=this.fragmentTracker.getState(i);if((f===jr||f===Wr&&i.gap)&&(a=i),a&&i.sn===a.sn&&(!d||h[0].fragment.sn>i.sn)&&a&&i.level===a.level){var g=s[c+1];i=i.sn=a-e.maxFragLookUpTolerance&&n<=s;if(null!==i&&r.duration>i&&(n"+t.startSN+" prev-sn: "+(o?o.sn:"na")+" fragments: "+i),l}return n},r.waitForCdnTuneIn=function(t){return t.live&&t.canBlockReload&&t.partTarget&&t.tuneInGoal>Math.max(t.partHoldBack,3*t.partTarget)},r.setStartPosition=function(t,e){var r=this.startPosition;if(r "+(null==(n=this.fragCurrent)?void 0:n.url))}else{var a=e.details===A.FRAG_GAP;a&&this.fragmentTracker.fragBuffered(i,!0);var s=e.errorAction,o=s||{},l=o.action,u=o.retryCount,h=void 0===u?0:u,d=o.retryConfig;if(s&&l===Rr&&d){this.resetStartWhenNotLoaded(this.levelLastLoaded);var c=gr(d,h);this.warn("Fragment "+i.sn+" of "+t+" "+i.level+" errored with "+e.details+", retrying loading "+(h+1)+"/"+d.maxNumRetry+" in "+c+"ms"),s.resolved=!0,this.retryDate=self.performance.now()+c,this.state=yi}else if(d&&s){if(this.resetFragmentErrors(t),!(h.5;i&&this.reduceMaxBufferLength(r.len);var n=!i;return n&&this.warn("Buffer full error while media.currentTime is not buffered, flush "+e+" buffer"),t.frag&&(this.fragmentTracker.removeFragment(t.frag),this.nextLoadPosition=t.frag.start),this.resetLoadingState(),n}return!1},r.resetFragmentErrors=function(t){t===Ce&&(this.fragCurrent=null),this.loadedmetadata||(this.startFragRequested=!1),this.state!==gi&&(this.state=vi)},r.afterBufferFlushed=function(t,e,r){if(t){var i=Jr.getBuffered(t);this.fragmentTracker.detectEvictedFragments(e,i,r),this.state===Li&&this.resetLoadingState()}},r.resetLoadingState=function(){this.log("Reset loading state"),this.fragCurrent=null,this.fragPrevious=null,this.state=vi},r.resetStartWhenNotLoaded=function(t){if(!this.loadedmetadata){this.startFragRequested=!1;var e=t?t.details:null;null!=e&&e.live?(this.startPosition=-1,this.setStartPosition(e,0),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}},r.resetWhenMissingContext=function(t){this.warn("The loading context changed while buffering fragment "+t.sn+" of level "+t.level+". This chunk will not be buffered."),this.removeUnbufferedFrags(),this.resetStartWhenNotLoaded(this.levelLastLoaded),this.resetLoadingState()},r.removeUnbufferedFrags=function(t){void 0===t&&(t=0),this.fragmentTracker.removeFragmentsInRange(t,1/0,this.playlistType,!1,!0)},r.updateLevelTiming=function(t,e,r,i){var n,a=this,s=r.details;if(s){if(!Object.keys(t.elementaryStreams).reduce((function(e,n){var o=t.elementaryStreams[n];if(o){var l=o.endPTS-o.startPTS;if(l<=0)return a.warn("Could not parse fragment "+t.sn+" "+n+" duration reliably ("+l+")"),e||!1;var u=i?0:ar(s,t,o.startPTS,o.endPTS,o.startDTS,o.endDTS);return a.hls.trigger(S.LEVEL_PTS_UPDATED,{details:s,level:r,drift:u,type:n,frag:t,start:o.startPTS,end:o.endPTS}),!0}return e}),!1)&&null===(null==(n=this.transmuxer)?void 0:n.error)){var o=new Error("Found no media in fragment "+t.sn+" of level "+t.level+" resetting transmuxer to fallback to playlist timing");if(0===r.fragmentError&&(r.fragmentError++,t.gap=!0,this.fragmentTracker.removeFragment(t),this.fragmentTracker.fragBuffered(t,!0)),this.warn(o.message),this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!1,error:o,frag:t,reason:"Found no media in msn "+t.sn+' of level "'+r.url+'"'}),!this.hls)return;this.resetTransmuxer()}this.state=Si,this.hls.trigger(S.FRAG_PARSED,{frag:t,part:e})}else this.warn("level.details undefined")},r.resetTransmuxer=function(){this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null)},r.recoverWorkerError=function(t){"demuxerWorker"===t.event&&(this.fragmentTracker.removeAllFragments(),this.resetTransmuxer(),this.resetStartWhenNotLoaded(this.levelLastLoaded),this.resetLoadingState())},s(e,[{key:"state",get:function(){return this._state},set:function(t){var e=this._state;e!==t&&(this._state=t,this.log(e+"->"+t))}}]),e}(Hr),Di=function(){function t(){this.chunks=[],this.dataLength=0}var e=t.prototype;return e.push=function(t){this.chunks.push(t),this.dataLength+=t.length},e.flush=function(){var t,e=this.chunks,r=this.dataLength;return e.length?(t=1===e.length?e[0]:function(t,e){for(var r=new Uint8Array(e),i=0,n=0;n0&&s.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:Ge,duration:Number.POSITIVE_INFINITY});n>>5}function Fi(t,e){return e+1=t.length)return!1;var i=Pi(t,e);if(i<=r)return!1;var n=e+i;return n===t.length||Fi(t,n)}return!1}function Oi(t,e,r,i,n){if(!t.samplerate){var a=function(t,e,r,i){var n,a,s,o,l=navigator.userAgent.toLowerCase(),u=i,h=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];n=1+((192&e[r+2])>>>6);var d=(60&e[r+2])>>>2;if(!(d>h.length-1))return s=(1&e[r+2])<<2,s|=(192&e[r+3])>>>6,w.log("manifest codec:"+i+", ADTS type:"+n+", samplingIndex:"+d),/firefox/i.test(l)?d>=6?(n=5,o=new Array(4),a=d-3):(n=2,o=new Array(2),a=d):-1!==l.indexOf("android")?(n=2,o=new Array(2),a=d):(n=5,o=new Array(4),i&&(-1!==i.indexOf("mp4a.40.29")||-1!==i.indexOf("mp4a.40.5"))||!i&&d>=6?a=d-3:((i&&-1!==i.indexOf("mp4a.40.2")&&(d>=6&&1===s||/vivaldi/i.test(l))||!i&&1===s)&&(n=2,o=new Array(2)),a=d)),o[0]=n<<3,o[0]|=(14&d)>>1,o[1]|=(1&d)<<7,o[1]|=s<<3,5===n&&(o[1]|=(14&a)>>1,o[2]=(1&a)<<7,o[2]|=8,o[3]=0),{config:o,samplerate:h[d],channelCount:s,codec:"mp4a.40."+n,manifestCodec:u};var c=new Error("invalid ADTS sampling index:"+d);t.emit(S.ERROR,S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!0,error:c,reason:c.message})}(e,r,i,n);if(!a)return;t.config=a.config,t.samplerate=a.samplerate,t.channelCount=a.channelCount,t.codec=a.codec,t.manifestCodec=a.manifestCodec,w.log("parsed codec:"+t.codec+", rate:"+a.samplerate+", channels:"+a.channelCount)}}function Ni(t){return 9216e4/t}function Ui(t,e,r,i,n){var a,s=i+n*Ni(t.samplerate),o=function(t,e){var r=xi(t,e);if(e+r<=t.length){var i=Pi(t,e)-r;if(i>0)return{headerLength:r,frameLength:i}}}(e,r);if(o){var l=o.frameLength,u=o.headerLength,h=u+l,d=Math.max(0,r+h-e.length);d?(a=new Uint8Array(h-u)).set(e.subarray(r+u,e.length),0):a=e.subarray(r+u,r+h);var c={unit:a,pts:s};return d||t.samples.push(c),{sample:c,length:h,missing:d}}var f=e.length-r;return(a=new Uint8Array(f)).set(e.subarray(r,e.length),0),{sample:{unit:a,pts:s},length:f,missing:-1}}var Bi=null,Gi=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],Ki=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],Hi=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],Vi=[0,1,1,4];function Yi(t,e,r,i,n){if(!(r+24>e.length)){var a=Wi(e,r);if(a&&r+a.frameLength<=e.length){var s=i+n*(9e4*a.samplesPerFrame/a.sampleRate),o={unit:e.subarray(r,r+a.frameLength),pts:s,dts:s};return t.config=[],t.channelCount=a.channelCount,t.samplerate=a.sampleRate,t.samples.push(o),{sample:o,length:a.frameLength,missing:0}}}}function Wi(t,e){var r=t[e+1]>>3&3,i=t[e+1]>>1&3,n=t[e+2]>>4&15,a=t[e+2]>>2&3;if(1!==r&&0!==n&&15!==n&&3!==a){var s=t[e+2]>>1&1,o=t[e+3]>>6,l=1e3*Gi[14*(3===r?3-i:3===i?3:4)+n-1],u=Ki[3*(3===r?0:2===r?1:2)+a],h=3===o?1:2,d=Hi[r][i],c=Vi[i],f=8*d*c,g=Math.floor(d*l/u+s)*c;if(null===Bi){var v=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);Bi=v?parseInt(v[1]):0}return!!Bi&&Bi<=87&&2===i&&l>=224e3&&0===o&&(t[e+3]=128|t[e+3]),{sampleRate:u,channelCount:h,frameLength:g,samplesPerFrame:f}}}function ji(t,e){return 255===t[e]&&224==(224&t[e+1])&&0!=(6&t[e+1])}function qi(t,e){return e+18&&109===t[r+4]&&111===t[r+5]&&111===t[r+6]&&102===t[r+7])return!0;r=i>1?r+i:e}return!1}(t)},e.demux=function(t,e){this.timeOffset=e;var r=t,i=this.videoTrack,n=this.txtTrack;if(this.config.progressive){this.remainderData&&(r=Kt(this.remainderData,t));var a=function(t){var e={valid:null,remainder:null},r=xt(t,["moof"]);if(r.length<2)return e.remainder=t,e;var i=r[r.length-1];return e.valid=nt(t,0,i.byteOffset-8),e.remainder=nt(t,i.byteOffset-8),e}(r);this.remainderData=a.remainder,i.samples=a.valid||new Uint8Array}else i.samples=r;var s=this.extractID3Track(i,e);return n.samples=Ht(e,i),{videoTrack:i,audioTrack:this.audioTrack,id3Track:s,textTrack:this.txtTrack}},e.flush=function(){var t=this.timeOffset,e=this.videoTrack,r=this.txtTrack;e.samples=this.remainderData||new Uint8Array,this.remainderData=null;var i=this.extractID3Track(e,this.timeOffset);return r.samples=Ht(t,e),{videoTrack:e,audioTrack:Ii(),id3Track:i,textTrack:Ii()}},e.extractID3Track=function(t,e){var r=this.id3Track;if(t.samples.length){var i=xt(t.samples,["emsg"]);i&&i.forEach((function(t){var i=function(t){var e=t[0],r="",i="",n=0,a=0,s=0,o=0,l=0,u=0;if(0===e){for(;"\0"!==bt(t.subarray(u,u+1));)r+=bt(t.subarray(u,u+1)),u+=1;for(r+=bt(t.subarray(u,u+1)),u+=1;"\0"!==bt(t.subarray(u,u+1));)i+=bt(t.subarray(u,u+1)),u+=1;i+=bt(t.subarray(u,u+1)),u+=1,n=It(t,12),a=It(t,16),o=It(t,20),l=It(t,24),u=28}else if(1===e){n=It(t,u+=4);var h=It(t,u+=4),d=It(t,u+=4);for(u+=4,s=Math.pow(2,32)*h+d,E(s)||(s=Number.MAX_SAFE_INTEGER,w.warn("Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")),o=It(t,u),l=It(t,u+=4),u+=4;"\0"!==bt(t.subarray(u,u+1));)r+=bt(t.subarray(u,u+1)),u+=1;for(r+=bt(t.subarray(u,u+1)),u+=1;"\0"!==bt(t.subarray(u,u+1));)i+=bt(t.subarray(u,u+1)),u+=1;i+=bt(t.subarray(u,u+1)),u+=1}return{schemeIdUri:r,value:i,timeScale:n,presentationTime:s,presentationTimeDelta:a,eventDuration:o,id:l,payload:t.subarray(u,t.byteLength)}}(t);if(Qi.test(i.schemeIdUri)){var n=y(i.presentationTime)?i.presentationTime/i.timeScale:e+i.presentationTimeDelta/i.timeScale,a=4294967295===i.eventDuration?Number.POSITIVE_INFINITY:i.eventDuration/i.timeScale;a<=.001&&(a=Number.POSITIVE_INFINITY);var s=i.payload;r.samples.push({data:s,len:s.byteLength,dts:n,pts:n,type:He,duration:a})}}))}return r},e.demuxSampleAes=function(t,e,r){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))},e.destroy=function(){},t}(),$i=function(t,e){var r=0,i=5;e+=i;for(var n=new Uint32Array(1),a=new Uint32Array(1),s=new Uint8Array(1);i>0;){s[0]=t[e];var o=Math.min(i,8),l=8-o;a[0]=4278190080>>>24+l<>l,r=r?r<e.length)return-1;if(11!==e[r]||119!==e[r+1])return-1;var a=e[r+4]>>6;if(a>=3)return-1;var s=[48e3,44100,32e3][a],o=63&e[r+4],l=2*[64,69,96,64,70,96,80,87,120,80,88,120,96,104,144,96,105,144,112,121,168,112,122,168,128,139,192,128,140,192,160,174,240,160,175,240,192,208,288,192,209,288,224,243,336,224,244,336,256,278,384,256,279,384,320,348,480,320,349,480,384,417,576,384,418,576,448,487,672,448,488,672,512,557,768,512,558,768,640,696,960,640,697,960,768,835,1152,768,836,1152,896,975,1344,896,976,1344,1024,1114,1536,1024,1115,1536,1152,1253,1728,1152,1254,1728,1280,1393,1920,1280,1394,1920][3*o+a];if(r+l>e.length)return-1;var u=e[r+6]>>5,h=0;2===u?h+=2:(1&u&&1!==u&&(h+=2),4&u&&(h+=2));var d=(e[r+6]<<8|e[r+7])>>12-h&1,c=[2,1,2,3,3,4,4,5][u]+d,f=e[r+5]>>3,g=7&e[r+5],v=new Uint8Array([a<<6|f<<1|g>>2,(3&g)<<6|u<<3|d<<2|o>>4,o<<4&224]),m=i+n*(1536/s*9e4),p=e.subarray(r,r+l);return t.config=v,t.channelCount=c,t.samplerate=s,t.samples.push({unit:p,pts:m}),l}var en=function(){function t(){this.VideoSample=null}var e=t.prototype;return e.createVideoSample=function(t,e,r,i){return{key:t,frame:!1,pts:e,dts:r,units:[],debug:i,length:0}},e.getLastNalUnit=function(t){var e,r,i=this.VideoSample;if(i&&0!==i.units.length||(i=t[t.length-1]),null!=(e=i)&&e.units){var n=i.units;r=n[n.length-1]}return r},e.pushAccessUnit=function(t,e){if(t.units.length&&t.frame){if(void 0===t.pts){var r=e.samples,i=r.length;if(!i)return void e.dropped++;var n=r[i-1];t.pts=n.pts,t.dts=n.dts}e.samples.push(t)}t.debug.length&&w.log(t.pts+"/"+t.dts+":"+t.debug)},t}(),rn=function(){function t(t){this.data=void 0,this.bytesAvailable=void 0,this.word=void 0,this.bitsAvailable=void 0,this.data=t,this.bytesAvailable=t.byteLength,this.word=0,this.bitsAvailable=0}var e=t.prototype;return e.loadWord=function(){var t=this.data,e=this.bytesAvailable,r=t.byteLength-e,i=new Uint8Array(4),n=Math.min(4,e);if(0===n)throw new Error("no bytes available");i.set(t.subarray(r,r+n)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=8*n,this.bytesAvailable-=n},e.skipBits=function(t){var e;t=Math.min(t,8*this.bytesAvailable+this.bitsAvailable),this.bitsAvailable>t?(this.word<<=t,this.bitsAvailable-=t):(t-=this.bitsAvailable,t-=(e=t>>3)<<3,this.bytesAvailable-=e,this.loadWord(),this.word<<=t,this.bitsAvailable-=t)},e.readBits=function(t){var e=Math.min(this.bitsAvailable,t),r=this.word>>>32-e;if(t>32&&w.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=e,this.bitsAvailable>0)this.word<<=e;else{if(!(this.bytesAvailable>0))throw new Error("no bits available");this.loadWord()}return(e=t-e)>0&&this.bitsAvailable?r<>>t))return this.word<<=t,this.bitsAvailable-=t,t;return this.loadWord(),t+this.skipLZ()},e.skipUEG=function(){this.skipBits(1+this.skipLZ())},e.skipEG=function(){this.skipBits(1+this.skipLZ())},e.readUEG=function(){var t=this.skipLZ();return this.readBits(t+1)-1},e.readEG=function(){var t=this.readUEG();return 1&t?1+t>>>1:-1*(t>>>1)},e.readBoolean=function(){return 1===this.readBits(1)},e.readUByte=function(){return this.readBits(8)},e.readUShort=function(){return this.readBits(16)},e.readUInt=function(){return this.readBits(32)},e.skipScalingList=function(t){for(var e=8,r=8,i=0;i4){var f=new rn(c).readSliceType();2!==f&&4!==f&&7!==f&&9!==f||(h=!0)}h&&null!=(d=l)&&d.frame&&!l.key&&(s.pushAccessUnit(l,t),l=s.VideoSample=null),l||(l=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts,"")),l.frame=!0,l.key=h;break;case 5:a=!0,null!=(o=l)&&o.frame&&!l.key&&(s.pushAccessUnit(l,t),l=s.VideoSample=null),l||(l=s.VideoSample=s.createVideoSample(!0,r.pts,r.dts,"")),l.key=!0,l.frame=!0;break;case 6:a=!0,Yt(i.data,1,r.pts,e.samples);break;case 7:var g,v;a=!0,u=!0;var m=i.data,p=new rn(m).readSPS();if(!t.sps||t.width!==p.width||t.height!==p.height||(null==(g=t.pixelRatio)?void 0:g[0])!==p.pixelRatio[0]||(null==(v=t.pixelRatio)?void 0:v[1])!==p.pixelRatio[1]){t.width=p.width,t.height=p.height,t.pixelRatio=p.pixelRatio,t.sps=[m],t.duration=n;for(var y=m.subarray(1,4),E="avc1.",T=0;T<3;T++){var S=y[T].toString(16);S.length<2&&(S="0"+S),E+=S}t.codec=E}break;case 8:a=!0,t.pps=[i.data];break;case 9:a=!0,t.audFound=!0,l&&s.pushAccessUnit(l,t),l=s.VideoSample=s.createVideoSample(!1,r.pts,r.dts,"");break;case 12:a=!0;break;default:a=!1,l&&(l.debug+="unknown NAL "+i.type+" ")}l&&a&&l.units.push(i)})),i&&l&&(this.pushAccessUnit(l,t),this.VideoSample=null)},r.parseAVCNALu=function(t,e){var r,i,n=e.byteLength,a=t.naluState||0,s=a,o=[],l=0,u=-1,h=0;for(-1===a&&(u=0,h=31&e[0],a=0,l=1);l=0){var d={data:e.subarray(u,i),type:h};o.push(d)}else{var c=this.getLastNalUnit(t.samples);c&&(s&&l<=4-s&&c.state&&(c.data=c.data.subarray(0,c.data.byteLength-s)),i>0&&(c.data=Kt(c.data,e.subarray(0,i)),c.state=0))}l=0&&a>=0){var f={data:e.subarray(u,n),type:h,state:a};o.push(f)}if(0===o.length){var g=this.getLastNalUnit(t.samples);g&&(g.data=Kt(g.data,e))}return t.naluState=a,o},e}(en),an=function(){function t(t,e,r){this.keyData=void 0,this.decrypter=void 0,this.keyData=r,this.decrypter=new ci(e,{removePKCS7Padding:!1})}var e=t.prototype;return e.decryptBuffer=function(t){return this.decrypter.decrypt(t,this.keyData.key.buffer,this.keyData.iv.buffer)},e.decryptAacSample=function(t,e,r){var i=this,n=t[e].unit;if(!(n.length<=16)){var a=n.subarray(16,n.length-n.length%16),s=a.buffer.slice(a.byteOffset,a.byteOffset+a.length);this.decryptBuffer(s).then((function(a){var s=new Uint8Array(a);n.set(s,16),i.decrypter.isSync()||i.decryptAacSamples(t,e+1,r)}))}},e.decryptAacSamples=function(t,e,r){for(;;e++){if(e>=t.length)return void r();if(!(t[e].unit.length<32||(this.decryptAacSample(t,e,r),this.decrypter.isSync())))return}},e.getAvcEncryptedData=function(t){for(var e=16*Math.floor((t.length-48)/160)+16,r=new Int8Array(e),i=0,n=32;n=t.length)return void i();for(var n=t[e].units;!(r>=n.length);r++){var a=n[r];if(!(a.data.length<=48||1!==a.type&&5!==a.type||(this.decryptAvcSample(t,e,r,i,a),this.decrypter.isSync())))return}}},t}(),sn=188,on=function(){function t(t,e,r){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this._pmtId=-1,this._videoTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.remainderData=null,this.videoParser=void 0,this.observer=t,this.config=e,this.typeSupported=r,this.videoParser=new nn}t.probe=function(e){var r=t.syncOffset(e);return r>0&&w.warn("MPEG2-TS detected but first sync word found @ offset "+r),-1!==r},t.syncOffset=function(t){for(var e=t.length,r=Math.min(940,e-sn)+1,i=0;i1&&(0===a&&s>2||o+sn>r))return a}i++}return-1},t.createTrack=function(t,e){return{container:"video"===t||"audio"===t?"video/mp2t":void 0,type:t,id:kt[t],pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0,duration:"audio"===t?e:void 0}};var e=t.prototype;return e.resetInitSegment=function(e,r,i,n){this.pmtParsed=!1,this._pmtId=-1,this._videoTrack=t.createTrack("video"),this._audioTrack=t.createTrack("audio",n),this._id3Track=t.createTrack("id3"),this._txtTrack=t.createTrack("text"),this._audioTrack.segmentCodec="aac",this.aacOverFlow=null,this.remainderData=null,this.audioCodec=r,this.videoCodec=i,this._duration=n},e.resetTimeStamp=function(){},e.resetContiguity=function(){var t=this._audioTrack,e=this._videoTrack,r=this._id3Track;t&&(t.pesData=null),e&&(e.pesData=null),r&&(r.pesData=null),this.aacOverFlow=null,this.remainderData=null},e.demux=function(e,r,i,n){var a;void 0===i&&(i=!1),void 0===n&&(n=!1),i||(this.sampleAes=null);var s=this._videoTrack,o=this._audioTrack,l=this._id3Track,u=this._txtTrack,h=s.pid,d=s.pesData,c=o.pid,f=l.pid,g=o.pesData,v=l.pesData,m=null,p=this.pmtParsed,y=this._pmtId,E=e.length;if(this.remainderData&&(E=(e=Kt(this.remainderData,e)).length,this.remainderData=null),E>4>1){if((I=k+5+e[k+4])===k+sn)continue}else I=k+4;switch(D){case h:b&&(d&&(a=cn(d))&&this.videoParser.parseAVCPES(s,u,a,!1,this._duration),d={data:[],size:0}),d&&(d.data.push(e.subarray(I,k+sn)),d.size+=k+sn-I);break;case c:if(b){if(g&&(a=cn(g)))switch(o.segmentCodec){case"aac":this.parseAACPES(o,a);break;case"mp3":this.parseMPEGPES(o,a);break;case"ac3":this.parseAC3PES(o,a)}g={data:[],size:0}}g&&(g.data.push(e.subarray(I,k+sn)),g.size+=k+sn-I);break;case f:b&&(v&&(a=cn(v))&&this.parseID3PES(l,a),v={data:[],size:0}),v&&(v.data.push(e.subarray(I,k+sn)),v.size+=k+sn-I);break;case 0:b&&(I+=e[I]+1),y=this._pmtId=un(e,I);break;case y:b&&(I+=e[I]+1);var C=hn(e,I,this.typeSupported,i);(h=C.videoPid)>0&&(s.pid=h,s.segmentCodec=C.segmentVideoCodec),(c=C.audioPid)>0&&(o.pid=c,o.segmentCodec=C.segmentAudioCodec),(f=C.id3Pid)>0&&(l.pid=f),null===m||p||(w.warn("MPEG-TS PMT found at "+k+" after unknown PID '"+m+"'. Backtracking to sync byte @"+T+" to parse all TS packets."),m=null,k=T-188),p=this.pmtParsed=!0;break;case 17:case 8191:break;default:m=D}}else R++;if(R>0){var _=new Error("Found "+R+" TS packet/s that do not start with 0x47");this.observer.emit(S.ERROR,S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!1,error:_,reason:_.message})}s.pesData=d,o.pesData=g,l.pesData=v;var x={audioTrack:o,videoTrack:s,id3Track:l,textTrack:u};return n&&this.extractRemainingSamples(x),x},e.flush=function(){var t,e=this.remainderData;return this.remainderData=null,t=e?this.demux(e,-1,!1,!0):{videoTrack:this._videoTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(t),this.sampleAes?this.decrypt(t,this.sampleAes):t},e.extractRemainingSamples=function(t){var e,r=t.audioTrack,i=t.videoTrack,n=t.id3Track,a=t.textTrack,s=i.pesData,o=r.pesData,l=n.pesData;if(s&&(e=cn(s))?(this.videoParser.parseAVCPES(i,a,e,!0,this._duration),i.pesData=null):i.pesData=s,o&&(e=cn(o))){switch(r.segmentCodec){case"aac":this.parseAACPES(r,e);break;case"mp3":this.parseMPEGPES(r,e);break;case"ac3":this.parseAC3PES(r,e)}r.pesData=null}else null!=o&&o.size&&w.log("last AAC PES packet truncated,might overlap between fragments"),r.pesData=o;l&&(e=cn(l))?(this.parseID3PES(n,e),n.pesData=null):n.pesData=l},e.demuxSampleAes=function(t,e,r){var i=this.demux(t,r,!0,!this.config.progressive),n=this.sampleAes=new an(this.observer,this.config,e);return this.decrypt(i,n)},e.decrypt=function(t,e){return new Promise((function(r){var i=t.audioTrack,n=t.videoTrack;i.samples&&"aac"===i.segmentCodec?e.decryptAacSamples(i.samples,0,(function(){n.samples?e.decryptAvcSamples(n.samples,0,0,(function(){r(t)})):r(t)})):n.samples&&e.decryptAvcSamples(n.samples,0,0,(function(){r(t)}))}))},e.destroy=function(){this._duration=0},e.parseAACPES=function(t,e){var r,i,n,a=0,s=this.aacOverFlow,o=e.data;if(s){this.aacOverFlow=null;var l=s.missing,u=s.sample.unit.byteLength;if(-1===l)o=Kt(s.sample.unit,o);else{var h=u-l;s.sample.unit.set(o.subarray(0,l),h),t.samples.push(s.sample),a=s.missing}}for(r=a,i=o.length;r0;)o+=n;else w.warn("[tsdemuxer]: AC3 PES unknown PTS")},e.parseID3PES=function(t,e){if(void 0!==e.pts){var r=o({},e,{type:this._videoTrack?He:Ge,duration:Number.POSITIVE_INFINITY});t.samples.push(r)}else w.warn("[tsdemuxer]: ID3 PES unknown PTS")},t}();function ln(t,e){return((31&t[e+1])<<8)+t[e+2]}function un(t,e){return(31&t[e+10])<<8|t[e+11]}function hn(t,e,r,i){var n={audioPid:-1,videoPid:-1,id3Pid:-1,segmentVideoCodec:"avc",segmentAudioCodec:"aac"},a=e+3+((15&t[e+1])<<8|t[e+2])-4;for(e+=12+((15&t[e+10])<<8|t[e+11]);e0)for(var l=e+5,u=o;u>2;){106===t[l]&&(!0!==r.ac3?w.log("AC-3 audio found, not supported in this browser for now"):(n.audioPid=s,n.segmentAudioCodec="ac3"));var h=t[l+1]+2;l+=h,u-=h}break;case 194:case 135:w.warn("Unsupported EC-3 in M2TS found");break;case 36:w.warn("Unsupported HEVC in M2TS found")}e+=o+5}return n}function dn(t){w.log(t+" with AES-128-CBC encryption found in unencrypted stream")}function cn(t){var e,r,i,n,a,s=0,o=t.data;if(!t||0===t.size)return null;for(;o[0].length<19&&o.length>1;)o[0]=Kt(o[0],o[1]),o.splice(1,1);if(1===((e=o[0])[0]<<16)+(e[1]<<8)+e[2]){if((r=(e[4]<<8)+e[5])&&r>t.size-6)return null;var l=e[7];192&l&&(n=536870912*(14&e[9])+4194304*(255&e[10])+16384*(254&e[11])+128*(255&e[12])+(254&e[13])/2,64&l?n-(a=536870912*(14&e[14])+4194304*(255&e[15])+16384*(254&e[16])+128*(255&e[17])+(254&e[18])/2)>54e5&&(w.warn(Math.round((n-a)/9e4)+"s delta between PTS and DTS, align them"),n=a):a=n);var u=(i=e[8])+9;if(t.size<=u)return null;t.size-=u;for(var h=new Uint8Array(t.size),d=0,c=o.length;df){u-=f;continue}e=e.subarray(u),f-=u,u=0}h.set(e,s),s+=f}return r&&(r-=i+3),{data:h,pts:n,dts:a,len:r}}return null}var fn=function(t){function e(){return t.apply(this,arguments)||this}l(e,t);var r=e.prototype;return r.resetInitSegment=function(e,r,i,n){t.prototype.resetInitSegment.call(this,e,r,i,n),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:r,duration:n,inputTimeScale:9e4,dropped:0}},e.probe=function(t){if(!t)return!1;var e=lt(t,0),r=(null==e?void 0:e.length)||0;if(e&&11===t[r]&&119===t[r+1]&&void 0!==dt(e)&&$i(t,r)<=16)return!1;for(var i=t.length;r1?r-1:0),n=1;n>24&255,o[1]=e>>16&255,o[2]=e>>8&255,o[3]=255&e,o.set(t,4),a=0,e=8;a>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,85,196,0,0]))},t.mdia=function(e){return t.box(t.types.mdia,t.mdhd(e.timescale,e.duration),t.hdlr(e.type),t.minf(e))},t.mfhd=function(e){return t.box(t.types.mfhd,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,255&e]))},t.minf=function(e){return"audio"===e.type?t.box(t.types.minf,t.box(t.types.smhd,t.SMHD),t.DINF,t.stbl(e)):t.box(t.types.minf,t.box(t.types.vmhd,t.VMHD),t.DINF,t.stbl(e))},t.moof=function(e,r,i){return t.box(t.types.moof,t.mfhd(e),t.traf(i,r))},t.moov=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trak(e[r]);return t.box.apply(null,[t.types.moov,t.mvhd(e[0].timescale,e[0].duration)].concat(i).concat(t.mvex(e)))},t.mvex=function(e){for(var r=e.length,i=[];r--;)i[r]=t.trex(e[r]);return t.box.apply(null,[t.types.mvex].concat(i))},t.mvhd=function(e,r){r*=e;var i=Math.floor(r/(vn+1)),n=Math.floor(r%(vn+1)),a=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,255&e,i>>24,i>>16&255,i>>8&255,255&i,n>>24,n>>16&255,n>>8&255,255&n,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return t.box(t.types.mvhd,a)},t.sdtp=function(e){var r,i,n=e.samples||[],a=new Uint8Array(4+n.length);for(r=0;r>>8&255),a.push(255&n),a=a.concat(Array.prototype.slice.call(i));for(r=0;r>>8&255),s.push(255&n),s=s.concat(Array.prototype.slice.call(i));var o=t.box(t.types.avcC,new Uint8Array([1,a[3],a[4],a[5],255,224|e.sps.length].concat(a).concat([e.pps.length]).concat(s))),l=e.width,u=e.height,h=e.pixelRatio[0],d=e.pixelRatio[1];return t.box(t.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,l>>8&255,255&l,u>>8&255,255&u,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,t.box(t.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),t.box(t.types.pasp,new Uint8Array([h>>24,h>>16&255,h>>8&255,255&h,d>>24,d>>16&255,d>>8&255,255&d])))},t.esds=function(t){var e=t.config.length;return new Uint8Array([0,0,0,0,3,23+e,0,1,0,4,15+e,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([e]).concat(t.config).concat([6,1,2]))},t.audioStsd=function(t){var e=t.samplerate;return new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t.channelCount,0,16,0,0,0,0,e>>8&255,255&e,0,0])},t.mp4a=function(e){return t.box(t.types.mp4a,t.audioStsd(e),t.box(t.types.esds,t.esds(e)))},t.mp3=function(e){return t.box(t.types[".mp3"],t.audioStsd(e))},t.ac3=function(e){return t.box(t.types["ac-3"],t.audioStsd(e),t.box(t.types.dac3,e.config))},t.stsd=function(e){return"audio"===e.type?"mp3"===e.segmentCodec&&"mp3"===e.codec?t.box(t.types.stsd,t.STSD,t.mp3(e)):"ac3"===e.segmentCodec?t.box(t.types.stsd,t.STSD,t.ac3(e)):t.box(t.types.stsd,t.STSD,t.mp4a(e)):t.box(t.types.stsd,t.STSD,t.avc1(e))},t.tkhd=function(e){var r=e.id,i=e.duration*e.timescale,n=e.width,a=e.height,s=Math.floor(i/(vn+1)),o=Math.floor(i%(vn+1));return t.box(t.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,r>>24&255,r>>16&255,r>>8&255,255&r,0,0,0,0,s>>24,s>>16&255,s>>8&255,255&s,o>>24,o>>16&255,o>>8&255,255&o,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,n>>8&255,255&n,0,0,a>>8&255,255&a,0,0]))},t.traf=function(e,r){var i=t.sdtp(e),n=e.id,a=Math.floor(r/(vn+1)),s=Math.floor(r%(vn+1));return t.box(t.types.traf,t.box(t.types.tfhd,new Uint8Array([0,0,0,0,n>>24,n>>16&255,n>>8&255,255&n])),t.box(t.types.tfdt,new Uint8Array([1,0,0,0,a>>24,a>>16&255,a>>8&255,255&a,s>>24,s>>16&255,s>>8&255,255&s])),t.trun(e,i.length+16+20+8+16+8+8),i)},t.trak=function(e){return e.duration=e.duration||4294967295,t.box(t.types.trak,t.tkhd(e),t.mdia(e))},t.trex=function(e){var r=e.id;return t.box(t.types.trex,new Uint8Array([0,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))},t.trun=function(e,r){var i,n,a,s,o,l,u=e.samples||[],h=u.length,d=12+16*h,c=new Uint8Array(d);for(r+=8+d,c.set(["video"===e.type?1:0,0,15,1,h>>>24&255,h>>>16&255,h>>>8&255,255&h,r>>>24&255,r>>>16&255,r>>>8&255,255&r],0),i=0;i>>24&255,a>>>16&255,a>>>8&255,255&a,s>>>24&255,s>>>16&255,s>>>8&255,255&s,o.isLeading<<2|o.dependsOn,o.isDependedOn<<6|o.hasRedundancy<<4|o.paddingValue<<1|o.isNonSync,61440&o.degradPrio,15&o.degradPrio,l>>>24&255,l>>>16&255,l>>>8&255,255&l],12+16*i);return t.box(t.types.trun,c)},t.initSegment=function(e){t.types||t.init();var r=t.moov(e);return Kt(t.FTYP,r)},t}();mn.types=void 0,mn.HDLR_TYPES=void 0,mn.STTS=void 0,mn.STSC=void 0,mn.STCO=void 0,mn.STSZ=void 0,mn.VMHD=void 0,mn.SMHD=void 0,mn.STSD=void 0,mn.FTYP=void 0,mn.DINF=void 0;var pn=9e4;function yn(t,e,r,i){void 0===r&&(r=1),void 0===i&&(i=!1);var n=t*e*r;return i?Math.round(n):n}function En(t,e){return void 0===e&&(e=!1),yn(t,1e3,1/pn,e)}var Tn=null,Sn=null,Ln=function(){function t(t,e,r,i){if(this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.ISGenerated=!1,this._initPTS=null,this._initDTS=null,this.nextAvcDts=null,this.nextAudioPts=null,this.videoSampleDuration=null,this.isAudioContiguous=!1,this.isVideoContiguous=!1,this.videoTrackConfig=void 0,this.observer=t,this.config=e,this.typeSupported=r,this.ISGenerated=!1,null===Tn){var n=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);Tn=n?parseInt(n[1]):0}if(null===Sn){var a=navigator.userAgent.match(/Safari\/(\d+)/i);Sn=a?parseInt(a[1]):0}}var e=t.prototype;return e.destroy=function(){this.config=this.videoTrackConfig=this._initPTS=this._initDTS=null},e.resetTimeStamp=function(t){w.log("[mp4-remuxer]: initPTS & initDTS reset"),this._initPTS=this._initDTS=t},e.resetNextTimestamp=function(){w.log("[mp4-remuxer]: reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1},e.resetInitSegment=function(){w.log("[mp4-remuxer]: ISGenerated flag reset"),this.ISGenerated=!1,this.videoTrackConfig=void 0},e.getVideoStartPts=function(t){var e=!1,r=t.reduce((function(t,r){var i=r.pts-t;return i<-4294967296?(e=!0,An(t,r.pts)):i>0?t:r.pts}),t[0].pts);return e&&w.debug("PTS rollover detected"),r},e.remux=function(t,e,r,i,n,a,s,o){var l,u,h,d,c,f,g=n,v=n,m=t.pid>-1,p=e.pid>-1,y=e.samples.length,E=t.samples.length>0,T=s&&y>0||y>1;if((!m||E)&&(!p||T)||this.ISGenerated||s){if(this.ISGenerated){var S,L,A,R,k=this.videoTrackConfig;!k||e.width===k.width&&e.height===k.height&&(null==(S=e.pixelRatio)?void 0:S[0])===(null==(L=k.pixelRatio)?void 0:L[0])&&(null==(A=e.pixelRatio)?void 0:A[1])===(null==(R=k.pixelRatio)?void 0:R[1])||this.resetInitSegment()}else h=this.generateIS(t,e,n,a);var b,D=this.isVideoContiguous,I=-1;if(T&&(I=function(t){for(var e=0;e0){w.warn("[mp4-remuxer]: Dropped "+I+" out of "+y+" video samples due to a missing keyframe");var C=this.getVideoStartPts(e.samples);e.samples=e.samples.slice(I),e.dropped+=I,b=v+=(e.samples[0].pts-C)/e.inputTimeScale}else-1===I&&(w.warn("[mp4-remuxer]: No keyframe found out of "+y+" video samples"),f=!1);if(this.ISGenerated){if(E&&T){var _=this.getVideoStartPts(e.samples),x=(An(t.samples[0].pts,_)-_)/e.inputTimeScale;g+=Math.max(0,x),v+=Math.max(0,-x)}if(E){if(t.samplerate||(w.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),h=this.generateIS(t,e,n,a)),u=this.remuxAudio(t,g,this.isAudioContiguous,a,p||T||o===Ce?v:void 0),T){var P=u?u.endPTS-u.startPTS:0;e.inputTimeScale||(w.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),h=this.generateIS(t,e,n,a)),l=this.remuxVideo(e,v,D,P)}}else T&&(l=this.remuxVideo(e,v,D,0));l&&(l.firstKeyFrame=I,l.independent=-1!==I,l.firstKeyFramePTS=b)}}return this.ISGenerated&&this._initPTS&&this._initDTS&&(r.samples.length&&(c=Rn(r,n,this._initPTS,this._initDTS)),i.samples.length&&(d=kn(i,n,this._initPTS))),{audio:u,video:l,initSegment:h,independent:f,text:d,id3:c}},e.generateIS=function(t,e,r,i){var n,a,s,o=t.samples,l=e.samples,u=this.typeSupported,h={},d=this._initPTS,c=!d||i,f="audio/mp4";if(c&&(n=a=1/0),t.config&&o.length){switch(t.timescale=t.samplerate,t.segmentCodec){case"mp3":u.mpeg?(f="audio/mpeg",t.codec=""):u.mp3&&(t.codec="mp3");break;case"ac3":t.codec="ac-3"}h.audio={id:"audio",container:f,codec:t.codec,initSegment:"mp3"===t.segmentCodec&&u.mpeg?new Uint8Array(0):mn.initSegment([t]),metadata:{channelCount:t.channelCount}},c&&(s=t.inputTimeScale,d&&s===d.timescale?c=!1:n=a=o[0].pts-Math.round(s*r))}if(e.sps&&e.pps&&l.length){if(e.timescale=e.inputTimeScale,h.video={id:"main",container:"video/mp4",codec:e.codec,initSegment:mn.initSegment([e]),metadata:{width:e.width,height:e.height}},c)if(s=e.inputTimeScale,d&&s===d.timescale)c=!1;else{var g=this.getVideoStartPts(l),v=Math.round(s*r);a=Math.min(a,An(l[0].dts,g)-v),n=Math.min(n,g-v)}this.videoTrackConfig={width:e.width,height:e.height,pixelRatio:e.pixelRatio}}if(Object.keys(h).length)return this.ISGenerated=!0,c?(this._initPTS={baseTime:n,timescale:s},this._initDTS={baseTime:a,timescale:s}):n=s=void 0,{tracks:h,initPTS:n,timescale:s}},e.remuxVideo=function(t,e,r,i){var n,a,s=t.inputTimeScale,l=t.samples,u=[],h=l.length,d=this._initPTS,c=this.nextAvcDts,f=8,g=this.videoSampleDuration,v=Number.POSITIVE_INFINITY,m=Number.NEGATIVE_INFINITY,p=!1;if(!r||null===c){var y=e*s,E=l[0].pts-An(l[0].dts,l[0].pts);Tn&&null!==c&&Math.abs(y-E-c)<15e3?r=!0:c=y-E}for(var T=d.baseTime*s/d.timescale,R=0;R0?R-1:R].dts&&(p=!0)}p&&l.sort((function(t,e){var r=t.dts-e.dts,i=t.pts-e.pts;return r||i})),n=l[0].dts;var b=(a=l[l.length-1].dts)-n,D=b?Math.round(b/(h-1)):g||t.inputTimeScale/30;if(r){var I=n-c,C=I>D,_=I<-1;if((C||_)&&(C?w.warn("AVC: "+En(I,!0)+" ms ("+I+"dts) hole between fragments detected at "+e.toFixed(3)):w.warn("AVC: "+En(-I,!0)+" ms ("+I+"dts) overlapping between fragments detected at "+e.toFixed(3)),!_||c>=l[0].pts||Tn)){n=c;var x=l[0].pts-I;if(C)l[0].dts=n,l[0].pts=x;else for(var P=0;Px);P++)l[P].dts-=I,l[P].pts-=I;w.log("Video: Initial PTS/DTS adjusted: "+En(x,!0)+"/"+En(n,!0)+", delta: "+En(I,!0)+" ms")}}for(var F=0,M=0,O=n=Math.max(0,n),N=0;N0?$.dts-l[J-1].dts:D;if(st=J>0?$.pts-l[J-1].pts:D,ot.stretchShortVideoTrack&&null!==this.nextAudioPts){var ut=Math.floor(ot.maxBufferHole*s),ht=(i?v+i*s:this.nextAudioPts)-$.pts;ht>ut?((g=ht-lt)<0?g=lt:j=!0,w.log("[mp4-remuxer]: It is approximately "+ht/90+" ms to the next segment; using duration "+g/90+" ms for the last video frame.")):g=lt}else g=lt}var dt=Math.round($.pts-$.dts);q=Math.min(q,g),z=Math.max(z,g),X=Math.min(X,st),Q=Math.max(Q,st),u.push(new Dn($.key,g,tt,dt))}if(u.length)if(Tn){if(Tn<70){var ct=u[0].flags;ct.dependsOn=2,ct.isNonSync=0}}else if(Sn&&Q-X0&&(i&&Math.abs(p-m)<9e3||Math.abs(An(g[0].pts-y,p)-m)<20*u),g.forEach((function(t){t.pts=An(t.pts-y,p)})),!r||m<0){if(g=g.filter((function(t){return t.pts>=0})),!g.length)return;m=0===n?0:i&&!f?Math.max(0,p):g[0].pts}if("aac"===t.segmentCodec)for(var E=this.config.maxAudioFramesDrift,T=0,R=m;T=E*u&&I<1e4&&f){var C=Math.round(D/u);(R=b-C*u)<0&&(C--,R+=u),0===T&&(this.nextAudioPts=m=R),w.warn("[mp4-remuxer]: Injecting "+C+" audio frame @ "+(R/a).toFixed(3)+"s due to "+Math.round(1e3*D/a)+" ms gap.");for(var _=0;_0))return;N+=v;try{F=new Uint8Array(N)}catch(t){return void this.observer.emit(S.ERROR,S.ERROR,{type:L.MUX_ERROR,details:A.REMUX_ALLOC_ERROR,fatal:!1,error:t,bytes:N,reason:"fail allocating audio mdat "+N})}d||(new DataView(F.buffer).setUint32(0,N),F.set(mn.types.mdat,4))}F.set(H,v);var Y=H.byteLength;v+=Y,c.push(new Dn(!0,l,Y,0)),O=V}var W=c.length;if(W){var j=c[c.length-1];this.nextAudioPts=m=O+s*j.duration;var q=d?new Uint8Array(0):mn.moof(t.sequenceNumber++,M/s,o({},t,{samples:c}));t.samples=[];var X=M/a,z=m/a,Q={data1:q,data2:F,startPTS:X,endPTS:z,startDTS:X,endDTS:z,type:"audio",hasAudio:!0,hasVideo:!1,nb:W};return this.isAudioContiguous=!0,Q}},e.remuxEmptyAudio=function(t,e,r,i){var n=t.inputTimeScale,a=n/(t.samplerate?t.samplerate:n),s=this.nextAudioPts,o=this._initDTS,l=9e4*o.baseTime/o.timescale,u=(null!==s?s:i.startDTS*n)+l,h=i.endDTS*n+l,d=1024*a,c=Math.ceil((h-u)/d),f=gn.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);if(w.warn("[mp4-remuxer]: remux empty Audio"),f){for(var g=[],v=0;v4294967296;)t+=r;return t}function Rn(t,e,r,i){var n=t.samples.length;if(n){for(var a=t.inputTimeScale,s=0;s0;n||(i=xt(e,["encv"])),i.forEach((function(t){xt(n?t.subarray(28):t.subarray(78),["sinf"]).forEach((function(t){var e=Bt(t);if(e){var i=e.subarray(8,24);i.some((function(t){return 0!==t}))||(w.log("[eme] Patching keyId in 'enc"+(n?"a":"v")+">sinf>>tenc' box: "+Lt(i)+" -> "+Lt(r)),e.set(r,8))}}))}))})),t}(t,i)),this.emitInitSegment=!0},e.generateInitSegment=function(t){var e=this.audioCodec,r=this.videoCodec;if(null==t||!t.byteLength)return this.initTracks=void 0,void(this.initData=void 0);var i=this.initData=Ft(t);i.audio&&(e=wn(i.audio,O)),i.video&&(r=wn(i.video,N));var n={};i.audio&&i.video?n.audiovideo={container:"video/mp4",codec:e+","+r,initSegment:t,id:"main"}:i.audio?n.audio={container:"audio/mp4",codec:e,initSegment:t,id:"audio"}:i.video?n.video={container:"video/mp4",codec:r,initSegment:t,id:"main"}:w.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=n},e.remux=function(t,e,r,i,n,a){var s,o,l=this.initPTS,u=this.lastEndTime,h={audio:void 0,video:void 0,text:i,id3:r,initSegment:void 0};y(u)||(u=this.lastEndTime=n||0);var d=e.samples;if(null==d||!d.length)return h;var c={initPTS:void 0,timescale:1},f=this.initData;if(null!=(s=f)&&s.length||(this.generateInitSegment(d),f=this.initData),null==(o=f)||!o.length)return w.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),h;this.emitInitSegment&&(c.tracks=this.initTracks,this.emitInitSegment=!1);var g=function(t,e){for(var r=0,i=0,n=0,a=xt(t,["moof","traf"]),s=0;sn}(l,m,n,g)||c.timescale!==l.timescale&&a)&&(c.initPTS=m-n,l&&1===l.timescale&&w.warn("Adjusting initPTS by "+(c.initPTS-l.baseTime)),this.initPTS=l={baseTime:c.initPTS,timescale:1});var p=t?m-l.baseTime/l.timescale:u,E=p+g;!function(t,e,r){xt(e,["moof","traf"]).forEach((function(e){xt(e,["tfhd"]).forEach((function(i){var n=It(i,4),a=t[n];if(a){var s=a.timescale||9e4;xt(e,["tfdt"]).forEach((function(t){var e=t[0],i=r*s;if(i){var n=It(t,4);if(0===e)n-=i,_t(t,4,n=Math.max(n,0));else{n*=Math.pow(2,32),n+=It(t,8),n-=i,n=Math.max(n,0);var a=Math.floor(n/(At+1)),o=Math.floor(n%(At+1));_t(t,4,a),_t(t,8,o)}}}))}}))}))}(f,d,l.baseTime/l.timescale),g>0?this.lastEndTime=E:(w.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());var T=!!f.audio,S=!!f.video,L="";T&&(L+="audio"),S&&(L+="video");var A={data1:d,startPTS:p,startDTS:p,endPTS:E,endDTS:E,type:L,hasAudio:T,hasVideo:S,nb:1,dropped:0};return h.audio="audio"===A.type?A:void 0,h.video="audio"!==A.type?A:void 0,h.initSegment=c,h.id3=Rn(r,n,l,l),i.samples.length&&(h.text=kn(i,n,l)),h},t}();function wn(t,e){var r=null==t?void 0:t.codec;if(r&&r.length>4)return r;if(e===O){if("ec-3"===r||"ac-3"===r||"alac"===r)return r;if("fLaC"===r||"Opus"===r)return he(r,!1);var i="mp4a.40.5";return w.info('Parsed audio codec "'+r+'" or audio object type not handled. Using "'+i+'"'),i}return w.warn('Unhandled video codec "'+r+'"'),"hvc1"===r||"hev1"===r?"hvc1.1.6.L120.90":"av01"===r?"av01.0.04M.08":"avc1.42e01e"}try{bn=self.performance.now.bind(self.performance)}catch(t){w.debug("Unable to use Performance API on this environment"),bn=null==j?void 0:j.Date.now}var Cn=[{demux:Ji,remux:In},{demux:on,remux:Ln},{demux:zi,remux:Ln},{demux:fn,remux:Ln}];Cn.splice(2,0,{demux:Zi,remux:Ln});var _n=function(){function t(t,e,r,i,n){this.async=!1,this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.observer=t,this.typeSupported=e,this.config=r,this.vendor=i,this.id=n}var e=t.prototype;return e.configure=function(t){this.transmuxConfig=t,this.decrypter&&this.decrypter.reset()},e.push=function(t,e,r,i){var n=this,a=r.transmuxing;a.executeStart=bn();var s=new Uint8Array(t),o=this.currentTransmuxState,l=this.transmuxConfig;i&&(this.currentTransmuxState=i);var u=i||o,h=u.contiguous,d=u.discontinuity,c=u.trackSwitch,f=u.accurateTimeOffset,g=u.timeOffset,v=u.initSegmentChange,m=l.audioCodec,p=l.videoCodec,y=l.defaultInitPts,E=l.duration,T=l.initSegmentData,R=function(t,e){var r=null;return t.byteLength>0&&null!=(null==e?void 0:e.key)&&null!==e.iv&&null!=e.method&&(r=e),r}(s,e);if(R&&"AES-128"===R.method){var k=this.getDecrypter();if(!k.isSync())return this.decryptionPromise=k.webCryptoDecrypt(s,R.key.buffer,R.iv.buffer).then((function(t){var e=n.push(t,null,r);return n.decryptionPromise=null,e})),this.decryptionPromise;var b=k.softwareDecrypt(s,R.key.buffer,R.iv.buffer);if(r.part>-1&&(b=k.flush()),!b)return a.executeEnd=bn(),xn(r);s=new Uint8Array(b)}var D=this.needsProbing(d,c);if(D){var I=this.configureTransmuxer(s);if(I)return w.warn("[transmuxer] "+I.message),this.observer.emit(S.ERROR,S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,fatal:!1,error:I,reason:I.message}),a.executeEnd=bn(),xn(r)}(d||c||v||D)&&this.resetInitSegment(T,m,p,E,e),(d||v||D)&&this.resetInitialTimestamp(y),h||this.resetContiguity();var C=this.transmux(s,R,g,f,r),_=this.currentTransmuxState;return _.contiguous=!0,_.discontinuity=!1,_.trackSwitch=!1,a.executeEnd=bn(),C},e.flush=function(t){var e=this,r=t.transmuxing;r.executeStart=bn();var i=this.decrypter,n=this.currentTransmuxState,a=this.decryptionPromise;if(a)return a.then((function(){return e.flush(t)}));var s=[],o=n.timeOffset;if(i){var l=i.flush();l&&s.push(this.push(l,null,t))}var u=this.demuxer,h=this.remuxer;if(!u||!h)return r.executeEnd=bn(),[xn(t)];var d=u.flush(o);return Pn(d)?d.then((function(r){return e.flushRemux(s,r,t),s})):(this.flushRemux(s,d,t),s)},e.flushRemux=function(t,e,r){var i=e.audioTrack,n=e.videoTrack,a=e.id3Track,s=e.textTrack,o=this.currentTransmuxState,l=o.accurateTimeOffset,u=o.timeOffset;w.log("[transmuxer.ts]: Flushed fragment "+r.sn+(r.part>-1?" p: "+r.part:"")+" of level "+r.level);var h=this.remuxer.remux(i,n,a,s,u,l,!0,this.id);t.push({remuxResult:h,chunkMeta:r}),r.transmuxing.executeEnd=bn()},e.resetInitialTimestamp=function(t){var e=this.demuxer,r=this.remuxer;e&&r&&(e.resetTimeStamp(t),r.resetTimeStamp(t))},e.resetContiguity=function(){var t=this.demuxer,e=this.remuxer;t&&e&&(t.resetContiguity(),e.resetNextTimestamp())},e.resetInitSegment=function(t,e,r,i,n){var a=this.demuxer,s=this.remuxer;a&&s&&(a.resetInitSegment(t,e,r,i),s.resetInitSegment(t,e,r,n))},e.destroy=function(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)},e.transmux=function(t,e,r,i,n){return e&&"SAMPLE-AES"===e.method?this.transmuxSampleAes(t,e,r,i,n):this.transmuxUnencrypted(t,r,i,n)},e.transmuxUnencrypted=function(t,e,r,i){var n=this.demuxer.demux(t,e,!1,!this.config.progressive),a=n.audioTrack,s=n.videoTrack,o=n.id3Track,l=n.textTrack;return{remuxResult:this.remuxer.remux(a,s,o,l,e,r,!1,this.id),chunkMeta:i}},e.transmuxSampleAes=function(t,e,r,i,n){var a=this;return this.demuxer.demuxSampleAes(t,e,r).then((function(t){return{remuxResult:a.remuxer.remux(t.audioTrack,t.videoTrack,t.id3Track,t.textTrack,r,i,!1,a.id),chunkMeta:n}}))},e.configureTransmuxer=function(t){for(var e,r=this.config,i=this.observer,n=this.typeSupported,a=this.vendor,s=0,o=Cn.length;s1&&l.id===(null==m?void 0:m.stats.chunkCount),L=!y&&(1===E||0===E&&(1===T||S&&T<=0)),A=self.performance.now();(y||E||0===n.stats.parsing.start)&&(n.stats.parsing.start=A),!a||!T&&L||(a.stats.parsing.start=A);var R=!(m&&(null==(h=n.initSegment)?void 0:h.url)===(null==(d=m.initSegment)?void 0:d.url)),k=new Mn(p,L,o,y,g,R);if(!L||p||R){w.log("[transmuxer-interface, "+n.type+"]: Starting new transmux session for sn: "+l.sn+" p: "+l.part+" level: "+l.level+" id: "+l.id+"\n discontinuity: "+p+"\n trackSwitch: "+y+"\n contiguous: "+L+"\n accurateTimeOffset: "+o+"\n timeOffset: "+g+"\n initSegmentChange: "+R);var b=new Fn(r,i,e,s,u);this.configureTransmuxer(b)}if(this.frag=n,this.part=a,this.workerContext)this.workerContext.worker.postMessage({cmd:"demux",data:t,decryptdata:v,chunkMeta:l,state:k},t instanceof ArrayBuffer?[t]:[]);else if(f){var D=f.push(t,v,l,k);Pn(D)?(f.async=!0,D.then((function(t){c.handleTransmuxComplete(t)})).catch((function(t){c.transmuxerError(t,l,"transmuxer-interface push error")}))):(f.async=!1,this.handleTransmuxComplete(D))}},r.flush=function(t){var e=this;t.transmuxing.start=self.performance.now();var r=this.transmuxer;if(this.workerContext)this.workerContext.worker.postMessage({cmd:"flush",chunkMeta:t});else if(r){var i=r.flush(t);Pn(i)||r.async?(Pn(i)||(i=Promise.resolve(i)),i.then((function(r){e.handleFlushResult(r,t)})).catch((function(r){e.transmuxerError(r,t,"transmuxer-interface flush error")}))):this.handleFlushResult(i,t)}},r.transmuxerError=function(t,e,r){this.hls&&(this.error=t,this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_PARSING_ERROR,chunkMeta:e,fatal:!1,error:t,err:t,reason:r}))},r.handleFlushResult=function(t,e){var r=this;t.forEach((function(t){r.handleTransmuxComplete(t)})),this.onFlush(e)},r.onWorkerMessage=function(t){var e=t.data,r=this.hls;switch(e.event){case"init":var i,n=null==(i=this.workerContext)?void 0:i.objectURL;n&&self.URL.revokeObjectURL(n);break;case"transmuxComplete":this.handleTransmuxComplete(e.data);break;case"flush":this.onFlush(e.data);break;case"workerLog":w[e.data.logType]&&w[e.data.logType](e.data.message);break;default:e.data=e.data||{},e.data.frag=this.frag,e.data.id=this.id,r.trigger(e.event,e.data)}},r.configureTransmuxer=function(t){var e=this.transmuxer;this.workerContext?this.workerContext.worker.postMessage({cmd:"configure",config:t}):e&&e.configure(t)},r.handleTransmuxComplete=function(t){t.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(t)},e}();function Hn(t,e){if(t.length!==e.length)return!1;for(var r=0;r0&&-1===t?(this.log("Override startPosition with lastCurrentTime @"+e.toFixed(3)),t=e,this.state=vi):(this.loadedmetadata=!1,this.state=Ei),this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()},r.doTick=function(){switch(this.state){case vi:this.doTickIdle();break;case Ei:var e,r=this.levels,i=this.trackId,n=null==r||null==(e=r[i])?void 0:e.details;if(n){if(this.waitForCdnTuneIn(n))break;this.state=Ri}break;case yi:var a,s=performance.now(),o=this.retryDate;if(!o||s>=o||null!=(a=this.media)&&a.seeking){var l=this.levels,u=this.trackId;this.log("RetryDate reached, switch back to IDLE state"),this.resetStartWhenNotLoaded((null==l?void 0:l[u])||null),this.state=vi}break;case Ri:var h=this.waitingData;if(h){var d=h.frag,c=h.part,f=h.cache,g=h.complete;if(void 0!==this.initPTS[d.cc]){this.waitingData=null,this.waitingVideoCC=-1,this.state=pi;var v={frag:d,part:c,payload:f.flush(),networkDetails:null};this._handleFragmentLoadProgress(v),g&&t.prototype._handleFragmentLoadComplete.call(this,v)}else if(this.videoTrackCC!==this.waitingVideoCC)this.log("Waiting fragment cc ("+d.cc+") cancelled because video is at cc "+this.videoTrackCC),this.clearWaitingFragment();else{var m=this.getLoadPosition(),p=Jr.bufferInfo(this.mediaBuffer,m,this.config.maxBufferHole);Er(p.end,this.config.maxFragLookUpTolerance,d)<0&&(this.log("Waiting fragment cc ("+d.cc+") @ "+d.start+" cancelled because another fragment at "+p.end+" is needed"),this.clearWaitingFragment())}}else this.state=vi}this.onTickEnd()},r.clearWaitingFragment=function(){var t=this.waitingData;t&&(this.fragmentTracker.removeFragment(t.frag),this.waitingData=null,this.waitingVideoCC=-1,this.state=vi)},r.resetLoadingState=function(){this.clearWaitingFragment(),t.prototype.resetLoadingState.call(this)},r.onTickEnd=function(){var t=this.media;null!=t&&t.readyState&&(this.lastCurrentTime=t.currentTime)},r.doTickIdle=function(){var t=this.hls,e=this.levels,r=this.media,i=this.trackId,n=t.config;if((r||!this.startFragRequested&&n.startFragPrefetch)&&null!=e&&e[i]){var a=e[i],s=a.details;if(!s||s.live&&this.levelLastLoaded!==a||this.waitForCdnTuneIn(s))this.state=Ei;else{var o=this.mediaBuffer?this.mediaBuffer:this.media;this.bufferFlushed&&o&&(this.bufferFlushed=!1,this.afterBufferFlushed(o,O,Ce));var l=this.getFwdBufferInfo(o,Ce);if(null!==l){var u=this.bufferedTrack,h=this.switchingTrack;if(!h&&this._streamEnded(l,s))return t.trigger(S.BUFFER_EOS,{type:"audio"}),void(this.state=Li);var d=this.getFwdBufferInfo(this.videoBuffer?this.videoBuffer:this.media,we),c=l.len,f=this.getMaxBufferLength(null==d?void 0:d.len),g=s.fragments,v=g[0].start,m=this.flushing?this.getLoadPosition():l.end;if(h&&r){var p=this.getLoadPosition();u&&!Vn(h.attrs,u.attrs)&&(m=p),s.PTSKnown&&pv||l.nextStart)&&(this.log("Alt audio track ahead of main track, seek to start of alt audio track"),r.currentTime=v+.05)}if(!(c>=f&&!h&&md.end+s.targetduration;if(T||(null==d||!d.len)&&l.len){var L=this.getAppendedFrag(y.start,we);if(null===L)return;if(E||(E=!!L.gap||!!T&&0===d.len),T&&!E||E&&l.nextStart&&l.nextStart-1)n=a[o];else{var l=Nr(s,this.tracks);n=this.tracks[l]}}var u=this.findTrackId(n);-1===u&&n&&(u=this.findTrackId(null));var h={audioTracks:a};this.log("Updating audio tracks, "+a.length+" track(s) found in group(s): "+(null==r?void 0:r.join(","))),this.hls.trigger(S.AUDIO_TRACKS_UPDATED,h);var d=this.trackId;if(-1!==u&&-1===d)this.setAudioTrack(u);else if(a.length&&-1===d){var c,f=new Error("No audio track selected for current audio group-ID(s): "+(null==(c=this.groupIds)?void 0:c.join(","))+" track count: "+a.length);this.warn(f.message),this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.AUDIO_TRACK_LOAD_ERROR,fatal:!0,error:f})}}else this.shouldReloadPlaylist(n)&&this.setAudioTrack(this.trackId)}},r.onError=function(t,e){!e.fatal&&e.context&&(e.context.type!==De||e.context.id!==this.trackId||this.groupIds&&-1===this.groupIds.indexOf(e.context.groupId)||(this.requestScheduled=-1,this.checkRetry(e)))},r.setAudioOption=function(t){var e=this.hls;if(e.config.audioPreference=t,t){var r=this.allAudioTracks;if(this.selectDefaultTrack=!1,r.length){var i=this.currentTrack;if(i&&Ur(t,i,Br))return i;var n=Nr(t,this.tracksInGroup,Br);if(n>-1){var a=this.tracksInGroup[n];return this.setAudioTrack(n),a}if(i){var s=e.loadLevel;-1===s&&(s=e.firstAutoLevel);var o=function(t,e,r,i,n){var a=e[i],s=e.reduce((function(t,e,r){var i=e.uri;return(t[i]||(t[i]=[])).push(r),t}),{})[a.uri];s.length>1&&(i=Math.max.apply(Math,s));var o=a.videoRange,l=a.frameRate,u=a.codecSet.substring(0,4),h=Gr(e,i,(function(e){if(e.videoRange!==o||e.frameRate!==l||e.codecSet.substring(0,4)!==u)return!1;var i=e.audioGroups,a=r.filter((function(t){return!i||-1!==i.indexOf(t.groupId)}));return Nr(t,a,n)>-1}));return h>-1?h:Gr(e,i,(function(e){var i=e.audioGroups,a=r.filter((function(t){return!i||-1!==i.indexOf(t.groupId)}));return Nr(t,a,n)>-1}))}(t,e.levels,r,s,Br);if(-1===o)return null;e.nextLoadLevel=o}if(t.channels||t.audioCodec){var l=Nr(t,r);if(l>-1)return r[l]}}}return null},r.setAudioTrack=function(t){var e=this.tracksInGroup;if(t<0||t>=e.length)this.warn("Invalid audio track id: "+t);else{this.clearTimer(),this.selectDefaultTrack=!1;var r=this.currentTrack,n=e[t],a=n.details&&!n.details.live;if(!(t===this.trackId&&n===r&&a||(this.log("Switching to audio-track "+t+' "'+n.name+'" lang:'+n.lang+" group:"+n.groupId+" channels:"+n.channels),this.trackId=t,this.currentTrack=n,this.hls.trigger(S.AUDIO_TRACK_SWITCHING,i({},n)),a))){var s=this.switchParams(n.url,null==r?void 0:r.details,n.details);this.loadPlaylist(s)}}},r.findTrackId=function(t){for(var e=this.tracksInGroup,r=0;r=n[o].start&&s<=n[o].end){a=n[o];break}var l=r.start+r.duration;a?a.end=l:(a={start:s,end:l},n.push(a)),this.fragmentTracker.fragBuffered(r),this.fragBufferedComplete(r,null)}}},r.onBufferFlushing=function(t,e){var r=e.startOffset,i=e.endOffset;if(0===r&&i!==Number.POSITIVE_INFINITY){var n=i-1;if(n<=0)return;e.endOffsetSubtitles=Math.max(0,n),this.tracksBuffered.forEach((function(t){for(var e=0;e=n.length||s!==i)&&o){this.log("Subtitle track "+s+" loaded ["+a.startSN+","+a.endSN+"]"+(a.lastPartSn?"[part-"+a.lastPartSn+"-"+a.lastPartIndex+"]":"")+",duration:"+a.totalduration),this.mediaBuffer=this.mediaBufferTimeRanges;var l=0;if(a.live||null!=(r=o.details)&&r.live){var u=this.mainDetails;if(a.deltaUpdateFailed||!u)return;var h,d=u.fragments[0];o.details?0===(l=this.alignPlaylists(a,o.details,null==(h=this.levelLastLoaded)?void 0:h.details))&&d&&lr(a,l=d.start):a.hasProgramDateTime&&u.hasProgramDateTime?(ii(a,u),l=a.fragments[0].start):d&&lr(a,l=d.start)}o.details=a,this.levelLastLoaded=o,this.startFragRequested||!this.mainDetails&&a.live||this.setStartPosition(this.mainDetails||a,l),this.tick(),a.live&&!this.fragCurrent&&this.media&&this.state===vi&&(yr(null,a.fragments,this.media.currentTime,0)||(this.warn("Subtitle playlist not aligned with playback"),o.details=void 0))}}else this.warn("Subtitle tracks were reset while loading level "+s)},r._handleFragmentLoadComplete=function(t){var e=this,r=t.frag,i=t.payload,n=r.decryptdata,a=this.hls;if(!this.fragContextChanged(r)&&i&&i.byteLength>0&&null!=n&&n.key&&n.iv&&"AES-128"===n.method){var s=performance.now();this.decrypter.decrypt(new Uint8Array(i),n.key.buffer,n.iv.buffer).catch((function(t){throw a.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.FRAG_DECRYPT_ERROR,fatal:!1,error:t,reason:t.message,frag:r}),t})).then((function(t){var e=performance.now();a.trigger(S.FRAG_DECRYPTED,{frag:r,payload:t,stats:{tstart:s,tdecrypt:e}})})).catch((function(t){e.warn(t.name+": "+t.message),e.state=vi}))}},r.doTick=function(){if(this.media){if(this.state===vi){var t=this.currentTrackId,e=this.levels,r=null==e?void 0:e[t];if(!r||!e.length||!r.details)return;var i=this.config,n=this.getLoadPosition(),a=Jr.bufferedInfo(this.tracksBuffered[this.currentTrackId]||[],n,i.maxBufferHole),s=a.end,o=a.len,l=this.getFwdBufferInfo(this.media,we),u=r.details;if(o>this.getMaxBufferLength(null==l?void 0:l.len)+u.levelTargetDuration)return;var h=u.fragments,d=h.length,c=u.edge,f=null,g=this.fragPrevious;if(sc-v?0:v;!(f=yr(g,h,Math.max(h[0].start,s),m))&&g&&g.start>>=0)>i-1)throw new DOMException("Failed to execute '"+e+"' on 'TimeRanges': The index provided ("+r+") is greater than the maximum bound ("+i+")");return t[r][e]};this.buffered={get length(){return t.length},end:function(r){return e("end",r,t.length)},start:function(r){return e("start",r,t.length)}}},zn=function(t){function e(e){var r;return(r=t.call(this,e,"[subtitle-track-controller]")||this).media=null,r.tracks=[],r.groupIds=null,r.tracksInGroup=[],r.trackId=-1,r.currentTrack=null,r.selectDefaultTrack=!0,r.queuedDefaultTrack=-1,r.asyncPollTrackChange=function(){return r.pollTrackChange(0)},r.useTextTrackPolling=!1,r.subtitlePollingInterval=-1,r._subtitleDisplay=!0,r.onTextTracksChanged=function(){if(r.useTextTrackPolling||self.clearInterval(r.subtitlePollingInterval),r.media&&r.hls.config.renderTextTracksNatively){for(var t=null,e=Be(r.media.textTracks),i=0;i-1&&(this.subtitleTrack=this.queuedDefaultTrack,this.queuedDefaultTrack=-1),this.useTextTrackPolling=!(this.media.textTracks&&"onchange"in this.media.textTracks),this.useTextTrackPolling?this.pollTrackChange(500):this.media.textTracks.addEventListener("change",this.asyncPollTrackChange))},r.pollTrackChange=function(t){self.clearInterval(this.subtitlePollingInterval),this.subtitlePollingInterval=self.setInterval(this.onTextTracksChanged,t)},r.onMediaDetaching=function(){this.media&&(self.clearInterval(this.subtitlePollingInterval),this.useTextTrackPolling||this.media.textTracks.removeEventListener("change",this.asyncPollTrackChange),this.trackId>-1&&(this.queuedDefaultTrack=this.trackId),Be(this.media.textTracks).forEach((function(t){Ne(t)})),this.subtitleTrack=-1,this.media=null)},r.onManifestLoading=function(){this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0},r.onManifestParsed=function(t,e){this.tracks=e.subtitleTracks},r.onSubtitleTrackLoaded=function(t,e){var r=e.id,i=e.groupId,n=e.details,a=this.tracksInGroup[r];if(a&&a.groupId===i){var s=a.details;a.details=e.details,this.log("Subtitle track "+r+' "'+a.name+'" lang:'+a.lang+" group:"+i+" loaded ["+n.startSN+"-"+n.endSN+"]"),r===this.trackId&&this.playlistLoaded(r,e,s)}else this.warn("Subtitle track with id:"+r+" and group:"+i+" not found in active group "+(null==a?void 0:a.groupId))},r.onLevelLoading=function(t,e){this.switchLevel(e.level)},r.onLevelSwitching=function(t,e){this.switchLevel(e.level)},r.switchLevel=function(t){var e=this.hls.levels[t];if(e){var r=e.subtitleGroups||null,i=this.groupIds,n=this.currentTrack;if(!r||(null==i?void 0:i.length)!==(null==r?void 0:r.length)||null!=r&&r.some((function(t){return-1===(null==i?void 0:i.indexOf(t))}))){this.groupIds=r,this.trackId=-1,this.currentTrack=null;var a=this.tracks.filter((function(t){return!r||-1!==r.indexOf(t.groupId)}));if(a.length)this.selectDefaultTrack&&!a.some((function(t){return t.default}))&&(this.selectDefaultTrack=!1),a.forEach((function(t,e){t.id=e}));else if(!n&&!this.tracksInGroup.length)return;this.tracksInGroup=a;var s=this.hls.config.subtitlePreference;if(!n&&s){this.selectDefaultTrack=!1;var o=Nr(s,a);if(o>-1)n=a[o];else{var l=Nr(s,this.tracks);n=this.tracks[l]}}var u=this.findTrackId(n);-1===u&&n&&(u=this.findTrackId(null));var h={subtitleTracks:a};this.log("Updating subtitle tracks, "+a.length+' track(s) found in "'+(null==r?void 0:r.join(","))+'" group-id'),this.hls.trigger(S.SUBTITLE_TRACKS_UPDATED,h),-1!==u&&-1===this.trackId&&this.setSubtitleTrack(u)}else this.shouldReloadPlaylist(n)&&this.setSubtitleTrack(this.trackId)}},r.findTrackId=function(t){for(var e=this.tracksInGroup,r=this.selectDefaultTrack,i=0;i-1){var n=this.tracksInGroup[i];return this.setSubtitleTrack(i),n}if(r)return null;var a=Nr(t,e);if(a>-1)return e[a]}}return null},r.loadPlaylist=function(e){t.prototype.loadPlaylist.call(this);var r=this.currentTrack;if(this.shouldLoadPlaylist(r)&&r){var i=r.id,n=r.groupId,a=r.url;if(e)try{a=e.addDirectives(a)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}this.log("Loading subtitle playlist for id "+i),this.hls.trigger(S.SUBTITLE_TRACK_LOADING,{url:a,id:i,groupId:n,deliveryDirectives:e||null})}},r.toggleTrackModes=function(){var t=this.media;if(t){var e,r=Be(t.textTracks),i=this.currentTrack;if(i&&((e=r.filter((function(t){return Yn(i,t)}))[0])||this.warn('Unable to find subtitle TextTrack with name "'+i.name+'" and language "'+i.lang+'"')),[].slice.call(r).forEach((function(t){"disabled"!==t.mode&&t!==e&&(t.mode="disabled")})),e){var n=this.subtitleDisplay?"showing":"hidden";e.mode!==n&&(e.mode=n)}}},r.setSubtitleTrack=function(t){var e=this.tracksInGroup;if(this.media)if(t<-1||t>=e.length||!y(t))this.warn("Invalid subtitle track id: "+t);else{this.clearTimer(),this.selectDefaultTrack=!1;var r=this.currentTrack,i=e[t]||null;if(this.trackId=t,this.currentTrack=i,this.toggleTrackModes(),i){var n=!!i.details&&!i.details.live;if(t!==this.trackId||i!==r||!n){this.log("Switching to subtitle-track "+t+(i?' "'+i.name+'" lang:'+i.lang+" group:"+i.groupId:""));var a=i.id,s=i.groupId,o=void 0===s?"":s,l=i.name,u=i.type,h=i.url;this.hls.trigger(S.SUBTITLE_TRACK_SWITCH,{id:a,groupId:o,name:l,type:u,url:h});var d=this.switchParams(i.url,null==r?void 0:r.details,i.details);this.loadPlaylist(d)}}else this.hls.trigger(S.SUBTITLE_TRACK_SWITCH,{id:t})}else this.queuedDefaultTrack=t},s(e,[{key:"subtitleDisplay",get:function(){return this._subtitleDisplay},set:function(t){this._subtitleDisplay=t,this.trackId>-1&&this.toggleTrackModes()}},{key:"allSubtitleTracks",get:function(){return this.tracks}},{key:"subtitleTracks",get:function(){return this.tracksInGroup}},{key:"subtitleTrack",get:function(){return this.trackId},set:function(t){this.selectDefaultTrack=!1,this.setSubtitleTrack(t)}}]),e}(wr),Qn=function(){function t(t){this.buffers=void 0,this.queues={video:[],audio:[],audiovideo:[]},this.buffers=t}var e=t.prototype;return e.append=function(t,e,r){var i=this.queues[e];i.push(t),1!==i.length||r||this.executeNext(e)},e.insertAbort=function(t,e){this.queues[e].unshift(t),this.executeNext(e)},e.appendBlocker=function(t){var e,r=new Promise((function(t){e=t})),i={execute:e,onStart:function(){},onComplete:function(){},onError:function(){}};return this.append(i,t),r},e.executeNext=function(t){var e=this.queues[t];if(e.length){var r=e[0];try{r.execute()}catch(e){w.warn('[buffer-operation-queue]: Exception executing "'+t+'" SourceBuffer operation: '+e),r.onError(e);var i=this.buffers[t];null!=i&&i.updating||this.shiftAndExecuteNext(t)}}},e.shiftAndExecuteNext=function(t){this.queues[t].shift(),this.executeNext(t)},e.current=function(t){return this.queues[t][0]},t}(),Jn=/(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/,$n=function(){function t(t){var e=this;this.details=null,this._objectUrl=null,this.operationQueue=void 0,this.listeners=void 0,this.hls=void 0,this.bufferCodecEventsExpected=0,this._bufferCodecEventsTotal=0,this.media=null,this.mediaSource=null,this.lastMpegAudioChunk=null,this.appendSource=void 0,this.appendErrors={audio:0,video:0,audiovideo:0},this.tracks={},this.pendingTracks={},this.sourceBuffer=void 0,this.log=void 0,this.warn=void 0,this.error=void 0,this._onEndStreaming=function(t){e.hls&&e.hls.pauseBuffering()},this._onStartStreaming=function(t){e.hls&&e.hls.resumeBuffering()},this._onMediaSourceOpen=function(){var t=e.media,r=e.mediaSource;e.log("Media source opened"),t&&(t.removeEventListener("emptied",e._onMediaEmptied),e.updateMediaElementDuration(),e.hls.trigger(S.MEDIA_ATTACHED,{media:t,mediaSource:r})),r&&r.removeEventListener("sourceopen",e._onMediaSourceOpen),e.checkPendingTracks()},this._onMediaSourceClose=function(){e.log("Media source closed")},this._onMediaSourceEnded=function(){e.log("Media source ended")},this._onMediaEmptied=function(){var t=e.mediaSrc,r=e._objectUrl;t!==r&&w.error("Media element src was set while attaching MediaSource ("+r+" > "+t+")")},this.hls=t;var r,i="[buffer-controller]";this.appendSource=(r=ee(t.config.preferManagedMediaSource),"undefined"!=typeof self&&r===self.ManagedMediaSource),this.log=w.log.bind(w,i),this.warn=w.warn.bind(w,i),this.error=w.error.bind(w,i),this._initSourceBuffer(),this.registerListeners()}var e=t.prototype;return e.hasSourceTypes=function(){return this.getSourceBufferTypes().length>0||Object.keys(this.pendingTracks).length>0},e.destroy=function(){this.unregisterListeners(),this.details=null,this.lastMpegAudioChunk=null,this.hls=null},e.registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.BUFFER_RESET,this.onBufferReset,this),t.on(S.BUFFER_APPENDING,this.onBufferAppending,this),t.on(S.BUFFER_CODECS,this.onBufferCodecs,this),t.on(S.BUFFER_EOS,this.onBufferEos,this),t.on(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(S.LEVEL_UPDATED,this.onLevelUpdated,this),t.on(S.FRAG_PARSED,this.onFragParsed,this),t.on(S.FRAG_CHANGED,this.onFragChanged,this)},e.unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.BUFFER_RESET,this.onBufferReset,this),t.off(S.BUFFER_APPENDING,this.onBufferAppending,this),t.off(S.BUFFER_CODECS,this.onBufferCodecs,this),t.off(S.BUFFER_EOS,this.onBufferEos,this),t.off(S.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(S.LEVEL_UPDATED,this.onLevelUpdated,this),t.off(S.FRAG_PARSED,this.onFragParsed,this),t.off(S.FRAG_CHANGED,this.onFragChanged,this)},e._initSourceBuffer=function(){this.sourceBuffer={},this.operationQueue=new Qn(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]},this.appendErrors={audio:0,video:0,audiovideo:0},this.lastMpegAudioChunk=null},e.onManifestLoading=function(){this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=0,this.details=null},e.onManifestParsed=function(t,e){var r=2;(e.audio&&!e.video||!e.altAudio)&&(r=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=r,this.log(this.bufferCodecEventsExpected+" bufferCodec event(s) expected")},e.onMediaAttaching=function(t,e){var r=this.media=e.media,i=ee(this.appendSource);if(r&&i){var n,a=this.mediaSource=new i;this.log("created media source: "+(null==(n=a.constructor)?void 0:n.name)),a.addEventListener("sourceopen",this._onMediaSourceOpen),a.addEventListener("sourceended",this._onMediaSourceEnded),a.addEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(a.addEventListener("startstreaming",this._onStartStreaming),a.addEventListener("endstreaming",this._onEndStreaming));var s=this._objectUrl=self.URL.createObjectURL(a);if(this.appendSource)try{r.removeAttribute("src");var o=self.ManagedMediaSource;r.disableRemotePlayback=r.disableRemotePlayback||o&&a instanceof o,Zn(r),function(t,e){var r=self.document.createElement("source");r.type="video/mp4",r.src=e,t.appendChild(r)}(r,s),r.load()}catch(t){r.src=s}else r.src=s;r.addEventListener("emptied",this._onMediaEmptied)}},e.onMediaDetaching=function(){var t=this.media,e=this.mediaSource,r=this._objectUrl;if(e){if(this.log("media source detaching"),"open"===e.readyState)try{e.endOfStream()}catch(t){this.warn("onMediaDetaching: "+t.message+" while calling endOfStream")}this.onBufferReset(),e.removeEventListener("sourceopen",this._onMediaSourceOpen),e.removeEventListener("sourceended",this._onMediaSourceEnded),e.removeEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(e.removeEventListener("startstreaming",this._onStartStreaming),e.removeEventListener("endstreaming",this._onEndStreaming)),t&&(t.removeEventListener("emptied",this._onMediaEmptied),r&&self.URL.revokeObjectURL(r),this.mediaSrc===r?(t.removeAttribute("src"),this.appendSource&&Zn(t),t.load()):this.warn("media|source.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(S.MEDIA_DETACHED,void 0)},e.onBufferReset=function(){var t=this;this.getSourceBufferTypes().forEach((function(e){t.resetBuffer(e)})),this._initSourceBuffer()},e.resetBuffer=function(t){var e=this.sourceBuffer[t];try{var r;e&&(this.removeBufferListeners(t),this.sourceBuffer[t]=void 0,null!=(r=this.mediaSource)&&r.sourceBuffers.length&&this.mediaSource.removeSourceBuffer(e))}catch(e){this.warn("onBufferReset "+t,e)}},e.onBufferCodecs=function(t,e){var r=this,i=this.getSourceBufferTypes().length,n=Object.keys(e);if(n.forEach((function(t){if(i){var n=r.tracks[t];if(n&&"function"==typeof n.buffer.changeType){var a,s=e[t],o=s.id,l=s.codec,u=s.levelCodec,h=s.container,d=s.metadata,c=de(n.codec,n.levelCodec),f=null==c?void 0:c.replace(Jn,"$1"),g=de(l,u),v=null==(a=g)?void 0:a.replace(Jn,"$1");if(g&&f!==v){"audio"===t.slice(0,5)&&(g=he(g,r.appendSource));var m=h+";codecs="+g;r.appendChangeType(t,m),r.log("switching codec "+c+" to "+g),r.tracks[t]={buffer:n.buffer,codec:l,container:h,levelCodec:u,metadata:d,id:o}}}}else r.pendingTracks[t]=e[t]})),!i){var a=Math.max(this.bufferCodecEventsExpected-1,0);this.bufferCodecEventsExpected!==a&&(this.log(a+" bufferCodec event(s) expected "+n.join(",")),this.bufferCodecEventsExpected=a),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks()}},e.appendChangeType=function(t,e){var r=this,i=this.operationQueue,n={execute:function(){var n=r.sourceBuffer[t];n&&(r.log("changing "+t+" sourceBuffer type to "+e),n.changeType(e)),i.shiftAndExecuteNext(t)},onStart:function(){},onComplete:function(){},onError:function(e){r.warn("Failed to change "+t+" SourceBuffer type",e)}};i.append(n,t,!!this.pendingTracks[t])},e.onBufferAppending=function(t,e){var r=this,i=this.hls,n=this.operationQueue,a=this.tracks,s=e.data,o=e.type,l=e.frag,u=e.part,h=e.chunkMeta,d=h.buffering[o],c=self.performance.now();d.start=c;var f=l.stats.buffering,g=u?u.stats.buffering:null;0===f.start&&(f.start=c),g&&0===g.start&&(g.start=c);var v=a.audio,m=!1;"audio"===o&&"audio/mpeg"===(null==v?void 0:v.container)&&(m=!this.lastMpegAudioChunk||1===h.id||this.lastMpegAudioChunk.sn!==h.sn,this.lastMpegAudioChunk=h);var p=l.start,y={execute:function(){if(d.executeStart=self.performance.now(),m){var t=r.sourceBuffer[o];if(t){var e=p-t.timestampOffset;Math.abs(e)>=.1&&(r.log("Updating audio SourceBuffer timestampOffset to "+p+" (delta: "+e+") sn: "+l.sn+")"),t.timestampOffset=p)}}r.appendExecutor(s,o)},onStart:function(){},onComplete:function(){var t=self.performance.now();d.executeEnd=d.end=t,0===f.first&&(f.first=t),g&&0===g.first&&(g.first=t);var e=r.sourceBuffer,i={};for(var n in e)i[n]=Jr.getBuffered(e[n]);r.appendErrors[o]=0,"audio"===o||"video"===o?r.appendErrors.audiovideo=0:(r.appendErrors.audio=0,r.appendErrors.video=0),r.hls.trigger(S.BUFFER_APPENDED,{type:o,frag:l,part:u,chunkMeta:h,parent:l.type,timeRanges:i})},onError:function(t){var e={type:L.MEDIA_ERROR,parent:l.type,details:A.BUFFER_APPEND_ERROR,sourceBufferName:o,frag:l,part:u,chunkMeta:h,error:t,err:t,fatal:!1};if(t.code===DOMException.QUOTA_EXCEEDED_ERR)e.details=A.BUFFER_FULL_ERROR;else{var n=++r.appendErrors[o];e.details=A.BUFFER_APPEND_ERROR,r.warn("Failed "+n+"/"+i.config.appendErrorMaxRetry+' times to append segment in "'+o+'" sourceBuffer'),n>=i.config.appendErrorMaxRetry&&(e.fatal=!0)}i.trigger(S.ERROR,e)}};n.append(y,o,!!this.pendingTracks[o])},e.onBufferFlushing=function(t,e){var r=this,i=this.operationQueue,n=function(t){return{execute:r.removeExecutor.bind(r,t,e.startOffset,e.endOffset),onStart:function(){},onComplete:function(){r.hls.trigger(S.BUFFER_FLUSHED,{type:t})},onError:function(e){r.warn("Failed to remove from "+t+" SourceBuffer",e)}}};e.type?i.append(n(e.type),e.type):this.getSourceBufferTypes().forEach((function(t){i.append(n(t),t)}))},e.onFragParsed=function(t,e){var r=this,i=e.frag,n=e.part,a=[],s=n?n.elementaryStreams:i.elementaryStreams;s[U]?a.push("audiovideo"):(s[O]&&a.push("audio"),s[N]&&a.push("video")),0===a.length&&this.warn("Fragments must have at least one ElementaryStreamType set. type: "+i.type+" level: "+i.level+" sn: "+i.sn),this.blockBuffers((function(){var t=self.performance.now();i.stats.buffering.end=t,n&&(n.stats.buffering.end=t);var e=n?n.stats:i.stats;r.hls.trigger(S.FRAG_BUFFERED,{frag:i,part:n,stats:e,id:i.type})}),a)},e.onFragChanged=function(t,e){this.trimBuffers()},e.onBufferEos=function(t,e){var r=this;this.getSourceBufferTypes().reduce((function(t,i){var n=r.sourceBuffer[i];return!n||e.type&&e.type!==i||(n.ending=!0,n.ended||(n.ended=!0,r.log(i+" sourceBuffer now EOS"))),t&&!(n&&!n.ended)}),!0)&&(this.log("Queueing mediaSource.endOfStream()"),this.blockBuffers((function(){r.getSourceBufferTypes().forEach((function(t){var e=r.sourceBuffer[t];e&&(e.ending=!1)}));var t=r.mediaSource;t&&"open"===t.readyState?(r.log("Calling mediaSource.endOfStream()"),t.endOfStream()):t&&r.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: "+t.readyState)})))},e.onLevelUpdated=function(t,e){var r=e.details;r.fragments.length&&(this.details=r,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())},e.trimBuffers=function(){var t=this.hls,e=this.details,r=this.media;if(r&&null!==e&&this.getSourceBufferTypes().length){var i=t.config,n=r.currentTime,a=e.levelTargetDuration,s=e.live&&null!==i.liveBackBufferLength?i.liveBackBufferLength:i.backBufferLength;if(y(s)&&s>0){var o=Math.max(s,a),l=Math.floor(n/a)*a-o;this.flushBackBuffer(n,a,l)}if(y(i.frontBufferFlushThreshold)&&i.frontBufferFlushThreshold>0){var u=Math.max(i.maxBufferLength,i.frontBufferFlushThreshold),h=Math.max(u,a),d=Math.floor(n/a)*a+h;this.flushFrontBuffer(n,a,d)}}},e.flushBackBuffer=function(t,e,r){var i=this,n=this.details,a=this.sourceBuffer;this.getSourceBufferTypes().forEach((function(s){var o=a[s];if(o){var l=Jr.getBuffered(o);if(l.length>0&&r>l.start(0)){if(i.hls.trigger(S.BACK_BUFFER_REACHED,{bufferEnd:r}),null!=n&&n.live)i.hls.trigger(S.LIVE_BACK_BUFFER_REACHED,{bufferEnd:r});else if(o.ended&&l.end(l.length-1)-t<2*e)return void i.log("Cannot flush "+s+" back buffer while SourceBuffer is in ended state");i.hls.trigger(S.BUFFER_FLUSHING,{startOffset:0,endOffset:r,type:s})}}}))},e.flushFrontBuffer=function(t,e,r){var i=this,n=this.sourceBuffer;this.getSourceBufferTypes().forEach((function(a){var s=n[a];if(s){var o=Jr.getBuffered(s),l=o.length;if(l<2)return;var u=o.start(l-1),h=o.end(l-1);if(r>u||t>=u&&t<=h)return;if(s.ended&&t-h<2*e)return void i.log("Cannot flush "+a+" front buffer while SourceBuffer is in ended state");i.hls.trigger(S.BUFFER_FLUSHING,{startOffset:u,endOffset:1/0,type:a})}}))},e.updateMediaElementDuration=function(){if(this.details&&this.media&&this.mediaSource&&"open"===this.mediaSource.readyState){var t=this.details,e=this.hls,r=this.media,i=this.mediaSource,n=t.fragments[0].start+t.totalduration,a=r.duration,s=y(i.duration)?i.duration:0;t.live&&e.config.liveDurationInfinity?(i.duration=1/0,this.updateSeekableRange(t)):(n>s&&n>a||!y(a))&&(this.log("Updating Media Source duration to "+n.toFixed(3)),i.duration=n)}},e.updateSeekableRange=function(t){var e=this.mediaSource,r=t.fragments;if(r.length&&t.live&&null!=e&&e.setLiveSeekableRange){var i=Math.max(0,r[0].start),n=Math.max(i,i+t.totalduration);this.log("Media Source duration is set to "+e.duration+". Setting seekable range to "+i+"-"+n+"."),e.setLiveSeekableRange(i,n)}},e.checkPendingTracks=function(){var t=this.bufferCodecEventsExpected,e=this.operationQueue,r=this.pendingTracks,i=Object.keys(r).length;if(i&&(!t||2===i||"audiovideo"in r)){this.createSourceBuffers(r),this.pendingTracks={};var n=this.getSourceBufferTypes();if(n.length)this.hls.trigger(S.BUFFER_CREATED,{tracks:this.tracks}),n.forEach((function(t){e.executeNext(t)}));else{var a=new Error("could not create source buffer for media codec(s)");this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,error:a,reason:a.message})}}},e.createSourceBuffers=function(t){var e=this,r=this.sourceBuffer,i=this.mediaSource;if(!i)throw Error("createSourceBuffers called when mediaSource was null");var n=function(n){if(!r[n]){var a,s=t[n];if(!s)throw Error("source buffer exists for track "+n+", however track does not");var o=-1===(null==(a=s.levelCodec)?void 0:a.indexOf(","))?s.levelCodec:s.codec;o&&"audio"===n.slice(0,5)&&(o=he(o,e.appendSource));var l=s.container+";codecs="+o;e.log("creating sourceBuffer("+l+")");try{var u=r[n]=i.addSourceBuffer(l),h=n;e.addBufferListener(h,"updatestart",e._onSBUpdateStart),e.addBufferListener(h,"updateend",e._onSBUpdateEnd),e.addBufferListener(h,"error",e._onSBUpdateError),e.appendSource&&e.addBufferListener(h,"bufferedchange",(function(t,r){var i=r.removedRanges;null!=i&&i.length&&e.hls.trigger(S.BUFFER_FLUSHED,{type:n})})),e.tracks[n]={buffer:u,codec:o,container:s.container,levelCodec:s.levelCodec,metadata:s.metadata,id:s.id}}catch(t){e.error("error while trying to add sourceBuffer: "+t.message),e.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:t,sourceBufferName:n,mimeType:l})}}};for(var a in t)n(a)},e._onSBUpdateStart=function(t){this.operationQueue.current(t).onStart()},e._onSBUpdateEnd=function(t){var e;if("closed"!==(null==(e=this.mediaSource)?void 0:e.readyState)){var r=this.operationQueue;r.current(t).onComplete(),r.shiftAndExecuteNext(t)}else this.resetBuffer(t)},e._onSBUpdateError=function(t,e){var r,i=new Error(t+" SourceBuffer error. MediaSource readyState: "+(null==(r=this.mediaSource)?void 0:r.readyState));this.error(""+i,e),this.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_APPENDING_ERROR,sourceBufferName:t,error:i,fatal:!1});var n=this.operationQueue.current(t);n&&n.onError(i)},e.removeExecutor=function(t,e,r){var i=this.media,n=this.mediaSource,a=this.operationQueue,s=this.sourceBuffer[t];if(!i||!n||!s)return this.warn("Attempting to remove from the "+t+" SourceBuffer, but it does not exist"),void a.shiftAndExecuteNext(t);var o=y(i.duration)?i.duration:1/0,l=y(n.duration)?n.duration:1/0,u=Math.max(0,e),h=Math.min(r,o,l);h>u&&(!s.ending||s.ended)?(s.ended=!1,this.log("Removing ["+u+","+h+"] from the "+t+" SourceBuffer"),s.remove(u,h)):a.shiftAndExecuteNext(t)},e.appendExecutor=function(t,e){var r=this.sourceBuffer[e];if(r)r.ended=!1,r.appendBuffer(t);else if(!this.pendingTracks[e])throw new Error("Attempting to append to the "+e+" SourceBuffer, but it does not exist")},e.blockBuffers=function(t,e){var r=this;if(void 0===e&&(e=this.getSourceBufferTypes()),!e.length)return this.log("Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve().then(t);var i=this.operationQueue,n=e.map((function(t){return i.appendBlocker(t)}));Promise.all(n).then((function(){t(),e.forEach((function(t){var e=r.sourceBuffer[t];null!=e&&e.updating||i.shiftAndExecuteNext(t)}))}))},e.getSourceBufferTypes=function(){return Object.keys(this.sourceBuffer)},e.addBufferListener=function(t,e,r){var i=this.sourceBuffer[t];if(i){var n=r.bind(this,t);this.listeners[t].push({event:e,listener:n}),i.addEventListener(e,n)}},e.removeBufferListeners=function(t){var e=this.sourceBuffer[t];e&&this.listeners[t].forEach((function(t){e.removeEventListener(t.event,t.listener)}))},s(t,[{key:"mediaSrc",get:function(){var t,e=(null==(t=this.media)?void 0:t.firstChild)||this.media;return null==e?void 0:e.src}}]),t}();function Zn(t){var e=t.querySelectorAll("source");[].slice.call(e).forEach((function(e){t.removeChild(e)}))}var ta={42:225,92:233,94:237,95:243,96:250,123:231,124:247,125:209,126:241,127:9608,128:174,129:176,130:189,131:191,132:8482,133:162,134:163,135:9834,136:224,137:32,138:232,139:226,140:234,141:238,142:244,143:251,144:193,145:201,146:211,147:218,148:220,149:252,150:8216,151:161,152:42,153:8217,154:9473,155:169,156:8480,157:8226,158:8220,159:8221,160:192,161:194,162:199,163:200,164:202,165:203,166:235,167:206,168:207,169:239,170:212,171:217,172:249,173:219,174:171,175:187,176:195,177:227,178:205,179:204,180:236,181:210,182:242,183:213,184:245,185:123,186:125,187:92,188:94,189:95,190:124,191:8764,192:196,193:228,194:214,195:246,196:223,197:165,198:164,199:9475,200:197,201:229,202:216,203:248,204:9487,205:9491,206:9495,207:9499},ea=function(t){var e=t;return ta.hasOwnProperty(t)&&(e=ta[t]),String.fromCharCode(e)},ra=15,ia=100,na={17:1,18:3,21:5,22:7,23:9,16:11,19:12,20:14},aa={17:2,18:4,21:6,22:8,23:10,19:13,20:15},sa={25:1,26:3,29:5,30:7,31:9,24:11,27:12,28:14},oa={25:2,26:4,29:6,30:8,31:10,27:13,28:15},la=["white","green","blue","cyan","red","yellow","magenta","black","transparent"],ua=function(){function t(){this.time=null,this.verboseLevel=0}return t.prototype.log=function(t,e){if(this.verboseLevel>=t){var r="function"==typeof e?e():e;w.log(this.time+" ["+t+"] "+r)}},t}(),ha=function(t){for(var e=[],r=0;ria&&(this.logger.log(3,"Too large cursor position "+this.pos),this.pos=ia)},e.moveCursor=function(t){var e=this.pos+t;if(t>1)for(var r=this.pos+1;r=144&&this.backSpace();var r=ea(t);this.pos>=ia?this.logger.log(0,(function(){return"Cannot insert "+t.toString(16)+" ("+r+") at position "+e.pos+". Skipping it!"})):(this.chars[this.pos].setChar(r,this.currPenState),this.moveCursor(1))},e.clearFromPos=function(t){var e;for(e=t;e0&&(r=t?"["+e.join(" | ")+"]":e.join("\n")),r},e.getTextAndFormat=function(){return this.rows},t}(),va=function(){function t(t,e,r){this.chNr=void 0,this.outputFilter=void 0,this.mode=void 0,this.verbose=void 0,this.displayedMemory=void 0,this.nonDisplayedMemory=void 0,this.lastOutputScreen=void 0,this.currRollUpRow=void 0,this.writeScreen=void 0,this.cueStartTime=void 0,this.logger=void 0,this.chNr=t,this.outputFilter=e,this.mode=null,this.verbose=0,this.displayedMemory=new ga(r),this.nonDisplayedMemory=new ga(r),this.lastOutputScreen=new ga(r),this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null,this.logger=r}var e=t.prototype;return e.reset=function(){this.mode=null,this.displayedMemory.reset(),this.nonDisplayedMemory.reset(),this.lastOutputScreen.reset(),this.outputFilter.reset(),this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null},e.getHandler=function(){return this.outputFilter},e.setHandler=function(t){this.outputFilter=t},e.setPAC=function(t){this.writeScreen.setPAC(t)},e.setBkgData=function(t){this.writeScreen.setBkgData(t)},e.setMode=function(t){t!==this.mode&&(this.mode=t,this.logger.log(2,(function(){return"MODE="+t})),"MODE_POP-ON"===this.mode?this.writeScreen=this.nonDisplayedMemory:(this.writeScreen=this.displayedMemory,this.writeScreen.reset()),"MODE_ROLL-UP"!==this.mode&&(this.displayedMemory.nrRollUpRows=null,this.nonDisplayedMemory.nrRollUpRows=null),this.mode=t)},e.insertChars=function(t){for(var e=this,r=0;r=46,e.italics)e.foreground="white";else{var r=Math.floor(t/2)-16;e.foreground=["white","green","blue","cyan","red","yellow","magenta"][r]}this.logger.log(2,"MIDROW: "+JSON.stringify(e)),this.writeScreen.setPen(e)},e.outputDataUpdate=function(t){void 0===t&&(t=!1);var e=this.logger.time;null!==e&&this.outputFilter&&(null!==this.cueStartTime||this.displayedMemory.isEmpty()?this.displayedMemory.equals(this.lastOutputScreen)||(this.outputFilter.newCue(this.cueStartTime,e,this.lastOutputScreen),t&&this.outputFilter.dispatchCue&&this.outputFilter.dispatchCue(),this.cueStartTime=this.displayedMemory.isEmpty()?null:e):this.cueStartTime=e,this.lastOutputScreen.copy(this.displayedMemory))},e.cueSplitAtTime=function(t){this.outputFilter&&(this.displayedMemory.isEmpty()||(this.outputFilter.newCue&&this.outputFilter.newCue(this.cueStartTime,t,this.displayedMemory),this.cueStartTime=t))},t}(),ma=function(){function t(t,e,r){this.channels=void 0,this.currentChannel=0,this.cmdHistory={a:null,b:null},this.logger=void 0;var i=this.logger=new ua;this.channels=[null,new va(t,e,i),new va(t+1,r,i)]}var e=t.prototype;return e.getHandler=function(t){return this.channels[t].getHandler()},e.setHandler=function(t,e){this.channels[t].setHandler(e)},e.addData=function(t,e){var r,i,n,a=!1;this.logger.time=t;for(var s=0;s ("+ha([i,n])+")"),(r=this.parseCmd(i,n))||(r=this.parseMidrow(i,n)),r||(r=this.parsePAC(i,n)),r||(r=this.parseBackgroundAttributes(i,n)),!r&&(a=this.parseChars(i,n))){var o=this.currentChannel;o&&o>0?this.channels[o].insertChars(a):this.logger.log(2,"No channel found yet. TEXT-MODE?")}r||a||this.logger.log(2,"Couldn't parse cleaned data "+ha([i,n])+" orig: "+ha([e[s],e[s+1]]))}},e.parseCmd=function(t,e){var r=this.cmdHistory;if(!((20===t||28===t||21===t||29===t)&&e>=32&&e<=47||(23===t||31===t)&&e>=33&&e<=35))return!1;if(ya(t,e,r))return pa(null,null,r),this.logger.log(3,"Repeated command ("+ha([t,e])+") is dropped"),!0;var i=20===t||21===t||23===t?1:2,n=this.channels[i];return 20===t||21===t||28===t||29===t?32===e?n.ccRCL():33===e?n.ccBS():34===e?n.ccAOF():35===e?n.ccAON():36===e?n.ccDER():37===e?n.ccRU(2):38===e?n.ccRU(3):39===e?n.ccRU(4):40===e?n.ccFON():41===e?n.ccRDC():42===e?n.ccTR():43===e?n.ccRTD():44===e?n.ccEDM():45===e?n.ccCR():46===e?n.ccENM():47===e&&n.ccEOC():n.ccTO(e-32),pa(t,e,r),this.currentChannel=i,!0},e.parseMidrow=function(t,e){var r=0;if((17===t||25===t)&&e>=32&&e<=47){if((r=17===t?1:2)!==this.currentChannel)return this.logger.log(0,"Mismatch channel in midrow parsing"),!1;var i=this.channels[r];return!!i&&(i.ccMIDROW(e),this.logger.log(3,"MIDROW ("+ha([t,e])+")"),!0)}return!1},e.parsePAC=function(t,e){var r,i=this.cmdHistory;if(!((t>=17&&t<=23||t>=25&&t<=31)&&e>=64&&e<=127||(16===t||24===t)&&e>=64&&e<=95))return!1;if(ya(t,e,i))return pa(null,null,i),!0;var n=t<=23?1:2;r=e>=64&&e<=95?1===n?na[t]:sa[t]:1===n?aa[t]:oa[t];var a=this.channels[n];return!!a&&(a.setPAC(this.interpretPAC(r,e)),pa(t,e,i),this.currentChannel=n,!0)},e.interpretPAC=function(t,e){var r,i={color:null,italics:!1,indent:null,underline:!1,row:t};return r=e>95?e-96:e-64,i.underline=1==(1&r),r<=13?i.color=["white","green","blue","cyan","red","yellow","magenta","white"][Math.floor(r/2)]:r<=15?(i.italics=!0,i.color="white"):i.indent=4*Math.floor((r-16)/2),i},e.parseChars=function(t,e){var r,i,n=null,a=null;if(t>=25?(r=2,a=t-8):(r=1,a=t),a>=17&&a<=19?(i=17===a?e+80:18===a?e+112:e+144,this.logger.log(2,"Special char '"+ea(i)+"' in channel "+r),n=[i]):t>=32&&t<=127&&(n=0===e?[t]:[t,e]),n){var s=ha(n);this.logger.log(3,"Char codes = "+s.join(",")),pa(t,e,this.cmdHistory)}return n},e.parseBackgroundAttributes=function(t,e){var r;if(!((16===t||24===t)&&e>=32&&e<=47||(23===t||31===t)&&e>=45&&e<=47))return!1;var i={};16===t||24===t?(r=Math.floor((e-32)/2),i.background=la[r],e%2==1&&(i.background=i.background+"_semi")):45===e?i.background="transparent":(i.foreground="black",47===e&&(i.underline=!0));var n=t<=23?1:2;return this.channels[n].setBkgData(i),pa(t,e,this.cmdHistory),!0},e.reset=function(){for(var t=0;tt)&&(this.startTime=t),this.endTime=e,this.screen=r,this.timelineController.createCaptionsTrack(this.trackName)},e.reset=function(){this.cueRanges=[],this.startTime=null},t}(),Ta=function(){if(null!=j&&j.VTTCue)return self.VTTCue;var t=["","lr","rl"],e=["start","middle","end","left","right"];function r(t,e){if("string"!=typeof e)return!1;if(!Array.isArray(t))return!1;var r=e.toLowerCase();return!!~t.indexOf(r)&&r}function i(t){return r(e,t)}function n(t){for(var e=arguments.length,r=new Array(e>1?e-1:0),i=1;i100)throw new Error("Position must be between 0 and 100.");E=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"positionAlign",n({},l,{get:function(){return T},set:function(t){var e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");T=e,this.hasBeenReset=!0}})),Object.defineProperty(o,"size",n({},l,{get:function(){return S},set:function(t){if(t<0||t>100)throw new Error("Size must be between 0 and 100.");S=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"align",n({},l,{get:function(){return L},set:function(t){var e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");L=e,this.hasBeenReset=!0}})),o.displayState=void 0}return a.prototype.getCueAsHTML=function(){return self.WebVTT.convertCueToDOMTree(self,this.text)},a}(),Sa=function(){function t(){}return t.prototype.decode=function(t,e){if(!t)return"";if("string"!=typeof t)throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(t))},t}();function La(t){function e(t,e,r,i){return 3600*(0|t)+60*(0|e)+(0|r)+parseFloat(i||0)}var r=t.match(/^(?:(\d+):)?(\d{2}):(\d{2})(\.\d+)?/);return r?parseFloat(r[2])>59?e(r[2],r[3],0,r[4]):e(r[1],r[2],r[3],r[4]):null}var Aa=function(){function t(){this.values=Object.create(null)}var e=t.prototype;return e.set=function(t,e){this.get(t)||""===e||(this.values[t]=e)},e.get=function(t,e,r){return r?this.has(t)?this.values[t]:e[r]:this.has(t)?this.values[t]:e},e.has=function(t){return t in this.values},e.alt=function(t,e,r){for(var i=0;i=0&&r<=100)return this.set(t,r),!0}return!1},t}();function Ra(t,e,r,i){var n=i?t.split(i):[t];for(var a in n)if("string"==typeof n[a]){var s=n[a].split(r);2===s.length&&e(s[0],s[1])}}var ka=new Ta(0,0,""),ba="middle"===ka.align?"middle":"center";function Da(t,e,r){var i=t;function n(){var e=La(t);if(null===e)throw new Error("Malformed timestamp: "+i);return t=t.replace(/^[^\sa-zA-Z-]+/,""),e}function a(){t=t.replace(/^\s+/,"")}if(a(),e.startTime=n(),a(),"--\x3e"!==t.slice(0,3))throw new Error("Malformed time stamp (time stamps must be separated by '--\x3e'): "+i);t=t.slice(3),a(),e.endTime=n(),a(),function(t,e){var i=new Aa;Ra(t,(function(t,e){var n;switch(t){case"region":for(var a=r.length-1;a>=0;a--)if(r[a].id===e){i.set(t,r[a].region);break}break;case"vertical":i.alt(t,e,["rl","lr"]);break;case"line":n=e.split(","),i.integer(t,n[0]),i.percent(t,n[0])&&i.set("snapToLines",!1),i.alt(t,n[0],["auto"]),2===n.length&&i.alt("lineAlign",n[1],["start",ba,"end"]);break;case"position":n=e.split(","),i.percent(t,n[0]),2===n.length&&i.alt("positionAlign",n[1],["start",ba,"end","line-left","line-right","auto"]);break;case"size":i.percent(t,e);break;case"align":i.alt(t,e,["start",ba,"end","left","right"])}}),/:/,/\s/),e.region=i.get("region",null),e.vertical=i.get("vertical","");var n=i.get("line","auto");"auto"===n&&-1===ka.line&&(n=-1),e.line=n,e.lineAlign=i.get("lineAlign","start"),e.snapToLines=i.get("snapToLines",!0),e.size=i.get("size",100),e.align=i.get("align",ba);var a=i.get("position","auto");"auto"===a&&50===ka.position&&(a="start"===e.align||"left"===e.align?0:"end"===e.align||"right"===e.align?100:50),e.position=a}(t,e)}function Ia(t){return t.replace(//gi,"\n")}var wa=function(){function t(){this.state="INITIAL",this.buffer="",this.decoder=new Sa,this.regionList=[],this.cue=null,this.oncue=void 0,this.onparsingerror=void 0,this.onflush=void 0}var e=t.prototype;return e.parse=function(t){var e=this;function r(){var t=e.buffer,r=0;for(t=Ia(t);r>>0).toString()};function Pa(t,e,r){return xa(t.toString())+xa(e.toString())+xa(r)}function Fa(t,e,r,i,n,a,s){var o,l,u,h=new wa,d=Tt(new Uint8Array(t)).trim().replace(Ca,"\n").split("\n"),c=[],f=e?(o=e.baseTime,void 0===(l=e.timescale)&&(l=1),yn(o,pn,1/l)):0,g="00:00.000",v=0,m=0,p=!0;h.oncue=function(t){var a=r[i],s=r.ccOffset,o=(v-f)/9e4;if(null!=a&&a.new&&(void 0!==m?s=r.ccOffset=a.start:function(t,e,r){var i=t[e],n=t[i.prevCC];if(!n||!n.new&&i.new)return t.ccOffset=t.presentationOffset=i.start,void(i.new=!1);for(;null!=(a=n)&&a.new;){var a;t.ccOffset+=i.start-n.start,i.new=!1,n=t[(i=n).prevCC]}t.presentationOffset=r}(r,i,o)),o){if(!e)return void(u=new Error("Missing initPTS for VTT MPEGTS"));s=o-r.presentationOffset}var l=t.endTime-t.startTime,h=An(9e4*(t.startTime+s-m),9e4*n)/9e4;t.startTime=Math.max(h,0),t.endTime=Math.max(h+l,0);var d=t.text.trim();t.text=decodeURIComponent(encodeURIComponent(d)),t.id||(t.id=Pa(t.startTime,t.endTime,d)),t.endTime>0&&c.push(t)},h.onparsingerror=function(t){u=t},h.onflush=function(){u?s(u):a(c)},d.forEach((function(t){if(p){if(_a(t,"X-TIMESTAMP-MAP=")){p=!1,t.slice(16).split(",").forEach((function(t){_a(t,"LOCAL:")?g=t.slice(6):_a(t,"MPEGTS:")&&(v=parseInt(t.slice(7)))}));try{m=function(t){var e=parseInt(t.slice(-3)),r=parseInt(t.slice(-6,-4)),i=parseInt(t.slice(-9,-7)),n=t.length>9?parseInt(t.substring(0,t.indexOf(":"))):0;if(!(y(e)&&y(r)&&y(i)&&y(n)))throw Error("Malformed X-TIMESTAMP-MAP: Local:"+t);return e+=1e3*r,(e+=6e4*i)+36e5*n}(g)/1e3}catch(t){u=t}return}""===t&&(p=!1)}h.parse(t+"\n")})),h.flush()}var Ma="stpp.ttml.im1t",Oa=/^(\d{2,}):(\d{2}):(\d{2}):(\d{2})\.?(\d+)?$/,Na=/^(\d*(?:\.\d*)?)(h|m|s|ms|f|t)$/,Ua={left:"start",center:"center",right:"end",start:"start",end:"end"};function Ba(t,e,r,i){var n=xt(new Uint8Array(t),["mdat"]);if(0!==n.length){var a,s,l,u,h=n.map((function(t){return Tt(t)})),d=(a=e.baseTime,s=1,void 0===(l=e.timescale)&&(l=1),void 0===u&&(u=!1),yn(a,s,1/l,u));try{h.forEach((function(t){return r(function(t,e){var r=(new DOMParser).parseFromString(t,"text/xml"),i=r.getElementsByTagName("tt")[0];if(!i)throw new Error("Invalid ttml");var n={frameRate:30,subFrameRate:1,frameRateMultiplier:0,tickRate:0},a=Object.keys(n).reduce((function(t,e){return t[e]=i.getAttribute("ttp:"+e)||n[e],t}),{}),s="preserve"!==i.getAttribute("xml:space"),l=Ka(Ga(i,"styling","style")),u=Ka(Ga(i,"layout","region")),h=Ga(i,"body","[begin]");return[].map.call(h,(function(t){var r=Ha(t,s);if(!r||!t.hasAttribute("begin"))return null;var i=Wa(t.getAttribute("begin"),a),n=Wa(t.getAttribute("dur"),a),h=Wa(t.getAttribute("end"),a);if(null===i)throw Ya(t);if(null===h){if(null===n)throw Ya(t);h=i+n}var d=new Ta(i-e,h-e,r);d.id=Pa(d.startTime,d.endTime,d.text);var c=function(t,e,r){var i="http://www.w3.org/ns/ttml#styling",n=null,a=["displayAlign","textAlign","color","backgroundColor","fontSize","fontFamily"],s=null!=t&&t.hasAttribute("style")?t.getAttribute("style"):null;return s&&r.hasOwnProperty(s)&&(n=r[s]),a.reduce((function(r,a){var s=Va(e,i,a)||Va(t,i,a)||Va(n,i,a);return s&&(r[a]=s),r}),{})}(u[t.getAttribute("region")],l[t.getAttribute("style")],l),f=c.textAlign;if(f){var g=Ua[f];g&&(d.lineAlign=g),d.align=f}return o(d,c),d})).filter((function(t){return null!==t}))}(t,d))}))}catch(t){i(t)}}else i(new Error("Could not parse IMSC1 mdat"))}function Ga(t,e,r){var i=t.getElementsByTagName(e)[0];return i?[].slice.call(i.querySelectorAll(r)):[]}function Ka(t){return t.reduce((function(t,e){var r=e.getAttribute("xml:id");return r&&(t[r]=e),t}),{})}function Ha(t,e){return[].slice.call(t.childNodes).reduce((function(t,r,i){var n;return"br"===r.nodeName&&i?t+"\n":null!=(n=r.childNodes)&&n.length?Ha(r,e):e?t+r.textContent.trim().replace(/\s+/g," "):t+r.textContent}),"")}function Va(t,e,r){return t&&t.hasAttributeNS(e,r)?t.getAttributeNS(e,r):null}function Ya(t){return new Error("Could not parse ttml timestamp "+t)}function Wa(t,e){if(!t)return null;var r=La(t);return null===r&&(Oa.test(t)?r=function(t,e){var r=Oa.exec(t),i=(0|r[4])+(0|r[5])/e.subFrameRate;return 3600*(0|r[1])+60*(0|r[2])+(0|r[3])+i/e.frameRate}(t,e):Na.test(t)&&(r=function(t,e){var r=Na.exec(t),i=Number(r[1]);switch(r[2]){case"h":return 3600*i;case"m":return 60*i;case"ms":return 1e3*i;case"f":return i/e.frameRate;case"t":return i/e.tickRate}return i}(t,e))),r}var ja=function(){function t(t){this.hls=void 0,this.media=null,this.config=void 0,this.enabled=!0,this.Cues=void 0,this.textTracks=[],this.tracks=[],this.initPTS=[],this.unparsedVttFrags=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.cea608Parser1=void 0,this.cea608Parser2=void 0,this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs={ccOffset:0,presentationOffset:0,0:{start:0,prevCC:-1,new:!0}},this.captionsProperties=void 0,this.hls=t,this.config=t.config,this.Cues=t.config.cueHandler,this.captionsProperties={textTrack1:{label:this.config.captionsTextTrack1Label,languageCode:this.config.captionsTextTrack1LanguageCode},textTrack2:{label:this.config.captionsTextTrack2Label,languageCode:this.config.captionsTextTrack2LanguageCode},textTrack3:{label:this.config.captionsTextTrack3Label,languageCode:this.config.captionsTextTrack3LanguageCode},textTrack4:{label:this.config.captionsTextTrack4Label,languageCode:this.config.captionsTextTrack4LanguageCode}},t.on(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(S.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.on(S.FRAG_LOADING,this.onFragLoading,this),t.on(S.FRAG_LOADED,this.onFragLoaded,this),t.on(S.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),t.on(S.FRAG_DECRYPTED,this.onFragDecrypted,this),t.on(S.INIT_PTS_FOUND,this.onInitPtsFound,this),t.on(S.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),t.on(S.BUFFER_FLUSHING,this.onBufferFlushing,this)}var e=t.prototype;return e.destroy=function(){var t=this.hls;t.off(S.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(S.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(S.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.off(S.FRAG_LOADING,this.onFragLoading,this),t.off(S.FRAG_LOADED,this.onFragLoaded,this),t.off(S.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),t.off(S.FRAG_DECRYPTED,this.onFragDecrypted,this),t.off(S.INIT_PTS_FOUND,this.onInitPtsFound,this),t.off(S.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),t.off(S.BUFFER_FLUSHING,this.onBufferFlushing,this),this.hls=this.config=null,this.cea608Parser1=this.cea608Parser2=void 0},e.initCea608Parsers=function(){if(this.config.enableCEA708Captions&&(!this.cea608Parser1||!this.cea608Parser2)){var t=new Ea(this,"textTrack1"),e=new Ea(this,"textTrack2"),r=new Ea(this,"textTrack3"),i=new Ea(this,"textTrack4");this.cea608Parser1=new ma(1,t,e),this.cea608Parser2=new ma(3,r,i)}},e.addCues=function(t,e,r,i,n){for(var a,s,o,l,u=!1,h=n.length;h--;){var d=n[h],c=(a=d[0],s=d[1],o=e,l=r,Math.min(s,l)-Math.max(a,o));if(c>=0&&(d[0]=Math.min(d[0],e),d[1]=Math.max(d[1],r),u=!0,c/(r-e)>.5))return}if(u||n.push([e,r]),this.config.renderTextTracksNatively){var f=this.captionsTracks[t];this.Cues.newCue(f,e,r,i)}else{var g=this.Cues.newCue(null,e,r,i);this.hls.trigger(S.CUES_PARSED,{type:"captions",cues:g,track:t})}},e.onInitPtsFound=function(t,e){var r=this,i=e.frag,n=e.id,a=e.initPTS,s=e.timescale,o=this.unparsedVttFrags;"main"===n&&(this.initPTS[i.cc]={baseTime:a,timescale:s}),o.length&&(this.unparsedVttFrags=[],o.forEach((function(t){r.onFragLoaded(S.FRAG_LOADED,t)})))},e.getExistingTrack=function(t,e){var r=this.media;if(r)for(var i=0;ii.cc||l.trigger(S.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:i,error:e})}))}else s.push(t)},e._fallbackToIMSC1=function(t,e){var r=this,i=this.tracks[t.level];i.textCodec||Ba(e,this.initPTS[t.cc],(function(){i.textCodec=Ma,r._parseIMSC1(t,e)}),(function(){i.textCodec="wvtt"}))},e._appendCues=function(t,e){var r=this.hls;if(this.config.renderTextTracksNatively){var i=this.textTracks[e];if(!i||"disabled"===i.mode)return;t.forEach((function(t){return Oe(i,t)}))}else{var n=this.tracks[e];if(!n)return;var a=n.default?"default":"subtitles"+e;r.trigger(S.CUES_PARSED,{type:"subtitles",cues:t,track:a})}},e.onFragDecrypted=function(t,e){e.frag.type===_e&&this.onFragLoaded(S.FRAG_LOADED,e)},e.onSubtitleTracksCleared=function(){this.tracks=[],this.captionsTracks={}},e.onFragParsingUserdata=function(t,e){this.initCea608Parsers();var r=this.cea608Parser1,i=this.cea608Parser2;if(this.enabled&&r&&i){var n=e.frag,a=e.samples;if(n.type!==we||"NONE"!==this.closedCaptionsForLevel(n))for(var s=0;sthis.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=e.autoLevelCapping}}},e.getMaxLevel=function(e){var r=this,i=this.hls.levels;if(!i.length)return-1;var n=i.filter((function(t,i){return r.isLevelAllowed(t)&&i<=e}));return this.clientRect=null,t.getMaxLevelByMediaSize(n,this.mediaWidth,this.mediaHeight)},e.startCapping=function(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())},e.stopCapping=function(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)},e.getDimensions=function(){if(this.clientRect)return this.clientRect;var t=this.media,e={width:0,height:0};if(t){var r=t.getBoundingClientRect();e.width=r.width,e.height=r.height,e.width||e.height||(e.width=r.right-r.left||t.width||0,e.height=r.bottom-r.top||t.height||0)}return this.clientRect=e,e},e.isLevelAllowed=function(t){return!this.restrictedLevels.some((function(e){return t.bitrate===e.bitrate&&t.width===e.width&&t.height===e.height}))},t.getMaxLevelByMediaSize=function(t,e,r){if(null==t||!t.length)return-1;for(var i,n,a=t.length-1,s=Math.max(e,r),o=0;o=s||l.height>=s)&&(i=l,!(n=t[o+1])||i.width!==n.width||i.height!==n.height)){a=o;break}}return a},s(t,[{key:"mediaWidth",get:function(){return this.getDimensions().width*this.contentScaleFactor}},{key:"mediaHeight",get:function(){return this.getDimensions().height*this.contentScaleFactor}},{key:"contentScaleFactor",get:function(){var t=1;if(!this.hls.config.ignoreDevicePixelRatio)try{t=self.devicePixelRatio}catch(t){}return t}}]),t}(),Qa=function(){function t(t){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=t,this.registerListeners()}var e=t.prototype;return e.setStreamController=function(t){this.streamController=t},e.registerListeners=function(){this.hls.on(S.MEDIA_ATTACHING,this.onMediaAttaching,this)},e.unregisterListeners=function(){this.hls.off(S.MEDIA_ATTACHING,this.onMediaAttaching,this)},e.destroy=function(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null},e.onMediaAttaching=function(t,e){var r=this.hls.config;if(r.capLevelOnFPSDrop){var i=e.media instanceof self.HTMLVideoElement?e.media:null;this.media=i,i&&"function"==typeof i.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),r.fpsDroppedMonitoringPeriod)}},e.checkFPS=function(t,e,r){var i=performance.now();if(e){if(this.lastTime){var n=i-this.lastTime,a=r-this.lastDroppedFrames,s=e-this.lastDecodedFrames,o=1e3*a/n,l=this.hls;if(l.trigger(S.FPS_DROP,{currentDropped:a,currentDecoded:s,totalDroppedFrames:r}),o>0&&a>l.config.fpsDroppedMonitoringThreshold*s){var u=l.currentLevel;w.warn("drop FPS ratio greater than max allowed value for currentLevel: "+u),u>0&&(-1===l.autoLevelCapping||l.autoLevelCapping>=u)&&(u-=1,l.trigger(S.FPS_DROP_LEVEL_CAPPING,{level:u,droppedLevel:l.currentLevel}),l.autoLevelCapping=u,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=r,this.lastDecodedFrames=e}},e.checkFPSInterval=function(){var t=this.media;if(t)if(this.isVideoPlaybackQualityAvailable){var e=t.getVideoPlaybackQuality();this.checkFPS(t,e.totalVideoFrames,e.droppedVideoFrames)}else this.checkFPS(t,t.webkitDecodedFrameCount,t.webkitDroppedFrameCount)},t}(),Ja="[eme]",$a=function(){function t(e){this.hls=void 0,this.config=void 0,this.media=null,this.keyFormatPromise=null,this.keySystemAccessPromises={},this._requestLicenseFailureCount=0,this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},this.setMediaKeysQueue=t.CDMCleanupPromise?[t.CDMCleanupPromise]:[],this.onMediaEncrypted=this._onMediaEncrypted.bind(this),this.onWaitingForKey=this._onWaitingForKey.bind(this),this.debug=w.debug.bind(w,Ja),this.log=w.log.bind(w,Ja),this.warn=w.warn.bind(w,Ja),this.error=w.error.bind(w,Ja),this.hls=e,this.config=e.config,this.registerListeners()}var e=t.prototype;return e.destroy=function(){this.unregisterListeners(),this.onMediaDetached();var t=this.config;t.requestMediaKeySystemAccessFunc=null,t.licenseXhrSetup=t.licenseResponseCallback=void 0,t.drmSystems=t.drmSystemOptions={},this.hls=this.onMediaEncrypted=this.onWaitingForKey=this.keyIdToKeySessionPromise=null,this.config=null},e.registerListeners=function(){this.hls.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(S.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.on(S.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(S.MANIFEST_LOADED,this.onManifestLoaded,this)},e.unregisterListeners=function(){this.hls.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.off(S.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.off(S.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.off(S.MANIFEST_LOADED,this.onManifestLoaded,this)},e.getLicenseServerUrl=function(t){var e=this.config,r=e.drmSystems,i=e.widevineLicenseUrl,n=r[t];if(n)return n.licenseUrl;if(t===q.WIDEVINE&&i)return i;throw new Error('no license server URL configured for key-system "'+t+'"')},e.getServerCertificateUrl=function(t){var e=this.config.drmSystems[t];if(e)return e.serverCertificateUrl;this.log('No Server Certificate in config.drmSystems["'+t+'"]')},e.attemptKeySystemAccess=function(t){var e=this,r=this.hls.levels,i=function(t,e,r){return!!t&&r.indexOf(t)===e},n=r.map((function(t){return t.audioCodec})).filter(i),a=r.map((function(t){return t.videoCodec})).filter(i);return n.length+a.length===0&&a.push("avc1.42e01e"),new Promise((function(r,i){!function t(s){var o=s.shift();e.getMediaKeysPromise(o,n,a).then((function(t){return r({keySystem:o,mediaKeys:t})})).catch((function(e){s.length?t(s):i(e instanceof is?e:new is({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_NO_ACCESS,error:e,fatal:!0},e.message))}))}(t)}))},e.requestMediaKeySystemAccess=function(t,e){var r=this.config.requestMediaKeySystemAccessFunc;if("function"!=typeof r){var i="Configured requestMediaKeySystemAccess is not a function "+r;return null===it&&"http:"===self.location.protocol&&(i="navigator.requestMediaKeySystemAccess is not available over insecure protocol "+location.protocol),Promise.reject(new Error(i))}return r(t,e)},e.getMediaKeysPromise=function(t,e,r){var i=this,n=function(t,e,r,i){var n;switch(t){case q.FAIRPLAY:n=["cenc","sinf"];break;case q.WIDEVINE:case q.PLAYREADY:n=["cenc"];break;case q.CLEARKEY:n=["cenc","keyids"];break;default:throw new Error("Unknown key-system: "+t)}return function(t,e,r,i){return[{initDataTypes:t,persistentState:i.persistentState||"optional",distinctiveIdentifier:i.distinctiveIdentifier||"optional",sessionTypes:i.sessionTypes||[i.sessionType||"temporary"],audioCapabilities:e.map((function(t){return{contentType:'audio/mp4; codecs="'+t+'"',robustness:i.audioRobustness||"",encryptionScheme:i.audioEncryptionScheme||null}})),videoCapabilities:r.map((function(t){return{contentType:'video/mp4; codecs="'+t+'"',robustness:i.videoRobustness||"",encryptionScheme:i.videoEncryptionScheme||null}}))}]}(n,e,r,i)}(t,e,r,this.config.drmSystemOptions),a=this.keySystemAccessPromises[t],s=null==a?void 0:a.keySystemAccess;if(!s){this.log('Requesting encrypted media "'+t+'" key-system access with config: '+JSON.stringify(n)),s=this.requestMediaKeySystemAccess(t,n);var o=this.keySystemAccessPromises[t]={keySystemAccess:s};return s.catch((function(e){i.log('Failed to obtain access to key-system "'+t+'": '+e)})),s.then((function(e){i.log('Access for key-system "'+e.keySystem+'" obtained');var r=i.fetchServerCertificate(t);return i.log('Create media-keys for "'+t+'"'),o.mediaKeys=e.createMediaKeys().then((function(e){return i.log('Media-keys created for "'+t+'"'),r.then((function(r){return r?i.setMediaKeysServerCertificate(e,t,r):e}))})),o.mediaKeys.catch((function(e){i.error('Failed to create media-keys for "'+t+'"}: '+e)})),o.mediaKeys}))}return s.then((function(){return a.mediaKeys}))},e.createMediaKeySessionContext=function(t){var e=t.decryptdata,r=t.keySystem,i=t.mediaKeys;this.log('Creating key-system session "'+r+'" keyId: '+Lt(e.keyId||[]));var n=i.createSession(),a={decryptdata:e,keySystem:r,mediaKeys:i,mediaKeysSession:n,keyStatus:"status-pending"};return this.mediaKeySessions.push(a),a},e.renewKeySession=function(t){var e=t.decryptdata;if(e.pssh){var r=this.createMediaKeySessionContext(t),i=this.getKeyIdString(e);this.keyIdToKeySessionPromise[i]=this.generateRequestWithPreferredKeySession(r,"cenc",e.pssh,"expired")}else this.warn("Could not renew expired session. Missing pssh initData.");this.removeSession(t)},e.getKeyIdString=function(t){if(!t)throw new Error("Could not read keyId of undefined decryptdata");if(null===t.keyId)throw new Error("keyId is null");return Lt(t.keyId)},e.updateKeySession=function(t,e){var r,i=t.mediaKeysSession;return this.log('Updating key-session "'+i.sessionId+'" for keyID '+Lt((null==(r=t.decryptdata)?void 0:r.keyId)||[])+"\n } (data length: "+(e?e.byteLength:e)+")"),i.update(e)},e.selectKeySystemFormat=function(t){var e=Object.keys(t.levelkeys||{});return this.keyFormatPromise||(this.log("Selecting key-system from fragment (sn: "+t.sn+" "+t.type+": "+t.level+") key formats "+e.join(", ")),this.keyFormatPromise=this.getKeyFormatPromise(e)),this.keyFormatPromise},e.getKeyFormatPromise=function(t){var e=this;return new Promise((function(r,i){var n=et(e.config),a=t.map($).filter((function(t){return!!t&&-1!==n.indexOf(t)}));return e.getKeySystemSelectionPromise(a).then((function(t){var e=t.keySystem,n=tt(e);n?r(n):i(new Error('Unable to find format for key-system "'+e+'"'))})).catch(i)}))},e.loadKey=function(t){var e=this,r=t.keyInfo.decryptdata,i=this.getKeyIdString(r),n="(keyId: "+i+' format: "'+r.keyFormat+'" method: '+r.method+" uri: "+r.uri+")";this.log("Starting session for key "+n);var a=this.keyIdToKeySessionPromise[i];return a||(a=this.keyIdToKeySessionPromise[i]=this.getKeySystemForKeyPromise(r).then((function(i){var a=i.keySystem,s=i.mediaKeys;return e.throwIfDestroyed(),e.log("Handle encrypted media sn: "+t.frag.sn+" "+t.frag.type+": "+t.frag.level+" using key "+n),e.attemptSetMediaKeys(a,s).then((function(){e.throwIfDestroyed();var t=e.createMediaKeySessionContext({keySystem:a,mediaKeys:s,decryptdata:r});return e.generateRequestWithPreferredKeySession(t,"cenc",r.pssh,"playlist-key")}))}))).catch((function(t){return e.handleError(t)})),a},e.throwIfDestroyed=function(t){if(!this.hls)throw new Error("invalid state")},e.handleError=function(t){this.hls&&(this.error(t.message),t instanceof is?this.hls.trigger(S.ERROR,t.data):this.hls.trigger(S.ERROR,{type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_NO_KEYS,error:t,fatal:!0}))},e.getKeySystemForKeyPromise=function(t){var e=this.getKeyIdString(t),r=this.keyIdToKeySessionPromise[e];if(!r){var i=$(t.keyFormat),n=i?[i]:et(this.config);return this.attemptKeySystemAccess(n)}return r},e.getKeySystemSelectionPromise=function(t){if(t.length||(t=et(this.config)),0===t.length)throw new is({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_NO_CONFIGURED_LICENSE,fatal:!0},"Missing key-system license configuration options "+JSON.stringify({drmSystems:this.config.drmSystems}));return this.attemptKeySystemAccess(t)},e._onMediaEncrypted=function(t){var e=this,r=t.initDataType,i=t.initData;if(this.debug('"'+t.type+'" event: init data type: "'+r+'"'),null!==i){var n,a;if("sinf"===r&&this.config.drmSystems[q.FAIRPLAY]){var s=bt(new Uint8Array(i));try{var o=V(JSON.parse(s).sinf),l=Bt(new Uint8Array(o));if(!l)return;n=l.subarray(8,24),a=q.FAIRPLAY}catch(t){return void this.warn('Failed to parse sinf "encrypted" event message initData')}}else{var u=function(t){if(!(t instanceof ArrayBuffer)||t.byteLength<32)return null;var e={version:0,systemId:"",kids:null,data:null},r=new DataView(t),i=r.getUint32(0);if(t.byteLength!==i&&i>44)return null;if(1886614376!==r.getUint32(4))return null;if(e.version=r.getUint32(8)>>>24,e.version>1)return null;e.systemId=Lt(new Uint8Array(t,12,16));var n=r.getUint32(28);if(0===e.version){if(i-320)for(var a,s=0,o=n.length;s in key message");return W(atob(f))},e.setupLicenseXHR=function(t,e,r,i){var n=this,a=this.config.licenseXhrSetup;return a?Promise.resolve().then((function(){if(!r.decryptdata)throw new Error("Key removed");return a.call(n.hls,t,e,r,i)})).catch((function(s){if(!r.decryptdata)throw s;return t.open("POST",e,!0),a.call(n.hls,t,e,r,i)})).then((function(r){return t.readyState||t.open("POST",e,!0),{xhr:t,licenseChallenge:r||i}})):(t.open("POST",e,!0),Promise.resolve({xhr:t,licenseChallenge:i}))},e.requestLicense=function(t,e){var r=this,i=this.config.keyLoadPolicy.default;return new Promise((function(n,a){var s=r.getLicenseServerUrl(t.keySystem);r.log("Sending license request to URL: "+s);var o=new XMLHttpRequest;o.responseType="arraybuffer",o.onreadystatechange=function(){if(!r.hls||!t.mediaKeysSession)return a(new Error("invalid state"));if(4===o.readyState)if(200===o.status){r._requestLicenseFailureCount=0;var l=o.response;r.log("License received "+(l instanceof ArrayBuffer?l.byteLength:l));var u=r.config.licenseResponseCallback;if(u)try{l=u.call(r.hls,o,s,t)}catch(t){r.error(t)}n(l)}else{var h=i.errorRetry,d=h?h.maxNumRetry:0;if(r._requestLicenseFailureCount++,r._requestLicenseFailureCount>d||o.status>=400&&o.status<500)a(new is({type:L.KEY_SYSTEM_ERROR,details:A.KEY_SYSTEM_LICENSE_REQUEST_FAILED,fatal:!0,networkDetails:o,response:{url:s,data:void 0,code:o.status,text:o.statusText}},"License Request XHR failed ("+s+"). Status: "+o.status+" ("+o.statusText+")"));else{var c=d-r._requestLicenseFailureCount+1;r.warn("Retrying license request, "+c+" attempts left"),r.requestLicense(t,e).then(n,a)}}},t.licenseXhr&&t.licenseXhr.readyState!==XMLHttpRequest.DONE&&t.licenseXhr.abort(),t.licenseXhr=o,r.setupLicenseXHR(o,s,t,e).then((function(e){var i=e.xhr,n=e.licenseChallenge;t.keySystem==q.PLAYREADY&&(n=r.unpackPlayReadyKeyMessage(i,n)),i.send(n)}))}))},e.onMediaAttached=function(t,e){if(this.config.emeEnabled){var r=e.media;this.media=r,r.addEventListener("encrypted",this.onMediaEncrypted),r.addEventListener("waitingforkey",this.onWaitingForKey)}},e.onMediaDetached=function(){var e=this,r=this.media,i=this.mediaKeySessions;r&&(r.removeEventListener("encrypted",this.onMediaEncrypted),r.removeEventListener("waitingforkey",this.onWaitingForKey),this.media=null),this._requestLicenseFailureCount=0,this.setMediaKeysQueue=[],this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},Xt.clearKeyUriToKeyIdMap();var n=i.length;t.CDMCleanupPromise=Promise.all(i.map((function(t){return e.removeSession(t)})).concat(null==r?void 0:r.setMediaKeys(null).catch((function(t){e.log("Could not clear media keys: "+t)})))).then((function(){n&&(e.log("finished closing key sessions and clearing media keys"),i.length=0)})).catch((function(t){e.log("Could not close sessions and clear media keys: "+t)}))},e.onManifestLoading=function(){this.keyFormatPromise=null},e.onManifestLoaded=function(t,e){var r=e.sessionKeys;if(r&&this.config.emeEnabled&&!this.keyFormatPromise){var i=r.reduce((function(t,e){return-1===t.indexOf(e.keyFormat)&&t.push(e.keyFormat),t}),[]);this.log("Selecting key-system from session-keys "+i.join(", ")),this.keyFormatPromise=this.getKeyFormatPromise(i)}},e.removeSession=function(t){var e=this,r=t.mediaKeysSession,i=t.licenseXhr;if(r){this.log("Remove licenses and keys and close session "+r.sessionId),t._onmessage&&(r.removeEventListener("message",t._onmessage),t._onmessage=void 0),t._onkeystatuseschange&&(r.removeEventListener("keystatuseschange",t._onkeystatuseschange),t._onkeystatuseschange=void 0),i&&i.readyState!==XMLHttpRequest.DONE&&i.abort(),t.mediaKeysSession=t.decryptdata=t.licenseXhr=void 0;var n=this.mediaKeySessions.indexOf(t);return n>-1&&this.mediaKeySessions.splice(n,1),r.remove().catch((function(t){e.log("Could not remove session: "+t)})).then((function(){return r.close()})).catch((function(t){e.log("Could not close session: "+t)}))}},t}();$a.CDMCleanupPromise=void 0;var Za,ts,es,rs,is=function(t){function e(e,r){var i;return(i=t.call(this,r)||this).data=void 0,e.error||(e.error=new Error(r)),i.data=e,e.err=e.error,i}return l(e,t),e}(c(Error));!function(t){t.MANIFEST="m",t.AUDIO="a",t.VIDEO="v",t.MUXED="av",t.INIT="i",t.CAPTION="c",t.TIMED_TEXT="tt",t.KEY="k",t.OTHER="o"}(Za||(Za={})),function(t){t.DASH="d",t.HLS="h",t.SMOOTH="s",t.OTHER="o"}(ts||(ts={})),function(t){t.OBJECT="CMCD-Object",t.REQUEST="CMCD-Request",t.SESSION="CMCD-Session",t.STATUS="CMCD-Status"}(es||(es={}));var ns=((rs={})[es.OBJECT]=["br","d","ot","tb"],rs[es.REQUEST]=["bl","dl","mtp","nor","nrr","su"],rs[es.SESSION]=["cid","pr","sf","sid","st","v"],rs[es.STATUS]=["bs","rtp"],rs),as=function t(e,r){this.value=void 0,this.params=void 0,Array.isArray(e)&&(e=e.map((function(e){return e instanceof t?e:new t(e)}))),this.value=e,this.params=r},ss=function(t){this.description=void 0,this.description=t},os="Dict";function ls(t,e,r,i){return new Error("failed to "+t+' "'+(n=e,(Array.isArray(n)?JSON.stringify(n):n instanceof Map?"Map{}":n instanceof Set?"Set{}":"object"==typeof n?JSON.stringify(n):String(n))+'" as ')+r,{cause:i});var n}var us="Bare Item",hs="Boolean",ds="Byte Sequence",cs="Decimal",fs="Integer",gs=/[\x00-\x1f\x7f]+/,vs="Token",ms="Key";function ps(t,e,r){return ls("serialize",t,e,r)}function ys(t){if(!1===ArrayBuffer.isView(t))throw ps(t,ds);return":"+(e=t,btoa(String.fromCharCode.apply(String,e))+":");var e}function Es(t){if(function(t){return t<-999999999999999||99999999999999912)throw ps(t,cs);var r=e.toString();return r.includes(".")?r:r+".0"}var Ls="String";function As(t){var e,r=(e=t).description||e.toString().slice(7,-1);if(!1===/^([a-zA-Z*])([!#$%&'*+\-.^_`|~\w:/]*)$/.test(r))throw ps(r,vs);return r}function Rs(t){switch(typeof t){case"number":if(!y(t))throw ps(t,us);return Number.isInteger(t)?Es(t):Ss(t);case"string":return function(t){if(gs.test(t))throw ps(t,Ls);return'"'+t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'}(t);case"symbol":return As(t);case"boolean":return function(t){if("boolean"!=typeof t)throw ps(t,hs);return t?"?1":"?0"}(t);case"object":if(t instanceof Date)return function(t){return"@"+Es(t.getTime()/1e3)}(t);if(t instanceof Uint8Array)return ys(t);if(t instanceof ss)return As(t);default:throw ps(t,us)}}function ks(t){if(!1===/^[a-z*][a-z0-9\-_.*]*$/.test(t))throw ps(t,ms);return t}function bs(t){return null==t?"":Object.entries(t).map((function(t){var e=t[0],r=t[1];return!0===r?";"+ks(e):";"+ks(e)+"="+Rs(r)})).join("")}function Ds(t){return t instanceof as?""+Rs(t.value)+bs(t.params):Rs(t)}function Is(t,e){var r;if(void 0===e&&(e={whitespace:!0}),"object"!=typeof t)throw ps(t,os);var i=t instanceof Map?t.entries():Object.entries(t),n=null!=(r=e)&&r.whitespace?" ":"";return Array.from(i).map((function(t){var e=t[0],r=t[1];r instanceof as==0&&(r=new as(r));var i,n=ks(e);return!0===r.value?n+=bs(r.params):(n+="=",Array.isArray(r.value)?n+="("+(i=r).value.map(Ds).join(" ")+")"+bs(i.params):n+=Ds(r)),n})).join(","+n)}var ws=function(t){return"ot"===t||"sf"===t||"st"===t},Cs=function(t){return"number"==typeof t?y(t):null!=t&&""!==t&&!1!==t},_s=function(t){return Math.round(t)},xs=function(t){return 100*_s(t/100)},Ps={br:_s,d:_s,bl:xs,dl:xs,mtp:xs,nor:function(t,e){return null!=e&&e.baseUrl&&(t=function(t,e){var r=new URL(t),i=new URL(e);if(r.origin!==i.origin)return t;for(var n=r.pathname.split("/").slice(1),a=i.pathname.split("/").slice(1,-1);n[0]===a[0];)n.shift(),a.shift();for(;a.length;)a.shift(),n.unshift("..");return n.join("/")}(t,e.baseUrl)),encodeURIComponent(t)},rtp:xs,tb:_s};function Fs(t,e){return void 0===e&&(e={}),t?function(t,e){return Is(t,e)}(function(t,e){var r={};if(null==t||"object"!=typeof t)return r;var i=Object.keys(t).sort(),n=o({},Ps,null==e?void 0:e.formatters),a=null==e?void 0:e.filter;return i.forEach((function(i){if(null==a||!a(i)){var s=t[i],o=n[i];o&&(s=o(s,e)),"v"===i&&1===s||"pr"==i&&1===s||Cs(s)&&(ws(i)&&"string"==typeof s&&(s=new ss(s)),r[i]=s)}})),r}(t,e),o({whitespace:!1},e)):""}function Ms(t,e,r){return o(t,function(t,e){var r;if(void 0===e&&(e={}),!t)return{};var i=Object.entries(t),n=Object.entries(ns).concat(Object.entries((null==(r=e)?void 0:r.customHeaderMap)||{})),a=i.reduce((function(t,e){var r,i=e[0],a=e[1],s=(null==(r=n.find((function(t){return t[1].includes(i)})))?void 0:r[0])||es.REQUEST;return null!=t[s]||(t[s]={}),t[s][i]=a,t}),{});return Object.entries(a).reduce((function(t,r){var i=r[0],n=r[1];return t[i]=Fs(n,e),t}),{})}(e,r))}var Os="CMCD",Ns=/CMCD=[^&#]+/;function Us(t,e,r){var i=function(t,e){if(void 0===e&&(e={}),!t)return"";var r=Fs(t,e);return Os+"="+encodeURIComponent(r)}(e,r);if(!i)return t;if(Ns.test(t))return t.replace(Ns,i);var n=t.includes("?")?"&":"?";return""+t+n+i}var Bs=function(){function t(t){var e=this;this.hls=void 0,this.config=void 0,this.media=void 0,this.sid=void 0,this.cid=void 0,this.useHeaders=!1,this.includeKeys=void 0,this.initialized=!1,this.starved=!1,this.buffering=!0,this.audioBuffer=void 0,this.videoBuffer=void 0,this.onWaiting=function(){e.initialized&&(e.starved=!0),e.buffering=!0},this.onPlaying=function(){e.initialized||(e.initialized=!0),e.buffering=!1},this.applyPlaylistData=function(t){try{e.apply(t,{ot:Za.MANIFEST,su:!e.initialized})}catch(t){w.warn("Could not generate manifest CMCD data.",t)}},this.applyFragmentData=function(t){try{var r=t.frag,i=e.hls.levels[r.level],n=e.getObjectType(r),a={d:1e3*r.duration,ot:n};n!==Za.VIDEO&&n!==Za.AUDIO&&n!=Za.MUXED||(a.br=i.bitrate/1e3,a.tb=e.getTopBandwidth(n)/1e3,a.bl=e.getBufferLength(n)),e.apply(t,a)}catch(t){w.warn("Could not generate segment CMCD data.",t)}},this.hls=t;var r=this.config=t.config,i=r.cmcd;null!=i&&(r.pLoader=this.createPlaylistLoader(),r.fLoader=this.createFragmentLoader(),this.sid=i.sessionId||function(){try{return crypto.randomUUID()}catch(i){try{var t=URL.createObjectURL(new Blob),e=t.toString();return URL.revokeObjectURL(t),e.slice(e.lastIndexOf("/")+1)}catch(t){var r=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(t){var e=(r+16*Math.random())%16|0;return r=Math.floor(r/16),("x"==t?e:3&e|8).toString(16)}))}}}(),this.cid=i.contentId,this.useHeaders=!0===i.useHeaders,this.includeKeys=i.includeKeys,this.registerListeners())}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(S.MEDIA_DETACHED,this.onMediaDetached,this),t.on(S.BUFFER_CREATED,this.onBufferCreated,this)},e.unregisterListeners=function(){var t=this.hls;t.off(S.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(S.MEDIA_DETACHED,this.onMediaDetached,this),t.off(S.BUFFER_CREATED,this.onBufferCreated,this)},e.destroy=function(){this.unregisterListeners(),this.onMediaDetached(),this.hls=this.config=this.audioBuffer=this.videoBuffer=null,this.onWaiting=this.onPlaying=null},e.onMediaAttached=function(t,e){this.media=e.media,this.media.addEventListener("waiting",this.onWaiting),this.media.addEventListener("playing",this.onPlaying)},e.onMediaDetached=function(){this.media&&(this.media.removeEventListener("waiting",this.onWaiting),this.media.removeEventListener("playing",this.onPlaying),this.media=null)},e.onBufferCreated=function(t,e){var r,i;this.audioBuffer=null==(r=e.tracks.audio)?void 0:r.buffer,this.videoBuffer=null==(i=e.tracks.video)?void 0:i.buffer},e.createData=function(){var t;return{v:1,sf:ts.HLS,sid:this.sid,cid:this.cid,pr:null==(t=this.media)?void 0:t.playbackRate,mtp:this.hls.bandwidthEstimate/1e3}},e.apply=function(t,e){void 0===e&&(e={}),o(e,this.createData());var r=e.ot===Za.INIT||e.ot===Za.VIDEO||e.ot===Za.MUXED;this.starved&&r&&(e.bs=!0,e.su=!0,this.starved=!1),null==e.su&&(e.su=this.buffering);var i=this.includeKeys;i&&(e=Object.keys(e).reduce((function(t,r){return i.includes(r)&&(t[r]=e[r]),t}),{})),this.useHeaders?(t.headers||(t.headers={}),Ms(t.headers,e)):t.url=Us(t.url,e)},e.getObjectType=function(t){var e=t.type;return"subtitle"===e?Za.TIMED_TEXT:"initSegment"===t.sn?Za.INIT:"audio"===e?Za.AUDIO:"main"===e?this.hls.audioTracks.length?Za.VIDEO:Za.MUXED:void 0},e.getTopBandwidth=function(t){var e,r=0,i=this.hls;if(t===Za.AUDIO)e=i.audioTracks;else{var n=i.maxAutoLevel,a=n>-1?n+1:i.levels.length;e=i.levels.slice(0,a)}for(var s,o=g(e);!(s=o()).done;){var l=s.value;l.bitrate>r&&(r=l.bitrate)}return r>0?r:NaN},e.getBufferLength=function(t){var e=this.hls.media,r=t===Za.AUDIO?this.audioBuffer:this.videoBuffer;return r&&e?1e3*Jr.bufferInfo(r,e.currentTime,this.config.maxBufferHole).len:NaN},e.createPlaylistLoader=function(){var t=this.config.pLoader,e=this.applyPlaylistData,r=t||this.config.loader;return function(){function t(t){this.loader=void 0,this.loader=new r(t)}var i=t.prototype;return i.destroy=function(){this.loader.destroy()},i.abort=function(){this.loader.abort()},i.load=function(t,r,i){e(t),this.loader.load(t,r,i)},s(t,[{key:"stats",get:function(){return this.loader.stats}},{key:"context",get:function(){return this.loader.context}}]),t}()},e.createFragmentLoader=function(){var t=this.config.fLoader,e=this.applyFragmentData,r=t||this.config.loader;return function(){function t(t){this.loader=void 0,this.loader=new r(t)}var i=t.prototype;return i.destroy=function(){this.loader.destroy()},i.abort=function(){this.loader.abort()},i.load=function(t,r,i){e(t),this.loader.load(t,r,i)},s(t,[{key:"stats",get:function(){return this.loader.stats}},{key:"context",get:function(){return this.loader.context}}]),t}()},t}(),Gs=function(){function t(t){this.hls=void 0,this.log=void 0,this.loader=null,this.uri=null,this.pathwayId=".",this.pathwayPriority=null,this.timeToLoad=300,this.reloadTimer=-1,this.updated=0,this.started=!1,this.enabled=!0,this.levels=null,this.audioTracks=null,this.subtitleTracks=null,this.penalizedPathways={},this.hls=t,this.log=w.log.bind(w,"[content-steering]:"),this.registerListeners()}var e=t.prototype;return e.registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(S.MANIFEST_PARSED,this.onManifestParsed,this),t.on(S.ERROR,this.onError,this)},e.unregisterListeners=function(){var t=this.hls;t&&(t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(S.MANIFEST_PARSED,this.onManifestParsed,this),t.off(S.ERROR,this.onError,this))},e.startLoad=function(){if(this.started=!0,this.clearTimeout(),this.enabled&&this.uri){if(this.updated){var t=1e3*this.timeToLoad-(performance.now()-this.updated);if(t>0)return void this.scheduleRefresh(this.uri,t)}this.loadSteeringManifest(this.uri)}},e.stopLoad=function(){this.started=!1,this.loader&&(this.loader.destroy(),this.loader=null),this.clearTimeout()},e.clearTimeout=function(){-1!==this.reloadTimer&&(self.clearTimeout(this.reloadTimer),this.reloadTimer=-1)},e.destroy=function(){this.unregisterListeners(),this.stopLoad(),this.hls=null,this.levels=this.audioTracks=this.subtitleTracks=null},e.removeLevel=function(t){var e=this.levels;e&&(this.levels=e.filter((function(e){return e!==t})))},e.onManifestLoading=function(){this.stopLoad(),this.enabled=!0,this.timeToLoad=300,this.updated=0,this.uri=null,this.pathwayId=".",this.levels=this.audioTracks=this.subtitleTracks=null},e.onManifestLoaded=function(t,e){var r=e.contentSteering;null!==r&&(this.pathwayId=r.pathwayId,this.uri=r.uri,this.started&&this.startLoad())},e.onManifestParsed=function(t,e){this.audioTracks=e.audioTracks,this.subtitleTracks=e.subtitleTracks},e.onError=function(t,e){var r=e.errorAction;if((null==r?void 0:r.action)===Lr&&r.flags===br){var i=this.levels,n=this.pathwayPriority,a=this.pathwayId;if(e.context){var s=e.context,o=s.groupId,l=s.pathwayId,u=s.type;o&&i?a=this.getPathwayForGroupId(o,u,a):l&&(a=l)}a in this.penalizedPathways||(this.penalizedPathways[a]=performance.now()),!n&&i&&(n=i.reduce((function(t,e){return-1===t.indexOf(e.pathwayId)&&t.push(e.pathwayId),t}),[])),n&&n.length>1&&(this.updatePathwayPriority(n),r.resolved=this.pathwayId!==a),r.resolved||w.warn("Could not resolve "+e.details+' ("'+e.error.message+'") with content-steering for Pathway: '+a+" levels: "+(i?i.length:i)+" priorities: "+JSON.stringify(n)+" penalized: "+JSON.stringify(this.penalizedPathways))}},e.filterParsedLevels=function(t){this.levels=t;var e=this.getLevelsForPathway(this.pathwayId);if(0===e.length){var r=t[0].pathwayId;this.log("No levels found in Pathway "+this.pathwayId+'. Setting initial Pathway to "'+r+'"'),e=this.getLevelsForPathway(r),this.pathwayId=r}return e.length!==t.length?(this.log("Found "+e.length+"/"+t.length+' levels in Pathway "'+this.pathwayId+'"'),e):t},e.getLevelsForPathway=function(t){return null===this.levels?[]:this.levels.filter((function(e){return t===e.pathwayId}))},e.updatePathwayPriority=function(t){var e;this.pathwayPriority=t;var r=this.penalizedPathways,i=performance.now();Object.keys(r).forEach((function(t){i-r[t]>3e5&&delete r[t]}));for(var n=0;n0){this.log('Setting Pathway to "'+a+'"'),this.pathwayId=a,dr(e),this.hls.trigger(S.LEVELS_UPDATED,{levels:e});var l=this.hls.levels[s];o&&l&&this.levels&&(l.attrs["STABLE-VARIANT-ID"]!==o.attrs["STABLE-VARIANT-ID"]&&l.bitrate!==o.bitrate&&this.log("Unstable Pathways change from bitrate "+o.bitrate+" to "+l.bitrate),this.hls.nextLoadLevel=s);break}}}},e.getPathwayForGroupId=function(t,e,r){for(var i=this.getLevelsForPathway(r).concat(this.levels||[]),n=0;n=2&&(0===r.loading.first&&(r.loading.first=Math.max(self.performance.now(),r.loading.start),n.timeout!==n.loadPolicy.maxLoadTimeMs&&(self.clearTimeout(this.requestTimeout),n.timeout=n.loadPolicy.maxLoadTimeMs,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),n.loadPolicy.maxLoadTimeMs-(r.loading.first-r.loading.start)))),4===i)){self.clearTimeout(this.requestTimeout),e.onreadystatechange=null,e.onprogress=null;var a=e.status,s="text"!==e.responseType;if(a>=200&&a<300&&(s&&e.response||null!==e.responseText)){r.loading.end=Math.max(self.performance.now(),r.loading.first);var o=s?e.response:e.responseText,l="arraybuffer"===e.responseType?o.byteLength:o.length;if(r.loaded=r.total=l,r.bwEstimate=8e3*r.total/(r.loading.end-r.loading.first),!this.callbacks)return;var u=this.callbacks.onProgress;if(u&&u(r,t,o,e),!this.callbacks)return;var h={url:e.responseURL,data:o,code:a};this.callbacks.onSuccess(h,r,t,e)}else{var d=n.loadPolicy.errorRetry;mr(d,r.retry,!1,{url:t.url,data:void 0,code:a})?this.retry(d):(w.error(a+" while loading "+t.url),this.callbacks.onError({code:a,text:e.statusText},t,e,r))}}}},e.loadtimeout=function(){var t,e=null==(t=this.config)?void 0:t.loadPolicy.timeoutRetry;if(mr(e,this.stats.retry,!0))this.retry(e);else{var r;w.warn("timeout while loading "+(null==(r=this.context)?void 0:r.url));var i=this.callbacks;i&&(this.abortInternal(),i.onTimeout(this.stats,this.context,this.loader))}},e.retry=function(t){var e=this.context,r=this.stats;this.retryDelay=gr(t,r.retry),r.retry++,w.warn((status?"HTTP Status "+status:"Timeout")+" while loading "+(null==e?void 0:e.url)+", retrying "+r.retry+"/"+t.maxNumRetry+" in "+this.retryDelay+"ms"),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay)},e.loadprogress=function(t){var e=this.stats;e.loaded=t.loaded,t.lengthComputable&&(e.total=t.total)},e.getCacheAge=function(){var t=null;if(this.loader&&Vs.test(this.loader.getAllResponseHeaders())){var e=this.loader.getResponseHeader("age");t=e?parseFloat(e):null}return t},e.getResponseHeader=function(t){return this.loader&&new RegExp("^"+t+":\\s*[\\d.]+\\s*$","im").test(this.loader.getAllResponseHeaders())?this.loader.getResponseHeader(t):null},t}(),Ws=/(\d+)-(\d+)\/(\d+)/,js=function(){function t(t){this.fetchSetup=void 0,this.requestTimeout=void 0,this.request=null,this.response=null,this.controller=void 0,this.context=null,this.config=null,this.callbacks=null,this.stats=void 0,this.loader=null,this.fetchSetup=t.fetchSetup||qs,this.controller=new self.AbortController,this.stats=new M}var e=t.prototype;return e.destroy=function(){this.loader=this.callbacks=this.context=this.config=this.request=null,this.abortInternal(),this.response=null,this.fetchSetup=this.controller=this.stats=null},e.abortInternal=function(){this.controller&&!this.stats.loading.end&&(this.stats.aborted=!0,this.controller.abort())},e.abort=function(){var t;this.abortInternal(),null!=(t=this.callbacks)&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.response)},e.load=function(t,e,r){var i=this,n=this.stats;if(n.loading.start)throw new Error("Loader can only be used once.");n.loading.start=self.performance.now();var a=function(t,e){var r={method:"GET",mode:"cors",credentials:"same-origin",signal:e,headers:new self.Headers(o({},t.headers))};return t.rangeEnd&&r.headers.set("Range","bytes="+t.rangeStart+"-"+String(t.rangeEnd-1)),r}(t,this.controller.signal),s=r.onProgress,l="arraybuffer"===t.responseType,u=l?"byteLength":"length",h=e.loadPolicy,d=h.maxTimeToFirstByteMs,c=h.maxLoadTimeMs;this.context=t,this.config=e,this.callbacks=r,this.request=this.fetchSetup(t,a),self.clearTimeout(this.requestTimeout),e.timeout=d&&y(d)?d:c,this.requestTimeout=self.setTimeout((function(){i.abortInternal(),r.onTimeout(n,t,i.response)}),e.timeout),self.fetch(this.request).then((function(a){i.response=i.loader=a;var o=Math.max(self.performance.now(),n.loading.start);if(self.clearTimeout(i.requestTimeout),e.timeout=c,i.requestTimeout=self.setTimeout((function(){i.abortInternal(),r.onTimeout(n,t,i.response)}),c-(o-n.loading.start)),!a.ok){var u=a.status,h=a.statusText;throw new zs(h||"fetch, bad network response",u,a)}return n.loading.first=o,n.total=function(t){var e=t.get("Content-Range");if(e){var r=function(t){var e=Ws.exec(t);if(e)return parseInt(e[2])-parseInt(e[1])+1}(e);if(y(r))return r}var i=t.get("Content-Length");if(i)return parseInt(i)}(a.headers)||n.total,s&&y(e.highWaterMark)?i.loadProgressively(a,n,t,e.highWaterMark,s):l?a.arrayBuffer():"json"===t.responseType?a.json():a.text()})).then((function(a){var o=i.response;if(!o)throw new Error("loader destroyed");self.clearTimeout(i.requestTimeout),n.loading.end=Math.max(self.performance.now(),n.loading.first);var l=a[u];l&&(n.loaded=n.total=l);var h={url:o.url,data:a,code:o.status};s&&!y(e.highWaterMark)&&s(n,t,a,o),r.onSuccess(h,n,t,o)})).catch((function(e){if(self.clearTimeout(i.requestTimeout),!n.aborted){var a=e&&e.code||0,s=e?e.message:null;r.onError({code:a,text:s},t,e?e.details:null,n)}}))},e.getCacheAge=function(){var t=null;if(this.response){var e=this.response.headers.get("age");t=e?parseFloat(e):null}return t},e.getResponseHeader=function(t){return this.response?this.response.headers.get(t):null},e.loadProgressively=function(t,e,r,i,n){void 0===i&&(i=0);var a=new Di,s=t.body.getReader();return function o(){return s.read().then((function(s){if(s.done)return a.dataLength&&n(e,r,a.flush(),t),Promise.resolve(new ArrayBuffer(0));var l=s.value,u=l.length;return e.loaded+=u,u=i&&n(e,r,a.flush(),t)):n(e,r,l,t),o()})).catch((function(){return Promise.reject()}))}()},t}();function qs(t,e){return new self.Request(t.url,e)}var Xs,zs=function(t){function e(e,r,i){var n;return(n=t.call(this,e)||this).code=void 0,n.details=void 0,n.code=r,n.details=i,n}return l(e,t),e}(c(Error)),Qs=/\s/,Js=i(i({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,preferManagedMediaSource:!0,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,frontBufferFlushThreshold:1/0,maxBufferSize:6e7,maxBufferHole:.1,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,maxFragLookUpTolerance:.25,liveSyncDurationCount:3,liveMaxLatencyDurationCount:1/0,liveSyncDuration:void 0,liveMaxLatencyDuration:void 0,maxLiveSyncPlaybackRate:1,liveDurationInfinity:!1,liveBackBufferLength:null,maxMaxBufferLength:600,enableWorker:!0,workerPath:null,enableSoftwareAES:!0,startLevel:void 0,startFragPrefetch:!1,fpsDroppedMonitoringPeriod:5e3,fpsDroppedMonitoringThreshold:.2,appendErrorMaxRetry:3,loader:Ys,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,abrController:Kr,bufferController:$n,capLevelController:za,errorController:Ir,fpsController:Qa,stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrEwmaDefaultEstimateMax:5e6,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystems:{},drmSystemOptions:{},requestMediaKeySystemAccessFunc:it,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableID3MetadataCues:!0,useMediaCapabilities:!0,certLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:null,errorRetry:null}},keyLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"},errorRetry:{maxNumRetry:8,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"}}},manifestLoadPolicy:{default:{maxTimeToFirstByteMs:1/0,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},playlistLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:2,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},fragLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:12e4,timeoutRetry:{maxNumRetry:4,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:6,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},steeringManifestLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3},{cueHandler:{newCue:function(t,e,r,i){for(var n,a,s,o,l,u=[],h=self.VTTCue||self.TextTrackCue,d=0;d=16?o--:o++;var g=Ia(l.trim()),v=Pa(e,r,g);null!=t&&null!=(c=t.cues)&&c.getCueById(v)||((a=new h(e,r,g)).id=v,a.line=d+1,a.align="left",a.position=10+Math.min(80,10*Math.floor(8*o/32)),u.push(a))}return t&&u.length&&(u.sort((function(t,e){return"auto"===t.line||"auto"===e.line?0:t.line>8&&e.line>8?e.line-t.line:t.line-e.line})),u.forEach((function(e){return Oe(t,e)}))),u}},enableWebVTT:!0,enableIMSC1:!0,enableCEA708Captions:!0,captionsTextTrack1Label:"English",captionsTextTrack1LanguageCode:"en",captionsTextTrack2Label:"Spanish",captionsTextTrack2LanguageCode:"es",captionsTextTrack3Label:"Unknown CC",captionsTextTrack3LanguageCode:"",captionsTextTrack4Label:"Unknown CC",captionsTextTrack4LanguageCode:"",renderTextTracksNatively:!0}),{},{subtitleStreamController:qn,subtitleTrackController:zn,timelineController:ja,audioStreamController:Wn,audioTrackController:jn,emeController:$a,cmcdController:Bs,contentSteeringController:Gs});function $s(t){return t&&"object"==typeof t?Array.isArray(t)?t.map($s):Object.keys(t).reduce((function(e,r){return e[r]=$s(t[r]),e}),{}):t}function Zs(t){var e=t.loader;e!==js&&e!==Ys?(w.log("[config]: Custom loader detected, cannot enable progressive streaming"),t.progressive=!1):function(){if(self.fetch&&self.AbortController&&self.ReadableStream&&self.Request)try{return new self.ReadableStream({}),!0}catch(t){}return!1}()&&(t.loader=js,t.progressive=!0,t.enableSoftwareAES=!0,w.log("[config]: Progressive streaming enabled, using FetchLoader"))}var to=function(t){function e(e,r){var i;return(i=t.call(this,e,"[level-controller]")||this)._levels=[],i._firstLevel=-1,i._maxAutoLevel=-1,i._startLevel=void 0,i.currentLevel=null,i.currentLevelIndex=-1,i.manualLevelIndex=-1,i.steering=void 0,i.onParsedComplete=void 0,i.steering=r,i._registerListeners(),i}l(e,t);var r=e.prototype;return r._registerListeners=function(){var t=this.hls;t.on(S.MANIFEST_LOADING,this.onManifestLoading,this),t.on(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(S.LEVEL_LOADED,this.onLevelLoaded,this),t.on(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(S.FRAG_BUFFERED,this.onFragBuffered,this),t.on(S.ERROR,this.onError,this)},r._unregisterListeners=function(){var t=this.hls;t.off(S.MANIFEST_LOADING,this.onManifestLoading,this),t.off(S.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(S.LEVEL_LOADED,this.onLevelLoaded,this),t.off(S.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(S.FRAG_BUFFERED,this.onFragBuffered,this),t.off(S.ERROR,this.onError,this)},r.destroy=function(){this._unregisterListeners(),this.steering=null,this.resetLevels(),t.prototype.destroy.call(this)},r.stopLoad=function(){this._levels.forEach((function(t){t.loadError=0,t.fragmentError=0})),t.prototype.stopLoad.call(this)},r.resetLevels=function(){this._startLevel=void 0,this.manualLevelIndex=-1,this.currentLevelIndex=-1,this.currentLevel=null,this._levels=[],this._maxAutoLevel=-1},r.onManifestLoading=function(t,e){this.resetLevels()},r.onManifestLoaded=function(t,e){var r=this.hls.config.preferManagedMediaSource,i=[],n={},a={},s=!1,o=!1,l=!1;e.levels.forEach((function(t){var e,u,h=t.attrs,d=t.audioCodec,c=t.videoCodec;-1!==(null==(e=d)?void 0:e.indexOf("mp4a.40.34"))&&(Xs||(Xs=/chrome|firefox/i.test(navigator.userAgent)),Xs&&(t.audioCodec=d=void 0)),d&&(t.audioCodec=d=he(d,r)),0===(null==(u=c)?void 0:u.indexOf("avc1"))&&(c=t.videoCodec=function(t){var e=t.split(".");if(e.length>2){var r=e.shift()+".";return(r+=parseInt(e.shift()).toString(16))+("000"+parseInt(e.shift()).toString(16)).slice(-4)}return t}(c));var f=t.width,g=t.height,v=t.unknownCodecs;if(s||(s=!(!f||!g)),o||(o=!!c),l||(l=!!d),!(null!=v&&v.length||d&&!ie(d,"audio",r)||c&&!ie(c,"video",r))){var m=h.CODECS,p=h["FRAME-RATE"],y=h["HDCP-LEVEL"],E=h["PATHWAY-ID"],T=h.RESOLUTION,S=h["VIDEO-RANGE"],L=(E||".")+"-"+t.bitrate+"-"+T+"-"+p+"-"+m+"-"+S+"-"+y;if(n[L])if(n[L].uri===t.url||t.attrs["PATHWAY-ID"])n[L].addGroupId("audio",h.AUDIO),n[L].addGroupId("text",h.SUBTITLES);else{var A=a[L]+=1;t.attrs["PATHWAY-ID"]=new Array(A+1).join(".");var R=new rr(t);n[L]=R,i.push(R)}else{var k=new rr(t);n[L]=k,a[L]=1,i.push(k)}}})),this.filterAndSortMediaOptions(i,e,s,o,l)},r.filterAndSortMediaOptions=function(t,e,r,i,n){var a=this,s=[],o=[],l=t;if((r||i)&&n&&(l=l.filter((function(t){var e,r=t.videoCodec,i=t.videoRange,n=t.width,a=t.height;return(!!r||!(!n||!a))&&!!(e=i)&&Qe.indexOf(e)>-1}))),0!==l.length){if(e.audioTracks){var u=this.hls.config.preferManagedMediaSource;eo(s=e.audioTracks.filter((function(t){return!t.audioCodec||ie(t.audioCodec,"audio",u)})))}e.subtitles&&eo(o=e.subtitles);var h=l.slice(0);l.sort((function(t,e){if(t.attrs["HDCP-LEVEL"]!==e.attrs["HDCP-LEVEL"])return(t.attrs["HDCP-LEVEL"]||"")>(e.attrs["HDCP-LEVEL"]||"")?1:-1;if(r&&t.height!==e.height)return t.height-e.height;if(t.frameRate!==e.frameRate)return t.frameRate-e.frameRate;if(t.videoRange!==e.videoRange)return Qe.indexOf(t.videoRange)-Qe.indexOf(e.videoRange);if(t.videoCodec!==e.videoCodec){var i=se(t.videoCodec),n=se(e.videoCodec);if(i!==n)return n-i}if(t.uri===e.uri&&t.codecSet!==e.codecSet){var a=oe(t.codecSet),s=oe(e.codecSet);if(a!==s)return s-a}return t.averageBitrate!==e.averageBitrate?t.averageBitrate-e.averageBitrate:0}));var d=h[0];if(this.steering&&(l=this.steering.filterParsedLevels(l)).length!==h.length)for(var c=0;cm&&m===Js.abrEwmaDefaultEstimate&&(this.hls.bandwidthEstimate=p)}break}var y=n&&!i,E={levels:l,audioTracks:s,subtitleTracks:o,sessionData:e.sessionData,sessionKeys:e.sessionKeys,firstLevel:this._firstLevel,stats:e.stats,audio:n,video:i,altAudio:!y&&s.some((function(t){return!!t.url}))};this.hls.trigger(S.MANIFEST_PARSED,E),(this.hls.config.autoStartLoad||this.hls.forceStartLoad)&&this.hls.startLoad(this.hls.config.startPosition)}else Promise.resolve().then((function(){if(a.hls){e.levels.length&&a.warn("One or more CODECS in variant not supported: "+JSON.stringify(e.levels[0].attrs));var t=new Error("no level with compatible codecs found in manifest");a.hls.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:e.url,error:t,reason:t.message})}}))},r.onError=function(t,e){!e.fatal&&e.context&&e.context.type===be&&e.context.level===this.level&&this.checkRetry(e)},r.onFragBuffered=function(t,e){var r=e.frag;if(void 0!==r&&r.type===we){var i=r.elementaryStreams;if(!Object.keys(i).some((function(t){return!!i[t]})))return;var n=this._levels[r.level];null!=n&&n.loadError&&(this.log("Resetting level error count of "+n.loadError+" on frag buffered"),n.loadError=0)}},r.onLevelLoaded=function(t,e){var r,i,n=e.level,a=e.details,s=this._levels[n];if(!s)return this.warn("Invalid level index "+n),void(null!=(i=e.deliveryDirectives)&&i.skip&&(a.deltaUpdateFailed=!0));n===this.currentLevelIndex?(0===s.fragmentError&&(s.loadError=0),this.playlistLoaded(n,e,s.details)):null!=(r=e.deliveryDirectives)&&r.skip&&(a.deltaUpdateFailed=!0)},r.loadPlaylist=function(e){t.prototype.loadPlaylist.call(this);var r=this.currentLevelIndex,i=this.currentLevel;if(i&&this.shouldLoadPlaylist(i)){var n=i.uri;if(e)try{n=e.addDirectives(n)}catch(t){this.warn("Could not construct new URL with HLS Delivery Directives: "+t)}var a=i.attrs["PATHWAY-ID"];this.log("Loading level index "+r+(void 0!==(null==e?void 0:e.msn)?" at sn "+e.msn+" part "+e.part:"")+" with"+(a?" Pathway "+a:"")+" "+n),this.clearTimer(),this.hls.trigger(S.LEVEL_LOADING,{url:n,level:r,pathwayId:i.attrs["PATHWAY-ID"],id:0,deliveryDirectives:e||null})}},r.removeLevel=function(t){var e,r=this,i=this._levels.filter((function(e,i){return i!==t||(r.steering&&r.steering.removeLevel(e),e===r.currentLevel&&(r.currentLevel=null,r.currentLevelIndex=-1,e.details&&e.details.fragments.forEach((function(t){return t.level=-1}))),!1)}));dr(i),this._levels=i,this.currentLevelIndex>-1&&null!=(e=this.currentLevel)&&e.details&&(this.currentLevelIndex=this.currentLevel.details.fragments[0].level),this.hls.trigger(S.LEVELS_UPDATED,{levels:i})},r.onLevelsUpdated=function(t,e){var r=e.levels;this._levels=r},r.checkMaxAutoUpdated=function(){var t=this.hls,e=t.autoLevelCapping,r=t.maxAutoLevel,i=t.maxHdcpLevel;this._maxAutoLevel!==r&&(this._maxAutoLevel=r,this.hls.trigger(S.MAX_AUTO_LEVEL_UPDATED,{autoLevelCapping:e,levels:this.levels,maxAutoLevel:r,minAutoLevel:this.hls.minAutoLevel,maxHdcpLevel:i}))},s(e,[{key:"levels",get:function(){return 0===this._levels.length?null:this._levels}},{key:"level",get:function(){return this.currentLevelIndex},set:function(t){var e=this._levels;if(0!==e.length){if(t<0||t>=e.length){var r=new Error("invalid level idx"),i=t<0;if(this.hls.trigger(S.ERROR,{type:L.OTHER_ERROR,details:A.LEVEL_SWITCH_ERROR,level:t,fatal:i,error:r,reason:r.message}),i)return;t=Math.min(t,e.length-1)}var n=this.currentLevelIndex,a=this.currentLevel,s=a?a.attrs["PATHWAY-ID"]:void 0,o=e[t],l=o.attrs["PATHWAY-ID"];if(this.currentLevelIndex=t,this.currentLevel=o,n!==t||!o.details||!a||s!==l){this.log("Switching to level "+t+" ("+(o.height?o.height+"p ":"")+(o.videoRange?o.videoRange+" ":"")+(o.codecSet?o.codecSet+" ":"")+"@"+o.bitrate+")"+(l?" with Pathway "+l:"")+" from level "+n+(s?" with Pathway "+s:""));var u={level:t,attrs:o.attrs,details:o.details,bitrate:o.bitrate,averageBitrate:o.averageBitrate,maxBitrate:o.maxBitrate,realBitrate:o.realBitrate,width:o.width,height:o.height,codecSet:o.codecSet,audioCodec:o.audioCodec,videoCodec:o.videoCodec,audioGroups:o.audioGroups,subtitleGroups:o.subtitleGroups,loaded:o.loaded,loadError:o.loadError,fragmentError:o.fragmentError,name:o.name,id:o.id,uri:o.uri,url:o.url,urlId:0,audioGroupIds:o.audioGroupIds,textGroupIds:o.textGroupIds};this.hls.trigger(S.LEVEL_SWITCHING,u);var h=o.details;if(!h||h.live){var d=this.switchParams(o.uri,null==a?void 0:a.details,h);this.loadPlaylist(d)}}}}},{key:"manualLevel",get:function(){return this.manualLevelIndex},set:function(t){this.manualLevelIndex=t,void 0===this._startLevel&&(this._startLevel=t),-1!==t&&(this.level=t)}},{key:"firstLevel",get:function(){return this._firstLevel},set:function(t){this._firstLevel=t}},{key:"startLevel",get:function(){if(void 0===this._startLevel){var t=this.hls.config.startLevel;return void 0!==t?t:this.hls.firstAutoLevel}return this._startLevel},set:function(t){this._startLevel=t}},{key:"nextLoadLevel",get:function(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel},set:function(t){this.level=t,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=t)}}]),e}(wr);function eo(t){var e={};t.forEach((function(t){var r=t.groupId||"";t.id=e[r]=e[r]||0,e[r]++}))}var ro=function(){function t(t){this.config=void 0,this.keyUriToKeyInfo={},this.emeController=null,this.config=t}var e=t.prototype;return e.abort=function(t){for(var e in this.keyUriToKeyInfo){var r=this.keyUriToKeyInfo[e].loader;if(r){var i;if(t&&t!==(null==(i=r.context)?void 0:i.frag.type))return;r.abort()}}},e.detach=function(){for(var t in this.keyUriToKeyInfo){var e=this.keyUriToKeyInfo[t];(e.mediaKeySessionContext||e.decryptdata.isCommonEncryption)&&delete this.keyUriToKeyInfo[t]}},e.destroy=function(){for(var t in this.detach(),this.keyUriToKeyInfo){var e=this.keyUriToKeyInfo[t].loader;e&&e.destroy()}this.keyUriToKeyInfo={}},e.createKeyLoadError=function(t,e,r,i,n){return void 0===e&&(e=A.KEY_LOAD_ERROR),new li({type:L.NETWORK_ERROR,details:e,fatal:!1,frag:t,response:n,error:r,networkDetails:i})},e.loadClear=function(t,e){var r=this;if(this.emeController&&this.config.emeEnabled)for(var i=t.sn,n=t.cc,a=function(){var t=e[s];if(n<=t.cc&&("initSegment"===i||"initSegment"===t.sn||i2,c=!h||e&&e.start<=a||h-a>2&&!this.fragmentTracker.getPartialFragment(a);if(d||c)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var f;if(!(u.len>0||h))return;var g=Math.max(h,u.start||0)-a,v=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,m=(null==v||null==(f=v.details)?void 0:f.live)?2*v.details.targetduration:2,p=this.fragmentTracker.getPartialFragment(a);if(g>0&&(g<=m||p))return void(i.paused||this._trySkipBufferHole(p))}var y=self.performance.now();if(null!==n){var E=y-n;if(s||!(E>=250)||(this._reportStall(u),this.media)){var T=Jr.bufferInfo(i,a,r.maxBufferHole);this._tryFixBufferStall(T,E)}}else this.stalled=y}else if(this.moved=!0,s||(this.nudgeRetry=0),null!==n){if(this.stallReported){var S=self.performance.now()-n;w.warn("playback not stuck anymore @"+a+", after "+Math.round(S)+"ms"),this.stallReported=!1}this.stalled=null}}},e._tryFixBufferStall=function(t,e){var r=this.config,i=this.fragmentTracker,n=this.media;if(null!==n){var a=n.currentTime,s=i.getPartialFragment(a);if(s&&(this._trySkipBufferHole(s)||!this.media))return;(t.len>r.maxBufferHole||t.nextStart&&t.nextStart-a1e3*r.highBufferWatchdogPeriod&&(w.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())}},e._reportStall=function(t){var e=this.hls,r=this.media;if(!this.stallReported&&r){this.stallReported=!0;var i=new Error("Playback stalling at @"+r.currentTime+" due to low buffer ("+JSON.stringify(t)+")");w.warn(i.message),e.trigger(S.ERROR,{type:L.MEDIA_ERROR,details:A.BUFFER_STALLED_ERROR,fatal:!1,error:i,buffer:t.len})}},e._trySkipBufferHole=function(t){var e=this.config,r=this.hls,i=this.media;if(null===i)return 0;var n=i.currentTime,a=Jr.bufferInfo(i,n,0),s=n0&&a.len<1&&i.readyState<3,u=s-n;if(u>0&&(o||l)){if(u>e.maxBufferHole){var h=this.fragmentTracker,d=!1;if(0===n){var c=h.getAppendedFrag(0,we);c&&s1?(i=0,this.bitrateTest=!0):i=r.firstAutoLevel),r.nextLoadLevel=i,this.level=r.loadLevel,this.loadedmetadata=!1}e>0&&-1===t&&(this.log("Override startPosition with lastCurrentTime @"+e.toFixed(3)),t=e),this.state=vi,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}else this._forceStartLoad=!0,this.state=gi},r.stopLoad=function(){this._forceStartLoad=!1,t.prototype.stopLoad.call(this)},r.doTick=function(){switch(this.state){case ki:var t=this.levels,e=this.level,r=null==t?void 0:t[e],i=null==r?void 0:r.details;if(i&&(!i.live||this.levelLastLoaded===r)){if(this.waitForCdnTuneIn(i))break;this.state=vi;break}if(this.hls.nextLoadLevel!==this.level){this.state=vi;break}break;case yi:var n,a=self.performance.now(),s=this.retryDate;if(!s||a>=s||null!=(n=this.media)&&n.seeking){var o=this.levels,l=this.level,u=null==o?void 0:o[l];this.resetStartWhenNotLoaded(u||null),this.state=vi}}this.state===vi&&this.doTickIdle(),this.onTickEnd()},r.onTickEnd=function(){t.prototype.onTickEnd.call(this),this.checkBuffer(),this.checkFragmentChanged()},r.doTickIdle=function(){var t=this.hls,e=this.levelLastLoaded,r=this.levels,i=this.media;if(null!==e&&(i||!this.startFragRequested&&t.config.startFragPrefetch)&&(!this.altAudio||!this.audioOnly)){var n=t.nextLoadLevel;if(null!=r&&r[n]){var a=r[n],s=this.getMainFwdBufferInfo();if(null!==s){var o=this.getLevelDetails();if(o&&this._streamEnded(s,o)){var l={};return this.altAudio&&(l.type="video"),this.hls.trigger(S.BUFFER_EOS,l),void(this.state=Li)}t.loadLevel!==n&&-1===t.manualLevel&&this.log("Adapting to level "+n+" from level "+this.level),this.level=t.nextLoadLevel=n;var u=a.details;if(!u||this.state===ki||u.live&&this.levelLastLoaded!==a)return this.level=n,void(this.state=ki);var h=s.len,d=this.getMaxBufferLength(a.maxBitrate);if(!(h>=d)){this.backtrackFragment&&this.backtrackFragment.start>s.end&&(this.backtrackFragment=null);var c=this.backtrackFragment?this.backtrackFragment.start:s.end,f=this.getNextFragment(c,u);if(this.couldBacktrack&&!this.fragPrevious&&f&&"initSegment"!==f.sn&&this.fragmentTracker.getState(f)!==jr){var g,v=(null!=(g=this.backtrackFragment)?g:f).sn-u.startSN,m=u.fragments[v-1];m&&f.cc===m.cc&&(f=m,this.fragmentTracker.removeFragment(m))}else this.backtrackFragment&&s.len&&(this.backtrackFragment=null);if(f&&this.isLoopLoading(f,c)){if(!f.gap){var p=this.audioOnly&&!this.altAudio?O:N,y=(p===N?this.videoBuffer:this.mediaBuffer)||this.media;y&&this.afterBufferFlushed(y,p,we)}f=this.getNextFragmentLoopLoading(f,u,s,we,d)}f&&(!f.initSegment||f.initSegment.data||this.bitrateTest||(f=f.initSegment),this.loadFragment(f,a,c))}}}}},r.loadFragment=function(e,r,i){var n=this.fragmentTracker.getState(e);this.fragCurrent=e,n===Vr||n===Wr?"initSegment"===e.sn?this._loadInitSegment(e,r):this.bitrateTest?(this.log("Fragment "+e.sn+" of level "+e.level+" is being downloaded to test bitrate and will not be buffered"),this._loadBitrateTestFrag(e,r)):(this.startFragRequested=!0,t.prototype.loadFragment.call(this,e,r,i)):this.clearTrackerIfNeeded(e)},r.getBufferedFrag=function(t){return this.fragmentTracker.getBufferedFrag(t,we)},r.followingBufferedFrag=function(t){return t?this.getBufferedFrag(t.end+.5):null},r.immediateLevelSwitch=function(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)},r.nextLevelSwitch=function(){var t=this.levels,e=this.media;if(null!=e&&e.readyState){var r,i=this.getAppendedFrag(e.currentTime);i&&i.start>1&&this.flushMainBuffer(0,i.start-1);var n=this.getLevelDetails();if(null!=n&&n.live){var a=this.getMainFwdBufferInfo();if(!a||a.len<2*n.targetduration)return}if(!e.paused&&t){var s=t[this.hls.nextLoadLevel],o=this.fragLastKbps;r=o&&this.fragCurrent?this.fragCurrent.duration*s.maxBitrate/(1e3*o)+1:0}else r=0;var l=this.getBufferedFrag(e.currentTime+r);if(l){var u=this.followingBufferedFrag(l);if(u){this.abortCurrentFrag();var h=u.maxStartPTS?u.maxStartPTS:u.start,d=u.duration,c=Math.max(l.end,h+Math.min(Math.max(d-this.config.maxFragLookUpTolerance,d*(this.couldBacktrack?.5:.125)),d*(this.couldBacktrack?.75:.25)));this.flushMainBuffer(c,Number.POSITIVE_INFINITY)}}}},r.abortCurrentFrag=function(){var t=this.fragCurrent;switch(this.fragCurrent=null,this.backtrackFragment=null,t&&(t.abortRequests(),this.fragmentTracker.removeFragment(t)),this.state){case mi:case pi:case yi:case Ti:case Si:this.state=vi}this.nextLoadPosition=this.getLoadPosition()},r.flushMainBuffer=function(e,r){t.prototype.flushMainBuffer.call(this,e,r,this.altAudio?"video":null)},r.onMediaAttached=function(e,r){t.prototype.onMediaAttached.call(this,e,r);var i=r.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),i.addEventListener("playing",this.onvplaying),i.addEventListener("seeked",this.onvseeked),this.gapController=new ao(this.config,i,this.fragmentTracker,this.hls)},r.onMediaDetaching=function(){var e=this.media;e&&this.onvplaying&&this.onvseeked&&(e.removeEventListener("playing",this.onvplaying),e.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),t.prototype.onMediaDetaching.call(this)},r.onMediaPlaying=function(){this.tick()},r.onMediaSeeked=function(){var t=this.media,e=t?t.currentTime:null;y(e)&&this.log("Media seeked to "+e.toFixed(3));var r=this.getMainFwdBufferInfo();null!==r&&0!==r.len?this.tick():this.warn('Main forward buffer length on "seeked" event '+(r?r.len:"empty")+")")},r.onManifestLoading=function(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(S.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=!1,this.startPosition=this.lastCurrentTime=this.fragLastKbps=0,this.levels=this.fragPlaying=this.backtrackFragment=this.levelLastLoaded=null,this.altAudio=this.audioOnly=this.startFragRequested=!1},r.onManifestParsed=function(t,e){var r,i,n=!1,a=!1;e.levels.forEach((function(t){var e=t.audioCodec;e&&(n=n||-1!==e.indexOf("mp4a.40.2"),a=a||-1!==e.indexOf("mp4a.40.5"))})),this.audioCodecSwitch=n&&a&&!("function"==typeof(null==(i=io())||null==(r=i.prototype)?void 0:r.changeType)),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=e.levels,this.startFragRequested=!1},r.onLevelLoading=function(t,e){var r=this.levels;if(r&&this.state===vi){var i=r[e.level];(!i.details||i.details.live&&this.levelLastLoaded!==i||this.waitForCdnTuneIn(i.details))&&(this.state=ki)}},r.onLevelLoaded=function(t,e){var r,i=this.levels,n=e.level,a=e.details,s=a.totalduration;if(i){this.log("Level "+n+" loaded ["+a.startSN+","+a.endSN+"]"+(a.lastPartSn?"[part-"+a.lastPartSn+"-"+a.lastPartIndex+"]":"")+", cc ["+a.startCC+", "+a.endCC+"] duration:"+s);var o=i[n],l=this.fragCurrent;!l||this.state!==pi&&this.state!==yi||l.level!==e.level&&l.loader&&this.abortCurrentFrag();var u=0;if(a.live||null!=(r=o.details)&&r.live){var h;if(this.checkLiveUpdate(a),a.deltaUpdateFailed)return;u=this.alignPlaylists(a,o.details,null==(h=this.levelLastLoaded)?void 0:h.details)}if(o.details=a,this.levelLastLoaded=o,this.hls.trigger(S.LEVEL_UPDATED,{details:a,level:n}),this.state===ki){if(this.waitForCdnTuneIn(a))return;this.state=vi}this.startFragRequested?a.live&&this.synchronizeToLiveEdge(a):this.setStartPosition(a,u),this.tick()}else this.warn("Levels were reset while loading level "+n)},r._handleFragmentLoadProgress=function(t){var e,r=t.frag,i=t.part,n=t.payload,a=this.levels;if(a){var s=a[r.level],o=s.details;if(!o)return this.warn("Dropping fragment "+r.sn+" of level "+r.level+" after level details were reset"),void this.fragmentTracker.removeFragment(r);var l=s.videoCodec,u=o.PTSKnown||!o.live,h=null==(e=r.initSegment)?void 0:e.data,d=this._getAudioCodec(s),c=this.transmuxer=this.transmuxer||new Kn(this.hls,we,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),f=i?i.index:-1,g=-1!==f,v=new $r(r.level,r.sn,r.stats.chunkCount,n.byteLength,f,g),m=this.initPTS[r.cc];c.push(n,h,d,l,r,i,o.totalduration,u,v,m)}else this.warn("Levels were reset while fragment load was in progress. Fragment "+r.sn+" of level "+r.level+" will not be buffered")},r.onAudioTrackSwitching=function(t,e){var r=this.altAudio;if(!e.url){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;var i=this.fragCurrent;i&&(this.log("Switching to main audio track, cancel main fragment load"),i.abortRequests(),this.fragmentTracker.removeFragment(i)),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();var n=this.hls;r&&(n.trigger(S.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:null}),this.fragmentTracker.removeAllFragments()),n.trigger(S.AUDIO_TRACK_SWITCHED,e)}},r.onAudioTrackSwitched=function(t,e){var r=e.id,i=!!this.hls.audioTracks[r].url;if(i){var n=this.videoBuffer;n&&this.mediaBuffer!==n&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=n)}this.altAudio=i,this.tick()},r.onBufferCreated=function(t,e){var r,i,n=e.tracks,a=!1;for(var s in n){var o=n[s];if("main"===o.id){if(i=s,r=o,"video"===s){var l=n[s];l&&(this.videoBuffer=l.buffer)}}else a=!0}a&&r?(this.log("Alternate track found, use "+i+".buffered to schedule main fragment loading"),this.mediaBuffer=r.buffer):this.mediaBuffer=this.media},r.onFragBuffered=function(t,e){var r=e.frag,i=e.part;if(!r||r.type===we){if(this.fragContextChanged(r))return this.warn("Fragment "+r.sn+(i?" p: "+i.index:"")+" of level "+r.level+" finished buffering, but was aborted. state: "+this.state),void(this.state===Si&&(this.state=vi));var n=i?i.stats:r.stats;this.fragLastKbps=Math.round(8*n.total/(n.buffering.end-n.loading.first)),"initSegment"!==r.sn&&(this.fragPrevious=r),this.fragBufferedComplete(r,i)}},r.onError=function(t,e){var r;if(e.fatal)this.state=Ai;else switch(e.details){case A.FRAG_GAP:case A.FRAG_PARSING_ERROR:case A.FRAG_DECRYPT_ERROR:case A.FRAG_LOAD_ERROR:case A.FRAG_LOAD_TIMEOUT:case A.KEY_LOAD_ERROR:case A.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(we,e);break;case A.LEVEL_LOAD_ERROR:case A.LEVEL_LOAD_TIMEOUT:case A.LEVEL_PARSING_ERROR:e.levelRetry||this.state!==ki||(null==(r=e.context)?void 0:r.type)!==be||(this.state=vi);break;case A.BUFFER_APPEND_ERROR:case A.BUFFER_FULL_ERROR:if(!e.parent||"main"!==e.parent)return;if(e.details===A.BUFFER_APPEND_ERROR)return void this.resetLoadingState();this.reduceLengthAndFlushBuffer(e)&&this.flushMainBuffer(0,Number.POSITIVE_INFINITY);break;case A.INTERNAL_EXCEPTION:this.recoverWorkerError(e)}},r.checkBuffer=function(){var t=this.media,e=this.gapController;if(t&&e&&t.readyState){if(this.loadedmetadata||!Jr.getBuffered(t).length){var r=this.state!==vi?this.fragCurrent:null;e.poll(this.lastCurrentTime,r)}this.lastCurrentTime=t.currentTime}},r.onFragLoadEmergencyAborted=function(){this.state=vi,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()},r.onBufferFlushed=function(t,e){var r=e.type;if(r!==O||this.audioOnly&&!this.altAudio){var i=(r===N?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(i,r,we),this.tick()}},r.onLevelsUpdated=function(t,e){this.level>-1&&this.fragCurrent&&(this.level=this.fragCurrent.level),this.levels=e.levels},r.swapAudioCodec=function(){this.audioCodecSwap=!this.audioCodecSwap},r.seekToStartPos=function(){var t=this.media;if(t){var e=t.currentTime,r=this.startPosition;if(r>=0&&e0&&(nT.cc;if(!1!==n.independent){var R=h.startPTS,k=h.endPTS,b=h.startDTS,D=h.endDTS;if(l)l.elementaryStreams[h.type]={startPTS:R,endPTS:k,startDTS:b,endDTS:D};else if(h.firstKeyFrame&&h.independent&&1===a.id&&!A&&(this.couldBacktrack=!0),h.dropped&&h.independent){var I=this.getMainFwdBufferInfo(),w=(I?I.end:this.getLoadPosition())+this.config.maxBufferHole,C=h.firstKeyFramePTS?h.firstKeyFramePTS:R;if(!L&&w2&&(o.gap=!0);o.setElementaryStreamInfo(h.type,R,k,b,D),this.backtrackFragment&&(this.backtrackFragment=o),this.bufferFragmentData(h,o,l,a,L||A)}else{if(!L&&!A)return void this.backtrack(o);o.gap=!0}}if(v){var _=v.startPTS,x=v.endPTS,P=v.startDTS,F=v.endDTS;l&&(l.elementaryStreams[O]={startPTS:_,endPTS:x,startDTS:P,endDTS:F}),o.setElementaryStreamInfo(O,_,x,P,F),this.bufferFragmentData(v,o,l,a)}if(g&&null!=c&&null!=(e=c.samples)&&e.length){var M={id:r,frag:o,details:g,samples:c.samples};i.trigger(S.FRAG_PARSING_METADATA,M)}if(g&&d){var N={id:r,frag:o,details:g,samples:d.samples};i.trigger(S.FRAG_PARSING_USERDATA,N)}}}else this.resetWhenMissingContext(a)},r._bufferInitSegment=function(t,e,r,i){var n=this;if(this.state===Ti){this.audioOnly=!!e.audio&&!e.video,this.altAudio&&!this.audioOnly&&delete e.audio;var a=e.audio,s=e.video,o=e.audiovideo;if(a){var l=t.audioCodec,u=navigator.userAgent.toLowerCase();this.audioCodecSwitch&&(l&&(l=-1!==l.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5"),1!==a.metadata.channelCount&&-1===u.indexOf("firefox")&&(l="mp4a.40.5")),l&&-1!==l.indexOf("mp4a.40.5")&&-1!==u.indexOf("android")&&"audio/mpeg"!==a.container&&(l="mp4a.40.2",this.log("Android: force audio codec to "+l)),t.audioCodec&&t.audioCodec!==l&&this.log('Swapping manifest audio codec "'+t.audioCodec+'" for "'+l+'"'),a.levelCodec=l,a.id="main",this.log("Init audio buffer, container:"+a.container+", codecs[selected/level/parsed]=["+(l||"")+"/"+(t.audioCodec||"")+"/"+a.codec+"]")}s&&(s.levelCodec=t.videoCodec,s.id="main",this.log("Init video buffer, container:"+s.container+", codecs[level/parsed]=["+(t.videoCodec||"")+"/"+s.codec+"]")),o&&this.log("Init audiovideo buffer, container:"+o.container+", codecs[level/parsed]=["+t.codecs+"/"+o.codec+"]"),this.hls.trigger(S.BUFFER_CODECS,e),Object.keys(e).forEach((function(t){var a=e[t].initSegment;null!=a&&a.byteLength&&n.hls.trigger(S.BUFFER_APPENDING,{type:t,data:a,frag:r,part:null,chunkMeta:i,parent:r.type})})),this.tickImmediate()}},r.getMainFwdBufferInfo=function(){return this.getFwdBufferInfo(this.mediaBuffer?this.mediaBuffer:this.media,we)},r.backtrack=function(t){this.couldBacktrack=!0,this.backtrackFragment=t,this.resetTransmuxer(),this.flushBufferGap(t),this.fragmentTracker.removeFragment(t),this.fragPrevious=null,this.nextLoadPosition=t.start,this.state=vi},r.checkFragmentChanged=function(){var t=this.media,e=null;if(t&&t.readyState>1&&!1===t.seeking){var r=t.currentTime;if(Jr.isBuffered(t,r)?e=this.getAppendedFrag(r):Jr.isBuffered(t,r+.1)&&(e=this.getAppendedFrag(r+.1)),e){this.backtrackFragment=null;var i=this.fragPlaying,n=e.level;i&&e.sn===i.sn&&i.level===n||(this.fragPlaying=e,this.hls.trigger(S.FRAG_CHANGED,{frag:e}),i&&i.level===n||this.hls.trigger(S.LEVEL_SWITCHED,{level:n}))}}},s(e,[{key:"nextLevel",get:function(){var t=this.nextBufferedFrag;return t?t.level:-1}},{key:"currentFrag",get:function(){var t=this.media;return t?this.fragPlaying||this.getAppendedFrag(t.currentTime):null}},{key:"currentProgramDateTime",get:function(){var t=this.media;if(t){var e=t.currentTime,r=this.currentFrag;if(r&&y(e)&&y(r.programDateTime)){var i=r.programDateTime+1e3*(e-r.start);return new Date(i)}}return null}},{key:"currentLevel",get:function(){var t=this.currentFrag;return t?t.level:-1}},{key:"nextBufferedFrag",get:function(){var t=this.currentFrag;return t?this.followingBufferedFrag(t):null}},{key:"forceStartLoad",get:function(){return this._forceStartLoad}}]),e}(bi),oo=function(){function t(e){void 0===e&&(e={}),this.config=void 0,this.userConfig=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this.started=!1,this._emitter=new Nn,this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.abrController=void 0,this.bufferController=void 0,this.capLevelController=void 0,this.latencyController=void 0,this.levelController=void 0,this.streamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this.url=null,this.triggeringException=void 0,I(e.debug||!1,"Hls instance");var r=this.config=function(t,e){if((e.liveSyncDurationCount||e.liveMaxLatencyDurationCount)&&(e.liveSyncDuration||e.liveMaxLatencyDuration))throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");if(void 0!==e.liveMaxLatencyDurationCount&&(void 0===e.liveSyncDurationCount||e.liveMaxLatencyDurationCount<=e.liveSyncDurationCount))throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"');if(void 0!==e.liveMaxLatencyDuration&&(void 0===e.liveSyncDuration||e.liveMaxLatencyDuration<=e.liveSyncDuration))throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"');var r=$s(t),n=["TimeOut","MaxRetry","RetryDelay","MaxRetryTimeout"];return["manifest","level","frag"].forEach((function(t){var i=("level"===t?"playlist":t)+"LoadPolicy",a=void 0===e[i],s=[];n.forEach((function(n){var o=t+"Loading"+n,l=e[o];if(void 0!==l&&a){s.push(o);var u=r[i].default;switch(e[i]={default:u},n){case"TimeOut":u.maxLoadTimeMs=l,u.maxTimeToFirstByteMs=l;break;case"MaxRetry":u.errorRetry.maxNumRetry=l,u.timeoutRetry.maxNumRetry=l;break;case"RetryDelay":u.errorRetry.retryDelayMs=l,u.timeoutRetry.retryDelayMs=l;break;case"MaxRetryTimeout":u.errorRetry.maxRetryDelayMs=l,u.timeoutRetry.maxRetryDelayMs=l}}})),s.length&&w.warn('hls.js config: "'+s.join('", "')+'" setting(s) are deprecated, use "'+i+'": '+JSON.stringify(e[i]))})),i(i({},r),e)}(t.DefaultConfig,e);this.userConfig=e,r.progressive&&Zs(r);var n=r.abrController,a=r.bufferController,s=r.capLevelController,o=r.errorController,l=r.fpsController,u=new o(this),h=this.abrController=new n(this),d=this.bufferController=new a(this),c=this.capLevelController=new s(this),f=new l(this),g=new Fe(this),v=new qe(this),m=r.contentSteeringController,p=m?new m(this):null,y=this.levelController=new to(this,p),E=new qr(this),T=new ro(this.config),L=this.streamController=new so(this,E,T);c.setStreamController(L),f.setStreamController(L);var A=[g,y,L];p&&A.splice(1,0,p),this.networkControllers=A;var R=[h,d,c,f,v,E];this.audioTrackController=this.createController(r.audioTrackController,A);var k=r.audioStreamController;k&&A.push(new k(this,E,T)),this.subtitleTrackController=this.createController(r.subtitleTrackController,A);var b=r.subtitleStreamController;b&&A.push(new b(this,E,T)),this.createController(r.timelineController,R),T.emeController=this.emeController=this.createController(r.emeController,R),this.cmcdController=this.createController(r.cmcdController,R),this.latencyController=this.createController(Xe,R),this.coreComponents=R,A.push(u);var D=u.onErrorOut;"function"==typeof D&&this.on(S.ERROR,D,u)}t.isMSESupported=function(){return no()},t.isSupported=function(){return function(){if(!no())return!1;var t=ee();return"function"==typeof(null==t?void 0:t.isTypeSupported)&&(["avc1.42E01E,mp4a.40.2","av01.0.01M.08","vp09.00.50.08"].some((function(e){return t.isTypeSupported(ae(e,"video"))}))||["mp4a.40.2","fLaC"].some((function(e){return t.isTypeSupported(ae(e,"audio"))})))}()},t.getMediaSource=function(){return ee()};var e=t.prototype;return e.createController=function(t,e){if(t){var r=new t(this);return e&&e.push(r),r}return null},e.on=function(t,e,r){void 0===r&&(r=this),this._emitter.on(t,e,r)},e.once=function(t,e,r){void 0===r&&(r=this),this._emitter.once(t,e,r)},e.removeAllListeners=function(t){this._emitter.removeAllListeners(t)},e.off=function(t,e,r,i){void 0===r&&(r=this),this._emitter.off(t,e,r,i)},e.listeners=function(t){return this._emitter.listeners(t)},e.emit=function(t,e,r){return this._emitter.emit(t,e,r)},e.trigger=function(t,e){if(this.config.debug)return this.emit(t,t,e);try{return this.emit(t,t,e)}catch(e){if(w.error("An internal error happened while handling event "+t+'. Error message: "'+e.message+'". Here is a stacktrace:',e),!this.triggeringException){this.triggeringException=!0;var r=t===S.ERROR;this.trigger(S.ERROR,{type:L.OTHER_ERROR,details:A.INTERNAL_EXCEPTION,fatal:r,event:t,error:e}),this.triggeringException=!1}}return!1},e.listenerCount=function(t){return this._emitter.listenerCount(t)},e.destroy=function(){w.log("destroy"),this.trigger(S.DESTROYING,void 0),this.detachMedia(),this.removeAllListeners(),this._autoLevelCapping=-1,this.url=null,this.networkControllers.forEach((function(t){return t.destroy()})),this.networkControllers.length=0,this.coreComponents.forEach((function(t){return t.destroy()})),this.coreComponents.length=0;var t=this.config;t.xhrSetup=t.fetchSetup=void 0,this.userConfig=null},e.attachMedia=function(t){w.log("attachMedia"),this._media=t,this.trigger(S.MEDIA_ATTACHING,{media:t})},e.detachMedia=function(){w.log("detachMedia"),this.trigger(S.MEDIA_DETACHING,void 0),this._media=null},e.loadSource=function(t){this.stopLoad();var e=this.media,r=this.url,i=this.url=p.buildAbsoluteURL(self.location.href,t,{alwaysNormalize:!0});this._autoLevelCapping=-1,this._maxHdcpLevel=null,w.log("loadSource:"+i),e&&r&&(r!==i||this.bufferController.hasSourceTypes())&&(this.detachMedia(),this.attachMedia(e)),this.trigger(S.MANIFEST_LOADING,{url:t})},e.startLoad=function(t){void 0===t&&(t=-1),w.log("startLoad("+t+")"),this.started=!0,this.networkControllers.forEach((function(e){e.startLoad(t)}))},e.stopLoad=function(){w.log("stopLoad"),this.started=!1,this.networkControllers.forEach((function(t){t.stopLoad()}))},e.resumeBuffering=function(){this.started&&this.networkControllers.forEach((function(t){"fragmentLoader"in t&&t.startLoad(-1)}))},e.pauseBuffering=function(){this.networkControllers.forEach((function(t){"fragmentLoader"in t&&t.stopLoad()}))},e.swapAudioCodec=function(){w.log("swapAudioCodec"),this.streamController.swapAudioCodec()},e.recoverMediaError=function(){w.log("recoverMediaError");var t=this._media;this.detachMedia(),t&&this.attachMedia(t)},e.removeLevel=function(t){this.levelController.removeLevel(t)},e.setAudioOption=function(t){var e;return null==(e=this.audioTrackController)?void 0:e.setAudioOption(t)},e.setSubtitleOption=function(t){var e;return null==(e=this.subtitleTrackController)||e.setSubtitleOption(t),null},s(t,[{key:"levels",get:function(){var t=this.levelController.levels;return t||[]}},{key:"currentLevel",get:function(){return this.streamController.currentLevel},set:function(t){w.log("set currentLevel:"+t),this.levelController.manualLevel=t,this.streamController.immediateLevelSwitch()}},{key:"nextLevel",get:function(){return this.streamController.nextLevel},set:function(t){w.log("set nextLevel:"+t),this.levelController.manualLevel=t,this.streamController.nextLevelSwitch()}},{key:"loadLevel",get:function(){return this.levelController.level},set:function(t){w.log("set loadLevel:"+t),this.levelController.manualLevel=t}},{key:"nextLoadLevel",get:function(){return this.levelController.nextLoadLevel},set:function(t){this.levelController.nextLoadLevel=t}},{key:"firstLevel",get:function(){return Math.max(this.levelController.firstLevel,this.minAutoLevel)},set:function(t){w.log("set firstLevel:"+t),this.levelController.firstLevel=t}},{key:"startLevel",get:function(){var t=this.levelController.startLevel;return-1===t&&this.abrController.forcedAutoLevel>-1?this.abrController.forcedAutoLevel:t},set:function(t){w.log("set startLevel:"+t),-1!==t&&(t=Math.max(t,this.minAutoLevel)),this.levelController.startLevel=t}},{key:"capLevelToPlayerSize",get:function(){return this.config.capLevelToPlayerSize},set:function(t){var e=!!t;e!==this.config.capLevelToPlayerSize&&(e?this.capLevelController.startCapping():(this.capLevelController.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=e)}},{key:"autoLevelCapping",get:function(){return this._autoLevelCapping},set:function(t){this._autoLevelCapping!==t&&(w.log("set autoLevelCapping:"+t),this._autoLevelCapping=t,this.levelController.checkMaxAutoUpdated())}},{key:"bandwidthEstimate",get:function(){var t=this.abrController.bwEstimator;return t?t.getEstimate():NaN},set:function(t){this.abrController.resetEstimator(t)}},{key:"ttfbEstimate",get:function(){var t=this.abrController.bwEstimator;return t?t.getEstimateTTFB():NaN}},{key:"maxHdcpLevel",get:function(){return this._maxHdcpLevel},set:function(t){(function(t){return ze.indexOf(t)>-1})(t)&&this._maxHdcpLevel!==t&&(this._maxHdcpLevel=t,this.levelController.checkMaxAutoUpdated())}},{key:"autoLevelEnabled",get:function(){return-1===this.levelController.manualLevel}},{key:"manualLevel",get:function(){return this.levelController.manualLevel}},{key:"minAutoLevel",get:function(){var t=this.levels,e=this.config.minAutoBitrate;if(!t)return 0;for(var r=t.length,i=0;i=e)return i;return 0}},{key:"maxAutoLevel",get:function(){var t,e=this.levels,r=this.autoLevelCapping,i=this.maxHdcpLevel;if(t=-1===r&&null!=e&&e.length?e.length-1:r,i)for(var n=t;n--;){var a=e[n].attrs["HDCP-LEVEL"];if(a&&a<=i)return n}return t}},{key:"firstAutoLevel",get:function(){return this.abrController.firstAutoLevel}},{key:"nextAutoLevel",get:function(){return this.abrController.nextAutoLevel},set:function(t){this.abrController.nextAutoLevel=t}},{key:"playingDate",get:function(){return this.streamController.currentProgramDateTime}},{key:"mainForwardBufferInfo",get:function(){return this.streamController.getMainFwdBufferInfo()}},{key:"allAudioTracks",get:function(){var t=this.audioTrackController;return t?t.allAudioTracks:[]}},{key:"audioTracks",get:function(){var t=this.audioTrackController;return t?t.audioTracks:[]}},{key:"audioTrack",get:function(){var t=this.audioTrackController;return t?t.audioTrack:-1},set:function(t){var e=this.audioTrackController;e&&(e.audioTrack=t)}},{key:"allSubtitleTracks",get:function(){var t=this.subtitleTrackController;return t?t.allSubtitleTracks:[]}},{key:"subtitleTracks",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTracks:[]}},{key:"subtitleTrack",get:function(){var t=this.subtitleTrackController;return t?t.subtitleTrack:-1},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleTrack=t)}},{key:"media",get:function(){return this._media}},{key:"subtitleDisplay",get:function(){var t=this.subtitleTrackController;return!!t&&t.subtitleDisplay},set:function(t){var e=this.subtitleTrackController;e&&(e.subtitleDisplay=t)}},{key:"lowLatencyMode",get:function(){return this.config.lowLatencyMode},set:function(t){this.config.lowLatencyMode=t}},{key:"liveSyncPosition",get:function(){return this.latencyController.liveSyncPosition}},{key:"latency",get:function(){return this.latencyController.latency}},{key:"maxLatency",get:function(){return this.latencyController.maxLatency}},{key:"targetLatency",get:function(){return this.latencyController.targetLatency}},{key:"drift",get:function(){return this.latencyController.drift}},{key:"forceStartLoad",get:function(){return this.streamController.forceStartLoad}}],[{key:"version",get:function(){return"1.5.8"}},{key:"Events",get:function(){return S}},{key:"ErrorTypes",get:function(){return L}},{key:"ErrorDetails",get:function(){return A}},{key:"DefaultConfig",get:function(){return t.defaultConfig?t.defaultConfig:Js},set:function(e){t.defaultConfig=e}}]),t}();return oo.defaultConfig=void 0,oo},"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(r="undefined"!=typeof globalThis?globalThis:r||self).Hls=i()}(!1); +//# sourceMappingURL=hls.min.js.map diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/index.html b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/index.html new file mode 100644 index 0000000000..0dd53297e4 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/index.html @@ -0,0 +1,18 @@ + + + + LWS Example + + + + +
+ + Hello from the minimal http server example. +
+ You can confirm the 404 page handler by going to this + nonexistant
page. +

+ View Media Library + + diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/libwebsockets.org-logo.svg b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/libwebsockets.org-logo.svg new file mode 100644 index 0000000000..ef241b37c1 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/libwebsockets.org-logo.svg @@ -0,0 +1,66 @@ + + + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.css b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.css new file mode 100644 index 0000000000..8dd37af7be --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.css @@ -0,0 +1,73 @@ +body { + font-family: Arial, sans-serif; + background-color: #1a1a1a; + color: #ffffff; + margin: 0; + padding: 20px; + display: flex; + flex-direction: column; + align-items: center; +} +.player-container { + width: 100%; + max-width: 800px; + margin-top: 20px; + background-color: #000; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 15px rgba(0,0,0,0.5); +} +video { + width: 100%; + height: auto; + display: block; +} +.header { + width: 100%; + max-width: 800px; + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; +} +.back-link { + color: #4CAF50; + text-decoration: none; + font-weight: bold; +} +.back-link:hover { + text-decoration: underline; +} +#auth-status { + float: right; + margin-top: 10px; + margin-right: 20px; +} + +.debug-panel { + width: 100%; + max-width: 800px; + margin-top: 20px; + background-color: #2b2b2b; + border-radius: 8px; + padding: 15px; + box-sizing: border-box; +} + +.debug-panel h3 { + margin-top: 0; + color: #4CAF50; + font-size: 16px; + border-bottom: 1px solid #444; + padding-bottom: 8px; +} + +.debug-logs { + max-height: 200px; + overflow-y: auto; + font-family: monospace; + font-size: 12px; + color: #00FF00; + line-height: 1.5; +} + diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.html b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.html new file mode 100644 index 0000000000..d1ecd8a695 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.html @@ -0,0 +1,29 @@ + + + + + LWS Media Player + + + + + + +
+
+ +

LWS Media Player

+ ← Back to Library +
+ +
+ +
+ +
+

Player Logs

+
Ready.
+
+ + + diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.js b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.js new file mode 100644 index 0000000000..dd963555ae --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/player.js @@ -0,0 +1,125 @@ +document.addEventListener('DOMContentLoaded', function() { + if (typeof window.renderLwsLoginStatus === 'function') { + window.renderLwsLoginStatus('auth-status'); + } + + var video = document.getElementById('video'); + + var urlParams = new URLSearchParams(window.location.search); + var videoSrc = urlParams.get('v'); + var rawSrc = urlParams.get('raw'); + + if (rawSrc) { + // Play directly via HTTP Range requests natively supported by lws + video.src = rawSrc; + video.addEventListener('loadedmetadata', function() { + video.play(); + }); + return; + } + + if (!videoSrc) { + alert("No video source provided."); + return; + } + + var logsContainer = document.getElementById('debug-logs'); + function logMsg(msg) { + if (logsContainer) { + var time = new Date().toLocaleTimeString(); + logsContainer.innerHTML = '[' + time + '] ' + msg + '
' + logsContainer.innerHTML; + } + } + + // Video Element Events + video.addEventListener('play', function() { logMsg('video: play'); }); + video.addEventListener('playing', function() { logMsg('video: playing'); }); + video.addEventListener('pause', function() { logMsg('video: pause'); }); + video.addEventListener('waiting', function() { logMsg('video: waiting (buffering)'); }); + video.addEventListener('stalled', function() { logMsg('video: stalled'); }); + video.addEventListener('seeking', function() { logMsg('video: seeking to ' + video.currentTime.toFixed(3) + 's'); }); + video.addEventListener('seeked', function() { logMsg('video: seeked to ' + video.currentTime.toFixed(3) + 's'); }); + video.addEventListener('error', function() { + var err = video.error; + logMsg('video error: code ' + (err ? err.code : 'unknown') + ', msg: ' + (err ? err.message : 'unknown')); + }); + + if (Hls.isSupported()) { + logMsg('hls.js supported'); + var hls = new Hls({ + debug: false, + maxBufferLength: 60, + maxMaxBufferLength: 120, + maxBufferHole: 0.5, + startPosition: 0, + nudgeMaxRetry: 5, + }); + hls.loadSource(videoSrc); + hls.attachMedia(video); + + hls.on(Hls.Events.MANIFEST_PARSED, function() { + logMsg('hls: manifest parsed, playing'); + video.play(); + }); + + hls.on(Hls.Events.ERROR, function(event, data) { + var msg = 'hls error: type=' + data.type + ', details=' + data.details + ', fatal=' + data.fatal; + logMsg(msg); + if (data.fatal) { + switch(data.type) { + case Hls.ErrorTypes.NETWORK_ERROR: + logMsg('hls: fatal network error, trying to recover'); + hls.startLoad(); + break; + case Hls.ErrorTypes.MEDIA_ERROR: + logMsg('hls: fatal media error, trying to recover'); + hls.recoverMediaError(); + break; + default: + logMsg('hls: unrecoverable fatal error'); + hls.destroy(); + break; + } + } + }); + + hls.on(Hls.Events.BUFFER_APPENDED, function() { + if (video.buffered.length > 0) { + var ranges = []; + for (var i = 0; i < video.buffered.length; i++) { + ranges.push('[' + video.buffered.start(i).toFixed(1) + 's - ' + video.buffered.end(i).toFixed(1) + 's]'); + } + logMsg('buffer: ' + ranges.join(', ')); + } + }); + hls.on(Hls.Events.FRAG_LOADING, function(event, data) { + if (data.frag) { + logMsg('loading seg ' + data.frag.sn + ' (' + data.frag.start.toFixed(1) + 's - ' + (data.frag.start + data.frag.duration).toFixed(1) + 's)'); + } + }); + } + // For Safari, which natively supports HLS + else if (video.canPlayType('application/vnd.apple.mpegurl')) { + logMsg('native HLS supported'); + video.src = videoSrc; + video.addEventListener('loadedmetadata', function() { + logMsg('native HLS metadata loaded, playing'); + video.play(); + }); + + // Polling buffer status for native HLS + setInterval(function() { + if (video.buffered.length > 0) { + var ranges = []; + for (var i = 0; i < video.buffered.length; i++) { + ranges.push('[' + video.buffered.start(i).toFixed(1) + 's - ' + video.buffered.end(i).toFixed(1) + 's]'); + } + logMsg('native buffer: ' + ranges.join(', ')); + } + }, 2000); + } + else { + logMsg('error: browser does not support HLS'); + alert("Your browser does not support playing this video."); + } +}); diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/strict-csp.svg b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/strict-csp.svg new file mode 100644 index 0000000000..cd128f1d21 --- /dev/null +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-hls/mount-origin/strict-csp.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/protocol_lws_hls/CMakeLists.txt b/plugins/protocol_lws_hls/CMakeLists.txt new file mode 100644 index 0000000000..368afe9ea0 --- /dev/null +++ b/plugins/protocol_lws_hls/CMakeLists.txt @@ -0,0 +1,53 @@ +# +# libwebsockets - small server side websockets and web server implementation +# +# Copyright (C) 2010 - 2026 Andy Green +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# + + find_package(PkgConfig QUIET) + pkg_check_modules(PC_AVFORMAT libavformat) + pkg_check_modules(PC_AVCODEC libavcodec) + pkg_check_modules(PC_AVUTIL libavutil) + pkg_check_modules(PC_SWSCALE libswscale) + + if (PC_AVFORMAT_FOUND AND PC_AVCODEC_FOUND AND PC_AVUTIL_FOUND AND PC_SWSCALE_FOUND) + set(PLUGIN_NAME "protocol_lws_hls") + set(PLUGIN_SRCS + protocol_lws_hls.c + hls-av.c + hls-dir.c + ) + set(PLUGIN_HDR private-lws-hls.h) + + # Create the plugin target using the macro from plugins/CMakeLists.txt + create_plugin(${PLUGIN_NAME} + "" + "${PLUGIN_SRCS}" + "" + "" + ) + + if (TARGET ${PLUGIN_NAME}) + target_compile_definitions(${PLUGIN_NAME} PRIVATE LWS_BUILDING_SHARED) + target_include_directories(${PLUGIN_NAME} PRIVATE + ${PC_AVFORMAT_INCLUDE_DIRS} + ${PC_AVCODEC_INCLUDE_DIRS} + ${PC_AVUTIL_INCLUDE_DIRS} + ${PC_SWSCALE_INCLUDE_DIRS}) + target_link_libraries(${PLUGIN_NAME} + ${PC_AVFORMAT_LIBRARIES} + ${PC_AVCODEC_LIBRARIES} + ${PC_AVUTIL_LIBRARIES} + ${PC_SWSCALE_LIBRARIES}) + endif() + endif() diff --git a/plugins/protocol_lws_hls/README.md b/plugins/protocol_lws_hls/README.md new file mode 100644 index 0000000000..1bc55bc4d8 --- /dev/null +++ b/plugins/protocol_lws_hls/README.md @@ -0,0 +1,56 @@ +# lws-hls plugin + +This plugin implements an Apple HLS (HTTP Live Streaming) server using `libavformat`. +It dynamically builds an `.m3u8` playlist for media files within a specified directory, +and extracts/remuxes MPEG-TS segments on the fly without performing disk I/O. + +## Minimal Example + +A minimal example server is provided at `minimal-examples-lowlevel/http-server/minimal-http-server-hls`. +This standalone example demonstrates how to: + +- Create an LWS vhost that explicitly loads `protocol_lws_hls`. +- Provide an overarching `media-dir` via the per-vhost options (PVO) mapping to expose local videos. +- Stream media files without transcoding. + +To test the minimal server: + +```bash +cd build/minimal-examples-lowlevel/http-server/minimal-http-server-hls +./lws-minimal-http-server-hls --media-dir /path/to/my/videos +``` +Then visit `http://localhost:7681` to view the directory listing. + +## lwsws configuration + +If you are using the generic `lwsws` (lws web server) framework, you can configure the plugin on a vhost through your JSON configuration file (parsed by `lejp`). You must instantiate the protocol on the vhost, and also create two separate mounts in the URL space: one for static file serving of the player assets, and one using `callback://` to route incoming HTTP requests to the protocol plugin callback. + +Example JSON snippet (e.g. inside `/etc/lwsws/conf.d/myvhost.json`): + +```json +{ + "vhosts": [{ + "name": "localhost", + "port": 7681, + "ws-protocols": [{ + "lws-hls": { + "status": "ok", + "media-dir": "/var/lib/media" + } + }], + "mounts": [ + { + "mountpoint": "/media", + "origin": "file:///usr/local/share/libwebsockets-test-server/hls/mount-origin", + "default": "index.html" + }, + { + "mountpoint": "/media/hls", + "origin": "callback://lws-hls" + } + ] + }] +} +``` + +This attaches the `lws-hls` protocol to the vhost, maps the `/media` URL path to a standard file mount pointing to your player HTML assets, and creates a `/media/hls` callback mount bound to the `lws-hls` protocol callback. This ensures player files (like `player.html` and `dir.css`) are served statically, while dynamic playlist generator, thumbnail generator, and streaming requests are properly routed to the plugin. diff --git a/plugins/protocol_lws_hls/hls-av.c b/plugins/protocol_lws_hls/hls-av.c new file mode 100644 index 0000000000..704ba2b41d --- /dev/null +++ b/plugins/protocol_lws_hls/hls-av.c @@ -0,0 +1,882 @@ +#include "private-lws-hls.h" +#include +#include + +#define HLS_SEGMENT_DUR 10 + + +void * +lws_hls_thumbnail_worker(void *d) +{ + struct per_vhost_data__lws_hls *vhd = (struct per_vhost_data__lws_hls *)d; + + while (1) { + pthread_mutex_lock(&vhd->lock); + + while (!vhd->thread_exit && !vhd->task_head) { + pthread_cond_wait(&vhd->cond, &vhd->lock); + } + + if (vhd->thread_exit) { + pthread_mutex_unlock(&vhd->lock); + break; + } + + struct thumb_task *t = vhd->task_head; + vhd->task_head = t->next; + if (!vhd->task_head) + vhd->task_tail = NULL; + + pthread_mutex_unlock(&vhd->lock); + + char filepath[512]; + snprintf(filepath, sizeof(filepath), "%s/%s", vhd->media_dir, t->filename); + + AVFormatContext *fmt_ctx = NULL; + AVCodecContext *dec_ctx = NULL; + AVCodecContext *enc_ctx = NULL; + struct SwsContext *sws_ctx = NULL; + AVFrame *frame = NULL; + AVFrame *rgb_frame = NULL; + AVPacket *pkt = NULL; + AVPacket *enc_pkt = NULL; + uint8_t *jpeg_data = NULL; + int jpeg_size = 0; + + if (avformat_open_input(&fmt_ctx, filepath, NULL, NULL) == 0) { + if (avformat_find_stream_info(fmt_ctx, NULL) >= 0) { + int video_idx = -1; + for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) { + if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + video_idx = (int)i; + break; + } + } + + if (video_idx >= 0) { + const AVCodec *decoder = avcodec_find_decoder(fmt_ctx->streams[video_idx]->codecpar->codec_id); + if (decoder) { + dec_ctx = avcodec_alloc_context3(decoder); + avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_idx]->codecpar); + + if (avcodec_open2(dec_ctx, decoder, NULL) == 0) { + frame = av_frame_alloc(); + pkt = av_packet_alloc(); + + while (av_read_frame(fmt_ctx, pkt) >= 0) { + if (pkt->stream_index == video_idx) { + if (avcodec_send_packet(dec_ctx, pkt) == 0) { + if (avcodec_receive_frame(dec_ctx, frame) == 0) { + const AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_MJPEG); + if (encoder) { + enc_ctx = avcodec_alloc_context3(encoder); + enc_ctx->width = frame->width; + enc_ctx->height = frame->height; + enc_ctx->time_base = (AVRational){1, 25}; + enc_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; + + if (avcodec_open2(enc_ctx, encoder, NULL) == 0) { + sws_ctx = sws_getContext(frame->width, frame->height, dec_ctx->pix_fmt, + enc_ctx->width, enc_ctx->height, enc_ctx->pix_fmt, + SWS_BILINEAR, NULL, NULL, NULL); + + if (sws_ctx) { + rgb_frame = av_frame_alloc(); + rgb_frame->format = enc_ctx->pix_fmt; + rgb_frame->width = enc_ctx->width; + rgb_frame->height = enc_ctx->height; + av_frame_get_buffer(rgb_frame, 32); + + sws_scale(sws_ctx, (const uint8_t * const*)frame->data, frame->linesize, + 0, frame->height, rgb_frame->data, rgb_frame->linesize); + + enc_pkt = av_packet_alloc(); + if (avcodec_send_frame(enc_ctx, rgb_frame) == 0) { + if (avcodec_receive_packet(enc_ctx, enc_pkt) == 0) { + jpeg_data = malloc((size_t)enc_pkt->size); + memcpy(jpeg_data, enc_pkt->data, (size_t)enc_pkt->size); + jpeg_size = enc_pkt->size; + } + } + av_packet_free(&enc_pkt); + av_frame_free(&rgb_frame); + sws_freeContext(sws_ctx); + } + } + avcodec_free_context(&enc_ctx); + } + av_packet_unref(pkt); + break; + } + } + } + av_packet_unref(pkt); + } + av_packet_free(&pkt); + av_frame_free(&frame); + } + avcodec_free_context(&dec_ctx); + } + } + } + avformat_close_input(&fmt_ctx); + } + + pthread_mutex_lock(&vhd->lock); + + struct thumb_cache *c = malloc(sizeof(*c)); + strncpy(c->filename, t->filename, sizeof(c->filename)); + c->data = jpeg_data; + c->len = (size_t)jpeg_size; + + c->next = vhd->cache_head; + vhd->cache_head = c; + vhd->cache_count++; + + if (vhd->cache_count > 20) { + struct thumb_cache *prev = NULL; + struct thumb_cache *curr = vhd->cache_head; + while (curr && curr->next) { + prev = curr; + curr = curr->next; + } + if (prev) { + prev->next = NULL; + if (curr->data) free(curr->data); + free(curr); + vhd->cache_count--; + } + } + + pthread_mutex_unlock(&vhd->lock); + free(t); + + lws_cancel_service(vhd->context); + } + + return NULL; +} + +int +lws_hls_serve_thumbnail(struct lws *wsi, const char *media_dir, const char *filename) +{ + struct per_vhost_data__lws_hls *vhd = (struct per_vhost_data__lws_hls *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); + struct per_session_data__lws_hls *pss = (struct per_session_data__lws_hls *) + lws_wsi_user(wsi); + + if (!vhd || !pss) return -1; + + pthread_mutex_lock(&vhd->lock); + + struct thumb_cache *c = vhd->cache_head; + while (c) { + if (!strcmp(c->filename, filename)) + break; + c = c->next; + } + + if (c) { + pthread_mutex_unlock(&vhd->lock); + strncpy(pss->thumb_filename, filename, sizeof(pss->thumb_filename)); + pss->waiting_for_thumbnail = 1; + lws_callback_on_writable(wsi); + return 0; + } + + struct thumb_task *t = vhd->task_head; + int already_queued = 0; + while (t) { + if (!strcmp(t->filename, filename)) { + already_queued = 1; + break; + } + t = t->next; + } + + if (!already_queued) { + struct thumb_task *nt = malloc(sizeof(*nt)); + if (!nt) { + pthread_mutex_unlock(&vhd->lock); + return -1; + } + strncpy(nt->filename, filename, sizeof(nt->filename)); + nt->next = NULL; + + if (vhd->task_tail) + vhd->task_tail->next = nt; + else + vhd->task_head = nt; + vhd->task_tail = nt; + + pthread_cond_signal(&vhd->cond); + } + + pthread_mutex_unlock(&vhd->lock); + + strncpy(pss->thumb_filename, filename, sizeof(pss->thumb_filename)); + pss->waiting_for_thumbnail = 1; + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 30); + + return 0; +} + + +/* Custom AVIOContext writer for memory */ +struct hls_buffer { + uint8_t *ptr; + size_t size; + size_t allocated; +}; + +#if LIBAVFORMAT_VERSION_MAJOR >= 61 +static int write_packet(void *opaque, const uint8_t *buf, int buf_size) { +#else +static int write_packet(void *opaque, uint8_t *buf, int buf_size) { +#endif + struct hls_buffer *hb = (struct hls_buffer *)opaque; + if (hb->size + (size_t)buf_size > hb->allocated) { + hb->allocated = (hb->size + (size_t)buf_size) * 2; + hb->ptr = realloc(hb->ptr, hb->allocated); + } + memcpy(hb->ptr + hb->size, buf, (size_t)buf_size); + hb->size += (size_t)buf_size; + return buf_size; +} + +static size_t find_moof_offset(uint8_t *buf, size_t size) { + size_t offset = 0; + while (offset + 8 <= size) { + uint32_t box_size = (buf[offset] << 24) | (buf[offset+1] << 16) | (buf[offset+2] << 8) | buf[offset+3]; + if (box_size == 1 || box_size < 8) break; + if (memcmp(buf + offset + 4, "moof", 4) == 0) { + return offset; + } + offset += box_size; + } + return 0; +} + +int +lws_hls_serve_init(struct lws *wsi, const char *media_dir, const char *filename) +{ + char filepath[1024]; + snprintf(filepath, sizeof(filepath), "%s/%s", media_dir, filename); + + AVFormatContext *in_ctx = NULL; + if (avformat_open_input(&in_ctx, filepath, NULL, NULL) < 0) { + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, "File not found"); + return -1; + } + + if (avformat_find_stream_info(in_ctx, NULL) < 0) { + avformat_close_input(&in_ctx); + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Stream info error"); + return -1; + } + + AVFormatContext *out_ctx = NULL; + avformat_alloc_output_context2(&out_ctx, NULL, "mp4", NULL); + if (!out_ctx) { + avformat_close_input(&in_ctx); + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Out ctx error"); + return -1; + } + + for (unsigned int i = 0; i < in_ctx->nb_streams; i++) { + AVStream *in_stream = in_ctx->streams[i]; + AVCodecParameters *in_codecpar = in_stream->codecpar; + + if (in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO && + in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { + continue; + } + + AVStream *out_stream = avformat_new_stream(out_ctx, NULL); + avcodec_parameters_copy(out_stream->codecpar, in_codecpar); + out_stream->codecpar->codec_tag = 0; + out_stream->time_base = in_stream->time_base; + } + + struct hls_buffer hb; + hb.size = 0; + hb.allocated = 1024 * 1024; + hb.ptr = malloc(hb.allocated); + + unsigned char *avio_ctx_buffer = av_malloc(32768); + AVIOContext *avio_ctx = avio_alloc_context(avio_ctx_buffer, 32768, 1, &hb, NULL, write_packet, NULL); + out_ctx->pb = avio_ctx; + + AVDictionary *opts = NULL; + av_dict_set(&opts, "movflags", "empty_moov+default_base_moof+delay_moov", 0); + + char timescale_str[32]; + for (unsigned int i = 0; i < out_ctx->nb_streams; i++) { + if (out_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + snprintf(timescale_str, sizeof(timescale_str), "%d", out_ctx->streams[i]->time_base.den); + av_dict_set(&opts, "video_track_timescale", timescale_str, 0); + break; + } + } + + if (avformat_write_header(out_ctx, &opts) < 0) { + av_dict_free(&opts); + if (out_ctx) { + av_free(out_ctx->pb->buffer); + av_free(out_ctx->pb); + avformat_free_context(out_ctx); + } + avformat_close_input(&in_ctx); + free(hb.ptr); + return -1; + } + av_write_trailer(out_ctx); + av_dict_free(&opts); + + if (out_ctx) { + av_free(out_ctx->pb->buffer); + av_free(out_ctx->pb); + avformat_free_context(out_ctx); + } + avformat_close_input(&in_ctx); + + struct per_session_data__lws_hls *pss = (struct per_session_data__lws_hls *)lws_wsi_user(wsi); + if (!pss || hb.size == 0) { + free(hb.ptr); + return -1; + } + + /* + * The init segment (EXT-X-MAP) must contain ftyp + moov only. + * If av_write_trailer produced a trailing moof+mdat, strip it. + * For init: send everything BEFORE the moof. + * (For media segments the opposite is done: send moof onwards.) + */ + size_t moof_off = find_moof_offset(hb.ptr, hb.size); + size_t send_size = moof_off > 0 ? moof_off : hb.size; + + lwsl_user("HLS: Init segment: total=%zu, moof_offset=%zu, " + "sending=%zu (ftyp+moov)\n", hb.size, moof_off, send_size); + + pss->segment_buf = malloc(LWS_PRE + send_size); + if (!pss->segment_buf) { + free(hb.ptr); + return -1; + } + + memcpy(pss->segment_buf + LWS_PRE, hb.ptr, send_size); + pss->segment_len = send_size; + + pss->segment_pos = 0; + free(hb.ptr); + + uint8_t hbuf[LWS_PRE + 2048], *start = hbuf + LWS_PRE, *p = start, *end = p + 2048; + if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, "video/mp4", + (lws_filepos_t)pss->segment_len, &p, end) || + lws_finalize_write_http_header(wsi, start, &p, end)) { + return -1; + } + + lws_callback_on_writable(wsi); + return 0; +} +int +lws_hls_serve_manifest(struct lws *wsi, const char *media_dir, const char *filename) +{ + char filepath[1024]; + snprintf(filepath, sizeof(filepath), "%s/%s", media_dir, filename); + + AVFormatContext *fmt_ctx = NULL; + if (avformat_open_input(&fmt_ctx, filepath, NULL, NULL) < 0) { + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, "File not found"); + return -1; + } + + if (avformat_find_stream_info(fmt_ctx, NULL) < 0) { + avformat_close_input(&fmt_ctx); + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Stream info error"); + return -1; + } + + int video_idx = -1; + unsigned int i_stream; + int total_segments; + + for (i_stream = 0; i_stream < fmt_ctx->nb_streams; i_stream++) { + if (fmt_ctx->streams[i_stream]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + video_idx = (int)i_stream; + break; + } + } + + int64_t duration = fmt_ctx->duration; + if (duration <= 0 && video_idx >= 0 && fmt_ctx->streams[video_idx]->duration > 0) { + duration = av_rescale_q(fmt_ctx->streams[video_idx]->duration, + fmt_ctx->streams[video_idx]->time_base, AV_TIME_BASE_Q); + } + + if (duration <= 0) { + avformat_close_input(&fmt_ctx); + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Unknown duration"); + return -1; + } + + total_segments = (int)(duration / ((int64_t)HLS_SEGMENT_DUR * AV_TIME_BASE)); + if (duration % ((int64_t)HLS_SEGMENT_DUR * AV_TIME_BASE) != 0) + total_segments++; + + avformat_close_input(&fmt_ctx); + + int target_duration = HLS_SEGMENT_DUR; + size_t m3u8_max = 1024 + (size_t)(total_segments * 128); + char *m3u8 = malloc(LWS_PRE + m3u8_max); + if (!m3u8) { + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "OOM"); + return -1; + } + + char *p_m3u8 = m3u8 + LWS_PRE; + p_m3u8 += snprintf(p_m3u8, m3u8_max, + "#EXTM3U\n" + "#EXT-X-VERSION:7\n" + "#EXT-X-TARGETDURATION:%d\n" + "#EXT-X-MEDIA-SEQUENCE:0\n" + "#EXT-X-MAP:URI=\"../init/%s\"\n" + "#EXT-X-PLAYLIST-TYPE:VOD\n", target_duration, filename); + + for (int i = 0; i < total_segments; i++) { + double dur = (double)HLS_SEGMENT_DUR; + if (i == total_segments - 1) { + int64_t rem = duration - (int64_t)i * HLS_SEGMENT_DUR * AV_TIME_BASE; + dur = (double)rem / AV_TIME_BASE; + } + if (dur <= 0.0) { + dur = 0.1; + } + size_t rem_buf = m3u8_max - (size_t)(p_m3u8 - (m3u8 + LWS_PRE)); + p_m3u8 += snprintf(p_m3u8, rem_buf, + "#EXTINF:%f,\n" + "../segment/%s/%d\n", + dur, filename, i); + } + + size_t rem = m3u8_max - (size_t)(p_m3u8 - (m3u8 + LWS_PRE)); + snprintf(p_m3u8, rem, "#EXT-X-ENDLIST\n"); + + size_t len = strlen(m3u8 + LWS_PRE); + + /* Write out to LWS */ + uint8_t *buf = malloc(LWS_PRE + 2048); + if (!buf) { + free(m3u8); + return -1; + } + + uint8_t *start = buf + LWS_PRE; + uint8_t *p = start; + uint8_t *end = p + 2048; + + if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, "application/vnd.apple.mpegurl", + (lws_filepos_t)len, &p, end) || + lws_finalize_write_http_header(wsi, start, &p, end)) { + free(buf); + free(m3u8); + return 1; + } + + lws_write(wsi, (uint8_t *)(m3u8 + LWS_PRE), len, LWS_WRITE_HTTP_FINAL); + + free(buf); + free(m3u8); + return 1; /* Close connection after sending manifest */ +} + + +int +lws_hls_serve_segment(struct lws *wsi, const char *media_dir, const char *filename, int segment_idx) +{ + char filepath[1024]; + snprintf(filepath, sizeof(filepath), "%s/%s", media_dir, filename); + + AVFormatContext *in_ctx = NULL; + if (avformat_open_input(&in_ctx, filepath, NULL, NULL) < 0) { + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, "File not found"); + return -1; + } + + if (avformat_find_stream_info(in_ctx, NULL) < 0) { + avformat_close_input(&in_ctx); + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Stream info error"); + return -1; + } + + AVFormatContext *out_ctx = NULL; + avformat_alloc_output_context2(&out_ctx, NULL, "mp4", NULL); + if (!out_ctx) { + avformat_close_input(&in_ctx); + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Out ctx error"); + return -1; + } + + int *stream_mapping = malloc((size_t)in_ctx->nb_streams * sizeof(int)); + int stream_index = 0; + int has_video = 0; + int video_idx = -1; + int audio_idx = -1; + + for (unsigned int i = 0; i < in_ctx->nb_streams; i++) { + AVStream *in_stream = in_ctx->streams[i]; + AVCodecParameters *in_codecpar = in_stream->codecpar; + + if (in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO && + in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { + stream_mapping[i] = -1; + continue; + } + + if (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + has_video = 1; + video_idx = (int)i; + } + if (in_codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + audio_idx = (int)i; + } + + stream_mapping[i] = stream_index++; + AVStream *out_stream = avformat_new_stream(out_ctx, NULL); + avcodec_parameters_copy(out_stream->codecpar, in_codecpar); + out_stream->codecpar->codec_tag = 0; + out_stream->time_base = in_stream->time_base; + } + + struct hls_buffer hb; + hb.size = 0; + hb.allocated = 1024 * 1024; /* 1MB init */ + hb.ptr = malloc(hb.allocated); + + unsigned char *avio_ctx_buffer = av_malloc(32768); + AVIOContext *avio_ctx = avio_alloc_context(avio_ctx_buffer, 32768, + 1, &hb, NULL, write_packet, NULL); + out_ctx->pb = avio_ctx; + + + AVDictionary *opts = NULL; + av_dict_set(&opts, "movflags", "empty_moov+default_base_moof+delay_moov", 0); + + /* Set video_track_timescale to match input for accurate TFDT */ + char timescale_str[32]; + for (unsigned int i = 0; i < out_ctx->nb_streams; i++) { + if (out_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + snprintf(timescale_str, sizeof(timescale_str), "%d", out_ctx->streams[i]->time_base.den); + av_dict_set(&opts, "video_track_timescale", timescale_str, 0); + break; + } + } + + if (avformat_write_header(out_ctx, &opts) < 0) { + + /* Error */ + goto done; + } + + /* + * Do NOT set avoid_negative_ts here. Our timestamps are absolute + * positions in the media file (always non-negative). The fMP4 muxer + * with delay_moov will write the correct tfdt (Track Fragment Decode + * Time) reflecting the absolute position, which hls.js needs to + * stitch segments seamlessly without gaps. + */ + + int64_t start_time = (int64_t)segment_idx * HLS_SEGMENT_DUR * AV_TIME_BASE; + int64_t end_time = (int64_t)(segment_idx + 1) * HLS_SEGMENT_DUR * AV_TIME_BASE; + + int64_t duration = in_ctx->duration; + if (duration <= 0 && video_idx >= 0 && in_ctx->streams[video_idx]->duration > 0) { + duration = av_rescale_q(in_ctx->streams[video_idx]->duration, + in_ctx->streams[video_idx]->time_base, AV_TIME_BASE_Q); + } + + if (duration > 0 && start_time >= duration) { + goto done; + } + + lwsl_user("HLS: Segment %d requested. start_time=%lld (%.3fs), end_time=%lld (%.3fs)\n", + segment_idx, (long long)start_time, (double)start_time / AV_TIME_BASE, + (long long)end_time, (double)end_time / AV_TIME_BASE); + + if (video_idx >= 0) { + int64_t seek_time = start_time > 5000 ? start_time - 5000 : 0; + int64_t target_ts = av_rescale_q(seek_time, AV_TIME_BASE_Q, in_ctx->streams[video_idx]->time_base); + av_seek_frame(in_ctx, video_idx, target_ts, AVSEEK_FLAG_BACKWARD); + lwsl_user("HLS: Segment %d video seek requested to %lld (%.3fs)\n", + segment_idx, (long long)target_ts, (double)start_time / AV_TIME_BASE); + } else { + av_seek_frame(in_ctx, -1, start_time, AVSEEK_FLAG_BACKWARD); + lwsl_user("HLS: Segment %d generic seek requested to %.3fs\n", + segment_idx, (double)start_time / AV_TIME_BASE); + } + + int64_t last_dts[32]; + for (int i = 0; i < 32; i++) { + last_dts[i] = AV_NOPTS_VALUE; + } + int started = 0; + int video_finished = 0; + + /* Diagnostics variables */ + int64_t first_video_pts = AV_NOPTS_VALUE, last_video_pts = AV_NOPTS_VALUE; + int64_t first_video_dts = AV_NOPTS_VALUE, last_video_dts = AV_NOPTS_VALUE; + int64_t first_audio_pts = AV_NOPTS_VALUE, last_audio_pts = AV_NOPTS_VALUE; + int64_t first_audio_dts = AV_NOPTS_VALUE, last_audio_dts = AV_NOPTS_VALUE; + int video_packets_written = 0, audio_packets_written = 0; + int video_packets_discarded = 0, audio_packets_discarded = 0; + AVRational video_out_time_base = {0, 0}; + AVRational audio_out_time_base = {0, 0}; + + AVPacket pkt; + while (av_read_frame(in_ctx, &pkt) >= 0) { AVStream *in_stream = in_ctx->streams[pkt.stream_index]; + if (stream_mapping[pkt.stream_index] < 0) { + av_packet_unref(&pkt); + continue; + } + /* + * Synthesize missing DTS/PTS (MKV has no DTS). + * Must happen before boundary checks. + */ + if (pkt.dts == AV_NOPTS_VALUE) pkt.dts = pkt.pts; + if (pkt.pts == AV_NOPTS_VALUE) pkt.pts = pkt.dts; + + /* Use PTS for boundary checks (always valid after synthesis) */ + int64_t pkt_ts = pkt.pts != AV_NOPTS_VALUE ? pkt.pts : pkt.dts; + if (pkt_ts != AV_NOPTS_VALUE) { + int64_t pkt_time = av_rescale_q(pkt_ts, in_stream->time_base, AV_TIME_BASE_Q); + + + + if (!started) { + if (has_video) { + if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if ((pkt.flags & AV_PKT_FLAG_KEY) && pkt_time >= start_time - 5000) { + started = 1; + lwsl_user("HLS: Segment %d started writing video at pkt_time=%.3fs (pts=%lld, dts=%lld)\n", + segment_idx, (double)pkt_time / AV_TIME_BASE, (long long)pkt.pts, (long long)pkt.dts); + } else { + video_packets_discarded++; + av_packet_unref(&pkt); + continue; + } + } else { + /* Audio: discard until video has started */ + if (!started) { + audio_packets_discarded++; + av_packet_unref(&pkt); + continue; + } + } + } else { + if (pkt_time >= start_time - 5000) { + started = 1; + lwsl_user("HLS: Segment %d started writing audio-only at pkt_time=%.3fs (pts=%lld, dts=%lld)\n", + segment_idx, (double)pkt_time / AV_TIME_BASE, (long long)pkt.pts, (long long)pkt.dts); + } else { + audio_packets_discarded++; + av_packet_unref(&pkt); + continue; + } + } + } + + /* Stop at end of segment on next video keyframe */ + if (pkt_time >= end_time - 5000 && in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (pkt.flags & AV_PKT_FLAG_KEY) { + lwsl_user("HLS: Segment %d reached next video keyframe at pkt_time=%.3fs (pts=%lld, dts=%lld). Video finished.\n", + segment_idx, (double)pkt_time / AV_TIME_BASE, (long long)pkt.pts, (long long)pkt.dts); + video_finished = 1; + end_time = pkt_time; /* Extend or shrink end_time to match ACTUAL video end */ + av_packet_unref(&pkt); + continue; + } + } + + /* If there's no audio track, we must break once we're sure no more B-frames exist */ + if (video_finished && audio_idx < 0) { + int64_t dts_time = av_rescale_q(pkt.dts != AV_NOPTS_VALUE ? pkt.dts : pkt.pts, in_stream->time_base, AV_TIME_BASE_Q); + if (dts_time >= end_time) { + av_packet_unref(&pkt); + break; + } + } + + if (video_finished && in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (pkt_time >= end_time) { + video_packets_discarded++; + av_packet_unref(&pkt); + continue; + } + /* Otherwise, it's a B-frame belonging to the current segment, keep it! */ + } + + /* Stop audio after video has finished and audio reaches the actual video end boundary */ + if (video_finished && in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (pkt_time >= end_time) { + av_packet_unref(&pkt); + lwsl_user("HLS: Segment %d reached audio end at pkt_time=%.3fs. Stopping.\n", + segment_idx, (double)pkt_time / AV_TIME_BASE); + break; + } + } + } + + int out_stream_idx = stream_mapping[pkt.stream_index]; + pkt.stream_index = out_stream_idx; + AVStream *out_stream = out_ctx->streams[out_stream_idx]; + + pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); + pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); + pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); + pkt.pos = -1; + + if (pkt.dts != AV_NOPTS_VALUE) { + if (last_dts[out_stream_idx] != AV_NOPTS_VALUE && pkt.dts <= last_dts[out_stream_idx]) { + pkt.dts = last_dts[out_stream_idx] + 1; + } + last_dts[out_stream_idx] = pkt.dts; + } + if (pkt.pts != AV_NOPTS_VALUE && pkt.pts < pkt.dts) { + pkt.pts = pkt.dts; + } + + /* Track stats for output packets */ + if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (first_video_pts == AV_NOPTS_VALUE) { + first_video_pts = pkt.pts; + first_video_dts = pkt.dts; + } + last_video_pts = pkt.pts; + last_video_dts = pkt.dts; + video_packets_written++; + } else if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (first_audio_pts == AV_NOPTS_VALUE) { + first_audio_pts = pkt.pts; + first_audio_dts = pkt.dts; + } + last_audio_pts = pkt.pts; + last_audio_dts = pkt.dts; + audio_packets_written++; + } + + av_interleaved_write_frame(out_ctx, &pkt); + av_packet_unref(&pkt); + } + + av_write_trailer(out_ctx); + av_dict_free(&opts); + + if (video_idx >= 0 && stream_mapping[video_idx] >= 0) { + video_out_time_base = out_ctx->streams[stream_mapping[video_idx]]->time_base; + } + if (audio_idx >= 0 && stream_mapping[audio_idx] >= 0) { + audio_out_time_base = out_ctx->streams[stream_mapping[audio_idx]]->time_base; + } + +done: + if (out_ctx) { + av_free(out_ctx->pb->buffer); + av_free(out_ctx->pb); + avformat_free_context(out_ctx); + } + avformat_close_input(&in_ctx); + free(stream_mapping); + + /* Now we have the segment in hb.ptr! Send it to the client via LWS. + For proper LWS async writing, we should save `hb` to per-session data and trigger WRITEABLE. + We will allocate the buffer with LWS_PRE, attach to wsi, and request WRITEABLE. */ + + struct per_session_data__lws_hls *pss = + (struct per_session_data__lws_hls *)lws_protocol_vh_priv_get( + lws_get_vhost(wsi), lws_get_protocol(wsi)); + + /* Wait, lws_protocol_vh_priv_get gets VHD. We want PSS! */ + pss = (struct per_session_data__lws_hls *)lws_wsi_user(wsi); + + if (!pss || hb.size == 0) { + free(hb.ptr); + return -1; + } + + + size_t offset = find_moof_offset(hb.ptr, hb.size); + size_t send_size = hb.size - offset; + + /* Calculate and log stats */ + double video_duration_sec = 0.0; + double audio_duration_sec = 0.0; + double start_av_delta_sec = 0.0; + double end_av_delta_sec = 0.0; + + if (first_video_pts != AV_NOPTS_VALUE && video_out_time_base.den > 0) { + video_duration_sec = (double)(last_video_pts - first_video_pts) * av_q2d(video_out_time_base); + } + if (first_audio_pts != AV_NOPTS_VALUE && audio_out_time_base.den > 0) { + audio_duration_sec = (double)(last_audio_pts - first_audio_pts) * av_q2d(audio_out_time_base); + } + if (first_video_pts != AV_NOPTS_VALUE && video_out_time_base.den > 0 && + first_audio_pts != AV_NOPTS_VALUE && audio_out_time_base.den > 0) { + double first_v_sec = (double)first_video_pts * av_q2d(video_out_time_base); + double first_a_sec = (double)first_audio_pts * av_q2d(audio_out_time_base); + double last_v_sec = (double)last_video_pts * av_q2d(video_out_time_base); + double last_a_sec = (double)last_audio_pts * av_q2d(audio_out_time_base); + start_av_delta_sec = first_a_sec - first_v_sec; + end_av_delta_sec = last_a_sec - last_v_sec; + } + + lwsl_user("HLS: Segment %d summary:\n" + " Discarded: video=%d, audio=%d\n" + " Written: video=%d, audio=%d\n" + " Video output PTS: [%lld to %lld] (diff = %lld, %.3fs)\n" + " Video output DTS: [%lld to %lld]\n" + " Audio output PTS: [%lld to %lld] (diff = %lld, %.3fs)\n" + " Audio output DTS: [%lld to %lld]\n" + " First Audio-Video PTS delta: %.3fs\n" + " Last Audio-Video PTS delta: %.3fs\n" + " Segment size: %zu bytes (sent %zu bytes)\n", + segment_idx, + video_packets_discarded, audio_packets_discarded, + video_packets_written, audio_packets_written, + (long long)first_video_pts, (long long)last_video_pts, + (long long)(last_video_pts - first_video_pts), + video_duration_sec, + (long long)first_video_dts, (long long)last_video_dts, + (long long)first_audio_pts, (long long)last_audio_pts, + (long long)(last_audio_pts - first_audio_pts), + audio_duration_sec, + (long long)first_audio_dts, (long long)last_audio_dts, + start_av_delta_sec, + end_av_delta_sec, + hb.size, send_size); + + pss->segment_buf = malloc(LWS_PRE + send_size); + if (!pss->segment_buf) { + free(hb.ptr); + return -1; + } + + memcpy(pss->segment_buf + LWS_PRE, hb.ptr + offset, send_size); + pss->segment_len = send_size; + + pss->segment_pos = 0; + free(hb.ptr); + + /* Send HTTP headers */ + uint8_t hbuf[LWS_PRE + 2048], *start = hbuf + LWS_PRE, *p = start, *end = p + 2048; + if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, "video/mp4", + (lws_filepos_t)pss->segment_len, &p, end) || + lws_finalize_write_http_header(wsi, start, &p, end)) { + return -1; + } + + /* Request writable callback to pump data */ + lws_callback_on_writable(wsi); + + return 0; /* Keep connection alive to write data */ +} diff --git a/plugins/protocol_lws_hls/hls-dir.c b/plugins/protocol_lws_hls/hls-dir.c new file mode 100644 index 0000000000..8e020af20e --- /dev/null +++ b/plugins/protocol_lws_hls/hls-dir.c @@ -0,0 +1,153 @@ +#include "private-lws-hls.h" +#include +#include + +struct file_entry { + char name[256]; + time_t mtime; +}; + +struct dir_state { + struct file_entry *entries; + size_t count; + size_t max; + const char *base_dir; +}; + +static int +hls_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +{ + struct dir_state *ds = (struct dir_state *)user; + struct stat st; + char path[1024]; + + if (!strcmp(lde->name, ".") || !strcmp(lde->name, "..")) + return 0; + + snprintf(path, sizeof(path), "%s/%s", dirpath, lde->name); + + if (lde->type == LDOT_DIR) { + lws_dir(path, ds, hls_dir_cb); + return 0; + } + + if (lde->type != LDOT_FILE) + return 0; + + /* only list media files */ + if (!strstr(lde->name, ".mp4") && !strstr(lde->name, ".mkv")) + return 0; + + if (ds->count >= ds->max) { + ds->max += 64; + ds->entries = realloc(ds->entries, ds->max * sizeof(struct file_entry)); + if (!ds->entries) + return 1; + } + + if (stat(path, &st) == 0) { + const char *rel_path = path; + size_t base_len = strlen(ds->base_dir); + if (!strncmp(path, ds->base_dir, base_len) && path[base_len] == '/') + rel_path = path + base_len + 1; + + strncpy(ds->entries[ds->count].name, rel_path, sizeof(ds->entries[ds->count].name) - 1); + ds->entries[ds->count].name[sizeof(ds->entries[ds->count].name) - 1] = '\0'; + ds->entries[ds->count].mtime = st.st_mtime; + ds->count++; + } + + return 0; +} + +static int +cmp_mtime(const void *a, const void *b) +{ + const struct file_entry *fa = (const struct file_entry *)a; + const struct file_entry *fb = (const struct file_entry *)b; + if (fb->mtime > fa->mtime) return 1; + if (fb->mtime < fa->mtime) return -1; + return 0; +} + +int +lws_hls_serve_dir(struct lws *wsi, const char *media_dir) +{ + struct dir_state ds; + memset(&ds, 0, sizeof(ds)); + ds.base_dir = media_dir; + + lws_dir(media_dir, &ds, hls_dir_cb); + + if (ds.count > 0 && ds.entries) + qsort(ds.entries, ds.count, sizeof(struct file_entry), cmp_mtime); + + /* Generate HTML */ + size_t html_size = 8192 + (ds.count * 512); + char *html = malloc(LWS_PRE + html_size); + if (!html) { + if (ds.entries) free(ds.entries); + lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); + return -1; + } + + char *p_html = html + LWS_PRE; + p_html += snprintf(p_html, html_size, + "LWS HLS Media" + "" + "" + "" + "" + "
" + "

Media Directory

"); + + for (size_t i = 0; i < ds.count; i++) { + size_t rem = html_size - (size_t)(p_html - (html + LWS_PRE)); + p_html += snprintf(p_html, rem, + "", + ds.entries[i].name, ds.entries[i].name, ds.entries[i].name); + } + + size_t rem = html_size - (size_t)(p_html - (html + LWS_PRE)); + snprintf(p_html, rem, "
"); + + size_t len = strlen(html + LWS_PRE); + + uint8_t *buf = malloc(LWS_PRE + 2048); + if (!buf) { + free(html); + if (ds.entries) free(ds.entries); + return -1; + } + + uint8_t *start = buf + LWS_PRE; + uint8_t *p = start; + uint8_t *end = p + 2048; + + if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, "text/html", + (lws_filepos_t)len, &p, end)) { + free(buf); + free(html); + if (ds.entries) free(ds.entries); + return 1; + } + + if (lws_finalize_write_http_header(wsi, start, &p, end)) { + free(buf); + free(html); + if (ds.entries) free(ds.entries); + return 1; + } + + /* Write body */ + lws_write(wsi, (uint8_t *)(html + LWS_PRE), len, LWS_WRITE_HTTP_FINAL); + + free(buf); + free(html); + if (ds.entries) free(ds.entries); + + return 1; /* Return 1 to close, we sent FINAL */ +} diff --git a/plugins/protocol_lws_hls/private-lws-hls.h b/plugins/protocol_lws_hls/private-lws-hls.h new file mode 100644 index 0000000000..0661898f12 --- /dev/null +++ b/plugins/protocol_lws_hls/private-lws-hls.h @@ -0,0 +1,111 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2026 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#if !defined (LWS_PLUGIN_STATIC) +#if !defined(LWS_DLL) +#define LWS_DLL +#endif +#if !defined(LWS_INTERNAL) +#define LWS_INTERNAL +#endif +#include +#endif + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +#include +#include +#include +#include + +#include + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +struct thumb_task { + struct thumb_task *next; + char filename[256]; +}; + +struct thumb_cache { + struct thumb_cache *next; + char filename[256]; + uint8_t *data; + size_t len; +}; + +struct per_vhost_data__lws_hls { + struct lws_context *context; + struct lws_vhost *vhost; + const struct lws_protocols *protocol; + + const char *media_dir; /* configured via pvo */ + + /* Thumbnail worker thread */ + pthread_t thumb_thread; + pthread_mutex_t lock; + pthread_cond_t cond; + int thread_exit; + + struct thumb_task *task_head; + struct thumb_task *task_tail; + + struct thumb_cache *cache_head; + int cache_count; + + struct per_session_data__lws_hls *pss_list; /* active sessions */ +}; + +struct per_session_data__lws_hls { + struct per_session_data__lws_hls *pss_list; + struct lws *wsi; + uint8_t *segment_buf; + size_t segment_len; + size_t segment_pos; + + /* Thumbnail async state */ + int waiting_for_thumbnail; + char thumb_filename[256]; +}; + +/* hls-av.c */ +void * +lws_hls_thumbnail_worker(void *d); + +int +lws_hls_serve_thumbnail(struct lws *wsi, const char *media_dir, const char *filename); +int +lws_hls_serve_dir(struct lws *wsi, const char *media_dir); + +/* hls-av.c */ +int +lws_hls_serve_thumbnail(struct lws *wsi, const char *media_dir, const char *filename); + +int +lws_hls_serve_init(struct lws *wsi, const char *media_dir, const char *filename); + +int +lws_hls_serve_manifest(struct lws *wsi, const char *media_dir, const char *filename); + +int +lws_hls_serve_segment(struct lws *wsi, const char *media_dir, const char *filename, int segment_idx); diff --git a/plugins/protocol_lws_hls/protocol_lws_hls.c b/plugins/protocol_lws_hls/protocol_lws_hls.c new file mode 100644 index 0000000000..466ef8d0cc --- /dev/null +++ b/plugins/protocol_lws_hls/protocol_lws_hls.c @@ -0,0 +1,316 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2026 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include "private-lws-hls.h" +#include + +static int +callback_lws_hls(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct per_vhost_data__lws_hls *vhd = + (struct per_vhost_data__lws_hls *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + const struct lws_protocol_vhost_options *pvo; + + struct per_session_data__lws_hls *pss = + (struct per_session_data__lws_hls *)user; + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + if (!in) + return 0; + + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), sizeof(struct per_vhost_data__lws_hls)); + if (!vhd) + return 1; + + if ((pvo = lws_pvo_search((const struct lws_protocol_vhost_options *)in, "media-dir"))) + vhd->media_dir = pvo->value; + else { + lwsl_err("%s: media-dir pvo required\n", __func__); + return 1; + } + + vhd->context = lws_get_context(wsi); + vhd->protocol = lws_get_protocol(wsi); + vhd->vhost = lws_get_vhost(wsi); + + pthread_mutex_init(&vhd->lock, NULL); + pthread_cond_init(&vhd->cond, NULL); + vhd->thread_exit = 0; + if (pthread_create(&vhd->thumb_thread, NULL, lws_hls_thumbnail_worker, vhd)) { + lwsl_err("Failed to create thumbnail thread\n"); + return 1; + } + + break; + + case LWS_CALLBACK_PROTOCOL_DESTROY: + if (!vhd) + break; + vhd->thread_exit = 1; + pthread_cond_signal(&vhd->cond); + pthread_join(vhd->thumb_thread, NULL); + pthread_mutex_destroy(&vhd->lock); + pthread_cond_destroy(&vhd->cond); + + /* free cache */ + struct thumb_cache *c = vhd->cache_head; + while (c) { + struct thumb_cache *next = c->next; + free(c->data); + free(c); + c = next; + } + + /* free task queue */ + struct thumb_task *t = vhd->task_head; + while (t) { + struct thumb_task *next = t->next; + free(t); + t = next; + } + break; + + case LWS_CALLBACK_HTTP: + { + const char *url = (const char *)in; + + if (!vhd) + return lws_callback_http_dummy(wsi, reason, user, in, len); + + lwsl_user("HLS plugin received HTTP request for '%s'\n", url ? url : "NULL"); + + if (!strcmp(url, "")) { + /* Redirect to add trailing slash */ + char uri[512]; + int ulen = lws_hdr_copy(wsi, uri, sizeof(uri) - 2, WSI_TOKEN_GET_URI); + if (ulen > 0) { + unsigned char redirect_buf[512 + LWS_PRE]; + unsigned char *p_red = redirect_buf + LWS_PRE; + unsigned char *end_red = redirect_buf + sizeof(redirect_buf) - 1; + + uri[ulen] = '/'; + uri[ulen + 1] = '\0'; + ulen++; + + int m = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + (unsigned char *)uri, ulen, &p_red, end_red); + if (m < 0) + return -1; + return lws_http_transaction_completed(wsi); + } + } + + /* Simple routing based on URL prefix */ + if (!strcmp(url, "/") || !strcmp(url, "/index.html")) { + return lws_hls_serve_dir(wsi, vhd->media_dir); + } + else if (!strncmp(url, "/preview/", 9)) { + return lws_hls_serve_thumbnail(wsi, vhd->media_dir, url + 9); + } + else if (!strncmp(url, "/stream/", 8)) { + return lws_hls_serve_manifest(wsi, vhd->media_dir, url + 8); + } + else if (!strncmp(url, "/init/", 6)) { + return lws_hls_serve_init(wsi, vhd->media_dir, url + 6); + } + else if (!strncmp(url, "/segment/", 9)) { + const char *p = url + 9; + const char *sep = strchr(p, '/'); + if (!sep) + goto err_404; + + char filename[256]; + size_t fn_len = (size_t)(sep - p); + if (fn_len >= sizeof(filename)) + goto err_404; + + strncpy(filename, p, fn_len); + filename[fn_len] = '\0'; + + int segment_idx = atoi(sep + 1); + return lws_hls_serve_segment(wsi, vhd->media_dir, filename, segment_idx); + } else { + /* Let LWS standard file serving handle static files from the mount origin */ + return lws_callback_http_dummy(wsi, reason, user, in, len); + } + + return 0; + +err_404: + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); + return -1; + } + + case LWS_CALLBACK_EVENT_WAIT_CANCELLED: + if (!vhd) + break; + /* Thread finished a thumbnail. Wake up all waiting HTTP sessions */ + lws_start_foreach_llp(struct per_session_data__lws_hls **, + ppss, vhd->pss_list) { + if ((*ppss)->waiting_for_thumbnail) + lws_callback_on_writable((*ppss)->wsi); + } lws_end_foreach_llp(ppss, pss_list); + break; + + case LWS_CALLBACK_HTTP_WRITEABLE: + if (pss && pss->waiting_for_thumbnail) { + pthread_mutex_lock(&vhd->lock); + struct thumb_cache *c = vhd->cache_head; + while (c) { + if (!strcmp(c->filename, pss->thumb_filename)) + break; + c = c->next; + } + + if (c) { + /* Found it in cache! */ + size_t len = c->len; + uint8_t *buf = malloc(LWS_PRE + len); + if (!buf) { + pthread_mutex_unlock(&vhd->lock); + return -1; + } + + uint8_t *start = buf + LWS_PRE; + uint8_t *p = start; + uint8_t *end = p + len; + + if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, "image/jpeg", + (lws_filepos_t)len, &p, end)) { + free(buf); + pthread_mutex_unlock(&vhd->lock); + return 1; + } + + if (lws_finalize_write_http_header(wsi, start, &p, end)) { + free(buf); + pthread_mutex_unlock(&vhd->lock); + return 1; + } + + memcpy(buf + LWS_PRE, c->data, len); + lws_write(wsi, buf + LWS_PRE, len, LWS_WRITE_HTTP_FINAL); + free(buf); + + pss->waiting_for_thumbnail = 0; + pthread_mutex_unlock(&vhd->lock); + + if (lws_http_transaction_completed(wsi)) + return -1; + return 0; + } + + /* Not in cache. Did it fail? */ + int is_pending = 0; + struct thumb_task *t = vhd->task_head; + while (t) { + if (!strcmp(t->filename, pss->thumb_filename)) { + is_pending = 1; + break; + } + t = t->next; + } + + pthread_mutex_unlock(&vhd->lock); + + if (!is_pending) { + /* Not pending and not in cache -> extraction failed */ + pss->waiting_for_thumbnail = 0; + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); + return -1; + } + + /* Still pending, keep waiting */ + return 0; + } + + if (!pss || !pss->segment_buf || pss->segment_pos >= pss->segment_len) + return 1; /* Done or nothing to write */ + + size_t rem = pss->segment_len - pss->segment_pos; + int flags = (pss->segment_pos + rem == pss->segment_len) ? LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP; + + int m = lws_write(wsi, pss->segment_buf + LWS_PRE + pss->segment_pos, rem, (enum lws_write_protocol)flags); + if (m < 0) { + free(pss->segment_buf); + pss->segment_buf = NULL; + return -1; + } + + pss->segment_pos += (size_t)m; + if (pss->segment_pos < pss->segment_len) { + lws_callback_on_writable(wsi); + return 0; + } + + free(pss->segment_buf); + pss->segment_buf = NULL; + return 1; /* Close connection after sending segment */ + + case LWS_CALLBACK_CLOSED_HTTP: + if (pss) { + if (vhd) + lws_ll_fwd_remove(struct per_session_data__lws_hls, pss_list, + pss, vhd->pss_list); + if (pss->segment_buf) { + free(pss->segment_buf); + pss->segment_buf = NULL; + } + } + break; + + default: + break; + } + + return 0; +} + +#define LWS_PLUGIN_PROTOCOL_LWS_HLS \ + { \ + "lws-hls", \ + callback_lws_hls, \ + sizeof(struct per_session_data__lws_hls), \ + 1024, \ + 0, NULL, 0 \ + } + +#if !defined (LWS_PLUGIN_STATIC) + +LWS_VISIBLE const struct lws_protocols lws_hls_protocols[] = { + LWS_PLUGIN_PROTOCOL_LWS_HLS +}; + +LWS_VISIBLE const lws_plugin_protocol_t lws_hls = { + .hdr = { + .name = "lws hls", + ._class = "lws_protocol_plugin", + .lws_build_hash = LWS_BUILD_HASH, + .api_magic = LWS_PLUGIN_API_MAGIC + }, + + .protocols = lws_hls_protocols, + .count_protocols = LWS_ARRAY_SIZE(lws_hls_protocols), + .extensions = NULL, + .count_extensions = 0, +}; + +#endif diff --git a/plugins/protocol_lws_login/protocol_lws_login.c b/plugins/protocol_lws_login/protocol_lws_login.c index 2316852250..4494da1686 100644 --- a/plugins/protocol_lws_login/protocol_lws_login.c +++ b/plugins/protocol_lws_login/protocol_lws_login.c @@ -644,8 +644,8 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason, } if (!vhd) { - lwsl_info("%s: ALLOWING (vhd is NULL !!! protocol init failed?)\n", __func__); - return 0; + lwsl_err("%s: DENYING (vhd is NULL !!! protocol init failed or unconfigured)\n", __func__); + return 1; } if (vhd->unauth_protocols) { diff --git a/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c b/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c index 06b72d81ab..2ea0d6f52c 100644 --- a/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c +++ b/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c @@ -39,6 +39,9 @@ callback_shared_world(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (!in) + return 0; + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct vhd__shared_world)); @@ -49,7 +52,6 @@ callback_shared_world(struct lws *wsi, enum lws_callback_reasons reason, lws_xos_init(&xos, 0x12345678); /* Simple predictable seed for PRNG to gen actual world seed */ vhd->seed = (uint32_t)lws_xos(&xos); } - lwsl_notice("Shared World Plugin Initialized, Seed: %u\n", vhd->seed); break; case LWS_CALLBACK_ESTABLISHED: diff --git a/plugins/protocol_webtransport_test/protocol_webtransport_test.c b/plugins/protocol_webtransport_test/protocol_webtransport_test.c index ed8da869d9..607c7ce2b5 100644 --- a/plugins/protocol_webtransport_test/protocol_webtransport_test.c +++ b/plugins/protocol_webtransport_test/protocol_webtransport_test.c @@ -58,6 +58,9 @@ callback_wt_test(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: + if (!in) + return 0; + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct vhd__wt_test)); @@ -87,7 +90,6 @@ callback_wt_test(struct lws *wsi, enum lws_callback_reasons reason, } lws_genhash_destroy(&hctx, vhd->hash); } - lwsl_notice("WT Test Plugin Initialized\n"); break; case LWS_CALLBACK_PROTOCOL_DESTROY: From 05f8a9921c778946cdec5abebfecd78e3cc6b6b6 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 1 Jul 2026 09:21:55 +0100 Subject: [PATCH 02/44] qir: additional tests quic-version-negotiation qir-last-tests xr-29 qir-l1-fix sai: QIR with QIR_LWS_BRANCH --- .sai.json | 14 + include/libwebsockets/lws-client.h | 4 + include/libwebsockets/lws-context-vhost.h | 12 +- include/libwebsockets/lws-quic.h | 1 + lib/core-net/client/connect.c | 8 +- lib/core-net/client/connect4.c | 8 +- lib/core-net/close.c | 9 +- lib/core-net/private-lib-core-net.h | 2 + lib/core-net/service.c | 2 - lib/core-net/vhost.c | 14 +- lib/core-net/wsi.c | 12 +- lib/core/libwebsockets.c | 10 + lib/roles/h1/ops-h1.c | 7 + lib/roles/h3/ops-h3.c | 112 +- lib/roles/http/server/lejp-conf.c | 26 + lib/roles/http/server/server.c | 11 +- lib/roles/quic/crypto-quic.c | 265 +++- lib/roles/quic/ops-quic-cc-cubic.c | 22 +- lib/roles/quic/ops-quic-cc-newreno.c | 49 +- lib/roles/quic/ops-quic.c | 1153 +++++++++++++---- lib/roles/quic/parse-quic.c | 197 ++- lib/roles/quic/private-lib-roles-quic.h | 41 +- lib/tls/gnutls/gnutls-quic.c | 60 +- lib/tls/gnutls/gnutls-session.c | 179 ++- lib/tls/gnutls/gnutls-tls.c | 132 +- lib/tls/private-network.h | 5 + lib/tls/tls-sessions.c | 2 + .../minimal-http-client-multi.c | 149 ++- 28 files changed, 2032 insertions(+), 474 deletions(-) diff --git a/.sai.json b/.sai.json index 546c355b91..9279e68d43 100644 --- a/.sai.json +++ b/.sai.json @@ -65,6 +65,15 @@ "cd build && SAI_CPACK=\"-G DEB\" ${cpack}" ] }, + + "linux-debian13/x86_64-amd/gcc": { + "default": false, + "build": [ + "${cmake}" + ] + }, + + # "netbsd/aarch64BE-bcm2837-a53/gcc": { # "default": false, # "build": [ @@ -309,6 +318,11 @@ "platforms": "none, coverity/x86_64/gcc", "cpack": "export STAMP=`git log -1 --pretty=format:%h` && rm -f libwebsockets.tar.xz && tar cJvf libwebsockets.tar.xz cov-int && script -e -q -c \"cat /etc/coverity/secrets.sh | lws-minimal-http-client-post-form --h1 https://scan.coverity.com:443/builds?project=warmcat%2Flibwebsockets --form file=@libwebsockets.tar.xz --form version=${STAMP} --form 'description=lws qa'\" /dev/null", "branches": "coverity" + }, + + "qir": { + "cmake": "cd /opt/quic-interop-runner ; source venv/bin/activate ; rm -rf logs_* ; export QIR_LWS_BRANCH=main ; cd /opt/quic-interop-runner/lws ; if [ \"`docker ps -q`\" ] ; then docker kill $(docker ps -q) ; fi ; docker system prune -a --volumes -f && docker build --build-arg QIR_LWS_BRANCH=${QIR_LWS_BRANCH} -t lws-quic-interop . --no-cache && cd .. && python3 run.py -c lws -s lws --delete-successful-logs", + "platforms": "none, linux-debian13/x86_64-amd/gcc" } # , diff --git a/include/libwebsockets/lws-client.h b/include/libwebsockets/lws-client.h index b7c572d69d..c96261b9c4 100644 --- a/include/libwebsockets/lws-client.h +++ b/include/libwebsockets/lws-client.h @@ -227,6 +227,10 @@ struct lws_client_connect_info { * to the client connection. */ + struct lws *quic_migrate_from_wsi; + /**< If set, this connection acts as a Make-Before-Break migration probe for + * the existing QUIC network connection in quic_migrate_from_wsi. */ + uint8_t priority; /**< 0 means normal priority... otherwise sets the IP priority on * packets coming from this connection, from 1 - 7. Setting 7 diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 8be14a4d13..bed02c8ac7 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -284,6 +284,12 @@ #define LWS_SERVER_OPTION_QUIC_PAD_CRYPTO (1ll << 49) /**< (VH) Pad the QUIC handshake crypto data to artificially hit anti-amplification limits */ +#define LWS_SERVER_OPTION_QUIC_EARLY_KEY_UPDATE (1ll << 51) + /**< CONTEXT: For testing. Trigger a QUIC early key update */ + +#define LWS_SERVER_OPTION_QUIC_LATEST_VERSION (1ll << 50) + /**< (CTX) Force client to initiate QUIC connections using the latest supported version (e.g. v2) */ + /****** add new things just above ---^ ******/ @@ -1102,7 +1108,11 @@ struct lws_context_creation_info { const struct lws_cc_ops *quic_cc_ops; /**< CONTEXT: QUIC congestion control algorithm ops to use. If NULL, defaults to &lws_cc_ops_newreno. */ - void *_unused[1]; /**< dummy */ + const char *quic_preferred_addresses; + /**< VHOST: Comma-separated list of IPv4 and/or IPv6 preferred addresses (e.g., "1.2.3.4:443,[2001:db8::1]:443") + * to send to the client during the handshake to facilitate Connection Migration. */ + + void *_unused[0]; /**< dummy */ }; /** diff --git a/include/libwebsockets/lws-quic.h b/include/libwebsockets/lws-quic.h index 717ba108f8..8580bd536a 100644 --- a/include/libwebsockets/lws-quic.h +++ b/include/libwebsockets/lws-quic.h @@ -159,6 +159,7 @@ struct lws_cc_ops { void (*on_sent)(struct lws *nwsi, size_t bytes); void (*on_ack)(struct lws *nwsi, size_t bytes_acked, lws_usec_t rtt); void (*on_loss)(struct lws *nwsi, size_t bytes_lost); + void (*on_discard)(struct lws *nwsi, size_t bytes_discarded); int (*can_send)(struct lws *nwsi, size_t bytes); lws_usec_t (*get_pacing_delay)(struct lws *nwsi, size_t bytes_to_send); }; diff --git a/lib/core-net/client/connect.c b/lib/core-net/client/connect.c index 0223acf6b3..ba48f6ba09 100644 --- a/lib/core-net/client/connect.c +++ b/lib/core-net/client/connect.c @@ -208,6 +208,10 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) wsi->client_proxy_onward = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD); #endif +#if defined(LWS_ROLE_QUIC) + wsi->quic.migrate_from_wsi = i->quic_migrate_from_wsi; +#endif + #if defined(LWS_WITH_SYS_FAULT_INJECTION) wsi->fic.name = "wsi"; if (i->fic.fi_owner.count) @@ -250,6 +254,8 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) wsi->keep_warm_secs = 5; wsi->flags = i->ssl_connection; + if (i->context->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA) + wsi->flags |= LCCSCF_ALLOW_EARLY_DATA; wsi->c_pri = i->priority; @@ -347,7 +353,7 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) } #if defined(LWS_WITH_TLS) - wsi->tls.use_ssl = (unsigned int)i->ssl_connection; + wsi->tls.use_ssl = (unsigned int)wsi->flags; #else if (i->ssl_connection & LCCSCF_USE_SSL) { lwsl_wsi_err(wsi, "lws not configured for tls"); diff --git a/lib/core-net/client/connect4.c b/lib/core-net/client/connect4.c index 8d7eb750c9..cef4e434eb 100644 --- a/lib/core-net/client/connect4.c +++ b/lib/core-net/client/connect4.c @@ -135,7 +135,7 @@ lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback, send_hs: -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3) if (wsi_piggyback && !lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) { /* @@ -250,7 +250,11 @@ lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback, #if defined(LWS_ROLE_QUIC) if ((meth && !strcmp(meth, "QUIC")) || !strcmp(wsi->role_ops->name, "quic")) { - lwsi_set_state(wsi, LRS_WAITING_SSL); + if (wsi->quic.migrate_from_wsi) { + lwsi_set_state(wsi, LRS_ESTABLISHED); + } else { + lwsi_set_state(wsi, LRS_WAITING_SSL); + } wsi->hdr_parsing_completed = 1; lws_callback_on_writable(wsi); return wsi; diff --git a/lib/core-net/close.c b/lib/core-net/close.c index 246a672af6..725fd4cd6a 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -507,7 +507,14 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, #if defined(LWS_WITH_CLIENT) #if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_WITH_GNUTLS) - lws_tls_session_new_gnutls(wsi); + /* + * For multiplexed connections (H3/H2), the TLS session belongs + * to the network-level WSI, not to individual stream WSIs. Don't + * perform the (expensive) session ticket snapshot on every mux + * substream close — only on the actual network wsi. + */ + if (!wsi->client_mux_substream) + lws_tls_session_new_gnutls(wsi); #endif lws_free_set_NULL(wsi->cli_hostname_copy); diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 2898a5026f..902efccd82 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -540,6 +540,8 @@ struct lws_vhost { uint64_t options; + const char *quic_preferred_addresses; + struct lws_context *context; struct lws_vhost *vhost_next; diff --git a/lib/core-net/service.c b/lib/core-net/service.c index 2d1ffbf062..4917dacbf7 100644 --- a/lib/core-net/service.c +++ b/lib/core-net/service.c @@ -677,8 +677,6 @@ lws_service_flag_pending(struct lws_context *context, int tsi) if (!lws_is_flowcontrolled(wsi) && lwsi_state(wsi) != LRS_DEFERRING_ACTION && - lwsi_state(wsi) != LRS_ISSUING_FILE && - lwsi_state(wsi) != LRS_DOING_TRANSACTION && lwsi_state(wsi) != LRS_AWAITING_FILE_READ && lwsi_state(wsi) != LRS_AWAITING_SSL_ACCEPT) { forced = 1; diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c index f5eea5048b..c1885d904a 100644 --- a/lib/core-net/vhost.c +++ b/lib/core-net/vhost.c @@ -835,8 +835,9 @@ lws_create_vhost(struct lws_context *context, vh->count_protocols++) ; - vh->options = info->options; - vh->pvo = info->pvo; + vh->options = info->options; + vh->quic_preferred_addresses = info->quic_preferred_addresses; + vh->pvo = info->pvo; #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) vh->headers = info->headers; #endif @@ -879,8 +880,8 @@ lws_create_vhost(struct lws_context *context, if (info->tls1_3_plus_cipher_list) vh->tls.cfg_tls1_3_plus_cipher_list = lws_strdup(info->tls1_3_plus_cipher_list); #if defined(LWS_WITH_CLIENT) - if (info->client_ssl_cipher_list) - vh->tls.cfg_tls_client_cipher_list = lws_strdup(info->client_ssl_cipher_list); + if (info->client_ssl_cipher_list) + vh->tls.cfg_tls_client_cipher_list = lws_strdup(info->client_ssl_cipher_list); #endif if (info->tls_ciphers_iana) vh->tls.cfg_tls_ciphers_iana = lws_strdup(info->tls_ciphers_iana); @@ -2101,7 +2102,7 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin) * h2: if in usable state already: just use it without * going through the queue */ - if (w->client_h2_alpn && w->client_mux_migrated && + if (lwsi_role_h2(w) && w->client_h2_alpn && w->client_mux_migrated && (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS || lwsi_state(w) == LRS_ESTABLISHED || lwsi_state(w) == LRS_IDLING)) { @@ -2130,7 +2131,7 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin) * h3: if in usable state already: just use it without * going through the queue */ - if (lwsi_role_h3(w) && w->client_h2_alpn && w->client_mux_migrated && + if ((w->role_ops && !strcmp(w->role_ops->name, "quic")) && w->client_h2_alpn && w->client_mux_migrated && (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS || lwsi_state(w) == LRS_ESTABLISHED || lwsi_state(w) == LRS_IDLING)) { @@ -2138,6 +2139,7 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin) lwsl_wsi_info(w, "just join h3 directly 0x%x", lwsi_state(w)); + if (lwsi_state(w) == LRS_IDLING) _lws_generic_transaction_completed_active_conn(&w, 0); diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index f5e2f3592a..f5fec8af7d 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -608,8 +608,7 @@ int lws_has_buffered_out(struct lws *wsi) { lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->pending_tx[i].head) { struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) || - (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid) || - f->type == LWS_QUIC_FT_DATA_BLOCKED) { + (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) { lwsl_notice("lws_has_buffered_out: %s has pending_tx stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid); return 1; } @@ -618,8 +617,7 @@ int lws_has_buffered_out(struct lws *wsi) { lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->in_flight[i].head) { struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) || - (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid) || - f->type == LWS_QUIC_FT_DATA_BLOCKED) { + (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) { lwsl_notice("lws_has_buffered_out: %s has in_flight stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid); return 1; } @@ -1511,7 +1509,7 @@ int lws_wsi_mux_mark_parents_needing_writeable(struct lws *wsi) { wsi2 = wsi; while (wsi2) { wsi2->mux.requested_POLLOUT = 1; - lwsl_wsi_info(wsi2, "sid %u, pending writable", wsi2->mux.my_sid); + lwsl_wsi_debug(wsi2, "sid %u, pending writable", wsi2->mux.my_sid); wsi2 = wsi2->mux.parent_wsi; } @@ -1650,6 +1648,8 @@ int lws_wsi_mux_apply_queue(struct lws *wsi) { lwsl_wsi_notice(wsi, "evaluating queued conn %s (state 0x%x, par role %s)", lws_wsi_tag(w), lwsi_state(w), wsi->role_ops ? wsi->role_ops->name : "none"); + lwsl_notice("AGY-MUX: parent role=%s, child state=0x%x (expected 0x%x)\n", + wsi->role_ops ? wsi->role_ops->name : "none", lwsi_state(w), LRS_H2_WAITING_TO_SEND_HEADERS); #if defined(LWS_ROLE_H2) if (lwsi_role_h2(wsi) && @@ -1669,7 +1669,7 @@ int lws_wsi_mux_apply_queue(struct lws *wsi) { #endif #if defined(LWS_ROLE_H3) - if ((wsi->role_ops && !strcmp(wsi->role_ops->name, "quic")) && + if ((wsi->role_ops && (!strcmp(wsi->role_ops->name, "quic") || !strcmp(wsi->role_ops->name, "h3"))) && lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS) { if (!lws_wsi_h3_can_adopt(wsi)) { diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index fed5020bca..cb7f199301 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -1602,6 +1602,8 @@ static const struct lws_switches builtins[] = { { "--h3", "Force HTTP/3 (client)" }, { "--cpd-bypass", "Bypass captive portal detect" }, { "--quic-pad-crypto", "Pad QUIC Handshake Crypto for tests" }, + { "--quic-only-latest", "Force QUIC to use the latest supported version (e.g. v2)" }, + { "--0rtt", "Allow QUIC 0-RTT early data" }, { "-h", "Print this help" }, { "--help", "Print this help" }, }; @@ -1620,6 +1622,8 @@ enum opts { OPT_H3, OPT_CPD_BYPASS, OPT_QUIC_PAD_CRYPTO, + OPT_QUIC_ONLY_LATEST, + OPT_0RTT, OPT_HELP1, OPT_HELP2, }; @@ -1897,6 +1901,12 @@ lws_cmdline_option_handle_builtin(int argc, const char **argv, case OPT_QUIC_PAD_CRYPTO: info->options |= LWS_SERVER_OPTION_QUIC_PAD_CRYPTO; break; + case OPT_QUIC_ONLY_LATEST: + info->options |= LWS_SERVER_OPTION_QUIC_LATEST_VERSION; + break; + case OPT_0RTT: + info->options |= LWS_SERVER_OPTION_ALLOW_EARLY_DATA; + break; case OPT_HELP1: case OPT_HELP2: diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 63beaab780..52e4248988 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -111,6 +111,13 @@ lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len) case LRS_DOING_TRANSACTION: if (!lwsi_role_h1(wsi)) break; + /* + * Consume and discard spurious pipelined data + * while we are actively sending a response. + * This avoids CPU spins from stashing it, and + * allows the current transaction to complete. + */ + buf += len; return lws_ptr_diff(buf, oldbuf); case LRS_DISCARD_BODY: case LRS_BODY: diff --git a/lib/roles/h3/ops-h3.c b/lib/roles/h3/ops-h3.c index f5735bde1d..22305a511c 100644 --- a/lib/roles/h3/ops-h3.c +++ b/lib/roles/h3/ops-h3.c @@ -167,8 +167,40 @@ lws_h3_client_handshake(struct lws *wsi) static int rops_perform_user_POLLOUT_h3(struct lws *wsi) { +#if defined(LWS_WITH_HTTP2) + if (wsi->h2.pending_status_body) { + int n = lws_write(wsi, (uint8_t *)wsi->h2.pending_status_body + + LWS_PRE, + strlen(wsi->h2.pending_status_body + + LWS_PRE), LWS_WRITE_HTTP_FINAL); + (void)n; + lws_free_set_NULL(wsi->h2.pending_status_body); + lwsl_wsi_notice(wsi, "closing stream after sending pending status body"); + return -1; + } +#endif + lwsl_wsi_info(wsi, "rops_perform_user_POLLOUT_h3: entry, state=%d", lwsi_state(wsi)); + /* + * For QUIC/H3, lws_has_buffered_out() also checks QUIC pending_tx + * for stream frames. But those are drained by the QUIC POLLOUT + * handler, not by lws_issue_raw(). Only check buflist_out here — + * blocking on QUIC pending_tx prevents the H3 state machine from + * ever progressing past this point for client streams whose HEADERS + * STREAM frames are still in the QUIC TX queue. + */ + if (wsi->buflist_out) { + lwsl_wsi_debug(wsi, "%s: completing partial", __func__); + if (lws_issue_raw(wsi, NULL, 0) < 0) { + lwsl_wsi_info(wsi, "%s signalling to close", __func__); + wsi->socket_is_permanently_unusable = 1; + return -1; + } + if (wsi->buflist_out) + return 0; + } + #if defined(LWS_WITH_SERVER) if (wsi->http.deferred_transaction_completed) { if (!lws_has_buffered_out(wsi)) { @@ -177,6 +209,9 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi) wsi->socket_is_permanently_unusable = 1; return -1; } + } else { + lws_callback_on_writable(wsi); + wsi->mux.requested_POLLOUT = 1; } return 0; } @@ -187,6 +222,8 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi) wsi->socket_is_permanently_unusable = 1; return -1; } + lws_callback_on_writable(wsi); + wsi->mux.requested_POLLOUT = 1; return 0; } @@ -244,6 +281,16 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi) tx_credit(wsi, LWSTXCR_US_TO_PEER, 0); } if (lws_wsi_txc_check_skint(&wsi->txc, usable_credit)) { + /* + * No TX credit — either QUIC peer flow control is + * exhausted, or the pending_tx buffer throttle (64KB cap + * in rops_tx_credit_quic) is full. Call + * lws_callback_on_writable() which for QUIC will invoke + * lws_wsi_mux_mark_parents_needing_writeable(), propagating + * requested_POLLOUT up the full parent chain to nwsi so the + * post-ACK wakeup loop can find and restart us. + */ + lws_callback_on_writable(wsi); return 0; } @@ -261,15 +308,8 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi) return -1; } if (!n) { - int32_t usable_credit2 = wsi->txc.tx_cr; - if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_tx_credit)) { - usable_credit2 = lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_tx_credit). - tx_credit(wsi, LWSTXCR_US_TO_PEER, 0); - } - if (usable_credit2 > 0) { - lws_callback_on_writable(wsi); - wsi->mux.requested_POLLOUT = 1; - } + lws_callback_on_writable(wsi); + wsi->mux.requested_POLLOUT = 1; } return 0; @@ -324,9 +364,14 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi) lwsl_debug("H3_TRACE: wsi %p lws_http_action failed, returning %d\n", wsi, n); return -1; } - if (n > 0) { + if (n > 0 +#if defined(LWS_WITH_HTTP2) + && !wsi->h2.pending_status_body +#endif + ) { lwsl_wsi_notice(wsi, "closing stream after h3 action completed (%d)", n); - return -1; + lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE); + return 0; } lwsl_debug("H3_TRACE: wsi %p lws_http_action returned 0 (success)\n", wsi); @@ -334,6 +379,12 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi) lws_header_table_detach(wsi, 0); lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + /* We are returning 0 because the file might be read synchronously and FIN queued, + * but not sent yet. We need to preserve requested_POLLOUT so it gets polled + * again and transitions properly. */ + lws_callback_on_writable(wsi); + wsi->mux.requested_POLLOUT = 1; + return 0; } #endif @@ -1002,7 +1053,8 @@ lws_quic_enter_closing_state(nwsi, LWS_QPACK_ENCODER_STREAM_ERROR, 0, 1); len -= chunk; /* Replenish flow control window */ - // lwsl_notice("H3 RX: Replenishing %d bytes", (int)chunk); lws_wsi_tx_credit(wsi, LWSTXCR_PEER_TO_US, (int)chunk); + // lwsl_notice("H3 RX: Replenishing %d bytes", (int)chunk); + lws_wsi_tx_credit(wsi, LWSTXCR_PEER_TO_US, (int)chunk); if (wsi->h3.rx_frame_payload_read == wsi->h3.rx_frame_len) { if (wsi->h3.stream_type == 0x00 && wsi->h3.rx_frame_type == 0x04) { @@ -1199,18 +1251,23 @@ rops_alpn_negotiated_h3(struct lws *wsi, const char *alpn) wsi->h3.h3n = nwsi->h3.h3n; wsi->h3.qpack_tx_encoder = nwsi->h3.qpack_tx_encoder; - /* If we are the network wsi, we must notify our children! */ - if (wsi == nwsi) { + /* Notify all children and update their h3n */ + { struct lws *child = nwsi->mux.child_list; lwsl_wsi_info(wsi, "H3 ALPN Negotiated, child_list=%p", child); while (child) { lwsl_wsi_info(child, "H3 ALPN child state=%d", lwsi_state(child)); + child->h3.h3n = nwsi->h3.h3n; + child->h3.qpack_tx_encoder = nwsi->h3.qpack_tx_encoder; + if (lwsi_state(child) == LRS_UNCONNECTED || lwsi_state(child) == LRS_WAITING_CONNECT) { lwsl_wsi_info(child, "H3 ALPN Negotiated, transitioning child"); lws_role_transition(child, lwsi_role_client(nwsi) ? LWSIFR_CLIENT : LWSIFR_SERVER, LRS_H2_WAITING_TO_SEND_HEADERS, &role_ops_h3); - child->h3.h3n = nwsi->h3.h3n; - child->h3.qpack_tx_encoder = nwsi->h3.qpack_tx_encoder; lws_callback_on_writable(child); + } else if (child->role_ops && !strcmp(child->role_ops->name, "quic")) { + /* Server-side peer-initiated stream adopted during 0-RTT! Transition it to H3 now. */ + lwsl_wsi_info(child, "H3 ALPN Negotiated, transitioning 0-RTT child to H3"); + lws_role_transition(child, lwsi_role_client(nwsi) ? LWSIFR_CLIENT : LWSIFR_SERVER, LRS_ESTABLISHED, &role_ops_h3); } child = child->mux.sibling_list; } @@ -1328,8 +1385,12 @@ rops_write_role_protocol_h3(struct lws *wsi, unsigned char *buf, size_t len, { struct lws *nwsi = lws_get_quic_network_wsi(wsi); - if (nwsi && lws_rops_fidx(nwsi->role_ops, LWS_ROPS_write_role_protocol)) { - n = lws_rops_func_fidx(nwsi->role_ops, LWS_ROPS_write_role_protocol). + const struct lws_role_ops *role = nwsi ? nwsi->role_ops : NULL; + if (role && !strcmp(role->name, "h3")) { + role = lws_role_by_name("quic"); + } + if (nwsi && role && lws_rops_fidx(role, LWS_ROPS_write_role_protocol)) { + n = lws_rops_func_fidx(role, LWS_ROPS_write_role_protocol). write_role_protocol(wsi, (is_http || is_headers) ? pre : buf, len, wp); if (n <= 0) return n; @@ -1353,9 +1414,8 @@ rops_callback_on_writable_h3(struct lws *wsi) { struct lws *nwsi = lws_get_quic_network_wsi(wsi); - if (wsi->mux.requested_POLLOUT) { + if (wsi->mux.requested_POLLOUT) lwsl_debug("already pending writable\n"); - } lws_wsi_mux_mark_parents_needing_writeable(wsi); @@ -1512,8 +1572,13 @@ lws_wsi_h3_can_adopt(struct lws *parent_wsi) if (next_id == 0) next_id = 4; - if (next_id / 4 >= qn->max_streams_bidi_remote) - return 0; /* limit reached */ + if (next_id / 4 >= qn->max_streams_bidi_remote) { + /* Allow if we are doing 0-RTT but haven't received/cached the peer's limit */ + if (qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED && !qn->max_streams_bidi_remote) + return 1; + + return 0; /* limit reached */ + } return 1; } @@ -1565,6 +1630,9 @@ lws_wsi_h3_adopt(struct lws *parent_wsi, struct lws *wsi) wsi->client_h2_alpn = 1; #endif + if (!qn->is_server && qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED) + wsi->quic.qs->opted_into_early_data = 1; + lws_wsi_mux_insert(wsi, nwsi, (unsigned int)sid); /* Initialize flow control credits */ diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c index dfb8a6e1ee..b96b7bd072 100644 --- a/lib/roles/http/server/lejp-conf.c +++ b/lib/roles/http/server/lejp-conf.c @@ -51,6 +51,9 @@ static const char * const paths_global[] = { "global.rlimit-nofile", "global.cpd-bypass", "global.quic-pad-crypto", + "global.allow-early-data", + "global.quic-only-latest", + "global.quic-early-key-update", }; enum lejp_global_paths { @@ -73,6 +76,9 @@ enum lejp_global_paths { LWJPGP_FD_LIMIT_PT, LEJPGP_CPD_BYPASS, LEJPGP_QUIC_PAD_CRYPTO, + LEJPGP_ALLOW_EARLY_DATA, + LEJPGP_QUIC_ONLY_LATEST, + LEJPGP_QUIC_EARLY_KEY_UPDATE, }; static const char * const paths_vhosts[] = { @@ -168,6 +174,7 @@ static const char * const paths_vhosts[] = { "vhosts[].dht[]", #endif "vhosts[].quic-mtu", + "vhosts[].quic-preferred-addresses", }; enum lejp_vhost_paths { @@ -265,6 +272,7 @@ enum lejp_vhost_paths { LEJPVP_DHT, #endif LEJPVP_QUIC_MTU, + LEJPVP_QUIC_PREFERRED_ADDRESSES, }; #define MAX_PLUGIN_DIRS 10 @@ -405,6 +413,18 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason) if (arg_to_bool(ctx->buf)) a->info->options |= LWS_SERVER_OPTION_QUIC_PAD_CRYPTO; return 0; + case LEJPGP_ALLOW_EARLY_DATA: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_ALLOW_EARLY_DATA; + return 0; + case LEJPGP_QUIC_ONLY_LATEST: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_QUIC_LATEST_VERSION; + return 0; + case LEJPGP_QUIC_EARLY_KEY_UPDATE: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_QUIC_EARLY_KEY_UPDATE; + return 0; case LEJPGP_SERVER_STRING: #if defined(LWS_WITH_SERVER) a->info->server_string = a->p; @@ -498,6 +518,8 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN | LWS_SERVER_OPTION_LIBEVENT | LWS_SERVER_OPTION_QUIC_PAD_CRYPTO | + LWS_SERVER_OPTION_ALLOW_EARLY_DATA | + LWS_SERVER_OPTION_QUIC_EARLY_KEY_UPDATE | LWS_SERVER_OPTION_LIBEV ); #if defined(LWS_WITH_SERVER) @@ -1174,6 +1196,10 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) case LEJPVP_QUIC_MTU: a->info->quic_mtu = (uint32_t)atoi(ctx->buf); return 0; + case LEJPVP_QUIC_PREFERRED_ADDRESSES: + a->info->quic_preferred_addresses = a->p; + lwsl_notice("Parsed quic-preferred-addresses: %s\n", a->p); + break; #endif case LEJPVP_LISTEN_ACCEPT_ROLE: diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index b61eaf452f..d0ad29b265 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -787,7 +787,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin, #endif int spin = 0; #endif - char path[256], sym[2048]; + char path[1024], sym[2048]; unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p; unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE; #if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS) @@ -3604,11 +3604,16 @@ int lws_serve_http_file_fragment(struct lws *wsi) if (!txc) { /* - * We shouldn't've been able to get the - * WRITEABLE if we are skint + * tx credit is 0. This can happen because the + * QUIC pending_tx buffer throttle (64KB cap) kicked + * in *between* the POLLOUT handler's check and here. + * lws_callback_on_writable propagates requested_POLLOUT + * up the full parent chain so the post-ACK wakeup loop + * can restart us once the buffer drains. */ lwsl_notice("%s: %s: no tx credit\n", __func__, lws_wsi_tag(wsi)); + lws_callback_on_writable(wsi); return 0; } diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index 35f9aef7a4..69f528fabe 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -25,6 +25,8 @@ #include "private-lib-core.h" #include "roles/quic/private-lib-roles-quic.h" +extern const struct lws_role_ops role_ops_h3; + static const uint8_t quic_v1_initial_salt[20] = { 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, @@ -93,7 +95,7 @@ lws_quic_derive_initial_keys(struct lws *wsi, const struct lws_quic_cid *dcid) if (!k) return -1; - if (wsi->quic.qn->version == LWS_QUIC_VERSION_2) { + if (wsi->quic.qn->original_version == LWS_QUIC_VERSION_2) { salt = quic_v2_initial_salt; salt_len = sizeof(quic_v2_initial_salt); } else { @@ -166,16 +168,23 @@ lws_quic_initiate_key_update(struct lws *wsi) qn = nwsi->quic.qn; /* We only update keys if the handshake is done and we have APP keys */ - if (!qn->handshake_done || !qn->keys[LWS_QUIC_LEVEL_APP]) + if (!qn->handshake_done || !qn->keys[LWS_QUIC_LEVEL_APP]) { + lwsl_wsi_notice(wsi, "QUIC Key Update failed: handshake_done=%d, app_keys=%p", + qn->handshake_done, qn->keys[LWS_QUIC_LEVEL_APP]); return -1; + } /* If an update is already pending, wait for it to be echoed/completed */ - if (qn->key_update_pending) + if (qn->key_update_pending) { + lwsl_wsi_notice(wsi, "QUIC Key Update ignored: pending"); return -1; + } /* Derive the new TX keys */ - if (lws_quic_update_keys(qn->keys[LWS_QUIC_LEVEL_APP], 0)) + if (lws_quic_update_keys(qn->keys[LWS_QUIC_LEVEL_APP], 0)) { + lwsl_wsi_err(wsi, "QUIC Key Update failed: lws_quic_update_keys error"); return -1; + } /* Flip the TX key phase bit */ qn->tx_key_phase ^= 1; @@ -186,6 +195,8 @@ lws_quic_initiate_key_update(struct lws *wsi) /* Reset the packet counter for AEAD limits */ qn->tx_packets_since_update = 0; + lwsl_wsi_notice(wsi, "QUIC TX: Key Update Initiated! tx_key_phase is now %d", qn->tx_key_phase); + return 0; } @@ -213,22 +224,27 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin case LWS_TLS_QUIC_SECRET_CLIENT_EARLY: level = LWS_QUIC_LEVEL_EARLY; is_rx = qn->is_server ? 1 : 0; + lwsl_notice("lws_quic_set_keys: CLIENT_EARLY (is_rx=%d, len=%d)\n", is_rx, (int)secret_len); break; case LWS_TLS_QUIC_SECRET_CLIENT_HANDSHAKE: level = LWS_QUIC_LEVEL_HANDSHAKE; is_rx = qn->is_server ? 1 : 0; + lwsl_notice("lws_quic_set_keys: CLIENT_HANDSHAKE (is_rx=%d, len=%d)\n", is_rx, (int)secret_len); break; case LWS_TLS_QUIC_SECRET_SERVER_HANDSHAKE: level = LWS_QUIC_LEVEL_HANDSHAKE; is_rx = qn->is_server ? 0 : 1; + lwsl_notice("lws_quic_set_keys: SERVER_HANDSHAKE (is_rx=%d, len=%d)\n", is_rx, (int)secret_len); break; case LWS_TLS_QUIC_SECRET_CLIENT_APPLICATION: level = LWS_QUIC_LEVEL_APP; is_rx = qn->is_server ? 1 : 0; + lwsl_notice("lws_quic_set_keys: CLIENT_APP (is_rx=%d, len=%d)\n", is_rx, (int)secret_len); break; case LWS_TLS_QUIC_SECRET_SERVER_APPLICATION: level = LWS_QUIC_LEVEL_APP; is_rx = qn->is_server ? 0 : 1; + lwsl_notice("lws_quic_set_keys: SERVER_APP (is_rx=%d, len=%d)\n", is_rx, (int)secret_len); break; default: return -1; @@ -238,6 +254,11 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin k = lws_zalloc(sizeof(*k), "quic_keys"); if (!k) return -1; qn->keys[level] = k; + + /* Inherit packet number spaces between 0-RTT and 1-RTT */ + if (level == LWS_QUIC_LEVEL_APP && qn->keys[LWS_QUIC_LEVEL_EARLY]) { + k->pn_tx = qn->keys[LWS_QUIC_LEVEL_EARLY]->pn_tx; + } } else { k = qn->keys[level]; } @@ -266,23 +287,66 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin if (type == LWS_TLS_QUIC_SECRET_CLIENT_EARLY && !qn->is_server) { qn->early_data_status = LWS_0RTT_STATUS_ATTEMPTED; - /* The initial stream is currently also the network wsi, because ALPN - * hasn't migrated it yet. If it returns 1, it opts into 0-RTT. */ - if (wsi->a.protocol && wsi->a.protocol->callback) { - int ret = wsi->a.protocol->callback(wsi, - LWS_CALLBACK_CLIENT_ESTABLISHED_EARLY, - wsi->user_space, NULL, 0); - if (ret == 1) { - lwsl_wsi_notice(wsi, "Stream %s opted into 0-RTT", lws_wsi_tag(wsi)); - if (wsi->quic.qs) - wsi->quic.qs->opted_into_early_data = 1; - lws_callback_on_writable(wsi); - } else { - lwsl_wsi_notice(wsi, "Stream %s ignored 0-RTT", lws_wsi_tag(wsi)); + /* Trigger ALPN migration immediately to create nwsi and transition wsi to h3 */ + lws_role_call_alpn_negotiated(wsi, "h3"); + + struct lws *nwsi = lws_get_network_wsi(wsi); + if (nwsi) { + lws_wsi_mux_apply_queue(nwsi); + struct lws *w = nwsi->mux.child_list; + while (w) { + if (w->a.protocol && w->a.protocol->callback) { + int ret = w->a.protocol->callback(w, + LWS_CALLBACK_CLIENT_ESTABLISHED_EARLY, + w->user_space, NULL, 0); + if (ret == 1) { + lwsl_wsi_notice(w, "Stream %s opted into 0-RTT", lws_wsi_tag(w)); + + if (!w->quic.qs) { + w->quic.qs = lws_zalloc(sizeof(*w->quic.qs), "quic stream"); + if (w->quic.qs) { + w->quic.qs->wsi = w; + w->quic.qs->stream_id = (w == wsi) ? 0 : w->mux.my_sid; + w->quic.qs->rx_max_data = LWS_QUIC_DEFAULT_WINDOW; + w->quic.qs->advertised_rx_max_data = LWS_QUIC_DEFAULT_WINDOW; + w->quic.qs->rx_window_size = LWS_QUIC_DEFAULT_WINDOW; + w->quic.qs->last_rx_update_us = lws_now_usecs(); + } + } + + lws_role_transition(w, LWSIFR_CLIENT, LRS_H2_WAITING_TO_SEND_HEADERS, &role_ops_h3); + + if (w->quic.qs) + w->quic.qs->opted_into_early_data = 1; + + lws_callback_on_writable(w); + } else { + lwsl_wsi_notice(w, "Stream %s ignored 0-RTT", lws_wsi_tag(w)); + } + } + w = w->mux.sibling_list; } } } else if (type == LWS_TLS_QUIC_SECRET_CLIENT_EARLY && qn->is_server) { qn->early_data_status = LWS_0RTT_STATUS_ACCEPTED; + + /* On server, migrate connection to H3 immediately to support 0-RTT stream adoption */ + const unsigned char *prot = NULL; + unsigned int plen = 0; +#if defined(LWS_WITH_GNUTLS) + gnutls_datum_t dt; + if (gnutls_alpn_get_selected_protocol(wsi->tls.ssl, &dt) >= 0) { + prot = dt.data; + plen = dt.size; + } +#endif + if (plen) { + lws_strncpy(wsi->alpn, (const char *)prot, plen + 1); + } else { + lws_strncpy(wsi->alpn, "h3", sizeof(wsi->alpn)); + } + lwsl_wsi_notice(wsi, "QUIC Server 0-RTT ALPN: %s", wsi->alpn); + lws_role_call_alpn_negotiated(wsi, wsi->alpn); } return 0; @@ -311,26 +375,38 @@ lws_quic_update_keys(struct lws_quic_keys *k, int is_rx) size_t key_len = (k->cipher_type == 0) ? 16 : 32; if (is_rx) { - enum lws_genhmac_types hash_type = (k->secret_len == 48) ? LWS_GENHMAC_TYPE_SHA384 : LWS_GENHMAC_TYPE_SHA256; - if (lws_genhkdf_expand_label(hash_type, k->secret_rx, k->secret_len, "quic ku", NULL, 0, new_secret, k->secret_len)) return -1; - memcpy(k->secret_rx, new_secret, k->secret_len); - - if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic key", NULL, 0, k->key_aead_rx, key_len)) return -1; - if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic iv", NULL, 0, k->iv_rx, 12)) return -1; - - k->el_aead_rx.buf = k->key_aead_rx; - k->el_aead_rx.len = (uint32_t)key_len; - } else { - enum lws_genhmac_types hash_type = (k->secret_len == 48) ? LWS_GENHMAC_TYPE_SHA384 : LWS_GENHMAC_TYPE_SHA256; - if (lws_genhkdf_expand_label(hash_type, k->secret_tx, k->secret_len, "quic ku", NULL, 0, new_secret, k->secret_len)) return -1; - memcpy(k->secret_tx, new_secret, k->secret_len); - - if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic key", NULL, 0, k->key_aead_tx, key_len)) return -1; - if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic iv", NULL, 0, k->iv_tx, 12)) return -1; - - k->el_aead_tx.buf = k->key_aead_tx; - k->el_aead_tx.len = (uint32_t)key_len; - } + enum lws_genhmac_types hash_type = (k->secret_len == 48) ? LWS_GENHMAC_TYPE_SHA384 : LWS_GENHMAC_TYPE_SHA256; + if (lws_genhkdf_expand_label(hash_type, k->secret_rx, k->secret_len, "quic ku", NULL, 0, new_secret, k->secret_len)) return -1; + memcpy(k->secret_rx, new_secret, k->secret_len); + + if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic key", NULL, 0, k->key_aead_rx, key_len)) return -1; + if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic iv", NULL, 0, k->iv_rx, 12)) return -1; + + k->el_aead_rx.buf = k->key_aead_rx; + k->el_aead_rx.len = (uint32_t)key_len; +#if defined(LWS_WITH_GNUTLS) + if (k->aead_rx) { + gnutls_aead_cipher_deinit((gnutls_aead_cipher_hd_t)k->aead_rx); + k->aead_rx = NULL; + } +#endif + } else { + enum lws_genhmac_types hash_type = (k->secret_len == 48) ? LWS_GENHMAC_TYPE_SHA384 : LWS_GENHMAC_TYPE_SHA256; + if (lws_genhkdf_expand_label(hash_type, k->secret_tx, k->secret_len, "quic ku", NULL, 0, new_secret, k->secret_len)) return -1; + memcpy(k->secret_tx, new_secret, k->secret_len); + + if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic key", NULL, 0, k->key_aead_tx, key_len)) return -1; + if (lws_genhkdf_expand_label(hash_type, new_secret, k->secret_len, "quic iv", NULL, 0, k->iv_tx, 12)) return -1; + + k->el_aead_tx.buf = k->key_aead_tx; + k->el_aead_tx.len = (uint32_t)key_len; +#if defined(LWS_WITH_GNUTLS) + if (k->aead_tx) { + gnutls_aead_cipher_deinit((gnutls_aead_cipher_hd_t)k->aead_tx); + k->aead_tx = NULL; + } +#endif + } lws_explicit_bzero(new_secret, sizeof(new_secret)); return 0; @@ -344,16 +420,19 @@ lws_quic_unmask_header(struct lws_quic_keys *keys, uint8_t *packet, size_t packe uint8_t pn_len; if (sample_offset + 16 > packet_len) - return -1; /* Truncated packet */ + { lwsl_notice("unmask: Truncated\n"); return -1; } memcpy(sample, &packet[sample_offset], 16); if (keys->cipher_type == 0 || keys->cipher_type == 2) { /* AES-GCM uses AES-ECB for Header Protection */ struct lws_genaes_ctx hp; - if (lws_genaes_create(&hp, LWS_GAESO_ENC, LWS_GAESM_ECB, &keys->el_hp_rx, LWS_GAESP_NO_PADDING, NULL)) - return -1; + if (lws_genaes_create(&hp, LWS_GAESO_ENC, LWS_GAESM_ECB, &keys->el_hp_rx, LWS_GAESP_NO_PADDING, NULL)) { + lwsl_notice("unmask: genaes_create failed, el_hp_rx.len=%d, cipher_type=%d, valid=%d, el_aead_rx.len=%d\n", (int)keys->el_hp_rx.len, (int)keys->cipher_type, (int)keys->valid, (int)keys->el_aead_rx.len); + return -1; + } if (lws_genaes_crypt(&hp, sample, 16, mask, NULL, NULL, NULL, 0)) { + lwsl_notice("unmask: genaes_crypt failed\n"); lws_genaes_destroy(&hp, NULL, 0); return -1; } @@ -651,44 +730,104 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le return -1; if (len > 0 && wsi->quic.qn) { + if (wsi->quic.qn->crypto_rx_buf_len[level] > 0) { + uint8_t *new_buf = lws_realloc(wsi->quic.qn->crypto_rx_buf[level], + wsi->quic.qn->crypto_rx_buf_len[level] + len, + "crypto rx buf"); + if (!new_buf) { + lws_free(out); + return -1; + } + memcpy(new_buf + wsi->quic.qn->crypto_rx_buf_len[level], buf, len); + wsi->quic.qn->crypto_rx_buf[level] = new_buf; + wsi->quic.qn->crypto_rx_buf_len[level] += len; + + buf = wsi->quic.qn->crypto_rx_buf[level]; + len = wsi->quic.qn->crypto_rx_buf_len[level]; + } + lwsl_wsi_notice(wsi, "lws_tls_quic_rx_crypto: level %d, len %zu, buf[0]=%d", level, len, buf[0]); lwsl_hexdump_notice(buf, len > 32 ? 32 : len); - size_t i = 0; - while (i < len) { - if (wsi->quic.qn->crypto_rx_expected_msg_len[level] > 0) { - size_t consume = wsi->quic.qn->crypto_rx_expected_msg_len[level]; - if (consume > len - i) - consume = len - i; - wsi->quic.qn->crypto_rx_expected_msg_len[level] -= consume; - i += consume; - continue; + + size_t scan = 0; + size_t complete_len = 0; + while (scan < len) { + if (len - scan < 4) { + break; } - - /* We are at the start of a new Handshake message! */ - uint8_t type = buf[i]; - lwsl_wsi_notice(wsi, "QUIC RX CRYPTO: checking message type %d at offset %zu", type, i); + + uint8_t type = buf[scan]; + lwsl_wsi_notice(wsi, "QUIC RX CRYPTO: checking message type %d at offset %zu", type, scan); if (type == 24 || type == 5) { lwsl_wsi_notice(wsi, "QUIC RX CRYPTO: Illegal TLS Handshake type %d", type); lws_quic_enter_closing_state(wsi, 0x0100 + 10 /* unexpected_message */, 0, 0); lws_free(out); return -1; } - - if (i + 3 >= len) { - /* Fragmented header - extremely rare in h3spec, but we'd need to handle it. - * For now, assume headers are not fragmented across chunks. */ + + uint32_t msg_len = ((uint32_t)buf[scan+1] << 16) | ((uint32_t)buf[scan+2] << 8) | buf[scan+3]; + if (scan + 4 + msg_len > len) { break; } - uint32_t msg_len = ((uint32_t)buf[i+1] << 16) | ((uint32_t)buf[i+2] << 8) | buf[i+3]; - wsi->quic.qn->crypto_rx_expected_msg_len[level] = msg_len; - lwsl_wsi_notice(wsi, "QUIC RX CRYPTO: Expecting %u bytes for message type %d", msg_len, type); - i += 4; + scan += 4 + msg_len; + complete_len = scan; } - } - n = lws_tls_quic_advance_handshake(wsi, level, buf, len, out, &out_len); + if (complete_len == 0) { + if (wsi->quic.qn->crypto_rx_buf_len[level] == 0) { + uint8_t *new_buf = lws_malloc(len, "crypto rx buf"); + if (!new_buf) { + lws_free(out); + return -1; + } + memcpy(new_buf, buf, len); + wsi->quic.qn->crypto_rx_buf[level] = new_buf; + wsi->quic.qn->crypto_rx_buf_len[level] = len; + } + lws_free(out); + return 0; + } + + n = lws_tls_quic_advance_handshake(wsi, level, buf, complete_len, out, &out_len); + + { + struct lws *nwsi = lws_get_quic_network_wsi(wsi); + if (nwsi) wsi = nwsi; + } + + if (n < 0) { + goto error_handling; + } + + size_t remainder = len - complete_len; + if (remainder > 0) { + if (wsi->quic.qn->crypto_rx_buf_len[level] == 0) { + uint8_t *new_buf = lws_malloc(remainder, "crypto rx buf"); + if (new_buf) { + memcpy(new_buf, buf + complete_len, remainder); + wsi->quic.qn->crypto_rx_buf[level] = new_buf; + } + } else { + memmove(wsi->quic.qn->crypto_rx_buf[level], buf + complete_len, remainder); + } + wsi->quic.qn->crypto_rx_buf_len[level] = remainder; + } else { + if (wsi->quic.qn->crypto_rx_buf_len[level] > 0) { + lws_free(wsi->quic.qn->crypto_rx_buf[level]); + wsi->quic.qn->crypto_rx_buf[level] = NULL; + wsi->quic.qn->crypto_rx_buf_len[level] = 0; + } + } + } else { + n = lws_tls_quic_advance_handshake(wsi, level, buf, len, out, &out_len); + { + struct lws *nwsi = lws_get_quic_network_wsi(wsi); + if (nwsi) wsi = nwsi; + } + } +error_handling: if (n < 0) { #if defined(LWS_WITH_GNUTLS) int alert_level = 0; diff --git a/lib/roles/quic/ops-quic-cc-cubic.c b/lib/roles/quic/ops-quic-cc-cubic.c index 4edf5a5b46..e6344f7487 100644 --- a/lib/roles/quic/ops-quic-cc-cubic.c +++ b/lib/roles/quic/ops-quic-cc-cubic.c @@ -255,32 +255,44 @@ cubic_get_pacing_delay(struct lws *nwsi, size_t bytes_to_send) lws_usec_t now = lws_now_usecs(); lws_usec_t elapsed = now - st->last_pacing_time; - st->last_pacing_time = now; /* Replenish credit based on elapsed time: R = cwnd / srtt */ size_t credit_added = (size_t)(((uint64_t)elapsed * (uint64_t)st->cwnd) / (uint64_t)rtt); st->pacing_credit += credit_added; - /* Cap credit to max burst to prevent micro-bursts (e.g. 10 packets) */ - size_t max_burst = 10 * (lws_get_vhost(nwsi)->quic_mtu ? lws_get_vhost(nwsi)->quic_mtu : 1280); + /* + * Cap credit to max burst (64 packets) to prevent very large bursts + * while still allowing good throughput for large concurrent stream + * queues (e.g. 1999 streams in the multiplexing test). + */ + size_t max_burst = 64 * (lws_get_vhost(nwsi)->quic_mtu ? lws_get_vhost(nwsi)->quic_mtu : 1280); if (st->pacing_credit > max_burst) st->pacing_credit = max_burst; if (st->pacing_credit >= bytes_to_send) { - /* We have enough credit to send this packet */ + /* + * Enough credit to send this packet: consume credit and + * record the time only when we actually allow a send. + * See newreno equivalent for the rationale. + */ + st->last_pacing_time = now; return 0; } - /* Not enough credit. Calculate how long it will take to earn the missing credit. */ + /* Not enough credit. Calculate how long to wait for the missing credit. */ size_t missing_credit = bytes_to_send - st->pacing_credit; delay_us = (lws_usec_t)(((uint64_t)missing_credit * (uint64_t)rtt) / (uint64_t)(st->cwnd ? st->cwnd : 1)); if (delay_us == 0) delay_us = 1; + /* Do NOT update last_pacing_time here: credit already earned in + * pacing_credit is preserved for the next call. */ + return delay_us; } + const struct lws_cc_ops lws_cc_ops_cubic = { .init = cubic_init, .on_sent = cubic_on_sent, diff --git a/lib/roles/quic/ops-quic-cc-newreno.c b/lib/roles/quic/ops-quic-cc-newreno.c index cb24a8d0ff..68aed1243d 100644 --- a/lib/roles/quic/ops-quic-cc-newreno.c +++ b/lib/roles/quic/ops-quic-cc-newreno.c @@ -16,15 +16,6 @@ #include "private-lib-core.h" #include "private-lib-roles-quic.h" -struct lws_quic_cc_newreno { - size_t cwnd; - size_t ssthresh; - size_t bytes_in_flight; - lws_usec_t congestion_recovery_start_time; - - lws_usec_t last_pacing_time; - size_t pacing_credit; -}; static void newreno_init(struct lws *nwsi) @@ -105,6 +96,20 @@ newreno_on_ack(struct lws *nwsi, size_t bytes_acked, lws_usec_t rtt) } } +static void +newreno_on_discard(struct lws *nwsi, size_t bytes_discarded) +{ + struct lws_quic_netconn *qn = nwsi->quic.qn; + struct lws_quic_cc_newreno *st = (struct lws_quic_cc_newreno *)qn->cc_state; + + if (!st) return; + + if (st->bytes_in_flight >= bytes_discarded) + st->bytes_in_flight -= bytes_discarded; + else + st->bytes_in_flight = 0; +} + static void newreno_on_loss(struct lws *nwsi, size_t bytes_lost) { @@ -165,29 +170,44 @@ newreno_get_pacing_delay(struct lws *nwsi, size_t bytes_to_send) lws_usec_t now = lws_now_usecs(); lws_usec_t elapsed = now - st->last_pacing_time; - st->last_pacing_time = now; /* Replenish credit based on elapsed time: R = cwnd / srtt */ size_t credit_added = (size_t)(((uint64_t)elapsed * (uint64_t)st->cwnd) / (uint64_t)rtt); st->pacing_credit += credit_added; - /* Cap credit to max burst to prevent micro-bursts (e.g. 10 packets) */ - size_t max_burst = 10 * (lws_get_vhost(nwsi)->quic_mtu ? lws_get_vhost(nwsi)->quic_mtu : 1280); + /* + * Cap credit to max burst (64 packets) to prevent very large bursts + * while still allowing good throughput for large concurrent stream + * queues (e.g. 1999 streams in the multiplexing test). + */ + size_t max_burst = 64 * (lws_get_vhost(nwsi)->quic_mtu ? lws_get_vhost(nwsi)->quic_mtu : 1280); if (st->pacing_credit > max_burst) st->pacing_credit = max_burst; if (st->pacing_credit >= bytes_to_send) { - /* We have enough credit to send this packet */ + /* + * Enough credit to send this packet: consume credit and + * record the time only when we actually allow a send. + * This is critical: if we updated last_pacing_time even + * when blocking, the next call after a short timer would + * compute elapsed ≈ 0 → near-zero credit added → endless + * tight-loop stall. + */ + st->last_pacing_time = now; return 0; } - /* Not enough credit. Calculate how long it will take to earn the missing credit. */ + /* Not enough credit. Calculate how long to wait for the missing credit. */ size_t missing_credit = bytes_to_send - st->pacing_credit; delay_us = (lws_usec_t)(((uint64_t)missing_credit * (uint64_t)rtt) / (uint64_t)st->cwnd); if (delay_us == 0) delay_us = 1; + /* Do NOT update last_pacing_time here: the credit already earned + * (credit_added) is preserved in st->pacing_credit, and next call + * the elapsed time from *now* will be added on top of it. */ + return delay_us; } @@ -196,6 +216,7 @@ const struct lws_cc_ops lws_cc_ops_newreno = { .on_sent = newreno_on_sent, .on_ack = newreno_on_ack, .on_loss = newreno_on_loss, + .on_discard = newreno_on_discard, .can_send = newreno_can_send, .get_pacing_delay = newreno_get_pacing_delay, }; diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 7bec9283cb..9427ce85f9 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -29,6 +29,24 @@ static int quic_secret_cb(struct lws *wsi, enum lws_tls_quic_secret_type type, const uint8_t *secret, size_t secret_len); +void +lws_quic_queue_path_challenge(struct lws *nwsi) +{ + if (!nwsi || !nwsi->quic.qn) return; + struct lws_quic_tx_frame *f_pc = lws_zalloc(sizeof(*f_pc) + 8, "quic path_chall"); + if (f_pc) { + f_pc->type = LWS_QUIC_FT_PATH_CHALLENGE; + f_pc->len = 8; + f_pc->data = (uint8_t *)&f_pc[1]; + lws_get_random(nwsi->a.context, f_pc->data, 8); + memcpy(nwsi->quic.qn->path_challenge, f_pc->data, 8); + nwsi->quic.qn->path_challenge_pending = 1; + + lws_dll2_add_tail(&f_pc->list, &nwsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP]); + lws_callback_on_writable(nwsi); + } +} + static void lws_quic_pacer_cb(lws_sorted_usec_list_t *sul) { @@ -49,34 +67,54 @@ lws_quic_pto_cb(lws_sorted_usec_list_t *sul) return; } - qn->pto_probe_needed = 1; + qn->pto_probe_needed = 2; #if (_LWS_ENABLED_LOGS & LLL_INFO) LWS_RATELIMIT_DEFINE_STATIC(rl); lwsl_ratelimit_info(&rl, 1000000, "QUIC PTO Timer Fired! Forcing POLLOUT for retransmission sweep\n"); #endif int sent_ping = 0; - /* Add a PING frame to force a packet out if we don't have pending tx */ - for (int i = 0; i < LWS_QUIC_LEVEL_COUNT; i++) { + size_t total_bytes_lost = 0; + /* Aggressively retransmit Initial/Handshake frames (RFC 9002 6.2.4) */ + for (int i = 0; i < LWS_QUIC_LEVEL_APP; i++) { if (qn->in_flight[i].count) { - struct lws_quic_tx_frame *ping = lws_zalloc(sizeof(*ping), "pto ping"); - if (ping) { - ping->type = LWS_QUIC_FT_PING; - lws_dll2_add_tail(&ping->list, &qn->pending_tx[i]); - lwsl_wsi_notice(qn->nwsi, "QUIC PTO: Enqueued PING for level %d", i); - } + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->in_flight[i].head) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + lws_dll2_remove(d); + total_bytes_lost += f->wire_len; + f->wire_len = 0; /* CRITICAL: Reset wire_len */ + lws_dll2_add_tail(&f->list, &qn->pending_tx[i]); + } lws_end_foreach_dll_safe(d, d1); + lwsl_wsi_notice(qn->nwsi, "QUIC PTO: Retransmitting Initial/Handshake for level %d", i); sent_ping = 1; - break; } } - if (!sent_ping && !qn->is_server && !qn->handshake_done) { + if (total_bytes_lost && qn->cc_ops && qn->cc_ops->on_loss) + qn->cc_ops->on_loss(qn->nwsi, total_bytes_lost); + + /* For App Data, just send a PING to elicit an ACK */ + if (!sent_ping && qn->in_flight[LWS_QUIC_LEVEL_APP].count) { + struct lws_quic_tx_frame *ping = lws_zalloc(sizeof(*ping), "pto ping"); + if (ping) { + ping->type = LWS_QUIC_FT_PING; + /* Add at HEAD so the PING is serialized first, before data frames + * fill the MTU and cause pto_probe_needed to decrement prematurely */ + lws_dll2_add_head(&ping->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); + lwsl_wsi_notice(qn->nwsi, "QUIC PTO: Enqueued PING for level 3"); + qn->pto_probe_needed = 1; + } + sent_ping = 1; + } + + if (!sent_ping && !qn->handshake_done) { int target_level = LWS_QUIC_LEVEL_INITIAL; if (qn->keys[LWS_QUIC_LEVEL_HANDSHAKE]) target_level = LWS_QUIC_LEVEL_HANDSHAKE; struct lws_quic_tx_frame *ping = lws_zalloc(sizeof(*ping), "pto ping"); if (ping) { ping->type = LWS_QUIC_FT_PING; lws_dll2_add_tail(&ping->list, &qn->pending_tx[target_level]); - lwsl_wsi_notice(qn->nwsi, "QUIC PTO: Enqueued PING for level %d (no in-flight, client handshake incomplete)", target_level); + lwsl_wsi_notice(qn->nwsi, "QUIC PTO: Enqueued PING for level %d (no in-flight, handshake incomplete)", target_level); + qn->pto_probe_needed = 1; } sent_ping = 1; } @@ -92,7 +130,7 @@ lws_quic_pto_cb(lws_sorted_usec_list_t *sul) } } - if (any_in_flight || (!qn->is_server && !qn->handshake_done)) { + if (any_in_flight || !qn->handshake_done) { lws_usec_t pto_base = qn->smoothed_rtt ? (qn->smoothed_rtt + (4 * qn->rttvar) + 25000) : LWS_QUIC_DEFAULT_PTO_US; lws_usec_t pto_delay = pto_base << qn->pto_count; if (pto_delay > 10000000) @@ -120,6 +158,50 @@ lws_quic_decode_packet_number(uint64_t largest_pn, uint64_t truncated_pn, int pn return candidate_pn; } +void +lws_quic_detect_loss(struct lws *nwsi, int level, uint64_t largest_acked) +{ + struct lws_quic_netconn *qn = nwsi->quic.qn; + if (!qn) return; + + size_t total_bytes_lost = 0; + lws_usec_t now = lws_now_usecs(); + lws_usec_t loss_time = qn->smoothed_rtt ? (qn->smoothed_rtt * 9 / 8) : 50000; + + int check_levels[] = { level, level == LWS_QUIC_LEVEL_APP ? LWS_QUIC_LEVEL_EARLY : -1 }; + for (int i = 0; i < 2; i++) { + int curlvl = check_levels[i]; + if (curlvl == -1) continue; + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->in_flight[curlvl].head) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + + if (f->sent_in_pn <= largest_acked && + (f->sent_in_pn + 3 <= largest_acked || (f->sent_in_pn < largest_acked && now > f->sent_time_us + loss_time))) { + + /* Mark as lost */ + total_bytes_lost += f->wire_len; + lwsl_wsi_info(nwsi, "QUIC LOSS: Packet %llu lost, frame %d", (unsigned long long)f->sent_in_pn, f->type); + + lws_dll2_remove(&f->list); + f->wire_len = 0; + + int pending_lvl = curlvl; + if (curlvl == LWS_QUIC_LEVEL_EARLY) + pending_lvl = LWS_QUIC_LEVEL_APP; + + lws_dll2_add_tail(&f->list, &qn->pending_tx[pending_lvl]); + } + } lws_end_foreach_dll_safe(d, d1); + } + + if (total_bytes_lost && qn->cc_ops && qn->cc_ops->on_loss) + qn->cc_ops->on_loss(nwsi, total_bytes_lost); + + if (total_bytes_lost) + lws_callback_on_writable(nwsi); +} + void lws_quic_handle_ack(struct lws *nwsi, int level, uint64_t acked_pn) { @@ -142,27 +224,33 @@ lws_quic_handle_ack(struct lws *nwsi, int level, uint64_t acked_pn) lws_usec_t rtt = 0; lws_usec_t now = lws_now_usecs(); - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->in_flight[level].head) { - struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + int check_levels[] = { level, level == LWS_QUIC_LEVEL_APP ? LWS_QUIC_LEVEL_EARLY : -1 }; + for (int i = 0; i < 2; i++) { + int curlvl = check_levels[i]; + if (curlvl == -1) continue; - if (f->sent_in_pn == acked_pn) { - uint64_t sid = f->stream_id; - bytes_acked += f->wire_len; - rtt = now > f->sent_time_us ? now - f->sent_time_us : 0; - /* Packet was received successfully, free the frame! */ - lws_dll2_remove(&f->list); - lws_free(f); + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->in_flight[curlvl].head) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + + if (f->sent_in_pn == acked_pn) { + uint64_t sid = f->stream_id; + bytes_acked += f->wire_len; + rtt = now > f->sent_time_us ? now - f->sent_time_us : 0; + /* Packet was received successfully, free the frame! */ + lws_dll2_remove(&f->list); + lws_free(f); - struct lws *child = lws_quic_stream_find(nwsi, sid); - if (child && (lwsi_state(child) == LRS_FLUSHING_BEFORE_CLOSE + struct lws *child = lws_quic_stream_find(nwsi, sid); + if (child && (lwsi_state(child) == LRS_FLUSHING_BEFORE_CLOSE #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3) - || child->http.deferred_transaction_completed + || child->http.deferred_transaction_completed #endif - )) { - lws_callback_on_writable(child); + )) { + lws_callback_on_writable(child); + } } - } - } lws_end_foreach_dll_safe(d, d1); + } lws_end_foreach_dll_safe(d, d1); + } if (bytes_acked) { lws_callback_on_writable(nwsi); @@ -194,21 +282,65 @@ lws_quic_handle_ack(struct lws *nwsi, int level, uint64_t acked_pn) } if (pending) lws_callback_on_writable(nwsi); + + { + struct lws_quic_cc_newreno *_cc = qn->cc_ops ? (struct lws_quic_cc_newreno *)qn->cc_state : NULL; + lwsl_notice("QUIC TX: ACK processing: bytes_acked=%zu, cc_bif=%zu cc_cwnd=%zu cc_pacing=%zu, in_flight_app=%d, pending_tx=%d, blocked=%d\n", + bytes_acked, + _cc ? _cc->bytes_in_flight : 0, + _cc ? _cc->cwnd : 0, + _cc ? _cc->pacing_credit : 0, + qn->in_flight[LWS_QUIC_LEVEL_APP].count, + qn->pending_tx[LWS_QUIC_LEVEL_APP].count, + (int)(qn->cc_ops && qn->cc_ops->can_send ? !qn->cc_ops->can_send(nwsi, 1000) : 0)); + } + + + /* + * ACKs opened the congestion window. Child streams may have + * been stalled by the application-layer pacing throttle in + * rops_tx_credit_quic() (64KB pending_tx cap). Wake them up + * so they can generate more application data. + */ + { + struct lws *w = nwsi->mux.child_list; + + while (w) { + if (w->mux.requested_POLLOUT) + lws_callback_on_writable(w); + w = w->mux.sibling_list; + } + } } /* If there are no more in-flight packets across all levels, we can cancel the PTO timer */ - int any_in_flight = 0; - for (int i = 0; i < LWS_QUIC_LEVEL_COUNT; i++) { - if (qn->in_flight[i].count) { - any_in_flight = 1; - break; + { + int any_in_flight2 = 0; + for (int i2 = 0; i2 < LWS_QUIC_LEVEL_COUNT; i2++) { + if (qn->in_flight[i2].count) { + any_in_flight2 = 1; + break; + } } - } - if (!any_in_flight) { - if (!qn->is_server && !qn->handshake_done) { - /* Keep PTO timer running to probe if server gets blocked (RFC 9002 6.2.2.1) */ - } else { - lws_sul_cancel(&qn->pto_sul); + if (!any_in_flight2) { + if (!qn->handshake_done) { /* was !qn->is_server && ... */ + /* Keep PTO timer running to probe if peer gets blocked (RFC 9002 6.2.2.1) */ + lwsl_notice("lws_quic_handle_ack: Keeping PTO timer active (no in-flight, handshake incomplete)\n"); + } else { + lws_sul_cancel(&qn->pto_sul); + } + } else if (bytes_acked) { + /* + * RFC 9002 Section 6.2.1: When a packet is acknowledged, the + * PTO timer must be updated. Reset it using pto_count=0 (since + * we just reset pto_count above) so the doubled timer from the + * PTO callback is replaced by a fresh base-rate timer. + */ + lws_usec_t pto_base2 = qn->smoothed_rtt ? + (qn->smoothed_rtt + (4 * qn->rttvar) + 25000) : + LWS_QUIC_DEFAULT_PTO_US; + lws_sul_schedule(nwsi->a.context, 0, &qn->pto_sul, + lws_quic_pto_cb, pto_base2); } } } @@ -397,6 +529,13 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, if (nwsi->quic.qn->rem_cid.len != scid.len || memcmp(nwsi->quic.qn->rem_cid.id, scid.id, scid.len)) { nwsi->quic.qn->rem_cid = scid; } + + /* Check if the server upgraded the version (Compatible Version Negotiation) */ + uint32_t pkt_version = ((uint32_t)p[1] << 24) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 8) | p[4]; + if (pkt_version != nwsi->quic.qn->version && pkt_version == LWS_QUIC_VERSION_2) { + nwsi->quic.qn->version = pkt_version; + lwsl_wsi_notice(wsi, "QUIC RX: Upgraded to QUIC v2 via Compatible Version Negotiation"); + } } } } else { @@ -536,9 +675,12 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, nwsi->quic.qn->nwsi = nwsi; nwsi->quic.qn->is_server = 1; + nwsi->quic.qn->next_stream_id_bidi_local = 1; + nwsi->quic.qn->next_stream_id_unidi_local = 3; nwsi->quic.qn->version = pkt_version; - nwsi->quic.qn->max_streams_bidi_local = 100; - nwsi->quic.qn->max_streams_unidi_local = 100; + nwsi->quic.qn->original_version = pkt_version; + nwsi->quic.qn->max_streams_bidi_local = 400; + nwsi->quic.qn->max_streams_unidi_local = 400; nwsi->quic.qn->current_mtu = 1280; nwsi->quic.qn->probed_mtu = 1380; /* first probe size */ @@ -620,7 +762,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, tp += (_len); \ } while (0) - lwsl_notice("QUIC CLIENT: Writing TP buffer! buf size %d", (int)(tp - wsi->quic.qn->local_tp_buf)); LWS_QUIC_WRITE_TP_VARINT(0x04, LWS_QUIC_DEFAULT_WINDOW); + lwsl_notice("QUIC CLIENT: Writing TP buffer! buf size %d", (int)(tp - nwsi->quic.qn->local_tp_buf)); LWS_QUIC_WRITE_TP_VARINT(0x04, LWS_QUIC_DEFAULT_WINDOW); LWS_QUIC_WRITE_TP_VARINT(0x05, LWS_QUIC_DEFAULT_WINDOW); LWS_QUIC_WRITE_TP_VARINT(0x06, LWS_QUIC_DEFAULT_WINDOW); LWS_QUIC_WRITE_TP_VARINT(0x07, LWS_QUIC_DEFAULT_WINDOW); @@ -634,6 +776,22 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, if (nwsi->quic.qn->retry_scid.len) { LWS_QUIC_WRITE_TP_BUF(0x10, nwsi->quic.qn->retry_scid.id, nwsi->quic.qn->retry_scid.len); } + if (wsi->a.context->options & LWS_SERVER_OPTION_QUIC_LATEST_VERSION) { + uint8_t vi_buf[12]; + vi_buf[0] = (uint8_t)(nwsi->quic.qn->version >> 24); + vi_buf[1] = (uint8_t)(nwsi->quic.qn->version >> 16); + vi_buf[2] = (uint8_t)(nwsi->quic.qn->version >> 8); + vi_buf[3] = (uint8_t)(nwsi->quic.qn->version); + vi_buf[4] = (uint8_t)(LWS_QUIC_VERSION_1 >> 24); + vi_buf[5] = (uint8_t)(LWS_QUIC_VERSION_1 >> 16); + vi_buf[6] = (uint8_t)(LWS_QUIC_VERSION_1 >> 8); + vi_buf[7] = (uint8_t)(LWS_QUIC_VERSION_1); + vi_buf[8] = (uint8_t)(LWS_QUIC_VERSION_2 >> 24); + vi_buf[9] = (uint8_t)(LWS_QUIC_VERSION_2 >> 16); + vi_buf[10] = (uint8_t)(LWS_QUIC_VERSION_2 >> 8); + vi_buf[11] = (uint8_t)(LWS_QUIC_VERSION_2); + LWS_QUIC_WRITE_TP_BUF(0x11, vi_buf, 12); + } if (wsi->a.vhost->options & LWS_SERVER_OPTION_QUIC_PAD_CRYPTO) { lwsl_notice("QUIC TX: Padding handshake crypto data to artificially hit anti-amplification limits\n"); @@ -646,6 +804,85 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, tp += 3000; } + if (wsi->a.vhost->quic_preferred_addresses) { + lwsl_notice("QUIC TX: Processing preferred_address config: '%s'\n", wsi->a.vhost->quic_preferred_addresses); + const char *p = wsi->a.vhost->quic_preferred_addresses; + lws_sockaddr46 sa4, sa6; + memset(&sa4, 0, sizeof(sa4)); + memset(&sa6, 0, sizeof(sa6)); + + while (p && *p) { + char chunk[128]; + const char *comma = strchr(p, ','); + size_t len = comma ? (size_t)(comma - p) : strlen(p); + if (len >= sizeof(chunk)) len = sizeof(chunk) - 1; + memcpy(chunk, p, len); + chunk[len] = '\0'; + + char *colon = strrchr(chunk, ':'); + int port = 443; + if (colon && colon > strchr(chunk, ']')) { + port = atoi(colon + 1); + *colon = '\0'; + } else if (colon && !strchr(chunk, ']')) { + port = atoi(colon + 1); + *colon = '\0'; + } + + char *addr = chunk; + if (addr[0] == '[') { + addr++; + char *rb = strchr(addr, ']'); + if (rb) *rb = '\0'; + } + + lws_sockaddr46 sa; + memset(&sa, 0, sizeof(sa)); + if (lws_sa46_parse_numeric_address(addr, &sa) == 0) { + if (sa.sa4.sin_family == AF_INET) { + sa4 = sa; + sa4.sa4.sin_port = htons((uint16_t)port); + } else { + sa6 = sa; + sa6.sa6.sin6_port = htons((uint16_t)port); + } + } + p = comma ? comma + 1 : NULL; + } + + /* We only send preferred_address if we have a valid address */ + if (sa4.sa4.sin_family == AF_INET || sa6.sa6.sin6_family == AF_INET6) { + uint8_t pref_buf[128]; + uint8_t *pb = pref_buf; + + /* IPv4 (4 bytes addr, 2 bytes port) */ + if (sa4.sa4.sin_family == AF_INET) { + memcpy(pb, &sa4.sa4.sin_addr.s_addr, 4); pb += 4; + memcpy(pb, &sa4.sa4.sin_port, 2); pb += 2; + } else { + memset(pb, 0, 6); pb += 6; + } + + /* IPv6 (16 bytes addr, 2 bytes port) */ + if (sa6.sa6.sin6_family == AF_INET6) { + memcpy(pb, sa6.sa6.sin6_addr.s6_addr, 16); pb += 16; + memcpy(pb, &sa6.sa6.sin6_port, 2); pb += 2; + } else { + memset(pb, 0, 18); pb += 18; + } + + /* CID Length (1 byte) */ + *pb++ = 8; /* Generating an 8-byte CID */ + /* CID Bytes */ + lws_get_random(wsi->a.context, pb, 8); pb += 8; + /* Stateless Reset Token */ + lws_get_random(wsi->a.context, pb, 16); pb += 16; + + LWS_QUIC_WRITE_TP_BUF(0x0D, pref_buf, (size_t)(pb - pref_buf)); + lwsl_notice("QUIC TX: Sent preferred_address TP (0x0D)\n"); + } + } + lws_tls_quic_set_transport_parameters(nwsi, nwsi->quic.qn->local_tp_buf, (size_t)(tp - nwsi->quic.qn->local_tp_buf)); goto tp_ok; @@ -739,10 +976,25 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, continue; } if (p[0] & 0x80) { - uint8_t type = (uint8_t)((p[0] & 0x30) >> 4); - if (type == LWS_QUIC_PT_INITIAL) level = LWS_QUIC_LEVEL_INITIAL; - else if (type == LWS_QUIC_PT_HANDSHAKE) level = LWS_QUIC_LEVEL_HANDSHAKE; - else if (type == LWS_QUIC_PT_RETRY) { + uint8_t type = (uint8_t)((p[0] & 0x30) >> 4); + uint32_t parsed_pkt_version = (uint32_t)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); + int is_v2 = (parsed_pkt_version == LWS_QUIC_VERSION_2) || + (nwsi && nwsi->quic.qn && nwsi->quic.qn->version == LWS_QUIC_VERSION_2); + + uint8_t v1_type = type; + if (is_v2) { + switch (type) { + case 0: v1_type = LWS_QUIC_PT_RETRY; break; + case 1: v1_type = LWS_QUIC_PT_INITIAL; break; + case 2: v1_type = LWS_QUIC_PT_0RTT; break; + case 3: v1_type = LWS_QUIC_PT_HANDSHAKE; break; + } + } + + if (v1_type == LWS_QUIC_PT_INITIAL) level = LWS_QUIC_LEVEL_INITIAL; + else if (v1_type == LWS_QUIC_PT_HANDSHAKE) level = LWS_QUIC_LEVEL_HANDSHAKE; + else if (v1_type == LWS_QUIC_PT_0RTT) level = LWS_QUIC_LEVEL_EARLY; + else if (v1_type == LWS_QUIC_PT_RETRY) { if (nwsi && nwsi->quic.qn && !nwsi->quic.qn->is_server) { size_t tag_pos = (size_t)n - 16; int client_scid_pos = 6 + dcid_len; @@ -788,6 +1040,8 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, nwsi->quic.qn->highest_rx_level = (uint8_t)level; } + int pn_space = (level == LWS_QUIC_LEVEL_EARLY || level == LWS_QUIC_LEVEL_APP) ? LWS_QUIC_LEVEL_APP : level; + /* Enforce 1200-byte padding for subsequent client-to-server Initial packets (RFC 9000 Section 14.1) */ if (level == LWS_QUIC_LEVEL_INITIAL && nwsi && nwsi->quic.qn && nwsi->quic.qn->is_server && n < 1200) { lwsl_wsi_notice(wsi, "QUIC RX: Dropping under-padded Initial packet (len %d)", n); @@ -810,7 +1064,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, struct lws_quic_keys *k = nwsi->quic.qn->keys[level]; - if (!k || !k->valid) { + if (!k || !k->el_hp_rx.len) { lwsl_wsi_notice(wsi, "QUIC RX: No valid keys for this packet level %d, skipping %zu bytes", level, packet_size); p += packet_size; n -= (int)packet_size; @@ -856,7 +1110,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, for (int i = 0; i < pn_len; i++) truncated_pn = (truncated_pn << 8) | p[pn_offset + (size_t)i]; - uint64_t largest_pn = nwsi->quic.qn ? nwsi->quic.qn->highest_rx_pn[level] : 0; + uint64_t largest_pn = nwsi->quic.qn ? nwsi->quic.qn->highest_rx_pn[pn_space] : 0; uint64_t full_pn = lws_quic_decode_packet_number(largest_pn, truncated_pn, pn_len * 8); lwsl_wsi_info(wsi, "QUIC RX: Packet level %d, decoded full_pn = %llu", level, (unsigned long long)full_pn); @@ -892,6 +1146,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, if (!nwsi->quic.qn->key_update_pending) { /* Peer initiated, so we echo by updating TX keys */ lws_quic_initiate_key_update(nwsi); + nwsi->quic.qn->key_update_pending = 0; /* Echoed */ } else { /* We initiated, this is the peer echoing back */ nwsi->quic.qn->key_update_pending = 0; @@ -927,24 +1182,24 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, /* Check for duplicate/replayed packet numbers (Security Fix) */ if (nwsi->quic.qn) { - uint64_t highest = nwsi->quic.qn->highest_rx_pn[level]; - if ((nwsi->quic.qn->rx_pn_bitmask[level] != 0 || highest != 0) && full_pn <= highest) { + uint64_t highest = nwsi->quic.qn->highest_rx_pn[pn_space]; + if ((nwsi->quic.qn->rx_pn_bitmask[pn_space] != 0 || highest != 0) && full_pn <= highest) { uint64_t diff = highest - full_pn; - if (diff >= 64 || (nwsi->quic.qn->rx_pn_bitmask[level] & (1ULL << diff))) { + if (diff >= 64 || (nwsi->quic.qn->rx_pn_bitmask[pn_space] & (1ULL << diff))) { lwsl_wsi_notice(wsi, "QUIC RX: Dropping duplicated or very old packet %llu", (unsigned long long)full_pn); goto next_packet; } - nwsi->quic.qn->rx_pn_bitmask[level] |= (1ULL << diff); + nwsi->quic.qn->rx_pn_bitmask[pn_space] |= (1ULL << diff); } else { - if (nwsi->quic.qn->rx_pn_bitmask[level] != 0 || highest != 0) { + if (nwsi->quic.qn->rx_pn_bitmask[pn_space] != 0 || highest != 0) { uint64_t diff = full_pn - highest; if (diff >= 64) - nwsi->quic.qn->rx_pn_bitmask[level] = 0; + nwsi->quic.qn->rx_pn_bitmask[pn_space] = 0; else - nwsi->quic.qn->rx_pn_bitmask[level] <<= diff; + nwsi->quic.qn->rx_pn_bitmask[pn_space] <<= diff; } - nwsi->quic.qn->rx_pn_bitmask[level] |= 1ULL; - nwsi->quic.qn->highest_rx_pn[level] = full_pn; + nwsi->quic.qn->rx_pn_bitmask[pn_space] |= 1ULL; + nwsi->quic.qn->highest_rx_pn[pn_space] = full_pn; } /* Connection Migration: Execute pending migration now that the packet is cryptographically verified */ @@ -1008,22 +1263,24 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, nwsi->quic.qn->bytes_received = (uint64_t)orig_n; nwsi->quic.qn->bytes_sent = 0; - /* Initiate Path Validation (Generate PATH_CHALLENGE) */ - struct lws_quic_tx_frame *f_pc = lws_zalloc(sizeof(*f_pc) + 8, "quic path_chall"); - if (f_pc) { - f_pc->type = LWS_QUIC_FT_PATH_CHALLENGE; - f_pc->len = 8; - f_pc->data = (uint8_t *)&f_pc[1]; - lws_get_random(wsi->a.context, f_pc->data, 8); - memcpy(nwsi->quic.qn->path_challenge, f_pc->data, 8); - nwsi->quic.qn->path_challenge_pending = 1; - - lws_dll2_add_head(&f_pc->list, &nwsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP]); - lws_callback_on_writable(nwsi); + /* Initiate Path Validation (Generate PATH_CHALLENGE) if none pending */ + if (!nwsi->quic.qn->path_challenge_pending) { + struct lws_quic_tx_frame *f_pc = lws_zalloc(sizeof(*f_pc) + 8, "quic path_chall"); + if (f_pc) { + f_pc->type = LWS_QUIC_FT_PATH_CHALLENGE; + f_pc->len = 8; + f_pc->data = (uint8_t *)&f_pc[1]; + lws_get_random(wsi->a.context, f_pc->data, 8); + memcpy(nwsi->quic.qn->path_challenge, f_pc->data, 8); + nwsi->quic.qn->path_challenge_pending = 1; + + lws_dll2_add_head(&f_pc->list, &nwsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP]); + lws_callback_on_writable(nwsi); + } } } - /* 5. Parse the plaintext frames */ + /* 5. Parse and handle all frames in the payload */ if (dec_len == 0) { lwsl_wsi_notice(wsi, "QUIC RX: Packet payload is empty (no frames)"); if (nwsi && nwsi != wsi) { @@ -1033,16 +1290,16 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, return LWS_HPI_RET_PLEASE_CLOSE_ME; } - int ack_eliciting = lws_quic_parse_frames(nwsi, level, &p[pn_offset + (size_t)pn_len], (size_t)dec_len); + int parse_res = lws_quic_parse_frames(nwsi, pn_space, &p[pn_offset + (size_t)pn_len], (size_t)dec_len, &sa46); /* ALPN negotiation might have migrated the network WSI! */ if (nwsi && !nwsi->quic.qn) { nwsi = lws_get_quic_network_wsi(nwsi); } - if (ack_eliciting < 0) { + if (parse_res < 0) { lwsl_wsi_notice(wsi, "QUIC RX: Frame parsing aborted"); - if (ack_eliciting == -3) { + if (parse_res == -3) { /* Peer closed the connection via CONNECTION_CLOSE. Drop silently without replying. */ lwsl_wsi_notice(nwsi ? nwsi : wsi, "QUIC RX: Peer closed connection. Dropping silently."); if (nwsi && nwsi != wsi) { @@ -1053,13 +1310,13 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, } /* We found an error and queued a CONNECTION_CLOSE frame */ if (nwsi) { - lws_quic_enter_closing_state(nwsi, ack_eliciting == -2 ? LWS_QUIC_ERR_PROTOCOL_VIOLATION : LWS_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0); + lws_quic_enter_closing_state(nwsi, parse_res == -2 ? LWS_QUIC_ERR_PROTOCOL_VIOLATION : LWS_QUIC_ERR_FRAME_ENCODING_ERROR, 0, 0); lws_callback_on_writable(nwsi); } goto next_packet; - } else if (ack_eliciting > 0) { + } else if (parse_res > 0) { if (nwsi && nwsi->quic.qn) { - nwsi->quic.qn->needs_ack[level] = 1; + nwsi->quic.qn->needs_ack[pn_space] = 1; lws_callback_on_writable(nwsi); /* Ensure POLLOUT fires so we send the ACK! */ } } @@ -1161,7 +1418,7 @@ lws_quic_enter_closing_state(struct lws *wsi, uint64_t err_code, uint64_t frame_ /* Determine highest available encryption level to send CONNECTION_CLOSE */ int start_level = qn->highest_rx_level; for (level = start_level; level >= LWS_QUIC_LEVEL_INITIAL; level--) { - if (qn->keys[level] && qn->keys[level]->valid) { + if (qn->keys[level] && qn->keys[level]->el_hp_tx.len) { target_level = level; break; } @@ -1219,6 +1476,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) struct lws_quic_netconn *qn = wsi->quic.qn; int level, n; int blocked = 0; + int eagain_blocked = 0; uint8_t pkt[2048]; memset(pkt, 0, sizeof(pkt)); wsi->mux.requested_POLLOUT = 0; @@ -1246,30 +1504,42 @@ rops_handle_POLLOUT_quic(struct lws *wsi) if (pto_delay > 10000000) pto_delay = 10000000; - if (!wsi->quic.initialized && !qn->is_server) { - wsi->quic.initialized = 1; + if (!wsi->quic.initialized && !qn->is_server) { + wsi->quic.initialized = 1; + if (qn->migration_probing_wsi == wsi) { + /* Active Migration: just queue path challenge! */ + lws_quic_queue_path_challenge(wsi); + } else { #if defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT) - if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { - if (!wsi->tls.ssl) { - const char *cce = NULL; - if (lws_client_create_tls(wsi, &cce, 0) == CCTLS_RETURN_ERROR) { - lwsl_wsi_err(wsi, "Failed to create TLS BIO: %s", cce ? cce : "unknown"); - return LWS_HP_RET_BAIL_DIE; - } - } - - /* The BIO was already created, just init QUIC TLS */ - if (lws_tls_quic_init(wsi, quic_secret_cb)) { - lwsl_wsi_err(wsi, "Failed to init QUIC TLS"); - return LWS_HP_RET_BAIL_DIE; - } - /* Kick off the handshake */ - // lwsl_wsi_notice(wsi, "Kicking off QUIC TLS handshake"); - lws_tls_quic_rx_crypto(wsi, LWS_QUIC_LEVEL_INITIAL, NULL, 0); - } + if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { + if (!wsi->tls.ssl) { + const char *cce = NULL; + if (lws_client_create_tls(wsi, &cce, 0) == CCTLS_RETURN_ERROR) { + lwsl_wsi_err(wsi, "Failed to create TLS BIO: %s", cce ? cce : "unknown"); + return LWS_HP_RET_BAIL_DIE; + } + } + /* The BIO was already created, just init QUIC TLS */ + if (lws_tls_quic_init(wsi, quic_secret_cb)) { + lwsl_wsi_err(wsi, "Failed to init QUIC TLS"); + return LWS_HP_RET_BAIL_DIE; + } + /* Kick off the handshake */ + // lwsl_wsi_notice(wsi, "Kicking off QUIC TLS handshake"); + lws_tls_quic_rx_crypto(wsi, LWS_QUIC_LEVEL_INITIAL, NULL, 0); + + { + struct lws *nwsi = lws_get_quic_network_wsi(wsi); + if (nwsi) { + wsi = nwsi; + qn = wsi->quic.qn; + } + } + } #endif - } + } + } if (qn->is_closing) { /* We are in the Closing State. Only process the CONNECTION_CLOSE frame. */ @@ -1330,8 +1600,15 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } } lws_end_foreach_dll_safe(d, d1); } - if (total_bytes_lost && qn->cc_ops && qn->cc_ops->on_loss) - qn->cc_ops->on_loss(wsi, total_bytes_lost); + /* + * RFC 9002 Section 6.2.4: A PTO timer expiry does not indicate packet + * loss and MUST NOT cause the congestion window to be reduced (no on_loss). + * However, bytes_in_flight must be decremented for frames moved back to + * pending_tx, otherwise the CC state diverges from reality. Use on_discard + * which adjusts bytes_in_flight without touching cwnd or ssthresh. + */ + if (total_bytes_lost && qn->cc_ops && qn->cc_ops->on_discard) + qn->cc_ops->on_discard(wsi, total_bytes_lost); send_frames: /* @@ -1343,16 +1620,27 @@ rops_handle_POLLOUT_quic(struct lws *wsi) continue; } - if (!qn->keys[level]->valid) { + if (!qn->keys[level]->el_hp_tx.len) { + continue; + } + + /* Server MUST NOT send 0-RTT (EARLY) packets */ + if (level == LWS_QUIC_LEVEL_EARLY && qn->is_server) { continue; } - if (!qn->pending_tx[level].count && !qn->needs_ack[level]) { + int pn_space = (level == LWS_QUIC_LEVEL_EARLY || level == LWS_QUIC_LEVEL_APP) ? LWS_QUIC_LEVEL_APP : level; + int is_ack_allowed = (level != LWS_QUIC_LEVEL_EARLY); + + if (!qn->pending_tx[level].count && !(qn->needs_ack[pn_space] && is_ack_allowed)) { continue; } - // lwsl_wsi_notice(wsi, "QUIC TX: Processing level %d. pending=%d, needs_ack=%d, pto_needed=%d", level, qn->pending_tx[level].count, qn->needs_ack[level], qn->pto_probe_needed); + // lwsl_wsi_notice(wsi, "QUIC TX: Processing level %d. pending=%d, needs_ack=%d, pto_needed=%d", level, qn->pending_tx[level].count, qn->needs_ack[pn_space], qn->pto_probe_needed); + /* Determine if we're allowed to send */ + int is_congestion_limited = 0; + (void)is_congestion_limited; uint32_t mtu = qn->current_mtu ? qn->current_mtu : 1280; /* Enforce RFC 9000 Anti-Amplification Limit (Section 8.1) for servers */ @@ -1375,8 +1663,9 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } } - /* Check congestion window */ - if (!qn->pto_probe_needed && !qn->needs_ack[level] && qn->cc_ops && qn->cc_ops->can_send && !qn->cc_ops->can_send(wsi, mtu)) { + /* Check congestion window - bypass for PTO probes and ACKs */ + if (!qn->pto_probe_needed && !(qn->needs_ack[pn_space] && is_ack_allowed) && qn->cc_ops && qn->cc_ops->can_send && !qn->cc_ops->can_send(wsi, mtu)) { + is_congestion_limited = 1; #if (_LWS_ENABLED_LOGS & LLL_INFO) LWS_RATELIMIT_DEFINE_STATIC(rl); lwsl_ratelimit_info(&rl, 1000000, "QUIC TX: Congestion window full, blocking POLLOUT\n"); @@ -1385,8 +1674,8 @@ rops_handle_POLLOUT_quic(struct lws *wsi) break; /* Stop processing sending loops */ } - /* Check pacing */ - if (!qn->needs_ack[level] && qn->cc_ops && qn->cc_ops->get_pacing_delay) { + /* Check pacing - bypass for PTO probes, exactly as we bypass CC */ + if (!qn->pto_probe_needed && !(qn->needs_ack[pn_space] && is_ack_allowed) && qn->cc_ops && qn->cc_ops->get_pacing_delay) { lws_usec_t delay = qn->cc_ops->get_pacing_delay(wsi, mtu); if (delay > 0) { lws_sul_schedule(wsi->a.context, 0, &qn->pacer_sul, lws_quic_pacer_cb, delay); @@ -1396,23 +1685,41 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } /* AEAD Confidentiality Limits Check (RFC 9001 Section 6.6) */ - if (level == LWS_QUIC_LEVEL_APP && qn->tx_packets_since_update > (1ULL << 20)) { + uint64_t update_limit = (1ULL << 20); + if (wsi->a.vhost && (wsi->a.vhost->options & LWS_SERVER_OPTION_QUIC_EARLY_KEY_UPDATE)) { + update_limit = 10; /* Trigger early for interop testing */ + } + if (level == LWS_QUIC_LEVEL_APP && qn->tx_packets_since_update > update_limit) { lws_quic_initiate_key_update(wsi); } /* We have frames to send at this encryption level! */ uint8_t *p = pkt; uint64_t my_pn = qn->keys[level]->pn_tx++; + lws_sockaddr46 packet_dest_sa46; + int has_packet_dest = 0; /* 1. Serialize Header */ size_t pn_offset = 0; size_t header_len = 0; - if (level == LWS_QUIC_LEVEL_INITIAL || level == LWS_QUIC_LEVEL_HANDSHAKE) { - if (level == LWS_QUIC_LEVEL_INITIAL) - *p++ = 0xc0 | 0x00 | 0x01; /* Long Header, Initial, 2-byte PN */ - else - *p++ = 0xc0 | 0x20 | 0x01; /* Long Header, Handshake, 2-byte PN */ + if (level == LWS_QUIC_LEVEL_INITIAL || level == LWS_QUIC_LEVEL_HANDSHAKE || level == LWS_QUIC_LEVEL_EARLY) { + if (level == LWS_QUIC_LEVEL_INITIAL) { + if (qn->version == LWS_QUIC_VERSION_2) + *p++ = 0xc0 | 0x10 | 0x01; /* v2 Initial */ + else + *p++ = 0xc0 | 0x00 | 0x01; /* v1 Initial */ + } else if (level == LWS_QUIC_LEVEL_EARLY) { + if (qn->version == LWS_QUIC_VERSION_2) + *p++ = 0xc0 | 0x20 | 0x01; /* v2 0-RTT */ + else + *p++ = 0xc0 | 0x10 | 0x01; /* v1 0-RTT */ + } else { + if (qn->version == LWS_QUIC_VERSION_2) + *p++ = 0xc0 | 0x30 | 0x01; /* v2 Handshake */ + else + *p++ = 0xc0 | 0x20 | 0x01; /* v1 Handshake */ + } /* Version */ *p++ = (uint8_t)(qn->version >> 24); @@ -1456,17 +1763,26 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } /* 1.5 Generate ACK frame if needed */ - if (qn->needs_ack[level]) { + int has_ack = 0; + int skip_ack_for_dest = 0; + if (qn->pending_tx[level].head) { + struct lws_quic_tx_frame *first_f = lws_container_of(qn->pending_tx[level].head, struct lws_quic_tx_frame, list); + if (first_f->has_dest) { + /* Skip ACK for now, this packet will be dedicated to first_f's destination */ + skip_ack_for_dest = 1; + } + } + if (qn->needs_ack[pn_space] && is_ack_allowed && !skip_ack_for_dest) { if (qn->ecn_rx_ect0 || qn->ecn_rx_ect1 || qn->ecn_rx_ce) { *p++ = 0x03; /* ACK_ECN */ } else { *p++ = LWS_QUIC_FT_ACK; /* ACK (0x02) */ } - p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), qn->highest_rx_pn[level]); /* Largest Acknowledged */ + p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), qn->highest_rx_pn[pn_space]); /* Largest Acknowledged */ p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), 0); /* ACK Delay (0 for now) */ p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), 0); /* ACK Range Count */ uint64_t first_ack_range = 0; - uint64_t bm = qn->rx_pn_bitmask[level] >> 1; + uint64_t bm = qn->rx_pn_bitmask[pn_space] >> 1; while (bm & 1) { first_ack_range++; bm >>= 1; @@ -1479,13 +1795,44 @@ rops_handle_POLLOUT_quic(struct lws *wsi) p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), qn->ecn_rx_ce); } - qn->needs_ack[level] = 0; + qn->needs_ack[pn_space] = 0; + has_ack = 1; } /* 2. Bundle frames from pending_tx until MTU is reached */ lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->pending_tx[level].head) { struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + if (has_packet_dest) { + if (!f->has_dest) { + d = d1; + continue; + } + /* Compare f->dest_sa46 with packet_dest_sa46 */ + int match = 0; + if (packet_dest_sa46.sa4.sin_family == f->dest_sa46.sa4.sin_family) { + if (packet_dest_sa46.sa4.sin_family == AF_INET) { + if (packet_dest_sa46.sa4.sin_addr.s_addr == f->dest_sa46.sa4.sin_addr.s_addr && + packet_dest_sa46.sa4.sin_port == f->dest_sa46.sa4.sin_port) + match = 1; + } +#if defined(LWS_WITH_IPV6) + else if (packet_dest_sa46.sa6.sin6_family == AF_INET6) { + if (!memcmp(&packet_dest_sa46.sa6.sin6_addr, &f->dest_sa46.sa6.sin6_addr, sizeof(struct in6_addr)) && + packet_dest_sa46.sa6.sin6_port == f->dest_sa46.sa6.sin6_port) + match = 1; + } +#endif + } + if (!match) { + d = d1; + continue; + } + } else if (f->has_dest) { + packet_dest_sa46 = f->dest_sa46; + has_packet_dest = 1; + } + /* Check if frame fits in remaining MTU (leaving room for headers and 16-byte AEAD tag) */ size_t frame_header_max_len = 1 + 8 + 8; size_t max_udp_payload = mtu > 48 ? mtu - 48 : 1200; @@ -1496,6 +1843,21 @@ rops_handle_POLLOUT_quic(struct lws *wsi) break; size_t send_len = f->len; + + if (f->type == LWS_QUIC_FT_PATH_CHALLENGE || f->type == LWS_QUIC_FT_PATH_RESPONSE) { + if (qn->migration_probing_wsi && wsi != qn->migration_probing_wsi) { + /* Only send path validation frames on the new probing socket */ + d = d1; + continue; + } + } else if (f->type != LWS_QUIC_FT_PADDING && f->type != LWS_QUIC_FT_NEW_CONNECTION_ID) { + if (qn->migration_probing_wsi && wsi == qn->migration_probing_wsi) { + /* Do not send non-probing frames on the new socket until validated */ + d = d1; + continue; + } + } + if ((size_t)(p - pkt) + frame_header_max_len + send_len + 32 > max_udp_payload) { if ((f->type & 0xf8) == LWS_QUIC_FT_STREAM || f->type == LWS_QUIC_FT_CRYPTO) { send_len = max_udp_payload - (size_t)(p - pkt) - frame_header_max_len - 32; @@ -1548,7 +1910,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), f->offset); /* app_err_code */ } else if (type == LWS_QUIC_FT_MAX_STREAMS_BIDI || type == LWS_QUIC_FT_MAX_STREAMS_UNIDI || type == LWS_QUIC_FT_STREAMS_BLOCKED_BIDI || type == LWS_QUIC_FT_STREAMS_BLOCKED_UNIDI) { - p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), f->limit); + p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), f->limit); lwsl_err("QUIC TX: Serialized MAX_STREAMS! limit %llu\n", (unsigned long long)f->limit); } else if (type == LWS_QUIC_FT_NEW_CONNECTION_ID) { //lwsl_notice( "QUIC TX: Formatting MAX_STREAM_DATA for stream %llu", (unsigned long long)f->stream_id); p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), f->stream_id); /* seq */ @@ -1584,6 +1946,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) lws_dll2_add_tail(&f_sent->list, &qn->in_flight[level]); + /* Update original f in pending_tx */ f->offset += send_len; f->len -= send_len; @@ -1601,6 +1964,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) f->wire_len = 0; lws_dll2_add_tail(&f->list, &qn->in_flight[level]); + /* Schedule PTO timer since we have data in-flight */ lws_sul_schedule(wsi->a.context, 0, &qn->pto_sul, lws_quic_pto_cb, pto_delay); } @@ -1608,7 +1972,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } lws_end_foreach_dll_safe(d, d1); size_t payload_len = (size_t)(p - (pkt + header_len)); - if (payload_len == 0) + if (payload_len == 0 && !has_ack) continue; /* Ensure payload is at least 4 bytes for header protection sampling (RFC 9000 Section 5.4.2) */ @@ -1686,13 +2050,19 @@ rops_handle_POLLOUT_quic(struct lws *wsi) n = (int)send_len; /* Pretend it succeeded */ } else { #if defined(WIN32) || defined(_WIN32) - if (wsi->mux_substream && wsi->udp) + if (has_packet_dest) + n = sendto(fd, (const char *)pkt, (int)send_len, 0, + sa46_sockaddr(&packet_dest_sa46), sa46_socklen(&packet_dest_sa46)); + else if (wsi->mux_substream && wsi->udp) n = sendto(fd, (const char *)pkt, (int)send_len, 0, sa46_sockaddr(&wsi->udp->sa46), sa46_socklen(&wsi->udp->sa46)); else n = send(fd, (const char *)pkt, (int)send_len, 0); #else - if (wsi->mux_substream && wsi->udp) + if (has_packet_dest) + n = (int)sendto(fd, (const void *)pkt, send_len, 0, + sa46_sockaddr(&packet_dest_sa46), sa46_socklen(&packet_dest_sa46)); + else if (wsi->mux_substream && wsi->udp) n = (int)sendto(fd, (const void *)pkt, send_len, 0, sa46_sockaddr(&wsi->udp->sa46), sa46_socklen(&wsi->udp->sa46)); else @@ -1724,8 +2094,39 @@ rops_handle_POLLOUT_quic(struct lws *wsi) || e == ENOBUFS #endif ) { - lwsl_wsi_info(wsi, "QUIC TX: dropping packet (transient tx error), errno=%d", e); - n = (int)send_len; + lwsl_wsi_info(wsi, "QUIC TX: UDP socket EAGAIN/transient error, errno=%d. Pausing send.", e); + + /* + * The OS UDP socket buffer is full. We cannot send this packet. + * We MUST NOT pretend it was sent, because it would move to in_flight + * and wait for a PTO timer to retransmit, causing massive stalls. + * Instead, we keep the frames in pending_tx, and stop sending. + * The OS will wake us up with POLLOUT when the socket drains. + */ + struct lws_dll2 *d = qn->in_flight[level].tail; + while (d) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + if (f->sent_in_pn == my_pn) { + struct lws_dll2 *prev = d->prev; + lws_dll2_remove(d); + f->sent_in_pn = 0; + f->sent_time_us = 0; + f->wire_len = 0; + /* Push to head so it is sent first next time */ + lws_dll2_add_head(&f->list, &qn->pending_tx[level]); + d = prev; + } else { + break; + } + } + + /* Revert PN */ + qn->keys[level]->pn_tx--; + + /* Since we are stopping the send loop, we should keep POLLOUT enabled! */ + blocked = 1; + eagain_blocked = 1; + break; } else { lwsl_wsi_err(wsi, "QUIC TX: Write failed, errno=%d", e); return LWS_HP_RET_BAIL_OK; @@ -1751,8 +2152,12 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } lws_end_foreach_dll(d); } - if (ack_eliciting && qn->cc_ops && qn->cc_ops->on_sent) - qn->cc_ops->on_sent(wsi, send_len); + if (ack_eliciting) { + if (qn->pto_probe_needed > 0) + qn->pto_probe_needed--; + if (qn->cc_ops && qn->cc_ops->on_sent) + qn->cc_ops->on_sent(wsi, send_len); + } // lwsl_wsi_info(wsi, "QUIC TX: Sent %d bytes, bundled frames into PN %llu", // n, (unsigned long long)my_pn); @@ -1770,23 +2175,23 @@ rops_handle_POLLOUT_quic(struct lws *wsi) * has shrunk. We MUST wake up the child streams because they may have * been stalled by the application-layer pacing throttle in tx_credit! */ - if (level == LWS_QUIC_LEVEL_APP && send_len > 0) { - struct lws *curr = wsi->mux.child_list; - while (curr) { - // lwsl_wsi_notice(wsi, "QUIC TX: requesting POLLOUT for child %s", lws_wsi_tag(curr)); - lws_callback_on_writable(curr); - curr = curr->mux.sibling_list; + if (level == LWS_QUIC_LEVEL_APP) { + struct lws *w = wsi->mux.child_list; + + while (w) { + if (w->mux.requested_POLLOUT) + lws_callback_on_writable(w); + w = w->mux.sibling_list; } } } - qn->pto_probe_needed = 0; - /* If we handled all pending crypto/internal frames, give the user a chance to write */ struct lws *nwsi = lws_get_quic_network_wsi(wsi); - lwsl_info("QUIC TX POLLOUT: handshake_done=%d, tx_cr=%d, nwsi_tx_cr=%d\n", + lwsl_debug("QUIC TX POLLOUT: handshake_done=%d, tx_cr=%d, nwsi_tx_cr=%d\n", qn->handshake_done, (int)wsi->txc.tx_cr, (nwsi ? (int)nwsi->txc.tx_cr : 0)); - if (qn && qn->handshake_done) { + /* Process stream queues for Application (1-RTT) and Early (0-RTT) data */ + if (qn && (qn->handshake_done || qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED || qn->early_data_status == LWS_0RTT_STATUS_ACCEPTED)) { if (lws_wsi_txc_check_skint(&wsi->txc, (int32_t)wsi->txc.tx_cr)) goto end_children; if (nwsi && lws_wsi_txc_check_skint(&nwsi->txc, (int32_t)nwsi->txc.tx_cr)) @@ -1797,9 +2202,9 @@ rops_handle_POLLOUT_quic(struct lws *wsi) { struct lws *curr = wsi->mux.child_list; int sanity = 1000000; - lwsl_info("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); + lwsl_debug("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); while (curr && sanity--) { - lwsl_info("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", + lwsl_debug("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", lws_wsi_tag(curr), curr->mux.requested_POLLOUT, (int)curr->txc.tx_cr); curr = curr->mux.sibling_list; } @@ -1816,7 +2221,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) wa = &(*wsi2)->mux.sibling_list; - lwsl_info("QUIC TX POLLOUT: visiting child %s, requested_POLLOUT=%d\n", lws_wsi_tag(*wsi2), (*wsi2)->mux.requested_POLLOUT); + lwsl_debug("QUIC TX POLLOUT: visiting child %s, requested_POLLOUT=%d\n", lws_wsi_tag(*wsi2), (*wsi2)->mux.requested_POLLOUT); if (!(*wsi2)->mux.requested_POLLOUT) goto next_child; @@ -1832,6 +2237,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) int is_peer_initiated = (w->mux.my_sid & 1) != (qn->is_server ? 1 : 0); int is_unidiri = (w->mux.my_sid & 2); if (is_peer_initiated && is_unidiri) { + w->mux.requested_POLLOUT = 0; goto next_child; } @@ -1840,27 +2246,42 @@ rops_handle_POLLOUT_quic(struct lws *wsi) usable_credit = lws_rops_func_fidx(w->role_ops, LWS_ROPS_tx_credit). tx_credit(w, LWSTXCR_US_TO_PEER, 0); } - if (lws_wsi_txc_check_skint(&w->txc, usable_credit)) { - if (!w->quic.tx_blocked_sent) { - struct lws_quic_tx_frame *f_sdb = lws_zalloc(sizeof(*f_sdb), "quic sdb"); - if (f_sdb) { - f_sdb->type = LWS_QUIC_FT_STREAM_DATA_BLOCKED; - f_sdb->stream_id = w->mux.my_sid; - f_sdb->limit = w->quic.qs ? w->quic.qs->tx_offset : 0; - lws_dll2_add_head(&f_sdb->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); - } - w->quic.tx_blocked_sent = 1; - lws_callback_on_writable(wsi); // request POLLOUT for nwsi to send the frame - } - goto next_child; - } + + /* Check for actual flow control exhaustion */ + if (lws_wsi_txc_check_skint(&w->txc, w->txc.tx_cr)) { + if (!w->quic.tx_blocked_sent) { + struct lws_quic_tx_frame *f_sdb = lws_zalloc(sizeof(*f_sdb), "quic sdb"); + if (f_sdb) { + f_sdb->type = LWS_QUIC_FT_STREAM_DATA_BLOCKED; + f_sdb->stream_id = w->mux.my_sid; + f_sdb->limit = w->quic.qs ? w->quic.qs->tx_offset : 0; + lws_dll2_add_head(&f_sdb->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); + } + w->quic.tx_blocked_sent = 1; + lws_callback_on_writable(wsi); /* request POLLOUT for nwsi to send the frame */ + } + if (lwsi_state(w) != LRS_FLUSHING_BEFORE_CLOSE) { + w->mux.requested_POLLOUT = 0; + goto next_child; + } + } + + /* Check for local throttling (e.g., pending_tx queue full) */ + if (usable_credit <= 0 && lwsi_state(w) != LRS_FLUSHING_BEFORE_CLOSE) { + w->mux.requested_POLLOUT = 1; + break; + } w->mux.requested_POLLOUT = 0; - lwsl_info("QUIC TX POLLOUT: calling perform_user_POLLOUT/lws_callback_as_writeable for child %s\n", lws_wsi_tag(w)); + lwsl_notice("QUIC TX POLLOUT: calling perform_user_POLLOUT for child %s, usable_credit=%d, pending_tx=%d\n", + lws_wsi_tag(w), (int)usable_credit, qn->pending_tx[LWS_QUIC_LEVEL_APP].count); + + lwsl_debug("QUIC TX POLLOUT: calling perform_user_POLLOUT/lws_callback_as_writeable for child %s\n", lws_wsi_tag(w)); if (lws_rops_fidx(w->role_ops, LWS_ROPS_perform_user_POLLOUT)) { if (lws_rops_func_fidx(w->role_ops, LWS_ROPS_perform_user_POLLOUT). perform_user_POLLOUT(w) == -1) { + lwsl_wsi_info(w, "QUIC TX: child perform_user_POLLOUT requested close"); int _found = 0; lws_start_foreach_ll(struct lws *, _w1, wsi->mux.child_list) { @@ -1888,28 +2309,83 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } end_children: - lwsl_info("QUIC TX POLLOUT: calling lws_wsi_mux_action_pending_writeable_reqs\n"); - - int can_process_children = (qn->handshake_done && wsi->txc.tx_cr > 0 && (!nwsi || nwsi->txc.tx_cr > 0)); + lwsl_debug("QUIC TX POLLOUT: calling lws_wsi_mux_action_pending_writeable_reqs\n"); + /* Give children output credits based on fair sharing */ + int can_process_children = ((qn->handshake_done || qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED || qn->early_data_status == LWS_0RTT_STATUS_ACCEPTED) && wsi->txc.tx_cr > 0 && (!nwsi || nwsi->txc.tx_cr > 0)); int have_pending_tx = 0; for (level = 0; level < LWS_QUIC_LEVEL_COUNT; level++) { - if (qn->pending_tx[level].count) { - have_pending_tx = 1; - break; + if (qn->pending_tx[level].count && qn->keys[level] && qn->keys[level]->el_hp_tx.len) { + if (level == LWS_QUIC_LEVEL_APP && qn->migration_probing_wsi) { + lws_start_foreach_dll(struct lws_dll2 *, d, qn->pending_tx[level].head) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + int is_path_frame = (f->type == LWS_QUIC_FT_PATH_CHALLENGE || f->type == LWS_QUIC_FT_PATH_RESPONSE); + if (wsi == qn->nwsi && is_path_frame) continue; + if (wsi == qn->migration_probing_wsi && !is_path_frame && f->type != LWS_QUIC_FT_PADDING && f->type != LWS_QUIC_FT_NEW_CONNECTION_ID) continue; + have_pending_tx = 1; + break; + } lws_end_foreach_dll(d); + } else { + have_pending_tx = 1; + } + if (have_pending_tx) + break; + } + } + + if (!blocked && can_process_children && !have_pending_tx) { + if (lws_wsi_mux_action_pending_writeable_reqs(wsi)) + return LWS_HP_RET_BAIL_DIE; + + for (level = 0; level < LWS_QUIC_LEVEL_COUNT; level++) { + if (qn->pending_tx[level].count && qn->keys[level] && qn->keys[level]->el_hp_tx.len) { + if (level == LWS_QUIC_LEVEL_APP && qn->migration_probing_wsi) { + lws_start_foreach_dll(struct lws_dll2 *, d, qn->pending_tx[level].head) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + int is_path_frame = (f->type == LWS_QUIC_FT_PATH_CHALLENGE || f->type == LWS_QUIC_FT_PATH_RESPONSE); + if (wsi == qn->nwsi && is_path_frame) continue; + if (wsi == qn->migration_probing_wsi && !is_path_frame && f->type != LWS_QUIC_FT_PADDING && f->type != LWS_QUIC_FT_NEW_CONNECTION_ID) continue; + have_pending_tx = 1; + break; + } lws_end_foreach_dll(d); + } else { + have_pending_tx = 1; + } + if (have_pending_tx) + break; + } } } // lwsl_wsi_notice(wsi, "QUIC TX: blocked=%d, have_pending_tx=%d, can_process_children=%d", blocked, have_pending_tx, can_process_children); - if (blocked || (!have_pending_tx && !can_process_children)) { - // lwsl_wsi_notice(wsi, "QUIC TX: dropping POLLOUT manually (LWS_POLLOUT, 0)"); - /* We are blocked by QUIC limits, or have nothing to send and children can't write. - * Stop asking the OS for POLLOUT. We will re-enable it when POLLIN brings ACKs. */ + if ((blocked && !eagain_blocked) || !have_pending_tx) { + lwsl_notice("QUIC TX: dropping POLLOUT manually for %s (blocked=%d, eagain_blocked=%d, have_pending_tx=%d)\n", lws_wsi_tag(wsi), blocked, eagain_blocked, have_pending_tx); + /* We are blocked by QUIC limits, or have nothing to send right now. + * Stop asking the OS for POLLOUT. We will re-enable it if children + * need to write. */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) return LWS_HP_RET_BAIL_DIE; - } else { - // lwsl_wsi_notice(wsi, "QUIC TX: calling lws_wsi_mux_action_pending_writeable_reqs (wsi->mux.requested_POLLOUT=%d)", wsi->mux.requested_POLLOUT); - if (lws_wsi_mux_action_pending_writeable_reqs(wsi)) - return LWS_HP_RET_BAIL_DIE; + } + + if (have_pending_tx) + return LWS_HP_RET_BAIL_OK; + + /* + * Check if any child still needs writeable service. + * lws_wsi_mux_action_pending_writeable_reqs() may have + * re-enabled POLLOUT on the fd, but the caller in service.c + * will drop it again if we return LWS_HP_RET_DROP_POLLOUT. + * This happens when children transition to LRS_ISSUING_FILE + * and call lws_callback_on_writable() — they need another + * POLLOUT cycle to call lws_serve_http_file_fragment(). + */ + { + struct lws *w = wsi->mux.child_list; + + while (w) { + if (w->mux.requested_POLLOUT) + return LWS_HP_RET_BAIL_OK; + w = w->mux.sibling_list; + } } } @@ -1935,44 +2411,40 @@ rops_write_role_protocol_quic(struct lws *wsi, unsigned char *buf, size_t len, /* Enforce stream and connection flow control limits */ if (len > 0) { - lwsl_info("QUIC TX WRITE: wsi->txc.tx_cr=%d, nwsi->txc.tx_cr=%d\n", (int)wsi->txc.tx_cr, nwsi ? (int)nwsi->txc.tx_cr : -1); - if (wsi->txc.tx_cr < (int)len || wsi->txc.tx_cr <= 0 || - (nwsi && (nwsi->txc.tx_cr < (int)len || nwsi->txc.tx_cr <= 0))) { - int did_enqueue = 0; - if ((wsi->txc.tx_cr < (int)len || wsi->txc.tx_cr <= 0) && !wsi->quic.tx_blocked_sent) { - /* Generate STREAM_DATA_BLOCKED */ - struct lws_quic_tx_frame *f_sdb = lws_zalloc(sizeof(*f_sdb), "quic sdb"); - if (f_sdb) { - f_sdb->type = LWS_QUIC_FT_STREAM_DATA_BLOCKED; - f_sdb->stream_id = wsi->mux.my_sid; - f_sdb->limit = wsi->quic.qs ? wsi->quic.qs->tx_offset : 0; - lws_dll2_add_head(&f_sdb->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); - } - wsi->quic.tx_blocked_sent = 1; - did_enqueue = 1; + lwsl_debug("QUIC TX WRITE: wsi->txc.tx_cr=%d, nwsi->txc.tx_cr=%d\n", (int)wsi->txc.tx_cr, nwsi ? (int)nwsi->txc.tx_cr : -1); + + int did_enqueue = 0; + if (wsi->txc.tx_cr <= 0 && !wsi->quic.tx_blocked_sent) { + /* Generate STREAM_DATA_BLOCKED */ + struct lws_quic_tx_frame *f_sdb = lws_zalloc(sizeof(*f_sdb), "quic sdb"); + if (f_sdb) { + f_sdb->type = LWS_QUIC_FT_STREAM_DATA_BLOCKED; + f_sdb->stream_id = wsi->mux.my_sid; + f_sdb->limit = wsi->quic.qs ? wsi->quic.qs->tx_offset : 0; + lws_dll2_add_head(&f_sdb->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); } + wsi->quic.tx_blocked_sent = 1; + did_enqueue = 1; + } - if (nwsi && nwsi != wsi && (nwsi->txc.tx_cr < (int)len || nwsi->txc.tx_cr <= 0) && !nwsi->quic.tx_blocked_sent) { - /* Generate DATA_BLOCKED */ - struct lws_quic_tx_frame *f_db = lws_zalloc(sizeof(*f_db), "quic db"); - if (f_db) { - f_db->type = LWS_QUIC_FT_DATA_BLOCKED; - f_db->limit = qn->tx_conn_offset; - lws_dll2_add_head(&f_db->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); - } - nwsi->quic.tx_blocked_sent = 1; - did_enqueue = 1; + if (nwsi && nwsi != wsi && nwsi->txc.tx_cr <= 0 && !nwsi->quic.tx_blocked_sent) { + /* Generate DATA_BLOCKED */ + struct lws_quic_tx_frame *f_db = lws_zalloc(sizeof(*f_db), "quic db"); + if (f_db) { + f_db->type = LWS_QUIC_FT_DATA_BLOCKED; + f_db->limit = qn->tx_conn_offset; + lws_dll2_add_head(&f_db->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); } - - /* Kick output to send these control frames */ - if (did_enqueue) - lws_callback_on_writable(nwsi ? nwsi : wsi); - - return 0; /* Consumed 0 bytes, caller should yield and try again later */ + nwsi->quic.tx_blocked_sent = 1; + did_enqueue = 1; } + + /* Kick output to send these control frames */ + if (did_enqueue) + lws_callback_on_writable(nwsi ? nwsi : wsi); } - lwsl_info("QUIC TX WRITE: Stream %llu. Requested: %d, Stream tx_cr: %d, Conn tx_cr: %d\n", wsi->quic.qs ? (unsigned long long)wsi->quic.qs->stream_id : 0, (int)len, (int)wsi->txc.tx_cr, nwsi ? (int)nwsi->txc.tx_cr : -1); + lwsl_debug("QUIC TX WRITE: Stream %llu. Requested: %d, Stream tx_cr: %d, Conn tx_cr: %d\n", wsi->quic.qs ? (unsigned long long)wsi->quic.qs->stream_id : 0, (int)len, (int)wsi->txc.tx_cr, nwsi ? (int)nwsi->txc.tx_cr : -1); // lwsl_notice("%s: allocating frame of size %d\n", __func__, (int)(sizeof(*f) + len)); /* Allocate frame struct + payload buffer natively */ @@ -1981,7 +2453,8 @@ rops_write_role_protocol_quic(struct lws *wsi, unsigned char *buf, size_t len, return -1; f->type = LWS_QUIC_FT_STREAM | 0x02 | 0x04; /* STREAM | OFF | LEN */ - if ((*wp) & LWS_WRITE_H2_STREAM_END) { + if (((*wp) & LWS_WRITE_H2_STREAM_END) || + ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL) { f->type |= 0x01; /* FIN */ /* wsi->quic.qs->sent_fin = 1; could do if we had sent_fin flag */ } @@ -1998,7 +2471,7 @@ rops_write_role_protocol_quic(struct lws *wsi, unsigned char *buf, size_t len, f->type = LWS_QUIC_FT_DATAGRAM + 1; /* with LEN */ f->stream_id = 0; /* Datagrams aren't attached to a stream ID */ f->offset = 0; - lwsl_info("QUIC TX WRITE: Datagram len=%u\n", (unsigned int)f->len); + lwsl_debug("QUIC TX WRITE: Datagram len=%u\n", (unsigned int)f->len); } else { if (!wsi->quic.qs) { lwsl_wsi_err(wsi, "QUIC: Cannot send stream data without a quic stream structure!"); @@ -2009,15 +2482,13 @@ rops_write_role_protocol_quic(struct lws *wsi, unsigned char *buf, size_t len, f->offset = wsi->quic.qs->tx_offset; wsi->quic.qs->tx_offset += len; - lwsl_info("QUIC TX WRITE: stream_id=%llu, offset=%llu, len=%u, fin=%d\n", + lwsl_debug("QUIC TX WRITE: stream_id=%llu, offset=%llu, len=%u, fin=%d\n", (unsigned long long)f->stream_id, (unsigned long long)f->offset, (unsigned int)f->len, (f->type & 0x01)); - /* Deduct credit */ + /* Deduct credit here so current_max calculations work correctly */ wsi->txc.tx_cr -= (int)len; - if (nwsi && nwsi != wsi) { - nwsi->txc.tx_cr -= (int)len; - } if (nwsi) { + nwsi->txc.tx_cr -= (int)len; qn->tx_conn_offset += len; } } @@ -2067,17 +2538,32 @@ rops_client_bind_quic(struct lws *wsi, const struct lws_client_connect_info *i) memset(wsi->udp, 0, sizeof(*wsi->udp)); } - /* Allocate QUIC netconn for client! */ - if (!wsi->quic.qn) { - wsi->quic.qn = lws_zalloc(sizeof(*wsi->quic.qn), "quic_netconn"); + /* Handle Make-Before-Break Active Migration */ + if (wsi->quic.migrate_from_wsi && wsi->quic.migrate_from_wsi->quic.qn) { + wsi->quic.qn = wsi->quic.migrate_from_wsi->quic.qn; + wsi->quic.qn->migration_probing_wsi = wsi; + lwsl_notice("QUIC: Created migration probing wsi %p for qn %p\n", wsi, wsi->quic.qn); + + lws_quic_queue_path_challenge(wsi); + + /* Skip all initialization and key derivation, just return */ + return 1; + } + + /* Allocate QUIC netconn for client! */ + if (!wsi->quic.qn) { + wsi->quic.qn = lws_zalloc(sizeof(*wsi->quic.qn), "quic_netconn"); if (!wsi->quic.qn) return 1; } wsi->quic.qn->nwsi = wsi; wsi->quic.qn->is_server = 0; + wsi->quic.qn->next_stream_id_bidi_local = 0; + wsi->quic.qn->next_stream_id_unidi_local = 2; wsi->quic.qn->version = LWS_QUIC_VERSION_1; - wsi->quic.qn->max_streams_bidi_local = 1024; + wsi->quic.qn->original_version = wsi->quic.qn->version; + wsi->quic.qn->max_streams_bidi_local = 1000; wsi->quic.qn->max_streams_unidi_local = 1024; wsi->quic.qn->current_mtu = 1280; @@ -2152,6 +2638,22 @@ rops_client_bind_quic(struct lws *wsi, const struct lws_client_connect_info *i) LWS_QUIC_WRITE_TP_VARINT(0x01, 30000); LWS_QUIC_WRITE_TP_BUF(0x0F, wsi->quic.qn->loc_cid.id, wsi->quic.qn->loc_cid.len); + if (wsi->a.context->options & LWS_SERVER_OPTION_QUIC_LATEST_VERSION) { + uint8_t vi_buf[12]; + vi_buf[0] = (uint8_t)(LWS_QUIC_VERSION_1 >> 24); + vi_buf[1] = (uint8_t)(LWS_QUIC_VERSION_1 >> 16); + vi_buf[2] = (uint8_t)(LWS_QUIC_VERSION_1 >> 8); + vi_buf[3] = (uint8_t)(LWS_QUIC_VERSION_1); + vi_buf[4] = (uint8_t)(LWS_QUIC_VERSION_1 >> 24); + vi_buf[5] = (uint8_t)(LWS_QUIC_VERSION_1 >> 16); + vi_buf[6] = (uint8_t)(LWS_QUIC_VERSION_1 >> 8); + vi_buf[7] = (uint8_t)(LWS_QUIC_VERSION_1); + vi_buf[8] = (uint8_t)(LWS_QUIC_VERSION_2 >> 24); + vi_buf[9] = (uint8_t)(LWS_QUIC_VERSION_2 >> 16); + vi_buf[10] = (uint8_t)(LWS_QUIC_VERSION_2 >> 8); + vi_buf[11] = (uint8_t)(LWS_QUIC_VERSION_2); + LWS_QUIC_WRITE_TP_BUF(0x11, vi_buf, 12); + } lws_tls_quic_set_transport_parameters(wsi, wsi->quic.qn->local_tp_buf, (size_t)(tp - wsi->quic.qn->local_tp_buf)); @@ -2248,33 +2750,26 @@ static int rops_callback_on_writable_quic(struct lws *wsi) { struct lws *nwsi = lws_get_quic_network_wsi(wsi); - int already; if (!nwsi) return 0; - if (wsi->mux.requested_POLLOUT) { - // lwsl_info("rops_callback_on_writable_quic: %s already pending writable\n", lws_wsi_tag(wsi)); - } else { - lwsl_info("rops_callback_on_writable_quic: marking %s as pending writable (nwsi=%s)\n", lws_wsi_tag(wsi), lws_wsi_tag(nwsi)); - } - - already = lws_wsi_mux_mark_parents_needing_writeable(wsi); - - /* for network action, act only on the network wsi */ - if (already && nwsi && nwsi != wsi) - return lws_callback_on_writable(nwsi); + if (!wsi->mux.requested_POLLOUT) + lwsl_debug("rops_callback_on_writable_quic: marking %s as pending writable (nwsi=%s)\n", + lws_wsi_tag(wsi), lws_wsi_tag(nwsi)); - /* If we are the network wsi but we have a listener parent (shared UDP port), propagate to it */ - if (already && wsi->mux.parent_wsi) - return lws_callback_on_writable(wsi->mux.parent_wsi); + /* Mark the entire parent chain as needing POLLOUT */ + lws_wsi_mux_mark_parents_needing_writeable(wsi); - return 0; /* not handled, let core handle it */ + return 0; /* not handled, let core enable socket POLLOUT */ } void lws_quic_stream_cleanup(struct lws *wsi) { - struct lws *nwsi = lws_get_network_wsi(wsi); + struct lws *nwsi = wsi; + while (nwsi && !nwsi->quic.qn) { + nwsi = nwsi->mux.parent_wsi; + } struct lws_quic_netconn *qn = nwsi ? nwsi->quic.qn : NULL; int i; @@ -2329,22 +2824,61 @@ lws_quic_stream_cleanup(struct lws *wsi) if (nwsi) lws_callback_on_writable(nwsi); } - /* If this was a remote-initiated stream, increase our limit and notify peer */ + /* If this was a remote-initiated stream, increase our limit and notify peer. + * + * Coalesce: scan the pending_tx queue for an existing MAX_STREAMS frame + * of the same type and update its limit in place, rather than allocating + * a new frame per closed stream. With many concurrent streams (e.g. the + * multiplexing QIR test with 1999 streams), the O(N) frame flood would + * fill the TX queue and trigger PTO/congestion-window collapse. + */ if (is_remote_initiated) { - struct lws_quic_tx_frame *f_max = lws_zalloc(sizeof(*f_max), "quic max strm"); - if (f_max) { - if (!is_unidirectional) { - qn->max_streams_bidi_local++; - f_max->type = LWS_QUIC_FT_MAX_STREAMS_BIDI; - f_max->limit = qn->max_streams_bidi_local; - } else { - qn->max_streams_unidi_local++; - f_max->type = LWS_QUIC_FT_MAX_STREAMS_UNIDI; - f_max->limit = qn->max_streams_unidi_local; + uint8_t want_type = is_unidirectional ? + LWS_QUIC_FT_MAX_STREAMS_UNIDI : LWS_QUIC_FT_MAX_STREAMS_BIDI; + struct lws_quic_tx_frame *f_exist = NULL; + + /* Increment our local limit first */ + if (!is_unidirectional) + qn->max_streams_bidi_local++; + else + qn->max_streams_unidi_local++; + + /* Look for an already-queued MAX_STREAMS frame to coalesce into */ + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&qn->pending_tx[LWS_QUIC_LEVEL_APP])) { + struct lws_quic_tx_frame *f = lws_container_of(d, + struct lws_quic_tx_frame, list); + if (f->type == want_type) { + f_exist = f; + break; + } + } lws_end_foreach_dll(d); + + if (f_exist) { + /* Update limit in the existing frame — no new allocation */ + f_exist->limit = is_unidirectional ? + qn->max_streams_unidi_local : + qn->max_streams_bidi_local; + lwsl_debug("QUIC TX: Coalesced MAX_STREAMS_%s limit → %llu\n", + is_unidirectional ? "UNIDI" : "BIDI", + (unsigned long long)f_exist->limit); + } else { + struct lws_quic_tx_frame *f_max = + lws_zalloc(sizeof(*f_max), "quic max strm"); + if (f_max) { + f_max->stream_id = -1ULL; + f_max->type = want_type; + f_max->limit = is_unidirectional ? + qn->max_streams_unidi_local : + qn->max_streams_bidi_local; + lwsl_notice("QUIC TX: Queued MAX_STREAMS_%s with limit %llu\n", + is_unidirectional ? "UNIDI" : "BIDI", + (unsigned long long)f_max->limit); + lws_dll2_add_tail(&f_max->list, + &qn->pending_tx[LWS_QUIC_LEVEL_APP]); } - lws_dll2_add_tail(&f_max->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); - if (nwsi) lws_callback_on_writable(nwsi); } + if (nwsi) lws_callback_on_writable(nwsi); } if (is_abort) { @@ -2352,7 +2886,10 @@ lws_quic_stream_cleanup(struct lws *wsi) /* Purge pending_tx */ lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->pending_tx[i].head) { struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); - if (f->stream_id == sid && f->type != LWS_QUIC_FT_RESET_STREAM && f->type != LWS_QUIC_FT_STOP_SENDING) { + int is_stream_frame = ((f->type & 0xf8) == LWS_QUIC_FT_STREAM) || + (f->type == LWS_QUIC_FT_MAX_STREAM_DATA) || + (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED); + if (is_stream_frame && f->stream_id == sid) { lws_dll2_remove(&f->list); lws_free(f); } @@ -2361,7 +2898,12 @@ lws_quic_stream_cleanup(struct lws *wsi) /* Purge in_flight */ lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->in_flight[i].head) { struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); - if (f->stream_id == sid) { + int is_stream_frame = ((f->type & 0xf8) == LWS_QUIC_FT_STREAM) || + (f->type == LWS_QUIC_FT_MAX_STREAM_DATA) || + (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED); + if (is_stream_frame && f->stream_id == sid) { + if (qn->cc_ops && qn->cc_ops->on_discard) + qn->cc_ops->on_discard(nwsi, f->wire_len); lws_dll2_remove(&f->list); lws_free(f); } @@ -2387,7 +2929,7 @@ rops_close_kill_connection_quic(struct lws *wsi, enum lws_close_status reason) if (wsi->mux.parent_wsi) { struct lws *nwsi = wsi->mux.parent_wsi; lws_wsi_mux_sibling_disconnect(wsi); - if (nwsi->mux.child_count == 0) + if (nwsi->mux.child_count == 0 && nwsi->quic.qn) lws_set_timeout(nwsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, nwsi->a.vhost->keepalive_timeout ? nwsi->a.vhost->keepalive_timeout : 5); @@ -2454,6 +2996,13 @@ rops_close_kill_connection_quic(struct lws *wsi, enum lws_close_status reason) if (qn->cc_state) lws_free_set_NULL(qn->cc_state); + for (int _i = 0; _i < 4; _i++) { + if (qn->crypto_rx_buf[_i]) { + lws_free(qn->crypto_rx_buf[_i]); + qn->crypto_rx_buf[_i] = NULL; + } + } + lws_free_set_NULL(wsi->quic.qn); } @@ -2513,6 +3062,7 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) wsi->quic.qs->advertised_rx_max_data = wsi->quic.qs->rx_max_data; lws_dll2_add_tail(&f_msd->list, &qn->pending_tx[LWS_QUIC_LEVEL_APP]); } + lws_callback_on_writable(nwsi); } } @@ -2534,6 +3084,7 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) qn->last_rx_update_us = now; struct lws_quic_tx_frame *f_md = lws_zalloc(sizeof(*f_md), "quic md"); if (f_md) { + lwsl_notice("QUIC: Generating MAX_DATA limit %llu", (unsigned long long)qn->rx_max_data); f_md->type = LWS_QUIC_FT_MAX_DATA; f_md->limit = qn->rx_max_data; qn->advertised_rx_max_data = qn->rx_max_data; @@ -2548,11 +3099,8 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) /* We're being told we can write an additional "add" bytes to the peer */ wsi->txc.tx_cr += add; - wsi->quic.tx_blocked_sent = 0; - if (nwsi != wsi) { - nwsi->txc.tx_cr += add; - nwsi->quic.tx_blocked_sent = 0; - } + if (add > 0) + wsi->quic.tx_blocked_sent = 0; /* Unblock if blocked */ if (wsi->txc.tx_cr > 0) { @@ -2577,23 +3125,55 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) if (nwsi->txc.tx_cr < cr) cr = nwsi->txc.tx_cr; - /* - * Throttle application-layer writes if the QUIC pending_tx list is growing too large. - * On WAN links, the CC limits the physical sending rate. If we don't throttle - * the application, it will buffer the entire file into RAM, consuming MBs of memory. + /* + * Throttle application-layer writes to prevent the pending_tx + * buffer from growing beyond what the congestion window can + * deliver promptly. The original flat 64KB cap caused a + * permanent stall: at startup cwnd=10×1280=12800 bytes, so + * 64KB is 5× cwnd, creating a bytes_in_flight debt that takes + * tens of seconds of ACKs to drain below the growing cwnd. + * + * Cap at max(128KB, 4×cwnd): + * - 128KB floor handles multiplexing (many small streams each + * needing HEADERS frames before the first ACK returns). + * - 4×cwnd keeps large-file transfers bounded to a manageable + * debt: at cwnd=12800 the cap is 51200 bytes (4 windows), + * which drains in <1 RTT once ACKs arrive. + * - As cwnd grows via slow-start the cap grows with it, + * maintaining full throughput. */ + size_t queued_bytes = 0; if (nwsi->quic.qn) { - size_t queued_bytes = 0; - lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&nwsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP])) { - struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + uint32_t mtu = nwsi->quic.qn->current_mtu ? + nwsi->quic.qn->current_mtu : 1280; + + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&nwsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP])) { + struct lws_quic_tx_frame *f = lws_container_of(d, + struct lws_quic_tx_frame, list); queued_bytes += f->len; } lws_end_foreach_dll(d); - /* Cap at 64KB of buffered TX frames to pace application */ - if (queued_bytes >= 65536) - cr = 0; - else if (cr > (int)(65536 - queued_bytes)) - cr = (int)(65536 - queued_bytes); + /* + * Throttle based on available cwnd headroom: cap new writes + * to cwnd - bytes_in_flight - queued_bytes. bytes_in_flight + * only counts serialized frames; queued_bytes (pending_tx) is + * already committed but not yet on the wire. Together they + * represent our total debt against the cwnd. + * + * Floor: always allow at least 1 MTU so the stream + * makes progress even when the window is exactly full. + */ + if (nwsi->quic.qn->cc_state) { + struct lws_quic_cc_newreno *cc = + (struct lws_quic_cc_newreno *)nwsi->quic.qn->cc_state; + size_t debt = cc->bytes_in_flight + queued_bytes; + size_t headroom = (cc->cwnd > debt) ? cc->cwnd - debt : 0; + if (headroom < mtu) + headroom = mtu; /* always allow 1 packet */ + if (cr > (int)headroom) + cr = (int)headroom; + } } /* @@ -2607,7 +3187,15 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) else cr = 0; - lwsl_info("rops_tx_credit_quic: LWSTXCR_US_TO_PEER returning %d (wsi->txc.tx_cr=%d, nwsi->txc.tx_cr=%d)\n", + if (!cr && !add) + lwsl_notice("rops_tx_credit_quic: US_TO_PEER returning 0: " + "wsi_tx_cr=%d, nwsi_tx_cr=%d, queued=%zu, " + "wsi=%s, nwsi=%s\n", + (int)wsi->txc.tx_cr, (int)nwsi->txc.tx_cr, + queued_bytes, lws_wsi_tag(wsi), + lws_wsi_tag(nwsi)); + + lwsl_debug("rops_tx_credit_quic: LWSTXCR_US_TO_PEER returning %d (wsi->txc.tx_cr=%d, nwsi->txc.tx_cr=%d)\n", cr, (int)wsi->txc.tx_cr, (int)nwsi->txc.tx_cr); return cr; /* how much we can write to peer */ } @@ -2616,7 +3204,7 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) if (n > nwsi->txc.peer_tx_cr_est) n = nwsi->txc.peer_tx_cr_est; - lwsl_info("rops_tx_credit_quic: returning %d\n", n); + lwsl_debug("rops_tx_credit_quic: returning %d\n", n); return n; } @@ -2690,15 +3278,17 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) memset(&wsi->quic, 0, sizeof(wsi->quic)); memset(&wsi->tls, 0, sizeof(wsi->tls)); lws_tls_quic_migrate_wsi(wsi, nwsi); - wsi->quic.qs = lws_zalloc(sizeof(*wsi->quic.qs), "quic stream"); - if (wsi->quic.qs) { - wsi->quic.qs->rx_max_data = LWS_QUIC_DEFAULT_WINDOW; - wsi->quic.qs->advertised_rx_max_data = LWS_QUIC_DEFAULT_WINDOW; - wsi->quic.qs->rx_window_size = LWS_QUIC_DEFAULT_WINDOW; - wsi->quic.qs->last_rx_update_us = lws_now_usecs(); - } else { - lws_close_free_wsi(nwsi, LWS_CLOSE_STATUS_NOSTATUS, "quic stream oom"); - return 1; + if (!wsi->quic.qs) { + wsi->quic.qs = lws_zalloc(sizeof(*wsi->quic.qs), "quic stream"); + if (wsi->quic.qs) { + wsi->quic.qs->rx_max_data = LWS_QUIC_DEFAULT_WINDOW; + wsi->quic.qs->advertised_rx_max_data = LWS_QUIC_DEFAULT_WINDOW; + wsi->quic.qs->rx_window_size = LWS_QUIC_DEFAULT_WINDOW; + wsi->quic.qs->last_rx_update_us = lws_now_usecs(); + } else { + lws_close_free_wsi(nwsi, LWS_CLOSE_STATUS_NOSTATUS, "quic stream oom"); + return 1; + } } /* Initialize flow control credits for the new child stream */ @@ -2752,6 +3342,8 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) wsi->client_h2_alpn = 1; wsi->client_mux_migrated = 1; wsi->hdr_parsing_completed = 0; + nwsi->client_h2_alpn = 1; + nwsi->client_mux_migrated = 1; } #endif @@ -2803,7 +3395,24 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) if (wsi->mux.parent_wsi) lws_wsi_mux_sibling_disconnect(wsi); + struct lws *old_children = wsi->mux.child_list; + unsigned int old_child_count = wsi->mux.child_count; + + wsi->mux.child_list = NULL; + wsi->mux.child_count = 0; + + nwsi->mux.child_list = old_children; + nwsi->mux.child_count = old_child_count; + + struct lws *c = old_children; + while (c) { + c->mux.parent_wsi = nwsi; + c = c->mux.sibling_list; + } + lws_wsi_mux_insert(wsi, nwsi, 0); /* client first request is stream ID 0 */ + if (nwsi->quic.qn->next_stream_id_bidi_local == 0) + nwsi->quic.qn->next_stream_id_bidi_local = 4; lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0); if (listener) { diff --git a/lib/roles/quic/parse-quic.c b/lib/roles/quic/parse-quic.c index 9e7c9ff0f0..fb1e109069 100644 --- a/lib/roles/quic/parse-quic.c +++ b/lib/roles/quic/parse-quic.c @@ -92,6 +92,14 @@ lws_quic_get_pn_offset(const uint8_t *buf, size_t len, size_t *payload_len) /* Long Header (Form = 1) */ type = (buf[0] & 0x30) >> 4; + uint32_t version = (uint32_t)((buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]); + if (version == LWS_QUIC_VERSION_2) { + if (type == 1) type = 0; + else if (type == 0) type = 3; + else if (type == 2) type = 1; + else if (type == 3) type = 2; + } + /* Skip Version (4 bytes) */ pos += 4; @@ -340,8 +348,11 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ } if (!is_crypto && !wsi_child) { - lws_dll2_remove(&c->list); - lws_free(c); + /* + * The WSI was closed and freed. Its cleanup routine + * already freed all buffered chunks, including c. + * We must not touch c, qs or the list anymore. + */ return; } @@ -412,14 +423,43 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ struct lws * lws_quic_stream_find(struct lws *nwsi, uint64_t stream_id) { - struct lws *wsi_child = nwsi->mux.child_list; - while (wsi_child) { - if (wsi_child->quic.qs && wsi_child->quic.qs->stream_id == stream_id) - return wsi_child; - if (wsi_child->mux.my_sid == stream_id) /* Fallback */ - return wsi_child; - wsi_child = wsi_child->mux.sibling_list; + struct lws_quic_netconn *qn = nwsi ? nwsi->quic.qn : NULL; + struct lws *wsi_child; + + if (!qn) { + wsi_child = nwsi ? nwsi->mux.child_list : NULL; + while (wsi_child) { + if (wsi_child->quic.qs && wsi_child->quic.qs->stream_id == stream_id) + return wsi_child; + if (wsi_child->mux.my_sid == (unsigned int)stream_id) + return wsi_child; + wsi_child = wsi_child->mux.sibling_list; + } + return NULL; + } + + if (qn->nwsi) { + wsi_child = qn->nwsi->mux.child_list; + while (wsi_child) { + if (wsi_child->quic.qs && wsi_child->quic.qs->stream_id == stream_id) + return wsi_child; + if (wsi_child->mux.my_sid == (unsigned int)stream_id) + return wsi_child; + wsi_child = wsi_child->mux.sibling_list; + } } + + if (qn->migration_probing_wsi) { + wsi_child = qn->migration_probing_wsi->mux.child_list; + while (wsi_child) { + if (wsi_child->quic.qs && wsi_child->quic.qs->stream_id == stream_id) + return wsi_child; + if (wsi_child->mux.my_sid == (unsigned int)stream_id) + return wsi_child; + wsi_child = wsi_child->mux.sibling_list; + } + } + return NULL; } @@ -428,7 +468,7 @@ lws_quic_stream_find(struct lws *nwsi, uint64_t stream_id) * Parses QUIC frames from a decrypted payload and routes them. */ int -lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payload_len) +lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payload_len, const lws_sockaddr46 *sa46) { size_t pos = 0; uint64_t type, offset, len; @@ -565,6 +605,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl pos += consumed; } } + lws_quic_detect_loss(nwsi, level, largest_ack); break; } @@ -591,7 +632,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl } if (!is_peer_initiated) { - if (is_unidirectional || !wsi_child) { + if (is_unidirectional) { lwsl_wsi_notice(nwsi, "QUIC RX: Invalid RESET_STREAM on stream ID %llu", (unsigned long long)stream_id); lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_STREAM_STATE_ERROR, type, 0); return -1; @@ -707,9 +748,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl if (!is_peer_initiated) { if (!wsi_child) { - lwsl_wsi_notice(nwsi, "QUIC RX: STOP_SENDING on non-existing stream ID %llu", (unsigned long long)stream_id); - lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_STREAM_STATE_ERROR, type, 0); - return -1; + // Ignore STOP_SENDING for closed streams } } else { if (is_unidirectional) { @@ -747,18 +786,22 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_FRAME_ENCODING_ERROR, type, 0); return -1; } - lwsl_wsi_info(nwsi, "QUIC RX: Parsed MAX/BLOCKED STREAMS! max_streams %llu", (unsigned long long)max_streams); + lwsl_wsi_notice(nwsi, "QUIC RX: Parsed MAX/BLOCKED STREAMS! max_streams %llu", (unsigned long long)max_streams); if (qn) { if (type == LWS_QUIC_FT_MAX_STREAMS_BIDI) { - qn->max_streams_bidi_remote = max_streams; + if (max_streams > qn->max_streams_bidi_remote) { + qn->max_streams_bidi_remote = max_streams; #if defined(LWS_WITH_CLIENT) - lws_wsi_mux_apply_queue(nwsi); + lws_wsi_mux_apply_queue(nwsi); #endif + } } else if (type == LWS_QUIC_FT_MAX_STREAMS_UNIDI) { - qn->max_streams_unidi_remote = max_streams; + if (max_streams > qn->max_streams_unidi_remote) { + qn->max_streams_unidi_remote = max_streams; #if defined(LWS_WITH_CLIENT) - lws_wsi_mux_apply_queue(nwsi); + lws_wsi_mux_apply_queue(nwsi); #endif + } } } break; @@ -816,6 +859,10 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl f_pr->len = 8; f_pr->data = (uint8_t *)&f_pr[1]; memcpy(f_pr->data, path_data, 8); + if (sa46) { + f_pr->dest_sa46 = *sa46; + f_pr->has_dest = 1; + } lws_dll2_add_head(&f_pr->list, &nwsi->quic.qn->pending_tx[level]); lws_callback_on_writable(nwsi); } @@ -824,6 +871,47 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl lwsl_wsi_notice(nwsi, "QUIC RX: Path validated via PATH_RESPONSE!"); nwsi->quic.qn->address_validated = 1; nwsi->quic.qn->path_challenge_pending = 0; + + if (nwsi->quic.qn->migration_probing_wsi == nwsi) { + struct lws *old_nwsi = nwsi->quic.qn->nwsi; + /* Reparent the connection! */ + nwsi->quic.qn->nwsi = nwsi; + nwsi->quic.qn->migration_probing_wsi = NULL; + + if (old_nwsi && old_nwsi != nwsi) { + lwsl_wsi_notice(nwsi, "QUIC: Active Migration Make-Before-Break Complete! Closing old nwsi."); + old_nwsi->quic.qn = NULL; /* Detach so close doesn't free it */ + + /* Transition new nwsi to established state and cancel its timeout/grace timers */ + lwsi_set_state(nwsi, LRS_ESTABLISHED); + lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0); + lws_sul_cancel(&nwsi->sul_h3_grace); + lws_sul_cancel(&nwsi->sul_happy_eyeballs); + lws_sul_cancel(&nwsi->sul_connect_timeout); + + /* Reparent all child streams to the new nwsi */ + struct lws *w = old_nwsi->mux.child_list; + while (w) { + w->mux.parent_wsi = nwsi; + w = w->mux.sibling_list; + } + nwsi->mux.child_list = old_nwsi->mux.child_list; + old_nwsi->mux.child_list = NULL; + nwsi->mux.child_count = old_nwsi->mux.child_count; + old_nwsi->mux.child_count = 0; + nwsi->mux.highest_sid = old_nwsi->mux.highest_sid; + +#if defined(LWS_WITH_TLS) + extern int lws_tls_quic_migrate_wsi(struct lws *old_wsi, struct lws *new_wsi); + /* Move TLS object so we can decrypt NEW_SESSION_TICKET and KeyUpdate */ + nwsi->tls = old_nwsi->tls; + memset(&old_nwsi->tls, 0, sizeof(old_nwsi->tls)); + lws_tls_quic_migrate_wsi(old_nwsi, nwsi); +#endif + + lws_close_free_wsi(old_nwsi, LWS_CLOSE_STATUS_NOSTATUS, "migrated"); + } + } } else { lwsl_wsi_notice(nwsi, "QUIC RX: Spurious or mismatched PATH_RESPONSE, ignoring"); } @@ -1234,7 +1322,7 @@ lws_quic_parse_transport_parameters(struct lws *wsi, const uint8_t *buf, size_t /* Check for duplicates */ for (size_t i = 0; i < num_seen; i++) { if (seen_params[i] == param_id) { - lwsl_wsi_err(wsi, "QUIC TP error: Duplicate parameter ID %llu", (unsigned long long)param_id); + lwsl_wsi_err(wsi, "QUIC TP error: Duplicate parameter ID %llu", (unsigned long long)param_id); return -1; } } @@ -1242,6 +1330,28 @@ lws_quic_parse_transport_parameters(struct lws *wsi, const uint8_t *buf, size_t seen_params[num_seen++] = param_id; switch (param_id) { + case 0x11: { /* version_information */ + if (param_len < 4 || (param_len % 4) != 0) { + lwsl_wsi_err(wsi, "QUIC TP error: version_information bad length"); + return -1; + } + if (qn->is_server && (wsi->a.context->options & LWS_SERVER_OPTION_QUIC_LATEST_VERSION)) { + size_t offset = 4; + while (offset < param_len) { + uint32_t av = ((uint32_t)buf[pos + offset] << 24) | + ((uint32_t)buf[pos + offset + 1] << 16) | + ((uint32_t)buf[pos + offset + 2] << 8) | + ((uint32_t)buf[pos + offset + 3]); + if (av == LWS_QUIC_VERSION_2) { + qn->version = LWS_QUIC_VERSION_2; + lwsl_wsi_notice(wsi, "QUIC: Upgrading to QUIC v2 via Compatible Version Negotiation"); + break; + } + offset += 4; + } + } + break; + } case 0x0f: /* initial_source_connection_id */ seen_initial_source_cid = 1; break; @@ -1339,17 +1449,60 @@ lws_quic_parse_transport_parameters(struct lws *wsi, const uint8_t *buf, size_t } break; case 0x02: /* stateless_reset_token */ - case 0x0d: /* preferred_address */ if (qn->is_server) { /* Client cannot send these */ lwsl_wsi_err(wsi, "QUIC TP error: Client sent server-only parameter %llu", (unsigned long long)param_id); return -1; } - if (param_id == 0x02 && param_len != 16) { + if (param_len != 16) { lwsl_wsi_err(wsi, "QUIC TP error: stateless_reset_token length %llu != 16", (unsigned long long)param_len); return -1; } break; + case 0x0d: /* preferred_address */ + if (qn->is_server) { + /* Client cannot send these */ + lwsl_wsi_err(wsi, "QUIC TP error: Client sent server-only parameter %llu", (unsigned long long)param_id); + return -1; + } + if (param_len >= 4+2+16+2+1+16) { + /* Pick IPv4 if present, else IPv6 */ + char addr_str[64]; + int port = 0; + struct lws_client_connect_info i; + + /* Try IPv4 first (RFC says it's 4 bytes IP + 2 bytes port) */ + uint32_t ip4; + memcpy(&ip4, &buf[pos], 4); + if (ip4 != 0) { + lws_snprintf(addr_str, sizeof(addr_str), "%u.%u.%u.%u", + buf[pos], buf[pos+1], buf[pos+2], buf[pos+3]); + port = (buf[pos+4] << 8) | buf[pos+5]; + } else { + /* Try IPv6 */ + const uint8_t *v6 = &buf[pos+6]; + lws_snprintf(addr_str, sizeof(addr_str), "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", + v6[0], v6[1], v6[2], v6[3], v6[4], v6[5], v6[6], v6[7], + v6[8], v6[9], v6[10], v6[11], v6[12], v6[13], v6[14], v6[15]); + port = (buf[pos+22] << 8) | buf[pos+23]; + } + + if (port > 0) { + lwsl_wsi_notice(wsi, "QUIC TP: Migrating to preferred_address %s:%d", addr_str, port); + memset(&i, 0, sizeof(i)); + i.context = wsi->a.context; + i.vhost = wsi->a.vhost; + i.address = addr_str; + i.host = addr_str; + i.origin = addr_str; + i.port = port; + i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_INSECURE; + i.quic_migrate_from_wsi = qn->nwsi; + + lws_client_connect_via_info(&i); + } + } + break; default: break; } diff --git a/lib/roles/quic/private-lib-roles-quic.h b/lib/roles/quic/private-lib-roles-quic.h index f52c847cc3..02e6437983 100644 --- a/lib/roles/quic/private-lib-roles-quic.h +++ b/lib/roles/quic/private-lib-roles-quic.h @@ -29,8 +29,8 @@ extern const struct lws_role_ops role_ops_quic; #define LWS_QUIC_MAX_CID_LEN 20 -#define LWS_QUIC_VERSION_1 0x00000001 -#define LWS_QUIC_VERSION_2 0x709a50c4 +#define LWS_QUIC_VERSION_1 0x1 +#define LWS_QUIC_VERSION_2 0x6b3343cf #define LWS_QUIC_DEFAULT_WINDOW (1024 * 1024) #define LWS_QUIC_MAX_WINDOW (16 * 1024 * 1024) @@ -169,6 +169,8 @@ struct lws_quic_tx_frame { lws_usec_t sent_time_us; size_t wire_len; uint16_t packet_size; + lws_sockaddr46 dest_sa46; + uint8_t has_dest; }; struct lws_quic_rx_chunk { @@ -247,6 +249,8 @@ struct lws_quic_netconn { uint64_t next_stream_id_bidi_local; uint64_t next_stream_id_unidi_local; + uint64_t next_stream_id_bidi_remote; + uint64_t next_stream_id_unidi_remote; /* Frames waiting to be bundled into outgoing packets */ lws_dll2_owner_t pending_tx[LWS_QUIC_LEVEL_COUNT]; @@ -283,10 +287,13 @@ struct lws_quic_netconn { uint64_t bytes_sent; uint32_t version; + uint32_t original_version; - uint64_t conn_close_err; - size_t crypto_rx_expected_msg_len[4]; - uint8_t highest_rx_level; + uint64_t conn_close_err; + size_t crypto_rx_expected_msg_len[4]; + uint8_t *crypto_rx_buf[4]; + size_t crypto_rx_buf_len[4]; + uint8_t highest_rx_level; uint8_t pto_count; /* Key Update Tracking */ @@ -303,6 +310,7 @@ struct lws_quic_netconn { /* Path Validation (RFC 9000 Section 8.2) */ uint8_t path_challenge[8]; uint8_t path_challenge_pending:1; + struct lws *migration_probing_wsi; /* ECN (Explicit Congestion Notification) */ uint64_t ecn_rx_ect0; @@ -313,7 +321,7 @@ struct lws_quic_netconn { uint8_t handshake_done:1; uint8_t tp_parsed:1; uint8_t alpn_migrated:1; - uint8_t pto_probe_needed:1; + uint8_t pto_probe_needed:2; uint8_t address_validated:1; uint8_t is_closing:1; @@ -329,6 +337,16 @@ struct lws_quic_netconn { struct lws_quic_cid retry_scid; }; +struct lws_quic_cc_newreno { + size_t cwnd; + size_t ssthresh; + size_t bytes_in_flight; + lws_usec_t congestion_recovery_start_time; + + lws_usec_t last_pacing_time; + size_t pacing_credit; +}; + extern const struct lws_cc_ops lws_cc_ops_newreno; int @@ -340,6 +358,9 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin void lws_quic_keys_destroy(struct lws_quic_keys *keys); +void +lws_quic_queue_path_challenge(struct lws *nwsi); + int lws_quic_update_keys(struct lws_quic_keys *k, int is_rx); @@ -364,10 +385,14 @@ size_t lws_quic_write_varint(uint8_t *buf, size_t len, uint64_t val); int -lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payload_len); +lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payload_len, const lws_sockaddr46 *sa46); void lws_quic_handle_ack(struct lws *nwsi, int level, uint64_t acked_pn); +void + +lws_quic_detect_loss(struct lws *nwsi, int level, uint64_t largest_acked); + void lws_quic_discard_keys(struct lws *nwsi, int level); @@ -397,6 +422,8 @@ struct _lws_quic_related { struct lws_quic_netconn *qn; /* malloc'd for root net conn */ struct lws_quic_stream *qs; /* malloc'd for stream child wsi */ + struct lws *migrate_from_wsi; /* if set, this nwsi is migrating from an existing nwsi */ + lws_usec_t quic_race_start_us; uint8_t initialized:1; diff --git a/lib/tls/gnutls/gnutls-quic.c b/lib/tls/gnutls/gnutls-quic.c index eb5f3ceaf9..a96bdd0af6 100644 --- a/lib/tls/gnutls/gnutls-quic.c +++ b/lib/tls/gnutls/gnutls-quic.c @@ -276,10 +276,31 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) return -1; } + const char *user_ciphers = NULL; + char priority[256]; + +#if defined(LWS_WITH_CLIENT) + if (wsi->quic.qn && !wsi->quic.qn->is_server) { + user_ciphers = wsi->a.vhost ? wsi->a.vhost->tls.cfg_tls_client_cipher_list : NULL; + } else +#endif + { + user_ciphers = wsi->a.vhost ? wsi->a.vhost->tls.cfg_ssl_cipher_list : NULL; + } + + if (user_ciphers) { + lws_snprintf(priority, sizeof(priority), "%s:%%DISABLE_TLS13_COMPAT_MODE", user_ciphers); + } else { + lws_snprintf(priority, sizeof(priority), "NORMAL:-VERS-ALL:+VERS-TLS1.3:%%DISABLE_TLS13_COMPAT_MODE"); + } + + lwsl_notice("AGY-DEBUG: gnutls_quic_init priority string used = %s\n", priority); + /* Enforce QUIC requirements: TLS 1.3 only, NO compatibility mode (empty legacy_session_id) */ - ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:%DISABLE_TLS13_COMPAT_MODE", NULL); + ret = gnutls_priority_set_direct(session, priority, NULL); if (ret < 0) { - lwsl_err("gnutls_priority_set_direct failed: %s\n", gnutls_strerror(ret)); + lwsl_notice("gnutls_priority_set_direct failed: %s\n", gnutls_strerror(ret)); + lwsl_notice("AGY-DEBUG: gnutls_priority_set_direct failed: %s\n", gnutls_strerror(ret)); } if (!wsi->tls.quic_tp_send) { @@ -441,14 +462,32 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, *out_len = 0; return -1; } - if (gnutls_handshake_write(session, glevel, in, in_len) < 0) - return -1; + int hw = gnutls_handshake_write(session, glevel, in, in_len); + if (hw < 0) { + lwsl_err("gnutls_handshake_write failed: %d\n", hw); + return hw; + } } +#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_TLS_SESSIONS) + /* + * Try to cache any newly available session data (like NewSessionTicket) + * BEFORE calling gnutls_handshake, because gnutls_handshake sets + * handshake_in_progress=1 and causes gnutls_session_get_data2 to fail with -408. + */ + if (lwsi_role_client(wsi) && wsi->quic.qn && wsi->quic.qn->handshake_done) + lws_tls_session_new_gnutls(wsi); +#endif + n = gnutls_handshake(session); + lwsl_notice("gnutls_handshake returned %d\n", n); + + struct lws *active_wsi = (struct lws *)gnutls_session_get_ptr(session); + if (!active_wsi) + active_wsi = wsi; #if defined(LWS_WITH_SERVER) && defined(LWS_WITH_TLS_SESSIONS) - if (n == GNUTLS_E_SUCCESS && !lwsi_role_client(wsi) && wsi->quic.qn && !wsi->quic.qn->handshake_done) { + if (n == GNUTLS_E_SUCCESS && !lwsi_role_client(active_wsi) && active_wsi->quic.qn && !active_wsi->quic.qn->handshake_done) { #if GNUTLS_VERSION_NUMBER >= 0x030603 int r = gnutls_session_ticket_send(session, 1, 0); (void)r; @@ -462,8 +501,8 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, for (l = 0; l < 4; l++) { if (b->other_out_len[l]) { - if (wsi->quic.qn) { - lws_tls_quic_tx_crypto_cb(wsi, l, b->other_out[l], b->other_out_len[l]); + if (active_wsi->quic.qn) { + lws_tls_quic_tx_crypto_cb(active_wsi, l, b->other_out[l], b->other_out_len[l]); } lws_free_set_NULL(b->other_out[l]); b->other_out_len[l] = 0; @@ -472,14 +511,15 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, if (n == GNUTLS_E_SUCCESS) { #if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_TLS_SESSIONS) - if (lwsi_role_client(wsi)) - lws_tls_session_new_gnutls(wsi); + if (lwsi_role_client(active_wsi)) + lws_tls_session_new_gnutls(active_wsi); #endif return 0; } - if (n == GNUTLS_E_AGAIN || n == GNUTLS_E_INTERRUPTED) + if (n == GNUTLS_E_AGAIN || n == GNUTLS_E_INTERRUPTED) { return 1; + } lwsl_err("gnutls_handshake failed: %d\n", n); return n < 0 ? n : -1; diff --git a/lib/tls/gnutls/gnutls-session.c b/lib/tls/gnutls/gnutls-session.c index 981c54a4b5..2959d6988e 100644 --- a/lib/tls/gnutls/gnutls-session.c +++ b/lib/tls/gnutls/gnutls-session.c @@ -97,12 +97,14 @@ lws_tls_reuse_session(struct lws *wsi) ts = __lws_tls_session_lookup_by_name(wsi->a.vhost, buf); if (!ts) { - lwsl_tlssess("%s: no existing session for %s\n", __func__, buf); + lwsl_notice("%s: no existing session for %s\n", __func__, buf); goto bail; } - if (!ts->ser_data || !ts->ser_data->data) /* cache entry is invalid */ + if (!ts->ser_data || !ts->ser_data->data) { /* cache entry is invalid */ + lwsl_notice("%s: cache entry invalid for %s\n", __func__, buf); goto bail; + } if (gnutls_session_set_data((gnutls_session_t)wsi->tls.ssl, ts->ser_data->data, @@ -111,7 +113,7 @@ lws_tls_reuse_session(struct lws *wsi) goto bail; } - lwsl_tlssess("%s: resumed session for %s\n", __func__, (const char *)&ts[1]); + lwsl_notice("%s: resumed session for %s\n", __func__, (const char *)&ts[1]); wsi->tls_session_reused = 1; /* keep our session list sorted in lru -> mru order */ @@ -192,12 +194,29 @@ lws_tls_session_new_gnutls(struct lws *wsi) nl = strlen(buf); + /* Check if a session ticket has actually been received (TLS 1.3 requirement) */ + unsigned sess_flags = gnutls_session_get_flags((gnutls_session_t)wsi->tls.ssl); + lwsl_notice("%s: QUIC session ticket check: flags=0x%x, has_ticket=%d\n", __func__, + sess_flags, !!(sess_flags & GNUTLS_SFLAGS_SESSION_TICKET)); + int ret = gnutls_session_get_data2((gnutls_session_t)wsi->tls.ssl, &gd); + lwsl_notice("%s: gnutls_session_get_data2 ret=%d, len=%u\n", __func__, ret, gd.size); if (ret != GNUTLS_E_SUCCESS) { if (ret == GNUTLS_E_INTERNAL_ERROR) - lwsl_debug("%s: gnutls_session_get_data2 failed: %d (%s)\n", __func__, ret, gnutls_strerror(ret)); + lwsl_notice("%s: gnutls_session_get_data2 INTERNAL_ERROR (no ticket yet?): %s\n", __func__, gnutls_strerror(ret)); else - lwsl_tlssess("%s: gnutls_session_get_data2 failed: %d (%s)\n", __func__, ret, gnutls_strerror(ret)); + lwsl_notice("%s: gnutls_session_get_data2 failed: %d (%s)\n", __func__, ret, gnutls_strerror(ret)); + return 0; + } + + /* A real TLS 1.3 session ticket is always well over 32 bytes. + * GnuTLS may return a 4-byte placeholder before NewSessionTicket + * arrives. Reject any suspiciously small blob to avoid persisting + * a useless stub that will fail to resume on next connection. */ + if (gd.size < 32) { + lwsl_notice("%s: session data too small (%u bytes), ignoring stub ticket\n", + __func__, gd.size); + gnutls_free(gd.data); return 0; } @@ -316,3 +335,153 @@ lws_tls_session_cache(struct lws_vhost *vh, uint32_t ttl) /* Default to 1hr max recommendation from RFC5246 F.1.4 */ vh->tls.tls_session_cache_ttl = !ttl ? 3600 : ttl; } + +static lws_tls_scm_t * +lws_tls_session_add_entry(struct lws_vhost *vh, const char *tag) +{ + lws_tls_scm_t *ts; + size_t nl = strlen(tag); + + if (vh->tls_sessions.count == (vh->tls_session_cache_max ? + vh->tls_session_cache_max : 10)) { + /* + * We have reached the vhost's session cache limit, + * prune the LRU / head + */ + ts = lws_container_of(vh->tls_sessions.head, + lws_tls_scm_t, list); + + if (ts) { + lwsl_tlssess("%s: pruning oldest session\n", __func__); + /* Note: we are already holding vh lock when calling this */ + __lws_tls_session_destroy(ts); + } + } + + ts = lws_malloc(sizeof(*ts) + nl + 1, __func__); + if (!ts) + return NULL; + + memset(ts, 0, sizeof(*ts)); + memcpy(&ts[1], tag, nl + 1); + + lws_dll2_add_tail(&ts->list, &vh->tls_sessions); + + return ts; +} + +int +lws_tls_session_dump_save(struct lws_vhost *vh, const char *host, uint16_t port, + lws_tls_sess_cb_t cb_save, void *opq) +{ + struct lws_tls_session_dump d; + lws_tls_scm_t *ts; + int ret = 1; + void *v; + + if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE) + return 1; + + lws_tls_session_tag_discrete(vh->name, host, port, d.tag, sizeof(d.tag)); + + lws_context_lock(vh->context, __func__); /* -------------- cx { */ + lws_vhost_lock(vh); /* -------------- vh { */ + + ts = __lws_tls_session_lookup_by_name(vh, d.tag); + if (!ts || !ts->ser_data || !ts->ser_data->data) + goto bail; + + d.blob_len = ts->ser_data->len; + v = d.blob = lws_malloc(d.blob_len, __func__); + if (!d.blob) + goto bail; + + memcpy(d.blob, ts->ser_data->data, d.blob_len); + d.opaque = opq; + + if (cb_save(vh->context, &d)) + lwsl_notice("%s: save failed\n", __func__); + else + ret = 0; + + lws_free(v); + +bail: + lws_vhost_unlock(vh); /* } vh -------------- */ + lws_context_unlock(vh->context); /* } cx -------------- */ + + return ret; +} + +int +lws_tls_session_dump_load(struct lws_vhost *vh, const char *host, uint16_t port, + lws_tls_sess_cb_t cb_load, void *opq) +{ + struct lws_tls_session_dump d; + lws_tls_scm_t *ts; + void *v; + + if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE) + return 1; + + d.opaque = opq; + lws_tls_session_tag_discrete(vh->name, host, port, d.tag, sizeof(d.tag)); + lwsl_notice("%s: looking for tag %s\n", __func__, d.tag); + + lws_context_lock(vh->context, __func__); /* -------------- cx { */ + lws_vhost_lock(vh); /* -------------- vh { */ + + ts = __lws_tls_session_lookup_by_name(vh, d.tag); + if (ts) { + /* session already in cache — no need to load from cold storage */ + lwsl_notice("%s: session already exists for %s\n", __func__, d.tag); + goto bail1; + } + + if (cb_load(vh->context, &d)) { + lwsl_warn("%s: load failed\n", __func__); + goto bail1; + } + + /* cb_load allocated the blob; insert it into the session cache */ + v = d.blob; + + ts = lws_tls_session_add_entry(vh, d.tag); + if (!ts) { + lwsl_warn("%s: unable to add cache entry\n", __func__); + free(v); + goto bail1; + } + + if (!ts->ser_data) { + ts->ser_data = lws_malloc(sizeof(*ts->ser_data), __func__); + if (!ts->ser_data) { + free(v); + goto bail1; + } + memset(ts->ser_data, 0, sizeof(*ts->ser_data)); + } + + ts->ser_data->data = lws_malloc(d.blob_len, __func__); + if (!ts->ser_data->data) { + free(v); + goto bail1; + } + + memcpy(ts->ser_data->data, v, d.blob_len); + ts->ser_data->len = d.blob_len; + free(v); + + lwsl_tlssess("%s: session loaded OK\n", __func__); + + lws_vhost_unlock(vh); /* } vh -------------- */ + lws_context_unlock(vh->context); /* } cx -------------- */ + + return 0; + +bail1: + lws_vhost_unlock(vh); /* } vh -------------- */ + lws_context_unlock(vh->context); /* } cx -------------- */ + + return 1; +} diff --git a/lib/tls/gnutls/gnutls-tls.c b/lib/tls/gnutls/gnutls-tls.c index 83558b7ff7..f8c01fca4a 100644 --- a/lib/tls/gnutls/gnutls-tls.c +++ b/lib/tls/gnutls/gnutls-tls.c @@ -128,6 +128,43 @@ lws_tls_vhost_backend_create_ctx(struct lws_vhost *vhost) } #if defined(LWS_WITH_SERVER) + +#if GNUTLS_VERSION_NUMBER >= 0x030605 +struct lws_gnutls_ar_entry { + lws_dll2_t list; + size_t size; + uint8_t key[128]; +}; + +static int +lws_gnutls_anti_replay_db_add(void *db_ptr, long int exp_time, + const gnutls_datum_t *key, + const gnutls_datum_t *data) +{ + lws_dll2_owner_t *owner = (lws_dll2_owner_t *)db_ptr; + struct lws_gnutls_ar_entry *e; + + lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(owner)) { + e = lws_container_of(d, struct lws_gnutls_ar_entry, list); + if (e->size == key->size && !memcmp(e->key, key->data, key->size)) + return GNUTLS_E_DB_ENTRY_EXISTS; + } lws_end_foreach_dll(d); + + if (owner->count >= 256) { + e = lws_container_of(lws_dll2_get_head(owner), struct lws_gnutls_ar_entry, list); + lws_dll2_remove(&e->list); + lws_free(e); + } + + e = lws_zalloc(sizeof(*e), "anti_replay"); + if (!e) return GNUTLS_E_MEMORY_ERROR; + e->size = key->size < sizeof(e->key) ? key->size : sizeof(e->key); + memcpy(e->key, key->data, e->size); + lws_dll2_add_tail(&e->list, owner); + return 0; +} +#endif + int lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, struct lws_vhost *vhost, struct lws *wsi) @@ -162,6 +199,21 @@ lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, return 1; } +#if GNUTLS_VERSION_NUMBER >= 0x030605 + if (gnutls_anti_replay_init((gnutls_anti_replay_t *)&vhost->tls.anti_replay) < 0) { + lwsl_err("%s: failed to init anti-replay\n", __func__); + return 1; + } + + lws_dll2_owner_t *ar_owner = lws_zalloc(sizeof(*ar_owner), "anti_replay_owner"); + if (ar_owner) { + vhost->tls.anti_replay_owner = ar_owner; + gnutls_anti_replay_set_ptr((gnutls_anti_replay_t)vhost->tls.anti_replay, ar_owner); + gnutls_anti_replay_set_add_function((gnutls_anti_replay_t)vhost->tls.anti_replay, + (gnutls_db_add_func)lws_gnutls_anti_replay_db_add); + } +#endif + return 0; } #endif @@ -275,14 +327,48 @@ lws_gnutls_server_name_cb(gnutls_session_t session) return 0; } +// static void my_gnutls_log(int level, const char *msg) { +// fprintf(stderr, "GNUTLS_LOG[%d]: %s", level, msg); +// fflush(stderr); +// } + int lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) { gnutls_session_t session; - if (gnutls_init(&session, GNUTLS_SERVER) < 0) + unsigned int flags = GNUTLS_SERVER; +#if GNUTLS_VERSION_NUMBER >= 0x030605 + if (wsi->a.vhost && (wsi->a.vhost->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA)) { + flags |= GNUTLS_ENABLE_EARLY_DATA; +#if defined(LWS_ROLE_QUIC) && GNUTLS_VERSION_NUMBER >= 0x030702 + extern const struct lws_role_ops role_ops_quic; + if (wsi->role_ops == &role_ops_quic) { + flags |= GNUTLS_NO_END_OF_EARLY_DATA; + lwsl_notice("gnutls_init: enabling server 0-RTT with GNUTLS_NO_END_OF_EARLY_DATA\n"); + } else +#endif + lwsl_notice("gnutls_init: enabling server 0-RTT/early data\n"); + } +#endif + if (gnutls_init(&session, flags) < 0) + return 1; + // gnutls_global_set_log_level(99); + // gnutls_global_set_log_function(my_gnutls_log); + if (0) return 1; +#if GNUTLS_VERSION_NUMBER >= 0x030605 + if (flags & GNUTLS_ENABLE_EARLY_DATA) { + gnutls_record_set_max_early_data_size(session, 0xFFFFFFFF); + if (wsi->a.vhost->tls.anti_replay) + gnutls_anti_replay_enable(session, (gnutls_anti_replay_t)wsi->a.vhost->tls.anti_replay); + } +#endif + + // gnutls_global_set_log_level(99); + // gnutls_global_set_log_function(my_gnutls_log); + wsi->tls.ssl = (lws_tls_conn *)session; wsi->tls.ctx_ref = lws_tls_ctx_ref_get(wsi->a.vhost); @@ -349,7 +435,25 @@ lws_ssl_client_bio_create(struct lws *wsi) p++; } - if (gnutls_init(&session, GNUTLS_CLIENT) < 0) + unsigned int flags = GNUTLS_CLIENT; +#if GNUTLS_VERSION_NUMBER >= 0x030605 + if (wsi->flags & LCCSCF_ALLOW_EARLY_DATA) { + flags |= GNUTLS_ENABLE_EARLY_DATA; +#if defined(LWS_ROLE_QUIC) && GNUTLS_VERSION_NUMBER >= 0x030702 + extern const struct lws_role_ops role_ops_quic; + if (wsi->role_ops == &role_ops_quic) { + flags |= GNUTLS_NO_END_OF_EARLY_DATA; + lwsl_notice("gnutls_init: enabling client 0-RTT with GNUTLS_NO_END_OF_EARLY_DATA\n"); + } else +#endif + lwsl_notice("gnutls_init: enabling client 0-RTT/early data\n"); + } +#endif + if (gnutls_init(&session, flags) < 0) + return 1; + // gnutls_global_set_log_level(99); + // gnutls_global_set_log_function(my_gnutls_log); + if (0) return 1; wsi->tls.ssl = (lws_tls_conn *)session; @@ -389,6 +493,21 @@ lws_ssl_client_bio_create(struct lws *wsi) void lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) { +#if GNUTLS_VERSION_NUMBER >= 0x030605 + if (vhost->tls.anti_replay) { + lws_dll2_owner_t *owner = (lws_dll2_owner_t *)vhost->tls.anti_replay_owner; + if (owner) { + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, lws_dll2_get_head(owner)) { + lws_dll2_remove(d); + lws_free(lws_container_of(d, struct lws_gnutls_ar_entry, list)); + } lws_end_foreach_dll_safe(d, d1); + lws_free(owner); + } + gnutls_anti_replay_deinit((gnutls_anti_replay_t)vhost->tls.anti_replay); + vhost->tls.anti_replay = NULL; + vhost->tls.anti_replay_owner = NULL; + } +#endif if (vhost->tls.ssl_ctx) { lws_tls_vhost_backend_free_ctx(vhost->tls.ssl_ctx); vhost->tls.ssl_ctx = NULL; @@ -623,18 +742,23 @@ lws_tls_session_is_reused(struct lws *wsi) return (int)gnutls_session_is_resumed((gnutls_session_t)wsi->tls.ssl); } +#if !defined(LWS_WITH_TLS_SESSIONS) int lws_tls_session_dump_save(struct lws_vhost *vh, const char *host, uint16_t port, - int (*cb)(struct lws_context *, struct lws_tls_session_dump *), void *user) + lws_tls_sess_cb_t cb_save, void *opq) { return -1; } int lws_tls_session_dump_load(struct lws_vhost *vh, const char *host, uint16_t port, - int (*cb)(struct lws_context *, struct lws_tls_session_dump *), void *user) + lws_tls_sess_cb_t cb_load, void *opq) { return -1; } +#endif + + + #endif diff --git a/lib/tls/private-network.h b/lib/tls/private-network.h index b780ca1aa1..fd0afd0ae3 100644 --- a/lib/tls/private-network.h +++ b/lib/tls/private-network.h @@ -102,6 +102,11 @@ struct lws_vhost_tls { uint32_t tls_session_cache_ttl; #endif +#if defined(LWS_WITH_GNUTLS) + struct gnutls_anti_replay_st *anti_replay; + void *anti_replay_owner; +#endif + unsigned int user_supplied_ssl_ctx:1; unsigned int skipped_certs:1; }; diff --git a/lib/tls/tls-sessions.c b/lib/tls/tls-sessions.c index dc1eb8490c..574b8a06ad 100644 --- a/lib/tls/tls-sessions.c +++ b/lib/tls/tls-sessions.c @@ -62,6 +62,8 @@ lws_tls_session_tag_from_wsi(struct lws *wsi, char *buf, size_t len) lws_tls_session_tag_discrete(wsi->a.vhost->name, host, wsi->c_port, buf, len); + lwsl_notice("lws_tls_session_tag_from_wsi: generated tag '%s' for host '%s'\n", buf, host); + return 0; } diff --git a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c index 35eab256ff..184819d64a 100644 --- a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c +++ b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c @@ -98,7 +98,7 @@ static const struct lws_switches switches[] = { #include #endif -#define COUNT 1024 +#define COUNT 4096 struct cliuser { int index; @@ -126,11 +126,11 @@ static char save_ticket[256]; static char load_ticket[256]; struct req { - char address[128]; + char path[512]; + char address[256]; int port; - char path[128]; }; -static struct req reqs[1024]; +static struct req reqs[COUNT]; struct pss { char body_part; @@ -232,6 +232,10 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED_EARLY: + lwsl_user("LWS_CALLBACK_CLIENT_ESTABLISHED_EARLY: idx: %d, opting into 0-RTT\n", idx); + return 1; + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: idx: %d, resp %u\n", idx, lws_http_client_http_response(wsi)); @@ -257,15 +261,12 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, #if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) if (lws_tls_session_is_reused(wsi)) reuse++; - else - /* - * Attempt to store any new session into - * external storage - */ - if (lws_tls_session_dump_save(lws_get_vhost_by_name(context, "default"), - i.host, (uint16_t)i.port, - sess_save_cb, save_ticket)) - lwsl_warn("%s: session save failed\n", __func__); + /* + * Don't attempt to save the session here: the NewSessionTicket + * arrives asynchronously AFTER this callback fires, so the + * vhost session cache is empty or has only a stub at this + * point. The deferred save happens after the event loop exits. + */ #endif break; @@ -439,11 +440,10 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, lwsl_err("Done: failed: %d\n", failed); intr = 1; /* - * This is how we can exit the event loop even when it's an - * event library backing it... it will start and stage the - * destroy to happen after we exited this service for each pt + * Exit the loop by cancelling service so we can save TLS + * sessions in main() before actually destroying the context. */ - lws_context_destroy(lws_get_context(wsi)); + lws_cancel_service(lws_get_context(wsi)); } else if (sequential) { lws_try_client_connection(&i, completed); } @@ -502,7 +502,8 @@ lws_try_client_connection(struct lws_client_connect_info *ii, int m) lwsl_user("%s: failed: conn idx %d\n", __func__, m); if (++completed == count) { lwsl_user("Done: failed: %d\n", failed); - lws_context_destroy(context); + intr = 1; + lws_cancel_service(context); } } } else { @@ -556,7 +557,8 @@ signal_cb(void *handle, int signum) lwsl_err("%s: signal %d\n", __func__, signum); break; } - lws_context_destroy(context); + intr = 1; + lws_cancel_service(context); } static void @@ -612,7 +614,7 @@ stagger_cb(lws_sorted_usec_list_t *sul) if (stagger_idx == count) return; - next = 150 * LWS_US_PER_MS; + if (count > 100) next = 1 * LWS_US_PER_MS; else next = 150 * LWS_US_PER_MS; if (stagger_idx == count - 1) next += 400 * LWS_US_PER_MS; @@ -641,12 +643,14 @@ int main(int argc, const char **argv) int pl = 0; #endif - lws_context_info_defaults(&info, NULL);memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ + lws_context_info_defaults(&info, NULL); + + lws_cmdline_option_handle_builtin(argc, argv, &info); - lws_cmdline_option_handle_builtin(argc, argv, &info); + info.timeout_secs = 120; info.signal_cb = signal_cb; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; if (lws_cmdline_option(argc, argv, switches[LWS_SW_UV].sw)) info.options |= LWS_SERVER_OPTION_LIBUV; @@ -717,7 +721,56 @@ int main(int argc, const char **argv) */ info.simultaneous_ssl_handshake_restriction = atoi(p); - context = lws_create_context(&info); + if ((p = lws_cmdline_option(argc, argv, "--tls13-ciphers"))) { + info.client_tls_1_3_plus_cipher_list = p; + info.client_ssl_cipher_list = p; + } + + lwsl_notice("AGY-DEBUG: minimal: before TESTCASE check, client_ssl_cipher_list is '%s'\n", info.client_ssl_cipher_list ? info.client_ssl_cipher_list : "NULL"); + + const char *testcase = getenv("TESTCASE"); + if (testcase && !strcmp(testcase, "chacha20")) { +#if defined(LWS_WITH_GNUTLS) + info.client_tls_1_3_plus_cipher_list = "NORMAL:-CIPHER-ALL:-AES-256-GCM:-AES-128-GCM:-AES-128-CCM:-AES-128-CCM-8:+CHACHA20-POLY1305"; + info.client_ssl_cipher_list = "NORMAL:-CIPHER-ALL:-AES-256-GCM:-AES-128-GCM:-AES-128-CCM:-AES-128-CCM-8:+CHACHA20-POLY1305"; +#else + info.client_tls_1_3_plus_cipher_list = "TLS_CHACHA20_POLY1305_SHA256"; + info.client_ssl_cipher_list = "TLS_CHACHA20_POLY1305_SHA256"; +#endif + } + +#if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) + /* + * When the QIR harness sets TESTCASE=zerortt it does not pass + * --save-ticket / --load-ticket / --0rtt on the command line. + * Auto-configure the ticket persistence path so that phase 1 + * saves the ticket and phase 2 can load it and attempt 0-RTT, + * provided the caller hasn't already set an explicit path. + */ + if (testcase && !strcmp(testcase, "zerortt")) { + if (!save_ticket[0]) + lws_strncpy(save_ticket, "/tmp/lws_session_ticket", + sizeof(save_ticket)); + if (!load_ticket[0]) + lws_strncpy(load_ticket, "/tmp/lws_session_ticket", + sizeof(load_ticket)); + lwsl_notice("%s: TESTCASE=zerortt: ticket path '%s'\n", + __func__, save_ticket); + } +#endif + + if (lws_cmdline_option(argc, argv, "--quicv2")) + info.options |= LWS_SERVER_OPTION_QUIC_LATEST_VERSION; + + if (lws_cmdline_option(argc, argv, "--quic-early-key-update")) + info.options |= LWS_SERVER_OPTION_QUIC_EARLY_KEY_UPDATE; + + /* lws_context_info_defaults sets LWS_SERVER_OPTION_EXPLICIT_VHOSTS by default now, + * which skips creating the default vhost using our info struct. Clear it so + * our TLS cipher overrides are actually used to create the default vhost! */ + info.options &= ~(uint64_t)LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + context = lws_create_context(&info); if (!context) { lwsl_err("lws init failed\n"); return 1; @@ -768,6 +821,14 @@ int main(int argc, const char **argv) if (lws_cmdline_option(argc, argv, "--h3")) i.alpn = "h3"; + if (lws_cmdline_option(argc, argv, "--quicv2")) + info.options |= LWS_SERVER_OPTION_QUIC_LATEST_VERSION; + + if (lws_cmdline_option(argc, argv, "--quic-early-key-update")) + info.options |= LWS_SERVER_OPTION_QUIC_EARLY_KEY_UPDATE; + + + if (lws_cmdline_option(argc, argv, "--h1")) i.alpn = "http/1.1"; @@ -806,7 +867,7 @@ int main(int argc, const char **argv) lws_strncpy(reqs[count].address, url, sizeof(reqs[count].address)); } count++; - if (count == 1024) break; + if (count == COUNT) break; } } int default_urls = 0; @@ -846,6 +907,10 @@ int main(int argc, const char **argv) if (lws_cmdline_option(argc, argv, switches[LWS_SW_NO_TLS].sw)) i.ssl_connection &= ~(LCCSCF_USE_SSL); + if (lws_cmdline_option(argc, argv, "--0rtt") || + (testcase && !strcmp(testcase, "zerortt"))) + i.ssl_connection |= LCCSCF_ALLOW_EARLY_DATA; + int c_opt = count; if ((p = lws_cmdline_option(argc, argv, switches[LWS_SW_C].sw))) { c_opt = atoi(p); @@ -878,9 +943,11 @@ int main(int argc, const char **argv) /* * Attempt to preload a session from external storage */ - if (lws_tls_session_dump_load(lws_get_vhost_by_name(context, "default"), - i.host, (uint16_t)i.port, sess_load_cb, load_ticket)) - lwsl_warn("%s: session load failed\n", __func__); + if (load_ticket[0]) { + if (lws_tls_session_dump_load(lws_get_vhost_by_name(context, "default"), + i.host, (uint16_t)reqs[0].port, sess_load_cb, load_ticket)) + lwsl_warn("%s: session load failed\n", __func__); + } #endif start = us(); @@ -898,6 +965,32 @@ int main(int argc, const char **argv) #endif lwsl_user("Duration: %lldms\n", (us() - start) / 1000); + +#if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) + /* + * Save TLS session AFTER the event loop, not at ESTABLISHED time. + * The TLS 1.3 NewSessionTicket arrives from the server asynchronously + * after the HTTP response begins. By the time we reach here the + * session is fully cached in-memory and ready for disk persistence. + * This is especially important for zerortt: phase 2 is a separate + * process and needs the ticket on disk to attempt early data. + */ + lwsl_user("save_ticket = %s\n", save_ticket); + if (save_ticket[0]) { + struct lws_vhost *save_vh = lws_get_vhost_by_name(context, "default"); + if (save_vh) { + if (lws_tls_session_dump_save(save_vh, i.host, + (uint16_t)reqs[0].port, + sess_save_cb, save_ticket)) + lwsl_warn("%s: deferred session save failed\n", + __func__); + else + lwsl_notice("%s: session saved to %s\n", + __func__, save_ticket); + } + } +#endif + lws_context_destroy(context); lwsl_user("Exiting with %d\n", failed || completed != count); From bc21958247a0daf02063850cf4dcdfc6629c1f32 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 2 Jul 2026 08:53:47 +0100 Subject: [PATCH 03/44] cmake: work with abs paths --- CMakeLists.txt | 16 +++++++++++++++- cmake/lws_config.h.in | 2 +- lib/CMakeLists.txt | 8 ++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7065691645..ed9df1b266 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -680,6 +680,20 @@ set(LWS_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE PATH "Installati set(LWS_INSTALL_EXAMPLES_DIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Installation directory for example files") set(LWS_INSTALL_PLUGIN_DIR "${CMAKE_INSTALL_PREFIX}/share/libwebsockets-test-server/plugins" CACHE PATH "Installation directory for plugins") +if(IS_ABSOLUTE "${LWS_INSTALL_LIB_DIR}") + set(LWS_ABSOLUTE_INSTALL_LIBDIR "${LWS_INSTALL_LIB_DIR}") + set(PKG_CONFIG_LIBDIR "${LWS_INSTALL_LIB_DIR}") +else() + set(LWS_ABSOLUTE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${LWS_INSTALL_LIB_DIR}") + set(PKG_CONFIG_LIBDIR "\${exec_prefix}/${LWS_INSTALL_LIB_DIR}") +endif() + +if(IS_ABSOLUTE "${LWS_INSTALL_INCLUDE_DIR}") + set(PKG_CONFIG_INCLUDEDIR "${LWS_INSTALL_INCLUDE_DIR}") +else() + set(PKG_CONFIG_INCLUDEDIR "\${prefix}/${LWS_INSTALL_INCLUDE_DIR}") +endif() + # if you gave LWS_WITH_MINIZ, point to MINIZ here if not found # automatically @@ -1483,7 +1497,7 @@ file(RELATIVE_PATH "${LWS_ABSOLUTE_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the cmake dir. if (DEFINED REL_INCLUDE_DIR) - set(LWS__INCLUDE_DIRS "${LWS_CMAKE_DIR}/${REL_INCLUDE_DIR}") + set(LWS__INCLUDE_DIRS "\${LWS_CMAKE_DIR}/${REL_INCLUDE_DIR}") endif() set(STRIPPED_LIB_LIST_AT_END "") diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index d06c38c7cb..b87241d304 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -7,7 +7,7 @@ #endif #define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share" -#define LWS_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${LWS_INSTALL_LIB_DIR}" +#define LWS_INSTALL_LIBDIR "${LWS_ABSOLUTE_INSTALL_LIBDIR}" #define LWS_PLUGIN_DIR "${LWS_INSTALL_PLUGIN_DIR}" #define LWS_LIBRARY_VERSION_MAJOR ${LWS_LIBRARY_VERSION_MAJOR} #define LWS_LIBRARY_VERSION_MINOR ${LWS_LIBRARY_VERSION_MINOR} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fedb0ed95f..1e748882d0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -429,8 +429,8 @@ endif() file(WRITE "${PROJECT_BINARY_DIR}/libwebsockets.pc" "prefix=\"${CMAKE_INSTALL_PREFIX}\" exec_prefix=\${prefix} -libdir=\${exec_prefix}/${LWS_INSTALL_LIB_DIR} -includedir=\${prefix}/${LWS_INSTALL_INCLUDE_DIR} +libdir=${PKG_CONFIG_LIBDIR} +includedir=${PKG_CONFIG_INCLUDEDIR} Name: libwebsockets Description: Websockets server and client library @@ -451,8 +451,8 @@ endif() file(WRITE "${PROJECT_BINARY_DIR}/libwebsockets_static.pc" "prefix=\"${CMAKE_INSTALL_PREFIX}\" exec_prefix=\${prefix} -libdir=\${exec_prefix}/${LWS_INSTALL_LIB_DIR} -includedir=\${prefix}/${LWS_INSTALL_INCLUDE_DIR} +libdir=${PKG_CONFIG_LIBDIR} +includedir=${PKG_CONFIG_INCLUDEDIR} Name: libwebsockets_static Description: Websockets server and client static library From 6ed928670d939a4856387e6c56da53eef31198c6 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 9 Jul 2026 10:01:50 +0100 Subject: [PATCH 04/44] fixes fixes-1 --- include/libwebsockets/lws-context-vhost.h | 4 ++- include/libwebsockets/lws-lecp.h | 1 + lib/core-net/client/connect3.c | 4 ++- lib/cose/cose_sign.c | 9 +++---- lib/cose/cose_validate.c | 6 ++--- lib/misc/lecp.c | 1 + lib/roles/h2/http2.c | 7 +++++ lib/roles/h2/ops-h2.c | 5 ++++ lib/roles/quic/crypto-quic.c | 5 +++- lib/roles/quic/ops-quic.c | 13 ++++++++- lib/roles/quic/parse-quic.c | 27 +++++++++++++++++++ lib/tls/gnutls/gnutls-session.c | 2 ++ lib/tls/gnutls/gnutls-tls.c | 11 +++++--- .../minimal-http-client-multi.c | 4 +-- 14 files changed, 82 insertions(+), 17 deletions(-) diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index bed02c8ac7..a16d0ba921 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -1112,7 +1112,9 @@ struct lws_context_creation_info { /**< VHOST: Comma-separated list of IPv4 and/or IPv6 preferred addresses (e.g., "1.2.3.4:443,[2001:db8::1]:443") * to send to the client during the handshake to facilitate Connection Migration. */ - void *_unused[0]; /**< dummy */ +#if !defined(__STRICT_ANSI__) + void *_unused[1]; /**< dummy */ +#endif }; /** diff --git a/include/libwebsockets/lws-lecp.h b/include/libwebsockets/lws-lecp.h index 8133e4b148..76456bbf2b 100644 --- a/include/libwebsockets/lws-lecp.h +++ b/include/libwebsockets/lws-lecp.h @@ -290,6 +290,7 @@ struct lecp_ctx { uint8_t pst_sp; /* parsing stack head */ uint8_t outer_array; uint8_t cbor_pos; + uint8_t cbor_len; uint8_t literal_cbor_report; char present; /* temp for cb reason to use */ diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index ac0773b045..45882c181e 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -592,8 +592,10 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads, lws_dll2_remove(&curr->list); wsi->sa46_peer = curr->dest; #if defined(LWS_WITH_UDP) - if (wsi->udp) + if (wsi->udp) { wsi->udp->sa46 = curr->dest; + sa46_sockport(&wsi->udp->sa46, htons(port)); + } #endif #if defined(LWS_WITH_ROUTING) wsi->peer_route_uidx = curr->uidx; diff --git a/lib/cose/cose_sign.c b/lib/cose/cose_sign.c index c268c85f69..7169575ae5 100644 --- a/lib/cose/cose_sign.c +++ b/lib/cose/cose_sign.c @@ -456,11 +456,10 @@ lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, lws_lec_printf(&lec, "{1:%lld}", (long long)csc->alg->cose_alg); - lws_lec_init(&lec1, lb, sizeof(lb)); - lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, - lec.used); - if (lws_lec_printf(csc->info.lec, "{1:%lld}", - (long long)csc->alg->cose_alg) != LWS_LECPCTX_RET_FINISHED) + lws_lec_scratch(&lec); + + if (lws_lec_printf(csc->info.lec, "%.*b", + (int)lec.used, lec.start) != LWS_LECPCTX_RET_FINISHED) /* coverity */ return 0; break; diff --git a/lib/cose/cose_validate.c b/lib/cose/cose_validate.c index abb483381d..35a333e16d 100644 --- a/lib/cose/cose_validate.c +++ b/lib/cose/cose_validate.c @@ -947,13 +947,13 @@ cb_cose_sig(struct lecp_ctx *ctx, char reason) case ST_OUTER_UNPROTECTED: sl = &cps->st[cps->sp]; hi = ph_index(cps); - if (sl->ph_pos[hi] + 3 + ctx->cbor_pos > + if (sl->ph_pos[hi] + 3 + ctx->cbor_len > (int)sizeof(sl->ph[hi]) - 3) /* more protected cbor than we can handle */ goto bail; memcpy(sl->ph[hi] + 3 + sl->ph_pos[hi], ctx->cbor, - ctx->cbor_pos); - sl->ph_pos[hi] += ctx->cbor_pos; + ctx->cbor_len); + sl->ph_pos[hi] += ctx->cbor_len; break; } } diff --git a/lib/misc/lecp.c b/lib/misc/lecp.c index 13e6ba476b..edf084b13f 100644 --- a/lib/misc/lecp.c +++ b/lib/misc/lecp.c @@ -325,6 +325,7 @@ report_raw_cbor(struct lecp_ctx *ctx) if (!ctx->cbor_pos) return 0; + ctx->cbor_len = ctx->cbor_pos; ctx->cbor_pos = 0; /* reset BEFORE callback */ if (pst->cb(ctx, LECPCB_LITERAL_CBOR)) diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 39c33dc426..f205455284 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -2758,6 +2758,13 @@ lws_h2_client_handshake(struct lws *wsi) lws_h2_state(wsi, LWS_H2_STATE_OPEN); lwsi_set_state(wsi, LRS_ESTABLISHED); + if (wsi->mux.my_sid == 1) { + lws_start_foreach_ll(struct lws *, w1, nwsi->mux.child_list) { + if (w1 != wsi && lwsi_state(w1) == LRS_H2_WAITING_TO_SEND_HEADERS) + lws_callback_on_writable(w1); + } lws_end_foreach_ll(w1, mux.sibling_list); + } + if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) lws_callback_on_writable(wsi); diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 111e48091b..8b57506507 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -801,7 +801,12 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason) if (wsi->mux.parent_wsi->h2.h2n && wsi->mux.parent_wsi->h2.h2n->swsi == wsi) { + struct lws *nwsi = wsi->mux.parent_wsi; wsi->mux.parent_wsi->h2.h2n->swsi = NULL; + lws_start_foreach_ll(struct lws *, sibling, nwsi->mux.child_list) { + if (sibling != wsi && lwsi_state(sibling) == LRS_H2_WAITING_TO_SEND_HEADERS) + lws_callback_on_writable(sibling); + } lws_end_foreach_ll(sibling, mux.sibling_list); } lws_wsi_mux_sibling_disconnect(wsi); diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index 69f528fabe..457b40df72 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -283,6 +283,7 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin /* For simplicity, we just mark valid and rely on tx/rx logic */ k->valid = 1; +#if defined(LWS_WITH_CLIENT) /* If this is the client deriving the early secret, check if the stream opts in */ if (type == LWS_TLS_QUIC_SECRET_CLIENT_EARLY && !qn->is_server) { qn->early_data_status = LWS_0RTT_STATUS_ATTEMPTED; @@ -327,7 +328,9 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin w = w->mux.sibling_list; } } - } else if (type == LWS_TLS_QUIC_SECRET_CLIENT_EARLY && qn->is_server) { + } else +#endif + if (type == LWS_TLS_QUIC_SECRET_CLIENT_EARLY && qn->is_server) { qn->early_data_status = LWS_0RTT_STATUS_ACCEPTED; /* On server, migrate connection to H3 immediately to support 0-RTT stream adoption */ diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 9427ce85f9..96932e76f8 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -285,6 +285,7 @@ lws_quic_handle_ack(struct lws *nwsi, int level, uint64_t acked_pn) { struct lws_quic_cc_newreno *_cc = qn->cc_ops ? (struct lws_quic_cc_newreno *)qn->cc_state : NULL; + (void)_cc; lwsl_notice("QUIC TX: ACK processing: bytes_acked=%zu, cc_bif=%zu cc_cwnd=%zu cc_pacing=%zu, in_flight_app=%d, pending_tx=%d, blocked=%d\n", bytes_acked, _cc ? _cc->bytes_in_flight : 0, @@ -842,16 +843,22 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, if (sa.sa4.sin_family == AF_INET) { sa4 = sa; sa4.sa4.sin_port = htons((uint16_t)port); +#if defined(LWS_WITH_IPV6) } else { sa6 = sa; sa6.sa6.sin6_port = htons((uint16_t)port); +#endif } } p = comma ? comma + 1 : NULL; } /* We only send preferred_address if we have a valid address */ +#if defined(LWS_WITH_IPV6) if (sa4.sa4.sin_family == AF_INET || sa6.sa6.sin6_family == AF_INET6) { +#else + if (sa4.sa4.sin_family == AF_INET) { +#endif uint8_t pref_buf[128]; uint8_t *pb = pref_buf; @@ -864,12 +871,16 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, } /* IPv6 (16 bytes addr, 2 bytes port) */ +#if defined(LWS_WITH_IPV6) if (sa6.sa6.sin6_family == AF_INET6) { memcpy(pb, sa6.sa6.sin6_addr.s6_addr, 16); pb += 16; memcpy(pb, &sa6.sa6.sin6_port, 2); pb += 2; } else { memset(pb, 0, 18); pb += 18; } +#else + memset(pb, 0, 18); pb += 18; +#endif /* CID Length (1 byte) */ *pb++ = 8; /* Generating an 8-byte CID */ @@ -2866,7 +2877,7 @@ lws_quic_stream_cleanup(struct lws *wsi) struct lws_quic_tx_frame *f_max = lws_zalloc(sizeof(*f_max), "quic max strm"); if (f_max) { - f_max->stream_id = -1ULL; + f_max->stream_id = ~0ULL; f_max->type = want_type; f_max->limit = is_unidirectional ? qn->max_streams_unidi_local : diff --git a/lib/roles/quic/parse-quic.c b/lib/roles/quic/parse-quic.c index fb1e109069..dd3bc7f693 100644 --- a/lib/roles/quic/parse-quic.c +++ b/lib/roles/quic/parse-quic.c @@ -568,6 +568,11 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl pn -= (first_ack_range + 1); /* 5. Additional ACK Ranges */ + if (ack_range_count > (payload_len - pos) / 2) { + lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_FRAME_ENCODING_ERROR, type, 0); + return -1; + } + for (uint64_t r = 0; r < ack_range_count; r++) { uint64_t gap, ack_range; @@ -637,6 +642,16 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_STREAM_STATE_ERROR, type, 0); return -1; } + if (!wsi_child) { + /* It could be a stream we already closed and freed, or one we never created. + * Check if we created it by comparing with next_stream_id. */ + uint64_t next_id = is_unidirectional ? qn->next_stream_id_unidi_local : qn->next_stream_id_bidi_local; + if (stream_id >= next_id) { + lwsl_wsi_notice(nwsi, "QUIC RX: RESET_STREAM on uncreated stream ID %llu", (unsigned long long)stream_id); + lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_STREAM_STATE_ERROR, type, 0); + return -1; + } + } } pos += consumed; consumed = lws_quic_parse_varint(&payload[pos], payload_len - pos, &app_err_code); @@ -748,6 +763,14 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl if (!is_peer_initiated) { if (!wsi_child) { + /* It could be a stream we already closed and freed, or one we never created. + * Check if we created it by comparing with next_stream_id. */ + uint64_t next_id = is_unidirectional ? qn->next_stream_id_unidi_local : qn->next_stream_id_bidi_local; + if (stream_id >= next_id) { + lwsl_wsi_notice(nwsi, "QUIC RX: STOP_SENDING on uncreated stream ID %llu", (unsigned long long)stream_id); + lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_STREAM_STATE_ERROR, type, 0); + return -1; + } // Ignore STOP_SENDING for closed streams } } else { @@ -885,8 +908,10 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl /* Transition new nwsi to established state and cancel its timeout/grace timers */ lwsi_set_state(nwsi, LRS_ESTABLISHED); lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0); +#if defined(LWS_WITH_CLIENT) lws_sul_cancel(&nwsi->sul_h3_grace); lws_sul_cancel(&nwsi->sul_happy_eyeballs); +#endif lws_sul_cancel(&nwsi->sul_connect_timeout); /* Reparent all child streams to the new nwsi */ @@ -1459,6 +1484,7 @@ lws_quic_parse_transport_parameters(struct lws *wsi, const uint8_t *buf, size_t return -1; } break; +#if defined(LWS_WITH_CLIENT) case 0x0d: /* preferred_address */ if (qn->is_server) { /* Client cannot send these */ @@ -1503,6 +1529,7 @@ lws_quic_parse_transport_parameters(struct lws *wsi, const uint8_t *buf, size_t } } break; +#endif default: break; } diff --git a/lib/tls/gnutls/gnutls-session.c b/lib/tls/gnutls/gnutls-session.c index 2959d6988e..17c9fed91c 100644 --- a/lib/tls/gnutls/gnutls-session.c +++ b/lib/tls/gnutls/gnutls-session.c @@ -194,10 +194,12 @@ lws_tls_session_new_gnutls(struct lws *wsi) nl = strlen(buf); +#if (_LWS_ENABLED_LOGS & LLL_NOTICE) /* Check if a session ticket has actually been received (TLS 1.3 requirement) */ unsigned sess_flags = gnutls_session_get_flags((gnutls_session_t)wsi->tls.ssl); lwsl_notice("%s: QUIC session ticket check: flags=0x%x, has_ticket=%d\n", __func__, sess_flags, !!(sess_flags & GNUTLS_SFLAGS_SESSION_TICKET)); +#endif int ret = gnutls_session_get_data2((gnutls_session_t)wsi->tls.ssl, &gd); lwsl_notice("%s: gnutls_session_get_data2 ret=%d, len=%u\n", __func__, ret, gd.size); diff --git a/lib/tls/gnutls/gnutls-tls.c b/lib/tls/gnutls/gnutls-tls.c index f8c01fca4a..4c5828745a 100644 --- a/lib/tls/gnutls/gnutls-tls.c +++ b/lib/tls/gnutls/gnutls-tls.c @@ -336,10 +336,15 @@ int lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) { gnutls_session_t session; - unsigned int flags = GNUTLS_SERVER; + + if (!wsi->a.vhost) { + lwsl_err("%s: NULL vhost\n", __func__); + return 1; + } + #if GNUTLS_VERSION_NUMBER >= 0x030605 - if (wsi->a.vhost && (wsi->a.vhost->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA)) { + if (wsi->a.vhost->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA) { flags |= GNUTLS_ENABLE_EARLY_DATA; #if defined(LWS_ROLE_QUIC) && GNUTLS_VERSION_NUMBER >= 0x030702 extern const struct lws_role_ops role_ops_quic; @@ -493,7 +498,7 @@ lws_ssl_client_bio_create(struct lws *wsi) void lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) { -#if GNUTLS_VERSION_NUMBER >= 0x030605 +#if defined(LWS_WITH_SERVER) && GNUTLS_VERSION_NUMBER >= 0x030605 if (vhost->tls.anti_replay) { lws_dll2_owner_t *owner = (lws_dll2_owner_t *)vhost->tls.anti_replay_owner; if (owner) { diff --git a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c index 184819d64a..aa59553dd4 100644 --- a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c +++ b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c @@ -749,10 +749,10 @@ int main(int argc, const char **argv) */ if (testcase && !strcmp(testcase, "zerortt")) { if (!save_ticket[0]) - lws_strncpy(save_ticket, "/tmp/lws_session_ticket", + lws_strncpy(save_ticket, "./lws_session_ticket", sizeof(save_ticket)); if (!load_ticket[0]) - lws_strncpy(load_ticket, "/tmp/lws_session_ticket", + lws_strncpy(load_ticket, "./lws_session_ticket", sizeof(load_ticket)); lwsl_notice("%s: TESTCASE=zerortt: ticket path '%s'\n", __func__, save_ticket); From d9c0a7bc58c93dc78b90db0875dfcd895dcb2b39 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 9 Jul 2026 13:52:26 +0100 Subject: [PATCH 05/44] tls: remove 32KB hs buffer --- lib/roles/quic/crypto-quic.c | 30 +++++------------------------- lib/tls/gnutls/gnutls-quic.c | 4 ++-- lib/tls/openssl/openssl-quic.c | 14 +++++++++++++- lib/tls/schannel/schannel-quic.c | 32 +++++++++++++++++++++----------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index 457b40df72..41f4867dec 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -725,20 +725,14 @@ lws_quic_encrypt_payload(struct lws_quic_keys *keys, uint8_t *packet, size_t pac int lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t len) { - uint8_t *out = lws_malloc(32768, "quic rx crypto"); - size_t out_len = 32768; int n; - if (!out) - return -1; - if (len > 0 && wsi->quic.qn) { if (wsi->quic.qn->crypto_rx_buf_len[level] > 0) { uint8_t *new_buf = lws_realloc(wsi->quic.qn->crypto_rx_buf[level], wsi->quic.qn->crypto_rx_buf_len[level] + len, "crypto rx buf"); if (!new_buf) { - lws_free(out); return -1; } memcpy(new_buf + wsi->quic.qn->crypto_rx_buf_len[level], buf, len); @@ -764,7 +758,6 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le if (type == 24 || type == 5) { lwsl_wsi_notice(wsi, "QUIC RX CRYPTO: Illegal TLS Handshake type %d", type); lws_quic_enter_closing_state(wsi, 0x0100 + 10 /* unexpected_message */, 0, 0); - lws_free(out); return -1; } @@ -781,18 +774,16 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le if (wsi->quic.qn->crypto_rx_buf_len[level] == 0) { uint8_t *new_buf = lws_malloc(len, "crypto rx buf"); if (!new_buf) { - lws_free(out); return -1; } memcpy(new_buf, buf, len); wsi->quic.qn->crypto_rx_buf[level] = new_buf; wsi->quic.qn->crypto_rx_buf_len[level] = len; } - lws_free(out); return 0; } - n = lws_tls_quic_advance_handshake(wsi, level, buf, complete_len, out, &out_len); + n = lws_tls_quic_advance_handshake(wsi, level, buf, complete_len, NULL, NULL); { struct lws *nwsi = lws_get_quic_network_wsi(wsi); @@ -823,7 +814,7 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le } } } else { - n = lws_tls_quic_advance_handshake(wsi, level, buf, len, out, &out_len); + n = lws_tls_quic_advance_handshake(wsi, level, buf, len, NULL, NULL); { struct lws *nwsi = lws_get_quic_network_wsi(wsi); if (nwsi) wsi = nwsi; @@ -855,15 +846,9 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le lws_quic_enter_closing_state(wsi, 0x0100 + 10 /* unexpected_message fallback */, 0, 0); } #endif - lws_free(out); return -1; } - if (out_len > 0) { - /* Pass the generated TX CRYPTO data back to the QUIC transport queues */ - lws_tls_quic_tx_crypto_cb(wsi, level, out, out_len); - } - lwsl_wsi_debug(wsi, "lws_tls_quic_advance_handshake returned %d, tp_parsed=%d", n, wsi->quic.qn ? wsi->quic.qn->tp_parsed : -1); @@ -876,16 +861,14 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le if (lws_quic_parse_transport_parameters(wsi, peer_tp, peer_tp_len) < 0) { lwsl_wsi_err(wsi, "QUIC transport parameters validation failed"); lws_quic_enter_closing_state(wsi, LWS_QUIC_ERR_TRANSPORT_PARAMETER_ERROR, 0, 0); - lws_free(out); return -1; } } else { lwsl_wsi_debug(wsi, "lws_tls_quic_get_transport_parameters returned non-zero or NULL"); } - if (wsi->quic.qn->is_server && out_len > 0 && !wsi->quic.qn->tp_parsed) { + if (wsi->quic.qn->is_server && wsi->quic.qn->crypto_tx_offset[LWS_QUIC_LEVEL_INITIAL] > 0 && !wsi->quic.qn->tp_parsed) { lwsl_wsi_err(wsi, "QUIC Peer provided no transport parameters in ClientHello!"); lws_quic_enter_closing_state(wsi, 0x0100 + 109 /* missing_extension */, 0, 0); - lws_free(out); return -1; } } @@ -896,7 +879,6 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le if (!wsi->quic.qn->tp_parsed) { lwsl_wsi_err(wsi, "QUIC Peer provided no transport parameters!"); lws_quic_enter_closing_state(wsi, 0x0100 + 109 /* missing_extension */, 0, 0); - lws_free(out); return -1; } @@ -943,12 +925,11 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le lwsl_wsi_notice(wsi, "QUIC ALPN negotiated: %s", wsi->alpn); lws_role_call_alpn_negotiated(wsi, wsi->alpn); } else if (wsi->alpn[0]) { - lwsl_wsi_notice(wsi, "QUIC ALPN already negotiated: %s", wsi->alpn); - lws_role_call_alpn_negotiated(wsi, wsi->alpn); + lwsl_wsi_notice(wsi, "QUIC ALPN already negotiated: %s", wsi->alpn); + lws_role_call_alpn_negotiated(wsi, wsi->alpn); } else { lwsl_wsi_err(wsi, "QUIC requires ALPN, but none was negotiated!"); lws_quic_enter_closing_state(wsi, 0x0100 + 120 /* no_application_protocol */, 0, 0); - lws_free(out); return -1; } } @@ -964,7 +945,6 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le lws_callback_on_writable(wsi); } - lws_free(out); return 0; } diff --git a/lib/tls/gnutls/gnutls-quic.c b/lib/tls/gnutls/gnutls-quic.c index a96bdd0af6..36c42843f9 100644 --- a/lib/tls/gnutls/gnutls-quic.c +++ b/lib/tls/gnutls/gnutls-quic.c @@ -161,8 +161,8 @@ gnutls_quic_read_func(gnutls_session_t session, return 0; } - if (qlevel == b->target_level) { - if (!b->out || b->out_len + data_size > b->out_max) + if (b->out && qlevel == b->target_level) { + if (b->out_len + data_size > b->out_max) return GNUTLS_E_MEMORY_ERROR; memcpy(b->out + b->out_len, data, data_size); b->out_len += data_size; diff --git a/lib/tls/openssl/openssl-quic.c b/lib/tls/openssl/openssl-quic.c index df15384aa2..961bb3ec9c 100644 --- a/lib/tls/openssl/openssl-quic.c +++ b/lib/tls/openssl/openssl-quic.c @@ -340,7 +340,7 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, if (wsi->tls.quic_secret_cb == test_secret_cb) { wsi->tls.quic_tp_send = out; - wsi->tls.quic_tp_recv_len = *out_len; + wsi->tls.quic_tp_recv_len = out_len ? *out_len : 0; wsi->tls.quic_tp_recv = NULL; } else { if (out_len) @@ -653,6 +653,18 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, written = (size_t)read_n; *out_len = written; lwsl_info("QUIC TLS: BIO_read extracted %d bytes of TX data\n", (int)written); + } else { + size_t pending = (size_t)BIO_ctrl_pending(wbio); + if (pending > 0) { + uint8_t *exact_buf = lws_malloc(pending, "quic tx exact"); + if (exact_buf) { + int read_n = BIO_read(wbio, exact_buf, (int)pending); + if (read_n > 0) { + lws_tls_quic_tx_crypto_cb(wsi, level, exact_buf, (size_t)read_n); + } + lws_free(exact_buf); + } + } } if (hs_n <= 0) { diff --git a/lib/tls/schannel/schannel-quic.c b/lib/tls/schannel/schannel-quic.c index 2fd28e9788..1cb23ca747 100644 --- a/lib/tls/schannel/schannel-quic.c +++ b/lib/tls/schannel/schannel-quic.c @@ -556,17 +556,27 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, } } - if (split_offset > 0 && split_offset < *out_len) { - size_t remainder = *out_len - split_offset; - - /* - * We return the Initial bytes back to the caller in `out`, but we must - * instantly push the Handshake bytes into the QUIC layer at Level 2, - * because the caller does not loop to pull them! - */ - lws_tls_quic_tx_crypto_cb(wsi, 2 /* LWS_QUIC_LEVEL_HANDSHAKE */, out + split_offset, remainder); - - *out_len = split_offset; + if (out && out_len) { + if (split_offset > 0 && split_offset < *out_len) { + size_t remainder = *out_len - split_offset; + + /* + * We return the Initial bytes back to the caller in `out`, but we must + * instantly push the Handshake bytes into the QUIC layer at Level 2, + * because the caller does not loop to pull them! + */ + lws_tls_quic_tx_crypto_cb(wsi, 2 /* LWS_QUIC_LEVEL_HANDSHAKE */, out + split_offset, remainder); + + *out_len = split_offset; + } + } else if (out_bufs[0].cbBuffer && out_bufs[0].pvBuffer) { + size_t len = out_bufs[0].cbBuffer; + if (split_offset > 0 && split_offset < len) { + size_t remainder = len - split_offset; + lws_tls_quic_tx_crypto_cb(wsi, 2 /* LWS_QUIC_LEVEL_HANDSHAKE */, (uint8_t *)out_bufs[0].pvBuffer + split_offset, remainder); + len = split_offset; + } + lws_tls_quic_tx_crypto_cb(wsi, level, out_bufs[0].pvBuffer, len); } #else From 185559e54357818de7229c3d55d61882d99e2656 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 9 Jul 2026 14:32:47 +0100 Subject: [PATCH 06/44] tls: migrate header buf to ah --- lib/roles/h3/ops-h3.c | 34 ++++++++++++++++++++++--- lib/roles/h3/private-lib-roles-h3.h | 2 +- lib/roles/quic/ops-quic.c | 33 ++++++++++++++++-------- lib/roles/quic/private-lib-roles-quic.h | 2 -- lib/tls/openssl/openssl-quic.c | 28 +++++++++++++++----- lib/tls/openssl/openssl-ssl.c | 9 +++++++ lib/tls/schannel/schannel-quic.c | 15 +++++++---- lib/tls/schannel/schannel-ssl.c | 9 +++++++ 8 files changed, 103 insertions(+), 29 deletions(-) diff --git a/lib/roles/h3/ops-h3.c b/lib/roles/h3/ops-h3.c index 22305a511c..a97899ef33 100644 --- a/lib/roles/h3/ops-h3.c +++ b/lib/roles/h3/ops-h3.c @@ -777,7 +777,12 @@ lws_h3_rx_stream_data(struct lws *wsi, const uint8_t *buf, size_t len) wsi->h3.stream_type = (uint8_t)type; wsi->h3.type_set = 1; if (type == 0x02) { - wsi->h3.qpack_dec_state.state = LQP_DEC_INSTRUCTION; + if (!wsi->h3.qpack_dec_state) { + wsi->h3.qpack_dec_state = lws_zalloc(sizeof(*wsi->h3.qpack_dec_state), "qpack dec state"); + if (!wsi->h3.qpack_dec_state) + return 1; + } + wsi->h3.qpack_dec_state->state = LQP_DEC_INSTRUCTION; } buf += consumed; len -= consumed; @@ -802,10 +807,17 @@ lws_h3_rx_stream_data(struct lws *wsi, const uint8_t *buf, size_t len) struct lws_qpack_context *ctx = wsi->h3.h3n ? &wsi->h3.h3n->qpack_dec_ctx : NULL; lwsl_wsi_info(wsi, "LWS_H3_RX_STREAM_DATA: Encoder Stream payload received, len=%d", (int)len); - if (lws_qpack_decode_encoder_stream(&wsi->h3.qpack_dec_state, ctx, buf, len)) { + if (!wsi->h3.qpack_dec_state) { + wsi->h3.qpack_dec_state = lws_zalloc(sizeof(*wsi->h3.qpack_dec_state), "qpack dec state"); + if (!wsi->h3.qpack_dec_state) + return 1; + wsi->h3.qpack_dec_state->state = LQP_DEC_INSTRUCTION; + } + + if (lws_qpack_decode_encoder_stream(wsi->h3.qpack_dec_state, ctx, buf, len)) { struct lws *nwsi = lws_get_quic_network_wsi(wsi); lwsl_err("ERROR: QPACK_ENCODER_STREAM_ERROR!!!!\n"); -lws_quic_enter_closing_state(nwsi, LWS_QPACK_ENCODER_STREAM_ERROR, 0, 1); + lws_quic_enter_closing_state(nwsi, LWS_QPACK_ENCODER_STREAM_ERROR, 0, 1); return 1; } buf += len; len = 0; @@ -945,7 +957,12 @@ lws_quic_enter_closing_state(nwsi, LWS_QPACK_ENCODER_STREAM_ERROR, 0, 1); if (!wsi->quic.qs || !wsi->quic.qs->is_unidirectional) { if (wsi->h3.rx_frame_type == 0x01) { /* HEADERS */ struct lws_qpack_context *ctx = wsi->h3.h3n ? &wsi->h3.h3n->qpack_dec_ctx : NULL; - if (lws_qpack_decode_header_block(&wsi->h3.qpack_dec_state, ctx, buf, chunk, lws_h3_qpack_header_cb, wsi)) { + if (!wsi->h3.qpack_dec_state) { + wsi->h3.qpack_dec_state = lws_zalloc(sizeof(*wsi->h3.qpack_dec_state), "qpack dec state"); + if (!wsi->h3.qpack_dec_state) + return 1; + } + if (lws_qpack_decode_header_block(wsi->h3.qpack_dec_state, ctx, buf, chunk, lws_h3_qpack_header_cb, wsi)) { struct lws *nwsi = lws_get_quic_network_wsi(wsi); lws_quic_enter_closing_state(nwsi, LWS_QPACK_DECOMPRESSION_FAILED, 0, 1); return 1; @@ -1198,6 +1215,10 @@ lws_quic_enter_closing_state(nwsi, LWS_QPACK_ENCODER_STREAM_ERROR, 0, 1); } #endif } + if (wsi->h3.rx_frame_type == 0x01 && wsi->h3.qpack_dec_state) { + lws_free(wsi->h3.qpack_dec_state); + wsi->h3.qpack_dec_state = NULL; + } wsi->h3.rx_frame_state = 0; /* Next frame */ } } @@ -1281,6 +1302,11 @@ rops_close_kill_connection_h3(struct lws *wsi, enum lws_close_status reason) { lws_quic_stream_cleanup(wsi); + if (wsi->h3.qpack_dec_state) { + lws_free(wsi->h3.qpack_dec_state); + wsi->h3.qpack_dec_state = NULL; + } + if (wsi->mux.parent_wsi) lws_wsi_mux_sibling_disconnect(wsi); diff --git a/lib/roles/h3/private-lib-roles-h3.h b/lib/roles/h3/private-lib-roles-h3.h index f53b7ef9ff..7b1481118f 100644 --- a/lib/roles/h3/private-lib-roles-h3.h +++ b/lib/roles/h3/private-lib-roles-h3.h @@ -91,7 +91,7 @@ struct lws_h3_netconn { struct _lws_h3_related { struct lws_h3_netconn *h3n; /* malloc'd for root net conn */ struct lws_qpack_tx_encoder *qpack_tx_encoder; - struct lws_qpack_stream_state qpack_dec_state; + struct lws_qpack_stream_state *qpack_dec_state; uint8_t h3_state; uint8_t stream_type; uint8_t type_set:1; diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 96932e76f8..fab785a3d5 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -740,8 +740,13 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, lws_get_random(wsi->a.context, nwsi->quic.qn->loc_cid.id, 8); { - uint8_t *tp = nwsi->quic.qn->local_tp_buf; - uint8_t *tp_end = tp + sizeof(nwsi->quic.qn->local_tp_buf); + uint8_t *local_tp_buf = lws_malloc(4096, "quic tp scratch"); + if (!local_tp_buf) { + lwsl_wsi_err(wsi, "OOM allocating tp scratch buffer"); + return LWS_HPI_RET_HANDLED; + } + uint8_t *tp = local_tp_buf; + uint8_t *tp_end = tp + 4096; #define LWS_QUIC_WRITE_TP_VARINT(_id, _val) \ do { \ @@ -763,7 +768,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, tp += (_len); \ } while (0) - lwsl_notice("QUIC CLIENT: Writing TP buffer! buf size %d", (int)(tp - nwsi->quic.qn->local_tp_buf)); LWS_QUIC_WRITE_TP_VARINT(0x04, LWS_QUIC_DEFAULT_WINDOW); + lwsl_notice("QUIC CLIENT: Writing TP buffer! buf size %d", (int)(tp - local_tp_buf)); LWS_QUIC_WRITE_TP_VARINT(0x04, LWS_QUIC_DEFAULT_WINDOW); LWS_QUIC_WRITE_TP_VARINT(0x05, LWS_QUIC_DEFAULT_WINDOW); LWS_QUIC_WRITE_TP_VARINT(0x06, LWS_QUIC_DEFAULT_WINDOW); LWS_QUIC_WRITE_TP_VARINT(0x07, LWS_QUIC_DEFAULT_WINDOW); @@ -894,15 +899,15 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, } } - lws_tls_quic_set_transport_parameters(nwsi, nwsi->quic.qn->local_tp_buf, (size_t)(tp - nwsi->quic.qn->local_tp_buf)); - + lws_tls_quic_set_transport_parameters(nwsi, local_tp_buf, (size_t)(tp - local_tp_buf)); + lws_free(local_tp_buf); goto tp_ok; tp_overflow: + lws_free(local_tp_buf); lwsl_wsi_err(wsi, "QUIC TX: tp buffer overflow"); lws_close_free_wsi(nwsi, LWS_CLOSE_STATUS_NOSTATUS, "tp overflow"); return LWS_HPI_RET_HANDLED; tp_ok: - ; #undef LWS_QUIC_WRITE_TP_VARINT #undef LWS_QUIC_WRITE_TP_BUF } @@ -2616,8 +2621,13 @@ rops_client_bind_quic(struct lws *wsi, const struct lws_client_connect_info *i) } { - uint8_t *tp = wsi->quic.qn->local_tp_buf; - uint8_t *tp_end = tp + sizeof(wsi->quic.qn->local_tp_buf); + uint8_t *local_tp_buf = lws_malloc(4096, "quic tp scratch"); + if (!local_tp_buf) { + lwsl_wsi_err(wsi, "OOM allocating tp scratch buffer"); + return 1; + } + uint8_t *tp = local_tp_buf; + uint8_t *tp_end = tp + 4096; #define LWS_QUIC_WRITE_TP_VARINT(_id, _val) \ do { \ @@ -2639,7 +2649,7 @@ rops_client_bind_quic(struct lws *wsi, const struct lws_client_connect_info *i) tp += (_len); \ } while (0) - lwsl_notice("QUIC CLIENT: Writing TP buffer! buf size %d", (int)(tp - wsi->quic.qn->local_tp_buf)); LWS_QUIC_WRITE_TP_VARINT(0x04, 1048576); + lwsl_notice("QUIC CLIENT: Writing TP buffer! buf size %d", (int)(tp - local_tp_buf)); LWS_QUIC_WRITE_TP_VARINT(0x04, 1048576); LWS_QUIC_WRITE_TP_VARINT(0x05, 1048576); LWS_QUIC_WRITE_TP_VARINT(0x06, 1048576); LWS_QUIC_WRITE_TP_VARINT(0x07, 1048576); @@ -2666,10 +2676,11 @@ rops_client_bind_quic(struct lws *wsi, const struct lws_client_connect_info *i) LWS_QUIC_WRITE_TP_BUF(0x11, vi_buf, 12); } - lws_tls_quic_set_transport_parameters(wsi, wsi->quic.qn->local_tp_buf, (size_t)(tp - wsi->quic.qn->local_tp_buf)); - + lws_tls_quic_set_transport_parameters(wsi, local_tp_buf, (size_t)(tp - local_tp_buf)); + lws_free(local_tp_buf); goto tp_ok2; tp_overflow2: + lws_free(local_tp_buf); lwsl_wsi_err(wsi, "QUIC TX: tp buffer overflow"); return 1; tp_ok2: diff --git a/lib/roles/quic/private-lib-roles-quic.h b/lib/roles/quic/private-lib-roles-quic.h index 02e6437983..0f436707f3 100644 --- a/lib/roles/quic/private-lib-roles-quic.h +++ b/lib/roles/quic/private-lib-roles-quic.h @@ -222,8 +222,6 @@ struct lws_quic_netconn { struct lws_quic_cid rem_cid; /* Remote peer's Connection ID */ struct lws_quic_cid orig_dcid; /* Original Destination Connection ID from client */ - uint8_t local_tp_buf[4096]; /* buffer for transport parameters */ - /* Array of pointers to lazily allocated key material */ struct lws_quic_keys *keys[LWS_QUIC_LEVEL_COUNT]; diff --git a/lib/tls/openssl/openssl-quic.c b/lib/tls/openssl/openssl-quic.c index 961bb3ec9c..204feb5272 100644 --- a/lib/tls/openssl/openssl-quic.c +++ b/lib/tls/openssl/openssl-quic.c @@ -427,26 +427,33 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, return 0; } - int lws_tls_quic_set_transport_parameters(struct lws *wsi, const uint8_t *tp, size_t tp_len) { - wsi->tls.quic_tp_send = tp; + if (wsi->tls.quic_tp_send) { + lws_free((void *)wsi->tls.quic_tp_send); + wsi->tls.quic_tp_send = NULL; + } + + uint8_t *p = lws_malloc(tp_len, "quic tp"); + if (!p) + return -1; + memcpy(p, tp, tp_len); + wsi->tls.quic_tp_send = p; wsi->tls.quic_tp_send_len = tp_len; if (!wsi->tls.ssl) return 0; #if defined(USE_WOLFSSL) - if (wolfSSL_set_quic_transport_params(wsi->tls.ssl, tp, tp_len) != 1) + if (wolfSSL_set_quic_transport_params(wsi->tls.ssl, p, tp_len) != 1) return -1; #else - if (SSL_set_quic_transport_params(wsi->tls.ssl, tp, tp_len) != 1) + if (SSL_set_quic_transport_params(wsi->tls.ssl, p, tp_len) != 1) return -1; #endif return 0; } - int lws_tls_quic_get_transport_parameters(struct lws *wsi, const uint8_t **tp, size_t *tp_len) { @@ -687,7 +694,16 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, int lws_tls_quic_set_transport_parameters(struct lws *wsi, const uint8_t *tp, size_t tp_len) { - wsi->tls.quic_tp_send = tp; + if (wsi->tls.quic_tp_send) { + lws_free((void *)wsi->tls.quic_tp_send); + wsi->tls.quic_tp_send = NULL; + } + + uint8_t *p = lws_malloc(tp_len, "quic tp"); + if (!p) + return -1; + memcpy(p, tp, tp_len); + wsi->tls.quic_tp_send = p; wsi->tls.quic_tp_send_len = tp_len; return 0; } diff --git a/lib/tls/openssl/openssl-ssl.c b/lib/tls/openssl/openssl-ssl.c index 140234ff01..b52c077cfb 100644 --- a/lib/tls/openssl/openssl-ssl.c +++ b/lib/tls/openssl/openssl-ssl.c @@ -525,6 +525,15 @@ lws_ssl_close(struct lws *wsi) SSL_free(wsi->tls.ssl); wsi->tls.ssl = NULL; + if (wsi->tls.quic_tp_recv) { + lws_free((void *)wsi->tls.quic_tp_recv); + wsi->tls.quic_tp_recv = NULL; + } + if (wsi->tls.quic_tp_send) { + lws_free((void *)wsi->tls.quic_tp_send); + wsi->tls.quic_tp_send = NULL; + } + lws_tls_restrict_return(wsi); // lwsl_notice("%s: ssl restr %d, simul %d\n", __func__, diff --git a/lib/tls/schannel/schannel-quic.c b/lib/tls/schannel/schannel-quic.c index 1cb23ca747..c0523f1a48 100644 --- a/lib/tls/schannel/schannel-quic.c +++ b/lib/tls/schannel/schannel-quic.c @@ -175,11 +175,16 @@ int lws_tls_quic_set_transport_parameters(struct lws *wsi, const uint8_t *tp, size_t tp_len) { #if defined(SECPKG_ATTR_APPLICATION_PROTOCOL) || defined(SECPKG_ATTR_APP_DATA) - /* - * Transport parameter exchange on Windows using Schannel for QUIC - * typically requires MsQuic or newer Windows 11 / Server 2022 APIs. - */ - wsi->tls.quic_tp_send = tp; + if (wsi->tls.quic_tp_send) { + lws_free((void *)wsi->tls.quic_tp_send); + wsi->tls.quic_tp_send = NULL; + } + + uint8_t *p = lws_malloc(tp_len, "quic tp"); + if (!p) + return -1; + memcpy(p, tp, tp_len); + wsi->tls.quic_tp_send = p; wsi->tls.quic_tp_send_len = tp_len; return 0; #else diff --git a/lib/tls/schannel/schannel-ssl.c b/lib/tls/schannel/schannel-ssl.c index 7ebe5776be..3d6d7f1745 100644 --- a/lib/tls/schannel/schannel-ssl.c +++ b/lib/tls/schannel/schannel-ssl.c @@ -897,6 +897,15 @@ lws_ssl_close(struct lws *wsi) wsi->tls.ctx_ref = NULL; } + if (wsi->tls.quic_tp_recv) { + lws_free((void *)wsi->tls.quic_tp_recv); + wsi->tls.quic_tp_recv = NULL; + } + if (wsi->tls.quic_tp_send) { + lws_free((void *)wsi->tls.quic_tp_send); + wsi->tls.quic_tp_send = NULL; + } + return 0; } From 5d9eebd940a6e9b0a6b6a11ec4d18659de2ba5d4 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 9 Jul 2026 15:46:24 +0100 Subject: [PATCH 07/44] oauth: grant level display --- plugins/protocol_lws_login/protocol_lws_login.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/protocol_lws_login/protocol_lws_login.c b/plugins/protocol_lws_login/protocol_lws_login.c index 4494da1686..7410ee171b 100644 --- a/plugins/protocol_lws_login/protocol_lws_login.c +++ b/plugins/protocol_lws_login/protocol_lws_login.c @@ -114,7 +114,10 @@ static const char * const canned_css = ".pie-timer circle{fill:none;stroke:#fff;stroke-width:10;stroke-dasharray:31.4;transition:stroke-dashoffset 1s linear;}" ".lws-login-refgirl{position:absolute;bottom:0px;right:-10px;height:120px;opacity:0.8;pointer-events:none;z-index:1;}" ".lws-preauth-widget{display:none;position:absolute;top:0;left:100%;margin-left:15px;border:1px solid rgba(255,255,255,0.1);border-radius:6px;padding:8px;max-height:200px;overflow-y:auto;background:#2b2d31;box-shadow:0 8px 16px rgba(0,0,0,0.3);z-index:1000;min-width:280px;}" - ".lws-preauth-widget.active{display:block;}"; + ".lws-preauth-widget.active{display:block;}" + ".lws-login-avatar-admin{color:#007bff;margin-right:6px;display:inline-block;vertical-align:middle;}" + ".lws-login-avatar-user{color:#333;margin-right:6px;display:inline-block;vertical-align:middle;}" + "@media(prefers-color-scheme:dark){.lws-login-avatar-user{color:#888;}}"; static const char * const canned_js = "window.lwsLoginSilentRefresh=async function(){" @@ -152,6 +155,10 @@ static const char * const canned_js = "window.lwsLoginRetry=0;" "var u='.lws-login-logout?redirect_uri='+encodeURIComponent(window.location.href);" "var a=st.is_admin?'':'';" + "var av='';" + "var gl=st.is_admin?255:(st.grant_level||0);" + "var ac=gl>=2?'lws-login-avatar-admin':'lws-login-avatar-user';" + "c+=''+av+'';" "c+='
';" "c+=a+' ';" "if(!st.has_grant&&!st.is_admin)c+='
';" From 8ef9a259efe1c69789d7c89120289ef56864363f Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 9 Jul 2026 15:09:02 +0100 Subject: [PATCH 08/44] quic: sai fixes --- lib/core-net/service.c | 2 +- lib/roles/quic/crypto-quic.c | 11 ++++++----- lib/roles/quic/ops-quic.c | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/core-net/service.c b/lib/core-net/service.c index 4917dacbf7..25067b8704 100644 --- a/lib/core-net/service.c +++ b/lib/core-net/service.c @@ -148,7 +148,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) #if defined(LWS_WITH_CLIENT) /* Intercept POLLOUT for parallel sockets if we are racing H3 */ - if (pollfd && pollfd->fd != wsi->desc.sockfd) { + if (pollfd && wsi->parallel_count > 0 && pollfd->fd != wsi->desc.sockfd) { lws_client_connect_3_connect(wsi, NULL, NULL, 0, pollfd); return 0; } diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index 41f4867dec..f7841cb9eb 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -725,6 +725,7 @@ lws_quic_encrypt_payload(struct lws_quic_keys *keys, uint8_t *packet, size_t pac int lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t len) { + struct lws *orig_wsi = wsi; int n; if (len > 0 && wsi->quic.qn) { @@ -935,14 +936,14 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le } #endif - if (wsi->role_ops) { - enum lws_callback_reasons cb = (enum lws_callback_reasons)wsi->role_ops->adoption_cb[lwsi_role_server(wsi)]; - if (cb && wsi->a.protocol && wsi->a.protocol->callback) { - wsi->a.protocol->callback(wsi, cb, wsi->user_space, NULL, 0); + if (orig_wsi->role_ops) { + enum lws_callback_reasons cb = (enum lws_callback_reasons)orig_wsi->role_ops->adoption_cb[lwsi_role_server(orig_wsi)]; + if (cb && orig_wsi->a.protocol && orig_wsi->a.protocol->callback) { + orig_wsi->a.protocol->callback(orig_wsi, cb, orig_wsi->user_space, NULL, 0); } } - lws_callback_on_writable(wsi); + lws_callback_on_writable(orig_wsi); } return 0; diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index fab785a3d5..3cb51305b4 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -908,6 +908,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, lws_close_free_wsi(nwsi, LWS_CLOSE_STATUS_NOSTATUS, "tp overflow"); return LWS_HPI_RET_HANDLED; tp_ok: + ; #undef LWS_QUIC_WRITE_TP_VARINT #undef LWS_QUIC_WRITE_TP_BUF } @@ -1495,8 +1496,6 @@ rops_handle_POLLOUT_quic(struct lws *wsi) int eagain_blocked = 0; uint8_t pkt[2048]; memset(pkt, 0, sizeof(pkt)); - wsi->mux.requested_POLLOUT = 0; - // lwsl_notice("QUIC TX: POLLOUT called for %s, qn=%p, is_server=%d\n", lws_wsi_tag(wsi), qn, qn ? qn->is_server : -1); if (!qn) { @@ -1515,6 +1514,8 @@ rops_handle_POLLOUT_quic(struct lws *wsi) return LWS_HP_RET_DROP_POLLOUT; } + wsi->mux.requested_POLLOUT = 0; + lws_usec_t pto_base = qn->smoothed_rtt ? (qn->smoothed_rtt + (4 * qn->rttvar) + 25000) : LWS_QUIC_DEFAULT_PTO_US; lws_usec_t pto_delay = pto_base << qn->pto_count; if (pto_delay > 10000000) From 8efc209ea492a6bff094441b3cbf0036c464c76a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 9 Jul 2026 17:43:43 +0100 Subject: [PATCH 09/44] oauth: increase grant size --- .../protocol_lws_auth_server.c | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c index 143556f1e2..3b574555ec 100644 --- a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c +++ b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c @@ -2436,7 +2436,7 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, int lacks_grant = 0; char sname[128] = {0}; char user_email[128] = {0}; - char grants[256] = {0}; + char grants[2048] = {0}; char *gp = grants, *gend = grants + sizeof(grants); char logs[LWS_SSO_MAX_COOKIE] = {0}; lws_get_urlarg_by_name_safe(wsi, "service_name=", sname, sizeof(sname)); @@ -3184,7 +3184,7 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, { char op[32] = {0}; int req_uid = -1; - char new_grants[512] = {0}; + char new_grants[2048] = {0}; char *gp; if ((gp = (char *)strstr((const char *)in, "\"op\":\""))) { @@ -3200,7 +3200,7 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, if ((gp = (char *)strstr((const char *)in, "\"grants\":\""))) { gp += 10; int i = 0; - while (*gp && *gp != '"' && i < 511) + while (*gp && *gp != '"' && i < (int)sizeof(new_grants) - 1) new_grants[i++] = *gp++; } @@ -3311,7 +3311,17 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, char *p2 = new_grants; while (*p2) { char *comma = (char *)strchr(p2, ','); - if (comma) *comma = '\0'; + char *semi = (char *)strchr(p2, ';'); + char *delim = NULL; + + if (comma && semi) + delim = (comma < semi) ? comma : semi; + else if (comma) + delim = comma; + else + delim = semi; + + if (delim) *delim = '\0'; char *colon = (char *)strchr(p2, ':'); if (colon) { *colon = '\0'; @@ -3334,8 +3344,8 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, } } } - if (!comma) break; - p2 = comma + 1; + if (!delim) break; + p2 = delim + 1; } sqlite3_exec(vhd->db, "COMMIT;", NULL, NULL, NULL); } From 49e58485cca7578b97340e578a4aef1ed81435aa Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 10 Jul 2026 11:31:35 +0100 Subject: [PATCH 10/44] cov4 --- lib/roles/quic/parse-quic.c | 38 +++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/roles/quic/parse-quic.c b/lib/roles/quic/parse-quic.c index dd3bc7f693..46e0811690 100644 --- a/lib/roles/quic/parse-quic.c +++ b/lib/roles/quic/parse-quic.c @@ -185,8 +185,22 @@ void lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_stream *qs, uint64_t offset, uint8_t *buf, size_t len, int is_crypto, int level) { - uint64_t *expected_offset = is_crypto ? &nwsi->quic.qn->rx_crypto_offset[level] : &qs->rx_offset; - lws_dll2_owner_t *owner = is_crypto ? &nwsi->quic.qn->rx_crypto_chunks[level] : &qs->rx_chunks; + uint64_t *expected_offset; + lws_dll2_owner_t *owner; + + if (is_crypto) { + if (nwsi && !nwsi->quic.qn) + nwsi = lws_get_quic_network_wsi(nwsi); + if (!nwsi || !nwsi->quic.qn) + return; + expected_offset = &nwsi->quic.qn->rx_crypto_offset[level]; + owner = &nwsi->quic.qn->rx_crypto_chunks[level]; + } else { + if (!qs) + return; + expected_offset = &qs->rx_offset; + owner = &qs->rx_chunks; + } /* 1. If it's a past or overlapping frame, ignore it (simple version) */ if (offset + len <= *expected_offset && !(len == 0 && offset == *expected_offset)) @@ -208,12 +222,12 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0); return; } - if (nwsi && !nwsi->quic.qn) { + if (!nwsi->quic.qn) { nwsi = lws_get_quic_network_wsi(nwsi); - if (nwsi && nwsi->quic.qn) { - expected_offset = &nwsi->quic.qn->rx_crypto_offset[level]; - owner = &nwsi->quic.qn->rx_crypto_chunks[level]; - } + if (!nwsi || !nwsi->quic.qn) + return; + expected_offset = &nwsi->quic.qn->rx_crypto_offset[level]; + owner = &nwsi->quic.qn->rx_crypto_chunks[level]; } } else if (wsi_child) { int is_final = (wsi_child && qs && qs->fin_received && *expected_offset + len == qs->rx_final_size && !qs->fin_delivered); @@ -293,12 +307,12 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0); return; } - if (nwsi && !nwsi->quic.qn) { + if (!nwsi->quic.qn) { nwsi = lws_get_quic_network_wsi(nwsi); - if (nwsi && nwsi->quic.qn) { - expected_offset = &nwsi->quic.qn->rx_crypto_offset[level]; - owner = &nwsi->quic.qn->rx_crypto_chunks[level]; - } + if (!nwsi || !nwsi->quic.qn) + return; + expected_offset = &nwsi->quic.qn->rx_crypto_offset[level]; + owner = &nwsi->quic.qn->rx_crypto_chunks[level]; } } else if (wsi_child) { #if defined(LWS_ROLE_H3) From ce26cf15269c5e4a3838935b2dfd5d97c9bc3530 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 10 Jul 2026 12:23:45 +0100 Subject: [PATCH 11/44] sonarqube: fix all blocker maintainability --- lib/core-net/service.c | 24 ++-- lib/core-net/socks5-client.c | 50 +++---- lib/cose/cose_key.c | 13 +- lib/cose/cose_sign_alg.c | 124 +++++++++++------- lib/cose/cose_validate.c | 13 +- lib/cose/cose_validate_alg.c | 108 ++++++++------- lib/event-libs/libevent/libevent.c | 8 +- lib/event-libs/libuv/libuv.c | 9 +- .../protocol_lws_acme_client_core.c | 13 +- .../protocol_lws_auth_server.c | 26 +++- .../protocol_lws_cert_dist_server.c | 15 ++- .../protocol_lws_dht_dnssec.c | 13 +- .../protocol_lws_login/protocol_lws_login.c | 13 +- .../protocol_lws_mirror/protocol_lws_mirror.c | 8 +- plugins/protocol_urlarg/protocol_urlarg.c | 13 +- win32port/win32helpers/getopt.c | 20 ++- win32port/win32helpers/getopt_long.c | 21 ++- 17 files changed, 309 insertions(+), 182 deletions(-) diff --git a/lib/core-net/service.c b/lib/core-net/service.c index 25067b8704..9f69c5612d 100644 --- a/lib/core-net/service.c +++ b/lib/core-net/service.c @@ -898,17 +898,7 @@ _lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, case LWS_HPI_RET_PLEASE_CLOSE_ME: //lwsl_notice("%s: %s pollin says please close me\n", __func__, // wsi->role_ops->name); -close_and_handled_l: - lwsl_wsi_debug(wsi, "Close and handled"); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, - "close_and_handled"); - /* - * pollfd may point to something else after the close - * due to pollfd swapping scheme on delete on some platforms - * we can't clear revents now because it'd be the wrong guy's - * revents - */ - return 1; + goto close_and_handled_l; default: assert(0); } @@ -938,6 +928,18 @@ _lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, } #endif return 0; + +close_and_handled_l: + lwsl_wsi_debug(wsi, "Close and handled"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "close_and_handled"); + /* + * pollfd may point to something else after the close + * due to pollfd swapping scheme on delete on some platforms + * we can't clear revents now because it'd be the wrong guy's + * revents + */ + return 1; } int diff --git a/lib/core-net/socks5-client.c b/lib/core-net/socks5-client.c index 3788d2363c..2fa7147554 100644 --- a/lib/core-net/socks5-client.c +++ b/lib/core-net/socks5-client.c @@ -323,33 +323,12 @@ lws_socks5c_handle_state(struct lws *wsi, struct lws_pollfd *pollfd, lwsl_wsi_client(wsi, "SOCKS password OK, sending connect"); if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT, &len)) { -socks_send_msg_fail_l: - *pcce = "socks gen msg fail"; - return LW5CHS_RET_BAIL3; + goto socks_send_msg_fail_l; } conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY; pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; -socks_send_l: - // lwsl_hexdump_notice(pt->serv_buf, len); - n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, - LWS_POSIX_LENGTH_CAST(len), MSG_NOSIGNAL); - if (n < 0) { - lwsl_wsi_debug(wsi, "ERROR writing to socks proxy"); - *pcce = "socks write fail"; - return LW5CHS_RET_BAIL3; - } - - lws_set_timeout(wsi, (enum pending_timeout)pending_timeout, - (int)wsi->a.context->timeout_secs); - lwsi_set_state(wsi, (lws_wsi_state_t)conn_mode); - break; - -socks_reply_fail_l: - lwsl_wsi_err(wsi, "socks reply: v%d, err %d", - pt->serv_buf[0], pt->serv_buf[1]); - *pcce = "socks reply fail"; - return LW5CHS_RET_BAIL3; + goto socks_send_l; case LRS_WAITING_SOCKS_CONNECT_REPLY: if (pt->serv_buf[0] != SOCKS_VERSION_5 || @@ -377,4 +356,29 @@ lws_socks5c_handle_state(struct lws *wsi, struct lws_pollfd *pollfd, } return LW5CHS_RET_NOTHING; + +socks_send_l: + // lwsl_hexdump_notice(pt->serv_buf, len); + n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, + LWS_POSIX_LENGTH_CAST(len), MSG_NOSIGNAL); + if (n < 0) { + lwsl_wsi_debug(wsi, "ERROR writing to socks proxy"); + *pcce = "socks write fail"; + return LW5CHS_RET_BAIL3; + } + + lws_set_timeout(wsi, (enum pending_timeout)pending_timeout, + (int)wsi->a.context->timeout_secs); + lwsi_set_state(wsi, (lws_wsi_state_t)conn_mode); + return LW5CHS_RET_NOTHING; + +socks_send_msg_fail_l: + *pcce = "socks gen msg fail"; + return LW5CHS_RET_BAIL3; + +socks_reply_fail_l: + lwsl_wsi_err(wsi, "socks reply: v%d, err %d", + pt->serv_buf[0], pt->serv_buf[1]); + *pcce = "socks reply fail"; + return LW5CHS_RET_BAIL3; } diff --git a/lib/cose/cose_key.c b/lib/cose/cose_key.c index 45fbd6a924..0d024aae08 100644 --- a/lib/cose/cose_key.c +++ b/lib/cose/cose_key.c @@ -328,10 +328,19 @@ cb_cose_key(struct lecp_ctx *ctx, char reason) case LECPCB_OBJECT_START: if (cps->ck) break; - goto ak_l; + cps->ck = lws_zalloc(sizeof(*cps->ck), __func__); + if (!cps->ck) + goto bail; + cps->cose_state = 0; + cps->meta_idx = -1; + cps->gencrypto_eidx = -1; + cps->seen_count = 0; + + if (cps->pkey_set) + lws_dll2_add_tail(&cps->ck->list, cps->pkey_set); + break; case LECPCB_ARRAY_ITEM_START: if (cps->pkey_set && ctx->pst[ctx->pst_sp].ppos == 2) { - ak_l: cps->ck = lws_zalloc(sizeof(*cps->ck), __func__); if (!cps->ck) goto bail; diff --git a/lib/cose/cose_sign_alg.c b/lib/cose/cose_sign_alg.c index a9c4124242..7267660d67 100644 --- a/lib/cose/cose_sign_alg.c +++ b/lib/cose/cose_sign_alg.c @@ -41,6 +41,8 @@ lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, alg->cose_alg = cose_alg; alg->cose_key = ck; + int type = 0; + switch (cose_alg) { /* ECDSA algs */ @@ -49,18 +51,74 @@ lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, crv = "P-256"; gh = LWS_GENHASH_TYPE_SHA256; alg->keybits = 256; - goto ecdsa_l; + type = 1; + break; case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ crv = "P-384"; gh = LWS_GENHASH_TYPE_SHA384; alg->keybits = 384; - goto ecdsa_l; + type = 1; + break; case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ crv = "P-521"; gh = LWS_GENHASH_TYPE_SHA512; alg->keybits = 521; -ecdsa_l: + type = 1; + break; + + case LWSCOSE_WKAEDDSA_ALG_EDDSA: + type = 2; + break; + + + /* HMAC algs */ + + case LWSCOSE_WKAHMAC_256_64: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 64; + type = 3; + break; + case LWSCOSE_WKAHMAC_256_256: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 256; + type = 3; + break; + case LWSCOSE_WKAHMAC_384_384: + ghm = LWS_GENHMAC_TYPE_SHA384; + alg->keybits = 384; + type = 3; + break; + case LWSCOSE_WKAHMAC_512_512: + ghm = LWS_GENHMAC_TYPE_SHA512; + alg->keybits = 512; + type = 3; + break; + + /* RSASSA algs */ + + case LWSCOSE_WKARSA_ALG_RS256: + gh = LWS_GENHASH_TYPE_SHA256; + type = 4; + break; + + case LWSCOSE_WKARSA_ALG_RS384: + gh = LWS_GENHASH_TYPE_SHA384; + type = 4; + break; + + case LWSCOSE_WKARSA_ALG_RS512: + gh = LWS_GENHASH_TYPE_SHA512; + type = 4; + break; + + default: + lwsl_warn("%s: unsupported alg %lld\n", __func__, + (long long)cose_alg); + goto bail_hmac; + } + switch (type) { + case 1: /* ECDSA */ /* the key is good for this? */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_EC2, cose_alg, @@ -80,10 +138,9 @@ lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, lwsl_notice("%s: ec key import fail\n", __func__); goto bail_ecdsa2; } - break; - case LWSCOSE_WKAEDDSA_ALG_EDDSA: + case 2: /* EDDSA */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_OKP, cose_alg, op, NULL)) goto bail_ecdsa; @@ -92,29 +149,9 @@ lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, if (lws_geneddsa_set_key(&alg->u.ecdsactx, ck->e)) goto bail_ecdsa2; - break; - - /* HMAC algs */ - - case LWSCOSE_WKAHMAC_256_64: - ghm = LWS_GENHMAC_TYPE_SHA256; - alg->keybits = 64; - goto hmac_l; - case LWSCOSE_WKAHMAC_256_256: - ghm = LWS_GENHMAC_TYPE_SHA256; - alg->keybits = 256; - goto hmac_l; - case LWSCOSE_WKAHMAC_384_384: - ghm = LWS_GENHMAC_TYPE_SHA384; - alg->keybits = 384; - goto hmac_l; - case LWSCOSE_WKAHMAC_512_512: - ghm = LWS_GENHMAC_TYPE_SHA512; - alg->keybits = 512; - -hmac_l: + case 3: /* HMAC */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_SYMMETRIC, cose_alg, op, NULL)) goto bail_hmac; @@ -122,23 +159,9 @@ lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; if (lws_genhmac_init(&alg->u.hmacctx, ghm, ke->buf, ke->len)) goto bail_hmac; - break; - /* RSASSA algs */ - - case LWSCOSE_WKARSA_ALG_RS256: - gh = LWS_GENHASH_TYPE_SHA256; - goto rsassa_l; - - case LWSCOSE_WKARSA_ALG_RS384: - gh = LWS_GENHASH_TYPE_SHA384; - goto rsassa_l; - - case LWSCOSE_WKARSA_ALG_RS512: - gh = LWS_GENHASH_TYPE_SHA512; - -rsassa_l: + case 4: /* RSASSA */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_RSA, cose_alg, op, NULL)) goto bail_hmac; @@ -154,11 +177,6 @@ lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, goto bail_hmac; } break; - - default: - lwsl_warn("%s: unsupported alg %lld\n", __func__, - (long long)cose_alg); - goto bail_hmac; } return alg; @@ -252,13 +270,17 @@ lws_cose_sign_alg_complete(lws_cose_sig_alg_t *alg) case LWSCOSE_WKAEDDSA_ALG_EDDSA: { int len; - if (!alg->failed && - (len = lws_geneddsa_hash_sign_jws(&alg->u.ecdsactx, alg->eddsa_in, + if (!alg->failed) { + len = lws_geneddsa_hash_sign_jws(&alg->u.ecdsactx, alg->eddsa_in, alg->eddsa_in_len, alg->rhash, - sizeof(alg->rhash))) >= 0) - alg->rhash_len = len; - else + sizeof(alg->rhash)); + if (len >= 0) + alg->rhash_len = len; + else + alg->failed = 1; + } else { alg->failed = 1; + } lws_genec_destroy(&alg->u.ecdsactx); lws_free_set_NULL(alg->eddsa_in); diff --git a/lib/cose/cose_validate.c b/lib/cose/cose_validate.c index 35a333e16d..d2ce6be938 100644 --- a/lib/cose/cose_validate.c +++ b/lib/cose/cose_validate.c @@ -509,12 +509,8 @@ cb_cose_sig(struct lecp_ctx *ctx, char reason) goto unexpected_tag_l; break; case SIGTYPE_MAC: - if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC) { -unexpected_tag_l: - lwsl_warn("%s: unexpected tag %d\n", __func__, - (int)ctx->item.u.u64); - goto bail; - } + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC) + goto unexpected_tag_l; break; } @@ -960,6 +956,11 @@ cb_cose_sig(struct lecp_ctx *ctx, char reason) return 0; +unexpected_tag_l: + lwsl_warn("%s: unexpected tag %d\n", __func__, + (int)ctx->item.u.u64); + goto bail; + bail: return -1; diff --git a/lib/cose/cose_validate_alg.c b/lib/cose/cose_validate_alg.c index d7cb9b612e..87945b32aa 100644 --- a/lib/cose/cose_validate_alg.c +++ b/lib/cose/cose_validate_alg.c @@ -41,6 +41,8 @@ lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, alg->cose_alg = cose_alg; alg->cose_key = ck; + int type = 0; + switch (cose_alg) { /* ECDSA algs */ @@ -49,18 +51,73 @@ lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, crv = "P-256"; gh = LWS_GENHASH_TYPE_SHA256; alg->keybits = 256; - goto ecdsa_l; + type = 1; + break; case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ crv = "P-384"; gh = LWS_GENHASH_TYPE_SHA384; alg->keybits = 384; - goto ecdsa_l; + type = 1; + break; case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ crv = "P-521"; gh = LWS_GENHASH_TYPE_SHA512; alg->keybits = 521; -ecdsa_l: + type = 1; + break; + case LWSCOSE_WKAEDDSA_ALG_EDDSA: + type = 2; + break; + + /* HMAC algs */ + + case LWSCOSE_WKAHMAC_256_64: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 64; + type = 3; + break; + case LWSCOSE_WKAHMAC_256_256: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 256; + type = 3; + break; + case LWSCOSE_WKAHMAC_384_384: + ghm = LWS_GENHMAC_TYPE_SHA384; + alg->keybits = 384; + type = 3; + break; + case LWSCOSE_WKAHMAC_512_512: + ghm = LWS_GENHMAC_TYPE_SHA512; + alg->keybits = 512; + type = 3; + break; + + /* RSASSA algs */ + + case LWSCOSE_WKARSA_ALG_RS256: + gh = LWS_GENHASH_TYPE_SHA256; + type = 4; + break; + + case LWSCOSE_WKARSA_ALG_RS384: + gh = LWS_GENHASH_TYPE_SHA384; + type = 4; + break; + + case LWSCOSE_WKARSA_ALG_RS512: + gh = LWS_GENHASH_TYPE_SHA512; + type = 4; + break; + + default: + lwsl_warn("%s: unsupported alg %lld\n", __func__, + (long long)cose_alg); + goto bail_hmac; + } + + switch (type) { + case 1: /* ECDSA */ /* the key is good for this? */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_EC2, cose_alg, @@ -80,10 +137,9 @@ lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, lwsl_notice("%s: ec key import fail\n", __func__); goto bail_ecdsa2; } - break; - case LWSCOSE_WKAEDDSA_ALG_EDDSA: + case 2: /* EDDSA */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_OKP, cose_alg, op, NULL)) goto bail_ecdsa; @@ -92,28 +148,9 @@ lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, if (lws_geneddsa_set_key(&alg->u.ecdsactx, ck->e)) goto bail_ecdsa2; - break; - /* HMAC algs */ - - case LWSCOSE_WKAHMAC_256_64: - ghm = LWS_GENHMAC_TYPE_SHA256; - alg->keybits = 64; - goto hmac_l; - case LWSCOSE_WKAHMAC_256_256: - ghm = LWS_GENHMAC_TYPE_SHA256; - alg->keybits = 256; - goto hmac_l; - case LWSCOSE_WKAHMAC_384_384: - ghm = LWS_GENHMAC_TYPE_SHA384; - alg->keybits = 384; - goto hmac_l; - case LWSCOSE_WKAHMAC_512_512: - ghm = LWS_GENHMAC_TYPE_SHA512; - alg->keybits = 512; - -hmac_l: + case 3: /* HMAC */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_SYMMETRIC, cose_alg, op, NULL)) goto bail_hmac; @@ -121,23 +158,9 @@ lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; if (lws_genhmac_init(&alg->u.hmacctx, ghm, ke->buf, ke->len)) goto bail_hmac; - break; - /* RSASSA algs */ - - case LWSCOSE_WKARSA_ALG_RS256: - gh = LWS_GENHASH_TYPE_SHA256; - goto rsassa_l; - - case LWSCOSE_WKARSA_ALG_RS384: - gh = LWS_GENHASH_TYPE_SHA384; - goto rsassa_l; - - case LWSCOSE_WKARSA_ALG_RS512: - gh = LWS_GENHASH_TYPE_SHA512; - -rsassa_l: + case 4: /* RSASSA */ if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_RSA, cose_alg, op, NULL)) goto bail_hmac; @@ -152,11 +175,6 @@ lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, goto bail_ecdsa1; } break; - - default: - lwsl_warn("%s: unsupported alg %lld\n", __func__, - (long long)cose_alg); - goto bail_hmac; } return alg; diff --git a/lib/event-libs/libevent/libevent.c b/lib/event-libs/libevent/libevent.c index 4ea3a101a9..e275f8c300 100644 --- a/lib/event-libs/libevent/libevent.c +++ b/lib/event-libs/libevent/libevent.c @@ -466,9 +466,11 @@ elops_destroy_context2_event(struct lws_context *context) event_base_loopexit(ptpr->io_loop, NULL); continue; } - while (budget-- && - (m = event_base_loop(ptpr->io_loop, EVLOOP_NONBLOCK))) - ; + while (budget--) { + m = event_base_loop(ptpr->io_loop, EVLOOP_NONBLOCK); + if (!m) + break; + } lwsl_cx_info(context, "event_base_free"); diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c index c02687e1c9..684d5e64aa 100644 --- a/lib/event-libs/libuv/libuv.c +++ b/lib/event-libs/libuv/libuv.c @@ -381,9 +381,12 @@ elops_destroy_context1_uv(struct lws_context *context) if (!pt->event_loop_foreign && pt_to_priv_uv(pt)->io_loop) { - while (budget-- && (m = uv_run(pt_to_priv_uv(pt)->io_loop, - UV_RUN_NOWAIT))) - ; + while (budget--) { + m = uv_run(pt_to_priv_uv(pt)->io_loop, + UV_RUN_NOWAIT); + if (!m) + break; + } if (m) lwsl_cx_info(context, "tsi %d: unclosed", n); diff --git a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c index 14de99d32f..2131009fcb 100644 --- a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c +++ b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c @@ -2028,8 +2028,17 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, } time(&t); - tm = localtime(&t); - strftime(timebuf, sizeof(timebuf), "%Y%m%d-%H%M%S", tm); +#if defined(WIN32) || defined(_WIN32) + struct tm tmp; + tm = localtime_s(&tmp, &t) == 0 ? &tmp : NULL; +#else + struct tm tmp; + tm = localtime_r(&t, &tmp); +#endif + if (tm) + strftime(timebuf, sizeof(timebuf), "%Y%m%d-%H%M%S", tm); + else + timebuf[0] = '\0'; lws_strncpy(cert_ts, cert_latest, sizeof(cert_ts)); p = (char *)strstr(cert_ts, "-latest.crt"); diff --git a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c index 3b574555ec..3a1806401a 100644 --- a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c +++ b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c @@ -2334,8 +2334,17 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, char cookie_hdr3[256], cookie_hdr3_host[256]; char exp[64]; time_t t = 0; - struct tm *tm = gmtime(&t); - strftime(exp, sizeof(exp), "%a, %d %b %Y %H:%M:%S GMT", tm); +#if defined(WIN32) || defined(_WIN32) + struct tm tmp; + struct tm *tm = gmtime_s(&tmp, &t) == 0 ? &tmp : NULL; +#else + struct tm tmp; + struct tm *tm = gmtime_r(&t, &tmp); +#endif + if (tm) + strftime(exp, sizeof(exp), "%a, %d %b %Y %H:%M:%S GMT", tm); + else + exp[0] = '\0'; if (vhd->cookie_domain[0]) { lws_snprintf(cookie_hdr1, sizeof(cookie_hdr1), "%s=; Path=/; Domain=%s; Expires=%s; Max-Age=0; HttpOnly; SameSite=None; Secure", vhd->cookie_name, vhd->cookie_domain, exp); @@ -2559,8 +2568,17 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, char cookie_hdr1[256], cookie_hdr1_host[256], cookie_hdr2[256], cookie_hdr2_host[256], cookie_hdr3[256], cookie_hdr3_host[256]; char exp[64]; time_t t = 0; - struct tm *tm = gmtime(&t); - strftime(exp, sizeof(exp), "%a, %d %b %Y %H:%M:%S GMT", tm); +#if defined(WIN32) || defined(_WIN32) + struct tm tmp; + struct tm *tm = gmtime_s(&tmp, &t) == 0 ? &tmp : NULL; +#else + struct tm tmp; + struct tm *tm = gmtime_r(&t, &tmp); +#endif + if (tm) + strftime(exp, sizeof(exp), "%a, %d %b %Y %H:%M:%S GMT", tm); + else + exp[0] = '\0'; if (vhd->cookie_domain[0]) { lws_snprintf(cookie_hdr1, sizeof(cookie_hdr1), "%s=; Path=/; Domain=%s; Expires=%s; Max-Age=0; HttpOnly; SameSite=None; Secure", vhd->cookie_name, vhd->cookie_domain, exp); diff --git a/plugins/protocol_lws_cert_dist_server/protocol_lws_cert_dist_server.c b/plugins/protocol_lws_cert_dist_server/protocol_lws_cert_dist_server.c index 99cbb7a58d..63d48a51f7 100644 --- a/plugins/protocol_lws_cert_dist_server/protocol_lws_cert_dist_server.c +++ b/plugins/protocol_lws_cert_dist_server/protocol_lws_cert_dist_server.c @@ -80,12 +80,15 @@ read_newest_file_in_dir(const char *dirpath, const char *suffix) lws_snprintf(path, sizeof(path), "%s/%s", dirpath, best_name); fd = open(path, O_RDONLY); if (fd >= 0) { - if (fstat(fd, &st) == 0 && (buf = malloc((size_t)st.st_size + 1))) { - if (read(fd, buf, (size_t)st.st_size) == (ssize_t)st.st_size) - buf[st.st_size] = '\0'; - else { - free(buf); - buf = NULL; + if (fstat(fd, &st) == 0) { + buf = malloc((size_t)st.st_size + 1); + if (buf) { + if (read(fd, buf, (size_t)st.st_size) == (ssize_t)st.st_size) + buf[st.st_size] = '\0'; + else { + free(buf); + buf = NULL; + } } } close(fd); diff --git a/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c b/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c index 09717c48c4..130dc84ffa 100644 --- a/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c +++ b/plugins/protocol_lws_dht_dnssec/protocol_lws_dht_dnssec.c @@ -3794,9 +3794,18 @@ int lws_dht_dnssec_bump_zone_serial(struct lws_context *context, const char *fil old_serial[serial_len] = '\0'; time_t t = time(NULL); - struct tm *tm = gmtime(&t); +#if defined(WIN32) || defined(_WIN32) + struct tm tmp; + struct tm *tm = gmtime_s(&tmp, &t) == 0 ? &tmp : NULL; +#else + struct tm tmp; + struct tm *tm = gmtime_r(&t, &tmp); +#endif char new_date[16]; - lws_snprintf(new_date, sizeof(new_date), "%04d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + if (tm) + lws_snprintf(new_date, sizeof(new_date), "%04d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + else + new_date[0] = '\0'; char new_serial[32]; if (strncmp(old_serial, new_date, 8) == 0 && serial_len >= 10) { diff --git a/plugins/protocol_lws_login/protocol_lws_login.c b/plugins/protocol_lws_login/protocol_lws_login.c index 7410ee171b..113f7a2308 100644 --- a/plugins/protocol_lws_login/protocol_lws_login.c +++ b/plugins/protocol_lws_login/protocol_lws_login.c @@ -1163,8 +1163,17 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason, char cookie_hdr1[256], cookie_hdr1_host[256]; char exp[64]; time_t t = 0; - struct tm *tm = gmtime(&t); - strftime(exp, sizeof(exp), "%a, %d %b %Y %H:%M:%S GMT", tm); +#if defined(WIN32) || defined(_WIN32) + struct tm tmp; + struct tm *tm = gmtime_s(&tmp, &t) == 0 ? &tmp : NULL; +#else + struct tm tmp; + struct tm *tm = gmtime_r(&t, &tmp); +#endif + if (tm) + strftime(exp, sizeof(exp), "%a, %d %b %Y %H:%M:%S GMT", tm); + else + exp[0] = '\0'; redirect_uri[0] = '\0'; if (lws_get_urlarg_by_name_safe(wsi, "redirect_uri=", redirect_uri, sizeof(redirect_uri)) >= 0) diff --git a/plugins/protocol_lws_mirror/protocol_lws_mirror.c b/plugins/protocol_lws_mirror/protocol_lws_mirror.c index d86f596ceb..3c549ba495 100644 --- a/plugins/protocol_lws_mirror/protocol_lws_mirror.c +++ b/plugins/protocol_lws_mirror/protocol_lws_mirror.c @@ -291,10 +291,6 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason, lws_pthread_mutex_unlock(&v->lock); /* } vhost lock */ break; -bail1: - lws_pthread_mutex_unlock(&v->lock); /* } vhost lock */ - return 1; - case LWS_CALLBACK_CLOSED: /* detach our pss from the mirror instance */ mi = pss->mi; @@ -474,6 +470,10 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason, } return 0; + +bail1: + lws_pthread_mutex_unlock(&v->lock); /* } vhost lock */ + return 1; } #define LWS_PLUGIN_PROTOCOL_MIRROR { \ diff --git a/plugins/protocol_urlarg/protocol_urlarg.c b/plugins/protocol_urlarg/protocol_urlarg.c index 243398ab2e..20af41d1fd 100644 --- a/plugins/protocol_urlarg/protocol_urlarg.c +++ b/plugins/protocol_urlarg/protocol_urlarg.c @@ -101,12 +101,7 @@ callback_lws_urlarg(struct lws *wsi, enum lws_callback_reasons reason, pss->wlen = pss->alen; - -bail: - if (lws_http_transaction_completed(wsi)) - return -1; - - return 0; + goto bail; } break; @@ -115,6 +110,12 @@ callback_lws_urlarg(struct lws *wsi, enum lws_callback_reasons reason, } return 0; + +bail: + if (lws_http_transaction_completed(wsi)) + return -1; + + return 0; } #define LWS_PLUGIN_PROTOCOL_URLARG { \ diff --git a/win32port/win32helpers/getopt.c b/win32port/win32helpers/getopt.c index 7e11b3b983..8fa0b33929 100644 --- a/win32port/win32helpers/getopt.c +++ b/win32port/win32helpers/getopt.c @@ -99,19 +99,27 @@ getopt(nargc, nargv, ostr) if (optreset || !*place) { /* update scanning pointer */ optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { + if (optind >= nargc) { place = EMSG; return (-1); } - if (place[1] && *++place == '-' /* found "--" */ - && place[1] == '\0') { - ++optind; + place = nargv[optind]; + if (*place != '-') { place = EMSG; return (-1); } + if (place[1]) { + place++; + if (*place == '-' && place[1] == '\0') { + ++optind; + place = EMSG; + return (-1); + } + } } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = (char *)strchr(ostr, optopt))) { + optopt = (int)*place++; + oli = (char *)strchr(ostr, optopt); + if (optopt == (int)':' || !oli) { /* * if the user didn't specify '-' as an option, * assume it means -1. diff --git a/win32port/win32helpers/getopt_long.c b/win32port/win32helpers/getopt_long.c index 840fa75594..c971c1bd44 100644 --- a/win32port/win32helpers/getopt_long.c +++ b/win32port/win32helpers/getopt_long.c @@ -91,18 +91,27 @@ getopt_internal(nargc, nargv, ostr) if (optreset || !*place) { /* update scanning pointer */ optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { + if (optind >= nargc) { place = EMSG; return (-1); } - if (place[1] && *++place == '-') { /* found "--" */ - /* ++optind; */ + place = nargv[optind]; + if (*place != '-') { place = EMSG; - return (-2); + return (-1); + } + if (place[1]) { + place++; + if (*place == '-') { /* found "--" */ + /* ++optind; */ + place = EMSG; + return (-2); + } } } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = (char *)strchr(ostr, optopt))) { + optopt = (int)*place++; + oli = (char *)strchr(ostr, optopt); + if (optopt == (int)':' || !oli) { /* * if the user didn't specify '-' as an option, * assume it means -1. From 42260e282c0e417e1784dc7b17ea8f9dab952b0a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 11 Jul 2026 04:37:15 +0100 Subject: [PATCH 12/44] oauth: use nwsi for protocol --- .../protocol_lws_login/protocol_lws_login.c | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/plugins/protocol_lws_login/protocol_lws_login.c b/plugins/protocol_lws_login/protocol_lws_login.c index 113f7a2308..6826fced09 100644 --- a/plugins/protocol_lws_login/protocol_lws_login.c +++ b/plugins/protocol_lws_login/protocol_lws_login.c @@ -980,9 +980,24 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason, } } - lws_snprintf(fq_uri, sizeof(fq_uri), "%s://%s%s", - lws_is_ssl(wsi) ? "https" : "http", - h ? h : "localhost", path); + { + const char *scheme = "http"; +#if defined(LWS_WITH_CUSTOM_HEADERS) + char proto[16] = ""; + + if (lws_hdr_custom_copy(wsi, proto, sizeof(proto), "x-forwarded-proto:", 18) > 0) { + if (!strcasecmp(proto, "https")) + scheme = "https"; + } else +#endif + if (lws_is_ssl(lws_get_network_wsi(wsi))) { + scheme = "https"; + } + + lws_snprintf(fq_uri, sizeof(fq_uri), "%s://%s%s", + scheme, + h ? h : "localhost", path); + } if (lws_add_http_common_headers(wsi, HTTP_STATUS_FOUND, "text/html", 0, (unsigned char **)&p, (unsigned char *)end)) return 1; if (lws_add_http_header_by_name(wsi, (unsigned char *)"set-cookie:", (unsigned char *)cookie, (int)strlen(cookie), (unsigned char **)&p, (unsigned char *)end)) return 1; @@ -1126,10 +1141,25 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason, } } - lws_snprintf(fq_uri, sizeof(fq_uri), "%s://%s%s", - lws_is_ssl(wsi) ? "https" : "http", - h ? h : "localhost", - path); + { + const char *scheme = "http"; +#if defined(LWS_WITH_CUSTOM_HEADERS) + char proto[16] = ""; + + if (lws_hdr_custom_copy(wsi, proto, sizeof(proto), "x-forwarded-proto:", 18) > 0) { + if (!strcasecmp(proto, "https")) + scheme = "https"; + } else +#endif + if (lws_is_ssl(lws_get_network_wsi(wsi))) { + scheme = "https"; + } + + lws_snprintf(fq_uri, sizeof(fq_uri), "%s://%s%s", + scheme, + h ? h : "localhost", + path); + } lws_urlencode(urlenc_path, fq_uri, sizeof(urlenc_path)); From 09869c4d4d4ff7f5d754fed8c3f4f15f095ce391 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 11 Jul 2026 10:58:40 +0100 Subject: [PATCH 13/44] quic: avoid looping on migration --- lib/roles/quic/ops-quic.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 3cb51305b4..0ccb9361b3 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -3281,6 +3281,19 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) return 1; } wsi->desc.sockfd = LWS_SOCK_INVALID; +#if defined(LWS_WITH_EVENT_LIBS) + if (wsi->a.context->event_loop_ops->evlib_size_wsi) { + memcpy(nwsi->evlib_wsi, wsi->evlib_wsi, wsi->a.context->event_loop_ops->evlib_size_wsi); + memset(wsi->evlib_wsi, 0, wsi->a.context->event_loop_ops->evlib_size_wsi); + + if (!strcmp(wsi->a.context->event_loop_ops->name, "libuv")) { + void **ppwatcher = (void **)nwsi->evlib_wsi; + if (ppwatcher && *ppwatcher) { + *(void **)(*ppwatcher) = nwsi; + } + } + } +#endif if (__insert_wsi_socket_into_fds(wsi->a.context, nwsi)) { lws_pt_unlock(pt); lws_close_free_wsi(nwsi, LWS_CLOSE_STATUS_NOSTATUS, "fd table fail"); @@ -3447,7 +3460,11 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) } /* Inform the H3 role that it negotiated ALPN */ - lws_role_call_alpn_negotiated(wsi, alpn); + if (role && role != &role_ops_quic && + lws_rops_fidx(role, LWS_ROPS_alpn_negotiated)) { + (lws_rops_func_fidx(role, LWS_ROPS_alpn_negotiated)). + alpn_negotiated(wsi, alpn); + } /* We are ready to send headers! */ lws_callback_on_writable(wsi); From 2b42eeea92154bde6b6857ee6faba4d50539fa7c Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 11 Jul 2026 10:57:51 +0100 Subject: [PATCH 14/44] certs: new monitoring and grace --- lib/tls/mbedtls/mbedtls-server.c | 13 +- lib/tls/openssl/openssl-server.c | 12 ++ lib/tls/private-lib-tls.h | 10 ++ lib/tls/tls-network.c | 37 +++++ lib/tls/tls.c | 229 +++++++++++++++++++++++++++++++ 5 files changed, 298 insertions(+), 3 deletions(-) diff --git a/lib/tls/mbedtls/mbedtls-server.c b/lib/tls/mbedtls/mbedtls-server.c index a24e0fe356..71edfdc0f2 100644 --- a/lib/tls/mbedtls/mbedtls-server.c +++ b/lib/tls/mbedtls/mbedtls-server.c @@ -131,9 +131,16 @@ lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, uint8_t *p = NULL; int n; - if ((!cert || !private_key) && (!mem_cert || !mem_privkey)) { - lwsl_notice("%s: no usable input\n", __func__); - return 0; + char resolved_cert[256]; + char resolved_key[256]; + + if (cert && private_key) { + if (lws_tls_resolve_grace_period_certs(vhost->context, cert, private_key, + resolved_cert, sizeof(resolved_cert), + resolved_key, sizeof(resolved_key)) == 0) { + cert = resolved_cert; + private_key = resolved_key; + } } n = (int)lws_tls_generic_cert_checks(vhost, cert, private_key); diff --git a/lib/tls/openssl/openssl-server.c b/lib/tls/openssl/openssl-server.c index ec4843655c..90a7f183e0 100644 --- a/lib/tls/openssl/openssl-server.c +++ b/lib/tls/openssl/openssl-server.c @@ -193,6 +193,18 @@ lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, #if OPENSSL_VERSION_NUMBER >= 0x10100000L int ret; #endif + char resolved_cert[256]; + char resolved_key[256]; + + if (cert && private_key) { + if (lws_tls_resolve_grace_period_certs(vhost->context, cert, private_key, + resolved_cert, sizeof(resolved_cert), + resolved_key, sizeof(resolved_key)) == 0) { + cert = resolved_cert; + private_key = resolved_key; + } + } + int n = (int)lws_tls_generic_cert_checks(vhost, cert, private_key), m; if (!cert && !private_key) diff --git a/lib/tls/private-lib-tls.h b/lib/tls/private-lib-tls.h index 6f2c0498ec..fc7fb18365 100644 --- a/lib/tls/private-lib-tls.h +++ b/lib/tls/private-lib-tls.h @@ -320,6 +320,16 @@ lws_tls_session_tag_discrete(const char *vhname, const char *host, int lws_tls_session_tag_from_wsi(struct lws *wsi, char *buf, size_t len); +int +lws_tls_resolve_grace_period_certs(struct lws_context *context, + const char *certpath, const char *keypath, + char *resolved_cert, size_t resolved_cert_len, + char *resolved_key, size_t resolved_key_len); + +int +lws_tls_cert_get_x509_validity(struct lws_context *context, const char *filepath, + time_t *not_before, time_t *not_after); + #else /* ! WITH_TLS */ #define lws_tls_restrict_borrow(xxx) (0) diff --git a/lib/tls/tls-network.c b/lib/tls/tls-network.c index b047c983fc..a17982e598 100644 --- a/lib/tls/tls-network.c +++ b/lib/tls/tls-network.c @@ -163,6 +163,43 @@ lws_tls_check_cert_lifetime(struct lws_vhost *v) union lws_tls_cert_info_results ir; int n; + /* Check if the active cert context needs rotation under grace period policy */ + if (v->tls.ssl_ctx && v->tls.cfg_alloc_cert_path && v->tls.cfg_key_path && + (strstr(v->tls.cfg_alloc_cert_path, "-latest.crt") || + strstr(v->tls.cfg_alloc_cert_path, "-latest-fullchain.crt"))) { + char resolved_cert[256]; + char resolved_key[256]; + time_t resolved_from = 0, resolved_to = 0; + union lws_tls_cert_info_results loaded_from, loaded_to; + + /* Resolve what certificate path should be active right now */ + if (lws_tls_resolve_grace_period_certs(v->context, + v->tls.cfg_alloc_cert_path, + v->tls.cfg_key_path, + resolved_cert, sizeof(resolved_cert), + resolved_key, sizeof(resolved_key)) == 0) { + /* Get validity of resolved cert file */ + if (lws_tls_cert_get_x509_validity(v->context, resolved_cert, + &resolved_from, &resolved_to) == 0) { + /* Get validity of currently loaded cert context */ + if (lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_FROM, + &loaded_from, 0) == 0 && + lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, + &loaded_to, 0) == 0) { + if (resolved_from != loaded_from.time || + resolved_to != loaded_to.time) { + lwsl_notice("%s: Active certificate for vhost %s is out of date. Rotating dynamically.\n", + __func__, v->name); + lws_tls_cert_updated(v->context, + v->tls.cfg_alloc_cert_path, + v->tls.cfg_key_path, + NULL, 0, NULL, 0); + } + } + } + } + } + if (v->tls.ssl_ctx && !v->tls.skipped_certs) { if (now < 1542933698) /* Nov 23 2018 00:42 UTC */ diff --git a/lib/tls/tls.c b/lib/tls/tls.c index 2f388367bb..535e659fa6 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -747,3 +747,232 @@ lws_tls_cert_get_x509_remaining(struct lws_context *context, const char *filepat return res; } + +int +lws_tls_cert_get_x509_validity(struct lws_context *context, const char *filepath, + time_t *not_before, time_t *not_after) +{ + struct lws_x509_cert *x = NULL; + union lws_tls_cert_info_results cri, cri1; + uint8_t *p; + lws_filepos_t amount; + int res = -1; + + if (alloc_file(context, filepath, &p, &amount)) + return 1; + + p[amount] = '\0'; + + if (lws_x509_create(&x)) + goto bail; + + if (lws_x509_parse_from_pem(x, p, (size_t)amount)) + goto bail_destroy; + + if (!lws_x509_info(x, LWS_TLS_CERT_INFO_VALIDITY_FROM, &cri, 0) && + !lws_x509_info(x, LWS_TLS_CERT_INFO_VALIDITY_TO, &cri1, 0)) { + if (not_before) + *not_before = cri.time; + if (not_after) + *not_after = cri1.time; + res = 0; + } + +bail_destroy: + lws_x509_destroy(&x); +bail: + lws_free(p); + + return res; +} + +struct versioned_certs_scan { + const char *prefix; + const char *suffix; + char newest[256]; + char previous[256]; + int count; +}; + +static int +lws_tls_versioned_certs_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +{ + struct versioned_certs_scan *scan = (struct versioned_certs_scan *)user; + size_t len_prefix = strlen(scan->prefix); + size_t len_name = strlen(lde->name); + size_t len_suffix = strlen(scan->suffix); + + (void)dirpath; + + /* Filter files that start with prefix, end with suffix, and have YYYYMMDD-HHMMSS in between */ + if (lde->type == LDOT_FILE && + len_name == len_prefix + 16 + len_suffix && + !strncmp(lde->name, scan->prefix, len_prefix) && + !strcmp(lde->name + len_name - len_suffix, scan->suffix) && + lde->name[len_prefix] == '-') { + /* Check timestamp format: -YYYYMMDD-HHMMSS */ + const char *ts = lde->name + len_prefix + 1; + int i; + int ok = 1; + for (i = 0; i < 15; i++) { + if (i == 8) { + if (ts[i] != '-') { ok = 0; break; } + } else { + if (ts[i] < '0' || ts[i] > '9') { ok = 0; break; } + } + } + if (ok) { + /* Since lws_dir sorts alphabetically, each match is newer than the previous one */ + lws_strncpy(scan->previous, scan->newest, sizeof(scan->previous)); + lws_strncpy(scan->newest, lde->name, sizeof(scan->newest)); + scan->count++; + } + } + return 0; +} + +static void +lws_tls_find_versioned_certs(const char *filepath, char *dirpath, size_t dirpath_len, + char *newest, size_t newest_len, + char *previous, size_t previous_len) +{ + struct versioned_certs_scan scan; + char file_prefix[128]; + const char *suffix = NULL; + const char *p; + + newest[0] = '\0'; + previous[0] = '\0'; + dirpath[0] = '\0'; + + /* Find last separator to split path into directory and file */ + p = strrchr(filepath, '/'); +#if defined(WIN32) + if (!p) + p = strrchr(filepath, '\\'); +#endif + if (!p) + return; + + lws_strncpy(dirpath, filepath, lws_ptr_diff_size_t(p, filepath) + 2); + + /* Determine suffix */ + if (strstr(p, "-latest-fullchain.crt")) { + suffix = "-fullchain.crt"; + } else if (strstr(p, "-latest.crt")) { + suffix = ".crt"; + } else if (strstr(p, "-latest.key")) { + suffix = ".key"; + } else { + /* Not a versioned path suffix we support */ + return; + } + + /* Extract prefix */ + p++; /* skip separator */ + const char *latest_ptr = strstr(p, "-latest"); + if (!latest_ptr || lws_ptr_diff_size_t(latest_ptr, p) >= sizeof(file_prefix)) + return; + + lws_strncpy(file_prefix, p, lws_ptr_diff_size_t(latest_ptr, p) + 1); + + memset(&scan, 0, sizeof(scan)); + scan.prefix = file_prefix; + scan.suffix = suffix; + + lws_dir(dirpath, &scan, lws_tls_versioned_certs_cb); + + if (scan.newest[0]) + lws_snprintf(newest, newest_len, "%s%s", dirpath, scan.newest); + if (scan.previous[0]) + lws_snprintf(previous, previous_len, "%s%s", dirpath, scan.previous); +} + +int +lws_tls_resolve_grace_period_certs(struct lws_context *context, + const char *certpath, const char *keypath, + char *resolved_cert, size_t resolved_cert_len, + char *resolved_key, size_t resolved_key_len) +{ + char dirpath_cert[256], newest_cert[256], previous_cert[256]; + char dirpath_key[256], newest_key[256], previous_key[256]; + time_t now = time(NULL); + time_t newest_not_before = 0; + time_t previous_not_after = 0; + lws_system_policy_t *policy = NULL; + char d_path[1024]; + int grace_period = 900; /* 15 mins default */ + int fd_cfg; + + lws_strncpy(resolved_cert, certpath, resolved_cert_len); + lws_strncpy(resolved_key, keypath, resolved_key_len); + + /* Check if it is a versioned path */ + if (!strstr(certpath, "-latest.crt") && !strstr(certpath, "-latest-fullchain.crt")) + return 0; + + lws_tls_find_versioned_certs(certpath, dirpath_cert, sizeof(dirpath_cert), + newest_cert, sizeof(newest_cert), + previous_cert, sizeof(previous_cert)); + + lws_tls_find_versioned_certs(keypath, dirpath_key, sizeof(dirpath_key), + newest_key, sizeof(newest_key), + previous_key, sizeof(previous_key)); + + if (!newest_cert[0] || !newest_key[0]) + return 0; + + if (lws_tls_cert_get_x509_validity(context, newest_cert, &newest_not_before, NULL)) { + /* Failed to read newest cert validity, fallback to newest on disk */ + lws_strncpy(resolved_cert, newest_cert, resolved_cert_len); + lws_strncpy(resolved_key, newest_key, resolved_key_len); + return 0; + } + + lws_snprintf(d_path, sizeof(d_path), "/etc/lwsws/acme/acme_config.json"); + if (lws_system_parse_policy(context, "/etc/lwsws/policy", &policy) == 0 && policy) { + lws_snprintf(d_path, sizeof(d_path), "%s/acme_config.json", policy->dns_base_dir); + lws_system_policy_free(policy); + } + + fd_cfg = open(d_path, O_RDONLY); + if (fd_cfg >= 0) { + char buf[1024]; + ssize_t nr = read(fd_cfg, buf, sizeof(buf) - 1); + if (nr > 0) { + buf[nr] = '\0'; + const char *grace_ptr = strstr(buf, "\"rotation-grace-period\""); + if (!grace_ptr) + grace_ptr = strstr(buf, "\"rotation_grace_period\""); + if (grace_ptr) { + const char *num_ptr = strchr(grace_ptr, ':'); + if (num_ptr) { + num_ptr++; + while (*num_ptr == ' ' || *num_ptr == '\t') + num_ptr++; + if (*num_ptr >= '0' && *num_ptr <= '9') + grace_period = atoi(num_ptr); + } + } + } + close(fd_cfg); + } + + /* If previous cert exists and is still valid, and we are within the grace period of the new cert */ + if (previous_cert[0] && previous_key[0] && + !lws_tls_cert_get_x509_validity(context, previous_cert, NULL, &previous_not_after) && + now < previous_not_after && + now < newest_not_before + grace_period) { + lwsl_notice("%s: Deferring to previous cert %s (grace period: %llds left)\n", + __func__, previous_cert, (long long)(newest_not_before + grace_period - now)); + lws_strncpy(resolved_cert, previous_cert, resolved_cert_len); + lws_strncpy(resolved_key, previous_key, resolved_key_len); + } else { + lwsl_notice("%s: Using newest cert %s\n", __func__, newest_cert); + lws_strncpy(resolved_cert, newest_cert, resolved_cert_len); + lws_strncpy(resolved_key, newest_key, resolved_key_len); + } + + return 0; +} + From 790be247dcc1aa0ab5199e583e2c9d7fadafee47 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 11 Jul 2026 11:41:54 +0100 Subject: [PATCH 15/44] proxy: deliver public URL for redirects not internal proxy target --- lib/roles/http/server/server.c | 38 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index d0ad29b265..4703eab409 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -1506,8 +1506,14 @@ lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit, pslash + 1, uri_ptr + hit->mountpoint_len) - 1; lws_clean_url(rpath); n = (int)strlen(rpath); - if (n && rpath[n - 1] == '/') - n--; + { + int orig_had_slash = 0; + int u_len = (int)strlen(uri_ptr); + if (u_len && uri_ptr[u_len - 1] == '/') + orig_had_slash = 1; + if (!orig_had_slash && n && rpath[n - 1] == '/') + n--; + } na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS); if (na) { @@ -1714,6 +1720,7 @@ lws_http_redirect_hit(struct lws_context_per_thread *pt, struct lws *wsi, if ((hit->mountpoint_len > 1 || (hit->origin_protocol == LWSMPRO_REDIR_HTTP || hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && + (uri_ptr[0] && uri_ptr[strlen(uri_ptr) - 1] != '/') && (*s != '/' || (hit->origin_protocol == LWSMPRO_REDIR_HTTP || hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && @@ -1743,30 +1750,29 @@ lws_http_redirect_hit(struct lws_context_per_thread *pt, struct lws *wsi, oprot[hit->origin_protocol & 1], hit->origin); } else { - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + const char *host_hdr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST); #if defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3) - if (!lws_hdr_total_length(wsi, - WSI_TOKEN_HTTP_COLON_AUTHORITY)) -#endif - goto bail_nuke_ah; -#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3) - n = lws_snprintf((char *)end, 256, - "%s%s%s/", oprot[!!lws_is_ssl(wsi)], - lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_COLON_AUTHORITY), - uri_ptr); + if (!host_hdr) { + host_hdr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY); + } #endif - } else + if (!host_hdr) + goto bail_nuke_ah; + + if (host_hdr[0] == '+' || strchr(host_hdr, '/')) { + n = lws_snprintf((char *)end, 256, "%s/", uri_ptr); + } else { n = lws_snprintf((char *)end, 256, "%s%s%s/", oprot[!!lws_is_ssl(wsi)], - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), - uri_ptr); + host_hdr, uri_ptr); + } } // lwsl_notice("%s: redirecting to '%s' (vhost port %d, peer %s)\n", // __func__, (char *)end, wsi->a.vhost->listen_port, peer_buf); lws_clean_url((char *)end); + n = (int)strlen((char *)end); n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, end, n, &p, end); if ((int)n < 0) From 1f7aef9db9dcb6e642eeedbf5519ee47d2a4c924 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 12 Jul 2026 10:34:23 +0100 Subject: [PATCH 16/44] web-transport: support for qir --- include/libwebsockets/lws-webtransport.h | 9 + lib/core-net/wsi.c | 2 +- lib/roles/h3/ops-h3.c | 256 ++++ lib/roles/http/client/client-http.c | 10 + lib/roles/quic/crypto-quic.c | 1 + lib/roles/quic/ops-quic.c | 55 +- lib/roles/quic/parse-quic.c | 109 +- lib/roles/quic/private-lib-roles-quic.h | 2 +- lib/roles/wt/ops-wt.c | 78 +- .../minimal-webtransport-qir/CMakeLists.txt | 23 + .../minimal-webtransport-qir.c | 1150 +++++++++++++++++ 11 files changed, 1621 insertions(+), 74 deletions(-) create mode 100644 minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt create mode 100644 minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c diff --git a/include/libwebsockets/lws-webtransport.h b/include/libwebsockets/lws-webtransport.h index e593fd68b7..9aac088dd2 100644 --- a/include/libwebsockets/lws-webtransport.h +++ b/include/libwebsockets/lws-webtransport.h @@ -41,5 +41,14 @@ lws_wt_create_stream(struct lws *wsi_session, int unidi); LWS_VISIBLE LWS_EXTERN int lws_wt_is_session(struct lws *wsi); +LWS_VISIBLE LWS_EXTERN int +lws_wt_is_unidi(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_wt_create_stream_from_child(struct lws *child_wsi, int unidi); + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_wt_get_session_wsi(struct lws *wsi); + #endif /* LWS_ROLE_WT */ #endif /* _LWS_WEBTRANSPORT_H */ diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index f5fec8af7d..5ad5ca86f6 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -527,7 +527,7 @@ const struct lws_protocols *lws_get_protocol(struct lws *wsi) { return wsi->a.protocol; } -int lws_ensure_user_space(struct lws *wsi) { +LWS_VISIBLE int lws_ensure_user_space(struct lws *wsi) { if (!wsi->a.protocol) return 0; diff --git a/lib/roles/h3/ops-h3.c b/lib/roles/h3/ops-h3.c index a97899ef33..ccbf850ce4 100644 --- a/lib/roles/h3/ops-h3.c +++ b/lib/roles/h3/ops-h3.c @@ -58,6 +58,10 @@ lws_h3_client_handshake(struct lws *wsi) if (wsi->do_ws) meth = "CONNECT"; +#if defined(LWS_ROLE_WT) + else if (wsi->a.protocol && !strcmp(wsi->a.protocol->name, "webtransport")) + meth = "CONNECT"; +#endif else if (!meth) meth = "GET"; @@ -75,6 +79,13 @@ lws_h3_client_handshake(struct lws *wsi) (unsigned char *)"websocket", 9, &p, end)) return -1; } +#if defined(LWS_ROLE_WT) + else if (wsi->a.protocol && !strcmp(wsi->a.protocol->name, "webtransport")) { + if (lws_add_http3_header_by_token(wsi, WSI_TOKEN_COLON_PROTOCOL, + (unsigned char *)"webtransport", 12, &p, end)) + return -1; + } +#endif if (lws_add_http3_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_SCHEME, (unsigned char *)"https", 5, &p, end)) @@ -549,6 +560,28 @@ lws_h3_qpack_header_cb(void *user, int name_idx, const char *name, size_t name_l return -1; } } else { +#if defined(LWS_WITH_CUSTOM_HEADERS) + struct allocated_headers *ah = wsi->http.ah; + if (ah && name && name_len > 0 && ah->pos + 8 + name_len + value_len < (unsigned int)wsi->a.context->max_http_header_data) { + uint32_t unk_pos = ah->pos; + + lws_ser_wu16be((uint8_t *)&ah->data[unk_pos + 0], (uint16_t)name_len); + lws_ser_wu16be((uint8_t *)&ah->data[unk_pos + 2], (uint16_t)value_len); + lws_ser_wu32be((uint8_t *)&ah->data[unk_pos + 4], 0); + + memcpy(&ah->data[unk_pos + 8], name, name_len); + memcpy(&ah->data[unk_pos + 8 + name_len], value, value_len); + + if (!ah->unk_ll_head) + ah->unk_ll_head = unk_pos; + if (ah->unk_ll_tail) + lws_ser_wu32be((uint8_t *)&ah->data[ah->unk_ll_tail + 4], unk_pos); + ah->unk_ll_tail = unk_pos; + + ah->pos += (ah_data_idx_t)(8 + name_len + value_len); + lwsl_wsi_notice(wsi, "Added H3 custom header: %.*s = %.*s", (int)name_len, name, (int)value_len, value); + } else +#endif lwsl_wsi_debug(wsi, "Ignoring unknown header: %s", name ? name : "unknown"); } @@ -767,6 +800,135 @@ lws_h3_rx_stream_data(struct lws *wsi, const uint8_t *buf, size_t len) // lwsl_notice("H3 RX: %d bytes\n", (int)len); // lwsl_hexdump_notice(buf, len); +#if defined(LWS_ROLE_WT) + extern const struct lws_role_ops role_ops_wt; + struct lws *nwsi = lws_get_quic_network_wsi(wsi); + int has_wt_session = 0; + if (nwsi) { + struct lws *child = nwsi->mux.child_list; + while (child) { + if (child->wt.is_session) { + has_wt_session = 1; + break; + } + child = child->mux.sibling_list; + } + } + + if (has_wt_session) { + if (wsi->quic.qs && wsi->quic.qs->is_unidirectional) { + uint64_t type = 0; + size_t consumed_type = 0; + + if (!wsi->h3.type_set) { + consumed_type = lws_quic_parse_varint(buf, len, &type); + if (!consumed_type) return 0; /* Need more data */ + + if (type == 0x54) { + wsi->h3.stream_type = 0x54; + wsi->h3.type_set = 1; + } + } else if (wsi->h3.stream_type == 0x54) { + type = 0x54; + } + + if (type == 0x54) { + /* We need to parse the Session ID next */ + uint64_t session_id = 0; + size_t consumed_sid = lws_quic_parse_varint(buf + consumed_type, len - consumed_type, &session_id); + if (!consumed_sid) return 0; /* Need more data */ + + /* Find matching WT session */ + struct lws *session_wsi = NULL; + if (nwsi) { + struct lws *child = nwsi->mux.child_list; + while (child) { + if (child->wt.is_session && (child->mux.my_sid / 4 == session_id)) { + session_wsi = child; + break; + } + child = child->mux.sibling_list; + } + } + + if (session_wsi) { + lwsl_notice("Transitioning client-initiated uni stream to WT (session ID %llu)\n", (unsigned long long)session_id); + lws_role_transition(wsi, lwsi_role_client(wsi) ? LWSIFR_CLIENT : LWSIFR_SERVER, LRS_ESTABLISHED, &role_ops_wt); + wsi->wt.is_unidi = 1; + wsi->wt.is_session = 0; + wsi->a.protocol = session_wsi->a.protocol; + + if (lws_ensure_user_space(wsi)) + return 1; + + if (wsi->a.protocol && wsi->a.protocol->callback) { + wsi->a.protocol->callback(wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, wsi->user_space, NULL, 0); + } + + size_t total_consumed = consumed_type + consumed_sid; + if (len > total_consumed) { + wsi->a.protocol->callback(wsi, LWS_CALLBACK_RECEIVE, wsi->user_space, (void *)(buf + total_consumed), len - total_consumed); + } + return 0; + } else { + lwsl_err("WT session WSI not found for session ID %llu\n", (unsigned long long)session_id); + return 1; + } + } + } else if (wsi->quic.qs && !wsi->quic.qs->is_unidirectional && !wsi->h3.type_set) { + uint64_t type = 0; + size_t consumed_type = lws_quic_parse_varint(buf, len, &type); + if (!consumed_type) return 0; /* Need more data */ + + if (type == 0x41) { + uint64_t session_id = 0; + size_t consumed_sid = lws_quic_parse_varint(buf + consumed_type, len - consumed_type, &session_id); + if (!consumed_sid) return 0; /* Need more data */ + + /* Check if it matches an active WT session */ + struct lws *session_wsi = NULL; + if (nwsi) { + struct lws *child = nwsi->mux.child_list; + while (child) { + if (child->wt.is_session && (child->mux.my_sid / 4 == session_id)) { + session_wsi = child; + break; + } + child = child->mux.sibling_list; + } + } + + if (session_wsi) { + lwsl_notice("Transitioning client-initiated bidi stream to WT (session ID %llu)\n", (unsigned long long)session_id); + lws_role_transition(wsi, lwsi_role_client(wsi) ? LWSIFR_CLIENT : LWSIFR_SERVER, LRS_ESTABLISHED, &role_ops_wt); + wsi->wt.is_unidi = 0; + wsi->wt.is_session = 0; + wsi->a.protocol = session_wsi->a.protocol; + + if (lws_ensure_user_space(wsi)) + return 1; + + if (wsi->a.protocol && wsi->a.protocol->callback) { + wsi->a.protocol->callback(wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, wsi->user_space, NULL, 0); + } + + size_t total_consumed = consumed_type + consumed_sid; + if (len > total_consumed) { + wsi->a.protocol->callback(wsi, LWS_CALLBACK_RECEIVE, wsi->user_space, (void *)(buf + total_consumed), len - total_consumed); + } + return 0; + } else { + lwsl_err("WT session WSI not found for session ID %llu\n", (unsigned long long)session_id); + return 1; + } + } else { + /* Not a WT stream, mark type_set to avoid parsing again */ + wsi->h3.type_set = 1; + } + } + } +#endif + /* If it's unidirectional and we don't know the type yet */ if (wsi->quic.qs && wsi->quic.qs->is_unidirectional && !wsi->h3.type_set) { uint64_t type; @@ -1491,11 +1653,105 @@ rops_check_upgrades_h3(struct lws *wsi) #if defined(LWS_ROLE_WT) lwsl_info("Upgrade h3 to wt\n"); extern const struct lws_role_ops role_ops_wt; + unsigned char response_buf[LWS_PRE + 1024], *rp = response_buf + LWS_PRE, *end = response_buf + sizeof(response_buf); + char draft[32]; + char client_protos[256]; + char negotiated[64] = ""; + int draft_len, cp_len; + lws_mux_mark_immortal(wsi); lws_metrics_tag_wsi_add(wsi, "upg", "wt_over_h3"); + /* Construct HTTP/3 response headers for CONNECT upgrade to WebTransport */ + if (lws_add_http_header_status(wsi, 200, &rp, end)) + return LWS_UPG_RET_BAIL; + + /* Copy and send back the draft version */ + draft_len = lws_hdr_custom_copy(wsi, draft, sizeof(draft) - 1, + "sec-webtransport-http3-draft", 28); + if (draft_len > 0) { + draft[draft_len] = '\0'; + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"sec-webtransport-http3-draft:", + (const unsigned char *)draft, draft_len, &rp, end)) + return LWS_UPG_RET_BAIL; + } else { + /* Default to draft02 if not sent */ + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"sec-webtransport-http3-draft:", + (const unsigned char *)"draft02", 7, &rp, end)) + return LWS_UPG_RET_BAIL; + } + + /* Subprotocol negotiation */ + cp_len = lws_hdr_custom_copy(wsi, client_protos, sizeof(client_protos) - 1, + "wt-available-protocols", 22); + if (cp_len > 0) { + const char *env_protocols = getenv("PROTOCOLS_SERVER"); + client_protos[cp_len] = '\0'; + if (!env_protocols) + env_protocols = getenv("PROTOCOLS"); + + /* client_protos format: '"proto1", "proto2"' */ + char *cp_ptr = client_protos; + char *token; + while ((token = strsep(&cp_ptr, ","))) { + while (*token == ' ' || *token == '"') token++; + char *t_end = token + strlen(token); + while (t_end > token && (t_end[-1] == ' ' || t_end[-1] == '"' || t_end[-1] == '\r' || t_end[-1] == '\n')) { + t_end[-1] = '\0'; + t_end--; + } + if (!*token) continue; + + if (env_protocols) { + char server_protos[256]; + lws_strncpy(server_protos, env_protocols, sizeof(server_protos)); + char *sp_ptr = server_protos; + char *sp_tok; + while ((sp_tok = strsep(&sp_ptr, " "))) { + if (strcmp(token, sp_tok) == 0) { + lws_strncpy(negotiated, token, sizeof(negotiated)); + break; + } + } + } + if (negotiated[0]) + break; + } + } + + if (negotiated[0]) { + char wt_prot_val[128]; + int wpl = lws_snprintf(wt_prot_val, sizeof(wt_prot_val), "\"%s\"", negotiated); + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"wt-protocol:", + (const unsigned char *)wt_prot_val, wpl, &rp, end)) + return LWS_UPG_RET_BAIL; + + /* Save negotiated protocol to /downloads/negotiated_protocol.txt */ + mkdir("/downloads", 0777); + int nfd = open("/downloads/negotiated_protocol.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (nfd >= 0) { + if (write(nfd, negotiated, strlen(negotiated)) < 0) { + lwsl_err("Failed to write negotiated protocol\n"); + } + close(nfd); + } + } + + if (lws_finalize_http_header(wsi, &rp, end)) + return LWS_UPG_RET_BAIL; + + if (lws_write(wsi, response_buf + LWS_PRE, lws_ptr_diff_size_t(rp, response_buf + LWS_PRE), LWS_WRITE_HTTP_HEADERS) < 0) + return LWS_UPG_RET_BAIL; + /* Switch role to WebTransport */ lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, &role_ops_wt); + wsi->wt.is_session = 1; + + if (lws_ensure_user_space(wsi)) + return LWS_UPG_RET_BAIL; if (wsi->a.protocol && wsi->a.protocol->callback) { if (wsi->a.protocol->callback(wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, wsi->user_space, NULL, 0)) diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c index b5facb884b..73c2f80725 100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@ -1061,6 +1061,16 @@ lws_client_interpret_server_handshake(struct lws *wsi) if (!wsi->do_ws) { /* we are being an http client... */ +#if defined(LWS_ROLE_WT) + if (wsi->a.protocol && !strcmp(wsi->a.protocol->name, "webtransport")) { + extern const struct lws_role_ops role_ops_wt; + lwsl_debug("%s: %s: transitioning to WebTransport client\n", + __func__, lws_wsi_tag(wsi)); + lws_role_transition(wsi, LWSIFR_CLIENT, + LRS_ESTABLISHED, &role_ops_wt); + wsi->wt.is_session = 1; + } else +#endif #if defined(LWS_ROLE_H2) if (wsi->client_h2_alpn || wsi->client_mux_substream) { lwsl_debug("%s: %s: transitioning to mux client\n", diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index f7841cb9eb..8b08ebf3b6 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -367,6 +367,7 @@ lws_quic_keys_destroy(struct lws_quic_keys *keys) gnutls_aead_cipher_deinit((gnutls_aead_cipher_hd_t)keys->aead_tx); #endif + lws_explicit_bzero(keys, sizeof(*keys)); lws_free(keys); } diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 0ccb9361b3..edf30ed492 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -1065,9 +1065,10 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, break; } - /* 2. Parsing: Safely find the Packet Number offset */ - size_t payload_len_stated; - size_t pn_offset = lws_quic_get_pn_offset(p, (size_t)n, &payload_len_stated); + size_t local_dcid_len = (nwsi && nwsi->quic.qn) ? nwsi->quic.qn->loc_cid.len : 8; + if (local_dcid_len == 0) local_dcid_len = 8; + size_t payload_len_stated = 0; + size_t pn_offset = lws_quic_get_pn_offset(p, (size_t)n, &payload_len_stated, local_dcid_len); if (!pn_offset) { lwsl_wsi_notice(wsi, "QUIC RX: Malformed or truncated packet"); break; @@ -1500,18 +1501,23 @@ rops_handle_POLLOUT_quic(struct lws *wsi) if (!qn) { struct lws *w; + lws_handling_result_t hr_ret = LWS_HP_RET_DROP_POLLOUT; if (wsi->mux.child_list) { w = wsi->mux.child_list; while (w) { struct lws *next = w->mux.sibling_list; if (w->mux.requested_POLLOUT) { w->mux.requested_POLLOUT = 0; - rops_handle_POLLOUT_quic(w); + lws_handling_result_t hr_child = rops_handle_POLLOUT_quic(w); + if (hr_child == LWS_HP_RET_BAIL_DIE) + return LWS_HP_RET_BAIL_DIE; + if (hr_child == LWS_HP_RET_BAIL_OK) + hr_ret = LWS_HP_RET_BAIL_OK; } w = next; } } - return LWS_HP_RET_DROP_POLLOUT; + return hr_ret; } wsi->mux.requested_POLLOUT = 0; @@ -2219,9 +2225,9 @@ rops_handle_POLLOUT_quic(struct lws *wsi) { struct lws *curr = wsi->mux.child_list; int sanity = 1000000; - lwsl_debug("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); + lwsl_notice("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); while (curr && sanity--) { - lwsl_debug("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", + lwsl_notice("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", lws_wsi_tag(curr), curr->mux.requested_POLLOUT, (int)curr->txc.tx_cr); curr = curr->mux.sibling_list; } @@ -2238,7 +2244,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) wa = &(*wsi2)->mux.sibling_list; - lwsl_debug("QUIC TX POLLOUT: visiting child %s, requested_POLLOUT=%d\n", lws_wsi_tag(*wsi2), (*wsi2)->mux.requested_POLLOUT); + lwsl_notice("QUIC TX POLLOUT: visiting child %s, requested_POLLOUT=%d\n", lws_wsi_tag(*wsi2), (*wsi2)->mux.requested_POLLOUT); if (!(*wsi2)->mux.requested_POLLOUT) goto next_child; @@ -2331,6 +2337,11 @@ rops_handle_POLLOUT_quic(struct lws *wsi) int can_process_children = ((qn->handshake_done || qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED || qn->early_data_status == LWS_0RTT_STATUS_ACCEPTED) && wsi->txc.tx_cr > 0 && (!nwsi || nwsi->txc.tx_cr > 0)); int have_pending_tx = 0; for (level = 0; level < LWS_QUIC_LEVEL_COUNT; level++) { + if (qn->pending_tx[level].count) { + lwsl_notice("AGY-DEBUG: pending_tx[%d].count = %d, keys = %p, hp_len = %d\n", + level, qn->pending_tx[level].count, qn->keys[level], + qn->keys[level] ? (int)qn->keys[level]->el_hp_tx.len : -1); + } if (qn->pending_tx[level].count && qn->keys[level] && qn->keys[level]->el_hp_tx.len) { if (level == LWS_QUIC_LEVEL_APP && qn->migration_probing_wsi) { lws_start_foreach_dll(struct lws_dll2 *, d, qn->pending_tx[level].head) { @@ -2349,11 +2360,16 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } } - if (!blocked && can_process_children && !have_pending_tx) { + if (!blocked && can_process_children) { if (lws_wsi_mux_action_pending_writeable_reqs(wsi)) return LWS_HP_RET_BAIL_DIE; for (level = 0; level < LWS_QUIC_LEVEL_COUNT; level++) { + if (qn->pending_tx[level].count) { + lwsl_notice("AGY-DEBUG2: pending_tx[%d].count = %d, keys = %p, hp_len = %d\n", + level, qn->pending_tx[level].count, qn->keys[level], + qn->keys[level] ? (int)qn->keys[level]->el_hp_tx.len : -1); + } if (qn->pending_tx[level].count && qn->keys[level] && qn->keys[level]->el_hp_tx.len) { if (level == LWS_QUIC_LEVEL_APP && qn->migration_probing_wsi) { lws_start_foreach_dll(struct lws_dll2 *, d, qn->pending_tx[level].head) { @@ -2374,7 +2390,19 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } // lwsl_wsi_notice(wsi, "QUIC TX: blocked=%d, have_pending_tx=%d, can_process_children=%d", blocked, have_pending_tx, can_process_children); - if ((blocked && !eagain_blocked) || !have_pending_tx) { + int children_need_POLLOUT = 0; + { + struct lws *w_child = wsi->mux.child_list; + while (w_child) { + if (w_child->mux.requested_POLLOUT) { + children_need_POLLOUT = 1; + break; + } + w_child = w_child->mux.sibling_list; + } + } + + if (((blocked && !eagain_blocked) || !have_pending_tx) && !children_need_POLLOUT) { lwsl_notice("QUIC TX: dropping POLLOUT manually for %s (blocked=%d, eagain_blocked=%d, have_pending_tx=%d)\n", lws_wsi_tag(wsi), blocked, eagain_blocked, have_pending_tx); /* We are blocked by QUIC limits, or have nothing to send right now. * Stop asking the OS for POLLOUT. We will re-enable it if children @@ -3330,9 +3358,10 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) /* Initialize flow control credits for the new child stream */ int32_t init_cr = nwsi->txc.manual_initial_tx_credit; if (!init_cr) { - if (nwsi->quic.qn && nwsi->quic.qn->peer_initial_max_stream_data_bidi_remote) - init_cr = (int32_t)nwsi->quic.qn->peer_initial_max_stream_data_bidi_remote; - else + if (nwsi->quic.qn && nwsi->quic.qn->peer_initial_max_stream_data_bidi_remote) { + uint64_t val = nwsi->quic.qn->peer_initial_max_stream_data_bidi_remote; + init_cr = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; + } else init_cr = 65535; } wsi->txc.peer_tx_cr_est = init_cr; diff --git a/lib/roles/quic/parse-quic.c b/lib/roles/quic/parse-quic.c index 46e0811690..96bc3ffe82 100644 --- a/lib/roles/quic/parse-quic.c +++ b/lib/roles/quic/parse-quic.c @@ -72,21 +72,21 @@ lws_quic_parse_varint(const uint8_t *buf, size_t len, uint64_t *val) * Returns the offset in bytes, or 0 if the packet is malformed/truncated. */ size_t -lws_quic_get_pn_offset(const uint8_t *buf, size_t len, size_t *payload_len) +lws_quic_get_pn_offset(const uint8_t *buf, size_t len, size_t *payload_len, size_t dcid_len) { size_t pos = 1; uint64_t token_len, p_len; size_t consumed; - uint8_t type, dcid_len, scid_len; + uint8_t type, scid_len; if (len < 6) return 0; if (!(buf[0] & 0x80)) { /* Short Header: pn_offset immediately follows the 1-byte header + DCID */ - /* Assuming an 8-byte DCID for LWS endpoints */ - *payload_len = len - (1 + 8); - return 1 + 8; + if (len < 1 + dcid_len) return 0; + *payload_len = len - (1 + dcid_len); + return 1 + dcid_len; } /* Long Header (Form = 1) */ @@ -246,8 +246,11 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ #endif if (wsi_child && wsi_child->a.protocol && wsi_child->a.protocol->callback) { /* Application Stream Data */ - enum lws_callback_reasons reason = lwsi_role_client(wsi_child) ? - LWS_CALLBACK_QT_CLIENT_RECEIVE : LWS_CALLBACK_QT_SERVER_RECEIVE; + enum lws_callback_reasons reason = + wsi_child->role_ops->rx_cb[lwsi_role_server(wsi_child)] ? + (enum lws_callback_reasons)wsi_child->role_ops->rx_cb[lwsi_role_server(wsi_child)] : + (lwsi_role_client(wsi_child) ? + LWS_CALLBACK_QT_CLIENT_RECEIVE : LWS_CALLBACK_QT_SERVER_RECEIVE); int n = wsi_child->a.protocol->callback(wsi_child, reason, wsi_child->user_space, buf, len); if (n == 0) { /* Data consumed by application, replenish rx credit to generate MAX_DATA! */ @@ -324,8 +327,11 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ } else #endif if (wsi_child->a.protocol && wsi_child->a.protocol->callback) { - enum lws_callback_reasons reason = lwsi_role_client(wsi_child) ? - LWS_CALLBACK_QT_CLIENT_RECEIVE : LWS_CALLBACK_QT_SERVER_RECEIVE; + enum lws_callback_reasons reason = + wsi_child->role_ops->rx_cb[lwsi_role_server(wsi_child)] ? + (enum lws_callback_reasons)wsi_child->role_ops->rx_cb[lwsi_role_server(wsi_child)] : + (lwsi_role_client(wsi_child) ? + LWS_CALLBACK_QT_CLIENT_RECEIVE : LWS_CALLBACK_QT_SERVER_RECEIVE); int n = wsi_child->a.protocol->callback(wsi_child, reason, wsi_child->user_space, c->data, c->len); if (n == 0) { /* Data consumed by application, replenish rx credit to generate MAX_DATA! */ @@ -445,7 +451,7 @@ lws_quic_stream_find(struct lws *nwsi, uint64_t stream_id) while (wsi_child) { if (wsi_child->quic.qs && wsi_child->quic.qs->stream_id == stream_id) return wsi_child; - if (wsi_child->mux.my_sid == (unsigned int)stream_id) + if ((uint64_t)wsi_child->mux.my_sid == stream_id) return wsi_child; wsi_child = wsi_child->mux.sibling_list; } @@ -457,7 +463,7 @@ lws_quic_stream_find(struct lws *nwsi, uint64_t stream_id) while (wsi_child) { if (wsi_child->quic.qs && wsi_child->quic.qs->stream_id == stream_id) return wsi_child; - if (wsi_child->mux.my_sid == (unsigned int)stream_id) + if ((uint64_t)wsi_child->mux.my_sid == stream_id) return wsi_child; wsi_child = wsi_child->mux.sibling_list; } @@ -468,7 +474,7 @@ lws_quic_stream_find(struct lws *nwsi, uint64_t stream_id) while (wsi_child) { if (wsi_child->quic.qs && wsi_child->quic.qs->stream_id == stream_id) return wsi_child; - if (wsi_child->mux.my_sid == (unsigned int)stream_id) + if ((uint64_t)wsi_child->mux.my_sid == stream_id) return wsi_child; wsi_child = wsi_child->mux.sibling_list; } @@ -572,7 +578,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl /* Process the First ACK Range */ uint64_t pn = largest_ack; - if (first_ack_range > pn) { + if (first_ack_range > 4096 || first_ack_range > pn) { lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_FRAME_ENCODING_ERROR, type, 0); return -1; } @@ -582,7 +588,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl pn -= (first_ack_range + 1); /* 5. Additional ACK Ranges */ - if (ack_range_count > (payload_len - pos) / 2) { + if (ack_range_count > 1024 || ack_range_count > (payload_len - pos) / 2) { lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_FRAME_ENCODING_ERROR, type, 0); return -1; } @@ -604,7 +610,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl } pn -= gap + 1; - if (ack_range > pn) { + if (ack_range > 4096 || ack_range > pn) { lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_FRAME_ENCODING_ERROR, type, 0); return -1; } @@ -702,7 +708,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl struct lws *child = nwsi->mux.child_list; while (child) { - if (child->mux.my_sid == stream_id) { + if ((uint64_t)child->mux.my_sid == stream_id) { lwsl_wsi_notice(child, "QUIC RX: Stream closed by peer via RESET_STREAM"); #if defined(LWS_ROLE_H3) if (child->role_ops == &role_ops_h3 && child->quic.qs && child->quic.qs->is_unidirectional) { @@ -801,7 +807,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl lwsl_wsi_info(nwsi, "QUIC RX: Parsed STOP_SENDING! stream_id %llu", (unsigned long long)stream_id); struct lws *child = nwsi->mux.child_list; while (child) { - if (child->mux.my_sid == stream_id) { + if ((uint64_t)child->mux.my_sid == stream_id) { lwsl_wsi_notice(child, "QUIC RX: Stream closed by peer via STOP_SENDING"); lws_close_free_wsi(child, LWS_CLOSE_STATUS_ABNORMAL_CLOSE, "quic stop sending"); break; @@ -1000,31 +1006,18 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl return -1; } lwsl_wsi_info(nwsi, "QUIC RX: Parsed DATAGRAM! len %llu", (unsigned long long)datagram_len); - - /* Pass datagram payload to protocol callback via LWS_CALLBACK_RECEIVE on network wsi - Wait, actually we should use the h3 role's receive processing or just dispatch to user callback. - Since DATAGRAMs in H3 have Quarter Stream IDs, we will just queue them to the nwsi rx chunks, - or just invoke a direct callback. - For now, lws_quic_rx_reassemble to the network wsi's own rx_chunks if it was supported, - but network wsi doesn't have a stream. Let's just create an rx_chunk on nwsi if we can, - or directly call LWS_CALLBACK_RECEIVE if possible. We will call the user callback directly. */ - if (nwsi->role_ops && nwsi->a.protocol && nwsi->a.protocol->callback) { - /* Note: WebTransport datagrams will be dispatched inside H3 role */ - /* We can push this data to a special list or just call rxflow right away */ - if (lws_rops_fidx(nwsi->role_ops, LWS_ROPS_handle_POLLIN)) { - /* Create a dummy rx chunk on a special nwsi datagram queue? - Actually it's better to just call LWS_CALLBACK_RECEIVE on nwsi here. */ - /* But to keep rx flow control, let's just dispatch it. */ + + /* Parse Quarter Session ID for WebTransport */ + uint64_t qsid = 0; + size_t qsid_len = lws_quic_parse_varint(&payload[pos], (size_t)datagram_len, &qsid); + if (qsid_len > 0 && qsid_len <= (size_t)datagram_len) { + uint64_t sid = qsid * 4; + struct lws *wsi_session = lws_quic_stream_find(nwsi, sid); + if (wsi_session && wsi_session->wt.is_session && wsi_session->a.protocol && wsi_session->a.protocol->callback) { + /* Route the datagram payload to the WebTransport session's callback */ + wsi_session->a.protocol->callback(wsi_session, LWS_CALLBACK_RECEIVE, + wsi_session->user_space, &payload[pos + qsid_len], (size_t)(datagram_len - qsid_len)); } - /* For now, direct callback. H3 will intercept this in its rxflow/rx handling */ - /* Let's set a flag or just call LWS_CALLBACK_RECEIVE with a new reason, - * but LWS_CALLBACK_RECEIVE_CLIENT_HTTP works. */ - nwsi->quic.qn->rx_packets_since_update++; /* Just a dummy increment */ - - /* A better way: store in nwsi->quic.qn->rx_crypto_chunks[LWS_QUIC_LEVEL_APP] for datagrams? No. */ - /* We will call lws_quic_rx_reassemble but with wsi_child = nwsi, and qs = NULL? - * Yes, let's add a datagram callback or just use lws_quic_rx_reassemble with is_crypto=2 */ - lws_quic_rx_reassemble(nwsi, nwsi, NULL, 0, &payload[pos], (size_t)datagram_len, 2, LWS_QUIC_LEVEL_APP); } pos += (size_t)datagram_len; @@ -1168,19 +1161,25 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl if (is_bidi) { if (is_remote_initiated) { if (qn->peer_initial_max_stream_data_bidi_remote) { - wsi_child->txc.peer_tx_cr_est = (int32_t)qn->peer_initial_max_stream_data_bidi_remote; - wsi_child->txc.tx_cr = (int32_t)qn->peer_initial_max_stream_data_bidi_remote; + uint64_t val = qn->peer_initial_max_stream_data_bidi_remote; + int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; + wsi_child->txc.peer_tx_cr_est = cval; + wsi_child->txc.tx_cr = cval; } } else { if (qn->peer_initial_max_stream_data_bidi_local) { - wsi_child->txc.peer_tx_cr_est = (int32_t)qn->peer_initial_max_stream_data_bidi_local; - wsi_child->txc.tx_cr = (int32_t)qn->peer_initial_max_stream_data_bidi_local; + uint64_t val = qn->peer_initial_max_stream_data_bidi_local; + int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; + wsi_child->txc.peer_tx_cr_est = cval; + wsi_child->txc.tx_cr = cval; } } } else { if (qn->peer_initial_max_stream_data_uni) { - wsi_child->txc.peer_tx_cr_est = (int32_t)qn->peer_initial_max_stream_data_uni; - wsi_child->txc.tx_cr = (int32_t)qn->peer_initial_max_stream_data_uni; + uint64_t val = qn->peer_initial_max_stream_data_uni; + int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; + wsi_child->txc.peer_tx_cr_est = cval; + wsi_child->txc.tx_cr = cval; } } } @@ -1245,10 +1244,12 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl type == LWS_QUIC_FT_MAX_DATA ? "MAX_DATA" : "DATA_BLOCKED", (unsigned long long)max_data); if (type == LWS_QUIC_FT_MAX_DATA) { - int32_t current_max = (int32_t)(nwsi->quic.qn->tx_conn_offset + (uint64_t)nwsi->txc.tx_cr); - int32_t delta = (int32_t)max_data - current_max; - if (delta > 0) + int64_t current_max = (int64_t)nwsi->quic.qn->tx_conn_offset + nwsi->txc.tx_cr; + if ((int64_t)max_data > current_max) { + int64_t delta64 = (int64_t)max_data - current_max; + int32_t delta = (delta64 > (int64_t)INT32_MAX) ? INT32_MAX : (int32_t)delta64; lws_wsi_tx_credit(nwsi, LWSTXCR_US_TO_PEER, delta); + } } break; } else if (type == LWS_QUIC_FT_MAX_STREAM_DATA || type == LWS_QUIC_FT_STREAM_DATA_BLOCKED) { @@ -1303,10 +1304,12 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl if (type == LWS_QUIC_FT_MAX_STREAM_DATA) { struct lws *child = lws_quic_stream_find(nwsi, stream_id); if (child) { - int32_t current_max = (int32_t)((child->quic.qs ? child->quic.qs->tx_offset : 0) + (uint64_t)child->txc.tx_cr); - int32_t delta = (int32_t)max_stream_data - current_max; - if (delta > 0) + int64_t current_max = (int64_t)(child->quic.qs ? child->quic.qs->tx_offset : 0) + child->txc.tx_cr; + if ((int64_t)max_stream_data > current_max) { + int64_t delta64 = (int64_t)max_stream_data - current_max; + int32_t delta = (delta64 > (int64_t)INT32_MAX) ? INT32_MAX : (int32_t)delta64; lws_wsi_tx_credit(child, LWSTXCR_US_TO_PEER, delta); + } } } break; diff --git a/lib/roles/quic/private-lib-roles-quic.h b/lib/roles/quic/private-lib-roles-quic.h index 0f436707f3..b26071510a 100644 --- a/lib/roles/quic/private-lib-roles-quic.h +++ b/lib/roles/quic/private-lib-roles-quic.h @@ -377,7 +377,7 @@ size_t lws_quic_parse_varint(const uint8_t *buf, size_t len, uint64_t *val); size_t -lws_quic_get_pn_offset(const uint8_t *buf, size_t len, size_t *payload_len); +lws_quic_get_pn_offset(const uint8_t *buf, size_t len, size_t *payload_len, size_t dcid_len); size_t lws_quic_write_varint(uint8_t *buf, size_t len, uint64_t val); diff --git a/lib/roles/wt/ops-wt.c b/lib/roles/wt/ops-wt.c index 5c228b027c..a44d04741f 100644 --- a/lib/roles/wt/ops-wt.c +++ b/lib/roles/wt/ops-wt.c @@ -91,10 +91,13 @@ rops_close_kill_connection_wt(struct lws *wsi, enum lws_close_status reason) return 0; } +struct lws * +lws_get_quic_network_wsi(struct lws *wsi); + LWS_VISIBLE struct lws * lws_wt_create_stream(struct lws *wsi_session, int unidi) { - struct lws *nwsi = lws_get_network_wsi(wsi_session); + struct lws *nwsi = lws_get_quic_network_wsi(wsi_session); struct lws_quic_netconn *qn = nwsi ? nwsi->quic.qn : NULL; struct lws *cwsi; @@ -179,10 +182,73 @@ lws_wt_is_session(struct lws *wsi) return wsi->wt.is_session; } + +LWS_VISIBLE int +lws_wt_is_unidi(struct lws *wsi) +{ + return wsi->quic.qs && wsi->quic.qs->is_unidirectional; +} + +LWS_VISIBLE struct lws * +lws_wt_create_stream_from_child(struct lws *child_wsi, int unidi) +{ + struct lws *quic_nwsi = lws_get_quic_network_wsi(child_wsi); + if (quic_nwsi) { + struct lws *child = quic_nwsi->mux.child_list; + while (child) { + if (child->wt.is_session) { + return lws_wt_create_stream(child, unidi); + } + child = child->mux.sibling_list; + } + } + return NULL; +} + +LWS_VISIBLE struct lws * +lws_wt_get_session_wsi(struct lws *wsi) +{ + struct lws *quic_nwsi = lws_get_quic_network_wsi(wsi); + if (quic_nwsi) { + struct lws *child = quic_nwsi->mux.child_list; + while (child) { + if (child->wt.is_session) { + return child; + } + child = child->mux.sibling_list; + } + } + return NULL; +} + +static int +rops_perform_user_POLLOUT_wt(struct lws *wsi) +{ + return lws_callback_as_writeable(wsi); +} + +static int +rops_callback_on_writable_wt(struct lws *wsi) +{ + struct lws *nwsi = lws_get_network_wsi(wsi); + + lwsl_notice("rops_callback_on_writable_wt called for %s (nwsi=%s)\n", + lws_wsi_tag(wsi), nwsi ? lws_wsi_tag(nwsi) : "none"); + + if (!nwsi) + return 0; + + lws_wsi_mux_mark_parents_needing_writeable(wsi); + + return lws_callback_on_writable(nwsi); +} + static const lws_rops_t rops_table_wt[] = { /* 1 */ { .handle_POLLIN = rops_handle_POLLIN_wt }, /* 2 */ { .write_role_protocol = rops_write_role_protocol_wt }, /* 3 */ { .close_kill_connection = rops_close_kill_connection_wt }, + /* 4 */ { .perform_user_POLLOUT = rops_perform_user_POLLOUT_wt }, + /* 5 */ { .callback_on_writable = rops_callback_on_writable_wt }, }; const struct lws_role_ops role_ops_wt = { @@ -196,17 +262,17 @@ const struct lws_role_ops role_ops_wt = { /* LWS_ROPS_init_vhost */ /* LWS_ROPS_destroy_vhost */ 0x00, /* LWS_ROPS_service_flag_pending */ - /* LWS_ROPS_handle_POLLIN */ 0x10, + /* LWS_ROPS_handle_POLLIN */ 0x01, /* LWS_ROPS_handle_POLLOUT */ - /* LWS_ROPS_perform_user_POLLOUT */ 0x00, + /* LWS_ROPS_perform_user_POLLOUT */ 0x04, /* LWS_ROPS_callback_on_writable */ - /* LWS_ROPS_tx_credit */ 0x00, + /* LWS_ROPS_tx_credit */ 0x50, /* LWS_ROPS_write_role_protocol */ - /* LWS_ROPS_encapsulation_parent */ 0x02, + /* LWS_ROPS_encapsulation_parent */ 0x20, /* LWS_ROPS_alpn_negotiated */ /* LWS_ROPS_close_via_role_protocol */ 0x00, /* LWS_ROPS_close_role */ - /* LWS_ROPS_close_kill_connection */ 0x30, + /* LWS_ROPS_close_kill_connection */ 0x03, /* LWS_ROPS_destroy_role */ /* LWS_ROPS_adoption_bind */ 0x00, /* LWS_ROPS_client_bind */ diff --git a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt new file mode 100644 index 0000000000..d2f45cdde6 --- /dev/null +++ b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt @@ -0,0 +1,23 @@ +project(lws-minimal-webtransport-qir C) +cmake_minimum_required(VERSION 3.10) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-minimal-webtransport-qir) +set(SRCS minimal-webtransport-qir.c) + +set(requirements 1) +require_lws_config(LWS_ROLE_WT 1 requirements) +require_lws_config(LWS_WITH_HTTP3 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() +endif() diff --git a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c new file mode 100644 index 0000000000..0215b8f496 --- /dev/null +++ b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c @@ -0,0 +1,1150 @@ +/* + * lws-minimal-webtransport-qir + * + * Written in 2010-2026 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * WebTransport Interop Runner shim implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int lws_ensure_user_space(struct lws *wsi); + +static int interrupted; +static int is_server; +static char testcase[64] = ""; +static char requests_env[2048] = ""; +static struct lws_context *context; +static struct lws *first_session_wsi; + +struct request_item { + char url[512]; + char host[128]; + int port; + char endpoint[128]; + char filename[128]; + struct lws *session_wsi; + int started; + int completed; +}; + +static struct request_item client_requests[64]; +static int client_requests_count; + +struct server_request_item { + char endpoint[128]; + char filename[128]; + int started; +}; + +static struct server_request_item server_requests[64]; +static int server_requests_count; + +struct pss_qir { + struct lws *wsi; + int is_session; + char endpoint[128]; + + /* For child streams */ + int is_unidi; + int is_initiator; /* 1 if we sent GET, 0 if we received GET */ + char filename[256]; + int request_index; + + /* Send state */ + int fd_in; + size_t file_len; + size_t sent_len; + int header_sent; + + /* Receive state */ + int fd_out; + char push_hdr[512]; + size_t push_hdr_len; + size_t push_hdr_read; + int push_hdr_done; + int initialized; + int write_completed; +}; + +static void init_pss(struct pss_qir *pss) +{ + if (pss && !pss->initialized) { + pss->fd_in = -1; + pss->fd_out = -1; + pss->request_index = -1; + pss->write_completed = 0; + pss->initialized = 1; + } +} + +static int pss_is_file_sender(struct pss_qir *pss) +{ + if (pss->is_unidi) { + if (strstr(testcase, "unidirectional-receive")) { + return is_server && !pss->is_initiator; + } + if (strstr(testcase, "unidirectional-send")) { + return !is_server && pss->is_initiator; + } + } else { + if (strstr(testcase, "bidirectional-receive")) { + return is_server; + } + if (strstr(testcase, "bidirectional-send")) { + return !is_server; + } + } + return 0; +} + +static int pss_is_file_receiver(struct pss_qir *pss) +{ + if (pss->is_unidi) { + if (strstr(testcase, "unidirectional-receive")) { + return !is_server && !pss->is_initiator; + } + if (strstr(testcase, "unidirectional-send")) { + return is_server && !pss->is_initiator; + } + } else { + if (strstr(testcase, "bidirectional-receive")) { + return !is_server; + } + if (strstr(testcase, "bidirectional-send")) { + return is_server; + } + } + return 0; +} + +#if defined(LWS_WITH_CUSTOM_HEADERS) +static void print_custom_header_cb(const char *name, int nlen, void *custom) +{ + struct lws *wsi = (struct lws *)custom; + char val[128]; + int vl = lws_hdr_custom_copy(wsi, val, sizeof(val) - 1, name, nlen); + if (vl >= 0) { + val[vl] = '\0'; + lwsl_user(" Custom Header: %.*s = %s\n", nlen, name, val); + } +} +#endif + + +static void sigint_handler(int sig) +{ + interrupted = 1; +} + +static int parse_client_requests(void) +{ + const char *reqs = getenv("REQUESTS_CLIENT"); + if (!reqs) + reqs = getenv("REQUESTS"); + if (!reqs) + return 0; + + lws_strncpy(requests_env, reqs, sizeof(requests_env)); + char *r_ptr = requests_env; + char *token; + + while ((token = strsep(&r_ptr, " "))) { + if (!*token) + continue; + + struct request_item *item = &client_requests[client_requests_count]; + lws_strncpy(item->url, token, sizeof(item->url)); + + /* Parse URL: https://:// */ + char *p = token; + if (strncmp(p, "https://", 8) == 0) + p += 8; + + char *host_start = p; + char *slash = strchr(p, '/'); + if (!slash) + continue; + + *slash = '\0'; + char *port_colon = strchr(host_start, ':'); + if (port_colon) { + *port_colon = '\0'; + item->port = atoi(port_colon + 1); + } else { + item->port = 443; + } + lws_strncpy(item->host, host_start, sizeof(item->host)); + *slash = '/'; + + p = slash + 1; + char *next_slash = strchr(p, '/'); + if (next_slash) { + *next_slash = '\0'; + lws_snprintf(item->endpoint, sizeof(item->endpoint), "/%s", p); + *next_slash = '/'; + lws_strncpy(item->filename, next_slash + 1, sizeof(item->filename)); + } else { + lws_snprintf(item->endpoint, sizeof(item->endpoint), "/%s", p); + item->filename[0] = '\0'; + } + + client_requests_count++; + if (client_requests_count >= (int)LWS_ARRAY_SIZE(client_requests)) + break; + } + + /* For bidirectional-send, we also need to parse files from REQUESTS_SERVER */ + if (strstr(testcase, "bidirectional-send")) { + const char *sreqs = getenv("REQUESTS_SERVER"); + if (sreqs && client_requests_count == 1 && client_requests[0].filename[0] == '\0') { + char sreqs_env[1024]; + lws_strncpy(sreqs_env, sreqs, sizeof(sreqs_env)); + char *sr_ptr = sreqs_env; + char *stoken; + int first = 1; + + while ((stoken = strsep(&sr_ptr, " "))) { + if (!*stoken) + continue; + + /* format: / */ + char *slash = strchr(stoken, '/'); + if (!slash) + continue; + + *slash = '\0'; + char filename[256]; + lws_strncpy(filename, slash + 1, sizeof(filename)); + *slash = '/'; + + if (first) { + lws_strncpy(client_requests[0].filename, filename, sizeof(client_requests[0].filename)); + first = 0; + } else { + struct request_item *item = &client_requests[client_requests_count]; + item->port = client_requests[0].port; + lws_strncpy(item->host, client_requests[0].host, sizeof(item->host)); + lws_strncpy(item->endpoint, client_requests[0].endpoint, sizeof(item->endpoint)); + lws_strncpy(item->filename, filename, sizeof(item->filename)); + client_requests_count++; + if (client_requests_count >= (int)LWS_ARRAY_SIZE(client_requests)) + break; + } + } + } + } + + return client_requests_count; +} + +static int parse_server_requests(void) +{ + const char *reqs = getenv("REQUESTS_SERVER"); + if (!reqs) + reqs = getenv("REQUESTS"); + if (!reqs) + return 0; + + lws_strncpy(requests_env, reqs, sizeof(requests_env)); + char *r_ptr = requests_env; + char *token; + + while ((token = strsep(&r_ptr, " "))) { + if (!*token) + continue; + + struct server_request_item *item = &server_requests[server_requests_count]; + + /* format: / */ + char *slash = strchr(token, '/'); + if (!slash) + continue; + + *slash = '\0'; + lws_snprintf(item->endpoint, sizeof(item->endpoint), "/%s", token); + *slash = '/'; + lws_strncpy(item->filename, slash + 1, sizeof(item->filename)); + + server_requests_count++; + if (server_requests_count >= (int)LWS_ARRAY_SIZE(server_requests)) + break; + } + + return server_requests_count; +} + +static void trigger_client_transfers(struct lws *wsi_session, const char *endpoint) +{ + int i; + lwsl_user("trigger_client_transfers called for endpoint %s, client_requests_count=%d\n", endpoint, client_requests_count); + for (i = 0; i < client_requests_count; i++) { + struct request_item *item = &client_requests[i]; + lwsl_user(" Client Request %d: url=%s endpoint=%s started=%d filename=%s\n", i, item->url, item->endpoint, item->started, item->filename); + if (strcmp(item->endpoint, endpoint) == 0 && !item->started && item->filename[0]) { + /* Start the transfer according to the testcase */ + item->started = 1; + lwsl_user(" Triggering request %d (%s) on testcase %s\n", i, item->filename, testcase); + if (strstr(testcase, "unidirectional")) { + struct lws *cwsi = lws_wt_create_stream(wsi_session, 1); + lwsl_user(" lws_wt_create_stream(unidi=1) returned wsi %p\n", cwsi); + if (cwsi) { + int err = lws_ensure_user_space(cwsi); + lwsl_user(" lws_ensure_user_space returned %d\n", err); + if (!err) { + struct pss_qir *pss = (struct pss_qir *)lws_wsi_user(cwsi); + lwsl_user(" pss user space: %p\n", pss); + if (pss) { + init_pss(pss); + pss->is_unidi = 1; + pss->is_initiator = 1; + pss->request_index = i; + lws_strncpy(pss->endpoint, item->endpoint, sizeof(pss->endpoint)); + lws_strncpy(pss->filename, item->filename, sizeof(pss->filename)); + lws_callback_on_writable(cwsi); + lwsl_user(" Requested writable callback for client stream wsi %p\n", cwsi); + } + } + } + } else if (strstr(testcase, "bidirectional")) { + struct lws *cwsi = lws_wt_create_stream(wsi_session, 0); + lwsl_user(" lws_wt_create_stream(unidi=0) returned wsi %p\n", cwsi); + if (cwsi) { + int err = lws_ensure_user_space(cwsi); + lwsl_user(" lws_ensure_user_space returned %d\n", err); + if (!err) { + struct pss_qir *pss = (struct pss_qir *)lws_wsi_user(cwsi); + lwsl_user(" pss user space: %p\n", pss); + if (pss) { + init_pss(pss); + pss->is_unidi = 0; + pss->is_initiator = 1; + pss->request_index = i; + lws_strncpy(pss->endpoint, item->endpoint, sizeof(pss->endpoint)); + lws_strncpy(pss->filename, item->filename, sizeof(pss->filename)); + lws_callback_on_writable(cwsi); + lwsl_user(" Requested writable callback for client stream wsi %p\n", cwsi); + } + } + } + } else if (strstr(testcase, "datagram")) { + if (strstr(testcase, "-send")) { + /* Send PUSH \n as a datagram */ + char filepath[512]; + lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", item->endpoint, item->filename); + int fd = open(filepath, O_RDONLY); + if (fd >= 0) { + struct stat st; + if (fstat(fd, &st) == 0) { + uint8_t dgbuf[LWS_PRE + 65536]; + int hn = lws_snprintf((char *)&dgbuf[LWS_PRE], sizeof(dgbuf) - LWS_PRE, "PUSH %s\n", item->filename); + int rn = (int)read(fd, &dgbuf[LWS_PRE + hn], sizeof(dgbuf) - LWS_PRE - (size_t)hn); + if (rn >= 0) { + enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; + lws_write(wsi_session, &dgbuf[LWS_PRE], (size_t)(hn + rn), wp); + lwsl_user("Client sent datagram PUSH: %s (%d bytes)\n", item->filename, hn + rn); + item->completed = 1; + } + } + close(fd); + } else { + lwsl_err("Failed to open file %s for datagram send\n", filepath); + } + } else { + /* Send GET as a datagram */ + uint8_t buf[LWS_PRE + 512]; + size_t len = (size_t)lws_snprintf((char *)&buf[LWS_PRE], 512, "GET %s", item->filename); + enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; + lws_write(wsi_session, &buf[LWS_PRE], len, wp); + lwsl_user("Client sent datagram: GET %s\n", item->filename); + } + } + } + } +} + +static void trigger_server_transfers(struct lws *wsi_session, const char *endpoint) +{ + int i; + lwsl_user("trigger_server_transfers called for endpoint %s, server_requests_count=%d\n", endpoint, server_requests_count); + for (i = 0; i < server_requests_count; i++) { + struct server_request_item *item = &server_requests[i]; + lwsl_user(" Server Request %d: endpoint=%s started=%d filename=%s\n", i, item->endpoint, item->started, item->filename); + if (strcmp(item->endpoint, endpoint) == 0 && !item->started) { + item->started = 1; + lwsl_user(" Triggering server request %d (%s) on testcase %s\n", i, item->filename, testcase); + if (strstr(testcase, "unidirectional")) { + struct lws *cwsi = lws_wt_create_stream(wsi_session, 1); + lwsl_user(" lws_wt_create_stream(unidi=1) returned wsi %p\n", cwsi); + if (cwsi) { + int err = lws_ensure_user_space(cwsi); + lwsl_user(" lws_ensure_user_space returned %d\n", err); + if (!err) { + struct pss_qir *pss = (struct pss_qir *)lws_wsi_user(cwsi); + lwsl_user(" pss user space: %p\n", pss); + if (pss) { + init_pss(pss); + pss->is_unidi = 1; + pss->is_initiator = 1; + pss->request_index = i; + lws_strncpy(pss->endpoint, item->endpoint, sizeof(pss->endpoint)); + lws_strncpy(pss->filename, item->filename, sizeof(pss->filename)); + lws_callback_on_writable(cwsi); + lwsl_user(" Requested writable callback for server stream wsi %p\n", cwsi); + } + } + } + } else if (strstr(testcase, "bidirectional")) { + struct lws *cwsi = lws_wt_create_stream(wsi_session, 0); + lwsl_user(" lws_wt_create_stream(unidi=0) returned wsi %p\n", cwsi); + if (cwsi) { + int err = lws_ensure_user_space(cwsi); + lwsl_user(" lws_ensure_user_space returned %d\n", err); + if (!err) { + struct pss_qir *pss = (struct pss_qir *)lws_wsi_user(cwsi); + lwsl_user(" pss user space: %p\n", pss); + if (pss) { + init_pss(pss); + pss->is_unidi = 0; + pss->is_initiator = 1; + pss->request_index = i; + lws_strncpy(pss->endpoint, item->endpoint, sizeof(pss->endpoint)); + lws_strncpy(pss->filename, item->filename, sizeof(pss->filename)); + lws_callback_on_writable(cwsi); + lwsl_user(" Requested writable callback for server stream wsi %p\n", cwsi); + } + } + } + } else if (strstr(testcase, "datagram")) { + if (strstr(testcase, "-send")) { + /* Send PUSH \n as a datagram */ + char filepath[512]; + lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", item->endpoint, item->filename); + int fd = open(filepath, O_RDONLY); + if (fd >= 0) { + struct stat st; + if (fstat(fd, &st) == 0) { + uint8_t dgbuf[LWS_PRE + 65536]; + int hn = lws_snprintf((char *)&dgbuf[LWS_PRE], sizeof(dgbuf) - LWS_PRE, "PUSH %s\n", item->filename); + int rn = (int)read(fd, &dgbuf[LWS_PRE + hn], sizeof(dgbuf) - LWS_PRE - (size_t)hn); + if (rn >= 0) { + enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; + lws_write(wsi_session, &dgbuf[LWS_PRE], (size_t)(hn + rn), wp); + lwsl_user("Server sent datagram PUSH: %s (%d bytes)\n", item->filename, hn + rn); + } + } + close(fd); + } + } else { + /* Send GET as a datagram */ + uint8_t buf[LWS_PRE + 512]; + size_t len = (size_t)lws_snprintf((char *)&buf[LWS_PRE], 512, "GET %s", item->filename); + enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; + lws_write(wsi_session, &buf[LWS_PRE], len, wp); + lwsl_user("Server sent datagram: GET %s\n", item->filename); + } + } + } + } +} + +static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct pss_qir *pss = (struct pss_qir *)user; + uint8_t buf[LWS_PRE + 4096], *p; + int n, m; + + if (pss) { + init_pss(pss); + } + + if (!pss && reason != LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER) + return 0; + + switch (reason) { + + case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: + { + unsigned char **hp = (unsigned char **)in, *end = (*hp) + len; + + /* Add sec-webtransport-http3-draft header */ + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"sec-webtransport-http3-draft:", + (const unsigned char *)"draft02", 7, hp, end)) + return -1; + + /* Format and send PROTOCOLS as custom headers */ + const char *env_protocols = getenv("PROTOCOLS_CLIENT"); + if (!env_protocols) + env_protocols = getenv("PROTOCOLS"); + + if (env_protocols) { + char av_buf[512] = ""; + char env_copy[512]; + lws_strncpy(env_copy, env_protocols, sizeof(env_copy)); + char *ec_ptr = env_copy; + char *tok; + int first = 1; + while ((tok = strsep(&ec_ptr, " "))) { + if (!*tok) continue; + if (!first) + strcat(av_buf, ", "); + strcat(av_buf, "\""); + strcat(av_buf, tok); + strcat(av_buf, "\""); + first = 0; + } + + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"wt-available-protocols:", + (const unsigned char *)av_buf, (int)strlen(av_buf), hp, end)) + return -1; + } + break; + } + + case LWS_CALLBACK_ESTABLISHED: + case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: + pss->wsi = wsi; + pss->request_index = -1; + if (lws_wt_is_session(wsi)) { + if (!is_server) + break; + pss->is_session = 1; + /* Extract endpoint path */ + char path[128]; + int path_len = lws_hdr_copy(wsi, path, sizeof(path) - 1, WSI_TOKEN_HTTP_COLON_PATH); + if (path_len > 0) { + path[path_len] = '\0'; + lws_strncpy(pss->endpoint, path, sizeof(pss->endpoint)); + } + lwsl_user("Server WebTransport session established on %s\n", pss->endpoint); + + /* If server needs to request files, trigger them now */ + trigger_server_transfers(wsi, pss->endpoint); + } else { + pss->is_session = 0; + pss->is_unidi = lws_wt_is_unidi(wsi); + struct lws *swsi = lws_wt_get_session_wsi(wsi); + if (swsi) { + struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); + if (spss) { + lws_strncpy(pss->endpoint, spss->endpoint, sizeof(pss->endpoint)); + } + } + lwsl_user("%s stream established (unidi=%d)\n", is_server ? "Server" : "Client", pss->is_unidi); + + if (is_server && !pss->is_unidi) { + /* Server receives files over client-initiated bidi streams. + * Find first unstarted server request, assign it to this stream, + * and trigger writable callback to send GET . */ + int i; + for (i = 0; i < server_requests_count; i++) { + if (!server_requests[i].started) { + server_requests[i].started = 1; + pss->request_index = i; + pss->is_initiator = 1; + lws_strncpy(pss->filename, server_requests[i].filename, sizeof(pss->filename)); + lws_callback_on_writable(wsi); + lwsl_user("Server assigned request %d (%s) to bidi stream %p\n", i, pss->filename, wsi); + break; + } + } + } + } + break; + + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: + case LWS_CALLBACK_CLIENT_ESTABLISHED: + if (is_server) + break; + pss->wsi = wsi; + pss->request_index = -1; + if (lws_wt_is_session(wsi)) { + pss->is_session = 1; + + /* Match the endpoint from client_requests */ + int i; + for (i = 0; i < client_requests_count; i++) { + if (!client_requests[i].session_wsi) { + lws_strncpy(pss->endpoint, client_requests[i].endpoint, sizeof(pss->endpoint)); + client_requests[i].session_wsi = wsi; + break; + } + } + lwsl_user("Client WebTransport session established on %s. Listing all custom headers:\n", pss->endpoint); +#if defined(LWS_WITH_CUSTOM_HEADERS) + lws_hdr_custom_name_foreach(wsi, print_custom_header_cb, wsi); +#endif + + /* Parse and save negotiated protocol to /downloads/negotiated_protocol.txt */ + char negotiated[64]; + int nl = lws_hdr_custom_copy(wsi, negotiated, sizeof(negotiated) - 1, "wt-protocol", 11); + lwsl_user("wt-protocol header lookup result: %d\n", nl); + if (nl > 0) { + negotiated[nl] = '\0'; + /* Remove quotes */ + char *n_ptr = negotiated; + while (*n_ptr == '"') n_ptr++; + char *ne = n_ptr + strlen(n_ptr); + while (ne > n_ptr && (ne[-1] == '"' || ne[-1] == '\r' || ne[-1] == '\n')) { + ne[-1] = '\0'; + ne--; + } + mkdir("/downloads", 0777); + int nfd = open("/downloads/negotiated_protocol.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (nfd >= 0) { + if (write(nfd, n_ptr, strlen(n_ptr)) < 0) { + lwsl_err("Failed to write negotiated protocol\n"); + } + close(nfd); + } + } + + /* If client needs to request files, trigger them now */ + trigger_client_transfers(wsi, pss->endpoint); + } else { + pss->is_session = 0; + pss->is_unidi = lws_wt_is_unidi(wsi); + struct lws *swsi = lws_wt_get_session_wsi(wsi); + if (swsi) { + struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); + if (spss) { + lws_strncpy(pss->endpoint, spss->endpoint, sizeof(pss->endpoint)); + } + } + lwsl_user("Client stream established (unidi=%d)\n", pss->is_unidi); + } + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + case LWS_CALLBACK_CLIENT_WRITEABLE: + if (pss->is_session) + break; + + if (pss_is_file_sender(pss)) { + /* Sender: write file data */ + if (!pss->filename[0] || pss->write_completed) + break; + if (pss->fd_in < 0) { + char filepath[512]; + const char *endpoint = pss->endpoint; + if (!endpoint[0]) { + struct lws *swsi = lws_wt_get_session_wsi(wsi); + if (swsi) { + struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); + if (spss) endpoint = spss->endpoint; + } + } + lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", endpoint, pss->filename); + pss->fd_in = open(filepath, O_RDONLY); + if (pss->fd_in >= 0) { + struct stat st; + if (fstat(pss->fd_in, &st) == 0) { + pss->file_len = (size_t)st.st_size; + } + pss->sent_len = 0; + pss->header_sent = 0; + lwsl_user("Sender opened file %s (%zu bytes) for transmission\n", filepath, pss->file_len); + } else { + lwsl_err("Sender failed to open file %s\n", filepath); + return -1; + } + } + + if (pss->fd_in >= 0) { + if (!pss->is_unidi || pss->header_sent) { + /* Read and send file chunk */ + p = &buf[LWS_PRE]; + n = (int)read(pss->fd_in, p, sizeof(buf) - LWS_PRE); + if (n > 0) { + pss->sent_len += (size_t)n; + int is_final = (pss->sent_len == pss->file_len); + m = lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY | (is_final ? LWS_WRITE_H2_STREAM_END : LWS_WRITE_NO_FIN)); + if (m < 0) + return -1; + if (!is_final) + lws_callback_on_writable(wsi); + else { + close(pss->fd_in); + pss->fd_in = -1; + pss->write_completed = 1; + lwsl_user("Sender completed file write: %zu bytes\n", pss->sent_len); + } + } else { + /* EOF or error */ + close(pss->fd_in); + pss->fd_in = -1; + pss->write_completed = 1; + lws_write(wsi, NULL, 0, LWS_WRITE_BINARY | LWS_WRITE_H2_STREAM_END); + } + } else { + /* Unidirectional stream requires PUSH \n first */ + p = &buf[LWS_PRE]; + n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE, "PUSH %s\n", pss->filename); + m = lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY | LWS_WRITE_NO_FIN); + if (m < 0) + return -1; + pss->header_sent = 1; + lws_callback_on_writable(wsi); + lwsl_user("Sender sent PUSH %s\\n\n", pss->filename); + } + } + } else { + if (pss->is_initiator) { + /* Initiator: write GET */ + if (!pss->header_sent) { + p = &buf[LWS_PRE]; + n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE, "GET %s", pss->filename); + m = lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY | LWS_WRITE_H2_STREAM_END); + if (m < 0) + return -1; + pss->header_sent = 1; + lwsl_user("Initiator sent GET %s\n", pss->filename); + } + } + } + break; + + case LWS_CALLBACK_RECEIVE: + if (pss->is_session) { + /* Datagram received! format: GET or PUSH \n */ + char *payload = (char *)in; + size_t payload_len = len; + + if (payload_len > 4 && strncmp(payload, "GET ", 4) == 0) { + /* GET */ + char filename[256]; + size_t fn_len = payload_len - 4; + if (fn_len >= sizeof(filename)) fn_len = sizeof(filename) - 1; + memcpy(filename, payload + 4, fn_len); + filename[fn_len] = '\0'; + + lwsl_user("Session WSI received datagram GET: %s\n", filename); + + /* Respond by sending PUSH \n as a datagram */ + char filepath[512]; + lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", pss->endpoint, filename); + int fd = open(filepath, O_RDONLY); + if (fd >= 0) { + struct stat st; + if (fstat(fd, &st) == 0) { + uint8_t dgbuf[LWS_PRE + 65536]; + int hn = lws_snprintf((char *)&dgbuf[LWS_PRE], sizeof(dgbuf) - LWS_PRE, "PUSH %s\n", filename); + int rn = (int)read(fd, &dgbuf[LWS_PRE + hn], sizeof(dgbuf) - LWS_PRE - (size_t)hn); + if (rn >= 0) { + enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; + lws_write(wsi, &dgbuf[LWS_PRE], (size_t)(hn + rn), wp); + lwsl_user("Session WSI sent datagram PUSH: %s (%d bytes)\n", filename, hn + rn); + } + } + close(fd); + } + } else if (payload_len > 5 && strncmp(payload, "PUSH ", 5) == 0) { + /* PUSH \n */ + char *newline = memchr(payload, '\n', payload_len); + if (newline) { + char filename[256]; + size_t fn_len = (size_t)(newline - (payload + 5)); + if (fn_len >= sizeof(filename)) fn_len = sizeof(filename) - 1; + memcpy(filename, payload + 5, fn_len); + filename[fn_len] = '\0'; + + char *data_start = newline + 1; + size_t data_len = payload_len - (size_t)(data_start - payload); + + lwsl_user("Session WSI received datagram PUSH: %s (%zu bytes)\n", filename, data_len); + + char dirpath[512], filepath[512]; + lws_snprintf(dirpath, sizeof(dirpath), "/downloads%s", pss->endpoint); + mkdir(dirpath, 0777); + lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, filename); + + int fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd >= 0) { + if (write(fd, data_start, data_len) < 0) { + lwsl_err("Failed to write datagram content\n"); + } + close(fd); + + /* Mark request completed for datagram-receive */ + if (!is_server) { + int r_idx = -1; + int i; + for (i = 0; i < client_requests_count; i++) { + if (strcmp(client_requests[i].filename, filename) == 0) { + r_idx = i; + break; + } + } + if (r_idx >= 0) { + client_requests[r_idx].completed = 1; + lwsl_user("Client datagram request index %d (%s) completed (datagram received)\n", r_idx, filename); + } + } + } + } + } + break; + } + + /* Child Stream Data Received */ + { + char *data = (char *)in; + size_t data_len = len; + int is_file_rec = pss_is_file_receiver(pss); + + lwsl_user("RECEIVE: wsi=%p, len=%zu, is_unidi=%d, is_initiator=%d, push_hdr_done=%d, is_file_rec=%d\n", + wsi, data_len, pss->is_unidi, pss->is_initiator, pss->push_hdr_done, is_file_rec); + + if (is_file_rec) { + /* Receiver of file */ + if (pss->is_unidi) { + /* Unidirectional: parse PUSH \n first */ + if (!pss->push_hdr_done) { + size_t i; + for (i = 0; i < data_len; i++) { + if (pss->push_hdr_len < sizeof(pss->push_hdr) - 1) { + pss->push_hdr[pss->push_hdr_len++] = data[i]; + if (data[i] == '\n') { + pss->push_hdr[pss->push_hdr_len] = '\0'; + pss->push_hdr_done = 1; + /* Parse filename */ + if (strncmp(pss->push_hdr, "PUSH ", 5) == 0) { + char *nl = strchr(pss->push_hdr, '\n'); + if (nl) *nl = '\0'; + lws_strncpy(pss->filename, pss->push_hdr + 5, sizeof(pss->filename)); + lwsl_user("RECEIVE: Parsed filename '%s'\n", pss->filename); + } + /* Open file regardless of rem */ + char dirpath[512], filepath[512]; + const char *endpoint = pss->endpoint; + if (!endpoint[0]) { + struct lws *swsi = lws_wt_get_session_wsi(wsi); + if (swsi) { + struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); + if (spss) endpoint = spss->endpoint; + } + } + lws_snprintf(dirpath, sizeof(dirpath), "/downloads%s", endpoint); + mkdir(dirpath, 0777); + lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, pss->filename); + pss->fd_out = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + size_t rem = data_len - i - 1; + lwsl_user("RECEIVE: Opened file '%s' -> fd %d (rem=%zu bytes written)\n", filepath, pss->fd_out, rem); + if (pss->fd_out >= 0 && rem > 0) { + if (write(pss->fd_out, data + i + 1, rem) < 0) { + lwsl_err("Failed to write stream chunk\n"); + } + } + break; + } + } + } + } else { + if (pss->fd_out >= 0) { + if (write(pss->fd_out, data, data_len) < 0) { + lwsl_err("Failed to write stream data\n"); + } + } else { + lwsl_user("RECEIVE: fd_out is closed/invalid (%d) while trying to write %zu bytes\n", pss->fd_out, data_len); + } + } + } else { + /* Bidirectional: no header, just raw file contents */ + if (pss->fd_out < 0) { + char dirpath[512], filepath[512]; + const char *endpoint = pss->endpoint; + if (!endpoint[0]) { + struct lws *swsi = lws_wt_get_session_wsi(wsi); + if (swsi) { + struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); + if (spss) endpoint = spss->endpoint; + } + } + lws_snprintf(dirpath, sizeof(dirpath), "/downloads%s", endpoint); + mkdir(dirpath, 0777); + lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, pss->filename); + pss->fd_out = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); + lwsl_user("RECEIVE (bidi): Opened file '%s' -> fd %d\n", filepath, pss->fd_out); + } + if (pss->fd_out >= 0) { + if (write(pss->fd_out, data, data_len) < 0) { + lwsl_err("Failed to write stream data\n"); + } + } + } + } else { + /* Sender (received GET ) */ + if (data_len > 4 && strncmp(data, "GET ", 4) == 0) { + char filename[256]; + size_t fn_len = data_len - 4; + /* Remove trailing spaces or newlines if any */ + while (fn_len > 0 && (data[4 + fn_len - 1] == ' ' || data[4 + fn_len - 1] == '\r' || data[4 + fn_len - 1] == '\n')) + fn_len--; + if (fn_len >= sizeof(filename)) fn_len = sizeof(filename) - 1; + memcpy(filename, data + 4, fn_len); + filename[fn_len] = '\0'; + + lws_strncpy(pss->filename, filename, sizeof(pss->filename)); + lwsl_user("Sender WSI received GET %s\n", pss->filename); + + /* Open local file from /www// */ + char filepath[512]; + const char *endpoint = pss->endpoint; + if (!endpoint[0]) { + struct lws *swsi = lws_wt_get_session_wsi(wsi); + if (swsi) { + struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); + if (spss) endpoint = spss->endpoint; + } + } + lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", endpoint, pss->filename); + pss->fd_in = open(filepath, O_RDONLY); + if (pss->fd_in >= 0) { + struct stat st; + if (fstat(pss->fd_in, &st) == 0) { + pss->file_len = (size_t)st.st_size; + } + + if (lws_wt_is_unidi(wsi)) { + struct lws *cwsi = lws_wt_create_stream_from_child(wsi, 1); + if (cwsi) { + lws_ensure_user_space(cwsi); + struct pss_qir *cpss = (struct pss_qir *)lws_wsi_user(cwsi); + if (cpss) { + cpss->is_unidi = 1; + cpss->is_initiator = 0; + lws_strncpy(cpss->endpoint, endpoint, sizeof(cpss->endpoint)); + lws_strncpy(cpss->filename, filename, sizeof(cpss->filename)); + cpss->fd_in = pss->fd_in; + pss->fd_in = -1; + cpss->file_len = pss->file_len; + lws_callback_on_writable(cwsi); + lwsl_user("Created server-initiated stream %p for unidirectional file response\n", cwsi); + } + } else { + lwsl_err("Failed to create server-initiated stream for response\n"); + close(pss->fd_in); + pss->fd_in = -1; + } + } else { + lws_callback_on_writable(wsi); + } + } + } + } + } + break; + + case LWS_CALLBACK_CLOSED: + case LWS_CALLBACK_CLIENT_CLOSED: + case LWS_CALLBACK_CLOSED_CLIENT_HTTP: + if (pss->fd_in >= 0) { + close(pss->fd_in); + pss->fd_in = -1; + } + if (pss->fd_out >= 0) { + close(pss->fd_out); + pss->fd_out = -1; + } + if (!pss->is_session) { + if (!is_server) { + int r_idx = -1; + int i; + for (i = 0; i < client_requests_count; i++) { + if (strcmp(client_requests[i].filename, pss->filename) == 0) { + r_idx = i; + break; + } + } + if (r_idx >= 0) { + client_requests[r_idx].completed = 1; + lwsl_user("Client transfer request index %d (%s) completed (stream closed)\n", r_idx, pss->filename); + } + } + } + lwsl_user("WSI closed\n"); + break; + + default: + break; + } + + return 0; +} + +static const struct lws_protocols protocols[] = { + { "webtransport", callback_qir, sizeof(struct pss_qir), 65536, 0, NULL, 0 }, + { NULL, NULL, 0, 0, 0, NULL, 0 } +}; + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + const char *p; + int n = 0; + int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + + signal(SIGINT, sigint_handler); + + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + + /* Determine role */ + if (argc > 1 && strcmp(argv[1], "server") == 0) + is_server = 1; + else + is_server = 0; + + /* Read testcase env */ + const char *tc = getenv("TESTCASE_NAME"); + if (!tc) + tc = getenv("TESTCASE"); + if (tc) + lws_strncpy(testcase, tc, sizeof(testcase)); + + lwsl_user("LWS WebTransport QIR Tool | Role: %s | Testcase: %s\n", + is_server ? "server" : "client", testcase); + + /* Determine port */ + int port = 443; + const char *port_env = getenv("PORT"); + if (port_env) + port = atoi(port_env); + + memset(&info, 0, sizeof info); + info.port = is_server ? port : CONTEXT_PORT_NO_LISTEN; + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.protocols = protocols; + info.alpn = "h3"; + + if (is_server) { + static char cert_path[512]; + static char key_path[512]; + int fd_check; + + /* The interop runner mounts host certificates at /certs inside the container */ + fd_check = open("/certs/cert.pem", O_RDONLY); + if (fd_check >= 0) { + close(fd_check); + lws_strncpy(cert_path, "/certs/cert.pem", sizeof(cert_path)); + lws_strncpy(key_path, "/certs/priv.key", sizeof(key_path)); + } else { + /* Fallback to local files if running outside QIR simulation */ + lws_strncpy(cert_path, "localhost-100y.cert", sizeof(cert_path)); + lws_strncpy(key_path, "localhost-100y.key", sizeof(key_path)); + } + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + parse_server_requests(); + } else { + parse_client_requests(); + } + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + /* Client connection triggering */ + if (!is_server && client_requests_count > 0) { + int i; + for (i = 0; i < client_requests_count; i++) { + struct request_item *item = &client_requests[i]; + + /* Connect only once per unique host/port/endpoint */ + int already_triggered = 0; + int j; + for (j = 0; j < i; j++) { + if (strcmp(client_requests[j].endpoint, item->endpoint) == 0 && + strcmp(client_requests[j].host, item->host) == 0 && + client_requests[j].port == item->port) { + already_triggered = 1; + break; + } + } + if (already_triggered) + continue; + + struct lws_client_connect_info cinfo; + + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.context = context; + cinfo.address = item->host; + cinfo.port = item->port; + cinfo.path = item->endpoint; + cinfo.host = item->host; + cinfo.origin = item->host; + cinfo.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; + cinfo.protocol = "webtransport"; + cinfo.alpn = "h3"; + + if (first_session_wsi) { + /* Force multiplexing on the same QUIC network connection */ + cinfo.parent_wsi = first_session_wsi; + } + + struct lws *wsi = lws_client_connect_via_info(&cinfo); + if (!wsi) { + lwsl_err("Failed to initiate WebTransport client connection to %s\n", item->url); + } else { + if (!first_session_wsi) { + first_session_wsi = wsi; + } + } + } + } + + while (n >= 0 && !interrupted) { + n = lws_service(context, 0); + + /* Check if all client requests are done and exit */ + if (!is_server && client_requests_count > 0) { + int all_done = 1; + int i; + for (i = 0; i < client_requests_count; i++) { + if (!client_requests[i].completed) + all_done = 0; + } + if (all_done && strcmp(testcase, "handshake") != 0) { + lwsl_user("All client requests completed. Waiting 500ms before exiting.\n"); + usleep(500000); + break; + } + /* Handshake testcase does not download files, just wait a bit and exit */ + if (strcmp(testcase, "handshake") == 0) { + char client_proto_path[512]; + lws_snprintf(client_proto_path, sizeof(client_proto_path), "/downloads/negotiated_protocol.txt"); + int fd = open(client_proto_path, O_RDONLY); + if (fd >= 0) { + close(fd); + lwsl_user("Handshake verification file found, client exiting successfully.\n"); + break; + } + } + } + } + + lws_context_destroy(context); + return 0; +} From ac1aab8778a71af7161d4a313cf38bd02b9802dc Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 13 Jul 2026 11:17:47 +0100 Subject: [PATCH 17/44] quic-security-audit --- lib/roles/quic/crypto-quic.c | 37 ++++++++++++++++++- lib/tls/gnutls/gnutls-quic.c | 8 +++- lib/tls/mbedtls/mbedtls-quic.c | 2 +- lib/tls/openssl/openssl-quic.c | 17 +++++---- lib/tls/schannel/schannel-quic.c | 5 +++ .../protocol_deaddrop/protocol_lws_deaddrop.c | 2 +- .../protocol_lws_acme_client.c | 16 ++++---- .../protocol_lws_acme_client_core.c | 16 ++++---- .../protocol_lws_auth_dns.c | 17 ++++++++- .../protocol_lws_auth_server.c | 20 ++++++---- 10 files changed, 101 insertions(+), 39 deletions(-) diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index 8b08ebf3b6..372aa0bd55 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -514,7 +514,7 @@ lws_quic_decrypt_payload(struct lws_quic_keys *keys, uint8_t *packet, size_t pac size_t ct_len = payload_len + 16; size_t pt_len = payload_len; - uint8_t tmp[2048]; + uint8_t tmp[4096]; if (ct_len > sizeof(tmp)) return -1; memcpy(tmp, &packet[payload_offset], ct_len); @@ -604,7 +604,7 @@ lws_quic_encrypt_payload(struct lws_quic_keys *keys, uint8_t *packet, size_t pac hd = (gnutls_aead_cipher_hd_t)keys->aead_tx; size_t ct_len = payload_len + 16; - uint8_t tmp[2048]; + uint8_t tmp[4096]; if (payload_len > sizeof(tmp)) return -1; memcpy(tmp, &packet[payload_offset], payload_len); @@ -730,6 +730,10 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le int n; if (len > 0 && wsi->quic.qn) { + if (wsi->quic.qn->crypto_rx_buf_len[level] + len > 262144) { + lwsl_wsi_err(wsi, "QUIC: CRYPTO reassembly buffer size limit exceeded on level %d", level); + return -1; + } if (wsi->quic.qn->crypto_rx_buf_len[level] > 0) { uint8_t *new_buf = lws_realloc(wsi->quic.qn->crypto_rx_buf[level], wsi->quic.qn->crypto_rx_buf_len[level] + len, @@ -1063,6 +1067,7 @@ lws_quic_create_retry_token(struct lws *wsi, uint8_t pt[256]; size_t pt_len = 0; uint8_t nonce[12]; + uint64_t now = (uint64_t)lws_now_usecs(); lws_get_random(wsi->a.context, nonce, 12); @@ -1078,6 +1083,17 @@ lws_quic_create_retry_token(struct lws *wsi, memcpy(&pt[pt_len], client_ip, ip_len); pt_len += ip_len; + if (pt_len + 8 > sizeof(pt)) + return -1; + pt[pt_len++] = (uint8_t)(now >> 56); + pt[pt_len++] = (uint8_t)(now >> 48); + pt[pt_len++] = (uint8_t)(now >> 40); + pt[pt_len++] = (uint8_t)(now >> 32); + pt[pt_len++] = (uint8_t)(now >> 24); + pt[pt_len++] = (uint8_t)(now >> 16); + pt[pt_len++] = (uint8_t)(now >> 8); + pt[pt_len++] = (uint8_t)now; + struct lws_gencrypto_keyelem keys[1]; keys[0].buf = wsi->a.context->quic_retry_secret; keys[0].len = 16; @@ -1151,6 +1167,23 @@ lws_quic_validate_retry_token(struct lws *wsi, const uint8_t *token, size_t toke if (pt[p++] != ip_len) return -1; if (p + ip_len > ct_len || p + ip_len > sizeof(pt)) return -1; if (ip_len && memcmp(pt + p, client_ip, ip_len)) return -1; + p += ip_len; + + if (p + 8 > ct_len || p + 8 > sizeof(pt)) return -1; + uint64_t token_time = ((uint64_t)pt[p] << 56) | + ((uint64_t)pt[p+1] << 48) | + ((uint64_t)pt[p+2] << 40) | + ((uint64_t)pt[p+3] << 32) | + ((uint64_t)pt[p+4] << 24) | + ((uint64_t)pt[p+5] << 16) | + ((uint64_t)pt[p+6] << 8) | + pt[p+7]; + + uint64_t now = (uint64_t)lws_now_usecs(); + if (now < token_time || now - token_time > 15ULL * 1000000ULL) { + lwsl_wsi_notice(wsi, "QUIC: Retry token expired. age = %lld us", (long long)(now - token_time)); + return -1; + } return 0; } diff --git a/lib/tls/gnutls/gnutls-quic.c b/lib/tls/gnutls/gnutls-quic.c index 36c42843f9..814d16ca8a 100644 --- a/lib/tls/gnutls/gnutls-quic.c +++ b/lib/tls/gnutls/gnutls-quic.c @@ -69,7 +69,7 @@ gnutls_quic_secret_func(gnutls_session_t session, struct lws *wsi = (struct lws *)gnutls_session_get_ptr(session); int is_client; - if (!wsi || !wsi->tls.quic_secret_cb) + if (!wsi || !wsi->tls.quic_secret_cb || secret_size > 48) return 0; if (wsi->a.vhost) @@ -306,7 +306,7 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) if (!wsi->tls.quic_tp_send) { /* Construct dynamically to include loc_cid */ struct lws_quic_netconn *qn = wsi->quic.qn; - static uint8_t dynamic_tp[128]; + uint8_t dynamic_tp[128]; uint8_t *p = dynamic_tp; /* initial_max_stream_data_bidi_local (0x05), len 4, val 65535 */ @@ -325,6 +325,10 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) *p++ = 0x01; *p++ = 0x04; *p++ = 0x80; *p++ = 0x00; *p++ = 0x75; *p++ = 0x30; /* initial_source_connection_id (0x0f) */ if (qn && qn->loc_cid.len > 0) { + if ((size_t)(p - dynamic_tp) + 2 + qn->loc_cid.len > sizeof(dynamic_tp)) { + lwsl_err("loc_cid too large for TP buffer\n"); + return -1; + } *p++ = 0x0f; *p++ = qn->loc_cid.len; memcpy(p, qn->loc_cid.id, qn->loc_cid.len); diff --git a/lib/tls/mbedtls/mbedtls-quic.c b/lib/tls/mbedtls/mbedtls-quic.c index cd02470abc..d8b0b5ba60 100644 --- a/lib/tls/mbedtls/mbedtls-quic.c +++ b/lib/tls/mbedtls/mbedtls-quic.c @@ -118,7 +118,7 @@ mbedtls_quic_set_traffic_secrets(mbedtls_ssl_context *ssl, struct lws *wsi = (struct lws *)mbedtls_ssl_get_user_data_p(ssl); enum lws_tls_quic_secret_type ct, st; - if (!wsi || !wsi->tls.quic_secret_cb) + if (!wsi || !wsi->tls.quic_secret_cb || secret_len > 48) return 0; if (wsi->tls.quic_secret_cb == (lws_tls_quic_secret_cb)1) diff --git a/lib/tls/openssl/openssl-quic.c b/lib/tls/openssl/openssl-quic.c index 204feb5272..dff3496ccf 100644 --- a/lib/tls/openssl/openssl-quic.c +++ b/lib/tls/openssl/openssl-quic.c @@ -44,8 +44,8 @@ set_encryption_secrets(WOLFSSL *ssl, enum wolfssl_encryption_level_t level, struct lws *wsi = (struct lws *)SSL_get_app_data((SSL *)ssl); enum lws_tls_quic_secret_type rt, wt; - if (!wsi) - return 1; + if (!wsi || secret_len > 48) + return 0; switch (level) { case wolfssl_encryption_early_data: @@ -147,8 +147,8 @@ set_read_secret(SSL *ssl, enum ssl_encryption_level_t level, struct lws *wsi = (struct lws *)SSL_get_app_data(ssl); enum lws_tls_quic_secret_type t; - if (!wsi) - return 1; + if (!wsi || secret_len > 48) + return 0; switch (level) { case ssl_encryption_early_data: @@ -178,8 +178,8 @@ set_write_secret(SSL *ssl, enum ssl_encryption_level_t level, struct lws *wsi = (struct lws *)SSL_get_app_data(ssl); enum lws_tls_quic_secret_type t; - if (!wsi) - return 1; + if (!wsi || secret_len > 48) + return 0; switch (level) { case ssl_encryption_early_data: @@ -536,7 +536,7 @@ static void openssl_quic_keylog_cb(const SSL *ssl, const char *line) { struct lws *wsi = (struct lws *)SSL_get_app_data(ssl); - enum lws_tls_quic_secret_type type; + enum lws_tls_quic_secret_type type = (enum lws_tls_quic_secret_type)-1; const char *secret_hex = NULL; uint8_t secret[64]; size_t len = 0; @@ -561,7 +561,7 @@ openssl_quic_keylog_cb(const SSL *ssl, const char *line) secret_hex = (char *)strchr(line + 24, ' '); } - if (!secret_hex) + if (!secret_hex || (int)type == -1) return; secret_hex++; /* skip space */ @@ -572,6 +572,7 @@ openssl_quic_keylog_cb(const SSL *ssl, const char *line) } wsi->tls.quic_secret_cb(wsi, type, secret, len); + lws_explicit_bzero(secret, sizeof(secret)); } int diff --git a/lib/tls/schannel/schannel-quic.c b/lib/tls/schannel/schannel-quic.c index c0523f1a48..dfaf77bb8a 100644 --- a/lib/tls/schannel/schannel-quic.c +++ b/lib/tls/schannel/schannel-quic.c @@ -551,6 +551,11 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, split_offset = secrets->MsgSequenceStart; } + if (secrets->TrafficSecretSize > 48) { + lwsl_err("%s: TrafficSecretSize %zu exceeds 48\n", __func__, secrets->TrafficSecretSize); + return -1; + } + if (wsi->tls.quic_secret_cb(wsi, mapped_type, secrets->TrafficSecret, secrets->TrafficSecretSize) < 0) { lwsl_err("%s: quic_secret_cb failed for type %d\n", __func__, mapped_type); return -1; diff --git a/plugins/protocol_deaddrop/protocol_lws_deaddrop.c b/plugins/protocol_deaddrop/protocol_lws_deaddrop.c index 4795741363..aa03d641d8 100644 --- a/plugins/protocol_deaddrop/protocol_lws_deaddrop.c +++ b/plugins/protocol_deaddrop/protocol_lws_deaddrop.c @@ -862,7 +862,7 @@ deaddrop_handler_server_ws_rx(struct vhd_deaddrop *vhd, struct pss_deaddrop *pss lws_snprintf(path, sizeof(path), "%s/%s", vhd->upload_dir, fname); -#if defined(__linux__) +#if !defined(WIN32) && !defined(_WIN32) && !defined(LWS_WITH_ESP32) if (!realpath(path, resolved_path)) { lwsl_wsi_warn(wsi, "delete: realpath failed %s", path); return; diff --git a/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c b/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c index 5c3c3f6028..6b5931722b 100644 --- a/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c +++ b/plugins/protocol_lws_acme_client/protocol_lws_acme_client.c @@ -986,7 +986,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, ac->state = ACME_STATE_NEW_ACCOUNT; lws_acme_report_status(vhd->vhost, LWS_CUS_REG, NULL); - strcpy(buf, ac->urls[JAD_NEW_ACCOUNT_URL]); + lws_strncpy(buf, ac->urls[JAD_NEW_ACCOUNT_URL], sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); if (!cwsi) { @@ -1271,7 +1271,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, ac->state = ACME_STATE_NEW_NONCE; - strcpy(buf, ac->urls[JAD_NEW_NONCE_URL]); + lws_strncpy(buf, ac->urls[JAD_NEW_NONCE_URL], sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "GET"); @@ -1291,7 +1291,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, */ ac->state = ACME_STATE_NEW_ORDER; - strcpy(buf, ac->urls[JAD_NEW_ORDER_URL]); + lws_strncpy(buf, ac->urls[JAD_NEW_ORDER_URL], sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1322,7 +1322,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, lws_acme_report_status(vhd->vhost, LWS_CUS_AUTH, NULL); - strcpy(buf, ac->authz_url); + lws_strncpy(buf, ac->authz_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1428,7 +1428,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, goto failed; } - strcpy(buf, ac->order_url); + lws_strncpy(buf, ac->order_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1477,7 +1477,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, lws_acme_report_status(vhd->vhost, LWS_CUS_REQ, NULL); ac->goes_around = 0; - strcpy(buf, ac->finalize_url); + lws_strncpy(buf, ac->finalize_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1501,7 +1501,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, goto failed; } - strcpy(buf, ac->order_url); + lws_strncpy(buf, ac->order_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, @@ -1518,7 +1518,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, ac->state = ACME_STATE_DOWNLOAD_CERT; - strcpy(buf, ac->cert_url); + lws_strncpy(buf, ac->cert_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); diff --git a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c index 2131009fcb..81d92cac84 100644 --- a/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c +++ b/plugins/protocol_lws_acme_client/protocol_lws_acme_client_core.c @@ -1403,7 +1403,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, ac->state = ACME_STATE_NEW_ACCOUNT; lws_acme_report_status(vhd->vhost, LWS_CUS_REG, NULL); - strcpy(buf, ac->urls[JAD_NEW_ACCOUNT_URL]); + lws_strncpy(buf, ac->urls[JAD_NEW_ACCOUNT_URL], sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); if (!cwsi) { @@ -1707,7 +1707,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, ac->state = ACME_STATE_NEW_NONCE; - strcpy(buf, ac->urls[JAD_NEW_NONCE_URL]); + lws_strncpy(buf, ac->urls[JAD_NEW_NONCE_URL], sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "GET"); @@ -1727,7 +1727,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, */ ac->state = ACME_STATE_NEW_ORDER; - strcpy(buf, ac->urls[JAD_NEW_ORDER_URL]); + lws_strncpy(buf, ac->urls[JAD_NEW_ORDER_URL], sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1758,7 +1758,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, lws_acme_report_status(vhd->vhost, LWS_CUS_AUTH, NULL); - strcpy(buf, ac->authz_url); + lws_strncpy(buf, ac->authz_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1883,7 +1883,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, goto failed; } - strcpy(buf, ac->order_url); + lws_strncpy(buf, ac->order_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1935,7 +1935,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, lws_acme_report_status(vhd->vhost, LWS_CUS_REQ, NULL); ac->goes_around = 0; - strcpy(buf, ac->finalize_url); + lws_strncpy(buf, ac->finalize_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); @@ -1959,7 +1959,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, goto failed; } - strcpy(buf, ac->order_url); + lws_strncpy(buf, ac->order_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, @@ -1976,7 +1976,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason, ac->state = ACME_STATE_DOWNLOAD_CERT; - strcpy(buf, ac->cert_url); + lws_strncpy(buf, ac->cert_url, sizeof(buf)); cwsi = lws_acme_client_connect(vhd->context, vhd->vhost, &ac->cwsi, &ac->i, buf, "POST"); diff --git a/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c b/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c index b475502e52..d9ca497b9b 100644 --- a/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c +++ b/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c @@ -1232,6 +1232,10 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, rp += 12; int qlen = (int)(q - (p + 12)); + if ((size_t)(rp - dbuf) + 12 + (size_t)qlen > max_buf) { + lwsl_notice("DNS: response too large for buffer\n"); + goto done; + } memcpy(rp, p + 12, (size_t)qlen); rp += qlen; @@ -1276,8 +1280,9 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, struct auth_dns_rr *rr = lws_container_of(d2, struct auth_dns_rr, list); size_t nlen = strlen(rs->name); if ((size_t)(rp - dbuf) + 12 + nlen + 1 + rr->wire_rdata_len <= max_buf) { - if (name_to_wire(rs->name, matched_ce->zone.origin, rp, &max_buf) == 0) { - size_t written_len = strlen((char *)rp) + 1; /* Name length including root dot */ + size_t rem = max_buf - (size_t)(rp - dbuf); + if (name_to_wire(rs->name, matched_ce->zone.origin, rp, &rem) == 0) { + size_t written_len = rem; /* Name length including root dot */ rp += written_len; *rp++ = (uint8_t)(rs->type >> 8); *rp++ = (uint8_t)(rs->type & 0xff); *rp++ = (uint8_t)(rs->class_ >> 8); *rp++ = (uint8_t)(rs->class_ & 0xff); @@ -1310,6 +1315,10 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, rp[10] = 0; rp[11] = 0; /* ARCOUNT = 0 */ rp += 12; int qlen = (int)(q - (p + 12)); + if ((size_t)(rp - dbuf) + 12 + (size_t)qlen > max_buf) { + lwsl_notice("DNS: response too large for buffer\n"); + goto done; + } memcpy(rp, p + 12, (size_t)qlen); rp += qlen; @@ -1366,6 +1375,10 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, rp[10] = (uint8_t)(added_opt >> 8); rp[11] = (uint8_t)(added_opt & 0xff); rp += 12; int qlen = (int)(q - (p + 12)); + if ((size_t)(rp - dbuf) + 12 + (size_t)qlen > max_buf) { + lwsl_notice("DNS: response too large for buffer\n"); + goto done; + } memcpy(rp, p + 12, (size_t)qlen); rp += qlen; diff --git a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c index 3a1806401a..aa32ec04bd 100644 --- a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c +++ b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c @@ -2290,7 +2290,7 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, lws_get_urlarg_by_name_safe(wsi, "redirect_uri=", redirect_uri, sizeof(redirect_uri)); lws_urldecode(redirect_uri, redirect_uri, sizeof(redirect_uri)); - if (!redirect_uri[0]) + if (!redirect_uri[0] || !auth_verify_redirect_uri(vhd, NULL, redirect_uri)) lws_strncpy(redirect_uri, "/", sizeof(redirect_uri)); lwsl_notice("%s: Extracted redirect_uri: %s\n", __func__, redirect_uri); @@ -2866,15 +2866,21 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, /* Always add public grant to newly minted users */ sqlite3_exec(vhd->db, "INSERT OR IGNORE INTO services (name) VALUES ('public')", NULL, NULL, NULL); - char public_grant_query[256]; - lws_snprintf(public_grant_query, sizeof(public_grant_query), "INSERT INTO grants (uid, service_id, grant_level) VALUES ((SELECT uid FROM users WHERE username='%s'), (SELECT service_id FROM services WHERE name='public'), 1)", email); - sqlite3_exec(vhd->db, public_grant_query, NULL, NULL, NULL); + sqlite3_stmt *gstmt = NULL; + if (sqlite3_prepare_v2(vhd->db, "INSERT INTO grants (uid, service_id, grant_level) VALUES ((SELECT uid FROM users WHERE username=?), (SELECT service_id FROM services WHERE name='public'), 1)", -1, &gstmt, NULL) == SQLITE_OK) { + sqlite3_bind_text(gstmt, 1, email, -1, SQLITE_TRANSIENT); + sqlite3_step(gstmt); + sqlite3_finalize(gstmt); + } if (users_count == 1) { sqlite3_exec(vhd->db, "INSERT OR IGNORE INTO services (service_id, name) VALUES (1, '*')", NULL, NULL, NULL); - char grant_query[256]; - lws_snprintf(grant_query, sizeof(grant_query), "INSERT INTO grants (uid, service_id, grant_level) VALUES ((SELECT uid FROM users WHERE username='%s'), 1, 2)", email); - sqlite3_exec(vhd->db, grant_query, NULL, NULL, NULL); + gstmt = NULL; + if (sqlite3_prepare_v2(vhd->db, "INSERT INTO grants (uid, service_id, grant_level) VALUES ((SELECT uid FROM users WHERE username=?), 1, 2)", -1, &gstmt, NULL) == SQLITE_OK) { + sqlite3_bind_text(gstmt, 1, email, -1, SQLITE_TRANSIENT); + sqlite3_step(gstmt); + sqlite3_finalize(gstmt); + } } lws_snprintf(uri, sizeof(uri), "otpauth://totp/%s:%s?secret=%s&issuer=%s", From 2cf11280657974b4311c233716b5968e742ac4d8 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 13 Jul 2026 14:15:33 +0100 Subject: [PATCH 18/44] ddos-harden --- lib/core-net/wsi.c | 9 +++++++++ lib/roles/h2/http2.c | 17 +++++++++++++++++ lib/roles/h2/private-lib-roles-h2.h | 1 + 3 files changed, 27 insertions(+) diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index 5ad5ca86f6..4faaf9e084 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -1409,6 +1409,15 @@ void lws_wsi_mux_insert(struct lws *wsi, struct lws *parent_wsi, if (!wsi->role_ops) wsi->role_ops = parent_wsi->role_ops; +#if defined(LWS_WITH_PEER_LIMITS) + if (parent_wsi->peer && !wsi->peer) { + wsi->peer = parent_wsi->peer; + lws_context_lock(wsi->a.context, "mux peer child adopt"); + wsi->peer->count_wsi++; + lws_context_unlock(wsi->a.context); + } +#endif + /* new guy's sibling is whoever was the first child before */ wsi->mux.sibling_list = parent_wsi->mux.child_list; diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index f205455284..382bb885bf 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -437,8 +437,17 @@ lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pps) return; } + if (pps->type != LWS_H2_PPS_GOAWAY && h2n->pps_count >= 50) { + lwsl_warn("%s: too many pending protocol sends (%u), dropping conn\n", + __func__, (unsigned int)h2n->pps_count); + lws_free(pps); + lws_h2_goaway(nwsi, H2_ERR_ENHANCE_YOUR_CALM, "too many pending pps"); + return; + } + pps->next = h2n->pps; h2n->pps = pps; + h2n->pps_count++; lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_DISABLE | LWS_RXFLOW_REASON_H2_PPS_PENDING); lws_callback_on_writable(wsi); @@ -741,6 +750,9 @@ int lws_h2_do_pps_send(struct lws *wsi) if (!pps) return 1; + if (h2n->pps_count) + h2n->pps_count--; + lwsl_info("%s: %s: %d\n", __func__, lws_wsi_tag(wsi), pps->type); switch (pps->type) { @@ -2007,6 +2019,11 @@ lws_h2_parse_end_of_frame(struct lws *wsi) lwsl_info("LWS_H2_FRAME_TYPE_RST_STREAM: sid %u: reason 0x%x\n", (unsigned int)h2n->sid, (unsigned int)h2n->hpack_e_dep); + if (h2n->swsi) { + lws_close_free_wsi(h2n->swsi, LWS_CLOSE_STATUS_NOSTATUS, + "peer RST_STREAM"); + h2n->swsi = NULL; + } break; case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */ diff --git a/lib/roles/h2/private-lib-roles-h2.h b/lib/roles/h2/private-lib-roles-h2.h index c41c36772e..800d377087 100644 --- a/lib/roles/h2/private-lib-roles-h2.h +++ b/lib/roles/h2/private-lib-roles-h2.h @@ -251,6 +251,7 @@ struct lws_h2_netconn { char goaway_str[32]; /* for rx */ struct lws *swsi; struct lws_h2_protocol_send *pps; /* linked list */ + uint32_t pps_count; enum http2_hpack_state hpack; enum http2_hpack_type hpack_type; From 2a02bc5d42fbfadc60c265b115d193ed6c10ea5a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 17 Jul 2026 06:09:42 +0100 Subject: [PATCH 19/44] quic: add formal quic-initial-cwnd --- include/libwebsockets/lws-context-vhost.h | 3 +++ lib/core/context.c | 1 + lib/core/private-lib-core.h | 1 + lib/roles/quic/crypto-quic.c | 8 ++++++++ lib/roles/quic/ops-quic-cc-cubic.c | 5 ++++- lib/roles/quic/ops-quic-cc-newreno.c | 5 ++++- .../minimal-http-client-multi.c | 10 ++++++++++ 7 files changed, 31 insertions(+), 2 deletions(-) diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index a16d0ba921..39975e8630 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -1112,6 +1112,9 @@ struct lws_context_creation_info { /**< VHOST: Comma-separated list of IPv4 and/or IPv6 preferred addresses (e.g., "1.2.3.4:443,[2001:db8::1]:443") * to send to the client during the handshake to facilitate Connection Migration. */ + uint32_t quic_initial_cwnd; + /**< CONTEXT: 0 for default (10 * MTU), or the desired initial congestion window in bytes */ + #if !defined(__STRICT_ANSI__) void *_unused[1]; /**< dummy */ #endif diff --git a/lib/core/context.c b/lib/core/context.c index b17ee42fe4..0587fe2e44 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -722,6 +722,7 @@ lws_create_context(const struct lws_context_creation_info *info) context->quic_tx_credit_cb = info->quic_tx_credit_cb; context->quic_cc_ops = info->quic_cc_ops; + context->quic_initial_cwnd = info->quic_initial_cwnd; context->pt_serv_buf_size = (unsigned int)s1; context->protocols_copy = info->protocols; diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index bba4b8b086..3a7611b155 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -866,6 +866,7 @@ struct lws_context { lws_quic_tx_credit_cb_t quic_tx_credit_cb; const struct lws_cc_ops *quic_cc_ops; + uint32_t quic_initial_cwnd; uint8_t quic_retry_secret[16]; }; diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index 372aa0bd55..931de526c5 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -890,6 +890,14 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le wsi->quic.qn->handshake_done = 1; + /* Move any unsent 0-RTT (early) data frames to 1-RTT (app) level */ + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + wsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_EARLY].head) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + lws_dll2_remove(&f->list); + lws_dll2_add_tail(&f->list, &wsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP]); + } lws_end_foreach_dll_safe(d, d1); + if (wsi->quic.qn->is_server) { struct lws_quic_tx_frame *f_hd = lws_zalloc(sizeof(*f_hd), "HANDSHAKE_DONE"); if (f_hd) { diff --git a/lib/roles/quic/ops-quic-cc-cubic.c b/lib/roles/quic/ops-quic-cc-cubic.c index e6344f7487..ce039226a4 100644 --- a/lib/roles/quic/ops-quic-cc-cubic.c +++ b/lib/roles/quic/ops-quic-cc-cubic.c @@ -76,7 +76,10 @@ cubic_init(struct lws *nwsi) st = (struct lws_quic_cc_cubic *)qn->cc_state; /* RFC 9002: Initial Window */ - st->cwnd = 10 * mtu; + if (nwsi->a.context->quic_initial_cwnd) + st->cwnd = nwsi->a.context->quic_initial_cwnd; + else + st->cwnd = 10 * mtu; st->ssthresh = (size_t)-1; /* Infinity */ st->bytes_in_flight = 0; st->congestion_recovery_start_time = 0; diff --git a/lib/roles/quic/ops-quic-cc-newreno.c b/lib/roles/quic/ops-quic-cc-newreno.c index 68aed1243d..142b68e16f 100644 --- a/lib/roles/quic/ops-quic-cc-newreno.c +++ b/lib/roles/quic/ops-quic-cc-newreno.c @@ -34,7 +34,10 @@ newreno_init(struct lws *nwsi) st = (struct lws_quic_cc_newreno *)qn->cc_state; /* RFC 9002: Initial Window */ - st->cwnd = 10 * mtu; + if (nwsi->a.context->quic_initial_cwnd) + st->cwnd = nwsi->a.context->quic_initial_cwnd; + else + st->cwnd = 10 * mtu; st->ssthresh = (size_t)-1; /* Infinity */ st->bytes_in_flight = 0; st->congestion_recovery_start_time = 0; diff --git a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c index aa59553dd4..48c84d9c17 100644 --- a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c +++ b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c @@ -59,6 +59,7 @@ enum { LWS_SW_SAVE_TICKET, LWS_SW_LOAD_TICKET, LWS_SW_SEQ, + LWS_SW_QUIC_INITIAL_CWND, LWS_SW_HELP, }; @@ -85,6 +86,7 @@ static const struct lws_switches switches[] = { [LWS_SW_SAVE_TICKET] = { "--save-ticket", "Path to save TLS session ticket" }, [LWS_SW_LOAD_TICKET] = { "--load-ticket", "Path to load TLS session ticket" }, [LWS_SW_SEQ] = { "--seq", "Run sequentially" }, + [LWS_SW_QUIC_INITIAL_CWND] = { "--quic-initial-cwnd", "Initial congestion window in bytes" }, [LWS_SW_HELP] = { "--help", "Show this help information" }, }; @@ -739,6 +741,12 @@ int main(int argc, const char **argv) #endif } + if ((p = lws_cmdline_option(argc, argv, switches[LWS_SW_QUIC_INITIAL_CWND].sw))) { + int val = atoi(p); + if (val > 0) + info.quic_initial_cwnd = (uint32_t)val; + } + #if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) /* * When the QIR harness sets TESTCASE=zerortt it does not pass @@ -756,6 +764,8 @@ int main(int argc, const char **argv) sizeof(load_ticket)); lwsl_notice("%s: TESTCASE=zerortt: ticket path '%s'\n", __func__, save_ticket); + if (!info.quic_initial_cwnd) + info.quic_initial_cwnd = 250000; } #endif From 9d07bf0ff25a62f33ac4a56d0d886a5103db669e Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 13 Jul 2026 13:28:17 +0100 Subject: [PATCH 20/44] webtransport: passing qir OK wt-detrash-qir --- .sai.json | 2 +- include/libwebsockets/lws-x509.h | 14 + lib/core-net/adopt.c | 12 + lib/core-net/client/connect3.c | 5 + lib/core-net/pollfd.c | 5 + lib/core-net/wsi.c | 7 +- lib/roles/h3/ops-h3.c | 99 ++- lib/roles/quic/ops-quic.c | 184 ++-- lib/roles/quic/parse-quic.c | 55 +- lib/roles/quic/private-lib-roles-quic.h | 2 + lib/roles/wt/ops-wt.c | 14 +- lib/tls/gnutls/gnutls-quic.c | 2 + lib/tls/tls.c | 41 + .../minimal-webtransport-qir.c | 784 ++++++++++++------ .../CMakeLists.txt | 25 - .../minimal-webtransport-test-server.c | 78 -- .../protocol_lws_auth_dns.c | 3 +- .../protocol_webtransport_test/CMakeLists.txt | 4 + .../protocol_webtransport_test/assets/app.js | 71 +- .../assets/index.html | 9 - .../assets/style.css | 4 +- .../protocol_webtransport_test.c | 18 +- 22 files changed, 925 insertions(+), 513 deletions(-) delete mode 100644 minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/CMakeLists.txt delete mode 100644 minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/minimal-webtransport-test-server.c diff --git a/.sai.json b/.sai.json index 9279e68d43..d1fe05b8bd 100644 --- a/.sai.json +++ b/.sai.json @@ -321,7 +321,7 @@ }, "qir": { - "cmake": "cd /opt/quic-interop-runner ; source venv/bin/activate ; rm -rf logs_* ; export QIR_LWS_BRANCH=main ; cd /opt/quic-interop-runner/lws ; if [ \"`docker ps -q`\" ] ; then docker kill $(docker ps -q) ; fi ; docker system prune -a --volumes -f && docker build --build-arg QIR_LWS_BRANCH=${QIR_LWS_BRANCH} -t lws-quic-interop . --no-cache && cd .. && python3 run.py -c lws -s lws --delete-successful-logs", + "cmake": "cd /opt/quic-interop-runner ; source venv/bin/activate ; rm -rf logs_* ; export QIR_LWS_BRANCH=main ; cd /opt/quic-interop-runner/lws ; if [ \"`docker ps -q`\" ] ; then docker kill $(docker ps -q) ; fi ; docker system prune -a --volumes -f && docker build --build-arg QIR_LWS_BRANCH=${QIR_LWS_BRANCH} -t lws-quic-interop . --no-cache && cd .. && python3 run.py -c lws -s lws --delete-successful-logs ; python3 run.py -p webtransport -c lws -s lws --delete-successful-logs -t handshake,transfer-unidirectional-receive,transfer-unidirectional-send,transfer-bidirectional-receive,transfer-bidirectional-send,transfer-datagram-receive,transfer-datagram-send", "platforms": "none, linux-debian13/x86_64-amd/gcc" } diff --git a/include/libwebsockets/lws-x509.h b/include/libwebsockets/lws-x509.h index d9946a352a..0f9074f07a 100644 --- a/include/libwebsockets/lws-x509.h +++ b/include/libwebsockets/lws-x509.h @@ -240,6 +240,20 @@ LWS_VISIBLE LWS_EXTERN int lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, union lws_tls_cert_info_results *buf, size_t len); +/** + * lws_x509_cert_fingerprint() - Calculate the fingerprint of a certificate + * + * \param x509: the certificate to fingerprint + * \param type: the hash type (e.g., LWS_GENHASH_TYPE_SHA256) + * \param buf: buffer to receive the binary hash + * \param len: max length of the buffer + * + * Returns the hash length on success, or -1 on failure. + */ +LWS_VISIBLE LWS_EXTERN int +lws_x509_cert_fingerprint(struct lws_x509_cert *x509, int type, + uint8_t *buf, size_t len); + /** * lws_tls_peer_cert_info() - get information from the peer's TLS cert * diff --git a/lib/core-net/adopt.c b/lib/core-net/adopt.c index a797597098..8abb3b0882 100644 --- a/lib/core-net/adopt.c +++ b/lib/core-net/adopt.c @@ -805,6 +805,12 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads, if (sock.sockfd == LWS_SOCK_INVALID) goto resume; + { + int opt = 4 * 1024 * 1024; + setsockopt(sock.sockfd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)); + setsockopt(sock.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)); + } + if (lws_plat_apply_FD_CLOEXEC((int)sock.sockfd) || lws_plat_set_nonblocking(sock.sockfd)) { compatible_close(sock.sockfd); @@ -974,6 +980,12 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads, if (sock.sockfd == LWS_SOCK_INVALID) goto bail; + { + int opt = 4 * 1024 * 1024; + setsockopt(sock.sockfd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)); + setsockopt(sock.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)); + } + if (lws_plat_apply_FD_CLOEXEC((int)sock.sockfd) || lws_plat_set_nonblocking(sock.sockfd)) { compatible_close(sock.sockfd); diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 45882c181e..31b307cfa4 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -648,6 +648,11 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads, want_udp = (wsi->role_ops && !strcmp(wsi->role_ops->name, "quic") && !is_parallel); #endif new_fd = socket(wsi->sa46_peer.sa4.sin_family, want_udp ? SOCK_DGRAM : SOCK_STREAM, 0); + if (lws_socket_is_valid(new_fd) && want_udp) { + int opt = 4 * 1024 * 1024; + setsockopt(new_fd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)); + setsockopt(new_fd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)); + } } if (!lws_socket_is_valid(new_fd)) { diff --git a/lib/core-net/pollfd.c b/lib/core-net/pollfd.c index 344b32c89c..81a413bdcc 100644 --- a/lib/core-net/pollfd.c +++ b/lib/core-net/pollfd.c @@ -507,6 +507,11 @@ __lws_change_pollfd(struct lws *wsi, int _and, int _or) struct lws_pollargs pa; int ret = 0; + if (wsi && wsi->role_ops && !strcmp(wsi->role_ops->name, "quic")) { + lwsl_notice("AGY-DEBUG: __lws_change_pollfd for %s: position=%d, protocol=%p, event_pipe=%d, _and=%d, _or=%d\n", + lws_wsi_tag(wsi), wsi->position_in_fds_table, wsi->a.protocol, wsi->event_pipe, _and, _or); + } + if (!wsi || (!wsi->a.protocol && !wsi->event_pipe) || wsi->position_in_fds_table == LWS_NO_FDS_POS) return 0; diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index 4faaf9e084..cce4ba2093 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -1562,23 +1562,24 @@ struct lws *lws_wsi_mux_move_child_to_tail(struct lws **wsi2) { int lws_wsi_mux_action_pending_writeable_reqs(struct lws *wsi) { struct lws *w = wsi->mux.child_list; + struct lws *nwsi = lws_get_network_wsi(wsi); if (wsi->mux.requested_POLLOUT) { - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + if (lws_change_pollfd(nwsi, 0, LWS_POLLOUT)) return -1; return 0; } while (w) { if (w->mux.requested_POLLOUT) { - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + if (lws_change_pollfd(nwsi, 0, LWS_POLLOUT)) return -1; return 0; } w = w->mux.sibling_list; } - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + if (lws_change_pollfd(nwsi, LWS_POLLOUT, 0)) return -1; return 0; diff --git a/lib/roles/h3/ops-h3.c b/lib/roles/h3/ops-h3.c index ccbf850ce4..55385d9260 100644 --- a/lib/roles/h3/ops-h3.c +++ b/lib/roles/h3/ops-h3.c @@ -1692,52 +1692,87 @@ rops_check_upgrades_h3(struct lws *wsi) if (!env_protocols) env_protocols = getenv("PROTOCOLS"); - /* client_protos format: '"proto1", "proto2"' */ - char *cp_ptr = client_protos; - char *token; - while ((token = strsep(&cp_ptr, ","))) { - while (*token == ' ' || *token == '"') token++; - char *t_end = token + strlen(token); - while (t_end > token && (t_end[-1] == ' ' || t_end[-1] == '"' || t_end[-1] == '\r' || t_end[-1] == '\n')) { - t_end[-1] = '\0'; - t_end--; - } - if (!*token) continue; - - if (env_protocols) { - char server_protos[256]; - lws_strncpy(server_protos, env_protocols, sizeof(server_protos)); - char *sp_ptr = server_protos; - char *sp_tok; - while ((sp_tok = strsep(&sp_ptr, " "))) { - if (strcmp(token, sp_tok) == 0) { - lws_strncpy(negotiated, token, sizeof(negotiated)); - break; + struct lws_tokenize ts; + lws_tokenize_init(&ts, client_protos, LWS_TOKENIZE_F_COMMA_SEP_LIST | + LWS_TOKENIZE_F_MINUS_NONTERM); + ts.len = (unsigned int)cp_len; + lws_tokenize_elem e; + + do { + e = lws_tokenize(&ts); + if (e == LWS_TOKZE_TOKEN || e == LWS_TOKZE_QUOTED_STRING) { + char name[64]; + if (!lws_tokenize_cstr(&ts, name, sizeof(name))) { + if (env_protocols) { + struct lws_tokenize ts_srv; + lws_tokenize_init(&ts_srv, env_protocols, LWS_TOKENIZE_F_MINUS_NONTERM); + lws_tokenize_elem e_srv; + do { + e_srv = lws_tokenize(&ts_srv); + if (e_srv == LWS_TOKZE_TOKEN || e_srv == LWS_TOKZE_QUOTED_STRING) { + char srv_name[64]; + if (!lws_tokenize_cstr(&ts_srv, srv_name, sizeof(srv_name))) { + if (strcmp(name, srv_name) == 0) { + lws_strncpy(negotiated, name, sizeof(negotiated)); + break; + } + } + } + } while (e_srv > 0); } } } if (negotiated[0]) break; + } while (e > 0); + } + + if (!negotiated[0]) { + char *uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH); + int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH); + lwsl_notice("H3 WT Upgrade: path '%.*s'\n", uri_len, uri_ptr ? uri_ptr : "NULL"); + if (uri_ptr && uri_len > 0) { + const struct lws_http_mount *hit = lws_find_mount(wsi, uri_ptr, uri_len); + if (hit) { + lwsl_notice("H3 WT Upgrade: matched mount '%s', origin '%s', protocol '%s'\n", + hit->mountpoint, hit->origin ? hit->origin : "NULL", + hit->protocol ? hit->protocol : "NULL"); + const char *name = hit->origin; + if (hit->protocol) + name = hit->protocol; + else if (!strncmp(name, "callback://", 11)) + name += 11; + + lws_strncpy(negotiated, name, sizeof(negotiated)); + } else { + lwsl_notice("H3 WT Upgrade: no mount matched path\n"); + } } } + const struct lws_protocols *prot = NULL; if (negotiated[0]) { + prot = lws_vhost_name_to_protocol(wsi->a.vhost, negotiated); + } + if (!prot) { + int n = wsi->a.vhost->default_protocol_index; + if (n < wsi->a.vhost->count_protocols) { + prot = &wsi->a.vhost->protocols[n]; + } + } + + if (prot) { + wsi->a.protocol = prot; + lwsl_notice("H3 WT Upgrade: bound to protocol '%s'\n", prot->name); + char wt_prot_val[128]; - int wpl = lws_snprintf(wt_prot_val, sizeof(wt_prot_val), "\"%s\"", negotiated); + int wpl = lws_snprintf(wt_prot_val, sizeof(wt_prot_val), "\"%s\"", prot->name); if (lws_add_http_header_by_name(wsi, (const unsigned char *)"wt-protocol:", (const unsigned char *)wt_prot_val, wpl, &rp, end)) return LWS_UPG_RET_BAIL; - - /* Save negotiated protocol to /downloads/negotiated_protocol.txt */ - mkdir("/downloads", 0777); - int nfd = open("/downloads/negotiated_protocol.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (nfd >= 0) { - if (write(nfd, negotiated, strlen(negotiated)) < 0) { - lwsl_err("Failed to write negotiated protocol\n"); - } - close(nfd); - } + } else { + lwsl_notice("H3 WT Upgrade: no WebTransport protocol found on vhost\n"); } if (lws_finalize_http_header(wsi, &rp, end)) diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index edf30ed492..8149e9712d 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -286,7 +286,7 @@ lws_quic_handle_ack(struct lws *nwsi, int level, uint64_t acked_pn) { struct lws_quic_cc_newreno *_cc = qn->cc_ops ? (struct lws_quic_cc_newreno *)qn->cc_state : NULL; (void)_cc; - lwsl_notice("QUIC TX: ACK processing: bytes_acked=%zu, cc_bif=%zu cc_cwnd=%zu cc_pacing=%zu, in_flight_app=%d, pending_tx=%d, blocked=%d\n", + lwsl_debug("QUIC TX: ACK processing: bytes_acked=%zu, cc_bif=%zu cc_cwnd=%zu cc_pacing=%zu, in_flight_app=%d, pending_tx=%d, blocked=%d\n", bytes_acked, _cc ? _cc->bytes_in_flight : 0, _cc ? _cc->cwnd : 0, @@ -1059,11 +1059,13 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, int pn_space = (level == LWS_QUIC_LEVEL_EARLY || level == LWS_QUIC_LEVEL_APP) ? LWS_QUIC_LEVEL_APP : level; - /* Enforce 1200-byte padding for subsequent client-to-server Initial packets (RFC 9000 Section 14.1) */ - if (level == LWS_QUIC_LEVEL_INITIAL && nwsi && nwsi->quic.qn && nwsi->quic.qn->is_server && n < 1200) { - lwsl_wsi_notice(wsi, "QUIC RX: Dropping under-padded Initial packet (len %d)", n); - break; - } + /* + * Enforce 1200-byte padding for subsequent client-to-server Initial packets (RFC 9000 Section 14.1) + * Note: RFC 9000 allows the client to stop padding Initial packets once it has received an + * acknowledgment from the server or sent/received a packet other than Initial/VN. Therefore, + * we MUST NOT discard subsequent unpadded Initial packets. The first Initial packet (which + * instantiates the connection) is already checked and validated for 1200-byte padding earlier. + */ size_t local_dcid_len = (nwsi && nwsi->quic.qn) ? nwsi->quic.qn->loc_cid.len : 8; if (local_dcid_len == 0) local_dcid_len = 8; @@ -1315,6 +1317,18 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi, nwsi = lws_get_quic_network_wsi(nwsi); } + if (nwsi) { + struct lws *w = nwsi->mux.child_list; + while (w) { + struct lws *next = w->mux.sibling_list; + if (w->quic.qs && w->quic.qs->close_after_rx) { + lwsl_wsi_notice(w, "QUIC RX Post-Processing: Closing stream WSI"); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "quic post rx stream close"); + } + w = next; + } + } + if (parse_res < 0) { lwsl_wsi_notice(wsi, "QUIC RX: Frame parsing aborted"); if (parse_res == -3) { @@ -1615,11 +1629,15 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } } - /* Packet lost! Move it back to pending_tx */ + /* Packet lost! */ lws_dll2_remove(&f->list); - lws_dll2_add_head(&f->list, &qn->pending_tx[level]); total_bytes_lost += f->wire_len; - f->wire_len = 0; + if ((f->type & 0xfe) == LWS_QUIC_FT_DATAGRAM) { + lws_free(f); + } else { + lws_dll2_add_head(&f->list, &qn->pending_tx[level]); + f->wire_len = 0; + } } } lws_end_foreach_dll_safe(d, d1); } @@ -1659,7 +1677,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) continue; } - // lwsl_wsi_notice(wsi, "QUIC TX: Processing level %d. pending=%d, needs_ack=%d, pto_needed=%d", level, qn->pending_tx[level].count, qn->needs_ack[pn_space], qn->pto_probe_needed); + lwsl_debug("QUIC TX: Processing level %d. pending=%d, needs_ack=%d, pto_needed=%d", level, qn->pending_tx[level].count, qn->needs_ack[pn_space], qn->pto_probe_needed); /* Determine if we're allowed to send */ int is_congestion_limited = 0; @@ -1885,6 +1903,8 @@ rops_handle_POLLOUT_quic(struct lws *wsi) if ((f->type & 0xf8) == LWS_QUIC_FT_STREAM || f->type == LWS_QUIC_FT_CRYPTO) { send_len = max_udp_payload - (size_t)(p - pkt) - frame_header_max_len - 32; } else { + lwsl_notice("QUIC TX: Frame doesn't fit! type=0x%02x, len=%zu, p-pkt=%zu, max_udp_payload=%zu", + f->type, send_len, (size_t)(p - pkt), max_udp_payload); break; /* Non-fragmentable frame doesn't fit */ } } @@ -1914,6 +1934,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) if (type & 0x02) /* LEN */ p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), send_len); } else if ((type & 0xfe) == LWS_QUIC_FT_DATAGRAM) { + lwsl_debug("QUIC TX: Serialized DATAGRAM frame! len=%zu", send_len); if (type & 0x01) /* LEN */ p += lws_quic_write_varint(p, sizeof(pkt) - (size_t)(p - pkt), send_len); } else if (type == LWS_QUIC_FT_MAX_DATA || type == LWS_QUIC_FT_DATA_BLOCKED) { @@ -1987,7 +2008,6 @@ rops_handle_POLLOUT_quic(struct lws *wsi) f->wire_len = 0; lws_dll2_add_tail(&f->list, &qn->in_flight[level]); - /* Schedule PTO timer since we have data in-flight */ lws_sul_schedule(wsi->a.context, 0, &qn->pto_sul, lws_quic_pto_cb, pto_delay); } @@ -2072,25 +2092,39 @@ rops_handle_POLLOUT_quic(struct lws *wsi) lwsl_wsi_debug(wsi, "QUIC TX: Dropping packet via lws_fi fault injection!"); n = (int)send_len; /* Pretend it succeeded */ } else { -#if defined(WIN32) || defined(_WIN32) + const lws_sockaddr46 *dest_sa46 = NULL; if (has_packet_dest) + dest_sa46 = &packet_dest_sa46; + else if (wsi->udp) + dest_sa46 = &wsi->udp->sa46; + else if (wsi->mux_substream && wsi->mux.parent_wsi && wsi->mux.parent_wsi->udp) + dest_sa46 = &wsi->mux.parent_wsi->udp->sa46; + + { + char adrbuf[64] = "none"; + if (dest_sa46) + lws_sa46_write_numeric_address((lws_sockaddr46 *)dest_sa46, adrbuf, sizeof(adrbuf)); + lwsl_debug("QUIC TX: sendto fd=%d, len=%zu, dest=%s, substream=%d, udp=%p", + fd, send_len, adrbuf, wsi->mux_substream, wsi->udp); + } +#if defined(WIN32) || defined(_WIN32) + if (dest_sa46) n = sendto(fd, (const char *)pkt, (int)send_len, 0, - sa46_sockaddr(&packet_dest_sa46), sa46_socklen(&packet_dest_sa46)); - else if (wsi->mux_substream && wsi->udp) - n = sendto(fd, (const char *)pkt, (int)send_len, 0, - sa46_sockaddr(&wsi->udp->sa46), sa46_socklen(&wsi->udp->sa46)); + sa46_sockaddr(dest_sa46), sa46_socklen(dest_sa46)); else n = send(fd, (const char *)pkt, (int)send_len, 0); #else - if (has_packet_dest) - n = (int)sendto(fd, (const void *)pkt, send_len, 0, - sa46_sockaddr(&packet_dest_sa46), sa46_socklen(&packet_dest_sa46)); - else if (wsi->mux_substream && wsi->udp) + if (dest_sa46) n = (int)sendto(fd, (const void *)pkt, send_len, 0, - sa46_sockaddr(&wsi->udp->sa46), sa46_socklen(&wsi->udp->sa46)); + sa46_sockaddr(dest_sa46), sa46_socklen(dest_sa46)); else n = (int)send(fd, (const void *)pkt, send_len, 0); #endif + if (n < 0) { + lwsl_warn("QUIC TX: sendto/send failed: returned %d, errno=%d\n", n, LWS_ERRNO); + } else { + lwsl_debug("QUIC TX: sent %d bytes successfully\n", n); + } } if (n < 0) { int e = LWS_ERRNO; @@ -2225,9 +2259,9 @@ rops_handle_POLLOUT_quic(struct lws *wsi) { struct lws *curr = wsi->mux.child_list; int sanity = 1000000; - lwsl_notice("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); + lwsl_debug("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); while (curr && sanity--) { - lwsl_notice("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", + lwsl_debug("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", lws_wsi_tag(curr), curr->mux.requested_POLLOUT, (int)curr->txc.tx_cr); curr = curr->mux.sibling_list; } @@ -2244,7 +2278,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) wa = &(*wsi2)->mux.sibling_list; - lwsl_notice("QUIC TX POLLOUT: visiting child %s, requested_POLLOUT=%d\n", lws_wsi_tag(*wsi2), (*wsi2)->mux.requested_POLLOUT); + lwsl_debug("QUIC TX POLLOUT: visiting child %s, requested_POLLOUT=%d\n", lws_wsi_tag(*wsi2), (*wsi2)->mux.requested_POLLOUT); if (!(*wsi2)->mux.requested_POLLOUT) goto next_child; @@ -2297,7 +2331,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) w->mux.requested_POLLOUT = 0; - lwsl_notice("QUIC TX POLLOUT: calling perform_user_POLLOUT for child %s, usable_credit=%d, pending_tx=%d\n", + lwsl_debug("QUIC TX POLLOUT: calling perform_user_POLLOUT for child %s, usable_credit=%d, pending_tx=%d\n", lws_wsi_tag(w), (int)usable_credit, qn->pending_tx[LWS_QUIC_LEVEL_APP].count); lwsl_debug("QUIC TX POLLOUT: calling perform_user_POLLOUT/lws_callback_as_writeable for child %s\n", lws_wsi_tag(w)); @@ -2337,11 +2371,6 @@ rops_handle_POLLOUT_quic(struct lws *wsi) int can_process_children = ((qn->handshake_done || qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED || qn->early_data_status == LWS_0RTT_STATUS_ACCEPTED) && wsi->txc.tx_cr > 0 && (!nwsi || nwsi->txc.tx_cr > 0)); int have_pending_tx = 0; for (level = 0; level < LWS_QUIC_LEVEL_COUNT; level++) { - if (qn->pending_tx[level].count) { - lwsl_notice("AGY-DEBUG: pending_tx[%d].count = %d, keys = %p, hp_len = %d\n", - level, qn->pending_tx[level].count, qn->keys[level], - qn->keys[level] ? (int)qn->keys[level]->el_hp_tx.len : -1); - } if (qn->pending_tx[level].count && qn->keys[level] && qn->keys[level]->el_hp_tx.len) { if (level == LWS_QUIC_LEVEL_APP && qn->migration_probing_wsi) { lws_start_foreach_dll(struct lws_dll2 *, d, qn->pending_tx[level].head) { @@ -2365,11 +2394,6 @@ rops_handle_POLLOUT_quic(struct lws *wsi) return LWS_HP_RET_BAIL_DIE; for (level = 0; level < LWS_QUIC_LEVEL_COUNT; level++) { - if (qn->pending_tx[level].count) { - lwsl_notice("AGY-DEBUG2: pending_tx[%d].count = %d, keys = %p, hp_len = %d\n", - level, qn->pending_tx[level].count, qn->keys[level], - qn->keys[level] ? (int)qn->keys[level]->el_hp_tx.len : -1); - } if (qn->pending_tx[level].count && qn->keys[level] && qn->keys[level]->el_hp_tx.len) { if (level == LWS_QUIC_LEVEL_APP && qn->migration_probing_wsi) { lws_start_foreach_dll(struct lws_dll2 *, d, qn->pending_tx[level].head) { @@ -2403,7 +2427,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) } if (((blocked && !eagain_blocked) || !have_pending_tx) && !children_need_POLLOUT) { - lwsl_notice("QUIC TX: dropping POLLOUT manually for %s (blocked=%d, eagain_blocked=%d, have_pending_tx=%d)\n", lws_wsi_tag(wsi), blocked, eagain_blocked, have_pending_tx); + lwsl_debug("QUIC TX: dropping POLLOUT manually for %s (blocked=%d, eagain_blocked=%d, have_pending_tx=%d)\n", lws_wsi_tag(wsi), blocked, eagain_blocked, have_pending_tx); /* We are blocked by QUIC limits, or have nothing to send right now. * Stop asking the OS for POLLOUT. We will re-enable it if children * need to write. */ @@ -2411,8 +2435,20 @@ rops_handle_POLLOUT_quic(struct lws *wsi) return LWS_HP_RET_BAIL_DIE; } - if (have_pending_tx) - return LWS_HP_RET_BAIL_OK; + { + struct lws *w = wsi->mux.child_list; + while (w) { + struct lws *next = w->mux.sibling_list; + if (w->quic.qs && w->quic.qs->close_after_rx) { + lwsl_wsi_notice(w, "QUIC TX Post-Processing: Closing stream WSI"); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "quic post tx stream close"); + } + w = next; + } + } + + if (have_pending_tx) + return LWS_HP_RET_BAIL_OK; /* * Check if any child still needs writeable service. @@ -2491,25 +2527,13 @@ rops_write_role_protocol_quic(struct lws *wsi, unsigned char *buf, size_t len, lwsl_debug("QUIC TX WRITE: Stream %llu. Requested: %d, Stream tx_cr: %d, Conn tx_cr: %d\n", wsi->quic.qs ? (unsigned long long)wsi->quic.qs->stream_id : 0, (int)len, (int)wsi->txc.tx_cr, nwsi ? (int)nwsi->txc.tx_cr : -1); - // lwsl_notice("%s: allocating frame of size %d\n", __func__, (int)(sizeof(*f) + len)); - /* Allocate frame struct + payload buffer natively */ f = lws_zalloc(sizeof(*f) + len, "quic tx frame"); if (!f) return -1; - f->type = LWS_QUIC_FT_STREAM | 0x02 | 0x04; /* STREAM | OFF | LEN */ - if (((*wp) & LWS_WRITE_H2_STREAM_END) || - ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL) { - f->type |= 0x01; /* FIN */ - /* wsi->quic.qs->sent_fin = 1; could do if we had sent_fin flag */ - } f->data = (uint8_t *)&f[1]; f->len = len; - - // lwsl_notice("%s: copying data from buf=%p to f->data=%p\n", __func__, buf, f->data); - /* Copy the user payload */ memcpy(f->data, buf, len); - // lwsl_notice("%s: copied data\n", __func__); if (((*wp) & 0x1f) == LWS_WRITE_QUIC_DATAGRAM) { /* It's a DATAGRAM frame */ @@ -2518,6 +2542,18 @@ rops_write_role_protocol_quic(struct lws *wsi, unsigned char *buf, size_t len, f->offset = 0; lwsl_debug("QUIC TX WRITE: Datagram len=%u\n", (unsigned int)f->len); } else { + f->type = LWS_QUIC_FT_STREAM | 0x02 | 0x04; /* STREAM | OFF | LEN */ + if (((*wp) & LWS_WRITE_H2_STREAM_END) || + ((*wp) & 0x1f) == LWS_WRITE_HTTP_FINAL) { + f->type |= 0x01; /* FIN */ + if (wsi->quic.qs) { + wsi->quic.qs->sent_fin = 1; + if (wsi->quic.qs->is_unidirectional || wsi->quic.qs->fin_received) { + wsi->quic.qs->close_after_rx = 1; + } + } + } + if (!wsi->quic.qs) { lwsl_wsi_err(wsi, "QUIC: Cannot send stream data without a quic stream structure!"); lws_free(f); @@ -2550,8 +2586,8 @@ rops_write_role_protocol_quic(struct lws *wsi, unsigned char *buf, size_t len, tx_level = LWS_QUIC_LEVEL_EARLY; } - lwsl_info("QUIC TX: Enqueued STREAM frame for sid %llu, len %d, FIN=%d (level %d)\n", - (unsigned long long)f->stream_id, (int)f->len, (f->type & 0x01), tx_level); + lwsl_debug("QUIC TX: Enqueued frame type=0x%02x, len=%d, tx_level=%d, pending_count=%d", + f->type, (int)f->len, tx_level, qn->pending_tx[tx_level].count + 1); lws_dll2_add_tail(&f->list, &qn->pending_tx[tx_level]); /* Wake up the event loop to send the packet */ @@ -2840,7 +2876,7 @@ lws_quic_stream_cleanup(struct lws *wsi) if (qn) { uint64_t sid = wsi->quic.qs->stream_id; - int is_abort = (!wsi->quic.qs->fin_received || !wsi->quic.qs->fin_delivered); + int is_abort = (!wsi->quic.qs->fin_received || !wsi->quic.qs->sent_fin); /* If this is a unidirectional stream, adjust abort logic */ int is_unidirectional = (sid & 2) != 0; @@ -2848,7 +2884,7 @@ lws_quic_stream_cleanup(struct lws *wsi) int we_are_sender = (qn->is_server == is_remote_initiated) ? 1 : 0; if (is_unidirectional) { we_are_sender = is_remote_initiated ? 0 : 1; - is_abort = we_are_sender ? !wsi->quic.qs->fin_delivered : !wsi->quic.qs->fin_received; + is_abort = we_are_sender ? !wsi->quic.qs->sent_fin : !wsi->quic.qs->fin_received; } /* If we're closing the stream before FINs were exchanged, notify the peer */ @@ -2922,7 +2958,7 @@ lws_quic_stream_cleanup(struct lws *wsi) f_max->limit = is_unidirectional ? qn->max_streams_unidi_local : qn->max_streams_bidi_local; - lwsl_notice("QUIC TX: Queued MAX_STREAMS_%s with limit %llu\n", + lwsl_debug("QUIC TX: Queued MAX_STREAMS_%s with limit %llu\n", is_unidirectional ? "UNIDI" : "BIDI", (unsigned long long)f_max->limit); lws_dll2_add_tail(&f_max->list, @@ -3090,7 +3126,9 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) if (wsi->quic.qs) { wsi->quic.qs->rx_max_data += (uint64_t)(add > 0 ? add : 0); - uint64_t ungranted = wsi->quic.qs->advertised_rx_max_data - wsi->quic.qs->highest_rx_offset; + uint64_t ungranted = 0; + if (wsi->quic.qs->advertised_rx_max_data > wsi->quic.qs->highest_rx_offset) + ungranted = wsi->quic.qs->advertised_rx_max_data - wsi->quic.qs->highest_rx_offset; if (nwsi->a.context->quic_tx_credit_cb) { uint64_t new_win = nwsi->a.context->quic_tx_credit_cb( @@ -3099,7 +3137,9 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) if (new_win > wsi->quic.qs->rx_window_size && new_win <= LWS_QUIC_MAX_WINDOW) { wsi->quic.qs->rx_max_data += (new_win - wsi->quic.qs->rx_window_size); wsi->quic.qs->rx_window_size = new_win; - ungranted = wsi->quic.qs->advertised_rx_max_data - wsi->quic.qs->highest_rx_offset; + ungranted = 0; + if (wsi->quic.qs->advertised_rx_max_data > wsi->quic.qs->highest_rx_offset) + ungranted = wsi->quic.qs->advertised_rx_max_data - wsi->quic.qs->highest_rx_offset; } } @@ -3118,7 +3158,9 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) } qn->rx_max_data += (uint64_t)(add > 0 ? add : 0); - uint64_t ungranted = qn->advertised_rx_max_data - qn->highest_rx_offset; + uint64_t ungranted = 0; + if (qn->advertised_rx_max_data > qn->highest_rx_offset) + ungranted = qn->advertised_rx_max_data - qn->highest_rx_offset; if (nwsi->a.context->quic_tx_credit_cb) { uint64_t new_win = nwsi->a.context->quic_tx_credit_cb( @@ -3127,7 +3169,9 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) if (new_win > qn->rx_window_size && new_win <= LWS_QUIC_MAX_WINDOW) { qn->rx_max_data += (new_win - qn->rx_window_size); qn->rx_window_size = new_win; - ungranted = qn->advertised_rx_max_data - qn->highest_rx_offset; + ungranted = 0; + if (qn->advertised_rx_max_data > qn->highest_rx_offset) + ungranted = qn->advertised_rx_max_data - qn->highest_rx_offset; } } @@ -3235,11 +3279,13 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) */ if (cr > 9) cr -= 9; + else if (cr > 0) + cr = 1; else cr = 0; if (!cr && !add) - lwsl_notice("rops_tx_credit_quic: US_TO_PEER returning 0: " + lwsl_debug("rops_tx_credit_quic: US_TO_PEER returning 0: " "wsi_tx_cr=%d, nwsi_tx_cr=%d, queued=%zu, " "wsi=%s, nwsi=%s\n", (int)wsi->txc.tx_cr, (int)nwsi->txc.tx_cr, @@ -3358,10 +3404,22 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) /* Initialize flow control credits for the new child stream */ int32_t init_cr = nwsi->txc.manual_initial_tx_credit; if (!init_cr) { - if (nwsi->quic.qn && nwsi->quic.qn->peer_initial_max_stream_data_bidi_remote) { - uint64_t val = nwsi->quic.qn->peer_initial_max_stream_data_bidi_remote; + int is_bidi = (wsi->quic.qs && wsi->quic.qs->is_unidirectional == 0); + int is_remote_initiated = (wsi->quic.qs && wsi->quic.qs->is_server_initiated == nwsi->quic.qn->is_server ? 0 : 1); + uint64_t val = 0; + if (nwsi->quic.qn) { + if (is_bidi) { + if (is_remote_initiated) + val = nwsi->quic.qn->peer_initial_max_stream_data_bidi_local; + else + val = nwsi->quic.qn->peer_initial_max_stream_data_bidi_remote; + } else { + val = nwsi->quic.qn->peer_initial_max_stream_data_uni; + } + } + if (val) init_cr = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; - } else + else init_cr = 65535; } wsi->txc.peer_tx_cr_est = init_cr; diff --git a/lib/roles/quic/parse-quic.c b/lib/roles/quic/parse-quic.c index 96bc3ffe82..f2c32c309d 100644 --- a/lib/roles/quic/parse-quic.c +++ b/lib/roles/quic/parse-quic.c @@ -287,6 +287,19 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ } } #endif + if (wsi_child && wsi_child->role_ops && (!strcmp(wsi_child->role_ops->name, "wt") || !strcmp(wsi_child->role_ops->name, "quic"))) { + if (qs->is_unidirectional) { + lwsl_wsi_notice(wsi_child, "QUIC/WT unidirectional stream received FIN. Flagging close_after_rx"); + qs->close_after_rx = 1; + } else { + if (qs->sent_fin) { + lwsl_wsi_notice(wsi_child, "QUIC/WT bidi stream received FIN (sent_fin=1). Flagging close_after_rx"); + qs->close_after_rx = 1; + } else { + lwsl_wsi_notice(wsi_child, "QUIC/WT bidi stream received FIN but sent_fin=0. Keeping open."); + } + } + } } } @@ -364,6 +377,19 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_ } } #endif + if (wsi_child && wsi_child->role_ops && (!strcmp(wsi_child->role_ops->name, "wt") || !strcmp(wsi_child->role_ops->name, "quic"))) { + if (qs->is_unidirectional) { + lwsl_wsi_notice(wsi_child, "QUIC/WT unidirectional stream received FIN (flushed). Flagging close_after_rx"); + qs->close_after_rx = 1; + } else { + if (qs->sent_fin) { + lwsl_wsi_notice(wsi_child, "QUIC/WT bidi stream received FIN (flushed, sent_fin=1). Flagging close_after_rx"); + qs->close_after_rx = 1; + } else { + lwsl_wsi_notice(wsi_child, "QUIC/WT bidi stream received FIN (flushed) but sent_fin=0. Keeping open."); + } + } + } } } @@ -578,7 +604,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl /* Process the First ACK Range */ uint64_t pn = largest_ack; - if (first_ack_range > 4096 || first_ack_range > pn) { + if (first_ack_range > pn) { lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_FRAME_ENCODING_ERROR, type, 0); return -1; } @@ -610,7 +636,7 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl } pn -= gap + 1; - if (ack_range > 4096 || ack_range > pn) { + if (ack_range > pn) { lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_FRAME_ENCODING_ERROR, type, 0); return -1; } @@ -1005,14 +1031,16 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl lwsl_wsi_notice(nwsi, "QUIC RX: Truncated DATAGRAM frame"); return -1; } - lwsl_wsi_info(nwsi, "QUIC RX: Parsed DATAGRAM! len %llu", (unsigned long long)datagram_len); + lwsl_wsi_notice(nwsi, "QUIC RX: Parsed DATAGRAM! len %llu", (unsigned long long)datagram_len); /* Parse Quarter Session ID for WebTransport */ uint64_t qsid = 0; size_t qsid_len = lws_quic_parse_varint(&payload[pos], (size_t)datagram_len, &qsid); + lwsl_wsi_notice(nwsi, "QUIC RX: datagram qsid=%llu, qsid_len=%zu", (unsigned long long)qsid, qsid_len); if (qsid_len > 0 && qsid_len <= (size_t)datagram_len) { uint64_t sid = qsid * 4; struct lws *wsi_session = lws_quic_stream_find(nwsi, sid); + lwsl_wsi_notice(nwsi, "QUIC RX: wsi_session found=%p (is_session=%d)", wsi_session, wsi_session ? wsi_session->wt.is_session : 0); if (wsi_session && wsi_session->wt.is_session && wsi_session->a.protocol && wsi_session->a.protocol->callback) { /* Route the datagram payload to the WebTransport session's callback */ wsi_session->a.protocol->callback(wsi_session, LWS_CALLBACK_RECEIVE, @@ -1160,15 +1188,15 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl int is_remote_initiated = (wsi_child->quic.qs->is_server_initiated == nwsi->quic.qn->is_server ? 0 : 1); if (is_bidi) { if (is_remote_initiated) { - if (qn->peer_initial_max_stream_data_bidi_remote) { - uint64_t val = qn->peer_initial_max_stream_data_bidi_remote; + if (qn->peer_initial_max_stream_data_bidi_local) { + uint64_t val = qn->peer_initial_max_stream_data_bidi_local; int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; wsi_child->txc.peer_tx_cr_est = cval; wsi_child->txc.tx_cr = cval; } } else { - if (qn->peer_initial_max_stream_data_bidi_local) { - uint64_t val = qn->peer_initial_max_stream_data_bidi_local; + if (qn->peer_initial_max_stream_data_bidi_remote) { + uint64_t val = qn->peer_initial_max_stream_data_bidi_remote; int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; wsi_child->txc.peer_tx_cr_est = cval; wsi_child->txc.tx_cr = cval; @@ -1276,11 +1304,18 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl if (!is_peer_initiated) { if (!wsi_child) { - lwsl_wsi_notice(nwsi, "QUIC RX: %s on non-existing stream ID %llu", + uint64_t next_id = is_unidirectional ? qn->next_stream_id_unidi_local : qn->next_stream_id_bidi_local; + if (stream_id >= next_id) { + lwsl_wsi_notice(nwsi, "QUIC RX: %s on never-created stream ID %llu", + type == LWS_QUIC_FT_MAX_STREAM_DATA ? "MAX_STREAM_DATA" : "STREAM_DATA_BLOCKED", + (unsigned long long)stream_id); + lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_STREAM_STATE_ERROR, type, 0); + return -1; + } + lwsl_wsi_info(nwsi, "QUIC RX: Ignoring %s on already closed stream ID %llu", type == LWS_QUIC_FT_MAX_STREAM_DATA ? "MAX_STREAM_DATA" : "STREAM_DATA_BLOCKED", (unsigned long long)stream_id); - lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_STREAM_STATE_ERROR, type, 0); - return -1; + break; } if (is_unidirectional && type == LWS_QUIC_FT_STREAM_DATA_BLOCKED) { lwsl_wsi_notice(nwsi, "QUIC RX: STREAM_DATA_BLOCKED on receive-only stream ID %llu", diff --git a/lib/roles/quic/private-lib-roles-quic.h b/lib/roles/quic/private-lib-roles-quic.h index b26071510a..302ff40540 100644 --- a/lib/roles/quic/private-lib-roles-quic.h +++ b/lib/roles/quic/private-lib-roles-quic.h @@ -211,6 +211,8 @@ struct lws_quic_stream { uint8_t is_unidirectional:1; uint8_t is_server_initiated:1; uint8_t opted_into_early_data:1; + uint8_t close_after_rx:1; + uint8_t sent_fin:1; }; diff --git a/lib/roles/wt/ops-wt.c b/lib/roles/wt/ops-wt.c index a44d04741f..af9989c25c 100644 --- a/lib/roles/wt/ops-wt.c +++ b/lib/roles/wt/ops-wt.c @@ -38,11 +38,13 @@ rops_handle_POLLIN_wt(struct lws_context_per_thread *pt, struct lws *wsi, return LWS_HPI_RET_HANDLED; } +struct lws *lws_get_quic_network_wsi(struct lws *wsi); + static int rops_write_role_protocol_wt(struct lws *wsi, unsigned char *buf, size_t len, enum lws_write_protocol *wp) { - struct lws *nwsi = lws_get_network_wsi(wsi); + struct lws *nwsi = lws_get_quic_network_wsi(wsi); if (!nwsi) return -1; @@ -88,6 +90,9 @@ rops_close_kill_connection_wt(struct lws *wsi, enum lws_close_status reason) if (wsi->wt.wtn) { lws_free_set_NULL(wsi->wt.wtn); } + if (wsi->mux.parent_wsi) { + lws_wsi_mux_sibling_disconnect(wsi); + } return 0; } @@ -232,7 +237,7 @@ rops_callback_on_writable_wt(struct lws *wsi) { struct lws *nwsi = lws_get_network_wsi(wsi); - lwsl_notice("rops_callback_on_writable_wt called for %s (nwsi=%s)\n", + lwsl_debug("rops_callback_on_writable_wt called for %s (nwsi=%s)\n", lws_wsi_tag(wsi), nwsi ? lws_wsi_tag(nwsi) : "none"); if (!nwsi) @@ -243,12 +248,15 @@ rops_callback_on_writable_wt(struct lws *wsi) return lws_callback_on_writable(nwsi); } +extern int rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add); + static const lws_rops_t rops_table_wt[] = { /* 1 */ { .handle_POLLIN = rops_handle_POLLIN_wt }, /* 2 */ { .write_role_protocol = rops_write_role_protocol_wt }, /* 3 */ { .close_kill_connection = rops_close_kill_connection_wt }, /* 4 */ { .perform_user_POLLOUT = rops_perform_user_POLLOUT_wt }, /* 5 */ { .callback_on_writable = rops_callback_on_writable_wt }, + /* 6 */ { .tx_credit = rops_tx_credit_quic }, }; const struct lws_role_ops role_ops_wt = { @@ -266,7 +274,7 @@ const struct lws_role_ops role_ops_wt = { /* LWS_ROPS_handle_POLLOUT */ /* LWS_ROPS_perform_user_POLLOUT */ 0x04, /* LWS_ROPS_callback_on_writable */ - /* LWS_ROPS_tx_credit */ 0x50, + /* LWS_ROPS_tx_credit */ 0x56, /* LWS_ROPS_write_role_protocol */ /* LWS_ROPS_encapsulation_parent */ 0x20, /* LWS_ROPS_alpn_negotiated */ diff --git a/lib/tls/gnutls/gnutls-quic.c b/lib/tls/gnutls/gnutls-quic.c index 814d16ca8a..f37982d78e 100644 --- a/lib/tls/gnutls/gnutls-quic.c +++ b/lib/tls/gnutls/gnutls-quic.c @@ -323,6 +323,8 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) *p++ = 0x09; *p++ = 0x02; *p++ = 0x40; *p++ = 0x64; /* max_idle_timeout (0x01), len 4, val 30000 */ *p++ = 0x01; *p++ = 0x04; *p++ = 0x80; *p++ = 0x00; *p++ = 0x75; *p++ = 0x30; + /* max_datagram_frame_size (0x20), len 4, val 65535 */ + *p++ = 0x20; *p++ = 0x04; *p++ = 0x80; *p++ = 0x00; *p++ = 0xff; *p++ = 0xff; /* initial_source_connection_id (0x0f) */ if (qn && qn->loc_cid.len > 0) { if ((size_t)(p - dynamic_tp) + 2 + qn->loc_cid.len > sizeof(dynamic_tp)) { diff --git a/lib/tls/tls.c b/lib/tls/tls.c index 535e659fa6..8095d87c0c 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -748,6 +748,47 @@ lws_tls_cert_get_x509_remaining(struct lws_context *context, const char *filepat return res; } +int +lws_x509_cert_fingerprint(struct lws_x509_cert *x509, int type, + uint8_t *buf, size_t len) +{ + union lws_tls_cert_info_results *res; + struct lws_genhash_ctx hctx; + size_t hash_len; + uint8_t *der; + int ret = -1; + + hash_len = lws_genhash_size(type); + if (!hash_len || len < hash_len) + return -1; + + der = lws_malloc(4096, "cert_fingerprint_der"); + if (!der) + return -1; + + res = (union lws_tls_cert_info_results *)der; + + if (lws_x509_info(x509, LWS_TLS_CERT_INFO_DER_RAW, res, 4096)) + goto bail; + + if (lws_genhash_init(&hctx, type)) + goto bail; + + if (lws_genhash_update(&hctx, res->ns.name, (size_t)res->ns.len)) { + lws_genhash_destroy(&hctx, NULL); + goto bail; + } + + if (lws_genhash_destroy(&hctx, buf)) + goto bail; + + ret = (int)hash_len; + +bail: + lws_free(der); + return ret; +} + int lws_tls_cert_get_x509_validity(struct lws_context *context, const char *filepath, time_t *not_before, time_t *not_after) diff --git a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c index 0215b8f496..02769f2ee4 100644 --- a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c +++ b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c @@ -10,22 +10,31 @@ */ #include -#include #include #include #include #include +#if defined(_WIN32) +#include +#include +#define mkdir(path, mode) _mkdir(path) +static void usleep(unsigned long l) { Sleep(l / 1000); } +#else #include +#endif #include +#include + -int lws_ensure_user_space(struct lws *wsi); static int interrupted; static int is_server; static char testcase[64] = ""; -static char requests_env[2048] = ""; static struct lws_context *context; static struct lws *first_session_wsi; +static char global_endpoint[128] = ""; + + struct request_item { char url[512]; @@ -38,18 +47,49 @@ struct request_item { int completed; }; -static struct request_item client_requests[64]; +static struct request_item client_requests[256]; static int client_requests_count; struct server_request_item { char endpoint[128]; char filename[128]; int started; + int completed; }; -static struct server_request_item server_requests[64]; +static struct server_request_item server_requests[256]; static int server_requests_count; +struct pending_datagram { + char filename[256]; + int is_push; /* 1 for PUSH, 0 for GET */ +}; + +static struct pending_datagram dg_queue[512]; +static int dg_queue_head; +static int dg_queue_tail; + +static void enqueue_datagram(const char *filename, int is_push) +{ + if ((dg_queue_tail + 1) % 512 == dg_queue_head) { + lwsl_err("Datagram queue overflow!\n"); + return; + } + struct pending_datagram *dg = &dg_queue[dg_queue_tail]; + lws_strncpy(dg->filename, filename, sizeof(dg->filename)); + dg->is_push = is_push; + dg_queue_tail = (dg_queue_tail + 1) % 512; +} + +static struct pending_datagram *dequeue_datagram(void) +{ + if (dg_queue_head == dg_queue_tail) + return NULL; + struct pending_datagram *dg = &dg_queue[dg_queue_head]; + dg_queue_head = (dg_queue_head + 1) % 512; + return dg; +} + struct pss_qir { struct lws *wsi; int is_session; @@ -90,42 +130,22 @@ static void init_pss(struct pss_qir *pss) static int pss_is_file_sender(struct pss_qir *pss) { - if (pss->is_unidi) { - if (strstr(testcase, "unidirectional-receive")) { - return is_server && !pss->is_initiator; - } - if (strstr(testcase, "unidirectional-send")) { - return !is_server && pss->is_initiator; - } - } else { - if (strstr(testcase, "bidirectional-receive")) { - return is_server; - } - if (strstr(testcase, "bidirectional-send")) { - return !is_server; - } - } - return 0; + int local_is_sender = (strstr(testcase, "-receive") && is_server) || + (strstr(testcase, "-send") && !is_server) || + (strcmp(testcase, "transfer") == 0 && (is_server || !client_requests_count || client_requests[0].filename[0] == '\0')); + if (pss->is_unidi) + return local_is_sender && !pss->is_initiator; + return local_is_sender; } static int pss_is_file_receiver(struct pss_qir *pss) { - if (pss->is_unidi) { - if (strstr(testcase, "unidirectional-receive")) { - return !is_server && !pss->is_initiator; - } - if (strstr(testcase, "unidirectional-send")) { - return is_server && !pss->is_initiator; - } - } else { - if (strstr(testcase, "bidirectional-receive")) { - return !is_server; - } - if (strstr(testcase, "bidirectional-send")) { - return is_server; - } - } - return 0; + int local_is_receiver = (strstr(testcase, "-receive") && !is_server) || + (strstr(testcase, "-send") && is_server) || + (strcmp(testcase, "transfer") == 0 && !is_server && client_requests_count && client_requests[0].filename[0] != '\0'); + if (pss->is_unidi) + return local_is_receiver && !pss->is_initiator; + return local_is_receiver; } #if defined(LWS_WITH_CUSTOM_HEADERS) @@ -146,28 +166,49 @@ static void sigint_handler(int sig) { interrupted = 1; } +static void trim_trailing_whitespace(char *str) +{ + size_t len = strlen(str); + while (len > 0 && (str[len - 1] == ' ' || str[len - 1] == '\r' || str[len - 1] == '\n' || str[len - 1] == '\t')) { + str[len - 1] = '\0'; + len--; + } +} static int parse_client_requests(void) { const char *reqs = getenv("REQUESTS_CLIENT"); + struct lws_tokenize ts; + lws_tokenize_elem e; + char token[512]; + if (!reqs) reqs = getenv("REQUESTS"); if (!reqs) return 0; - lws_strncpy(requests_env, reqs, sizeof(requests_env)); - char *r_ptr = requests_env; - char *token; + lws_tokenize_init(&ts, reqs, LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_SLASH_NONTERM | + LWS_TOKENIZE_F_DOT_NONTERM | + LWS_TOKENIZE_F_COLON_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS); - while ((token = strsep(&r_ptr, " "))) { - if (!*token) + while ((e = lws_tokenize(&ts)) > 0) { + if (e != LWS_TOKZE_TOKEN) continue; + if (lws_tokenize_cstr(&ts, token, sizeof(token))) { + lwsl_err("Client request URL too long\n"); + continue; + } + struct request_item *item = &client_requests[client_requests_count]; lws_strncpy(item->url, token, sizeof(item->url)); + trim_trailing_whitespace(item->url); /* Parse URL: https://:// */ - char *p = token; + char *p = item->url; if (strncmp(p, "https://", 8) == 0) p += 8; @@ -194,6 +235,7 @@ static int parse_client_requests(void) lws_snprintf(item->endpoint, sizeof(item->endpoint), "/%s", p); *next_slash = '/'; lws_strncpy(item->filename, next_slash + 1, sizeof(item->filename)); + trim_trailing_whitespace(item->filename); } else { lws_snprintf(item->endpoint, sizeof(item->endpoint), "/%s", p); item->filename[0] = '\0'; @@ -204,43 +246,50 @@ static int parse_client_requests(void) break; } - /* For bidirectional-send, we also need to parse files from REQUESTS_SERVER */ - if (strstr(testcase, "bidirectional-send")) { - const char *sreqs = getenv("REQUESTS_SERVER"); - if (sreqs && client_requests_count == 1 && client_requests[0].filename[0] == '\0') { - char sreqs_env[1024]; - lws_strncpy(sreqs_env, sreqs, sizeof(sreqs_env)); - char *sr_ptr = sreqs_env; - char *stoken; - int first = 1; + /* If we are the client and acting as the sender, we also need to parse files from REQUESTS_SERVER */ + const char *sreqs = getenv("REQUESTS_SERVER"); + if (sreqs && client_requests_count == 1 && client_requests[0].filename[0] == '\0') { + struct lws_tokenize sts; + lws_tokenize_elem se; + char stoken[256]; + int first = 1; + + lws_tokenize_init(&sts, sreqs, LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_SLASH_NONTERM | + LWS_TOKENIZE_F_DOT_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS); + + while ((se = lws_tokenize(&sts)) > 0) { + if (se != LWS_TOKZE_TOKEN) + continue; - while ((stoken = strsep(&sr_ptr, " "))) { - if (!*stoken) - continue; + if (lws_tokenize_cstr(&sts, stoken, sizeof(stoken))) + continue; - /* format: / */ - char *slash = strchr(stoken, '/'); - if (!slash) - continue; + /* format: / */ + char *slash = strchr(stoken, '/'); + if (!slash) + continue; - *slash = '\0'; - char filename[256]; - lws_strncpy(filename, slash + 1, sizeof(filename)); - *slash = '/'; + *slash = '\0'; + char filename[256]; + lws_strncpy(filename, slash + 1, sizeof(filename)); + trim_trailing_whitespace(filename); + *slash = '/'; - if (first) { - lws_strncpy(client_requests[0].filename, filename, sizeof(client_requests[0].filename)); - first = 0; - } else { - struct request_item *item = &client_requests[client_requests_count]; - item->port = client_requests[0].port; - lws_strncpy(item->host, client_requests[0].host, sizeof(item->host)); - lws_strncpy(item->endpoint, client_requests[0].endpoint, sizeof(item->endpoint)); - lws_strncpy(item->filename, filename, sizeof(item->filename)); - client_requests_count++; - if (client_requests_count >= (int)LWS_ARRAY_SIZE(client_requests)) - break; - } + if (first) { + lws_strncpy(client_requests[0].filename, filename, sizeof(client_requests[0].filename)); + first = 0; + } else { + struct request_item *item = &client_requests[client_requests_count]; + item->port = client_requests[0].port; + lws_strncpy(item->host, client_requests[0].host, sizeof(item->host)); + lws_strncpy(item->endpoint, client_requests[0].endpoint, sizeof(item->endpoint)); + lws_strncpy(item->filename, filename, sizeof(item->filename)); + client_requests_count++; + if (client_requests_count >= (int)LWS_ARRAY_SIZE(client_requests)) + break; } } } @@ -251,17 +300,26 @@ static int parse_client_requests(void) static int parse_server_requests(void) { const char *reqs = getenv("REQUESTS_SERVER"); + struct lws_tokenize ts; + lws_tokenize_elem e; + char token[256]; + if (!reqs) reqs = getenv("REQUESTS"); if (!reqs) return 0; - lws_strncpy(requests_env, reqs, sizeof(requests_env)); - char *r_ptr = requests_env; - char *token; + lws_tokenize_init(&ts, reqs, LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_SLASH_NONTERM | + LWS_TOKENIZE_F_DOT_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS); + + while ((e = lws_tokenize(&ts)) > 0) { + if (e != LWS_TOKZE_TOKEN) + continue; - while ((token = strsep(&r_ptr, " "))) { - if (!*token) + if (lws_tokenize_cstr(&ts, token, sizeof(token))) continue; struct server_request_item *item = &server_requests[server_requests_count]; @@ -275,6 +333,7 @@ static int parse_server_requests(void) lws_snprintf(item->endpoint, sizeof(item->endpoint), "/%s", token); *slash = '/'; lws_strncpy(item->filename, slash + 1, sizeof(item->filename)); + trim_trailing_whitespace(item->filename); server_requests_count++; if (server_requests_count >= (int)LWS_ARRAY_SIZE(server_requests)) @@ -287,7 +346,19 @@ static int parse_server_requests(void) static void trigger_client_transfers(struct lws *wsi_session, const char *endpoint) { int i; + int local_is_sender = (strstr(testcase, "-receive") && is_server) || + (strstr(testcase, "-send") && !is_server) || + (strcmp(testcase, "transfer") == 0 && (is_server || !client_requests_count || client_requests[0].filename[0] == '\0')); + int local_is_receiver = (strstr(testcase, "-receive") && !is_server) || + (strstr(testcase, "-send") && is_server) || + (strcmp(testcase, "transfer") == 0 && !is_server && client_requests_count && client_requests[0].filename[0] != '\0'); + lwsl_user("trigger_client_transfers called for endpoint %s, client_requests_count=%d\n", endpoint, client_requests_count); + if (local_is_sender) { + lwsl_user(" Local node is sender, not initiating client transfers.\n"); + return; + } + for (i = 0; i < client_requests_count; i++) { struct request_item *item = &client_requests[i]; lwsl_user(" Client Request %d: url=%s endpoint=%s started=%d filename=%s\n", i, item->url, item->endpoint, item->started, item->filename); @@ -338,35 +409,9 @@ static void trigger_client_transfers(struct lws *wsi_session, const char *endpoi } } } else if (strstr(testcase, "datagram")) { - if (strstr(testcase, "-send")) { - /* Send PUSH \n as a datagram */ - char filepath[512]; - lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", item->endpoint, item->filename); - int fd = open(filepath, O_RDONLY); - if (fd >= 0) { - struct stat st; - if (fstat(fd, &st) == 0) { - uint8_t dgbuf[LWS_PRE + 65536]; - int hn = lws_snprintf((char *)&dgbuf[LWS_PRE], sizeof(dgbuf) - LWS_PRE, "PUSH %s\n", item->filename); - int rn = (int)read(fd, &dgbuf[LWS_PRE + hn], sizeof(dgbuf) - LWS_PRE - (size_t)hn); - if (rn >= 0) { - enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; - lws_write(wsi_session, &dgbuf[LWS_PRE], (size_t)(hn + rn), wp); - lwsl_user("Client sent datagram PUSH: %s (%d bytes)\n", item->filename, hn + rn); - item->completed = 1; - } - } - close(fd); - } else { - lwsl_err("Failed to open file %s for datagram send\n", filepath); - } - } else { - /* Send GET as a datagram */ - uint8_t buf[LWS_PRE + 512]; - size_t len = (size_t)lws_snprintf((char *)&buf[LWS_PRE], 512, "GET %s", item->filename); - enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; - lws_write(wsi_session, &buf[LWS_PRE], len, wp); - lwsl_user("Client sent datagram: GET %s\n", item->filename); + if (local_is_receiver) { + enqueue_datagram(item->filename, 0); /* 0 for GET */ + lws_callback_on_writable(wsi_session); } } } @@ -376,7 +421,19 @@ static void trigger_client_transfers(struct lws *wsi_session, const char *endpoi static void trigger_server_transfers(struct lws *wsi_session, const char *endpoint) { int i; + int local_is_sender = (strstr(testcase, "-receive") && is_server) || + (strstr(testcase, "-send") && !is_server) || + (strcmp(testcase, "transfer") == 0 && (is_server || !client_requests_count || client_requests[0].filename[0] == '\0')); + int local_is_receiver = (strstr(testcase, "-receive") && !is_server) || + (strstr(testcase, "-send") && is_server) || + (strcmp(testcase, "transfer") == 0 && !is_server && client_requests_count && client_requests[0].filename[0] != '\0'); + lwsl_user("trigger_server_transfers called for endpoint %s, server_requests_count=%d\n", endpoint, server_requests_count); + if (local_is_sender) { + lwsl_user(" Local node is sender, not initiating server transfers.\n"); + return; + } + for (i = 0; i < server_requests_count; i++) { struct server_request_item *item = &server_requests[i]; lwsl_user(" Server Request %d: endpoint=%s started=%d filename=%s\n", i, item->endpoint, item->started, item->filename); @@ -426,32 +483,9 @@ static void trigger_server_transfers(struct lws *wsi_session, const char *endpoi } } } else if (strstr(testcase, "datagram")) { - if (strstr(testcase, "-send")) { - /* Send PUSH \n as a datagram */ - char filepath[512]; - lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", item->endpoint, item->filename); - int fd = open(filepath, O_RDONLY); - if (fd >= 0) { - struct stat st; - if (fstat(fd, &st) == 0) { - uint8_t dgbuf[LWS_PRE + 65536]; - int hn = lws_snprintf((char *)&dgbuf[LWS_PRE], sizeof(dgbuf) - LWS_PRE, "PUSH %s\n", item->filename); - int rn = (int)read(fd, &dgbuf[LWS_PRE + hn], sizeof(dgbuf) - LWS_PRE - (size_t)hn); - if (rn >= 0) { - enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; - lws_write(wsi_session, &dgbuf[LWS_PRE], (size_t)(hn + rn), wp); - lwsl_user("Server sent datagram PUSH: %s (%d bytes)\n", item->filename, hn + rn); - } - } - close(fd); - } - } else { - /* Send GET as a datagram */ - uint8_t buf[LWS_PRE + 512]; - size_t len = (size_t)lws_snprintf((char *)&buf[LWS_PRE], 512, "GET %s", item->filename); - enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; - lws_write(wsi_session, &buf[LWS_PRE], len, wp); - lwsl_user("Server sent datagram: GET %s\n", item->filename); + if (local_is_receiver) { + enqueue_datagram(item->filename, 0); /* 0 for GET */ + lws_callback_on_writable(wsi_session); } } } @@ -491,19 +525,30 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, if (env_protocols) { char av_buf[512] = ""; - char env_copy[512]; - lws_strncpy(env_copy, env_protocols, sizeof(env_copy)); - char *ec_ptr = env_copy; - char *tok; + struct lws_tokenize ts; + lws_tokenize_elem e; + char tok[128]; int first = 1; - while ((tok = strsep(&ec_ptr, " "))) { - if (!*tok) continue; - if (!first) - strcat(av_buf, ", "); - strcat(av_buf, "\""); - strcat(av_buf, tok); - strcat(av_buf, "\""); - first = 0; + + lws_tokenize_init(&ts, env_protocols, LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS); + + while ((e = lws_tokenize(&ts)) > 0) { + if (e != LWS_TOKZE_TOKEN) + continue; + + if (lws_tokenize_cstr(&ts, tok, sizeof(tok))) + continue; + + if (sizeof(av_buf) - strlen(av_buf) > strlen(tok) + 5) { + if (!first) + strcat(av_buf, ", "); + strcat(av_buf, "\""); + strcat(av_buf, tok); + strcat(av_buf, "\""); + first = 0; + } } if (lws_add_http_header_by_name(wsi, @@ -528,24 +573,90 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, if (path_len > 0) { path[path_len] = '\0'; lws_strncpy(pss->endpoint, path, sizeof(pss->endpoint)); + lws_strncpy(global_endpoint, path, sizeof(global_endpoint)); } lwsl_user("Server WebTransport session established on %s\n", pss->endpoint); + /* Save negotiated protocol to /downloads/negotiated_protocol.txt */ + { + char client_protos[256]; + char negotiated[64] = ""; + int cp_len = lws_hdr_custom_copy(wsi, client_protos, sizeof(client_protos) - 1, + "wt-available-protocols", 22); + if (cp_len > 0) { + const char *env_protocols = getenv("PROTOCOLS_SERVER"); + client_protos[cp_len] = '\0'; + if (!env_protocols) + env_protocols = getenv("PROTOCOLS"); + + if (env_protocols) { + /* client_protos format: '"proto1", "proto2"' */ + struct lws_tokenize ts; + lws_tokenize_elem e; + char token[64]; + + lws_tokenize_init(&ts, client_protos, LWS_TOKENIZE_F_COMMA_SEP_LIST | + LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS); + + while ((e = lws_tokenize(&ts)) > 0) { + if (e != LWS_TOKZE_TOKEN && e != LWS_TOKZE_QUOTED_STRING) + continue; + + if (lws_tokenize_cstr(&ts, token, sizeof(token))) + continue; + + struct lws_tokenize sts; + lws_tokenize_elem se; + char sp_tok[64]; + + lws_tokenize_init(&sts, env_protocols, LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS); + + while ((se = lws_tokenize(&sts)) > 0) { + if (se != LWS_TOKZE_TOKEN) + continue; + + if (lws_tokenize_cstr(&sts, sp_tok, sizeof(sp_tok))) + continue; + + if (strcmp(token, sp_tok) == 0) { + lws_strncpy(negotiated, token, sizeof(negotiated)); + break; + } + } + if (negotiated[0]) + break; + } + } + } + + if (negotiated[0]) { + if (mkdir("/downloads", 0777) < 0 && errno != EEXIST) { // NOSONAR + lwsl_err("Failed to create /downloads: %d\n", errno); + } + int nfd = open("/downloads/negotiated_protocol.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOSONAR + if (nfd >= 0) { + if (write(nfd, negotiated, LWS_POSIX_LENGTH_CAST(strlen(negotiated))) < 0) { + lwsl_err("Failed to write negotiated protocol\n"); + } + close(nfd); + } + } + } + /* If server needs to request files, trigger them now */ trigger_server_transfers(wsi, pss->endpoint); } else { pss->is_session = 0; pss->is_unidi = lws_wt_is_unidi(wsi); - struct lws *swsi = lws_wt_get_session_wsi(wsi); - if (swsi) { - struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); - if (spss) { - lws_strncpy(pss->endpoint, spss->endpoint, sizeof(pss->endpoint)); - } - } + lws_strncpy(pss->endpoint, global_endpoint, sizeof(pss->endpoint)); lwsl_user("%s stream established (unidi=%d)\n", is_server ? "Server" : "Client", pss->is_unidi); - if (is_server && !pss->is_unidi) { + if (is_server && !pss->is_unidi && + lws_get_parent(wsi) && lws_wt_is_session(lws_get_parent(wsi))) { /* Server receives files over client-initiated bidi streams. * Find first unstarted server request, assign it to this stream, * and trigger writable callback to send GET . */ @@ -580,6 +691,7 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, if (!client_requests[i].session_wsi) { lws_strncpy(pss->endpoint, client_requests[i].endpoint, sizeof(pss->endpoint)); client_requests[i].session_wsi = wsi; + lws_strncpy(global_endpoint, pss->endpoint, sizeof(global_endpoint)); break; } } @@ -602,10 +714,12 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, ne[-1] = '\0'; ne--; } - mkdir("/downloads", 0777); - int nfd = open("/downloads/negotiated_protocol.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (mkdir("/downloads", 0777) < 0 && errno != EEXIST) { // NOSONAR + lwsl_err("Failed to create /downloads: %d\n", errno); + } + int nfd = open("/downloads/negotiated_protocol.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOSONAR if (nfd >= 0) { - if (write(nfd, n_ptr, strlen(n_ptr)) < 0) { + if (write(nfd, n_ptr, LWS_POSIX_LENGTH_CAST(strlen(n_ptr))) < 0) { lwsl_err("Failed to write negotiated protocol\n"); } close(nfd); @@ -617,21 +731,74 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, } else { pss->is_session = 0; pss->is_unidi = lws_wt_is_unidi(wsi); - struct lws *swsi = lws_wt_get_session_wsi(wsi); - if (swsi) { - struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); - if (spss) { - lws_strncpy(pss->endpoint, spss->endpoint, sizeof(pss->endpoint)); - } - } + lws_strncpy(pss->endpoint, global_endpoint, sizeof(pss->endpoint)); lwsl_user("Client stream established (unidi=%d)\n", pss->is_unidi); } break; case LWS_CALLBACK_SERVER_WRITEABLE: case LWS_CALLBACK_CLIENT_WRITEABLE: - if (pss->is_session) + if (pss->is_session) { + struct pending_datagram *dg = dequeue_datagram(); + if (dg) { + if (dg->is_push) { + /* Send PUSH \n as a datagram */ + char filepath[512]; + const char *endpoint = pss->endpoint[0] ? pss->endpoint : global_endpoint; + lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", endpoint, dg->filename); + int fd = open(filepath, O_RDONLY); + if (fd >= 0) { + struct stat st; + if (fstat(fd, &st) == 0) { + uint8_t dgbuf[LWS_PRE + 65536]; + int hn = lws_snprintf((char *)&dgbuf[LWS_PRE], sizeof(dgbuf) - LWS_PRE, "PUSH %s\n", dg->filename); + if (hn > 0 && (size_t)hn < sizeof(dgbuf) - LWS_PRE) { + int rn = (int)read(fd, &dgbuf[LWS_PRE + hn], sizeof(dgbuf) - LWS_PRE - (size_t)hn); + if (rn >= 0) { + size_t total_len = (size_t)hn + (size_t)rn; + if (total_len <= sizeof(dgbuf) - LWS_PRE) { + enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; + lws_write(wsi, &dgbuf[LWS_PRE], total_len, wp); + lwsl_user("Session WSI sent datagram PUSH: %s (%zu bytes)\n", dg->filename, total_len); + usleep(5000); /* Pace datagram sends to prevent queue overflow */ + + /* Mark completed on client side (if we are the client sending PUSH) */ + if (!is_server) { + int r_idx = -1; + int i; + for (i = 0; i < client_requests_count; i++) { + if (strcmp(client_requests[i].filename, dg->filename) == 0) { + r_idx = i; + break; + } + } + if (r_idx >= 0) { + client_requests[r_idx].completed = 1; + lwsl_user("Client datagram transfer completed: %s\n", dg->filename); + } + } + } + } + } + } + close(fd); + } else { + lwsl_err("Failed to open file %s for datagram PUSH\n", filepath); + } + } else { + /* Send GET as a datagram */ + uint8_t buf[LWS_PRE + 512]; + size_t len = (size_t)lws_snprintf((char *)&buf[LWS_PRE], 512, "GET %s", dg->filename); + enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; + lws_write(wsi, &buf[LWS_PRE], len, wp); + lwsl_user("Session WSI sent datagram GET: %s\n", dg->filename); + usleep(5000); /* Pace datagram sends to prevent queue overflow */ + } + /* Request next writable callback to process remaining queue items */ + lws_callback_on_writable(wsi); + } break; + } if (pss_is_file_sender(pss)) { /* Sender: write file data */ @@ -639,14 +806,7 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, break; if (pss->fd_in < 0) { char filepath[512]; - const char *endpoint = pss->endpoint; - if (!endpoint[0]) { - struct lws *swsi = lws_wt_get_session_wsi(wsi); - if (swsi) { - struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); - if (spss) endpoint = spss->endpoint; - } - } + const char *endpoint = pss->endpoint[0] ? pss->endpoint : global_endpoint; lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", endpoint, pss->filename); pss->fd_in = open(filepath, O_RDONLY); if (pss->fd_in >= 0) { @@ -674,13 +834,22 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, m = lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY | (is_final ? LWS_WRITE_H2_STREAM_END : LWS_WRITE_NO_FIN)); if (m < 0) return -1; - if (!is_final) + if (m < n) { + /* Seek back the unwritten bytes and retry */ + off_t diff = (off_t)(n - m); + lseek(pss->fd_in, -diff, SEEK_CUR); + pss->sent_len -= (size_t)diff; lws_callback_on_writable(wsi); - else { - close(pss->fd_in); - pss->fd_in = -1; - pss->write_completed = 1; - lwsl_user("Sender completed file write: %zu bytes\n", pss->sent_len); + } else { + if (!is_final) + lws_callback_on_writable(wsi); + else { + close(pss->fd_in); + pss->fd_in = -1; + pss->write_completed = 1; + lwsl_user("Sender completed file write: %zu bytes\n", pss->sent_len); + return 0; + } } } else { /* EOF or error */ @@ -688,6 +857,7 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, pss->fd_in = -1; pss->write_completed = 1; lws_write(wsi, NULL, 0, LWS_WRITE_BINARY | LWS_WRITE_H2_STREAM_END); + return 0; } } else { /* Unidirectional stream requires PUSH \n first */ @@ -696,9 +866,14 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, m = lws_write(wsi, p, (size_t)n, LWS_WRITE_BINARY | LWS_WRITE_NO_FIN); if (m < 0) return -1; - pss->header_sent = 1; - lws_callback_on_writable(wsi); - lwsl_user("Sender sent PUSH %s\\n\n", pss->filename); + if (m < n) { + /* Throttled or partial, retry header next time */ + lws_callback_on_writable(wsi); + } else { + pss->header_sent = 1; + lws_callback_on_writable(wsi); + lwsl_user("Sender sent PUSH %s\\n\n", pss->filename); + } } } } else { @@ -734,23 +909,8 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, lwsl_user("Session WSI received datagram GET: %s\n", filename); /* Respond by sending PUSH \n as a datagram */ - char filepath[512]; - lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", pss->endpoint, filename); - int fd = open(filepath, O_RDONLY); - if (fd >= 0) { - struct stat st; - if (fstat(fd, &st) == 0) { - uint8_t dgbuf[LWS_PRE + 65536]; - int hn = lws_snprintf((char *)&dgbuf[LWS_PRE], sizeof(dgbuf) - LWS_PRE, "PUSH %s\n", filename); - int rn = (int)read(fd, &dgbuf[LWS_PRE + hn], sizeof(dgbuf) - LWS_PRE - (size_t)hn); - if (rn >= 0) { - enum lws_write_protocol wp = LWS_WRITE_QUIC_DATAGRAM; - lws_write(wsi, &dgbuf[LWS_PRE], (size_t)(hn + rn), wp); - lwsl_user("Session WSI sent datagram PUSH: %s (%d bytes)\n", filename, hn + rn); - } - } - close(fd); - } + enqueue_datagram(filename, 1); /* 1 for PUSH */ + lws_callback_on_writable(wsi); } else if (payload_len > 5 && strncmp(payload, "PUSH ", 5) == 0) { /* PUSH \n */ char *newline = memchr(payload, '\n', payload_len); @@ -768,12 +928,14 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, char dirpath[512], filepath[512]; lws_snprintf(dirpath, sizeof(dirpath), "/downloads%s", pss->endpoint); - mkdir(dirpath, 0777); + if (mkdir(dirpath, 0777) < 0 && errno != EEXIST) { // NOSONAR + lwsl_err("Failed to create directory %s: %d\n", dirpath, errno); + } lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, filename); - int fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); + int fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOSONAR if (fd >= 0) { - if (write(fd, data_start, data_len) < 0) { + if (write(fd, data_start, LWS_POSIX_LENGTH_CAST(data_len)) < 0) { lwsl_err("Failed to write datagram content\n"); } close(fd); @@ -792,6 +954,31 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, client_requests[r_idx].completed = 1; lwsl_user("Client datagram request index %d (%s) completed (datagram received)\n", r_idx, filename); } + } else { + int r_idx = -1; + int i; + for (i = 0; i < server_requests_count; i++) { + if (strcmp(server_requests[i].filename, filename) == 0) { + r_idx = i; + break; + } + } + if (r_idx >= 0) { + server_requests[r_idx].completed = 1; + lwsl_user("Server datagram request index %d (%s) completed (datagram received)\n", r_idx, filename); + + int all_done = 1; + for (i = 0; i < server_requests_count; i++) { + if (!server_requests[i].completed) { + all_done = 0; + break; + } + } + if (all_done) { + lwsl_user("All server requests completed. Setting interrupted = 1 to exit.\n"); + interrupted = 1; + } + } } } } @@ -829,23 +1016,18 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, } /* Open file regardless of rem */ char dirpath[512], filepath[512]; - const char *endpoint = pss->endpoint; - if (!endpoint[0]) { - struct lws *swsi = lws_wt_get_session_wsi(wsi); - if (swsi) { - struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); - if (spss) endpoint = spss->endpoint; - } - } + const char *endpoint = pss->endpoint[0] ? pss->endpoint : global_endpoint; lws_snprintf(dirpath, sizeof(dirpath), "/downloads%s", endpoint); - mkdir(dirpath, 0777); + if (mkdir(dirpath, 0777) < 0 && errno != EEXIST) { // NOSONAR + lwsl_err("Failed to create directory %s: %d\n", dirpath, errno); + } lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, pss->filename); - pss->fd_out = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); + pss->fd_out = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOSONAR size_t rem = data_len - i - 1; lwsl_user("RECEIVE: Opened file '%s' -> fd %d (rem=%zu bytes written)\n", filepath, pss->fd_out, rem); if (pss->fd_out >= 0 && rem > 0) { - if (write(pss->fd_out, data + i + 1, rem) < 0) { + if (write(pss->fd_out, data + i + 1, LWS_POSIX_LENGTH_CAST(rem)) < 0) { lwsl_err("Failed to write stream chunk\n"); } } @@ -855,7 +1037,7 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, } } else { if (pss->fd_out >= 0) { - if (write(pss->fd_out, data, data_len) < 0) { + if (write(pss->fd_out, data, LWS_POSIX_LENGTH_CAST(data_len)) < 0) { lwsl_err("Failed to write stream data\n"); } } else { @@ -866,22 +1048,17 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, /* Bidirectional: no header, just raw file contents */ if (pss->fd_out < 0) { char dirpath[512], filepath[512]; - const char *endpoint = pss->endpoint; - if (!endpoint[0]) { - struct lws *swsi = lws_wt_get_session_wsi(wsi); - if (swsi) { - struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); - if (spss) endpoint = spss->endpoint; - } - } + const char *endpoint = pss->endpoint[0] ? pss->endpoint : global_endpoint; lws_snprintf(dirpath, sizeof(dirpath), "/downloads%s", endpoint); - mkdir(dirpath, 0777); + if (mkdir(dirpath, 0777) < 0 && errno != EEXIST) { // NOSONAR + lwsl_err("Failed to create directory %s: %d\n", dirpath, errno); + } lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, pss->filename); - pss->fd_out = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); + pss->fd_out = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOSONAR lwsl_user("RECEIVE (bidi): Opened file '%s' -> fd %d\n", filepath, pss->fd_out); } if (pss->fd_out >= 0) { - if (write(pss->fd_out, data, data_len) < 0) { + if (write(pss->fd_out, data, LWS_POSIX_LENGTH_CAST(data_len)) < 0) { lwsl_err("Failed to write stream data\n"); } } @@ -903,14 +1080,7 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, /* Open local file from /www// */ char filepath[512]; - const char *endpoint = pss->endpoint; - if (!endpoint[0]) { - struct lws *swsi = lws_wt_get_session_wsi(wsi); - if (swsi) { - struct pss_qir *spss = (struct pss_qir *)lws_wsi_user(swsi); - if (spss) endpoint = spss->endpoint; - } - } + const char *endpoint = pss->endpoint[0] ? pss->endpoint : global_endpoint; lws_snprintf(filepath, sizeof(filepath), "/www%s/%s", endpoint, pss->filename); pss->fd_in = open(filepath, O_RDONLY); if (pss->fd_in >= 0) { @@ -922,18 +1092,27 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, if (lws_wt_is_unidi(wsi)) { struct lws *cwsi = lws_wt_create_stream_from_child(wsi, 1); if (cwsi) { - lws_ensure_user_space(cwsi); - struct pss_qir *cpss = (struct pss_qir *)lws_wsi_user(cwsi); - if (cpss) { - cpss->is_unidi = 1; - cpss->is_initiator = 0; - lws_strncpy(cpss->endpoint, endpoint, sizeof(cpss->endpoint)); - lws_strncpy(cpss->filename, filename, sizeof(cpss->filename)); - cpss->fd_in = pss->fd_in; + if (!lws_ensure_user_space(cwsi)) { + struct pss_qir *cpss = (struct pss_qir *)lws_wsi_user(cwsi); + if (cpss) { + cpss->is_unidi = 1; + cpss->is_initiator = 0; + lws_strncpy(cpss->endpoint, endpoint, sizeof(cpss->endpoint)); + lws_strncpy(cpss->filename, filename, sizeof(cpss->filename)); + cpss->fd_in = pss->fd_in; + pss->fd_in = -1; + cpss->file_len = pss->file_len; + lws_callback_on_writable(cwsi); + lwsl_user("Created server-initiated stream %p for unidirectional file response\n", cwsi); + } else { + lwsl_err("Child stream user space is NULL\n"); + close(pss->fd_in); + pss->fd_in = -1; + } + } else { + lwsl_err("Failed to ensure user space for child stream\n"); + close(pss->fd_in); pss->fd_in = -1; - cpss->file_len = pss->file_len; - lws_callback_on_writable(cwsi); - lwsl_user("Created server-initiated stream %p for unidirectional file response\n", cwsi); } } else { lwsl_err("Failed to create server-initiated stream for response\n"); @@ -943,6 +1122,8 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, } else { lws_callback_on_writable(wsi); } + } else { + lwsl_err("Sender WSI failed to open file %s\n", filepath); } } } @@ -961,6 +1142,10 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, pss->fd_out = -1; } if (!pss->is_session) { + if (pss->is_unidi && pss->is_initiator) { + lwsl_user("Initiator unidirectional GET stream closed (not marking request completed yet)\n"); + break; + } if (!is_server) { int r_idx = -1; int i; @@ -974,7 +1159,35 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, client_requests[r_idx].completed = 1; lwsl_user("Client transfer request index %d (%s) completed (stream closed)\n", r_idx, pss->filename); } + } else { + int r_idx = -1; + int i; + for (i = 0; i < server_requests_count; i++) { + if (strcmp(server_requests[i].filename, pss->filename) == 0) { + r_idx = i; + break; + } + } + if (r_idx >= 0) { + server_requests[r_idx].completed = 1; + lwsl_user("Server transfer request index %d (%s) completed (stream closed)\n", r_idx, pss->filename); + + int all_done = 1; + for (i = 0; i < server_requests_count; i++) { + if (!server_requests[i].completed) { + all_done = 0; + break; + } + } + if (all_done) { + lwsl_user("All server requests completed. Setting interrupted = 1 to exit.\n"); + interrupted = 1; + } + } } + } else { + lwsl_user("%s session closed. Setting interrupted = 1 to exit.\n", is_server ? "Server" : "Client"); + interrupted = 1; } lwsl_user("WSI closed\n"); break; @@ -991,6 +1204,69 @@ static const struct lws_protocols protocols[] = { { NULL, NULL, 0, 0, 0, NULL, 0 } }; +static struct lws_protocols *dyn_protocols = NULL; + +static void setup_dynamic_protocols(void) +{ + const char *env_protocols = getenv("PROTOCOLS_SERVER"); + if (!env_protocols) + env_protocols = getenv("PROTOCOLS"); + + if (!env_protocols) + return; + + /* Count protocols */ + struct lws_tokenize ts; + lws_tokenize_init(&ts, env_protocols, LWS_TOKENIZE_F_MINUS_NONTERM); + lws_tokenize_elem e; + int count = 0; + + do { + e = lws_tokenize(&ts); + if (e == LWS_TOKZE_TOKEN || e == LWS_TOKZE_QUOTED_STRING) { + count++; + } + } while (e > 0); + + if (count == 0) + return; + + /* Allocate count + 2 protocols: one for each protocol, plus "webtransport", plus NULL terminator */ + dyn_protocols = malloc((size_t)(count + 2) * sizeof(struct lws_protocols)); + if (!dyn_protocols) { + lwsl_err("OOM allocating dynamic protocols\n"); + return; + } + + memset(dyn_protocols, 0, (size_t)(count + 2) * sizeof(struct lws_protocols)); + + /* Re-tokenize and copy */ + lws_tokenize_init(&ts, env_protocols, LWS_TOKENIZE_F_MINUS_NONTERM); + int idx = 0; + do { + e = lws_tokenize(&ts); + if (e == LWS_TOKZE_TOKEN || e == LWS_TOKZE_QUOTED_STRING) { + char name[64]; + if (!lws_tokenize_cstr(&ts, name, sizeof(name))) { + dyn_protocols[idx].name = strdup(name); + dyn_protocols[idx].callback = callback_qir; + dyn_protocols[idx].per_session_data_size = sizeof(struct pss_qir); + dyn_protocols[idx].rx_buffer_size = 65536; + idx++; + } + } + } while (e > 0); + + /* Add generic "webtransport" protocol at the end of the list just in case */ + dyn_protocols[idx].name = "webtransport"; + dyn_protocols[idx].callback = callback_qir; + dyn_protocols[idx].per_session_data_size = sizeof(struct pss_qir); + dyn_protocols[idx].rx_buffer_size = 65536; + idx++; + + /* Terminator is already zeroed out by memset */ +} + int main(int argc, const char **argv) { struct lws_context_creation_info info; @@ -1030,10 +1306,12 @@ int main(int argc, const char **argv) if (port_env) port = atoi(port_env); + setup_dynamic_protocols(); + memset(&info, 0, sizeof info); info.port = is_server ? port : CONTEXT_PORT_NO_LISTEN; info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.protocols = protocols; + info.protocols = dyn_protocols ? dyn_protocols : protocols; info.alpn = "h3"; if (is_server) { @@ -1146,5 +1424,17 @@ int main(int argc, const char **argv) } lws_context_destroy(context); + + if (dyn_protocols) { + int idx = 0; + while (dyn_protocols[idx].name) { + if (strcmp(dyn_protocols[idx].name, "webtransport") != 0) { + free((void *)dyn_protocols[idx].name); + } + idx++; + } + free(dyn_protocols); + } + return 0; } diff --git a/minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/CMakeLists.txt b/minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/CMakeLists.txt deleted file mode 100644 index e97b2245e0..0000000000 --- a/minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -project(lws-minimal-webtransport-test-server C) -cmake_minimum_required(VERSION 3.10) -find_package(libwebsockets CONFIG REQUIRED) -list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) -include(CheckCSourceCompiles) -include(LwsCheckRequirements) - -set(SAMP lws-minimal-webtransport-test-server) -set(SRCS minimal-webtransport-test-server.c ../../../../plugins/protocol_webtransport_test/protocol_webtransport_test.c) - -set(requirements 1) -require_lws_config(LWS_ROLE_WT 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_HTTP3 1 requirements) - - -if (requirements) - add_executable(${SAMP} ${SRCS}) - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) - endif() -endif() diff --git a/minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/minimal-webtransport-test-server.c b/minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/minimal-webtransport-test-server.c deleted file mode 100644 index 0471f5b1f9..0000000000 --- a/minimal-examples-lowlevel/webtransport/minimal-webtransport-test-server/minimal-webtransport-test-server.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * lws-minimal-webtransport-test-server - * - * Written in 2010-2026 by Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This server mounts the assets for the WebTransport test UI and loads the - * `protocol_webtransport_test` plugin. - */ - -#include -#include -#include - -static int interrupted; - -static const struct lws_http_mount mount = { - .mount_next = NULL, - .mountpoint = "/", - .origin = "../../../../plugins/protocol_webtransport_test/assets", - .def = "index.html", - .origin_protocol = LWSMPRO_FILE, - .mountpoint_len = 1, -}; - -extern const lws_plugin_protocol_t webtransport_test; - -static void -sigint_handler(int sig) -{ - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - struct lws_context *context; - const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; - - signal(SIGINT, sigint_handler); - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal webtransport test server | visit https://localhost:7681\n"); - - memset(&info, 0, sizeof info); - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; - - info.pvo = NULL; - info.protocols = webtransport_test.protocols; - - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - info.alpn = "h3,h2,http/1.1"; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff --git a/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c b/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c index d9ca497b9b..7fe95e0bba 100644 --- a/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c +++ b/plugins/protocol_lws_auth_dns/protocol_lws_auth_dns.c @@ -1033,6 +1033,7 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, uint8_t *dbuf = pss->buf + LWS_PRE; uint8_t *rp = dbuf; + size_t max_buf = is_tcp ? (sizeof(pss->buf) - LWS_PRE) : (udp_payload_size < sizeof(pss->buf) - LWS_PRE ? udp_payload_size : sizeof(pss->buf) - LWS_PRE); if (is_tcp) rp += 2; rp[0] = (uint8_t)(id >> 8); rp[1] = (uint8_t)(id & 0xff); @@ -1219,7 +1220,6 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, } } lws_end_foreach_dll(cd); - size_t max_buf = is_tcp ? (sizeof(pss->buf) - LWS_PRE) : (udp_payload_size < sizeof(pss->buf) - LWS_PRE ? udp_payload_size : sizeof(pss->buf) - LWS_PRE); rp[2] = (uint8_t)(rflags >> 8); rp[3] = (uint8_t)(rflags & 0xff); rp[4] = 0; rp[5] = 1; /* QDCOUNT = 1 */ @@ -1326,7 +1326,6 @@ callback_auth_dns(struct lws *wsi, enum lws_callback_reasons reason, void *user, ; } else { int anc = 0; - size_t max_buf = is_tcp ? (sizeof(pss->buf) - LWS_PRE) : (udp_payload_size < sizeof(pss->buf) - LWS_PRE ? udp_payload_size : sizeof(pss->buf) - LWS_PRE); size_t total_size = lws_ptr_diff_size_t(rp, dbuf) + 12 + (size_t)(q - (p + 12)); lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&found_rs->rr_list)) { struct auth_dns_rr *rr = lws_container_of(d, struct auth_dns_rr, list); diff --git a/plugins/protocol_webtransport_test/CMakeLists.txt b/plugins/protocol_webtransport_test/CMakeLists.txt index 14827ee9fd..cc6edf3d44 100644 --- a/plugins/protocol_webtransport_test/CMakeLists.txt +++ b/plugins/protocol_webtransport_test/CMakeLists.txt @@ -35,4 +35,8 @@ if (requirements) if (NOT LWS_WITH_PLUGINS_BUILTIN) target_compile_definitions(${PLUGIN_NAME} PRIVATE LWS_BUILDING_SHARED) endif() + + install(DIRECTORY assets/ + DESTINATION "share/libwebsockets-test-server/wt-assets" + COMPONENT examples) endif() diff --git a/plugins/protocol_webtransport_test/assets/app.js b/plugins/protocol_webtransport_test/assets/app.js index 7a18d00148..38de6bdaed 100644 --- a/plugins/protocol_webtransport_test/assets/app.js +++ b/plugins/protocol_webtransport_test/assets/app.js @@ -1,6 +1,4 @@ const DOM = { - urlInput: document.getElementById('server-url'), - hashInput: document.getElementById('cert-hash'), startBtn: document.getElementById('start-btn'), connStatus: document.getElementById('conn-status'), progressText: document.getElementById('progress-text'), @@ -48,19 +46,14 @@ function hexString(buffer) { return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } -function parseHash(hashStr) { - const cleaned = hashStr.replace(/[:\s]/g, ''); - const bytes = new Uint8Array(cleaned.length / 2); - for (let i = 0; i < cleaned.length; i += 2) { - bytes[i / 2] = Number.parseInt(cleaned.substring(i, i + 2), 16); - } - return bytes; -} - async function generateTestPayload() { log('Generating 4MB random payload...'); const buffer = new Uint8Array(BLOB_SIZE); - crypto.getRandomValues(buffer); // Generates 4MB of PRNG + const maxRandomChunk = 65536; + for (let offset = 0; offset < BLOB_SIZE; offset += maxRandomChunk) { + const chunk = buffer.subarray(offset, offset + maxRandomChunk); + crypto.getRandomValues(chunk); + } log('Calculating SHA-256 hash...'); const hashBuffer = await crypto.subtle.digest('SHA-256', buffer); @@ -84,16 +77,34 @@ async function sendStreamData(writer, data) { } } +let leftover = null; + async function receiveAndVerify(reader, expectedBytes) { const receivedData = new Uint8Array(expectedBytes); let bytesRead = 0; + if (leftover && leftover.length > 0) { + const copyLen = Math.min(leftover.length, expectedBytes - bytesRead); + receivedData.set(leftover.subarray(0, copyLen), bytesRead); + bytesRead += copyLen; + if (copyLen < leftover.length) { + leftover = leftover.subarray(copyLen); + } else { + leftover = null; + } + } + while (bytesRead < expectedBytes) { const { value, done } = await reader.read(); if (done) break; - receivedData.set(value, bytesRead); - bytesRead += value.length; + const copyLen = Math.min(value.length, expectedBytes - bytesRead); + receivedData.set(value.subarray(0, copyLen), bytesRead); + bytesRead += copyLen; + + if (copyLen < value.length) { + leftover = value.subarray(copyLen); + } } if (bytesRead < expectedBytes) { @@ -118,32 +129,23 @@ async function receiveAndVerify(reader, expectedBytes) { } async function startTest() { + leftover = null; try { DOM.startBtn.disabled = true; updateStatus('Connecting...', 'connecting'); updateProgress(0); DOM.logContainer.innerHTML = ''; - const url = DOM.urlInput.value; - const certHashStr = DOM.hashInput.value.trim(); - - const options = {}; - if (certHashStr) { - try { - const bytes = parseHash(certHashStr); - options.serverCertificateHashes = [{ - algorithm: "sha-256", - value: bytes.buffer - }]; - log('Using provided certificate hash for validation.'); - } catch (e) { - log('Invalid certificate hash format.', 'error'); - throw e; - } + let url = "https://localhost:7681/"; + if (window.location.protocol.startsWith('http')) { + const base = new URL('../', window.location.href).href; + url = base.replace(/^http:/, 'https:'); } log(`Connecting to WebTransport at ${url}`); - wt = new WebTransport(url, options); + wt = new WebTransport(url, { + protocols: ['webtransport-test'] + }); await wt.ready; updateStatus('Connected', 'connected'); @@ -177,7 +179,12 @@ async function startTest() { } log('Test Completed Successfully!', 'success'); - await writer.close(); + try { + writer.releaseLock(); + } catch (e) {} + try { + reader.releaseLock(); + } catch (e) {} wt.close(); updateStatus('Disconnected', 'disconnected'); diff --git a/plugins/protocol_webtransport_test/assets/index.html b/plugins/protocol_webtransport_test/assets/index.html index 2440c60b93..baede4fbf5 100644 --- a/plugins/protocol_webtransport_test/assets/index.html +++ b/plugins/protocol_webtransport_test/assets/index.html @@ -16,15 +16,6 @@

WebTransport Test

-
- - -
-
- - - Required if using self-signed certs without --ignore-certificate-errors flag. -
diff --git a/plugins/protocol_webtransport_test/assets/style.css b/plugins/protocol_webtransport_test/assets/style.css index 48e9255528..50877db5ac 100644 --- a/plugins/protocol_webtransport_test/assets/style.css +++ b/plugins/protocol_webtransport_test/assets/style.css @@ -1,5 +1,3 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); - :root { --bg-color: #0f172a; --card-bg: rgba(30, 41, 59, 0.7); @@ -20,7 +18,7 @@ } body { - font-family: 'Inter', sans-serif; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background-color: var(--bg-color); color: var(--text-primary); line-height: 1.6; diff --git a/plugins/protocol_webtransport_test/protocol_webtransport_test.c b/plugins/protocol_webtransport_test/protocol_webtransport_test.c index 607c7ce2b5..9d56eb2b26 100644 --- a/plugins/protocol_webtransport_test/protocol_webtransport_test.c +++ b/plugins/protocol_webtransport_test/protocol_webtransport_test.c @@ -21,10 +21,10 @@ #include #include -#define BLOB_SIZE (4 * 1024 * 1024) -#define HASH_SIZE 32 -#define TOTAL_SEND_SIZE (BLOB_SIZE + HASH_SIZE) -#define TEST_ITERATIONS 10 +#define BLOB_SIZE (4 * 1024 * 1024) +#define HASH_SIZE 32 +#define TOTAL_SEND_SIZE (BLOB_SIZE + HASH_SIZE) +#define TEST_ITERATIONS 10 struct vhd__wt_test { uint8_t *blob; @@ -98,7 +98,7 @@ callback_wt_test(struct lws *wsi, enum lws_callback_reasons reason, vhd->blob = NULL; } break; - + case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: case LWS_CALLBACK_ESTABLISHED: /* We only care about WebTransport child streams for data transfer */ if (lws_wt_is_session(wsi)) { @@ -123,6 +123,13 @@ callback_wt_test(struct lws *wsi, enum lws_callback_reasons reason, break; to_send = TOTAL_SEND_SIZE - pss->send_pos; + { + int tx_cr = lws_wsi_tx_credit(wsi, LWSTXCR_US_TO_PEER, 0); + if (tx_cr <= 0) + break; + if (to_send > (size_t)tx_cr) + to_send = (size_t)tx_cr; + } if (to_send > 65536) to_send = 65536; /* write in chunks */ @@ -144,6 +151,7 @@ callback_wt_test(struct lws *wsi, enum lws_callback_reasons reason, } m = lws_write(wsi, p, (unsigned int)to_send, LWS_WRITE_BINARY); + lwsl_info("WT WRITEABLE: send_pos %zu, to_send %zu, write_ret %d\n", pss->send_pos, to_send, m); if (m < 0) { lwsl_err("write error\n"); return -1; From 4ae6d2415908804751c3794e0fef5a9f748750e6 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 14 Jul 2026 11:33:25 +0100 Subject: [PATCH 21/44] cleanup --- README.md | 6 +- READMEs/README.lwsws.md | 12 +++- include/libwebsockets.h | 1 + include/libwebsockets/lws-misc.h | 9 +++ lib/core-net/adopt.c | 16 +++-- lib/core-net/client/connect3.c | 8 ++- lib/core-net/pollfd.c | 5 -- lib/roles/h3/ops-h3.c | 4 +- lib/roles/quic/crypto-quic.c | 4 -- lib/roles/quic/ops-quic.c | 69 +++++++++++++------ lib/secure-streams/cpp/lss.cxx | 4 +- lib/tls/gnutls/gnutls-quic.c | 5 +- lib/tls/openssl/openssl-ssl.c | 8 ++- lib/tls/tls.c | 15 +++- .../api-tests/api-test-qpack/CMakeLists.txt | 1 + .../minimal-raw-webrtc-camshow.c | 32 ++++++--- .../minimal-webtransport-qir/CMakeLists.txt | 3 + .../minimal-webtransport-qir.c | 5 +- .../protocol_deaddrop/protocol_lws_deaddrop.c | 2 +- 19 files changed, 146 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 4065041c3d..8ba49580bb 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ** NEW features available on main ** - - Support for SChannel (windows native TLS, no need for OpenSSL build!), GnuTLS, and BearSSL added - - QUIC + H3 + Webtranspoer implementation, using aws-lc, wolfssl, boringssl, libressl, gnutls, and schannel (OpenSSL is h1/h2 -only, mbedtls can do it via a patch) + - Support for SChannel (windows native TLS, no need for OpenSSL build!), GnuTLS, openHiTLS and BearSSL added + - QUIC + H3 + Webtransport implementation, using aws-lc, wolfssl, boringssl, libressl, gnutls, and schannel (OpenSSL is h1/h2 -only; mbedtls can do it via a patch) |LWS version|Platform|Protocols|Default TLS| |---|---|---|---| @@ -33,7 +33,7 @@ quic/h3 is enabled for build by default... necessitating GnuTLS instead of OpenS | **openHiTLS** | **Yes** | **Yes** | **No** | **Yes** | **Yes** | **Yes** | Yes (not SRTP) | **Yes** | **Yes** | **Yes** | -\* *Note: 1) Upstream OpenSSL does not provide the necessary QUIC TLS API (`SSL_set_quic_method`) to act as a cryptographic engine for LWS's QUIC transport. If you need QUIC/HTTP3 support, we recommend using BoringSSL, GnuTLS, WolfSSL, or the `quictls` fork of OpenSSL.* +\* *Note: 1) Upstream OpenSSL does not provide the necessary QUIC TLS API (`SSL_set_quic_method`) to act as a cryptographic engine for LWS's QUIC transport. If you need QUIC/HTTP3 support, we recommend using BoringSSL or GnuTLS.* \* *Note: 2) openHiTLS does not provide the necessary QUIC TLS API * - DHT support built-in: `-DLWS_WITH_DHT=1` diff --git a/READMEs/README.lwsws.md b/READMEs/README.lwsws.md index 7702ff87f5..656c4e85f4 100644 --- a/READMEs/README.lwsws.md +++ b/READMEs/README.lwsws.md @@ -217,6 +217,16 @@ to be selected using "raw": "1" See also "apply-listen-accept" below. +## Care about redirecting the trailing / + +In lws the mount applies at the URL folder, so a mount at /myapp actually works starting from /myapp/. + +This means you often need to add an additional redirect mount to do the last step for the user who +may have typed .../myapp, to get them to .../myapp/ where the app actually takes over. + + "mountpoint": "/git", + "origin": ">https://warmcat.com/git/", + ## Lwsws Other vhost options - If the three options `host-ssl-cert`, `host-ssl-ca` and `host-ssl-key` are given, then the vhost supports SSL. @@ -479,7 +489,7 @@ Auth before the ws upgrade, this is also possible. In this case, the The mounts in lws allow you to stack up other plugins that run "before" the main mountpoint. There are two "interceptor plugins" provided which can be useful for this, -`lws_login` and `lws_captcha_ratelimit` +`lws_login` and `lws_captcha_ratelimit`, you can create your own based on these. To indicate you want to use an interceptor plugin for a mount, you add an "interceptor-path" entry to the original mount definition, pointing to the diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 5cf39a6019..cdddc47818 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -869,6 +869,7 @@ lws_fx_string(const lws_fx_t *a, char *buf, size_t size); #include #if defined(LWS_ROLE_H3) #include +#include #endif #include #include diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h index f0cd02f37c..1c150ed776 100644 --- a/include/libwebsockets/lws-misc.h +++ b/include/libwebsockets/lws-misc.h @@ -712,6 +712,15 @@ lws_get_library_version(void); LWS_VISIBLE LWS_EXTERN void * lws_wsi_user(struct lws *wsi); +/** + * lws_ensure_user_space() - allocate user space for wsi if not already allocated + * \param wsi: lws connection + * + * returns 0 if user space exists or was allocated, else non-zero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_ensure_user_space(struct lws *wsi); + /** * lws_wsi_tsi() - get the service thread index the wsi is bound to * \param wsi: lws connection diff --git a/lib/core-net/adopt.c b/lib/core-net/adopt.c index 8abb3b0882..34d5661ee5 100644 --- a/lib/core-net/adopt.c +++ b/lib/core-net/adopt.c @@ -807,8 +807,12 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads, { int opt = 4 * 1024 * 1024; - setsockopt(sock.sockfd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)); - setsockopt(sock.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)); + if (setsockopt(sock.sockfd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)) < 0) { + lwsl_wsi_warn(wsi, "Failed to set SO_RCVBUF"); + } + if (setsockopt(sock.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)) < 0) { + lwsl_wsi_warn(wsi, "Failed to set SO_SNDBUF"); + } } if (lws_plat_apply_FD_CLOEXEC((int)sock.sockfd) || @@ -982,8 +986,12 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads, { int opt = 4 * 1024 * 1024; - setsockopt(sock.sockfd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)); - setsockopt(sock.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)); + if (setsockopt(sock.sockfd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)) < 0) { + lwsl_wsi_warn(wsi, "Failed to set SO_RCVBUF"); + } + if (setsockopt(sock.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)) < 0) { + lwsl_wsi_warn(wsi, "Failed to set SO_SNDBUF"); + } } if (lws_plat_apply_FD_CLOEXEC((int)sock.sockfd) || diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 31b307cfa4..1fb46c37f5 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -650,8 +650,12 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads, new_fd = socket(wsi->sa46_peer.sa4.sin_family, want_udp ? SOCK_DGRAM : SOCK_STREAM, 0); if (lws_socket_is_valid(new_fd) && want_udp) { int opt = 4 * 1024 * 1024; - setsockopt(new_fd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)); - setsockopt(new_fd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)); + if (setsockopt(new_fd, SOL_SOCKET, SO_RCVBUF, (const char *)&opt, sizeof(opt)) < 0) { + lwsl_wsi_warn(wsi, "Failed to set SO_RCVBUF"); + } + if (setsockopt(new_fd, SOL_SOCKET, SO_SNDBUF, (const char *)&opt, sizeof(opt)) < 0) { + lwsl_wsi_warn(wsi, "Failed to set SO_SNDBUF"); + } } } diff --git a/lib/core-net/pollfd.c b/lib/core-net/pollfd.c index 81a413bdcc..344b32c89c 100644 --- a/lib/core-net/pollfd.c +++ b/lib/core-net/pollfd.c @@ -507,11 +507,6 @@ __lws_change_pollfd(struct lws *wsi, int _and, int _or) struct lws_pollargs pa; int ret = 0; - if (wsi && wsi->role_ops && !strcmp(wsi->role_ops->name, "quic")) { - lwsl_notice("AGY-DEBUG: __lws_change_pollfd for %s: position=%d, protocol=%p, event_pipe=%d, _and=%d, _or=%d\n", - lws_wsi_tag(wsi), wsi->position_in_fds_table, wsi->a.protocol, wsi->event_pipe, _and, _or); - } - if (!wsi || (!wsi->a.protocol && !wsi->event_pipe) || wsi->position_in_fds_table == LWS_NO_FDS_POS) return 0; diff --git a/lib/roles/h3/ops-h3.c b/lib/roles/h3/ops-h3.c index 55385d9260..99d53de416 100644 --- a/lib/roles/h3/ops-h3.c +++ b/lib/roles/h3/ops-h3.c @@ -866,7 +866,7 @@ lws_h3_rx_stream_data(struct lws *wsi, const uint8_t *buf, size_t len) } size_t total_consumed = consumed_type + consumed_sid; - if (len > total_consumed) { + if (len > total_consumed && wsi->a.protocol && wsi->a.protocol->callback) { wsi->a.protocol->callback(wsi, LWS_CALLBACK_RECEIVE, wsi->user_space, (void *)(buf + total_consumed), len - total_consumed); } return 0; @@ -913,7 +913,7 @@ lws_h3_rx_stream_data(struct lws *wsi, const uint8_t *buf, size_t len) } size_t total_consumed = consumed_type + consumed_sid; - if (len > total_consumed) { + if (len > total_consumed && wsi->a.protocol && wsi->a.protocol->callback) { wsi->a.protocol->callback(wsi, LWS_CALLBACK_RECEIVE, wsi->user_space, (void *)(buf + total_consumed), len - total_consumed); } return 0; diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index 931de526c5..c8eb1c4332 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -749,9 +749,6 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le len = wsi->quic.qn->crypto_rx_buf_len[level]; } - lwsl_wsi_notice(wsi, "lws_tls_quic_rx_crypto: level %d, len %zu, buf[0]=%d", level, len, buf[0]); - lwsl_hexdump_notice(buf, len > 32 ? 32 : len); - size_t scan = 0; size_t complete_len = 0; while (scan < len) { @@ -760,7 +757,6 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le } uint8_t type = buf[scan]; - lwsl_wsi_notice(wsi, "QUIC RX CRYPTO: checking message type %d at offset %zu", type, scan); if (type == 24 || type == 5) { lwsl_wsi_notice(wsi, "QUIC RX CRYPTO: Illegal TLS Handshake type %d", type); lws_quic_enter_closing_state(wsi, 0x0100 + 10 /* unexpected_message */, 0, 0); diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 8149e9712d..c387a4428a 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -2093,12 +2093,17 @@ rops_handle_POLLOUT_quic(struct lws *wsi) n = (int)send_len; /* Pretend it succeeded */ } else { const lws_sockaddr46 *dest_sa46 = NULL; - if (has_packet_dest) - dest_sa46 = &packet_dest_sa46; - else if (wsi->udp) - dest_sa46 = &wsi->udp->sa46; - else if (wsi->mux_substream && wsi->mux.parent_wsi && wsi->mux.parent_wsi->udp) - dest_sa46 = &wsi->mux.parent_wsi->udp->sa46; + struct lws *nwsi_quic = lws_get_quic_network_wsi(wsi); + int is_client = nwsi_quic ? lwsi_role_client(nwsi_quic) : lwsi_role_client(wsi); + + if (!is_client) { + if (has_packet_dest) + dest_sa46 = &packet_dest_sa46; + else if (wsi->udp) + dest_sa46 = &wsi->udp->sa46; + else if (wsi->mux_substream && wsi->mux.parent_wsi && wsi->mux.parent_wsi->udp) + dest_sa46 = &wsi->mux.parent_wsi->udp->sa46; + } { char adrbuf[64] = "none"; @@ -2249,6 +2254,25 @@ rops_handle_POLLOUT_quic(struct lws *wsi) qn->handshake_done, (int)wsi->txc.tx_cr, (nwsi ? (int)nwsi->txc.tx_cr : 0)); /* Process stream queues for Application (1-RTT) and Early (0-RTT) data */ if (qn && (qn->handshake_done || qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED || qn->early_data_status == LWS_0RTT_STATUS_ACCEPTED)) { + /* + * If the parent connection is blocked by congestion window or pacing, + * we do not poll child streams to prevent buffering bloat. + * However, we still proceed to end_children to allow ACKs and PTO probes to be flushed. + */ + if (qn->handshake_done && + !qn->pto_probe_needed && + qn->cc_ops && + qn->cc_ops->can_send && + !qn->cc_ops->can_send(wsi, qn->current_mtu)) + goto end_children; + + if (qn->handshake_done && + !qn->pto_probe_needed && + qn->cc_ops && + qn->cc_ops->get_pacing_delay && + qn->cc_ops->get_pacing_delay(wsi, qn->current_mtu) > 0) + goto end_children; + if (lws_wsi_txc_check_skint(&wsi->txc, (int32_t)wsi->txc.tx_cr)) goto end_children; if (nwsi && lws_wsi_txc_check_skint(&nwsi->txc, (int32_t)nwsi->txc.tx_cr)) @@ -2256,25 +2280,28 @@ rops_handle_POLLOUT_quic(struct lws *wsi) struct lws **wsi2 = &wsi->mux.child_list; - { - struct lws *curr = wsi->mux.child_list; - int sanity = 1000000; - lwsl_debug("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); - while (curr && sanity--) { - lwsl_debug("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", - lws_wsi_tag(curr), curr->mux.requested_POLLOUT, (int)curr->txc.tx_cr); - curr = curr->mux.sibling_list; - } - } - if (*wsi2) { + { + struct lws *curr = wsi->mux.child_list; int sanity = 1000000; + lwsl_debug("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); + while (curr && sanity--) { + lwsl_debug("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", + lws_wsi_tag(curr), curr->mux.requested_POLLOUT, (int)curr->txc.tx_cr); + curr = curr->mux.sibling_list; + } + } + if (*wsi2) { + int num_children = 0; + struct lws *curr = *wsi2; + while (curr) { + num_children++; + curr = curr->mux.sibling_list; + } do { struct lws *w, **wa; - if (!sanity--) { - lwsl_wsi_warn(wsi, "POLLOUT multiplexer loop sanity limit reached, closing"); - return LWS_HP_RET_BAIL_DIE; - } + if (num_children-- <= 0) + break; wa = &(*wsi2)->mux.sibling_list; diff --git a/lib/secure-streams/cpp/lss.cxx b/lib/secure-streams/cpp/lss.cxx index c1cb89ed34..c5bfba3057 100644 --- a/lib/secure-streams/cpp/lss.cxx +++ b/lib/secure-streams/cpp/lss.cxx @@ -31,9 +31,9 @@ static const char *pcols[] = { "https://", "h2://", /* LWSSSP_H2 */ "h2s://", - "ws://", /* LWSSSP_WS */ + "ws://", /* LWSSSP_WS */ // NOSONAR "wss://", - "mqtt://", /* LWSSSP_MQTT */ + "mqtt://", /* LWSSSP_MQTT */ // NOSONAR "mqtts://", "raw://", /* LWSSSP_RAW */ "raws://", diff --git a/lib/tls/gnutls/gnutls-quic.c b/lib/tls/gnutls/gnutls-quic.c index f37982d78e..a2566aa8fd 100644 --- a/lib/tls/gnutls/gnutls-quic.c +++ b/lib/tls/gnutls/gnutls-quic.c @@ -294,14 +294,11 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) lws_snprintf(priority, sizeof(priority), "NORMAL:-VERS-ALL:+VERS-TLS1.3:%%DISABLE_TLS13_COMPAT_MODE"); } - lwsl_notice("AGY-DEBUG: gnutls_quic_init priority string used = %s\n", priority); /* Enforce QUIC requirements: TLS 1.3 only, NO compatibility mode (empty legacy_session_id) */ ret = gnutls_priority_set_direct(session, priority, NULL); - if (ret < 0) { + if (ret < 0) lwsl_notice("gnutls_priority_set_direct failed: %s\n", gnutls_strerror(ret)); - lwsl_notice("AGY-DEBUG: gnutls_priority_set_direct failed: %s\n", gnutls_strerror(ret)); - } if (!wsi->tls.quic_tp_send) { /* Construct dynamically to include loc_cid */ diff --git a/lib/tls/openssl/openssl-ssl.c b/lib/tls/openssl/openssl-ssl.c index b52c077cfb..ab771ad578 100644 --- a/lib/tls/openssl/openssl-ssl.c +++ b/lib/tls/openssl/openssl-ssl.c @@ -238,7 +238,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, size_t len) { unsigned int ms = (unsigned int)((lws_now_usecs() - _lws_start) / 1000); if (ms > 2) { - lws_latency_note(&wsi->a.context->pt[(int)wsi->tsi], _lws_start, 2000, "SSL_read:%dms", ms); + lws_latency_note(pt, _lws_start, 2000, "SSL_read:%dms", ms); } } #endif @@ -381,6 +381,10 @@ lws_ssl_pending(struct lws *wsi) int lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, size_t len) { +#if defined(LWS_WITH_LATENCY) + struct lws_context *context = wsi->a.context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; +#endif int n, m; #if defined(LWS_TLS_LOG_PLAINTEXT_TX) @@ -404,7 +408,7 @@ lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, size_t len) n = SSL_write(wsi->tls.ssl, buf, (int)(ssize_t)len); unsigned int ms = (unsigned int)((lws_now_usecs() - _lws_start) / 1000); if (ms > 2) { - lws_latency_note(&wsi->a.context->pt[(int)wsi->tsi], _lws_start, 2000, "SSL_write:%dms", ms); + lws_latency_note(pt, _lws_start, 2000, "SSL_write:%dms", ms); } } #else diff --git a/lib/tls/tls.c b/lib/tls/tls.c index 8095d87c0c..02f440257a 100644 --- a/lib/tls/tls.c +++ b/lib/tls/tls.c @@ -758,7 +758,7 @@ lws_x509_cert_fingerprint(struct lws_x509_cert *x509, int type, uint8_t *der; int ret = -1; - hash_len = lws_genhash_size(type); + hash_len = lws_genhash_size((enum lws_genhash_types)type); if (!hash_len || len < hash_len) return -1; @@ -771,7 +771,7 @@ lws_x509_cert_fingerprint(struct lws_x509_cert *x509, int type, if (lws_x509_info(x509, LWS_TLS_CERT_INFO_DER_RAW, res, 4096)) goto bail; - if (lws_genhash_init(&hctx, type)) + if (lws_genhash_init(&hctx, (enum lws_genhash_types)type)) goto bail; if (lws_genhash_update(&hctx, res->ns.name, (size_t)res->ns.len)) { @@ -827,6 +827,7 @@ lws_tls_cert_get_x509_validity(struct lws_context *context, const char *filepath return res; } +#if defined(LWS_WITH_DIR) struct versioned_certs_scan { const char *prefix; const char *suffix; @@ -928,6 +929,7 @@ lws_tls_find_versioned_certs(const char *filepath, char *dirpath, size_t dirpath if (scan.previous[0]) lws_snprintf(previous, previous_len, "%s%s", dirpath, scan.previous); } +#endif int lws_tls_resolve_grace_period_certs(struct lws_context *context, @@ -935,19 +937,24 @@ lws_tls_resolve_grace_period_certs(struct lws_context *context, char *resolved_cert, size_t resolved_cert_len, char *resolved_key, size_t resolved_key_len) { +#if defined(LWS_WITH_DIR) char dirpath_cert[256], newest_cert[256], previous_cert[256]; char dirpath_key[256], newest_key[256], previous_key[256]; time_t now = time(NULL); time_t newest_not_before = 0; time_t previous_not_after = 0; +#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_FILE_OPS) lws_system_policy_t *policy = NULL; +#endif char d_path[1024]; int grace_period = 900; /* 15 mins default */ int fd_cfg; +#endif lws_strncpy(resolved_cert, certpath, resolved_cert_len); lws_strncpy(resolved_key, keypath, resolved_key_len); +#if defined(LWS_WITH_DIR) /* Check if it is a versioned path */ if (!strstr(certpath, "-latest.crt") && !strstr(certpath, "-latest-fullchain.crt")) return 0; @@ -971,10 +978,12 @@ lws_tls_resolve_grace_period_certs(struct lws_context *context, } lws_snprintf(d_path, sizeof(d_path), "/etc/lwsws/acme/acme_config.json"); +#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_FILE_OPS) if (lws_system_parse_policy(context, "/etc/lwsws/policy", &policy) == 0 && policy) { lws_snprintf(d_path, sizeof(d_path), "%s/acme_config.json", policy->dns_base_dir); lws_system_policy_free(policy); } +#endif fd_cfg = open(d_path, O_RDONLY); if (fd_cfg >= 0) { @@ -1013,7 +1022,9 @@ lws_tls_resolve_grace_period_certs(struct lws_context *context, lws_strncpy(resolved_cert, newest_cert, resolved_cert_len); lws_strncpy(resolved_key, newest_key, resolved_key_len); } +#endif return 0; } + diff --git a/minimal-examples-lowlevel/api-tests/api-test-qpack/CMakeLists.txt b/minimal-examples-lowlevel/api-tests/api-test-qpack/CMakeLists.txt index ccdb7ec809..f0f010789b 100644 --- a/minimal-examples-lowlevel/api-tests/api-test-qpack/CMakeLists.txt +++ b/minimal-examples-lowlevel/api-tests/api-test-qpack/CMakeLists.txt @@ -62,6 +62,7 @@ set(requirements 1) require_lws_config(LWS_WITH_HTTP3 1 requirements) require_lws_config(LWS_WITH_NETWORK 1 requirements) +require_lws_config(LWS_WITH_DIR 1 requirements) if (requirements) if (LWS_WITH_LS_QPACK) diff --git a/minimal-examples-lowlevel/raw/minimal-raw-webrtc-camshow/minimal-raw-webrtc-camshow.c b/minimal-examples-lowlevel/raw/minimal-raw-webrtc-camshow/minimal-raw-webrtc-camshow.c index f7f0aa7ac7..4c0c13752b 100644 --- a/minimal-examples-lowlevel/raw/minimal-raw-webrtc-camshow/minimal-raw-webrtc-camshow.c +++ b/minimal-examples-lowlevel/raw/minimal-raw-webrtc-camshow/minimal-raw-webrtc-camshow.c @@ -43,7 +43,6 @@ static const struct lws_switches switches[] = { static const char *url = "wss://127.0.0.1:7681"; static const char *devs_list = "/dev/video0"; static const char *audio_dev = "default"; -static char *devices_copy = NULL; static const char *client_name; static uint32_t app_width = 1280; static uint32_t app_height = 720; @@ -121,16 +120,33 @@ set_clock(lws_usec_t us) static void start_app_attach(struct lws_vhost *vh, const char *logical_name, const char *access_token) { - char *devices_copy_local = strdup(devs_list); - char *p = devices_copy_local, *token; + struct lws_tokenize ts; + lws_tokenize_elem e; + char token[256]; + + lws_tokenize_init(&ts, devs_list, LWS_TOKENIZE_F_COMMA_SEP_LIST | + LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_SLASH_NONTERM | + LWS_TOKENIZE_F_DOT_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS); + + do { + e = lws_tokenize(&ts); + if (e != LWS_TOKZE_TOKEN) + continue; + + if (lws_tokenize_cstr(&ts, token, sizeof(token))) { + lwsl_err("Token too long\n"); + continue; + } - while ((token = strsep(&p, ","))) { lwsl_notice("Attaching %s (audio %s) to WebRTC mixer\n", token, audio_dev); - if (cam_ops->attach(vh, url, token, audio_dev, client_name, app_width, app_height, access_token)) + if (cam_ops->attach(vh, url, token, audio_dev, client_name, + app_width, app_height, access_token)) lwsl_err("Failed to queue attach for %s\n", token); - } - free(devices_copy_local); + } while (e > 0); } static void pairing_indication(struct lws_vhost *vh, const char *logical_name, int start) @@ -268,7 +284,5 @@ main(int argc, const char **argv) lws_context_destroy(cx); - if (devices_copy) free(devices_copy); - return 0; } diff --git a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt index d2f45cdde6..332b4502fd 100644 --- a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt +++ b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/CMakeLists.txt @@ -9,6 +9,9 @@ set(SAMP lws-minimal-webtransport-qir) set(SRCS minimal-webtransport-qir.c) set(requirements 1) +require_lws_config(LWS_WITH_NETWORK 1 requirements) +require_lws_config(LWS_WITH_CLIENT 1 requirements) +require_lws_config(LWS_WITH_SERVER 1 requirements) require_lws_config(LWS_ROLE_WT 1 requirements) require_lws_config(LWS_WITH_HTTP3 1 requirements) diff --git a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c index 02769f2ee4..89e00568cd 100644 --- a/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c +++ b/minimal-examples-lowlevel/webtransport/minimal-webtransport-qir/minimal-webtransport-qir.c @@ -837,7 +837,10 @@ static int callback_qir(struct lws *wsi, enum lws_callback_reasons reason, if (m < n) { /* Seek back the unwritten bytes and retry */ off_t diff = (off_t)(n - m); - lseek(pss->fd_in, -diff, SEEK_CUR); + if (lseek(pss->fd_in, -diff, SEEK_CUR) == (off_t)-1) { + lwsl_err("lseek failed: %d\n", errno); + return -1; + } pss->sent_len -= (size_t)diff; lws_callback_on_writable(wsi); } else { diff --git a/plugins/protocol_deaddrop/protocol_lws_deaddrop.c b/plugins/protocol_deaddrop/protocol_lws_deaddrop.c index aa03d641d8..09b420dc4c 100644 --- a/plugins/protocol_deaddrop/protocol_lws_deaddrop.c +++ b/plugins/protocol_deaddrop/protocol_lws_deaddrop.c @@ -818,7 +818,7 @@ static void deaddrop_handler_server_ws_rx(struct vhd_deaddrop *vhd, struct pss_deaddrop *pss, struct lws *wsi, void *in, size_t len) { -#if defined(__linux__) +#if !defined(WIN32) && !defined(_WIN32) && !defined(LWS_WITH_ESP32) char path[512], resolved_path[PATH_MAX]; #else char path[512]; From ea0833528fba19585bf480e381336614cbf3fee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3FUTF-8=3Fq=3FSa=3DC3=3DBAl=3D20Ibarra=3D20Corretg=3DC?= =?UTF-8?q?3=3DA9=3F=3D?= Date: Tue, 14 Jul 2026 22:00:30 +0200 Subject: [PATCH 22/44] system: policy: build lws_system_parse_policy() without LEJP lws_system_parse_policy() is compiled whenever LWS_WITH_NETWORK && LWS_WITH_FILE_OPS, but it is implemented with the LEJP JSON parser and referenced lejp_construct()/lejp_parse()/lejp_destruct() unconditionally. Callers such as the versioned-cert / ACME rotation path in tls.c (gated only on LWS_WITH_DIR + LWS_WITH_NETWORK + LWS_WITH_FILE_OPS) do not gate on LWS_WITH_LEJP, so a build with LWS_WITH_LEJP=OFF fails to link with undefined lejp_* symbols. Gate the JSON-parsing implementation on LWS_WITH_LEJP and provide a stub that reports "no policy" (returns nonzero, *_policy = NULL) when LEJP is disabled. Existing callers already treat a nonzero return as "no policy available", so they continue to link and behave correctly without a JSON parser. --- lib/system/policy.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/system/policy.c b/lib/system/policy.c index 9d0270e04e..a9c3441da0 100644 --- a/lib/system/policy.c +++ b/lib/system/policy.c @@ -26,6 +26,14 @@ #if defined(LWS_WITH_NETWORK) #if defined(LWS_WITH_FILE_OPS) +/* + * lws_system_parse_policy() parses a JSON policy file and so depends on the + * LEJP JSON parser. Only build the real implementation when LEJP is enabled; + * otherwise provide a stub (in the #else below) that reports "no policy", so + * callers that reference it without gating on LWS_WITH_LEJP (eg, the ACME / + * versioned-cert path in tls.c) still link without pulling in lejp. + */ +#if defined(LWS_WITH_LEJP) static const char * const policy_paths[] = { "dns_base_dir", @@ -157,7 +165,19 @@ lws_system_parse_policy(struct lws_context *cx, const char *filepath, lws_system lws_system_policy_free(p); return 1; } -#endif + +#else /* !LWS_WITH_LEJP */ + +int +lws_system_parse_policy(struct lws_context *cx, const char *filepath, + lws_system_policy_t **_policy) +{ + *_policy = NULL; + return 1; /* no JSON parser available without LEJP */ +} + +#endif /* LWS_WITH_LEJP */ +#endif /* LWS_WITH_FILE_OPS */ void lws_system_policy_free(lws_system_policy_t *policy) From b5a8aa4a926205c1379d4bbd04c3e9821a1a23ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3FUTF-8=3Fq=3FSa=3DC3=3DBAl=3D20Ibarra=3D20Corretg=3DC?= =?UTF-8?q?3=3DA9=3F=3D?= Date: Tue, 14 Jul 2026 22:00:30 +0200 Subject: [PATCH 23/44] http2/http3: client: send request when reusing an idle mux connection When a client mux (h2/h3) connection goes idle between transactions it sits in LRS_IDLING. On reusing it for a new stream, lws_vhost_active_conns() adopts the new stream directly onto the network wsi but left it in LRS_IDLING. LRS_IDLING does not carry LWSIFS_POCB, so lwsi_state_can_handle_POLLOUT() is false for it and the network wsi's POLLOUT is never serviced: the child-walking POLLOUT loop never runs, the new stream's HEADERS are never sent (lws_h2_client_handshake() is never reached), and its response is never read. The request just hangs. Put the revived connection back into LRS_ESTABLISHED (the state it uses while actively muxing) and clear the keep-warm idle timeout, so its POLLOUT is serviced and the queued stream's headers go out. Same fix for the h3/quic branch. --- lib/core-net/vhost.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c index c1885d904a..c451a3a641 100644 --- a/lib/core-net/vhost.c +++ b/lib/core-net/vhost.c @@ -2110,10 +2110,28 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin) lwsl_wsi_info(w, "just join h2 directly 0x%x", lwsi_state(w)); - if (lwsi_state(w) == LRS_IDLING) + if (lwsi_state(w) == LRS_IDLING) { _lws_generic_transaction_completed_active_conn(&w, 0); - //lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2); + /* + * The connection was kept warm in + * LRS_IDLING, which does not carry + * LWSIFS_POCB, so its POLLOUT is never + * serviced (lwsi_state_can_handle_POLLOUT() + * is false). If we adopt the new stream + * while the network wsi is still IDLING, the + * child-walking POLLOUT loop never runs, the + * new stream's HEADERS are never sent + * (lws_h2_client_handshake() is never + * reached) and its response would not be read + * either. Put it back into the same + * LRS_ESTABLISHED state it uses while actively + * muxing, and drop the keep-warm idle timeout + * since it is no longer idle. + */ + lwsi_set_state(w, LRS_ESTABLISHED); + lws_set_timeout(w, NO_PENDING_TIMEOUT, 0); + } wsi->client_h2_alpn = 1; lws_wsi_h2_adopt(w, wsi); @@ -2140,9 +2158,17 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin) lwsi_state(w)); - if (lwsi_state(w) == LRS_IDLING) + if (lwsi_state(w) == LRS_IDLING) { _lws_generic_transaction_completed_active_conn(&w, 0); + /* See the h2 branch above: a revived idle + * mux connection must leave LRS_IDLING so its + * POLLOUT is serviced and the new stream's + * headers get sent. */ + lwsi_set_state(w, LRS_ESTABLISHED); + lws_set_timeout(w, NO_PENDING_TIMEOUT, 0); + } + wsi->client_h2_alpn = 1; if (lws_wsi_h3_adopt(w, wsi)) { lws_vhost_unlock(wsi->a.vhost); /* } ---------- */ From da7743e21e950e910eee75c1514f16ae6179215d Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 17 Jul 2026 12:55:47 +0100 Subject: [PATCH 24/44] delta-to-2152b1b9 --- lib/roles/quic/crypto-quic.c | 8 ------ lib/roles/quic/ops-quic.c | 52 ++++++++++------------------------ lib/tls/gnutls/lws-genaes.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/lib/roles/quic/crypto-quic.c b/lib/roles/quic/crypto-quic.c index c8eb1c4332..4e4ab87677 100644 --- a/lib/roles/quic/crypto-quic.c +++ b/lib/roles/quic/crypto-quic.c @@ -886,14 +886,6 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le wsi->quic.qn->handshake_done = 1; - /* Move any unsent 0-RTT (early) data frames to 1-RTT (app) level */ - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - wsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_EARLY].head) { - struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); - lws_dll2_remove(&f->list); - lws_dll2_add_tail(&f->list, &wsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP]); - } lws_end_foreach_dll_safe(d, d1); - if (wsi->quic.qn->is_server) { struct lws_quic_tx_frame *f_hd = lws_zalloc(sizeof(*f_hd), "HANDSHAKE_DONE"); if (f_hd) { diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index c387a4428a..f79b43a13d 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -2254,25 +2254,6 @@ rops_handle_POLLOUT_quic(struct lws *wsi) qn->handshake_done, (int)wsi->txc.tx_cr, (nwsi ? (int)nwsi->txc.tx_cr : 0)); /* Process stream queues for Application (1-RTT) and Early (0-RTT) data */ if (qn && (qn->handshake_done || qn->early_data_status == LWS_0RTT_STATUS_ATTEMPTED || qn->early_data_status == LWS_0RTT_STATUS_ACCEPTED)) { - /* - * If the parent connection is blocked by congestion window or pacing, - * we do not poll child streams to prevent buffering bloat. - * However, we still proceed to end_children to allow ACKs and PTO probes to be flushed. - */ - if (qn->handshake_done && - !qn->pto_probe_needed && - qn->cc_ops && - qn->cc_ops->can_send && - !qn->cc_ops->can_send(wsi, qn->current_mtu)) - goto end_children; - - if (qn->handshake_done && - !qn->pto_probe_needed && - qn->cc_ops && - qn->cc_ops->get_pacing_delay && - qn->cc_ops->get_pacing_delay(wsi, qn->current_mtu) > 0) - goto end_children; - if (lws_wsi_txc_check_skint(&wsi->txc, (int32_t)wsi->txc.tx_cr)) goto end_children; if (nwsi && lws_wsi_txc_check_skint(&nwsi->txc, (int32_t)nwsi->txc.tx_cr)) @@ -2280,28 +2261,25 @@ rops_handle_POLLOUT_quic(struct lws *wsi) struct lws **wsi2 = &wsi->mux.child_list; - { - struct lws *curr = wsi->mux.child_list; - int sanity = 1000000; - lwsl_debug("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); - while (curr && sanity--) { - lwsl_debug("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", - lws_wsi_tag(curr), curr->mux.requested_POLLOUT, (int)curr->txc.tx_cr); - curr = curr->mux.sibling_list; - } - } + { + struct lws *curr = wsi->mux.child_list; + int sanity = 1000000; + lwsl_debug("QUIC TX POLLOUT: nwsi=%s, tx_cr=%d\n", lws_wsi_tag(wsi), (int)wsi->txc.tx_cr); + while (curr && sanity--) { + lwsl_debug("QUIC TX POLLOUT: child: %s, requested_POLLOUT=%d, tx_cr=%d\n", + lws_wsi_tag(curr), curr->mux.requested_POLLOUT, (int)curr->txc.tx_cr); + curr = curr->mux.sibling_list; + } + } if (*wsi2) { - int num_children = 0; - struct lws *curr = *wsi2; - while (curr) { - num_children++; - curr = curr->mux.sibling_list; - } + int sanity = 1000000; do { struct lws *w, **wa; - if (num_children-- <= 0) - break; + if (!sanity--) { + lwsl_wsi_warn(wsi, "POLLOUT multiplexer loop sanity limit reached, closing"); + return LWS_HP_RET_BAIL_DIE; + } wa = &(*wsi2)->mux.sibling_list; diff --git a/lib/tls/gnutls/lws-genaes.c b/lib/tls/gnutls/lws-genaes.c index 0b7c4b7890..88a7450bcc 100644 --- a/lib/tls/gnutls/lws-genaes.c +++ b/lib/tls/gnutls/lws-genaes.c @@ -55,6 +55,13 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op, case LWS_GAESM_CFB8: if (key.size == 16) alg = GNUTLS_CIPHER_AES_128_CFB8; break; + case LWS_GAESM_CTR: + switch (key.size) { + case 16: alg = GNUTLS_CIPHER_AES_128_CBC; break; + case 24: alg = GNUTLS_CIPHER_AES_192_CBC; break; + case 32: alg = GNUTLS_CIPHER_AES_256_CBC; break; + } + break; case LWS_GAESM_GCM: switch (key.size) { case 16: alg = GNUTLS_CIPHER_AES_128_GCM; break; @@ -182,6 +189,54 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len, uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen) { int n; + (void)n; + + if (ctx->mode == LWS_GAESM_CTR) { + uint8_t counter[16]; + uint8_t keystream[16]; + size_t offset = 0; + + if (!iv_or_nonce_ctr_or_data_unit_16) { + lwsl_err("%s: CTR mode requires counter IV\n", __func__); + return -1; + } + + memcpy(counter, iv_or_nonce_ctr_or_data_unit_16, 16); + + while (offset < len) { + size_t block_len = len - offset; + if (block_len > 16) + block_len = 16; + + /* Reset CBC IV to zero for single block encryption (ECB mode emulation) */ + uint8_t zero_iv[16] = {0}; + gnutls_cipher_set_iv(ctx->ctx, zero_iv, 16); + + /* Encrypt the counter block */ + if (gnutls_cipher_encrypt2(ctx->ctx, counter, 16, keystream, 16) < 0) { + lwsl_err("%s: CTR block encryption failed\n", __func__); + return -1; + } + + /* XOR with input */ + for (size_t i = 0; i < block_len; i++) { + out[offset + i] = in[offset + i] ^ keystream[i]; + } + + /* Increment counter (128-bit big-endian) */ + for (int i = 15; i >= 0; i--) { + counter[i]++; + if (counter[i] != 0) + break; + } + + offset += block_len; + } + + /* Save updated counter back to IV buffer */ + memcpy(iv_or_nonce_ctr_or_data_unit_16, counter, 16); + return 0; + } if (ctx->mode == LWS_GAESM_CBC || ctx->mode == LWS_GAESM_ECB) { if (!(ctx->op == LWS_GAESO_ENC && ctx->padding == LWS_GAESP_WITH_PADDING)) { From 32e71fc5350e1d7a1645884e524dab970d5d8d1a Mon Sep 17 00:00:00 2001 From: Harry Bock Date: Thu, 16 Jul 2026 16:09:35 -0400 Subject: [PATCH 25/44] libuv: clear told_event_loop_closed when new poll watcher attached during accept If wsi->told_event_loop_closed was set during a manual close, eg lws_libuv_closehandle() or elops_close_handle_manually_uv(), it would remain set even if the wsi is re-used. This causes SSL timeout issues and fd leaks with HTTP -> HTTPS client redirects. Clear wsi->told_event_loop_closed in elops_accept_uv(), to prevent this issue on wsi re-use. It's harmless on a new connection. --- lib/event-libs/libuv/libuv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c index 684d5e64aa..7bb9aa0249 100644 --- a/lib/event-libs/libuv/libuv.c +++ b/lib/event-libs/libuv/libuv.c @@ -531,6 +531,14 @@ elops_accept_uv(struct lws *wsi) ((uv_handle_t *)w_read->pwatcher)->data = (void *)wsi; + /* + * A fresh poll watcher has just been attached to this wsi. If the wsi + * is being reused (eg, http->https redirect via lws_client_reset()), + * told_event_loop_closed may still be latched from the previous close. + * Clear it now that a new handle is live. + */ + wsi->told_event_loop_closed = 0; + ptpriv->extant_handles++; lwsl_wsi_debug(wsi, "thr %d: sa left %d: dyn left: %d", From 1c12b9a44fcacd8c3a3417c24382ba9866863746 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 17 Jul 2026 05:02:06 +0100 Subject: [PATCH 26/44] ZDI-CAN-31039: enter wrong state When handling LMQCPP_PUBACK_PROPERTIES_LEN_VBI/COMPLETED This vulnerability was discovered by: Anonymous working with TrendAI Zero Day Initiative --- lib/roles/mqtt/mqtt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/roles/mqtt/mqtt.c b/lib/roles/mqtt/mqtt.c index 85e9840155..e743c38122 100644 --- a/lib/roles/mqtt/mqtt.c +++ b/lib/roles/mqtt/mqtt.c @@ -1278,8 +1278,7 @@ _lws_mqtt_rx_parser(struct lws *wsi, lws_mqtt_parser_t *par, * Otherwise consume the properties before * completing the command */ - lws_mqtt_vbi_init(&par->vbit); - par->state = LMQCPP_PUBACK_VH_PKT_ID; + par->state = LMQCPP_EAT_PROPERTIES_AND_COMPLETE; break; default: lwsl_notice("%s: puback pr bad vbi\n", __func__); From 699ab7c92280962b5e1bf9293ad3f77ac23312b0 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 17 Jul 2026 08:42:52 +0100 Subject: [PATCH 27/44] http parser: fixes --- lib/roles/http/parsers.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/roles/http/parsers.c b/lib/roles/http/parsers.c index 4053a244e1..5e13a5c02d 100644 --- a/lib/roles/http/parsers.c +++ b/lib/roles/http/parsers.c @@ -618,7 +618,7 @@ lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen) ll = wsi->http.ah->unk_ll_head; while (ll) { - if (ll >= wsi->http.ah->data_length) + if (ll + UHO_NAME >= wsi->http.ah->data_length) return -1; if (nlen == lws_ser_ru16be( (uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) && @@ -646,7 +646,7 @@ lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name, ll = wsi->http.ah->unk_ll_head; while (ll) { - if (ll >= wsi->http.ah->data_length) + if (ll + UHO_NAME >= wsi->http.ah->data_length) return -1; if (nlen == lws_ser_ru16be( (uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) && @@ -678,7 +678,7 @@ lws_hdr_custom_name_foreach(struct lws *wsi, lws_hdr_custom_fe_cb_t cb, ll = wsi->http.ah->unk_ll_head; while (ll) { - if (ll >= wsi->http.ah->data_length) + if (ll + UHO_NAME >= wsi->http.ah->data_length) return -1; cb(&wsi->http.ah->data[ll + UHO_NAME], From 52c539fb07b670a305482fb4c1a08728ca993eba Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 16 Jul 2026 17:31:49 +0100 Subject: [PATCH 28/44] wt-shared-world --- lib/roles/h3/ops-h3.c | 57 +- lib/roles/quic/parse-quic.c | 9 + .../minimal-http-server-sse-ring.c | 2 + .../raw/minimal-raw-file/minimal-raw-file.c | 2 + .../raw/minimal-raw-serial/minimal-raw-file.c | 2 + .../minimal-ws-client-tx/minimal-ws-client.c | 2 + .../protocol_lws_minimal.c | 2 + plugins/protocol_lws_ssh_base/telnet.c | 2 + .../assets/app2.js | 494 ++++++++++++++---- .../assets/index.html | 1 + .../assets/style.css | 73 +++ .../protocol_webtransport_shared_world.c | 238 ++++++++- 12 files changed, 749 insertions(+), 135 deletions(-) diff --git a/lib/roles/h3/ops-h3.c b/lib/roles/h3/ops-h3.c index 99d53de416..856930a996 100644 --- a/lib/roles/h3/ops-h3.c +++ b/lib/roles/h3/ops-h3.c @@ -856,9 +856,7 @@ lws_h3_rx_stream_data(struct lws *wsi, const uint8_t *buf, size_t len) lws_role_transition(wsi, lwsi_role_client(wsi) ? LWSIFR_CLIENT : LWSIFR_SERVER, LRS_ESTABLISHED, &role_ops_wt); wsi->wt.is_unidi = 1; wsi->wt.is_session = 0; - wsi->a.protocol = session_wsi->a.protocol; - - if (lws_ensure_user_space(wsi)) + if (lws_bind_protocol(wsi, session_wsi->a.protocol, __func__)) return 1; if (wsi->a.protocol && wsi->a.protocol->callback) { @@ -903,9 +901,7 @@ lws_h3_rx_stream_data(struct lws *wsi, const uint8_t *buf, size_t len) lws_role_transition(wsi, lwsi_role_client(wsi) ? LWSIFR_CLIENT : LWSIFR_SERVER, LRS_ESTABLISHED, &role_ops_wt); wsi->wt.is_unidi = 0; wsi->wt.is_session = 0; - wsi->a.protocol = session_wsi->a.protocol; - - if (lws_ensure_user_space(wsi)) + if (lws_bind_protocol(wsi, session_wsi->a.protocol, __func__)) return 1; if (wsi->a.protocol && wsi->a.protocol->callback) { @@ -1653,7 +1649,7 @@ rops_check_upgrades_h3(struct lws *wsi) #if defined(LWS_ROLE_WT) lwsl_info("Upgrade h3 to wt\n"); extern const struct lws_role_ops role_ops_wt; - unsigned char response_buf[LWS_PRE + 1024], *rp = response_buf + LWS_PRE, *end = response_buf + sizeof(response_buf); + unsigned char response_buf[LWS_PRE + 4096], *rp = response_buf + LWS_PRE, *end = response_buf + sizeof(response_buf); char draft[32]; char client_protos[256]; char negotiated[64] = ""; @@ -1676,11 +1672,22 @@ rops_check_upgrades_h3(struct lws *wsi) (const unsigned char *)draft, draft_len, &rp, end)) return LWS_UPG_RET_BAIL; } else { - /* Default to draft02 if not sent */ - if (lws_add_http_header_by_name(wsi, - (const unsigned char *)"sec-webtransport-http3-draft:", - (const unsigned char *)"draft02", 7, &rp, end)) - return LWS_UPG_RET_BAIL; + /* Check if client sent sec-webtransport-http3-draft02 */ + char draft02_val[16]; + int d02_len = lws_hdr_custom_copy(wsi, draft02_val, sizeof(draft02_val) - 1, + "sec-webtransport-http3-draft02", 30); + if (d02_len > 0) { + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"sec-webtransport-http3-draft02:", + (const unsigned char *)"1", 1, &rp, end)) + return LWS_UPG_RET_BAIL; + } else { + /* Default to draft02 if not sent */ + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"sec-webtransport-http3-draft:", + (const unsigned char *)"draft02", 7, &rp, end)) + return LWS_UPG_RET_BAIL; + } } /* Subprotocol negotiation */ @@ -1719,6 +1726,12 @@ rops_check_upgrades_h3(struct lws *wsi) } } } while (e_srv > 0); + } else { + /* No env protocols filter - select if loaded on this vhost */ + if (lws_vhost_name_to_protocol(wsi->a.vhost, name)) { + lws_strncpy(negotiated, name, sizeof(negotiated)); + break; + } } } } @@ -1754,6 +1767,9 @@ rops_check_upgrades_h3(struct lws *wsi) if (negotiated[0]) { prot = lws_vhost_name_to_protocol(wsi->a.vhost, negotiated); } + if (!prot) { + prot = lws_vhost_name_to_protocol(wsi->a.vhost, "webtransport-shared-world"); + } if (!prot) { int n = wsi->a.vhost->default_protocol_index; if (n < wsi->a.vhost->count_protocols) { @@ -1762,15 +1778,18 @@ rops_check_upgrades_h3(struct lws *wsi) } if (prot) { - wsi->a.protocol = prot; + if (lws_bind_protocol(wsi, prot, __func__)) + return LWS_UPG_RET_BAIL; lwsl_notice("H3 WT Upgrade: bound to protocol '%s'\n", prot->name); - char wt_prot_val[128]; - int wpl = lws_snprintf(wt_prot_val, sizeof(wt_prot_val), "\"%s\"", prot->name); - if (lws_add_http_header_by_name(wsi, - (const unsigned char *)"wt-protocol:", - (const unsigned char *)wt_prot_val, wpl, &rp, end)) - return LWS_UPG_RET_BAIL; + if (negotiated[0]) { + char wt_prot_val[128]; + int wpl = lws_snprintf(wt_prot_val, sizeof(wt_prot_val), "\"%s\"", prot->name); + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)"wt-protocol:", + (const unsigned char *)wt_prot_val, wpl, &rp, end)) + return LWS_UPG_RET_BAIL; + } } else { lwsl_notice("H3 WT Upgrade: no WebTransport protocol found on vhost\n"); } diff --git a/lib/roles/quic/parse-quic.c b/lib/roles/quic/parse-quic.c index f2c32c309d..2e5c4d9570 100644 --- a/lib/roles/quic/parse-quic.c +++ b/lib/roles/quic/parse-quic.c @@ -1193,6 +1193,9 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; wsi_child->txc.peer_tx_cr_est = cval; wsi_child->txc.tx_cr = cval; + } else { + wsi_child->txc.peer_tx_cr_est = 65535; + wsi_child->txc.tx_cr = 65535; } } else { if (qn->peer_initial_max_stream_data_bidi_remote) { @@ -1200,6 +1203,9 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; wsi_child->txc.peer_tx_cr_est = cval; wsi_child->txc.tx_cr = cval; + } else { + wsi_child->txc.peer_tx_cr_est = 65535; + wsi_child->txc.tx_cr = 65535; } } } else { @@ -1208,6 +1214,9 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl int32_t cval = (val > (uint64_t)INT32_MAX) ? INT32_MAX : (int32_t)val; wsi_child->txc.peer_tx_cr_est = cval; wsi_child->txc.tx_cr = cval; + } else { + wsi_child->txc.peer_tx_cr_est = 65535; + wsi_child->txc.tx_cr = 65535; } } } diff --git a/minimal-examples-lowlevel/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c b/minimal-examples-lowlevel/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c index d18b7f6c33..6760a0e9a0 100644 --- a/minimal-examples-lowlevel/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c +++ b/minimal-examples-lowlevel/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c @@ -180,6 +180,8 @@ callback_sse(struct lws *wsi, enum lws_callback_reasons reason, void *user, case LWS_CALLBACK_PROTOCOL_INIT: vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct vhd)); + if (!vhd) + return 1; vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); diff --git a/minimal-examples-lowlevel/raw/minimal-raw-file/minimal-raw-file.c b/minimal-examples-lowlevel/raw/minimal-raw-file/minimal-raw-file.c index e6c3548ddf..88ebde7cf8 100644 --- a/minimal-examples-lowlevel/raw/minimal-raw-file/minimal-raw-file.c +++ b/minimal-examples-lowlevel/raw/minimal-raw-file/minimal-raw-file.c @@ -49,6 +49,8 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_PROTOCOL_INIT: vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct raw_vhd)); + if (!vhd) + return 1; vhd->filefd = lws_open(filepath, O_RDWR); if (vhd->filefd == -1) { lwsl_err("Unable to open %s\n", filepath); diff --git a/minimal-examples-lowlevel/raw/minimal-raw-serial/minimal-raw-file.c b/minimal-examples-lowlevel/raw/minimal-raw-serial/minimal-raw-file.c index 06387f8349..da3ef757e1 100644 --- a/minimal-examples-lowlevel/raw/minimal-raw-serial/minimal-raw-file.c +++ b/minimal-examples-lowlevel/raw/minimal-raw-serial/minimal-raw-file.c @@ -72,6 +72,8 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_PROTOCOL_INIT: vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct raw_vhd)); + if (!vhd) + return 1; vhd->filefd = lws_open(filepath, O_RDWR); if (vhd->filefd == -1) { lwsl_err("Unable to open %s\n", filepath); diff --git a/minimal-examples-lowlevel/ws-client/minimal-ws-client-tx/minimal-ws-client.c b/minimal-examples-lowlevel/ws-client/minimal-ws-client-tx/minimal-ws-client.c index 40561af164..6345cd4443 100644 --- a/minimal-examples-lowlevel/ws-client/minimal-ws-client-tx/minimal-ws-client.c +++ b/minimal-examples-lowlevel/ws-client/minimal-ws-client-tx/minimal-ws-client.c @@ -185,6 +185,8 @@ callback_minimal_broker(struct lws *wsi, enum lws_callback_reasons reason, vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__minimal)); + if (!vhd) + return 1; vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); diff --git a/minimal-examples-lowlevel/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c b/minimal-examples-lowlevel/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c index 00287d7235..bcf3c5c042 100644 --- a/minimal-examples-lowlevel/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c +++ b/minimal-examples-lowlevel/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c @@ -76,6 +76,8 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__minimal)); + if (!vhd) + return 1; vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); diff --git a/plugins/protocol_lws_ssh_base/telnet.c b/plugins/protocol_lws_ssh_base/telnet.c index 33bf209152..681ae2b7eb 100644 --- a/plugins/protocol_lws_ssh_base/telnet.c +++ b/plugins/protocol_lws_ssh_base/telnet.c @@ -135,6 +135,8 @@ lws_callback_raw_telnet(struct lws *wsi, enum lws_callback_reasons reason, vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__telnet)); + if (!vhd) + return -1; vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); diff --git a/plugins/protocol_webtransport_shared_world/assets/app2.js b/plugins/protocol_webtransport_shared_world/assets/app2.js index 9eb5c8b1d5..b15d9b4367 100644 --- a/plugins/protocol_webtransport_shared_world/assets/app2.js +++ b/plugins/protocol_webtransport_shared_world/assets/app2.js @@ -7,7 +7,27 @@ let shadowGenerator = null; let sunLight = null; let skyDome = null; let myAvatar = null; +let camera = null; let currentSeed = 12345; +let myPlayerId = null; + +const otherAvatars = {}; +const keys = {}; + +let localX = 0; +let localZ = 0; +let localAngle = 0; +let localIsMoving = false; + +const stats = { + sentCount: 0, + recvCount: 0, + lastSent: "", + lastRecv: "" +}; + +window.addEventListener("keydown", (e) => { keys[e.code] = true; }); +window.addEventListener("keyup", (e) => { keys[e.code] = false; }); async function initEngine() { try { @@ -30,24 +50,11 @@ function initScene() { const skyColor = new BABYLON.Color3(0.7, 0.8, 0.9); scene.clearColor = new BABYLON.Color4(skyColor.r, skyColor.g, skyColor.b, 1.0); - const camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 100, -40), scene); // Start high to avoid spawning underground - camera.setTarget(BABYLON.Vector3.Zero()); - camera.attachControl(canvas, true); - - // WASD Controls - camera.keysUp.push(87); // W - camera.keysDown.push(83); // S - camera.keysLeft.push(65); // A - camera.keysRight.push(68); // D - camera.speed = 2.0; + camera = new BABYLON.TargetCamera("camera1", new BABYLON.Vector3(0, 100, -40), scene); // Start high to avoid spawning underground camera.maxZ = 4000; - // Enable Collisions and Gravity scene.collisionsEnabled = true; scene.gravity = new BABYLON.Vector3(0, -9.81, 0); - camera.checkCollisions = true; - camera.applyGravity = true; - camera.ellipsoid = new BABYLON.Vector3(2, 5, 2); // Click to capture mouse (Pointer Lock) canvas.addEventListener("click", () => { @@ -81,6 +88,26 @@ function initScene() { skyDome.material = skyMat; } +function updateCamera() { + if (!myAvatar) return; + const avatarPos = myAvatar.pathWrapper.position; + const angle = myAvatar.pathWrapper.rotation.y; + + const distance = 80; + const height = 30; + + const camX = avatarPos.x - Math.sin(angle) * distance; + const camZ = avatarPos.z - Math.cos(angle) * distance; + const camY = getTerrainHeight(camX, camZ, currentSeed) + height; + + // Smooth follow + camera.position.x = BABYLON.Scalar.Lerp(camera.position.x, camX, 0.1); + camera.position.y = BABYLON.Scalar.Lerp(camera.position.y, camY, 0.1); + camera.position.z = BABYLON.Scalar.Lerp(camera.position.z, camZ, 0.1); + + camera.setTarget(avatarPos); +} + function hash2D(x, y, seed) { let n = (Math.imul(x, 137) + Math.imul(y, 149) + seed) | 0; n = (n ^ (n >>> 13)) | 0; @@ -117,14 +144,12 @@ function fbm(x, y, octaves, seed) { for (let i = 0; i < octaves; i++) { total += smoothNoise2D(x * frequency, y * frequency, seed + i) * amplitude; maxVal += amplitude; - amplitude *= 0.5; frequency *= 2.0; } return total / maxVal; } -// Function to calculate exact terrain height at any point function getTerrainHeight(px, pz, seed) { let n = fbm(px * 0.005, pz * 0.005, 4, seed); const distFromCenter = Math.sqrt(px * px + pz * pz); @@ -213,15 +238,11 @@ function buildWorld(seed) { class Avatar { constructor(meshes, animationGroups, skeletons, scene) { - // Create a parent wrapper to handle translation and lookAt along the circle this.pathWrapper = new BABYLON.TransformNode("pathWrapper", scene); - - // Create an inner wrapper (kept for clean hierarchy, but with 0 rotation since the model faces +Z natively) this.offsetWrapper = new BABYLON.TransformNode("offsetWrapper", scene); this.offsetWrapper.parent = this.pathWrapper; this.offsetWrapper.rotation = new BABYLON.Vector3(0, 0, 0); - // Parent ALL imported root meshes to the offsetWrapper (this ensures the skeleton/bones are rotated too!) meshes.forEach(m => { if (!m.parent) { m.parent = this.offsetWrapper; @@ -232,53 +253,70 @@ class Avatar { m.receiveShadows = true; }); - // The overall scale can be applied to the pathWrapper this.pathWrapper.scaling = new BABYLON.Vector3(10, 10, 10); - // Try playing via AnimationGroups (newer glTF/GLB) - if (animationGroups && animationGroups.length > 0) { - const walkAnim = animationGroups.find(ag => ag.name.toLowerCase().includes("walk")) || animationGroups[0]; - walkAnim.play(true); - } - // Fallback to older .babylon skeleton animations (like dummy3.babylon) - else if (skeletons && skeletons.length > 0) { - const skeleton = skeletons[0]; - const ranges = skeleton.getAnimationRanges(); - let walkRange = null; - + this.walkRange = null; + this.skeleton = null; + if (skeletons && skeletons.length > 0) { + this.skeleton = skeletons[0]; + const ranges = this.skeleton.getAnimationRanges(); if (ranges && ranges.length > 0) { - walkRange = ranges.find(r => r.name.toLowerCase().includes("walk")) || ranges[0]; + this.walkRange = ranges.find(r => r.name.toLowerCase().includes("walk")) || ranges[0]; } - - if (walkRange) { - scene.beginAnimation(skeleton, walkRange.from, walkRange.to, true, 1.0); - } else { - scene.beginAnimation(skeleton, 0, 100, true, 1.0); + } + + this.animControl = null; + this.isMoving = false; + this.speed = 100.0; + this.rotationSpeed = 3.0; + } + + setMoving(moving) { + if (this.isMoving === moving) return; + this.isMoving = moving; + if (moving) { + if (this.walkRange && this.skeleton) { + this.animControl = scene.beginAnimation(this.skeleton, this.walkRange.from, this.walkRange.to, true, 1.0); + } + } else { + if (this.animControl) { + this.animControl.pause(); } } + } - this.angle = 0; - this.radius = 20; // Radius of the circle to walk in - this.speed = 0.5; - this.center = new BABYLON.Vector3(0, 0, 0); + dispose() { + if (this.pathWrapper) { + this.pathWrapper.dispose(); + } } +} - update(deltaTime) { - if (!this.pathWrapper) return; - - this.angle -= (this.speed * deltaTime) / 1000.0; - - const px = this.center.x + Math.cos(this.angle) * this.radius; - const pz = this.center.z + Math.sin(this.angle) * this.radius; - const py = getTerrainHeight(px, pz, currentSeed); - - this.pathWrapper.position = new BABYLON.Vector3(px, py, pz); +function colorAvatar(avatar, id) { + try { + if (!avatar || !avatar.pathWrapper) return; + console.log("Coloring avatar for player ID:", id); + const hue = (id * 137.5) % 360; + const color = BABYLON.Color3.FromHSV(hue, 0.8, 0.8); - // Point the pathWrapper in the direction of travel (tangent of the circle) - const tx = this.center.x + Math.cos(this.angle - 0.1) * this.radius; - const tz = this.center.z + Math.sin(this.angle - 0.1) * this.radius; - const ty = getTerrainHeight(tx, tz, currentSeed); - this.pathWrapper.lookAt(new BABYLON.Vector3(tx, ty, tz)); + const meshes = avatar.pathWrapper.getChildMeshes(); + meshes.forEach(m => { + if (m.material) { + try { + m.material = m.material.clone("mat-" + id); + if (m.material.diffuseColor !== undefined) { + m.material.diffuseColor = color; + } + if (m.material.albedoColor !== undefined) { + m.material.albedoColor = color; + } + } catch (e) { + console.warn("Could not clone or color material for mesh", m.name, e); + } + } + }); + } catch (e) { + console.error("Error in colorAvatar:", e); } } @@ -293,27 +331,97 @@ async function loadAvatar() { return; } myAvatar = new Avatar(result.meshes, result.animationGroups, result.skeletons, scene); - console.log("Avatar loaded successfully!"); + console.log("Local Avatar loaded successfully!"); + if (myPlayerId !== null) { + colorAvatar(myAvatar, myPlayerId); + } } catch (e) { alert("Failed to load dummy3.babylon. Did you run 'cmake .. && make install'? Error: " + e.message); console.error("Failed to load avatar model:", e); } } +async function spawnRemoteAvatar(id, x, z, angle, isMoving) { + if (otherAvatars[id]) return; + + console.log("Spawning remote avatar placeholder for player ID:", id, "at", x, z); + const placeholder = BABYLON.MeshBuilder.CreateSphere("placeholder-" + id, {diameter: 5}, scene); + placeholder.position = new BABYLON.Vector3(x, getTerrainHeight(x, z, currentSeed), z); + const mat = new BABYLON.StandardMaterial("placeholder-mat-" + id, scene); + mat.diffuseColor = new BABYLON.Color3(0.8, 0.2, 0.2); + placeholder.material = mat; + + otherAvatars[id] = { placeholder, avatar: null }; + + let path = window.location.pathname; + if (!path.endsWith('/')) path += '/'; + + try { + const result = await BABYLON.SceneLoader.ImportMeshAsync("", path, "dummy3.babylon", scene); + if (otherAvatars[id]) { + const currentPos = placeholder.position.clone(); + placeholder.dispose(); + + const avatar = new Avatar(result.meshes, result.animationGroups, result.skeletons, scene); + avatar.pathWrapper.position = currentPos; + avatar.pathWrapper.rotation.y = angle; + avatar.setMoving(isMoving); + + otherAvatars[id].avatar = avatar; + otherAvatars[id].placeholder = null; + colorAvatar(avatar, id); + console.log("Remote avatar mesh spawned successfully for player ID:", id); + } else { + result.meshes.forEach(m => m.dispose()); + } + } catch (e) { + console.error("Failed to load remote avatar:", e); + } +} + +function updateRemoteAvatar(id, x, z, angle, isMoving) { + const entry = otherAvatars[id]; + if (entry) { + if (entry.avatar) { + entry.avatar.pathWrapper.position = new BABYLON.Vector3(x, getTerrainHeight(x, z, currentSeed), z); + entry.avatar.pathWrapper.rotation.y = angle; + entry.avatar.setMoving(isMoving); + } else if (entry.placeholder) { + entry.placeholder.position = new BABYLON.Vector3(x, getTerrainHeight(x, z, currentSeed), z); + } + } else { + spawnRemoteAvatar(id, x, z, angle, isMoving); + } +} + +function removeRemoteAvatar(id) { + const entry = otherAvatars[id]; + if (entry) { + if (entry.placeholder) entry.placeholder.dispose(); + if (entry.avatar) entry.avatar.dispose(); + delete otherAvatars[id]; + console.log("Remote avatar removed for player ID:", id); + } +} + function tryReload() { + if (window.isReloading) return; + window.isReloading = true; console.log("Server disconnected. Waiting for it to come back..."); + const checkServer = async () => { try { - const u = new URL(window.location.href); - if (u.origin !== window.location.origin) { - return; - } - const resp = await fetch(u.href, { method: "HEAD" }); - if (resp.ok) { - window.location.reload(); - } else { - setTimeout(checkServer, 2000); + const pathname = window.location.pathname; + if (/^[a-zA-Z0-9\/\-_\.]+$/.test(pathname)) { + const testUrl = pathname + "?cb=" + Date.now(); + const resp = await fetch(testUrl, { method: "HEAD", cache: "no-store" }); + if (resp.ok) { + console.log("Server is back! Reloading page..."); + window.location.reload(); + return; + } } + setTimeout(checkServer, 2000); } catch (e) { setTimeout(checkServer, 2000); } @@ -321,20 +429,179 @@ function tryReload() { setTimeout(checkServer, 2000); } +function updateOverlay() { + const overlay = document.getElementById("connection-overlay"); + if (!overlay) return; + + let typeText = "Connecting..."; + let typeClass = "connection-overlay"; + if (wtWriter) { + typeText = "WebTransport (HTTP/3)"; + typeClass = "connection-overlay wt"; + } else if (wsConn && wsConn.readyState === WebSocket.OPEN) { + typeText = "WebSocket (WSS)"; + typeClass = "connection-overlay ws"; + } + + overlay.className = typeClass; + overlay.innerHTML = ` +
${typeText}
+
+
Sent: ${stats.sentCount}
+
Recv: ${stats.recvCount}
+
+
+
Last Sent: ${stats.lastSent || 'None'}
+
Last Recv: ${stats.lastRecv || 'None'}
+
+ `; +} + +function handleServerMessage(msg) { + console.log("handleServerMessage received raw:", msg); + const parts = msg.split(/}\s*{/); + for (let i = 0; i < parts.length; i++) { + let part = parts[i].trim(); + if (!part) continue; + if (i > 0) part = "{" + part; + if (i < parts.length - 1) part = part + "}"; + + try { + const obj = JSON.parse(part); + console.log("Parsed JSON message:", obj); + stats.recvCount++; + stats.lastRecv = part; + updateOverlay(); + + if (obj.seed) { + myPlayerId = obj.player_id; + console.log("Welcome message. Player ID:", myPlayerId, "Seed:", obj.seed, "Players in list:", obj.players); + if (myAvatar) { + colorAvatar(myAvatar, myPlayerId); + } + if (obj.seed !== currentSeed) { + currentSeed = obj.seed; + console.log("Received new seed:", currentSeed); + buildWorld(currentSeed); + } + if (obj.players) { + obj.players.forEach(p => { + console.log("Spawning existing player ID:", p.id, "at pos", p.x, p.z); + spawnRemoteAvatar(p.id, p.x, p.z, p.angle, p.isMoving); + }); + } + } else if (obj.join !== undefined) { + console.log("Player ID joined:", obj.join); + if (obj.join === myPlayerId) { + if (myAvatar) { + myAvatar.pathWrapper.position = new BABYLON.Vector3(0, getTerrainHeight(0, 0, currentSeed), 0); + } + } else { + spawnRemoteAvatar(obj.join, 0, 0, 0, false); + } + } else if (obj.leave !== undefined) { + console.log("Player ID left:", obj.leave); + removeRemoteAvatar(obj.leave); + } else if (obj.player_id !== undefined) { + console.log("Player ID update:", obj.player_id, "Pos:", obj.x, obj.z, "Angle:", obj.angle, "isMoving:", obj.isMoving); + if (obj.player_id === myPlayerId) { + if (myAvatar) { + myAvatar.pathWrapper.position = new BABYLON.Vector3(obj.x, getTerrainHeight(obj.x, obj.z, currentSeed), obj.z); + myAvatar.pathWrapper.rotation.y = obj.angle; + myAvatar.setMoving(obj.isMoving); + } + } else { + updateRemoteAvatar(obj.player_id, obj.x, obj.z, obj.angle, obj.isMoving); + } + } + } catch (e) { + console.error("Error parsing JSON part:", part, e); + } + } +} + +let wtWriter = null; +let wsConn = null; + +function sendUpdate(obj) { + const msg = JSON.stringify(obj); + stats.sentCount++; + stats.lastSent = msg; + updateOverlay(); + + if (wtWriter) { + const encoder = new TextEncoder(); + wtWriter.write(encoder.encode(msg)).catch(e => console.error("WT write error:", e)); + } else if (wsConn && wsConn.readyState === WebSocket.OPEN) { + wsConn.send(msg); + } +} + +let lastSentX = -999; +let lastSentZ = -999; +let lastSentAngle = -999; +let lastSentMoving = false; + +setInterval(() => { + // 1. Calculate intended position change from keys + const deltaTime = 100; // tick interval + const dtSec = deltaTime / 1000.0; + let moving = false; + const speed = 100.0; + const rotationSpeed = 3.0; + + if (keys["KeyW"]) { + localX += Math.sin(localAngle) * speed * dtSec; + localZ += Math.cos(localAngle) * speed * dtSec; + moving = true; + } + if (keys["KeyS"]) { + localX -= Math.sin(localAngle) * speed * dtSec; + localZ -= Math.cos(localAngle) * speed * dtSec; + moving = true; + } + if (keys["KeyA"]) { + localAngle -= rotationSpeed * dtSec; + moving = true; + } + if (keys["KeyD"]) { + localAngle += rotationSpeed * dtSec; + moving = true; + } + localIsMoving = moving; + + // 2. Check if anything changed from last sent + if (myPlayerId !== null && (Math.abs(localX - lastSentX) > 0.05 || + Math.abs(localZ - lastSentZ) > 0.05 || + Math.abs(localAngle - lastSentAngle) > 0.01 || + localIsMoving !== lastSentMoving)) { + + sendUpdate({ + x: Number.parseFloat(localX.toFixed(2)), + z: Number.parseFloat(localZ.toFixed(2)), + angle: Number.parseFloat(localAngle.toFixed(2)), + isMoving: localIsMoving + }); + + lastSentX = localX; + lastSentZ = localZ; + lastSentAngle = localAngle; + lastSentMoving = localIsMoving; + } +}, 100); + async function connectAndInit() { await initEngine(); initScene(); currentSeed = 12345; buildWorld(currentSeed); - - // Kick off avatar loading loadAvatar(); engine.runRenderLoop(function () { scene.render(); if (myAvatar) { - myAvatar.update(engine.getDeltaTime()); + updateCamera(); } }); @@ -349,15 +616,18 @@ async function connectAndInit() { const wtUrl = `https://${host}${path}`; let connected = false; + updateOverlay(); if (typeof WebTransport !== 'undefined') { try { console.log("Attempting WebTransport connection to", wtUrl); - const transport = new WebTransport(wtUrl); + const transport = new WebTransport(wtUrl, { + protocols: ['webtransport-shared-world'] + }); await Promise.race([ transport.ready, - new Promise((_, reject) => setTimeout(() => reject(new Error("WT timeout")), 3000)) + new Promise((_, reject) => setTimeout(() => reject(new Error("WT timeout")), 10000)) ]); console.log("WebTransport connected!"); @@ -365,30 +635,40 @@ async function connectAndInit() { const stream = await transport.createBidirectionalStream(); if (stream) { const streamReader = stream.readable.getReader(); - const { value: data, done } = await streamReader.read(); - if (done) { - console.log("WT stream closed, waiting to reload..."); - tryReload(); - } else if (data) { - const msg = new TextDecoder().decode(data); - const obj = JSON.parse(msg); - if (obj.seed && obj.seed !== currentSeed) { - currentSeed = obj.seed; - console.log("Received WT seed:", currentSeed); - buildWorld(currentSeed); + wtWriter = stream.writable.getWriter(); + connected = true; + updateOverlay(); + + (async () => { + try { + while (true) { + const { value: data, done } = await streamReader.read(); + if (done) { + console.log("WT stream done, reloading..."); + tryReload(); + break; + } + if (data) { + const msg = new TextDecoder().decode(data); + handleServerMessage(msg); + } + } + } catch (e) { + console.error("WT read error:", e); + tryReload(); } - } + })(); } - connected = true; - // Handle WT close - transport.closed.then(() => { - console.log("WT connection closed, waiting to reload..."); - tryReload(); - }).catch(() => { - console.log("WT connection error, waiting to reload..."); + (async () => { + try { + await transport.closed; + console.log("WT session closed cleanly, reloading..."); + } catch (e) { + console.warn("WT session closed with error, reloading...", e); + } tryReload(); - }); + })(); } catch (e) { console.warn("WebTransport connection failed or timed out:", e); @@ -399,27 +679,29 @@ async function connectAndInit() { try { console.log("Attempting WebSocket connection to", wsUrl); await new Promise((resolve, reject) => { - const ws = new WebSocket(wsUrl, "webtransport-shared-world"); - ws.onmessage = (event) => { + wsConn = new WebSocket(wsUrl, "webtransport-shared-world"); + wsConn.onmessage = (event) => { try { - const obj = JSON.parse(event.data); - if (obj.seed && obj.seed !== currentSeed) { - currentSeed = obj.seed; - console.log("Received WS seed:", currentSeed); - buildWorld(currentSeed); - connected = true; - } + handleServerMessage(event.data); } catch (e) { console.error("Invalid WS message", e); } - resolve(); }; - ws.onerror = reject; - ws.onclose = () => { - console.log("WS connection closed, waiting to reload..."); + wsConn.onerror = (err) => { + console.error("WebSocket error:", err); tryReload(); + reject(new Error("WebSocket connection failed")); + }; + wsConn.onclose = () => { + console.log("WS connection closed, reloading..."); + tryReload(); + }; + wsConn.onopen = () => { + console.log("WebSocket connected!"); + connected = true; + updateOverlay(); + resolve(); }; - ws.onopen = () => console.log("WebSocket connected!"); setTimeout(() => { if (!connected) reject(new Error("WS timeout")); }, 3000); }); diff --git a/plugins/protocol_webtransport_shared_world/assets/index.html b/plugins/protocol_webtransport_shared_world/assets/index.html index daa136e978..8d0d37c348 100644 --- a/plugins/protocol_webtransport_shared_world/assets/index.html +++ b/plugins/protocol_webtransport_shared_world/assets/index.html @@ -7,6 +7,7 @@ +
Connecting...
diff --git a/plugins/protocol_webtransport_shared_world/assets/style.css b/plugins/protocol_webtransport_shared_world/assets/style.css index 9405c05cc0..42eb4bb32a 100644 --- a/plugins/protocol_webtransport_shared_world/assets/style.css +++ b/plugins/protocol_webtransport_shared_world/assets/style.css @@ -13,3 +13,76 @@ html, body { touch-action: none; outline: none; } + +.connection-overlay { + position: absolute; + top: 20px; + right: 20px; + width: 320px; + padding: 15px; + background: rgba(15, 15, 25, 0.75); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + color: #f0f0f5; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 13px; + border-radius: 12px; + pointer-events: none; + z-index: 10; + box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); + border: 1px solid rgba(255, 255, 255, 0.1); + transition: all 0.3s ease; +} + +.connection-overlay.wt { + border-color: rgba(46, 204, 113, 0.4); + box-shadow: 0 8px 32px 0 rgba(46, 204, 113, 0.1); +} + +.connection-overlay.ws { + border-color: rgba(241, 196, 15, 0.4); + box-shadow: 0 8px 32px 0 rgba(241, 196, 15, 0.1); +} + +.overlay-title { + font-size: 15px; + font-weight: 700; + margin-bottom: 10px; + letter-spacing: 0.5px; +} + +.connection-overlay.wt .overlay-title { + color: #2ecc71; +} + +.connection-overlay.ws .overlay-title { + color: #f1c40f; +} + +.overlay-stats { + display: flex; + justify-content: space-between; + margin-bottom: 12px; + padding-bottom: 8px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.overlay-json { + display: flex; + flex-direction: column; + gap: 8px; +} + +.json-text { + display: block; + font-family: monospace; + font-size: 11px; + background: rgba(0, 0, 0, 0.4); + padding: 6px; + border-radius: 6px; + word-break: break-all; + max-height: 80px; + overflow-y: auto; + border: 1px solid rgba(255, 255, 255, 0.05); + color: #a2a2d0; +} diff --git a/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c b/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c index 2ea0d6f52c..7f5f33ab14 100644 --- a/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c +++ b/plugins/protocol_webtransport_shared_world/protocol_webtransport_shared_world.c @@ -18,15 +18,35 @@ #endif #include #include +#include + +struct msg { + char payload[LWS_PRE + 256]; + size_t len; + uint32_t sender_id; +}; struct vhd__shared_world { + lws_dll2_owner_t sessions; + struct lws_ring *ring; uint32_t seed; + uint32_t next_player_id; }; struct pss__shared_world { + lws_dll2_t list; + struct lws *wsi; + uint32_t player_id; + uint32_t tail; + double x; + double z; + double angle; + double speed; int seed_sent; + int is_moving; }; + static int callback_shared_world(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) @@ -34,8 +54,7 @@ callback_shared_world(struct lws *wsi, enum lws_callback_reasons reason, struct pss__shared_world *pss = (struct pss__shared_world *)user; struct vhd__shared_world *vhd = (struct vhd__shared_world *) lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); - uint8_t buf[LWS_PRE + 128]; - int m; + uint8_t buf[LWS_PRE + 4096]; switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: @@ -47,34 +66,233 @@ callback_shared_world(struct lws *wsi, enum lws_callback_reasons reason, sizeof(struct vhd__shared_world)); if (!vhd) return -1; + + vhd->ring = lws_ring_create(sizeof(struct msg), 32, NULL); + if (!vhd->ring) + return -1; + + lws_dll2_owner_clear(&vhd->sessions); + vhd->next_player_id = 0; { struct lws_xos xos; - lws_xos_init(&xos, 0x12345678); /* Simple predictable seed for PRNG to gen actual world seed */ + lws_xos_init(&xos, 0x12345678); vhd->seed = (uint32_t)lws_xos(&xos); } break; + case LWS_CALLBACK_PROTOCOL_DESTROY: + if (vhd) { + if (vhd->ring) { + lws_ring_destroy(vhd->ring); + vhd->ring = NULL; + } + } + break; + + case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: +#if defined(LWS_ROLE_WT) + if (!lws_wt_is_session(wsi) && lws_wt_get_session_wsi(wsi) != NULL) { + goto init_session; + } +#endif + break; + case LWS_CALLBACK_ESTABLISHED: - /* Could be WS or WT stream */ #if defined(LWS_ROLE_WT) if (lws_wt_is_session(wsi)) { lwsl_user("WT Session established\n"); - break; /* We don't send data on the session itself */ + break; } #endif - lwsl_user("Connection/Stream established\n"); +#if defined(LWS_ROLE_WT) + init_session: +#endif + if (pss->wsi) { + lwsl_user("Session already initialized\n"); + break; + } + { + const char *tt = "WebSocket"; +#if defined(LWS_ROLE_WT) + if (lws_wt_is_session(wsi) || lws_wt_get_session_wsi(wsi) != NULL) { + tt = "WebTransport"; + } +#endif + lwsl_user("Connection/Stream established (protocol: %s, transport: %s)\n", + lws_get_protocol(wsi)->name, tt); + } + pss->wsi = wsi; + pss->player_id = ++vhd->next_player_id; + pss->x = 0.0; + pss->z = 0.0; + pss->angle = 0.0; + pss->speed = 0.0; + pss->is_moving = 0; pss->seed_sent = 0; + + lws_dll2_add_tail(&pss->list, &vhd->sessions); + pss->tail = lws_ring_get_oldest_tail(vhd->ring); + + /* Broadcast join message */ + { + struct msg jmsg; + jmsg.sender_id = pss->player_id; + jmsg.len = (size_t)lws_snprintf(jmsg.payload + LWS_PRE, sizeof(jmsg.payload) - LWS_PRE, + "{\"join\":%u}", pss->player_id); + if (lws_ring_insert(vhd->ring, &jmsg, 1) != 1) { + lwsl_wsi_warn(wsi, "Failed to insert join message to ring"); + } + } + lws_callback_on_writable(wsi); + lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); + break; + + case LWS_CALLBACK_CLOSED: +#if defined(LWS_ROLE_WT) + if (lws_wt_is_session(wsi)) { + break; + } +#endif + if (!pss->wsi) { + break; + } + { + const char *tt = "WebSocket"; +#if defined(LWS_ROLE_WT) + if (lws_wt_is_session(wsi) || lws_wt_get_session_wsi(wsi) != NULL) { + tt = "WebTransport"; + } +#endif + lwsl_user("Connection/Stream closed (transport: %s)\n", tt); + } + lws_dll2_remove(&pss->list); + + /* Broadcast leave message */ + { + struct msg lmsg; + lmsg.sender_id = pss->player_id; + lmsg.len = (size_t)lws_snprintf(lmsg.payload + LWS_PRE, sizeof(lmsg.payload) - LWS_PRE, + "{\"leave\":%u}", pss->player_id); + if (lws_ring_insert(vhd->ring, &lmsg, 1) != 1) { + lwsl_wsi_warn(wsi, "Failed to insert leave message to ring"); + } + } + + lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); + break; + + case LWS_CALLBACK_RECEIVE: +#if defined(LWS_ROLE_WT) + if (lws_wt_is_session(wsi)) { + break; + } +#endif + if (!pss->wsi) { + break; + } + { + size_t alen; + const char *val; + + val = lws_json_simple_find(in, len, "\"x\":", &alen); + if (val) pss->x = atof(val); + + val = lws_json_simple_find(in, len, "\"z\":", &alen); + if (val) pss->z = atof(val); + + val = lws_json_simple_find(in, len, "\"angle\":", &alen); + if (val) pss->angle = atof(val); + + val = lws_json_simple_find(in, len, "\"speed\":", &alen); + if (val) pss->speed = atof(val); + + val = lws_json_simple_find(in, len, "\"isMoving\":", &alen); + if (val) pss->is_moving = (!strncmp(val, "true", 4)) ? 1 : 0; + + /* Broadcast update */ + { + struct msg umsg; + umsg.sender_id = pss->player_id; + umsg.len = (size_t)lws_snprintf(umsg.payload + LWS_PRE, sizeof(umsg.payload) - LWS_PRE, + "{\"player_id\":%u,\"x\":%.2f,\"z\":%.2f,\"angle\":%.2f,\"isMoving\":%s}", + pss->player_id, pss->x, pss->z, pss->angle, pss->is_moving ? "true" : "false"); + if (lws_ring_insert(vhd->ring, &umsg, 1) != 1) { + lwsl_wsi_warn(wsi, "Failed to insert update message to ring"); + } + } + + lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); + } break; case LWS_CALLBACK_SERVER_WRITEABLE: +#if defined(LWS_ROLE_WT) + if (lws_wt_is_session(wsi)) { + break; + } +#endif + if (!pss->wsi) { + break; + } if (!pss->seed_sent) { - m = lws_snprintf((char *)buf + LWS_PRE, sizeof(buf) - LWS_PRE, - "{\"seed\": %u}", vhd->seed); - if (lws_write(wsi, buf + LWS_PRE, (unsigned int)m, LWS_WRITE_TEXT) < m) + char *p = (char *)buf + LWS_PRE; + char *end = (char *)buf + sizeof(buf); + + p += lws_snprintf(p, (size_t)(end - p), "{\"seed\":%u,\"player_id\":%u,\"players\":[", + vhd->seed, pss->player_id); + + int first = 1; + lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&vhd->sessions)) { + struct pss__shared_world *other = lws_container_of(d, struct pss__shared_world, list); + if (other != pss) { + if (!first) + p += lws_snprintf(p, (size_t)(end - p), ","); + first = 0; + p += lws_snprintf(p, (size_t)(end - p), "{\"id\":%u,\"x\":%.2f,\"z\":%.2f,\"angle\":%.2f,\"isMoving\":%s}", + other->player_id, other->x, other->z, other->angle, other->is_moving ? "true" : "false"); + } + } lws_end_foreach_dll(d); + + p += lws_snprintf(p, (size_t)(end - p), "]}"); + + size_t slen = (size_t)(p - ((char *)buf + LWS_PRE)); + lwsl_user("Sending initial welcome JSON: %s\n", (char *)buf + LWS_PRE); + if (lws_write(wsi, buf + LWS_PRE, (unsigned int)slen, LWS_WRITE_TEXT) < (int)slen) return -1; + pss->seed_sent = 1; } + + { + const struct msg *pmsg = lws_ring_get_element(vhd->ring, &pss->tail); + if (pmsg) { + if (lws_write(wsi, (unsigned char *)pmsg->payload + LWS_PRE, pmsg->len, LWS_WRITE_TEXT) < (int)pmsg->len) + return -1; + + int oldest_consumed = (lws_ring_get_oldest_tail(vhd->ring) == pss->tail); + lws_ring_consume(vhd->ring, &pss->tail, NULL, 1); + + if (oldest_consumed) { + uint32_t oldest = pss->tail; + size_t max_waiting = 0; + + lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&vhd->sessions)) { + struct pss__shared_world *other = lws_container_of(d, struct pss__shared_world, list); + size_t waiting = lws_ring_get_count_waiting_elements(vhd->ring, &other->tail); + if (waiting >= max_waiting) { + max_waiting = waiting; + oldest = other->tail; + } + } lws_end_foreach_dll(d); + + lws_ring_update_oldest_tail(vhd->ring, oldest); + } + + if (lws_ring_get_element(vhd->ring, &pss->tail)) + lws_callback_on_writable(wsi); + } + } break; default: @@ -89,7 +307,7 @@ callback_shared_world(struct lws *wsi, enum lws_callback_reasons reason, "webtransport-shared-world", \ callback_shared_world, \ sizeof(struct pss__shared_world), \ - 1024, \ + 4096, \ 0, NULL, 0 \ } From bdedb13e20aed341e62f16235726a84ba58d7c75 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 17 Jul 2026 09:25:09 +0100 Subject: [PATCH 29/44] quic: zerortt large send option --- include/libwebsockets/lws-context-vhost.h | 7 +++++++ lib/core/context.c | 1 + lib/core/libwebsockets.c | 15 +++++++++++++++ lib/core/private-lib-core.h | 1 + lib/roles/quic/ops-quic-cc-cubic.c | 9 +++++++-- lib/roles/quic/ops-quic-cc-newreno.c | 9 +++++++-- lib/roles/quic/ops-quic.c | 8 ++++---- lib/tls/gnutls/gnutls-tls.c | 2 +- lib/tls/openssl/openssl-quic.c | 2 ++ lwsws/main.c | 2 ++ .../minimal-http-client-multi.c | 10 +++++----- 11 files changed, 52 insertions(+), 14 deletions(-) diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 39975e8630..a6a0efa3d4 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -221,6 +221,10 @@ #define LWS_SERVER_OPTION_GLIB (1ll << 33) /**< (CTX) Use glib event loop */ +#define LWS_SERVER_OPTION_QUIC_FORCE_RETRY (1ll << 52) + /**< (VH) For QUIC, force the server to always send a Retry packet. + * Normally used only for testing. */ + #define LWS_SERVER_OPTION_H2_PRIOR_KNOWLEDGE (1ll << 34) /**< (VH) Tell the vhost to treat plain text http connections as * H2 with prior knowledge (no upgrade request involved) @@ -1115,6 +1119,9 @@ struct lws_context_creation_info { uint32_t quic_initial_cwnd; /**< CONTEXT: 0 for default (10 * MTU), or the desired initial congestion window in bytes */ + uint32_t quic_0rtt_max_size; + /**< CONTEXT: 0 for default (4096), or the desired max 0-RTT early data size */ + #if !defined(__STRICT_ANSI__) void *_unused[1]; /**< dummy */ #endif diff --git a/lib/core/context.c b/lib/core/context.c index 0587fe2e44..18a5c3d39e 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -723,6 +723,7 @@ lws_create_context(const struct lws_context_creation_info *info) context->quic_tx_credit_cb = info->quic_tx_credit_cb; context->quic_cc_ops = info->quic_cc_ops; context->quic_initial_cwnd = info->quic_initial_cwnd; + context->quic_0rtt_max_size = info->quic_0rtt_max_size; context->pt_serv_buf_size = (unsigned int)s1; context->protocols_copy = info->protocols; diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index cb7f199301..92a8482228 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -1603,7 +1603,10 @@ static const struct lws_switches builtins[] = { { "--cpd-bypass", "Bypass captive portal detect" }, { "--quic-pad-crypto", "Pad QUIC Handshake Crypto for tests" }, { "--quic-only-latest", "Force QUIC to use the latest supported version (e.g. v2)" }, + { "--quic-force-retry", "Force QUIC to always send a Retry packet" }, { "--0rtt", "Allow QUIC 0-RTT early data" }, + { "--quic-initial-cwnd", "Initial QUIC congestion window (cwnd) in bytes" }, + { "--0rtt-max-size", "Max QUIC 0-RTT early data size in bytes" }, { "-h", "Print this help" }, { "--help", "Print this help" }, }; @@ -1623,7 +1626,10 @@ enum opts { OPT_CPD_BYPASS, OPT_QUIC_PAD_CRYPTO, OPT_QUIC_ONLY_LATEST, + OPT_QUIC_FORCE_RETRY, OPT_0RTT, + OPT_QUIC_INITIAL_CWND, + OPT_0RTT_MAX_SIZE, OPT_HELP1, OPT_HELP2, }; @@ -1904,9 +1910,18 @@ lws_cmdline_option_handle_builtin(int argc, const char **argv, case OPT_QUIC_ONLY_LATEST: info->options |= LWS_SERVER_OPTION_QUIC_LATEST_VERSION; break; + case OPT_QUIC_FORCE_RETRY: + info->options |= LWS_SERVER_OPTION_QUIC_FORCE_RETRY; + break; case OPT_0RTT: info->options |= LWS_SERVER_OPTION_ALLOW_EARLY_DATA; break; + case OPT_QUIC_INITIAL_CWND: + info->quic_initial_cwnd = (uint32_t)atoll(p); + break; + case OPT_0RTT_MAX_SIZE: + info->quic_0rtt_max_size = (uint32_t)atoll(p); + break; case OPT_HELP1: case OPT_HELP2: diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index 3a7611b155..56675fc4c9 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -867,6 +867,7 @@ struct lws_context { lws_quic_tx_credit_cb_t quic_tx_credit_cb; const struct lws_cc_ops *quic_cc_ops; uint32_t quic_initial_cwnd; + uint32_t quic_0rtt_max_size; uint8_t quic_retry_secret[16]; }; diff --git a/lib/roles/quic/ops-quic-cc-cubic.c b/lib/roles/quic/ops-quic-cc-cubic.c index ce039226a4..52547e9c90 100644 --- a/lib/roles/quic/ops-quic-cc-cubic.c +++ b/lib/roles/quic/ops-quic-cc-cubic.c @@ -92,7 +92,7 @@ cubic_init(struct lws *nwsi) st->k = 0; st->is_in_fast_convergence = 0; - lwsl_cx_info(nwsi->a.context, "QUIC CUBIC: init cwnd=%zu, mtu=%u", st->cwnd, mtu); + lwsl_notice("QUIC CUBIC: init cwnd=%zu, mtu=%u", st->cwnd, mtu); } static void @@ -240,7 +240,12 @@ cubic_can_send(struct lws *nwsi, size_t bytes) if (!st) return 0; - return (st->bytes_in_flight + bytes <= st->cwnd); + int ok = (st->bytes_in_flight + bytes <= st->cwnd); + if (!ok) { + lwsl_notice("AGY-DEBUG: cubic_can_send failed: bytes_in_flight=%zu, bytes=%zu, cwnd=%zu\n", + st->bytes_in_flight, bytes, st->cwnd); + } + return ok; } static lws_usec_t diff --git a/lib/roles/quic/ops-quic-cc-newreno.c b/lib/roles/quic/ops-quic-cc-newreno.c index 142b68e16f..3d59a28e3d 100644 --- a/lib/roles/quic/ops-quic-cc-newreno.c +++ b/lib/roles/quic/ops-quic-cc-newreno.c @@ -44,7 +44,7 @@ newreno_init(struct lws *nwsi) st->last_pacing_time = lws_now_usecs(); st->pacing_credit = st->cwnd; /* initial burst allowed */ - lwsl_cx_info(nwsi->a.context, "QUIC NewReno: init cwnd=%zu, mtu=%u", st->cwnd, mtu); + lwsl_notice("QUIC NewReno: init cwnd=%zu, mtu=%u", st->cwnd, mtu); } static void @@ -155,7 +155,12 @@ newreno_can_send(struct lws *nwsi, size_t bytes) if (!st) return 0; - return (st->bytes_in_flight + bytes <= st->cwnd); + int ok = (st->bytes_in_flight + bytes <= st->cwnd); + if (!ok) { + lwsl_notice("AGY-DEBUG: newreno_can_send failed: bytes_in_flight=%zu, bytes=%zu, cwnd=%zu\n", + st->bytes_in_flight, bytes, st->cwnd); + } + return ok; } static lws_usec_t diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index f79b43a13d..3a1daab168 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -1707,10 +1707,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) /* Check congestion window - bypass for PTO probes and ACKs */ if (!qn->pto_probe_needed && !(qn->needs_ack[pn_space] && is_ack_allowed) && qn->cc_ops && qn->cc_ops->can_send && !qn->cc_ops->can_send(wsi, mtu)) { is_congestion_limited = 1; -#if (_LWS_ENABLED_LOGS & LLL_INFO) - LWS_RATELIMIT_DEFINE_STATIC(rl); - lwsl_ratelimit_info(&rl, 1000000, "QUIC TX: Congestion window full, blocking POLLOUT\n"); -#endif + lwsl_notice("AGY-DEBUG: Congestion window full at level %d, blocking POLLOUT\n", level); blocked = 1; break; /* Stop processing sending loops */ } @@ -1719,6 +1716,7 @@ rops_handle_POLLOUT_quic(struct lws *wsi) if (!qn->pto_probe_needed && !(qn->needs_ack[pn_space] && is_ack_allowed) && qn->cc_ops && qn->cc_ops->get_pacing_delay) { lws_usec_t delay = qn->cc_ops->get_pacing_delay(wsi, mtu); if (delay > 0) { + lwsl_notice("AGY-DEBUG: Pacing delay %llu us at level %d, scheduling timer\n", (unsigned long long)delay, level); lws_sul_schedule(wsi->a.context, 0, &qn->pacer_sul, lws_quic_pacer_cb, delay); blocked = 1; break; /* Stop processing sending loops */ @@ -3269,6 +3267,8 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) (struct lws_quic_cc_newreno *)nwsi->quic.qn->cc_state; size_t debt = cc->bytes_in_flight + queued_bytes; size_t headroom = (cc->cwnd > debt) ? cc->cwnd - debt : 0; + lwsl_notice("AGY-DEBUG: rops_tx_credit_quic: cwnd=%zu, bytes_in_flight=%zu, queued_bytes=%zu, headroom=%zu, cr_before=%d\n", + cc->cwnd, cc->bytes_in_flight, queued_bytes, headroom, cr); if (headroom < mtu) headroom = mtu; /* always allow 1 packet */ if (cr > (int)headroom) diff --git a/lib/tls/gnutls/gnutls-tls.c b/lib/tls/gnutls/gnutls-tls.c index 4c5828745a..ccd57c0b58 100644 --- a/lib/tls/gnutls/gnutls-tls.c +++ b/lib/tls/gnutls/gnutls-tls.c @@ -365,7 +365,7 @@ lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) #if GNUTLS_VERSION_NUMBER >= 0x030605 if (flags & GNUTLS_ENABLE_EARLY_DATA) { - gnutls_record_set_max_early_data_size(session, 0xFFFFFFFF); + gnutls_record_set_max_early_data_size(session, wsi->a.context->quic_0rtt_max_size ? wsi->a.context->quic_0rtt_max_size : 0xFFFFFFFF); if (wsi->a.vhost->tls.anti_replay) gnutls_anti_replay_enable(session, (gnutls_anti_replay_t)wsi->a.vhost->tls.anti_replay); } diff --git a/lib/tls/openssl/openssl-quic.c b/lib/tls/openssl/openssl-quic.c index dff3496ccf..e08d54ab68 100644 --- a/lib/tls/openssl/openssl-quic.c +++ b/lib/tls/openssl/openssl-quic.c @@ -301,6 +301,7 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) if (wsi->a.vhost && (wsi->a.vhost->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA)) { #if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) SSL_set_early_data_enabled(wsi->tls.ssl, 1); + SSL_set_max_early_data(wsi->tls.ssl, wsi->a.context->quic_0rtt_max_size ? wsi->a.context->quic_0rtt_max_size : 0xFFFFFFFF); #endif } SSL_set_accept_state(wsi->tls.ssl); @@ -626,6 +627,7 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) if (wsi->a.vhost && (wsi->a.vhost->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA)) { #if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) SSL_set_early_data_enabled(wsi->tls.ssl, 1); + SSL_set_max_early_data(wsi->tls.ssl, wsi->a.context->quic_0rtt_max_size ? wsi->a.context->quic_0rtt_max_size : 0xFFFFFFFF); #endif } SSL_set_accept_state(wsi->tls.ssl); diff --git a/lwsws/main.c b/lwsws/main.c index a23dcf6afa..f929975000 100644 --- a/lwsws/main.c +++ b/lwsws/main.c @@ -187,6 +187,8 @@ context_creation(int argc, const char **argv) info.argc = argc; info.argv = argv; + lws_cmdline_option_handle_builtin(argc, argv, &info); + context = lws_create_context(&info); if (context == NULL) { lwsl_err("libwebsocket init failed\n"); diff --git a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c index 48c84d9c17..eacbaf1741 100644 --- a/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c +++ b/minimal-examples-lowlevel/http-client/minimal-http-client-multi/minimal-http-client-multi.c @@ -749,20 +749,20 @@ int main(int argc, const char **argv) #if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) /* - * When the QIR harness sets TESTCASE=zerortt it does not pass - * --save-ticket / --load-ticket / --0rtt on the command line. - * Auto-configure the ticket persistence path so that phase 1 + * When --0rtt or the QIR harness (TESTCASE=zerortt) is used, + * auto-configure the ticket persistence path so that phase 1 * saves the ticket and phase 2 can load it and attempt 0-RTT, * provided the caller hasn't already set an explicit path. */ - if (testcase && !strcmp(testcase, "zerortt")) { + if ((info.options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA) || + (testcase && !strcmp(testcase, "zerortt"))) { if (!save_ticket[0]) lws_strncpy(save_ticket, "./lws_session_ticket", sizeof(save_ticket)); if (!load_ticket[0]) lws_strncpy(load_ticket, "./lws_session_ticket", sizeof(load_ticket)); - lwsl_notice("%s: TESTCASE=zerortt: ticket path '%s'\n", + lwsl_notice("%s: early-data/zerortt: ticket path '%s'\n", __func__, save_ticket); if (!info.quic_initial_cwnd) info.quic_initial_cwnd = 250000; From 21e3d6b85dcb021d55e1cec9c388043f5fa677d3 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 17 Jul 2026 19:09:42 +0100 Subject: [PATCH 30/44] webrtc-align-pre-backout --- CMakeLists-implied-options.txt | 32 ++++++++++---- lib/tls/gnutls/lws-gendtls.c | 19 ++++---- .../protocol_lws_webrtc/protocol_lws_webrtc.c | 38 +++++++++------- .../protocol_lws_webrtc/protocol_lws_webrtc.h | 1 + .../protocol_lws_webrtc_mixer/assets/main.js | 6 +++ .../protocol_lws_webrtc_mixer/mixer-media.c | 44 +++++++++++++------ 6 files changed, 95 insertions(+), 45 deletions(-) diff --git a/CMakeLists-implied-options.txt b/CMakeLists-implied-options.txt index 6170f4675b..fca6ad5ecc 100644 --- a/CMakeLists-implied-options.txt +++ b/CMakeLists-implied-options.txt @@ -185,24 +185,40 @@ endif() if (LWS_WITH_WEBRTC_MIXER) set(LWS_WITH_WEBRTC 1) - set(LWS_WITH_ALSA 1 CACHE BOOL "Enable alsa audio example" FORCE) - set(LWS_WITH_OPUS 1 CACHE BOOL "Enable opus audio codec" FORCE) - set(LWS_WITH_GSTREAMER 1 CACHE BOOL "Enable gstreamer" FORCE) + if (NOT DEFINED LWS_WITH_ALSA) + set(LWS_WITH_ALSA 1 CACHE BOOL "Enable alsa audio example" FORCE) + endif() + if (NOT DEFINED LWS_WITH_OPUS) + set(LWS_WITH_OPUS 1 CACHE BOOL "Enable opus audio codec" FORCE) + endif() + if (NOT DEFINED LWS_WITH_GSTREAMER) + set(LWS_WITH_GSTREAMER 1 CACHE BOOL "Enable gstreamer" FORCE) + endif() endif() if (LWS_WITH_WEBRTC) set(LWS_WITH_UDP 1) set(LWS_WITH_DTLS 1) set(LWS_WITH_PLUGINS 1 CACHE BOOL "Enable plugins" FORCE) - set(LWS_WITH_V4L2 1) - set(LWS_WITH_LIBV4L2 1) - set(LWS_WITH_DRM 1) + if (NOT DEFINED LWS_WITH_V4L2) + set(LWS_WITH_V4L2 1) + endif() + if (NOT DEFINED LWS_WITH_LIBV4L2) + set(LWS_WITH_LIBV4L2 1) + endif() + if (NOT DEFINED LWS_WITH_DRM) + set(LWS_WITH_DRM 1) + endif() set(LWS_WITH_GENCRYPTO 1) set(LWS_WITH_JOSE 1) set(LWS_WITH_NETWORK 1) set(LWS_WITH_CLIENT 1) - set(LWS_WITH_ALSA 1) - set(LWS_WITH_OPUS 1) + if (NOT DEFINED LWS_WITH_ALSA) + set(LWS_WITH_ALSA 1) + endif() + if (NOT DEFINED LWS_WITH_OPUS) + set(LWS_WITH_OPUS 1) + endif() endif() diff --git a/lib/tls/gnutls/lws-gendtls.c b/lib/tls/gnutls/lws-gendtls.c index 882811b095..b621e519ba 100644 --- a/lib/tls/gnutls/lws-gendtls.c +++ b/lib/tls/gnutls/lws-gendtls.c @@ -134,10 +134,11 @@ lws_gendtls_create(struct lws_gendtls_ctx *ctx, goto bail; } -#if defined(GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80) /* SRTP is supported in GnuTLS >= 3.1.4 */ +#if 1 /* SRTP is supported in GnuTLS >= 3.1.4 */ if (info->use_srtp) { gnutls_srtp_profile_t profiles[4]; int n = 0; + int i; if ((char *)strstr(info->use_srtp, "SRTP_AES128_CM_SHA1_80")) profiles[n++] = GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80; @@ -148,10 +149,10 @@ lws_gendtls_create(struct lws_gendtls_ctx *ctx, if ((char *)strstr(info->use_srtp, "SRTP_NULL_HMAC_SHA1_32")) profiles[n++] = GNUTLS_SRTP_NULL_HMAC_SHA1_32; - if (n) { - ret = gnutls_srtp_set_profile_direct(ctx->session, profiles, n); + for (i = 0; i < n; i++) { + ret = gnutls_srtp_set_profile(ctx->session, profiles[i]); if (ret != GNUTLS_E_SUCCESS) { - lwsl_err("%s: gnutls_srtp_set_profile_direct failed: %s\n", + lwsl_err("%s: gnutls_srtp_set_profile failed: %s\n", __func__, gnutls_strerror(ret)); goto bail; } @@ -244,8 +245,9 @@ lws_gendtls_set_cert_mem(struct lws_gendtls_ctx *ctx, const uint8_t *cert, size_ if (ctx->key_mem) { gnutls_datum_t c = { ctx->cert_mem, (unsigned int)ctx->cert_len - 1 }; gnutls_datum_t k = { ctx->key_mem, (unsigned int)ctx->key_len - 1 }; + gnutls_x509_crt_fmt_t fmt = (ctx->cert_mem[0] == '-') ? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER; - if (gnutls_certificate_set_x509_key_mem(ctx->cred, &c, &k, GNUTLS_X509_FMT_PEM) < 0) { + if (gnutls_certificate_set_x509_key_mem(ctx->cred, &c, &k, fmt) < 0) { lwsl_err("%s: failed to set cert/key\n", __func__); return -1; } @@ -271,8 +273,9 @@ lws_gendtls_set_key_mem(struct lws_gendtls_ctx *ctx, const uint8_t *key, size_t if (ctx->cert_mem) { gnutls_datum_t c = { ctx->cert_mem, (unsigned int)ctx->cert_len - 1 }; gnutls_datum_t k = { ctx->key_mem, (unsigned int)ctx->key_len - 1 }; + gnutls_x509_crt_fmt_t fmt = (ctx->cert_mem[0] == '-') ? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER; - if (gnutls_certificate_set_x509_key_mem(ctx->cred, &c, &k, GNUTLS_X509_FMT_PEM) < 0) { + if (gnutls_certificate_set_x509_key_mem(ctx->cred, &c, &k, fmt) < 0) { lwsl_err("%s: failed to set cert/key\n", __func__); return -1; } @@ -431,10 +434,10 @@ lws_gendtls_is_clean(struct lws_gendtls_ctx *ctx) const char * lws_gendtls_get_srtp_profile(struct lws_gendtls_ctx *ctx) { -#if defined(GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80) +#if 1 gnutls_srtp_profile_t profile = 0; - if (gnutls_srtp_get_profile(ctx->session, &profile) != GNUTLS_E_SUCCESS) + if (gnutls_srtp_get_selected_profile(ctx->session, &profile) != GNUTLS_E_SUCCESS) return NULL; switch (profile) { diff --git a/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c b/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c index 61967a7dba..2e120a7d73 100644 --- a/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c +++ b/plugins/protocol_lws_webrtc/protocol_lws_webrtc.c @@ -629,7 +629,7 @@ lws_webrtc_create_offer(struct pss_webrtc *pss) /* Default PTs for Offer */ pss->media->pt_audio = 111; pss->media->pt_video_h264 = 102; - pss->media->pt_video_av1 = 104; + pss->media->pt_video_av1 = 0; pss->media->pt_video = pss->media->pt_video_h264; /* Default to H264 */ pss->media->rtp_ctx_video.ts = (uint32_t)(lws_now_usecs() * 9 / 100); @@ -651,7 +651,7 @@ lws_webrtc_create_offer(struct pss_webrtc *pss) /* Video Section */ lws_snprintf(video_m, sizeof(video_m), - "m=video %u UDP/TLS/RTP/SAVPF %u %u\\r\\n" + "m=video %u UDP/TLS/RTP/SAVPF %u\\r\\n" "c=IN IP4 0.0.0.0\\r\\n" "a=rtcp-mux\\r\\n" "a=ice-ufrag:%s\\r\\n" @@ -663,22 +663,16 @@ lws_webrtc_create_offer(struct pss_webrtc *pss) "a=msid:lws-stream lws-track-video\\r\\n" "a=rtpmap:%u H264/90000\\r\\n" "a=fmtp:%u level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e02a\\r\\n" - "a=rtpmap:%u AV1/90000\\r\\n" - "a=fmtp:%u profile=0;level-idx=5;tier=0\\r\\n" - "a=rtcp-fb:%u nack\\r\\n" - "a=rtcp-fb:%u nack pli\\r\\n" "a=rtcp-fb:%u nack\\r\\n" "a=rtcp-fb:%u nack pli\\r\\n" "a=ssrc:%u cname:lws-video\\r\\n" "a=ssrc:%u msid:lws-stream lws-track-video\\r\\n" "%s" "a=end-of-candidates\\r\\n", - vhd->udp_port, pss->media->pt_video_h264, pss->media->pt_video_av1, + vhd->udp_port, pss->media->pt_video_h264, pss->ice_ufrag, pss->ice_pwd, vhd->fingerprint, pss->media->pt_video_h264, pss->media->pt_video_h264, - pss->media->pt_video_av1, pss->media->pt_video_av1, pss->media->pt_video_h264, pss->media->pt_video_h264, - pss->media->pt_video_av1, pss->media->pt_video_av1, pss->media->ssrc_video, pss->media->ssrc_video, candidates); /* Audio Section */ @@ -829,7 +823,7 @@ handle_candidate(struct pss_webrtc *pss, struct vhd_webrtc *vhd, const char *can while (lws_tokenize(&ts) != LWS_TOKZE_ENDED) { lwsl_notice("%s: Token: '%.*s' (len %d, type %d), state %d\n", __func__, (int)ts.token_len, ts.token, (int)ts.token_len, ts.e, state); - if (state == 0 && ts.token_len == 3 && !strncmp(ts.token, "udp", 3)) { + if (state == 0 && ts.token_len == 3 && !strncasecmp(ts.token, "udp", 3)) { state = 1; /* Found Protocol udp */ } else if (state == 1) { /* Priority */ @@ -853,6 +847,11 @@ handle_candidate(struct pss_webrtc *pss, struct vhd_webrtc *vhd, const char *can if (state == 5 && port > 0) { webrtc_pss_log(pss, "Found ICE Candidate: %s:%d\n", ip_str, port); + if (pss->media && pss->media->peer_stun_received) { + webrtc_pss_log(pss, "Skipping candidate parsing as ICE is already resolved via STUN.\n"); + return 0; + } + if (lws_sa46_parse_numeric_address(ip_str, &pss->media->peer_sa46) < 0) return -1; sa46_sockport(&pss->media->peer_sa46, htons((uint16_t)port)); @@ -1003,7 +1002,7 @@ lws_webrtc_parse_sdp_codecs(struct pss_webrtc *pss, const char *sdp_clean) if (lws_tokenize(&ts) == LWS_TOKZE_TOKEN) { if (!strncasecmp(ts.token, "H264/90000", 10)) { /* We found H264. Map already populated in Pass 1. */ - } else if (!strncasecmp(ts.token, "AV1/90000", 9)) { + } else if (0 && !strncasecmp(ts.token, "AV1/90000", 9)) { pss->media->pt_video_av1 = (uint8_t)pt; } else if (!strncasecmp(ts.token, "VP9/90000", 9)) { } else if (!strncasecmp(ts.token, "opus/48000", 10)) { @@ -1340,7 +1339,7 @@ handle_offer(struct lws *wsi, struct pss_webrtc *pss, struct vhd_webrtc *vhd, co lwsl_warn(" SDP Parsing: PT %d -> Token '%.*s'\n", pt, (int)ts.token_len, ts.token); if (!strncasecmp(ts.token, "H264/90000", 10)) { /* We found H264. Map already populated in Pass 1. */ - } else if (!strncasecmp(ts.token, "AV1/90000", 9)) { + } else if (0 && !strncasecmp(ts.token, "AV1/90000", 9)) { pss->media->pt_video_av1 = (uint8_t)pt; lwsl_info(" Found AV1 PT: %d\n", pt); } else if (!strncasecmp(ts.token, "VP9/90000", 9)) { @@ -1733,9 +1732,14 @@ lws_shared_webrtc_callback(struct lws *wsi, enum lws_callback_reasons reason, lwsl_notice("%s: Generating self-signed certificate (this may take a few seconds)...\n", __func__); lws_usec_t t1 = lws_now_usecs(); - if (lws_x509_create_self_signed(vhd->context, &vhd->cert_mem, &vhd->cert_len, - &vhd->key_mem, &vhd->key_len, - vhd->external_ip, 2048)) { + struct lws_x509_cert_gen_info cert_info; + memset(&cert_info, 0, sizeof(cert_info)); + cert_info.san = vhd->external_ip[0] ? vhd->external_ip : "localhost"; + cert_info.curve_name = "P-256"; + cert_info.is_server = 1; + + if (lws_x509_create_cert(vhd->context, &vhd->cert_mem, &vhd->cert_len, + &vhd->key_mem, &vhd->key_len, &cert_info)) { lwsl_err("%s: Cert generation failed\n", __func__); return -1; } @@ -1753,7 +1757,7 @@ lws_shared_webrtc_callback(struct lws *wsi, enum lws_callback_reasons reason, } vhd->wsi_udp = lws_create_adopt_udp(vhd->vhost, - NULL, vhd->udp_port, LWS_CAUDP_BIND, + "0.0.0.0", vhd->udp_port, LWS_CAUDP_BIND, "lws-webrtc-udp", NULL, NULL, NULL, NULL, NULL); if (!vhd->wsi_udp) { lwsl_err("%s: UDP socket creation failed\n", __func__); @@ -2035,6 +2039,7 @@ webrtc_handle_stun(struct lws *wsi, struct vhd_webrtc *vhd, struct pss_webrtc ** if (pss->media) { pss->media->peer_sa46 = udp_desc->sa46; pss->media->has_peer_sa46 = 1; + pss->media->peer_stun_received = 1; } int fd = (int)(lws_intptr_t)lws_get_socket_fd(wsi); @@ -2350,6 +2355,7 @@ lws_shared_webrtc_udp_callback(struct lws *wsi, enum lws_callback_reasons reason case LWS_CALLBACK_RAW_RX: if (!vhd || !udp_desc) return 0; + lwsl_notice("%s: UDP packet received on port %u (len %zu)\n", __func__, vhd->udp_port, len); /* Create pure IPv4 mapping for logic checks */ struct sockaddr_in pure_sin; diff --git a/plugins/protocol_lws_webrtc/protocol_lws_webrtc.h b/plugins/protocol_lws_webrtc/protocol_lws_webrtc.h index 61a391b8e2..c81b59094e 100644 --- a/plugins/protocol_lws_webrtc/protocol_lws_webrtc.h +++ b/plugins/protocol_lws_webrtc/protocol_lws_webrtc.h @@ -43,6 +43,7 @@ struct lws_webrtc_peer_media { struct lws *wsi_udp; lws_sockaddr46 peer_sa46; int has_peer_sa46; + uint8_t peer_stun_received; struct lws_rtp_ctx rtp_ctx_video; struct lws_rtp_ctx rtp_ctx_audio; diff --git a/plugins/protocol_lws_webrtc_mixer/assets/main.js b/plugins/protocol_lws_webrtc_mixer/assets/main.js index 258ead2d49..6b7a9db7a3 100644 --- a/plugins/protocol_lws_webrtc_mixer/assets/main.js +++ b/plugins/protocol_lws_webrtc_mixer/assets/main.js @@ -115,6 +115,12 @@ function updateView() { window.addEventListener('resize', adjustOverlaySize); displayVideo.addEventListener('resize', adjustOverlaySize); displayVideo.addEventListener('loadedmetadata', adjustOverlaySize); + displayVideo.addEventListener('click', () => { + if (displayVideo.paused) { + console.log("Video clicked, attempting manual play..."); + displayVideo.play().catch(e => console.error("Manual play failed:", e)); + } + }); startFPSMonitor(); } diff --git a/plugins/protocol_lws_webrtc_mixer/mixer-media.c b/plugins/protocol_lws_webrtc_mixer/mixer-media.c index dc94026b70..bad57a444b 100644 --- a/plugins/protocol_lws_webrtc_mixer/mixer-media.c +++ b/plugins/protocol_lws_webrtc_mixer/mixer-media.c @@ -1518,24 +1518,46 @@ init_participant_media(struct participant *p, enum lws_video_codec codec) lws_snprintf(n_cfilt, sizeof(n_cfilt), "cfilt_%p", s); s->appsrc = gst_element_factory_make("appsrc", n_appsrc); - s->decodebin = gst_element_factory_make("avdec_h264", n_dec); + + GstElement *decoder = NULL; + GstElement *parser = NULL; + GstCaps *caps = NULL; + + if (codec == LWS_WEBRTC_CODEC_AV1) { + decoder = gst_element_factory_make("dav1ddec", n_dec); + if (!decoder) + decoder = gst_element_factory_make("avdec_av1", n_dec); + parser = gst_element_factory_make("av1parse", n_parse); + caps = gst_caps_new_empty_simple("video/x-av1"); + } else { + decoder = gst_element_factory_make("avdec_h264", n_dec); + parser = gst_element_factory_make("h264parse", n_parse); + caps = gst_caps_new_simple("video/x-h264", + "stream-format", G_TYPE_STRING, "byte-stream", + "alignment", G_TYPE_STRING, "au", + NULL); + } + + s->decodebin = decoder; + if (!s->decodebin) { - lwsl_err("%s: Critical: avdec_h264 not found, cannot build static chain\n", __func__); + lwsl_err("%s: Critical: Decoder not found for codec %d, cannot build static chain\n", __func__, codec); + if (caps) gst_caps_unref(caps); return -1; } + GstElement *que = gst_element_factory_make("queue", n_que); g_object_set(G_OBJECT(que), "max-size-buffers", 0, "max-size-time", (guint64)0, "max-size-bytes", (guint)0, NULL); - GstElement *h264parse = gst_element_factory_make("h264parse", n_parse); - GstElement *deint = gst_element_factory_make("deinterlace", n_deint); GstElement *vconv = gst_element_factory_make("videoconvert", n_vconv); GstElement *vscale = gst_element_factory_make("videoscale", n_vscale); GstElement *vrate = gst_element_factory_make("videorate", n_vrate); GstElement *cfilter = gst_element_factory_make("capsfilter", n_cfilt); - if (!s->appsrc || !s->decodebin || !que || !h264parse || !deint || !vconv || !vscale || !vrate || !cfilter) { + if (!s->appsrc || !s->decodebin || !que || !parser || !deint || !vconv || !vscale || !vrate || !cfilter) { lwsl_err("%s: Failed to create GStreamer elements\n", __func__); + if (caps) gst_caps_unref(caps); return -1; } @@ -1544,10 +1566,6 @@ init_participant_media(struct participant *p, enum lws_video_codec codec) g_object_set(G_OBJECT(cfilter), "caps", icaps, NULL); gst_caps_unref(icaps); - GstCaps *caps = gst_caps_new_simple("video/x-h264", - "stream-format", G_TYPE_STRING, "byte-stream", - "alignment", G_TYPE_STRING, "au", - NULL); g_object_set(G_OBJECT(s->appsrc), "caps", caps, "format", GST_FORMAT_TIME, "is-live", TRUE, "do-timestamp", FALSE, NULL); gst_caps_unref(caps); @@ -1564,10 +1582,10 @@ init_participant_media(struct participant *p, enum lws_video_codec codec) /* Set compositor pad to be as lenient as possible */ g_object_set(G_OBJECT(p->room->compositor), "latency", (GstClockTime)0, NULL); - gst_bin_add_many(GST_BIN(p->room->pipeline), s->appsrc, que, h264parse, s->decodebin, deint, vconv, vscale, vrate, cfilter, NULL); + gst_bin_add_many(GST_BIN(p->room->pipeline), s->appsrc, que, parser, s->decodebin, deint, vconv, vscale, vrate, cfilter, NULL); - /* Direct link: appsrc -> que -> h264parse -> avdec_h264 -> deinterlace -> vconv -> vscale -> videorate -> cfilter */ - if (!gst_element_link_many(s->appsrc, que, h264parse, s->decodebin, deint, vconv, vscale, vrate, cfilter, NULL)) { + /* Direct link: appsrc -> que -> parser -> decoder -> deinterlace -> vconv -> vscale -> videorate -> cfilter */ + if (!gst_element_link_many(s->appsrc, que, parser, s->decodebin, deint, vconv, vscale, vrate, cfilter, NULL)) { lwsl_err("%s: Failed to link static participant chain\n", __func__); } @@ -1591,7 +1609,7 @@ init_participant_media(struct participant *p, enum lws_video_codec codec) gst_element_sync_state_with_parent(s->appsrc); gst_element_sync_state_with_parent(que); - gst_element_sync_state_with_parent(h264parse); + gst_element_sync_state_with_parent(parser); gst_element_sync_state_with_parent(s->decodebin); gst_element_sync_state_with_parent(deint); gst_element_sync_state_with_parent(vconv); From ef7103a53159aabec6b2727b5c6c8888a1beb8a3 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 18 Jul 2026 10:11:20 +0100 Subject: [PATCH 31/44] qlr1 --- lib/core-net/private-lib-core-net.h | 3 +++ lib/core-net/wsi.c | 41 +++++++++++++++++++++++++++-- lib/roles/h3/ops-h3.c | 6 +++-- lib/roles/quic/ops-quic.c | 12 +++++---- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 902efccd82..4e55efd908 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -1360,6 +1360,9 @@ lws_service_flag_pending(struct lws_context *context, int tsi); int lws_has_buffered_out(struct lws *wsi); +int +lws_has_unsent_buffered_out(struct lws *wsi); + lws_handling_result_t LWS_WARN_UNUSED_RESULT lws_ws_client_rx_sm(struct lws *wsi, unsigned char c); diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index cce4ba2093..02f403ee28 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -609,7 +609,7 @@ int lws_has_buffered_out(struct lws *wsi) { struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) || (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) { - lwsl_notice("lws_has_buffered_out: %s has pending_tx stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid); + lwsl_info("lws_has_buffered_out: %s has pending_tx stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid); return 1; } } lws_end_foreach_dll_safe(d, d1); @@ -618,7 +618,44 @@ int lws_has_buffered_out(struct lws *wsi) { struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) || (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) { - lwsl_notice("lws_has_buffered_out: %s has in_flight stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid); + lwsl_info("lws_has_buffered_out: %s has in_flight stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid); + return 1; + } + } lws_end_foreach_dll_safe(d, d1); + } + } + } +#endif + + return 0; +} + +int lws_has_unsent_buffered_out(struct lws *wsi) { + if (wsi->buflist_out) + return 1; + +#if defined(LWS_ROLE_H2) + { + struct lws *nwsi = lws_get_network_wsi(wsi); + + if (nwsi && nwsi->buflist_out) + return 1; + } +#endif + +#if defined(LWS_ROLE_QUIC) + if (wsi->quic.qs) { + struct lws *nwsi = lws_get_network_wsi(wsi); + struct lws_quic_netconn *qn = nwsi ? nwsi->quic.qn : NULL; + if (qn) { + uint64_t sid = wsi->quic.qs->stream_id; + int i; + + for (i = 0; i < LWS_QUIC_LEVEL_COUNT; i++) { + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->pending_tx[i].head) { + struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list); + if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) || + (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) { return 1; } } lws_end_foreach_dll_safe(d, d1); diff --git a/lib/roles/h3/ops-h3.c b/lib/roles/h3/ops-h3.c index 856930a996..2d15dc18ba 100644 --- a/lib/roles/h3/ops-h3.c +++ b/lib/roles/h3/ops-h3.c @@ -233,8 +233,10 @@ rops_perform_user_POLLOUT_h3(struct lws *wsi) wsi->socket_is_permanently_unusable = 1; return -1; } - lws_callback_on_writable(wsi); - wsi->mux.requested_POLLOUT = 1; + if (lws_has_unsent_buffered_out(wsi)) { + lws_callback_on_writable(wsi); + wsi->mux.requested_POLLOUT = 1; + } return 0; } diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 3a1daab168..6879eed912 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -3282,12 +3282,14 @@ rops_tx_credit_quic(struct lws *wsi, char peer_to_us, int add) * H3 frames it (adding overhead), resulting in a write request of `cr + overhead` bytes, * which exceeds the flow control window and blocks, causing issues on non-seekable streams. */ - if (cr > 9) - cr -= 9; - else if (cr > 0) - cr = 1; - else + if (cr >= 3) { + if (cr > 9) + cr -= 9; + else + cr -= 2; + } else { cr = 0; + } if (!cr && !add) lwsl_debug("rops_tx_credit_quic: US_TO_PEER returning 0: " From 3eea88f47889e4d0a717d8e352db8c71c936da82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3FUTF-8=3Fq=3FSa=3DC3=3DBAl=3D20Ibarra=3D20Corretg=3DC?= =?UTF-8?q?3=3DA9=3F=3D?= Date: Fri, 17 Jul 2026 13:58:32 +0200 Subject: [PATCH 32/44] h2: client: mark reused mux streams as mux_substream lws_wsi_h2_adopt() set client_mux_substream but not mux_substream on a reused (pooled) HTTP/2 client stream, whereas the first stream on a connection carries mux_substream = 1. The HPACK decoder only captures custom / non-indexed response headers into the ah unknown-header list (ah->unk_ll_head) when mux_substream is set, so on a reused client stream those headers are never stored: the unknown-header list is left stale, and lws_hdr_custom_length/copy/name_foreach then walk it and read out of bounds (observed as a heap-use-after-free when the walk runs past the ah into an adjacent freed header table). This also silently drops all custom response headers on the 2nd+ request of a pooled h2 client connection. Set mux_substream on the reused client stream too, matching the first stream, so HPACK captures custom response headers correctly. --- lib/roles/h2/http2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 382bb885bf..dfde74e3aa 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -339,6 +339,15 @@ lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi) wsi->seen_nonpseudoheader = 0; #if defined(LWS_WITH_CLIENT) wsi->client_mux_substream = 1; + /* + * A reused client stream is a mux substream just like the first one. + * Without this, HPACK skips capturing custom (non-indexed) response + * headers into the ah unknown-header list (that capture is gated on + * mux_substream), so a pooled client's 2nd+ request drops custom + * response headers and leaves a stale list that lws_hdr_custom_* + * subsequently walks out of bounds. + */ + wsi->mux_substream = 1; #endif wsi->h2.initialized = 1; From 294700946b2cf7b19724212bfa704585d4b13db5 Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Fri, 17 Jul 2026 16:28:24 +0530 Subject: [PATCH 33/44] client: don't re-remove already-failed parallel connect attempts A parallel (happy-eyeballs) connect attempt that fails synchronously in connect() (eg, EHOSTUNREACH for an unroutable AAAA result) is cleaned up at the failure site: its socket is closed, its fds-table slot removed and the parallel_conns entry invalidated. Its position_in_fds_table was never assigned (that only happens on the EINPROGRESS path). When the surviving attempt then connects, the completion path iterated all parallel_conns slots up to parallel_count without checking is_valid, calling lws_remove_parallel_fd_safely() on the dead entry: that used the unassigned position (LWS_NO_FDS_POS) to remove an unrelated fds-table slot and double-closed a possibly since-reused fd. Depending on what the scrambled fds table hit, the connection then lost its POLLOUT events (client POST body never sent, transaction stalls to timeout) and context destroy walked a stale entry into a freed wsi (SIGSEGV/SIGBUS). Seen on macOS against dual-stack (A+AAAA) endpoints with no IPv6 route, eg the AWS Kinesis Video *.api.aws hosts. Guard lws_remove_parallel_fd_safely() against invalid/never-registered entries and restore the is_valid check at the connected-path cleanup loop. --- lib/core-net/client/connect3.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 1fb46c37f5..2d90698733 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -271,6 +271,16 @@ lws_remove_parallel_fd_safely(struct lws *wsi, int pidx) int saved_pos = wsi->position_in_fds_table; lws_sock_file_fd_type saved_fd = wsi->desc; + /* + * An attempt that already failed (eg, synchronous connect() failure + * like EHOSTUNREACH on an unroutable AAAA result) was invalidated and + * its socket closed at the failure site; its fds-table position was + * never assigned. Removing it again would use the bogus position to + * remove an unrelated fds slot and double-close a possibly-reused fd. + */ + if (!wsi->parallel_conns[pidx].is_valid || hole_pos == LWS_NO_FDS_POS) + return; + wsi->desc.sockfd = wsi->parallel_conns[pidx].desc.sockfd; wsi->position_in_fds_table = hole_pos; @@ -477,6 +487,7 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads, promote_parallel_fd(wsi, pidx); /* close all remaining parallel */ for (m = 0; m < wsi->parallel_count; m++) + if (wsi->parallel_conns[m].is_valid) /* A parallel socket failed. Just close it and remove from fds */ lws_remove_parallel_fd_safely(wsi, m); wsi->parallel_count = 0; From 66c631a4564d3050bf79646fd05bf6a647f7265e Mon Sep 17 00:00:00 2001 From: Yucong Sun Date: Sat, 18 Jul 2026 16:00:38 +0000 Subject: [PATCH 34/44] h2: client: make ws-over-h2 (RFC 8441 extended CONNECT) actually work The client-side extended CONNECT support had rotted in several ways that each prevented a ws-over-h2 client connection from establishing or passing data: - migrating the original client ask onto h2 sid 1 dropped the ws ask itself: wsi->ws and the do_ws flag stayed on the network wsi, so lws_h2_client_handshake() issued a plain GET instead of CONNECT - the client handshake emitted the ws regular headers (sec-websocket-version / -protocol) BEFORE the :authority pseudo-header; strict peers (including lws itself as server) fail the stream with 'Pseudoheader after normal hdrs' - the requested subprotocol was read from the client's ORIGIN instead of the sent-protocols list - the 200 reply to extended CONNECT has no sec-websocket-accept (there is no key exchange in RFC 8441), and the accept-token confirmation strcmp'd the missing header ptr: NULL deref - the established client stream transitioned to the ws role without LWSIFR_P_ENCAP_H2, so writes / writable requests / tx credit did not route via the h2 parent (the server side sets it; the client didn't) - DATA rx on a client stream carrying ws was delivered as RECEIVE_CLIENT_HTTP_READ http body instead of being run through the ws parser, so ws payload never reached LWS_CALLBACK_CLIENT_RECEIVE With these, lws_client_connect_via_info() with alpn "h2" and no method performs a working RFC 8441 ws client connection against an lws server; covered by the new api-test-ws-h2-txcredit in a later patch. --- lib/roles/h2/http2.c | 72 ++++++++++++++++++++++++++++------------ lib/roles/ws/client-ws.c | 20 ++++++++--- 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index dfde74e3aa..755230e6f3 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -1550,6 +1550,19 @@ lws_h2_parse_end_of_frame(struct lws *wsi) #endif h2n->swsi->client_mux_substream = 1; h2n->swsi->client_h2_alpn = 1; +#if defined(LWS_ROLE_WS) + /* + * If the original client ask was a ws connection, the + * migrated stream carries it (RFC 8441 extended + * CONNECT); without this the h2 client handshake + * issues a plain GET and the ws ask is lost. + */ + if (wsi->ws) { + h2n->swsi->ws = wsi->ws; + wsi->ws = NULL; + h2n->swsi->do_ws = 1; + } +#endif #if defined(LWS_WITH_CLIENT) h2n->swsi->flags = wsi->flags; #if defined(LWS_WITH_CONMON) @@ -2289,7 +2302,15 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t _inlen, "\n", n); } #if defined(LWS_WITH_CLIENT) - if (h2n->swsi->client_mux_substream) { + /* + * A client stream carrying ws (RFC 8441) is + * NOT http body: its DATA is ws framing that + * must go through the ws parser via the + * lws_read_h1() path below, like the server + * side of the same situation. + */ + if (h2n->swsi->client_mux_substream && + !h2n->swsi->h23_stream_carries_ws) { if (!h2n->swsi->a.protocol) { lwsl_err("%s: %p doesn't have protocol\n", __func__, lws_wsi_tag(h2n->swsi)); @@ -2675,27 +2696,6 @@ lws_h2_client_handshake(struct lws *wsi) (unsigned char *)path, n, &p, end)) goto fail_length; -#if defined(LWS_ROLE_WS) - if (wsi->do_ws) { - const char *prot = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); - - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_VERSION, - (unsigned char *)"13", 2, &p, end)) - goto fail_length; - - if (!prot && wsi->stash && wsi->stash->cis[CIS_PROTOCOL]) - prot = wsi->stash->cis[CIS_PROTOCOL]; - - if (prot) { - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL, - (unsigned char *)prot, (int)strlen(prot), &p, end)) - goto fail_length; - } - - wsi->h23_stream_carries_ws = 1; - } -#endif - n = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_HOST); simp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST); if (!n && wsi->stash && wsi->stash->cis[CIS_ADDRESS]) { @@ -2716,6 +2716,34 @@ lws_h2_client_handshake(struct lws *wsi) (unsigned char *)simp, n, &p, end)) goto fail_length; +#if defined(LWS_ROLE_WS) + if (wsi->do_ws) { + /* + * These are regular headers, so they must come after every + * pseudo-header (:authority is the last one above), or strict + * peers fail the stream with "pseudoheader after normal hdrs". + * The requested subprotocol list rides in the same header as + * for h1 upgrades (RFC 8441 Sect 5). + */ + const char *prot = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_VERSION, + (unsigned char *)"13", 2, &p, end)) + goto fail_length; + + if (!prot && wsi->stash && wsi->stash->cis[CIS_PROTOCOL]) + prot = wsi->stash->cis[CIS_PROTOCOL]; + + if (prot) { + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL, + (unsigned char *)prot, (int)strlen(prot), &p, end)) + goto fail_length; + } + + wsi->h23_stream_carries_ws = 1; + } +#endif if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) { p1 = lws_http_multipart_headers(wsi, p); diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c index ea3e9c09ea..d9db397933 100644 --- a/lib/roles/ws/client-ws.c +++ b/lib/roles/ws/client-ws.c @@ -592,12 +592,16 @@ lws_client_ws_upgrade(struct lws *wsi, const char **cce) #endif /* - * Confirm his accept token is the one we precomputed + * Confirm his accept token is the one we precomputed. An extended + * CONNECT reply (ws-over-h2, RFC 8441) has no key exchange and so no + * accept token; p is NULL there and there is nothing to check. */ p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); - if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) { - lwsl_wsi_warn(wsi, "lws_client_int_s_hs: accept '%s' wrong vs '%s'", p, + if (!wsi->client_mux_substream && + (!p || strcmp(p, wsi->http.ah->initial_handshake_hash_base64))) { + lwsl_wsi_warn(wsi, "lws_client_int_s_hs: accept '%s' wrong vs '%s'", + p ? p : "(null)", wsi->http.ah->initial_handshake_hash_base64); *cce = "HS: Accept hash wrong"; goto bail2; @@ -627,7 +631,15 @@ lws_client_ws_upgrade(struct lws *wsi, const char **cce) /* free up his parsing allocations */ lws_header_table_detach(wsi, 0); - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, &role_ops_ws); + /* + * A ws stream carried inside an h2 connection (RFC 8441) must keep + * the encapsulation visible in its role, like the server side does: + * writes, writable requests and tx credit all route via the h2 + * parent based on lwsi_role_h2_ENCAPSULATION(). + */ + lws_role_transition(wsi, LWSIFR_CLIENT | + (wsi->client_mux_substream ? LWSIFR_P_ENCAP_H2 : 0), + LRS_ESTABLISHED, &role_ops_ws); lws_validity_confirmed(wsi); wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; From 57effc6561bc7f60931b53e94ac8c50500103569 Mon Sep 17 00:00:00 2001 From: Yucong Sun Date: Sat, 18 Jul 2026 16:00:38 +0000 Subject: [PATCH 35/44] ws-over-h2: respect the peer's h2 flow-control window on tx lws_h2_frame_write() sent DATA regardless of the stream's tx credit (just logging at info level), relying on the user code to size writes to lws_get_peer_write_allowance(). But the ws role never exposed a tx_credit rops, so for ws-over-h2 (RFC 8441) streams that API returned -1 'no guidance' and user code COULD NOT cooperate even if it wanted to: any ws server bulk-sending into a stream whose window ran out overran the peer's advertised window, which strict peers (nghttp2, browsers) punish by killing the connection with FLOW_CONTROL_ERROR. Handle it inside lws where the accounting lives: - lws_h2_frame_write() parks a whole carries-ws DATA frame on the stream's buflist_out when there is not enough credit (or earlier frames are already parked, preserving order) - the POLLOUT servicing loop drains parked frames oldest-first as WINDOW_UPDATE restores credit (which already re-arms every mux child). h2 DATA is a byte stream, so a parked frame larger than the available credit is SPLIT, sending a chunk under a fresh frame header and rewriting the stored header for the remainder -- the peer's SETTINGS_INITIAL_WINDOW_SIZE may be permanently smaller than a parked frame, and waiting for whole-frame credit would deadlock. Flags (END_STREAM) ride only on a frame's final chunk. - lws_send_pipe_choked() reports a stream with parked frames as choked, so user write loops gated on it back off naturally and parking stays bounded around one frame past the window - the ws role gets a tx_credit rops delegating to the h2 role when the stream is h2-encapsulated, so lws_get_peer_write_allowance() gives real guidance on ws streams and lws_wsi_tx_credit() can grant manual rx credit (LCCSCF_H2_MANUAL_RXFLOW) on a ws-upgraded stream; non-encapsulated ws keeps returning -1 as if the rops were absent Covered by api-test-ws-h2-txcredit in a later patch, which fails on the previous behaviour ('flow control violated: rx 2048 > granted 1024') and passes with this. --- lib/plat/unix/unix-sockets.c | 8 ++ lib/plat/windows/windows-sockets.c | 8 ++ lib/roles/h2/http2.c | 123 ++++++++++++++++++++++++++++ lib/roles/h2/ops-h2.c | 26 ++++++ lib/roles/h2/private-lib-roles-h2.h | 4 + lib/roles/ws/ops-ws.c | 23 +++++- 6 files changed, 191 insertions(+), 1 deletion(-) diff --git a/lib/plat/unix/unix-sockets.c b/lib/plat/unix/unix-sockets.c index 7042d1150b..f34a266ea1 100644 --- a/lib/plat/unix/unix-sockets.c +++ b/lib/plat/unix/unix-sockets.c @@ -68,6 +68,14 @@ lws_send_pipe_choked(struct lws *wsi) #if defined(LWS_WITH_HTTP2) wsi_eff = lws_get_network_wsi(wsi); + + /* + * ws-over-h2: whole DATA frames parked on the stream awaiting h2 + * tx credit (see lws_h2_frame_write) mean the pipe is choked for + * this stream even though the network wsi could accept bytes. + */ + if (wsi_eff != wsi && lws_has_buffered_out(wsi)) + return 1; #else wsi_eff = wsi; #endif diff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c index 1a737a7350..306e0fbe4e 100644 --- a/lib/plat/windows/windows-sockets.c +++ b/lib/plat/windows/windows-sockets.c @@ -42,6 +42,14 @@ lws_send_pipe_choked(struct lws *wsi) #if defined(LWS_WITH_HTTP2) wsi_eff = lws_get_network_wsi(wsi); + + /* + * ws-over-h2: whole DATA frames parked on the stream awaiting h2 + * tx credit (see lws_h2_frame_write) mean the pipe is choked for + * this stream even though the network wsi could accept bytes. + */ + if (wsi_eff != wsi && lws_has_buffered_out(wsi)) + return 1; #else wsi_eff = wsi; #endif diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 755230e6f3..1bc0dbcabb 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -704,6 +704,33 @@ int lws_h2_frame_write(struct lws *wsi, int type, int flags, sid, len, (int)wsi->txc.tx_cr, (int)nwsi->txc.tx_cr); if (type == LWS_H2_FRAME_TYPE_DATA) { +#if defined(LWS_ROLE_WS) + if (wsi->h23_stream_carries_ws && + (lws_has_buffered_out(wsi) || + lws_h2_tx_cr_get(wsi) < (int)len)) { + /* + * ws-over-h2 (RFC 8441): not enough h2 flow-control + * credit for this DATA frame (or earlier frames are + * already parked and must keep their order). Sending + * DATA past the peer's advertised window is a + * connection-fatal FLOW_CONTROL_ERROR, and unlike the + * http file-serving path the ws user API has no seat + * at the flow-control table -- so park the whole + * frame (header included) on the stream's buflist and + * send it from the POLLOUT servicing loop once + * WINDOW_UPDATE restores credit (which re-arms every + * mux child). Credit is consumed at drain time. + */ + if (lws_buflist_append_segment(&wsi->buflist_out, + &buf[-LWS_H2_FRAME_HEADER_LENGTH], + len + LWS_H2_FRAME_HEADER_LENGTH) < 0) + return -1; + + lws_callback_on_writable(wsi); + + return (int)len; + } +#endif if (wsi->txc.tx_cr < (int)len) lwsl_info("%s: %s: sending payload len %d" @@ -723,6 +750,102 @@ int lws_h2_frame_write(struct lws *wsi, int type, int flags, return n; } +#if defined(LWS_ROLE_WS) +/* + * Send as much parked ws-over-h2 DATA (see lws_h2_frame_write) as the + * stream's tx credit allows, oldest first. Each parked buflist segment is one + * complete h2 frame: the 9-byte header (carrying the payload length) plus the + * payload. h2 DATA is a byte stream, so a frame larger than the available + * credit is SPLIT: a chunk goes out under a fresh header and the stored + * header is rewritten in place for the remainder -- the peer's stream window + * (SETTINGS_INITIAL_WINDOW_SIZE) may be permanently smaller than a parked + * frame, so waiting for whole-frame credit can deadlock. Flags (e.g. + * END_STREAM) are only sent with a frame's final chunk. Returns <0 on a + * fatal write error; leftover bytes stay parked until the next WINDOW_UPDATE. + */ +int +lws_h2_ws_drain_parked_tx(struct lws *nwsi, struct lws *wsi) +{ + uint8_t out[LWS_H2_FRAME_HEADER_LENGTH + 4096]; + + while (lws_has_buffered_out(wsi)) { + uint8_t *seg = NULL; + size_t sl = lws_buflist_next_segment_len(&wsi->buflist_out, + &seg); + unsigned int plen, chunk, rem; + uint8_t type, flags, sid[4]; + int cr; + + if (!sl || !seg || sl < LWS_H2_FRAME_HEADER_LENGTH) + return -1; + + plen = ((unsigned int)seg[0] << 16) | + ((unsigned int)seg[1] << 8) | seg[2]; + type = seg[3]; + flags = seg[4]; + memcpy(sid, &seg[5], 4); + + if (sl < LWS_H2_FRAME_HEADER_LENGTH + plen) + return -1; + + cr = lws_h2_tx_cr_get(wsi); + if (plen && cr <= 0) + /* still skint; WINDOW_UPDATE will re-arm us */ + return 0; + + chunk = plen; + if (chunk > (unsigned int)cr) + chunk = (unsigned int)cr; + if (chunk > sizeof(out) - LWS_H2_FRAME_HEADER_LENGTH) + chunk = sizeof(out) - LWS_H2_FRAME_HEADER_LENGTH; + + out[0] = (uint8_t)(chunk >> 16); + out[1] = (uint8_t)(chunk >> 8); + out[2] = (uint8_t)chunk; + out[3] = type; + out[4] = chunk == plen ? flags : 0; + memcpy(&out[5], sid, 4); + memcpy(&out[LWS_H2_FRAME_HEADER_LENGTH], + &seg[LWS_H2_FRAME_HEADER_LENGTH], chunk); + + lws_h2_tx_cr_consume(wsi, (int)chunk); + + /* + * lws_issue_raw() either sends the bytes or copies any + * remainder onto nwsi's own buflist, so the parked bytes can + * be released either way. + */ + if (lws_issue_raw(nwsi, out, + LWS_H2_FRAME_HEADER_LENGTH + chunk) < 0) + return -1; + + if (chunk == plen) { + lws_buflist_use_segment(&wsi->buflist_out, + LWS_H2_FRAME_HEADER_LENGTH + plen); + continue; + } + + /* + * Partial send: stamp a header for the remainder over the + * last 9 already-consumed bytes (the sent payload ended at + * seg[chunk + 9], so seg[chunk..chunk+8] only covers spent + * header/payload bytes for any chunk >= 1), then advance the + * segment onto it. + */ + rem = plen - chunk; + seg[chunk + 0] = (uint8_t)(rem >> 16); + seg[chunk + 1] = (uint8_t)(rem >> 8); + seg[chunk + 2] = (uint8_t)rem; + seg[chunk + 3] = type; + seg[chunk + 4] = flags; + memcpy(&seg[chunk + 5], sid, 4); + lws_buflist_use_segment(&wsi->buflist_out, chunk); + } + + return 0; +} +#endif + static void lws_h2_set_bin(struct lws *wsi, int n, unsigned char *buf) { *buf++ = (uint8_t)(n >> 8); diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 8b57506507..b3c94e7481 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -1090,6 +1090,32 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) /* priority 1: post compression-transform buffered output */ if (lws_has_buffered_out(w)) { +#if defined(LWS_ROLE_WS) + if (w->h23_stream_carries_ws) { + /* + * ws-over-h2: whole DATA frames parked by + * lws_h2_frame_write() waiting for tx credit + */ + if (lws_h2_ws_drain_parked_tx(wsi, w) < 0) { + lwsl_info("%s signalling to close\n", + __func__); + lws_close_free_wsi(w, + LWS_CLOSE_STATUS_NOSTATUS, + "h2 end stream 1"); + wa = &wsi->mux.child_list; + goto next_child; + } + if (!lws_has_buffered_out(w)) + /* fully drained: let the user write */ + lws_callback_on_writable(w); + /* + * else still skint: stay quiet, the + * WINDOW_UPDATE handler re-arms every child + */ + wa = &wsi->mux.child_list; + goto next_child; + } +#endif lwsl_debug("%s: completing partial\n", __func__); if (lws_issue_raw(w, NULL, 0) < 0) { lwsl_info("%s signalling to close\n", __func__); diff --git a/lib/roles/h2/private-lib-roles-h2.h b/lib/roles/h2/private-lib-roles-h2.h index 800d377087..5fa716e308 100644 --- a/lib/roles/h2/private-lib-roles-h2.h +++ b/lib/roles/h2/private-lib-roles-h2.h @@ -331,6 +331,10 @@ lws_h2_do_pps_send(struct lws *wsi); int lws_h2_frame_write(struct lws *wsi, int type, int flags, unsigned int sid, unsigned int len, unsigned char *buf); +#if defined(LWS_ROLE_WS) +int +lws_h2_ws_drain_parked_tx(struct lws *nwsi, struct lws *wsi); +#endif struct lws * lws_wsi_mux_from_id(struct lws *wsi, unsigned int sid); int diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 2082767dd5..c6c8edbed6 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -2080,6 +2080,26 @@ rops_callback_on_writable_ws(struct lws *wsi) return 0; } +static int +rops_tx_credit_ws(struct lws *wsi, char peer_to_us, int add) +{ +#if defined(LWS_WITH_HTTP2) + /* + * ws-over-h2: flow control belongs to the encapsulating h2 stream. + * Delegate so lws_get_peer_write_allowance() reports the stream's + * real tx window and so lws_wsi_tx_credit() can grant manual rx + * credit (LCCSCF_H2_MANUAL_RXFLOW) on a ws-upgraded stream. + */ + if (lwsi_role_h2_ENCAPSULATION(wsi)) + return lws_rops_func_fidx(&role_ops_h2, LWS_ROPS_tx_credit). + tx_credit(wsi, peer_to_us, add); +#endif + (void)peer_to_us; + (void)add; + + return -1; /* no guidance, like the rops being absent */ +} + static int rops_init_vhost_ws(struct lws_vhost *vh, const struct lws_context_creation_info *info) @@ -2196,6 +2216,7 @@ static const lws_rops_t rops_table_ws[] = { /* 10 */ { .close_kill_connection = rops_close_kill_connection_ws }, /* 11 */ { .destroy_role = rops_destroy_role_ws }, /* 12 */ { .issue_keepalive = rops_issue_keepalive_ws }, + /* 13 */ { .tx_credit = rops_tx_credit_ws }, }; const struct lws_role_ops role_ops_ws = { @@ -2213,7 +2234,7 @@ const struct lws_role_ops role_ops_ws = { /* LWS_ROPS_handle_POLLOUT */ /* LWS_ROPS_perform_user_POLLOUT */ 0x50, /* LWS_ROPS_callback_on_writable */ - /* LWS_ROPS_tx_credit */ 0x60, + /* LWS_ROPS_tx_credit */ 0x6d, /* LWS_ROPS_write_role_protocol */ /* LWS_ROPS_encapsulation_parent */ 0x70, /* LWS_ROPS_alpn_negotiated */ From bc03c553e9090353c29a81e9e57adb4b35d65ea8 Mon Sep 17 00:00:00 2001 From: Yucong Sun Date: Sat, 18 Jul 2026 16:00:39 +0000 Subject: [PATCH 36/44] ws-over-h2: give extensions their PACKET_TX_PRESEND look at encapsulated frames The h1 tx path fires LWS_EXT_CB_PACKET_TX_PRESEND from lws_issue_raw_ext_access(), which is where permessage-deflate sets RSV1 (and fixes up the first-frame opcode) on the compressed frame it produced during LWS_EXT_CB_PAYLOAD_TX. The h2-encapsulation path in rops_write_role_protocol_ws() bypassed it entirely, so a ws-over-h2 connection that negotiated permessage-deflate sent DEFLATED payload WITHOUT RSV1: peers dutifully treated the compressed bytes as plain payload and rendered garbage. Observed against Firefox (which offers permessage-deflate on its RFC 8441 extended CONNECT) with the FluffOS mud driver embedding lws; also reproduced with a node http2 extended-CONNECT client. Fire PRESEND on the assembled frame before delegating it to the h2 role, mirroring what the h1 path does. --- lib/roles/ws/ops-ws.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index c6c8edbed6..58a8110b81 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -1979,6 +1979,28 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, assert(encap != wsi); +#if !defined(LWS_WITHOUT_EXTENSIONS) + /* + * The h1 path fires LWS_EXT_CB_PACKET_TX_PRESEND from + * lws_issue_raw_ext_access(); this encapsulated path used to + * bypass it, so permessage-deflate never got to set RSV1 (or + * fix up the first-frame opcode) on the compressed frame it + * already produced above -- peers then treated the deflated + * payload as plain text. Give active extensions the same + * look at the assembled frame here. + */ + { + struct lws_tokens ebuf; + + ebuf.token = buf - pre; + ebuf.len = (int)(len + (unsigned int)pre); + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND, + &ebuf, 0) < 0) + return -1; + } +#endif + return lws_rops_func_fidx(encap->role_ops, LWS_ROPS_write_role_protocol). write_role_protocol(wsi, buf - pre, From e4deec977614dbee44ccee5a2502c01636d1ff2c Mon Sep 17 00:00:00 2001 From: Yucong Sun Date: Sat, 18 Jul 2026 16:00:39 +0000 Subject: [PATCH 37/44] ws: don't deref a NULL h2 parent in callback_on_writable mid-teardown rops_close_kill_connection_h2() unlinks a carries-ws stream from its h2 parent BEFORE the protocol close callback fires (close_cb runs at __lws_close_free_wsi(), the sibling disconnect already happened in the close_kill_connection rops). User code that reacts to LWS_CALLBACK_CLOSED by trying to schedule a final flush with lws_callback_on_writable() then reaches rops_callback_on_writable_ws() with encapsulation_parent() == NULL: the assert is compiled out on release builds and the deref segfaults (seen in the field as a crash at offset 0x438 == offsetof role_ops, via the FluffOS mud driver when a browser dropped a ws-over-h2 connection with output still queued). Nothing can be scheduled on a stream at that point; return having done nothing instead of crashing. --- lib/roles/ws/ops-ws.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 58a8110b81..841656af62 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -2093,6 +2093,15 @@ rops_callback_on_writable_ws(struct lws *wsi) encapsulation_parent(wsi); assert(enc); + if (!enc) + /* + * Mid-teardown: close_kill_connection already unlinked + * us from the h2 parent (the close callback fires + * after that). Nothing can be scheduled any more; on + * release builds the assert above is compiled out and + * this used to segfault. + */ + return 1; if (lws_rops_func_fidx(enc->role_ops, LWS_ROPS_callback_on_writable). callback_on_writable(wsi)) From faf80469e32870490389f46779444d9657e712d6 Mon Sep 17 00:00:00 2001 From: Yucong Sun Date: Sat, 18 Jul 2026 16:00:39 +0000 Subject: [PATCH 38/44] api-test-ws-h2-txcredit: ws-over-h2 tx flow-control selftest A ws server vhost and an RFC 8441 h2 ws client in one process. The server bulk-sends a 64KB pattern in 1KB lws_write() chunks gated only on lws_send_pipe_choked(), like typical user code; the client connects with LCCSCF_H2_MANUAL_RXFLOW and drip-feeds tx credit in 1KB WINDOW_UPDATE quanta, forcing the h2 role to park DATA and split frames to fit the window. Fails (within a second) if the connection doesn't come up as genuine ws-over-h2, if the client ever receives more ws payload than it has granted credit for -- only possible when the server ignored the peer's window -- or if the pattern is corrupted / incomplete; passes when the whole transfer arrives in-window. Wired into ctest like the other api-tests. --- .../api-test-ws-h2-txcredit/CMakeLists.txt | 36 ++ .../api-tests/api-test-ws-h2-txcredit/main.c | 392 ++++++++++++++++++ 2 files changed, 428 insertions(+) create mode 100644 minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/CMakeLists.txt create mode 100644 minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/main.c diff --git a/minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/CMakeLists.txt b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/CMakeLists.txt new file mode 100644 index 0000000000..343beea838 --- /dev/null +++ b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/CMakeLists.txt @@ -0,0 +1,36 @@ +project(lws-api-test-ws-h2-txcredit C) +cmake_minimum_required(VERSION 3.10) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-api-test-ws-h2-txcredit) +set(SRCS main.c) + +set(requirements 1) +require_lws_config(LWS_ROLE_WS 1 requirements) +require_lws_config(LWS_ROLE_H2 1 requirements) +require_lws_config(LWS_WITH_CLIENT 1 requirements) +require_lws_config(LWS_WITH_SERVER 1 requirements) +require_lws_config(LWS_WITH_TLS 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() + + lws_get_free_port(PORT_WS_H2_TXCR) + + add_test(NAME api-test-ws-h2-txcredit COMMAND lws-api-test-ws-h2-txcredit -p ${PORT_WS_H2_TXCR}) + set_tests_properties(api-test-ws-h2-txcredit + PROPERTIES + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit + TIMEOUT 60) + +endif() diff --git a/minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/main.c b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/main.c new file mode 100644 index 0000000000..acaed35be1 --- /dev/null +++ b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-txcredit/main.c @@ -0,0 +1,392 @@ +/* + * lws-api-test-ws-h2-txcredit + * + * Written in 2010-2026 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * Tests that a ws-over-h2 (RFC 8441) server respects the peer's h2 + * flow-control window on tx. + * + * A ws server vhost and an h2 ws client run in one process. The server + * bulk-sends a 64KB pattern in 1KB lws_write() chunks, gated only on + * lws_send_pipe_choked(), like typical user code. The client connects with + * LCCSCF_H2_MANUAL_RXFLOW and drip-feeds tx credit in 1KB WINDOW_UPDATEs, + * so the server is forced to hold DATA back (parking it inside the h2 role, + * splitting frames larger than the available credit) instead of overrunning + * the peer's window, which is a connection-fatal FLOW_CONTROL_ERROR with + * strict peers like nghttp2 / browsers. + * + * The test fails if + * - the negotiated connection is not actually ws-over-h2, + * - the client ever received more payload than it granted credit for + * (the ws frame headers ride inside DATA too, so payload > granted can + * only happen when the server ignored the window), or + * - the received pattern is corrupted / the transfer doesn't complete. + */ + +#include +#include +#include + +static int interrupted; +static int result = 1; +static struct lws_context *context; + +static const char * const test_cert = +"-----BEGIN CERTIFICATE-----\n" +"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\n" +"e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=\n" +"-----END CERTIFICATE-----\n"; + +static const char * const test_key = +"-----BEGIN PRIVATE KEY-----\n" +"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\n" +"fBrpEY1IATtPq1taBZZogRqI3rOkkPk=\n" +"-----END PRIVATE KEY-----\n"; + +#define TEST_TOTAL 65536 +#define TEST_CHUNK 1024 +#define GRANT_QUANTUM 1024 +#define GRANT_US (10 * LWS_US_PER_MS) + +static struct lws *client_wsi; +static size_t srv_sent; +static size_t cli_rx; +static size_t cli_granted; +static int port_tcp = 7681; +static lws_sorted_usec_list_t sul_grant, sul_timeout; + +static void +sul_timeout_cb(lws_sorted_usec_list_t *sul) +{ + lwsl_err("--- timeout: rx %d / %d, granted %d ---\n", + (int)cli_rx, TEST_TOTAL, (int)cli_granted); + interrupted = 1; +} + +static void +sul_grant_cb(lws_sorted_usec_list_t *sul) +{ + if (!client_wsi || cli_rx >= TEST_TOTAL) + return; + + if (cli_granted < TEST_TOTAL + (2 * TEST_CHUNK)) { + cli_granted += GRANT_QUANTUM; + if (lws_wsi_tx_credit(client_wsi, LWSTXCR_PEER_TO_US, + GRANT_QUANTUM) < 0) + lwsl_warn("%s: tx_credit update failed\n", __func__); + } + + lws_sul_schedule(context, 0, &sul_grant, sul_grant_cb, GRANT_US); +} + +static int +callback_srv(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + lwsl_user("%s: server: established\n", __func__); + srv_sent = 0; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: { + uint8_t buf[LWS_PRE + TEST_CHUNK]; + size_t n; + + lwsl_info("%s: srv writeable: sent %d, choked %d\n", __func__, + (int)srv_sent, lws_send_pipe_choked(wsi)); + + /* + * bulk-send gated only on choked, like typical user code: + * the h2 role must hold DATA back itself when the peer's + * window is exhausted + */ + while (srv_sent < TEST_TOTAL && !lws_send_pipe_choked(wsi)) { + n = TEST_TOTAL - srv_sent; + if (n > TEST_CHUNK) + n = TEST_CHUNK; + for (size_t i = 0; i < n; i++) + buf[LWS_PRE + i] = + (uint8_t)('A' + ((srv_sent + i) % 26)); + int wr = lws_write(wsi, &buf[LWS_PRE], n, + LWS_WRITE_BINARY); + lwsl_info("%s: srv wrote %d of %d\n", __func__, wr, (int)n); + if (wr < (int)n) { + lwsl_err("%s: server write failed\n", __func__); + return -1; + } + srv_sent += n; + } + if (srv_sent < TEST_TOTAL) + lws_callback_on_writable(wsi); + break; + } + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +static int +callback_cli(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: + lwsl_user("%s: client: established\n", __func__); + if (lws_get_network_wsi(wsi) == wsi) { + lwsl_err("--- not encapsulated in h2 ---\n"); + interrupted = 1; + return -1; + } + client_wsi = wsi; + lws_sul_schedule(context, 0, &sul_grant, sul_grant_cb, + GRANT_US); + break; + + case LWS_CALLBACK_CLIENT_RECEIVE: + for (size_t i = 0; i < len; i++) + if (((uint8_t *)in)[i] != + (uint8_t)('A' + ((cli_rx + i) % 26))) { + lwsl_err("--- pattern corrupt at ofs %d ---\n", + (int)(cli_rx + i)); + interrupted = 1; + return -1; + } + cli_rx += len; + + /* + * ws frame headers consume window as well, so honest payload + * rx is always strictly below the granted DATA credit + */ + if (cli_rx > cli_granted) { + lwsl_err("--- flow control violated: rx %d > granted %d ---\n", + (int)cli_rx, (int)cli_granted); + interrupted = 1; + return -1; + } + + if (cli_rx == TEST_TOTAL) { + lwsl_user("--- transfer complete and in-window. " + "Test passed. ---\n"); + result = 0; + interrupted = 1; + return -1; + } + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("--- client connection error: %s ---\n", + in ? (char *)in : "(null)"); + interrupted = 1; + break; + + case LWS_CALLBACK_CLIENT_CLOSED: + lwsl_user("%s: client: closed (rx %d granted %d)\n", __func__, + (int)cli_rx, (int)cli_granted); + client_wsi = NULL; + if (result) + interrupted = 1; + break; + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +static const struct lws_protocols protocols_srv[] = { + { "http", lws_callback_http_dummy, 0, 0, 0, NULL, 0 }, + { "txcr", callback_srv, 0, TEST_CHUNK, 0, NULL, 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static const struct lws_protocols protocols_cli[] = { + { "txcr", callback_cli, 0, TEST_CHUNK, 0, NULL, 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_client_connect_info i; + struct lws_vhost *vh; + const char *p; + int n = 0; + + lws_context_info_defaults(&info, NULL); + info.fd_limit_per_thread = 0; + lws_cmdline_option_handle_builtin(argc, argv, &info); + + if ((p = lws_cmdline_option(argc, argv, "-p"))) + port_tcp = atoi(p); + + signal(SIGINT, sigint_handler); + + lwsl_user("LWS API selftest: ws-over-h2 tx credit\n"); + + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + /* ws server vhost, h2 alpn */ + info.port = port_tcp; + info.vhost_name = "srv"; + info.alpn = "h2,http/1.1"; + info.protocols = protocols_srv; + info.server_ssl_cert_mem = test_cert; + info.server_ssl_cert_mem_len = (unsigned int)strlen(test_cert); + info.server_ssl_private_key_mem = test_key; + info.server_ssl_private_key_mem_len = (unsigned int)strlen(test_key); + + vh = lws_create_vhost(context, &info); + if (!vh) { + lwsl_err("Failed to create server vhost\n"); + goto bail; + } + + /* client vhost */ + info.port = CONTEXT_PORT_NO_LISTEN; + info.vhost_name = "cli"; + info.protocols = protocols_cli; + info.server_ssl_cert_mem = NULL; + info.server_ssl_cert_mem_len = 0; + info.server_ssl_private_key_mem = NULL; + info.server_ssl_private_key_mem_len = 0; + + vh = lws_create_vhost(context, &info); + if (!vh) { + lwsl_err("Failed to create client vhost\n"); + goto bail; + } + + memset(&i, 0, sizeof(i)); + i.context = context; + i.vhost = vh; + i.address = "127.0.0.1"; + i.port = port_tcp; + i.path = "/"; + i.host = "localhost"; + i.origin = "localhost"; + i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED | + LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | + LCCSCF_H2_MANUAL_RXFLOW; + i.manual_initial_tx_credit = GRANT_QUANTUM; + i.alpn = "h2"; + i.protocol = "txcr"; + i.local_protocol_name = "txcr"; + cli_granted = GRANT_QUANTUM; + + if (!lws_client_connect_via_info(&i)) { + lwsl_err("client connect failed\n"); + goto bail; + } + + lws_sul_schedule(context, 0, &sul_timeout, sul_timeout_cb, + 20 * LWS_US_PER_SEC); + + while (n >= 0 && !interrupted) + n = lws_service(context, 0); + +bail: + lws_context_destroy(context); + + lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS"); + + return result; +} From 69eb90a14d3762e6fe39b3f8fff37f7747c0bd0f Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sat, 18 Jul 2026 19:19:02 +0100 Subject: [PATCH 39/44] gnutls: h2 fixes + clean pointer arithmetic --- lib/core/private-lib-core.h | 2 +- lib/roles/quic/ops-quic.c | 2 +- lib/tls/gnutls/gnutls-quic.c | 4 ++-- lib/tls/gnutls/gnutls-ssl.c | 1 + lib/tls/gnutls/gnutls-tls.c | 21 ++++++++++++++++++++- lib/tls/openssl/openssl-quic.c | 9 --------- lib/tls/schannel/schannel-quic.c | 4 ++-- lib/tls/schannel/schannel-ssl.c | 4 ++-- 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index 56675fc4c9..146ae9a307 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -443,7 +443,7 @@ typedef enum { LWS_H3_STATE_FAILED_IGNORE } lws_h3_state_t; -#define LWS_QUIC_GRACE_DEFAULT_US 30000000 +#define LWS_QUIC_GRACE_DEFAULT_US 2000000 #define LWS_QUIC_GRACE_MARGIN_US 200000 typedef struct { diff --git a/lib/roles/quic/ops-quic.c b/lib/roles/quic/ops-quic.c index 6879eed912..d75da0ea7d 100644 --- a/lib/roles/quic/ops-quic.c +++ b/lib/roles/quic/ops-quic.c @@ -3502,7 +3502,7 @@ rops_alpn_negotiated_quic(struct lws *wsi, const char *alpn) if (wsi->a.context->h3_cap_cache && wsi->stash && wsi->stash->cis[CIS_HOST]) { lws_h3_cap_info_t cap; cap.state = LWS_H3_STATE_KNOWN_GOOD; - cap.latency_us = (uint32_t)(lws_now_usecs() - wsi->quic.quic_race_start_us); + cap.latency_us = (uint32_t)(lws_now_usecs() - nwsi->quic.quic_race_start_us); lws_cache_write_through(wsi->a.context->h3_cap_cache, wsi->stash->cis[CIS_HOST], (const uint8_t *)&cap, sizeof(cap), diff --git a/lib/tls/gnutls/gnutls-quic.c b/lib/tls/gnutls/gnutls-quic.c index a2566aa8fd..03f02873be 100644 --- a/lib/tls/gnutls/gnutls-quic.c +++ b/lib/tls/gnutls/gnutls-quic.c @@ -351,10 +351,10 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) char *comma = strchr(p, ','); alpn[i].data = (uint8_t *)p; if (comma) { - alpn[i].size = (unsigned int)(comma - p); + alpn[i].size = (unsigned int)lws_ptr_diff_size_t(comma, p); p = comma + 1; } else { - alpn[i].size = (unsigned int)(end - p); + alpn[i].size = (unsigned int)lws_ptr_diff_size_t(end, p); p = end; } /* Replace comma with NUL for cleaner logging, though gnutls only uses size */ diff --git a/lib/tls/gnutls/gnutls-ssl.c b/lib/tls/gnutls/gnutls-ssl.c index 3be4c62ccd..93987e830d 100644 --- a/lib/tls/gnutls/gnutls-ssl.c +++ b/lib/tls/gnutls/gnutls-ssl.c @@ -201,6 +201,7 @@ lws_tls_client_connect(struct lws *wsi, char *errbuf, size_t len) #if defined(LWS_WITH_TLS_SESSIONS) lws_tls_session_new_gnutls(wsi); #endif + lws_tls_server_conn_alpn(wsi); return LWS_SSL_CAPABLE_DONE; } diff --git a/lib/tls/gnutls/gnutls-tls.c b/lib/tls/gnutls/gnutls-tls.c index ccd57c0b58..8f29e4e26a 100644 --- a/lib/tls/gnutls/gnutls-tls.c +++ b/lib/tls/gnutls/gnutls-tls.c @@ -479,7 +479,26 @@ lws_ssl_client_bio_create(struct lws *wsi) lws_tls_reuse_session(wsi); #endif - if (wsi->a.vhost->tls.alpn_ctx.len) { + if (wsi->alpn[0]) { + gnutls_datum_t alpn[4]; + unsigned int i = 0; + char *p = wsi->alpn; + char *end = p + strlen(p); + + while (p < end && i < 4) { + char *comma = strchr(p, ','); + alpn[i].data = (uint8_t *)p; + if (comma) { + alpn[i].size = (unsigned int)lws_ptr_diff_size_t(comma, p); + p = comma + 1; + } else { + alpn[i].size = (unsigned int)lws_ptr_diff_size_t(end, p); + p = end; + } + i++; + } + gnutls_alpn_set_protocols(session, alpn, i, 0); + } else if (wsi->a.vhost->tls.alpn_ctx.len) { gnutls_datum_t alpn[4]; unsigned int i = 0, p = 0; while (p < wsi->a.vhost->tls.alpn_ctx.len && i < 4) { diff --git a/lib/tls/openssl/openssl-quic.c b/lib/tls/openssl/openssl-quic.c index e08d54ab68..2d2ccf3fc0 100644 --- a/lib/tls/openssl/openssl-quic.c +++ b/lib/tls/openssl/openssl-quic.c @@ -301,7 +301,6 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) if (wsi->a.vhost && (wsi->a.vhost->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA)) { #if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) SSL_set_early_data_enabled(wsi->tls.ssl, 1); - SSL_set_max_early_data(wsi->tls.ssl, wsi->a.context->quic_0rtt_max_size ? wsi->a.context->quic_0rtt_max_size : 0xFFFFFFFF); #endif } SSL_set_accept_state(wsi->tls.ssl); @@ -617,18 +616,10 @@ lws_tls_quic_init(struct lws *wsi, lws_tls_quic_secret_cb cb) SSL_set_app_data(wsi->tls.ssl, wsi); if (lwsi_role_client(wsi)) { - if (wsi->flags & LCCSCF_ALLOW_EARLY_DATA) { -#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) - SSL_set_early_data_enabled(wsi->tls.ssl, 1); -#endif - } SSL_set_connect_state(wsi->tls.ssl); } else { if (wsi->a.vhost && (wsi->a.vhost->options & LWS_SERVER_OPTION_ALLOW_EARLY_DATA)) { -#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) - SSL_set_early_data_enabled(wsi->tls.ssl, 1); SSL_set_max_early_data(wsi->tls.ssl, wsi->a.context->quic_0rtt_max_size ? wsi->a.context->quic_0rtt_max_size : 0xFFFFFFFF); -#endif } SSL_set_accept_state(wsi->tls.ssl); } diff --git a/lib/tls/schannel/schannel-quic.c b/lib/tls/schannel/schannel-quic.c index dfaf77bb8a..626b49fb31 100644 --- a/lib/tls/schannel/schannel-quic.c +++ b/lib/tls/schannel/schannel-quic.c @@ -283,7 +283,7 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, while (p && *p) { const char *comma = strchr(p, ','); - size_t item_len = comma ? (size_t)(comma - p) : strlen(p); + size_t item_len = comma ? lws_ptr_diff_size_t(comma, p) : strlen(p); if (item_len > 255 || (pData + item_len + 1 - alpn_u.buf) > 256) break; *pData++ = (uint8_t)item_len; memcpy(pData, p, item_len); @@ -327,7 +327,7 @@ lws_tls_quic_advance_handshake(struct lws *wsi, int level, while (p && *p) { const char *comma = strchr(p, ','); - size_t item_len = comma ? (size_t)(comma - p) : strlen(p); + size_t item_len = comma ? lws_ptr_diff_size_t(comma, p) : strlen(p); if (item_len > 255 || (pData + item_len + 1 - alpn_u.buf) > 256) break; *pData++ = (uint8_t)item_len; memcpy(pData, p, item_len); diff --git a/lib/tls/schannel/schannel-ssl.c b/lib/tls/schannel/schannel-ssl.c index 3d6d7f1745..adf2567920 100644 --- a/lib/tls/schannel/schannel-ssl.c +++ b/lib/tls/schannel/schannel-ssl.c @@ -151,7 +151,7 @@ lws_tls_client_connect(struct lws *wsi, char *errbuf, size_t len) while (p < end) { const char *comma = strchr(p, ','); size_t item_len; - if (comma) item_len = comma - p; + if (comma) item_len = lws_ptr_diff_size_t(comma, p); else item_len = strlen(p); if (item_len > 0 && item_len < 256) { @@ -417,7 +417,7 @@ lws_tls_server_accept(struct lws *wsi) while (p && *p) { const char *comma = strchr(p, ','); - size_t item_len = comma ? (size_t)(comma - p) : strlen(p); + size_t item_len = comma ? lws_ptr_diff_size_t(comma, p) : strlen(p); if (item_len > 255 || (pData + item_len + 1 - alpn_buf) > 256) break; *pData++ = (uint8_t)item_len; memcpy(pData, p, item_len); From 347764c1f83141c81955300cda7cbb8c3bf55d71 Mon Sep 17 00:00:00 2001 From: Yucong Sun Date: Sat, 18 Jul 2026 20:44:38 +0000 Subject: [PATCH 40/44] ws-over-h2: don't conflate nwsi buffered output with the stream's parked frames The tx flow-control parking introduced in 57effc65 gates the park, drain and re-arm logic on lws_has_buffered_out(), which for an h2 stream also reports the NETWORK wsi's buflist_out -- the buffer that fills whenever lws_issue_raw() takes a partial socket write. The drain logic, however, assumes the predicate describes the stream's OWN parked frames, which breaks four ways under ordinary socket-level backpressure: - lws_h2_ws_drain_parked_tx(): after the stream's last parked frame is consumed, a partial write left by its own lws_issue_raw(nwsi) keeps the while (lws_has_buffered_out(wsi)) loop alive via the nwsi branch; the next iteration reads the empty stream buflist and returns -1, and rops_perform_user_POLLOUT_h2() closes a healthy stream that had just flushed everything it owned. - rops_perform_user_POLLOUT_h2(): the post-drain re-arm is skipped whenever the nwsi still holds a partial, on the assumption that a WINDOW_UPDATE will re-arm the child. When the peer's window is large (tx credit ample, bottleneck is the socket) no WINDOW_UPDATE is coming, requested_POLLOUT was already consumed, and the pending- writable accounting then drops POLLOUT on the connection: the user callback never fires again and the transfer stalls permanently. - the same gate pulls a SIBLING ws stream -- nothing of its own parked, POLLOUT requested -- into the drain once an earlier child's drain congests the nwsi in the same service pass, hitting the identical empty-buflist close. - lws_h2_frame_write(): the parking predicate fires on nwsi congestion caused by another stream, needlessly deferring fully-credited frames into the drain path instead of letting lws_issue_raw(nwsi) append them in order. Test the stream's own buflist_out directly in all four places, and let a carries-ws child with nothing of its own parked fall through to normal servicing. The existing api-test doesn't catch these because its drip-fed WINDOW_UPDATEs continually re-arm every child and localhost doesn't produce partial writes. --- lib/roles/h2/http2.c | 10 ++++++++-- lib/roles/h2/ops-h2.c | 23 ++++++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 1bc0dbcabb..5aa70d2516 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -706,7 +706,7 @@ int lws_h2_frame_write(struct lws *wsi, int type, int flags, if (type == LWS_H2_FRAME_TYPE_DATA) { #if defined(LWS_ROLE_WS) if (wsi->h23_stream_carries_ws && - (lws_has_buffered_out(wsi) || + (wsi->buflist_out || lws_h2_tx_cr_get(wsi) < (int)len)) { /* * ws-over-h2 (RFC 8441): not enough h2 flow-control @@ -768,7 +768,13 @@ lws_h2_ws_drain_parked_tx(struct lws *nwsi, struct lws *wsi) { uint8_t out[LWS_H2_FRAME_HEADER_LENGTH + 4096]; - while (lws_has_buffered_out(wsi)) { + /* + * Test the stream's own buflist directly: lws_has_buffered_out() + * additionally reports the nwsi's buffer, which fills up whenever + * lws_issue_raw() below takes a partial socket write and must not + * keep us looping after our own parked frames are gone. + */ + while (wsi->buflist_out) { uint8_t *seg = NULL; size_t sl = lws_buflist_next_segment_len(&wsi->buflist_out, &seg); diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index b3c94e7481..926d60e3df 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -1089,13 +1089,20 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) /* priority 1: post compression-transform buffered output */ - if (lws_has_buffered_out(w)) { #if defined(LWS_ROLE_WS) - if (w->h23_stream_carries_ws) { - /* - * ws-over-h2: whole DATA frames parked by - * lws_h2_frame_write() waiting for tx credit - */ + if (w->h23_stream_carries_ws) { + /* + * ws-over-h2: whole DATA frames parked by + * lws_h2_frame_write() waiting for tx credit. Gate + * and re-arm on the stream's OWN buflist, not + * lws_has_buffered_out(): that also reports the + * nwsi's buffer (eg, another stream's partial socket + * write), which must neither pull us into the drain + * with nothing parked nor block re-arming the user + * once our own frames are gone. If nothing of ours + * is parked, fall through to normal servicing. + */ + if (w->buflist_out) { if (lws_h2_ws_drain_parked_tx(wsi, w) < 0) { lwsl_info("%s signalling to close\n", __func__); @@ -1105,7 +1112,7 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) wa = &wsi->mux.child_list; goto next_child; } - if (!lws_has_buffered_out(w)) + if (!w->buflist_out) /* fully drained: let the user write */ lws_callback_on_writable(w); /* @@ -1115,7 +1122,9 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) wa = &wsi->mux.child_list; goto next_child; } + } else #endif + if (lws_has_buffered_out(w)) { lwsl_debug("%s: completing partial\n", __func__); if (lws_issue_raw(w, NULL, 0) < 0) { lwsl_info("%s signalling to close\n", __func__); From 774c64a8d012876659fea2f51f737c54c21a20f3 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 19 Jul 2026 05:18:58 +0100 Subject: [PATCH 41/44] ws-over-h2: deal with choked nwsi --- lib/roles/h2/ops-h2.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 926d60e3df..90dc76e732 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -1090,7 +1090,7 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) /* priority 1: post compression-transform buffered output */ #if defined(LWS_ROLE_WS) - if (w->h23_stream_carries_ws) { + if (w->h23_stream_carries_ws && w->buflist_out) { /* * ws-over-h2: whole DATA frames parked by * lws_h2_frame_write() waiting for tx credit. Gate @@ -1102,28 +1102,29 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) * once our own frames are gone. If nothing of ours * is parked, fall through to normal servicing. */ - if (w->buflist_out) { - if (lws_h2_ws_drain_parked_tx(wsi, w) < 0) { - lwsl_info("%s signalling to close\n", - __func__); - lws_close_free_wsi(w, - LWS_CLOSE_STATUS_NOSTATUS, - "h2 end stream 1"); - wa = &wsi->mux.child_list; - goto next_child; - } - if (!w->buflist_out) - /* fully drained: let the user write */ - lws_callback_on_writable(w); - /* - * else still skint: stay quiet, the - * WINDOW_UPDATE handler re-arms every child - */ + + if (lws_h2_ws_drain_parked_tx(wsi, w) < 0) { + lwsl_info("%s signalling to close\n", + __func__); + lws_close_free_wsi(w, + LWS_CLOSE_STATUS_NOSTATUS, + "h2 end stream 1"); wa = &wsi->mux.child_list; goto next_child; } - } else + if (!w->buflist_out) + /* fully drained: let the user write */ + lws_callback_on_writable(w); + /* + * else still skint: stay quiet, the + * WINDOW_UPDATE handler re-arms every child + */ + wa = &wsi->mux.child_list; + goto next_child; + } #endif + /* ... for both ws-over-h2 and h2, deal with partial at nwsi */ + if (lws_has_buffered_out(w)) { lwsl_debug("%s: completing partial\n", __func__); if (lws_issue_raw(w, NULL, 0) < 0) { From ae2b73a4767f27cf17de92641b9ac732b5984ad6 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 19 Jul 2026 06:04:32 +0100 Subject: [PATCH 42/44] oauth: cleanup --- .../protocol_lws_auth_server.c | 14 +++++++++----- plugins/protocol_lws_login/protocol_lws_login.c | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c index aa32ec04bd..6bb37a75c2 100644 --- a/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c +++ b/plugins/protocol_lws_auth_server/protocol_lws_auth_server.c @@ -2442,6 +2442,7 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, } int logged_in = 0; + int is_admin = 0; int lacks_grant = 0; char sname[128] = {0}; char user_email[128] = {0}; @@ -2519,11 +2520,14 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, if (sqlite3_prepare_v2(vhd->db, "SELECT s.name, g.grant_level FROM grants g JOIN services s ON g.service_id = s.service_id WHERE g.uid = ?", -1, &stmt_u, NULL) == SQLITE_OK) { sqlite3_bind_int(stmt_u, 1, (int)suid); while (sqlite3_step(stmt_u) == SQLITE_ROW) { + const char *sname_db = (const char *)sqlite3_column_text(stmt_u, 0); + int gl = sqlite3_column_int(stmt_u, 1); + if (!strcmp(sname_db, "*") && gl >= 1) + is_admin = 1; + if (!first) gp += lws_snprintf(gp, lws_ptr_diff_size_t(gend, gp), ", "); first = 0; - gp += lws_snprintf(gp, lws_ptr_diff_size_t(gend, gp), "\"%s\": %d", - (const char *)sqlite3_column_text(stmt_u, 0), - sqlite3_column_int(stmt_u, 1)); + gp += lws_snprintf(gp, lws_ptr_diff_size_t(gend, gp), "\"%s\": %d", sname_db, gl); } sqlite3_finalize(stmt_u); } @@ -2642,8 +2646,8 @@ callback_auth_server(struct lws *wsi, enum lws_callback_reasons reason, pss->http_response_code = HTTP_STATUS_OK; char pl[LWS_SSO_MAX_COOKIE + LWS_PRE]; int len = lws_snprintf(pl + LWS_PRE, sizeof(pl) - LWS_PRE, - "{\"users_empty\":%d, \"csrf_token\":\"%s\", \"logged_in\":%d, \"lacks_grant\":%d, \"email\":\"%s\", \"strikes\":%d, \"grants\":{%s}, \"logs\":[%s]}", - users_empty, csrf, logged_in, lacks_grant, user_email, strikes, grants, logs); + "{\"users_empty\":%d, \"csrf_token\":\"%s\", \"logged_in\":%d, \"is_admin\":%d, \"lacks_grant\":%d, \"email\":\"%s\", \"strikes\":%d, \"grants\":{%s}, \"logs\":[%s]}", + users_empty, csrf, logged_in, is_admin, lacks_grant, user_email, strikes, grants, logs); if (lws_buflist_append_segment(&pss->tx_buflist, (uint8_t *)pl, (size_t)len + LWS_PRE) < 0) return -1; diff --git a/plugins/protocol_lws_login/protocol_lws_login.c b/plugins/protocol_lws_login/protocol_lws_login.c index 6826fced09..3cc16ae77f 100644 --- a/plugins/protocol_lws_login/protocol_lws_login.c +++ b/plugins/protocol_lws_login/protocol_lws_login.c @@ -1053,7 +1053,7 @@ callback_lws_login(struct lws *wsi, enum lws_callback_reasons reason, if (lws_login_ends_with(path, "/.lws-login-refresh")) { char csrf[64] = {0}; size_t csrf_len = sizeof(csrf); - char refresh_session[16] = {0}; + char refresh_session[128] = {0}; size_t refresh_session_len = sizeof(refresh_session); int ck_len; From 9428500582adadf0e1479a5abb108e21600100a8 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 19 Jul 2026 06:21:43 +0100 Subject: [PATCH 43/44] HE: if no rival grace period is whole timeout --- lib/core-net/client/connect3.c | 74 +++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 2d90698733..dfaf64c7c9 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -78,6 +78,25 @@ lws_client_h3_grace_cb(lws_sorted_usec_list_t *sul) { struct lws *wsi = lws_container_of(sul, struct lws, sul_h3_grace); + if (!wsi->role_ops || strcmp(wsi->role_ops->name, "quic") != 0) + return; + + int first_valid = -1; + for (int i = 0; i < wsi->parallel_count; i++) { + if (wsi->parallel_conns[i].is_valid) { + first_valid = i; + break; + } + } + + if (first_valid == -1 && !wsi->dns_sorted_list.count) { + /* There are no parallel TCP sockets, and no more DNS results to try! + * We cannot fallback to TCP. Do not abort the QUIC race, just + * let it run until the overall connect timeout expires. */ + lwsl_wsi_notice(wsi, "H3 grace timer expired, but no TCP fallback available. Keeping QUIC."); + return; + } + lwsl_wsi_notice(wsi, "H3 grace timer expired, abandoning QUIC race"); /* Mark H3 as FAILED in cache with 5s TTL */ @@ -91,41 +110,32 @@ lws_client_h3_grace_cb(lws_sorted_usec_list_t *sul) } /* Abort QUIC and revert to TCP */ - if (wsi->role_ops && strcmp(wsi->role_ops->name, "quic") == 0) { - if (lws_socket_is_valid(wsi->desc.sockfd)) { - struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; - lws_pt_lock(pt, __func__); - __remove_wsi_socket_from_fds(wsi); - lws_pt_unlock(pt); - compatible_close(wsi->desc.sockfd); - wsi->desc.sockfd = LWS_SOCK_INVALID; - } + if (lws_socket_is_valid(wsi->desc.sockfd)) { + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + lws_pt_lock(pt, __func__); + __remove_wsi_socket_from_fds(wsi); + lws_pt_unlock(pt); + compatible_close(wsi->desc.sockfd); + wsi->desc.sockfd = LWS_SOCK_INVALID; + } - if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection)) - lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection).close_kill_connection(wsi, LWS_CLOSE_STATUS_NOSTATUS); + if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection)) + lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection).close_kill_connection(wsi, LWS_CLOSE_STATUS_NOSTATUS); - /* Promote the first parallel TCP connection */ - int first_valid = -1; - for (int i = 0; i < wsi->parallel_count; i++) { - if (wsi->parallel_conns[i].is_valid) { - first_valid = i; - break; - } - } - if (first_valid != -1) { - const struct lws_role_ops *r = lws_role_by_name("h2"); - if (!r) r = lws_role_by_name("h1"); - if (r) { - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_WAITING_CONNECT, r); - } - wsi->desc.sockfd = wsi->parallel_conns[first_valid].desc.sockfd; - wsi->position_in_fds_table = wsi->parallel_conns[first_valid].position_in_fds_table; - wsi->parallel_conns[first_valid].is_valid = 0; - /* We changed the primary fd, the event loop will trigger POLLOUT if it's connected */ - } else { - /* No TCP sockets survived? Fail connection. */ - lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL); + /* Promote the first parallel TCP connection */ + if (first_valid != -1) { + const struct lws_role_ops *r = lws_role_by_name("h2"); + if (!r) r = lws_role_by_name("h1"); + if (r) { + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_WAITING_CONNECT, r); } + wsi->desc.sockfd = wsi->parallel_conns[first_valid].desc.sockfd; + wsi->position_in_fds_table = wsi->parallel_conns[first_valid].position_in_fds_table; + wsi->parallel_conns[first_valid].is_valid = 0; + /* We changed the primary fd, the event loop will trigger POLLOUT if it's connected */ + } else { + /* No TCP sockets survived? Fail connection. */ + lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL); } } From d4e6802230952720a9a76af4f354dffb70846dc7 Mon Sep 17 00:00:00 2001 From: Yucong Sun Date: Mon, 20 Jul 2026 19:34:35 +0000 Subject: [PATCH 44/44] ws-over-h2: permessage-deflate fixes: short return, ext tx-drain wedge, client extension offer -- with selftest Three fixes for permessage-deflate on ws-over-h2 (RFC 8441) encapsulated streams, plus a ctest-wired selftest: 1) The encapsulated tx path returned the h2 role's write result, which reflects the POST-COMPRESSION frame size -- with pmd active that is routinely smaller than what the caller passed in, so any well-compressed message made callers checking (wr < len) conclude a short write and kill a healthy connection. The lws_write() contract is to report how much of the CALLER's payload was accepted, and the h1 path already returns orig_len for exactly this reason; do the same, still propagating write errors as < 0. 2) When pmd's compressed output exceeds its chunk buffer, the ws role sets ws->tx_draining_ext and expects the next POLLOUT to send the remaining fragments -- the h1 path services this in rops_handle_POLLOUT_ws() priority 5, before letting the user write anything new. An h2-encapsulated child never reaches that path: its only POLLOUT servicing is the child loop in rops_perform_user_POLLOUT_h2(), which went straight to the user callback. Meanwhile lws_send_pipe_choked() reports choked while tx_draining_ext is set, so a user write loop gated on it never writes again either: the drain never advances and the stream wedges permanently. Service the drain from the child loop, ahead of the user callback. It slots in after the generic 'deal with partial at nwsi' branch that 774c64a8 ("ws-over-h2: deal with choked nwsi") routes carries-ws children through, preserving its ordering: a child on a backed-up network socket still defers there first, the same priority h1 connections get, and the extension drain only runs once the nwsi partial has cleared. 3) lws_h2_client_handshake() only emitted sec-websocket-version and sec-websocket-protocol, so an lws client could never negotiate permessage-deflate (or any extension) over h2 even against a willing server. RFC 8441 Sect 5 carries the ws handshake headers unchanged over extended CONNECT, so build the same offer list as lws_generate_client_ws_handshake() and emit it after the pseudo-headers; the response side already instantiates accepted extensions in the shared lws_client_ws_upgrade() path. api-test-ws-h2-pmd (needs LWS_WITHOUT_EXTENSIONS=OFF): single-process h2 ws server + client, both offering pmd; the server bulk-sends a 64KB deterministic pattern in 4KB messages gated only on lws_send_pipe_choked(), alternating trivially-compressible blocks (deflated frame far smaller than the payload) with incompressible hash noise (deflated output overflows pmd's default 1KB chunk buffer, forcing tx_draining_ext). It fails if the connection is not genuinely ws-over-h2, if the extended CONNECT did not offer pmd, if any lws_write() reports fewer bytes accepted than requested, if the pattern corrupts, or if the transfer stalls. Reverting any one of the three fixes makes it fail in the corresponding distinct way. --- lib/roles/h2/http2.c | 34 ++ lib/roles/h2/ops-h2.c | 27 ++ lib/roles/ws/ops-ws.c | 16 +- .../api-test-ws-h2-pmd/CMakeLists.txt | 37 ++ .../api-tests/api-test-ws-h2-pmd/main.c | 423 ++++++++++++++++++ 5 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/CMakeLists.txt create mode 100644 minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/main.c diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 5aa70d2516..48d2d925e9 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -2870,6 +2870,40 @@ lws_h2_client_handshake(struct lws *wsi) goto fail_length; } +#if !defined(LWS_WITHOUT_EXTENSIONS) + { + /* + * Offer the vhost's extensions the same way the h1 + * upgrade does (RFC 8441 Sect 5 carries the ws + * headers unchanged): without this the client can + * never negotiate eg, permessage-deflate over h2. + */ + const struct lws_extension *ext = + wsi->a.vhost->ws.extensions; + char eb[256]; + int el = 0; + + while (ext && ext->callback) { + if (wsi->a.vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, + wsi->user_space, (char *)ext->name, 0)) { + ext++; + continue; + } + el += lws_snprintf(eb + (size_t)el, + sizeof(eb) - (size_t)el, + "%s%s", el ? "," : "", + ext->client_offer); + ext++; + } + if (el && + lws_add_http_header_by_token(wsi, + WSI_TOKEN_EXTENSIONS, + (unsigned char *)eb, el, &p, end)) + goto fail_length; + } +#endif + wsi->h23_stream_carries_ws = 1; } #endif diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 90dc76e732..640005fc8a 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -1139,6 +1139,33 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) goto next_child; } +#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) + /* + * ws-over-h2: tx path extension with more to send (eg, + * permessage-deflate whose compressed output exceeded its + * chunk buffer). The h1 path services this from + * rops_handle_POLLOUT_ws() priority 5; this loop is the ONLY + * POLLOUT servicing an encapsulated child ever gets, so it + * must do the same -- while tx_draining_ext is set, + * lws_send_pipe_choked() reports choked, so the user + * callback will never write again and the connection wedges + * for good. + */ + if (lwsi_role_ws(w) && lwsi_state(w) == LRS_ESTABLISHED && + w->ws && w->ws->tx_draining_ext) { + if (lws_write(w, NULL, 0, LWS_WRITE_CONTINUATION) < 0) { + lwsl_info("%s signalling to close\n", __func__); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, + "h2 ws ext drain"); + wa = &wsi->mux.child_list; + goto next_child; + } + lws_callback_on_writable(w); + wa = &wsi->mux.child_list; + goto next_child; + } +#endif + /* priority 2: pre compression-transform buffered output */ #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 841656af62..bb2ef32c37 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -2001,10 +2001,24 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, } #endif - return lws_rops_func_fidx(encap->role_ops, + n = lws_rops_func_fidx(encap->role_ops, LWS_ROPS_write_role_protocol). write_role_protocol(wsi, buf - pre, len + (unsigned int)pre, wp); + if (n < 0) + return n; + + /* + * The lws_write() contract is to report how much of the + * CALLER's payload was accepted. len here is the + * post-extension (eg, permessage-deflate compressed) frame + * size, which is routinely SMALLER than what the caller + * passed in -- returning it (as this path used to) makes + * well-behaved callers conclude the write failed short and + * kill the connection. The h1 path returns orig_len for + * exactly this reason; do the same. + */ + return (int)orig_len; } switch ((*wp) & 0x1f) { diff --git a/minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/CMakeLists.txt b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/CMakeLists.txt new file mode 100644 index 0000000000..10102f2cf9 --- /dev/null +++ b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/CMakeLists.txt @@ -0,0 +1,37 @@ +project(lws-api-test-ws-h2-pmd C) +cmake_minimum_required(VERSION 3.10) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-api-test-ws-h2-pmd) +set(SRCS main.c) + +set(requirements 1) +require_lws_config(LWS_ROLE_WS 1 requirements) +require_lws_config(LWS_ROLE_H2 1 requirements) +require_lws_config(LWS_WITH_CLIENT 1 requirements) +require_lws_config(LWS_WITH_SERVER 1 requirements) +require_lws_config(LWS_WITH_TLS 1 requirements) +require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() + + lws_get_free_port(PORT_WS_H2_PMD) + + add_test(NAME api-test-ws-h2-pmd COMMAND lws-api-test-ws-h2-pmd -p ${PORT_WS_H2_PMD}) + set_tests_properties(api-test-ws-h2-pmd + PROPERTIES + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd + TIMEOUT 60) + +endif() diff --git a/minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/main.c b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/main.c new file mode 100644 index 0000000000..922693c586 --- /dev/null +++ b/minimal-examples-lowlevel/api-tests/api-test-ws-h2-pmd/main.c @@ -0,0 +1,423 @@ +/* + * lws-api-test-ws-h2-pmd + * + * Written in 2010-2026 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * Tests permessage-deflate over ws-over-h2 (RFC 8441) tx. + * + * A ws server vhost and an h2 ws client run in one process, both offering + * permessage-deflate. The server bulk-sends a 64KB deterministic pattern in + * 4KB messages, gated only on lws_send_pipe_choked(), like typical user code. + * Alternate messages are highly compressible (so the deflated frame is much + * smaller than the caller's payload) and incompressible (so the deflated + * output overflows pmd's default 1KB chunk buffer and the extension must + * drain across several POLLOUT services). + * + * That exercises the two encapsulated-tx bugs this test was written against: + * + * - lws_write() on a ws-over-h2 stream returned the POST-COMPRESSION frame + * size instead of the caller's payload length, so any well-compressed + * message made callers checking (wr < len) conclude a short write and + * kill the connection, and + * + * - ws->tx_draining_ext was never serviced for h2-encapsulated children + * (only rops_handle_POLLOUT_ws() does it, which mux children never + * reach), while lws_send_pipe_choked() reports choked during the drain, + * so the first message whose compressed output exceeded the pmd chunk + * buffer wedged the stream permanently. + * + * The test fails if + * - the negotiated connection is not actually ws-over-h2, + * - the client's upgrade did not offer permessage-deflate (the test would + * be vacuous), + * - any server lws_write() accepts fewer bytes than requested, + * - the received pattern is corrupted, or + * - the transfer doesn't complete in time (the drain wedge shows up here). + */ + +#include +#include +#include + +static int interrupted; +static int result = 1; +static struct lws_context *context; + +static const char * const test_cert = +"-----BEGIN CERTIFICATE-----\n" +"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\n" +"e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=\n" +"-----END CERTIFICATE-----\n"; + +static const char * const test_key = +"-----BEGIN PRIVATE KEY-----\n" +"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\n" +"fBrpEY1IATtPq1taBZZogRqI3rOkkPk=\n" +"-----END PRIVATE KEY-----\n"; + +#define TEST_TOTAL 65536 +#define TEST_CHUNK 4096 + +static struct lws *client_wsi; +static size_t srv_sent; +static size_t cli_rx; +static int saw_pmd_offer; +static int port_tcp = 7681; +static lws_sorted_usec_list_t sul_timeout; + +static const struct lws_extension extensions[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate" + "; client_no_context_takeover" + "; client_max_window_bits" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +/* + * Deterministic pattern both sides can generate from the absolute byte + * offset: even 4KB blocks are a trivially-compressible letter cycle (the + * deflated frame is a fraction of the payload, catching a short lws_write() + * return), odd blocks are multiplicative-hash noise that deflate cannot + * shrink (the compressed output overflows pmd's 1KB chunk buffer, forcing + * the tx_draining_ext path). + */ +static uint8_t +pattern_byte(size_t o) +{ + uint32_t v; + + if (!((o / TEST_CHUNK) & 1)) + return (uint8_t)('A' + (o % 26)); + + v = (uint32_t)o * 2654435761u; + + return (uint8_t)(v ^ (v >> 11) ^ (v >> 22)); +} + +static void +sul_timeout_cb(lws_sorted_usec_list_t *sul) +{ + lwsl_err("--- timeout: rx %d / %d, sent %d ---\n", + (int)cli_rx, TEST_TOTAL, (int)srv_sent); + interrupted = 1; +} + +static int +callback_srv(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + switch (reason) { + case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: { + char buf[256]; + + /* + * prove the client's upgrade actually offered pmd over the + * extended CONNECT, so a silently-dropped negotiation can't + * turn this into a test of nothing + */ + if (lws_hdr_copy(wsi, buf, sizeof(buf), + WSI_TOKEN_EXTENSIONS) > 0 && + strstr(buf, "permessage-deflate")) + saw_pmd_offer = 1; + break; + } + + case LWS_CALLBACK_ESTABLISHED: + lwsl_user("%s: server: established\n", __func__); + if (!saw_pmd_offer) { + lwsl_err("--- no permessage-deflate offer seen ---\n"); + interrupted = 1; + return -1; + } + srv_sent = 0; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: { + uint8_t buf[LWS_PRE + TEST_CHUNK]; + size_t n; + + /* + * bulk-send gated only on choked, like typical user code; + * while pmd is draining a big compressed message, choked + * reports 1 and the h2 child loop must advance the drain + * itself or we never come back here + */ + while (srv_sent < TEST_TOTAL && !lws_send_pipe_choked(wsi)) { + n = TEST_TOTAL - srv_sent; + if (n > TEST_CHUNK) + n = TEST_CHUNK; + for (size_t i = 0; i < n; i++) + buf[LWS_PRE + i] = pattern_byte(srv_sent + i); + int wr = lws_write(wsi, &buf[LWS_PRE], n, + LWS_WRITE_BINARY); + lwsl_info("%s: srv wrote %d of %d\n", __func__, wr, + (int)n); + if (wr < (int)n) { + lwsl_err("--- short write: accepted %d of %d " + "(post-compression size leaked?) ---\n", + wr, (int)n); + interrupted = 1; + return -1; + } + srv_sent += n; + } + if (srv_sent < TEST_TOTAL) + lws_callback_on_writable(wsi); + break; + } + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +static int +callback_cli(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: + lwsl_user("%s: client: established\n", __func__); + if (lws_get_network_wsi(wsi) == wsi) { + lwsl_err("--- not encapsulated in h2 ---\n"); + interrupted = 1; + return -1; + } + client_wsi = wsi; + break; + + case LWS_CALLBACK_CLIENT_RECEIVE: + for (size_t i = 0; i < len; i++) + if (((uint8_t *)in)[i] != pattern_byte(cli_rx + i)) { + lwsl_err("--- pattern corrupt at ofs %d ---\n", + (int)(cli_rx + i)); + interrupted = 1; + return -1; + } + cli_rx += len; + + if (cli_rx == TEST_TOTAL) { + lwsl_user("--- transfer complete and intact. " + "Test passed. ---\n"); + result = 0; + interrupted = 1; + return -1; + } + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("--- client connection error: %s ---\n", + in ? (char *)in : "(null)"); + interrupted = 1; + break; + + case LWS_CALLBACK_CLIENT_CLOSED: + lwsl_user("%s: client: closed (rx %d sent %d)\n", __func__, + (int)cli_rx, (int)srv_sent); + client_wsi = NULL; + if (result) + interrupted = 1; + break; + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +static const struct lws_protocols protocols_srv[] = { + { "http", lws_callback_http_dummy, 0, 0, 0, NULL, 0 }, + { "pmdt", callback_srv, 0, TEST_CHUNK, 0, NULL, 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +static const struct lws_protocols protocols_cli[] = { + { "pmdt", callback_cli, 0, TEST_CHUNK, 0, NULL, 0 }, + LWS_PROTOCOL_LIST_TERM +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_client_connect_info i; + struct lws_vhost *vh; + const char *p; + int n = 0; + + lws_context_info_defaults(&info, NULL); + info.fd_limit_per_thread = 0; + lws_cmdline_option_handle_builtin(argc, argv, &info); + + if ((p = lws_cmdline_option(argc, argv, "-p"))) + port_tcp = atoi(p); + + signal(SIGINT, sigint_handler); + + lwsl_user("LWS API selftest: ws-over-h2 permessage-deflate\n"); + + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + /* ws server vhost, h2 alpn, pmd on offer */ + info.port = port_tcp; + info.vhost_name = "srv"; + info.alpn = "h2,http/1.1"; + info.protocols = protocols_srv; + info.extensions = extensions; + info.server_ssl_cert_mem = test_cert; + info.server_ssl_cert_mem_len = (unsigned int)strlen(test_cert); + info.server_ssl_private_key_mem = test_key; + info.server_ssl_private_key_mem_len = (unsigned int)strlen(test_key); + + vh = lws_create_vhost(context, &info); + if (!vh) { + lwsl_err("Failed to create server vhost\n"); + goto bail; + } + + /* client vhost, pmd on offer */ + info.port = CONTEXT_PORT_NO_LISTEN; + info.vhost_name = "cli"; + info.protocols = protocols_cli; + info.server_ssl_cert_mem = NULL; + info.server_ssl_cert_mem_len = 0; + info.server_ssl_private_key_mem = NULL; + info.server_ssl_private_key_mem_len = 0; + + vh = lws_create_vhost(context, &info); + if (!vh) { + lwsl_err("Failed to create client vhost\n"); + goto bail; + } + + memset(&i, 0, sizeof(i)); + i.context = context; + i.vhost = vh; + i.address = "127.0.0.1"; + i.port = port_tcp; + i.path = "/"; + i.host = "localhost"; + i.origin = "localhost"; + i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED | + LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK; + i.alpn = "h2"; + i.protocol = "pmdt"; + i.local_protocol_name = "pmdt"; + + if (!lws_client_connect_via_info(&i)) { + lwsl_err("client connect failed\n"); + goto bail; + } + + lws_sul_schedule(context, 0, &sul_timeout, sul_timeout_cb, + 20 * LWS_US_PER_SEC); + + while (n >= 0 && !interrupted) + n = lws_service(context, 0); + +bail: + lws_context_destroy(context); + + lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS"); + + return result; +}